diff --git a/src/app/emu/cpu.cc b/src/app/emu/cpu.cc index 89feb295..74d4cf07 100644 --- a/src/app/emu/cpu.cc +++ b/src/app/emu/cpu.cc @@ -66,6 +66,9 @@ uint8_t CPU::FetchByteDirectPage(uint8_t operand) { } void CPU::ExecuteInstruction(uint8_t opcode) { + // Update the PC based on the Program Bank Register + PC |= (static_cast(PB) << 16); + // uint8_t opcode = FetchByte(); uint8_t operand = -1; switch (opcode) { @@ -94,7 +97,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) { ADC(operand); break; case 0x6F: // ADC Absolute Long - operand = memory.ReadByte(AbsoluteLong()); + operand = memory.ReadWord(AbsoluteLong()); ADC(operand); break; case 0x71: // ADC DP Indirect Indexed, Y @@ -153,8 +156,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) { AND(Absolute()); break; case 0x2F: // AND Absolute Long - operand = memory.ReadByte(AbsoluteLong()); - AND(operand); + ANDAbsoluteLong(AbsoluteLong()); break; case 0x31: // AND DP Indirect Indexed, Y operand = memory.ReadByte(DirectPageIndirectIndexedY()); @@ -177,16 +179,13 @@ void CPU::ExecuteInstruction(uint8_t opcode) { AND(operand); break; case 0x39: // AND Absolute Indexed, Y - operand = memory.ReadByte(AbsoluteIndexedY()); - AND(operand); + AND(AbsoluteIndexedY()); break; case 0x3D: // AND Absolute Indexed, X - operand = memory.ReadByte(AbsoluteIndexedX()); - AND(operand); + AND(AbsoluteIndexedX()); break; case 0x3F: // AND Absolute Long Indexed, X - operand = memory.ReadByte(AbsoluteLongIndexedX()); - AND(operand); + AND(AbsoluteLongIndexedX()); break; case 0x06: // ASL Direct Page @@ -451,7 +450,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) { JMP(Absolute()); break; case 0x5C: // JMP Absolute Long - // JMP(); + JML(AbsoluteLong()); break; case 0x6C: // JMP Absolute Indirect JMP(AbsoluteIndirect()); @@ -574,7 +573,7 @@ void CPU::ExecuteInstruction(uint8_t opcode) { break; case 0xEA: // NOP No Operation - // NOP(); + NOP(); break; 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 app } // namespace yaze \ No newline at end of file diff --git a/src/app/emu/cpu.h b/src/app/emu/cpu.h index a8d3c62e..e28e5250 100644 --- a/src/app/emu/cpu.h +++ b/src/app/emu/cpu.h @@ -105,7 +105,7 @@ class CPU : public Memory { // Low: First operand byte // // LDA long - uint16_t AbsoluteLong() { return FetchLong(); } + uint32_t AbsoluteLong() { return FetchLong(); } // Effective Address: // The 24-bit operand is added to X based on the emulation mode @@ -243,11 +243,14 @@ class CPU : public Memory { // Registers uint8_t A = 0; // Accumulator + uint8_t B = 0; // Accumulator (High) uint8_t X = 0; // X index register + uint8_t X2 = 0; // X index register (High) uint8_t Y = 0; // Y index register + uint8_t Y2 = 0; // Y index register (High) uint16_t D = 0; // Direct Page 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 uint8_t E = 1; // Emulation mode flag uint8_t status; // Processor Status (P) @@ -288,91 +291,47 @@ class CPU : public Memory { // ========================================================================== // Instructions + /// ``` Unimplemented - // Left to implement - // * = 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 - + // ADC: Add with carry void ADC(uint8_t operand); + void ANDAbsoluteLong(uint32_t address); + + // AND: Logical AND void AND(uint16_t address); - void BEQ(int8_t offset) { - if (GetZeroFlag()) { // If the zero flag is set - PC += offset; // Add the offset to the program counter - } - } + // ASL: Arithmetic shift left ``` + // BCC: Branch if carry clear void BCC(int8_t offset) { if (!GetCarryFlag()) { // If the carry flag is clear 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) { PC += offset; // Add the offset to the program counter } - void LDA() { - A = memory[PC]; - SetZeroFlag(A == 0); - SetNegativeFlag(A & 0x80); - PC++; - } - - // SEC: Set carry flag - void SEC() { status |= 0x01; } + // BVC: Branch if overflow clear ``` + // BVS: Branch if overflow set ``` // CLC: Clear carry flag void CLC() { status &= ~0x01; } @@ -386,20 +345,25 @@ class CPU : public Memory { // CLV: Clear overflow flag void CLV() { status &= ~0x40; } - bool emulation_mode = false; + // CMP: Compare ``` + // COP: Coprocessor ``` + // CPX: Compare X register void CPX(uint16_t address) { uint16_t memory_value = E ? memory.ReadByte(address) : memory.ReadWord(address); compare(X, memory_value); } + // CPY: Compare Y register void CPY(uint16_t address) { uint16_t memory_value = E ? memory.ReadByte(address) : memory.ReadWord(address); compare(Y, memory_value); } + // DEC: Decrement ``` + // DEX: Decrement X register void DEX() { X--; @@ -414,21 +378,10 @@ class CPU : public Memory { SetNegativeFlag(Y & 0x80); } - // INX: Increment X register - void INX() { - X++; - SetNegativeFlag(X & 0x80); - SetZeroFlag(X == 0); - } + // EOR: Exclusive OR ``` - // INY: Increment Y register - void INY() { - Y++; - SetNegativeFlag(Y & 0x80); - SetZeroFlag(Y == 0); - } - - // INC: Increment memory + // INC: Increment + // TODO: Check if this is correct void INC(uint16_t address) { if (GetAccumulatorSize()) { 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) { 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(address & 0xFFFF); + // Set the PBR to the upper 8 bits of the address + PB = static_cast((address >> 16) & 0xFF); + } + // JSR: Jump to subroutine void JSR(uint16_t address) { PC -= 1; // Subtract 1 from program counter @@ -471,51 +446,65 @@ class CPU : public Memory { 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); } - // 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() { 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 + // PLB: Pull data bank register 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(); @@ -523,89 +512,141 @@ class CPU : public Memory { SetZeroFlag(D == 0); } - // Push Program Bank Register on Stack - void PHK() { memory.PushByte(PB); } + // Pull Processor Status Register from Stack + void PLP() { status = memory.PopByte(); } - void SEI() { status |= 0x04; } - - void SED() { status |= 0x08; } - - void SEP() { - PC++; - auto byte = FetchByte(); - status |= byte; + // PLX: Pull X Index Register from Stack + void PLX() { + X = memory.PopByte(); + SetNegativeFlag((A & 0x80) != 0); + SetZeroFlag(X == 0); } + // 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() { PC++; auto byte = FetchByte(); status &= ~byte; } - void TCD() { - D = A; - SetZeroFlag(D == 0); - SetNegativeFlag(D & 0x80); + // ROL: Rotate left ``` + // ROR: Rotate right ``` + // RTI: Return from interrupt ``` + // 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() { - A = D; - SetZeroFlag(A == 0); - SetNegativeFlag(A & 0x80); - } - - void TCS() { memory.SetSP(A); } + // STA: Store accumulator ``` + // STP: Stop the clock ``` + // STX: Store X register ``` + // STY: Store Y register ``` + // STZ: Store zero ``` + // TAX: Transfer accumulator to X void TAX() { X = A; SetZeroFlag(X == 0); SetNegativeFlag(X & 0x80); } + // TAY: Transfer accumulator to Y void TAY() { Y = A; SetZeroFlag(Y == 0); SetNegativeFlag(Y & 0x80); } - void TYA() { - A = Y; + // TCD: Transfer accumulator to direct page register + 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); SetNegativeFlag(A & 0x80); } - void TXA() { - A = X; + // TRB: Test and reset bits ``` + // TSB: Test and set bits ``` + + // TSC: Transfer stack pointer to accumulator + void TSC() { + A = SP(); SetZeroFlag(A == 0); SetNegativeFlag(A & 0x80); } - void TXY() { - X = Y; - SetZeroFlag(X == 0); - SetNegativeFlag(X & 0x80); - } - - void TYX() { - Y = X; - SetZeroFlag(Y == 0); - SetNegativeFlag(Y & 0x80); - } - + // TSX: Transfer stack pointer to X void TSX() { X = SP(); SetZeroFlag(X == 0); SetNegativeFlag(X & 0x80); } - void TXS() { memory.SetSP(X); } - - void TSC() { - A = SP(); + // TXA: Transfer X to accumulator + void TXA() { + A = X; SetZeroFlag(A == 0); 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 void XCE() { uint8_t carry = status & 0x01; diff --git a/src/app/emu/mem.h b/src/app/emu/mem.h index 6c724cd1..be30d790 100644 --- a/src/app/emu/mem.h +++ b/src/app/emu/mem.h @@ -58,8 +58,8 @@ class MemoryImpl : public Memory { if (address < dp_memory_.size()) { return dp_memory_.ReadByte(static_cast(address)); } - // uint32_t mapped_address = GetMappedAddress(address); - return memory_.at(address); + uint32_t mapped_address = GetMappedAddress(address); + return memory_.at(mapped_address); } uint16_t ReadWord(uint16_t address) const override { if (address < dp_memory_.size()) { @@ -77,8 +77,8 @@ class MemoryImpl : public Memory { } void WriteByte(uint32_t address, uint8_t value) override { - // uint32_t mapped_address = GetMappedAddress(address); - memory_[address] = value; + uint32_t mapped_address = GetMappedAddress(address); + memory_[mapped_address] = value; } void WriteWord(uint32_t address, uint16_t value) override { uint32_t mapped_address = GetMappedAddress(address); diff --git a/test/cpu_test.cc b/test/cpu_test.cc index 5bf66b40..f16296a0 100644 --- a/test/cpu_test.cc +++ b/test/cpu_test.cc @@ -41,6 +41,14 @@ class MockMemory : public Memory { std::copy(data.begin(), data.end(), memory_.begin()); } + void InsertMemory(const uint64_t address, const std::vector& data) { + int i = 0; + for (const auto& each : data) { + memory_[address + i] = each; + i++; + } + } + void Init() { ON_CALL(*this, ReadByte(::testing::_)) .WillByDefault( @@ -111,6 +119,7 @@ class CPUTest : public ::testing::Test { public: void SetUp() override { mock_memory.Init(); + EXPECT_CALL(mock_memory, ClearMemory()).Times(::testing::AtLeast(1)); mock_memory.ClearMemory(); } @@ -189,7 +198,15 @@ TEST_F(CPUTest, ADC_AbsoluteLong) { cpu.A = 0x01; cpu.PC = 1; // PC register cpu.status = 0x00; // 16-bit mode - std::vector data = {0x2F, 0x03, 0x00, 0x00, 0x05, 0x00}; + std::vector 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 } +TEST_F(CPUTest, AND_AbsoluteLong) { + cpu.A = 0x01; + cpu.PC = 1; // PC register + cpu.status = 0x00; // 16-bit mode + std::vector 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) { cpu.A = 0b10101010; // A 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 } +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 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(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 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 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(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 @@ -510,6 +614,28 @@ TEST_F(CPUTest, JMP_Indirect) { 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 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 // ============================================================================