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/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++) { 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(); + } + } }