Skip to content

Commit

Permalink
Merge pull request #629 from mbbsemu/variable-ordinals
Browse files Browse the repository at this point in the history
Variable Ordinals
  • Loading branch information
paladine authored Aug 9, 2024
2 parents 75a89ef + 967d594 commit adc97ac
Show file tree
Hide file tree
Showing 5 changed files with 127 additions and 16 deletions.
3 changes: 2 additions & 1 deletion MBBSEmu.Tests/ExportedModules/Majorbbs/nctime_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -31,13 +31,14 @@ public void nctime_Test(int hour, int minutes, int seconds, string expectedTime,

//Execute Test
ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, NCTIME_ORDINAL, new List<ushort> { (ushort) packedTime });
var resultPointer = mbbsEmuCpuRegisters.GetPointer();

//Put Garbage After it to ensure the null terminator catches
var garbagePointer = mbbsEmuMemoryCore.AllocateVariable("GARBAGE", 10);
mbbsEmuMemoryCore.SetArray(garbagePointer, Encoding.ASCII.GetBytes(new string('C', 10)));

//Verify Results
Assert.Equal(expectedTime, Encoding.ASCII.GetString(mbbsEmuMemoryCore.GetString("NCTIME")));
Assert.Equal(expectedTime, Encoding.ASCII.GetString(mbbsEmuMemoryCore.GetString(resultPointer)));
}
}
}
66 changes: 66 additions & 0 deletions MBBSEmu.Tests/ExportedModules/Majorbbs/rawmsg_Tests.cs
Original file line number Diff line number Diff line change
@@ -0,0 +1,66 @@
using MBBSEmu.Memory;
using MBBSEmu.Module;
using System.Collections.Generic;
using System.Text;
using Xunit;

namespace MBBSEmu.Tests.ExportedModules.Majorbbs
{
public class rawmsg_Tests : ExportedModuleTestBase
{
private const int RAWMSG_ORDINAL = 487;

[Theory]
[InlineData(0, "Normal")]
[InlineData(1, "")]
[InlineData(2, "123456")]
[InlineData(3, "--==---")]
[InlineData(4, "!@)#!*$")]
public void rawmsg_Test(ushort ordinal, string msgValue)
{
//Reset State
Reset();

//Build a Test Dictionary containing all incorrect values
var incorrectValues = new Dictionary<int, byte[]>
{
{ 0, "INCORRECT"u8.ToArray() },
{ 1, "INCORRECT"u8.ToArray() },
{ 2, "INCORRECT"u8.ToArray() },
{ 3, "INCORRECT"u8.ToArray() },
{ 4, "INCORRECT"u8.ToArray() },
{ 5, "INCORRECT"u8.ToArray() },
{ 6, "INCORRECT"u8.ToArray() },
{ 7, "INCORRECT"u8.ToArray() },
{ 8, "INCORRECT"u8.ToArray() },
{ 9, "INCORRECT"u8.ToArray() },
{ 10, "INCORRECT"u8.ToArray() },
{ 11, "INCORRECT"u8.ToArray() },
{ 12, "INCORRECT"u8.ToArray() },
{ 13, "INCORRECT"u8.ToArray() },
{ 14, "INCORRECT"u8.ToArray() },
{ 15, "INCORRECT"u8.ToArray() },
{ 16, "INCORRECT"u8.ToArray() },
{ 17, "INCORRECT"u8.ToArray() },
{ 18, "INCORRECT"u8.ToArray() },
{ 19, "INCORRECT"u8.ToArray() },
{ 20, "INCORRECT"u8.ToArray() }
};

//Set Input Ordinal In Dictionary to Correct Value
incorrectValues[ordinal] = Encoding.ASCII.GetBytes(msgValue);

//Set Argument Values to be Passed In
var mcvPointer = (ushort)majorbbs.McvPointerDictionary.Allocate(new McvFile("TEST.MCV",
incorrectValues));

mbbsEmuMemoryCore.SetPointer("CURRENT-MCV", new FarPtr(0xFFFF, mcvPointer));

//Execute Test
ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, RAWMSG_ORDINAL, new List<ushort> { ordinal });

//Verify Results
Assert.Equal(msgValue, Encoding.ASCII.GetString(mbbsEmuMemoryCore.GetString(mbbsEmuCpuRegisters.DX, mbbsEmuCpuRegisters.AX, true)));
}
}
}
1 change: 1 addition & 0 deletions MBBSEmu.Tests/ExportedModules/Majorbbs/stpans_Tests.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,7 @@ public void STPANS_Test(string inputString, string expectedString)

//Execute Test
ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, STPANS_ORDINAL, new List<FarPtr> { stringPointer });
var resultPointer = mbbsEmuCpuRegisters.GetPointer();

//Verify Results
Assert.Equal(expectedString,
Expand Down
46 changes: 44 additions & 2 deletions MBBSEmu/HostProcess/ExportedModules/ExportedModuleBase.cs
Original file line number Diff line number Diff line change
Expand Up @@ -56,8 +56,7 @@ public LeadingNumberFromStringResult()
/// it lives in.
/// </summary>
private protected PointerDictionary<SessionBase> ChannelDictionary;



/// <summary>
/// Pointers to files opened using FOPEN
/// </summary>
Expand All @@ -84,6 +83,13 @@ public LeadingNumberFromStringResult()
/// </summary>
private protected ushort ChannelNumber;

/// <summary>
/// Dictionary that will hold the ordinals used for local variables. This allows methods that
/// return pointers to result values in memory (Strings, etc.) to have unique addresses so that
/// results aren't overwritten in the same execution cycle.
/// </summary>
private protected readonly Dictionary<string, int> LocalVariableOrdinalDictionary = new();

//Constants
private protected static readonly char[] SSCANF_SEPARATORS = { ' ', ',', '\r', '\n', '\0', ':' };
private protected static readonly char[] PRINTF_SPECIFIERS = { 'c', 'd', 's', 'e', 'E', 'f', 'g', 'G', 'o', 'x', 'X', 'u', 'i', 'P', 'N', '%' };
Expand Down Expand Up @@ -122,6 +128,42 @@ private protected ExportedModuleBase(IClock clock, IMessageLogger logger, AppSet
McvPointerDictionary = new PointerDictionary<McvFile>();
}

/// <summary>
/// Resets Local Variable Ordinals all back to Zero
/// </summary>
private protected void ResetLocalVariableOrdinals()
{
foreach (var k in LocalVariableOrdinalDictionary.Keys)
{
LocalVariableOrdinalDictionary[k] = 0;
}
}

/// <summary>
/// Retrieves a Local Variable Ordinal based on the ordinal name
///
/// This is tracked in a dictionary with each value incremented on each get
/// </summary>
/// <param name="variableName"></param>
/// <returns></returns>
private protected int GetLocalVariableOrdinal(string variableName)
{
if (!LocalVariableOrdinalDictionary.ContainsKey(variableName))
LocalVariableOrdinalDictionary[variableName] = 0;

return LocalVariableOrdinalDictionary[variableName]++;
}

/// <summary>
/// Creates a distinct variable name string to use internally when allocating variables
/// to store return results for methods. This gives each method call a distinct memory
/// pointer to be used to store the results based on the ordinal.
/// </summary>
/// <param name="variableName"></param>
/// <returns>String containing the variable name and ordinal (ie: "MyVar1")</returns>
private protected string GetLocalVariableName(string variableName) =>
$"{variableName}{GetLocalVariableOrdinal(variableName)}";

/// <summary>
/// Sets the parameter by ordinal passed into the routine
/// </summary>
Expand Down
27 changes: 14 additions & 13 deletions MBBSEmu/HostProcess/ExportedModules/Majorbbs.cs
Original file line number Diff line number Diff line change
Expand Up @@ -308,6 +308,7 @@ public void SetRegisters(ICpuRegisters registers)
/// <param name="numberOfModules"></param>
public void SetState(ushort channelNumber)
{
ResetLocalVariableOrdinals();
ChannelNumber = channelNumber;

_previousMcvFile.Clear();
Expand Down Expand Up @@ -1897,7 +1898,7 @@ private void l2as()
var outputValue = $"{highByte << 16 | lowByte}\0";

//Pre-allocate space for the maximum number of characters for a ulong
var variablePointer = Module.Memory.GetOrAllocateVariablePointer("L2AS", 0xFF);
var variablePointer = Module.Memory.GetOrAllocateVariablePointer(GetLocalVariableName("L2AS"), 0x20); //32 Byte Buffer

Module.Memory.SetArray(variablePointer, Encoding.Default.GetBytes(outputValue));

Expand Down Expand Up @@ -2379,7 +2380,7 @@ private void ncdate()
var month = (packedDate >> 5) & 0x000F;
var day = packedDate & 0x001F;
var outputDate = $"{month:D2}/{day:D2}/{year % 100}\0";
var variablePointer = Module.Memory.GetOrAllocateVariablePointer("NCDATE", (ushort)outputDate.Length);
var variablePointer = Module.Memory.GetOrAllocateVariablePointer(GetLocalVariableName("NCDATE"), (ushort)outputDate.Length);

Module.Memory.SetArray(variablePointer.Segment, variablePointer.Offset,
Encoding.Default.GetBytes(outputDate));
Expand Down Expand Up @@ -2885,7 +2886,7 @@ private void scnmdf()
var lineprefix = Encoding.ASCII.GetString(lineprefixBytes);

//Setup Host Memory Variables Pointer
var variablePointer = Module.Memory.GetOrAllocateVariablePointer("SCNMDF", 0xFF);
var variablePointer = Module.Memory.GetOrAllocateVariablePointer(GetLocalVariableName("SCNMDF"), 0xFF);

var recordFound = false;
foreach (var line in File.ReadAllLines(Path.Combine(Module.ModulePath, mdfName)))
Expand All @@ -2894,7 +2895,7 @@ private void scnmdf()
{
var result = Encoding.ASCII.GetBytes(line.Split(':')[1] + "\0");

if (result.Length > 256)
if (result.Length > 0xFF)
throw new OverflowException("SCNMDF result is > 256 bytes");

Module.Memory.SetArray(variablePointer.Segment, variablePointer.Offset, result);
Expand Down Expand Up @@ -3789,7 +3790,7 @@ private void getmsg()
{
var msgnum = GetParameter(0);

var variablePointer = Module.Memory.GetOrAllocateVariablePointer("GETMSG", 0x1000);
var variablePointer = Module.Memory.GetOrAllocateVariablePointer(GetLocalVariableName("GETMSG"), 0x1000);
var outputValue = McvPointerDictionary[_currentMcvFile.Offset].GetString(msgnum);

if (outputValue.Length > 0x1000)
Expand Down Expand Up @@ -4238,7 +4239,7 @@ private void nctime()
unpackedMinutes, unpackedSeconds);

var timeString = $"{unpackedTime.ToString("HH:mm:ss")}\0";
var variablePointer = Module.Memory.GetOrAllocateVariablePointer("NCTIME", (ushort)timeString.Length);
var variablePointer = Module.Memory.GetOrAllocateVariablePointer(GetLocalVariableName("NCTIME"), (ushort)timeString.Length);

Module.Memory.SetArray(variablePointer.Segment, variablePointer.Offset,
Encoding.Default.GetBytes(timeString));
Expand Down Expand Up @@ -5043,7 +5044,7 @@ private void stpans()
var stringToStripPointer = GetParameterPointer(0);
var inputString = Module.Memory.GetString(stringToStripPointer);

Module.Memory.GetOrAllocateVariablePointer("STPANS", 1920); //Max Screen Size of 80x24
var resultPointer = Module.Memory.GetOrAllocateVariablePointer(GetLocalVariableName("STPANS"), 1920); //Max Screen Size of 80x24

if (inputString.Length > 1920)
{
Expand Down Expand Up @@ -5099,13 +5100,13 @@ private void stpans()
}
}

Module.Memory.SetArray("STPANS", Encoding.ASCII.GetBytes(cleanedStringBuilder.ToString()));
Module.Memory.SetArray(resultPointer, Encoding.ASCII.GetBytes(cleanedStringBuilder.ToString()));

#if DEBUG
_logger.Debug($"({Module.ModuleIdentifier}) Ignoring, not stripping ANSI");
#endif

Registers.SetPointer(Module.Memory.GetVariablePointer("STPANS"));
Registers.SetPointer(resultPointer);
}

/// <summary>
Expand Down Expand Up @@ -5286,7 +5287,7 @@ private void rawmsg()
{
var msgnum = GetParameter(0);

var variablePointer = Module.Memory.GetOrAllocateVariablePointer("RAWMSG", 0x1000);
var variablePointer = Module.Memory.GetOrAllocateVariablePointer(GetLocalVariableName("RAWMSG"), 0x1000);
var outputValue = McvPointerDictionary[_currentMcvFile.Offset].GetString(msgnum);

if (outputValue.Length > 0x1000)
Expand Down Expand Up @@ -6331,7 +6332,7 @@ private void msgscan()
return;
}

var msgScanResultPointer = Module.Memory.GetOrAllocateVariablePointer("MSGSCAN", 0x1000);
var msgScanResultPointer = Module.Memory.GetOrAllocateVariablePointer(GetLocalVariableName("MSGSCAN"), 0x1000);

Module.Memory.SetArray(msgScanResultPointer, new byte[0x1000]); //Zero it out
Module.Memory.SetArray(msgScanResultPointer, msgVariableValue); //Write
Expand Down Expand Up @@ -6417,7 +6418,7 @@ private void getenv()
{
var name = GetParameterFilename(0);

var resultPointer = Module.Memory.GetOrAllocateVariablePointer("GETENV", 0xFF);
var resultPointer = Module.Memory.GetOrAllocateVariablePointer(GetLocalVariableName("GETENV"), 0xFF);

switch (name)
{
Expand Down Expand Up @@ -7519,7 +7520,7 @@ private void ul2as()

var outputValue = $"{(uint)(highByte << 16 | lowByte)}\0";

var resultPointer = Module.Memory.GetOrAllocateVariablePointer("UL2AS", 0xF);
var resultPointer = Module.Memory.GetOrAllocateVariablePointer(GetLocalVariableName("UL2AS"), 0x20); //32 Byte Buffer

Module.Memory.SetArray(resultPointer, Encoding.Default.GetBytes(outputValue));

Expand Down

0 comments on commit adc97ac

Please sign in to comment.