From 4ed34daa6f7d03aae58f537e079587b273aa666c Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Montiel=20Cardona?= Date: Mon, 2 Feb 2026 02:57:10 -0600 Subject: [PATCH] fix(mov): use rd variable and set N flag correctly; add MOV tests Use a local rd variable when decoding MOV immediate to avoid duplicating the bit extraction expression and to clarify intent. Set the negative (N) flag based on the signed interpretation of the loaded value instead of always clearing it. Add unit tests covering: - writes to SP clear the lower two bits - writes to PC clear the least significant bit - register-to-register moves for positive and negative values These tests increase coverage for MOV behavior and verify flag and special-register handling. --- src/RP2040.Core/Cpu/Instructions/BitOps.cs | 5 +- .../Cpu/Instructions/BitOpsTests.cs | 64 +++++++++++++++++++ 2 files changed, 67 insertions(+), 2 deletions(-) diff --git a/src/RP2040.Core/Cpu/Instructions/BitOps.cs b/src/RP2040.Core/Cpu/Instructions/BitOps.cs index 41df678..67d4866 100644 --- a/src/RP2040.Core/Cpu/Instructions/BitOps.cs +++ b/src/RP2040.Core/Cpu/Instructions/BitOps.cs @@ -185,10 +185,11 @@ public static void MovRegister(ushort opcode, CortexM0Plus cpu) public static void Movs(ushort opcode, CortexM0Plus cpu) { var value = (uint)(opcode & 0xFF); + var rd = (opcode >> 8) & 7; - cpu.Registers[(opcode >> 8) & 7] = value; + cpu.Registers[rd] = value; - cpu.Registers.N = false; + cpu.Registers.N = (int)value < 0; cpu.Registers.Z = value == 0; } diff --git a/tests/RP2040.Core.Tests/Cpu/Instructions/BitOpsTests.cs b/tests/RP2040.Core.Tests/Cpu/Instructions/BitOpsTests.cs index 3cb2c8a..7ab7470 100644 --- a/tests/RP2040.Core.Tests/Cpu/Instructions/BitOpsTests.cs +++ b/tests/RP2040.Core.Tests/Cpu/Instructions/BitOpsTests.cs @@ -351,6 +351,70 @@ public void Should_LoadImmediateValue_And_UpdateFlags() Cpu.Registers[R5].Should().Be(128); Cpu.Registers.PC.Should().Be(0x20000002); } + + [Fact] + public void Should_ClearLowerTwoBits_When_WritingToStackPointer() + { + // Arrange + var opcode = InstructionEmiter.Mov(SP, R5); + Bus.WriteHalfWord(0x20000000, opcode); + + Cpu.Registers[R5] = 0x53; + + // Act + Cpu.Step(); + + // Assert + Cpu.Registers.SP.Should().Be(0x50); + } + + [Fact] + public void Should_ClearLeastSignificantBit_When_WritingToProgramCounter() + { + // Arrange + var opcode = InstructionEmiter.Mov(PC, R5); + Bus.WriteHalfWord(0x20000000, opcode); + + Cpu.Registers[R5] = 0x53; + + // Act + Cpu.Step(); + + // Assert + Cpu.Registers.PC.Should().Be(0x52); + } + + [Fact] + public void Should_CopyValue_BetweenRegisters() + { + // Arrange + var opcode = InstructionEmiter.Mov(R6, R5); + Bus.WriteHalfWord(0x20000000, opcode); + + Cpu.Registers[R5] = 50; + + // Act + Cpu.Step(); + + // Assert + Cpu.Registers[R6].Should().Be(50); + } + + [Fact] + public void Should_CopyNegativeValue_BetweenRegisters() + { + // Arrange + var opcode = InstructionEmiter.Mov(R6, R5); + Bus.WriteHalfWord(0x20000000, opcode); + + Cpu.Registers[R5] = unchecked((uint)-50); + + // Act + Cpu.Step(); + + // Assert + Cpu.Registers[R6].Should().Be(unchecked((uint)-50)); + } } public class Mvns : CpuTestBase