diff --git a/src/cpu.cpp b/src/cpu.cpp index deea920..d6bc2eb 100644 --- a/src/cpu.cpp +++ b/src/cpu.cpp @@ -142,9 +142,86 @@ uint16_t CPU::PopWord() { return (high << 8) | low; } +void CPU::DoADC(uint16_t value) { + uint32_t result; + + if (P & FLAG_M) { + // 8-bit mode + const uint8_t acc_low = A & 0xFF; + const uint8_t val_low = value & 0xFF; + + if (P & FLAG_D) { + // Decimal mode + result = acc_low + val_low + (P & FLAG_C ? 1 : 0); + result = AdjustDecimal(result, false); + } else { + // Binary mode + result = acc_low + val_low + (P & FLAG_C ? 1 : 0); + } + + P = (P & ~FLAG_C) | (result > 0xFF ? FLAG_C : 0); + P = (P & ~FLAG_V) | (((acc_low ^ result) & (val_low ^ result) & 0x80) ? FLAG_V : 0); + + A = (A & 0xFF00) | (result & 0xFF); + UpdateNZ8(A & 0xFF); + + } else { + // 16-bit mode + if (P & FLAG_D) { + // Decimal mode + result = A + value + (P & FLAG_C ? 1 : 0); + result = AdjustDecimal(result, true); + } else { + // Binary mode + result = A + value + (P & FLAG_C ? 1 : 0); + } + + P = (P & ~FLAG_C) | (result > 0xFFFF ? FLAG_C : 0); + P = (P & ~FLAG_V) | (((A ^ result) & (value ^ result) & 0x8000) ? FLAG_V : 0); + + A = result & 0xFFFF; + UpdateNZ16(A); + } +} + +uint16_t CPU::AdjustDecimal(const uint16_t binary_result, const bool is_16bit) { + //TODO: Make sure this works? I'm not sure I understand. + if (is_16bit) { + // 16-bit decimal adjustment + uint16_t result = binary_result; + if ((result & 0x0F) > 0x09) result += 0x06; + if ((result & 0xF0) > 0x90) result += 0x60; + if ((result & 0x0F00) > 0x0900) result += 0x0600; + if ((result & 0xF000) > 0x9000) result += 0x6000; + return result; + } + // 8-bit decimal adjustment + uint16_t result = binary_result; + if ((result & 0x0F) > 0x09) result += 0x06; + if ((result & 0xF0) > 0x90) result += 0x60; + return result; +} + void CPU::ExecuteInstruction() { // TODO: Actual Opcode decoding switch (const uint8_t opcode = bus->Read(PC++)) { + // ADC - Add with Carry + case 0x69: ADC_Immediate(); break; // ADC #$nn/#$nnnn + case 0x6D: ADC_Absolute(); break; // ADC $nnnn + case 0x7D: ADC_AbsoluteX(); break; // ADC $nnnn,X + case 0x79: ADC_AbsoluteY(); break; // ADC $nnnn,Y + case 0x6F: ADC_AbsoluteLong(); break; // ADC $nnnnnn + case 0x7F: ADC_AbsoluteLongX(); break; // ADC $nnnnnn,X + case 0x65: ADC_DirectPage(); break; // ADC $nn + case 0x75: ADC_DirectPageX(); break; // ADC $nn,X + case 0x72: ADC_IndirectDirectPage(); break; // ADC ($nn) + case 0x71: ADC_IndirectDirectPageY(); break; // ADC ($nn),Y + case 0x61: ADC_DirectPageIndirectX(); break; // ADC ($nn,X) + case 0x67: ADC_DirectPageIndirectLong(); break; // ADC [$nn] + case 0x77: ADC_DirectPageIndirectLongY(); break;// ADC [$nn],Y + case 0x63: ADC_StackRelative(); break; // ADC $nn,S + case 0x73: ADC_StackRelativeIndirectY(); break; // ADC ($nn,S),Y + // Branch Instructions case 0xF0: BEQ_Relative(); break; // BEQ $nn case 0xD0: BNE_Relative(); break; // BNE $nn @@ -1743,4 +1820,310 @@ void CPU::PLD() { void CPU::PHK() { PushByte(PB); cycles += 3; +} + +void CPU::ADC_Immediate() { + if (P & FLAG_M) { + // 8-bit immediate + const uint8_t value = ReadByte(PC); + PC++; + DoADC(value); + cycles += 2; + } else { + // 16-bit immediate + const uint16_t value = ReadWord(PC); + PC += 2; + DoADC(value); + cycles += 3; + } +} + +void CPU::ADC_Absolute() { + const uint16_t address = ReadWord(PC); + PC += 2; + + const uint32_t full_address = (static_cast(DB) << 16) | address; + + if (P & FLAG_M) { + const uint8_t value = ReadByte(full_address); + DoADC(value); + cycles += 4; + } else { + const uint16_t value = ReadWord(full_address); + DoADC(value); + cycles += 5; + } +} + +void CPU::ADC_AbsoluteX() { + const uint16_t base_address = ReadWord(PC); + PC += 2; + + const uint32_t full_address = (static_cast(DB) << 16) | ((base_address + X) & 0xFFFF); + + if (P & FLAG_M) { + const uint8_t value = ReadByte(full_address); + DoADC(value); + cycles += 4; + if ((base_address & 0xFF00) != ((base_address + X) & 0xFF00)) { + cycles++; + } + } else { + const uint16_t value = ReadWord(full_address); + DoADC(value); + cycles += 5; + if ((base_address & 0xFF00) != ((base_address + X) & 0xFF00)) { + cycles++; + } + } +} + +void CPU::ADC_AbsoluteY() { + const uint16_t base_address = ReadWord(PC); + PC += 2; + + const uint32_t full_address = (static_cast(DB) << 16) | ((base_address + Y) & 0xFFFF); + + if (P & FLAG_M) { + const uint8_t value = ReadByte(full_address); + DoADC(value); + cycles += 4; + if ((base_address & 0xFF00) != ((base_address + Y) & 0xFF00)) { + cycles++; + } + } else { + const uint16_t value = ReadWord(full_address); + DoADC(value); + cycles += 5; + if ((base_address & 0xFF00) != ((base_address + Y) & 0xFF00)) { + cycles++; + } + } +} + +void CPU::ADC_DirectPage() { + const uint8_t offset = ReadByte(PC); + PC++; + + const uint32_t address = D + offset; + + if (P & FLAG_M) { + const uint8_t value = ReadByte(address); + DoADC(value); + cycles += 3; + if (D & 0xFF) cycles++; + } else { + const uint16_t value = ReadWord(address); + DoADC(value); + cycles += 4; + if (D & 0xFF) cycles++; + } +} + +void CPU::ADC_DirectPageX() { + const uint8_t offset = ReadByte(PC); + PC++; + + const uint32_t address = D + offset + X; + + if (P & FLAG_M) { + const uint8_t value = ReadByte(address); + DoADC(value); + cycles += 4; + if (D & 0xFF) cycles++; + } else { + const uint16_t value = ReadWord(address); + DoADC(value); + cycles += 5; + if (D & 0xFF) cycles++; + } +} + +void CPU::ADC_IndirectDirectPage() { + const uint8_t offset = ReadByte(PC); + PC++; + + const uint32_t pointer_address = D + offset; + const uint16_t target_address = ReadWord(pointer_address); + const uint32_t full_address = (static_cast(DB) << 16) | target_address; + + if (P & FLAG_M) { + const uint8_t value = ReadByte(full_address); + DoADC(value); + cycles += 5; + if (D & 0xFF) cycles++; + } else { + const uint16_t value = ReadWord(full_address); + DoADC(value); + cycles += 6; + if (D & 0xFF) cycles++; + } +} + +void CPU::ADC_IndirectDirectPageY() { + const uint8_t offset = ReadByte(PC); + PC++; + + const uint32_t pointer_address = D + offset; + const uint16_t base_address = ReadWord(pointer_address); + const uint32_t full_address = (static_cast(DB) << 16) | ((base_address + Y) & 0xFFFF); + + if (P & FLAG_M) { + const uint8_t value = ReadByte(full_address); + DoADC(value); + cycles += 5; + if (D & 0xFF) cycles++; + if ((base_address & 0xFF00) != ((base_address + Y) & 0xFF00)) { + cycles++; + } + } else { + const uint16_t value = ReadWord(full_address); + DoADC(value); + cycles += 6; + if (D & 0xFF) cycles++; + if ((base_address & 0xFF00) != ((base_address + Y) & 0xFF00)) { + cycles++; + } + } +} + +void CPU::ADC_DirectPageIndirectX() { + const uint8_t offset = ReadByte(PC); + PC++; + + const uint32_t pointer_address = D + offset + X; + const uint16_t target_address = ReadWord(pointer_address); + const uint32_t full_address = (static_cast(DB) << 16) | target_address; + + if (P & FLAG_M) { + const uint8_t value = ReadByte(full_address); + DoADC(value); + cycles += 6; + if (D & 0xFF) cycles++; + } else { + const uint16_t value = ReadWord(full_address); + DoADC(value); + cycles += 7; + if (D & 0xFF) cycles++; + } +} + +void CPU::ADC_AbsoluteLong() { + const uint16_t addr_low = ReadWord(PC); + PC += 2; + const uint8_t addr_high = ReadByte(PC); + PC++; + + const uint32_t full_address = (static_cast(addr_high) << 16) | addr_low; + + if (P & FLAG_M) { + const uint8_t value = ReadByte(full_address); + DoADC(value); + cycles += 5; + } else { + const uint16_t value = ReadWord(full_address); + DoADC(value); + cycles += 6; + } +} + +void CPU::ADC_AbsoluteLongX() { + const uint16_t addr_low = ReadWord(PC); + PC += 2; + const uint8_t addr_high = ReadByte(PC); + PC++; + + const uint32_t base_address = (static_cast(addr_high) << 16) | addr_low; + const uint32_t full_address = base_address + X; + + if (P & FLAG_M) { + const uint8_t value = ReadByte(full_address); + DoADC(value); + cycles += 5; + } else { + const uint16_t value = ReadWord(full_address); + DoADC(value); + cycles += 6; + } +} + +void CPU::ADC_DirectPageIndirectLong() { + const uint8_t offset = ReadByte(PC); + PC++; + + const uint32_t pointer_address = D + offset; + const uint16_t addr_low = ReadWord(pointer_address); + const uint8_t addr_high = ReadByte(pointer_address + 2); + const uint32_t target_address = (static_cast(addr_high) << 16) | addr_low; + + if (P & FLAG_M) { + const uint8_t value = ReadByte(target_address); + DoADC(value); + cycles += 6; + if (D & 0xFF) cycles++; + } else { + const uint16_t value = ReadWord(target_address); + DoADC(value); + cycles += 7; + if (D & 0xFF) cycles++; + } +} + +void CPU::ADC_DirectPageIndirectLongY() { + const uint8_t offset = ReadByte(PC); + PC++; + + const uint32_t pointer_address = D + offset; + const uint16_t addr_low = ReadWord(pointer_address); + const uint8_t addr_high = ReadByte(pointer_address + 2); + const uint32_t base_address = (static_cast(addr_high) << 16) | addr_low; + const uint32_t target_address = base_address + Y; + + if (P & FLAG_M) { + const uint8_t value = ReadByte(target_address); + DoADC(value); + cycles += 6; + if (D & 0xFF) cycles++; + } else { + const uint16_t value = ReadWord(target_address); + DoADC(value); + cycles += 7; + if (D & 0xFF) cycles++; + } +} + +void CPU::ADC_StackRelative() { + const uint8_t offset = ReadByte(PC); + PC++; + + const uint32_t address = SP + offset; + + if (P & FLAG_M) { + const uint8_t value = ReadByte(address); + DoADC(value); + cycles += 4; + } else { + const uint16_t value = ReadWord(address); + DoADC(value); + cycles += 5; + } +} + +void CPU::ADC_StackRelativeIndirectY() { + const uint8_t offset = ReadByte(PC); + PC++; + + const uint32_t pointer_address = SP + offset; + const uint16_t base_address = ReadWord(pointer_address); + const uint32_t target_address = (static_cast(DB) << 16) | ((base_address + Y) & 0xFFFF); + + if (P & FLAG_M) { + const uint8_t value = ReadByte(target_address); + DoADC(value); + cycles += 7; + } else { + const uint16_t value = ReadWord(target_address); + DoADC(value); + cycles += 8; + } } \ No newline at end of file diff --git a/src/cpu.h b/src/cpu.h index 2b3e64c..afbc2d4 100644 --- a/src/cpu.h +++ b/src/cpu.h @@ -69,6 +69,12 @@ public: uint8_t PopByte(); uint16_t PopWord(); + // Helper method for ADC instructions + void DoADC(uint16_t value); + + // Helper method to check for decimal mode adjustment + static uint16_t AdjustDecimal(uint16_t binary_result, bool is_16bit); + // Instruction implementations // TODO: Implement remaining instructions void CMP_Immediate(); @@ -184,6 +190,22 @@ public: void PHD(); void PLD(); void PHK(); + + void ADC_Immediate(); + void ADC_Absolute(); + void ADC_AbsoluteX(); + void ADC_AbsoluteY(); + void ADC_DirectPage(); + void ADC_DirectPageX(); + void ADC_IndirectDirectPage(); + void ADC_IndirectDirectPageY(); + void ADC_DirectPageIndirectX(); + void ADC_AbsoluteLong(); + void ADC_AbsoluteLongX(); + void ADC_DirectPageIndirectLong(); + void ADC_DirectPageIndirectLongY(); + void ADC_StackRelative(); + void ADC_StackRelativeIndirectY(); }; #endif //CPU_H