Add CMP, COP, DEC, EOR, update SNES and Memory
This commit is contained in:
@@ -67,6 +67,22 @@ uint8_t CPU::FetchByteDirectPage(uint8_t operand) {
|
||||
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) {
|
||||
// Update the PC based on the Program Bank Register
|
||||
PC |= (static_cast<uint16_t>(PB) << 16);
|
||||
@@ -262,11 +278,11 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
||||
break;
|
||||
|
||||
case 0x50: // BVC Branch if overflow clear
|
||||
// BVC();
|
||||
BVC(ReadByte(PC));
|
||||
break;
|
||||
|
||||
case 0x70: // BVS Branch if overflow set
|
||||
// BVS();
|
||||
BVS(ReadByte(PC));
|
||||
break;
|
||||
|
||||
case 0x18: // CLC Clear carry
|
||||
@@ -286,49 +302,49 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
||||
break;
|
||||
|
||||
case 0xC1: // CMP DP Indexed Indirect, X
|
||||
// CMP();
|
||||
CMP(DirectPageIndexedIndirectX());
|
||||
break;
|
||||
case 0xC3: // CMP Stack Relative
|
||||
// CMP();
|
||||
CMP(StackRelative());
|
||||
break;
|
||||
case 0xC5: // CMP Direct Page
|
||||
// CMP();
|
||||
CMP(DirectPage());
|
||||
break;
|
||||
case 0xC7: // CMP DP Indirect Long
|
||||
// CMP();
|
||||
CMP(DirectPageIndirectLong());
|
||||
break;
|
||||
case 0xC9: // CMP Immediate
|
||||
// CMP();
|
||||
CMP(Immediate(), true);
|
||||
break;
|
||||
case 0xCD: // CMP Absolute
|
||||
// CMP();
|
||||
CMP(Absolute());
|
||||
break;
|
||||
case 0xCF: // CMP Absolute Long
|
||||
// CMP();
|
||||
CMP(AbsoluteLong());
|
||||
break;
|
||||
case 0xD1: // CMP DP Indirect Indexed, Y
|
||||
// CMP();
|
||||
CMP(DirectPageIndirectIndexedY());
|
||||
break;
|
||||
case 0xD2: // CMP DP Indirect
|
||||
// CMP();
|
||||
CMP(DirectPageIndirect());
|
||||
break;
|
||||
case 0xD3: // CMP SR Indirect Indexed, Y
|
||||
// CMP();
|
||||
CMP(StackRelativeIndirectIndexedY());
|
||||
break;
|
||||
case 0xD5: // CMP DP Indexed, X
|
||||
// CMP();
|
||||
CMP(DirectPageIndexedX());
|
||||
break;
|
||||
case 0xD7: // CMP DP Indirect Long Indexed, Y
|
||||
// CMP();
|
||||
CMP(DirectPageIndirectLongIndexedY());
|
||||
break;
|
||||
case 0xD9: // CMP Absolute Indexed, Y
|
||||
// CMP();
|
||||
CMP(AbsoluteIndexedY());
|
||||
break;
|
||||
case 0xDD: // CMP Absolute Indexed, X
|
||||
// CMP();
|
||||
CMP(AbsoluteIndexedX());
|
||||
break;
|
||||
case 0xDF: // CMP Absolute Long Indexed, X
|
||||
// CMP();
|
||||
CMP(AbsoluteLongIndexedX());
|
||||
break;
|
||||
|
||||
case 0x02: // COP Coprocessor
|
||||
@@ -356,19 +372,19 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
||||
break;
|
||||
|
||||
case 0x3A: // DEC Accumulator
|
||||
// DEC();
|
||||
DEC(A);
|
||||
break;
|
||||
case 0xC6: // DEC Direct Page
|
||||
// DEC();
|
||||
DEC(DirectPage());
|
||||
break;
|
||||
case 0xCE: // DEC Absolute
|
||||
// DEC();
|
||||
DEC(Absolute());
|
||||
break;
|
||||
case 0xD6: // DEC DP Indexed, X
|
||||
// DEC();
|
||||
DEC(DirectPageIndexedX());
|
||||
break;
|
||||
case 0xDE: // DEC Absolute Indexed, X
|
||||
// DEC();
|
||||
DEC(AbsoluteIndexedX());
|
||||
break;
|
||||
|
||||
case 0xCA: // DEX Decrement X register
|
||||
@@ -380,49 +396,49 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
||||
break;
|
||||
|
||||
case 0x41: // EOR DP Indexed Indirect, X
|
||||
// EOR();
|
||||
EOR(DirectPageIndexedIndirectX());
|
||||
break;
|
||||
case 0x43: // EOR Stack Relative
|
||||
// EOR();
|
||||
EOR(StackRelative());
|
||||
break;
|
||||
case 0x45: // EOR Direct Page
|
||||
// EOR();
|
||||
EOR(DirectPage());
|
||||
break;
|
||||
case 0x47: // EOR DP Indirect Long
|
||||
// EOR();
|
||||
EOR(DirectPageIndirectLong());
|
||||
break;
|
||||
case 0x49: // EOR Immediate
|
||||
// EOR();
|
||||
EOR(Immediate(), true);
|
||||
break;
|
||||
case 0x4D: // EOR Absolute
|
||||
// EOR();
|
||||
EOR(Absolute());
|
||||
break;
|
||||
case 0x4F: // EOR Absolute Long
|
||||
// EOR();
|
||||
EOR(AbsoluteLong());
|
||||
break;
|
||||
case 0x51: // EOR DP Indirect Indexed, Y
|
||||
// EOR();
|
||||
EOR(DirectPageIndirectIndexedY());
|
||||
break;
|
||||
case 0x52: // EOR DP Indirect
|
||||
// EOR();
|
||||
EOR(DirectPageIndirect());
|
||||
break;
|
||||
case 0x53: // EOR SR Indirect Indexed, Y
|
||||
// EOR();
|
||||
EOR(StackRelativeIndirectIndexedY());
|
||||
break;
|
||||
case 0x55: // EOR DP Indexed, X
|
||||
// EOR();
|
||||
EOR(DirectPageIndexedX());
|
||||
break;
|
||||
case 0x57: // EOR DP Indirect Long Indexed, Y
|
||||
// EOR();
|
||||
EOR(DirectPageIndirectLongIndexedY());
|
||||
break;
|
||||
case 0x59: // EOR Absolute Indexed, Y
|
||||
// EOR();
|
||||
EOR(AbsoluteIndexedY());
|
||||
break;
|
||||
case 0x5D: // EOR Absolute Indexed, X
|
||||
// EOR();
|
||||
EOR(AbsoluteIndexedX());
|
||||
break;
|
||||
case 0x5F: // EOR Absolute Long Indexed, X
|
||||
// EOR();
|
||||
EOR(AbsoluteLongIndexedX());
|
||||
break;
|
||||
|
||||
case 0x1A: // INC Accumulator
|
||||
@@ -524,51 +540,51 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
||||
break;
|
||||
|
||||
case 0xA2: // LDX Immediate
|
||||
// LDX();
|
||||
LDX(Immediate(), true);
|
||||
break;
|
||||
case 0xA6: // LDX Direct Page
|
||||
// LDX();
|
||||
LDX(DirectPage());
|
||||
break;
|
||||
case 0xAE: // LDX Absolute
|
||||
// LDX();
|
||||
LDX(Absolute());
|
||||
break;
|
||||
case 0xB6: // LDX DP Indexed, Y
|
||||
// LDX();
|
||||
LDX(DirectPageIndexedY());
|
||||
break;
|
||||
case 0xBE: // LDX Absolute Indexed, Y
|
||||
// LDX();
|
||||
LDX(AbsoluteIndexedY());
|
||||
break;
|
||||
|
||||
case 0xA0: // LDY Immediate
|
||||
// LDY();
|
||||
LDY(Immediate(), true);
|
||||
break;
|
||||
case 0xA4: // LDY Direct Page
|
||||
// LDY();
|
||||
LDY(DirectPage());
|
||||
break;
|
||||
case 0xAC: // LDY Absolute
|
||||
// LDY();
|
||||
LDY(Absolute());
|
||||
break;
|
||||
case 0xB4: // LDY DP Indexed, X
|
||||
// LDY();
|
||||
LDY(DirectPageIndexedX());
|
||||
break;
|
||||
case 0xBC: // LDY Absolute Indexed, X
|
||||
// LDY();
|
||||
LDY(AbsoluteIndexedX());
|
||||
break;
|
||||
|
||||
case 0x46: // LSR Direct Page
|
||||
// LSR();
|
||||
LSR(DirectPage());
|
||||
break;
|
||||
case 0x4A: // LSR Accumulator
|
||||
// LSR();
|
||||
LSR(A);
|
||||
break;
|
||||
case 0x4E: // LSR Absolute
|
||||
// LSR();
|
||||
LSR(Absolute());
|
||||
break;
|
||||
case 0x56: // LSR DP Indexed, X
|
||||
// LSR();
|
||||
LSR(DirectPageIndexedX());
|
||||
break;
|
||||
case 0x5E: // LSR Absolute Indexed, X
|
||||
// LSR();
|
||||
LSR(AbsoluteIndexedX());
|
||||
break;
|
||||
|
||||
case 0x54: // MVN Block Move Next
|
||||
@@ -580,61 +596,61 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
||||
break;
|
||||
|
||||
case 0x01: // ORA DP Indexed Indirect, X
|
||||
// ORA();
|
||||
ORA(DirectPageIndexedIndirectX());
|
||||
break;
|
||||
case 0x03: // ORA Stack Relative
|
||||
// ORA();
|
||||
ORA(StackRelative());
|
||||
break;
|
||||
case 0x05: // ORA Direct Page
|
||||
// ORA();
|
||||
ORA(DirectPage());
|
||||
break;
|
||||
case 0x07: // ORA DP Indirect Long
|
||||
// ORA();
|
||||
ORA(DirectPageIndirectLong());
|
||||
break;
|
||||
case 0x09: // ORA Immediate
|
||||
// ORA();
|
||||
ORA(Immediate(), true);
|
||||
break;
|
||||
case 0x0D: // ORA Absolute
|
||||
// ORA();
|
||||
ORA(Absolute());
|
||||
break;
|
||||
case 0x0F: // ORA Absolute Long
|
||||
// ORA();
|
||||
ORA(AbsoluteLong());
|
||||
break;
|
||||
case 0x11: // ORA DP Indirect Indexed, Y
|
||||
// ORA();
|
||||
ORA(DirectPageIndirectIndexedY());
|
||||
break;
|
||||
case 0x12: // ORA DP Indirect
|
||||
// ORA();
|
||||
ORA(DirectPageIndirect());
|
||||
break;
|
||||
case 0x13: // ORA SR Indirect Indexed, Y
|
||||
// ORA();
|
||||
ORA(StackRelativeIndirectIndexedY());
|
||||
break;
|
||||
case 0x15: // ORA DP Indexed, X
|
||||
// ORA();
|
||||
ORA(DirectPageIndexedX());
|
||||
break;
|
||||
case 0x17: // ORA DP Indirect Long Indexed, Y
|
||||
// ORA();
|
||||
ORA(DirectPageIndirectLongIndexedY());
|
||||
break;
|
||||
case 0x19: // ORA Absolute Indexed, Y
|
||||
// ORA();
|
||||
ORA(AbsoluteIndexedY());
|
||||
break;
|
||||
case 0x1D: // ORA Absolute Indexed, X
|
||||
// ORA();
|
||||
ORA(AbsoluteIndexedX());
|
||||
break;
|
||||
case 0x1F: // ORA Absolute Long Indexed, X
|
||||
// ORA();
|
||||
ORA(AbsoluteLongIndexedX());
|
||||
break;
|
||||
|
||||
case 0xF4: // PEA Push Effective Absolute address
|
||||
// PEA();
|
||||
PEA();
|
||||
break;
|
||||
|
||||
case 0xD4: // PEI Push Effective Indirect address
|
||||
// PEI();
|
||||
PEI();
|
||||
break;
|
||||
|
||||
case 0x62: // PER Push Effective PC Relative Indirect address
|
||||
// PER();
|
||||
PER();
|
||||
break;
|
||||
|
||||
case 0x48: // PHA Push Accumulator
|
||||
@@ -694,94 +710,93 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
||||
break;
|
||||
|
||||
case 0x26: // ROL Direct Page
|
||||
// ROL();
|
||||
ROL(DirectPage());
|
||||
break;
|
||||
|
||||
case 0x2A: // ROL Accumulator
|
||||
// ROL();
|
||||
ROL(A);
|
||||
break;
|
||||
case 0x2E: // ROL Absolute
|
||||
// ROL();
|
||||
ROL(Absolute());
|
||||
break;
|
||||
case 0x36: // ROL DP Indexed, X
|
||||
// ROL();
|
||||
ROL(DirectPageIndexedX());
|
||||
break;
|
||||
case 0x3E: // ROL Absolute Indexed, X
|
||||
// ROL();
|
||||
ROL(AbsoluteIndexedX());
|
||||
break;
|
||||
|
||||
case 0x66: // ROR Direct Page
|
||||
// ROR();
|
||||
ROR(DirectPage());
|
||||
break;
|
||||
case 0x6A: // ROR Accumulator
|
||||
// ROR();
|
||||
ROR(A);
|
||||
break;
|
||||
case 0x6E: // ROR Absolute
|
||||
// ROR();
|
||||
ROR(Absolute());
|
||||
break;
|
||||
case 0x76: // ROR DP Indexed, X
|
||||
// ROR();
|
||||
ROR(DirectPageIndexedX());
|
||||
break;
|
||||
case 0x7E: // ROR Absolute Indexed, X
|
||||
// ROR();
|
||||
ROR(AbsoluteIndexedX());
|
||||
break;
|
||||
|
||||
case 0x40: // RTI Return from interrupt
|
||||
// RTI();
|
||||
RTI();
|
||||
break;
|
||||
|
||||
case 0x6B: // RTL Return from subroutine long
|
||||
// RTL();
|
||||
RTL();
|
||||
break;
|
||||
|
||||
case 0x60: // RTS Return from subroutine
|
||||
// RTS();
|
||||
RTS();
|
||||
break;
|
||||
|
||||
case 0xE1: // SBC DP Indexed Indirect, X
|
||||
// SBC();
|
||||
SBC(DirectPageIndexedIndirectX());
|
||||
break;
|
||||
case 0xE3: // SBC Stack Relative
|
||||
// SBC();
|
||||
SBC(StackRelative());
|
||||
break;
|
||||
case 0xE5: // SBC Direct Page
|
||||
// SBC();
|
||||
SBC(DirectPage());
|
||||
break;
|
||||
case 0xE7: // SBC DP Indirect Long
|
||||
// SBC();
|
||||
SBC(DirectPageIndirectLong());
|
||||
break;
|
||||
case 0xE9: // SBC Immediate
|
||||
// SBC();
|
||||
SBC(Immediate(), true);
|
||||
break;
|
||||
case 0xED: // SBC Absolute
|
||||
// SBC();
|
||||
SBC(Absolute());
|
||||
break;
|
||||
case 0xEF: // SBC Absolute Long
|
||||
// SBC();
|
||||
SBC(AbsoluteLong());
|
||||
break;
|
||||
case 0xF1: // SBC DP Indirect Indexed, Y
|
||||
// SBC();
|
||||
SBC(DirectPageIndirectIndexedY());
|
||||
break;
|
||||
case 0xF2: // SBC DP Indirect
|
||||
// SBC();
|
||||
SBC(DirectPageIndirect());
|
||||
break;
|
||||
case 0xF3: // SBC SR Indirect Indexed, Y
|
||||
// SBC();
|
||||
SBC(StackRelativeIndirectIndexedY());
|
||||
break;
|
||||
case 0xF5: // SBC DP Indexed, X
|
||||
// SBC();
|
||||
SBC(DirectPageIndexedX());
|
||||
break;
|
||||
case 0xF7: // SBC DP Indirect Long Indexed, Y
|
||||
// SBC();
|
||||
SBC(DirectPageIndirectLongIndexedY());
|
||||
break;
|
||||
case 0xF9: // SBC Absolute Indexed, Y
|
||||
// SBC();
|
||||
SBC(AbsoluteIndexedY());
|
||||
break;
|
||||
case 0xFD: // SBC Absolute Indexed, X
|
||||
// SBC();
|
||||
SBC(AbsoluteIndexedX());
|
||||
break;
|
||||
case 0xFF: // SBC Absolute Long Indexed, X
|
||||
// SBC();
|
||||
SBC(AbsoluteLongIndexedX());
|
||||
break;
|
||||
|
||||
case 0x38: // SEC Set carry
|
||||
@@ -801,90 +816,90 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
||||
break;
|
||||
|
||||
case 0x81: // STA DP Indexed Indirect, X
|
||||
// STA();
|
||||
STA(DirectPageIndexedIndirectX());
|
||||
break;
|
||||
case 0x83: // STA Stack Relative
|
||||
// STA();
|
||||
STA(StackRelative());
|
||||
break;
|
||||
case 0x85: // STA Direct Page
|
||||
// STA();
|
||||
STA(DirectPage());
|
||||
break;
|
||||
case 0x87: // STA DP Indirect Long
|
||||
// STA();
|
||||
STA(DirectPageIndirectLong());
|
||||
break;
|
||||
case 0x8D: // STA Absolute
|
||||
// STA();
|
||||
STA(Absolute());
|
||||
break;
|
||||
case 0x8F: // STA Absolute Long
|
||||
// STA();
|
||||
STA(AbsoluteLong());
|
||||
break;
|
||||
case 0x91: // STA DP Indirect Indexed, Y
|
||||
// STA();
|
||||
STA(DirectPageIndirectIndexedY());
|
||||
break;
|
||||
case 0x92: // STA DP Indirect
|
||||
// STA();
|
||||
STA(DirectPageIndirect());
|
||||
break;
|
||||
case 0x93: // STA SR Indirect Indexed, Y
|
||||
// STA();
|
||||
STA(StackRelativeIndirectIndexedY());
|
||||
break;
|
||||
case 0x95: // STA DP Indexed, X
|
||||
// STA();
|
||||
STA(DirectPageIndexedX());
|
||||
break;
|
||||
case 0x97: // STA DP Indirect Long Indexed, Y
|
||||
// STA();
|
||||
STA(DirectPageIndirectLongIndexedY());
|
||||
break;
|
||||
case 0x99: // STA Absolute Indexed, Y
|
||||
// STA();
|
||||
STA(AbsoluteIndexedY());
|
||||
break;
|
||||
case 0x9D: // STA Absolute Indexed, X
|
||||
// STA();
|
||||
STA(AbsoluteIndexedX());
|
||||
break;
|
||||
case 0x9F: // STA Absolute Long Indexed, X
|
||||
// STA();
|
||||
STA(AbsoluteLongIndexedX());
|
||||
break;
|
||||
|
||||
case 0xDB: // STP Stop the clock
|
||||
// STP();
|
||||
STP();
|
||||
break;
|
||||
|
||||
case 0x86: // STX Direct Page
|
||||
// STX();
|
||||
STX(DirectPage());
|
||||
break;
|
||||
case 0x8E: // STX Absolute
|
||||
// STX();
|
||||
STX(Absolute());
|
||||
break;
|
||||
case 0x96: // STX DP Indexed, Y
|
||||
// STX();
|
||||
STX(DirectPageIndexedY());
|
||||
break;
|
||||
|
||||
case 0x84: // STY Direct Page
|
||||
// STY();
|
||||
STY(DirectPage());
|
||||
break;
|
||||
case 0x8C: // STY Absolute
|
||||
// STY();
|
||||
STY(Absolute());
|
||||
break;
|
||||
case 0x94: // STY DP Indexed, X
|
||||
// STY();
|
||||
STY(DirectPageIndexedX());
|
||||
break;
|
||||
|
||||
case 0x64: // STZ Direct Page
|
||||
// STZ();
|
||||
STZ(DirectPage());
|
||||
break;
|
||||
case 0x74: // STZ DP Indexed, X
|
||||
// STZ();
|
||||
STZ(DirectPageIndexedX());
|
||||
break;
|
||||
case 0x9C: // STZ Absolute
|
||||
// STZ();
|
||||
STZ(Absolute());
|
||||
break;
|
||||
case 0x9E: // STZ Absolute Indexed, X
|
||||
// STZ();
|
||||
STZ(AbsoluteIndexedX());
|
||||
break;
|
||||
|
||||
case 0xAA: // TAX
|
||||
case 0xAA: // TAX Transfer accumulator to X
|
||||
TAX();
|
||||
break;
|
||||
|
||||
case 0xA8: // TAY
|
||||
case 0xA8: // TAY Transfer accumulator to Y
|
||||
TAY();
|
||||
break;
|
||||
|
||||
@@ -901,58 +916,56 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
||||
break;
|
||||
|
||||
case 0x14: // TRB Direct Page
|
||||
// TRB();
|
||||
TRB(DirectPage());
|
||||
break;
|
||||
|
||||
case 0x1C: // TRB Absolute
|
||||
// TRB();
|
||||
TRB(Absolute());
|
||||
break;
|
||||
|
||||
case 0x04: // TSB Direct Page
|
||||
// TSB();
|
||||
TSB(DirectPage());
|
||||
break;
|
||||
|
||||
case 0x0C: // TSB Absolute
|
||||
// TSB();
|
||||
TSB(Absolute());
|
||||
break;
|
||||
|
||||
case 0x3B: // TSC
|
||||
TSC();
|
||||
break;
|
||||
|
||||
case 0xBA: // TSX
|
||||
case 0xBA: // TSX Transfer stack pointer to X
|
||||
TSX();
|
||||
break;
|
||||
|
||||
case 0x8A: // TXA
|
||||
case 0x8A: // TXA Transfer X to accumulator
|
||||
TXA();
|
||||
break;
|
||||
|
||||
case 0x9A: // TXS
|
||||
case 0x9A: // TXS Transfer X to stack pointer
|
||||
TXS();
|
||||
break;
|
||||
|
||||
case 0x9B: // TXY
|
||||
case 0x9B: // TXY Transfer X to Y
|
||||
TXY();
|
||||
break;
|
||||
|
||||
case 0x98: // TYA
|
||||
case 0x98: // TYA Transfer Y to accumulator
|
||||
TYA();
|
||||
break;
|
||||
|
||||
case 0xBB: // TYX
|
||||
case 0xBB: // TYX Transfer Y to X
|
||||
TYX();
|
||||
break;
|
||||
|
||||
case 0xCB: // WAI
|
||||
// WAI();
|
||||
case 0xCB: // WAI Wait for interrupt
|
||||
WAI();
|
||||
break;
|
||||
|
||||
case 0xEB: // XBA
|
||||
// XBA();
|
||||
case 0xEB: // XBA Exchange B and A
|
||||
XBA();
|
||||
break;
|
||||
|
||||
case 0xFB: // XCE
|
||||
case 0xFB: // XCE Exchange carry and emulation bits
|
||||
XCE();
|
||||
break;
|
||||
default:
|
||||
@@ -961,29 +974,26 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void CPU::ADC(uint8_t operand) {
|
||||
auto C = GetCarryFlag();
|
||||
if (!E) { // 8-bit mode
|
||||
uint16_t result = (A & 0xFF) + (operand & 0xFF); // + (C ? 1 : 0);
|
||||
SetCarryFlag(!(result > 0xFF)); // Update the carry flag
|
||||
bool C = GetCarryFlag();
|
||||
if (GetAccumulatorSize()) { // 8-bit mode
|
||||
uint16_t result = static_cast<uint16_t>(A & 0xFF) +
|
||||
static_cast<uint16_t>(operand) + (C ? 1 : 0);
|
||||
SetCarryFlag(result > 0xFF); // Update the carry flag
|
||||
|
||||
// Update the overflow flag
|
||||
bool overflow = (~(A ^ operand) & (A ^ result) & 0x80) != 0;
|
||||
if (overflow) {
|
||||
status |= 0x40; // Set overflow flag
|
||||
} else {
|
||||
status &= ~0x40; // Clear overflow flag
|
||||
}
|
||||
SetOverflowFlag(overflow);
|
||||
|
||||
// Update the accumulator
|
||||
// Update the accumulator with proper wrap-around
|
||||
A = (A & 0xFF00) | (result & 0xFF);
|
||||
|
||||
SetZeroFlag(A == 0);
|
||||
SetZeroFlag((A & 0xFF) == 0);
|
||||
SetNegativeFlag(A & 0x80);
|
||||
} else {
|
||||
uint32_t result = A + operand; // + (C ? 1 : 0);
|
||||
SetCarryFlag(!(result > 0xFFFF)); // Update the carry flag
|
||||
uint32_t result =
|
||||
static_cast<uint32_t>(A) + static_cast<uint32_t>(operand) + (C ? 1 : 0);
|
||||
SetCarryFlag(result > 0xFFFF); // Update the carry flag
|
||||
|
||||
// Update the overflow flag
|
||||
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) {
|
||||
uint16_t operand;
|
||||
if (E == 0) { // 16-bit mode
|
||||
|
||||
@@ -51,7 +51,9 @@ class CPU : public Memory, public Clock {
|
||||
|
||||
uint8_t FetchByteDirectPage(uint8_t operand);
|
||||
|
||||
void Run();
|
||||
void ExecuteInstruction(uint8_t opcode);
|
||||
void HandleInterrupts();
|
||||
|
||||
// ==========================================================================
|
||||
// Addressing Modes
|
||||
@@ -274,12 +276,11 @@ class CPU : public Memory, public Clock {
|
||||
// ==========================================================================
|
||||
// Registers
|
||||
|
||||
uint8_t A = 0; // Accumulator
|
||||
uint8_t B = 0; // Accumulator (High)
|
||||
uint16_t A = 0; // Accumulator
|
||||
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 DB = 0; // Data Bank register
|
||||
uint8_t PB = 0; // Program Bank register
|
||||
uint16_t PC = 0; // Program Counter
|
||||
uint8_t E = 1; // Emulation mode flag
|
||||
@@ -435,29 +436,76 @@ class CPU : public Memory, public Clock {
|
||||
// CLV: Clear overflow flag
|
||||
void CLV() { status &= ~0x40; }
|
||||
|
||||
// CMP: Compare ```
|
||||
// COP: Coprocessor ```
|
||||
// CMP: Compare TESTME
|
||||
// n Set if MSB of result is set; else cleared
|
||||
// z Set if result is zero; else cleared
|
||||
// c Set if no borrow; else cleared
|
||||
void 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
|
||||
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);
|
||||
if (GetIndexSize()) { // 8-bit
|
||||
uint8_t memory_value = isImmediate ? value : memory.ReadByte(value);
|
||||
compare(X, memory_value);
|
||||
} else { // 16-bit
|
||||
uint16_t memory_value = isImmediate ? value : memory.ReadWord(value);
|
||||
compare(X, memory_value);
|
||||
}
|
||||
}
|
||||
|
||||
// CPY: Compare Y register
|
||||
void CPY(uint16_t value, bool isImmediate = false) {
|
||||
uint16_t memory_value = isImmediate
|
||||
? value
|
||||
: (GetIndexSize() ? memory.ReadByte(value)
|
||||
: memory.ReadWord(value));
|
||||
compare(Y, memory_value);
|
||||
if (GetIndexSize()) { // 8-bit
|
||||
uint8_t memory_value = isImmediate ? value : memory.ReadByte(value);
|
||||
compare(Y, memory_value);
|
||||
} else { // 16-bit
|
||||
uint16_t memory_value = isImmediate ? value : memory.ReadWord(value);
|
||||
compare(Y, memory_value);
|
||||
}
|
||||
}
|
||||
|
||||
// DEC: Decrement ```
|
||||
// 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
|
||||
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
|
||||
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
|
||||
void STX(uint16_t address) {
|
||||
@@ -900,15 +964,20 @@ class CPU : public Memory, public Clock {
|
||||
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
|
||||
void XBA() {
|
||||
uint8_t temp = A;
|
||||
A = B;
|
||||
B = temp;
|
||||
SetZeroFlag(A == 0);
|
||||
SetNegativeFlag(A & 0x80);
|
||||
uint8_t lowByte = A & 0xFF;
|
||||
uint8_t highByte = (A >> 8) & 0xFF;
|
||||
A = (lowByte << 8) | highByte;
|
||||
}
|
||||
|
||||
// XCE: Exchange Carry and Emulation Flags
|
||||
|
||||
@@ -5,6 +5,43 @@
|
||||
#include <iostream>
|
||||
#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 app {
|
||||
namespace emu {
|
||||
@@ -103,6 +140,86 @@ class Memory {
|
||||
|
||||
class MemoryImpl : public Memory {
|
||||
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 {
|
||||
uint32_t mapped_address = GetMappedAddress(address);
|
||||
return memory_.at(mapped_address);
|
||||
@@ -174,6 +291,7 @@ class MemoryImpl : public Memory {
|
||||
(static_cast<uint32_t>(mid) << 8) | low;
|
||||
}
|
||||
|
||||
// Stack Pointer access.
|
||||
int16_t SP() const override { return SP_; }
|
||||
void SetSP(int16_t value) override { SP_ = value; }
|
||||
|
||||
|
||||
@@ -14,6 +14,30 @@ namespace yaze {
|
||||
namespace app {
|
||||
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) {
|
||||
for (int i = 0; i < 8; ++i) {
|
||||
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
|
||||
}
|
||||
|
||||
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) {
|
||||
// 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)
|
||||
// Disable the emulation flag (switch to 65816 native mode)s
|
||||
cpu.Init();
|
||||
@@ -237,7 +317,7 @@ void SNES::Run() {
|
||||
|
||||
// Enable NMI Interrupts
|
||||
void SNES::EnableVBlankInterrupts() {
|
||||
vBlankFlag = 0;
|
||||
vBlankFlag = false;
|
||||
|
||||
// Clear the RDNMI VBlank flag
|
||||
memory_.ReadByte(0x4210); // RDNMI
|
||||
@@ -248,7 +328,7 @@ void SNES::EnableVBlankInterrupts() {
|
||||
|
||||
// Wait until the VBlank routine has been processed
|
||||
void SNES::WaitForVBlank() {
|
||||
vBlankFlag = 1;
|
||||
vBlankFlag = true;
|
||||
|
||||
// Loop until `vBlankFlag` is clear
|
||||
while (vBlankFlag) {
|
||||
|
||||
@@ -62,6 +62,8 @@ class SNES : public DMA {
|
||||
SNES() = default;
|
||||
~SNES() = default;
|
||||
|
||||
ROMInfo ReadRomHeader(uint32_t offset);
|
||||
|
||||
// Initialization
|
||||
void Init(ROM& rom);
|
||||
|
||||
@@ -110,6 +112,7 @@ class SNES : public DMA {
|
||||
APU apu{memory_};
|
||||
|
||||
// Helper classes
|
||||
ROMInfo rom_info_;
|
||||
Debugger debugger;
|
||||
|
||||
std::vector<uint8_t> rom_data;
|
||||
|
||||
154
test/cpu_test.cc
154
test/cpu_test.cc
@@ -168,7 +168,7 @@ TEST_F(CPUTest, CheckMemoryContents) {
|
||||
|
||||
TEST_F(CPUTest, ADC_Immediate_TwoPositiveNumbers) {
|
||||
cpu.A = 0x01;
|
||||
cpu.status = 0xFF; // 8-bit mode
|
||||
cpu.SetAccumulatorSize(true);
|
||||
std::vector<uint8_t> data = {0x01};
|
||||
mock_memory.SetMemoryContents(data);
|
||||
|
||||
@@ -180,7 +180,7 @@ TEST_F(CPUTest, ADC_Immediate_TwoPositiveNumbers) {
|
||||
|
||||
TEST_F(CPUTest, ADC_Immediate_PositiveAndNegativeNumbers) {
|
||||
cpu.A = 10;
|
||||
cpu.status = 0xFF; // 8-bit mode
|
||||
cpu.SetAccumulatorSize(true);
|
||||
std::vector<uint8_t> data = {0x69, static_cast<uint8_t>(-20)};
|
||||
mock_memory.SetMemoryContents(data);
|
||||
|
||||
@@ -270,7 +270,7 @@ TEST_F(CPUTest, ADC_DirectPageIndexedIndirectX) {
|
||||
|
||||
TEST_F(CPUTest, ADC_CheckCarryFlag) {
|
||||
cpu.A = 0xFF;
|
||||
cpu.status = 0xFF; // 8-bit mode
|
||||
cpu.SetAccumulatorSize(true);
|
||||
std::vector<uint8_t> data = {0x15, 0x01}; // Operand at address 0x15
|
||||
mock_memory.SetMemoryContents(data);
|
||||
|
||||
@@ -286,6 +286,8 @@ TEST_F(CPUTest, ADC_AbsoluteIndexedX) {
|
||||
cpu.A = 0x03;
|
||||
cpu.X = 0x02; // X register
|
||||
cpu.PC = 0x0001;
|
||||
cpu.SetCarryFlag(false);
|
||||
cpu.SetAccumulatorSize(false); // 16-bit mode
|
||||
std::vector<uint8_t> data = {0x7D, 0x03, 0x00, 0x00, 0x05, 0x00};
|
||||
mock_memory.SetMemoryContents(data);
|
||||
|
||||
@@ -612,6 +614,24 @@ TEST_F(CPUTest, BCS_WhenCarryFlagClear) {
|
||||
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) {
|
||||
cpu.A = 0x01;
|
||||
cpu.PC = 0x0001;
|
||||
@@ -665,6 +685,9 @@ TEST_F(CPUTest, BIT_AbsoluteIndexedX) {
|
||||
EXPECT_FALSE(cpu.GetZeroFlag());
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// BMI - Branch if Minus
|
||||
|
||||
TEST_F(CPUTest, BMI_BranchTaken) {
|
||||
cpu.PC = 0x0000;
|
||||
cpu.SetNegativeFlag(true);
|
||||
@@ -685,6 +708,9 @@ TEST_F(CPUTest, BMI_BranchNotTaken) {
|
||||
EXPECT_EQ(cpu.PC, 0x0000);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// BNE - Branch if Not Equal
|
||||
|
||||
TEST_F(CPUTest, BNE_BranchTaken) {
|
||||
cpu.PC = 0x0000;
|
||||
cpu.SetZeroFlag(false);
|
||||
@@ -705,6 +731,9 @@ TEST_F(CPUTest, BNE_BranchNotTaken) {
|
||||
EXPECT_EQ(cpu.PC, 0x0000);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// BPL - Branch if Positive
|
||||
|
||||
TEST_F(CPUTest, BPL_BranchTaken) {
|
||||
cpu.PC = 0x0000;
|
||||
cpu.SetNegativeFlag(false);
|
||||
@@ -725,6 +754,9 @@ TEST_F(CPUTest, BPL_BranchNotTaken) {
|
||||
EXPECT_EQ(cpu.PC, 0x0000);
|
||||
}
|
||||
|
||||
// ============================================================================
|
||||
// BRA - Branch Always
|
||||
|
||||
TEST_F(CPUTest, BRA) {
|
||||
cpu.PC = 0x0000;
|
||||
std::vector<uint8_t> data = {0x02}; // BRA
|
||||
@@ -761,8 +793,124 @@ TEST_F(CPUTest, BRL) {
|
||||
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_F(CPUTest, CPX_CarryFlagSet) {
|
||||
cpu.X = 0x1000;
|
||||
cpu.CPX(0x0FFF);
|
||||
|
||||
Reference in New Issue
Block a user