diff --git a/src/app/emu/cpu.cc b/src/app/emu/cpu.cc index fb669d12..b9059677 100644 --- a/src/app/emu/cpu.cc +++ b/src/app/emu/cpu.cc @@ -636,55 +636,55 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; case 0x48: // PHA Push Accumulator - // PHA(); + PHA(); break; case 0x8B: // PHB Push Data Bank Register - // PHB(); + PHB(); break; case 0x0B: // PHD Push Direct Page Register - // PHD(); + PHD(); break; case 0x4B: // PHK Push Program Bank Register - // PHK(); + PHK(); break; case 0x08: // PHP Push Processor Status Register - // PHP(); + PHP(); break; case 0xDA: // PHX Push X register - // PHX(); + PHX(); break; case 0x5A: // PHY Push Y register - // PHY(); + PHY(); break; case 0x68: // PLA Pull Accumulator - // PLA(); + PLA(); break; case 0xAB: // PLB Pull Data Bank Register - // PLB(); + PLB(); break; case 0x2B: // PLD Pull Direct Page Register - // PLD(); + PLD(); break; case 0x28: // PLP Pull Processor Status Register - // PLP(); + PLP(); break; case 0xFA: // PLX Pull X register - // PLX(); + PLX(); break; case 0x7A: // PLY Pull Y register - // PLY(); + PLY(); break; case 0xC2: // REP Reset status bits diff --git a/src/app/emu/cpu.h b/src/app/emu/cpu.h index 5d8263b4..6d047c16 100644 --- a/src/app/emu/cpu.h +++ b/src/app/emu/cpu.h @@ -400,18 +400,61 @@ class CPU : public Memory { void CLV() { status &= ~0x40; } + // Push Accumulator on Stack void PHA() { memory.PushByte(A); } + // Pull Accumulator from Stack void PLA() { A = memory.PopByte(); SetNegativeFlag((A & 0x80) != 0); SetZeroFlag(A == 0); } + // Push Processor Status Register on Stack 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() { + DB = memory.PopByte(); + SetNegativeFlag((DB & 0x80) != 0); + SetZeroFlag(DB == 0); + } + + // Push Program Bank Register on Stack + void PHD() { memory.PushWord(D); } + + // Pull Direct Page Register from Stack + void PLD() { + D = memory.PopWord(); + SetNegativeFlag((D & 0x8000) != 0); + SetZeroFlag(D == 0); + } + + // Push Program Bank Register on Stack + void PHK() { memory.PushByte(PB); } + void SEI() { status |= 0x04; } void SED() { status |= 0x08; } diff --git a/src/app/gfx/scad_format.cc b/src/app/gfx/scad_format.cc index c902e24c..a62ca21e 100644 --- a/src/app/gfx/scad_format.cc +++ b/src/app/gfx/scad_format.cc @@ -208,7 +208,7 @@ absl::Status DecodeObjFile( expected_cut = 0x900; } - std::ifstream file(filename, std::ios::binary); + std::ifstream file(filename.data(), std::ios::binary); if (!file.is_open()) { return absl::NotFoundError("OBJ file not found."); } diff --git a/test/cpu_test.cc b/test/cpu_test.cc index 6fda2493..0800fcc0 100644 --- a/test/cpu_test.cc +++ b/test/cpu_test.cc @@ -76,6 +76,12 @@ class MockMemory : public Memory { uint16_t SP_ = 0x01FF; }; +class CPUTest : public ::testing::Test { + public: + MockMemory mock_memory; + CPU cpu{mock_memory}; +}; + using ::testing::_; using ::testing::Return; @@ -83,7 +89,7 @@ using ::testing::Return; // Infrastructure // ============================================================================ -TEST(CPUTest, CheckMemoryContents) { +TEST_F(CPUTest, CheckMemoryContents) { MockMemory memory; std::vector data = {0x00, 0x01, 0x02, 0x03, 0x04}; memory.SetMemoryContents(data); @@ -106,9 +112,7 @@ TEST(CPUTest, CheckMemoryContents) { // ============================================================================ // ADC - Add with Carry -TEST(CPUTest, ADC_Immediate_TwoPositiveNumbers) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, ADC_Immediate_TwoPositiveNumbers) { cpu.A = 0x01; std::vector data = {0x69, 0x01}; mock_memory.SetMemoryContents(data); @@ -119,9 +123,7 @@ TEST(CPUTest, ADC_Immediate_TwoPositiveNumbers) { EXPECT_EQ(cpu.A, 0x02); } -TEST(CPUTest, ADC_Immediate_PositiveAndNegativeNumbers) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, ADC_Immediate_PositiveAndNegativeNumbers) { cpu.A = 10; std::vector data = {0x69, static_cast(-20)}; mock_memory.SetMemoryContents(data); @@ -132,9 +134,7 @@ TEST(CPUTest, ADC_Immediate_PositiveAndNegativeNumbers) { EXPECT_EQ(cpu.A, static_cast(-10)); } -TEST(CPUTest, ADC_Absolute) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, ADC_Absolute) { cpu.A = 0x01; cpu.PC = 1; // PC register cpu.status = 0x00; // 16-bit mode @@ -149,9 +149,7 @@ TEST(CPUTest, ADC_Absolute) { EXPECT_EQ(cpu.A, 0x06); } -TEST(CPUTest, ADC_AbsoluteLong) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, ADC_AbsoluteLong) { cpu.A = 0x01; cpu.PC = 1; // PC register cpu.status = 0x00; // 16-bit mode @@ -161,9 +159,9 @@ TEST(CPUTest, ADC_AbsoluteLong) { /** * Direct Page Unimplemented * -TEST(CPUTest, ADC_DirectPage) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, ADC_DirectPage) { + + cpu.A = 0x01; cpu.D = 0x0001; std::vector data = {0x65, 0x01, 0x05}; @@ -175,9 +173,9 @@ TEST(CPUTest, ADC_DirectPage) { EXPECT_EQ(cpu.A, 0x06); } -TEST(CPUTest, ADC_DirectPageIndirect) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, ADC_DirectPageIndirect) { + + cpu.A = 0x01; // A register cpu.X = 0x02; // X register cpu.PC = 0; // PC register @@ -192,9 +190,9 @@ TEST(CPUTest, ADC_DirectPageIndirect) { EXPECT_EQ(cpu.A, 0x06); } -TEST(CPUTest, ADC_DirectPageIndexedIndirectX) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, ADC_DirectPageIndexedIndirectX) { + + cpu.A = 0x01; cpu.X = 0x02; cpu.PC = 1; @@ -211,9 +209,7 @@ TEST(CPUTest, ADC_DirectPageIndexedIndirectX) { } **/ -TEST(CPUTest, ADC_CheckCarryFlag) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, ADC_CheckCarryFlag) { cpu.A = 0xFF; cpu.status = 0; std::vector data = {0x15, 0x01}; // Operand at address 0x15 @@ -230,9 +226,7 @@ TEST(CPUTest, ADC_CheckCarryFlag) { // ============================================================================ // AND - Logical AND -TEST(CPUTest, AND_Immediate) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, AND_Immediate) { cpu.PC = 0; cpu.status = 0xFF; // 8-bit mode cpu.A = 0b11110000; // A register @@ -245,9 +239,7 @@ TEST(CPUTest, AND_Immediate) { EXPECT_EQ(cpu.A, 0b10100000); // A register should now be 0b10100000 } -TEST(CPUTest, AND_Absolute) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, AND_Absolute) { cpu.A = 0b11111111; // A register cpu.status = 0x00; // 16-bit mode cpu.PC = 1; // PC register @@ -266,9 +258,7 @@ TEST(CPUTest, AND_Absolute) { EXPECT_EQ(cpu.A, 0b10101010); // A register should now be 0b10101010 } -TEST(CPUTest, AND_IndexedIndirect) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, AND_IndexedIndirect) { cpu.A = 0b10101010; // A register cpu.X = 0x02; // X register std::vector data = {0x21, 0x10, 0x18, 0x20, 0b01010101}; @@ -281,9 +271,7 @@ TEST(CPUTest, AND_IndexedIndirect) { // ============================================================================ // BCC - Branch if Carry Clear -TEST(CPUTest, BCC_WhenCarryFlagClear) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, BCC_WhenCarryFlagClear) { cpu.SetCarryFlag(false); cpu.PC = 0x1000; std::vector data(0x1001, 2); // Operand at address 0x1001 @@ -295,9 +283,7 @@ TEST(CPUTest, BCC_WhenCarryFlagClear) { EXPECT_EQ(cpu.PC, 0x1002); } -TEST(CPUTest, BCC_WhenCarryFlagSet) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, BCC_WhenCarryFlagSet) { cpu.SetCarryFlag(true); cpu.PC = 0x1000; std::vector data(0x1001, 2); // Operand at address 0x1001 @@ -313,9 +299,7 @@ TEST(CPUTest, BCC_WhenCarryFlagSet) { // ============================================================================ // BRL - Branch Long -TEST(CPUTest, BRL) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, BRL) { cpu.PC = 0x1000; std::vector data(0x1001, 2); // Operand at address 0x1001 mock_memory.SetMemoryContents(data); @@ -329,51 +313,130 @@ TEST(CPUTest, BRL) { // ============================================================================ // Stack Tests -class CPUTestKit : public ::testing::Test { - protected: - MockMemory memory_; - CPU cpu_{memory_}; -}; - -TEST_F(CPUTestKit, PHA_PLA_Test) { - cpu_.A = 0x42; - EXPECT_CALL(memory_, PushByte(0x42)).WillOnce(Return()); - cpu_.PHA(); - cpu_.A = 0x00; - EXPECT_CALL(memory_, PopByte()).WillOnce(Return(0x42)); - cpu_.PLA(); - EXPECT_EQ(cpu_.A, 0x42); +TEST_F(CPUTest, PHA_PLA_Ok) { + cpu.A = 0x42; + EXPECT_CALL(mock_memory, PushByte(0x42)).WillOnce(Return()); + cpu.PHA(); + cpu.A = 0x00; + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x42)); + cpu.PLA(); + EXPECT_EQ(cpu.A, 0x42); } -TEST_F(CPUTestKit, PHP_PLP_Test) { +TEST_F(CPUTest, PHP_PLP_Ok) { // Set some status flags - cpu_.SetNegativeFlag(true); - cpu_.SetZeroFlag(false); - EXPECT_TRUE(cpu_.GetNegativeFlag()); - EXPECT_FALSE(cpu_.GetZeroFlag()); + cpu.status = 0; + cpu.SetNegativeFlag(true); + cpu.SetZeroFlag(false); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); - EXPECT_CALL(memory_, PushByte(0x80)).WillOnce(Return()); - cpu_.PHP(); + EXPECT_CALL(mock_memory, PushByte(0x80)).WillOnce(Return()); + cpu.PHP(); // Clear status flags - cpu_.SetNegativeFlag(false); - cpu_.SetZeroFlag(true); - EXPECT_FALSE(cpu_.GetNegativeFlag()); - EXPECT_TRUE(cpu_.GetZeroFlag()); + cpu.SetNegativeFlag(false); + cpu.SetZeroFlag(true); + EXPECT_FALSE(cpu.GetNegativeFlag()); + EXPECT_TRUE(cpu.GetZeroFlag()); - EXPECT_CALL(memory_, PopByte()).WillOnce(Return(0x80)); - cpu_.PLP(); + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x80)); + cpu.PLP(); - EXPECT_TRUE(cpu_.GetNegativeFlag()); - EXPECT_FALSE(cpu_.GetZeroFlag()); + EXPECT_TRUE(cpu.GetNegativeFlag()); + EXPECT_FALSE(cpu.GetZeroFlag()); +} + +// ============================================================================ +// PHA, PHP, PHX, PHY, PHB, PHD, PHK +// ============================================================================ + +TEST_F(CPUTest, PHA_PushAccumulator) { + cpu.A = 0x12; + EXPECT_CALL(mock_memory, PushByte(0x12)); + cpu.ExecuteInstruction(0x48); // PHA +} + +TEST_F(CPUTest, PHP_PushProcessorStatusRegister) { + cpu.status = 0x34; + EXPECT_CALL(mock_memory, PushByte(0x34)); + cpu.ExecuteInstruction(0x08); // PHP +} + +TEST_F(CPUTest, PHX_PushXRegister) { + cpu.X = 0x56; + EXPECT_CALL(mock_memory, PushByte(0x56)); + cpu.ExecuteInstruction(0xDA); // PHX +} + +TEST_F(CPUTest, PHY_PushYRegister) { + cpu.Y = 0x78; + EXPECT_CALL(mock_memory, PushByte(0x78)); + cpu.ExecuteInstruction(0x5A); // PHY +} + +TEST_F(CPUTest, PHB_PushDataBankRegister) { + cpu.DB = 0x9A; + EXPECT_CALL(mock_memory, PushByte(0x9A)); + cpu.ExecuteInstruction(0x8B); // PHB +} + +TEST_F(CPUTest, PHD_PushDirectPageRegister) { + cpu.D = 0xBC; + EXPECT_CALL(mock_memory, PushWord(0xBC)); + cpu.ExecuteInstruction(0x0B); // PHD +} + +TEST_F(CPUTest, PHK_PushProgramBankRegister) { + cpu.PB = 0xDE; + EXPECT_CALL(mock_memory, PushByte(0xDE)); + cpu.ExecuteInstruction(0x4B); // PHK +} + +// ============================================================================ +// PLA, PLP, PLX, PLY, PLB, PLD +// ============================================================================ + +TEST_F(CPUTest, PLA_PullAccumulator) { + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x12)); + cpu.ExecuteInstruction(0x68); // PLA + EXPECT_EQ(cpu.A, 0x12); +} + +TEST_F(CPUTest, PLP_PullProcessorStatusRegister) { + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x34)); + cpu.ExecuteInstruction(0x28); // PLP + EXPECT_EQ(cpu.status, 0x34); +} + +TEST_F(CPUTest, PLX_PullXRegister) { + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x56)); + cpu.ExecuteInstruction(0xFA); // PLX + EXPECT_EQ(cpu.X, 0x56); +} + +TEST_F(CPUTest, PLY_PullYRegister) { + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x78)); + cpu.ExecuteInstruction(0x7A); // PLY + EXPECT_EQ(cpu.Y, 0x78); +} + +TEST_F(CPUTest, PLB_PullDataBankRegister) { + EXPECT_CALL(mock_memory, PopByte()).WillOnce(Return(0x9A)); + cpu.ExecuteInstruction(0xAB); // PLB + EXPECT_EQ(cpu.DB, 0x9A); +} + +TEST_F(CPUTest, PLD_PullDirectPageRegister) { + EXPECT_CALL(mock_memory, PopWord()).WillOnce(Return(0xBC)); + cpu.ExecuteInstruction(0x2B); // PLD + EXPECT_EQ(cpu.D, 0xBC); } // ============================================================================ // REP - Reset Processor Status Bits -TEST(CPUTest, REP) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, REP) { cpu.status = 0xFF; // All flags set std::vector data = {0xC2, 0x30, 0x00}; // REP #0x30 (clear N & Z flags) @@ -386,9 +449,7 @@ TEST(CPUTest, REP) { // ============================================================================ // SEP - Set Processor Status Bits -TEST(CPUTest, SEP) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, SEP) { cpu.status = 0x00; // All flags cleared std::vector data = {0xE2, 0x30, 0x00}; // SEP #0x30 (set N & Z flags) @@ -401,9 +462,7 @@ TEST(CPUTest, SEP) { // ============================================================================ // TXA - Transfer Index X to Accumulator -TEST(CPUTest, TXA) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, TXA) { cpu.X = 0xAB; // X register std::vector data = {0x8A}; // TXA mock_memory.SetMemoryContents(data); @@ -415,9 +474,7 @@ TEST(CPUTest, TXA) { // ============================================================================ // TAX - Transfer Accumulator to Index X -TEST(CPUTest, TAX) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, TAX) { cpu.A = 0xBC; // A register std::vector data = {0xAA}; // TAX mock_memory.SetMemoryContents(data); @@ -429,9 +486,7 @@ TEST(CPUTest, TAX) { // ============================================================================ // TYA - Transfer Index Y to Accumulator -TEST(CPUTest, TYA) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, TYA) { cpu.Y = 0xCD; // Y register std::vector data = {0x98}; // TYA mock_memory.SetMemoryContents(data); @@ -443,9 +498,7 @@ TEST(CPUTest, TYA) { // ============================================================================ // TAY - Transfer Accumulator to Index Y -TEST(CPUTest, TAY) { - MockMemory mock_memory; - CPU cpu(mock_memory); +TEST_F(CPUTest, TAY) { cpu.A = 0xDE; // A register std::vector data = {0xA8}; // TAY mock_memory.SetMemoryContents(data);