Add CMP, COP, DEC, EOR, update SNES and Memory

This commit is contained in:
scawful
2023-08-20 15:39:22 -04:00
parent 1795f8f3bf
commit 7448f80119
6 changed files with 617 additions and 187 deletions

View File

@@ -67,6 +67,22 @@ uint8_t CPU::FetchByteDirectPage(uint8_t operand) {
return fetchedByte; return fetchedByte;
} }
void CPU::Run() {
while (true) {
// Fetch the next opcode from memory at the current program counter
uint8_t opcode = memory.ReadByte(PC);
// Increment the program counter to point to the next instruction
PC++;
// Execute the instruction corresponding to the fetched opcode
ExecuteInstruction(opcode);
// Optionally, handle interrupts or other external events
HandleInterrupts();
}
}
void CPU::ExecuteInstruction(uint8_t opcode) { void CPU::ExecuteInstruction(uint8_t opcode) {
// Update the PC based on the Program Bank Register // Update the PC based on the Program Bank Register
PC |= (static_cast<uint16_t>(PB) << 16); PC |= (static_cast<uint16_t>(PB) << 16);
@@ -262,11 +278,11 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break; break;
case 0x50: // BVC Branch if overflow clear case 0x50: // BVC Branch if overflow clear
// BVC(); BVC(ReadByte(PC));
break; break;
case 0x70: // BVS Branch if overflow set case 0x70: // BVS Branch if overflow set
// BVS(); BVS(ReadByte(PC));
break; break;
case 0x18: // CLC Clear carry case 0x18: // CLC Clear carry
@@ -286,49 +302,49 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break; break;
case 0xC1: // CMP DP Indexed Indirect, X case 0xC1: // CMP DP Indexed Indirect, X
// CMP(); CMP(DirectPageIndexedIndirectX());
break; break;
case 0xC3: // CMP Stack Relative case 0xC3: // CMP Stack Relative
// CMP(); CMP(StackRelative());
break; break;
case 0xC5: // CMP Direct Page case 0xC5: // CMP Direct Page
// CMP(); CMP(DirectPage());
break; break;
case 0xC7: // CMP DP Indirect Long case 0xC7: // CMP DP Indirect Long
// CMP(); CMP(DirectPageIndirectLong());
break; break;
case 0xC9: // CMP Immediate case 0xC9: // CMP Immediate
// CMP(); CMP(Immediate(), true);
break; break;
case 0xCD: // CMP Absolute case 0xCD: // CMP Absolute
// CMP(); CMP(Absolute());
break; break;
case 0xCF: // CMP Absolute Long case 0xCF: // CMP Absolute Long
// CMP(); CMP(AbsoluteLong());
break; break;
case 0xD1: // CMP DP Indirect Indexed, Y case 0xD1: // CMP DP Indirect Indexed, Y
// CMP(); CMP(DirectPageIndirectIndexedY());
break; break;
case 0xD2: // CMP DP Indirect case 0xD2: // CMP DP Indirect
// CMP(); CMP(DirectPageIndirect());
break; break;
case 0xD3: // CMP SR Indirect Indexed, Y case 0xD3: // CMP SR Indirect Indexed, Y
// CMP(); CMP(StackRelativeIndirectIndexedY());
break; break;
case 0xD5: // CMP DP Indexed, X case 0xD5: // CMP DP Indexed, X
// CMP(); CMP(DirectPageIndexedX());
break; break;
case 0xD7: // CMP DP Indirect Long Indexed, Y case 0xD7: // CMP DP Indirect Long Indexed, Y
// CMP(); CMP(DirectPageIndirectLongIndexedY());
break; break;
case 0xD9: // CMP Absolute Indexed, Y case 0xD9: // CMP Absolute Indexed, Y
// CMP(); CMP(AbsoluteIndexedY());
break; break;
case 0xDD: // CMP Absolute Indexed, X case 0xDD: // CMP Absolute Indexed, X
// CMP(); CMP(AbsoluteIndexedX());
break; break;
case 0xDF: // CMP Absolute Long Indexed, X case 0xDF: // CMP Absolute Long Indexed, X
// CMP(); CMP(AbsoluteLongIndexedX());
break; break;
case 0x02: // COP Coprocessor case 0x02: // COP Coprocessor
@@ -356,19 +372,19 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break; break;
case 0x3A: // DEC Accumulator case 0x3A: // DEC Accumulator
// DEC(); DEC(A);
break; break;
case 0xC6: // DEC Direct Page case 0xC6: // DEC Direct Page
// DEC(); DEC(DirectPage());
break; break;
case 0xCE: // DEC Absolute case 0xCE: // DEC Absolute
// DEC(); DEC(Absolute());
break; break;
case 0xD6: // DEC DP Indexed, X case 0xD6: // DEC DP Indexed, X
// DEC(); DEC(DirectPageIndexedX());
break; break;
case 0xDE: // DEC Absolute Indexed, X case 0xDE: // DEC Absolute Indexed, X
// DEC(); DEC(AbsoluteIndexedX());
break; break;
case 0xCA: // DEX Decrement X register case 0xCA: // DEX Decrement X register
@@ -380,49 +396,49 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break; break;
case 0x41: // EOR DP Indexed Indirect, X case 0x41: // EOR DP Indexed Indirect, X
// EOR(); EOR(DirectPageIndexedIndirectX());
break; break;
case 0x43: // EOR Stack Relative case 0x43: // EOR Stack Relative
// EOR(); EOR(StackRelative());
break; break;
case 0x45: // EOR Direct Page case 0x45: // EOR Direct Page
// EOR(); EOR(DirectPage());
break; break;
case 0x47: // EOR DP Indirect Long case 0x47: // EOR DP Indirect Long
// EOR(); EOR(DirectPageIndirectLong());
break; break;
case 0x49: // EOR Immediate case 0x49: // EOR Immediate
// EOR(); EOR(Immediate(), true);
break; break;
case 0x4D: // EOR Absolute case 0x4D: // EOR Absolute
// EOR(); EOR(Absolute());
break; break;
case 0x4F: // EOR Absolute Long case 0x4F: // EOR Absolute Long
// EOR(); EOR(AbsoluteLong());
break; break;
case 0x51: // EOR DP Indirect Indexed, Y case 0x51: // EOR DP Indirect Indexed, Y
// EOR(); EOR(DirectPageIndirectIndexedY());
break; break;
case 0x52: // EOR DP Indirect case 0x52: // EOR DP Indirect
// EOR(); EOR(DirectPageIndirect());
break; break;
case 0x53: // EOR SR Indirect Indexed, Y case 0x53: // EOR SR Indirect Indexed, Y
// EOR(); EOR(StackRelativeIndirectIndexedY());
break; break;
case 0x55: // EOR DP Indexed, X case 0x55: // EOR DP Indexed, X
// EOR(); EOR(DirectPageIndexedX());
break; break;
case 0x57: // EOR DP Indirect Long Indexed, Y case 0x57: // EOR DP Indirect Long Indexed, Y
// EOR(); EOR(DirectPageIndirectLongIndexedY());
break; break;
case 0x59: // EOR Absolute Indexed, Y case 0x59: // EOR Absolute Indexed, Y
// EOR(); EOR(AbsoluteIndexedY());
break; break;
case 0x5D: // EOR Absolute Indexed, X case 0x5D: // EOR Absolute Indexed, X
// EOR(); EOR(AbsoluteIndexedX());
break; break;
case 0x5F: // EOR Absolute Long Indexed, X case 0x5F: // EOR Absolute Long Indexed, X
// EOR(); EOR(AbsoluteLongIndexedX());
break; break;
case 0x1A: // INC Accumulator case 0x1A: // INC Accumulator
@@ -524,51 +540,51 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break; break;
case 0xA2: // LDX Immediate case 0xA2: // LDX Immediate
// LDX(); LDX(Immediate(), true);
break; break;
case 0xA6: // LDX Direct Page case 0xA6: // LDX Direct Page
// LDX(); LDX(DirectPage());
break; break;
case 0xAE: // LDX Absolute case 0xAE: // LDX Absolute
// LDX(); LDX(Absolute());
break; break;
case 0xB6: // LDX DP Indexed, Y case 0xB6: // LDX DP Indexed, Y
// LDX(); LDX(DirectPageIndexedY());
break; break;
case 0xBE: // LDX Absolute Indexed, Y case 0xBE: // LDX Absolute Indexed, Y
// LDX(); LDX(AbsoluteIndexedY());
break; break;
case 0xA0: // LDY Immediate case 0xA0: // LDY Immediate
// LDY(); LDY(Immediate(), true);
break; break;
case 0xA4: // LDY Direct Page case 0xA4: // LDY Direct Page
// LDY(); LDY(DirectPage());
break; break;
case 0xAC: // LDY Absolute case 0xAC: // LDY Absolute
// LDY(); LDY(Absolute());
break; break;
case 0xB4: // LDY DP Indexed, X case 0xB4: // LDY DP Indexed, X
// LDY(); LDY(DirectPageIndexedX());
break; break;
case 0xBC: // LDY Absolute Indexed, X case 0xBC: // LDY Absolute Indexed, X
// LDY(); LDY(AbsoluteIndexedX());
break; break;
case 0x46: // LSR Direct Page case 0x46: // LSR Direct Page
// LSR(); LSR(DirectPage());
break; break;
case 0x4A: // LSR Accumulator case 0x4A: // LSR Accumulator
// LSR(); LSR(A);
break; break;
case 0x4E: // LSR Absolute case 0x4E: // LSR Absolute
// LSR(); LSR(Absolute());
break; break;
case 0x56: // LSR DP Indexed, X case 0x56: // LSR DP Indexed, X
// LSR(); LSR(DirectPageIndexedX());
break; break;
case 0x5E: // LSR Absolute Indexed, X case 0x5E: // LSR Absolute Indexed, X
// LSR(); LSR(AbsoluteIndexedX());
break; break;
case 0x54: // MVN Block Move Next case 0x54: // MVN Block Move Next
@@ -580,61 +596,61 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break; break;
case 0x01: // ORA DP Indexed Indirect, X case 0x01: // ORA DP Indexed Indirect, X
// ORA(); ORA(DirectPageIndexedIndirectX());
break; break;
case 0x03: // ORA Stack Relative case 0x03: // ORA Stack Relative
// ORA(); ORA(StackRelative());
break; break;
case 0x05: // ORA Direct Page case 0x05: // ORA Direct Page
// ORA(); ORA(DirectPage());
break; break;
case 0x07: // ORA DP Indirect Long case 0x07: // ORA DP Indirect Long
// ORA(); ORA(DirectPageIndirectLong());
break; break;
case 0x09: // ORA Immediate case 0x09: // ORA Immediate
// ORA(); ORA(Immediate(), true);
break; break;
case 0x0D: // ORA Absolute case 0x0D: // ORA Absolute
// ORA(); ORA(Absolute());
break; break;
case 0x0F: // ORA Absolute Long case 0x0F: // ORA Absolute Long
// ORA(); ORA(AbsoluteLong());
break; break;
case 0x11: // ORA DP Indirect Indexed, Y case 0x11: // ORA DP Indirect Indexed, Y
// ORA(); ORA(DirectPageIndirectIndexedY());
break; break;
case 0x12: // ORA DP Indirect case 0x12: // ORA DP Indirect
// ORA(); ORA(DirectPageIndirect());
break; break;
case 0x13: // ORA SR Indirect Indexed, Y case 0x13: // ORA SR Indirect Indexed, Y
// ORA(); ORA(StackRelativeIndirectIndexedY());
break; break;
case 0x15: // ORA DP Indexed, X case 0x15: // ORA DP Indexed, X
// ORA(); ORA(DirectPageIndexedX());
break; break;
case 0x17: // ORA DP Indirect Long Indexed, Y case 0x17: // ORA DP Indirect Long Indexed, Y
// ORA(); ORA(DirectPageIndirectLongIndexedY());
break; break;
case 0x19: // ORA Absolute Indexed, Y case 0x19: // ORA Absolute Indexed, Y
// ORA(); ORA(AbsoluteIndexedY());
break; break;
case 0x1D: // ORA Absolute Indexed, X case 0x1D: // ORA Absolute Indexed, X
// ORA(); ORA(AbsoluteIndexedX());
break; break;
case 0x1F: // ORA Absolute Long Indexed, X case 0x1F: // ORA Absolute Long Indexed, X
// ORA(); ORA(AbsoluteLongIndexedX());
break; break;
case 0xF4: // PEA Push Effective Absolute address case 0xF4: // PEA Push Effective Absolute address
// PEA(); PEA();
break; break;
case 0xD4: // PEI Push Effective Indirect address case 0xD4: // PEI Push Effective Indirect address
// PEI(); PEI();
break; break;
case 0x62: // PER Push Effective PC Relative Indirect address case 0x62: // PER Push Effective PC Relative Indirect address
// PER(); PER();
break; break;
case 0x48: // PHA Push Accumulator case 0x48: // PHA Push Accumulator
@@ -694,94 +710,93 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break; break;
case 0x26: // ROL Direct Page case 0x26: // ROL Direct Page
// ROL(); ROL(DirectPage());
break; break;
case 0x2A: // ROL Accumulator case 0x2A: // ROL Accumulator
// ROL(); ROL(A);
break; break;
case 0x2E: // ROL Absolute case 0x2E: // ROL Absolute
// ROL(); ROL(Absolute());
break; break;
case 0x36: // ROL DP Indexed, X case 0x36: // ROL DP Indexed, X
// ROL(); ROL(DirectPageIndexedX());
break; break;
case 0x3E: // ROL Absolute Indexed, X case 0x3E: // ROL Absolute Indexed, X
// ROL(); ROL(AbsoluteIndexedX());
break; break;
case 0x66: // ROR Direct Page case 0x66: // ROR Direct Page
// ROR(); ROR(DirectPage());
break; break;
case 0x6A: // ROR Accumulator case 0x6A: // ROR Accumulator
// ROR(); ROR(A);
break; break;
case 0x6E: // ROR Absolute case 0x6E: // ROR Absolute
// ROR(); ROR(Absolute());
break; break;
case 0x76: // ROR DP Indexed, X case 0x76: // ROR DP Indexed, X
// ROR(); ROR(DirectPageIndexedX());
break; break;
case 0x7E: // ROR Absolute Indexed, X case 0x7E: // ROR Absolute Indexed, X
// ROR(); ROR(AbsoluteIndexedX());
break; break;
case 0x40: // RTI Return from interrupt case 0x40: // RTI Return from interrupt
// RTI(); RTI();
break; break;
case 0x6B: // RTL Return from subroutine long case 0x6B: // RTL Return from subroutine long
// RTL(); RTL();
break; break;
case 0x60: // RTS Return from subroutine case 0x60: // RTS Return from subroutine
// RTS(); RTS();
break; break;
case 0xE1: // SBC DP Indexed Indirect, X case 0xE1: // SBC DP Indexed Indirect, X
// SBC(); SBC(DirectPageIndexedIndirectX());
break; break;
case 0xE3: // SBC Stack Relative case 0xE3: // SBC Stack Relative
// SBC(); SBC(StackRelative());
break; break;
case 0xE5: // SBC Direct Page case 0xE5: // SBC Direct Page
// SBC(); SBC(DirectPage());
break; break;
case 0xE7: // SBC DP Indirect Long case 0xE7: // SBC DP Indirect Long
// SBC(); SBC(DirectPageIndirectLong());
break; break;
case 0xE9: // SBC Immediate case 0xE9: // SBC Immediate
// SBC(); SBC(Immediate(), true);
break; break;
case 0xED: // SBC Absolute case 0xED: // SBC Absolute
// SBC(); SBC(Absolute());
break; break;
case 0xEF: // SBC Absolute Long case 0xEF: // SBC Absolute Long
// SBC(); SBC(AbsoluteLong());
break; break;
case 0xF1: // SBC DP Indirect Indexed, Y case 0xF1: // SBC DP Indirect Indexed, Y
// SBC(); SBC(DirectPageIndirectIndexedY());
break; break;
case 0xF2: // SBC DP Indirect case 0xF2: // SBC DP Indirect
// SBC(); SBC(DirectPageIndirect());
break; break;
case 0xF3: // SBC SR Indirect Indexed, Y case 0xF3: // SBC SR Indirect Indexed, Y
// SBC(); SBC(StackRelativeIndirectIndexedY());
break; break;
case 0xF5: // SBC DP Indexed, X case 0xF5: // SBC DP Indexed, X
// SBC(); SBC(DirectPageIndexedX());
break; break;
case 0xF7: // SBC DP Indirect Long Indexed, Y case 0xF7: // SBC DP Indirect Long Indexed, Y
// SBC(); SBC(DirectPageIndirectLongIndexedY());
break; break;
case 0xF9: // SBC Absolute Indexed, Y case 0xF9: // SBC Absolute Indexed, Y
// SBC(); SBC(AbsoluteIndexedY());
break; break;
case 0xFD: // SBC Absolute Indexed, X case 0xFD: // SBC Absolute Indexed, X
// SBC(); SBC(AbsoluteIndexedX());
break; break;
case 0xFF: // SBC Absolute Long Indexed, X case 0xFF: // SBC Absolute Long Indexed, X
// SBC(); SBC(AbsoluteLongIndexedX());
break; break;
case 0x38: // SEC Set carry case 0x38: // SEC Set carry
@@ -801,90 +816,90 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break; break;
case 0x81: // STA DP Indexed Indirect, X case 0x81: // STA DP Indexed Indirect, X
// STA(); STA(DirectPageIndexedIndirectX());
break; break;
case 0x83: // STA Stack Relative case 0x83: // STA Stack Relative
// STA(); STA(StackRelative());
break; break;
case 0x85: // STA Direct Page case 0x85: // STA Direct Page
// STA(); STA(DirectPage());
break; break;
case 0x87: // STA DP Indirect Long case 0x87: // STA DP Indirect Long
// STA(); STA(DirectPageIndirectLong());
break; break;
case 0x8D: // STA Absolute case 0x8D: // STA Absolute
// STA(); STA(Absolute());
break; break;
case 0x8F: // STA Absolute Long case 0x8F: // STA Absolute Long
// STA(); STA(AbsoluteLong());
break; break;
case 0x91: // STA DP Indirect Indexed, Y case 0x91: // STA DP Indirect Indexed, Y
// STA(); STA(DirectPageIndirectIndexedY());
break; break;
case 0x92: // STA DP Indirect case 0x92: // STA DP Indirect
// STA(); STA(DirectPageIndirect());
break; break;
case 0x93: // STA SR Indirect Indexed, Y case 0x93: // STA SR Indirect Indexed, Y
// STA(); STA(StackRelativeIndirectIndexedY());
break; break;
case 0x95: // STA DP Indexed, X case 0x95: // STA DP Indexed, X
// STA(); STA(DirectPageIndexedX());
break; break;
case 0x97: // STA DP Indirect Long Indexed, Y case 0x97: // STA DP Indirect Long Indexed, Y
// STA(); STA(DirectPageIndirectLongIndexedY());
break; break;
case 0x99: // STA Absolute Indexed, Y case 0x99: // STA Absolute Indexed, Y
// STA(); STA(AbsoluteIndexedY());
break; break;
case 0x9D: // STA Absolute Indexed, X case 0x9D: // STA Absolute Indexed, X
// STA(); STA(AbsoluteIndexedX());
break; break;
case 0x9F: // STA Absolute Long Indexed, X case 0x9F: // STA Absolute Long Indexed, X
// STA(); STA(AbsoluteLongIndexedX());
break; break;
case 0xDB: // STP Stop the clock case 0xDB: // STP Stop the clock
// STP(); STP();
break; break;
case 0x86: // STX Direct Page case 0x86: // STX Direct Page
// STX(); STX(DirectPage());
break; break;
case 0x8E: // STX Absolute case 0x8E: // STX Absolute
// STX(); STX(Absolute());
break; break;
case 0x96: // STX DP Indexed, Y case 0x96: // STX DP Indexed, Y
// STX(); STX(DirectPageIndexedY());
break; break;
case 0x84: // STY Direct Page case 0x84: // STY Direct Page
// STY(); STY(DirectPage());
break; break;
case 0x8C: // STY Absolute case 0x8C: // STY Absolute
// STY(); STY(Absolute());
break; break;
case 0x94: // STY DP Indexed, X case 0x94: // STY DP Indexed, X
// STY(); STY(DirectPageIndexedX());
break; break;
case 0x64: // STZ Direct Page case 0x64: // STZ Direct Page
// STZ(); STZ(DirectPage());
break; break;
case 0x74: // STZ DP Indexed, X case 0x74: // STZ DP Indexed, X
// STZ(); STZ(DirectPageIndexedX());
break; break;
case 0x9C: // STZ Absolute case 0x9C: // STZ Absolute
// STZ(); STZ(Absolute());
break; break;
case 0x9E: // STZ Absolute Indexed, X case 0x9E: // STZ Absolute Indexed, X
// STZ(); STZ(AbsoluteIndexedX());
break; break;
case 0xAA: // TAX case 0xAA: // TAX Transfer accumulator to X
TAX(); TAX();
break; break;
case 0xA8: // TAY case 0xA8: // TAY Transfer accumulator to Y
TAY(); TAY();
break; break;
@@ -901,58 +916,56 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break; break;
case 0x14: // TRB Direct Page case 0x14: // TRB Direct Page
// TRB(); TRB(DirectPage());
break; break;
case 0x1C: // TRB Absolute case 0x1C: // TRB Absolute
// TRB(); TRB(Absolute());
break; break;
case 0x04: // TSB Direct Page case 0x04: // TSB Direct Page
// TSB(); TSB(DirectPage());
break; break;
case 0x0C: // TSB Absolute case 0x0C: // TSB Absolute
// TSB(); TSB(Absolute());
break; break;
case 0x3B: // TSC case 0x3B: // TSC
TSC(); TSC();
break; break;
case 0xBA: // TSX case 0xBA: // TSX Transfer stack pointer to X
TSX(); TSX();
break; break;
case 0x8A: // TXA case 0x8A: // TXA Transfer X to accumulator
TXA(); TXA();
break; break;
case 0x9A: // TXS case 0x9A: // TXS Transfer X to stack pointer
TXS(); TXS();
break; break;
case 0x9B: // TXY case 0x9B: // TXY Transfer X to Y
TXY(); TXY();
break; break;
case 0x98: // TYA case 0x98: // TYA Transfer Y to accumulator
TYA(); TYA();
break; break;
case 0xBB: // TYX case 0xBB: // TYX Transfer Y to X
TYX(); TYX();
break; break;
case 0xCB: // WAI case 0xCB: // WAI Wait for interrupt
// WAI(); WAI();
break; break;
case 0xEB: // XBA case 0xEB: // XBA Exchange B and A
// XBA(); XBA();
break; break;
case 0xFB: // XCE case 0xFB: // XCE Exchange carry and emulation bits
XCE(); XCE();
break; break;
default: default:
@@ -961,29 +974,26 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break; break;
} }
} }
void CPU::ADC(uint8_t operand) { void CPU::ADC(uint8_t operand) {
auto C = GetCarryFlag(); bool C = GetCarryFlag();
if (!E) { // 8-bit mode if (GetAccumulatorSize()) { // 8-bit mode
uint16_t result = (A & 0xFF) + (operand & 0xFF); // + (C ? 1 : 0); uint16_t result = static_cast<uint16_t>(A & 0xFF) +
SetCarryFlag(!(result > 0xFF)); // Update the carry flag static_cast<uint16_t>(operand) + (C ? 1 : 0);
SetCarryFlag(result > 0xFF); // Update the carry flag
// Update the overflow flag // Update the overflow flag
bool overflow = (~(A ^ operand) & (A ^ result) & 0x80) != 0; bool overflow = (~(A ^ operand) & (A ^ result) & 0x80) != 0;
if (overflow) { SetOverflowFlag(overflow);
status |= 0x40; // Set overflow flag
} else {
status &= ~0x40; // Clear overflow flag
}
// Update the accumulator // Update the accumulator with proper wrap-around
A = (A & 0xFF00) | (result & 0xFF); A = (A & 0xFF00) | (result & 0xFF);
SetZeroFlag(A == 0); SetZeroFlag((A & 0xFF) == 0);
SetNegativeFlag(A & 0x80); SetNegativeFlag(A & 0x80);
} else { } else {
uint32_t result = A + operand; // + (C ? 1 : 0); uint32_t result =
SetCarryFlag(!(result > 0xFFFF)); // Update the carry flag static_cast<uint32_t>(A) + static_cast<uint32_t>(operand) + (C ? 1 : 0);
SetCarryFlag(result > 0xFFFF); // Update the carry flag
// Update the overflow flag // Update the overflow flag
bool overflow = (~(A ^ operand) & (A ^ result) & 0x8000) != 0; bool overflow = (~(A ^ operand) & (A ^ result) & 0x8000) != 0;
@@ -997,6 +1007,8 @@ void CPU::ADC(uint8_t operand) {
} }
} }
void CPU::HandleInterrupts() {}
void CPU::AND(uint16_t value, bool isImmediate) { void CPU::AND(uint16_t value, bool isImmediate) {
uint16_t operand; uint16_t operand;
if (E == 0) { // 16-bit mode if (E == 0) { // 16-bit mode

View File

@@ -51,7 +51,9 @@ class CPU : public Memory, public Clock {
uint8_t FetchByteDirectPage(uint8_t operand); uint8_t FetchByteDirectPage(uint8_t operand);
void Run();
void ExecuteInstruction(uint8_t opcode); void ExecuteInstruction(uint8_t opcode);
void HandleInterrupts();
// ========================================================================== // ==========================================================================
// Addressing Modes // Addressing Modes
@@ -274,12 +276,11 @@ class CPU : public Memory, public Clock {
// ========================================================================== // ==========================================================================
// Registers // Registers
uint8_t A = 0; // Accumulator uint16_t A = 0; // Accumulator
uint8_t B = 0; // Accumulator (High)
uint16_t X = 0; // X index register uint16_t X = 0; // X index register
uint16_t Y = 0; // Y index register uint16_t Y = 0; // Y index register
uint16_t D = 0; // Direct Page register uint16_t D = 0; // Direct Page register
uint16_t DB = 0; // Data Bank register uint8_t DB = 0; // Data Bank register
uint8_t PB = 0; // Program Bank register uint8_t PB = 0; // Program Bank register
uint16_t PC = 0; // Program Counter uint16_t PC = 0; // Program Counter
uint8_t E = 1; // Emulation mode flag uint8_t E = 1; // Emulation mode flag
@@ -435,29 +436,76 @@ class CPU : public Memory, public Clock {
// CLV: Clear overflow flag // CLV: Clear overflow flag
void CLV() { status &= ~0x40; } void CLV() { status &= ~0x40; }
// CMP: Compare ``` // CMP: Compare TESTME
// COP: Coprocessor ``` // 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 CMP(uint8_t value, bool isImmediate = false) {
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 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
// CPX: Compare X register // CPX: Compare X register
void CPX(uint16_t value, bool isImmediate = false) { void CPX(uint16_t value, bool isImmediate = false) {
uint16_t memory_value = isImmediate if (GetIndexSize()) { // 8-bit
? value uint8_t memory_value = isImmediate ? value : memory.ReadByte(value);
: (GetIndexSize() ? memory.ReadByte(value) compare(X, memory_value);
: memory.ReadWord(value)); } else { // 16-bit
compare(X, memory_value); uint16_t memory_value = isImmediate ? value : memory.ReadWord(value);
compare(X, memory_value);
}
} }
// CPY: Compare Y register // CPY: Compare Y register
void CPY(uint16_t value, bool isImmediate = false) { void CPY(uint16_t value, bool isImmediate = false) {
uint16_t memory_value = isImmediate if (GetIndexSize()) { // 8-bit
? value uint8_t memory_value = isImmediate ? value : memory.ReadByte(value);
: (GetIndexSize() ? memory.ReadByte(value) compare(Y, memory_value);
: memory.ReadWord(value)); } else { // 16-bit
compare(Y, memory_value); uint16_t memory_value = isImmediate ? value : memory.ReadWord(value);
compare(Y, memory_value);
}
} }
// DEC: Decrement ``` // DEC: Decrement TESTME
void 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 // DEX: Decrement X register
void DEX() { void DEX() {
@@ -485,7 +533,18 @@ class CPU : public Memory, public Clock {
} }
} }
// EOR: Exclusive OR ``` // EOR: Exclusive OR TESTMEs
void EOR(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);
}
}
// INC: Increment // INC: Increment
void INC(uint16_t address) { void INC(uint16_t address) {
@@ -779,7 +838,12 @@ class CPU : public Memory, public Clock {
} }
} }
// STP: Stop the clock ``` // TODO: Make this work with the Clock class of the CPU
// STP: Stop the clock
void 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 // STX: Store X register
void STX(uint16_t address) { void STX(uint16_t address) {
@@ -900,15 +964,20 @@ class CPU : public Memory, public Clock {
SetNegativeFlag(Y & 0x80); SetNegativeFlag(Y & 0x80);
} }
// WAI: Wait for interrupt ``` // TODO: Make this communicate with the SNES class
// WAI: Wait for interrupt TESTME
void 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 // XBA: Exchange B and A accumulator
void XBA() { void XBA() {
uint8_t temp = A; uint8_t lowByte = A & 0xFF;
A = B; uint8_t highByte = (A >> 8) & 0xFF;
B = temp; A = (lowByte << 8) | highByte;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
} }
// XCE: Exchange Carry and Emulation Flags // XCE: Exchange Carry and Emulation Flags

View File

@@ -5,6 +5,43 @@
#include <iostream> #include <iostream>
#include <vector> #include <vector>
// LoROM (Mode 20):
// Banks Offset Purpose
// 00-3F 0000-1FFF LowRAM (shadowed from 7E)
// 2000-2FFF PPU1, APU
// 3000-3FFF SFX, DSP, etc.
// 4000-41FF Controller
// 4200-5FFF PPU2, DMA, etc.
// 6000-7FFF Expansion RAM (reserved)
// 8000-FFFF 32k ROM Chunk
// 40-7C 0000-7FFF 32k ROM Chunk
// 8000-FFFF 32k ROM Chunk
// 7D 0000-FFFF SRAM
// 7E 0000-1FFF LowRAM
// 2000-FFFF System RAM
// 7F 0000-FFFF System RAM
// HiROM (Mode 21):
// Banks Offset Purpose
// 00-3F 0000-1FFF LowRAM (shadowed from 7E)
// 2000-2FFF PPU1, APU
// 3000-3FFF SFX, DSP, etc.
// 4000-41FF Controller
// 4200-5FFF PPU2, DMA, etc.
// 6000-7FFF SRAM (256KB)
// 8000-FFFF 32k ROM Chunk
// 40-6F 0000-FFFF 64k ROM Chunk
// 70-77 0000-FFFF SRAM (256KB)
// 78-7D 0000-FFFF Never Used
// 7E 0000-1FFF LowRAM
// 2000-7FFF HighRAM
// 8000-FFFF Expanded RAM
// 7F 0000-FFFF More Expanded RAM
// 80-EF 0000-FFFF Mirror of 00-6F
// F0-FF 0000-FFFF 64k ROM Chunk
namespace yaze { namespace yaze {
namespace app { namespace app {
namespace emu { namespace emu {
@@ -103,6 +140,86 @@ class Memory {
class MemoryImpl : public Memory { class MemoryImpl : public Memory {
public: public:
void Initialize(const std::vector<uint8_t>& romData) {
const size_t ROM_CHUNK_SIZE = 0x8000; // 32 KB
const size_t SRAM_SIZE = 0x10000; // 64 KB
const size_t SYSTEM_RAM_SIZE = 0x20000; // 128 KB
const size_t EXPANSION_RAM_SIZE = 0x2000; // 8 KB
const size_t HARDWARE_REGISTERS_SIZE = 0x4000; // 16 KB
// Clear memory
memory_.clear();
memory_.resize(0x1000000, 0); // 24-bit address space
// Load ROM data into memory based on LoROM mapping
size_t romSize = romData.size();
size_t romAddress = 0;
for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) {
for (size_t offset = 0x8000; offset <= 0xFFFF; offset += ROM_CHUNK_SIZE) {
if (romAddress < romSize) {
std::copy(romData.begin() + romAddress,
romData.begin() + romAddress + ROM_CHUNK_SIZE,
memory_.begin() + (bank << 16) + offset);
romAddress += ROM_CHUNK_SIZE;
}
}
}
// Initialize SRAM at banks 0x7D and 0xFD
std::fill(memory_.begin() + (0x7D << 16), memory_.begin() + (0x7E << 16),
0);
std::fill(memory_.begin() + (0xFD << 16), memory_.begin() + (0xFE << 16),
0);
// Initialize System RAM at banks 0x7E and 0x7F
std::fill(memory_.begin() + (0x7E << 16),
memory_.begin() + (0x7E << 16) + SYSTEM_RAM_SIZE, 0);
// Initialize Shadow RAM at banks 0x00-0x3F and 0x80-0xBF
for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) {
std::fill(memory_.begin() + (bank << 16),
memory_.begin() + (bank << 16) + 0x2000, 0);
}
// Initialize Hardware Registers at banks 0x00-0x3F and 0x80-0xBF
for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) {
std::fill(
memory_.begin() + (bank << 16) + 0x2000,
memory_.begin() + (bank << 16) + 0x2000 + HARDWARE_REGISTERS_SIZE, 0);
}
// Initialize Expansion RAM at banks 0x00-0x3F and 0x80-0xBF
for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) {
std::fill(memory_.begin() + (bank << 16) + 0x6000,
memory_.begin() + (bank << 16) + 0x6000 + EXPANSION_RAM_SIZE,
0);
}
// Initialize Reset and NMI Vectors at bank 0xFF
std::fill(memory_.begin() + (0xFF << 16) + 0xFF00,
memory_.begin() + (0xFF << 16) + 0xFFFF + 1, 0);
// Copy data into rom_ vector
rom_.resize(kROMSize);
std::copy(memory_.begin() + kROMStart,
memory_.begin() + kROMStart + kROMSize, rom_.begin());
// Copy data into ram_ vector
ram_.resize(kRAMSize);
std::copy(memory_.begin() + kRAMStart,
memory_.begin() + kRAMStart + kRAMSize, ram_.begin());
// Copy data into vram_ vector
vram_.resize(kVRAMSize);
std::copy(memory_.begin() + kVRAMStart,
memory_.begin() + kVRAMStart + kVRAMSize, vram_.begin());
// Copy data into oam_ vector
oam_.resize(kOAMSize);
std::copy(memory_.begin() + kOAMStart,
memory_.begin() + kOAMStart + kOAMSize, oam_.begin());
}
uint8_t ReadByte(uint16_t address) const override { uint8_t ReadByte(uint16_t address) const override {
uint32_t mapped_address = GetMappedAddress(address); uint32_t mapped_address = GetMappedAddress(address);
return memory_.at(mapped_address); return memory_.at(mapped_address);
@@ -174,6 +291,7 @@ class MemoryImpl : public Memory {
(static_cast<uint32_t>(mid) << 8) | low; (static_cast<uint32_t>(mid) << 8) | low;
} }
// Stack Pointer access.
int16_t SP() const override { return SP_; } int16_t SP() const override { return SP_; }
void SetSP(int16_t value) override { SP_ = value; } void SetSP(int16_t value) override { SP_ = value; }

View File

@@ -14,6 +14,30 @@ namespace yaze {
namespace app { namespace app {
namespace emu { namespace emu {
namespace {
uint8_t GetHeaderOffset(const Memory& memory) {
uint8_t mapMode = memory[(0x00 << 16) + 0xFFD5];
uint8_t offset;
switch (mapMode & 0x07) {
case 0: // LoROM
offset = 0x7F;
break;
case 1: // HiROM
offset = 0xFF;
break;
case 5: // ExHiROM
offset = 0x40;
break;
default:
throw std::runtime_error("Unsupported map mode");
}
return offset;
}
} // namespace
void DMA::StartDMATransfer(uint8_t channelMask) { void DMA::StartDMATransfer(uint8_t channelMask) {
for (int i = 0; i < 8; ++i) { for (int i = 0; i < 8; ++i) {
if ((channelMask & (1 << i)) != 0) { if ((channelMask & (1 << i)) != 0) {
@@ -78,7 +102,63 @@ void DMA::EnableHDMATransfers(uint8_t channelMask) {
HDMAEN = channelMask; // Set the HDMAEN register to the channel mask HDMAEN = channelMask; // Set the HDMAEN register to the channel mask
} }
ROMInfo SNES::ReadRomHeader(uint32_t offset) {
ROMInfo romInfo;
// Read cartridge title
char title[22];
for (int i = 0; i < 21; ++i) {
title[i] = cpu.ReadByte(offset + i);
}
title[21] = '\0'; // Null-terminate the string
romInfo.title = std::string(title);
// Read ROM speed and memory map mode
uint8_t romSpeedAndMapMode = cpu.ReadByte(offset + 0x15);
romInfo.romSpeed = (ROMSpeed)(romSpeedAndMapMode & 0x07);
romInfo.bankSize = (BankSize)((romSpeedAndMapMode >> 5) & 0x01);
// Read ROM type
romInfo.romType = (ROMType)cpu.ReadByte(offset + 0x16);
// Read ROM size
romInfo.romSize = (ROMSize)cpu.ReadByte(offset + 0x17);
// Read RAM size
romInfo.sramSize = (SRAMSize)cpu.ReadByte(offset + 0x18);
// Read country code
romInfo.countryCode = (CountryCode)cpu.ReadByte(offset + 0x19);
// Read license
romInfo.license = (License)cpu.ReadByte(offset + 0x1A);
// Read ROM version
romInfo.version = cpu.ReadByte(offset + 0x1B);
// Read checksum complement
romInfo.checksumComplement = cpu.ReadWord(offset + 0x1E);
// Read checksum
romInfo.checksum = cpu.ReadWord(offset + 0x1C);
// Read NMI VBL vector
romInfo.nmiVblVector = cpu.ReadWord(offset + 0x3E);
// Read reset vector
romInfo.resetVector = cpu.ReadWord(offset + 0x3C);
return romInfo;
}
void SNES::Init(ROM& rom) { void SNES::Init(ROM& rom) {
// Load the ROM into memory and set up the memory mapping
memory_.Initialize(rom.vector());
// Read the ROM header
auto header_offset = GetHeaderOffset(memory_);
rom_info_ = ReadRomHeader(header_offset);
// Perform a long jump into a FastROM bank (if the ROM speed is FastROM) // Perform a long jump into a FastROM bank (if the ROM speed is FastROM)
// Disable the emulation flag (switch to 65816 native mode)s // Disable the emulation flag (switch to 65816 native mode)s
cpu.Init(); cpu.Init();
@@ -237,7 +317,7 @@ void SNES::Run() {
// Enable NMI Interrupts // Enable NMI Interrupts
void SNES::EnableVBlankInterrupts() { void SNES::EnableVBlankInterrupts() {
vBlankFlag = 0; vBlankFlag = false;
// Clear the RDNMI VBlank flag // Clear the RDNMI VBlank flag
memory_.ReadByte(0x4210); // RDNMI memory_.ReadByte(0x4210); // RDNMI
@@ -248,7 +328,7 @@ void SNES::EnableVBlankInterrupts() {
// Wait until the VBlank routine has been processed // Wait until the VBlank routine has been processed
void SNES::WaitForVBlank() { void SNES::WaitForVBlank() {
vBlankFlag = 1; vBlankFlag = true;
// Loop until `vBlankFlag` is clear // Loop until `vBlankFlag` is clear
while (vBlankFlag) { while (vBlankFlag) {

View File

@@ -62,6 +62,8 @@ class SNES : public DMA {
SNES() = default; SNES() = default;
~SNES() = default; ~SNES() = default;
ROMInfo ReadRomHeader(uint32_t offset);
// Initialization // Initialization
void Init(ROM& rom); void Init(ROM& rom);
@@ -110,6 +112,7 @@ class SNES : public DMA {
APU apu{memory_}; APU apu{memory_};
// Helper classes // Helper classes
ROMInfo rom_info_;
Debugger debugger; Debugger debugger;
std::vector<uint8_t> rom_data; std::vector<uint8_t> rom_data;

View File

@@ -168,7 +168,7 @@ TEST_F(CPUTest, CheckMemoryContents) {
TEST_F(CPUTest, ADC_Immediate_TwoPositiveNumbers) { TEST_F(CPUTest, ADC_Immediate_TwoPositiveNumbers) {
cpu.A = 0x01; cpu.A = 0x01;
cpu.status = 0xFF; // 8-bit mode cpu.SetAccumulatorSize(true);
std::vector<uint8_t> data = {0x01}; std::vector<uint8_t> data = {0x01};
mock_memory.SetMemoryContents(data); mock_memory.SetMemoryContents(data);
@@ -180,7 +180,7 @@ TEST_F(CPUTest, ADC_Immediate_TwoPositiveNumbers) {
TEST_F(CPUTest, ADC_Immediate_PositiveAndNegativeNumbers) { TEST_F(CPUTest, ADC_Immediate_PositiveAndNegativeNumbers) {
cpu.A = 10; cpu.A = 10;
cpu.status = 0xFF; // 8-bit mode cpu.SetAccumulatorSize(true);
std::vector<uint8_t> data = {0x69, static_cast<uint8_t>(-20)}; std::vector<uint8_t> data = {0x69, static_cast<uint8_t>(-20)};
mock_memory.SetMemoryContents(data); mock_memory.SetMemoryContents(data);
@@ -270,7 +270,7 @@ TEST_F(CPUTest, ADC_DirectPageIndexedIndirectX) {
TEST_F(CPUTest, ADC_CheckCarryFlag) { TEST_F(CPUTest, ADC_CheckCarryFlag) {
cpu.A = 0xFF; cpu.A = 0xFF;
cpu.status = 0xFF; // 8-bit mode cpu.SetAccumulatorSize(true);
std::vector<uint8_t> data = {0x15, 0x01}; // Operand at address 0x15 std::vector<uint8_t> data = {0x15, 0x01}; // Operand at address 0x15
mock_memory.SetMemoryContents(data); mock_memory.SetMemoryContents(data);
@@ -286,6 +286,8 @@ TEST_F(CPUTest, ADC_AbsoluteIndexedX) {
cpu.A = 0x03; cpu.A = 0x03;
cpu.X = 0x02; // X register cpu.X = 0x02; // X register
cpu.PC = 0x0001; cpu.PC = 0x0001;
cpu.SetCarryFlag(false);
cpu.SetAccumulatorSize(false); // 16-bit mode
std::vector<uint8_t> data = {0x7D, 0x03, 0x00, 0x00, 0x05, 0x00}; std::vector<uint8_t> data = {0x7D, 0x03, 0x00, 0x00, 0x05, 0x00};
mock_memory.SetMemoryContents(data); mock_memory.SetMemoryContents(data);
@@ -612,6 +614,24 @@ TEST_F(CPUTest, BCS_WhenCarryFlagClear) {
EXPECT_EQ(cpu.PC, 0x1000); EXPECT_EQ(cpu.PC, 0x1000);
} }
// ============================================================================
// BEQ - Branch if Equal
TEST_F(CPUTest, BEQ) {
cpu.SetZeroFlag(true);
cpu.PC = 0x1000;
std::vector<uint8_t> data = {0xF0, 0x03, 0x02}; // Operand at address 0x1001
mock_memory.SetMemoryContents(data);
EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0x03));
cpu.ExecuteInstruction(0xF0); // BEQ
EXPECT_EQ(cpu.PC, 0x1003);
}
// ============================================================================
// BIT - Bit Test
TEST_F(CPUTest, BIT_Immediate) { TEST_F(CPUTest, BIT_Immediate) {
cpu.A = 0x01; cpu.A = 0x01;
cpu.PC = 0x0001; cpu.PC = 0x0001;
@@ -665,6 +685,9 @@ TEST_F(CPUTest, BIT_AbsoluteIndexedX) {
EXPECT_FALSE(cpu.GetZeroFlag()); EXPECT_FALSE(cpu.GetZeroFlag());
} }
// ============================================================================
// BMI - Branch if Minus
TEST_F(CPUTest, BMI_BranchTaken) { TEST_F(CPUTest, BMI_BranchTaken) {
cpu.PC = 0x0000; cpu.PC = 0x0000;
cpu.SetNegativeFlag(true); cpu.SetNegativeFlag(true);
@@ -685,6 +708,9 @@ TEST_F(CPUTest, BMI_BranchNotTaken) {
EXPECT_EQ(cpu.PC, 0x0000); EXPECT_EQ(cpu.PC, 0x0000);
} }
// ============================================================================
// BNE - Branch if Not Equal
TEST_F(CPUTest, BNE_BranchTaken) { TEST_F(CPUTest, BNE_BranchTaken) {
cpu.PC = 0x0000; cpu.PC = 0x0000;
cpu.SetZeroFlag(false); cpu.SetZeroFlag(false);
@@ -705,6 +731,9 @@ TEST_F(CPUTest, BNE_BranchNotTaken) {
EXPECT_EQ(cpu.PC, 0x0000); EXPECT_EQ(cpu.PC, 0x0000);
} }
// ============================================================================
// BPL - Branch if Positive
TEST_F(CPUTest, BPL_BranchTaken) { TEST_F(CPUTest, BPL_BranchTaken) {
cpu.PC = 0x0000; cpu.PC = 0x0000;
cpu.SetNegativeFlag(false); cpu.SetNegativeFlag(false);
@@ -725,6 +754,9 @@ TEST_F(CPUTest, BPL_BranchNotTaken) {
EXPECT_EQ(cpu.PC, 0x0000); EXPECT_EQ(cpu.PC, 0x0000);
} }
// ============================================================================
// BRA - Branch Always
TEST_F(CPUTest, BRA) { TEST_F(CPUTest, BRA) {
cpu.PC = 0x0000; cpu.PC = 0x0000;
std::vector<uint8_t> data = {0x02}; // BRA std::vector<uint8_t> data = {0x02}; // BRA
@@ -761,8 +793,124 @@ TEST_F(CPUTest, BRL) {
EXPECT_EQ(cpu.PC, 0x1004); EXPECT_EQ(cpu.PC, 0x1004);
} }
// ============================================================================
// BVC - Branch if Overflow Clear
TEST_F(CPUTest, BVC_BranchTaken) {
cpu.PC = 0x0000;
cpu.SetOverflowFlag(false);
std::vector<uint8_t> data = {0x02}; // BVC
mock_memory.SetMemoryContents(data);
cpu.ExecuteInstruction(0x50); // BVC
EXPECT_EQ(cpu.PC, 0x0002);
}
// ============================================================================
// BVS - Branch if Overflow Set
TEST_F(CPUTest, BVS_BranchTaken) {
cpu.PC = 0x0000;
cpu.SetOverflowFlag(true);
std::vector<uint8_t> data = {0x02}; // BVS
mock_memory.SetMemoryContents(data);
cpu.ExecuteInstruction(0x70); // BVS
EXPECT_EQ(cpu.PC, 0x0002);
}
// ============================================================================
// CLC - Clear Carry Flag
TEST_F(CPUTest, CLC) {
cpu.SetCarryFlag(true);
cpu.PC = 0x0000;
std::vector<uint8_t> data = {0x18}; // CLC
mock_memory.SetMemoryContents(data);
cpu.ExecuteInstruction(0x18); // CLC
EXPECT_FALSE(cpu.GetCarryFlag());
}
// ============================================================================
// CLD - Clear Decimal Mode Flag
TEST_F(CPUTest, CLD) {
cpu.SetDecimalFlag(true);
cpu.PC = 0x0000;
std::vector<uint8_t> data = {0xD8}; // CLD
mock_memory.SetMemoryContents(data);
cpu.ExecuteInstruction(0xD8); // CLD
EXPECT_FALSE(cpu.GetDecimalFlag());
}
// ============================================================================
// CLI - Clear Interrupt Disable Flag
TEST_F(CPUTest, CLI) {
cpu.SetInterruptFlag(true);
cpu.PC = 0x0000;
std::vector<uint8_t> data = {0x58}; // CLI
mock_memory.SetMemoryContents(data);
cpu.ExecuteInstruction(0x58); // CLI
EXPECT_FALSE(cpu.GetInterruptFlag());
}
// ============================================================================
// CLV - Clear Overflow Flag
TEST_F(CPUTest, CLV) {
cpu.SetOverflowFlag(true);
cpu.PC = 0x0000;
std::vector<uint8_t> data = {0xB8}; // CLV
mock_memory.SetMemoryContents(data);
cpu.ExecuteInstruction(0xB8); // CLV
EXPECT_FALSE(cpu.GetOverflowFlag());
}
// ============================================================================
// CMP - Compare Accumulator
TEST_F(CPUTest, CMP_Immediate_8Bit) {
// 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, {0x40});
// Set up the memory to return 0x40 when the Immediate addressing mode is used
EXPECT_CALL(mock_memory, ReadByte(0x00)).WillOnce(::testing::Return(0x40));
// Execute the CMP Immediate instruction
cpu.ExecuteInstruction(0xC9);
// 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_Absolute_16Bit) {
// Set the accumulator to 16-bit mode
cpu.SetAccumulatorSize(false);
cpu.A = 0x8000; // Set the accumulator to 0x8000
mock_memory.InsertMemory(0x0000, {0x34, 0x12});
// Execute the CMP Absolute instruction
cpu.ExecuteInstruction(0xCD);
// 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 for CPX instruction // Test for CPX instruction
TEST_F(CPUTest, CPX_CarryFlagSet) { TEST_F(CPUTest, CPX_CarryFlagSet) {
cpu.X = 0x1000; cpu.X = 0x1000;
cpu.CPX(0x0FFF); cpu.CPX(0x0FFF);