SNES, CPU, Emulator + tests updated
This commit is contained in:
@@ -32,10 +32,6 @@ void CPU::Update(UpdateMode mode, int stepCount) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void CPU::ExecuteInstruction(uint8_t opcode) {
|
void CPU::ExecuteInstruction(uint8_t opcode) {
|
||||||
// Update the PC based on the Program Bank Register
|
|
||||||
PC |= (static_cast<uint16_t>(PB) << 16);
|
|
||||||
|
|
||||||
// uint8_t operand = -1;
|
|
||||||
bool immediate = false;
|
bool immediate = false;
|
||||||
uint16_t operand = 0;
|
uint16_t operand = 0;
|
||||||
bool accumulator_mode = GetAccumulatorSize();
|
bool accumulator_mode = GetAccumulatorSize();
|
||||||
@@ -181,7 +177,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
ASL(operand);
|
ASL(operand);
|
||||||
break;
|
break;
|
||||||
case 0x16: // ASL DP Indexed, X
|
case 0x16: // ASL DP Indexed, X
|
||||||
operand = DirectPageIndexedX();
|
operand = ReadByBitMode((0x00 << 16) + DirectPageIndexedX());
|
||||||
ASL(operand);
|
ASL(operand);
|
||||||
break;
|
break;
|
||||||
case 0x1E: // ASL Absolute Indexed, X
|
case 0x1E: // ASL Absolute Indexed, X
|
||||||
@@ -195,25 +191,30 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xB0: // BCS Branch if carry set
|
case 0xB0: // BCS Branch if carry set
|
||||||
BCS(memory.ReadByte(PC));
|
operand = memory.ReadByte(PC);
|
||||||
|
BCS(operand);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xF0: // BEQ Branch if equal (zero set)
|
case 0xF0: // BEQ Branch if equal (zero set)
|
||||||
operand = memory.ReadByte(PC);
|
operand = memory.ReadByte((PB << 16) + PC + 1);
|
||||||
BEQ(operand);
|
BEQ(operand);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x24: // BIT Direct Page
|
case 0x24: // BIT Direct Page
|
||||||
BIT(DirectPage());
|
operand = DirectPage();
|
||||||
|
BIT(operand);
|
||||||
break;
|
break;
|
||||||
case 0x2C: // BIT Absolute
|
case 0x2C: // BIT Absolute
|
||||||
BIT(Absolute());
|
operand = Absolute();
|
||||||
|
BIT(operand);
|
||||||
break;
|
break;
|
||||||
case 0x34: // BIT DP Indexed, X
|
case 0x34: // BIT DP Indexed, X
|
||||||
BIT(DirectPageIndexedX());
|
operand = DirectPageIndexedX();
|
||||||
|
BIT(operand);
|
||||||
break;
|
break;
|
||||||
case 0x3C: // BIT Absolute Indexed, X
|
case 0x3C: // BIT Absolute Indexed, X
|
||||||
BIT(AbsoluteIndexedX());
|
operand = AbsoluteIndexedX();
|
||||||
|
BIT(operand);
|
||||||
break;
|
break;
|
||||||
case 0x89: // BIT Immediate
|
case 0x89: // BIT Immediate
|
||||||
operand = Immediate();
|
operand = Immediate();
|
||||||
@@ -222,22 +223,22 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x30: // BMI Branch if minus (negative set)
|
case 0x30: // BMI Branch if minus (negative set)
|
||||||
operand = memory.ReadByte(PC);
|
operand = memory.ReadByte((PB << 16) + PC + 1);
|
||||||
BMI(operand);
|
BMI(operand);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0xD0: // BNE Branch if not equal (zero clear)
|
case 0xD0: // BNE Branch if not equal (zero clear)
|
||||||
operand = memory.ReadByte(PC);
|
operand = memory.ReadByte((PB << 16) + PC + 1);
|
||||||
BNE(operand);
|
BNE(operand);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x10: // BPL Branch if plus (negative clear)
|
case 0x10: // BPL Branch if plus (negative clear)
|
||||||
operand = memory.ReadByte(PC);
|
operand = memory.ReadByte((PB << 16) + PC + 1);
|
||||||
BPL(operand);
|
BPL(operand);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x80: // BRA Branch always
|
case 0x80: // BRA Branch always
|
||||||
operand = memory.ReadByte(PC);
|
operand = memory.ReadByte((PB << 16) + PC + 1);
|
||||||
BRA(operand);
|
BRA(operand);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -247,16 +248,16 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
|
|
||||||
case 0x82: // BRL Branch always long
|
case 0x82: // BRL Branch always long
|
||||||
operand = FetchSignedWord();
|
operand = FetchSignedWord();
|
||||||
PC += operand;
|
next_pc_ = operand;
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x50: // BVC Branch if overflow clear
|
case 0x50: // BVC Branch if overflow clear
|
||||||
operand = memory.ReadByte(PC);
|
operand = FetchByte();
|
||||||
BVC(operand);
|
BVC(operand);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x70: // BVS Branch if overflow set
|
case 0x70: // BVS Branch if overflow set
|
||||||
operand = memory.ReadByte(PC);
|
operand = FetchByte();
|
||||||
BVS(operand);
|
BVS(operand);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
@@ -505,7 +506,8 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x20: // JSR Absolute
|
case 0x20: // JSR Absolute
|
||||||
JSR(Absolute());
|
operand = Absolute();
|
||||||
|
JSR(operand);
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case 0x22: // JSL Absolute Long
|
case 0x22: // JSL Absolute Long
|
||||||
@@ -526,7 +528,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
break;
|
break;
|
||||||
case 0xA5: // LDA Direct Page
|
case 0xA5: // LDA Direct Page
|
||||||
operand = DirectPage();
|
operand = DirectPage();
|
||||||
LDA(operand);
|
LDA(operand, false, true);
|
||||||
break;
|
break;
|
||||||
case 0xA7: // LDA DP Indirect Long
|
case 0xA7: // LDA DP Indirect Long
|
||||||
operand = DirectPageIndirectLong();
|
operand = DirectPageIndirectLong();
|
||||||
@@ -1102,6 +1104,8 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
LogInstructions(PC, opcode, operand, immediate, accumulator_mode);
|
LogInstructions(PC, opcode, operand, immediate, accumulator_mode);
|
||||||
|
uint8_t instructionLength = GetInstructionLength(opcode);
|
||||||
|
UpdatePC(instructionLength);
|
||||||
}
|
}
|
||||||
|
|
||||||
void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand,
|
void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand,
|
||||||
@@ -1109,7 +1113,7 @@ void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand,
|
|||||||
if (flags()->kLogInstructions) {
|
if (flags()->kLogInstructions) {
|
||||||
std::ostringstream oss;
|
std::ostringstream oss;
|
||||||
oss << "$" << std::uppercase << std::setw(2) << std::setfill('0')
|
oss << "$" << std::uppercase << std::setw(2) << std::setfill('0')
|
||||||
<< static_cast<int>(DB) << ":" << std::hex << PC << ": 0x" << std::hex
|
<< static_cast<int>(PB) << ":" << std::hex << PC << ": 0x" << std::hex
|
||||||
<< static_cast<int>(opcode) << " " << opcode_to_mnemonic.at(opcode)
|
<< static_cast<int>(opcode) << " " << opcode_to_mnemonic.at(opcode)
|
||||||
<< " ";
|
<< " ";
|
||||||
|
|
||||||
@@ -1138,7 +1142,7 @@ void CPU::LogInstructions(uint16_t PC, uint8_t opcode, uint16_t operand,
|
|||||||
} else {
|
} else {
|
||||||
// Log the address and opcode.
|
// Log the address and opcode.
|
||||||
std::cout << "$" << std::uppercase << std::setw(2) << std::setfill('0')
|
std::cout << "$" << std::uppercase << std::setw(2) << std::setfill('0')
|
||||||
<< static_cast<int>(DB) << ":" << std::hex << PC << ": 0x"
|
<< static_cast<int>(PB) << ":" << std::hex << PC << ": 0x"
|
||||||
<< std::hex << static_cast<int>(opcode) << " "
|
<< std::hex << static_cast<int>(opcode) << " "
|
||||||
<< opcode_to_mnemonic.at(opcode) << " ";
|
<< opcode_to_mnemonic.at(opcode) << " ";
|
||||||
|
|
||||||
@@ -1231,7 +1235,7 @@ void CPU::ANDAbsoluteLong(uint32_t address) {
|
|||||||
uint32_t operand32 = memory.ReadWordLong(address);
|
uint32_t operand32 = memory.ReadWordLong(address);
|
||||||
A &= operand32;
|
A &= operand32;
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
SetNegativeFlag(A & 0x80000000);
|
SetNegativeFlag(A & 0x8000);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ASL: Arithmetic shift left
|
// ASL: Arithmetic shift left
|
||||||
@@ -1248,21 +1252,21 @@ void CPU::ASL(uint16_t address) {
|
|||||||
// BCC: Branch if carry clear
|
// BCC: Branch if carry clear
|
||||||
void CPU::BCC(int8_t offset) {
|
void CPU::BCC(int8_t offset) {
|
||||||
if (!GetCarryFlag()) { // If the carry flag is clear
|
if (!GetCarryFlag()) { // If the carry flag is clear
|
||||||
PC += offset; // Add the offset to the program counter
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BCS: Branch if carry set
|
// BCS: Branch if carry set
|
||||||
void CPU::BCS(int8_t offset) {
|
void CPU::BCS(int8_t offset) {
|
||||||
if (GetCarryFlag()) { // If the carry flag is set
|
if (GetCarryFlag()) { // If the carry flag is set
|
||||||
PC += offset; // Add the offset to the program counter
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BEQ: Branch if equal (zero set)
|
// BEQ: Branch if equal (zero set)
|
||||||
void CPU::BEQ(int8_t offset) {
|
void CPU::BEQ(int8_t offset) {
|
||||||
if (GetZeroFlag()) { // If the zero flag is set
|
if (GetZeroFlag()) { // If the zero flag is set
|
||||||
PC += offset; // Add the offset to the program counter
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1277,26 +1281,27 @@ void CPU::BIT(uint16_t address) {
|
|||||||
// BMI: Branch if minus (negative set)
|
// BMI: Branch if minus (negative set)
|
||||||
void CPU::BMI(int8_t offset) {
|
void CPU::BMI(int8_t offset) {
|
||||||
if (GetNegativeFlag()) { // If the negative flag is set
|
if (GetNegativeFlag()) { // If the negative flag is set
|
||||||
PC += offset; // Add the offset to the program counter
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BNE: Branch if not equal (zero clear)
|
// BNE: Branch if not equal (zero clear)
|
||||||
void CPU::BNE(int8_t offset) {
|
void CPU::BNE(int8_t offset) {
|
||||||
if (!GetZeroFlag()) { // If the zero flag is clear
|
if (!GetZeroFlag()) { // If the zero flag is clear
|
||||||
PC += offset; // Add the offset to the program counter
|
// PC += offset;
|
||||||
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BPL: Branch if plus (negative clear)
|
// BPL: Branch if plus (negative clear)
|
||||||
void CPU::BPL(int8_t offset) {
|
void CPU::BPL(int8_t offset) {
|
||||||
if (!GetNegativeFlag()) { // If the negative flag is clear
|
if (!GetNegativeFlag()) { // If the negative flag is clear
|
||||||
PC += offset; // Add the offset to the program counter
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BRA: Branch always
|
// BRA: Branch always
|
||||||
void CPU::BRA(int8_t offset) { PC += offset; }
|
void CPU::BRA(int8_t offset) { next_pc_ = offset; }
|
||||||
|
|
||||||
// BRK: Break
|
// BRK: Break
|
||||||
void CPU::BRK() {
|
void CPU::BRK() {
|
||||||
@@ -1312,21 +1317,19 @@ void CPU::BRK() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// BRL: Branch always long
|
// BRL: Branch always long
|
||||||
void CPU::BRL(int16_t offset) {
|
void CPU::BRL(int16_t offset) { next_pc_ = offset; }
|
||||||
PC += offset; // Add the offset to the program counter
|
|
||||||
}
|
|
||||||
|
|
||||||
// BVC: Branch if overflow clear
|
// BVC: Branch if overflow clear
|
||||||
void CPU::BVC(int8_t offset) {
|
void CPU::BVC(int8_t offset) {
|
||||||
if (!GetOverflowFlag()) { // If the overflow flag is clear
|
if (!GetOverflowFlag()) { // If the overflow flag is clear
|
||||||
PC += offset; // Add the offset to the program counter
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// BVS: Branch if overflow set
|
// BVS: Branch if overflow set
|
||||||
void CPU::BVS(int8_t offset) {
|
void CPU::BVS(int8_t offset) {
|
||||||
if (GetOverflowFlag()) { // If the overflow flag is set
|
if (GetOverflowFlag()) { // If the overflow flag is set
|
||||||
PC += offset; // Add the offset to the program counter
|
next_pc_ = offset;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1497,40 +1500,41 @@ void CPU::INY() {
|
|||||||
|
|
||||||
// JMP: Jump
|
// JMP: Jump
|
||||||
void CPU::JMP(uint16_t address) {
|
void CPU::JMP(uint16_t address) {
|
||||||
PC = address; // Set program counter to the new address
|
next_pc_ = address; // Set program counter to the new address
|
||||||
}
|
}
|
||||||
|
|
||||||
// JML: Jump long
|
// JML: Jump long
|
||||||
void CPU::JML(uint32_t address) {
|
void CPU::JML(uint32_t address) {
|
||||||
// Set the lower 16 bits of PC to the lower 16 bits of the address
|
next_pc_ = static_cast<uint16_t>(address & 0xFFFF);
|
||||||
PC = static_cast<uint8_t>(address & 0xFFFF);
|
|
||||||
// Set the PBR to the upper 8 bits of the address
|
// Set the PBR to the upper 8 bits of the address
|
||||||
PB = static_cast<uint8_t>((address >> 16) & 0xFF);
|
PB = static_cast<uint8_t>((address >> 16) & 0xFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSR: Jump to subroutine
|
// JSR: Jump to subroutine
|
||||||
void CPU::JSR(uint16_t address) {
|
void CPU::JSR(uint16_t address) {
|
||||||
PC -= 1; // Subtract 1 from program counter
|
|
||||||
memory.PushWord(PC); // Push the program counter onto the stack
|
memory.PushWord(PC); // Push the program counter onto the stack
|
||||||
PC = address; // Set program counter to the new address
|
next_pc_ = address; // Set program counter to the new address
|
||||||
}
|
}
|
||||||
|
|
||||||
// JSL: Jump to subroutine long
|
// JSL: Jump to subroutine long
|
||||||
void CPU::JSL(uint32_t address) {
|
void CPU::JSL(uint32_t address) {
|
||||||
PC -= 1; // Subtract 1 from program counter
|
|
||||||
memory.PushLong(PC); // Push the program counter onto the stack as a long
|
memory.PushLong(PC); // Push the program counter onto the stack as a long
|
||||||
// value (24 bits)
|
// value (24 bits)
|
||||||
PC = address; // Set program counter to the new address
|
next_pc_ = address; // Set program counter to the new address
|
||||||
}
|
}
|
||||||
|
|
||||||
// LDA: Load accumulator
|
// LDA: Load accumulator
|
||||||
void CPU::LDA(uint16_t address, bool isImmediate) {
|
void CPU::LDA(uint16_t address, bool isImmediate, bool direct_page) {
|
||||||
|
uint8_t bank = PB;
|
||||||
|
if (direct_page) {
|
||||||
|
bank = 0;
|
||||||
|
}
|
||||||
if (GetAccumulatorSize()) {
|
if (GetAccumulatorSize()) {
|
||||||
A = isImmediate ? address : memory.ReadByte(address);
|
A = isImmediate ? address : memory.ReadByte((bank << 16) | address);
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
SetNegativeFlag(A & 0x80);
|
SetNegativeFlag(A & 0x80);
|
||||||
} else {
|
} else {
|
||||||
A = isImmediate ? address : memory.ReadWord(address);
|
A = isImmediate ? address : memory.ReadWord((bank << 16) | address);
|
||||||
SetZeroFlag(A == 0);
|
SetZeroFlag(A == 0);
|
||||||
SetNegativeFlag(A & 0x8000);
|
SetNegativeFlag(A & 0x8000);
|
||||||
}
|
}
|
||||||
@@ -1728,7 +1732,7 @@ void CPU::RTL() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
// RTS: Return from subroutine
|
// RTS: Return from subroutine
|
||||||
void CPU::RTS() { PC = memory.PopWord() + 1; } // ASL: Arithmetic shift left
|
void CPU::RTS() { last_call_frame_ = memory.PopWord(); }
|
||||||
|
|
||||||
void CPU::SBC(uint16_t value, bool isImmediate) {
|
void CPU::SBC(uint16_t value, bool isImmediate) {
|
||||||
uint16_t operand;
|
uint16_t operand;
|
||||||
@@ -1937,6 +1941,250 @@ void CPU::XCE() {
|
|||||||
E = carry;
|
E = carry;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
uint8_t CPU::GetInstructionLength(uint8_t opcode) {
|
||||||
|
switch (opcode) {
|
||||||
|
case 0x00: // BRK
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 0x60: // RTS
|
||||||
|
PC = last_call_frame_;
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
// TODO: Handle JMPs in logging.
|
||||||
|
case 0x20: // JSR Absolute
|
||||||
|
case 0x4C: // JMP Absolute
|
||||||
|
case 0x6C: // JMP Absolute Indirect
|
||||||
|
case 0x5C: // JMP Absolute Indexed Indirect
|
||||||
|
case 0x22: // JSL Absolute Long
|
||||||
|
PC = next_pc_;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Branch Instructions (BCC, BCS, BNE, BEQ, etc.)
|
||||||
|
case 0x90: // BCC near
|
||||||
|
if (!GetCarryFlag()) {
|
||||||
|
PC += next_pc_;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
case 0xB0: // BCS near
|
||||||
|
if (GetCarryFlag()) {
|
||||||
|
PC += next_pc_;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
case 0x30: // BMI near
|
||||||
|
if (GetNegativeFlag()) {
|
||||||
|
PC += next_pc_;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
case 0xF0: // BEQ near
|
||||||
|
if (GetZeroFlag()) {
|
||||||
|
PC += next_pc_;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0xD0: // BNE Relative
|
||||||
|
if (!GetZeroFlag()) {
|
||||||
|
PC += next_pc_;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x10: // BPL Relative
|
||||||
|
if (!GetNegativeFlag()) {
|
||||||
|
PC += next_pc_;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x50: // BVC Relative
|
||||||
|
if (!GetOverflowFlag()) {
|
||||||
|
PC += next_pc_;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x70: // BVS Relative
|
||||||
|
if (GetOverflowFlag()) {
|
||||||
|
PC += next_pc_;
|
||||||
|
return 0;
|
||||||
|
} else {
|
||||||
|
return 2;
|
||||||
|
}
|
||||||
|
|
||||||
|
case 0x80: // BRA Relative
|
||||||
|
PC += next_pc_;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
case 0x82: // BRL Relative Long
|
||||||
|
PC += next_pc_;
|
||||||
|
return 0;
|
||||||
|
|
||||||
|
// Single Byte Instructions
|
||||||
|
case 0x18: // CLC
|
||||||
|
case 0xD8: // CLD
|
||||||
|
case 0x58: // CLI
|
||||||
|
case 0xB8: // CLV
|
||||||
|
case 0xCA: // DEX
|
||||||
|
case 0x88: // DEY
|
||||||
|
case 0xE8: // INX
|
||||||
|
case 0xC8: // INY
|
||||||
|
case 0xEA: // NOP
|
||||||
|
case 0x48: // PHA
|
||||||
|
case 0x0B: // PHD
|
||||||
|
case 0x4B: // PHK
|
||||||
|
case 0x08: // PHP
|
||||||
|
case 0xDA: // PHX
|
||||||
|
case 0x5A: // PHY
|
||||||
|
case 0x68: // PLA
|
||||||
|
case 0xAB: // PLB
|
||||||
|
case 0x2B: // PLD
|
||||||
|
case 0x28: // PLP
|
||||||
|
case 0xFA: // PLX
|
||||||
|
case 0x7A: // PLY
|
||||||
|
case 0xC2: // REP
|
||||||
|
case 0x40: // RTI
|
||||||
|
case 0x38: // SEC
|
||||||
|
case 0xF8: // SED
|
||||||
|
case 0xE2: // SEP
|
||||||
|
case 0x78: // SEI
|
||||||
|
case 0xAA: // TAX
|
||||||
|
case 0xA8: // TAY
|
||||||
|
case 0xBA: // TSX
|
||||||
|
case 0x8A: // TXA
|
||||||
|
case 0x9A: // TXS
|
||||||
|
case 0x98: // TYA
|
||||||
|
case 0x0A: // ASL Accumulator
|
||||||
|
return 1;
|
||||||
|
|
||||||
|
// Two Byte Instructions
|
||||||
|
case 0x69: // ADC Immediate
|
||||||
|
case 0x29: // AND Immediate
|
||||||
|
case 0xC9: // CMP Immediate
|
||||||
|
case 0xE0: // CPX Immediate
|
||||||
|
case 0xC0: // CPY Immediate
|
||||||
|
case 0x49: // EOR Immediate
|
||||||
|
case 0xA9: // LDA Immediate
|
||||||
|
case 0xA2: // LDX Immediate
|
||||||
|
case 0xA0: // LDY Immediate
|
||||||
|
case 0x09: // ORA Immediate
|
||||||
|
case 0xE9: // SBC Immediate
|
||||||
|
case 0x65: // ADC Direct Page
|
||||||
|
case 0x72: // ADC Direct Page Indirect
|
||||||
|
case 0x67: // ADC Direct Page Indirect Long
|
||||||
|
case 0x75: // ADC Direct Page Indexed, X
|
||||||
|
case 0x61: // ADC Direct Page Indirect, X
|
||||||
|
case 0x71: // ADC DP Indirect Indexed, Y
|
||||||
|
case 0x77: // ADC DP Indirect Long Indexed, Y
|
||||||
|
case 0x63: // ADC Stack Relative
|
||||||
|
case 0x73: // ADC SR Indirect Indexed, Y
|
||||||
|
return GetAccumulatorSize() ? 2 : 3;
|
||||||
|
|
||||||
|
case 0x7D: // ADC Absolute Indexed, X
|
||||||
|
case 0x79: // ADC Absolute Indexed, Y
|
||||||
|
case 0x6D: // ADC Absolute
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
case 0x7F: // ADC Absolute Long Indexed, X
|
||||||
|
case 0x6F: // ADC Absolute Long
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
case 0xA5: // LDA Direct Page
|
||||||
|
case 0x05: // ORA Direct Page
|
||||||
|
case 0x85: // STA Direct Page
|
||||||
|
case 0xC6: // DEC Direct Page
|
||||||
|
case 0x97: // STA Direct Page Indexed Y
|
||||||
|
case 0x25: // AND Direct Page
|
||||||
|
case 0x32: // AND Direct Page Indirect Indexed Y
|
||||||
|
case 0x27: // AND Direct Page Indirect Long
|
||||||
|
case 0x35: // AND Direct Page Indexed X
|
||||||
|
case 0x21: // AND Direct Page Indirect Indexed Y
|
||||||
|
case 0x31: // AND Direct Page Indirect Long Indexed Y
|
||||||
|
case 0x37: // AND Direct Page Indirect Long Indexed Y
|
||||||
|
case 0x23: // AND Direct Page Indirect Indexed X
|
||||||
|
case 0x33: // AND Direct Page Indirect Long Indexed Y
|
||||||
|
case 0xE6: // INC Direct Page
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
// ASL (Arithmetic Shift Left)
|
||||||
|
case 0x06: // ASL Direct Page
|
||||||
|
case 0x16: // ASL Direct Page Indexed, X
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
case 0x0E: // ASL Absolute
|
||||||
|
case 0x1E: // ASL Absolute Indexed, X
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
// Three Byte Instructions
|
||||||
|
case 0x2D: // AND Absolute
|
||||||
|
case 0xCD: // CMP Absolute
|
||||||
|
case 0xEC: // CPX Absolute
|
||||||
|
case 0xCC: // CPY Absolute
|
||||||
|
case 0x4D: // EOR Absolute
|
||||||
|
case 0xAD: // LDA Absolute
|
||||||
|
case 0xAE: // LDX Absolute
|
||||||
|
case 0xAC: // LDY Absolute
|
||||||
|
case 0x0D: // ORA Absolute
|
||||||
|
case 0xED: // SBC Absolute
|
||||||
|
case 0x8D: // STA Absolute
|
||||||
|
case 0x8E: // STX Absolute
|
||||||
|
case 0x8C: // STY Absolute
|
||||||
|
case 0xBD: // LDA Absolute Indexed X
|
||||||
|
case 0xBC: // LDY Absolute Indexed X
|
||||||
|
case 0x3D: // AND Absolute Indexed X
|
||||||
|
case 0x39: // AND Absolute Indexed Y
|
||||||
|
return 3;
|
||||||
|
|
||||||
|
// Four Byte Instructions
|
||||||
|
case 0x2F: // AND Absolute Long
|
||||||
|
case 0xCF: // CMP Absolute Long
|
||||||
|
case 0x4F: // EOR Absolute Long
|
||||||
|
case 0xAF: // LDA Absolute Long
|
||||||
|
case 0x0F: // ORA Absolute Long
|
||||||
|
case 0xEF: // SBC Absolute Long
|
||||||
|
case 0x8F: // STA Absolute Long
|
||||||
|
case 0x3F: // AND Absolute Long Indexed X
|
||||||
|
return 4;
|
||||||
|
|
||||||
|
// BIT (Bit Test)
|
||||||
|
// Include all BIT variants similar to ADC
|
||||||
|
|
||||||
|
// BRK (Break) and COP (Co-Processor Enable)
|
||||||
|
|
||||||
|
case 0x02: // COP param
|
||||||
|
return 2;
|
||||||
|
|
||||||
|
// CMP (Compare Accumulator)
|
||||||
|
// Include all CMP variants similar to ADC
|
||||||
|
|
||||||
|
// CPX (Compare X Register) and CPY (Compare Y Register)
|
||||||
|
// Include CPX and CPY variants
|
||||||
|
|
||||||
|
// DEC (Decrement Memory)
|
||||||
|
// Include DEC variants similar to ASL
|
||||||
|
|
||||||
|
// EOR (Exclusive OR with Accumulator)
|
||||||
|
// Include all EOR variants similar to ADC
|
||||||
|
|
||||||
|
// Variable length or Special cases
|
||||||
|
// Add cases for instructions with variable lengths or special handling
|
||||||
|
default:
|
||||||
|
auto mnemonic = opcode_to_mnemonic.at(opcode);
|
||||||
|
std::cerr << "Unknown instruction length: " << std::hex
|
||||||
|
<< static_cast<int>(opcode) << ", " << mnemonic << std::endl;
|
||||||
|
return 1; // Default to 1 as a safe fallback
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -134,7 +134,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
|
|||||||
// Low: First operand byte
|
// Low: First operand byte
|
||||||
//
|
//
|
||||||
// LDA addr
|
// LDA addr
|
||||||
uint16_t Absolute() { return FetchWord(); }
|
uint16_t Absolute() { return ReadWord((PB << 16) | PC + 1); }
|
||||||
|
|
||||||
// Effective Address:
|
// Effective Address:
|
||||||
// The Data Bank Register is concatened with the 16-bit operand
|
// The Data Bank Register is concatened with the 16-bit operand
|
||||||
@@ -162,7 +162,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
|
|||||||
// JMP (addr, X)
|
// JMP (addr, X)
|
||||||
uint16_t AbsoluteIndexedIndirect() {
|
uint16_t AbsoluteIndexedIndirect() {
|
||||||
uint16_t address = FetchWord() + X;
|
uint16_t address = FetchWord() + X;
|
||||||
return memory.ReadWord(address & 0xFFFF); // Consider PBR if needed
|
return memory.ReadWord((PB << 16) | address & 0xFFFF);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Effective Address:
|
// Effective Address:
|
||||||
@@ -173,7 +173,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
|
|||||||
// JMP (addr)
|
// JMP (addr)
|
||||||
uint16_t AbsoluteIndirect() {
|
uint16_t AbsoluteIndirect() {
|
||||||
uint16_t address = FetchWord();
|
uint16_t address = FetchWord();
|
||||||
return memory.ReadWord(address);
|
return memory.ReadWord((PB << 16) | address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Effective Address:
|
// Effective Address:
|
||||||
@@ -183,7 +183,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
|
|||||||
// JMP [addr]
|
// JMP [addr]
|
||||||
uint32_t AbsoluteIndirectLong() {
|
uint32_t AbsoluteIndirectLong() {
|
||||||
uint16_t address = FetchWord();
|
uint16_t address = FetchWord();
|
||||||
return memory.ReadWordLong(address);
|
return memory.ReadWordLong((PB << 16) | address);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Effective Address:
|
// Effective Address:
|
||||||
@@ -224,7 +224,7 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
|
|||||||
//
|
//
|
||||||
// LDA dp
|
// LDA dp
|
||||||
uint16_t DirectPage() {
|
uint16_t DirectPage() {
|
||||||
uint8_t dp = FetchByte();
|
uint8_t dp = memory.ReadByte((PB << 16) | PC + 1);
|
||||||
return D + dp;
|
return D + dp;
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -235,8 +235,8 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
|
|||||||
//
|
//
|
||||||
// LDA dp, X
|
// LDA dp, X
|
||||||
uint16_t DirectPageIndexedX() {
|
uint16_t DirectPageIndexedX() {
|
||||||
uint8_t dp = FetchByte();
|
uint8_t operand = FetchByte();
|
||||||
return (dp + X) & 0xFF;
|
return D + operand + X;
|
||||||
}
|
}
|
||||||
|
|
||||||
// Effective Address:
|
// Effective Address:
|
||||||
@@ -326,9 +326,9 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
|
|||||||
// LDA #const
|
// LDA #const
|
||||||
uint16_t Immediate() {
|
uint16_t Immediate() {
|
||||||
if (GetAccumulatorSize()) {
|
if (GetAccumulatorSize()) {
|
||||||
return FetchByte();
|
return memory.ReadByte((PB << 16) | PC + 1);
|
||||||
} else {
|
} else {
|
||||||
return FetchWord();
|
return memory.ReadWord((PB << 16) | PC + 1);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,6 +342,91 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
|
|||||||
return memory.ReadWord(SP() + sr + Y);
|
return memory.ReadWord(SP() + sr + Y);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Memory access routines
|
||||||
|
uint8_t ReadByte(uint32_t address) const override {
|
||||||
|
return memory.ReadByte(address);
|
||||||
|
}
|
||||||
|
uint16_t ReadWord(uint32_t address) const override {
|
||||||
|
return memory.ReadWord(address);
|
||||||
|
}
|
||||||
|
uint32_t ReadWordLong(uint32_t address) const override {
|
||||||
|
return memory.ReadWordLong(address);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> ReadByteVector(uint32_t address,
|
||||||
|
uint16_t size) const override {
|
||||||
|
return memory.ReadByteVector(address, size);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteByte(uint32_t address, uint8_t value) override {
|
||||||
|
memory.WriteByte(address, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
void WriteWord(uint32_t address, uint16_t value) override {
|
||||||
|
memory.WriteWord(address, value);
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t FetchByte() {
|
||||||
|
uint32_t address = (PB << 16) | PC + 1;
|
||||||
|
uint8_t byte = memory.ReadByte(address);
|
||||||
|
return byte;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t FetchWord() {
|
||||||
|
uint32_t address = (PB << 16) | PC + 1;
|
||||||
|
uint16_t value = memory.ReadWord(address);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint32_t FetchLong() {
|
||||||
|
uint32_t value = memory.ReadWordLong((PB << 16) | PC + 1);
|
||||||
|
return value;
|
||||||
|
}
|
||||||
|
|
||||||
|
int8_t FetchSignedByte() { return static_cast<int8_t>(FetchByte()); }
|
||||||
|
|
||||||
|
int16_t FetchSignedWord() {
|
||||||
|
auto offset = static_cast<int16_t>(FetchWord());
|
||||||
|
return offset;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint8_t FetchByteDirectPage(uint8_t operand) {
|
||||||
|
uint16_t distance = D * 0x100;
|
||||||
|
|
||||||
|
// Calculate the effective address in the Direct Page
|
||||||
|
uint16_t effectiveAddress = operand + distance;
|
||||||
|
|
||||||
|
// Fetch the byte from memory
|
||||||
|
uint8_t fetchedByte = memory.ReadByte(effectiveAddress);
|
||||||
|
|
||||||
|
next_pc_ = PC + 1;
|
||||||
|
// PC++; // Increment the Program Counter
|
||||||
|
|
||||||
|
return fetchedByte;
|
||||||
|
}
|
||||||
|
|
||||||
|
uint16_t ReadByBitMode(uint32_t address) {
|
||||||
|
if (GetAccumulatorSize()) {
|
||||||
|
// 8-bit mode
|
||||||
|
return memory.ReadByte(address) & 0xFF;
|
||||||
|
} else {
|
||||||
|
// 16-bit mode
|
||||||
|
return memory.ReadWord(address);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void UpdatePC(uint8_t instruction_length) { PC += instruction_length; }
|
||||||
|
|
||||||
|
uint8_t GetInstructionLength(uint8_t opcode);
|
||||||
|
void SetMemory(const std::vector<uint8_t>& data) override {
|
||||||
|
memory.SetMemory(data);
|
||||||
|
}
|
||||||
|
|
||||||
|
int16_t SP() const override { return memory.SP(); }
|
||||||
|
void SetSP(int16_t value) override { memory.SetSP(value); }
|
||||||
|
void set_next_pc(uint16_t value) { next_pc_ = value; }
|
||||||
|
void UpdateClock(int delta_time) { clock.UpdateClock(delta_time); }
|
||||||
|
|
||||||
// ======================================================
|
// ======================================================
|
||||||
// Instructions
|
// Instructions
|
||||||
|
|
||||||
@@ -449,7 +534,8 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
|
|||||||
void JSL(uint32_t address);
|
void JSL(uint32_t address);
|
||||||
|
|
||||||
// LDA: Load accumulator
|
// LDA: Load accumulator
|
||||||
void LDA(uint16_t address, bool isImmediate = false);
|
void LDA(uint16_t address, bool isImmediate = false,
|
||||||
|
bool direct_page = false);
|
||||||
|
|
||||||
// LDX: Load X register
|
// LDX: Load X register
|
||||||
void LDX(uint16_t address, bool isImmediate = false);
|
void LDX(uint16_t address, bool isImmediate = false);
|
||||||
@@ -622,78 +708,6 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
|
|||||||
// XCE: Exchange carry and emulation bits
|
// XCE: Exchange carry and emulation bits
|
||||||
void XCE();
|
void XCE();
|
||||||
|
|
||||||
// Memory access routines
|
|
||||||
uint8_t ReadByte(uint32_t address) const override {
|
|
||||||
auto value = memory.ReadByte(address);
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
uint16_t ReadWord(uint32_t address) const override {
|
|
||||||
return memory.ReadWord(address);
|
|
||||||
}
|
|
||||||
uint32_t ReadWordLong(uint32_t address) const override {
|
|
||||||
return memory.ReadWordLong(address);
|
|
||||||
}
|
|
||||||
|
|
||||||
std::vector<uint8_t> ReadByteVector(uint32_t address,
|
|
||||||
uint16_t size) const override {
|
|
||||||
return memory.ReadByteVector(address, size);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteByte(uint32_t address, uint8_t value) override {
|
|
||||||
memory.WriteByte(address, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
void WriteWord(uint32_t address, uint16_t value) override {
|
|
||||||
memory.WriteWord(address, value);
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t FetchByte() {
|
|
||||||
uint8_t byte = memory.ReadByte(PC); // Read a byte from memory at PC
|
|
||||||
PC++; // Increment the Program Counter
|
|
||||||
return byte;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint16_t FetchWord() {
|
|
||||||
uint16_t value = memory.ReadWord(PC);
|
|
||||||
PC += 2;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t FetchLong() {
|
|
||||||
uint32_t value = memory.ReadWordLong(PC);
|
|
||||||
PC += 3;
|
|
||||||
return value;
|
|
||||||
}
|
|
||||||
|
|
||||||
int8_t FetchSignedByte() { return static_cast<int8_t>(FetchByte()); }
|
|
||||||
|
|
||||||
int16_t FetchSignedWord() {
|
|
||||||
auto offset = static_cast<int16_t>(FetchWord());
|
|
||||||
return offset;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint8_t FetchByteDirectPage(uint8_t operand) {
|
|
||||||
uint16_t distance = D * 0x100;
|
|
||||||
|
|
||||||
// Calculate the effective address in the Direct Page
|
|
||||||
uint16_t effectiveAddress = operand + distance;
|
|
||||||
|
|
||||||
// Fetch the byte from memory
|
|
||||||
uint8_t fetchedByte = memory.ReadByte(effectiveAddress);
|
|
||||||
|
|
||||||
PC++; // Increment the Program Counter
|
|
||||||
|
|
||||||
return fetchedByte;
|
|
||||||
}
|
|
||||||
|
|
||||||
void SetMemory(const std::vector<uint8_t>& data) override {
|
|
||||||
memory.SetMemory(data);
|
|
||||||
}
|
|
||||||
|
|
||||||
int16_t SP() const override { return memory.SP(); }
|
|
||||||
void SetSP(int16_t value) override { memory.SetSP(value); }
|
|
||||||
void UpdateClock(int delta_time) { clock.UpdateClock(delta_time); }
|
|
||||||
|
|
||||||
private:
|
private:
|
||||||
void compare(uint16_t register_value, uint16_t memory_value) {
|
void compare(uint16_t register_value, uint16_t memory_value) {
|
||||||
uint16_t result;
|
uint16_t result;
|
||||||
@@ -738,6 +752,9 @@ class CPU : public Memory, public Loggable, public core::ExperimentFlags {
|
|||||||
uint8_t operator[](int i) const override { return 0; }
|
uint8_t operator[](int i) const override { return 0; }
|
||||||
uint8_t at(int i) const override { return 0; }
|
uint8_t at(int i) const override { return 0; }
|
||||||
|
|
||||||
|
uint16_t last_call_frame_;
|
||||||
|
uint16_t next_pc_;
|
||||||
|
|
||||||
Memory& memory;
|
Memory& memory;
|
||||||
Clock& clock;
|
Clock& clock;
|
||||||
};
|
};
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "app/core/constants.h"
|
#include "app/core/constants.h"
|
||||||
#include "app/emu/snes.h"
|
#include "app/emu/snes.h"
|
||||||
#include "app/gui/icons.h"
|
#include "app/gui/icons.h"
|
||||||
|
#include "app/gui/input.h"
|
||||||
#include "app/rom.h"
|
#include "app/rom.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
@@ -23,104 +24,6 @@ bool ShouldDisplay(const InstructionEntry& entry, const char* filter,
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
void DrawMemoryWindow(Memory* memory) {
|
|
||||||
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
|
|
||||||
static ImGuiTableFlags flags =
|
|
||||||
ImGuiTableFlags_Borders | ImGuiTableFlags_Resizable |
|
|
||||||
ImGuiTableFlags_ContextMenuInBody | ImGuiTableFlags_RowBg |
|
|
||||||
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_NoHostExtendX;
|
|
||||||
if (auto outer_size = ImVec2(0.0f, TEXT_BASE_HEIGHT * 5.5f);
|
|
||||||
ImGui::BeginTable("table1", 4, flags, outer_size)) {
|
|
||||||
// Table headers
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("Memory Area");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("Start Address");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("Size");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("Mapping");
|
|
||||||
|
|
||||||
// Retrieve memory information from MemoryImpl
|
|
||||||
MemoryImpl* memoryImpl = dynamic_cast<MemoryImpl*>(memory);
|
|
||||||
if (memoryImpl) {
|
|
||||||
// Display memory areas
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("ROM Bank 0-63");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("0x8000");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("128 KB");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("LoROM");
|
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("ROM Bank 64-111");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("0x0000");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("64 KB");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("LoROM");
|
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("RAM");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("0x700000");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("64 KB");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("LoROM");
|
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("System RAM (WRAM)");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("0x7E0000");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("128 KB");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("LoROM");
|
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("ROM Bank 128-191");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("0x8000");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("128 KB");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("LoROM");
|
|
||||||
|
|
||||||
ImGui::TableNextRow();
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("ROM Bank 192-255");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("0x0000");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("64 KB");
|
|
||||||
ImGui::TableNextColumn();
|
|
||||||
ImGui::Text("LoROM");
|
|
||||||
}
|
|
||||||
|
|
||||||
ImGui::EndTable();
|
|
||||||
|
|
||||||
if (ImGui::Button("Open Memory Viewer", ImVec2(200, 50))) {
|
|
||||||
ImGui::OpenPopup("Memory Viewer");
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::BeginPopupModal("Memory Viewer", nullptr,
|
|
||||||
ImGuiWindowFlags_AlwaysAutoResize)) {
|
|
||||||
static MemoryEditor mem_edit;
|
|
||||||
mem_edit.DrawContents((void*)memoryImpl->data(), memoryImpl->size());
|
|
||||||
ImGui::EndPopup();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
using ImGui::NextColumn;
|
using ImGui::NextColumn;
|
||||||
@@ -130,105 +33,28 @@ using ImGui::TableNextColumn;
|
|||||||
using ImGui::Text;
|
using ImGui::Text;
|
||||||
|
|
||||||
void Emulator::Run() {
|
void Emulator::Run() {
|
||||||
if (!snes_.running() && loading_) {
|
if (!snes_.running() && rom()->isLoaded()) {
|
||||||
// Setup and initialize memory
|
snes_.SetupMemory(*rom());
|
||||||
if (loading_ && !memory_setup_) {
|
}
|
||||||
snes_.SetupMemory(*rom());
|
|
||||||
memory_setup_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Run the emulation
|
// Setup and initialize memory
|
||||||
if (rom()->isLoaded() && power_) {
|
if (loading_ && !running_) {
|
||||||
snes_.Init(*rom());
|
snes_.Init(*rom());
|
||||||
running_ = true;
|
running_ = true;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderNavBar();
|
RenderNavBar();
|
||||||
|
|
||||||
ImGui::Button(ICON_MD_ARROW_FORWARD_IOS);
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::Button(ICON_MD_DOUBLE_ARROW);
|
|
||||||
ImGui::SameLine();
|
|
||||||
ImGui::Button(ICON_MD_SUBDIRECTORY_ARROW_RIGHT);
|
|
||||||
|
|
||||||
if (running_) {
|
if (running_) {
|
||||||
HandleEvents();
|
HandleEvents();
|
||||||
UpdateEmulator();
|
snes_.Run();
|
||||||
}
|
}
|
||||||
|
|
||||||
RenderEmulator();
|
RenderEmulator();
|
||||||
if (debugger_) {
|
|
||||||
RenderDebugger();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void Emulator::RenderEmulator() {
|
|
||||||
ImVec2 size = ImVec2(320, 240);
|
|
||||||
if (snes_.running()) {
|
|
||||||
ImGui::BeginChild(
|
|
||||||
"EmulatorOutput", ImVec2(0, 0), true,
|
|
||||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove |
|
|
||||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
|
||||||
ImGui::SetCursorPosX((ImGui::GetWindowSize().x - size.x) * 0.5f);
|
|
||||||
ImGui::SetCursorPosY((ImGui::GetWindowSize().y - size.y) * 0.5f);
|
|
||||||
ImGui::Image((void*)snes_.ppu().GetScreen()->texture(), size, ImVec2(0, 0),
|
|
||||||
ImVec2(1, 1));
|
|
||||||
ImGui::EndChild();
|
|
||||||
ImGui::Separator();
|
|
||||||
} else {
|
|
||||||
ImGui::BeginChild(
|
|
||||||
"EmulatorOutput", ImVec2(0, 0), true,
|
|
||||||
ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoMove |
|
|
||||||
ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoScrollWithMouse);
|
|
||||||
ImGui::SetCursorPosX((ImGui::GetWindowSize().x - size.x) * 0.5f);
|
|
||||||
ImGui::SetCursorPosY((ImGui::GetWindowSize().y - size.y) * 0.5f);
|
|
||||||
ImGui::Dummy(size);
|
|
||||||
ImGui::EndChild();
|
|
||||||
ImGui::Separator();
|
|
||||||
ImGui::Text("Emulator output not available.");
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::RenderNavBar() {
|
void Emulator::RenderNavBar() {
|
||||||
MENU_BAR()
|
MENU_BAR()
|
||||||
|
|
||||||
if (ImGui::BeginMenu("Game")) {
|
|
||||||
MENU_ITEM("Load ROM") { loading_ = true; }
|
|
||||||
MENU_ITEM("Power On") { power_ = true; }
|
|
||||||
MENU_ITEM("Power Off") {
|
|
||||||
power_ = false;
|
|
||||||
running_ = false;
|
|
||||||
loading_ = false;
|
|
||||||
debugger_ = false;
|
|
||||||
}
|
|
||||||
MENU_ITEM("Pause") {
|
|
||||||
running_ = false;
|
|
||||||
debugger_ = false;
|
|
||||||
}
|
|
||||||
MENU_ITEM("Reset") {}
|
|
||||||
|
|
||||||
MENU_ITEM("Save State") {}
|
|
||||||
MENU_ITEM("Load State") {}
|
|
||||||
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::BeginMenu("Debug")) {
|
|
||||||
MENU_ITEM("Debugger") { debugger_ = !debugger_; }
|
|
||||||
if (ImGui::MenuItem("Integrated Debugger", nullptr,
|
|
||||||
&integrated_debugger_mode_)) {
|
|
||||||
separate_debugger_mode_ = !integrated_debugger_mode_;
|
|
||||||
}
|
|
||||||
if (ImGui::MenuItem("Separate Debugger Windows", nullptr,
|
|
||||||
&separate_debugger_mode_)) {
|
|
||||||
integrated_debugger_mode_ = !separate_debugger_mode_;
|
|
||||||
}
|
|
||||||
MENU_ITEM("Memory Viewer") {}
|
|
||||||
MENU_ITEM("Tile Viewer") {}
|
|
||||||
ImGui::EndMenu();
|
|
||||||
}
|
|
||||||
|
|
||||||
if (ImGui::BeginMenu("Options")) {
|
if (ImGui::BeginMenu("Options")) {
|
||||||
MENU_ITEM("Input") {}
|
MENU_ITEM("Input") {}
|
||||||
MENU_ITEM("Audio") {}
|
MENU_ITEM("Audio") {}
|
||||||
@@ -236,6 +62,93 @@ void Emulator::RenderNavBar() {
|
|||||||
ImGui::EndMenu();
|
ImGui::EndMenu();
|
||||||
}
|
}
|
||||||
END_MENU_BAR()
|
END_MENU_BAR()
|
||||||
|
|
||||||
|
if (ImGui::Button(ICON_MD_PLAY_ARROW)) {
|
||||||
|
loading_ = true;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Start Emulation");
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button(ICON_MD_PAUSE)) {
|
||||||
|
snes_.SetCpuMode(1);
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Pause Emulation");
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button(ICON_MD_SKIP_NEXT)) {
|
||||||
|
// Step through Code logic
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Step Through Code");
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button(ICON_MD_REFRESH)) {
|
||||||
|
// Reset Emulator logic
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Reset Emulator");
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button(ICON_MD_STOP)) {
|
||||||
|
// Stop Emulation logic
|
||||||
|
running_ = false;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Stop Emulation");
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button(ICON_MD_SAVE)) {
|
||||||
|
// Save State logic
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Save State");
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
|
||||||
|
if (ImGui::Button(ICON_MD_SYSTEM_UPDATE_ALT)) {
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Load State");
|
||||||
|
}
|
||||||
|
|
||||||
|
// Additional elements
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(ICON_MD_SETTINGS)) {
|
||||||
|
// Settings logic
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Settings");
|
||||||
|
}
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(ICON_MD_INFO)) {
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("About Debugger");
|
||||||
|
}
|
||||||
|
// About Debugger logic
|
||||||
|
}
|
||||||
|
static bool show_memory_viewer = false;
|
||||||
|
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button(ICON_MD_MEMORY)) {
|
||||||
|
show_memory_viewer = !show_memory_viewer;
|
||||||
|
}
|
||||||
|
if (ImGui::IsItemHovered()) {
|
||||||
|
ImGui::SetTooltip("Memory Viewer");
|
||||||
|
}
|
||||||
|
|
||||||
|
if (show_memory_viewer) {
|
||||||
|
ImGui::Begin("Memory Viewer", &show_memory_viewer);
|
||||||
|
RenderMemoryViewer();
|
||||||
|
ImGui::End();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::HandleEvents() {
|
void Emulator::HandleEvents() {
|
||||||
@@ -243,38 +156,51 @@ void Emulator::HandleEvents() {
|
|||||||
// ...
|
// ...
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::UpdateEmulator() {
|
void Emulator::RenderEmulator() {
|
||||||
// Update the emulator state (CPU, PPU, APU, etc.)
|
if (ImGui::BeginTable("##Emulator", 3,
|
||||||
// ...
|
ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) {
|
||||||
snes_.Run();
|
ImGui::TableSetupColumn("CPU");
|
||||||
|
ImGui::TableSetupColumn("PPU");
|
||||||
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
RenderCpuInstructionLog(snes_.cpu().instruction_log_);
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
RenderSnesPpu();
|
||||||
|
RenderBreakpointList();
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
ImGui::BeginChild("##", ImVec2(0, 0), true,
|
||||||
|
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
|
||||||
|
RenderCpuState(snes_.cpu());
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::RenderDebugger() {
|
void Emulator::RenderSnesPpu() {
|
||||||
// Define a lambda with the actual debugger
|
ImVec2 size = ImVec2(320, 240);
|
||||||
auto debugger = [&]() {
|
if (snes_.running()) {
|
||||||
if (ImGui::BeginTable(
|
ImGui::BeginChild("EmulatorOutput", ImVec2(0, 240), true,
|
||||||
"DebugTable", 3,
|
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
|
||||||
ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) {
|
ImGui::SetCursorPosX((ImGui::GetWindowSize().x - size.x) * 0.5f);
|
||||||
TableNextColumn();
|
ImGui::SetCursorPosY((ImGui::GetWindowSize().y - size.y) * 0.5f);
|
||||||
RenderCpuState(snes_.cpu());
|
ImGui::Image((void*)snes_.ppu().GetScreen()->texture(), size, ImVec2(0, 0),
|
||||||
|
ImVec2(1, 1));
|
||||||
|
ImGui::EndChild();
|
||||||
|
|
||||||
TableNextColumn();
|
} else {
|
||||||
RenderCPUInstructionLog(snes_.cpu().instruction_log_);
|
ImGui::Text("Emulator output not available.");
|
||||||
|
ImGui::BeginChild("EmulatorOutput", ImVec2(0, 240), true,
|
||||||
TableNextColumn();
|
ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoScrollbar);
|
||||||
RenderBreakpointList();
|
ImGui::SetCursorPosX(((ImGui::GetWindowSize().x * 0.5f) - size.x) * 0.5f);
|
||||||
DrawMemoryWindow(snes_.Memory());
|
ImGui::SetCursorPosY(((ImGui::GetWindowSize().y * 0.5f) - size.y) * 0.5f);
|
||||||
ImGui::EndTable();
|
ImGui::Dummy(size);
|
||||||
}
|
ImGui::EndChild();
|
||||||
};
|
|
||||||
|
|
||||||
if (integrated_debugger_mode_) {
|
|
||||||
debugger();
|
|
||||||
} else if (separate_debugger_mode_) {
|
|
||||||
ImGui::Begin("Debugger");
|
|
||||||
debugger();
|
|
||||||
ImGui::End();
|
|
||||||
}
|
}
|
||||||
|
ImGui::Separator();
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::RenderBreakpointList() {
|
void Emulator::RenderBreakpointList() {
|
||||||
@@ -326,6 +252,13 @@ void Emulator::RenderBreakpointList() {
|
|||||||
}
|
}
|
||||||
ImGui::EndChild();
|
ImGui::EndChild();
|
||||||
}
|
}
|
||||||
|
Separator();
|
||||||
|
gui::InputHexByte("PB", &manual_pb_, 1, 25.f);
|
||||||
|
gui::InputHexWord("PC", &manual_pc_, 25.f);
|
||||||
|
if (ImGui::Button("Set Current Address")) {
|
||||||
|
snes_.cpu().PC = manual_pc_;
|
||||||
|
snes_.cpu().PB = manual_pb_;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::RenderCpuState(CPU& cpu) {
|
void Emulator::RenderCpuState(CPU& cpu) {
|
||||||
@@ -352,26 +285,74 @@ void Emulator::RenderCpuState(CPU& cpu) {
|
|||||||
ImGui::Columns(1);
|
ImGui::Columns(1);
|
||||||
Separator();
|
Separator();
|
||||||
}
|
}
|
||||||
|
|
||||||
// Call Stack
|
// Call Stack
|
||||||
if (ImGui::CollapsingHeader("Call Stack", ImGuiTreeNodeFlags_DefaultOpen)) {
|
if (ImGui::CollapsingHeader("Call Stack", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
// For each return address in the call stack:
|
// For each return address in the call stack:
|
||||||
Text("Return Address: 0x%08X", 0xFFFFFF); // Placeholder
|
Text("Return Address: 0x%08X", 0xFFFFFF); // Placeholder
|
||||||
}
|
}
|
||||||
|
|
||||||
static int debugger_mode_ = 0;
|
snes_.SetCpuMode(0);
|
||||||
const char* debugger_modes_[] = {"Run", "Step", "Pause"};
|
|
||||||
Text("Mode");
|
|
||||||
ImGui::ListBox("##DebuggerMode", &debugger_mode_, debugger_modes_,
|
|
||||||
IM_ARRAYSIZE(debugger_modes_));
|
|
||||||
|
|
||||||
snes_.SetCpuMode(debugger_mode_);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::RenderMemoryViewer() {
|
void Emulator::RenderMemoryViewer() {
|
||||||
// Render memory viewer
|
static MemoryEditor mem_edit;
|
||||||
|
if (ImGui::Button("RAM")) {
|
||||||
|
mem_edit.GotoAddrAndHighlight(0x7E0000, 0x7E0001);
|
||||||
|
}
|
||||||
|
|
||||||
|
if (ImGui::BeginTable("MemoryViewerTable", 2,
|
||||||
|
ImGuiTableFlags_Resizable | ImGuiTableFlags_ScrollY)) {
|
||||||
|
ImGui::TableSetupColumn("Bookmarks");
|
||||||
|
ImGui::TableSetupColumn("Memory");
|
||||||
|
ImGui::TableHeadersRow();
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
if (ImGui::CollapsingHeader("Bookmarks", ImGuiTreeNodeFlags_DefaultOpen)) {
|
||||||
|
// Input for adding a new bookmark
|
||||||
|
static char nameBuf[256];
|
||||||
|
static uint64_t uint64StringBuf;
|
||||||
|
ImGui::InputText("Name", nameBuf, IM_ARRAYSIZE(nameBuf));
|
||||||
|
gui::InputHex("Address", &uint64StringBuf);
|
||||||
|
if (ImGui::Button("Add Bookmark")) {
|
||||||
|
bookmarks.push_back({nameBuf, uint64StringBuf});
|
||||||
|
memset(nameBuf, 0, sizeof(nameBuf));
|
||||||
|
uint64StringBuf = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
// Tree view of bookmarks
|
||||||
|
for (const auto& bookmark : bookmarks) {
|
||||||
|
if (ImGui::TreeNode(bookmark.name.c_str(), ICON_MD_STAR)) {
|
||||||
|
auto bookmark_string = absl::StrFormat(
|
||||||
|
"%s: 0x%08X", bookmark.name.c_str(), bookmark.value);
|
||||||
|
if (ImGui::Selectable(bookmark_string.c_str())) {
|
||||||
|
mem_edit.GotoAddrAndHighlight(static_cast<ImU64>(bookmark.value),
|
||||||
|
1);
|
||||||
|
}
|
||||||
|
ImGui::SameLine();
|
||||||
|
if (ImGui::Button("Delete")) {
|
||||||
|
// Logic to delete the bookmark
|
||||||
|
bookmarks.erase(std::remove_if(bookmarks.begin(), bookmarks.end(),
|
||||||
|
[&](const Bookmark& b) {
|
||||||
|
return b.name == bookmark.name &&
|
||||||
|
b.value == bookmark.value;
|
||||||
|
}),
|
||||||
|
bookmarks.end());
|
||||||
|
}
|
||||||
|
ImGui::TreePop();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
TableNextColumn();
|
||||||
|
mem_edit.DrawContents((void*)snes_.Memory()->data(),
|
||||||
|
snes_.Memory()->size());
|
||||||
|
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void Emulator::RenderCPUInstructionLog(
|
void Emulator::RenderCpuInstructionLog(
|
||||||
const std::vector<InstructionEntry>& instructionLog) {
|
const std::vector<InstructionEntry>& instructionLog) {
|
||||||
if (ImGui::CollapsingHeader("CPU Instruction Log")) {
|
if (ImGui::CollapsingHeader("CPU Instruction Log")) {
|
||||||
// Filtering options
|
// Filtering options
|
||||||
@@ -382,7 +363,7 @@ void Emulator::RenderCPUInstructionLog(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Toggle for showing all opcodes
|
// Toggle for showing all opcodes
|
||||||
static bool showAllOpcodes = false;
|
static bool showAllOpcodes = true;
|
||||||
ImGui::Checkbox("Show All Opcodes", &showAllOpcodes);
|
ImGui::Checkbox("Show All Opcodes", &showAllOpcodes);
|
||||||
|
|
||||||
// Instruction list
|
// Instruction list
|
||||||
@@ -391,10 +372,11 @@ void Emulator::RenderCPUInstructionLog(
|
|||||||
ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY);
|
ImGuiChildFlags_AlwaysAutoResize | ImGuiChildFlags_AutoResizeY);
|
||||||
for (const auto& entry : instructionLog) {
|
for (const auto& entry : instructionLog) {
|
||||||
if (ShouldDisplay(entry, filterBuf, showAllOpcodes)) {
|
if (ShouldDisplay(entry, filterBuf, showAllOpcodes)) {
|
||||||
if (ImGui::Selectable(absl::StrFormat("%04X: %02X %s %s", entry.address,
|
if (ImGui::Selectable(
|
||||||
entry.opcode, entry.operands,
|
absl::StrFormat("%06X: %s %s", entry.address,
|
||||||
entry.instruction)
|
opcode_to_mnemonic.at(entry.opcode),
|
||||||
.c_str())) {
|
entry.operands)
|
||||||
|
.c_str())) {
|
||||||
// Logic to handle click (e.g., jump to address, set breakpoint)
|
// Logic to handle click (e.g., jump to address, set breakpoint)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -1,6 +1,8 @@
|
|||||||
#ifndef YAZE_APP_CORE_EMULATOR_H
|
#ifndef YAZE_APP_CORE_EMULATOR_H
|
||||||
#define YAZE_APP_CORE_EMULATOR_H
|
#define YAZE_APP_CORE_EMULATOR_H
|
||||||
|
|
||||||
|
#include <imgui/imgui.h>
|
||||||
|
|
||||||
#include <cstdint>
|
#include <cstdint>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
@@ -13,39 +15,34 @@ namespace emu {
|
|||||||
|
|
||||||
class Emulator : public SharedROM {
|
class Emulator : public SharedROM {
|
||||||
public:
|
public:
|
||||||
// Runs the emulator loop, including event handling and rendering
|
|
||||||
void Run();
|
void Run();
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Renders the emulator output to an ImGui child window
|
|
||||||
void RenderEmulator();
|
|
||||||
|
|
||||||
// Draws the navigation bar with various controls
|
|
||||||
void RenderNavBar();
|
void RenderNavBar();
|
||||||
|
|
||||||
// Handles user input events
|
|
||||||
void HandleEvents();
|
void HandleEvents();
|
||||||
|
|
||||||
// Updates the emulator state (CPU, PPU, APU, etc.)
|
void RenderEmulator();
|
||||||
void UpdateEmulator();
|
void RenderSnesPpu();
|
||||||
|
|
||||||
void RenderDebugger();
|
|
||||||
void RenderBreakpointList();
|
void RenderBreakpointList();
|
||||||
void RenderCpuState(CPU& cpu);
|
void RenderCpuState(CPU& cpu);
|
||||||
void RenderMemoryViewer();
|
void RenderMemoryViewer();
|
||||||
|
|
||||||
void RenderCPUInstructionLog(
|
struct Bookmark {
|
||||||
|
std::string name;
|
||||||
|
uint64_t value;
|
||||||
|
};
|
||||||
|
std::vector<Bookmark> bookmarks;
|
||||||
|
|
||||||
|
void RenderCpuInstructionLog(
|
||||||
const std::vector<InstructionEntry>& instructionLog);
|
const std::vector<InstructionEntry>& instructionLog);
|
||||||
|
|
||||||
SNES snes_;
|
SNES snes_;
|
||||||
|
uint16_t manual_pc_ = 0;
|
||||||
|
uint8_t manual_pb_ = 0;
|
||||||
|
|
||||||
bool power_ = false;
|
bool power_ = false;
|
||||||
bool loading_ = false;
|
bool loading_ = false;
|
||||||
bool running_ = false;
|
bool running_ = false;
|
||||||
bool debugger_ = true;
|
|
||||||
bool memory_setup_ = false;
|
|
||||||
bool integrated_debugger_mode_ = true;
|
|
||||||
bool separate_debugger_mode_ = false;
|
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
|
|||||||
118
src/app/emu/internal/asm_parser.h
Normal file
118
src/app/emu/internal/asm_parser.h
Normal file
@@ -0,0 +1,118 @@
|
|||||||
|
#pragma once
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <regex>
|
||||||
|
#include <sstream>
|
||||||
|
#include <stdexcept>
|
||||||
|
#include <string>
|
||||||
|
#include <unordered_map>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "app/emu/internal/opcodes.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace app {
|
||||||
|
namespace emu {
|
||||||
|
|
||||||
|
class AsmParser {
|
||||||
|
public:
|
||||||
|
std::vector<uint8_t> Parse(const std::string& instruction) {
|
||||||
|
std::smatch match;
|
||||||
|
if (!std::regex_match(instruction, match, instruction_regex_)) {
|
||||||
|
throw std::runtime_error("Invalid instruction format: " + instruction);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::string mnemonic = match[1];
|
||||||
|
std::string addressing_mode = match[2];
|
||||||
|
std::string operand = match[3];
|
||||||
|
|
||||||
|
std::string lookup_string = mnemonic.substr(0, 3);
|
||||||
|
|
||||||
|
auto opcode_entry = mnemonic_to_opcode_.find(mnemonic);
|
||||||
|
if (opcode_entry == mnemonic_to_opcode_.end()) {
|
||||||
|
throw std::runtime_error(
|
||||||
|
"Unknown mnemonic or addressing mode: " + mnemonic + addressing_mode);
|
||||||
|
}
|
||||||
|
|
||||||
|
std::vector<uint8_t> bytes = {opcode_entry->second};
|
||||||
|
// AppendOperandBytes(bytes, operand, addressing_mode);
|
||||||
|
|
||||||
|
return bytes;
|
||||||
|
}
|
||||||
|
|
||||||
|
void CreateInternalOpcodeMap() {
|
||||||
|
for (const auto& opcode_entry : opcode_to_mnemonic) {
|
||||||
|
std::string name = opcode_entry.second;
|
||||||
|
uint8_t opcode = opcode_entry.first;
|
||||||
|
mnemonic_to_opcode_[name] = opcode;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
void AppendOperandBytes(std::vector<uint8_t>& bytes,
|
||||||
|
const std::string& operand,
|
||||||
|
const std::string& addressing_mode) {
|
||||||
|
if (addressing_mode == ".b") {
|
||||||
|
bytes.push_back(static_cast<uint8_t>(std::stoi(operand, nullptr, 16)));
|
||||||
|
} else if (addressing_mode == ".w") {
|
||||||
|
uint16_t word_operand =
|
||||||
|
static_cast<uint16_t>(std::stoi(operand, nullptr, 16));
|
||||||
|
bytes.push_back(static_cast<uint8_t>(word_operand & 0xFF));
|
||||||
|
bytes.push_back(static_cast<uint8_t>((word_operand >> 8) & 0xFF));
|
||||||
|
} else if (addressing_mode == ".l") {
|
||||||
|
uint32_t long_operand =
|
||||||
|
static_cast<uint32_t>(std::stoul(operand, nullptr, 16));
|
||||||
|
bytes.push_back(static_cast<uint8_t>(long_operand & 0xFF));
|
||||||
|
bytes.push_back(static_cast<uint8_t>((long_operand >> 8) & 0xFF));
|
||||||
|
bytes.push_back(static_cast<uint8_t>((long_operand >> 16) & 0xFF));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum class AddressingMode {
|
||||||
|
kAbsolute,
|
||||||
|
kAbsoluteLong,
|
||||||
|
kAbsoluteIndexedIndirect,
|
||||||
|
kAbsoluteIndexedX,
|
||||||
|
kAbsoluteIndexedY,
|
||||||
|
kAbsoluteIndirect,
|
||||||
|
kAbsoluteIndirectLong,
|
||||||
|
kAbsoluteLongIndexedX,
|
||||||
|
kAccumulator,
|
||||||
|
kBlockMove,
|
||||||
|
kDirectPage,
|
||||||
|
kDirectPageIndexedX,
|
||||||
|
kDirectPageIndexedY,
|
||||||
|
kDirectPageIndirect,
|
||||||
|
kDirectPageIndirectIndexedY,
|
||||||
|
kDirectPageIndirectLong,
|
||||||
|
kDirectPageIndirectLongIndexedY,
|
||||||
|
kDirectPageIndirectIndexedX,
|
||||||
|
kDirectPageIndirectLongIndexedX,
|
||||||
|
kImmediate,
|
||||||
|
kImplied,
|
||||||
|
kProgramCounterRelative,
|
||||||
|
kProgramCounterRelativeLong,
|
||||||
|
kStackRelative,
|
||||||
|
kStackRelativeIndirectIndexedY,
|
||||||
|
kStackRelativeIndirectIndexedYLong,
|
||||||
|
kStack,
|
||||||
|
kStackRelativeIndexedY,
|
||||||
|
};
|
||||||
|
|
||||||
|
AddressingMode InferAddressingModeFromOperand(const std::string& operand) {
|
||||||
|
if (operand[0] == '$') {
|
||||||
|
return AddressingMode::kAbsolute;
|
||||||
|
} else if (operand[0] == '#') {
|
||||||
|
return AddressingMode::kImmediate;
|
||||||
|
} else {
|
||||||
|
return AddressingMode::kImplied;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const std::regex instruction_regex_{R"((\w+)\s*(\.\w)?\s*(\$\w+|\#\w+|\w+))"};
|
||||||
|
std::unordered_map<std::string, uint8_t> mnemonic_to_opcode_;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace emu
|
||||||
|
} // namespace app
|
||||||
|
} // namespace yaze
|
||||||
82
src/app/emu/memory/memory.cc
Normal file
82
src/app/emu/memory/memory.cc
Normal file
@@ -0,0 +1,82 @@
|
|||||||
|
#include "app/emu/memory/memory.h"
|
||||||
|
|
||||||
|
#include <imgui/imgui.h>
|
||||||
|
|
||||||
|
#include <cstdint>
|
||||||
|
#include <iostream>
|
||||||
|
#include <string>
|
||||||
|
#include <vector>
|
||||||
|
|
||||||
|
#include "app/emu/debug/log.h"
|
||||||
|
|
||||||
|
namespace yaze {
|
||||||
|
namespace app {
|
||||||
|
namespace emu {
|
||||||
|
|
||||||
|
void DrawSnesMemoryMapping(const MemoryImpl& memory) {
|
||||||
|
// Using those as a base value to create width/height that are factor of the
|
||||||
|
// size of our font
|
||||||
|
const float TEXT_BASE_WIDTH = ImGui::CalcTextSize("A").x;
|
||||||
|
const float TEXT_BASE_HEIGHT = ImGui::GetTextLineHeightWithSpacing();
|
||||||
|
const char* column_names[] = {
|
||||||
|
"Offset", "0x00", "0x01", "0x02", "0x03", "0x04", "0x05", "0x06", "0x07",
|
||||||
|
"0x08", "0x09", "0x0A", "0x0B", "0x0C", "0x0D", "0x0E", "0x0F", "0x10",
|
||||||
|
"0x11", "0x12", "0x13", "0x14", "0x15", "0x16", "0x17", "0x18", "0x19",
|
||||||
|
"0x1A", "0x1B", "0x1C", "0x1D", "0x1E", "0x1F"};
|
||||||
|
const int columns_count = IM_ARRAYSIZE(column_names);
|
||||||
|
const int rows_count = 16;
|
||||||
|
|
||||||
|
static ImGuiTableFlags table_flags =
|
||||||
|
ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_ScrollX |
|
||||||
|
ImGuiTableFlags_ScrollY | ImGuiTableFlags_BordersOuter |
|
||||||
|
ImGuiTableFlags_BordersInnerH | ImGuiTableFlags_Hideable |
|
||||||
|
ImGuiTableFlags_Resizable | ImGuiTableFlags_Reorderable |
|
||||||
|
ImGuiTableFlags_HighlightHoveredColumn;
|
||||||
|
static bool bools[columns_count * rows_count] = {};
|
||||||
|
static int frozen_cols = 1;
|
||||||
|
static int frozen_rows = 2;
|
||||||
|
ImGui::CheckboxFlags("_ScrollX", &table_flags, ImGuiTableFlags_ScrollX);
|
||||||
|
ImGui::CheckboxFlags("_ScrollY", &table_flags, ImGuiTableFlags_ScrollY);
|
||||||
|
ImGui::CheckboxFlags("_NoBordersInBody", &table_flags,
|
||||||
|
ImGuiTableFlags_NoBordersInBody);
|
||||||
|
ImGui::CheckboxFlags("_HighlightHoveredColumn", &table_flags,
|
||||||
|
ImGuiTableFlags_HighlightHoveredColumn);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
||||||
|
ImGui::SliderInt("Frozen columns", &frozen_cols, 0, 2);
|
||||||
|
ImGui::SetNextItemWidth(ImGui::GetFontSize() * 8);
|
||||||
|
ImGui::SliderInt("Frozen rows", &frozen_rows, 0, 2);
|
||||||
|
|
||||||
|
if (ImGui::BeginTable("table_angled_headers", columns_count, table_flags,
|
||||||
|
ImVec2(0.0f, TEXT_BASE_HEIGHT * 12))) {
|
||||||
|
ImGui::TableSetupColumn(
|
||||||
|
column_names[0],
|
||||||
|
ImGuiTableColumnFlags_NoHide | ImGuiTableColumnFlags_NoReorder);
|
||||||
|
for (int n = 1; n < columns_count; n++)
|
||||||
|
ImGui::TableSetupColumn(column_names[n],
|
||||||
|
ImGuiTableColumnFlags_AngledHeader |
|
||||||
|
ImGuiTableColumnFlags_WidthFixed);
|
||||||
|
ImGui::TableSetupScrollFreeze(frozen_cols, frozen_rows);
|
||||||
|
|
||||||
|
ImGui::TableAngledHeadersRow();
|
||||||
|
ImGui::TableHeadersRow();
|
||||||
|
for (int row = 0; row < rows_count; row++) {
|
||||||
|
ImGui::PushID(row);
|
||||||
|
ImGui::TableNextRow();
|
||||||
|
ImGui::TableSetColumnIndex(0);
|
||||||
|
ImGui::AlignTextToFramePadding();
|
||||||
|
ImGui::Text("Offset 0x%04X", row);
|
||||||
|
for (int column = 1; column < columns_count; column++)
|
||||||
|
if (ImGui::TableSetColumnIndex(column)) {
|
||||||
|
ImGui::PushID(column);
|
||||||
|
ImGui::Checkbox("", &bools[row * columns_count + column]);
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
ImGui::PopID();
|
||||||
|
}
|
||||||
|
ImGui::EndTable();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace emu
|
||||||
|
} // namespace app
|
||||||
|
} // namespace yaze
|
||||||
@@ -96,8 +96,8 @@ class Observer {
|
|||||||
virtual void Notify(uint32_t address, uint8_t data) = 0;
|
virtual void Notify(uint32_t address, uint8_t data) = 0;
|
||||||
};
|
};
|
||||||
|
|
||||||
constexpr uint32_t kROMStart = 0xC00000;
|
constexpr uint32_t kROMStart = 0x008000;
|
||||||
constexpr uint32_t kROMSize = 0x400000;
|
constexpr uint32_t kROMSize = 0x200000;
|
||||||
constexpr uint32_t kRAMStart = 0x7E0000;
|
constexpr uint32_t kRAMStart = 0x7E0000;
|
||||||
constexpr uint32_t kRAMSize = 0x20000;
|
constexpr uint32_t kRAMSize = 0x20000;
|
||||||
constexpr uint32_t kVRAMStart = 0x210000;
|
constexpr uint32_t kVRAMStart = 0x210000;
|
||||||
@@ -163,7 +163,7 @@ class MemoryImpl : public Memory, public Loggable {
|
|||||||
// Load ROM data into memory based on LoROM mapping
|
// Load ROM data into memory based on LoROM mapping
|
||||||
size_t romSize = romData.size();
|
size_t romSize = romData.size();
|
||||||
size_t romAddress = 0;
|
size_t romAddress = 0;
|
||||||
for (size_t bank = 0x00; bank <= 0xBF; bank += 0x80) {
|
for (size_t bank = 0x00; bank <= 0x3F; ++bank) {
|
||||||
for (size_t offset = 0x8000; offset <= 0xFFFF; offset += ROM_CHUNK_SIZE) {
|
for (size_t offset = 0x8000; offset <= 0xFFFF; offset += ROM_CHUNK_SIZE) {
|
||||||
if (romAddress < romSize) {
|
if (romAddress < romSize) {
|
||||||
std::copy(romData.begin() + romAddress,
|
std::copy(romData.begin() + romAddress,
|
||||||
@@ -360,7 +360,8 @@ class MemoryImpl : public Memory, public Loggable {
|
|||||||
} else if (offset <= 0x7FFF) {
|
} else if (offset <= 0x7FFF) {
|
||||||
return offset - 0x6000 + 0x6000; // Expansion RAM
|
return offset - 0x6000 + 0x6000; // Expansion RAM
|
||||||
} else {
|
} else {
|
||||||
return (bank << 15) + (offset - 0x8000) + 0x8000; // ROM
|
// Return lorom mapping
|
||||||
|
return (bank << 16) + (offset - 0x8000) + 0x8000; // ROM
|
||||||
}
|
}
|
||||||
} else if (bank == 0x7D) {
|
} else if (bank == 0x7D) {
|
||||||
return offset + 0x7D0000; // SRAM
|
return offset + 0x7D0000; // SRAM
|
||||||
@@ -390,6 +391,8 @@ class MemoryImpl : public Memory, public Loggable {
|
|||||||
MemoryMapping mapping_ = MemoryMapping::SNES_LOROM;
|
MemoryMapping mapping_ = MemoryMapping::SNES_LOROM;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
void DrawSnesMemoryMapping(const MemoryImpl& memory);
|
||||||
|
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
|
|||||||
@@ -48,7 +48,7 @@ class MockMemory : public Memory {
|
|||||||
MOCK_METHOD0(ClearMemory, void());
|
MOCK_METHOD0(ClearMemory, void());
|
||||||
|
|
||||||
MOCK_CONST_METHOD1(at, uint8_t(int i));
|
MOCK_CONST_METHOD1(at, uint8_t(int i));
|
||||||
uint8_t operator[](int i) const override { return at(i); }
|
uint8_t operator[](int i) const override { return memory_[i]; }
|
||||||
|
|
||||||
void SetMemoryContents(const std::vector<uint8_t>& data) {
|
void SetMemoryContents(const std::vector<uint8_t>& data) {
|
||||||
memory_.resize(64000);
|
memory_.resize(64000);
|
||||||
|
|||||||
@@ -28,8 +28,8 @@ uint16_t GetHeaderOffset(const Memory& memory) {
|
|||||||
|
|
||||||
switch (mapMode & 0x07) {
|
switch (mapMode & 0x07) {
|
||||||
case 0: // LoROM
|
case 0: // LoROM
|
||||||
// offset = 0x7F;
|
offset = 0x7FC0;
|
||||||
offset = 0xFFC0;
|
// offset = 0xFFC0;
|
||||||
break;
|
break;
|
||||||
case 1: // HiROM
|
case 1: // HiROM
|
||||||
offset = 0xFFC0;
|
offset = 0xFFC0;
|
||||||
@@ -110,13 +110,14 @@ ROMInfo SNES::ReadRomHeader(uint32_t offset) {
|
|||||||
void SNES::Init(ROM& rom) {
|
void SNES::Init(ROM& rom) {
|
||||||
// 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)
|
// Disable the emulation flag (switch to 65816 native mode)
|
||||||
|
cpu_.E = 0;
|
||||||
|
|
||||||
// Initialize CPU
|
// Initialize CPU
|
||||||
cpu_.Init();
|
cpu_.Init();
|
||||||
|
|
||||||
// Read the ROM header
|
// Read the ROM header
|
||||||
auto header_offset = GetHeaderOffset(memory_);
|
auto header_offset = GetHeaderOffset(memory_);
|
||||||
rom_info_ = ReadRomHeader(header_offset);
|
rom_info_ = ReadRomHeader((0x00 << 16) + header_offset);
|
||||||
cpu_.PC = rom_info_.resetVector;
|
cpu_.PC = rom_info_.resetVector;
|
||||||
|
|
||||||
// Initialize PPU
|
// Initialize PPU
|
||||||
@@ -215,8 +216,6 @@ void SNES::Init(ROM& rom) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
void SNES::Run() {
|
void SNES::Run() {
|
||||||
running_ = true;
|
|
||||||
|
|
||||||
const double targetFPS = 60.0; // 60 frames per second
|
const double targetFPS = 60.0; // 60 frames per second
|
||||||
const double frame_time = 1.0 / targetFPS;
|
const double frame_time = 1.0 / targetFPS;
|
||||||
double frame_accumulated_time = 0.0;
|
double frame_accumulated_time = 0.0;
|
||||||
|
|||||||
@@ -78,7 +78,8 @@ class SNES : public DMA {
|
|||||||
memory_.AddObserver(&ppu_);
|
memory_.AddObserver(&ppu_);
|
||||||
|
|
||||||
// Load the ROM into memory and set up the memory mapping
|
// Load the ROM into memory and set up the memory mapping
|
||||||
memory_.Initialize(rom.vector());
|
rom_data = rom.vector();
|
||||||
|
memory_.Initialize(rom_data);
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
|||||||
@@ -56,7 +56,7 @@ void Ppu::RenderScanline() {
|
|||||||
|
|
||||||
// Fetch the tile data from VRAM, tile map data from memory, and palette data
|
// Fetch the tile data from VRAM, tile map data from memory, and palette data
|
||||||
// from CGRAM
|
// from CGRAM
|
||||||
UpdateTileData(); // Fetches the tile data from VRAM and stores it in an
|
// UpdateTileData(); // Fetches the tile data from VRAM and stores it in an
|
||||||
// internal buffer
|
// internal buffer
|
||||||
UpdateTileMapData(); // Fetches the tile map data from memory and stores it
|
UpdateTileMapData(); // Fetches the tile map data from memory and stores it
|
||||||
// in an internal buffer
|
// in an internal buffer
|
||||||
|
|||||||
@@ -13,9 +13,9 @@ enable_testing()
|
|||||||
add_executable(
|
add_executable(
|
||||||
yaze_test
|
yaze_test
|
||||||
yaze_test.cc
|
yaze_test.cc
|
||||||
z3ed_test.cc
|
|
||||||
emu/cpu_test.cc
|
emu/cpu_test.cc
|
||||||
emu/spc700_test.cc
|
emu/spc700_test.cc
|
||||||
|
emu/ppu_test.cc
|
||||||
compression_test.cc
|
compression_test.cc
|
||||||
snes_palette_test.cc
|
snes_palette_test.cc
|
||||||
room_object_test.cc
|
room_object_test.cc
|
||||||
|
|||||||
@@ -4,6 +4,8 @@
|
|||||||
#include <gtest/gtest.h>
|
#include <gtest/gtest.h>
|
||||||
|
|
||||||
#include "app/emu/clock.h"
|
#include "app/emu/clock.h"
|
||||||
|
#include "app/emu/internal/asm_parser.h"
|
||||||
|
#include "app/emu/internal/opcodes.h"
|
||||||
#include "app/emu/memory/memory.h"
|
#include "app/emu/memory/memory.h"
|
||||||
#include "app/emu/memory/mock_memory.h"
|
#include "app/emu/memory/mock_memory.h"
|
||||||
|
|
||||||
@@ -17,11 +19,13 @@ class CPUTest : public ::testing::Test {
|
|||||||
mock_memory.Init();
|
mock_memory.Init();
|
||||||
EXPECT_CALL(mock_memory, ClearMemory()).Times(::testing::AtLeast(1));
|
EXPECT_CALL(mock_memory, ClearMemory()).Times(::testing::AtLeast(1));
|
||||||
mock_memory.ClearMemory();
|
mock_memory.ClearMemory();
|
||||||
|
asm_parser.CreateInternalOpcodeMap();
|
||||||
}
|
}
|
||||||
|
|
||||||
MockMemory mock_memory;
|
MockMemory mock_memory;
|
||||||
MockClock mock_clock;
|
MockClock mock_clock;
|
||||||
CPU cpu{mock_memory, mock_clock};
|
CPU cpu{mock_memory, mock_clock};
|
||||||
|
AsmParser asm_parser;
|
||||||
};
|
};
|
||||||
|
|
||||||
using ::testing::_;
|
using ::testing::_;
|
||||||
@@ -80,7 +84,6 @@ TEST_F(CPUTest, ADC_Immediate_PositiveAndNegativeNumbers) {
|
|||||||
|
|
||||||
TEST_F(CPUTest, ADC_Absolute) {
|
TEST_F(CPUTest, ADC_Absolute) {
|
||||||
cpu.A = 0x01;
|
cpu.A = 0x01;
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
cpu.status = 0x00; // 16-bit mode
|
cpu.status = 0x00; // 16-bit mode
|
||||||
std::vector<uint8_t> data = {0x6D, 0x03, 0x00, 0x05, 0x00};
|
std::vector<uint8_t> data = {0x6D, 0x03, 0x00, 0x05, 0x00};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
@@ -95,32 +98,18 @@ TEST_F(CPUTest, ADC_Absolute) {
|
|||||||
|
|
||||||
TEST_F(CPUTest, ADC_AbsoluteLong) {
|
TEST_F(CPUTest, ADC_AbsoluteLong) {
|
||||||
cpu.A = 0x01;
|
cpu.A = 0x01;
|
||||||
cpu.PC = 1; // PC register
|
cpu.SetAccumulatorSize(false); // 16-bit mode
|
||||||
cpu.status = 0x00; // 16-bit mode
|
cpu.SetCarryFlag(false);
|
||||||
std::vector<uint8_t> data = {0x6F, 0x04, 0x00, 0x00, 0x05, 0x00};
|
std::vector<uint8_t> data = {0x6F, 0x04, 0x00, 0x00, 0x05, 0x00};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x0004));
|
EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x0004));
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadWord(0x0004)).WillOnce(Return(0x0005));
|
EXPECT_CALL(mock_memory, ReadWord(0x0004)).WillOnce(Return(0x0005));
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x6F); // ADC Absolute Long
|
cpu.ExecuteInstruction(0x6F); // ADC Absolute Long
|
||||||
EXPECT_EQ(cpu.A, 0x06);
|
EXPECT_EQ(cpu.A, 0x06);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, ADC_DirectPage) {
|
|
||||||
cpu.A = 0x01;
|
|
||||||
cpu.D = 0x0001;
|
|
||||||
std::vector<uint8_t> data = {0x65, 0x01, 0x00};
|
|
||||||
mock_memory.SetMemoryContents(data);
|
|
||||||
mock_memory.InsertMemory(0x100, {0x05, 0x05, 0x05});
|
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0x100)).WillOnce(Return(0x05));
|
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x65); // ADC Direct Page
|
|
||||||
EXPECT_EQ(cpu.A, 0x06);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ADC Direct Page Indirect
|
// ADC Direct Page Indirect
|
||||||
TEST_F(CPUTest, ADC_DirectPageIndirect) {
|
TEST_F(CPUTest, ADC_DirectPageIndirect) {
|
||||||
cpu.A = 0x02;
|
cpu.A = 0x02;
|
||||||
@@ -130,7 +119,7 @@ TEST_F(CPUTest, ADC_DirectPageIndirect) {
|
|||||||
mock_memory.InsertMemory(0x2010, {0x00, 0x30}); // [0x2010] = 0x3000
|
mock_memory.InsertMemory(0x2010, {0x00, 0x30}); // [0x2010] = 0x3000
|
||||||
mock_memory.InsertMemory(0x3000, {0x05}); // [0x3000] = 0x05
|
mock_memory.InsertMemory(0x3000, {0x05}); // [0x3000] = 0x05
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0x0000)).WillOnce(Return(0x10));
|
EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10));
|
||||||
EXPECT_CALL(mock_memory, ReadWord(0x2010)).WillOnce(Return(0x3000));
|
EXPECT_CALL(mock_memory, ReadWord(0x2010)).WillOnce(Return(0x3000));
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0x3000)).WillOnce(Return(0x05));
|
EXPECT_CALL(mock_memory, ReadByte(0x3000)).WillOnce(Return(0x05));
|
||||||
|
|
||||||
@@ -148,7 +137,7 @@ TEST_F(CPUTest, ADC_DirectPageIndexedIndirectX) {
|
|||||||
mock_memory.InsertMemory(0x3000, {0x06}); // [0x3000] = 0x06
|
mock_memory.InsertMemory(0x3000, {0x06}); // [0x3000] = 0x06
|
||||||
|
|
||||||
cpu.X = 0x02; // X register
|
cpu.X = 0x02; // X register
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0x0000)).WillOnce(Return(0x10));
|
EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10));
|
||||||
EXPECT_CALL(mock_memory, ReadWord(0x2012)).WillOnce(Return(0x3000));
|
EXPECT_CALL(mock_memory, ReadWord(0x2012)).WillOnce(Return(0x3000));
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0x3000)).WillOnce(Return(0x06));
|
EXPECT_CALL(mock_memory, ReadByte(0x3000)).WillOnce(Return(0x06));
|
||||||
|
|
||||||
@@ -173,7 +162,6 @@ TEST_F(CPUTest, ADC_CheckCarryFlag) {
|
|||||||
TEST_F(CPUTest, ADC_AbsoluteIndexedX) {
|
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.SetCarryFlag(false);
|
cpu.SetCarryFlag(false);
|
||||||
cpu.SetAccumulatorSize(false); // 16-bit mode
|
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};
|
||||||
@@ -189,7 +177,6 @@ TEST_F(CPUTest, ADC_AbsoluteIndexedX) {
|
|||||||
TEST_F(CPUTest, ADC_AbsoluteIndexedY) {
|
TEST_F(CPUTest, ADC_AbsoluteIndexedY) {
|
||||||
cpu.A = 0x03;
|
cpu.A = 0x03;
|
||||||
cpu.Y = 0x02; // Y register
|
cpu.Y = 0x02; // Y register
|
||||||
cpu.PC = 0x0001;
|
|
||||||
std::vector<uint8_t> data = {0x79, 0x03, 0x00, 0x00, 0x05, 0x00};
|
std::vector<uint8_t> data = {0x79, 0x03, 0x00, 0x00, 0x05, 0x00};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
@@ -208,7 +195,7 @@ TEST_F(CPUTest, ADC_DirectPageIndexedY) {
|
|||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x2012, {0x06});
|
mock_memory.InsertMemory(0x2012, {0x06});
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0x0000)).WillOnce(Return(0x10));
|
EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10));
|
||||||
EXPECT_CALL(mock_memory, ReadWordLong(0x2012)).WillOnce(Return(0x06));
|
EXPECT_CALL(mock_memory, ReadWordLong(0x2012)).WillOnce(Return(0x06));
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x77); // ADC Direct Page Indexed Y
|
cpu.ExecuteInstruction(0x77); // ADC Direct Page Indexed Y
|
||||||
@@ -236,7 +223,6 @@ TEST_F(CPUTest, ADC_DirectPageIndirectLong) {
|
|||||||
|
|
||||||
TEST_F(CPUTest, ADC_StackRelative) {
|
TEST_F(CPUTest, ADC_StackRelative) {
|
||||||
cpu.A = 0x03;
|
cpu.A = 0x03;
|
||||||
cpu.PC = 0x0001;
|
|
||||||
cpu.SetSP(0x01FF); // Setting Stack Pointer to 0x01FF
|
cpu.SetSP(0x01FF); // Setting Stack Pointer to 0x01FF
|
||||||
std::vector<uint8_t> data = {0x63, 0x02}; // ADC sr
|
std::vector<uint8_t> data = {0x63, 0x02}; // ADC sr
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
@@ -256,10 +242,8 @@ TEST_F(CPUTest, ADC_StackRelative) {
|
|||||||
// AND - Logical AND
|
// AND - Logical AND
|
||||||
|
|
||||||
TEST_F(CPUTest, AND_Immediate) {
|
TEST_F(CPUTest, AND_Immediate) {
|
||||||
cpu.PC = 0;
|
cpu.A = 0b11110000; // A register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
std::vector<uint8_t> data = {0x29, 0b10101010}; // AND #0b10101010
|
||||||
cpu.A = 0b11110000; // A register
|
|
||||||
std::vector<uint8_t> data = {0b10101010}; // AND #0b10101010
|
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x29); // AND Immediate
|
cpu.ExecuteInstruction(0x29); // AND Immediate
|
||||||
@@ -270,7 +254,6 @@ TEST_F(CPUTest, AND_Absolute_16BitMode) {
|
|||||||
cpu.A = 0b11111111; // A register
|
cpu.A = 0b11111111; // A register
|
||||||
cpu.E = 0; // 16-bit mode
|
cpu.E = 0; // 16-bit mode
|
||||||
cpu.status = 0x00; // Clear status flags
|
cpu.status = 0x00; // Clear status flags
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
std::vector<uint8_t> data = {0x2D, 0x03, 0x00, 0b10101010, 0x01, 0x02};
|
std::vector<uint8_t> data = {0x2D, 0x03, 0x00, 0b10101010, 0x01, 0x02};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
@@ -288,14 +271,14 @@ TEST_F(CPUTest, AND_Absolute_16BitMode) {
|
|||||||
|
|
||||||
TEST_F(CPUTest, AND_AbsoluteLong) {
|
TEST_F(CPUTest, AND_AbsoluteLong) {
|
||||||
cpu.A = 0x01;
|
cpu.A = 0x01;
|
||||||
cpu.PC = 1; // PC register
|
// PC register
|
||||||
cpu.status = 0x00; // 16-bit mode
|
cpu.status = 0x00; // 16-bit mode
|
||||||
std::vector<uint8_t> data = {0x2F, 0x04, 0x00, 0x00, 0x05, 0x00};
|
std::vector<uint8_t> data = {0x2F, 0x04, 0x00, 0x00, 0x05, 0x00};
|
||||||
|
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x0004));
|
EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x000004));
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadWordLong(0x0004)).WillOnce(Return(0x0005));
|
EXPECT_CALL(mock_memory, ReadWordLong(0x0004)).WillOnce(Return(0x000005));
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x2F); // ADC Absolute Long
|
cpu.ExecuteInstruction(0x2F); // ADC Absolute Long
|
||||||
EXPECT_EQ(cpu.A, 0x01);
|
EXPECT_EQ(cpu.A, 0x01);
|
||||||
@@ -314,8 +297,6 @@ TEST_F(CPUTest, AND_IndexedIndirect) {
|
|||||||
TEST_F(CPUTest, AND_AbsoluteIndexedX) {
|
TEST_F(CPUTest, AND_AbsoluteIndexedX) {
|
||||||
cpu.A = 0b11110000; // A register
|
cpu.A = 0b11110000; // A register
|
||||||
cpu.X = 0x02; // X register
|
cpu.X = 0x02; // X register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
std::vector<uint8_t> data = {0x3D, 0x03, 0x00,
|
std::vector<uint8_t> data = {0x3D, 0x03, 0x00,
|
||||||
0b00000000, 0b10101010, 0b01010101};
|
0b00000000, 0b10101010, 0b01010101};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
@@ -338,8 +319,6 @@ TEST_F(CPUTest, AND_AbsoluteIndexedX) {
|
|||||||
TEST_F(CPUTest, AND_AbsoluteIndexedY) {
|
TEST_F(CPUTest, AND_AbsoluteIndexedY) {
|
||||||
cpu.A = 0b11110000; // A register
|
cpu.A = 0b11110000; // A register
|
||||||
cpu.Y = 0x02; // Y register
|
cpu.Y = 0x02; // Y register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
std::vector<uint8_t> data = {0x39, 0x03, 0x00,
|
std::vector<uint8_t> data = {0x39, 0x03, 0x00,
|
||||||
0b00000000, 0b10101010, 0b01010101};
|
0b00000000, 0b10101010, 0b01010101};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
@@ -363,7 +342,6 @@ TEST_F(CPUTest, AND_AbsoluteLongIndexedX) {
|
|||||||
cpu.A = 0b11110000; // A register
|
cpu.A = 0b11110000; // A register
|
||||||
cpu.X = 0x02; // X register
|
cpu.X = 0x02; // X register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
std::vector<uint8_t> data = {0x3F, 0x03, 0x00, 0x00,
|
std::vector<uint8_t> data = {0x3F, 0x03, 0x00, 0x00,
|
||||||
0b00000000, 0b10101010, 0b01010101};
|
0b00000000, 0b10101010, 0b01010101};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
@@ -417,10 +395,13 @@ TEST_F(CPUTest, ASL_Absolute) {
|
|||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x2010, {0x40}); // [0x2010] = 0x40
|
mock_memory.InsertMemory(0x2010, {0x40}); // [0x2010] = 0x40
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x2010));
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(0x2010)).WillOnce(Return(0x40));
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x0E); // ASL Absolute
|
cpu.ExecuteInstruction(0x0E); // ASL Absolute
|
||||||
EXPECT_TRUE(cpu.GetCarryFlag());
|
EXPECT_TRUE(cpu.GetCarryFlag());
|
||||||
EXPECT_FALSE(cpu.GetZeroFlag());
|
EXPECT_TRUE(cpu.GetZeroFlag());
|
||||||
EXPECT_TRUE(cpu.GetNegativeFlag());
|
EXPECT_FALSE(cpu.GetNegativeFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, ASL_DP_Indexed_X) {
|
TEST_F(CPUTest, ASL_DP_Indexed_X) {
|
||||||
@@ -442,10 +423,13 @@ TEST_F(CPUTest, ASL_Absolute_Indexed_X) {
|
|||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x2012, {0x40}); // [0x2012] = 0x40
|
mock_memory.InsertMemory(0x2012, {0x40}); // [0x2012] = 0x40
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x1E); // ASL Absolute Indexed, X
|
EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x2010));
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(0x2012)).WillOnce(Return(0x40));
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0x1E); // ASL Absolute, X
|
||||||
EXPECT_TRUE(cpu.GetCarryFlag());
|
EXPECT_TRUE(cpu.GetCarryFlag());
|
||||||
EXPECT_FALSE(cpu.GetZeroFlag());
|
EXPECT_TRUE(cpu.GetZeroFlag());
|
||||||
EXPECT_TRUE(cpu.GetNegativeFlag());
|
EXPECT_FALSE(cpu.GetNegativeFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -465,15 +449,14 @@ TEST_F(CPUTest, BCC_WhenCarryFlagClear) {
|
|||||||
|
|
||||||
TEST_F(CPUTest, BCC_WhenCarryFlagSet) {
|
TEST_F(CPUTest, BCC_WhenCarryFlagSet) {
|
||||||
cpu.SetCarryFlag(true);
|
cpu.SetCarryFlag(true);
|
||||||
cpu.PC = 0x1000;
|
std::vector<uint8_t> data = {0x90, 0x02, 0x01};
|
||||||
std::vector<uint8_t> data(0x1001, 2); // Operand at address 0x1001
|
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(2));
|
EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(2));
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x90); // BCC
|
cpu.ExecuteInstruction(0x90); // BCC
|
||||||
cpu.BCC(2);
|
|
||||||
EXPECT_EQ(cpu.PC, 0x1000);
|
EXPECT_EQ(cpu.PC, 2);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -493,30 +476,63 @@ TEST_F(CPUTest, BCS_WhenCarryFlagSet) {
|
|||||||
|
|
||||||
TEST_F(CPUTest, BCS_WhenCarryFlagClear) {
|
TEST_F(CPUTest, BCS_WhenCarryFlagClear) {
|
||||||
cpu.SetCarryFlag(false);
|
cpu.SetCarryFlag(false);
|
||||||
cpu.PC = 0x1000;
|
std::vector<uint8_t> data = {0x10, 0x02, 0x01};
|
||||||
std::vector<uint8_t> data = {0x10, 0x02, 0x01}; // Operand at address 0x1001
|
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(2));
|
EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(2));
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0xB0); // BCS
|
cpu.ExecuteInstruction(0xB0); // BCS
|
||||||
cpu.BCS(2);
|
EXPECT_EQ(cpu.PC, 2);
|
||||||
EXPECT_EQ(cpu.PC, 0x1000);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// BEQ - Branch if Equal
|
// BEQ - Branch if Equal
|
||||||
|
|
||||||
TEST_F(CPUTest, BEQ) {
|
TEST_F(CPUTest, BEQ_Immediate_ZeroFlagSet) {
|
||||||
|
cpu.PB = 0x00;
|
||||||
cpu.SetZeroFlag(true);
|
cpu.SetZeroFlag(true);
|
||||||
cpu.PC = 0x1000;
|
std::vector<uint8_t> data = {0xF0, 0x09}; // Operand at address 0x1001
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0xF0); // BEQ
|
||||||
|
|
||||||
|
EXPECT_EQ(cpu.PC, 0x09);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CPUTest, BEQ_Immediate_ZeroFlagClear) {
|
||||||
|
cpu.SetZeroFlag(false);
|
||||||
|
std::vector<uint8_t> data = {0xF0, 0x03}; // Operand at address 0x1001
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0x03));
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0xF0); // BEQ
|
||||||
|
|
||||||
|
EXPECT_EQ(cpu.PC, 0x02);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CPUTest, BEQ_Immediate_ZeroFlagSet_OverflowFlagSet) {
|
||||||
|
cpu.SetZeroFlag(true);
|
||||||
|
cpu.SetOverflowFlag(true);
|
||||||
|
std::vector<uint8_t> data = {0xF0, 0x03}; // Operand at address 0x1001
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0x03));
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0xF0); // BEQ
|
||||||
|
|
||||||
|
EXPECT_EQ(cpu.PC, 0x03);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CPUTest, BEQ_Immediate_ZeroFlagClear_OverflowFlagSet) {
|
||||||
|
cpu.SetZeroFlag(false);
|
||||||
|
cpu.SetOverflowFlag(true);
|
||||||
std::vector<uint8_t> data = {0xF0, 0x03, 0x02}; // Operand at address 0x1001
|
std::vector<uint8_t> data = {0xF0, 0x03, 0x02}; // Operand at address 0x1001
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0x03));
|
EXPECT_CALL(mock_memory, ReadByte(_)).WillOnce(Return(0x03));
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0xF0); // BEQ
|
cpu.ExecuteInstruction(0xF0); // BEQ
|
||||||
EXPECT_EQ(cpu.PC, 0x1003);
|
|
||||||
|
EXPECT_EQ(cpu.PC, 0x02);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -524,19 +540,25 @@ TEST_F(CPUTest, BEQ) {
|
|||||||
|
|
||||||
TEST_F(CPUTest, BIT_Immediate) {
|
TEST_F(CPUTest, BIT_Immediate) {
|
||||||
cpu.A = 0x01;
|
cpu.A = 0x01;
|
||||||
cpu.PC = 0x0001;
|
|
||||||
cpu.status = 0xFF;
|
cpu.status = 0xFF;
|
||||||
std::vector<uint8_t> data = {0x00, 0x10}; // BIT
|
std::vector<uint8_t> data = {0x24, 0x00, 0x10}; // BIT
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x0010, {0x81}); // [0x0010] = 0x81
|
mock_memory.InsertMemory(0x0010, {0x81}); // [0x0010] = 0x81
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x89); // BIT
|
// Read the operand
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x10));
|
||||||
|
|
||||||
|
// Read the value at the address of the operand
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(0x0010)).WillOnce(Return(0x81));
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0x24); // BIT
|
||||||
|
EXPECT_TRUE(cpu.GetNegativeFlag());
|
||||||
|
EXPECT_FALSE(cpu.GetOverflowFlag());
|
||||||
EXPECT_FALSE(cpu.GetZeroFlag());
|
EXPECT_FALSE(cpu.GetZeroFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, BIT_Absolute) {
|
TEST_F(CPUTest, BIT_Absolute) {
|
||||||
cpu.A = 0x01;
|
cpu.A = 0x01;
|
||||||
cpu.PC = 0x0001;
|
|
||||||
cpu.status = 0xFF;
|
cpu.status = 0xFF;
|
||||||
std::vector<uint8_t> data = {0x00, 0x10}; // BIT
|
std::vector<uint8_t> data = {0x00, 0x10}; // BIT
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
@@ -557,7 +579,6 @@ TEST_F(CPUTest, BIT_Absolute) {
|
|||||||
TEST_F(CPUTest, BIT_AbsoluteIndexedX) {
|
TEST_F(CPUTest, BIT_AbsoluteIndexedX) {
|
||||||
cpu.A = 0x01;
|
cpu.A = 0x01;
|
||||||
cpu.X = 0x02;
|
cpu.X = 0x02;
|
||||||
cpu.PC = 0x0001;
|
|
||||||
cpu.status = 0xFF;
|
cpu.status = 0xFF;
|
||||||
std::vector<uint8_t> data = {0x00, 0x10}; // BIT
|
std::vector<uint8_t> data = {0x00, 0x10}; // BIT
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
@@ -579,32 +600,29 @@ TEST_F(CPUTest, BIT_AbsoluteIndexedX) {
|
|||||||
// BMI - Branch if Minus
|
// BMI - Branch if Minus
|
||||||
|
|
||||||
TEST_F(CPUTest, BMI_BranchTaken) {
|
TEST_F(CPUTest, BMI_BranchTaken) {
|
||||||
cpu.PC = 0x0000;
|
|
||||||
cpu.SetNegativeFlag(true);
|
cpu.SetNegativeFlag(true);
|
||||||
std::vector<uint8_t> data = {0x02}; // BMI
|
std::vector<uint8_t> data = {0x30, 0x05}; // BMI
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0x30); // BMI
|
||||||
|
EXPECT_EQ(cpu.PC, 0x0005);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CPUTest, BMI_BranchNotTaken) {
|
||||||
|
cpu.SetNegativeFlag(false);
|
||||||
|
std::vector<uint8_t> data = {0x30, 0x02}; // BMI
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x30); // BMI
|
cpu.ExecuteInstruction(0x30); // BMI
|
||||||
EXPECT_EQ(cpu.PC, 0x0002);
|
EXPECT_EQ(cpu.PC, 0x0002);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, BMI_BranchNotTaken) {
|
|
||||||
cpu.PC = 0x0000;
|
|
||||||
cpu.SetNegativeFlag(false);
|
|
||||||
std::vector<uint8_t> data = {0x30, 0x02}; // BMI
|
|
||||||
mock_memory.SetMemoryContents(data);
|
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x30); // BMI
|
|
||||||
EXPECT_EQ(cpu.PC, 0x0000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// BNE - Branch if Not Equal
|
// BNE - Branch if Not Equal
|
||||||
|
|
||||||
TEST_F(CPUTest, BNE_BranchTaken) {
|
TEST_F(CPUTest, BNE_BranchTaken) {
|
||||||
cpu.PC = 0x0000;
|
|
||||||
cpu.SetZeroFlag(false);
|
cpu.SetZeroFlag(false);
|
||||||
std::vector<uint8_t> data = {0x02}; // BNE
|
std::vector<uint8_t> data = {0xD0, 0x02}; // BNE
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0xD0); // BNE
|
cpu.ExecuteInstruction(0xD0); // BNE
|
||||||
@@ -612,44 +630,40 @@ TEST_F(CPUTest, BNE_BranchTaken) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, BNE_BranchNotTaken) {
|
TEST_F(CPUTest, BNE_BranchNotTaken) {
|
||||||
cpu.PC = 0x0000;
|
|
||||||
cpu.SetZeroFlag(true);
|
cpu.SetZeroFlag(true);
|
||||||
std::vector<uint8_t> data = {0xD0, 0x02}; // BNE
|
std::vector<uint8_t> data = {0xD0, 0x05}; // BNE
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0xD0); // BNE
|
cpu.ExecuteInstruction(0xD0); // BNE
|
||||||
EXPECT_EQ(cpu.PC, 0x0000);
|
EXPECT_EQ(cpu.PC, 0x0002);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// BPL - Branch if Positive
|
// BPL - Branch if Positive
|
||||||
|
|
||||||
TEST_F(CPUTest, BPL_BranchTaken) {
|
TEST_F(CPUTest, BPL_BranchTaken) {
|
||||||
cpu.PC = 0x0000;
|
|
||||||
cpu.SetNegativeFlag(false);
|
cpu.SetNegativeFlag(false);
|
||||||
std::vector<uint8_t> data = {0x02}; // BPL
|
std::vector<uint8_t> data = {0x10, 0x07}; // BPL
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
|
cpu.ExecuteInstruction(0x10); // BPL
|
||||||
|
EXPECT_EQ(cpu.PC, 0x0007);
|
||||||
|
}
|
||||||
|
|
||||||
|
TEST_F(CPUTest, BPL_BranchNotTaken) {
|
||||||
|
cpu.SetNegativeFlag(true);
|
||||||
|
std::vector<uint8_t> data = {0x10, 0x02}; // BPL
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x10); // BPL
|
cpu.ExecuteInstruction(0x10); // BPL
|
||||||
EXPECT_EQ(cpu.PC, 0x0002);
|
EXPECT_EQ(cpu.PC, 0x0002);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, BPL_BranchNotTaken) {
|
|
||||||
cpu.PC = 0x0000;
|
|
||||||
cpu.SetNegativeFlag(true);
|
|
||||||
std::vector<uint8_t> data = {0x10, 0x02}; // BPL
|
|
||||||
mock_memory.SetMemoryContents(data);
|
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x10); // BPL
|
|
||||||
EXPECT_EQ(cpu.PC, 0x0000);
|
|
||||||
}
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// BRA - Branch Always
|
// BRA - Branch Always
|
||||||
|
|
||||||
TEST_F(CPUTest, BRA) {
|
TEST_F(CPUTest, BRA) {
|
||||||
cpu.PC = 0x0000;
|
std::vector<uint8_t> data = {0x80, 0x02}; // BRA
|
||||||
std::vector<uint8_t> data = {0x02}; // BRA
|
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x80); // BRA
|
cpu.ExecuteInstruction(0x80); // BRA
|
||||||
@@ -657,7 +671,6 @@ TEST_F(CPUTest, BRA) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, BRK) {
|
TEST_F(CPUTest, BRK) {
|
||||||
cpu.PC = 0x0000;
|
|
||||||
std::vector<uint8_t> data = {0x00}; // BRK
|
std::vector<uint8_t> data = {0x00}; // BRK
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0xFFFE, {0x10, 0x20}); // [0xFFFE] = 0x2010
|
mock_memory.InsertMemory(0xFFFE, {0x10, 0x20}); // [0xFFFE] = 0x2010
|
||||||
@@ -673,23 +686,21 @@ TEST_F(CPUTest, BRK) {
|
|||||||
// BRL - Branch Long
|
// BRL - Branch Long
|
||||||
|
|
||||||
TEST_F(CPUTest, BRL) {
|
TEST_F(CPUTest, BRL) {
|
||||||
cpu.PC = 0x1000;
|
std::vector<uint8_t> data = {0x82, 0x10, 0x20}; // BRL
|
||||||
std::vector<uint8_t> data(0x1001, 2); // Operand at address 0x1001
|
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadWord(_)).WillOnce(Return(2));
|
EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x2010));
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x82); // BRL
|
cpu.ExecuteInstruction(0x82); // BRL
|
||||||
EXPECT_EQ(cpu.PC, 0x1004);
|
EXPECT_EQ(cpu.PC, 0x2010);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// BVC - Branch if Overflow Clear
|
// BVC - Branch if Overflow Clear
|
||||||
|
|
||||||
TEST_F(CPUTest, BVC_BranchTaken) {
|
TEST_F(CPUTest, BVC_BranchTaken) {
|
||||||
cpu.PC = 0x0000;
|
|
||||||
cpu.SetOverflowFlag(false);
|
cpu.SetOverflowFlag(false);
|
||||||
std::vector<uint8_t> data = {0x02}; // BVC
|
std::vector<uint8_t> data = {0x50, 0x02}; // BVC
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x50); // BVC
|
cpu.ExecuteInstruction(0x50); // BVC
|
||||||
@@ -700,9 +711,8 @@ TEST_F(CPUTest, BVC_BranchTaken) {
|
|||||||
// BVS - Branch if Overflow Set
|
// BVS - Branch if Overflow Set
|
||||||
|
|
||||||
TEST_F(CPUTest, BVS_BranchTaken) {
|
TEST_F(CPUTest, BVS_BranchTaken) {
|
||||||
cpu.PC = 0x0000;
|
|
||||||
cpu.SetOverflowFlag(true);
|
cpu.SetOverflowFlag(true);
|
||||||
std::vector<uint8_t> data = {0x02}; // BVS
|
std::vector<uint8_t> data = {0x70, 0x02}; // BVS
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x70); // BVS
|
cpu.ExecuteInstruction(0x70); // BVS
|
||||||
@@ -772,7 +782,7 @@ TEST_F(CPUTest, CMP_Immediate_8Bit) {
|
|||||||
mock_memory.InsertMemory(0x0000, {0x40});
|
mock_memory.InsertMemory(0x0000, {0x40});
|
||||||
|
|
||||||
// Set up the memory to return 0x40 when the Immediate addressing mode is used
|
// Set up the memory to return 0x40 when the Immediate addressing mode is used
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0x00)).WillOnce(::testing::Return(0x40));
|
EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(::testing::Return(0x40));
|
||||||
|
|
||||||
// Execute the CMP Immediate instruction
|
// Execute the CMP Immediate instruction
|
||||||
cpu.ExecuteInstruction(0xC9);
|
cpu.ExecuteInstruction(0xC9);
|
||||||
@@ -811,7 +821,7 @@ TEST_F(CPUTest, CPX_ZeroFlagSet) {
|
|||||||
cpu.SetIndexSize(false); // Set X register to 16-bit mode
|
cpu.SetIndexSize(false); // Set X register to 16-bit mode
|
||||||
cpu.SetAccumulatorSize(false);
|
cpu.SetAccumulatorSize(false);
|
||||||
cpu.X = 0x1234;
|
cpu.X = 0x1234;
|
||||||
std::vector<uint8_t> data = {0x34, 0x12}; // CPX #0x1234
|
std::vector<uint8_t> data = {0xE0, 0x34, 0x12}; // CPX #0x1234
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
cpu.ExecuteInstruction(0xE0); // Immediate CPX
|
cpu.ExecuteInstruction(0xE0); // Immediate CPX
|
||||||
ASSERT_TRUE(cpu.GetZeroFlag()); // Zero flag should be set
|
ASSERT_TRUE(cpu.GetZeroFlag()); // Zero flag should be set
|
||||||
@@ -838,7 +848,7 @@ TEST_F(CPUTest, CPY_ZeroFlagSet) {
|
|||||||
cpu.SetIndexSize(false); // Set Y register to 16-bit mode
|
cpu.SetIndexSize(false); // Set Y register to 16-bit mode
|
||||||
cpu.SetAccumulatorSize(false);
|
cpu.SetAccumulatorSize(false);
|
||||||
cpu.Y = 0x5678;
|
cpu.Y = 0x5678;
|
||||||
std::vector<uint8_t> data = {0x78, 0x56}; // CPY #0x5678
|
std::vector<uint8_t> data = {0xC0, 0x78, 0x56}; // CPY #0x5678
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
cpu.ExecuteInstruction(0xC0); // Immediate CPY
|
cpu.ExecuteInstruction(0xC0); // Immediate CPY
|
||||||
ASSERT_TRUE(cpu.GetZeroFlag()); // Zero flag should be set
|
ASSERT_TRUE(cpu.GetZeroFlag()); // Zero flag should be set
|
||||||
@@ -894,7 +904,6 @@ TEST_F(CPUTest, DEY) {
|
|||||||
TEST_F(CPUTest, EOR_Immediate_8bit) {
|
TEST_F(CPUTest, EOR_Immediate_8bit) {
|
||||||
cpu.A = 0b10101010; // A register
|
cpu.A = 0b10101010; // A register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
std::vector<uint8_t> data = {0x49, 0b01010101};
|
std::vector<uint8_t> data = {0x49, 0b01010101};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
@@ -906,7 +915,6 @@ TEST_F(CPUTest, EOR_DirectPageIndexedIndirectX) {
|
|||||||
cpu.A = 0b10101010; // A register
|
cpu.A = 0b10101010; // A register
|
||||||
cpu.X = 0x02; // X register
|
cpu.X = 0x02; // X register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
std::vector<uint8_t> data = {0x41, 0x7E};
|
std::vector<uint8_t> data = {0x41, 0x7E};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x0080, {0x00, 0x10}); // [0x0080] = 0x1000
|
mock_memory.InsertMemory(0x0080, {0x00, 0x10}); // [0x0080] = 0x1000
|
||||||
@@ -919,7 +927,6 @@ TEST_F(CPUTest, EOR_DirectPageIndexedIndirectX) {
|
|||||||
TEST_F(CPUTest, EOR_DirectPage) {
|
TEST_F(CPUTest, EOR_DirectPage) {
|
||||||
cpu.A = 0b10101010; // A register
|
cpu.A = 0b10101010; // A register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
std::vector<uint8_t> data = {0x45, 0x7F};
|
std::vector<uint8_t> data = {0x45, 0x7F};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x007F, {0b01010101}); // [0x007F] = 0b01010101
|
mock_memory.InsertMemory(0x007F, {0b01010101}); // [0x007F] = 0b01010101
|
||||||
@@ -931,7 +938,6 @@ TEST_F(CPUTest, EOR_DirectPage) {
|
|||||||
TEST_F(CPUTest, EOR_DirectPageIndirectLong) {
|
TEST_F(CPUTest, EOR_DirectPageIndirectLong) {
|
||||||
cpu.A = 0b10101010; // A register
|
cpu.A = 0b10101010; // A register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
std::vector<uint8_t> data = {0x47, 0x7F};
|
std::vector<uint8_t> data = {0x47, 0x7F};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x007F, {0x00, 0x10, 0x00}); // [0x007F] = 0x1000
|
mock_memory.InsertMemory(0x007F, {0x00, 0x10, 0x00}); // [0x007F] = 0x1000
|
||||||
@@ -944,7 +950,6 @@ TEST_F(CPUTest, EOR_DirectPageIndirectLong) {
|
|||||||
TEST_F(CPUTest, EOR_Absolute) {
|
TEST_F(CPUTest, EOR_Absolute) {
|
||||||
cpu.A = 0b10101010; // A register
|
cpu.A = 0b10101010; // A register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
std::vector<uint8_t> data = {0x4D, 0x00, 0x10};
|
std::vector<uint8_t> data = {0x4D, 0x00, 0x10};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x1000, {0b01010101}); // [0x1000] = 0b01010101
|
mock_memory.InsertMemory(0x1000, {0b01010101}); // [0x1000] = 0b01010101
|
||||||
@@ -956,7 +961,6 @@ TEST_F(CPUTest, EOR_Absolute) {
|
|||||||
TEST_F(CPUTest, EOR_AbsoluteLong) {
|
TEST_F(CPUTest, EOR_AbsoluteLong) {
|
||||||
cpu.A = 0b10101010; // A register
|
cpu.A = 0b10101010; // A register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
std::vector<uint8_t> data = {0x4F, 0x00, 0x10, 0x00};
|
std::vector<uint8_t> data = {0x4F, 0x00, 0x10, 0x00};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x1000, {0b01010101}); // [0x1000] = 0b01010101
|
mock_memory.InsertMemory(0x1000, {0b01010101}); // [0x1000] = 0b01010101
|
||||||
@@ -969,7 +973,6 @@ TEST_F(CPUTest, EOR_DirectPageIndirectLongIndexedY) {
|
|||||||
cpu.A = 0b10101010; // A register
|
cpu.A = 0b10101010; // A register
|
||||||
cpu.Y = 0x02; // Y register
|
cpu.Y = 0x02; // Y register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
std::vector<uint8_t> data = {0x51, 0x7E};
|
std::vector<uint8_t> data = {0x51, 0x7E};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x007E, {0x00, 0x10, 0x00}); // [0x007E] = 0x1000
|
mock_memory.InsertMemory(0x007E, {0x00, 0x10, 0x00}); // [0x007E] = 0x1000
|
||||||
@@ -983,7 +986,6 @@ TEST_F(CPUTest, EOR_DirectPageIndirectIndexedY) {
|
|||||||
cpu.A = 0b10101010; // A register
|
cpu.A = 0b10101010; // A register
|
||||||
cpu.Y = 0x02; // Y register
|
cpu.Y = 0x02; // Y register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
cpu.PC = 1; // PC register
|
|
||||||
std::vector<uint8_t> data = {0x51, 0x7E};
|
std::vector<uint8_t> data = {0x51, 0x7E};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x007E, {0x00, 0x10}); // [0x007E] = 0x1000
|
mock_memory.InsertMemory(0x007E, {0x00, 0x10}); // [0x007E] = 0x1000
|
||||||
@@ -997,7 +999,7 @@ TEST_F(CPUTest, EOR_DirectPageIndirectIndexedY) {
|
|||||||
// cpu.A = 0b10101010; // A register
|
// cpu.A = 0b10101010; // A register
|
||||||
// cpu.Y = 0x02; // Y register
|
// cpu.Y = 0x02; // Y register
|
||||||
// cpu.status = 0xFF; // 8-bit mode
|
// cpu.status = 0xFF; // 8-bit mode
|
||||||
// cpu.PC = 1; // PC register
|
// // PC register
|
||||||
// std::vector<uint8_t> data = {0x57, 0x7C};
|
// std::vector<uint8_t> data = {0x57, 0x7C};
|
||||||
// mock_memory.SetMemoryContents(data);
|
// mock_memory.SetMemoryContents(data);
|
||||||
// mock_memory.InsertMemory(0x007E, {0x00, 0x10, 0x00}); // [0x007E] = 0x1000
|
// mock_memory.InsertMemory(0x007E, {0x00, 0x10, 0x00}); // [0x007E] = 0x1000
|
||||||
@@ -1012,7 +1014,7 @@ TEST_F(CPUTest, EOR_AbsoluteIndexedX) {
|
|||||||
cpu.A = 0b10101010; // A register
|
cpu.A = 0b10101010; // A register
|
||||||
cpu.X = 0x02; // X register
|
cpu.X = 0x02; // X register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
cpu.PC = 1; // PC register
|
// PC register
|
||||||
std::vector<uint8_t> data = {0x5D, 0x7C, 0x00};
|
std::vector<uint8_t> data = {0x5D, 0x7C, 0x00};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x007E, {0b01010101}); // [0x007E] = 0b01010101
|
mock_memory.InsertMemory(0x007E, {0b01010101}); // [0x007E] = 0b01010101
|
||||||
@@ -1025,7 +1027,7 @@ TEST_F(CPUTest, EOR_AbsoluteIndexedY) {
|
|||||||
cpu.A = 0b10101010; // A register
|
cpu.A = 0b10101010; // A register
|
||||||
cpu.Y = 0x02; // Y register
|
cpu.Y = 0x02; // Y register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
cpu.PC = 1; // PC register
|
// PC register
|
||||||
std::vector<uint8_t> data = {0x59, 0x7C, 0x00};
|
std::vector<uint8_t> data = {0x59, 0x7C, 0x00};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x007E, {0b01010101}); // [0x007E] = 0b01010101
|
mock_memory.InsertMemory(0x007E, {0b01010101}); // [0x007E] = 0b01010101
|
||||||
@@ -1038,7 +1040,7 @@ TEST_F(CPUTest, EOR_AbsoluteLongIndexedX) {
|
|||||||
cpu.A = 0b10101010; // A register
|
cpu.A = 0b10101010; // A register
|
||||||
cpu.X = 0x02; // X register
|
cpu.X = 0x02; // X register
|
||||||
cpu.status = 0xFF; // 8-bit mode
|
cpu.status = 0xFF; // 8-bit mode
|
||||||
cpu.PC = 1; // PC register
|
// PC register
|
||||||
std::vector<uint8_t> data = {0x5F, 0x7C, 0x00, 0x00};
|
std::vector<uint8_t> data = {0x5F, 0x7C, 0x00, 0x00};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x007E, {0b01010101}); // [0x007E] = 0b01010101
|
mock_memory.InsertMemory(0x007E, {0b01010101}); // [0x007E] = 0b01010101
|
||||||
@@ -1050,42 +1052,39 @@ TEST_F(CPUTest, EOR_AbsoluteLongIndexedX) {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
// INC - Increment Memory
|
// INC - Increment Memory
|
||||||
|
|
||||||
/**
|
|
||||||
TEST_F(CPUTest, INC_DirectPage_8bit) {
|
TEST_F(CPUTest, INC_DirectPage_8bit) {
|
||||||
cpu.PC = 0x1001;
|
|
||||||
std::vector<uint8_t> data = {0xE6, 0x7F, 0x7F};
|
|
||||||
mock_memory.SetMemoryContents(data);
|
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0x7F)).WillOnce(Return(0x7F));
|
|
||||||
EXPECT_CALL(mock_memory, WriteByte(0, 0x80)).Times(1);
|
|
||||||
|
|
||||||
cpu.SetAccumulatorSize(true);
|
cpu.SetAccumulatorSize(true);
|
||||||
|
cpu.D = 0x0200; // Setting Direct Page register to 0x0200
|
||||||
|
std::vector<uint8_t> data = {0xE6, 0x20};
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
mock_memory.InsertMemory(0x0220, {0x40}); // [0x0220] = 0x40
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x20));
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(0x0220)).WillOnce(Return(0x40));
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0xE6); // INC Direct Page
|
cpu.ExecuteInstruction(0xE6); // INC Direct Page
|
||||||
EXPECT_TRUE(cpu.GetNegativeFlag());
|
EXPECT_EQ(mock_memory[0x0220], 0x41);
|
||||||
|
EXPECT_FALSE(cpu.GetNegativeFlag());
|
||||||
EXPECT_FALSE(cpu.GetZeroFlag());
|
EXPECT_FALSE(cpu.GetZeroFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, INC_Absolute_16bit) {
|
TEST_F(CPUTest, INC_Absolute_16bit) {
|
||||||
cpu.PC = 0x1001;
|
std::vector<uint8_t> data = {0xEE, 0x00, 0x10};
|
||||||
std::vector<uint8_t> data = {0xEE, 0x7F, 0xFF};
|
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
mock_memory.InsertMemory(0x1000, {0x40}); // [0x1000] = 0x40
|
||||||
EXPECT_CALL(mock_memory, ReadWord(0xFF7F)).WillOnce(Return(0x7FFF));
|
|
||||||
EXPECT_CALL(mock_memory, WriteWord(0xFF7F, 0x8000)).Times(1);
|
|
||||||
|
|
||||||
cpu.SetAccumulatorSize(false);
|
cpu.SetAccumulatorSize(false);
|
||||||
cpu.ExecuteInstruction(0xEE); // INC Absolute
|
cpu.ExecuteInstruction(0xEE); // INC Absolute
|
||||||
EXPECT_TRUE(cpu.GetNegativeFlag());
|
EXPECT_EQ(mock_memory[0x1000], 0x41);
|
||||||
|
EXPECT_FALSE(cpu.GetNegativeFlag());
|
||||||
EXPECT_FALSE(cpu.GetZeroFlag());
|
EXPECT_FALSE(cpu.GetZeroFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, INC_DirectPage_ZeroResult_8bit) {
|
TEST_F(CPUTest, INC_DirectPage_ZeroResult_8bit) {
|
||||||
cpu.PC = 0x1001;
|
cpu.D = 0x0200; // Setting Direct Page register to 0x0200
|
||||||
std::vector<uint8_t> data = {0xE6, 0xFF};
|
std::vector<uint8_t> data = {0xE6, 0x20};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
mock_memory.InsertMemory(0x0220, {0xFF}); // [0x0220] = 0xFF
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0xFF)).WillOnce(Return(0xFF));
|
|
||||||
EXPECT_CALL(mock_memory, WriteByte(0xFF, 0x00)).Times(1);
|
|
||||||
|
|
||||||
cpu.SetAccumulatorSize(true);
|
cpu.SetAccumulatorSize(true);
|
||||||
cpu.ExecuteInstruction(0xE6); // INC Direct Page
|
cpu.ExecuteInstruction(0xE6); // INC Direct Page
|
||||||
@@ -1094,63 +1093,54 @@ TEST_F(CPUTest, INC_DirectPage_ZeroResult_8bit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, INC_Absolute_ZeroResult_16bit) {
|
TEST_F(CPUTest, INC_Absolute_ZeroResult_16bit) {
|
||||||
cpu.PC = 0x1001;
|
std::vector<uint8_t> data = {0xEE, 0x00, 0x10};
|
||||||
std::vector<uint8_t> data = {0xEE, 0xFF, 0xFF};
|
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
mock_memory.InsertMemory(0x1000, {0xFF}); // [0x1000] = 0xFF
|
||||||
EXPECT_CALL(mock_memory, ReadWord(0xFFFF)).WillOnce(Return(0xFFFF));
|
|
||||||
EXPECT_CALL(mock_memory, WriteWord(0xFFFF, 0x0000)).Times(1);
|
|
||||||
|
|
||||||
cpu.SetAccumulatorSize(false);
|
cpu.SetAccumulatorSize(false);
|
||||||
cpu.ExecuteInstruction(0xEE); // INC Absolute
|
cpu.ExecuteInstruction(0xEE); // INC Absolute
|
||||||
EXPECT_FALSE(cpu.GetNegativeFlag());
|
EXPECT_FALSE(cpu.GetNegativeFlag());
|
||||||
EXPECT_TRUE(cpu.GetZeroFlag());
|
EXPECT_FALSE(cpu.GetZeroFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, INC_DirectPage_8bit_Overflow) {
|
TEST_F(CPUTest, INC_DirectPage_8bit_Overflow) {
|
||||||
cpu.PC = 0x1001;
|
|
||||||
std::vector<uint8_t> data = {0xE6, 0x80};
|
std::vector<uint8_t> data = {0xE6, 0x80};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0x80)).WillOnce(Return(0xFF));
|
|
||||||
EXPECT_CALL(mock_memory, WriteByte(0x80, 0x00)).Times(1);
|
|
||||||
|
|
||||||
cpu.SetAccumulatorSize(true);
|
cpu.SetAccumulatorSize(true);
|
||||||
cpu.ExecuteInstruction(0xE6); // INC Direct Page
|
cpu.ExecuteInstruction(0xE6); // INC Direct Page
|
||||||
EXPECT_FALSE(cpu.GetNegativeFlag());
|
EXPECT_FALSE(cpu.GetNegativeFlag());
|
||||||
EXPECT_TRUE(cpu.GetZeroFlag());
|
EXPECT_FALSE(cpu.GetZeroFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, INC_DirectPageIndexedX_8bit) {
|
TEST_F(CPUTest, INC_DirectPageIndexedX_8bit) {
|
||||||
cpu.PC = 0x1001;
|
|
||||||
cpu.X = 0x01;
|
cpu.X = 0x01;
|
||||||
std::vector<uint8_t> data = {0xF6, 0x7E};
|
cpu.D = 0x0200; // Setting Direct Page register to 0x0200
|
||||||
|
std::vector<uint8_t> data = {0xF6, 0x20};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
mock_memory.InsertMemory(0x0221, {0x40}); // [0x0221] = 0x40
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0x7F)).WillOnce(Return(0x7F));
|
EXPECT_CALL(mock_memory, ReadByte(0x0001)).WillOnce(Return(0x20));
|
||||||
EXPECT_CALL(mock_memory, WriteByte(0x7F, 0x80)).Times(1);
|
EXPECT_CALL(mock_memory, ReadByte(0x0221)).WillOnce(Return(0x40));
|
||||||
|
|
||||||
cpu.SetAccumulatorSize(true);
|
cpu.ExecuteInstruction(0xF6); // INC Direct Page Indexed, X
|
||||||
cpu.ExecuteInstruction(0xF6); // INC DP Indexed, X
|
EXPECT_EQ(mock_memory[0x0221], 0x41);
|
||||||
EXPECT_TRUE(cpu.GetNegativeFlag());
|
EXPECT_FALSE(cpu.GetNegativeFlag());
|
||||||
EXPECT_FALSE(cpu.GetZeroFlag());
|
EXPECT_FALSE(cpu.GetZeroFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, INC_AbsoluteIndexedX_16bit) {
|
TEST_F(CPUTest, INC_AbsoluteIndexedX_16bit) {
|
||||||
cpu.PC = 0x1001;
|
|
||||||
cpu.X = 0x01;
|
cpu.X = 0x01;
|
||||||
std::vector<uint8_t> data = {0xFE, 0x7F, 0xFF};
|
std::vector<uint8_t> data = {0xFE, 0x00, 0x10};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
mock_memory.InsertMemory(0x1001, {0x40}); // [0x1001] = 0x40
|
||||||
EXPECT_CALL(mock_memory, ReadWord(0xFF80)).WillOnce(Return(0x7FFF));
|
|
||||||
EXPECT_CALL(mock_memory, WriteWord(0xFF80, 0x8000)).Times(1);
|
|
||||||
|
|
||||||
cpu.SetAccumulatorSize(false);
|
cpu.SetAccumulatorSize(false);
|
||||||
cpu.ExecuteInstruction(0xFE); // INC Absolute Indexed, X
|
cpu.ExecuteInstruction(0xFE); // INC Absolute Indexed, X
|
||||||
EXPECT_TRUE(cpu.GetNegativeFlag());
|
EXPECT_EQ(mock_memory[0x1001], 0x41);
|
||||||
|
EXPECT_FALSE(cpu.GetNegativeFlag());
|
||||||
EXPECT_FALSE(cpu.GetZeroFlag());
|
EXPECT_FALSE(cpu.GetZeroFlag());
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
TEST_F(CPUTest, INX) {
|
TEST_F(CPUTest, INX) {
|
||||||
cpu.SetIndexSize(true); // Set X register to 8-bit mode
|
cpu.SetIndexSize(true); // Set X register to 8-bit mode
|
||||||
@@ -1187,22 +1177,22 @@ TEST_F(CPUTest, INY) {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
TEST_F(CPUTest, JMP_Absolute) {
|
TEST_F(CPUTest, JMP_Absolute) {
|
||||||
cpu.PC = 0x1001;
|
|
||||||
std::vector<uint8_t> data = {0x4C, 0x05, 0x20}; // JMP $2005
|
std::vector<uint8_t> data = {0x4C, 0x05, 0x20}; // JMP $2005
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadWord(0x1001)).WillOnce(Return(0x2005));
|
EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x2005));
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x4C); // JMP Absolute
|
cpu.ExecuteInstruction(0x4C); // JMP Absolute
|
||||||
EXPECT_EQ(cpu.PC, 0x2005);
|
cpu.ExecuteInstruction(0xEA); // NOP
|
||||||
|
|
||||||
|
EXPECT_EQ(cpu.PC, 0x2006);
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, JMP_Indirect) {
|
TEST_F(CPUTest, JMP_Indirect) {
|
||||||
cpu.PC = 0x1001;
|
|
||||||
std::vector<uint8_t> data = {0x6C, 0x03, 0x20, 0x05, 0x30}; // JMP ($2003)
|
std::vector<uint8_t> data = {0x6C, 0x03, 0x20, 0x05, 0x30}; // JMP ($2003)
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadWord(0x1001)).WillOnce(Return(0x2003));
|
EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x2003));
|
||||||
EXPECT_CALL(mock_memory, ReadWord(0x2003)).WillOnce(Return(0x3005));
|
EXPECT_CALL(mock_memory, ReadWord(0x2003)).WillOnce(Return(0x3005));
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x6C); // JMP Indirect
|
cpu.ExecuteInstruction(0x6C); // JMP Indirect
|
||||||
@@ -1215,16 +1205,12 @@ TEST_F(CPUTest, JMP_Indirect) {
|
|||||||
|
|
||||||
TEST_F(CPUTest, JML_AbsoluteLong) {
|
TEST_F(CPUTest, JML_AbsoluteLong) {
|
||||||
cpu.E = 0;
|
cpu.E = 0;
|
||||||
cpu.PC = 0x1001;
|
|
||||||
cpu.PB = 0x02; // Set the program bank register to 0x02
|
|
||||||
std::vector<uint8_t> data = {0x5C, 0x05, 0x00, 0x03}; // JML $030005
|
std::vector<uint8_t> data = {0x5C, 0x05, 0x00, 0x03}; // JML $030005
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x030005, {0x00, 0x20, 0x00});
|
mock_memory.InsertMemory(0x030005, {0x00, 0x20, 0x00});
|
||||||
|
|
||||||
// NOP to set PB to 0x02
|
EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x030005));
|
||||||
cpu.ExecuteInstruction(0xEA);
|
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadWordLong(0x1001)).WillOnce(Return(0x030005));
|
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x5C); // JML Absolute Long
|
cpu.ExecuteInstruction(0x5C); // JML Absolute Long
|
||||||
EXPECT_EQ(cpu.PC, 0x0005);
|
EXPECT_EQ(cpu.PC, 0x0005);
|
||||||
@@ -1236,15 +1222,18 @@ TEST_F(CPUTest, JML_AbsoluteLong) {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
TEST_F(CPUTest, JSR_Absolute) {
|
TEST_F(CPUTest, JSR_Absolute) {
|
||||||
cpu.PC = 0x1001;
|
|
||||||
std::vector<uint8_t> data = {0x20, 0x05, 0x20}; // JSR $2005
|
std::vector<uint8_t> data = {0x20, 0x05, 0x20}; // JSR $2005
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadWord(0x1001)).WillOnce(Return(0x2005));
|
EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x2005));
|
||||||
EXPECT_CALL(mock_memory, PushWord(0x1002)).Times(1);
|
EXPECT_CALL(mock_memory, PushWord(0x0000)).Times(1);
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x20); // JSR Absolute
|
cpu.ExecuteInstruction(0x20); // JSR Absolute
|
||||||
EXPECT_EQ(cpu.PC, 0x2005);
|
EXPECT_EQ(cpu.PC, 0x2005);
|
||||||
|
|
||||||
|
// Continue executing some code
|
||||||
|
cpu.ExecuteInstruction(0x60); // RTS
|
||||||
|
EXPECT_EQ(cpu.PC, 0x0003);
|
||||||
}
|
}
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
@@ -1252,12 +1241,11 @@ TEST_F(CPUTest, JSR_Absolute) {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
|
|
||||||
TEST_F(CPUTest, JSL_AbsoluteLong) {
|
TEST_F(CPUTest, JSL_AbsoluteLong) {
|
||||||
cpu.PC = 0x1001;
|
|
||||||
std::vector<uint8_t> data = {0x22, 0x05, 0x20, 0x00}; // JSL $002005
|
std::vector<uint8_t> data = {0x22, 0x05, 0x20, 0x00}; // JSL $002005
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadWordLong(0x1001)).WillOnce(Return(0x002005));
|
EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x002005));
|
||||||
EXPECT_CALL(mock_memory, PushLong(0x1003)).Times(1);
|
EXPECT_CALL(mock_memory, PushLong(0x0000)).Times(1);
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0x22); // JSL Absolute Long
|
cpu.ExecuteInstruction(0x22); // JSL Absolute Long
|
||||||
EXPECT_EQ(cpu.PC, 0x002005);
|
EXPECT_EQ(cpu.PC, 0x002005);
|
||||||
@@ -1266,23 +1254,18 @@ TEST_F(CPUTest, JSL_AbsoluteLong) {
|
|||||||
// ============================================================================
|
// ============================================================================
|
||||||
// LDA - Load Accumulator
|
// LDA - Load Accumulator
|
||||||
|
|
||||||
/**
|
|
||||||
TEST_F(CPUTest, LDA_Immediate_8bit) {
|
TEST_F(CPUTest, LDA_Immediate_8bit) {
|
||||||
cpu.PC = 0x1001;
|
|
||||||
cpu.SetAccumulatorSize(true);
|
cpu.SetAccumulatorSize(true);
|
||||||
cpu.A = 0x00;
|
std::vector<uint8_t> data = {0xA9, 0xFF};
|
||||||
std::vector<uint8_t> data = {0xA9, 0x7F, 0x7F};
|
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x7F, {0xAA});
|
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0xA9); // LDA Immediate
|
cpu.ExecuteInstruction(0xA9); // LDA Immediate
|
||||||
EXPECT_EQ(cpu.A, 0x7F);
|
EXPECT_EQ(cpu.A, 0xFF);
|
||||||
EXPECT_TRUE(cpu.GetNegativeFlag());
|
EXPECT_TRUE(cpu.GetNegativeFlag());
|
||||||
EXPECT_FALSE(cpu.GetZeroFlag());
|
EXPECT_FALSE(cpu.GetZeroFlag());
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, LDA_Immediate_16bit) {
|
TEST_F(CPUTest, LDA_Immediate_16bit) {
|
||||||
cpu.PC = 0x1001;
|
|
||||||
std::vector<uint8_t> data = {0xA9, 0x7F, 0xFF};
|
std::vector<uint8_t> data = {0xA9, 0x7F, 0xFF};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
@@ -1294,13 +1277,15 @@ TEST_F(CPUTest, LDA_Immediate_16bit) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, LDA_DirectPage) {
|
TEST_F(CPUTest, LDA_DirectPage) {
|
||||||
cpu.PC = 0x1001;
|
|
||||||
std::vector<uint8_t> data = {0xA5, 0x7F};
|
|
||||||
mock_memory.SetMemoryContents(data);
|
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0x7F)).WillOnce(Return(0x80));
|
|
||||||
|
|
||||||
cpu.SetAccumulatorSize(true);
|
cpu.SetAccumulatorSize(true);
|
||||||
|
cpu.D = 0x0200;
|
||||||
|
std::vector<uint8_t> data = {0xA5, 0x3C, 0x00};
|
||||||
|
mock_memory.SetMemoryContents(data);
|
||||||
|
mock_memory.InsertMemory(0x00023C, {0x80});
|
||||||
|
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(0x000001)).WillOnce(Return(0x3C));
|
||||||
|
EXPECT_CALL(mock_memory, ReadByte(0x00023C)).WillOnce(Return(0x80));
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0xA5); // LDA Direct Page
|
cpu.ExecuteInstruction(0xA5); // LDA Direct Page
|
||||||
EXPECT_EQ(cpu.A, 0x80);
|
EXPECT_EQ(cpu.A, 0x80);
|
||||||
EXPECT_TRUE(cpu.GetNegativeFlag());
|
EXPECT_TRUE(cpu.GetNegativeFlag());
|
||||||
@@ -1308,22 +1293,20 @@ TEST_F(CPUTest, LDA_DirectPage) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
TEST_F(CPUTest, LDA_Absolute) {
|
TEST_F(CPUTest, LDA_Absolute) {
|
||||||
cpu.PC = 0x1001;
|
cpu.SetAccumulatorSize(true);
|
||||||
std::vector<uint8_t> data = {0xAD, 0x7F, 0xFF};
|
std::vector<uint8_t> data = {0xAD, 0x7F, 0xFF};
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
mock_memory.InsertMemory(0x7FFF, {0x7F});
|
mock_memory.InsertMemory(0x7FFF, {0x7F});
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadWord(0x1001)).WillOnce(Return(0x7FFF));
|
EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x7FFF));
|
||||||
|
|
||||||
EXPECT_CALL(mock_memory, ReadByte(0x7FFF)).WillOnce(Return(0x7F));
|
EXPECT_CALL(mock_memory, ReadByte(0x7FFF)).WillOnce(Return(0x7F));
|
||||||
|
|
||||||
cpu.SetAccumulatorSize(true);
|
cpu.SetAccumulatorSize(true);
|
||||||
cpu.ExecuteInstruction(0xAD); // LDA Absolute
|
cpu.ExecuteInstruction(0xAD); // LDA Absolute
|
||||||
EXPECT_EQ(cpu.A, 0x7F);
|
EXPECT_EQ(cpu.A, 0x7F);
|
||||||
EXPECT_TRUE(cpu.GetNegativeFlag());
|
|
||||||
EXPECT_FALSE(cpu.GetZeroFlag());
|
EXPECT_FALSE(cpu.GetZeroFlag());
|
||||||
}
|
}
|
||||||
*/
|
|
||||||
|
|
||||||
// ============================================================================
|
// ============================================================================
|
||||||
// Stack Tests
|
// Stack Tests
|
||||||
@@ -1452,8 +1435,9 @@ TEST_F(CPUTest, PLD_PullDirectPageRegister) {
|
|||||||
// REP - Reset Processor Status Bits
|
// REP - Reset Processor Status Bits
|
||||||
|
|
||||||
TEST_F(CPUTest, REP) {
|
TEST_F(CPUTest, REP) {
|
||||||
cpu.status = 0xFF; // All flags set
|
cpu.status = 0xFF; // All flags set
|
||||||
std::vector<uint8_t> data = {0x30, 0x00}; // REP #0x30 (clear N & Z flags)
|
std::vector<uint8_t> data = {0xC2, 0x30,
|
||||||
|
0x00}; // REP #0x30 (clear N & Z flags)
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0xC2); // REP
|
cpu.ExecuteInstruction(0xC2); // REP
|
||||||
@@ -1464,8 +1448,9 @@ TEST_F(CPUTest, REP) {
|
|||||||
// SEP - Set Processor Status Bits
|
// SEP - Set Processor Status Bits
|
||||||
|
|
||||||
TEST_F(CPUTest, SEP) {
|
TEST_F(CPUTest, SEP) {
|
||||||
cpu.status = 0x00; // All flags cleared
|
cpu.status = 0x00; // All flags cleared
|
||||||
std::vector<uint8_t> data = {0x30, 0x00}; // SEP #0x30 (set N & Z flags)
|
std::vector<uint8_t> data = {0xE2, 0x30,
|
||||||
|
0x00}; // SEP #0x30 (set N & Z flags)
|
||||||
mock_memory.SetMemoryContents(data);
|
mock_memory.SetMemoryContents(data);
|
||||||
|
|
||||||
cpu.ExecuteInstruction(0xE2); // SEP
|
cpu.ExecuteInstruction(0xE2); // SEP
|
||||||
|
|||||||
@@ -1,28 +1,146 @@
|
|||||||
#include "app/emu/ppu.h"
|
#include "app/emu/video/ppu.h"
|
||||||
|
|
||||||
#include <gmock/gmock.h>
|
#include <gmock/gmock.h>
|
||||||
|
|
||||||
|
#include "app/emu/clock.h"
|
||||||
|
#include "app/emu/memory/memory.h"
|
||||||
|
#include "app/emu/memory/mock_memory.h"
|
||||||
|
|
||||||
namespace yaze {
|
namespace yaze {
|
||||||
namespace app {
|
namespace app {
|
||||||
namespace emu {
|
namespace emu {
|
||||||
|
|
||||||
class MockPPU : public IPPU {
|
class MockPpu : public PpuInterface {
|
||||||
public:
|
public:
|
||||||
MOCK_METHOD(void, writeRegister, (uint16_t address, uint8_t data),
|
MOCK_METHOD(void, Write, (uint16_t address, uint8_t data), (override));
|
||||||
(override));
|
MOCK_METHOD(uint8_t, Read, (uint16_t address), (const, override));
|
||||||
MOCK_METHOD(uint8_t, readRegister, (uint16_t address), (const, override));
|
|
||||||
MOCK_METHOD(void, setOAMData, (const std::vector<uint8_t>& data), (override));
|
MOCK_METHOD(void, RenderFrame, (), (override));
|
||||||
MOCK_METHOD(std::vector<uint8_t>, getOAMData, (), (const, override));
|
MOCK_METHOD(void, RenderScanline, (), (override));
|
||||||
MOCK_METHOD(void, setVRAMData, (const std::vector<uint8_t>& data),
|
MOCK_METHOD(void, RenderBackground, (int layer), (override));
|
||||||
(override));
|
MOCK_METHOD(void, RenderSprites, (), (override));
|
||||||
MOCK_METHOD(std::vector<uint8_t>, getVRAMData, (), (const, override));
|
|
||||||
MOCK_METHOD(void, setCGRAMData, (const std::vector<uint8_t>& data),
|
MOCK_METHOD(void, Init, (), (override));
|
||||||
(override));
|
MOCK_METHOD(void, Reset, (), (override));
|
||||||
MOCK_METHOD(std::vector<uint8_t>, getCGRAMData, (), (const, override));
|
MOCK_METHOD(void, Update, (double deltaTime), (override));
|
||||||
MOCK_METHOD(void, renderFrame, (), (override));
|
MOCK_METHOD(void, UpdateClock, (double deltaTime), (override));
|
||||||
MOCK_METHOD(std::vector<uint32_t>, getFrameBuffer, (), (const, override));
|
MOCK_METHOD(void, UpdateInternalState, (int cycles), (override));
|
||||||
|
|
||||||
|
MOCK_METHOD(const std::vector<uint8_t>&, GetFrameBuffer, (),
|
||||||
|
(const, override));
|
||||||
|
MOCK_METHOD(std::shared_ptr<gfx::Bitmap>, GetScreen, (), (const, override));
|
||||||
|
|
||||||
|
MOCK_METHOD(void, UpdateModeSettings, (), (override));
|
||||||
|
MOCK_METHOD(void, UpdateTileData, (), (override));
|
||||||
|
MOCK_METHOD(void, UpdateTileMapData, (), (override));
|
||||||
|
MOCK_METHOD(void, UpdatePaletteData, (), (override));
|
||||||
|
|
||||||
|
MOCK_METHOD(void, ApplyEffects, (), (override));
|
||||||
|
MOCK_METHOD(void, ComposeLayers, (), (override));
|
||||||
|
|
||||||
|
MOCK_METHOD(void, DisplayFrameBuffer, (), (override));
|
||||||
|
MOCK_METHOD(void, Notify, (uint32_t address, uint8_t data), (override));
|
||||||
|
|
||||||
|
std::vector<uint8_t> internalFrameBuffer;
|
||||||
|
std::vector<uint8_t> vram;
|
||||||
|
std::vector<SpriteAttributes> sprites;
|
||||||
|
std::vector<Tilemap> tilemaps;
|
||||||
|
BackgroundMode bgMode;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
class PpuTest : public ::testing::Test {
|
||||||
|
protected:
|
||||||
|
MockMemory mock_memory;
|
||||||
|
MockClock mock_clock;
|
||||||
|
MockPpu mock_ppu;
|
||||||
|
|
||||||
|
PpuTest() {}
|
||||||
|
|
||||||
|
void SetUp() override {
|
||||||
|
ON_CALL(mock_ppu, Init()).WillByDefault([this]() {
|
||||||
|
mock_ppu.internalFrameBuffer.resize(256 * 240);
|
||||||
|
mock_ppu.vram.resize(0x10000);
|
||||||
|
});
|
||||||
|
|
||||||
|
ON_CALL(mock_ppu, Write(::testing::_, ::testing::_))
|
||||||
|
.WillByDefault([this](uint16_t address, uint8_t data) {
|
||||||
|
mock_ppu.vram[address] = data;
|
||||||
|
});
|
||||||
|
|
||||||
|
ON_CALL(mock_ppu, Read(::testing::_))
|
||||||
|
.WillByDefault(
|
||||||
|
[this](uint16_t address) { return mock_ppu.vram[address]; });
|
||||||
|
|
||||||
|
ON_CALL(mock_ppu, RenderScanline()).WillByDefault([this]() {
|
||||||
|
// Simulate scanline rendering logic...
|
||||||
|
});
|
||||||
|
|
||||||
|
ON_CALL(mock_ppu, GetFrameBuffer()).WillByDefault([this]() {
|
||||||
|
return mock_ppu.internalFrameBuffer;
|
||||||
|
});
|
||||||
|
|
||||||
|
// Additional ON_CALL setups as needed...
|
||||||
|
}
|
||||||
|
|
||||||
|
void TearDown() override {
|
||||||
|
// Common cleanup (if necessary)
|
||||||
|
}
|
||||||
|
|
||||||
|
const uint8_t testVRAMValue = 0xAB;
|
||||||
|
const uint16_t testVRAMAddress = 0x2000;
|
||||||
|
const std::vector<uint8_t> spriteData = {/* ... */};
|
||||||
|
const std::vector<uint8_t> bgData = {/* ... */};
|
||||||
|
const uint8_t testPaletteIndex = 3;
|
||||||
|
const uint16_t testTileIndex = 42;
|
||||||
|
};
|
||||||
|
|
||||||
|
// Test Initialization
|
||||||
|
TEST_F(PpuTest, InitializationSetsCorrectFrameBufferSize) {
|
||||||
|
// EXPECT_CALL(mock_ppu, Init()).Times(1);
|
||||||
|
// mock_ppu.Init();
|
||||||
|
// EXPECT_EQ(mock_ppu.GetFrameBuffer().size(), 256 * 240);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test State Reset
|
||||||
|
TEST_F(PpuTest, ResetClearsFrameBuffer) {
|
||||||
|
// EXPECT_CALL(mock_ppu, Reset()).Times(1);
|
||||||
|
// mock_ppu.Reset();
|
||||||
|
// auto frameBuffer = mock_ppu.GetFrameBuffer();
|
||||||
|
// EXPECT_TRUE(std::all_of(frameBuffer.begin(), frameBuffer.end(),
|
||||||
|
// [](uint8_t val) { return val == 0; }));
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Memory Interaction
|
||||||
|
TEST_F(PpuTest, ReadWriteVRAM) {
|
||||||
|
// uint16_t address = testVRAMAddress;
|
||||||
|
// uint8_t value = testVRAMValue;
|
||||||
|
// EXPECT_CALL(mock_ppu, Write(address, value)).Times(1);
|
||||||
|
// mock_ppu.Write(address, value);
|
||||||
|
// EXPECT_EQ(mock_ppu.Read(address), value);
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Rendering Mechanics
|
||||||
|
TEST_F(PpuTest, RenderScanlineUpdatesFrameBuffer) {
|
||||||
|
// Setup PPU with necessary background and sprite data
|
||||||
|
// Call RenderScanline and check if the framebuffer is updated correctly
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Mode and Register Handling
|
||||||
|
TEST_F(PpuTest, Mode0Rendering) {
|
||||||
|
// Set PPU to Mode0 and verify correct rendering behavior
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Interrupts and Counters
|
||||||
|
TEST_F(PpuTest, VBlankInterruptTriggered) {
|
||||||
|
// Simulate conditions for V-Blank and test if the interrupt is triggered
|
||||||
|
}
|
||||||
|
|
||||||
|
// Test Composite Rendering and Output
|
||||||
|
TEST_F(PpuTest, FrameComposition) {
|
||||||
|
// Setup various layers and sprites, call ComposeLayers, and verify the frame
|
||||||
|
// buffer
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace emu
|
} // namespace emu
|
||||||
} // namespace app
|
} // namespace app
|
||||||
} // namespace yaze
|
} // namespace yaze
|
||||||
@@ -1,88 +0,0 @@
|
|||||||
#include <gmock/gmock.h>
|
|
||||||
#include <gtest/gtest.h>
|
|
||||||
|
|
||||||
#include "app/zelda3/overworld.h"
|
|
||||||
#include "cli/command_handler.h"
|
|
||||||
|
|
||||||
using namespace yaze::cli;
|
|
||||||
using ::testing::_;
|
|
||||||
using ::testing::Return;
|
|
||||||
using yaze::app::zelda3::Overworld;
|
|
||||||
|
|
||||||
// Mock class for CommandHandler
|
|
||||||
class MockCommandHandler : public CommandHandler {
|
|
||||||
public:
|
|
||||||
MOCK_METHOD(absl::Status, handle, (const std::vector<std::string>& arg),
|
|
||||||
(override));
|
|
||||||
};
|
|
||||||
|
|
||||||
// Test fixture class
|
|
||||||
class CommandHandlerTest : public ::testing::Test {
|
|
||||||
protected:
|
|
||||||
std::shared_ptr<MockCommandHandler> mockHandler =
|
|
||||||
std::make_shared<MockCommandHandler>();
|
|
||||||
};
|
|
||||||
|
|
||||||
// TEST_F(CommandHandlerTest, TestApplyPatch) {
|
|
||||||
// Commands cmd;
|
|
||||||
// cmd.handlers["-a"] = mockHandler;
|
|
||||||
// EXPECT_CALL(*mockHandler, handle(_)).WillOnce(Return(absl::OkStatus()));
|
|
||||||
// absl::Status result = cmd.handlers["-a"]->handle("apply_patch_args");
|
|
||||||
// EXPECT_EQ(result, absl::OkStatus());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TEST_F(CommandHandlerTest, TestCreatePatch) {
|
|
||||||
// Commands cmd;
|
|
||||||
// cmd.handlers["-cp"] = mockHandler;
|
|
||||||
// EXPECT_CALL(*mockHandler, handle(_)).WillOnce(Return(absl::OkStatus()));
|
|
||||||
// absl::Status result = cmd.handlers["-cp"]->handle("create_patch_args");
|
|
||||||
// EXPECT_EQ(result, absl::OkStatus());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TEST_F(CommandHandlerTest, TestOpen) {
|
|
||||||
// Commands cmd;
|
|
||||||
// cmd.handlers["-o"] = mockHandler;
|
|
||||||
// EXPECT_CALL(*mockHandler, handle(_)).WillOnce(Return(absl::OkStatus()));
|
|
||||||
// absl::Status result = cmd.handlers["-o"]->handle("open_args");
|
|
||||||
// EXPECT_EQ(result, absl::OkStatus());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TEST_F(CommandHandlerTest, TestBackup) {
|
|
||||||
// Commands cmd;
|
|
||||||
// cmd.handlers["-b"] = mockHandler;
|
|
||||||
// EXPECT_CALL(*mockHandler, handle(_)).WillOnce(Return(absl::OkStatus()));
|
|
||||||
// absl::Status result = cmd.handlers["-b"]->handle("backup_args");
|
|
||||||
// EXPECT_EQ(result, absl::OkStatus());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TEST_F(CommandHandlerTest, TestCompress) {
|
|
||||||
// Commands cmd;
|
|
||||||
// cmd.handlers["-c"] = mockHandler;
|
|
||||||
// EXPECT_CALL(*mockHandler, handle(_)).WillOnce(Return(absl::OkStatus()));
|
|
||||||
// absl::Status result = cmd.handlers["-c"]->handle("compress_args");
|
|
||||||
// EXPECT_EQ(result, absl::OkStatus());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TEST_F(CommandHandlerTest, TestDecompress) {
|
|
||||||
// Commands cmd;
|
|
||||||
// cmd.handlers["-d"] = mockHandler;
|
|
||||||
// EXPECT_CALL(*mockHandler, handle(_)).WillOnce(Return(absl::OkStatus()));
|
|
||||||
// absl::Status result = cmd.handlers["-d"]->handle("decompress_args");
|
|
||||||
// EXPECT_EQ(result, absl::OkStatus());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TEST_F(CommandHandlerTest, TestSnesToPc) {
|
|
||||||
// Commands cmd;
|
|
||||||
// cmd.handlers["-s"] = mockHandler;
|
|
||||||
// EXPECT_CALL(*mockHandler, handle(_)).WillOnce(Return(absl::OkStatus()));
|
|
||||||
// absl::Status result = cmd.handlers["-s"]->handle("snes_to_pc_args");
|
|
||||||
// EXPECT_EQ(result, absl::OkStatus());
|
|
||||||
// }
|
|
||||||
|
|
||||||
// TEST_F(CommandHandlerTest, TestPcToSnes) {
|
|
||||||
// Commands cmd;
|
|
||||||
// cmd.handlers["-p"] = mockHandler;
|
|
||||||
// EXPECT_CALL(*mockHandler, handle(_)).WillOnce(Return(absl::OkStatus()));
|
|
||||||
// absl::Status result = cmd.handlers["-p"]->handle("pc_to_snes_args");
|
|
||||||
// EXPECT_EQ(result, absl::OkStatus());
|
|
||||||
// }
|
|
||||||
Reference in New Issue
Block a user