diff --git a/src/CMakeLists.txt b/src/CMakeLists.txt index 411c2b61..d6781e00 100644 --- a/src/CMakeLists.txt +++ b/src/CMakeLists.txt @@ -51,6 +51,21 @@ set( app/gui/pipeline.cc ) +set( + YAZE_APP_EMU_SRC + app/emu/emulator.cc + app/emu/audio/apu.cc + app/emu/audio/spc700.cc + app/emu/audio/dsp.cc + app/emu/cpu/instructions.cc + app/emu/cpu/addressing.cc + app/emu/video/ppu.cc + app/emu/memory/dma.cc + app/emu/memory/memory.cc + app/emu/cpu.cc + app/emu/snes.cc +) + set(SDL_TARGETS SDL2::SDL2) if(WIN32 OR MINGW) diff --git a/src/app/CMakeLists.txt b/src/app/CMakeLists.txt index b17e6f4d..4e9dd430 100644 --- a/src/app/CMakeLists.txt +++ b/src/app/CMakeLists.txt @@ -2,15 +2,7 @@ add_executable( yaze app/yaze.cc app/rom.cc - app/emu/emulator.cc - app/emu/audio/apu.cc - app/emu/audio/spc700.cc - app/emu/audio/dsp.cc - app/emu/video/ppu.cc - app/emu/memory/dma.cc - app/emu/memory/memory.cc - app/emu/cpu.cc - app/emu/snes.cc + ${YAZE_APP_EMU_SRC} ${YAZE_APP_CORE_SRC} ${YAZE_APP_EDITOR_SRC} ${YAZE_APP_GFX_SRC} @@ -23,10 +15,10 @@ target_include_directories( yaze PUBLIC lib/ app/ + lib/SDL_mixer/include/ ${CMAKE_SOURCE_DIR}/src/ ${PNG_INCLUDE_DIRS} ${SDL2_INCLUDE_DIR} - lib/SDL_mixer/include/ ) target_link_libraries( diff --git a/src/app/emu/cpu.cc b/src/app/emu/cpu.cc index 9f05266e..d412854b 100644 --- a/src/app/emu/cpu.cc +++ b/src/app/emu/cpu.cc @@ -20,7 +20,7 @@ void CPU::Update(UpdateMode mode, int stepCount) { } // Fetch and execute an instruction - ExecuteInstruction(FetchByte()); + ExecuteInstruction(ReadByte((PB << 16) + PC)); // Handle any interrupts, if necessary HandleInterrupts(); @@ -32,617 +32,958 @@ void CPU::Update(UpdateMode mode, int stepCount) { } void CPU::ExecuteInstruction(uint8_t opcode) { + uint8_t cycles = 0; + uint8_t instruction_length = 0; + uint32_t operand = 0; bool immediate = false; - uint16_t operand = 0; bool accumulator_mode = GetAccumulatorSize(); switch (opcode) { case 0x61: // ADC DP Indexed Indirect, X - operand = memory.ReadByte(DirectPageIndexedIndirectX()); + { + cycles = 6; + if (!m()) cycles++; + instruction_length = 2; + operand = ReadByte(DirectPageIndexedIndirectX()); ADC(operand); break; + } case 0x63: // ADC Stack Relative - operand = memory.ReadByte(StackRelative()); + { + cycles = 4; + if (!m()) cycles++; + instruction_length = 2; + operand = ReadByte(StackRelative()); ADC(operand); break; + } case 0x65: // ADC Direct Page - operand = FetchByteDirectPage(PC); + { + cycles = 3; + if (!m()) cycles++; + instruction_length = 2; + operand = ReadByte(DirectPage()); ADC(operand); break; + } case 0x67: // ADC DP Indirect Long - operand = memory.ReadWord(DirectPageIndirectLong()); + { + cycles = 6; + if (!m()) cycles++; + instruction_length = 2; + operand = ReadWord(DirectPageIndirectLong()); ADC(operand); break; + } case 0x69: // ADC Immediate + { + cycles = 2; + if (!m()) cycles++; + if (GetAccumulatorSize()) { + instruction_length = 2; + } else { + instruction_length = 3; + } operand = Immediate(); immediate = true; ADC(operand); break; + } case 0x6D: // ADC Absolute - operand = memory.ReadWord(Absolute()); + { + cycles = 4; + if (!m()) cycles++; + instruction_length = 3; + operand = ReadWord(Absolute()); ADC(operand); break; + } case 0x6F: // ADC Absolute Long - operand = memory.ReadWord(AbsoluteLong()); + { + cycles = 5; + if (!m()) cycles++; + instruction_length = 4; + operand = ReadWord(AbsoluteLong()); ADC(operand); break; + } case 0x71: // ADC DP Indirect Indexed, Y - operand = memory.ReadByte(DirectPageIndirectIndexedY()); + { + cycles = 5; + if (!m()) cycles++; + instruction_length = 2; + operand = ReadByteOrWord(DirectPageIndirectIndexedY()); ADC(operand); break; + } case 0x72: // ADC DP Indirect - operand = memory.ReadByte(DirectPageIndirect()); + { + cycles = 5; + if (!m()) cycles++; + instruction_length = 2; + operand = ReadByte(DirectPageIndirect()); ADC(operand); break; + } case 0x73: // ADC SR Indirect Indexed, Y - operand = memory.ReadByte(StackRelativeIndirectIndexedY()); + { + cycles = 7; + if (!m()) cycles++; + instruction_length = 2; + operand = ReadByte(StackRelativeIndirectIndexedY()); ADC(operand); break; + } case 0x75: // ADC DP Indexed, X - operand = memory.ReadByte(DirectPageIndexedX()); + { + cycles = 4; + if (!m()) cycles++; + instruction_length = 2; + operand = ReadByteOrWord(DirectPageIndexedX()); ADC(operand); break; + } case 0x77: // ADC DP Indirect Long Indexed, Y - operand = DirectPageIndirectLongIndexedY(); + { + cycles = 6; + if (!m()) cycles++; + instruction_length = 2; + operand = ReadByteOrWord(DirectPageIndirectLongIndexedY()); ADC(operand); break; + } case 0x79: // ADC Absolute Indexed, Y - operand = memory.ReadWord(AbsoluteIndexedY()); + { + cycles = 4; + if (!m()) cycles++; + instruction_length = 3; + operand = ReadWord(AbsoluteIndexedY()); ADC(operand); break; + } case 0x7D: // ADC Absolute Indexed, X - operand = memory.ReadWord(AbsoluteIndexedX()); + { + cycles = 4; + if (!m()) cycles++; + instruction_length = 3; + operand = ReadWord(AbsoluteIndexedX()); ADC(operand); break; + } case 0x7F: // ADC Absolute Long Indexed, X - operand = memory.ReadByte(AbsoluteLongIndexedX()); + { + cycles = 5; + if (!m()) cycles++; + instruction_length = 4; + operand = ReadByteOrWord(AbsoluteLongIndexedX()); ADC(operand); break; + } case 0x21: // AND DP Indexed Indirect, X - operand = memory.ReadByte(DirectPageIndexedIndirectX()); - AND(operand); + { + operand = ReadByteOrWord(DirectPageIndexedIndirectX()); + AND(operand, true); // Not immediate, but value has been retrieved break; + } case 0x23: // AND Stack Relative - operand = memory.ReadByte(StackRelative()); + { + operand = StackRelative(); AND(operand); break; + } case 0x25: // AND Direct Page - operand = FetchByteDirectPage(PC); + { + operand = DirectPage(); AND(operand); break; + } case 0x27: // AND DP Indirect Long - operand = memory.ReadByte(DirectPageIndirectLong()); + { + operand = DirectPageIndirectLong(); AND(operand); break; + } case 0x29: // AND Immediate + { operand = Immediate(); immediate = true; AND(operand, true); break; + } case 0x2D: // AND Absolute + { operand = Absolute(); AND(operand); break; + } case 0x2F: // AND Absolute Long + { operand = AbsoluteLong(); ANDAbsoluteLong(operand); break; + } case 0x31: // AND DP Indirect Indexed, Y - operand = memory.ReadByte(DirectPageIndirectIndexedY()); + { + operand = DirectPageIndirectIndexedY(); AND(operand); break; + } case 0x32: // AND DP Indirect - operand = memory.ReadByte(DirectPageIndirect()); + { + operand = DirectPageIndirect(); AND(operand); break; + } case 0x33: // AND SR Indirect Indexed, Y - operand = memory.ReadByte(StackRelativeIndirectIndexedY()); + { + operand = StackRelativeIndirectIndexedY(); AND(operand); break; + } case 0x35: // AND DP Indexed, X - operand = memory.ReadByte(DirectPageIndexedX()); + { + operand = DirectPageIndexedX(); AND(operand); break; + } case 0x37: // AND DP Indirect Long Indexed, Y - operand = memory.ReadByte(DirectPageIndirectLongIndexedY()); + { + operand = DirectPageIndirectLongIndexedY(); AND(operand); break; + } case 0x39: // AND Absolute Indexed, Y + { operand = AbsoluteIndexedY(); AND(operand); break; + } case 0x3D: // AND Absolute Indexed, X + { operand = AbsoluteIndexedX(); AND(operand); break; + } case 0x3F: // AND Absolute Long Indexed, X + { operand = AbsoluteLongIndexedX(); AND(operand); break; + } case 0x06: // ASL Direct Page + { operand = DirectPage(); ASL(operand); break; + } case 0x0A: // ASL Accumulator + { A <<= 1; A &= 0xFE; SetCarryFlag(A & 0x80); SetNegativeFlag(A); SetZeroFlag(!A); break; + } case 0x0E: // ASL Absolute + { operand = Absolute(); ASL(operand); break; + } case 0x16: // ASL DP Indexed, X - operand = ReadByBitMode((0x00 << 16) + DirectPageIndexedX()); + { + operand = ReadByteOrWord((0x00 << 16) + DirectPageIndexedX()); ASL(operand); break; + } case 0x1E: // ASL Absolute Indexed, X + { operand = AbsoluteIndexedX(); ASL(operand); break; + } case 0x90: // BCC Branch if carry clear - operand = memory.ReadByte(PC); + { + operand = FetchByte(); BCC(operand); break; + } case 0xB0: // BCS Branch if carry set - operand = memory.ReadByte(PC); + { + operand = FetchByte(); BCS(operand); break; + } case 0xF0: // BEQ Branch if equal (zero set) - operand = memory.ReadByte((PB << 16) + PC + 1); + { + operand = FetchByte(); BEQ(operand); break; + } case 0x24: // BIT Direct Page + { operand = DirectPage(); BIT(operand); break; + } case 0x2C: // BIT Absolute + { operand = Absolute(); BIT(operand); break; + } case 0x34: // BIT DP Indexed, X + { operand = DirectPageIndexedX(); BIT(operand); break; + } case 0x3C: // BIT Absolute Indexed, X + { operand = AbsoluteIndexedX(); BIT(operand); break; + } case 0x89: // BIT Immediate + { operand = Immediate(); BIT(operand); immediate = true; break; + } case 0x30: // BMI Branch if minus (negative set) - operand = memory.ReadByte((PB << 16) + PC + 1); + { + operand = FetchByte(); BMI(operand); break; + } case 0xD0: // BNE Branch if not equal (zero clear) - operand = memory.ReadByte((PB << 16) + PC + 1); + { + operand = FetchByte(); BNE(operand); break; + } case 0x10: // BPL Branch if plus (negative clear) - operand = memory.ReadByte((PB << 16) + PC + 1); + { + operand = FetchByte(); BPL(operand); break; + } case 0x80: // BRA Branch always - operand = memory.ReadByte((PB << 16) + PC + 1); + { + operand = FetchByte(); BRA(operand); break; + } case 0x00: // BRK Break + { BRK(); break; + } case 0x82: // BRL Branch always long - operand = FetchSignedWord(); - next_pc_ = operand; + { // operand = FetchSignedWord(); + operand = FetchWord(); + BRL(operand); break; + } case 0x50: // BVC Branch if overflow clear + { operand = FetchByte(); BVC(operand); break; + } case 0x70: // BVS Branch if overflow set + { operand = FetchByte(); BVS(operand); break; + } case 0x18: // CLC Clear carry + { CLC(); break; + } case 0xD8: // CLD Clear decimal + { CLD(); break; + } case 0x58: // CLI Clear interrupt disable + { CLI(); break; + } case 0xB8: // CLV Clear overflow + { CLV(); break; + } case 0xC1: // CMP DP Indexed Indirect, X - operand = DirectPageIndexedIndirectX(); + { + operand = ReadByteOrWord(DirectPageIndexedIndirectX()); CMP(operand); break; + } case 0xC3: // CMP Stack Relative + { operand = StackRelative(); CMP(operand); break; + } case 0xC5: // CMP Direct Page + { operand = DirectPage(); CMP(operand); break; + } case 0xC7: // CMP DP Indirect Long + { operand = DirectPageIndirectLong(); CMP(operand); break; + } case 0xC9: // CMP Immediate + { operand = Immediate(); immediate = true; CMP(operand, immediate); break; + } case 0xCD: // CMP Absolute - operand = Absolute(); + { + operand = Absolute(AccessType::Data); CMP(operand); break; + } case 0xCF: // CMP Absolute Long + { operand = AbsoluteLong(); CMP(operand); break; + } case 0xD1: // CMP DP Indirect Indexed, Y + { operand = DirectPageIndirectIndexedY(); CMP(operand); break; + } case 0xD2: // CMP DP Indirect + { operand = DirectPageIndirect(); CMP(operand); break; + } case 0xD3: // CMP SR Indirect Indexed, Y + { operand = StackRelativeIndirectIndexedY(); CMP(operand); break; + } case 0xD5: // CMP DP Indexed, X + { operand = DirectPageIndexedX(); CMP(operand); break; + } case 0xD7: // CMP DP Indirect Long Indexed, Y + { operand = DirectPageIndirectLongIndexedY(); CMP(operand); break; + } case 0xD9: // CMP Absolute Indexed, Y + { operand = AbsoluteIndexedY(); CMP(operand); break; + } case 0xDD: // CMP Absolute Indexed, X + { operand = AbsoluteIndexedX(); CMP(operand); break; + } case 0xDF: // CMP Absolute Long Indexed, X + { operand = AbsoluteLongIndexedX(); CMP(operand); break; + } case 0x02: // COP + { COP(); break; + } case 0xE0: // CPX Immediate + { operand = Immediate(); immediate = true; CPX(operand, immediate); break; + } case 0xE4: // CPX Direct Page + { operand = DirectPage(); CPX(operand); break; + } case 0xEC: // CPX Absolute + { operand = Absolute(); CPX(operand); break; + } case 0xC0: // CPY Immediate + { operand = Immediate(); immediate = true; CPY(operand, immediate); break; + } case 0xC4: // CPY Direct Page + { operand = DirectPage(); CPY(operand); break; + } case 0xCC: // CPY Absolute + { operand = Absolute(); CPY(operand); break; + } case 0x3A: // DEC Accumulator - DEC(A); + { + DEC(A, /*accumulator=*/true); break; + } case 0xC6: // DEC Direct Page + { operand = DirectPage(); DEC(operand); break; + } case 0xCE: // DEC Absolute + { operand = Absolute(); DEC(operand); break; + } case 0xD6: // DEC DP Indexed, X + { operand = DirectPageIndexedX(); DEC(operand); break; + } case 0xDE: // DEC Absolute Indexed, X + { operand = AbsoluteIndexedX(); DEC(operand); break; + } case 0xCA: // DEX + { DEX(); break; + } case 0x88: // DEY + { DEY(); break; + } case 0x41: // EOR DP Indexed Indirect, X + { operand = DirectPageIndexedIndirectX(); EOR(operand); break; + } case 0x43: // EOR Stack Relative + { operand = StackRelative(); EOR(operand); break; + } case 0x45: // EOR Direct Page + { operand = DirectPage(); EOR(operand); break; + } case 0x47: // EOR DP Indirect Long + { operand = DirectPageIndirectLong(); EOR(operand); break; + } case 0x49: // EOR Immediate + { operand = Immediate(); immediate = true; EOR(operand, immediate); break; + } case 0x4D: // EOR Absolute + { operand = Absolute(); EOR(operand); break; + } case 0x4F: // EOR Absolute Long + { operand = AbsoluteLong(); EOR(operand); break; + } case 0x51: // EOR DP Indirect Indexed, Y + { operand = DirectPageIndirectIndexedY(); EOR(operand); break; + } case 0x52: // EOR DP Indirect + { operand = DirectPageIndirect(); EOR(operand); break; + } case 0x53: // EOR SR Indirect Indexed, Y + { operand = StackRelativeIndirectIndexedY(); EOR(operand); break; + } case 0x55: // EOR DP Indexed, X + { operand = DirectPageIndexedX(); EOR(operand); break; + } case 0x57: // EOR DP Indirect Long Indexed, Y - operand = DirectPageIndirectLongIndexedY(); + { + operand = ReadByteOrWord(DirectPageIndirectLongIndexedY()); EOR(operand); break; + } case 0x59: // EOR Absolute Indexed, Y + { operand = AbsoluteIndexedY(); EOR(operand); break; + } case 0x5D: // EOR Absolute Indexed, X + { operand = AbsoluteIndexedX(); EOR(operand); break; + } case 0x5F: // EOR Absolute Long Indexed, X + { operand = AbsoluteLongIndexedX(); EOR(operand); break; + } case 0x1A: // INC Accumulator - INC(A); + { + INC(A, /*accumulator=*/true); break; + } case 0xE6: // INC Direct Page + { operand = DirectPage(); INC(operand); break; + } case 0xEE: // INC Absolute + { operand = Absolute(); INC(operand); break; + } case 0xF6: // INC DP Indexed, X + { operand = DirectPageIndexedX(); INC(operand); break; + } case 0xFE: // INC Absolute Indexed, X + { operand = AbsoluteIndexedX(); INC(operand); break; + } case 0xE8: // INX + { INX(); break; + } case 0xC8: // INY + { INY(); break; + } case 0x4C: // JMP Absolute + { JMP(Absolute()); break; + } case 0x5C: // JMP Absolute Long + { JML(AbsoluteLong()); break; + } case 0x6C: // JMP Absolute Indirect + { JMP(AbsoluteIndirect()); break; + } case 0x7C: // JMP Absolute Indexed Indirect + { JMP(AbsoluteIndexedIndirect()); break; + } case 0xDC: // JMP Absolute Indirect Long - JMP(AbsoluteIndirectLong()); + { + operand = AbsoluteIndirectLong(); + JMP(operand); + PB = operand >> 16; break; + } case 0x20: // JSR Absolute + { operand = Absolute(); JSR(operand); break; + } case 0x22: // JSL Absolute Long + { JSL(AbsoluteLong()); break; + } case 0xFC: // JSR Absolute Indexed Indirect + { JSR(AbsoluteIndexedIndirect()); break; + } case 0xA1: // LDA DP Indexed Indirect, X + { operand = DirectPageIndexedIndirectX(); LDA(operand); break; + } case 0xA3: // LDA Stack Relative + { operand = StackRelative(); LDA(operand); break; + } case 0xA5: // LDA Direct Page + { operand = DirectPage(); LDA(operand, false, true); break; + } case 0xA7: // LDA DP Indirect Long + { operand = DirectPageIndirectLong(); LDA(operand); break; + } case 0xA9: // LDA Immediate + { operand = Immediate(); immediate = true; LDA(operand, immediate); break; + } case 0xAD: // LDA Absolute + { operand = Absolute(); LDA(operand); break; + } case 0xAF: // LDA Absolute Long + { operand = AbsoluteLong(); LDA(operand); break; + } case 0xB1: // LDA DP Indirect Indexed, Y + { operand = DirectPageIndirectIndexedY(); LDA(operand); break; + } case 0xB2: // LDA DP Indirect + { operand = DirectPageIndirect(); LDA(operand); break; + } case 0xB3: // LDA SR Indirect Indexed, Y + { operand = StackRelativeIndirectIndexedY(); LDA(operand); break; + } case 0xB5: // LDA DP Indexed, X + { operand = DirectPageIndexedX(); LDA(operand); break; + } case 0xB7: // LDA DP Indirect Long Indexed, Y + { operand = DirectPageIndirectLongIndexedY(); LDA(operand); break; + } case 0xB9: // LDA Absolute Indexed, Y + { operand = AbsoluteIndexedY(); LDA(operand); break; + } case 0xBD: // LDA Absolute Indexed, X + { operand = AbsoluteIndexedX(); LDA(operand); break; + } case 0xBF: // LDA Absolute Long Indexed, X + { operand = AbsoluteLongIndexedX(); LDA(operand); break; + } case 0xA2: // LDX Immediate + { operand = Immediate(); immediate = true; LDX(operand, immediate); break; + } case 0xA6: // LDX Direct Page + { operand = DirectPage(); LDX(operand); break; + } case 0xAE: // LDX Absolute + { operand = Absolute(); LDX(operand); break; + } case 0xB6: // LDX DP Indexed, Y + { operand = DirectPageIndexedY(); LDX(operand); break; + } case 0xBE: // LDX Absolute Indexed, Y + { operand = AbsoluteIndexedY(); LDX(operand); break; + } case 0xA0: // LDY Immediate + { operand = Immediate(); immediate = true; LDY(operand, immediate); break; + } case 0xA4: // LDY Direct Page + { operand = DirectPage(); LDY(operand); break; + } case 0xAC: // LDY Absolute + { operand = Absolute(); LDY(operand); break; + } case 0xB4: // LDY DP Indexed, X + { operand = DirectPageIndexedX(); LDY(operand); break; + } case 0xBC: // LDY Absolute Indexed, X + { operand = AbsoluteIndexedX(); LDY(operand); break; + } case 0x46: // LSR Direct Page + { operand = DirectPage(); LSR(operand); break; + } case 0x4A: // LSR Accumulator - LSR(A); + { + LSR(A, /*accumulator=*/true); break; + } case 0x4E: // LSR Absolute + { operand = Absolute(); LSR(operand); break; + } case 0x56: // LSR DP Indexed, X + { operand = DirectPageIndexedX(); LSR(operand); break; + } case 0x5E: // LSR Absolute Indexed, X + { operand = AbsoluteIndexedX(); LSR(operand); break; + } case 0x54: // MVN(); @@ -779,7 +1120,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; case 0xC2: // REP Reset status bits - operand = memory.ReadByte(PC); + operand = FetchByte(); immediate = true; REP(); break; @@ -789,7 +1130,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) { ROL(operand); break; case 0x2A: // ROL Accumulator - ROL(A); + ROL(A, /*accumulator=*/true); break; case 0x2E: // ROL Absolute operand = Absolute(); @@ -809,7 +1150,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) { ROR(operand); break; case 0x6A: // ROR Accumulator - ROR(A); + ROR(A, /*accumulator=*/true); break; case 0x6E: // ROR Absolute operand = Absolute(); @@ -911,7 +1252,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; case 0xE2: // SEP Set status bits - operand = memory.ReadByte(PC); + operand = FetchByte(); immediate = true; SEP(); break; @@ -933,7 +1274,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) { STA(operand); break; case 0x8D: // STA Absolute - operand = Absolute(); + operand = Absolute(AccessType::Data); STA(operand); break; case 0x8F: // STA Absolute Long @@ -1104,8 +1445,8 @@ void CPU::ExecuteInstruction(uint8_t opcode) { } LogInstructions(PC, opcode, operand, immediate, accumulator_mode); - uint8_t instructionLength = GetInstructionLength(opcode); - UpdatePC(instructionLength); + instruction_length = GetInstructionLength(opcode); + UpdatePC(instruction_length); } void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand, @@ -1113,7 +1454,8 @@ void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand, if (flags()->kLogInstructions) { std::ostringstream oss; oss << "$" << std::uppercase << std::setw(2) << std::setfill('0') - << static_cast(PB) << ":" << std::hex << PC << ": 0x" << std::hex + << static_cast(PB) << ":" << std::hex << PC << ": 0x" + << std::setw(2) << std::setfill('0') << std::hex << static_cast(opcode) << " " << opcode_to_mnemonic.at(opcode) << " "; @@ -1163,827 +1505,54 @@ void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand, } } -// Interrupt Vectors -// Emulation mode, e = 1 Native mode, e = 0 -// -// 0xFFFE,FF - IRQ/BRK 0xFFEE,EF - IRQ -// 0xFFFC,FD - RESET -// 0xFFFA,FB - NMI 0xFFEA,EB - NMI -// 0xFFF8,F9 - ABORT 0xFFE8,E9 - ABORT -// 0xFFE6,E7 - BRK -// 0xFFF4,F5 - COP 0xFFE4,E5 - COP -void CPU::HandleInterrupts() {} - -/** - * 65816 Instruction Set - * - * TODO: STP, WDM - */ - -// ADC: Add with carry -void CPU::ADC(uint8_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); - } -} - -// AND: Logical AND -void CPU::AND(uint16_t value, bool isImmediate) { - uint16_t operand; - if (E == 0) { // 16-bit mode - operand = isImmediate ? value : memory.ReadWord(value); - A &= operand; - SetZeroFlag(A == 0); - SetNegativeFlag(A & 0x8000); - } else { // 8-bit mode - operand = isImmediate ? value : memory.ReadByte(value); - A &= operand; - SetZeroFlag(A == 0); - SetNegativeFlag(A & 0x80); - } -} - -// 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); -} - -// ASL: Arithmetic shift left -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); -} - -// BCC: Branch if carry clear -void CPU::BCC(int8_t offset) { - if (!GetCarryFlag()) { // If the carry flag is clear - next_pc_ = offset; - } -} - -// BCS: Branch if carry set -void CPU::BCS(int8_t offset) { - if (GetCarryFlag()) { // If the carry flag is set - next_pc_ = offset; - } -} - -// BEQ: Branch if equal (zero set) -void CPU::BEQ(int8_t offset) { - if (GetZeroFlag()) { // If the zero flag is set - next_pc_ = offset; - } -} - -// BIT: Bit test -void CPU::BIT(uint16_t address) { - uint8_t value = memory.ReadByte(address); - SetNegativeFlag(value & 0x80); - SetOverflowFlag(value & 0x40); - SetZeroFlag((A & value) == 0); -} - -// BMI: Branch if minus (negative set) -void CPU::BMI(int8_t offset) { - if (GetNegativeFlag()) { // If the negative flag is set - next_pc_ = offset; - } -} - -// BNE: Branch if not equal (zero clear) -void CPU::BNE(int8_t offset) { - if (!GetZeroFlag()) { // If the zero flag is clear - // PC += offset; - next_pc_ = offset; - } -} - -// BPL: Branch if plus (negative clear) -void CPU::BPL(int8_t offset) { - if (!GetNegativeFlag()) { // If the negative flag is clear - next_pc_ = offset; - } -} - -// BRA: Branch always -void CPU::BRA(int8_t offset) { next_pc_ = offset; } - -// BRK: Break -void CPU::BRK() { - PC += 2; // Increment the program counter by 2 - memory.PushWord(PC); - memory.PushByte(status); - SetInterruptFlag(true); - try { - PC = memory.ReadWord(0xFFFE); - } catch (const std::exception& e) { - std::cout << "BRK: " << e.what() << std::endl; - } -} - -// BRL: Branch always long -void CPU::BRL(int16_t offset) { next_pc_ = offset; } - -// BVC: Branch if overflow clear -void CPU::BVC(int8_t offset) { - if (!GetOverflowFlag()) { // If the overflow flag is clear - next_pc_ = offset; - } -} - -// BVS: Branch if overflow set -void CPU::BVS(int8_t offset) { - if (GetOverflowFlag()) { // If the overflow flag is set - next_pc_ = offset; - } -} - -// CLC: Clear carry flag -void CPU::CLC() { status &= ~0x01; } - -// CLD: Clear decimal mode -void CPU::CLD() { status &= ~0x08; } - -// CLI: Clear interrupt disable flag -void CPU::CLI() { status &= ~0x04; } - -// CLV: Clear overflow flag -void CPU::CLV() { status &= ~0x40; } - -// CMP: Compare TESTME -// 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(uint16_t value, bool isImmediate) { - if (GetAccumulatorSize()) { // 8-bit - uint8_t result = isImmediate ? A - value : A - memory.ReadByte(value); - SetZeroFlag(result == 0); - SetNegativeFlag(result & 0x80); - SetCarryFlag(A >= value); - } else { // 16-bit - uint16_t result = isImmediate ? A - value : A - memory.ReadWord(value); - SetZeroFlag(result == 0); - SetNegativeFlag(result & 0x8000); - SetCarryFlag(A >= value); - } -} - -// COP: Coprocessor TESTME -void CPU::COP() { - PC += 2; // Increment the program counter by 2 - memory.PushWord(PC); - memory.PushByte(status); - SetInterruptFlag(true); - if (E) { - PC = memory.ReadWord(0xFFF4); - } else { - PC = memory.ReadWord(0xFFE4); - } - SetDecimalFlag(false); -} - -// CPX: Compare X register -void CPU::CPX(uint16_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); - } -} - -// CPY: Compare Y register -void CPU::CPY(uint16_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); - } -} - -// DEC: Decrement TESTME -void CPU::DEC(uint16_t address) { - 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); - } -} - -// DEX: Decrement X register -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); - } -} - -// DEY: Decrement Y register -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); - } -} - -// EOR: Exclusive OR TESTMEs -void CPU::EOR(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); - } -} - -// INC: Increment -void CPU::INC(uint16_t address) { - 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); - } -} - -// INX: Increment X register -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); - } -} - -// INY: Increment Y register -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); - } -} - -// JMP: Jump -void CPU::JMP(uint16_t address) { - next_pc_ = address; // Set program counter to the new address -} - -// JML: Jump long -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); -} - -// JSR: Jump to subroutine -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 -} - -// JSL: Jump to subroutine long -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 -} - -// LDA: Load accumulator -void CPU::LDA(uint16_t address, bool isImmediate, bool direct_page) { - 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); - } -} - -// LDX: Load X register -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); - } -} - -// LDY: Load Y register -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); - } -} - -// LSR: Logical shift right -void CPU::LSR(uint16_t address) { - uint8_t value = memory.ReadByte(address); - SetCarryFlag(value & 0x01); - value >>= 1; - memory.WriteByte(address, value); - SetNegativeFlag(false); - SetZeroFlag(value == 0); -} - -// MVN: Block Move Next -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++; - } -} - -// MVP: Block Move Previous -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--; - } -} - -// NOP: No operation -void CPU::NOP() { - // Do nothing -} - -// ORA: Logical OR -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); - } -} - -// PEA: Push effective address -void CPU::PEA() { - uint16_t address = FetchWord(); - memory.PushWord(address); -} - -// PEI: Push effective indirect address -void CPU::PEI() { - uint16_t address = FetchWord(); - memory.PushWord(memory.ReadWord(address)); -} - -// PER: Push effective PC-relative address -void CPU::PER() { - uint16_t address = FetchWord(); - memory.PushWord(PC + address); -} - -// PHA: Push Accumulator on Stack -void CPU::PHA() { memory.PushByte(A); } - -// PHB: Push Data Bank Register on Stack -void CPU::PHB() { memory.PushByte(DB); } - -// PHD: Push Program Bank Register on Stack -void CPU::PHD() { memory.PushWord(D); } - -// PHK: Push Program Bank Register on Stack -void CPU::PHK() { memory.PushByte(PB); } - -// PHP: Push Processor Status Register on Stack -void CPU::PHP() { memory.PushByte(status); } - -// PHX: Push X Index Register on Stack -void CPU::PHX() { memory.PushByte(X); } - -// PHY: Push Y Index Register on Stack -void CPU::PHY() { memory.PushByte(Y); } - -// PLA: Pull Accumulator from Stack -void CPU::PLA() { - A = memory.PopByte(); - SetNegativeFlag((A & 0x80) != 0); - SetZeroFlag(A == 0); -} - -// PLB: Pull data bank register -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(); } - -// PLX: Pull X Index Register from Stack -void CPU::PLX() { - X = memory.PopByte(); - SetNegativeFlag((A & 0x80) != 0); - SetZeroFlag(X == 0); -} - -// PHY: Pull Y Index Register from Stack -void CPU::PLY() { - Y = memory.PopByte(); - SetNegativeFlag((A & 0x80) != 0); - SetZeroFlag(Y == 0); -} - -// REP: Reset status bits -void CPU::REP() { - auto byte = FetchByte(); - status &= ~byte; -} - -// ROL: Rotate left -void CPU::ROL(uint16_t address) { - 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); -} - -// ROR: Rotate right -void CPU::ROR(uint16_t address) { - 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); -} - -// RTI: Return from interrupt -void CPU::RTI() { - status = memory.PopByte(); - PC = memory.PopWord(); -} - -// RTL: Return from subroutine long -void CPU::RTL() { - PC = memory.PopWord(); - PB = memory.PopByte(); -} - -// RTS: Return from subroutine -void CPU::RTS() { last_call_frame_ = memory.PopWord(); } - -void CPU::SBC(uint16_t value, bool isImmediate) { - uint16_t operand; - if (!GetAccumulatorSize()) { // 16-bit mode - operand = isImmediate ? value : memory.ReadWord(value); - uint32_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); - uint16_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); - } -} - -// SEC: Set carry flag -void CPU::SEC() { status |= 0x01; } - -// SED: Set decimal mode -void CPU::SED() { status |= 0x08; } - -// SEI: Set interrupt disable flag -void CPU::SEI() { status |= 0x04; } - -// SEP: Set status bits -void CPU::SEP() { - auto byte = FetchByte(); - status |= byte; -} - -// STA: Store accumulator -void CPU::STA(uint16_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 -// STP: Stop the clock -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). -} - -// STX: Store X register -void CPU::STX(uint16_t address) { - if (GetIndexSize()) { - memory.WriteByte(address, static_cast(X)); - } else { - memory.WriteWord(address, X); - } -} - -// STY: Store Y register -void CPU::STY(uint16_t address) { - if (GetIndexSize()) { - memory.WriteByte(address, static_cast(Y)); - } else { - memory.WriteWord(address, Y); - } -} - -// STZ: Store zero -void CPU::STZ(uint16_t address) { - if (GetAccumulatorSize()) { - memory.WriteByte(address, 0x00); - } else { - memory.WriteWord(address, 0x0000); - } -} - -// TAX: Transfer accumulator to X -void CPU::TAX() { - X = A; - SetZeroFlag(X == 0); - SetNegativeFlag(X & 0x80); -} - -// TAY: Transfer accumulator to Y -void CPU::TAY() { - Y = A; - SetZeroFlag(Y == 0); - SetNegativeFlag(Y & 0x80); -} - -// TCD: Transfer accumulator to direct page register -void CPU::TCD() { - D = A; - SetZeroFlag(D == 0); - SetNegativeFlag(D & 0x80); -} - -// TCS: Transfer accumulator to stack pointer -void CPU::TCS() { memory.SetSP(A); } - -// TDC: Transfer direct page register to accumulator -void CPU::TDC() { - A = D; - SetZeroFlag(A == 0); - SetNegativeFlag(A & 0x80); -} - -// TRB: Test and reset bits -void CPU::TRB(uint16_t address) { - uint8_t value = memory.ReadByte(address); - SetZeroFlag((A & value) == 0); - value &= ~A; - memory.WriteByte(address, value); -} - -// TSB: Test and set bits -void CPU::TSB(uint16_t address) { - uint8_t value = memory.ReadByte(address); - SetZeroFlag((A & value) == 0); - value |= A; - memory.WriteByte(address, value); -} - -// TSC: Transfer stack pointer to accumulator -void CPU::TSC() { - A = SP(); - SetZeroFlag(A == 0); - SetNegativeFlag(A & 0x80); -} - -// TSX: Transfer stack pointer to X -void CPU::TSX() { - X = SP(); - SetZeroFlag(X == 0); - SetNegativeFlag(X & 0x80); -} - -// TXA: Transfer X to accumulator -void CPU::TXA() { - A = X; - SetZeroFlag(A == 0); - SetNegativeFlag(A & 0x80); -} - -// TXS: Transfer X to stack pointer -void CPU::TXS() { memory.SetSP(X); } - -// TXY: Transfer X to Y -void CPU::TXY() { - X = Y; - SetZeroFlag(X == 0); - SetNegativeFlag(X & 0x80); -} - -// TYA: Transfer Y to accumulator -void CPU::TYA() { - A = Y; - SetZeroFlag(A == 0); - SetNegativeFlag(A & 0x80); -} - -// TYX: Transfer Y to X -void CPU::TYX() { - Y = X; - SetZeroFlag(Y == 0); - SetNegativeFlag(Y & 0x80); -} - -// TODO: Make this communicate with the SNES class -// WAI: Wait for interrupt TESTME -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 -} - -// XBA: Exchange B and A accumulator -void CPU::XBA() { - uint8_t lowByte = A & 0xFF; - uint8_t highByte = (A >> 8) & 0xFF; - A = (lowByte << 8) | highByte; -} - -// XCE: Exchange Carry and Emulation Flags -void CPU::XCE() { - uint8_t carry = status & 0x01; - status &= ~0x01; - status |= E; - E = carry; -} - uint8_t CPU::GetInstructionLength(uint8_t opcode) { switch (opcode) { case 0x00: // BRK + case 0x02: // COP + PC = next_pc_; return 0; - case 0x60: // RTS - PC = last_call_frame_; - return 3; - // TODO: Handle JMPs in logging. case 0x20: // JSR Absolute case 0x4C: // JMP Absolute case 0x6C: // JMP Absolute Indirect case 0x5C: // JMP Absolute Indexed Indirect case 0x22: // JSL Absolute Long + case 0x7C: // JMP Absolute Indexed Indirect + case 0xFC: // JSR Absolute Indexed Indirect + case 0xDC: // JMP Absolute Indirect Long + PC = next_pc_; + return 0; + + case 0x6B: // RTL PC = next_pc_; return 0; // Branch Instructions (BCC, BCS, BNE, BEQ, etc.) case 0x90: // BCC near if (!GetCarryFlag()) { - PC += next_pc_; + PC = next_pc_; return 0; } else { return 2; } case 0xB0: // BCS near if (GetCarryFlag()) { - PC += next_pc_; + PC = next_pc_; return 0; } else { return 2; } case 0x30: // BMI near if (GetNegativeFlag()) { - PC += next_pc_; + PC = next_pc_; return 0; } else { return 2; } case 0xF0: // BEQ near if (GetZeroFlag()) { - PC += next_pc_; + PC = next_pc_; return 0; } else { return 2; @@ -1991,7 +1560,7 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) { case 0xD0: // BNE Relative if (!GetZeroFlag()) { - PC += next_pc_; + PC = next_pc_; return 0; } else { return 2; @@ -1999,7 +1568,7 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) { case 0x10: // BPL Relative if (!GetNegativeFlag()) { - PC += next_pc_; + PC = next_pc_; return 0; } else { return 2; @@ -2007,7 +1576,7 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) { case 0x50: // BVC Relative if (!GetOverflowFlag()) { - PC += next_pc_; + PC = next_pc_; return 0; } else { return 2; @@ -2015,21 +1584,24 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) { case 0x70: // BVS Relative if (GetOverflowFlag()) { - PC += next_pc_; + PC = next_pc_; return 0; } else { return 2; } case 0x80: // BRA Relative - PC += next_pc_; + PC = next_pc_; return 0; case 0x82: // BRL Relative Long - PC += next_pc_; + PC = next_pc_; + return 0; + + case 0x60: // RTS + PC = last_call_frame_; return 0; - // Single Byte Instructions case 0x18: // CLC case 0xD8: // CLD case 0x58: // CLI @@ -2040,6 +1612,7 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) { case 0xC8: // INY case 0xEA: // NOP case 0x48: // PHA + case 0x8B: // PHB case 0x0B: // PHD case 0x4B: // PHK case 0x08: // PHP @@ -2051,11 +1624,10 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) { case 0x28: // PLP case 0xFA: // PLX case 0x7A: // PLY - case 0xC2: // REP case 0x40: // RTI case 0x38: // SEC case 0xF8: // SED - case 0xE2: // SEP + case 0xBB: // TYX case 0x78: // SEI case 0xAA: // TAX case 0xA8: // TAY @@ -2064,40 +1636,20 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) { case 0x9A: // TXS case 0x98: // TYA case 0x0A: // ASL Accumulator + case 0x2A: // ROL Accumulator + case 0xFB: // XCE + case 0x5B: // TCD + case 0x1B: // TCS + case 0x3A: // DEC Accumulator + case 0x1A: // INC Accumulator + case 0x7B: // TDC + case 0x3B: // TSC + case 0xEB: // XBA return 1; - // Two Byte Instructions - case 0x69: // ADC Immediate - case 0x29: // AND Immediate - case 0xC9: // CMP Immediate - case 0xE0: // CPX Immediate - case 0xC0: // CPY Immediate - case 0x49: // EOR Immediate - case 0xA9: // LDA Immediate - case 0xA2: // LDX Immediate - case 0xA0: // LDY Immediate - case 0x09: // ORA Immediate - case 0xE9: // SBC Immediate - case 0x65: // ADC Direct Page - case 0x72: // ADC Direct Page Indirect - case 0x67: // ADC Direct Page Indirect Long - case 0x75: // ADC Direct Page Indexed, X - case 0x61: // ADC Direct Page Indirect, X - case 0x71: // ADC DP Indirect Indexed, Y - case 0x77: // ADC DP Indirect Long Indexed, Y - case 0x63: // ADC Stack Relative - case 0x73: // ADC SR Indirect Indexed, Y - return GetAccumulatorSize() ? 2 : 3; - - case 0x7D: // ADC Absolute Indexed, X - case 0x79: // ADC Absolute Indexed, Y - case 0x6D: // ADC Absolute - return 3; - - case 0x7F: // ADC Absolute Long Indexed, X - case 0x6F: // ADC Absolute Long - return 4; - + case 0xC2: // REP + case 0xE2: // SEP + case 0x45: // EOR Direct Page case 0xA5: // LDA Direct Page case 0x05: // ORA Direct Page case 0x85: // STA Direct Page @@ -2113,18 +1665,88 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) { case 0x23: // AND Direct Page Indirect Indexed X case 0x33: // AND Direct Page Indirect Long Indexed Y case 0xE6: // INC Direct Page - return 2; - - // ASL (Arithmetic Shift Left) + case 0x81: // STA Direct Page Indirect, X + case 0x01: // ORA Direct Page Indirect, X + case 0x19: // ORA Direct Page Indirect Indexed, Y + case 0x1D: // ORA Absolute Indexed, X + case 0x89: // BIT Immediate + case 0x91: // STA Direct Page Indirect Indexed, Y + case 0x65: // ADC Direct Page + case 0x72: // ADC Direct Page Indirect + case 0x67: // ADC Direct Page Indirect Long + case 0x75: // ADC Direct Page Indexed, X + case 0x61: // ADC Direct Page Indirect, X + case 0x71: // ADC DP Indirect Indexed, Y + case 0x77: // ADC DP Indirect Long Indexed, Y + case 0x63: // ADC Stack Relative + case 0x73: // ADC SR Indirect Indexed, Y case 0x06: // ASL Direct Page case 0x16: // ASL Direct Page Indexed, X + case 0xB2: // LDA Direct Page Indirect + case 0x57: // EOR Direct Page Indirect Long Indexed, Y + case 0xC1: // CMP Direct Page Indexed Indirect, X + case 0xC3: // CMP Stack Relative + case 0xC5: // CMP Direct Page + case 0x47: // EOR Direct Page Indirect Long + case 0x55: // EOR Direct Page Indexed, X + case 0x41: // EOR Direct Page Indirect, X + case 0x51: // EOR Direct Page Indirect Indexed, Y + case 0x43: // EOR Direct Page Indirect Indexed, X + case 0x53: // EOR Direct Page Indirect Long Indexed, Y + case 0xA1: // LDA Direct Page Indexed Indirect, X + case 0xA3: // LDA Stack Relative + case 0xA7: // LDA Direct Page Indirect Long + case 0xB5: // LDA Direct Page Indexed, X + case 0xB1: // LDA Direct Page Indirect Indexed, Y + case 0xB7: // LDA Direct Page Indirect Long Indexed, Y + case 0xB3: // LDA Direct Page Indirect Indexed, X + case 0xB6: // LDX Direct Page Indexed, Y + case 0xB4: // LDY Direct Page Indexed, X + case 0x46: // LSR Direct Page + case 0x56: // LSR Direct Page Indexed, X + case 0xE1: // SBC Direct Page Indexed Indirect, X + case 0xE3: // SBC Stack Relative + case 0xE5: // SBC Direct Page + case 0xE7: // SBC Direct Page Indirect Long + case 0xF2: // SBC Direct Page Indirect + case 0xF1: // SBC Direct Page Indirect Indexed, Y + case 0xF3: // SBC SR Indirect Indexed, Y + case 0xF5: // SBC Direct Page Indexed, X + case 0xF7: // SBC Direct Page Indirect Long Indexed, Y + case 0xF6: // INC Direct Page Indexed, X + case 0x86: // STX Direct Page + case 0x84: // STY Direct Page + case 0x64: // STZ Direct Page + case 0x74: // STZ Direct Page Indexed, X + case 0x04: // TSB Direct Page + case 0x14: // TRB Direct Page + case 0x44: // MVN + case 0x54: // MVP + case 0x24: // BIT Direct Page + case 0x34: // BIT Direct Page Indexed, X + case 0x94: // STY Direct Page Indexed, X + case 0x87: // STA Direct Page Indirect Long + case 0x92: // STA Direct Page Indirect + case 0x93: // STA SR Indirect Indexed, Y + case 0x95: // STA Direct Page Indexed, X + case 0x96: // STX Direct Page Indexed, Y return 2; + case 0x69: // ADC Immediate + case 0x29: // AND Immediate + case 0xC9: // CMP Immediate + case 0xE0: // CPX Immediate + case 0xC0: // CPY Immediate + case 0x49: // EOR Immediate + case 0xA9: // LDA Immediate + case 0xA2: // LDX Immediate + case 0xA0: // LDY Immediate + case 0x09: // ORA Immediate + case 0xE9: // SBC Immediate + return GetAccumulatorSize() ? 2 : 3; + case 0x0E: // ASL Absolute case 0x1E: // ASL Absolute Indexed, X - return 3; - - // Three Byte Instructions case 0x2D: // AND Absolute case 0xCD: // CMP Absolute case 0xEC: // CPX Absolute @@ -2142,9 +1764,16 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) { case 0xBC: // LDY Absolute Indexed X case 0x3D: // AND Absolute Indexed X case 0x39: // AND Absolute Indexed Y + case 0x9C: // STZ Absolute Indexed X + case 0x9D: // STA Absolute Indexed X + case 0x7D: // ADC Absolute Indexed, X + case 0x79: // ADC Absolute Indexed, Y + case 0x6D: // ADC Absolute + case 0x5D: // EOR Absolute Indexed, X + case 0x59: // EOR Absolute Indexed, Y + case 0x83: // STA Stack Relative Indirect Indexed, Y return 3; - // Four Byte Instructions case 0x2F: // AND Absolute Long case 0xCF: // CMP Absolute Long case 0x4F: // EOR Absolute Long @@ -2152,31 +1781,12 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) { case 0x0F: // ORA Absolute Long case 0xEF: // SBC Absolute Long case 0x8F: // STA Absolute Long + case 0x5F: // EOR Absolute Long Indexed, X case 0x3F: // AND Absolute Long Indexed X + case 0x7F: // ADC Absolute Long Indexed, X + case 0x6F: // ADC Absolute Long return 4; - // BIT (Bit Test) - // Include all BIT variants similar to ADC - - // BRK (Break) and COP (Co-Processor Enable) - - case 0x02: // COP param - return 2; - - // CMP (Compare Accumulator) - // Include all CMP variants similar to ADC - - // CPX (Compare X Register) and CPY (Compare Y Register) - // Include CPX and CPY variants - - // DEC (Decrement Memory) - // Include DEC variants similar to ASL - - // EOR (Exclusive OR with Accumulator) - // Include all EOR variants similar to ADC - - // Variable length or Special cases - // Add cases for instructions with variable lengths or special handling default: auto mnemonic = opcode_to_mnemonic.at(opcode); std::cerr << "Unknown instruction length: " << std::hex @@ -2185,6 +1795,70 @@ uint8_t CPU::GetInstructionLength(uint8_t opcode) { } } +void CPU::HandleInterrupts() { + if (GetInterruptFlag()) { + return; + } + + /** + if (GetIRQFlag()) { + if (GetEmulationFlag()) { + PushWord(PC); + PushByte(status); + SetInterruptFlag(true); + SetDecimalFlag(false); + SetIRQFlag(false); + SetEmulationFlag(true); + try { + PC = memory.ReadWord(0xFFFE); + } catch (const std::exception& e) { + std::cout << "IRQ: " << e.what() << std::endl; + } + } else { + PushWord(PC); + PushByte(status); + SetInterruptFlag(true); + SetDecimalFlag(false); + SetIRQFlag(false); + SetEmulationFlag(false); + try { + PC = memory.ReadWord(0xFFFE); + } catch (const std::exception& e) { + std::cout << "IRQ: " << e.what() << std::endl; + } + } + } + + if (GetNMIFlag()) { + if (GetEmulationFlag()) { + PushWord(PC); + PushByte(status); + SetInterruptFlag(true); + SetDecimalFlag(false); + SetNMIFlag(false); + SetEmulationFlag(true); + try { + PC = memory.ReadWord(0xFFFA); + } catch (const std::exception& e) { + std::cout << "NMI: " << e.what() << std::endl; + } + } else { + PushWord(PC); + PushByte(status); + SetInterruptFlag(true); + SetDecimalFlag(false); + SetNMIFlag(false); + SetEmulationFlag(false); + try { + PC = memory.ReadWord(0xFFFA); + } catch (const std::exception& e) { + std::cout << "NMI: " << e.what() << std::endl; + } + } + } + */ +} + } // namespace emu } // namespace app } // namespace yaze \ No newline at end of file diff --git a/src/app/emu/cpu.h b/src/app/emu/cpu.h index fcdf46cc..1ce632da 100644 --- a/src/app/emu/cpu.h +++ b/src/app/emu/cpu.h @@ -50,31 +50,46 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { void ExecuteInstruction(uint8_t opcode); void LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand, bool immediate, bool accumulator_mode); - void HandleInterrupts(); + + void UpdatePC(uint8_t instruction_length) { PC += instruction_length; } + + uint8_t GetInstructionLength(uint8_t opcode); + uint16_t SP() const override { return memory.SP(); } + void SetSP(uint16_t value) override { memory.SetSP(value); } + void set_next_pc(uint16_t value) { next_pc_ = value; } + void UpdateClock(int delta_time) { clock.UpdateClock(delta_time); } bool IsBreakpoint(uint32_t address) { return std::find(breakpoints_.begin(), breakpoints_.end(), address) != breakpoints_.end(); } - void SetBreakpoint(uint32_t address) { breakpoints_.push_back(address); } - void ClearBreakpoint(uint32_t address) { breakpoints_.erase( std::remove(breakpoints_.begin(), breakpoints_.end(), address), breakpoints_.end()); } - void ClearBreakpoints() { breakpoints_.clear(); breakpoints_.shrink_to_fit(); } - auto GetBreakpoints() { return breakpoints_; } std::vector breakpoints_; std::vector instruction_log_; + // ====================================================== + // Interrupt Vectors + // Emulation mode, e = 1 Native mode, e = 0 + // + // 0xFFFE,FF - IRQ/BRK 0xFFEE,EF - IRQ + // 0xFFFC,FD - RESET + // 0xFFFA,FB - NMI 0xFFEA,EB - NMI + // 0xFFF8,F9 - ABORT 0xFFE8,E9 - ABORT + // 0xFFE6,E7 - BRK + // 0xFFF4,F5 - COP 0xFFE4,E5 - COP + void HandleInterrupts(); + // ====================================================== // Registers @@ -101,8 +116,17 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // B #$10 00010000 Break (emulation mode only) // Setting flags in the status register + bool m() { return GetAccumulatorSize() ? 1 : 0; } int GetAccumulatorSize() const { return status & 0x20; } int GetIndexSize() const { return status & 0x10; } + void set_16_bit_mode() { + SetAccumulatorSize(true); + SetIndexSize(true); + } + void set_8_bit_mode() { + SetAccumulatorSize(false); + SetIndexSize(false); + } void SetAccumulatorSize(bool set) { SetFlag(0x20, set); } void SetIndexSize(bool set) { SetFlag(0x10, set); } @@ -124,6 +148,8 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { bool GetZeroFlag() const { return GetFlag(0x02); } bool GetCarryFlag() const { return GetFlag(0x01); } + enum class AccessType { Control, Data }; + // ========================================================================== // Addressing Modes @@ -134,7 +160,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // Low: First operand byte // // LDA addr - uint16_t Absolute() { return ReadWord((PB << 16) | PC + 1); } + uint16_t Absolute(AccessType access_type = AccessType::Data); // Effective Address: // The Data Bank Register is concatened with the 16-bit operand @@ -142,7 +168,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // based on the emulation mode (16:X=0, 8:X=1) // // LDA addr, X - uint16_t AbsoluteIndexedX() { return FetchWord() + X; } + uint32_t AbsoluteIndexedX(); // Effective Address: // The Data Bank Register is concatened with the 16-bit operand @@ -150,7 +176,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // based on the emulation mode (16:Y=0, 8:Y=1) // // LDA addr, Y - uint16_t AbsoluteIndexedY() { return FetchWord() + Y; } + uint32_t AbsoluteIndexedY(); // Test Me :) // Effective Address: @@ -160,10 +186,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // the operand double byte and X based on the // emulation mode // JMP (addr, X) - uint16_t AbsoluteIndexedIndirect() { - uint16_t address = FetchWord() + X; - return memory.ReadWord((PB << 16) | address & 0xFFFF); - } + uint16_t AbsoluteIndexedIndirect(); // Effective Address: // Bank: Program Bank Register (PBR) @@ -171,20 +194,14 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // Indirect Address: Located in Bank Zero, at the operand double byte // // JMP (addr) - uint16_t AbsoluteIndirect() { - uint16_t address = FetchWord(); - return memory.ReadWord((PB << 16) | address); - } + uint16_t AbsoluteIndirect(); // Effective Address: // Bank/High/Low: The 24-bit Indirect Address // Indirect Address: Located in Bank Zero, at the operand double byte // // JMP [addr] - uint32_t AbsoluteIndirectLong() { - uint16_t address = FetchWord(); - return memory.ReadWordLong((PB << 16) | address); - } + uint32_t AbsoluteIndirectLong(); // Effective Address: // Bank: Third operand byte @@ -192,13 +209,13 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // Low: First operand byte // // LDA long - uint32_t AbsoluteLong() { return FetchLong(); } + uint32_t AbsoluteLong(); // Effective Address: // The 24-bit operand is added to X based on the emulation mode // // LDA long, X - uint16_t AbsoluteLongIndexedX() { return FetchLong() + X; } + uint32_t AbsoluteLongIndexedX(); // Source Effective Address: // Bank: Second operand byte @@ -212,21 +229,14 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // The number of bytes to be moved: 16-bit value in Acculumator C plus 1. // // MVN src, dst - void BlockMove(uint16_t source, uint16_t dest, uint16_t length) { - for (int i = 0; i < length; i++) { - memory.WriteByte(dest + i, memory.ReadByte(source + i)); - } - } + void BlockMove(uint16_t source, uint16_t dest, uint16_t length); // Effective Address: // Bank: Zero // High/low: Direct Page Register plus operand byte // // LDA dp - uint16_t DirectPage() { - uint8_t dp = memory.ReadByte((PB << 16) | PC + 1); - return D + dp; - } + uint16_t DirectPage(); // Effective Address: // Bank: Zero @@ -234,20 +244,14 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // based on the emulation mode // // LDA dp, X - uint16_t DirectPageIndexedX() { - uint8_t operand = FetchByte(); - return D + operand + X; - } + uint16_t DirectPageIndexedX(); // Effective Address: // Bank: Zero // High/low: Direct Page Register plus operand byte plus Y // based on the emulation mode // LDA dp, Y - uint16_t DirectPageIndexedY() { - uint8_t dp = FetchByte(); - return (dp + Y) & 0xFF; - } + uint16_t DirectPageIndexedY(); // Effective Address: // Bank: Data bank register @@ -256,12 +260,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // register, the operand byte, and X based on the emulation mode in bank zero. // // LDA (dp, X) - uint16_t DirectPageIndexedIndirectX() { - uint8_t dp = FetchByte(); - uint16_t effective_address = D + dp + X; - uint16_t indirect_address = memory.ReadWord(effective_address & 0xFFFF); - return indirect_address; - } + uint16_t DirectPageIndexedIndirectX(); // Effective Address: // Bank: Data bank register @@ -270,12 +269,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // zero. // // LDA (dp) - uint16_t DirectPageIndirect() { - uint8_t dp = FetchByte(); - // Add the Direct Page register to the fetched operand - uint16_t effective_address = D + dp; - return memory.ReadWord(effective_address); - } + uint16_t DirectPageIndirect(); // Effective Address: // Bank/High/Low: The 24-bit indirect address @@ -283,11 +277,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // register in bank zero. // // LDA [dp] - uint32_t DirectPageIndirectLong() { - uint8_t dp = FetchByte(); - uint16_t effective_address = D + dp; - return memory.ReadWordLong(effective_address); - } + uint32_t DirectPageIndirectLong(); // Effective Address: // Found by concatenating the data bank to the double-byte @@ -297,11 +287,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // register and the operand byte, in bank zero. // // LDA (dp), Y - uint16_t DirectPageIndirectIndexedY() { - uint8_t dp = FetchByte(); - uint16_t effective_address = D + dp; - return memory.ReadWord(effective_address) + Y; - } + uint16_t DirectPageIndirectIndexedY(); // Effective Address: // Found by adding to the triple-byte indirect address Y based on the @@ -312,11 +298,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // the operand byte in bank zero. // // LDA (dp), Y - uint16_t DirectPageIndirectLongIndexedY() { - uint8_t dp = FetchByte(); - uint16_t effective_address = D + dp + Y; - return memory.ReadWordLong(effective_address); - } + uint32_t DirectPageIndirectLongIndexedY(); // 8-bit data: Data Operand Byte // 16-bit data 65816 native mode m or x = 0 @@ -324,23 +306,19 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { // Data Low: First Operand Byte // // LDA #const - uint16_t Immediate() { - if (GetAccumulatorSize()) { - return memory.ReadByte((PB << 16) | PC + 1); - } else { - return memory.ReadWord((PB << 16) | PC + 1); - } - } + uint16_t Immediate(); - uint16_t StackRelative() { - uint8_t sr = FetchByte(); - return SP() + sr; - } + uint16_t StackRelative(); - uint16_t StackRelativeIndirectIndexedY() { - uint8_t sr = FetchByte(); - return memory.ReadWord(SP() + sr + Y); - } + // Effective Address: + // The Data Bank Register is concatenated to the Indirect Address; + // the 24-bit result is added to Y (16 bits if x = 0; else 8 bits) + // Indirect Address: + // Located at the 16-bit sum of the 8-bit operand and the 16-bit stack + // pointer + // + // LDA (sr, S), Y + uint32_t StackRelativeIndirectIndexedY(); // Memory access routines uint8_t ReadByte(uint32_t address) const override { @@ -400,12 +378,11 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { uint8_t fetchedByte = memory.ReadByte(effectiveAddress); next_pc_ = PC + 1; - // PC++; // Increment the Program Counter return fetchedByte; } - uint16_t ReadByBitMode(uint32_t address) { + uint16_t ReadByteOrWord(uint32_t address) { if (GetAccumulatorSize()) { // 8-bit mode return memory.ReadByte(address) & 0xFF; @@ -415,27 +392,15 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { } } - void UpdatePC(uint8_t instruction_length) { PC += instruction_length; } - - uint8_t GetInstructionLength(uint8_t opcode); - void SetMemory(const std::vector& data) override { - memory.SetMemory(data); - } - - int16_t SP() const override { return memory.SP(); } - void SetSP(int16_t value) override { memory.SetSP(value); } - void set_next_pc(uint16_t value) { next_pc_ = value; } - void UpdateClock(int delta_time) { clock.UpdateClock(delta_time); } - // ====================================================== // Instructions // ADC: Add with carry - void ADC(uint8_t operand); - void ANDAbsoluteLong(uint32_t address); + void ADC(uint16_t operand); // AND: Logical AND - void AND(uint16_t address, bool isImmediate = false); + void AND(uint32_t address, bool immediate = false); + void ANDAbsoluteLong(uint32_t address); // ASL: Arithmetic shift left void ASL(uint16_t address); @@ -489,19 +454,19 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { void CLV(); // CMP: Compare - void CMP(uint16_t address, bool isImmediate = false); + void CMP(uint32_t address, bool immediate = false); // COP: Coprocessor enable void COP(); // CPX: Compare X register - void CPX(uint16_t address, bool isImmediate = false); + void CPX(uint16_t address, bool immediate = false); // CPY: Compare Y register - void CPY(uint16_t address, bool isImmediate = false); + void CPY(uint16_t address, bool immediate = false); // DEC: Decrement memory - void DEC(uint16_t address); + void DEC(uint32_t address, bool accumulator = false); // DEX: Decrement X register void DEX(); @@ -510,10 +475,10 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { void DEY(); // EOR: Exclusive OR - void EOR(uint16_t address, bool isImmediate = false); + void EOR(uint32_t address, bool immediate = false); // INC: Increment memory - void INC(uint16_t address); + void INC(uint32_t address, bool accumulator = false); // INX: Increment X register void INX(); @@ -534,17 +499,16 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { void JSL(uint32_t address); // LDA: Load accumulator - void LDA(uint16_t address, bool isImmediate = false, - bool direct_page = false); + void LDA(uint16_t address, bool immediate = false, bool direct_page = false); // LDX: Load X register - void LDX(uint16_t address, bool isImmediate = false); + void LDX(uint16_t address, bool immediate = false); // LDY: Load Y register - void LDY(uint16_t address, bool isImmediate = false); + void LDY(uint16_t address, bool immediate = false); // LSR: Logical shift right - void LSR(uint16_t address); + void LSR(uint16_t address, bool accumulator = false); // MVN: Block move next void MVN(uint16_t source, uint16_t dest, uint16_t length); @@ -556,7 +520,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { void NOP(); // ORA: Logical inclusive OR - void ORA(uint16_t address, bool isImmediate = false); + void ORA(uint16_t address, bool immediate = false); // PEA: Push effective absolute address void PEA(); @@ -610,10 +574,10 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { void REP(); // ROL: Rotate left - void ROL(uint16_t address); + void ROL(uint32_t address, bool accumulator = false); // ROR: Rotate right - void ROR(uint16_t address); + void ROR(uint32_t address, bool accumulator = false); // RTI: Return from interrupt void RTI(); @@ -625,7 +589,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { void RTS(); // SBC: Subtract with carry - void SBC(uint16_t operand, bool isImmediate = false); + void SBC(uint32_t operand, bool immediate = false); // SEC: Set carry flag void SEC(); @@ -640,7 +604,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { void SEP(); // STA: Store accumulator - void STA(uint16_t address); + void STA(uint32_t address); // STP: Stop the processor void STP(); @@ -726,7 +690,6 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { SetCarryFlag(register_value >= memory_value); // Carry flag } - // Helper function to set or clear a specific flag bit void SetFlag(uint8_t mask, bool set) { if (set) { status |= mask; // Set the bit @@ -735,10 +698,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { } } - // Helper function to get the value of a specific flag bit bool GetFlag(uint8_t mask) const { return (status & mask) != 0; } - - // Appease the C++ Gods... void PushByte(uint8_t value) override { memory.PushByte(value); } void PushWord(uint16_t value) override { memory.PushWord(value); } uint8_t PopByte() override { return memory.PopByte(); } @@ -746,9 +706,6 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags { void PushLong(uint32_t value) override { memory.PushLong(value); } uint32_t PopLong() override { return memory.PopLong(); } void ClearMemory() override { memory.ClearMemory(); } - void LoadData(const std::vector& data) override { - memory.LoadData(data); - } uint8_t operator[](int i) const override { return 0; } uint8_t at(int i) const override { return 0; } diff --git a/src/app/emu/cpu/addressing.cc b/src/app/emu/cpu/addressing.cc new file mode 100644 index 00000000..c0aad3b7 --- /dev/null +++ b/src/app/emu/cpu/addressing.cc @@ -0,0 +1,122 @@ +#include "app/emu/cpu.h" + +namespace yaze { +namespace app { +namespace emu { + +uint16_t CPU::Absolute(CPU::AccessType access_type) { + auto operand = FetchWord(); + uint32_t bank = + (access_type == CPU::AccessType::Data) ? (DB << 16) : (PB << 16); + return bank | (operand & 0xFFFF); +} + +uint32_t CPU::AbsoluteIndexedX() { + uint16_t address = memory.ReadWord((PB << 16) | (PC + 1)); + uint32_t effective_address = (DB << 16) | ((address + X) & 0xFFFF); + return effective_address; +} + +uint32_t CPU::AbsoluteIndexedY() { + uint16_t address = memory.ReadWord((PB << 16) | (PC + 1)); + uint32_t effective_address = (DB << 16) | address + Y; + return effective_address; +} + +uint16_t CPU::AbsoluteIndexedIndirect() { + uint16_t address = FetchWord() + X; + return memory.ReadWord((DB << 16) | address & 0xFFFF); +} + +uint16_t CPU::AbsoluteIndirect() { + uint16_t address = FetchWord(); + return memory.ReadWord((PB << 16) | address); +} + +uint32_t CPU::AbsoluteIndirectLong() { + uint16_t address = FetchWord(); + return memory.ReadWordLong((PB << 16) | address); +} + +uint32_t CPU::AbsoluteLong() { return FetchLong(); } + +uint32_t CPU::AbsoluteLongIndexedX() { return FetchLong() + X; } + +void CPU::BlockMove(uint16_t source, uint16_t dest, uint16_t length) { + for (int i = 0; i < length; i++) { + memory.WriteByte(dest + i, memory.ReadByte(source + i)); + } +} + +uint16_t CPU::DirectPage() { + uint8_t dp = FetchByte(); + return D + dp; +} + +uint16_t CPU::DirectPageIndexedX() { + uint8_t operand = FetchByte(); + uint16_t x_by_mode = GetAccumulatorSize() ? X : X & 0xFF; + return D + operand + x_by_mode; +} + +uint16_t CPU::DirectPageIndexedY() { + uint8_t operand = FetchByte(); + return (operand + Y) & 0xFF; +} + +uint16_t CPU::DirectPageIndexedIndirectX() { + uint8_t operand = FetchByte(); + uint16_t indirect_address = D + operand + X; + uint16_t effective_address = memory.ReadWord(indirect_address & 0xFFFF); + return effective_address; +} + +uint16_t CPU::DirectPageIndirect() { + uint8_t dp = FetchByte(); + uint16_t effective_address = D + dp; + return memory.ReadWord(effective_address); +} + +uint32_t CPU::DirectPageIndirectLong() { + uint8_t dp = FetchByte(); + uint16_t effective_address = D + dp; + return memory.ReadWordLong((0x00 << 0x10) | effective_address); +} + +uint16_t CPU::DirectPageIndirectIndexedY() { + uint8_t operand = FetchByte(); + uint16_t indirect_address = D + operand; + return memory.ReadWord(indirect_address) + Y; +} + +uint32_t CPU::DirectPageIndirectLongIndexedY() { + uint8_t operand = FetchByte(); + uint16_t indirect_address = D + operand; + uint16_t y_by_mode = GetAccumulatorSize() ? Y : Y & 0xFF; + uint32_t effective_address = + memory.ReadWordLong(indirect_address) + y_by_mode; + return effective_address; +} + +uint16_t CPU::Immediate() { + if (GetAccumulatorSize()) { + return memory.ReadByte((PB << 16) | PC + 1); + } else { + return memory.ReadWord((PB << 16) | PC + 1); + } +} + +uint16_t CPU::StackRelative() { + uint8_t sr = FetchByte(); + uint16_t effective_address = SP() + sr; + return effective_address; +} + +uint32_t CPU::StackRelativeIndirectIndexedY() { + uint8_t sr = FetchByte(); + return (DB << 0x10) | (memory.ReadWord(SP() + sr) + Y); +} + +} // namespace emu +} // namespace app +} // namespace yaze \ No newline at end of file diff --git a/src/app/emu/cpu/instructions.cc b/src/app/emu/cpu/instructions.cc new file mode 100644 index 00000000..414a9165 --- /dev/null +++ b/src/app/emu/cpu/instructions.cc @@ -0,0 +1,818 @@ +#include +#include +#include + +#include "app/emu/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(uint16_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(uint16_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) { + 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 \ No newline at end of file diff --git a/src/app/emu/memory/memory.h b/src/app/emu/memory/memory.h index d9ca217c..d671f1d2 100644 --- a/src/app/emu/memory/memory.h +++ b/src/app/emu/memory/memory.h @@ -125,12 +125,10 @@ class Memory { virtual void PushLong(uint32_t value) = 0; virtual uint32_t PopLong() = 0; - virtual int16_t SP() const = 0; - virtual void SetSP(int16_t value) = 0; + virtual uint16_t SP() const = 0; + virtual void SetSP(uint16_t value) = 0; - virtual void SetMemory(const std::vector& data) = 0; virtual void ClearMemory() = 0; - virtual void LoadData(const std::vector& data) = 0; virtual uint8_t operator[](int i) const = 0; virtual uint8_t at(int i) const = 0; @@ -313,15 +311,9 @@ class MemoryImpl : public Memory, public Loggable { void AddObserver(Observer* observer) { observers_.push_back(observer); } // Stack Pointer access. - int16_t SP() const override { return SP_; } - void SetSP(int16_t value) override { SP_ = value; } + uint16_t SP() const override { return SP_; } + void SetSP(uint16_t value) override { SP_ = value; } void ClearMemory() override { std::fill(memory_.begin(), memory_.end(), 0); } - void SetMemory(const std::vector& data) override { - std::copy(data.begin(), data.end(), memory_.begin()); - } - void LoadData(const std::vector& data) override { - std::copy(data.begin(), data.end(), memory_.begin()); - } uint8_t at(int i) const override { return memory_[i]; } uint8_t operator[](int i) const override { diff --git a/src/app/emu/memory/mock_memory.h b/src/app/emu/memory/mock_memory.h index b43508b6..530c3de7 100644 --- a/src/app/emu/memory/mock_memory.h +++ b/src/app/emu/memory/mock_memory.h @@ -21,6 +21,9 @@ class MockClock : public Clock { MOCK_METHOD(float, GetFrequency, (), (const, override)); }; +// 0x1000000 is 16 MB, simplifying the memory layout for testing +// 2 MB is = 0x200000 + class MockMemory : public Memory { public: MOCK_CONST_METHOD1(ReadByte, uint8_t(uint32_t address)); @@ -39,8 +42,8 @@ class MockMemory : public Memory { MOCK_METHOD1(PushLong, void(uint32_t value)); MOCK_METHOD0(PopLong, uint32_t()); - MOCK_CONST_METHOD0(SP, int16_t()); - MOCK_METHOD1(SetSP, void(int16_t value)); + MOCK_CONST_METHOD0(SP, uint16_t()); + MOCK_METHOD1(SetSP, void(uint16_t value)); MOCK_METHOD1(SetMemory, void(const std::vector& data)); MOCK_METHOD1(LoadData, void(const std::vector& data)); @@ -51,12 +54,16 @@ class MockMemory : public Memory { uint8_t operator[](int i) const override { return memory_[i]; } void SetMemoryContents(const std::vector& data) { - memory_.resize(64000); + if (data.size() > memory_.size()) { + memory_.resize(data.size()); + } std::copy(data.begin(), data.end(), memory_.begin()); } void SetMemoryContents(const std::vector& data) { - memory_.resize(64000); + if (data.size() > memory_.size()) { + memory_.resize(data.size()); + } int i = 0; for (const auto& each : data) { memory_[i] = each & 0xFF; @@ -66,6 +73,10 @@ class MockMemory : public Memory { } void InsertMemory(const uint64_t address, const std::vector& data) { + if (address > memory_.size()) { + memory_.resize(address + data.size()); + } + int i = 0; for (const auto& each : data) { memory_[address + i] = each; @@ -160,6 +171,9 @@ class MockMemory : public Memory { this->SetSP(SP_ + 3); return value; }); + ON_CALL(*this, SP()).WillByDefault([this]() { return SP_; }); + ON_CALL(*this, SetSP(::testing::_)) + .WillByDefault([this](uint16_t value) { SP_ = value; }); ON_CALL(*this, ClearMemory()).WillByDefault([this]() { memory_.resize(64000, 0x00); }); diff --git a/src/app/emu/snes.cc b/src/app/emu/snes.cc index b734ddab..9d16bb88 100644 --- a/src/app/emu/snes.cc +++ b/src/app/emu/snes.cc @@ -29,7 +29,6 @@ uint16_t GetHeaderOffset(const Memory& memory) { switch (mapMode & 0x07) { case 0: // LoROM offset = 0x7FC0; - // offset = 0xFFC0; break; case 1: // HiROM offset = 0xFFC0; @@ -113,12 +112,13 @@ void SNES::Init(ROM& rom) { cpu_.E = 0; // Initialize CPU - cpu_.Init(); + cpu_.Init(); // Read the ROM header auto header_offset = GetHeaderOffset(memory_); rom_info_ = ReadRomHeader((0x00 << 16) + header_offset); - cpu_.PC = rom_info_.resetVector; + cpu_.PB = 0x00; + cpu_.PC = 0x8000; // Initialize PPU ppu_.Init(); @@ -300,16 +300,28 @@ void SNES::NmiIsr() { // VBlank routine void SNES::VBlankRoutine() { - // Execute code that needs to run during VBlank, such as transferring data to - // the PPU + // Read the joypad state + // ... + + // Update the PPU + // ... + + // Update the APU + // ... } -void SNES::BootAPUWithIPL() { - // 1. Waiting for the SPC700 to be ready - while (!apu_.IsReadySignalReceived()) { - // Active waiting (this can be optimized) +void SNES::BootApuWithIPL() { + // 1. Check if the SPC700 is ready, else set a callback for when it becomes + // ready + if (!apu_.IsReadySignalReceived()) { + apu_.SetReadyCallback([this]() { this->StartApuDataTransfer(); }); + return; // Exit and wait for callback to be called } + StartApuDataTransfer(); +} + +void SNES::StartApuDataTransfer() { // 2. Setting the starting address const uint16_t startAddress = 0x0200; memory_.WriteByte(0x2142, startAddress & 0xFF); // Lower byte diff --git a/src/app/emu/snes.h b/src/app/emu/snes.h index 70419d67..dea3069f 100644 --- a/src/app/emu/snes.h +++ b/src/app/emu/snes.h @@ -1,8 +1,6 @@ #ifndef YAZE_APP_EMU_SNES_H #define YAZE_APP_EMU_SNES_H -#include - #include #include #include @@ -48,7 +46,8 @@ class SNES : public DMA { void VBlankRoutine(); // Boot the APU with the IPL ROM - void BootAPUWithIPL(); + void BootApuWithIPL(); + void StartApuDataTransfer(); // Controller input handling void HandleInput(); diff --git a/src/cli/CMakeLists.txt b/src/cli/CMakeLists.txt index 9ccb7496..15f43dfc 100644 --- a/src/cli/CMakeLists.txt +++ b/src/cli/CMakeLists.txt @@ -6,6 +6,7 @@ add_executable( app/rom.cc app/core/common.cc app/gui/pipeline.cc + ${YAZE_APP_EMU_SRC} ${YAZE_APP_GFX_SRC} ${YAZE_APP_ZELDA3_SRC} ${YAZE_GUI_SRC} @@ -16,11 +17,11 @@ target_include_directories( z3ed PUBLIC lib/ app/ + lib/SDL_mixer/include/ ${CMAKE_SOURCE_DIR}/src/ ${PNG_INCLUDE_DIRS} ${SDL2_INCLUDE_DIR} ${GLEW_INCLUDE_DIRS} - lib/SDL_mixer/include/ ) target_link_libraries( diff --git a/src/cli/command_handler.h b/src/cli/command_handler.h index 2c0a995e..9675dc22 100644 --- a/src/cli/command_handler.h +++ b/src/cli/command_handler.h @@ -14,12 +14,13 @@ #include "absl/strings/str_cat.h" #include "app/core/common.h" // for PcToSnes, SnesToPc #include "app/core/constants.h" // for RETURN_IF_ERROR -#include "app/gui/pipeline.h" +#include "app/emu/snes.h" #include "app/gfx/bitmap.h" #include "app/gfx/compression.h" #include "app/gfx/snes_palette.h" #include "app/gfx/snes_tile.h" #include "app/gui/canvas.h" +#include "app/gui/pipeline.h" #include "app/rom.h" // for ROM #include "app/zelda3/overworld.h" #include "cli/patch.h" // for ApplyBpsPatch, CreateBpsPatch @@ -273,8 +274,32 @@ class Expand : public CommandHandler { } }; +// Start Emulator on a SNES rom file +// -emu +class Emulator : public CommandHandler { + public: + absl::Status handle(const std::vector& arg_vec) override { + std::string filename = arg_vec[0]; + RETURN_IF_ERROR(rom_.LoadFromFile(filename)) + + snes.SetupMemory(rom_); + snes.Init(rom_); + + int i = 0; + while (i < 80000) { + snes.Run(); + i++; + } + + return absl::OkStatus(); + } + + app::emu::SNES snes; +}; + struct Commands { std::unordered_map> handlers = { + {"-emu", std::make_shared()}, {"-a", std::make_shared()}, {"-c", std::make_shared()}, {"-o", std::make_shared()}, diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 59f786d3..7f139ee5 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -23,6 +23,8 @@ add_executable( ../src/cli/command_handler.cc ../src/app/rom.cc ../src/app/emu/cpu.cc + ../src/app/emu/cpu/instructions.cc + ../src/app/emu/cpu/addressing.cc ../src/app/emu/audio/apu.cc ../src/app/emu/video/ppu.cc ../src/app/emu/audio/dsp.cc diff --git a/test/emu/cpu_test.cc b/test/emu/cpu_test.cc index 9c081785..bda5c71d 100644 --- a/test/emu/cpu_test.cc +++ b/test/emu/cpu_test.cc @@ -58,6 +58,84 @@ TEST_F(CPUTest, CheckMemoryContents) { // ============================================================================ // ADC - Add with Carry +TEST_F(CPUTest, ADC_CheckCarryFlag) { + cpu.A = 0xFF; + cpu.SetAccumulatorSize(true); + std::vector data = {0x15, 0x01}; // Operand at address 0x15 + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(1)); + + cpu.ExecuteInstruction(0x69); // ADC Immediate + + EXPECT_EQ(cpu.A, 0x00); + EXPECT_TRUE(cpu.GetCarryFlag()); +} + +TEST_F(CPUTest, ADC_DirectPageIndexedIndirectX) { + cpu.A = 0x03; + cpu.D = 0x2000; // Setting Direct Page register to 0x2000 + std::vector data = {0x61, 0x10}; // ADC (dp, X) + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2012, {0x00, 0x30}); // [0x2012] = 0x3000 + mock_memory.InsertMemory(0x3000, {0x06}); // [0x3000] = 0x06 + + cpu.X = 0x02; // X register + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + EXPECT_CALL(mock_memory, ReadWord(0x2012)).WillOnce(Return(0x3000)); + EXPECT_CALL(mock_memory, ReadByte(0x3000)).WillOnce(Return(0x06)); + + cpu.ExecuteInstruction(0x61); // ADC (dp, X) + EXPECT_EQ(cpu.A, 0x09); // 0x03 + 0x06 = 0x09 +} + +TEST_F(CPUTest, ADC_StackRelative) { + cpu.A = 0x03; + cpu.SetSP(0x01FF); // Setting Stack Pointer to 0x01FF + std::vector data = {0x63, 0x02}; // ADC sr + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0201, {0x06}); // [0x0201] = 0x06 + + EXPECT_CALL(mock_memory, SP()).WillOnce(Return(0x01FF)); + + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); // Operand + EXPECT_CALL(mock_memory, ReadByte(0x0201)) + .WillOnce(Return(0x06)); // Memory value + + cpu.ExecuteInstruction(0x63); // ADC Stack Relative + EXPECT_EQ(cpu.A, 0x09); // 0x03 + 0x06 = 0x09 +} + +TEST_F(CPUTest, ADC_DirectPage) { + cpu.A = 0x01; + cpu.D = 0x2000; // Setting Direct Page register to 0x2000 + std::vector data = {0x65, 0x10}; // ADC dp + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2010, {0x05}); // [0x2010] = 0x05 + + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + EXPECT_CALL(mock_memory, ReadByte(0x2010)).WillOnce(Return(0x05)); + + cpu.ExecuteInstruction(0x65); // ADC Direct Page + EXPECT_EQ(cpu.A, 0x06); +} + +TEST_F(CPUTest, ADC_DirectPageIndirectLong) { + cpu.A = 0x03; + cpu.D = 0x2000; + std::vector data = {0x67, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2010, {0x05, 0x00, 0x30}); + mock_memory.InsertMemory(0x030005, {0x06}); + + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + EXPECT_CALL(mock_memory, ReadWordLong(0x2010)).WillOnce(Return(0x300005)); + EXPECT_CALL(mock_memory, ReadWord(0x300005)).WillOnce(Return(0x06)); + + cpu.ExecuteInstruction(0x67); // ADC Direct Page Indirect Long + EXPECT_EQ(cpu.A, 0x09); +} + TEST_F(CPUTest, ADC_Immediate_TwoPositiveNumbers) { cpu.A = 0x01; cpu.SetAccumulatorSize(true); @@ -110,7 +188,23 @@ TEST_F(CPUTest, ADC_AbsoluteLong) { EXPECT_EQ(cpu.A, 0x06); } -// ADC Direct Page Indirect +TEST_F(CPUTest, ADC_DirectPageIndirectIndexedY) { + cpu.A = 0x03; + cpu.Y = 0x02; + cpu.D = 0x2000; + std::vector data = {0x71, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2010, {0x00, 0x30}); // [0x2010] = 0x3000 + mock_memory.InsertMemory(0x3002, {0x06}); // [0x3002] = 0x06 + + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + EXPECT_CALL(mock_memory, ReadWord(0x2010)).WillOnce(Return(0x3000)); + EXPECT_CALL(mock_memory, ReadByte(0x3002)).WillOnce(Return(0x06)); + + cpu.ExecuteInstruction(0x71); // ADC Direct Page Indirect Indexed, Y + EXPECT_EQ(cpu.A, 0x09); // 0x03 + 0x06 = 0x09 +} + TEST_F(CPUTest, ADC_DirectPageIndirect) { cpu.A = 0x02; cpu.D = 0x2000; // Setting Direct Page register to 0x2000 @@ -127,120 +221,196 @@ TEST_F(CPUTest, ADC_DirectPageIndirect) { EXPECT_EQ(cpu.A, 0x07); // 0x02 + 0x05 = 0x07 } -// ADC Direct Page Indexed Indirect, X -TEST_F(CPUTest, ADC_DirectPageIndexedIndirectX) { - cpu.A = 0x03; - cpu.D = 0x2000; // Setting Direct Page register to 0x2000 - std::vector data = {0x61, 0x10}; // ADC (dp, X) +TEST_F(CPUTest, ADC_StackRelativeIndirectIndexedY) { + cpu.A = 0x03; // A register + cpu.Y = 0x02; // Y register + cpu.DB = 0x10; // Setting Data Bank register to 0x20 + cpu.SetSP(0x01FF); // Setting Stack Pointer to 0x01FF + std::vector data = {0x73, 0x02}; // ADC sr, Y mock_memory.SetMemoryContents(data); - mock_memory.InsertMemory(0x2012, {0x00, 0x30}); // [0x2012] = 0x3000 - mock_memory.InsertMemory(0x3000, {0x06}); // [0x3000] = 0x06 + mock_memory.InsertMemory(0x0201, {0x00, 0x30}); // [0x0201] = 0x3000 + mock_memory.InsertMemory(0x103002, {0x06}); // [0x3002] = 0x06 - cpu.X = 0x02; // X register - EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); - EXPECT_CALL(mock_memory, ReadWord(0x2012)).WillOnce(Return(0x3000)); - EXPECT_CALL(mock_memory, ReadByte(0x3000)).WillOnce(Return(0x06)); + EXPECT_CALL(mock_memory, SP()).WillOnce(Return(0x01FF)); + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); + EXPECT_CALL(mock_memory, ReadWord(0x0201)).WillOnce(Return(0x3000)); + EXPECT_CALL(mock_memory, ReadByte(0x103002)).WillOnce(Return(0x06)); - cpu.ExecuteInstruction(0x61); // ADC (dp, X) + cpu.ExecuteInstruction(0x73); // ADC Stack Relative Indexed Y EXPECT_EQ(cpu.A, 0x09); // 0x03 + 0x06 = 0x09 } -TEST_F(CPUTest, ADC_CheckCarryFlag) { - cpu.A = 0xFF; - cpu.SetAccumulatorSize(true); - std::vector data = {0x15, 0x01}; // Operand at address 0x15 +TEST_F(CPUTest, ADC_DirectPageIndexedX) { + cpu.A = 0x03; + cpu.X = 0x02; + cpu.D = 0x2000; + std::vector data = {0x75, 0x10}; mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2012, {0x06}); // [0x2012] = 0x3000 - EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(1)); + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + EXPECT_CALL(mock_memory, ReadByte(0x2012)).WillOnce(Return(0x06)); - cpu.ExecuteInstruction(0x69); // ADC Immediate - - EXPECT_EQ(cpu.A, 0x00); - EXPECT_TRUE(cpu.GetCarryFlag()); + cpu.ExecuteInstruction(0x75); // ADC Direct Page Indexed, X + EXPECT_EQ(cpu.A, 0x09); // 0x03 + 0x06 = 0x09 } -TEST_F(CPUTest, ADC_AbsoluteIndexedX) { +TEST_F(CPUTest, ADC_DirectPageIndirectLongIndexedY) { cpu.A = 0x03; - cpu.X = 0x02; // X register - cpu.SetCarryFlag(false); - cpu.SetAccumulatorSize(false); // 16-bit mode - std::vector data = {0x7D, 0x03, 0x00, 0x00, 0x05, 0x00}; + cpu.Y = 0x02; + cpu.D = 0x2000; + cpu.status = 0x00; + std::vector data = {0x77, 0x10}; mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2010, {0x05, 0x00, 0x01}); + mock_memory.InsertMemory(0x010007, {0x06}); - EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x0003)); - EXPECT_CALL(mock_memory, ReadWord(0x0005)).WillOnce(Return(0x0005)); + EXPECT_CALL(mock_memory, ReadWordLong(0x2010)).WillOnce(Return(0x010005)); + EXPECT_CALL(mock_memory, ReadWord(0x010007)).WillOnce(Return(0x06)); - cpu.ExecuteInstruction(0x7D); // ADC Absolute Indexed X - EXPECT_EQ(cpu.A, 0x08); + cpu.ExecuteInstruction(0x77); // ADC DP Indirect Long Indexed, Y + EXPECT_EQ(cpu.A, 0x09); } TEST_F(CPUTest, ADC_AbsoluteIndexedY) { cpu.A = 0x03; - cpu.Y = 0x02; // Y register + cpu.Y = 0x02; // Y register + cpu.DB = 0x20; // Setting Data Bank register to 0x20 std::vector data = {0x79, 0x03, 0x00, 0x00, 0x05, 0x00}; mock_memory.SetMemoryContents(data); EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x0003)); - EXPECT_CALL(mock_memory, ReadWord(0x0005)).WillOnce(Return(0x0005)); + EXPECT_CALL(mock_memory, ReadWord(0x200005)).WillOnce(Return(0x0005)); + + mock_memory.InsertMemory(0x200005, {0x05}); cpu.ExecuteInstruction(0x79); // ADC Absolute Indexed Y EXPECT_EQ(cpu.A, 0x08); } -TEST_F(CPUTest, ADC_DirectPageIndexedY) { +TEST_F(CPUTest, ADC_AbsoluteIndexedX) { cpu.A = 0x03; - cpu.D = 0x2000; - cpu.Y = 0x02; - std::vector data = {0x77, 0x10}; + cpu.X = 0x02; // X register + cpu.DB = 0x20; // Setting Data Bank register to 0x20 + cpu.SetCarryFlag(false); + cpu.SetAccumulatorSize(false); // 16-bit mode + std::vector data = {0x7D, 0x03, 0x00}; mock_memory.SetMemoryContents(data); - mock_memory.InsertMemory(0x2012, {0x06}); - EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); - EXPECT_CALL(mock_memory, ReadWordLong(0x2012)).WillOnce(Return(0x06)); + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x0003)); + EXPECT_CALL(mock_memory, ReadWord(0x200005)).WillOnce(Return(0x0005)); - cpu.ExecuteInstruction(0x77); // ADC Direct Page Indexed Y - EXPECT_EQ(cpu.A, 0x09); + mock_memory.InsertMemory(0x200005, {0x05}); // Inserting memory at 0x2005 + + cpu.ExecuteInstruction(0x7D); // ADC Absolute Indexed X + EXPECT_EQ(cpu.A, 0x08); } -/** Quarantined until we figure out what the hell is going on -TEST_F(CPUTest, ADC_DirectPageIndirectLong) { +TEST_F(CPUTest, ADC_AbsoluteLongIndexedX) { cpu.A = 0x03; - cpu.D = 0x2000; - cpu.PC = 0x0001; - std::vector data = {0x67, 0x10}; + cpu.X = 0x02; // X register + cpu.SetCarryFlag(false); + cpu.SetAccumulatorSize(false); // 16-bit mode + std::vector data = {0x7F, 0x00, 0x00, 0x01}; mock_memory.SetMemoryContents(data); - mock_memory.InsertMemory(0x2010, {0x05, 0x00, 0x30}); - mock_memory.InsertMemory(0x030005, {0x06}); + mock_memory.InsertMemory(0x010000, {0x03, 0x05}); - EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); - EXPECT_CALL(mock_memory, ReadWordLong(0x2010)).WillOnce(Return(0x300005)); - EXPECT_CALL(mock_memory, ReadWord(0x030005)).WillOnce(Return(0x06)); + EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x010000)); + EXPECT_CALL(mock_memory, ReadWord(0x010002)).WillOnce(Return(0x0005)); - cpu.ExecuteInstruction(0x67); // ADC Direct Page Indirect Long - EXPECT_EQ(cpu.A, 0x09); -} -*/ - -TEST_F(CPUTest, ADC_StackRelative) { - cpu.A = 0x03; - cpu.SetSP(0x01FF); // Setting Stack Pointer to 0x01FF - std::vector data = {0x63, 0x02}; // ADC sr - mock_memory.SetMemoryContents(data); - mock_memory.InsertMemory(0x0201, {0x06}); // [0x0201] = 0x06 - - EXPECT_CALL(mock_memory, SP()).WillOnce(Return(0x01FF)); - - EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); // Operand - EXPECT_CALL(mock_memory, ReadByte(0x0201)) - .WillOnce(Return(0x06)); // Memory value - - cpu.ExecuteInstruction(0x63); // ADC Stack Relative - EXPECT_EQ(cpu.A, 0x09); // 0x03 + 0x06 = 0x09 + cpu.ExecuteInstruction(0x7F); // ADC Absolute Long Indexed X + EXPECT_EQ(cpu.A, 0x08); } // ============================================================================ // AND - Logical AND +TEST_F(CPUTest, AND_DirectPageIndexedIndirectX) { + cpu.A = 0b11110000; // A register + cpu.D = 0x2000; // Setting Direct Page register to 0x2000 + cpu.X = 0x02; // X register + cpu.status = 0xFF; // 8-bit mode + std::vector data = {0x21, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2012, {0x00, 0x30}); // [0x2012] = 0x3000 + mock_memory.InsertMemory(0x3000, {0b10101010}); // [0x3000] = 0b10101010 + + // Get the operand + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + + // Get the value at the operand + EXPECT_CALL(mock_memory, ReadWord(0x2012)).WillOnce(Return(0x3000)); + + // Get the value at the operand + EXPECT_CALL(mock_memory, ReadByte(0x3000)).WillOnce(Return(0b10101010)); + + cpu.ExecuteInstruction(0x21); // AND Direct Page Indexed Indirect X + + EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 +} + +TEST_F(CPUTest, AND_StackRelative) { + cpu.A = 0b11110000; // A register + cpu.status = 0xFF; // 8-bit mode + cpu.SetSP(0x01FF); // Setting Stack Pointer to 0x01FF + std::vector data = {0x23, 0x02}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0201, {0b10101010}); // [0x0201] = 0b10101010 + + // Get the operand + EXPECT_CALL(mock_memory, SP()).WillOnce(Return(0x01FF)); + + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); + + // Get the value at the operand + EXPECT_CALL(mock_memory, ReadByte(0x0201)).WillOnce(Return(0b10101010)); + + cpu.ExecuteInstruction(0x23); // AND Stack Relative + + EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 +} + +TEST_F(CPUTest, AND_DirectPage) { + cpu.A = 0b11110000; // A register + cpu.D = 0x2000; // Setting Direct Page register to 0x2000 + std::vector data = {0x25, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2010, {0b10101010}); // [0x2010] = 0b10101010 + + // Get the operand + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + + // Get the value at the operand + EXPECT_CALL(mock_memory, ReadByte(0x2010)).WillOnce(Return(0b10101010)); + + cpu.ExecuteInstruction(0x25); // AND Direct Page + + EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 +} + +TEST_F(CPUTest, AND_DirectPageIndirectLong) { + cpu.A = 0b11110000; // A register + cpu.D = 0x2000; // Setting Direct Page register to 0x2000 + cpu.status = 0xFF; // 8-bit mode + std::vector data = {0x27, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2010, {0x05, 0x00, 0x30}); + mock_memory.InsertMemory(0x300005, {0b10101010}); + + // Get the operand + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + + // Get the value at the operand + EXPECT_CALL(mock_memory, ReadWordLong(0x2010)).WillOnce(Return(0x300005)); + + // Get the value at the operand + EXPECT_CALL(mock_memory, ReadByte(0x300005)).WillOnce(Return(0b10101010)); + + cpu.ExecuteInstruction(0x27); // AND Direct Page Indirect Long + + EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 +} + TEST_F(CPUTest, AND_Immediate) { cpu.A = 0b11110000; // A register std::vector data = {0x29, 0b10101010}; // AND #0b10101010 @@ -271,7 +441,6 @@ TEST_F(CPUTest, AND_Absolute_16BitMode) { TEST_F(CPUTest, AND_AbsoluteLong) { cpu.A = 0x01; - // PC register cpu.status = 0x00; // 16-bit mode std::vector data = {0x2F, 0x04, 0x00, 0x00, 0x05, 0x00}; @@ -284,35 +453,90 @@ TEST_F(CPUTest, AND_AbsoluteLong) { EXPECT_EQ(cpu.A, 0x01); } -TEST_F(CPUTest, AND_IndexedIndirect) { - cpu.A = 0b10101010; // A register - cpu.X = 0x02; // X register - std::vector data = {0x21, 0x10, 0x18, 0x20, 0b01010101}; +TEST_F(CPUTest, AND_DirectPageIndirectIndexedY) { + cpu.A = 0b11110000; // A register + cpu.D = 0x2000; // Setting Direct Page register to 0x2000 + cpu.Y = 0x02; // Y register + std::vector data = {0x31, 0x10}; mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2010, {0x00, 0x30}); // [0x2010] = 0x3000 + mock_memory.InsertMemory(0x3002, {0b10101010}); // [0x3002] = 0b10101010 - cpu.ExecuteInstruction(0x21); // AND Indexed Indirect - EXPECT_EQ(cpu.A, 0b00000000); // A register should now be 0b00000000 + // Get the value at the operand + EXPECT_CALL(mock_memory, ReadWord(0x2010)).WillOnce(Return(0x3000)); + + cpu.ExecuteInstruction(0x31); // AND Direct Page Indirect Indexed Y + + EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 } -TEST_F(CPUTest, AND_AbsoluteIndexedX) { +TEST_F(CPUTest, AND_DirectPageIndirect) { cpu.A = 0b11110000; // A register - cpu.X = 0x02; // X register - std::vector data = {0x3D, 0x03, 0x00, - 0b00000000, 0b10101010, 0b01010101}; + cpu.D = 0x2000; // Setting Direct Page register to 0x2000 + std::vector data = {0x32, 0x10}; mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2010, {0x00, 0x30}); // [0x2010] = 0x3000 + mock_memory.InsertMemory(0x3000, {0b10101010}); // [0x3000] = 0b10101010 - // Get the absolute address - EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x0003)); + // Get the value at the operand + EXPECT_CALL(mock_memory, ReadWord(0x2010)).WillOnce(Return(0x3000)); - // Add the offset from the X register to the absolute address - uint16_t address = 0x0003 + static_cast(cpu.X & 0xFF); + cpu.ExecuteInstruction(0x32); // AND Direct Page Indirect - // Get the value at the absolute address + X - EXPECT_CALL(mock_memory, ReadByte(address)).WillOnce(Return(0b10101010)); + EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 +} - cpu.ExecuteInstruction(0x3D); // AND Absolute, X +TEST_F(CPUTest, AND_StackRelativeIndirectIndexedY) { + cpu.A = 0b11110000; // A register + cpu.Y = 0x02; // Y register + cpu.DB = 0x10; // Setting Data Bank register to 0x20 + cpu.SetSP(0x01FF); // Setting Stack Pointer to 0x01FF + std::vector data = {0x33, 0x02}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0201, {0x00, 0x30}); // [0x0201] = 0x3000 + mock_memory.InsertMemory(0x103002, {0b10101010}); // [0x3002] = 0b10101010 + + EXPECT_CALL(mock_memory, SP()).WillOnce(Return(0x01FF)); + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); + EXPECT_CALL(mock_memory, ReadWord(0x0201)).WillOnce(Return(0x3000)); + EXPECT_CALL(mock_memory, ReadByte(0x103002)).WillOnce(Return(0b10101010)); + cpu.ExecuteInstruction(0x33); // AND Stack Relative Indirect Indexed Y + + EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 +} + +TEST_F(CPUTest, AND_DirectPageIndexedX) { + cpu.A = 0b11110000; // A register + cpu.D = 0x2000; // Setting Direct Page register to 0x2000 + cpu.X = 0x02; // X register + std::vector data = {0x35, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2012, {0b10101010}); // [0x2012] = 0b10101010 + + // Get the value at the operand + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + EXPECT_CALL(mock_memory, ReadByte(0x2012)).WillOnce(Return(0b10101010)); + + cpu.ExecuteInstruction(0x35); // AND Direct Page Indexed X + + EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 +} + +TEST_F(CPUTest, AND_DirectPageIndirectLongIndexedY) { + cpu.A = 0b11110000; // A register + cpu.D = 0x2000; // Setting Direct Page register to 0x2000 + cpu.Y = 0x02; // Y register + std::vector data = {0x37, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2010, {0x05, 0x00, 0x30}); + mock_memory.InsertMemory(0x300005, {0b10101010}); + + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + EXPECT_CALL(mock_memory, ReadWordLong(0x2010)).WillOnce(Return(0x300005)); + EXPECT_CALL(mock_memory, ReadByte(0x300007)).WillOnce(Return(0b10101010)); + + cpu.ExecuteInstruction(0x37); // AND Direct Page Indirect Long Indexed Y - EXPECT_THAT(cpu.PC, testing::Eq(0x03)); EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 } @@ -338,6 +562,29 @@ TEST_F(CPUTest, AND_AbsoluteIndexedY) { EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 } +TEST_F(CPUTest, AND_AbsoluteIndexedX) { + cpu.A = 0b11110000; // A register + cpu.X = 0x02; // X register + std::vector data = {0x3D, 0x03, 0x00, + 0b00000000, 0b10101010, 0b01010101}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x200005, {0b10101010}); + + // Get the absolute address + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x0003)); + + // Add the offset from the X register to the absolute address + uint16_t address = 0x0003 + static_cast(cpu.X & 0xFF); + + // Get the value at the absolute address + X + EXPECT_CALL(mock_memory, ReadByte(address)).WillOnce(Return(0b10101010)); + + cpu.ExecuteInstruction(0x3D); // AND Absolute, X + + // EXPECT_THAT(cpu.PC, testing::Eq(0x03)); + EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 +} + TEST_F(CPUTest, AND_AbsoluteLongIndexedX) { cpu.A = 0b11110000; // A register cpu.X = 0x02; // X register @@ -404,7 +651,7 @@ TEST_F(CPUTest, ASL_Absolute) { EXPECT_FALSE(cpu.GetNegativeFlag()); } -TEST_F(CPUTest, ASL_DP_Indexed_X) { +TEST_F(CPUTest, ASL_DirectPageIndexedX) { cpu.D = 0x1000; // Setting Direct Page register to 0x1000 cpu.X = 0x02; // Setting X register to 0x02 std::vector data = {0x16, 0x10}; // ASL dp,X @@ -417,7 +664,7 @@ TEST_F(CPUTest, ASL_DP_Indexed_X) { EXPECT_TRUE(cpu.GetNegativeFlag()); } -TEST_F(CPUTest, ASL_Absolute_Indexed_X) { +TEST_F(CPUTest, ASL_AbsoluteIndexedX) { cpu.X = 0x02; // Setting X register to 0x02 std::vector data = {0x1E, 0x10, 0x20}; // ASL abs,X mock_memory.SetMemoryContents(data); @@ -437,14 +684,13 @@ TEST_F(CPUTest, ASL_Absolute_Indexed_X) { TEST_F(CPUTest, BCC_WhenCarryFlagClear) { cpu.SetCarryFlag(false); - cpu.PC = 0x1000; - std::vector data(0x1001, 2); // Operand at address 0x1001 + std::vector data = {0x90, 0x05, 0x01}; // Operand at address 0x1001 mock_memory.SetMemoryContents(data); - EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(2)); + EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(5)); cpu.ExecuteInstruction(0x90); // BCC - EXPECT_EQ(cpu.PC, 0x1002); + EXPECT_EQ(cpu.PC, 0x05); } TEST_F(CPUTest, BCC_WhenCarryFlagSet) { @@ -464,14 +710,13 @@ TEST_F(CPUTest, BCC_WhenCarryFlagSet) { TEST_F(CPUTest, BCS_WhenCarryFlagSet) { cpu.SetCarryFlag(true); - cpu.PC = 0x1001; - std::vector data = {0xB0, 0x03, 0x02}; // Operand at address 0x1001 + std::vector data = {0xB0, 0x07, 0x02}; // Operand at address 0x1001 mock_memory.SetMemoryContents(data); - EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0x03)); + EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0x07)); cpu.ExecuteInstruction(0xB0); // BCS - EXPECT_EQ(cpu.PC, 0x1004); + EXPECT_EQ(cpu.PC, 0x07); } TEST_F(CPUTest, BCS_WhenCarryFlagClear) { @@ -538,18 +783,19 @@ TEST_F(CPUTest, BEQ_Immediate_ZeroFlagClear_OverflowFlagSet) { // ============================================================================ // BIT - Bit Test -TEST_F(CPUTest, BIT_Immediate) { +TEST_F(CPUTest, BIT_DirectPage) { cpu.A = 0x01; + cpu.D = 0x1000; // Setting Direct Page register to 0x1000 cpu.status = 0xFF; - std::vector data = {0x24, 0x00, 0x10}; // BIT + std::vector data = {0x24, 0x10}; // BIT mock_memory.SetMemoryContents(data); - mock_memory.InsertMemory(0x0010, {0x81}); // [0x0010] = 0x81 + mock_memory.InsertMemory(0x1010, {0x81}); // [0x1010] = 0x81 // Read the operand EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); // Read the value at the address of the operand - EXPECT_CALL(mock_memory, ReadByte(0x0010)).WillOnce(Return(0x81)); + EXPECT_CALL(mock_memory, ReadByte(0x1010)).WillOnce(Return(0x81)); cpu.ExecuteInstruction(0x24); // BIT EXPECT_TRUE(cpu.GetNegativeFlag()); @@ -576,6 +822,27 @@ TEST_F(CPUTest, BIT_Absolute) { EXPECT_FALSE(cpu.GetZeroFlag()); } +TEST_F(CPUTest, BIT_DirectPageIndexedX) { + cpu.A = 0x01; + cpu.X = 0x02; + cpu.D = 0x1000; // Setting Direct Page register to 0x1000 + cpu.status = 0xFF; + std::vector data = {0x34, 0x10}; // BIT + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x1012, {0x81}); // [0x1010] = 0x81 + + // Read the operand + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + + // Read the value at the address of the operand + EXPECT_CALL(mock_memory, ReadByte(0x1012)).WillOnce(Return(0x81)); + + cpu.ExecuteInstruction(0x34); // BIT + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetOverflowFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + TEST_F(CPUTest, BIT_AbsoluteIndexedX) { cpu.A = 0x01; cpu.X = 0x02; @@ -596,6 +863,25 @@ TEST_F(CPUTest, BIT_AbsoluteIndexedX) { EXPECT_FALSE(cpu.GetZeroFlag()); } +TEST_F(CPUTest, BIT_Immediate) { + cpu.A = 0x01; + cpu.status = 0xFF; + std::vector data = {0x24, 0x00, 0x10}; // BIT + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0010, {0x81}); // [0x0010] = 0x81 + + // Read the operand + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + + // Read the value at the address of the operand + EXPECT_CALL(mock_memory, ReadByte(0x0010)).WillOnce(Return(0x81)); + + cpu.ExecuteInstruction(0x24); // BIT + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetOverflowFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + // ============================================================================ // BMI - Branch if Minus @@ -622,11 +908,11 @@ TEST_F(CPUTest, BMI_BranchNotTaken) { TEST_F(CPUTest, BNE_BranchTaken) { cpu.SetZeroFlag(false); - std::vector data = {0xD0, 0x02}; // BNE + std::vector data = {0xD0, 0x05}; // BNE mock_memory.SetMemoryContents(data); cpu.ExecuteInstruction(0xD0); // BNE - EXPECT_EQ(cpu.PC, 0x0002); + EXPECT_EQ(cpu.PC, 0x0005); } TEST_F(CPUTest, BNE_BranchNotTaken) { @@ -670,6 +956,8 @@ TEST_F(CPUTest, BRA) { EXPECT_EQ(cpu.PC, 0x0002); } +// ============================================================================ + TEST_F(CPUTest, BRK) { std::vector data = {0x00}; // BRK mock_memory.SetMemoryContents(data); @@ -774,6 +1062,96 @@ TEST_F(CPUTest, CLV) { // ============================================================================ // CMP - Compare Accumulator +TEST_F(CPUTest, CMP_DirectPageIndexedIndirectX) { + cpu.status = 0x00; + cpu.SetAccumulatorSize(true); + cpu.A = 0x80; + cpu.X = 0x02; + cpu.D = 0x1000; + cpu.DB = 0x01; + std::vector data = {0xC1, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x1012, {0x00, 0x30}); // [0x1012] = 0x3000 + mock_memory.InsertMemory(0x013000, {0x40}); // [0x3000] = 0x40 + + cpu.ExecuteInstruction(0xC1); + + EXPECT_TRUE(cpu.GetCarryFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); + EXPECT_TRUE(cpu.GetNegativeFlag()); +} + +TEST_F(CPUTest, CMP_StackRelative) { + cpu.A = 0x80; + cpu.SetSP(0x01FF); + std::vector data = {0xC3, 0x02}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0201, {0x40, 0x9F}); + + EXPECT_CALL(mock_memory, SP()).WillRepeatedly(Return(0x01FF)); + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); + EXPECT_CALL(mock_memory, ReadByte(0x0201)).WillOnce(Return(0x30)); + + // Execute the CMP Stack Relative instruction + cpu.ExecuteInstruction(0xC3); + + EXPECT_TRUE(cpu.GetCarryFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); + EXPECT_FALSE(cpu.GetNegativeFlag()); + + mock_memory.InsertMemory(0x0002, {0xC3, 0x03}); + + EXPECT_CALL(mock_memory, SP()).WillRepeatedly(Return(0x01FF)); + EXPECT_CALL(mock_memory, ReadByte(0x0003)).WillOnce(Return(0x03)); + EXPECT_CALL(mock_memory, ReadByte(0x0202)).WillOnce(Return(0x9F)); + + cpu.status = 0b00110000; + cpu.ExecuteInstruction(0xC3); + + EXPECT_TRUE(cpu.GetCarryFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); + EXPECT_TRUE(cpu.GetNegativeFlag()); +} + +TEST_F(CPUTest, CMP_DirectPage) { + // Set the accumulator to 8-bit mode + cpu.status = 0x00; + cpu.SetAccumulatorSize(true); + cpu.A = 0x80; // Set the accumulator to 0x80 + mock_memory.InsertMemory(0x0000, {0xC5}); + + // Execute the CMP Direct Page instruction + cpu.ExecuteInstruction(0xC5); + + // Check the status flags + EXPECT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set + EXPECT_FALSE(cpu.GetZeroFlag()); // Zero flag should not be set + EXPECT_TRUE(cpu.GetNegativeFlag()); // Negative flag should be set +} + +TEST_F(CPUTest, CMP_DirectPageIndirectLong) { + // Set the accumulator to 8-bit mode + cpu.status = 0x00; + cpu.SetAccumulatorSize(true); + cpu.A = 0x80; // Set the accumulator to 0x80 + + // Set up the instruction and operand + mock_memory.InsertMemory(0x0000, {0xC7, 0x02}); + + cpu.D = 0x1000; // Set the Direct Page register to 0x1000 + + mock_memory.InsertMemory(0x1002, {0x00, 0x00, 0x01}); + mock_memory.InsertMemory(0x010000, {0x40}); // [0x010000] = 0x40 + + // Execute the CMP Direct Page Indirect Long instruction + cpu.ExecuteInstruction(0xC7); + + // Check the status flags + EXPECT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set + EXPECT_FALSE(cpu.GetZeroFlag()); // Zero flag should not be set + EXPECT_FALSE(cpu.GetNegativeFlag()); // Negative flag should be set +} + TEST_F(CPUTest, CMP_Immediate_8Bit) { // Set the accumulator to 8-bit mode cpu.status = 0x00; @@ -808,16 +1186,180 @@ TEST_F(CPUTest, CMP_Absolute_16Bit) { EXPECT_TRUE(cpu.GetNegativeFlag()); // Negative flag should be set } +TEST_F(CPUTest, CMP_AbsoluteLong) { + cpu.A = 0x01; + cpu.status = 0b00000001; // 16-bit mode + std::vector data = {0xCF, 0x04, 0x00, 0x00, 0x05, 0x00}; + + mock_memory.SetMemoryContents(data); + EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x000004)); + + EXPECT_CALL(mock_memory, ReadWord(0x0004)).WillOnce(Return(0x000005)); + + cpu.ExecuteInstruction(0xCF); // ADC Absolute Long + + EXPECT_FALSE(cpu.GetCarryFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); + EXPECT_TRUE(cpu.GetNegativeFlag()); +} + +TEST_F(CPUTest, CMP_DirectPageIndirect) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.D = 0x0200; + std::vector data = {0xD1, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023C, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00023C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1000)).WillOnce(Return(0x7F)); + + // Execute the CMP Direct Page Indirect instruction + cpu.ExecuteInstruction(0xD1); + + // Check the status flags + EXPECT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set + EXPECT_FALSE(cpu.GetZeroFlag()); // Zero flag should not be set + EXPECT_TRUE(cpu.GetNegativeFlag()); // Negative flag should be set +} + +TEST_F(CPUTest, CMP_StackRelativeIndirectIndexedY) { + cpu.A = 0x03; // A register + cpu.Y = 0x02; // Y register + cpu.DB = 0x10; // Setting Data Bank register to 0x20 + cpu.SetSP(0x01FF); // Setting Stack Pointer to 0x01FF + std::vector data = {0xD3, 0x02}; // ADC sr, Y + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0201, {0x00, 0x30}); // [0x0201] = 0x3000 + mock_memory.InsertMemory(0x103002, {0x06}); // [0x3002] = 0x06 + + EXPECT_CALL(mock_memory, SP()).WillOnce(Return(0x01FF)); + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); + EXPECT_CALL(mock_memory, ReadWord(0x0201)).WillOnce(Return(0x3000)); + EXPECT_CALL(mock_memory, ReadByte(0x103002)).WillOnce(Return(0x06)); + + // Execute the CMP Stack Relative Indirect Indexed Y instruction + cpu.ExecuteInstruction(0xD3); + + // Check the status flags + EXPECT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set + EXPECT_FALSE(cpu.GetZeroFlag()); // Zero flag should not be set + EXPECT_TRUE(cpu.GetNegativeFlag()); // Negative flag should be set +} + +TEST_F(CPUTest, CMP_DirectPageIndexedX) { + // Set the accumulator to 8-bit mode + cpu.status = 0x00; + cpu.SetAccumulatorSize(true); + cpu.A = 0x80; // Set the accumulator to 0x80 + cpu.X = 0x02; // Set the X register to 0x02 + mock_memory.InsertMemory(0x0000, {0xD5}); + + // Set up the memory to return 0x40 when the Direct Page Indexed X addressing + // mode is used + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(::testing::Return(0x40)); + EXPECT_CALL(mock_memory, ReadByte(0x0042)).WillOnce(::testing::Return(0x40)); + + // Execute the CMP Direct Page Indexed X instruction + cpu.ExecuteInstruction(0xD5); + + // Check the status flags + EXPECT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set + EXPECT_FALSE(cpu.GetZeroFlag()); // Zero flag should not be set + EXPECT_FALSE(cpu.GetNegativeFlag()); // Negative flag should be set +} + +TEST_F(CPUTest, CMP_DirectPageIndirectLongIndexedY) { + cpu.A = 0b11110000; // A register + cpu.D = 0x2000; // Setting Direct Page register to 0x2000 + cpu.Y = 0x02; // Y register + std::vector data = {0xD7, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2010, {0x05, 0x00, 0x30}); + mock_memory.InsertMemory(0x300005, {0b10101010}); + + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10)); + EXPECT_CALL(mock_memory, ReadWordLong(0x2010)).WillOnce(Return(0x300005)); + EXPECT_CALL(mock_memory, ReadByte(0x300007)).WillOnce(Return(0b10101010)); + + // Execute the CMP Direct Page Indirect Long Indexed Y instruction + cpu.ExecuteInstruction(0xD7); + + // Check the status flags + EXPECT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set + EXPECT_FALSE(cpu.GetZeroFlag()); // Zero flag should not be set + EXPECT_FALSE(cpu.GetNegativeFlag()); // Negative flag should be set +} + +TEST_F(CPUTest, CMP_AbsoluteIndexedY) { + // Set the accumulator to 16-bit mode + cpu.SetAccumulatorSize(false); + cpu.A = 0x8000; // Set the accumulator to 0x8000 + cpu.Y = 0x02; // Set the Y register to 0x02 + mock_memory.InsertMemory(0x0000, {0xD9}); + + // Execute the CMP Absolute Indexed Y instruction + cpu.ExecuteInstruction(0xD9); + + // Check the status flags + EXPECT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set + EXPECT_FALSE(cpu.GetZeroFlag()); // Zero flag should not be set + EXPECT_TRUE(cpu.GetNegativeFlag()); // Negative flag should be set +} + +TEST_F(CPUTest, CMP_AbsoluteIndexedX) { + // Set the accumulator to 16-bit mode + cpu.SetAccumulatorSize(false); + cpu.A = 0x8000; // Set the accumulator to 0x8000 + cpu.X = 0x02; // Set the X register to 0x02 + mock_memory.InsertMemory(0x0000, {0xDD}); + + // Execute the CMP Absolute Indexed X instruction + cpu.ExecuteInstruction(0xDD); + + // Check the status flags + EXPECT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set + EXPECT_FALSE(cpu.GetZeroFlag()); // Zero flag should not be set + EXPECT_TRUE(cpu.GetNegativeFlag()); // Negative flag should be set +} + +TEST_F(CPUTest, CMP_AbsoluteLongIndexedX) { + // Set the accumulator to 16-bit mode + cpu.SetAccumulatorSize(false); + cpu.A = 0x8000; // Set the accumulator to 0x8000 + cpu.X = 0x02; // Set the X register to 0x02 + mock_memory.InsertMemory(0x0000, {0xDF}); + + // Execute the CMP Absolute Long Indexed X instruction + cpu.ExecuteInstruction(0xDF); + + // Check the status flags + EXPECT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set + EXPECT_FALSE(cpu.GetZeroFlag()); // Zero flag should not be set + EXPECT_TRUE(cpu.GetNegativeFlag()); // Negative flag should be set +} + +// ============================================================================ + +TEST_F(CPUTest, COP) { + std::vector data = {0x02}; // COP + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0xFFF4, {0x10, 0x20}); // [0xFFFE] = 0x2010 + + EXPECT_CALL(mock_memory, PushWord(0x0002)); + EXPECT_CALL(mock_memory, PushByte(0x30)); + EXPECT_CALL(mock_memory, ReadWord(0xFFF4)).WillOnce(Return(0x2010)); + + cpu.ExecuteInstruction(0x02); // COP + EXPECT_TRUE(cpu.GetInterruptFlag()); + EXPECT_FALSE(cpu.GetDecimalFlag()); +} + // ============================================================================ // Test for CPX instruction -TEST_F(CPUTest, CPX_CarryFlagSet) { - cpu.X = 0x1000; - cpu.CPX(0x0FFF); - ASSERT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set -} - -TEST_F(CPUTest, CPX_ZeroFlagSet) { +TEST_F(CPUTest, CPX_Immediate_ZeroFlagSet) { cpu.SetIndexSize(false); // Set X register to 16-bit mode cpu.SetAccumulatorSize(false); cpu.X = 0x1234; @@ -827,7 +1369,7 @@ TEST_F(CPUTest, CPX_ZeroFlagSet) { ASSERT_TRUE(cpu.GetZeroFlag()); // Zero flag should be set } -TEST_F(CPUTest, CPX_NegativeFlagSet) { +TEST_F(CPUTest, CPX_Immediate_NegativeFlagSet) { cpu.SetIndexSize(false); // Set X register to 16-bit mode cpu.PC = 0; cpu.X = 0x9000; @@ -837,14 +1379,28 @@ TEST_F(CPUTest, CPX_NegativeFlagSet) { ASSERT_TRUE(cpu.GetNegativeFlag()); // Negative flag should be set } -// Test for CPY instruction -TEST_F(CPUTest, CPY_CarryFlagSet) { - cpu.Y = 0x1000; - cpu.CPY(0x0FFF); +// Test for CPX instruction +TEST_F(CPUTest, CPX_DirectPage) { + cpu.SetIndexSize(false); // Set Y register to 16-bit mode + cpu.PC = 0; + cpu.X = 0x1234; + std::vector data = {0xE4, 0x34, 0x12}; // CPY #0x1234 + mock_memory.SetMemoryContents(data); + cpu.ExecuteInstruction(0xE4); // Immediate CPY ASSERT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set } -TEST_F(CPUTest, CPY_ZeroFlagSet) { +TEST_F(CPUTest, CPX_Absolute) { + cpu.SetIndexSize(false); // Set Y register to 16-bit mode + cpu.PC = 0; + cpu.X = 0x1234; + std::vector data = {0xEC, 0x34, 0x12}; // CPY #0x1234 + mock_memory.SetMemoryContents(data); + cpu.ExecuteInstruction(0xEC); // Immediate CPY + ASSERT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set +} + +TEST_F(CPUTest, CPY_Immediate_ZeroFlagSet) { cpu.SetIndexSize(false); // Set Y register to 16-bit mode cpu.SetAccumulatorSize(false); cpu.Y = 0x5678; @@ -854,7 +1410,7 @@ TEST_F(CPUTest, CPY_ZeroFlagSet) { ASSERT_TRUE(cpu.GetZeroFlag()); // Zero flag should be set } -TEST_F(CPUTest, CPY_NegativeFlagSet) { +TEST_F(CPUTest, CPY_Immediate_NegativeFlagSet) { cpu.SetIndexSize(false); // Set Y register to 16-bit mode cpu.PC = 0; cpu.Y = 0x9000; @@ -864,10 +1420,96 @@ TEST_F(CPUTest, CPY_NegativeFlagSet) { ASSERT_TRUE(cpu.GetNegativeFlag()); // Negative flag should be set } +// Test for CPY instruction +TEST_F(CPUTest, CPY_DirectPage) { + cpu.SetIndexSize(false); // Set Y register to 16-bit mode + cpu.PC = 0; + cpu.Y = 0x1234; + std::vector data = {0xC4, 0x34, 0x12}; // CPY #0x1234 + mock_memory.SetMemoryContents(data); + cpu.ExecuteInstruction(0xC4); // Immediate CPY + ASSERT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set +} + +TEST_F(CPUTest, CPY_Absolute) { + cpu.SetIndexSize(false); // Set Y register to 16-bit mode + cpu.PC = 0; + cpu.Y = 0x1234; + std::vector data = {0xCC, 0x34, 0x12}; // CPY #0x1234 + mock_memory.SetMemoryContents(data); + cpu.ExecuteInstruction(0xCC); // Immediate CPY + ASSERT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set +} + // ============================================================================ // DEC - Decrement Memory +TEST_F(CPUTest, DEC_Accumulator) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x02; // Set A register to 2 + cpu.ExecuteInstruction(0x3A); // Execute DEC instruction + EXPECT_EQ(0x01, cpu.A); // Expected value of A register after decrementing + + cpu.A = 0x00; // Set A register to 0 + cpu.ExecuteInstruction(0x3A); // Execute DEC instruction + EXPECT_EQ(0xFF, cpu.A); // Expected value of A register after decrementing + + cpu.A = 0x80; // Set A register to 128 + cpu.ExecuteInstruction(0x3A); // Execute DEC instruction + EXPECT_EQ(0x7F, cpu.A); // Expected value of A register after decrementing +} + +TEST_F(CPUTest, DEC_DirectPage) { + cpu.status = 0xFF; // Set A register to 8-bit mode + cpu.D = 0x1000; // Set Direct Page register to 0x1000 + std::vector data = {0xC6, 0x7F}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x107F, {0x02}); // [0x107F] = 0x02 + + cpu.ExecuteInstruction(0xC6); // Execute DEC instruction + EXPECT_EQ(0x01, mock_memory.ReadByte(0x107F)); // Expected value of memory + // location after decrementing +} + +TEST_F(CPUTest, DEC_Absolute) { + cpu.status = 0xFF; // Set A register to 8-bit mode + std::vector data = {0xCE, 0x00, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x1000, {0x02}); // [0x1000] = 0x02 + + cpu.ExecuteInstruction(0xCE); // Execute DEC instruction + EXPECT_EQ(0x01, mock_memory.ReadByte(0x1000)); // Expected value of memory + // location after decrementing +} + +TEST_F(CPUTest, DEC_DirectPageIndexedX) { + cpu.status = 0xFF; // Set A register to 8-bit mode + cpu.D = 0x1000; // Set Direct Page register to 0x1000 + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0xD6, 0x7F}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x1081, {0x02}); // [0x1081] = 0x02 + + cpu.ExecuteInstruction(0xD6); // Execute DEC instruction + EXPECT_EQ(0x01, mock_memory.ReadByte(0x1081)); // Expected value of memory + // location after decrementing +} + +TEST_F(CPUTest, DEC_AbsoluteIndexedX) { + cpu.status = 0xFF; // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0xDE, 0x00, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x1002, {0x02}); // [0x1002] = 0x02 + + cpu.ExecuteInstruction(0xDE); // Execute DEC instruction + EXPECT_EQ(0x01, mock_memory.ReadByte(0x1002)); // Expected value of memory + // location after decrementing +} + +// ============================================================================ // Test for DEX instruction + TEST_F(CPUTest, DEX) { cpu.SetIndexSize(true); // Set X register to 8-bit mode cpu.X = 0x02; // Set X register to 2 @@ -883,7 +1525,9 @@ TEST_F(CPUTest, DEX) { EXPECT_EQ(0x7F, cpu.X); // Expected value of X register after decrementing } +// ============================================================================ // Test for DEY instruction + TEST_F(CPUTest, DEY) { cpu.SetIndexSize(true); // Set Y register to 8-bit mode cpu.Y = 0x02; // Set Y register to 2 @@ -899,18 +1543,9 @@ TEST_F(CPUTest, DEY) { EXPECT_EQ(0x7F, cpu.Y); // Expected value of Y register after decrementing } +// ============================================================================ // EOR -TEST_F(CPUTest, EOR_Immediate_8bit) { - cpu.A = 0b10101010; // A register - cpu.status = 0xFF; // 8-bit mode - std::vector data = {0x49, 0b01010101}; - mock_memory.SetMemoryContents(data); - - cpu.ExecuteInstruction(0x49); // EOR Immediate - EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 -} - TEST_F(CPUTest, EOR_DirectPageIndexedIndirectX) { cpu.A = 0b10101010; // A register cpu.X = 0x02; // X register @@ -924,6 +1559,22 @@ TEST_F(CPUTest, EOR_DirectPageIndexedIndirectX) { EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 } +TEST_F(CPUTest, EOR_StackRelative) { + cpu.A = 0b10101010; // A register + cpu.status = 0xFF; // 8-bit mode + cpu.SetSP(0x01FF); // Set Stack Pointer to 0x01FF + std::vector data = {0x43, 0x02}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0201, {0b01010101}); // [0x0201] = 0b01010101 + + EXPECT_CALL(mock_memory, SP()).WillRepeatedly(Return(0x01FF)); + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); + EXPECT_CALL(mock_memory, ReadByte(0x0201)).WillOnce(Return(0b01010101)); + + cpu.ExecuteInstruction(0x43); // EOR Stack Relative + EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 +} + TEST_F(CPUTest, EOR_DirectPage) { cpu.A = 0b10101010; // A register cpu.status = 0xFF; // 8-bit mode @@ -947,6 +1598,16 @@ TEST_F(CPUTest, EOR_DirectPageIndirectLong) { EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 } +TEST_F(CPUTest, EOR_Immediate_8bit) { + cpu.A = 0b10101010; // A register + cpu.status = 0xFF; // 8-bit mode + std::vector data = {0x49, 0b01010101}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x49); // EOR Immediate + EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 +} + TEST_F(CPUTest, EOR_Absolute) { cpu.A = 0b10101010; // A register cpu.status = 0xFF; // 8-bit mode @@ -969,19 +1630,6 @@ TEST_F(CPUTest, EOR_AbsoluteLong) { EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 } -TEST_F(CPUTest, EOR_DirectPageIndirectLongIndexedY) { - cpu.A = 0b10101010; // A register - cpu.Y = 0x02; // Y register - cpu.status = 0xFF; // 8-bit mode - std::vector data = {0x51, 0x7E}; - mock_memory.SetMemoryContents(data); - mock_memory.InsertMemory(0x007E, {0x00, 0x10, 0x00}); // [0x007E] = 0x1000 - mock_memory.InsertMemory(0x1002, {0b01010101}); // [0x1002] = 0b01010101 - - cpu.ExecuteInstruction(0x51); // EOR DP Indirect Long Indexed, Y - EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 -} - TEST_F(CPUTest, EOR_DirectPageIndirectIndexedY) { cpu.A = 0b10101010; // A register cpu.Y = 0x02; // Y register @@ -995,31 +1643,58 @@ TEST_F(CPUTest, EOR_DirectPageIndirectIndexedY) { EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 } -// TEST_F(CPUTest, EOR_DirectPageIndirectIndexedYLong) { -// cpu.A = 0b10101010; // A register -// cpu.Y = 0x02; // Y register -// cpu.status = 0xFF; // 8-bit mode -// // PC register -// std::vector data = {0x57, 0x7C}; -// mock_memory.SetMemoryContents(data); -// mock_memory.InsertMemory(0x007E, {0x00, 0x10, 0x00}); // [0x007E] = 0x1000 -// mock_memory.InsertMemory(0x1002, {0b01010101}); // [0x1002] = -// 0b01010101 +TEST_F(CPUTest, EOR_DirectPageIndirect) { + cpu.A = 0b10101010; // A register + cpu.status = 0xFF; // 8-bit mode + std::vector data = {0x52, 0x7E}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x007E, {0x00, 0x10}); // [0x007E] = 0x1000 + mock_memory.InsertMemory(0x1000, {0b01010101}); // [0x1000] = 0b01010101 -// cpu.ExecuteInstruction(0x57); // EOR DP Indirect Long Indexed, Y -// EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 -// } + cpu.ExecuteInstruction(0x52); // EOR DP Indirect + EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 +} -TEST_F(CPUTest, EOR_AbsoluteIndexedX) { +TEST_F(CPUTest, EOR_StackRelativeIndirectIndexedY) { + cpu.A = 0b10101010; // A register + cpu.Y = 0x02; // Y register + cpu.status = 0xFF; // 8-bit mode + std::vector data = {0x53, 0x02}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0201, {0x00, 0x10}); // [0x0201] = 0x1000 + mock_memory.InsertMemory(0x1002, {0b01010101}); // [0x1002] = 0b01010101 + + EXPECT_CALL(mock_memory, SP()).WillRepeatedly(Return(0x01FF)); + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); + EXPECT_CALL(mock_memory, ReadWord(0x0201)).WillOnce(Return(0x1000)); + EXPECT_CALL(mock_memory, ReadByte(0x1002)).WillOnce(Return(0b01010101)); + + cpu.ExecuteInstruction(0x53); // EOR Stack Relative Indirect Indexed, Y + EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 +} + +TEST_F(CPUTest, EOR_DirectPageIndexedX) { cpu.A = 0b10101010; // A register cpu.X = 0x02; // X register cpu.status = 0xFF; // 8-bit mode - // PC register - std::vector data = {0x5D, 0x7C, 0x00}; + std::vector data = {0x55, 0x7E}; mock_memory.SetMemoryContents(data); - mock_memory.InsertMemory(0x007E, {0b01010101}); // [0x007E] = 0b01010101 + mock_memory.InsertMemory(0x0080, {0b01010101}); // [0x0080] = 0b01010101 - cpu.ExecuteInstruction(0x5D); // EOR Absolute Indexed, X + cpu.ExecuteInstruction(0x55); // EOR DP Indexed, X + EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 +} + +TEST_F(CPUTest, EOR_DirectPageIndirectLongIndexedY) { + cpu.A = 0b10101010; // A register + cpu.Y = 0x02; // Y register + cpu.status = 0xFF; // 8-bit mode + std::vector data = {0x51, 0x7E}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x007E, {0x00, 0x10, 0x00}); // [0x007E] = 0x1000 + mock_memory.InsertMemory(0x1002, {0b01010101}); // [0x1002] = 0b01010101 + + cpu.ExecuteInstruction(0x51); // EOR DP Indirect Long Indexed, Y EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 } @@ -1036,6 +1711,19 @@ TEST_F(CPUTest, EOR_AbsoluteIndexedY) { EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 } +TEST_F(CPUTest, EOR_AbsoluteIndexedX) { + cpu.A = 0b10101010; // A register + cpu.X = 0x02; // X register + cpu.status = 0xFF; // 8-bit mode + // PC register + std::vector data = {0x5D, 0x7C, 0x00}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x007E, {0b01010101}); // [0x007E] = 0b01010101 + + cpu.ExecuteInstruction(0x5D); // EOR Absolute Indexed, X + EXPECT_EQ(cpu.A, 0b11111111); // A register should now be 0b11111111 +} + TEST_F(CPUTest, EOR_AbsoluteLongIndexedX) { cpu.A = 0b10101010; // A register cpu.X = 0x02; // X register @@ -1052,6 +1740,21 @@ TEST_F(CPUTest, EOR_AbsoluteLongIndexedX) { // ============================================================================ // INC - Increment Memory +TEST_F(CPUTest, INC_Accumulator) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x02; // Set A register to 2 + cpu.ExecuteInstruction(0x1A); // Execute INC instruction + EXPECT_EQ(0x03, cpu.A); // Expected value of A register after incrementing + + cpu.A = 0xFF; // Set A register to 0xFF + cpu.ExecuteInstruction(0x1A); // Execute INC instruction + EXPECT_EQ(0x00, cpu.A); // Expected value of A register after incrementing + + cpu.A = 0x7F; // Set A register to 127 + cpu.ExecuteInstruction(0x1A); // Execute INC instruction + EXPECT_EQ(0x80, cpu.A); // Expected value of A register after incrementing +} + TEST_F(CPUTest, INC_DirectPage_8bit) { cpu.SetAccumulatorSize(true); cpu.D = 0x0200; // Setting Direct Page register to 0x0200 @@ -1174,7 +1877,6 @@ TEST_F(CPUTest, INY) { // ============================================================================ // JMP - Jump to new location -// ============================================================================ TEST_F(CPUTest, JMP_Absolute) { std::vector data = {0x4C, 0x05, 0x20}; // JMP $2005 @@ -1201,7 +1903,6 @@ TEST_F(CPUTest, JMP_Indirect) { // ============================================================================ // JML - Jump Long -// ============================================================================ TEST_F(CPUTest, JML_AbsoluteLong) { cpu.E = 0; @@ -1217,9 +1918,34 @@ TEST_F(CPUTest, JML_AbsoluteLong) { EXPECT_EQ(cpu.PB, 0x03); // The PBR should be updated to 0x03 } +TEST_F(CPUTest, JMP_AbsoluteIndexedIndirectX) { + cpu.X = 0x02; + std::vector data = {0x7C, 0x05, 0x20, 0x00}; // JMP ($2005, X) + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2007, {0x30, 0x05}); // [0x2007] = 0x0530 + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x2005)); + EXPECT_CALL(mock_memory, ReadWord(0x2007)).WillOnce(Return(0x3005)); + + cpu.ExecuteInstruction(0x7C); // JMP Absolute Indexed Indirect + EXPECT_EQ(cpu.PC, 0x3005); +} + +TEST_F(CPUTest, JMP_AbsoluteIndirectLong) { + std::vector data = {0xDC, 0x05, 0x20, 0x00}; // JMP [$2005] + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2005, {0x01, 0x30, 0x05}); // [0x2005] = 0x0530 + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x2005)); + EXPECT_CALL(mock_memory, ReadWordLong(0x2005)).WillOnce(Return(0x013005)); + + cpu.ExecuteInstruction(0xDC); // JMP Absolute Indirect Long + EXPECT_EQ(cpu.PC, 0x3005); + EXPECT_EQ(cpu.PB, 0x01); +} + // ============================================================================ // JSR - Jump to Subroutine -// ============================================================================ TEST_F(CPUTest, JSR_Absolute) { std::vector data = {0x20, 0x05, 0x20}; // JSR $2005 @@ -1233,12 +1959,11 @@ TEST_F(CPUTest, JSR_Absolute) { // Continue executing some code cpu.ExecuteInstruction(0x60); // RTS - EXPECT_EQ(cpu.PC, 0x0003); + EXPECT_EQ(cpu.PC, 0x0000); } // ============================================================================ // JSL - Jump to Subroutine Long -// ============================================================================ TEST_F(CPUTest, JSL_AbsoluteLong) { std::vector data = {0x22, 0x05, 0x20, 0x00}; // JSL $002005 @@ -1251,9 +1976,93 @@ TEST_F(CPUTest, JSL_AbsoluteLong) { EXPECT_EQ(cpu.PC, 0x002005); } +TEST_F(CPUTest, JSL_AbsoluteIndexedIndirect) { + cpu.X = 0x02; + std::vector data = {0xFC, 0x05, 0x20, 0x00}; // JSL $002005 + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2007, {0x00, 0x20, 0x00}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x2005)); + EXPECT_CALL(mock_memory, ReadWord(0x2007)).WillOnce(Return(0x002000)); + + cpu.ExecuteInstruction(0xFC); // JSL Absolute Long + EXPECT_EQ(cpu.PC, 0x2000); +} + // ============================================================================ // LDA - Load Accumulator +TEST_F(CPUTest, LDA_DirectPageIndexedIndirectX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + cpu.D = 0x0200; // Set Direct Page register to 0x0200 + std::vector data = {0xA1, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023E, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00023E)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1000)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xA1); // LDA Direct Page Indexed Indirect, X + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_StackRelative) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0xFF; // 8-bit mode + cpu.SetSP(0x01FF); // Set Stack Pointer to 0x01FF + std::vector data = {0xA3, 0x02}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0201, {0x7F}); + + EXPECT_CALL(mock_memory, SP()).WillRepeatedly(Return(0x01FF)); + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); + EXPECT_CALL(mock_memory, ReadByte(0x0201)).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0xA3); // LDA Stack Relative + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_DirectPage) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.D = 0x0200; + std::vector data = {0xA5, 0x3C, 0x00}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023C, {0x80}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadByte(0x00023C)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xA5); // LDA Direct Page + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_DirectPageIndirectLong) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.D = 0x0200; + std::vector data = {0xA7, 0x3C, 0x00}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023C, {0x00, 0x10, 0x00}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWordLong(0x00023C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1000)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xA7); // LDA Direct Page Indirect Long + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + TEST_F(CPUTest, LDA_Immediate_8bit) { cpu.SetAccumulatorSize(true); std::vector data = {0xA9, 0xFF}; @@ -1276,24 +2085,8 @@ TEST_F(CPUTest, LDA_Immediate_16bit) { EXPECT_FALSE(cpu.GetZeroFlag()); } -TEST_F(CPUTest, LDA_DirectPage) { - cpu.SetAccumulatorSize(true); - cpu.D = 0x0200; - std::vector data = {0xA5, 0x3C, 0x00}; - mock_memory.SetMemoryContents(data); - mock_memory.InsertMemory(0x00023C, {0x80}); - - EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); - EXPECT_CALL(mock_memory, ReadByte(0x00023C)).WillOnce(Return(0x80)); - - cpu.ExecuteInstruction(0xA5); // LDA Direct Page - EXPECT_EQ(cpu.A, 0x80); - EXPECT_TRUE(cpu.GetNegativeFlag()); - EXPECT_FALSE(cpu.GetZeroFlag()); -} - TEST_F(CPUTest, LDA_Absolute) { - cpu.SetAccumulatorSize(true); + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode std::vector data = {0xAD, 0x7F, 0xFF}; mock_memory.SetMemoryContents(data); mock_memory.InsertMemory(0x7FFF, {0x7F}); @@ -1308,9 +2101,875 @@ TEST_F(CPUTest, LDA_Absolute) { EXPECT_FALSE(cpu.GetZeroFlag()); } +TEST_F(CPUTest, LDA_AbsoluteLong) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0xAF, 0x7F, 0xFF, 0x00}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFF, {0x7F}); + + EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x7FFF)); + + EXPECT_CALL(mock_memory, ReadByte(0x7FFF)).WillOnce(Return(0x7F)); + + cpu.SetAccumulatorSize(true); + cpu.ExecuteInstruction(0xAF); // LDA Absolute Long + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_DirectPageIndirectIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + cpu.D = 0x0200; // Set Direct Page register to 0x0200 + std::vector data = {0xB1, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023C, {0x00, 0x10}); + mock_memory.InsertMemory(0x1002, {0x80}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00023C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1002)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xB1); // LDA Direct Page Indirect Indexed, Y + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_DirectPageIndirect) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.D = 0x0200; + std::vector data = {0xA1, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023C, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00023C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1000)).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0xB2); // LDA Direct Page Indirect + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_StackRelativeIndirectIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + cpu.status = 0xFF; // 8-bit mode + std::vector data = {0xB3, 0x02}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0201, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, SP()).WillRepeatedly(Return(0x01FF)); + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); + EXPECT_CALL(mock_memory, ReadWord(0x0201)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1002)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xB3); // LDA Stack Relative Indirect Indexed, Y + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_DirectPageIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + cpu.D = 0x0200; // Set Direct Page register to 0x0200 + std::vector data = {0xB5, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023E, {0x7F}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadByte(0x00023E)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xB5); // LDA Direct Page Indexed, X + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_DirectPageIndirectLongIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + cpu.D = 0x0200; // Set Direct Page register to 0x0200 + std::vector data = {0xB7, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023C, {0x00, 0x10, 0x00}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWordLong(0x00023C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1002)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xB7); // LDA Direct Page Indirect Long Indexed, Y + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_AbsoluteIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + std::vector data = {0xB9, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x80}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + EXPECT_CALL(mock_memory, ReadByte(0x8001)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xB9); // LDA Absolute Indexed, Y + EXPECT_EQ(cpu.A, 0x80); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_AbsoluteIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0xBD, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x80}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + EXPECT_CALL(mock_memory, ReadByte(0x8001)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xBD); // LDA Absolute Indexed, X + EXPECT_EQ(cpu.A, 0x80); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_AbsoluteLongIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0xBF, 0x7F, 0xFF, 0x00}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x80}); + + EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x7FFF)); + + EXPECT_CALL(mock_memory, ReadByte(0x8001)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xBF); // LDA Absolute Long Indexed, X + EXPECT_EQ(cpu.A, 0x80); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +// ============================================================================ + +TEST_F(CPUTest, LDX_Immediate) { + cpu.SetIndexSize(true); // Set X register to 8-bit mode + std::vector data = {0xA2, 0x42}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0xA2); // LDX Immediate + EXPECT_EQ(cpu.X, 0x42); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDX_DirectPage) { + cpu.SetIndexSize(true); // Set X register to 8-bit mode + std::vector data = {0xA6, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0080, {0x42}); + + cpu.ExecuteInstruction(0xA6); // LDX Direct Page + EXPECT_EQ(cpu.X, 0x42); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDX_Absolute) { + cpu.SetIndexSize(true); // Set X register to 8-bit mode + std::vector data = {0xAE, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFF, {0x42}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0xAE); // LDX Absolute + EXPECT_EQ(cpu.X, 0x42); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDX_DirectPageIndexedY) { + cpu.SetIndexSize(true); // Set X register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + std::vector data = {0xB6, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0082, {0x42}); + + cpu.ExecuteInstruction(0xB6); // LDX Direct Page Indexed, Y + EXPECT_EQ(cpu.X, 0x42); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDX_AbsoluteIndexedY) { + cpu.SetIndexSize(true); // Set X register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + std::vector data = {0xBE, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x42}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0xBE); // LDX Absolute Indexed, Y + EXPECT_EQ(cpu.X, 0x42); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +// ============================================================================ + +TEST_F(CPUTest, LDY_Immediate) { + cpu.SetIndexSize(true); // Set Y register to 8-bit mode + std::vector data = {0xA0, 0x42}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0xA0); // LDY Immediate + EXPECT_EQ(cpu.Y, 0x42); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDY_DirectPage) { + cpu.SetIndexSize(true); // Set Y register to 8-bit mode + std::vector data = {0xA4, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0080, {0x42}); + + cpu.ExecuteInstruction(0xA4); // LDY Direct Page + EXPECT_EQ(cpu.Y, 0x42); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDY_Absolute) { + cpu.SetIndexSize(true); // Set Y register to 8-bit mode + std::vector data = {0xAC, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFF, {0x42}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0xAC); // LDY Absolute + EXPECT_EQ(cpu.Y, 0x42); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDY_DirectPageIndexedX) { + cpu.SetIndexSize(true); // Set Y register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0xB4, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0082, {0x42}); + + cpu.ExecuteInstruction(0xB4); // LDY Direct Page Indexed, X + EXPECT_EQ(cpu.Y, 0x42); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDY_AbsoluteIndexedX) { + cpu.SetIndexSize(true); // Set Y register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0xBC, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x42}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0xBC); // LDY Absolute Indexed, X + EXPECT_EQ(cpu.Y, 0x42); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +// ============================================================================ + +TEST_F(CPUTest, LSR_DirectPage) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0x46, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0080, {0x42}); + + cpu.ExecuteInstruction(0x46); // LSR Direct Page + EXPECT_EQ(mock_memory[0x0080], 0x21); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LSR_Accumulator) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.ExecuteInstruction(0x4A); // LSR Accumulator + EXPECT_EQ(cpu.A, 0x21); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LSR_Absolute) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0x4E, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFF, {0x42}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0x4E); // LSR Absolute + EXPECT_EQ(mock_memory[0x7FFF], 0x21); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LSR_DirectPageIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x56, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0082, {0x42}); + + cpu.ExecuteInstruction(0x56); // LSR Direct Page Indexed, X + EXPECT_EQ(mock_memory[0x0082], 0x21); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LSR_AbsoluteIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x5E, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x42}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0x5E); // LSR Absolute Indexed, X + EXPECT_EQ(mock_memory[0x8001], 0x21); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + // ============================================================================ // Stack Tests +TEST_F(CPUTest, ORA_DirectPageIndexedIndirectX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + cpu.D = 0x0200; // Set Direct Page register to 0x0200 + std::vector data = {0x01, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023E, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00023E)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1000)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0x01); // ORA Direct Page Indexed Indirect, X + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_StackRelative) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0xFF; // 8-bit mode + cpu.SetSP(0x01FF); // Set Stack Pointer to 0x01FF + std::vector data = {0x03, 0x02}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0201, {0x7F}); + + EXPECT_CALL(mock_memory, SP()).WillRepeatedly(Return(0x01FF)); + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); + EXPECT_CALL(mock_memory, ReadByte(0x0201)).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0x03); // ORA Stack Relative + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_DirectPage) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.D = 0x0200; + std::vector data = {0x05, 0x3C, 0x00}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023C, {0x80}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadByte(0x00023C)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0x05); // ORA Direct Page + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_DirectPageIndirectLong) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.D = 0x0200; + std::vector data = {0x07, 0x3C, 0x00}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023C, {0x00, 0x10, 0x00}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWordLong(0x00023C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1000)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0x07); // ORA Direct Page Indirect Long + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_Immediate) { + cpu.SetAccumulatorSize(true); + std::vector data = {0x09, 0xFF}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x09); // ORA Immediate + EXPECT_EQ(cpu.A, 0xFF); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_Absolute) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0x0D, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFF, {0x7F}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + EXPECT_CALL(mock_memory, ReadByte(0x7FFF)).WillOnce(Return(0x7F)); + + cpu.SetAccumulatorSize(true); + cpu.ExecuteInstruction(0x0D); // ORA Absolute + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_AbsoluteLong) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0x0F, 0x7F, 0xFF, 0x00}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFF, {0x7F}); + + EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x7FFF)); + + EXPECT_CALL(mock_memory, ReadByte(0x7FFF)).WillOnce(Return(0x7F)); + + cpu.SetAccumulatorSize(true); + cpu.ExecuteInstruction(0x0F); // ORA Absolute Long + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_DirectPageIndirectIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + cpu.D = 0x0200; // Set Direct Page register to 0x0200 + std::vector data = {0x11, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023C, {0x00, 0x10}); + mock_memory.InsertMemory(0x1002, {0x80}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00023C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1002)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0x11); // ORA Direct Page Indirect Indexed, Y + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_DirectPageIndirect) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.D = 0x0200; + std::vector data = {0x12, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023C, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00023C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1000)).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0x12); // ORA Direct Page Indirect + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_StackRelativeIndirectIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + cpu.status = 0xFF; // 8-bit mode + std::vector data = {0x13, 0x02}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0201, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, SP()).WillRepeatedly(Return(0x01FF)); + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x02)); + EXPECT_CALL(mock_memory, ReadWord(0x0201)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1002)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0x13); // ORA Stack Relative Indirect Indexed, Y + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_DirectPageIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + cpu.D = 0x0200; // Set Direct Page register to 0x0200 + std::vector data = {0x15, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023E, {0x80}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadByte(0x00023E)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0x15); // ORA Direct Page Indexed, X + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_DirectPageIndirectLongIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + cpu.D = 0x0200; // Set Direct Page register to 0x0200 + std::vector data = {0x17, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023C, {0x00, 0x10, 0x00}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWordLong(0x00023C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1002)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0x17); // ORA Direct Page Indirect Long Indexed, Y + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_AbsoluteIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + std::vector data = {0x19, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x7F}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + EXPECT_CALL(mock_memory, ReadByte(0x8001)).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0x19); // ORA Absolute Indexed, Y + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_AbsoluteIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x1D, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x7F}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + EXPECT_CALL(mock_memory, ReadByte(0x8001)).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0x1D); // ORA Absolute Indexed, X + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ORA_AbsoluteLongIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x1F, 0x7F, 0xFF, 0x00}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x7F}); + + EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x7FFF)); + + EXPECT_CALL(mock_memory, ReadByte(0x8001)).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0x1F); // ORA Absolute Long Indexed, X + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +// ============================================================================ + +TEST_F(CPUTest, PEA) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0xF4, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + EXPECT_CALL(mock_memory, PushWord(0x7FFF)); + + cpu.ExecuteInstruction(0xF4); // PEA +} + +TEST_F(CPUTest, PEI) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0xD4, 0x3C, 0x00}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00003C, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, ReadWord(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00003C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, PushWord(0x1000)); + + cpu.ExecuteInstruction(0xD4); // PEI +} + +TEST_F(CPUTest, PER) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0x62, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + EXPECT_CALL(mock_memory, PushWord(0x7FFF)); + + cpu.ExecuteInstruction(0x62); // PER +} + +TEST_F(CPUTest, PHD) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.D = 0x7FFF; + std::vector data = {0x0B}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, PushWord(0x7FFF)); + + cpu.ExecuteInstruction(0x0B); // PHD +} + +TEST_F(CPUTest, PHK) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.PB = 0x7F; + std::vector data = {0x4B}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, PushByte(0x7F)); + + cpu.ExecuteInstruction(0x4B); // PHK +} + +TEST_F(CPUTest, PHP) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0x7F; + std::vector data = {0x08}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, PushByte(0x7F)); + + cpu.ExecuteInstruction(0x08); // PHP +} + +TEST_F(CPUTest, PHX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x7F; + std::vector data = {0xDA}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, PushByte(0x7F)); + + cpu.ExecuteInstruction(0xDA); // PHX +} + +TEST_F(CPUTest, PHY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x7F; + std::vector data = {0x5A}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, PushByte(0x7F)); + + cpu.ExecuteInstruction(0x5A); // PHY +} + +TEST_F(CPUTest, PHB) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.DB = 0x7F; + std::vector data = {0x8B}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, PushByte(0x7F)); + + cpu.ExecuteInstruction(0x8B); // PHB +} + +TEST_F(CPUTest, PHA) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x7F; + std::vector data = {0x48}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, PushByte(0x7F)); + + cpu.ExecuteInstruction(0x48); // PHA +} + +TEST_F(CPUTest, PHA_16Bit) { + cpu.SetAccumulatorSize(false); // Set A register to 16-bit mode + cpu.A = 0x7FFF; + std::vector data = {0x48}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, PushWord(0x7FFF)); + + cpu.ExecuteInstruction(0x48); // PHA +} + +TEST_F(CPUTest, PLA) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x00; + std::vector data = {0x68}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0001, {0x7F}); + + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0x68); // PLA + EXPECT_EQ(cpu.A, 0x7F); +} + +TEST_F(CPUTest, PLA_16Bit) { + cpu.SetAccumulatorSize(false); // Set A register to 16-bit mode + cpu.A = 0x0000; + std::vector data = {0x68}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0001, {0x7F, 0xFF}); + + EXPECT_CALL(mock_memory, PopWord()).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0x68); // PLA + EXPECT_EQ(cpu.A, 0x7FFF); +} + +TEST_F(CPUTest, PLB) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.DB = 0x00; + std::vector data = {0xAB}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0001, {0x7F}); + + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0xAB); // PLB + EXPECT_EQ(cpu.DB, 0x7F); +} + +TEST_F(CPUTest, PLD) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.D = 0x0000; + std::vector data = {0x2B}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0001, {0x7F, 0xFF}); + + EXPECT_CALL(mock_memory, PopWord()).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0x2B); // PLD + EXPECT_EQ(cpu.D, 0x7FFF); +} + +TEST_F(CPUTest, PLP) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0x00; + std::vector data = {0x28}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0001, {0x7F}); + + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0x28); // PLP + EXPECT_EQ(cpu.status, 0x7F); +} + +TEST_F(CPUTest, PLX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x00; + std::vector data = {0xFA}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0001, {0x7F}); + + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0xFA); // PLX + EXPECT_EQ(cpu.X, 0x7F); +} + +TEST_F(CPUTest, PLX_16Bit) { + cpu.SetIndexSize(false); // Set A register to 16-bit mode + cpu.X = 0x0000; + + std::vector data = {0xFA}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x01FF, {0x7F, 0xFF}); + + EXPECT_CALL(mock_memory, PopWord()).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0xFA); // PLX + EXPECT_EQ(cpu.X, 0x7FFF); +} + +TEST_F(CPUTest, PLY) { + cpu.SetIndexSize(true); // Set A register to 8-bit mode + cpu.Y = 0x00; + std::vector data = {0x7A}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0001, {0x7F}); + + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0x7A); // PLY + EXPECT_EQ(cpu.Y, 0x7F); +} + +TEST_F(CPUTest, PLY_16Bit) { + cpu.SetIndexSize(false); // Set A register to 16-bit mode + cpu.Y = 0x0000; + std::vector data = {0x7A}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0001, {0x7F, 0xFF}); + + EXPECT_CALL(mock_memory, PopWord()).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0x7A); // PLY + EXPECT_EQ(cpu.Y, 0x7FFF); +} + +// ============================================================================ +// REP - Reset Processor Status Bits + +TEST_F(CPUTest, REP) { + cpu.status = 0xFF; + std::vector data = {0xC2, 0x30}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0xC2); // REP + EXPECT_EQ(cpu.status, 0xCF); // 11001111 +} + +TEST_F(CPUTest, REP_16Bit) { + cpu.status = 0xFF; + std::vector data = {0xC2, 0x30}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0xC2); // REP + EXPECT_EQ(cpu.status, 0xCF); // 00111111 +} + TEST_F(CPUTest, PHA_PLA_Ok) { cpu.A = 0x42; EXPECT_CALL(mock_memory, PushByte(0x42)).WillOnce(Return()); @@ -1431,19 +3090,6 @@ TEST_F(CPUTest, PLD_PullDirectPageRegister) { EXPECT_EQ(cpu.D, 0xBC); } -// ============================================================================ -// REP - Reset Processor Status Bits - -TEST_F(CPUTest, REP) { - cpu.status = 0xFF; // All flags set - std::vector data = {0xC2, 0x30, - 0x00}; // REP #0x30 (clear N & Z flags) - mock_memory.SetMemoryContents(data); - - cpu.ExecuteInstruction(0xC2); // REP - EXPECT_EQ(cpu.status, 0xCF); // 11001111 -} - // ============================================================================ // SEP - Set Processor Status Bits @@ -1458,15 +3104,807 @@ TEST_F(CPUTest, SEP) { } // ============================================================================ -// TXA - Transfer Index X to Accumulator -TEST_F(CPUTest, TXA) { - cpu.X = 0xAB; // X register - std::vector data = {0x8A}; // TXA +TEST_F(CPUTest, ROL_DirectPage) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0x26, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0080, {0x42}); + + cpu.ExecuteInstruction(0x26); // ROL Direct Page + EXPECT_EQ(mock_memory[0x0080], 0x84); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ROL_Accumulator) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.ExecuteInstruction(0x2A); // ROL Accumulator + EXPECT_EQ(cpu.A, 0x84); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ROL_Absolute) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0x2E, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFF, {0x42}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0x2E); // ROL Absolute + EXPECT_EQ(mock_memory[0x7FFF], 0x84); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ROL_DirectPageIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x36, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0082, {0x42}); + + cpu.ExecuteInstruction(0x36); // ROL Direct Page Indexed, X + EXPECT_EQ(mock_memory[0x0082], 0x84); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ROL_AbsoluteIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x3E, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x42}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0x3E); // ROL Absolute Indexed, X + EXPECT_EQ(mock_memory[0x8001], 0x84); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +// ============================================================================ + +TEST_F(CPUTest, ROR_DirectPage) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0x66, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0080, {0x42}); + + cpu.ExecuteInstruction(0x66); // ROR Direct Page + EXPECT_EQ(mock_memory[0x0080], 0x21); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ROR_Accumulator) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.ExecuteInstruction(0x6A); // ROR Accumulator + EXPECT_EQ(cpu.A, 0x21); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ROR_Absolute) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0x6E, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFF, {0x42}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0x6E); // ROR Absolute + EXPECT_EQ(mock_memory[0x7FFF], 0x21); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ROR_DirectPageIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x76, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0082, {0x42}); + + cpu.ExecuteInstruction(0x76); // ROR Direct Page Indexed, X + EXPECT_EQ(mock_memory[0x0082], 0x21); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, ROR_AbsoluteIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x7E, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x42}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0x7E); // ROR Absolute Indexed, X + EXPECT_EQ(mock_memory[0x8001], 0x21); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +// ============================================================================ + +TEST_F(CPUTest, RTI) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0x00; + std::vector data = {0x40}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0001, {0x7F}); + + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x7F)); + + cpu.ExecuteInstruction(0x40); // RTI + EXPECT_EQ(cpu.status, 0x7F); +} + +// ============================================================================ + +TEST_F(CPUTest, RTL) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.PC = 0x0000; + std::vector data = {0x6B}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0001, {0x7F, 0xFF}); + + EXPECT_CALL(mock_memory, PopWord()).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0x6B); // RTL + EXPECT_EQ(cpu.PC, 0x7FFF); +} + +// ============================================================================ + +TEST_F(CPUTest, RTS) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.PC = 0x0000; + std::vector data = {0x60}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0001, {0x7F, 0xFF}); + + EXPECT_CALL(mock_memory, PopWord()).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0x60); // RTS + EXPECT_EQ(cpu.PC, 0x7FFF); +} + +// ============================================================================ + +TEST_F(CPUTest, SBC_DirectPageIndexedIndirectX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + cpu.D = 0x0200; // Set Direct Page register to 0x0200 + cpu.A = 0x10; // Set A register to 0x80 + cpu.status = 0xFF; // 8-bit mode + std::vector data = {0xE1, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023E, {0x80}); + mock_memory.InsertMemory(0x0080, {0x80}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00023E)).WillOnce(Return(0x80)); + EXPECT_CALL(mock_memory, ReadByte(0x0080)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xE1); // SBC Direct Page Indexed Indirect, X + EXPECT_EQ(cpu.A, 0x90); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_StackRelative) { + std::vector data = {0xE3, 0x3C}; + mock_memory.SetMemoryContents(data); + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0xFF; // 8-bit mode + cpu.SetSP(0x01FF); // Set Stack Pointer to 0x01FF + mock_memory.InsertMemory(0x00003E, {0x02}); + mock_memory.InsertMemory(0x2002, {0x80}); + + EXPECT_CALL(mock_memory, SP()).Times(1); + // EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0x3C)); + + cpu.ExecuteInstruction(0xE3); // SBC Stack Relative + EXPECT_EQ(cpu.A, 0x00); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_TRUE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_DirectPage) { + std::vector data = {0xE5, 0x80}; + mock_memory.SetMemoryContents(data); + cpu.D = 0x0100; // Set Direct Page register to 0x0100 + + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0xFF; // 8-bit mode + cpu.A = 0x42; // Set A register to 0x42 + + mock_memory.InsertMemory(0x0180, {0x01}); + + cpu.ExecuteInstruction(0xE5); // SBC Direct Page + EXPECT_EQ(cpu.A, 0x41); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_DirectPageIndirectLong) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0xFF; // 8-bit mode + cpu.A = 0x80; // Set A register to 0x80 + std::vector data = {0xE7, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00003C, {0x00, 0x10, 0x00}); + mock_memory.InsertMemory(0x1000, {0x8F}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWordLong(0x00003C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1000)).WillOnce(Return(0x8F)); + + cpu.ExecuteInstruction(0xE7); // SBC Direct Page Indirect Long + EXPECT_EQ(cpu.A, 0xF1); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_Immediate) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0xFF; // 8-bit mode + cpu.A = 0x80; // Set A register to 0x80 + std::vector data = {0xE9, 0x80}; mock_memory.SetMemoryContents(data); - cpu.ExecuteInstruction(0x8A); // TXA - EXPECT_EQ(cpu.A, 0xAB); // A register should now be equal to X + cpu.ExecuteInstruction(0xE9); // SBC Immediate + EXPECT_EQ(cpu.A, 0x00); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_TRUE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_Absolute) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0xFF; // 8-bit mode + cpu.A = 0xFF; // Set A register to 0x80 + std::vector data = {0xED, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFF, {0x80}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0xED); // SBC Absolute + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_AbsoluteLong) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0xFF; // 8-bit mode + cpu.A = 0xFF; // Set A register to 0x80 + std::vector data = {0xEF, 0x7F, 0xFF, 0xFF, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFFFF, {0x80}); + + EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x7FFFFF)); + + cpu.ExecuteInstruction(0xEF); // SBC Absolute Long + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_DirectPageIndirectIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + cpu.A = 0xFF; // Set A register to 0x80 + cpu.status = 0xFF; // 8-bit mode + std::vector data = {0xF1, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00003E, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00003C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1002)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xF1); // SBC Direct Page Indirect Indexed, Y + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_DirectPageIndirect) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0xFF; // 8-bit mode + cpu.D = 0x0200; // Set Direct Page register to 0x0200 + cpu.A = 0x10; // Set A register to 0x80 + std::vector data = {0xF2, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023C, {0x00, 0x10}); + mock_memory.InsertMemory(0x1000, {0x80}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + + EXPECT_CALL(mock_memory, ReadWord(0x00023C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1000)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xF2); // SBC Direct Page Indirect + EXPECT_EQ(cpu.A, 0x90); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_StackRelativeIndirectIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + cpu.A = 0xFF; // Set A register to 0x80 + cpu.status = 0xFF; // 8-bit mode + cpu.SetSP(0x01FF); // Set Stack Pointer to 0x01FF + std::vector data = {0xF3, 0x02}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0201, {0x00, 0x30}); + mock_memory.InsertMemory(0x3002, {0x80}); + + EXPECT_CALL(mock_memory, SP()).Times(1); + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x02)); + EXPECT_CALL(mock_memory, ReadWord(0x0201)).WillOnce(Return(0x3000)); + EXPECT_CALL(mock_memory, ReadByte(0x3002)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xF3); // SBC Stack Relative Indirect Indexed, Y + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_DirectPageIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + cpu.A = 0x01; + std::vector data = {0xF5, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0082, {0x01}); + + cpu.ExecuteInstruction(0xF5); // SBC Direct Page Indexed, X + EXPECT_EQ(cpu.A, 0xFF); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_DirectPageIndirectLongIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + cpu.status = 0xFF; // 8-bit mode + cpu.A = 0xFF; + std::vector data = {0xF7, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00003C, {0x00, 0x10, 0x00}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWordLong(0x00003C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, ReadByte(0x1002)).WillOnce(Return(0x80)); + + cpu.ExecuteInstruction(0xF7); // SBC Direct Page Indirect Long Indexed, Y + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_AbsoluteIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x02; // Set Y register to 0x02 + cpu.status = 0xFF; // 8-bit mode + cpu.A = 0xFF; + std::vector data = {0xF9, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x80}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0xF9); // SBC Absolute Indexed, Y + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_AbsoluteIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + cpu.status = 0xFF; // 8-bit mode + cpu.A = 0xFF; + std::vector data = {0xFD, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x8001, {0x80}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + + cpu.ExecuteInstruction(0xFD); // SBC Absolute Indexed, X + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, SBC_AbsoluteLongIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + cpu.A = 0xFF; + cpu.status = 0xFF; // 8-bit mode + std::vector data = {0xFF, 0x7F, 0xFF, 0xFF, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x800001, {0x80}); + + EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x7FFFFF)); + + cpu.ExecuteInstruction(0xFF); // SBC Absolute Long Indexed, X + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +// ============================================================================ + +TEST_F(CPUTest, SEC) { + cpu.ExecuteInstruction(0x38); // SEC + EXPECT_TRUE(cpu.GetCarryFlag()); +} + +// ============================================================================ + +TEST_F(CPUTest, SED) { + cpu.ExecuteInstruction(0xF8); // SED + EXPECT_TRUE(cpu.GetDecimalFlag()); +} + +// ============================================================================ + +// SEI - Set Interrupt Disable Status Flag + +TEST_F(CPUTest, SEI) { + cpu.ExecuteInstruction(0x78); // SEI + // EXPECT_TRUE(cpu.GetInterruptDisableFlag()); +} + +// ============================================================================ + +TEST_F(CPUTest, SEP_8Bit) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.status = 0x00; + std::vector data = {0xE2, 0x30}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0xE2); // SEP + EXPECT_EQ(cpu.status, 0x30); // 00110000 +} + +TEST_F(CPUTest, SEP_16Bit) { + cpu.SetAccumulatorSize(false); // Set A register to 16-bit mode + cpu.status = 0x00; + std::vector data = {0xE2, 0x30}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0xE2); // SEP + EXPECT_EQ(cpu.status, 0x30); // 00110000 +} + +// ============================================================================ + +TEST_F(CPUTest, STA_DirectPageIndexedIndirectX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x81, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00003E, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00003E)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, WriteByte(0x1000, 0x42)); + + cpu.ExecuteInstruction(0x81); // STA Direct Page Indexed Indirect, X + EXPECT_EQ(cpu.A, 0x42); +} + +TEST_F(CPUTest, STA_StackRelative) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.SetSP(0x01FF); // Set Stack Pointer to 0x01FF + std::vector data = {0x83, 0x3C}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + + EXPECT_CALL(mock_memory, WriteByte(0x023B, 0x42)); + + cpu.ExecuteInstruction(0x83); // STA Stack Relative +} + +TEST_F(CPUTest, STA_DirectPage) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + std::vector data = {0x85, 0x80}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, WriteByte(0x0080, 0x42)); + + cpu.ExecuteInstruction(0x85); // STA Direct Page +} + +TEST_F(CPUTest, STA_DirectPageIndirectLong) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + std::vector data = {0x87, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00003C, {0x00, 0x10, 0x00}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWordLong(0x00003C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, WriteByte(0x1000, 0x42)); + + cpu.ExecuteInstruction(0x87); // STA Direct Page Indirect Long +} + +TEST_F(CPUTest, STA_Absolute) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + std::vector data = {0x8D, 0xFF, 0x7F}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, WriteByte(0x7FFF, 0x42)); + + cpu.ExecuteInstruction(0x8D); // STA Absolute +} + +TEST_F(CPUTest, STA_AbsoluteLong) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + std::vector data = {0x8F, 0xFF, 0x7F}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, WriteByte(0x7FFF, 0x42)); + + cpu.ExecuteInstruction(0x8F); // STA Absolute Long +} + +TEST_F(CPUTest, STA_DirectPageIndirectIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.Y = 0x02; // Set Y register to 0x02 + std::vector data = {0x91, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00003E, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00003C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, WriteByte(0x1002, 0x42)); + + cpu.ExecuteInstruction(0x91); // STA Direct Page Indirect Indexed, Y +} + +TEST_F(CPUTest, STA_DirectPageIndirect) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.Y = 0x02; // Set Y register to 0x02 + std::vector data = {0x92, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00003C, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00003C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, WriteByte(0x1000, 0x42)); + + cpu.ExecuteInstruction(0x92); // STA Direct Page Indirect +} + +TEST_F(CPUTest, STA_StackRelativeIndirectIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.Y = 0x02; // Set Y register to 0x02 + cpu.SetSP(0x01FF); // Set Stack Pointer to 0x01FF + std::vector data = {0x93, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00023B, {0x00, 0x10}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWord(0x00023B)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, WriteByte(0x1002, 0x42)); + + cpu.ExecuteInstruction(0x93); // STA Stack Relative Indirect Indexed, Y +} + +TEST_F(CPUTest, STA_DirectPageIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x95, 0x80}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, WriteByte(0x0082, 0x42)); + + cpu.ExecuteInstruction(0x95); // STA Direct Page Indexed, X +} + +TEST_F(CPUTest, STA_DirectPageIndirectLongIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.Y = 0x02; // Set Y register to 0x02 + std::vector data = {0x97, 0x3C}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x00003C, {0x00, 0x10, 0x00}); + + EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C)); + EXPECT_CALL(mock_memory, ReadWordLong(0x00003C)).WillOnce(Return(0x1000)); + + EXPECT_CALL(mock_memory, WriteByte(0x1002, 0x42)); + + cpu.ExecuteInstruction(0x97); // STA Direct Page Indirect Long Indexed, Y +} + +TEST_F(CPUTest, STA_AbsoluteIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.Y = 0x02; // Set Y register to 0x02 + std::vector data = {0x99, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + EXPECT_CALL(mock_memory, WriteByte(0x8001, 0x42)); + + cpu.ExecuteInstruction(0x99); // STA Absolute Indexed, Y +} + +TEST_F(CPUTest, STA_AbsoluteIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x9D, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + EXPECT_CALL(mock_memory, WriteByte(0x8001, 0x42)); + + cpu.ExecuteInstruction(0x9D); // STA Absolute Indexed, X +} + +TEST_F(CPUTest, STA_AbsoluteLongIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x9F, 0xFF, 0xFF, 0x7F}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x7FFFFF)); + EXPECT_CALL(mock_memory, WriteByte(0x800001, 0x42)); + + cpu.ExecuteInstruction(0x9F); // STA Absolute Long Indexed, X +} + +// ============================================================================ + +TEST_F(CPUTest, STP) { + cpu.ExecuteInstruction(0xDB); // STP + // EXPECT_TRUE(cpu.GetStoppedFlag()); +} + +// ============================================================================ + +TEST_F(CPUTest, STX_DirectPage) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x42; + std::vector data = {0x86, 0x80}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, WriteByte(0x0080, 0x42)); + + cpu.ExecuteInstruction(0x86); // STX Direct Page +} + +TEST_F(CPUTest, STX_Absolute) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x42; + std::vector data = {0x8E, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + EXPECT_CALL(mock_memory, WriteByte(0x7FFF, 0x42)); + + cpu.ExecuteInstruction(0x8E); // STX Absolute +} + +TEST_F(CPUTest, STX_DirectPageIndexedY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x42; + cpu.Y = 0x02; // Set Y register to 0x02 + std::vector data = {0x96, 0x80}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, WriteByte(0x0082, 0x42)); + + cpu.ExecuteInstruction(0x96); // STX Direct Page Indexed, Y +} + +// ============================================================================ + +TEST_F(CPUTest, STY_DirectPage) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x42; + std::vector data = {0x84, 0x80}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, WriteByte(0x0080, 0x42)); + + cpu.ExecuteInstruction(0x84); // STY Direct Page +} + +TEST_F(CPUTest, STY_Absolute) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x42; + std::vector data = {0x8C, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + EXPECT_CALL(mock_memory, WriteByte(0x7FFF, 0x42)); + + cpu.ExecuteInstruction(0x8C); // STY Absolute +} + +TEST_F(CPUTest, STY_DirectPageIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.Y = 0x42; + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x94, 0x80}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, WriteByte(0x0082, 0x42)); + + cpu.ExecuteInstruction(0x94); // STY Direct Page Indexed, X +} + +// ============================================================================ + +TEST_F(CPUTest, STZ_DirectPage) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0x64, 0x80}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, WriteByte(0x0080, 0x00)); + + cpu.ExecuteInstruction(0x64); // STZ Direct Page +} + +TEST_F(CPUTest, STZ_DirectPageIndexedX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x02; // Set X register to 0x02 + std::vector data = {0x74, 0x80}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, WriteByte(0x0082, 0x00)); + + cpu.ExecuteInstruction(0x74); // STZ Direct Page Indexed, X +} + +TEST_F(CPUTest, STZ_Absolute) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + std::vector data = {0x9C, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + EXPECT_CALL(mock_memory, WriteByte(0x7FFF, 0x00)); + + cpu.ExecuteInstruction(0x9C); // STZ Absolute } // ============================================================================ @@ -1481,6 +3919,176 @@ TEST_F(CPUTest, TAX) { EXPECT_EQ(cpu.X, 0xBC); // X register should now be equal to A } +// ============================================================================ +// TAY - Transfer Accumulator to Index Y + +TEST_F(CPUTest, TAY) { + cpu.A = 0xDE; // A register + std::vector data = {0xA8}; // TAY + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0xA8); // TAY + EXPECT_EQ(cpu.Y, 0xDE); // Y register should now be equal to A +} + +// ============================================================================ + +TEST_F(CPUTest, TCD) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + std::vector data = {0x5B}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x5B); // TCD + EXPECT_EQ(cpu.D, 0x42); +} + +// ============================================================================ + +TEST_F(CPUTest, TCS) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + std::vector data = {0x1B}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, SetSP(0x42)); + + cpu.ExecuteInstruction(0x1B); // TCS + EXPECT_EQ(mock_memory.SP(), 0x42); +} + +// ============================================================================ + +TEST_F(CPUTest, TDC) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.D = 0x42; + std::vector data = {0x7B}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x7B); // TDC + EXPECT_EQ(cpu.A, 0x42); +} + +// ============================================================================ + +TEST_F(CPUTest, TRB_DirectPage) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + std::vector data = {0x14, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0080, {0x00}); + + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x0080)); + EXPECT_CALL(mock_memory, ReadByte(0x0080)).WillOnce(Return(0x00)); + EXPECT_CALL(mock_memory, WriteByte(0x0080, 0x00)); + + cpu.ExecuteInstruction(0x14); // TRB Direct Page +} + +TEST_F(CPUTest, TRB_Absolute) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x42; + std::vector data = {0x1C, 0xFF, 0x7F}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFF, {0x00}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + EXPECT_CALL(mock_memory, ReadByte(0x7FFF)).WillOnce(Return(0x00)); + EXPECT_CALL(mock_memory, WriteByte(0x7FFF, 0x00)); + + cpu.ExecuteInstruction(0x1C); // TRB Absolute +} + +// ============================================================================ + +TEST_F(CPUTest, TSB_DirectPage) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x00; + std::vector data = {0x04, 0x80}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0080, {0x42}); + + EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x0080)); + EXPECT_CALL(mock_memory, ReadByte(0x0080)).WillOnce(Return(0x42)); + EXPECT_CALL(mock_memory, WriteByte(0x0080, 0x42)); + + cpu.ExecuteInstruction(0x04); // TSB Direct Page +} + +TEST_F(CPUTest, TSB_Absolute) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x00; + std::vector data = {0x0C, 0xFF, 0x7F}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFF, {0x42}); + + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF)); + EXPECT_CALL(mock_memory, ReadByte(0x7FFF)).WillOnce(Return(0x42)); + EXPECT_CALL(mock_memory, WriteByte(0x7FFF, 0x42)); + + cpu.ExecuteInstruction(0x0C); // TSB Absolute +} + +// ============================================================================ + +TEST_F(CPUTest, TSC) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.SetSP(0x42); + std::vector data = {0x3B}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x3B); // TSC + EXPECT_EQ(cpu.A, 0x42); +} + +// ============================================================================ + +TEST_F(CPUTest, TSX) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.SetSP(0x42); + std::vector data = {0xBA}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0xBA); // TSX + EXPECT_EQ(cpu.X, 0x42); +} + +// ============================================================================ +// TXA - Transfer Index X to Accumulator + +TEST_F(CPUTest, TXA) { + cpu.X = 0xAB; // X register + std::vector data = {0x8A}; // TXA + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x8A); // TXA + EXPECT_EQ(cpu.A, 0xAB); // A register should now be equal to X +} + +// ============================================================================ + +TEST_F(CPUTest, TXS) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x42; + std::vector data = {0x9A}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x9A); // TXS + EXPECT_EQ(cpu.SP(), 0x42); +} + +// ============================================================================ + +TEST_F(CPUTest, TXY) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.X = 0x42; + std::vector data = {0x9B}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x9B); // TXY + EXPECT_EQ(cpu.Y, 0x42); +} + // ============================================================================ // TYA - Transfer Index Y to Accumulator @@ -1494,15 +4102,43 @@ TEST_F(CPUTest, TYA) { } // ============================================================================ -// TAY - Transfer Accumulator to Index Y +// TYX - Transfer Index Y to Index X -TEST_F(CPUTest, TAY) { - cpu.A = 0xDE; // A register - std::vector data = {0xA8}; // TAY +TEST_F(CPUTest, TYX) { + cpu.Y = 0xCD; // Y register + std::vector data = {0xBB}; // TYX mock_memory.SetMemoryContents(data); - cpu.ExecuteInstruction(0xA8); // TAY - EXPECT_EQ(cpu.Y, 0xDE); // Y register should now be equal to A + cpu.ExecuteInstruction(0xBB); // TYX + EXPECT_EQ(cpu.X, 0xCD); // X register should now be equal to Y +} + +// ============================================================================ + +TEST_F(CPUTest, WAI) { + cpu.ExecuteInstruction(0xCB); // WAI + // EXPECT_TRUE(cpu.GetWaitingFlag()); +} + +// ============================================================================ + +TEST_F(CPUTest, WDM) { + std::vector data = {0x42}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x42); // WDM +} + +// ============================================================================ + +TEST_F(CPUTest, XBA) { + cpu.SetAccumulatorSize(true); // Set A register to 8-bit mode + cpu.A = 0x4002; + std::vector data = {0xEB}; + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0xEB); // XBA + EXPECT_EQ(cpu.A, 0x0240); } // ============================================================================ @@ -1534,6 +4170,8 @@ TEST_F(CPUTest, XCESwitchBackAndForth) { EXPECT_FALSE(cpu.E); // Emulation mode flag should be cleared } +// ============================================================================ + } // namespace emu } // namespace app } // namespace yaze