// // Created by Palindromic Bread Loaf on 7/21/25. // #include "cpu.h" #include // CPU Implementation void CPU::Reset() { A = X = Y = 0; SP = 0x01FF; PC = 0x8000; // Will be loaded from reset vector P = 0x34; // Start in emulation mode DB = PB = 0; D = 0; cycles = 0; } void CPU::Step() { ExecuteInstruction(); } // CPU Helper Methods uint8_t CPU::ReadByte(const uint32_t address) { cycles++; return bus->Read(address); } uint16_t CPU::ReadWord(const uint32_t address) { const uint8_t low = ReadByte(address); const uint8_t high = ReadByte(address + 1); return (high << 8) | low; } void CPU::UpdateNZ8(const uint8_t value) { P = (P & ~FLAG_N) | (value & 0x80); // Set N flag if bit 7 is set P = (P & ~FLAG_Z) | (value == 0 ? FLAG_Z : 0); // Set Z flag if value is zero } void CPU::UpdateNZ16(const uint16_t value) { P = (P & ~FLAG_N) | ((value & 0x8000) ? FLAG_N : 0); // Set N flag if bit 15 is set P = (P & ~FLAG_Z) | (value == 0 ? FLAG_Z : 0); // Set Z flag if value is zero } // Helper method to write bytes/words to memory void CPU::WriteByte(const uint32_t address, const uint8_t value) const { bus->Write(address, value); } void CPU::WriteWord(const uint32_t address, const uint16_t value) const { bus->Write(address, value & 0xFF); // Low byte bus->Write(address + 1, (value >> 8) & 0xFF); // High byte } // Helper function to update flags after compare operation void CPU::UpdateCompareFlags8(const uint8_t reg_value, const uint8_t compare_value) { const uint16_t result = reg_value - compare_value; // Set/clear flags if (result & 0x100) { P &= ~FLAG_C; } else { P |= FLAG_C; } if ((result & 0xFF) == 0) { P |= FLAG_Z; } else { P &= ~FLAG_Z; } if (result & 0x80) { P |= FLAG_N; } else { P &= ~FLAG_N; } } void CPU::UpdateCompareFlags16(const uint16_t reg_value, const uint16_t compare_value) { const uint32_t result = reg_value - compare_value; // Set/clear flags if (result & 0x10000) { P &= ~FLAG_C; } else { P |= FLAG_C; } if ((result & 0xFFFF) == 0) { P |= FLAG_Z; } else { P &= ~FLAG_Z; } if (result & 0x8000) { P |= FLAG_N; } else { P &= ~FLAG_N; } } void CPU::DoBranch(const bool condition) { int8_t displacement = static_cast(ReadByte(PC)); PC++; if (condition) { // Save the current PC for page boundary check const uint32_t old_pc = PC; PC = static_cast(static_cast(PC) + displacement); cycles++; if ((old_pc & 0xFF00) != (PC & 0xFF00)) cycles++; // Add one cycle for crossing a page boundary } } void CPU::ExecuteInstruction() { // TODO: Actual Opcode decoding switch (const uint8_t opcode = bus->Read(PC++)) { // Branch Instructions case 0xF0: BEQ_Relative(); break; // BEQ $nn case 0xD0: BNE_Relative(); break; // BNE $nn case 0x90: BCC_Relative(); break; // BCC $nn case 0xB0: BCS_Relative(); break; // BCS $nn case 0x30: BMI_Relative(); break; // BMI $nn case 0x10: BPL_Relative(); break; // BPL $nn // CMP - Compare Accumulator case 0xC9: CMP_Immediate(); break; // CMP #$nn or #$nnnn case 0xCD: CMP_Absolute(); break; // CMP $nnnn case 0xDD: CMP_AbsoluteX(); break; // CMP $nnnn,X case 0xD9: CMP_AbsoluteY(); break; // CMP $nnnn,Y case 0xC5: CMP_DirectPage(); break; // CMP $nn case 0xD5: CMP_DirectPageX(); break; // CMP $nn,X case 0xD2: CMP_IndirectDirectPage(); break; // CMP ($nn) case 0xD1: CMP_IndirectDirectPageY(); break; // CMP ($nn),Y case 0xC1: CMP_DirectPageIndirectX(); break; // CMP ($nn,X) case 0xCF: CMP_Long(); break; // CMP $nnnnnn case 0xDF: CMP_LongX(); break; // CMP $nnnnnn,X // CPX - Compare X Register case 0xE0: CPX_Immediate(); break; // CPX #$nn or #$nnnn case 0xEC: CPX_Absolute(); break; // CPX $nnnn case 0xE4: CPX_DirectPage(); break; // CPX $nn // CPY - Compare Y Register case 0xC0: CPY_Immediate(); break; // CPY #$nn or #$nnnn case 0xCC: CPY_Absolute(); break; // CPY $nnnn case 0xC4: CPY_DirectPage(); break; // CPY $nn // DEC - Decrement Memory case 0x3A: DEC_Accumulator(); break; // DEC A case 0xCE: DEC_Absolute(); break; // DEC $nnnn case 0xDE: DEC_AbsoluteX(); break; // DEC $nnnn,X case 0xC6: DEC_DirectPage(); break; // DEC $nn case 0xD6: DEC_DirectPageX(); break; // DEC $nn,X // JMP - Jump to an Address case 0x4C: JMP_Absolute(); break; // JMP $nnnn case 0x6C: JMP_AbsoluteIndirect(); break; // JMP ($nnnn) case 0x5C: JMP_AbsoluteLong(); break; // JMP $nnnnnn case 0x7C: JMP_AbsoluteIndirectX(); break; // JMP ($nnnn,X) // Single Register Decrement case 0xCA: DEX(); break; // DEX - Decrement X Register case 0x88: DEY(); break; // DEY - Decrement Y Register // INC - Increment Memory case 0x1A: INC_Accumulator(); break; // INC A case 0xEE: INC_Absolute(); break; // INC $nnnn case 0xFE: INC_AbsoluteX(); break; // INC $nnnn,X case 0xE6: INC_DirectPage(); break; // INC $nn case 0xF6: INC_DirectPageX(); break; // INC $nn,X // Single Register Increment case 0xE8: INX(); break; // INX - Increment X Register case 0xC8: INY(); break; // INY - Increment Y Register // No Operation case 0xEA: NOP(); break; //NOP // LDA - Load Accumulator case 0xA9: LDA_Immediate(); break; // LDA #$nn or #$nnnn case 0xAD: LDA_Absolute(); break; // LDA $nnnn case 0xBD: LDA_AbsoluteX(); break; // LDA $nnnn,X case 0xB9: LDA_AbsoluteY(); break; // LDA $nnnn,Y case 0xA5: LDA_DirectPage(); break; // LDA $nn case 0xB5: LDA_DirectPageX(); break; // LDA $nn,X case 0xB2: LDA_IndirectDirectPage(); break; // LDA ($nn) case 0xB1: LDA_IndirectDirectPageY(); break; // LDA ($nn),Y case 0xA1: LDA_DirectPageIndirectX(); break; // LDA ($nn,X) case 0xAF: LDA_Long(); break; // LDA $nnnnnn case 0xBF: LDA_LongX(); break; // LDA $nnnnnn,X // LDX - Load X Register case 0xA2: LDX_Immediate(); break; // LDX #$nn or LDX #$nnnn case 0xAE: LDX_Absolute(); break; // LDX $nnnn case 0xBE: LDX_AbsoluteY(); break; // LDX $nnnn,Y case 0xA6: LDX_DirectPage(); break; // LDX $nn case 0xB6: LDX_DirectPageY(); break; // LDX $nn,Y // LDY - Load Y Register case 0xA0: LDY_Immediate(); break; // LDY #$nn or LDY #$nnnn case 0xAC: LDY_Absolute(); break; // LDY $nnnn case 0xBC: LDY_AbsoluteX(); break; // LDY $nnnn,X case 0xA4: LDY_DirectPage(); break; // LDY $nn case 0xB4: LDY_DirectPageX(); break; // LDY $nn,X //SDA - Store Accumulator case 0x8D: STA_Absolute(); break; // STA $nnnn case 0x9D: STA_AbsoluteX(); break; // STA $nnnn,X case 0x99: STA_AbsoluteY(); break; // STA $nnnn,Y case 0x85: STA_DirectPage(); break; // STA $nn case 0x95: STA_DirectPageX(); break; // STA $nn,X case 0x92: STA_IndirectDirectPage(); break; // STA ($nn) case 0x91: STA_IndirectDirectPageY(); break; // STA ($nn),Y case 0x81: STA_DirectPageIndirectX(); break; // STA ($nn,X) case 0x8F: STA_Long(); break; // STA $nnnnnn case 0x9F: STA_LongX(); break; // STA $nnnnnn,X //SDX - Store X Register case 0x8E: STX_Absolute(); break; // STX $nnnn case 0x86: STX_DirectPage(); break; // STX $nn case 0x96: STX_DirectPageY(); break; // STX $nn,Y // SDY - Store Y Register case 0x8C: STY_Absolute(); break; // STY $nnnn case 0x84: STY_DirectPage(); break; // STY $nn case 0x94: STY_DirectPageX(); break; // STY $nn,X default: std::cout << "Unknown opcode: 0x" << std::hex << static_cast(opcode) << std::endl; break; } } void CPU::NOP() { // No operation } // Load Accumulator Instructions void CPU::LDA_Immediate() { if (P & FLAG_M) { // 8-bit accumulator mode const uint8_t value = ReadByte(PC++); A = (A & 0xFF00) | value; // Keep high byte, update low byte UpdateNZ8(value); cycles += 2; } else { // 16-bit accumulator mode A = ReadWord(PC); PC += 2; UpdateNZ16(A); cycles += 3; } } void CPU::LDA_Absolute() { const uint16_t address = ReadWord(PC); PC += 2; if (P & FLAG_M) { // 8-bit mode const uint8_t value = ReadByte(address); A = (A & 0xFF00) | value; UpdateNZ8(value); cycles += 4; } else { // 16-bit mode A = ReadWord(address); UpdateNZ16(A); cycles += 5; } } void CPU::LDA_AbsoluteX() { const uint16_t base_address = ReadWord(PC); PC += 2; const uint32_t address = base_address + X; // Page boundary crossing adds a cycle in some cases if ((base_address & 0xFF00) != (address & 0xFF00)) { cycles++; } if (P & FLAG_M) { // 8-bit mode const uint8_t value = ReadByte(address); A = (A & 0xFF00) | value; UpdateNZ8(value); cycles += 4; } else { // 16-bit mode A = ReadWord(address); UpdateNZ16(A); cycles += 5; } } void CPU::LDA_AbsoluteY() { uint16_t base_address = ReadWord(PC); PC += 2; uint32_t address = base_address + Y; // Page boundary crossing adds a cycle if ((base_address & 0xFF00) != (address & 0xFF00)) { cycles++; } if (P & FLAG_M) { // 8-bit mode uint8_t value = ReadByte(address); A = (A & 0xFF00) | value; UpdateNZ8(value); cycles += 4; } else { // 16-bit mode A = ReadWord(address); UpdateNZ16(A); cycles += 5; } } void CPU::LDA_DirectPage() { const uint8_t offset = ReadByte(PC++); const uint32_t address = D + offset; if (P & FLAG_M) { // 8-bit mode uint8_t value = ReadByte(address); A = (A & 0xFF00) | value; UpdateNZ8(value); cycles += 3; } else { // 16-bit mode A = ReadWord(address); UpdateNZ16(A); cycles += 4; } // Extra cycle if D register is not page-aligned if (D & 0xFF) cycles++; } void CPU::LDA_DirectPageX() { const uint8_t offset = ReadByte(PC++); const uint32_t address = D + offset + (P & FLAG_X ? (X & 0xFF) : X); if (P & FLAG_M) { // 8-bit mode const uint8_t value = ReadByte(address); A = (A & 0xFF00) | value; UpdateNZ8(value); cycles += 4; } else { // 16-bit mode A = ReadWord(address); UpdateNZ16(A); cycles += 5; } // Extra cycle if D register is not page-aligned if (D & 0xFF) cycles++; } void CPU::LDA_IndirectDirectPage() { const uint8_t offset = ReadByte(PC++); const uint32_t pointer_address = D + offset; const uint16_t address = ReadWord(pointer_address); if (P & FLAG_M) { // 8-bit mode const uint8_t value = ReadByte(address); A = (A & 0xFF00) | value; UpdateNZ8(value); cycles += 5; } else { // 16-bit mode A = ReadWord(address); UpdateNZ16(A); cycles += 6; } // Extra cycle if D register is not page-aligned if (D & 0xFF) cycles++; } void CPU::LDA_IndirectDirectPageY() { const uint8_t offset = ReadByte(PC++); const uint32_t pointer_address = D + offset; const uint16_t base_address = ReadWord(pointer_address); const uint32_t address = base_address + Y; // Page boundary crossing adds a cycle if ((base_address & 0xFF00) != (address & 0xFF00)) { cycles++; } if (P & FLAG_M) { // 8-bit mode const uint8_t value = ReadByte(address); A = (A & 0xFF00) | value; UpdateNZ8(value); cycles += 5; } else { // 16-bit mode A = ReadWord(address); UpdateNZ16(A); cycles += 6; } // Extra cycle if D register is not page-aligned if (D & 0xFF) cycles++; } void CPU::LDA_DirectPageIndirectX() { const uint8_t offset = ReadByte(PC++); const uint32_t pointer_address = D + offset + (P & FLAG_X ? (X & 0xFF) : X); const uint16_t address = ReadWord(pointer_address); if (P & FLAG_M) { // 8-bit mode uint8_t value = ReadByte(address); A = (A & 0xFF00) | value; UpdateNZ8(value); cycles += 6; } else { // 16-bit mode A = ReadWord(address); UpdateNZ16(A); cycles += 7; } // Extra cycle if D register is not page-aligned if (D & 0xFF) cycles++; } void CPU::LDA_Long() { const uint32_t address = ReadByte(PC++) | (ReadByte(PC++) << 8) | (ReadByte(PC++) << 16); if (P & FLAG_M) { // 8-bit mode const uint8_t value = ReadByte(address); A = (A & 0xFF00) | value; UpdateNZ8(value); cycles += 5; } else { // 16-bit mode A = ReadWord(address); UpdateNZ16(A); cycles += 6; } } void CPU::LDA_LongX() { const uint32_t base_address = ReadByte(PC++) | (ReadByte(PC++) << 8) | (ReadByte(PC++) << 16); const uint32_t address = base_address + X; if (P & FLAG_M) { // 8-bit mode const uint8_t value = ReadByte(address); A = (A & 0xFF00) | value; UpdateNZ8(value); cycles += 5; } else { // 16-bit mode A = ReadWord(address); UpdateNZ16(A); cycles += 6; } } // Load X Register Instructions void CPU::LDX_Immediate() { if (P & FLAG_X) { // 8-bit index mode X = ReadByte(PC++); UpdateNZ8(X & 0xFF); cycles += 2; } else { // 16-bit index mode X = ReadWord(PC); PC += 2; UpdateNZ16(X); cycles += 3; } } void CPU::LDX_Absolute() { const uint16_t address = ReadWord(PC); PC += 2; if (P & FLAG_X) { // 8-bit mode X = ReadByte(address); UpdateNZ8(X & 0xFF); cycles += 4; } else { // 16-bit mode X = ReadWord(address); UpdateNZ16(X); cycles += 5; } } void CPU::LDX_AbsoluteY() { const uint16_t base_address = ReadWord(PC); PC += 2; const uint32_t address = base_address + Y; // Page boundary crossing adds a cycle if ((base_address & 0xFF00) != (address & 0xFF00)) { cycles++; } if (P & FLAG_X) { // 8-bit mode X = ReadByte(address); UpdateNZ8(X & 0xFF); cycles += 4; } else { // 16-bit mode X = ReadWord(address); UpdateNZ16(X); cycles += 5; } } void CPU::LDX_DirectPage() { const uint8_t offset = ReadByte(PC++); const uint32_t address = D + offset; if (P & FLAG_X) { // 8-bit mode X = ReadByte(address); UpdateNZ8(X & 0xFF); cycles += 3; } else { // 16-bit mode X = ReadWord(address); UpdateNZ16(X); cycles += 4; } // Extra cycle if D register is not page-aligned if (D & 0xFF) cycles++; } void CPU::LDX_DirectPageY() { const uint8_t offset = ReadByte(PC++); const uint32_t address = D + offset + (P & FLAG_X ? (Y & 0xFF) : Y); if (P & FLAG_X) { // 8-bit mode X = ReadByte(address); UpdateNZ8(X & 0xFF); cycles += 4; } else { // 16-bit mode X = ReadWord(address); UpdateNZ16(X); cycles += 5; } // Extra cycle if D register is not page-aligned if (D & 0xFF) cycles++; } // Load Y Register Instructions void CPU::LDY_Immediate() { if (P & FLAG_X) { // 8-bit index mode Y = ReadByte(PC++); UpdateNZ8(Y & 0xFF); cycles += 2; } else { // 16-bit index mode Y = ReadWord(PC); PC += 2; UpdateNZ16(Y); cycles += 3; } } void CPU::LDY_Absolute() { const uint16_t address = ReadWord(PC); PC += 2; if (P & FLAG_X) { // 8-bit mode Y = ReadByte(address); UpdateNZ8(Y & 0xFF); cycles += 4; } else { // 16-bit mode Y = ReadWord(address); UpdateNZ16(Y); cycles += 5; } } void CPU::LDY_AbsoluteX() { const uint16_t base_address = ReadWord(PC); PC += 2; const uint32_t address = base_address + X; // Page boundary crossing adds a cycle if ((base_address & 0xFF00) != (address & 0xFF00)) { cycles++; } if (P & FLAG_X) { // 8-bit mode Y = ReadByte(address); UpdateNZ8(Y & 0xFF); cycles += 4; } else { // 16-bit mode Y = ReadWord(address); UpdateNZ16(Y); cycles += 5; } } void CPU::LDY_DirectPage() { const uint8_t offset = ReadByte(PC++); const uint32_t address = D + offset; if (P & FLAG_X) { // 8-bit mode Y = ReadByte(address); UpdateNZ8(Y & 0xFF); cycles += 3; } else { // 16-bit mode Y = ReadWord(address); UpdateNZ16(Y); cycles += 4; } // Extra cycle if D register is not page-aligned if (D & 0xFF) cycles++; } void CPU::LDY_DirectPageX() { const uint8_t offset = ReadByte(PC++); const uint32_t address = D + offset + (P & FLAG_X ? (X & 0xFF) : X); if (P & FLAG_X) { // 8-bit mode Y = ReadByte(address); UpdateNZ8(Y & 0xFF); cycles += 4; } else { // 16-bit mode Y = ReadWord(address); UpdateNZ16(Y); cycles += 5; } // Extra cycle if D register is not page-aligned if (D & 0xFF) cycles++; } //Store operations implementation void CPU::STA_Absolute() { const uint32_t address = ReadWord(PC + 1) | (DB << 16); PC += 3; if (P & FLAG_M) { // 8-bit mode WriteByte(address, A & 0xFF); cycles += 4; } else { // 16-bit mode WriteWord(address, A); cycles += 5; } } void CPU::STA_AbsoluteX() { const uint32_t base = ReadWord(PC + 1) | (DB << 16); const uint32_t address = base + X; PC += 3; if (P & FLAG_M) { // 8-bit mode WriteByte(address, A & 0xFF); cycles += 5; } else { // 16-bit mode WriteWord(address, A); cycles += 6; } } void CPU::STA_AbsoluteY() { const uint32_t base = ReadWord(PC + 1) | (DB << 16); const uint32_t address = base + Y; PC += 3; if (P & FLAG_M) { // 8-bit mode WriteByte(address, A & 0xFF); cycles += 5; } else { // 16-bit mode WriteWord(address, A); cycles += 6; } } void CPU::STA_DirectPage() { const uint8_t offset = ReadByte(PC + 1); const uint32_t address = (D + offset) & 0xFFFF; PC += 2; if (P & FLAG_M) { // 8-bit mode WriteByte(address, A & 0xFF); cycles += 3; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode WriteWord(address, A); cycles += 4; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::STA_DirectPageX() { const uint8_t offset = ReadByte(PC + 1); const uint32_t address = (D + offset + X) & 0xFFFF; PC += 2; if (P & FLAG_M) { // 8-bit mode WriteByte(address, A & 0xFF); cycles += 4; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode WriteWord(address, A); cycles += 5; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::STA_IndirectDirectPage() { const uint8_t offset = ReadByte(PC + 1); const uint32_t pointer = (D + offset) & 0xFFFF; const uint32_t address = ReadWord(pointer) | (DB << 16); PC += 2; if (P & FLAG_M) { // 8-bit mode WriteByte(address, A & 0xFF); cycles += 5; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode WriteWord(address, A); cycles += 6; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::STA_IndirectDirectPageY() { const uint8_t offset = ReadByte(PC + 1); const uint32_t pointer = (D + offset) & 0xFFFF; const uint32_t base = ReadWord(pointer) | (DB << 16); const uint32_t address = base + Y; PC += 2; if (P & FLAG_M) { // 8-bit mode WriteByte(address, A & 0xFF); cycles += 6; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode WriteWord(address, A); cycles += 7; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::STA_DirectPageIndirectX() { const uint8_t offset = ReadByte(PC + 1); const uint32_t pointer = (D + offset + X) & 0xFFFF; const uint32_t address = ReadWord(pointer) | (DB << 16); PC += 2; if (P & FLAG_M) { // 8-bit mode WriteByte(address, A & 0xFF); cycles += 6; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode WriteWord(address, A); cycles += 7; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::STA_Long() { const uint32_t address = ReadByte(PC + 1) | (ReadByte(PC + 2) << 8) | (ReadByte(PC + 3) << 16); PC += 4; if (P & FLAG_M) { // 8-bit mode WriteByte(address, A & 0xFF); cycles += 5; } else { // 16-bit mode WriteWord(address, A); cycles += 6; } } void CPU::STA_LongX() { const uint32_t base = ReadByte(PC + 1) | (ReadByte(PC + 2) << 8) | (ReadByte(PC + 3) << 16); const uint32_t address = base + X; PC += 4; if (P & FLAG_M) { // 8-bit mode WriteByte(address, A & 0xFF); cycles += 6; } else { // 16-bit mode WriteWord(address, A); cycles += 7; } } // STX - Store X Register void CPU::STX_Absolute() { const uint32_t address = ReadWord(PC + 1) | (DB << 16); PC += 3; if (P & FLAG_X) { // 8-bit mode WriteByte(address, X & 0xFF); cycles += 4; } else { // 16-bit mode WriteWord(address, X); cycles += 5; } } void CPU::STX_DirectPage() { const uint8_t offset = ReadByte(PC + 1); const uint32_t address = (D + offset) & 0xFFFF; PC += 2; if (P & FLAG_X) { // 8-bit mode WriteByte(address, X & 0xFF); cycles += 3; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode WriteWord(address, X); cycles += 4; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::STX_DirectPageY() { uint8_t offset = ReadByte(PC + 1); uint32_t address = (D + offset + Y) & 0xFFFF; PC += 2; if (P & FLAG_X) { // 8-bit mode WriteByte(address, X & 0xFF); cycles += 4; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode WriteWord(address, X); cycles += 5; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } // STY - Store Y Register void CPU::STY_Absolute() { uint32_t address = ReadWord(PC + 1) | (DB << 16); PC += 3; if (P & FLAG_X) { // 8-bit mode WriteByte(address, Y & 0xFF); cycles += 4; } else { // 16-bit mode WriteWord(address, Y); cycles += 5; } } void CPU::STY_DirectPage() { const uint8_t offset = ReadByte(PC + 1); const uint32_t address = (D + offset) & 0xFFFF; PC += 2; if (P & FLAG_X) { // 8-bit mode WriteByte(address, Y & 0xFF); cycles += 3; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode WriteWord(address, Y); cycles += 4; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::STY_DirectPageX() { const uint8_t offset = ReadByte(PC + 1); const uint32_t address = (D + offset + X) & 0xFFFF; PC += 2; if (P & FLAG_X) { // 8-bit mode WriteByte(address, Y & 0xFF); cycles += 4; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode WriteWord(address, Y); cycles += 5; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } // INC - Increment Memory // Important to note: PC doesn't increment in accumulator mode I think void CPU::INC_Accumulator() { if (P & FLAG_M) { // 8-bit mode A = (A & 0xFF00) | ((A + 1) & 0xFF); UpdateNZ8(A & 0xFF); cycles += 2; } else { // 16-bit mode A = (A + 1) & 0xFFFF; UpdateNZ16(A); cycles += 2; } } void CPU::INC_Absolute() { const uint32_t address = ReadWord(PC + 1) | (DB << 16); PC += 3; if (P & FLAG_M) { // 8-bit mode uint8_t value = ReadByte(address); value = (value + 1) & 0xFF; WriteByte(address, value); UpdateNZ8(value); cycles += 6; } else { // 16-bit mode uint16_t value = ReadWord(address); value = (value + 1) & 0xFFFF; WriteWord(address, value); UpdateNZ16(value); cycles += 8; } } void CPU::INC_AbsoluteX() { const uint32_t base = ReadWord(PC + 1) | (DB << 16); const uint32_t address = base + X; PC += 3; if (P & FLAG_M) { // 8-bit mode uint8_t value = ReadByte(address); value = (value + 1) & 0xFF; WriteByte(address, value); UpdateNZ8(value); cycles += 7; } else { // 16-bit mode uint16_t value = ReadWord(address); value = (value + 1) & 0xFFFF; WriteWord(address, value); UpdateNZ16(value); cycles += 9; } } void CPU::INC_DirectPage() { const uint8_t offset = ReadByte(PC + 1); const uint32_t address = (D + offset) & 0xFFFF; PC += 2; if (P & FLAG_M) { // 8-bit mode uint8_t value = ReadByte(address); value = (value + 1) & 0xFF; WriteByte(address, value); UpdateNZ8(value); cycles += 5; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode uint16_t value = ReadWord(address); value = (value + 1) & 0xFFFF; WriteWord(address, value); UpdateNZ16(value); cycles += 7; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::INC_DirectPageX() { const uint8_t offset = ReadByte(PC + 1); const uint32_t address = (D + offset + X) & 0xFFFF; PC += 2; if (P & FLAG_M) { // 8-bit mode uint8_t value = ReadByte(address); value = (value + 1) & 0xFF; WriteByte(address, value); UpdateNZ8(value); cycles += 6; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode uint16_t value = ReadWord(address); value = (value + 1) & 0xFFFF; WriteWord(address, value); UpdateNZ16(value); cycles += 8; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } // DEC - Decrement Memory void CPU::DEC_Accumulator() { if (P & FLAG_M) { // 8-bit mode A = (A & 0xFF00) | ((A - 1) & 0xFF); UpdateNZ8(A & 0xFF); cycles += 2; } else { // 16-bit mode A = (A - 1) & 0xFFFF; UpdateNZ16(A); cycles += 2; } } void CPU::DEC_Absolute() { const uint32_t address = ReadWord(PC + 1) | (DB << 16); PC += 3; if (P & FLAG_M) { // 8-bit mode uint8_t value = ReadByte(address); value = (value - 1) & 0xFF; WriteByte(address, value); UpdateNZ8(value); cycles += 6; } else { // 16-bit mode uint16_t value = ReadWord(address); value = (value - 1) & 0xFFFF; WriteWord(address, value); UpdateNZ16(value); cycles += 8; } } void CPU::DEC_AbsoluteX() { const uint32_t base = ReadWord(PC + 1) | (DB << 16); const uint32_t address = base + X; PC += 3; if (P & FLAG_M) { // 8-bit mode uint8_t value = ReadByte(address); value = (value - 1) & 0xFF; WriteByte(address, value); UpdateNZ8(value); cycles += 7; } else { // 16-bit mode uint16_t value = ReadWord(address); value = (value - 1) & 0xFFFF; WriteWord(address, value); UpdateNZ16(value); cycles += 9; } } void CPU::DEC_DirectPage() { const uint8_t offset = ReadByte(PC + 1); const uint32_t address = (D + offset) & 0xFFFF; PC += 2; if (P & FLAG_M) { // 8-bit mode uint8_t value = ReadByte(address); value = (value - 1) & 0xFF; WriteByte(address, value); UpdateNZ8(value); cycles += 5; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode uint16_t value = ReadWord(address); value = (value - 1) & 0xFFFF; WriteWord(address, value); UpdateNZ16(value); cycles += 7; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::DEC_DirectPageX() { const uint8_t offset = ReadByte(PC + 1); const uint32_t address = (D + offset + X) & 0xFFFF; PC += 2; if (P & FLAG_M) { // 8-bit mode uint8_t value = ReadByte(address); value = (value - 1) & 0xFF; WriteByte(address, value); UpdateNZ8(value); cycles += 6; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode uint16_t value = ReadWord(address); value = (value - 1) & 0xFFFF; WriteWord(address, value); UpdateNZ16(value); cycles += 8; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } // INX - Increment X Register void CPU::INX() { if (P & FLAG_X) { // 8-bit mode X = (X & 0xFF00) | ((X + 1) & 0xFF); UpdateNZ8(X & 0xFF); } else { // 16-bit mode X = (X + 1) & 0xFFFF; UpdateNZ16(X); } cycles += 2; } // INY - Increment Y Register void CPU::INY() { if (P & FLAG_X) { // 8-bit mode Y = (Y & 0xFF00) | ((Y + 1) & 0xFF); UpdateNZ8(Y & 0xFF); } else { // 16-bit mode Y = (Y + 1) & 0xFFFF; UpdateNZ16(Y); } cycles += 2; } // DEX - Decrement X Register void CPU::DEX() { if (P & FLAG_X) { // 8-bit mode X = (X & 0xFF00) | ((X - 1) & 0xFF); UpdateNZ8(X & 0xFF); } else { // 16-bit mode X = (X - 1) & 0xFFFF; UpdateNZ16(X); } cycles += 2; } // DEY - Decrement Y Register void CPU::DEY() { if (P & FLAG_X) { // 8-bit mode Y = (Y & 0xFF00) | ((Y - 1) & 0xFF); UpdateNZ8(Y & 0xFF); } else { // 16-bit mode Y = (Y - 1) & 0xFFFF; UpdateNZ16(Y); } cycles += 2; } // CMP - Compare Accumulator void CPU::CMP_Immediate() { if (P & FLAG_M) { // 8-bit mode const uint8_t operand = ReadByte(PC + 1); UpdateCompareFlags8(A & 0xFF, operand); PC += 2; cycles += 2; } else { // 16-bit mode const uint16_t operand = ReadWord(PC + 1); UpdateCompareFlags16(A, operand); PC += 3; cycles += 3; } } void CPU::CMP_Absolute() { const uint32_t address = ReadWord(PC + 1) | (DB << 16); PC += 3; if (P & FLAG_M) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(A & 0xFF, operand); cycles += 4; } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(A, operand); cycles += 5; } } void CPU::CMP_AbsoluteX() { const uint32_t base = ReadWord(PC + 1) | (DB << 16); const uint32_t address = base + X; PC += 3; if (P & FLAG_M) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(A & 0xFF, operand); cycles += 4; // Add extra cycle if page boundary crossed if ((base & 0xFF00) != (address & 0xFF00)) cycles++; } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(A, operand); cycles += 5; // Add extra cycle if page boundary crossed if ((base & 0xFF00) != (address & 0xFF00)) cycles++; } } void CPU::CMP_AbsoluteY() { const uint32_t base = ReadWord(PC + 1) | (DB << 16); const uint32_t address = base + Y; PC += 3; if (P & FLAG_M) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(A & 0xFF, operand); cycles += 4; // Add extra cycle if page boundary crossed if ((base & 0xFF00) != (address & 0xFF00)) cycles++; } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(A, operand); cycles += 5; // Add extra cycle if page boundary crossed if ((base & 0xFF00) != (address & 0xFF00)) cycles++; } } void CPU::CMP_DirectPage() { const uint8_t offset = ReadByte(PC + 1); const uint32_t address = (D + offset) & 0xFFFF; PC += 2; if (P & FLAG_M) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(A & 0xFF, operand); cycles += 3; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(A, operand); cycles += 4; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::CMP_DirectPageX() { const uint8_t offset = ReadByte(PC + 1); const uint32_t address = (D + offset + X) & 0xFFFF; PC += 2; if (P & FLAG_M) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(A & 0xFF, operand); cycles += 4; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(A, operand); cycles += 5; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::CMP_IndirectDirectPage() { const uint8_t offset = ReadByte(PC + 1); const uint32_t pointer = (D + offset) & 0xFFFF; const uint32_t address = ReadWord(pointer) | (DB << 16); PC += 2; if (P & FLAG_M) { // 8-bit mode uint8_t operand = ReadByte(address); UpdateCompareFlags8(A & 0xFF, operand); cycles += 5; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(A, operand); cycles += 6; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::CMP_IndirectDirectPageY() { const uint8_t offset = ReadByte(PC + 1); const uint32_t pointer = (D + offset) & 0xFFFF; const uint32_t base = ReadWord(pointer) | (DB << 16); const uint32_t address = base + Y; PC += 2; if (P & FLAG_M) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(A & 0xFF, operand); cycles += 5; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 // Add extra cycle if page boundary crossed if ((base & 0xFF00) != (address & 0xFF00)) cycles++; } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(A, operand); cycles += 6; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 // Add extra cycle if page boundary crossed if ((base & 0xFF00) != (address & 0xFF00)) cycles++; } } void CPU::CMP_DirectPageIndirectX() { const uint8_t offset = ReadByte(PC + 1); const uint32_t pointer = (D + offset + X) & 0xFFFF; const uint32_t address = ReadWord(pointer) | (DB << 16); PC += 2; if (P & FLAG_M) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(A & 0xFF, operand); cycles += 6; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(A, operand); cycles += 7; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::CMP_Long() { const uint32_t address = ReadByte(PC + 1) | (ReadByte(PC + 2) << 8) | (ReadByte(PC + 3) << 16); PC += 4; if (P & FLAG_M) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(A & 0xFF, operand); cycles += 5; } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(A, operand); cycles += 6; } } void CPU::CMP_LongX() { const uint32_t base = ReadByte(PC + 1) | (ReadByte(PC + 2) << 8) | (ReadByte(PC + 3) << 16); const uint32_t address = base + X; PC += 4; if (P & FLAG_M) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(A & 0xFF, operand); cycles += 6; } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(A, operand); cycles += 7; } } // CPX - Compare X Register void CPU::CPX_Immediate() { if (P & FLAG_X) { // 8-bit mode const uint8_t operand = ReadByte(PC + 1); UpdateCompareFlags8(X & 0xFF, operand); PC += 2; cycles += 2; } else { // 16-bit mode const uint16_t operand = ReadWord(PC + 1); UpdateCompareFlags16(X, operand); PC += 3; cycles += 3; } } void CPU::CPX_Absolute() { const uint32_t address = ReadWord(PC + 1) | (DB << 16); PC += 3; if (P & FLAG_X) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(X & 0xFF, operand); cycles += 4; } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(X, operand); cycles += 5; } } void CPU::CPX_DirectPage() { const uint8_t offset = ReadByte(PC + 1); const int32_t address = (D + offset) & 0xFFFF; PC += 2; if (P & FLAG_X) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(X & 0xFF, operand); cycles += 3; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(X, operand); cycles += 4; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } // CPY - Compare Y Register void CPU::CPY_Immediate() { if (P & FLAG_X) { // 8-bit mode const uint8_t operand = ReadByte(PC + 1); UpdateCompareFlags8(Y & 0xFF, operand); PC += 2; cycles += 2; } else { // 16-bit mode const uint16_t operand = ReadWord(PC + 1); UpdateCompareFlags16(Y, operand); PC += 3; cycles += 3; } } void CPU::CPY_Absolute() { const uint32_t address = ReadWord(PC + 1) | (DB << 16); PC += 3; if (P & FLAG_X) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(Y & 0xFF, operand); cycles += 4; } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(Y, operand); cycles += 5; } } void CPU::CPY_DirectPage() { const uint8_t offset = ReadByte(PC + 1); const uint32_t address = (D + offset) & 0xFFFF; PC += 2; if (P & FLAG_X) { // 8-bit mode const uint8_t operand = ReadByte(address); UpdateCompareFlags8(Y & 0xFF, operand); cycles += 3; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } else { // 16-bit mode const uint16_t operand = ReadWord(address); UpdateCompareFlags16(Y, operand); cycles += 4; if (D & 0xFF) cycles++; // Extra cycle if D register low byte != 0 } } void CPU::JMP_Absolute() { const uint16_t address = ReadWord(PC); PC += 2; PC = (static_cast(PB) << 16) | address; cycles += 3; } void CPU::JMP_AbsoluteIndirect() { const uint16_t indirect_addr = ReadWord(PC); PC += 2; const uint32_t full_indirect_addr = (static_cast(DB) << 16) | indirect_addr; const uint16_t target_addr = ReadWord(full_indirect_addr); PC = (static_cast(PB) << 16) | target_addr; cycles += 5; } void CPU::JMP_AbsoluteLong() { const uint16_t addr_low = ReadWord(PC); PC += 2; const uint8_t addr_high = ReadByte(PC); PC++; const uint32_t target_addr = (static_cast(addr_high) << 16) | addr_low; PB = addr_high; PC = target_addr; cycles += 4; } void CPU::JMP_AbsoluteIndirectX() { const uint16_t base_addr = ReadWord(PC); PC += 2; const uint32_t indirect_addr = (static_cast(PB) << 16) | (base_addr + X); const uint16_t target_addr = ReadWord(indirect_addr); PC = (static_cast(PB) << 16) | target_addr; cycles += 6; } void CPU::BEQ_Relative() { DoBranch(P & FLAG_Z); } void CPU::BNE_Relative() { DoBranch(!(P & FLAG_Z)); } void CPU::BCC_Relative() { DoBranch(!(P & FLAG_C)); } void CPU::BCS_Relative() { DoBranch(P & FLAG_C); } void CPU::BMI_Relative() { DoBranch(P & FLAG_N); } void CPU::BPL_Relative() { DoBranch(!(P & FLAG_N)); }