diff --git a/src/app/emu/cpu.cc b/src/app/emu/cpu.cc index 0ebed9fd..0770a1a1 100644 --- a/src/app/emu/cpu.cc +++ b/src/app/emu/cpu.cc @@ -86,12 +86,11 @@ void CPU::ExecuteInstruction(uint8_t opcode) { ADC(FetchByteDirectPage(PC)); break; case 0x67: // ADC DP Indirect Long - operand = memory.ReadByte(DirectPageIndirectLong()); + operand = memory.ReadWord(DirectPageIndirectLong()); ADC(operand); break; case 0x69: // ADC Immediate - operand = memory.ReadByte(Immediate()); - ADC(operand); + ADC(Immediate()); break; case 0x6D: // ADC Absolute operand = memory.ReadWord(Absolute()); @@ -118,15 +117,14 @@ void CPU::ExecuteInstruction(uint8_t opcode) { ADC(operand); break; case 0x77: // ADC DP Indirect Long Indexed, Y - operand = memory.ReadByte(DirectPageIndirectLongIndexedY()); - ADC(operand); + ADC(DirectPageIndirectLongIndexedY()); break; case 0x79: // ADC Absolute Indexed, Y - operand = memory.ReadByte(AbsoluteIndexedY()); + operand = memory.ReadWord(AbsoluteIndexedY()); ADC(operand); break; case 0x7D: // ADC Absolute Indexed, X - operand = memory.ReadByte(AbsoluteIndexedX()); + operand = memory.ReadWord(AbsoluteIndexedX()); ADC(operand); break; case 0x7F: // ADC Absolute Long Indexed, X @@ -151,7 +149,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) { AND(operand); break; case 0x29: // AND Immediate - AND(Immediate()); + AND(Immediate(), true); break; case 0x2D: // AND Absolute AND(Absolute()); @@ -190,19 +188,23 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; case 0x06: // ASL Direct Page - // ASL(); + ASL(DirectPage()); break; case 0x0A: // ASL Accumulator - // ASL(); + A <<= 1; + A &= 0xFE; + SetCarryFlag(A & 0x80); + SetNegativeFlag(A); + SetZeroFlag(!A); break; case 0x0E: // ASL Absolute - // ASL(); + ASL(Absolute()); break; case 0x16: // ASL DP Indexed, X - // ASL(); + ASL(DirectPageIndexedX()); break; case 0x1E: // ASL Absolute Indexed, X - // ASL(); + ASL(AbsoluteIndexedX()); break; case 0x90: // BCC Branch if carry clear @@ -211,7 +213,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; case 0xB0: // BCS Branch if carry set - // BCS(); + BCS(memory.ReadByte(PC)); break; case 0xF0: // BEQ Branch if equal (zero set) @@ -220,39 +222,39 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; case 0x24: // BIT Direct Page - // BIT(); + BIT(DirectPage()); break; case 0x2C: // BIT Absolute - // BIT(); + BIT(Absolute()); break; case 0x34: // BIT DP Indexed, X - // BIT(); + BIT(DirectPageIndexedX()); break; case 0x3C: // BIT Absolute Indexed, X - // BIT(); + BIT(AbsoluteIndexedX()); break; case 0x89: // BIT Immediate - // BIT(); + BIT(Immediate()); break; case 0x30: // BMI Branch if minus (negative set) - // BMI(); + BMI(ReadByte(PC)); break; case 0xD0: // BNE Branch if not equal (zero clear) - // BNE(); + BNE(ReadByte(PC)); break; case 0x10: // BPL Branch if plus (negative clear) - // BPL(); + BPL(ReadByte(PC)); break; case 0x80: // BRA Branch always - // BRA(); + BRA(ReadByte(PC)); break; case 0x00: // BRK Break - // BRK(); + BRK(); break; case 0x82: // BRL Branch always long @@ -334,20 +336,20 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; case 0xE0: // CPX Immediate - CPX(Immediate()); + CPX(Immediate(), true); break; case 0xE4: // CPX Direct Page - // CPX(); + CPX(DirectPage()); break; case 0xEC: // CPX Absolute CPX(Absolute()); break; case 0xC0: // CPY Immediate - CPY(Immediate()); + CPY(Immediate(), true); break; case 0xC4: // CPY Direct Page - // CPY(); + CPY(DirectPage()); break; case 0xCC: // CPY Absolute CPY(Absolute()); @@ -424,19 +426,19 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; case 0x1A: // INC Accumulator - // INC(); + INC(A); break; case 0xE6: // INC Direct Page - // INC(); + INC(DirectPage()); break; case 0xEE: // INC Absolute - // INC(); + INC(Absolute()); break; case 0xF6: // INC DP Indexed, X - // INC(); + INC(DirectPageIndexedX()); break; case 0xFE: // INC Absolute Indexed, X - // INC(); + INC(AbsoluteIndexedX()); break; case 0xE8: // INX Increment X register @@ -457,10 +459,10 @@ void CPU::ExecuteInstruction(uint8_t opcode) { JMP(AbsoluteIndirect()); break; case 0x7C: // JMP Absolute Indexed Indirect, X - // JMP(); + JMP(AbsoluteIndexedIndirect()); break; case 0xDC: // JMP Absolute Indirect Long - // JMP(); + JMP(AbsoluteIndirectLong()); break; case 0x20: // JSR Absolute @@ -472,53 +474,53 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; case 0xFC: // JSR Absolute Indexed Indirect, X - // JSR(); + JSR(AbsoluteIndexedIndirect()); break; case 0xA1: // LDA DP Indexed Indirect, X - // LDA(); + LDA(DirectPageIndexedIndirectX()); break; case 0xA3: // LDA Stack Relative - // LDA(); + LDA(StackRelative()); break; case 0xA5: // LDA Direct Page - // LDA(); + LDA(DirectPage()); break; case 0xA7: // LDA DP Indirect Long - // LDA(); + LDA(DirectPageIndirectLong()); break; case 0xA9: // LDA Immediate - LDA(); + LDA(PC + 1, true); break; case 0xAD: // LDA Absolute - // LDA(); + LDA(Absolute()); break; case 0xAF: // LDA Absolute Long - // LDA(); + LDA(AbsoluteLong()); break; case 0xB1: // LDA DP Indirect Indexed, Y - // LDA(); + LDA(DirectPageIndirectIndexedY()); break; case 0xB2: // LDA DP Indirect - // LDA(); + LDA(DirectPageIndirect()); break; case 0xB3: // LDA SR Indirect Indexed, Y - // LDA(); + LDA(StackRelativeIndirectIndexedY()); break; case 0xB5: // LDA DP Indexed, X - // LDA(); + LDA(DirectPageIndexedX()); break; case 0xB7: // LDA DP Indirect Long Indexed, Y - // LDA(); + LDA(DirectPageIndirectLongIndexedY()); break; case 0xB9: // LDA Absolute Indexed, Y - // LDA(); + LDA(DirectPageIndirectLongIndexedY()); break; case 0xBD: // LDA Absolute Indexed, X - // LDA(); + LDA(DirectPageIndirectLongIndexedY()); break; case 0xBF: // LDA Absolute Long Indexed, X - // LDA(); + LDA(DirectPageIndirectLongIndexedY()); break; case 0xA2: // LDX Immediate @@ -995,15 +997,15 @@ void CPU::ADC(uint8_t operand) { } } -void CPU::AND(uint16_t address) { - uint8_t operand; +void CPU::AND(uint16_t value, bool isImmediate) { + uint16_t operand; if (E == 0) { // 16-bit mode - uint16_t operand16 = memory.ReadWord(address); - A &= operand16; + operand = isImmediate ? value : memory.ReadWord(value); + A &= operand; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x8000); } else { // 8-bit mode - operand = memory.ReadByte(address); + operand = isImmediate ? value : memory.ReadByte(value); A &= operand; SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); @@ -1018,6 +1020,39 @@ void CPU::ANDAbsoluteLong(uint32_t address) { SetNegativeFlag(A & 0x80000000); } +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); + } +} + } // 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 adc396a7..39ef95b9 100644 --- a/src/app/emu/cpu.h +++ b/src/app/emu/cpu.h @@ -196,7 +196,7 @@ class CPU : public Memory { // register in bank zero. // // LDA [dp] - uint16_t DirectPageIndirectLong() { + uint32_t DirectPageIndirectLong() { uint8_t dp = FetchByte(); uint16_t effective_address = D + dp; return memory.ReadWordLong(effective_address); @@ -227,8 +227,8 @@ class CPU : public Memory { // LDA (dp), Y uint16_t DirectPageIndirectLongIndexedY() { uint8_t dp = FetchByte(); - uint16_t effective_address = D + dp; - return memory.ReadWordLong(effective_address) + Y; + uint16_t effective_address = D + dp + Y; + return memory.ReadWordLong(effective_address); } // 8-bit data: Data Operand Byte @@ -237,7 +237,13 @@ class CPU : public Memory { // Data Low: First Operand Byte // // LDA #const - uint16_t Immediate() { return PC++; } + uint16_t Immediate() { + if (GetAccumulatorSize()) { + return FetchByte(); + } else { + return FetchWord(); + } + } uint16_t StackRelative() { uint8_t sr = FetchByte(); @@ -246,7 +252,7 @@ class CPU : public Memory { uint16_t StackRelativeIndirectIndexedY() { uint8_t sr = FetchByte(); - return memory.ReadWord(SP() + sr) + Y; + return memory.ReadWord(SP() + sr + Y); } // ========================================================================== @@ -254,10 +260,8 @@ class CPU : public Memory { uint8_t A = 0; // Accumulator uint8_t B = 0; // Accumulator (High) - uint8_t X = 0; // X index register - uint8_t X2 = 0; // X index register (High) - uint8_t Y = 0; // Y index register - uint8_t Y2 = 0; // Y index register (High) + uint16_t X = 0; // X index register + uint16_t Y = 0; // Y index register uint16_t D = 0; // Direct Page register uint16_t DB = 0; // Data Bank register uint8_t PB = 0; // Program Bank register @@ -280,6 +284,8 @@ class CPU : public Memory { // Setting flags in the status register int GetAccumulatorSize() const { return status & 0x20; } int GetIndexSize() const { return status & 0x10; } + void SetAccumulatorSize(bool set) { SetFlag(0x20, set); } + void SetIndexSize(bool set) { SetFlag(0x10, set); } // Set individual flags void SetNegativeFlag(bool set) { SetFlag(0x80, set); } @@ -301,16 +307,24 @@ class CPU : public Memory { // ========================================================================== // Instructions - /// ``` Unimplemented // ADC: Add with carry void ADC(uint8_t operand); void ANDAbsoluteLong(uint32_t address); // AND: Logical AND - void AND(uint16_t address); + void AND(uint16_t address, bool isImmediate = false); - // ASL: Arithmetic shift left ``` + // ASL: Arithmetic shift left + void 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 BCC(int8_t offset) { @@ -319,7 +333,12 @@ class CPU : public Memory { } } - // BCS: Branch if carry set ``` + // BCS: Branch if carry set + void BCS(int8_t offset) { + if (GetCarryFlag()) { // If the carry flag is set + PC += offset; // Add the offset to the program counter + } + } // BEQ: Branch if equal (zero set) void BEQ(int8_t offset) { @@ -328,20 +347,65 @@ class CPU : public Memory { } } - // BIT: Bit test ``` - // BMI: Branch if minus (negative set) ``` - // BNE: Branch if not equal (zero clear) ``` - // BPL: Branch if plus (negative clear) ``` - // BRA: Branch always ``` - // BRK: Break ``` + // BIT: Bit test + void 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 BMI(int8_t offset) { + if (GetNegativeFlag()) { // If the negative flag is set + PC += offset; // Add the offset to the program counter + } + } + + // BNE: Branch if not equal (zero clear) + void BNE(int8_t offset) { + if (!GetZeroFlag()) { // If the zero flag is clear + PC += offset; // Add the offset to the program counter + } + } + + // BPL: Branch if plus (negative clear) + void BPL(int8_t offset) { + if (!GetNegativeFlag()) { // If the negative flag is clear + PC += offset; // Add the offset to the program counter + } + } + + // BRA: Branch always + void BRA(int8_t offset) { PC += offset; } + + // BRK: Break + void BRK() { + PC += 2; // Increment the program counter by 2 + memory.PushWord(PC); + memory.PushByte(status); + SetInterruptFlag(true); + PC = memory.ReadWord(0xFFFE); + } // BRL: Branch always long void BRL(int16_t offset) { PC += offset; // Add the offset to the program counter } - // BVC: Branch if overflow clear ``` - // BVS: Branch if overflow set ``` + // BVC: Branch if overflow clear + void BVC(int8_t offset) { + if (!GetOverflowFlag()) { // If the overflow flag is clear + PC += offset; // Add the offset to the program counter + } + } + + // BVS: Branch if overflow set + void BVS(int8_t offset) { + if (GetOverflowFlag()) { // If the overflow flag is set + PC += offset; // Add the offset to the program counter + } + } // CLC: Clear carry flag void CLC() { status &= ~0x01; } @@ -359,16 +423,21 @@ class CPU : public Memory { // COP: Coprocessor ``` // CPX: Compare X register - void CPX(uint16_t address) { - uint16_t memory_value = - E ? memory.ReadByte(address) : memory.ReadWord(address); + // CPX: Compare X register + void CPX(uint16_t value, bool isImmediate = false) { + uint16_t memory_value = isImmediate + ? value + : (GetIndexSize() ? memory.ReadByte(value) + : memory.ReadWord(value)); compare(X, memory_value); } // CPY: Compare Y register - void CPY(uint16_t address) { - uint16_t memory_value = - E ? memory.ReadByte(address) : memory.ReadWord(address); + void CPY(uint16_t value, bool isImmediate = false) { + uint16_t memory_value = isImmediate + ? value + : (GetIndexSize() ? memory.ReadByte(value) + : memory.ReadWord(value)); compare(Y, memory_value); } @@ -376,56 +445,73 @@ class CPU : public Memory { // DEX: Decrement X register void DEX() { - X--; - SetZeroFlag(X == 0); - SetNegativeFlag(X & 0x80); + 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 DEY() { - Y--; - SetZeroFlag(Y == 0); - SetNegativeFlag(Y & 0x80); + 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 ``` // INC: Increment - // TODO: Check if this is correct void INC(uint16_t address) { if (GetAccumulatorSize()) { - uint8_t value = ReadByte(address); + uint8_t value = memory.ReadByte(address); value++; - if (value == static_cast(0x100)) { - value = 0x00; // Wrap around in 8-bit mode - } - WriteByte(address, value); + memory.WriteByte(address, value); SetNegativeFlag(value & 0x80); SetZeroFlag(value == 0); } else { - uint16_t value = ReadWord(address); + uint16_t value = memory.ReadWord(address); value++; - if (value == static_cast(0x10000)) { - value = 0x0000; // Wrap around in 16-bit mode - } - WriteByte(address, value); - SetNegativeFlag(value & 0x80); + memory.WriteWord(address, value); + SetNegativeFlag(value & 0x8000); SetZeroFlag(value == 0); } } // INX: Increment X register void INX() { - X++; - SetNegativeFlag(X & 0x80); - SetZeroFlag(X == 0); + 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 INY() { - Y++; - SetNegativeFlag(Y & 0x80); - SetZeroFlag(Y == 0); + 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 @@ -457,16 +543,54 @@ class CPU : public Memory { } // LDA: Load accumulator - void LDA() { - A = memory[PC]; - SetZeroFlag(A == 0); - SetNegativeFlag(A & 0x80); - PC++; + void LDA(uint16_t address, bool isImmediate = false) { + 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); + } + } + + // LDX: Load X register + void LDX(uint16_t address, bool isImmediate = false) { + 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 LDY(uint16_t address, bool isImmediate = false) { + 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 LSR(uint16_t address) { + uint8_t value = memory.ReadByte(address); + SetCarryFlag(value & 0x01); + value >>= 1; + memory.WriteByte(address, value); + SetNegativeFlag(false); + SetZeroFlag(value == 0); } - // LDX: Load X register ``` - // LDY: Load Y register ``` - // LSR: Logical shift right ``` // MVN: Move negative ``` // MVP: Move positive ``` @@ -475,10 +599,36 @@ class CPU : public Memory { // Do nothing } - // ORA: Logical OR ``` - // PEA: Push effective address ``` - // PEI: Push effective indirect address ``` - // PER: Push effective PC-relative address ``` + // ORA: Logical OR + void ORA(uint16_t address, bool isImmediate = false) { + 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 PEA() { + uint16_t address = FetchWord(); + memory.PushWord(address); + } + + // PEI: Push effective indirect address + void PEI() { + uint16_t address = FetchWord(); + memory.PushWord(memory.ReadWord(address)); + } + + // PER: Push effective PC-relative address + void PER() { + uint16_t address = FetchWord(); + memory.PushWord(PC + address); + } // PHA: Push Accumulator on Stack void PHA() { memory.PushByte(A); } @@ -546,12 +696,47 @@ class CPU : public Memory { status &= ~byte; } - // ROL: Rotate left ``` - // ROR: Rotate right ``` - // RTI: Return from interrupt ``` - // RTL: Return from subroutine long ``` - // RTS: Return from subroutine ``` - // SBC: Subtract with carry ``` + // ROL: Rotate left + void 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 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 RTI() { + status = memory.PopByte(); + PC = memory.PopWord(); + } + + // RTL: Return from subroutine long + void RTL() { + PC = memory.PopWord(); + PB = memory.PopByte(); + } + + // RTS: Return from subroutine + void RTS() { PC = memory.PopWord() + 1; } + + // SBC: Subtract with carry + void SBC(uint16_t operand, bool isImmediate = false); // SEC: Set carry flag void SEC() { status |= 0x01; } @@ -569,11 +754,43 @@ class CPU : public Memory { status |= byte; } - // STA: Store accumulator ``` + // STA: Store accumulator + void STA(uint16_t address) { + if (GetAccumulatorSize()) { + memory.WriteByte(address, static_cast(A)); + } else { + memory.WriteWord(address, A); + } + } + // STP: Stop the clock ``` - // STX: Store X register ``` - // STY: Store Y register ``` - // STZ: Store zero ``` + + // STX: Store X register + void STX(uint16_t address) { + if (GetIndexSize()) { + memory.WriteByte(address, static_cast(X)); + } else { + memory.WriteWord(address, X); + } + } + + // STY: Store Y register + void STY(uint16_t address) { + if (GetIndexSize()) { + memory.WriteByte(address, static_cast(Y)); + } else { + memory.WriteWord(address, Y); + } + } + + // STZ: Store zero + void STZ(uint16_t address) { + if (GetAccumulatorSize()) { + memory.WriteByte(address, 0x00); + } else { + memory.WriteWord(address, 0x0000); + } + } // TAX: Transfer accumulator to X void TAX() { @@ -606,8 +823,21 @@ class CPU : public Memory { SetNegativeFlag(A & 0x80); } - // TRB: Test and reset bits ``` - // TSB: Test and set bits ``` + // TRB: Test and reset bits + void 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 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 TSC() { @@ -655,7 +885,15 @@ class CPU : public Memory { } // WAI: Wait for interrupt ``` - // XBA: Exchange B and A accumulator ``` + + // XBA: Exchange B and A accumulator + void XBA() { + uint8_t temp = A; + A = B; + B = temp; + SetZeroFlag(A == 0); + SetNegativeFlag(A & 0x80); + } // XCE: Exchange Carry and Emulation Flags void XCE() { @@ -667,10 +905,20 @@ class CPU : public Memory { private: void compare(uint16_t register_value, uint16_t memory_value) { - uint16_t result = register_value - memory_value; - SetNegativeFlag(result & (E ? 0x8000 : 0x80)); // Negative flag - SetZeroFlag(result == 0); // Zero flag - SetCarryFlag(register_value >= 0); // Carry flag + uint16_t result; + if (GetIndexSize()) { + // 8-bit mode + uint8_t result8 = static_cast(register_value) - + static_cast(memory_value); + result = result8; + SetNegativeFlag(result & 0x80); // Negative flag for 8-bit + } else { + // 16-bit mode + result = register_value - memory_value; + SetNegativeFlag(result & 0x8000); // Negative flag for 16-bit + } + SetZeroFlag(result == 0); // Zero flag + SetCarryFlag(register_value >= memory_value); // Carry flag } // Helper function to set or clear a specific flag bit diff --git a/test/cpu_test.cc b/test/cpu_test.cc index 179082ea..fda4849e 100644 --- a/test/cpu_test.cc +++ b/test/cpu_test.cc @@ -41,6 +41,16 @@ class MockMemory : public Memory { std::copy(data.begin(), data.end(), memory_.begin()); } + void SetMemoryContents(const std::vector& data) { + memory_.resize(64000); + int i = 0; + for (const auto& each : data) { + memory_[i] = each & 0xFF; + memory_[i + 1] = (each >> 8) & 0xFF; + i += 2; + } + } + void InsertMemory(const uint64_t address, const std::vector& data) { int i = 0; for (const auto& each : data) { @@ -158,7 +168,8 @@ TEST_F(CPUTest, CheckMemoryContents) { TEST_F(CPUTest, ADC_Immediate_TwoPositiveNumbers) { cpu.A = 0x01; - std::vector data = {0x69, 0x01}; + cpu.status = 0xFF; // 8-bit mode + std::vector data = {0x01}; mock_memory.SetMemoryContents(data); EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0x01)); @@ -169,6 +180,7 @@ TEST_F(CPUTest, ADC_Immediate_TwoPositiveNumbers) { TEST_F(CPUTest, ADC_Immediate_PositiveAndNegativeNumbers) { cpu.A = 10; + cpu.status = 0xFF; // 8-bit mode std::vector data = {0x69, static_cast(-20)}; mock_memory.SetMemoryContents(data); @@ -258,7 +270,7 @@ TEST_F(CPUTest, ADC_DirectPageIndexedIndirectX) { TEST_F(CPUTest, ADC_CheckCarryFlag) { cpu.A = 0xFF; - cpu.status = 0; + cpu.status = 0xFF; // 8-bit mode std::vector data = {0x15, 0x01}; // Operand at address 0x15 mock_memory.SetMemoryContents(data); @@ -270,18 +282,94 @@ TEST_F(CPUTest, ADC_CheckCarryFlag) { EXPECT_TRUE(cpu.GetCarryFlag()); } +TEST_F(CPUTest, ADC_AbsoluteIndexedX) { + cpu.A = 0x03; + cpu.X = 0x02; // X register + cpu.PC = 0x0001; + std::vector data = {0x7D, 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)); + + cpu.ExecuteInstruction(0x7D); // ADC Absolute Indexed X + EXPECT_EQ(cpu.A, 0x08); +} + +TEST_F(CPUTest, ADC_AbsoluteIndexedY) { + cpu.A = 0x03; + cpu.Y = 0x02; // Y register + cpu.PC = 0x0001; + 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)); + + cpu.ExecuteInstruction(0x79); // ADC Absolute Indexed Y + EXPECT_EQ(cpu.A, 0x08); +} + +TEST_F(CPUTest, ADC_DirectPageIndexedY) { + cpu.A = 0x03; + cpu.D = 0x2000; + cpu.Y = 0x02; + std::vector data = {0x77, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2012, {0x06}); + + EXPECT_CALL(mock_memory, ReadByte(0x0000)).WillOnce(Return(0x10)); + EXPECT_CALL(mock_memory, ReadWordLong(0x2012)).WillOnce(Return(0x06)); + + cpu.ExecuteInstruction(0x77); // ADC Direct Page Indexed Y + EXPECT_EQ(cpu.A, 0x09); +} + +TEST_F(CPUTest, ADC_DirectPageIndirectLong) { + cpu.A = 0x03; + cpu.D = 0x2000; + cpu.PC = 0x0001; + std::vector data = {0x67, 0x10}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2010, {0x05, 0x00, 0x30}); + mock_memory.InsertMemory(0x300005, {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_StackRelative) { + cpu.A = 0x03; + cpu.PC = 0x0001; + 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 +} + // ============================================================================ // AND - Logical AND TEST_F(CPUTest, AND_Immediate) { cpu.PC = 0; - cpu.status = 0xFF; // 8-bit mode - cpu.A = 0b11110000; // A register - std::vector data = {0x29, 0b10101010}; // AND #0b10101010 + cpu.status = 0xFF; // 8-bit mode + cpu.A = 0b11110000; // A register + std::vector data = {0b10101010}; // AND #0b10101010 mock_memory.SetMemoryContents(data); - EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0b10101010)); - cpu.ExecuteInstruction(0x29); // AND Immediate EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 } @@ -403,6 +491,71 @@ TEST_F(CPUTest, AND_AbsoluteLongIndexedX) { EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 } +// ============================================================================ +// ASL - Arithmetic Shift Left + +TEST_F(CPUTest, ASL_DirectPage) { + cpu.D = 0x1000; // Setting Direct Page register to 0x1000 + cpu.PC = 0x1000; + std::vector data = {0x06, 0x10}; // ASL dp + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x1010, {0x40}); // [0x1010] = 0x40 + + cpu.ExecuteInstruction(0x06); // ASL Direct Page + EXPECT_TRUE(cpu.GetCarryFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); + EXPECT_TRUE(cpu.GetNegativeFlag()); +} + +TEST_F(CPUTest, ASL_Accumulator) { + cpu.status = 0xFF; // 8-bit mode + cpu.A = 0x40; + std::vector data = {0x0A}; // ASL A + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x0A); // ASL Accumulator + EXPECT_EQ(cpu.A, 0x80); + EXPECT_TRUE(cpu.GetCarryFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); + EXPECT_TRUE(cpu.GetNegativeFlag()); +} + +TEST_F(CPUTest, ASL_Absolute) { + std::vector data = {0x0E, 0x10, 0x20}; // ASL abs + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2010, {0x40}); // [0x2010] = 0x40 + + cpu.ExecuteInstruction(0x0E); // ASL Absolute + EXPECT_TRUE(cpu.GetCarryFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); + EXPECT_TRUE(cpu.GetNegativeFlag()); +} + +TEST_F(CPUTest, ASL_DP_Indexed_X) { + 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 + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x1012, {0x40}); // [0x1012] = 0x40 + + cpu.ExecuteInstruction(0x16); // ASL DP Indexed, X + EXPECT_TRUE(cpu.GetCarryFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); + EXPECT_TRUE(cpu.GetNegativeFlag()); +} + +TEST_F(CPUTest, ASL_Absolute_Indexed_X) { + cpu.X = 0x02; // Setting X register to 0x02 + std::vector data = {0x1E, 0x10, 0x20}; // ASL abs,X + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x2012, {0x40}); // [0x2012] = 0x40 + + cpu.ExecuteInstruction(0x1E); // ASL Absolute Indexed, X + EXPECT_TRUE(cpu.GetCarryFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); + EXPECT_TRUE(cpu.GetNegativeFlag()); +} + // ============================================================================ // BCC - Branch if Carry Clear @@ -431,6 +584,169 @@ TEST_F(CPUTest, BCC_WhenCarryFlagSet) { EXPECT_EQ(cpu.PC, 0x1000); } +// ============================================================================ +// BCS - Branch if Carry Set + +TEST_F(CPUTest, BCS_WhenCarryFlagSet) { + cpu.SetCarryFlag(true); + cpu.PC = 0x1001; + std::vector data = {0xB0, 0x03, 0x02}; // Operand at address 0x1001 + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0x03)); + + cpu.ExecuteInstruction(0xB0); // BCS + EXPECT_EQ(cpu.PC, 0x1004); +} + +TEST_F(CPUTest, BCS_WhenCarryFlagClear) { + cpu.SetCarryFlag(false); + cpu.PC = 0x1000; + std::vector data = {0x10, 0x02, 0x01}; // Operand at address 0x1001 + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(2)); + + cpu.ExecuteInstruction(0xB0); // BCS + cpu.BCS(2); + EXPECT_EQ(cpu.PC, 0x1000); +} + +TEST_F(CPUTest, BIT_Immediate) { + cpu.A = 0x01; + cpu.PC = 0x0001; + cpu.status = 0xFF; + std::vector data = {0x00, 0x10}; // BIT + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0010, {0x81}); // [0x0010] = 0x81 + + cpu.ExecuteInstruction(0x89); // BIT + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, BIT_Absolute) { + cpu.A = 0x01; + cpu.PC = 0x0001; + cpu.status = 0xFF; + std::vector data = {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()); +} + +TEST_F(CPUTest, BIT_AbsoluteIndexedX) { + cpu.A = 0x01; + cpu.X = 0x02; + cpu.PC = 0x0001; + cpu.status = 0xFF; + std::vector data = {0x00, 0x10}; // BIT + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x0012, {0x81}); // [0x0010] = 0x81 + + // Read the operand + EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x10)); + + // Read the value at the address of the operand + EXPECT_CALL(mock_memory, ReadByte(0x0012)).WillOnce(Return(0x81)); + + cpu.ExecuteInstruction(0x3C); // BIT + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetOverflowFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, BMI_BranchTaken) { + cpu.PC = 0x0000; + cpu.SetNegativeFlag(true); + std::vector data = {0x02}; // BMI + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x30); // BMI + EXPECT_EQ(cpu.PC, 0x0002); +} + +TEST_F(CPUTest, BMI_BranchNotTaken) { + cpu.PC = 0x0000; + cpu.SetNegativeFlag(false); + std::vector data = {0x30, 0x02}; // BMI + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x30); // BMI + EXPECT_EQ(cpu.PC, 0x0000); +} + +TEST_F(CPUTest, BNE_BranchTaken) { + cpu.PC = 0x0000; + cpu.SetZeroFlag(false); + std::vector data = {0x02}; // BNE + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0xD0); // BNE + EXPECT_EQ(cpu.PC, 0x0002); +} + +TEST_F(CPUTest, BNE_BranchNotTaken) { + cpu.PC = 0x0000; + cpu.SetZeroFlag(true); + std::vector data = {0xD0, 0x02}; // BNE + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0xD0); // BNE + EXPECT_EQ(cpu.PC, 0x0000); +} + +TEST_F(CPUTest, BPL_BranchTaken) { + cpu.PC = 0x0000; + cpu.SetNegativeFlag(false); + std::vector data = {0x02}; // BPL + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x10); // BPL + EXPECT_EQ(cpu.PC, 0x0002); +} + +TEST_F(CPUTest, BPL_BranchNotTaken) { + cpu.PC = 0x0000; + cpu.SetNegativeFlag(true); + std::vector data = {0x10, 0x02}; // BPL + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x10); // BPL + EXPECT_EQ(cpu.PC, 0x0000); +} + +TEST_F(CPUTest, BRA) { + cpu.PC = 0x0000; + std::vector data = {0x02}; // BRA + mock_memory.SetMemoryContents(data); + + cpu.ExecuteInstruction(0x80); // BRA + EXPECT_EQ(cpu.PC, 0x0002); +} + +TEST_F(CPUTest, BRK) { + cpu.PC = 0x0000; + std::vector data = {0x00}; // BRK + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0xFFFE, {0x10, 0x20}); // [0xFFFE] = 0x2010 + + EXPECT_CALL(mock_memory, ReadWord(0xFFFE)).WillOnce(Return(0x2010)); + + cpu.ExecuteInstruction(0x00); // BRK + EXPECT_EQ(cpu.PC, 0x2010); + EXPECT_TRUE(cpu.GetInterruptFlag()); +} + // ============================================================================ // BRL - Branch Long @@ -446,50 +762,57 @@ TEST_F(CPUTest, BRL) { } // ============================================================================ - // Test for CPX instruction TEST_F(CPUTest, CPX_CarryFlagSet) { cpu.X = 0x1000; - cpu.CPX(0x0F00); + cpu.CPX(0x0FFF); ASSERT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set } TEST_F(CPUTest, CPX_ZeroFlagSet) { - cpu.X = 0x0F00; + cpu.SetIndexSize(false); // Set X register to 16-bit mode + cpu.SetAccumulatorSize(false); + cpu.X = 0x1234; + std::vector data = {0x34, 0x12}; // CPX #0x1234 + mock_memory.SetMemoryContents(data); cpu.ExecuteInstruction(0xE0); // Immediate CPX ASSERT_TRUE(cpu.GetZeroFlag()); // Zero flag should be set } TEST_F(CPUTest, CPX_NegativeFlagSet) { - cpu.PC = 1; - cpu.X = 0x8000; - std::vector data = {0xE0, 0xFF, 0xFF}; + cpu.SetIndexSize(false); // Set X register to 16-bit mode + cpu.PC = 0; + cpu.X = 0x9000; + std::vector data = {0xE0, 0x01, 0x80}; // CPX #0x8001 mock_memory.SetMemoryContents(data); - - cpu.ExecuteInstruction(0xE0); // Immediate CPX (0xFFFF) - + cpu.ExecuteInstruction(0xE0); // Immediate CPX ASSERT_TRUE(cpu.GetNegativeFlag()); // Negative flag should be set } // Test for CPY instruction TEST_F(CPUTest, CPY_CarryFlagSet) { cpu.Y = 0x1000; - cpu.CPY(0x0F00); + cpu.CPY(0x0FFF); ASSERT_TRUE(cpu.GetCarryFlag()); // Carry flag should be set } TEST_F(CPUTest, CPY_ZeroFlagSet) { - cpu.Y = 0x0F00; - cpu.CPY(0x0F00); + cpu.SetIndexSize(false); // Set Y register to 16-bit mode + cpu.SetAccumulatorSize(false); + cpu.Y = 0x5678; + std::vector data = {0x78, 0x56}; // CPY #0x5678 + mock_memory.SetMemoryContents(data); + cpu.ExecuteInstruction(0xC0); // Immediate CPY ASSERT_TRUE(cpu.GetZeroFlag()); // Zero flag should be set } TEST_F(CPUTest, CPY_NegativeFlagSet) { - cpu.PC = 1; - cpu.Y = 0x8000; - std::vector data = {0xC0, 0xFF, 0xFF}; + cpu.SetIndexSize(false); // Set Y register to 16-bit mode + cpu.PC = 0; + cpu.Y = 0x9000; + std::vector data = {0xC0, 0x01, 0x80}; // CPY #0x8001 mock_memory.SetMemoryContents(data); - cpu.ExecuteInstruction(0xC0); // Immediate CPY (0xFFFF) + cpu.ExecuteInstruction(0xC0); // Immediate CPY ASSERT_TRUE(cpu.GetNegativeFlag()); // Negative flag should be set } @@ -498,6 +821,7 @@ TEST_F(CPUTest, CPY_NegativeFlagSet) { // 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 cpu.ExecuteInstruction(0xCA); // Execute DEX instruction EXPECT_EQ(0x01, cpu.X); // Expected value of X register after decrementing @@ -513,6 +837,7 @@ TEST_F(CPUTest, DEX) { // 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 cpu.ExecuteInstruction(0x88); // Execute DEY instruction EXPECT_EQ(0x01, cpu.Y); // Expected value of Y register after decrementing @@ -530,30 +855,109 @@ TEST_F(CPUTest, DEY) { // INC - Increment Memory /** -TEST_F(CPUTest, INC) { - cpu.status &= 0x20; +TEST_F(CPUTest, INC_DirectPage_8bit) { + cpu.PC = 0x1001; + std::vector data = {0xE6, 0x7F, 0x7F}; + mock_memory.SetMemoryContents(data); - EXPECT_CALL(mock_memory, WriteByte(0x1000, 0x7F)).WillOnce(Return()); - EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0x7F)); - EXPECT_CALL(mock_memory, WriteByte(0x1000, 0x80)).WillOnce(Return()); + EXPECT_CALL(mock_memory, ReadByte(0x7F)).WillOnce(Return(0x7F)); + EXPECT_CALL(mock_memory, WriteByte(0, 0x80)).Times(1); - cpu.WriteByte(0x1000, 0x7F); - cpu.INC(0x1000); - EXPECT_EQ(cpu.ReadByte(0x1000), 0x80); + cpu.SetAccumulatorSize(true); + cpu.ExecuteInstruction(0xE6); // INC Direct Page EXPECT_TRUE(cpu.GetNegativeFlag()); EXPECT_FALSE(cpu.GetZeroFlag()); +} - EXPECT_CALL(mock_memory, WriteByte(0x1000, 0xFF)).WillOnce(Return()); - cpu.WriteByte(0x1000, 0xFF); - cpu.INC(0x1000); - EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0x00)); - EXPECT_EQ(cpu.ReadByte(0x1000), 0x00); +TEST_F(CPUTest, INC_Absolute_16bit) { + cpu.PC = 0x1001; + std::vector data = {0xEE, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0xFF7F)).WillOnce(Return(0x7FFF)); + EXPECT_CALL(mock_memory, WriteWord(0xFF7F, 0x8000)).Times(1); + + cpu.SetAccumulatorSize(false); + cpu.ExecuteInstruction(0xEE); // INC Absolute + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, INC_DirectPage_ZeroResult_8bit) { + cpu.PC = 0x1001; + std::vector data = {0xE6, 0xFF}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadByte(0xFF)).WillOnce(Return(0xFF)); + EXPECT_CALL(mock_memory, WriteByte(0xFF, 0x00)).Times(1); + + cpu.SetAccumulatorSize(true); + cpu.ExecuteInstruction(0xE6); // INC Direct Page EXPECT_FALSE(cpu.GetNegativeFlag()); EXPECT_TRUE(cpu.GetZeroFlag()); } + +TEST_F(CPUTest, INC_Absolute_ZeroResult_16bit) { + cpu.PC = 0x1001; + std::vector data = {0xEE, 0xFF, 0xFF}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0xFFFF)).WillOnce(Return(0xFFFF)); + EXPECT_CALL(mock_memory, WriteWord(0xFFFF, 0x0000)).Times(1); + + cpu.SetAccumulatorSize(false); + cpu.ExecuteInstruction(0xEE); // INC Absolute + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_TRUE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, INC_DirectPage_8bit_Overflow) { + cpu.PC = 0x1001; + std::vector data = {0xE6, 0x80}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadByte(0x80)).WillOnce(Return(0xFF)); + EXPECT_CALL(mock_memory, WriteByte(0x80, 0x00)).Times(1); + + cpu.SetAccumulatorSize(true); + cpu.ExecuteInstruction(0xE6); // INC Direct Page + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_TRUE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, INC_DirectPageIndexedX_8bit) { + cpu.PC = 0x1001; + cpu.X = 0x01; + std::vector data = {0xF6, 0x7E}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadByte(0x7F)).WillOnce(Return(0x7F)); + EXPECT_CALL(mock_memory, WriteByte(0x7F, 0x80)).Times(1); + + cpu.SetAccumulatorSize(true); + cpu.ExecuteInstruction(0xF6); // INC DP Indexed, X + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, INC_AbsoluteIndexedX_16bit) { + cpu.PC = 0x1001; + cpu.X = 0x01; + std::vector data = {0xFE, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0xFF80)).WillOnce(Return(0x7FFF)); + EXPECT_CALL(mock_memory, WriteWord(0xFF80, 0x8000)).Times(1); + + cpu.SetAccumulatorSize(false); + cpu.ExecuteInstruction(0xFE); // INC Absolute Indexed, X + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} */ TEST_F(CPUTest, INX) { + cpu.SetIndexSize(true); // Set X register to 8-bit mode cpu.X = 0x7F; cpu.INX(); EXPECT_EQ(cpu.X, 0x80); @@ -568,6 +972,7 @@ TEST_F(CPUTest, INX) { } TEST_F(CPUTest, INY) { + cpu.SetIndexSize(true); // Set Y register to 8-bit mode cpu.Y = 0x7F; cpu.INY(); EXPECT_EQ(cpu.Y, 0x80); @@ -662,6 +1067,68 @@ TEST_F(CPUTest, JSL_AbsoluteLong) { EXPECT_EQ(cpu.PC, 0x002005); } +// ============================================================================ +// LDA - Load Accumulator + +/** +TEST_F(CPUTest, LDA_Immediate_8bit) { + cpu.PC = 0x1001; + cpu.SetAccumulatorSize(true); + cpu.A = 0x00; + std::vector data = {0xA9, 0x7F, 0x7F}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7F, {0xAA}); + + cpu.ExecuteInstruction(0xA9); // LDA Immediate + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_Immediate_16bit) { + cpu.PC = 0x1001; + std::vector data = {0xA9, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + + cpu.SetAccumulatorSize(false); + cpu.ExecuteInstruction(0xA9); // LDA Immediate + EXPECT_EQ(cpu.A, 0xFF7F); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +TEST_F(CPUTest, LDA_DirectPage) { + cpu.PC = 0x1001; + std::vector data = {0xA5, 0x7F}; + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadByte(0x7F)).WillOnce(Return(0x80)); + + cpu.SetAccumulatorSize(true); + 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.PC = 0x1001; + std::vector data = {0xAD, 0x7F, 0xFF}; + mock_memory.SetMemoryContents(data); + mock_memory.InsertMemory(0x7FFF, {0x7F}); + + EXPECT_CALL(mock_memory, ReadWord(0x1001)).WillOnce(Return(0x7FFF)); + + EXPECT_CALL(mock_memory, ReadByte(0x7FFF)).WillOnce(Return(0x7F)); + + cpu.SetAccumulatorSize(true); + cpu.ExecuteInstruction(0xAD); // LDA Absolute + EXPECT_EQ(cpu.A, 0x7F); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} +*/ + // ============================================================================ // Stack Tests