From dfc8189920649018ff9d9037af60e3bd301a12fb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Montiel=20Cardona?= Date: Mon, 2 Feb 2026 03:07:30 -0600 Subject: [PATCH 1/2] feat(cpu): add TST instruction and tests; tighten emitter checks Add implementation and decoding for the TST (Rn, Rm) instruction, including: - Instruction handler in BitOps.Tst that computes Rn & Rm and updates the N and Z flags. - Opcode rule for TST in InstructionDecoder to route 0x4200 pattern to BitOps.Tst. - InstructionEmiter.Tst helper to emit the 16-bit TST opcode for low registers and argument validation for low-register constraints. Add unit tests in BitOpsTests: - Should_SetNegativeFlag_When_ResultHasSignBitSet verifies N is set when the bitwise AND has the sign bit. - Should_SetZeroFlag_When_ResultIsZero verifies Z is set when result is zero. Tighten register index validation in InstructionEmiter.Rsbs: - Change high/low register bounds checks to use low-register range (0-7) and throw LowRegisterIndexOutOfRange for low-register encodings. These changes implement bit-test functionality, ensure the decoder recognizes the instruction, and add tests to validate flag behavior. --- src/RP2040.Core/Cpu/InstructionDecoder.cs | 2 + src/RP2040.Core/Cpu/Instructions/BitOps.cs | 12 ++++++ src/RP2040.Core/Helpers/InstructionEmiter.cs | 15 ++++++-- .../Cpu/Instructions/BitOpsTests.cs | 37 +++++++++++++++++++ 4 files changed, 62 insertions(+), 4 deletions(-) diff --git a/src/RP2040.Core/Cpu/InstructionDecoder.cs b/src/RP2040.Core/Cpu/InstructionDecoder.cs index ef99387..c0c54e7 100644 --- a/src/RP2040.Core/Cpu/InstructionDecoder.cs +++ b/src/RP2040.Core/Cpu/InstructionDecoder.cs @@ -64,6 +64,8 @@ public InstructionDecoder() new OpcodeRule(0xFFC0, 0x4380, &BitOps.Bics), // CMN (Rn, Rm) new OpcodeRule(0xFFC0, 0x42C0, &ArithmeticOps.Cmn), + // TST Rn, Rm (Test bits) + new OpcodeRule(0xFFC0, 0x4200, &BitOps.Tst), // RSBS Rd, Rn, #0 (Negate) new OpcodeRule(0xFFC0, 0x4240, &ArithmeticOps.Rsbs), // CMP Rn, Rm (Low Registers - Encoding T1) diff --git a/src/RP2040.Core/Cpu/Instructions/BitOps.cs b/src/RP2040.Core/Cpu/Instructions/BitOps.cs index 41df678..22a725b 100644 --- a/src/RP2040.Core/Cpu/Instructions/BitOps.cs +++ b/src/RP2040.Core/Cpu/Instructions/BitOps.cs @@ -281,4 +281,16 @@ public static void Revsh(ushort opcode, CortexM0Plus cpu) var reversed = BinaryPrimitives.ReverseEndianness(val); cpu.Registers[rd] = (uint)(short)reversed; } + + [MethodImpl(MethodImplOptions.AggressiveInlining)] + public static void Tst(ushort opcode, CortexM0Plus cpu) + { + var rm = (opcode >> 3) & 0x7; + var rn = opcode & 0x7; + + var result = cpu.Registers[rn] & cpu.Registers[rm]; + + cpu.Registers.N = (int)result < 0; + cpu.Registers.Z = result == 0; + } } diff --git a/src/RP2040.Core/Helpers/InstructionEmiter.cs b/src/RP2040.Core/Helpers/InstructionEmiter.cs index 75cb1fb..575fe8e 100644 --- a/src/RP2040.Core/Helpers/InstructionEmiter.cs +++ b/src/RP2040.Core/Helpers/InstructionEmiter.cs @@ -325,10 +325,10 @@ public static ushort Rev16(uint rd, uint rn) public static ushort Rsbs(uint rd, uint rn) { - if (rd > 15) - throw new ArgumentException(HighRegisterIndexOutOfRange); - if (rn > 15) - throw new ArgumentException(HighRegisterIndexOutOfRange); + if (rd > 7) + throw new ArgumentException(LowRegisterIndexOutOfRange); + if (rn > 7) + throw new ArgumentException(LowRegisterIndexOutOfRange); return (ushort)(0x4240 | (rn & 0x7) << 3 | (rd & 0x7)); } @@ -367,4 +367,11 @@ public static ushort SubsReg(uint rd, uint rn, uint rm) throw new ArgumentException(LowRegisterIndexOutOfRange); return (ushort)(0x1A00 | ((rm & 0x7) << 6) | ((rn & 7) << 3) | (rd & 7)); } + + public static ushort Tst(uint rn, uint rm) + { + if (rn > 7 || rm > 7) + throw new ArgumentException(LowRegisterIndexOutOfRange); + return (ushort)(0x4200 | ((rm & 7) << 3) | (rn & 7)); + } } diff --git a/tests/RP2040.Core.Tests/Cpu/Instructions/BitOpsTests.cs b/tests/RP2040.Core.Tests/Cpu/Instructions/BitOpsTests.cs index 3cb2c8a..d38c422 100644 --- a/tests/RP2040.Core.Tests/Cpu/Instructions/BitOpsTests.cs +++ b/tests/RP2040.Core.Tests/Cpu/Instructions/BitOpsTests.cs @@ -558,4 +558,41 @@ public void Should_ReverseLowerHalfword_And_SignExtend() Cpu.Registers[R1].Should().Be(0xfffff055); } } + + public class Tst : CpuTestBase + { + [Fact] + public void Should_SetNegativeFlag_When_ResultHasSignBitSet() + { + // Arrange + var opcode = InstructionEmiter.Tst(R1, R3); + Bus.WriteHalfWord(0x20000000, opcode); + + Cpu.Registers[R1] = 0xf0000000; + Cpu.Registers[R3] = 0xf0004000; + + // Act + Cpu.Step(); + + // Assert + Cpu.Registers.N.Should().BeTrue(); + } + + [Fact] + public void Should_SetZeroFlag_When_ResultIsZero() + { + // Arrange + var opcode = InstructionEmiter.Tst(R1, R3); + Bus.WriteHalfWord(0x20000000, opcode); + + Cpu.Registers[R1] = 0xf0; + Cpu.Registers[R3] = 0x0f; + + // Act + Cpu.Step(); + + // Assert + Cpu.Registers.Z.Should().BeTrue(); + } + } } From d1ee842b02d1785190199729838f0c9b20535d04 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Iv=C3=A1n=20Montiel=20Cardona?= Date: Mon, 2 Feb 2026 03:13:55 -0600 Subject: [PATCH 2/2] test(cpu): add missing Rev16, Movs and Tst instruction tests Add unit tests for three previously untested ARM instructions in the InstructionDecoder suite: Rev16, Movs and Tst. These are added alongside existing bit-op and move variations to ensure the decoder emits the correct encodings and the corresponding BitOps handlers are validated. This improves coverage for byte/halfword reversal and flag-affecting move/test operations, catching decoder regressions and aligning tests with implemented instruction emitters. --- tests/RP2040.Core.Tests/Cpu/InstructionDecoderTests.cs | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/tests/RP2040.Core.Tests/Cpu/InstructionDecoderTests.cs b/tests/RP2040.Core.Tests/Cpu/InstructionDecoderTests.cs index 91cb326..6ee8cee 100644 --- a/tests/RP2040.Core.Tests/Cpu/InstructionDecoderTests.cs +++ b/tests/RP2040.Core.Tests/Cpu/InstructionDecoderTests.cs @@ -105,12 +105,16 @@ public static TheoryData GetInstructionTestCases() Add("Orrs", InstructionEmiter.Orrs(R5, R0), &BitOps.Orrs); Add("Rev", InstructionEmiter.Rev(R0, R1), &BitOps.Rev); Add("Revsh", InstructionEmiter.Revsh(R0, R1), &BitOps.Revsh); + Add("Rev16", InstructionEmiter.Rev16(R0, R1), &BitOps.Rev16); // Mov Variations Add("Mov (Reg)", InstructionEmiter.Mov(R3, R8), &BitOps.MovRegister); Add("Mov (Pc)", InstructionEmiter.Mov(PC, R8), &BitOps.MovToPc); Add("Mov (Sp)", InstructionEmiter.Mov(SP, R8), &BitOps.MovToSp); + Add("Movs", InstructionEmiter.Movs(R0, R1), &BitOps.Movs); + Add("Tst", InstructionEmiter.Tst(R0, R1), &BitOps.Tst); + // --- Flow Control --- for (uint cond = 0; cond <= 13; cond++) {