Add JML, AND AbsoluteLong, ADC AbsoluteLong

This commit is contained in:
scawful
2023-08-19 19:12:28 -04:00
parent f5c5c34b47
commit 7dcbc7f83c
4 changed files with 350 additions and 176 deletions

View File

@@ -66,6 +66,9 @@ uint8_t CPU::FetchByteDirectPage(uint8_t operand) {
} }
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 opcode = FetchByte(); // uint8_t opcode = FetchByte();
uint8_t operand = -1; uint8_t operand = -1;
switch (opcode) { switch (opcode) {
@@ -94,7 +97,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
ADC(operand); ADC(operand);
break; break;
case 0x6F: // ADC Absolute Long case 0x6F: // ADC Absolute Long
operand = memory.ReadByte(AbsoluteLong()); operand = memory.ReadWord(AbsoluteLong());
ADC(operand); ADC(operand);
break; break;
case 0x71: // ADC DP Indirect Indexed, Y case 0x71: // ADC DP Indirect Indexed, Y
@@ -153,8 +156,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
AND(Absolute()); AND(Absolute());
break; break;
case 0x2F: // AND Absolute Long case 0x2F: // AND Absolute Long
operand = memory.ReadByte(AbsoluteLong()); ANDAbsoluteLong(AbsoluteLong());
AND(operand);
break; break;
case 0x31: // AND DP Indirect Indexed, Y case 0x31: // AND DP Indirect Indexed, Y
operand = memory.ReadByte(DirectPageIndirectIndexedY()); operand = memory.ReadByte(DirectPageIndirectIndexedY());
@@ -177,16 +179,13 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
AND(operand); AND(operand);
break; break;
case 0x39: // AND Absolute Indexed, Y case 0x39: // AND Absolute Indexed, Y
operand = memory.ReadByte(AbsoluteIndexedY()); AND(AbsoluteIndexedY());
AND(operand);
break; break;
case 0x3D: // AND Absolute Indexed, X case 0x3D: // AND Absolute Indexed, X
operand = memory.ReadByte(AbsoluteIndexedX()); AND(AbsoluteIndexedX());
AND(operand);
break; break;
case 0x3F: // AND Absolute Long Indexed, X case 0x3F: // AND Absolute Long Indexed, X
operand = memory.ReadByte(AbsoluteLongIndexedX()); AND(AbsoluteLongIndexedX());
AND(operand);
break; break;
case 0x06: // ASL Direct Page case 0x06: // ASL Direct Page
@@ -451,7 +450,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
JMP(Absolute()); JMP(Absolute());
break; break;
case 0x5C: // JMP Absolute Long case 0x5C: // JMP Absolute Long
// JMP(); JML(AbsoluteLong());
break; break;
case 0x6C: // JMP Absolute Indirect case 0x6C: // JMP Absolute Indirect
JMP(AbsoluteIndirect()); JMP(AbsoluteIndirect());
@@ -574,7 +573,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) {
break; break;
case 0xEA: // NOP No Operation case 0xEA: // NOP No Operation
// NOP(); NOP();
break; break;
case 0x01: // ORA DP Indexed Indirect, X case 0x01: // ORA DP Indexed Indirect, X
@@ -1010,6 +1009,14 @@ void CPU::AND(uint16_t address) {
} }
} }
// New function for absolute long addressing mode
void CPU::ANDAbsoluteLong(uint32_t address) {
uint32_t operand32 = memory.ReadWordLong(address);
A &= operand32;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80000000);
}
} // namespace emu } // namespace emu
} // namespace app } // namespace app
} // namespace yaze } // namespace yaze

View File

@@ -105,7 +105,7 @@ class CPU : public Memory {
// Low: First operand byte // Low: First operand byte
// //
// LDA long // LDA long
uint16_t AbsoluteLong() { return FetchLong(); } uint32_t AbsoluteLong() { return FetchLong(); }
// Effective Address: // Effective Address:
// The 24-bit operand is added to X based on the emulation mode // The 24-bit operand is added to X based on the emulation mode
@@ -243,11 +243,14 @@ class CPU : public Memory {
// Registers // Registers
uint8_t A = 0; // Accumulator uint8_t A = 0; // Accumulator
uint8_t B = 0; // Accumulator (High)
uint8_t X = 0; // X index register uint8_t X = 0; // X index register
uint8_t X2 = 0; // X index register (High)
uint8_t Y = 0; // Y index register uint8_t Y = 0; // Y index register
uint8_t Y2 = 0; // Y index register (High)
uint16_t D = 0; // Direct Page register uint16_t D = 0; // Direct Page register
uint16_t DB = 0; // Data Bank register uint16_t DB = 0; // Data Bank register
uint16_t PB = 0; // Program Bank register uint8_t PB = 0; // Program Bank register
uint16_t PC = 0; // Program Counter uint16_t PC = 0; // Program Counter
uint8_t E = 1; // Emulation mode flag uint8_t E = 1; // Emulation mode flag
uint8_t status; // Processor Status (P) uint8_t status; // Processor Status (P)
@@ -288,91 +291,47 @@ class CPU : public Memory {
// ========================================================================== // ==========================================================================
// Instructions // Instructions
/// ``` Unimplemented
// Left to implement // ADC: Add with carry
// * = in progress
// ADC: Add with carry *
// AND: Logical AND *
// ASL: Arithmetic shift left
// BCC: Branch if carry clear *
// BCS: Branch if carry set *
// BEQ: Branch if equal (zero set) *
// BIT: Bit test
// BMI: Branch if minus (negative set)
// BNE: Branch if not equal (zero clear)
// BPL: Branch if plus (negative clear)
// BRA: Branch always
// BRK: Break
// BRL: Branch always long
// BVC: Branch if overflow clear
// BVS: Branch if overflow set
// CMP: Compare
// COP: Coprocessor
// CPX: Compare X register
// CPY: Compare Y register
// DEC: Decrement
// EOR: Exclusive OR
// JMP: Jump
// JML: Jump long
// JSR: Jump to subroutine
// JSL: Jump to subroutine long
// LDA: Load accumulator
// LDX: Load X register
// LDY: Load Y register
// LSR: Logical shift right
// MVN: Move negative
// MVP: Move positive
// NOP: No operation
// ORA: Logical OR
// PEA: Push effective address
// PEI: Push effective indirect address
// PER: Push effective PC-relative address
// ROL: Rotate left
// ROR: Rotate right
// RTI: Return from interrupt
// RTL: Return from subroutine long
// RTS: Return from subroutine
// SBC: Subtract with carry
// STA: Store accumulator
// STP: Stop the clock
// STX: Store X register
// STY: Store Y register
// STZ: Store zero
// TDC: Transfer direct page register to accumulator
// TRB: Test and reset bits
// TSB: Test and set bits
// WAI: Wait for interrupt
// XBA: Exchange B and A accumulator
void ADC(uint8_t operand); void ADC(uint8_t operand);
void ANDAbsoluteLong(uint32_t address);
// AND: Logical AND
void AND(uint16_t address); void AND(uint16_t address);
void BEQ(int8_t offset) { // ASL: Arithmetic shift left ```
if (GetZeroFlag()) { // If the zero flag is set
PC += offset; // Add the offset to the program counter
}
}
// BCC: Branch if carry clear
void BCC(int8_t offset) { void 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 PC += offset; // Add the offset to the program counter
} }
} }
// BCS: Branch if carry set ```
// BEQ: Branch if equal (zero set)
void BEQ(int8_t offset) {
if (GetZeroFlag()) { // If the zero flag is set
PC += offset; // Add the offset to the program counter
}
}
// BIT: Bit test ```
// BMI: Branch if minus (negative set) ```
// BNE: Branch if not equal (zero clear) ```
// BPL: Branch if plus (negative clear) ```
// BRA: Branch always ```
// BRK: Break ```
// BRL: Branch always long
void BRL(int16_t offset) { void BRL(int16_t offset) {
PC += offset; // Add the offset to the program counter PC += offset; // Add the offset to the program counter
} }
void LDA() { // BVC: Branch if overflow clear ```
A = memory[PC]; // BVS: Branch if overflow set ```
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
PC++;
}
// SEC: Set carry flag
void SEC() { status |= 0x01; }
// CLC: Clear carry flag // CLC: Clear carry flag
void CLC() { status &= ~0x01; } void CLC() { status &= ~0x01; }
@@ -386,20 +345,25 @@ class CPU : public Memory {
// CLV: Clear overflow flag // CLV: Clear overflow flag
void CLV() { status &= ~0x40; } void CLV() { status &= ~0x40; }
bool emulation_mode = false; // CMP: Compare ```
// COP: Coprocessor ```
// CPX: Compare X register
void CPX(uint16_t address) { void CPX(uint16_t address) {
uint16_t memory_value = uint16_t memory_value =
E ? memory.ReadByte(address) : memory.ReadWord(address); E ? memory.ReadByte(address) : memory.ReadWord(address);
compare(X, memory_value); compare(X, memory_value);
} }
// CPY: Compare Y register
void CPY(uint16_t address) { void CPY(uint16_t address) {
uint16_t memory_value = uint16_t memory_value =
E ? memory.ReadByte(address) : memory.ReadWord(address); E ? memory.ReadByte(address) : memory.ReadWord(address);
compare(Y, memory_value); compare(Y, memory_value);
} }
// DEC: Decrement ```
// DEX: Decrement X register // DEX: Decrement X register
void DEX() { void DEX() {
X--; X--;
@@ -414,21 +378,10 @@ class CPU : public Memory {
SetNegativeFlag(Y & 0x80); SetNegativeFlag(Y & 0x80);
} }
// INX: Increment X register // EOR: Exclusive OR ```
void INX() {
X++;
SetNegativeFlag(X & 0x80);
SetZeroFlag(X == 0);
}
// INY: Increment Y register // INC: Increment
void INY() { // TODO: Check if this is correct
Y++;
SetNegativeFlag(Y & 0x80);
SetZeroFlag(Y == 0);
}
// INC: Increment memory
void INC(uint16_t address) { void INC(uint16_t address) {
if (GetAccumulatorSize()) { if (GetAccumulatorSize()) {
uint8_t value = ReadByte(address); uint8_t value = ReadByte(address);
@@ -451,11 +404,33 @@ class CPU : public Memory {
} }
} }
// JMP: Jump to new address // INX: Increment X register
void INX() {
X++;
SetNegativeFlag(X & 0x80);
SetZeroFlag(X == 0);
}
// INY: Increment Y register
void INY() {
Y++;
SetNegativeFlag(Y & 0x80);
SetZeroFlag(Y == 0);
}
// JMP: Jump
void JMP(uint16_t address) { void JMP(uint16_t address) {
PC = address; // Set program counter to the new address PC = address; // Set program counter to the new address
} }
// JML: Jump long
void JML(uint32_t address) {
// Set the lower 16 bits of PC to the lower 16 bits of the address
PC = static_cast<uint8_t>(address & 0xFFFF);
// Set the PBR to the upper 8 bits of the address
PB = static_cast<uint8_t>((address >> 16) & 0xFF);
}
// JSR: Jump to subroutine // JSR: Jump to subroutine
void JSR(uint16_t address) { void JSR(uint16_t address) {
PC -= 1; // Subtract 1 from program counter PC -= 1; // Subtract 1 from program counter
@@ -471,51 +446,65 @@ class CPU : public Memory {
PC = address; // Set program counter to the new address PC = address; // Set program counter to the new address
} }
// Push Accumulator on Stack // LDA: Load accumulator
void LDA() {
A = memory[PC];
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
PC++;
}
// LDX: Load X register ```
// LDY: Load Y register ```
// LSR: Logical shift right ```
// MVN: Move negative ```
// MVP: Move positive ```
// NOP: No operation
void NOP() {
// Do nothing
}
// ORA: Logical OR ```
// PEA: Push effective address ```
// PEI: Push effective indirect address ```
// PER: Push effective PC-relative address ```
// PHA: Push Accumulator on Stack
void PHA() { memory.PushByte(A); } void PHA() { memory.PushByte(A); }
// Pull Accumulator from Stack // PHB: Push Data Bank Register on Stack
void PHB() { memory.PushByte(DB); }
// PHD: Push Program Bank Register on Stack
void PHD() { memory.PushWord(D); }
// PHK: Push Program Bank Register on Stack
void PHK() { memory.PushByte(PB); }
// PHP: Push Processor Status Register on Stack
void PHP() { memory.PushByte(status); }
// PHX: Push X Index Register on Stack
void PHX() { memory.PushByte(X); }
// PHY: Push Y Index Register on Stack
void PHY() { memory.PushByte(Y); }
// PLA: Pull Accumulator from Stack
void PLA() { void PLA() {
A = memory.PopByte(); A = memory.PopByte();
SetNegativeFlag((A & 0x80) != 0); SetNegativeFlag((A & 0x80) != 0);
SetZeroFlag(A == 0); SetZeroFlag(A == 0);
} }
// Push Processor Status Register on Stack // PLB: Pull data bank register
void PHP() { memory.PushByte(status); }
// Pull Processor Status Register from Stack
void PLP() { status = memory.PopByte(); }
void PHX() { memory.PushByte(X); }
void PLX() {
X = memory.PopByte();
SetNegativeFlag((A & 0x80) != 0);
SetZeroFlag(X == 0);
}
void PHY() { memory.PushByte(Y); }
void PLY() {
Y = memory.PopByte();
SetNegativeFlag((A & 0x80) != 0);
SetZeroFlag(Y == 0);
}
// Push Data Bank Register on Stack
void PHB() { memory.PushByte(DB); }
// Pull Data Bank Register from Stack
void PLB() { void PLB() {
DB = memory.PopByte(); DB = memory.PopByte();
SetNegativeFlag((DB & 0x80) != 0); SetNegativeFlag((DB & 0x80) != 0);
SetZeroFlag(DB == 0); SetZeroFlag(DB == 0);
} }
// Push Program Bank Register on Stack
void PHD() { memory.PushWord(D); }
// Pull Direct Page Register from Stack // Pull Direct Page Register from Stack
void PLD() { void PLD() {
D = memory.PopWord(); D = memory.PopWord();
@@ -523,89 +512,141 @@ class CPU : public Memory {
SetZeroFlag(D == 0); SetZeroFlag(D == 0);
} }
// Push Program Bank Register on Stack // Pull Processor Status Register from Stack
void PHK() { memory.PushByte(PB); } void PLP() { status = memory.PopByte(); }
void SEI() { status |= 0x04; } // PLX: Pull X Index Register from Stack
void PLX() {
void SED() { status |= 0x08; } X = memory.PopByte();
SetNegativeFlag((A & 0x80) != 0);
void SEP() { SetZeroFlag(X == 0);
PC++;
auto byte = FetchByte();
status |= byte;
} }
// PHY: Pull Y Index Register from Stack
void PLY() {
Y = memory.PopByte();
SetNegativeFlag((A & 0x80) != 0);
SetZeroFlag(Y == 0);
}
// REP: Reset status bits
void REP() { void REP() {
PC++; PC++;
auto byte = FetchByte(); auto byte = FetchByte();
status &= ~byte; status &= ~byte;
} }
void TCD() { // ROL: Rotate left ```
D = A; // ROR: Rotate right ```
SetZeroFlag(D == 0); // RTI: Return from interrupt ```
SetNegativeFlag(D & 0x80); // RTL: Return from subroutine long ```
// RTS: Return from subroutine ```
// SBC: Subtract with carry ```
// SEC: Set carry flag
void SEC() { status |= 0x01; }
// SED: Set decimal mode
void SED() { status |= 0x08; }
// SEI: Set interrupt disable flag
void SEI() { status |= 0x04; }
// SEP: Set status bits
void SEP() {
PC++;
auto byte = FetchByte();
status |= byte;
} }
void TDC() { // STA: Store accumulator ```
A = D; // STP: Stop the clock ```
SetZeroFlag(A == 0); // STX: Store X register ```
SetNegativeFlag(A & 0x80); // STY: Store Y register ```
} // STZ: Store zero ```
void TCS() { memory.SetSP(A); }
// TAX: Transfer accumulator to X
void TAX() { void TAX() {
X = A; X = A;
SetZeroFlag(X == 0); SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x80); SetNegativeFlag(X & 0x80);
} }
// TAY: Transfer accumulator to Y
void TAY() { void TAY() {
Y = A; Y = A;
SetZeroFlag(Y == 0); SetZeroFlag(Y == 0);
SetNegativeFlag(Y & 0x80); SetNegativeFlag(Y & 0x80);
} }
void TYA() { // TCD: Transfer accumulator to direct page register
A = Y; void TCD() {
D = A;
SetZeroFlag(D == 0);
SetNegativeFlag(D & 0x80);
}
// TCS: Transfer accumulator to stack pointer
void TCS() { memory.SetSP(A); }
// TDC: Transfer direct page register to accumulator
void TDC() {
A = D;
SetZeroFlag(A == 0); SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80); SetNegativeFlag(A & 0x80);
} }
void TXA() { // TRB: Test and reset bits ```
A = X; // TSB: Test and set bits ```
// TSC: Transfer stack pointer to accumulator
void TSC() {
A = SP();
SetZeroFlag(A == 0); SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80); SetNegativeFlag(A & 0x80);
} }
void TXY() { // TSX: Transfer stack pointer to X
X = Y;
SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x80);
}
void TYX() {
Y = X;
SetZeroFlag(Y == 0);
SetNegativeFlag(Y & 0x80);
}
void TSX() { void TSX() {
X = SP(); X = SP();
SetZeroFlag(X == 0); SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x80); SetNegativeFlag(X & 0x80);
} }
void TXS() { memory.SetSP(X); } // TXA: Transfer X to accumulator
void TXA() {
void TSC() { A = X;
A = SP();
SetZeroFlag(A == 0); SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80); SetNegativeFlag(A & 0x80);
} }
// TXS: Transfer X to stack pointer
void TXS() { memory.SetSP(X); }
// TXY: Transfer X to Y
void TXY() {
X = Y;
SetZeroFlag(X == 0);
SetNegativeFlag(X & 0x80);
}
// TYA: Transfer Y to accumulator
void TYA() {
A = Y;
SetZeroFlag(A == 0);
SetNegativeFlag(A & 0x80);
}
// TYX: Transfer Y to X
void TYX() {
Y = X;
SetZeroFlag(Y == 0);
SetNegativeFlag(Y & 0x80);
}
// WAI: Wait for interrupt ```
// XBA: Exchange B and A accumulator ```
// XCE: Exchange Carry and Emulation Flags // XCE: Exchange Carry and Emulation Flags
void XCE() { void XCE() {
uint8_t carry = status & 0x01; uint8_t carry = status & 0x01;

View File

@@ -58,8 +58,8 @@ class MemoryImpl : public Memory {
if (address < dp_memory_.size()) { if (address < dp_memory_.size()) {
return dp_memory_.ReadByte(static_cast<uint8_t>(address)); return dp_memory_.ReadByte(static_cast<uint8_t>(address));
} }
// uint32_t mapped_address = GetMappedAddress(address); uint32_t mapped_address = GetMappedAddress(address);
return memory_.at(address); return memory_.at(mapped_address);
} }
uint16_t ReadWord(uint16_t address) const override { uint16_t ReadWord(uint16_t address) const override {
if (address < dp_memory_.size()) { if (address < dp_memory_.size()) {
@@ -77,8 +77,8 @@ class MemoryImpl : public Memory {
} }
void WriteByte(uint32_t address, uint8_t value) override { void WriteByte(uint32_t address, uint8_t value) override {
// uint32_t mapped_address = GetMappedAddress(address); uint32_t mapped_address = GetMappedAddress(address);
memory_[address] = value; memory_[mapped_address] = value;
} }
void WriteWord(uint32_t address, uint16_t value) override { void WriteWord(uint32_t address, uint16_t value) override {
uint32_t mapped_address = GetMappedAddress(address); uint32_t mapped_address = GetMappedAddress(address);

View File

@@ -41,6 +41,14 @@ class MockMemory : public Memory {
std::copy(data.begin(), data.end(), memory_.begin()); std::copy(data.begin(), data.end(), memory_.begin());
} }
void InsertMemory(const uint64_t address, const std::vector<uint8_t>& data) {
int i = 0;
for (const auto& each : data) {
memory_[address + i] = each;
i++;
}
}
void Init() { void Init() {
ON_CALL(*this, ReadByte(::testing::_)) ON_CALL(*this, ReadByte(::testing::_))
.WillByDefault( .WillByDefault(
@@ -111,6 +119,7 @@ class CPUTest : public ::testing::Test {
public: public:
void SetUp() override { void SetUp() override {
mock_memory.Init(); mock_memory.Init();
EXPECT_CALL(mock_memory, ClearMemory()).Times(::testing::AtLeast(1));
mock_memory.ClearMemory(); mock_memory.ClearMemory();
} }
@@ -189,7 +198,15 @@ TEST_F(CPUTest, ADC_AbsoluteLong) {
cpu.A = 0x01; cpu.A = 0x01;
cpu.PC = 1; // PC register cpu.PC = 1; // PC register
cpu.status = 0x00; // 16-bit mode cpu.status = 0x00; // 16-bit mode
std::vector<uint8_t> data = {0x2F, 0x03, 0x00, 0x00, 0x05, 0x00}; std::vector<uint8_t> data = {0x6F, 0x04, 0x00, 0x00, 0x05, 0x00};
mock_memory.SetMemoryContents(data);
EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x0004));
EXPECT_CALL(mock_memory, ReadWord(0x0004)).WillOnce(Return(0x0005));
cpu.ExecuteInstruction(0x6F); // ADC Absolute Long
EXPECT_EQ(cpu.A, 0x06);
} }
/** /**
@@ -295,6 +312,21 @@ TEST_F(CPUTest, AND_Absolute_16BitMode) {
EXPECT_EQ(cpu.A, 0b10101010); // A register should now be 0b10101010 EXPECT_EQ(cpu.A, 0b10101010); // A register should now be 0b10101010
} }
TEST_F(CPUTest, AND_AbsoluteLong) {
cpu.A = 0x01;
cpu.PC = 1; // PC register
cpu.status = 0x00; // 16-bit mode
std::vector<uint8_t> data = {0x2F, 0x04, 0x00, 0x00, 0x05, 0x00};
mock_memory.SetMemoryContents(data);
EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x0004));
EXPECT_CALL(mock_memory, ReadWordLong(0x0004)).WillOnce(Return(0x0005));
cpu.ExecuteInstruction(0x2F); // ADC Absolute Long
EXPECT_EQ(cpu.A, 0x01);
}
TEST_F(CPUTest, AND_IndexedIndirect) { TEST_F(CPUTest, AND_IndexedIndirect) {
cpu.A = 0b10101010; // A register cpu.A = 0b10101010; // A register
cpu.X = 0x02; // X register cpu.X = 0x02; // X register
@@ -305,6 +337,78 @@ TEST_F(CPUTest, AND_IndexedIndirect) {
EXPECT_EQ(cpu.A, 0b00000000); // A register should now be 0b00000000 EXPECT_EQ(cpu.A, 0b00000000); // A register should now be 0b00000000
} }
TEST_F(CPUTest, AND_AbsoluteIndexedX) {
cpu.A = 0b11110000; // A 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,
0b00000000, 0b10101010, 0b01010101};
mock_memory.SetMemoryContents(data);
// Get the absolute address
EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x0003));
// Add the offset from the X register to the absolute address
uint16_t address = 0x0003 + static_cast<uint16_t>(cpu.X & 0xFF);
// Get the value at the absolute address + X
EXPECT_CALL(mock_memory, ReadByte(address)).WillOnce(Return(0b10101010));
cpu.ExecuteInstruction(0x3D); // AND Absolute, X
EXPECT_THAT(cpu.PC, testing::Eq(0x03));
EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000
}
TEST_F(CPUTest, AND_AbsoluteIndexedY) {
cpu.A = 0b11110000; // A 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,
0b00000000, 0b10101010, 0b01010101};
mock_memory.SetMemoryContents(data);
// Get the absolute address
EXPECT_CALL(mock_memory, ReadWord(0x0001)).WillOnce(Return(0x0003));
// Add the offset from the Y register to the absolute address
uint16_t address = 0x0003 + cpu.Y;
// Get the value at the absolute address + Y
EXPECT_CALL(mock_memory, ReadByte(address)).WillOnce(Return(0b10101010));
cpu.ExecuteInstruction(0x39); // AND Absolute, Y
EXPECT_THAT(cpu.PC, testing::Eq(0x03));
EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000
}
TEST_F(CPUTest, AND_AbsoluteLongIndexedX) {
cpu.A = 0b11110000; // A register
cpu.X = 0x02; // X register
cpu.status = 0xFF; // 8-bit mode
cpu.PC = 1; // PC register
std::vector<uint8_t> data = {0x3F, 0x03, 0x00, 0x00,
0b00000000, 0b10101010, 0b01010101};
mock_memory.SetMemoryContents(data);
// Get the absolute address
EXPECT_CALL(mock_memory, ReadWordLong(0x0001)).WillOnce(Return(0x0003));
// Add the offset from the X register to the absolute address
uint16_t address = 0x0003 + static_cast<uint16_t>(cpu.X & 0xFF);
// Get the value at the absolute address + X
EXPECT_CALL(mock_memory, ReadByte(address)).WillOnce(Return(0b10101010));
cpu.ExecuteInstruction(0x3F); // AND Absolute Long, X
EXPECT_THAT(cpu.PC, testing::Eq(0x04));
EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000
}
// ============================================================================ // ============================================================================
// BCC - Branch if Carry Clear // BCC - Branch if Carry Clear
@@ -510,6 +614,28 @@ TEST_F(CPUTest, JMP_Indirect) {
EXPECT_EQ(cpu.PC, 0x3005); EXPECT_EQ(cpu.PC, 0x3005);
} }
// ============================================================================
// JML - Jump Long
// ============================================================================
TEST_F(CPUTest, JML_AbsoluteLong) {
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
mock_memory.SetMemoryContents(data);
mock_memory.InsertMemory(0x030005, {0x00, 0x20, 0x00});
// NOP to set PB to 0x02
cpu.ExecuteInstruction(0xEA);
EXPECT_CALL(mock_memory, ReadWordLong(0x1001)).WillOnce(Return(0x030005));
cpu.ExecuteInstruction(0x5C); // JML Absolute Long
EXPECT_EQ(cpu.PC, 0x0005);
EXPECT_EQ(cpu.PB, 0x03); // The PBR should be updated to 0x03
}
// ============================================================================ // ============================================================================
// JSR - Jump to Subroutine // JSR - Jump to Subroutine
// ============================================================================ // ============================================================================