diff --git a/src/app/emu/cpu.cc b/src/app/emu/cpu.cc index bcb9e932..89feb295 100644 --- a/src/app/emu/cpu.cc +++ b/src/app/emu/cpu.cc @@ -448,13 +448,13 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; case 0x4C: // JMP Absolute - // JMP(); + JMP(Absolute()); break; case 0x5C: // JMP Absolute Long // JMP(); break; case 0x6C: // JMP Absolute Indirect - // JMP(); + JMP(AbsoluteIndirect()); break; case 0x7C: // JMP Absolute Indexed Indirect, X // JMP(); @@ -464,11 +464,11 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; case 0x20: // JSR Absolute - // JSR(); + JSR(Absolute()); break; case 0x22: // JSL Absolute Long - // JSL(); + JSL(AbsoluteLong()); break; case 0xFC: // JSR Absolute Indexed Indirect, X diff --git a/src/app/emu/cpu.h b/src/app/emu/cpu.h index 61212ee3..a8d3c62e 100644 --- a/src/app/emu/cpu.h +++ b/src/app/emu/cpu.h @@ -344,7 +344,6 @@ class CPU : public Memory { // TSB: Test and set bits // WAI: Wait for interrupt // XBA: Exchange B and A accumulator - // XCE: Exchange carry and emulation void ADC(uint8_t operand); void AND(uint16_t address); @@ -452,6 +451,26 @@ class CPU : public Memory { } } + // JMP: Jump to new address + void JMP(uint16_t address) { + PC = address; // Set program counter to the new address + } + + // JSR: Jump to subroutine + void JSR(uint16_t address) { + PC -= 1; // Subtract 1 from program counter + memory.PushWord(PC); // Push the program counter onto the stack + PC = address; // Set program counter to the new address + } + + // JSL: Jump to subroutine long + void JSL(uint32_t address) { + PC -= 1; // Subtract 1 from program counter + memory.PushLong(PC); // Push the program counter onto the stack as a long + // value (24 bits) + PC = address; // Set program counter to the new address + } + // Push Accumulator on Stack void PHA() { memory.PushByte(A); } @@ -620,6 +639,8 @@ class CPU : public Memory { void PushWord(uint16_t value) override { memory.PushWord(value); } uint8_t PopByte() override { return memory.PopByte(); } uint16_t PopWord() override { return memory.PopWord(); } + void PushLong(uint32_t value) override { memory.PushLong(value); } + uint32_t PopLong() override { return memory.PopLong(); } void ClearMemory() override { memory.ClearMemory(); } void LoadData(const std::vector& data) override { memory.LoadData(data); diff --git a/src/app/emu/mem.h b/src/app/emu/mem.h index d4cb5ee6..6c724cd1 100644 --- a/src/app/emu/mem.h +++ b/src/app/emu/mem.h @@ -38,6 +38,9 @@ class Memory { virtual uint8_t PopByte() = 0; virtual void PushWord(uint16_t value) = 0; virtual uint16_t PopWord() = 0; + virtual void PushLong(uint32_t value) = 0; + virtual uint32_t PopLong() = 0; + virtual int16_t SP() const = 0; virtual void SetSP(int16_t value) = 0; @@ -74,7 +77,7 @@ class MemoryImpl : public Memory { } void WriteByte(uint32_t address, uint8_t value) override { - // uint32_t mapped_address = GetMappedAddress(address); + // uint32_t mapped_address = GetMappedAddress(address); memory_[address] = value; } void WriteWord(uint32_t address, uint16_t value) override { @@ -114,6 +117,20 @@ class MemoryImpl : public Memory { return (static_cast(high) << 8) | low; } + void PushLong(uint32_t value) override { + PushByte(value >> 16); + PushByte(value >> 8); + PushByte(value & 0xFF); + } + + uint32_t PopLong() override { + uint8_t low = PopByte(); + uint8_t mid = PopByte(); + uint8_t high = PopByte(); + return (static_cast(high) << 16) | + (static_cast(mid) << 8) | low; + } + int16_t SP() const override { return SP_; } void SetSP(int16_t value) override { SP_ = value; } diff --git a/test/cpu_test.cc b/test/cpu_test.cc index beca912d..5bf66b40 100644 --- a/test/cpu_test.cc +++ b/test/cpu_test.cc @@ -22,6 +22,9 @@ class MockMemory : public Memory { MOCK_METHOD0(PopByte, uint8_t()); MOCK_METHOD1(PushWord, void(uint16_t value)); MOCK_METHOD0(PopWord, uint16_t()); + MOCK_METHOD1(PushLong, void(uint32_t value)); + MOCK_METHOD0(PopLong, uint32_t()); + MOCK_CONST_METHOD0(SP, int16_t()); MOCK_METHOD1(SetSP, void(int16_t value)); @@ -81,6 +84,19 @@ class MockMemory : public Memory { this->SetSP(SP_ + 2); return value; }); + ON_CALL(*this, PushLong(::testing::_)) + .WillByDefault([this](uint32_t value) { + memory_.at(SP_) = value & 0xFF; + memory_.at(SP_ + 1) = (value >> 8) & 0xFF; + memory_.at(SP_ + 2) = (value >> 16) & 0xFF; + }); + ON_CALL(*this, PopLong()).WillByDefault([this]() { + uint32_t value = static_cast(memory_.at(SP_)) | + (static_cast(memory_.at(SP_ + 1)) << 8) | + (static_cast(memory_.at(SP_ + 2)) << 16); + this->SetSP(SP_ + 3); + return value; + }); ON_CALL(*this, ClearMemory()).WillByDefault([this]() { memory_.resize(64000, 0x00); }); @@ -467,6 +483,65 @@ TEST_F(CPUTest, INY) { EXPECT_TRUE(cpu.GetZeroFlag()); } +// ============================================================================ +// JMP - Jump to new location +// ============================================================================ + +TEST_F(CPUTest, JMP_Absolute) { + cpu.PC = 0x1001; + std::vector data = {0x4C, 0x05, 0x20}; // JMP $2005 + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0x1001)).WillOnce(Return(0x2005)); + + cpu.ExecuteInstruction(0x4C); // JMP Absolute + EXPECT_EQ(cpu.PC, 0x2005); +} + +TEST_F(CPUTest, JMP_Indirect) { + cpu.PC = 0x1001; + std::vector data = {0x6C, 0x03, 0x20, 0x05, 0x30}; // JMP ($2003) + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0x1001)).WillOnce(Return(0x2003)); + EXPECT_CALL(mock_memory, ReadWord(0x2003)).WillOnce(Return(0x3005)); + + cpu.ExecuteInstruction(0x6C); // JMP Indirect + EXPECT_EQ(cpu.PC, 0x3005); +} + +// ============================================================================ +// JSR - Jump to Subroutine +// ============================================================================ + +TEST_F(CPUTest, JSR_Absolute) { + cpu.PC = 0x1001; + std::vector data = {0x20, 0x05, 0x20}; // JSR $2005 + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWord(0x1001)).WillOnce(Return(0x2005)); + EXPECT_CALL(mock_memory, PushWord(0x1002)).Times(1); + + cpu.ExecuteInstruction(0x20); // JSR Absolute + EXPECT_EQ(cpu.PC, 0x2005); +} + +// ============================================================================ +// JSL - Jump to Subroutine Long +// ============================================================================ + +TEST_F(CPUTest, JSL_AbsoluteLong) { + cpu.PC = 0x1001; + std::vector data = {0x22, 0x05, 0x20, 0x00}; // JSL $002005 + mock_memory.SetMemoryContents(data); + + EXPECT_CALL(mock_memory, ReadWordLong(0x1001)).WillOnce(Return(0x002005)); + EXPECT_CALL(mock_memory, PushLong(0x1003)).Times(1); + + cpu.ExecuteInstruction(0x22); // JSL Absolute Long + EXPECT_EQ(cpu.PC, 0x002005); +} + // ============================================================================ // Stack Tests