diff --git a/src/RP2040.Core/Cpu/InstructionDecoder.cs b/src/RP2040.Core/Cpu/InstructionDecoder.cs index 1ae41ed..454cea7 100644 --- a/src/RP2040.Core/Cpu/InstructionDecoder.cs +++ b/src/RP2040.Core/Cpu/InstructionDecoder.cs @@ -90,6 +90,8 @@ public InstructionDecoder() new OpcodeRule(0xFFC0, 0xBAC0, &BitOps.Revsh), // REV (Rd, Rn) new OpcodeRule(0xFFC0, 0xBA00, &BitOps.Rev), + // SBCS (Rn, Rm) + new OpcodeRule(0xFFC0, 0x4180, &ArithmeticOps.Sbcs), // ================================================================ // GROUP 4: Mask 0xFF87 (High Register Special Cases) // CRITICAL: These are specific cases of the 0xFF00 generic group. diff --git a/src/RP2040.Core/Cpu/Instructions/ArithmeticOps.cs b/src/RP2040.Core/Cpu/Instructions/ArithmeticOps.cs index 7cb7ac3..2be500b 100644 --- a/src/RP2040.Core/Cpu/Instructions/ArithmeticOps.cs +++ b/src/RP2040.Core/Cpu/Instructions/ArithmeticOps.cs @@ -205,6 +205,19 @@ public static void Rsbs(ushort opcode, CortexM0Plus cpu) ptrRd = SubWithFlags(cpu, 0, valRn); } + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Sbcs(ushort opcode, CortexM0Plus cpu) + { + var rm = (opcode >> 3) & 0x7; + var rdn = opcode & 0x7; + + ref var ptrRdn = ref cpu.Registers[rdn]; + var valRm = cpu.Registers[rm]; + var carry = cpu.Registers.C ? 1u : 0u; + + ptrRdn = AddWithFlags(cpu, ptrRdn, ~valRm, carry); + } + [MethodImpl(MethodImplOptions.AggressiveInlining)] public static void SubsImmediate3(ushort opcode, CortexM0Plus cpu) { diff --git a/src/RP2040.Core/Helpers/InstructionEmiter.cs b/src/RP2040.Core/Helpers/InstructionEmiter.cs index b79a21e..a22f0c3 100644 --- a/src/RP2040.Core/Helpers/InstructionEmiter.cs +++ b/src/RP2040.Core/Helpers/InstructionEmiter.cs @@ -379,6 +379,13 @@ public static ushort Rsbs(uint rd, uint rn) return (ushort)(0x4240 | (rn & 0x7) << 3 | (rd & 0x7)); } + public static ushort Sbcs(uint rn, uint rm) + { + if (rm > 7 || rn > 7) + throw new ArgumentException(LowRegisterIndexOutOfRange); + return (ushort)(0x4180 | ((rm & 0x7) << 3) | (rn & 0x7)); + } + public static ushort SubSp(uint imm) { if (imm > 508) diff --git a/tests/RP2040.Core.Tests/Cpu/InstructionDecoderTests.cs b/tests/RP2040.Core.Tests/Cpu/InstructionDecoderTests.cs index c3799e2..e5cac97 100644 --- a/tests/RP2040.Core.Tests/Cpu/InstructionDecoderTests.cs +++ b/tests/RP2040.Core.Tests/Cpu/InstructionDecoderTests.cs @@ -87,6 +87,7 @@ public static TheoryData GetInstructionTestCases() ); Add("Muls", InstructionEmiter.Muls(R0, R2), &ArithmeticOps.Muls); Add("Rsbs", InstructionEmiter.Rsbs(R0, R3), &ArithmeticOps.Rsbs); + Add("Sbcs", InstructionEmiter.Sbcs(R0, R3), &ArithmeticOps.Sbcs); Add("SubsRegister", InstructionEmiter.SubsReg(R1, R2, R4), &ArithmeticOps.SubsRegister); Add("SubsImm3", InstructionEmiter.SubsImm3(R1, R2, 3), &ArithmeticOps.SubsImmediate3); Add("SubsImm8", InstructionEmiter.SubsImm8(R1, 0x10), &ArithmeticOps.SubsImmediate8); diff --git a/tests/RP2040.Core.Tests/Cpu/Instructions/ArithmeticOpsTests.cs b/tests/RP2040.Core.Tests/Cpu/Instructions/ArithmeticOpsTests.cs index 2d2276b..7265fb1 100644 --- a/tests/RP2040.Core.Tests/Cpu/Instructions/ArithmeticOpsTests.cs +++ b/tests/RP2040.Core.Tests/Cpu/Instructions/ArithmeticOpsTests.cs @@ -865,4 +865,49 @@ public void Should_Execute_SubsR5R3R2_And_Set_ZCFlags() Cpu.Registers.V.Should().BeFalse(); } } + + public class Sbcs : CpuTestBase + { + [Fact] + public void Should_Execute_SbcsR0R3() + { + // Arrange + var opcode = InstructionEmiter.Sbcs(R0, R3); + Bus.WriteHalfWord(0x20000000, opcode); + Cpu.Registers.R0 = 100; + Cpu.Registers.R3 = 55; + Cpu.Registers.C = false; + + // Act + Cpu.Step(); + + // Assert + Cpu.Registers.R0.Should().Be(44); + Cpu.Registers.N.Should().BeFalse(); + Cpu.Registers.Z.Should().BeFalse(); + Cpu.Registers.C.Should().BeTrue(); + Cpu.Registers.V.Should().BeFalse(); + } + + [Fact] + public void Should_Execute_SbcsR0R3_To_Be_0() + { + // Arrange + var opcode = InstructionEmiter.Sbcs(R0, R3); + Bus.WriteHalfWord(0x20000000, opcode); + Cpu.Registers.R0 = 0; + Cpu.Registers.R3 = 0xffffffff; + Cpu.Registers.C = false; + + // Act + Cpu.Step(); + + // Assert + Cpu.Registers.R0.Should().Be(0); + Cpu.Registers.N.Should().BeFalse(); + Cpu.Registers.Z.Should().BeTrue(); + Cpu.Registers.C.Should().BeFalse(); + Cpu.Registers.V.Should().BeFalse(); + } + } }