From 6040f7816948255a1f7f05ea1bb4ed02f982d6c8 Mon Sep 17 00:00:00 2001 From: "Eric P. Nusbaum" Date: Tue, 27 Aug 2024 22:21:24 -0400 Subject: [PATCH] Update FPU Unit Tests for Rounding Mode - Add Control Word to Unit Tests for `FLDCW` and `FRNDINT` - Verify Different Rounding Modes in these Unit Test cases --- MBBSEmu.Tests/CPU/FLDCW_Tests.cs | 29 +++++++++++++++++++----- MBBSEmu.Tests/CPU/FRNDINT_Tests.cs | 36 +++++++++++++++++++++++------- MBBSEmu/CPU/CpuRegisters.cs | 1 + MBBSEmu/CPU/ICpuRegisters.cs | 6 +---- 4 files changed, 54 insertions(+), 18 deletions(-) diff --git a/MBBSEmu.Tests/CPU/FLDCW_Tests.cs b/MBBSEmu.Tests/CPU/FLDCW_Tests.cs index 4fa7e793..1104b415 100644 --- a/MBBSEmu.Tests/CPU/FLDCW_Tests.cs +++ b/MBBSEmu.Tests/CPU/FLDCW_Tests.cs @@ -7,22 +7,41 @@ namespace MBBSEmu.Tests.CPU { public class FLDCW_Tests : CpuTestBase { - [Fact] - public void FLDCW_Test() + /// + /// Loads the FPU Control Word from Memory and sets the FPU Control Word + /// + /// We'll verify this by not only setting the Control Word, but also verify + /// the Round Mode is set correctly + /// + /// Rounding Flag Conversion: + /// Rounding Mode = (ControlWord >> 10) & 0x3; + /// 0 => MidpointRounding.ToEven + /// 1 => MidpointRounding.ToNegativeInfinity + /// 2 => MidpointRounding.ToPositiveInfinity + /// 3 => MidpointRounding.ToZero + /// + [Theory] + [InlineData(0x0000, MidpointRounding.ToEven)] + [InlineData(0x0400, MidpointRounding.ToNegativeInfinity)] + [InlineData(0x0800, MidpointRounding.ToPositiveInfinity)] + [InlineData(0x0C00, MidpointRounding.ToZero)] + public void FLDCW_Test(ushort controlWord, MidpointRounding roundingMode) { Reset(); mbbsEmuCpuRegisters.Fpu.ControlWord = 0; CreateDataSegment(new ReadOnlySpan(), 2); - mbbsEmuMemoryCore.SetWord(2,0, 0xFFFF); + mbbsEmuMemoryCore.SetArray(2, 0, [0, 0, 0]); //Set some junk data ahead of the actual address + mbbsEmuMemoryCore.SetWord(2,3, controlWord); mbbsEmuCpuRegisters.DS = 2; var instructions = new Assembler(16); - instructions.fldcw(__word_ptr[0]); + instructions.fldcw(__word_ptr[3]); CreateCodeSegment(instructions); mbbsEmuCpuCore.Tick(); - Assert.Equal(0xFFFF, mbbsEmuCpuRegisters.Fpu.ControlWord); + Assert.Equal(controlWord, mbbsEmuCpuRegisters.Fpu.ControlWord); + Assert.Equal(roundingMode, mbbsEmuCpuRegisters.Fpu.GetRoundingControl()); } } } diff --git a/MBBSEmu.Tests/CPU/FRNDINT_Tests.cs b/MBBSEmu.Tests/CPU/FRNDINT_Tests.cs index 879e0cb7..b56782d4 100644 --- a/MBBSEmu.Tests/CPU/FRNDINT_Tests.cs +++ b/MBBSEmu.Tests/CPU/FRNDINT_Tests.cs @@ -5,20 +5,40 @@ namespace MBBSEmu.Tests.CPU { public class FRNDINT_Tests : CpuTestBase { + /// + /// Tests the FRNDINT Instruction which rounds the value in ST(0) to the nearest integer + /// + /// We'll test this by setting the FPU Control Mode to Round to different modes and verify. + /// + /// Rounding Flag Conversion: + /// Rounding Mode = (ControlWord >> 10) & 0x3; + /// 0 => MidpointRounding.ToEven + /// 1 => MidpointRounding.ToNegativeInfinity + /// 2 => MidpointRounding.ToPositiveInfinity + /// 3 => MidpointRounding.ToZero + /// + /// + /// [Theory] - [InlineData(2.1d, 2d)] - [InlineData(0.1d, 0d)] - [InlineData(1.9d, 2d)] - [InlineData(-1.9d, -2d)] - [InlineData(0.5d, 0d)] - [InlineData(0.49999999d, 0d)] - public void FRNDINT_Test(double ST0Value, double expectedValue) + //Round to Even + [InlineData(1.5, 2.0, 0x0000)] + [InlineData(-1.5, -2.0, 0x0000)] + //Round to Negative Infinity + [InlineData(1.5, 1.0, 0x0400)] + [InlineData(-1.5, -2.0, 0x0400)] + //Round to Positive Infinity + [InlineData(1.5, 2.0, 0x0800)] + [InlineData(-1.5, -1.0, 0x0800)] + //Round to Zero + [InlineData(1.5, 1.0, 0x0C00)] + [InlineData(-1.5, -1.0, 0x0C00)] + public void FRNDINT_Test(double ST0Value, double expectedValue, ushort controlWord) { Reset(); mbbsEmuCpuCore.FpuStack[mbbsEmuCpuRegisters.Fpu.GetStackTop()] = ST0Value; //Set FPU Control Word to set rounding to even - mbbsEmuCpuRegisters.Fpu.ControlWord = 0x0000000C; + mbbsEmuCpuRegisters.Fpu.ControlWord = controlWord; var instructions = new Assembler(16); instructions.frndint(); diff --git a/MBBSEmu/CPU/CpuRegisters.cs b/MBBSEmu/CPU/CpuRegisters.cs index 6c25d450..6bb85f73 100644 --- a/MBBSEmu/CPU/CpuRegisters.cs +++ b/MBBSEmu/CPU/CpuRegisters.cs @@ -89,6 +89,7 @@ public void SetPointer(FarPtr ptr) public void PopStackTop() => Registers.Fpu.PopStackTop(); public void PushStackTop() => Registers.Fpu.PushStackTop(); public void ClearExceptions() => Registers.Fpu.ClearExceptions(); + public MidpointRounding GetRoundingControl() => Registers.Fpu.GetRoundingControl(); /// /// Overridden ToString() to display the current state of the CPU Registers diff --git a/MBBSEmu/CPU/ICpuRegisters.cs b/MBBSEmu/CPU/ICpuRegisters.cs index edb6aee4..f244b782 100644 --- a/MBBSEmu/CPU/ICpuRegisters.cs +++ b/MBBSEmu/CPU/ICpuRegisters.cs @@ -10,20 +10,16 @@ namespace MBBSEmu.CPU public interface IFpuRegisters { ushort StatusWord { get; set; } - ushort ControlWord { get; set; } - void SetFlag(EnumFpuStatusFlags statusFlag); void ClearFlag(EnumFpuStatusFlags statusFlag); - byte GetStackTop(); - void SetStackTop(byte value); - int GetStackPointer(Register register); void PopStackTop(); void PushStackTop(); void ClearExceptions(); + MidpointRounding GetRoundingControl(); } ///