Esempio n. 1
0
        public ReadOnlySpan <byte> Invoke(ushort ordinal, bool offsetsOnly = false)
        {
            switch (ordinal)
            {
            case 248:
                return(_txtlen);
            }

            if (offsetsOnly)
            {
                var methodPointer = new IntPtr16(0xFFFC, ordinal);
#if DEBUG
                //_logger.Info($"Returning Method Offset {methodPointer.Segment:X4}:{methodPointer.Offset:X4}");
#endif
                return(methodPointer.Data);
            }

            switch (ordinal)
            {
            case 30:
                oldsend();
                break;

            case 123:
                simpsnd();
                break;

            default:
                throw new ArgumentOutOfRangeException($"Unknown Exported Function Ordinal in GALME: {ordinal}");
            }

            return(null);
        }
Esempio n. 2
0
        public ReadOnlySpan <byte> Invoke(ushort ordinal, bool offsetsOnly = false)
        {
            if (offsetsOnly)
            {
                var methodPointer = new IntPtr16(0xFFFC, ordinal);
#if DEBUG
                //_logger.Info($"Returning Method Offset {methodPointer.Segment:X4}:{methodPointer.Offset:X4}");
#endif
                return(methodPointer.Data);
            }

            switch (ordinal)
            {
            case 49:
                DosRealIntr();
                break;

            case 16:
                DosAllocRealSeg();
                break;

            default:
                throw new ArgumentOutOfRangeException($"Unknown Exported Function Ordinal in PHAPI: {ordinal}");
            }

            return(null);
        }
Esempio n. 3
0
        protected short fclose(IntPtr16 filep)
        {
            ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, FCLOSE_ORDINAL, new List <IntPtr16> {
                filep
            });

            return((short)mbbsEmuCpuRegisters.AX);
        }
Esempio n. 4
0
        private string GetFileFromFndblk(IntPtr16 fndblkPointer)
        {
            var fbs = new FndblkStruct(mbbsEmuMemoryCore.GetArray(fndblkPointer, FndblkStruct.StructSize));

            Assert.Equal(9, fbs.Size);
            Assert.Equal(0, (byte)fbs.Attributes & (byte)~FndblkStruct.AttributeFlags.Archive);
            Assert.True(Math.Abs(ticksNow - fbs.DateTime.Ticks) <= FIVE_SECOND_TICKS);

            return(fbs.Name);
        }
Esempio n. 5
0
        /// <summary>
        ///     Begins emulated x86 Execution at the given entry point
        /// </summary>
        /// <param name="entryPoint">Pointer to segment:offset emulation is to begin at</param>
        /// <param name="channelNumber">Channel Number code is being executed for (used to Set State of Exported Modules)</param>
        /// <param name="simulateCallFar">Simulating a CALL FAR pushes CS:IP to the stack and sets BP=SP</param>
        /// <param name="bypassState">Some method pointers don't require the Exported Module to have a state set</param>
        /// <param name="initialStackValues">Values to be on the stack at the start of emulation (arguments passed in)</param>
        /// <param name="initialStackPointer">Initial SP offset (used to shift SP as to not overlap memory space on nested execution)</param>
        /// <returns></returns>
        public CpuRegisters Execute(IntPtr16 entryPoint, ushort channelNumber, bool simulateCallFar = false, bool bypassState = false, Queue <ushort> initialStackValues = null, ushort initialStackPointer = CpuCore.STACK_BASE)
        {
            //Reset Registers to Startup State for the CPU
            ModuleCpu.Reset(initialStackPointer);

            //Reset Registers
            ModuleCpuRegisters.CS = entryPoint.Segment;
            ModuleCpuRegisters.IP = entryPoint.Offset;

            //Any parameters that need to be passed into the function
            if (initialStackValues != null)
            {
                //Push Parameters
                while (initialStackValues.TryDequeue(out var valueToPush))
                {
                    ModuleCpu.Push(valueToPush);
                }
            }

            //Simulating a CALL FAR
            if (simulateCallFar)
            {
                //Set stack to simulate CALL FAR
                ModuleCpuRegisters.BP = ModuleCpuRegisters.SP;
                ModuleCpu.Push(ushort.MaxValue); //CS
                ModuleCpu.Push(ushort.MaxValue); //IP
            }

            foreach (var em in ExportedModuleDictionary.Values)
            {
                //Things like TEXT_VARIABLES don't need us to re-setup the state, the Exported Functions are already setup properly
                if (!bypassState)
                {
                    em.SetState(channelNumber);
                }

                em.SetRegisters(ModuleCpuRegisters);
            }

            //Run until complete
            while (!ModuleCpuRegisters.Halt)
            {
                ModuleCpu.Tick();
            }

            //Update Session State
            if (!bypassState && channelNumber != ushort.MaxValue && initialStackValues == null)
            {
                ExportedModuleDictionary[Majorbbs.Segment].UpdateSession(channelNumber);
            }

            //Return Registers
            return(ModuleCpuRegisters);
        }
Esempio n. 6
0
        protected ushort fwrite(IntPtr16 destPtr, ushort size, ushort count, IntPtr16 filep)
        {
            ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, FWRITE_ORDINAL, new List <ushort>
            {
                destPtr.Offset,
                destPtr.Segment,
                size,
                count,
                filep.Offset,
                filep.Segment,
            });

            return(mbbsEmuCpuRegisters.AX);
        }
Esempio n. 7
0
        /// <summary>
        ///     Tells the module to begin x86 execution at the specified Entry Point
        /// </summary>
        /// <param name="entryPoint">Entry Point where execution will begin</param>
        /// <param name="channelNumber">Channel Number calling for execution</param>
        /// <param name="simulateCallFar">Are we simulating a CALL FAR? This is usually when we're calling a local function pointer</param>
        /// <param name="bypassSetState">Should we bypass setting a startup state? This is true for execution of things like Text Variable processing</param>
        /// <param name="initialStackValues">Initial Stack Values to be pushed to the stack prior to executing (simulating parameters on a method call)</param>
        /// <param name="initialStackPointer">
        ///     Initial Stack Pointer Value. Because EU's share the same memory space, including stack space, we need to sometimes need to manually decrement the
        ///     stack pointer enough to where the stack on the nested call won't overlap with the stack on the parent caller.
        /// </param>
        /// <returns></returns>
        public CpuRegisters Execute(IntPtr16 entryPoint, ushort channelNumber, bool simulateCallFar = false, bool bypassSetState = false,
                                    Queue <ushort> initialStackValues = null, ushort initialStackPointer = CpuCore.STACK_BASE)
        {
            //Try to dequeue an execution unit, if one doesn't exist, create a new one
            if (!ExecutionUnits.TryDequeue(out var executionUnit))
            {
                _logger.Warn($"{ModuleIdentifier} exhausted execution Units, creating additional");
                executionUnit = new ExecutionUnit(Memory, ExportedModuleDictionary);
            }

            var resultRegisters = executionUnit.Execute(entryPoint, channelNumber, simulateCallFar, bypassSetState, initialStackValues, initialStackPointer);

            ExecutionUnits.Enqueue(executionUnit);
            return(resultRegisters);
        }
Esempio n. 8
0
        public ReadOnlySpan <byte> Invoke(ushort ordinal, bool offsetsOnly = false)
        {
            if (offsetsOnly)
            {
                var methodPointer = new IntPtr16(0xFFFC, ordinal);
#if DEBUG
                //_logger.Info($"Returning Method Offset {methodPointer.Segment:X4}:{methodPointer.Offset:X4}");
#endif
                return(methodPointer.ToSpan());
            }

            switch (ordinal)
            {
            default:
                throw new ArgumentOutOfRangeException($"Unknown Exported Function Ordinal in GALMSG: {ordinal}");
            }
        }
Esempio n. 9
0
        public ReadOnlySpan <byte> Invoke(ushort ordinal, bool onlyProperties = false)
        {
            switch (ordinal)
            {
            case 89:
                return(dossetvec);
            }

            if (onlyProperties)
            {
                var methodPointer = new IntPtr16(Segment, ordinal);
#if DEBUG
                //_logger.Info($"Returning Method Offset {methodPointer.Segment:X4}:{methodPointer.Offset:X4}");
#endif
                return(methodPointer.ToSpan());
            }

            switch (ordinal)
            {
            case 34:
                DosAllocSeg();
                break;

            case 44:
                DosLoadModule();
                break;

            case 45:
                DosGetProcAddr();
                break;

            case 47:
                DosGetModHandle();
                break;

            case 48:
                DosGetModName();
                break;

            default:
                throw new ArgumentOutOfRangeException($"Unknown Exported Function Ordinal in DOSCALLS: {ordinal}");
            }

            return(null);
        }
Esempio n. 10
0
        protected ushort f_printf(IntPtr16 filep, string formatString, params object[] values)
        {
            var fprintfParameters = new List <ushort> {
                filep.Offset, filep.Segment
            };

            //Add Formatted String
            var inputStingParameterPointer = mbbsEmuMemoryCore.AllocateVariable(Guid.NewGuid().ToString(), (ushort)(formatString.Length + 1));

            mbbsEmuMemoryCore.SetArray(inputStingParameterPointer, Encoding.ASCII.GetBytes(formatString));
            fprintfParameters.Add(inputStingParameterPointer.Offset);
            fprintfParameters.Add(inputStingParameterPointer.Segment);

            //Add Parameters
            var parameterList = GenerateParameters(values);

            fprintfParameters.AddRange(parameterList);

            ExecuteApiTest(HostProcess.ExportedModules.Majorbbs.Segment, FPRINTF_ORDINAL, fprintfParameters);

            return(mbbsEmuCpuRegisters.AX);
        }
Esempio n. 11
0
        /// <summary>
        ///     Printf Parsing and Encoding
        /// </summary>
        /// <param name="s"></param>
        /// <param name="stringToParse"></param>
        /// <param name="startingParameterOrdinal"></param>
        /// <returns></returns>
        private protected ReadOnlySpan <byte> FormatPrintf(ReadOnlySpan <byte> stringToParse, ushort startingParameterOrdinal, bool isVsPrintf = false)
        {
            using var msOutput = new MemoryStream(stringToParse.Length);
            var currentParameter = startingParameterOrdinal;

            var vsPrintfBase = new IntPtr16();

            if (isVsPrintf)
            {
                vsPrintfBase      = GetParameterPointer(currentParameter);
                currentParameter += 2;
            }

            stringToParse = ProcessEscapeCharacters(stringToParse);

            for (var i = 0; i < stringToParse.Length; i++)
            {
                //Handle escaped %% as a single % -- or if % is the last character in a string
                if (stringToParse[i] == '%')
                {
                    switch ((char)stringToParse[i + 1])
                    {
                    case '%':     //escaped %
                        msOutput.WriteByte((byte)'%');
                        i++;
                        continue;

                    case '\0':     //last character is an invalid single %, just print it and move on
                        msOutput.WriteByte((byte)'%');
                        msOutput.WriteByte(0);
                        i++;
                        continue;
                    }
                }

                //Found a Control Character
                if (stringToParse[i] == '%' && stringToParse[i + 1] != '%')
                {
                    using var msFormattedValue = new MemoryStream();
                    i++;

                    //Found a Lone % as the last character in a string, consider it just outputting the string submitted
                    if (stringToParse[i] == 0x0)
                    {
                        var parameterOffset  = GetParameter(currentParameter++);
                        var parameterSegment = GetParameter(currentParameter++);
                        if (Module.Memory.HasSegment(parameterSegment))
                        {
                            msOutput.Write(Module.Memory.GetString(parameterSegment, parameterOffset));
                        }
                        else
                        {
                            msOutput.Write(Encoding.ASCII.GetBytes("Invalid Pointer"));
                            _logger.Error($"Invalid Pointer: {parameterSegment:X4}:{parameterOffset:X4}");
                        }

                        continue;
                    }

                    //Process Flags
                    var stringFlags = EnumPrintfFlags.None;
                    while (InSpan(PRINTF_FLAGS, stringToParse.Slice(i, 1)))
                    {
                        switch ((char)stringToParse[i])
                        {
                        case '-':
                            stringFlags |= EnumPrintfFlags.LeftJustify;
                            break;

                        case '+':
                            stringFlags |= EnumPrintfFlags.Signed;
                            break;

                        case ' ':
                            stringFlags |= EnumPrintfFlags.Space;
                            break;

                        case '#':
                            stringFlags |= EnumPrintfFlags.DecimalOrHex;
                            break;

                        case '0':
                            stringFlags |= EnumPrintfFlags.LeftPadZero;
                            break;
                        }
                        i++;
                    }

                    //Process Width
                    var stringWidth      = 0;
                    var stringWidthValue = string.Empty;
                    while (InSpan(PRINTF_WIDTH, stringToParse.Slice(i, 1)))
                    {
                        switch ((char)stringToParse[i])
                        {
                        case '*':
                            stringWidth = -1;
                            break;

                        default:
                            stringWidthValue += (char)stringToParse[i];
                            break;
                        }
                        i++;
                    }
                    if (!string.IsNullOrEmpty(stringWidthValue))
                    {
                        stringWidth = int.Parse(stringWidthValue);
                    }

                    if (stringWidth == -1)
                    {
                        if (!isVsPrintf)
                        {
                            //printf
                            stringWidth = GetParameter(currentParameter++);
                        }
                        else
                        {
                            //vsprintf
                            stringWidth          = Module.Memory.GetWord(vsPrintfBase.Segment, vsPrintfBase.Offset);
                            vsPrintfBase.Offset += 2;
                        }
                    }

                    //Process Precision
                    var stringPrecision      = 0;
                    var stringPrecisionValue = string.Empty;
                    while (InSpan(PRINTF_PRECISION, stringToParse.Slice(i, 1)))
                    {
                        switch ((char)stringToParse[i])
                        {
                        case '.':
                            break;

                        case '*':
                            stringPrecision = -1;
                            break;

                        default:
                            stringPrecisionValue += (char)stringToParse[i];
                            break;
                        }
                        i++;
                    }
                    if (!string.IsNullOrEmpty(stringPrecisionValue))
                    {
                        stringPrecision = int.Parse(stringPrecisionValue);
                    }

                    if (stringPrecision == -1)
                    {
                        if (!isVsPrintf)
                        {
                            //printf
                            stringPrecision = GetParameter(currentParameter++);
                        }
                        else
                        {
                            //vsprintf
                            stringPrecision      = Module.Memory.GetWord(vsPrintfBase.Segment, vsPrintfBase.Offset);
                            vsPrintfBase.Offset += 2;
                        }
                    }

                    //Process Length
                    //TODO -- We'll process it but ignore it for now
                    var variableLength = 0;
                    while (InSpan(PRINTF_LENGTH, stringToParse.Slice(i, 1)))
                    {
                        switch (stringToParse[i])
                        {
                        case (byte)'l':
                            variableLength = 4;
                            break;

                        default:
                            throw new Exception("Unsupported printf Length Specified");
                        }

                        i++;
                    }

                    //Finally i should be at the specifier
                    if (!InSpan(PRINTF_SPECIFIERS, stringToParse.Slice(i, 1)))
                    {
                        _logger.Warn($"Invalid printf format: {Encoding.ASCII.GetString(stringToParse)}");
                        continue;
                    }

                    switch ((char)stringToParse[i])
                    {
                    //Character
                    case 'c':
                    {
                        byte charParameter;
                        if (isVsPrintf)
                        {
                            charParameter        = Module.Memory.GetByte(vsPrintfBase.Segment, vsPrintfBase.Offset);
                            vsPrintfBase.Offset += 2;
                        }
                        else
                        {
                            charParameter = (byte)GetParameter(currentParameter++);
                        }

                        msFormattedValue.WriteByte(charParameter);
                        break;
                    }

                    //String of characters
                    case 's':
                    {
                        ReadOnlySpan <byte> parameter;
                        if (isVsPrintf)
                        {
                            var stringPointer = Module.Memory.GetPointer(vsPrintfBase);
                            parameter            = Module.Memory.GetString(stringPointer);
                            vsPrintfBase.Offset += 4;
                        }
                        else
                        {
                            var parameterOffset  = GetParameter(currentParameter++);
                            var parameterSegment = GetParameter(currentParameter++);
                            if (Module.Memory.HasSegment(parameterSegment))
                            {
                                parameter = Module.Memory.GetString(parameterSegment, parameterOffset);
                            }
                            else
                            {
                                parameter = Encoding.ASCII.GetBytes("Invalid Pointer");
                                _logger.Error($"Invalid Pointer: {parameterSegment:X4}:{parameterOffset:X4}");
                            }
                        }

                        if (parameter[^ 1] == 0x0)
Esempio n. 12
0
        public ReadOnlySpan <byte> Invoke(ushort ordinal, bool offsetsOnly = false)
        {
            switch (ordinal)
            {
            case 72:
                return(bturno());

            case 65:
                return(ticker);
            }

            if (offsetsOnly)
            {
                var methodPointer = new IntPtr16(0xFFFE, ordinal);
#if DEBUG
                //_logger.Info($"Returning Method Offset {methodPointer.Segment:X4}:{methodPointer.Offset:X4}");
#endif
                return(methodPointer.ToSpan());
            }

            switch (ordinal)
            {
            case 36:
                btuoba();
                break;

            case 49:
                btutrg();
                break;

            case 21:
                btuinj();
                break;

            case 60:
                btuxnf();
                break;

            case 39:
                btupbc();
                break;

            case 87:
                btuica();
                break;

            case 6:
                btucli();
                break;

            case 4:
                btuchi();
                break;

            case 63:
                chious();
                break;

            case 83:
                btueba();
                break;

            case 19:
                btuibw();
                break;

            case 59:
                btuxmt();
                break;

            case 7:
                btuclo();
                break;

            case 30:
                btumil();
                break;

            case 3:
                btuche();
                break;

            case 5:
                btuclc();
                break;

            case 8:
                btucls();
                break;

            case 52:
                btutru();
                break;

            case 37:
                btuoes();
                break;

            case 11:
                btuech();
                break;

            case 53:
                btutsw();
                break;

            case 58:
                btuxmn();
                break;

            case 34:
                btumon2();
                break;

            case 32:
                btumks2();
                break;

            case 48:
                btusts();
                break;

            case 29:
                btumds2();
                break;

            case 41:
                bturst();
                break;

            case 40:
                btupmt();
                break;

            case 9:
                btucmd();
                break;

            case 56:
                btuxct();
                break;

            case 64:
                chiout();
                break;

            case 61:
                chiinj();
                break;

            case 15:
                btuhcr();
                break;

            case 44:
                btuscr();
                break;

            case 26:
                btulok();
                break;

            default:
                throw new ArgumentOutOfRangeException($"Unknown Exported Function Ordinal in GALGSBL: {ordinal}");
            }

            return(null);
        }
Esempio n. 13
0
        /// <summary>
        ///     Constructor for MbbsModule
        ///
        ///     Pass in an empty/blank moduleIdentifier for a Unit Test/Fake Module
        /// </summary>
        /// <param name="moduleIdentifier"></param>
        /// <param name="path"></param>
        /// <param name="memoryCore"></param>
        public MbbsModule(string moduleIdentifier, string path = "", MemoryCore memoryCore = null)
        {
            ModuleIdentifier = moduleIdentifier;

            //Sanitize and setup Path
            if (string.IsNullOrEmpty(path))
            {
                path = Directory.GetCurrentDirectory();
            }

            if (!path.EndsWith(Path.DirectorySeparatorChar))
            {
                path += Path.DirectorySeparatorChar;
            }

            ModulePath = path;

            //Verify MDF File Exists
            if (!string.IsNullOrEmpty(ModuleIdentifier) && !System.IO.File.Exists($"{ModulePath}{ModuleIdentifier}.MDF"))
            {
                throw new FileNotFoundException($"Unable to locate Module: {ModulePath}{ModuleIdentifier}.MDF");
            }

            Mdf  = !string.IsNullOrEmpty(ModuleIdentifier) ? new MdfFile($"{ModulePath}{ModuleIdentifier}.MDF") : MdfFile.createForTest();
            File = !string.IsNullOrEmpty(ModuleIdentifier) ? new NEFile($"{ModulePath}{Mdf.DLLFiles[0].Trim()}.DLL") : NEFile.createForTest();

            if (Mdf.MSGFiles.Count > 0)
            {
                Msgs = new List <MsgFile>(Mdf.MSGFiles.Count);
                foreach (var m in Mdf.MSGFiles)
                {
                    Msgs.Add(new MsgFile(ModulePath, m));
                }
            }

            //Set Initial Values
            EntryPoints              = new Dictionary <string, IntPtr16>();
            RtkickRoutines           = new PointerDictionary <RealTimeRoutine>();
            RtihdlrRoutines          = new PointerDictionary <RealTimeRoutine>();
            TaskRoutines             = new PointerDictionary <RealTimeRoutine>();
            TextVariables            = new Dictionary <string, IntPtr16>();
            ExecutionUnits           = new Queue <ExecutionUnit>(2);
            ExportedModuleDictionary = new Dictionary <ushort, IExportedModule>(4);
            GlobalCommandHandlers    = new List <IntPtr16>();
            Memory = memoryCore ?? new MemoryCore();

            //If it's a Test, setup a fake _INIT_
            if (string.IsNullOrEmpty(ModuleIdentifier))
            {
                EntryPoints["_INIT_"] = null;
                return;
            }

            //Setup _INIT_ Entrypoint
            IntPtr16 initEntryPointPointer;
            var      initResidentName = File.ResidentNameTable.FirstOrDefault(x => x.Name.StartsWith("_INIT__"));

            if (initResidentName == null)
            {
                //This only happens with MajorMUD -- I have no idea why it's a special little snowflake ¯\_(ツ)_/¯
                _logger.Warn("Unable to locate _INIT_ in Resident Name Table, checking Non-Resident Name Table...");

                var initNonResidentName = File.NonResidentNameTable.FirstOrDefault(x => x.Name.StartsWith("_INIT__"));

                if (initNonResidentName == null)
                {
                    throw new Exception("Unable to locate _INIT__ entry in Resident Name Table");
                }

                var initEntryPoint = File.EntryTable.First(x => x.Ordinal == initNonResidentName.IndexIntoEntryTable);
                initEntryPointPointer = new IntPtr16(initEntryPoint.SegmentNumber, initEntryPoint.Offset);
            }
            else
            {
                var initEntryPoint = File.EntryTable.First(x => x.Ordinal == initResidentName.IndexIntoEntryTable);
                initEntryPointPointer = new IntPtr16(initEntryPoint.SegmentNumber, initEntryPoint.Offset);
            }


            _logger.Info($"Located _INIT__: {initEntryPointPointer}");
            EntryPoints["_INIT_"] = initEntryPointPointer;
        }
Esempio n. 14
0
        /// <summary>
        ///     Patches Relocation information from each Code Segment Relocation Records into the Segment Byte Code
        ///
        ///     Because the compiler doesn't know the location in memory of the hosts Exported Modules (Imported when
        ///     viewed from the standpoint of the DLL), it saves the information to the Relocation Records for the
        ///     given Code Segment.
        ///
        ///     The x86 Emulator knows that any CALL FAR to a Segment >= 0xFF00 is an emulated Exported Module and properly
        ///     handles calling the correct Module using the Segment of the target, and the Ordinal of the call using the Offset.
        ///     A relocation record for a call to MAJORBBS->ATOL() would be patched as:
        ///
        ///     CALL FAR 0xFFFF:0x004D
        ///
        ///     Segment & Offset meaning:
        ///     0xFFFF == MAJORBBS
        ///     0x004D == 77, Ordinal for ATOL()
        /// </summary>
        /// <param name="module"></param>
        private void PatchRelocation(MbbsModule module)
        {
            //Declare Host Functions
            var majorbbsHostFunctions = GetFunctions(module, "MAJORBBS");
            var galsblHostFunctions   = GetFunctions(module, "GALGSBL");
            var doscallsHostFunctions = GetFunctions(module, "DOSCALLS");
            var galmeFunctions        = GetFunctions(module, "GALME");
            var phapiFunctions        = GetFunctions(module, "PHAPI");
            var galmsgFunctions       = GetFunctions(module, "GALMSG");

            foreach (var s in module.File.SegmentTable)
            {
                if (s.RelocationRecords == null || s.RelocationRecords.Count == 0)
                {
                    continue;
                }

                foreach (var relocationRecord in s.RelocationRecords.Values)
                {
                    //Ignored Relocation Record
                    if (relocationRecord.TargetTypeValueTuple == null)
                    {
                        continue;
                    }

                    switch (relocationRecord.TargetTypeValueTuple.Item1)
                    {
                    case EnumRecordsFlag.ImportOrdinalAdditive:
                    case EnumRecordsFlag.ImportOrdinal:
                    {
                        var nametableOrdinal = relocationRecord.TargetTypeValueTuple.Item2;
                        var functionOrdinal  = relocationRecord.TargetTypeValueTuple.Item3;

                        var relocationResult = module.File.ImportedNameTable[nametableOrdinal].Name switch
                        {
                            "MAJORBBS" => majorbbsHostFunctions.Invoke(functionOrdinal, true),
                            "GALGSBL" => galsblHostFunctions.Invoke(functionOrdinal, true),
                            "DOSCALLS" => doscallsHostFunctions.Invoke(functionOrdinal, true),
                            "GALME" => galmeFunctions.Invoke(functionOrdinal, true),
                            "PHAPI" => phapiFunctions.Invoke(functionOrdinal, true),
                            "GALMSG" => galmsgFunctions.Invoke(functionOrdinal, true),
                            _ => throw new Exception(
                                      $"Unknown or Unimplemented Imported Library: {module.File.ImportedNameTable[nametableOrdinal].Name}")
                        };

                        var relocationPointer = new IntPtr16(relocationResult);

                        //32-Bit Pointer
                        if (relocationRecord.SourceType == 3)
                        {
                            Array.Copy(relocationPointer.ToArray(), 0, s.Data, relocationRecord.Offset, 4);
                            continue;
                        }

                        //16-Bit Values
                        var result = relocationRecord.SourceType switch
                        {
                            //Offset
                            2 => relocationPointer.Segment,
                            5 => relocationPointer.Offset,
                            _ => throw new ArgumentOutOfRangeException(
                                      $"Unhandled Relocation Source Type: {relocationRecord.SourceType}")
                        };

                        if (relocationRecord.Flag.HasFlag(EnumRecordsFlag.ImportOrdinalAdditive))
                        {
                            result += BitConverter.ToUInt16(s.Data, relocationRecord.Offset);
                        }

                        Array.Copy(BitConverter.GetBytes(result), 0, s.Data, relocationRecord.Offset, 2);
                        break;
                    }

                    case EnumRecordsFlag.InternalRef:
                    {
                        //32-Bit Pointer
                        if (relocationRecord.SourceType == 3)
                        {
                            var relocationPointer = new IntPtr16(relocationRecord.TargetTypeValueTuple.Item2,
                                                                 relocationRecord.TargetTypeValueTuple.Item4);

                            Array.Copy(relocationPointer.ToArray(), 0, s.Data, relocationRecord.Offset, 4);
                            break;
                        }
                        Array.Copy(BitConverter.GetBytes(relocationRecord.TargetTypeValueTuple.Item2), 0, s.Data, relocationRecord.Offset, 2);
                        break;
                    }

                    case EnumRecordsFlag.ImportNameAdditive:
                    case EnumRecordsFlag.ImportName:
                    {
                        var nametableOrdinal = relocationRecord.TargetTypeValueTuple.Item2;
                        var functionOrdinal  = relocationRecord.TargetTypeValueTuple.Item3;

                        var newSegment = module.File.ImportedNameTable[nametableOrdinal].Name switch
                        {
                            "MAJORBBS" => Majorbbs.Segment,
                            "GALGSBL" => Galgsbl.Segment,
                            "PHAPI" => Phapi.Segment,
                            "GALME" => Galme.Segment,
                            "DOSCALLS" => Doscalls.Segment,
                            _ => throw new Exception(
                                      $"Unknown or Unimplemented Imported Module: {module.File.ImportedNameTable[nametableOrdinal].Name}")
                        };

                        var relocationPointer = new IntPtr16(newSegment, functionOrdinal);

                        //32-Bit Pointer
                        if (relocationRecord.SourceType == 3)
                        {
                            Array.Copy(relocationPointer.ToArray(), 0, s.Data, relocationRecord.Offset, 4);
                            continue;
                        }

                        //16-Bit Values
                        var result = relocationRecord.SourceType switch
                        {
                            //Offset
                            2 => relocationPointer.Segment,
                            5 => relocationPointer.Offset,
                            _ => throw new ArgumentOutOfRangeException(
                                      $"Unhandled Relocation Source Type: {relocationRecord.SourceType}")
                        };

                        if (relocationRecord.Flag.HasFlag(EnumRecordsFlag.ImportNameAdditive))
                        {
                            result += BitConverter.ToUInt16(s.Data, relocationRecord.Offset);
                        }

                        Array.Copy(BitConverter.GetBytes(result), 0, s.Data, relocationRecord.Offset, 2);
                        break;
                    }

                    default:
                        throw new Exception("Unsupported Records Flag for Relocation Value");
                    }
                }
            }
        }
Esempio n. 15
0
        /// <summary>
        ///     Runs the specified routine in the specified module
        /// </summary>
        /// <param name="moduleName"></param>
        /// <param name="routine"></param>
        /// <param name="channelNumber"></param>
        /// <param name="initialStackValues"></param>
        private ushort Run(string moduleName, IntPtr16 routine, ushort channelNumber, bool simulateCallFar = false, Queue <ushort> initialStackValues = null)
        {
            var resultRegisters = _modules[moduleName].Execute(routine, channelNumber, simulateCallFar, false, initialStackValues);

            return(resultRegisters.AX);
        }
Esempio n. 16
0
        /// <summary>
        ///     Generates a Real Mode Interrupt from Protected Mode
        ///
        ///     Allows for calling/passing information to TSR's running in real mode. This is used by some MajorBBS modules
        ///     for directly accessing the Btrieve Driver
        /// </summary>
        private void DosRealIntr()
        {
            var interruptNumber = GetParameter(0);
            var registerPointer = GetParameterPointer(1);
            var reserved        = GetParameter(3); //Must Be Zero
            var wordCount       = GetParameter(4); //Count of word arguments on stack

            var regs = new Regs16Struct(Module.Memory.GetArray(registerPointer, Regs16Struct.Size).ToArray());

            switch (interruptNumber)
            {
            case 0x21:                //INT 21h Call

                switch (regs.AX >> 8) //INT 21h Function
                {
                case 0x35:            //Get Interrupt Vector
                {
                    switch ((byte)(regs.AX & 0xFF))
                    {
                    case 0x7B:                 //Btrieve Vector
                    {
                        //Modules will use this vector to see if Btrieve is running
                        regs.BX = 0x33;
                        break;
                    }

                    default:
                        throw new Exception($"Unknown Interrupt Vector: {(byte)(regs.AX & 0xFF):X2}h");
                    }

                    break;
                }

                default:
                    throw new Exception($"Unknown INT 21h Function: {regs.AX >> 8:X2}h");
                }
                break;

            case 0x7B:     //Btrieve Interrupt
            {
                var btvda = new BtvdatStruct(Module.Memory.GetArray(regs.DS, 0, BtvdatStruct.Size));

                switch ((EnumBtrieveOperationCodes)btvda.funcno)
                {
                case EnumBtrieveOperationCodes.Open:
                {
                    //Get the File Name to oPen
                    var fileName = Encoding.ASCII.GetString(Module.Memory.GetString(btvda.keyseg, 0, true));
                    var btvFile  = new BtrieveFileProcessor(_fileFinder, Module.ModulePath, fileName);

                    //Setup Pointers
                    var btvFileStructPointer = new IntPtr16(btvda.posblkseg, btvda.posblkoff);
                    var btvFileNamePointer   =
                        Module.Memory.AllocateVariable($"{fileName}-NAME", (ushort)(fileName.Length + 1));
                    var btvDataPointer = Module.Memory.AllocateVariable($"{fileName}-RECORD", (ushort)btvFile.RecordLength);

                    var newBtvStruct = new BtvFileStruct
                    {
                        filenam = btvFileNamePointer,
                        reclen  = (ushort)btvFile.RecordLength,
                        data    = btvDataPointer
                    };
                    BtrieveSaveProcessor(btvFileStructPointer, btvFile);
                    Module.Memory.SetArray(btvFileStructPointer, newBtvStruct.Data);
                    Module.Memory.SetArray(btvFileNamePointer, Encoding.ASCII.GetBytes(fileName + '\0'));

                    //Set the active Btrieve file (BB) to the now open file
                    Module.Memory.SetPointer("BB", btvFileStructPointer);

#if DEBUG
                    _logger.Info($"Opened file {fileName} and allocated it to {btvFileStructPointer}");
#endif

                    Registers.AX = 0;
                    break;
                }

                case EnumBtrieveOperationCodes.Stat:
                {
                    var currentBtrieveFile = BtrieveGetProcessor(Module.Memory.GetPointer("BB"));
                    var btvStats           = new BtvstatfbStruct
                    {
                        fs = new BtvfilespecStruct()
                        {
                            numofr = (uint)currentBtrieveFile.GetRecordCount(),
                            numofx = (ushort)currentBtrieveFile.Keys.Count,
                            pagsiz = (ushort)currentBtrieveFile.PageLength,
                            reclen = (ushort)currentBtrieveFile.RecordLength
                        }
                    };

                    var definedKeys = currentBtrieveFile.Keys.Values.SelectMany(k => k.Segments).Select(k =>
                                                                                                        new BtvkeyspecStruct()
                            {
                                flags  = (ushort)k.Attributes,
                                keylen = k.Length,
                                keypos = k.Position,
                                numofk = k.Number
                            }).ToList();
                    btvStats.keyspec = definedKeys.ToArray();

                    Module.Memory.SetArray(btvda.databufsegment, btvda.databufoffset, btvStats.Data);
                    Registers.AX = 0;
                    break;
                }

                case EnumBtrieveOperationCodes.SetOwner:             //Ignore
                    Registers.AX = 0;
                    break;

                default:
                    throw new Exception($"Unknown Btrieve Operation: {(EnumBtrieveOperationCodes)btvda.funcno}");
                }

                break;
            }

            default:
                throw new Exception($"Unhandled Interrupt: {interruptNumber:X2}h");
            }

            Module.Memory.SetArray(registerPointer, regs.Data);
        }