From fa89dfc38996a4cf5eb5dbaa823e51e6f7d6da97 Mon Sep 17 00:00:00 2001 From: "Eric P. Nusbaum" Date: Sun, 9 Jul 2023 16:10:53 -0400 Subject: [PATCH 1/4] Update RNG to use Borland C++ RNG Routine - Update `srand()` to set seed in memory - Change `rand()` from using internal C# RNG to ported Borland C++ routine --- .../ExportedModules/Majorbbs/rand_Tests.cs | 36 +++++++++++++++++++ .../ExportedModules/Majorbbs/srand_Tests.cs | 24 +++++++++++++ .../HostProcess/ExportedModules/Majorbbs.cs | 33 +++++++++++++---- 3 files changed, 86 insertions(+), 7 deletions(-) create mode 100644 MBBSEmu.Tests/ExportedModules/Majorbbs/rand_Tests.cs create mode 100644 MBBSEmu.Tests/ExportedModules/Majorbbs/srand_Tests.cs diff --git a/MBBSEmu.Tests/ExportedModules/Majorbbs/rand_Tests.cs b/MBBSEmu.Tests/ExportedModules/Majorbbs/rand_Tests.cs new file mode 100644 index 00000000..3ca220e1 --- /dev/null +++ b/MBBSEmu.Tests/ExportedModules/Majorbbs/rand_Tests.cs @@ -0,0 +1,36 @@ +using System.Collections.Generic; +using Xunit; + +namespace MBBSEmu.Tests.ExportedModules.Majorbbs +{ + /// + /// Tests verified against results from Borland C++ 4.5 for DOS + /// + public class rand_Tests : ExportedModuleTestBase + { + private const int RAND_ORDINAL = 486; + + [Theory] + [InlineData(1234, 1356)] + [InlineData(2236414843, 29133)] + [InlineData(4056779384, 21998)] + [InlineData(3589159641, 27018)] + [InlineData(1770671342, 30398)] + [InlineData(4139675975, 13799)] + [InlineData(904336820, 26300)] + [InlineData(3871102533, 16520)] + [InlineData(3230196298, 26957)] + [InlineData(1766679891, 21523)] + public void srandTest(uint seed, ushort expectedRandom) + { + Reset(); + + mbbsEmuMemoryCore.SetDWord("RANDSEED", seed); + + ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, RAND_ORDINAL, new List()); + + //Verify Results + Assert.Equal(expectedRandom, mbbsEmuCpuRegisters.AX); + } + } +} diff --git a/MBBSEmu.Tests/ExportedModules/Majorbbs/srand_Tests.cs b/MBBSEmu.Tests/ExportedModules/Majorbbs/srand_Tests.cs new file mode 100644 index 00000000..1ff4d5b4 --- /dev/null +++ b/MBBSEmu.Tests/ExportedModules/Majorbbs/srand_Tests.cs @@ -0,0 +1,24 @@ +using System.Collections.Generic; +using Xunit; + +namespace MBBSEmu.Tests.ExportedModules.Majorbbs +{ + public class srand_Tests : ExportedModuleTestBase + { + private const int SRAND_ORDINAL = 561; + + [Theory] + [InlineData(0)] + [InlineData(1)] + [InlineData(ushort.MaxValue)] + public void srandTest(ushort seed) + { + Reset(); + + ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, SRAND_ORDINAL, new List { seed }); + + //Verify Results + Assert.Equal(seed, mbbsEmuMemoryCore.GetDWord("RANDSEED")); + } + } +} diff --git a/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs b/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs index 95516119..0ba59720 100644 --- a/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs +++ b/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs @@ -229,7 +229,7 @@ public Majorbbs(IClock clock, ILogger logger, AppSettings configuration, IFileUt Module.Memory.AllocateVariable("TXTVARS", TextvarStruct.Size * MaxTextVariables, true); //Up to 64 Text Variables per Module Module.Memory.SetArray("TXTVARS", new TextvarStruct("SYSTEM", new FarPtr(Segment, 9000)).Data); //Set 1st var as SYSTEM variable reference with special pointer Module.Memory.AllocateVariable("HDLCON", FarPtr.Size); //Handles the connection for the current User - + Module.Memory.AllocateVariable("RANDSEED", sizeof(uint)); //Seed value for Borland C++ Pseudo-Random Number Generator _tfsState = Module.Memory.AllocateVariable("TFSTATE", sizeof(ushort)); _tfspst = Module.Memory.AllocateVariable("TFSPST", FarPtr.Size); @@ -585,7 +585,6 @@ public ReadOnlySpan Invoke(ushort ordinal, bool offsetsOnly = false) switch (ordinal) { //Ignored Ordinals - case 561: //srand() handled internally case 614: //unfrez -- unlocks video memory, ignored case 174: //DSAIRP case 189: //ENAIRP @@ -1363,6 +1362,9 @@ public ReadOnlySpan Invoke(ushort ordinal, bool offsetsOnly = false) case 129: cncuid(); break; + case 561: + srand(); + break; default: _logger.Error($"Unknown Exported Function Ordinal in MAJORBBS: {ordinal}:{Ordinals.MAJORBBS[ordinal]}"); throw new ArgumentOutOfRangeException($"Unknown Exported Function Ordinal in MAJORBBS: {ordinal}:{Ordinals.MAJORBBS[ordinal]}"); @@ -2344,12 +2346,14 @@ private void hasmkey() /// private void rand() { - var randomValue = _random.Next(1, short.MaxValue); + uint multiplier = 0x15A4E35; + var seed = Module.Memory.GetDWord("RANDSEED"); + + var newSeed = (seed * multiplier) + 1; -#if DEBUG - //_logger.Debug($"Generated random number {randomValue} and saved it to AX"); -#endif - Registers.AX = (ushort)randomValue; + Module.Memory.SetDWord("RANDSEED", newSeed); + + Registers.AX = (ushort)((newSeed >> 16) & 0x7FFF); } /// @@ -8334,5 +8338,20 @@ private ushort setINPLEN(FarPtr input) /// Returns Pointer to the HDLCON Routine /// private ReadOnlySpan hdlcon => Module.Memory.GetVariablePointer("HDLCON").Data; + + /// + /// Sets the Seed for the Pseudo-Random Number Generator + /// + /// While the seed stored in memory is a 32-bit long, only the lower 16-bits are set + /// via the srand() method. The high 16-bits are always set to zero. + /// + /// Signature: void srabd(int seed) + /// + private void srand() + { + var randomSeed = GetParameter(0); + + Module.Memory.SetDWord("RANDSEED", randomSeed); + } } } From 3669747760e5cac1e67acdc4d7993bdd9fd50b92 Mon Sep 17 00:00:00 2001 From: "Eric P. Nusbaum" Date: Sun, 9 Jul 2023 16:19:22 -0400 Subject: [PATCH 2/4] Enhanced Unit Tests - Enhanced Unit Tests to ensure proper new SEED value is saved - Removed older tests for `rand()` --- .../ExportedModules/Majorbbs/rand_Tests.cs | 23 ++++++++++--------- .../ExportedModules/Majorbbs/random_Tests.cs | 11 --------- 2 files changed, 12 insertions(+), 22 deletions(-) diff --git a/MBBSEmu.Tests/ExportedModules/Majorbbs/rand_Tests.cs b/MBBSEmu.Tests/ExportedModules/Majorbbs/rand_Tests.cs index 3ca220e1..b599e3b1 100644 --- a/MBBSEmu.Tests/ExportedModules/Majorbbs/rand_Tests.cs +++ b/MBBSEmu.Tests/ExportedModules/Majorbbs/rand_Tests.cs @@ -11,17 +11,17 @@ public class rand_Tests : ExportedModuleTestBase private const int RAND_ORDINAL = 486; [Theory] - [InlineData(1234, 1356)] - [InlineData(2236414843, 29133)] - [InlineData(4056779384, 21998)] - [InlineData(3589159641, 27018)] - [InlineData(1770671342, 30398)] - [InlineData(4139675975, 13799)] - [InlineData(904336820, 26300)] - [InlineData(3871102533, 16520)] - [InlineData(3230196298, 26957)] - [InlineData(1766679891, 21523)] - public void srandTest(uint seed, ushort expectedRandom) + [InlineData(1234, 1356, 2236414843)] + [InlineData(2236414843, 29133, 4056779384)] + [InlineData(4056779384, 21998, 3589159641)] + [InlineData(3589159641, 27018, 1770671342)] + [InlineData(1770671342, 30398, 4139675975)] + [InlineData(4139675975, 13799, 904336820)] + [InlineData(904336820, 26300, 3871102533)] + [InlineData(3871102533, 16520, 3230196298)] + [InlineData(3230196298, 26957, 1766679891)] + [InlineData(1766679891, 21523, 1410548784)] + public void srandTest(uint seed, ushort expectedRandom, uint newSeed) { Reset(); @@ -31,6 +31,7 @@ public void srandTest(uint seed, ushort expectedRandom) //Verify Results Assert.Equal(expectedRandom, mbbsEmuCpuRegisters.AX); + Assert.Equal(newSeed, mbbsEmuMemoryCore.GetDWord("RANDSEED")); } } } diff --git a/MBBSEmu.Tests/ExportedModules/Majorbbs/random_Tests.cs b/MBBSEmu.Tests/ExportedModules/Majorbbs/random_Tests.cs index 63b73a5e..75b1000a 100644 --- a/MBBSEmu.Tests/ExportedModules/Majorbbs/random_Tests.cs +++ b/MBBSEmu.Tests/ExportedModules/Majorbbs/random_Tests.cs @@ -69,16 +69,5 @@ public void lngrndTest_invalid() //Verify Results Assert.Equal(mbbsEmuCpuRegisters.GetLong(), expectedValue); } - - [Fact] - public void randTest() - { - Reset(); - - ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment,RAND_ORDINAL, new List()); - - //Verify Results - Assert.InRange(mbbsEmuCpuRegisters.AX, 1, 32767); - } } } From b1939868eb400bc9f85bc24453a9b168f5efd7b9 Mon Sep 17 00:00:00 2001 From: "Eric P. Nusbaum" Date: Sun, 9 Jul 2023 16:24:10 -0400 Subject: [PATCH 3/4] Init RNG Seed with a random value on startup --- MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs | 1 + 1 file changed, 1 insertion(+) diff --git a/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs b/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs index 0ba59720..b60e3260 100644 --- a/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs +++ b/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs @@ -230,6 +230,7 @@ public Majorbbs(IClock clock, ILogger logger, AppSettings configuration, IFileUt Module.Memory.SetArray("TXTVARS", new TextvarStruct("SYSTEM", new FarPtr(Segment, 9000)).Data); //Set 1st var as SYSTEM variable reference with special pointer Module.Memory.AllocateVariable("HDLCON", FarPtr.Size); //Handles the connection for the current User Module.Memory.AllocateVariable("RANDSEED", sizeof(uint)); //Seed value for Borland C++ Pseudo-Random Number Generator + Module.Memory.SetDWord("RANDSEED", (uint)_random.Next(1, ushort.MaxValue)); //Seed RNG with a random value _tfsState = Module.Memory.AllocateVariable("TFSTATE", sizeof(ushort)); _tfspst = Module.Memory.AllocateVariable("TFSPST", FarPtr.Size); From 12736f481d63cefcafff679a0b72624e1c07a7d6 Mon Sep 17 00:00:00 2001 From: "Eric P. Nusbaum" Date: Sat, 15 Jul 2023 18:28:40 -0400 Subject: [PATCH 4/4] Fix Typo --- MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs b/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs index b60e3260..8cb7dbf9 100644 --- a/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs +++ b/MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs @@ -8346,7 +8346,7 @@ private ushort setINPLEN(FarPtr input) /// While the seed stored in memory is a 32-bit long, only the lower 16-bits are set /// via the srand() method. The high 16-bits are always set to zero. /// - /// Signature: void srabd(int seed) + /// Signature: void srand(int seed) /// private void srand() {