diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 318b5282..1face64a 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -17,7 +17,7 @@ jobs: - name: Setup .NET uses: actions/setup-dotnet@v4 with: - dotnet-version: 6.0.x + dotnet-version: 5.0.x - name: Restore dependencies run: dotnet restore ProcessorEmulator.csproj diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index a9654729..4028bf5e 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -19,7 +19,7 @@ jobs: - name: Setup .NET SDK uses: actions/setup-dotnet@v3 with: - dotnet-version: '6.0.x' + dotnet-version: '5.0.x' - name: Restore dependencies run: dotnet restore Processor-Emulator.sln diff --git a/.github/workflows/dotnet-desktop-build.yml b/.github/workflows/dotnet-desktop-build.yml index 35d42f4b..2ce3a757 100644 --- a/.github/workflows/dotnet-desktop-build.yml +++ b/.github/workflows/dotnet-desktop-build.yml @@ -14,7 +14,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-dotnet@v3 with: - dotnet-version: '6.0.x' + dotnet-version: '5.0.x' # Restore NuGet packages before building - name: Restore packages run: dotnet restore ProcessorEmulator.csproj @@ -29,7 +29,7 @@ jobs: - uses: actions/checkout@v4 - uses: actions/setup-dotnet@v3 with: - dotnet-version: '6.0.x' + dotnet-version: '5.0.x' # Restore NuGet packages before building - name: Restore packages run: dotnet restore ProcessorEmulator.csproj diff --git a/.gitignore b/.gitignore index db046cb0..5091d2ea 100644 --- a/.gitignore +++ b/.gitignore @@ -25,3 +25,4 @@ MipsBranchTest/obj/Debug/net6.0-windows/MipsBranchTest.csproj.FileListAbsolute.t *.ps1 /.vscode .geminiignore +duplicate_definitions_report.md diff --git a/App.xaml.cs b/App.xaml.cs index 126bc3e1..074582d3 100644 --- a/App.xaml.cs +++ b/App.xaml.cs @@ -8,7 +8,7 @@ namespace ProcessorEmulator { - public partial class App : Application + public partial class App : System.Windows.Application { private static string StartupLogPath => System.IO.Path.Combine(System.IO.Path.GetTempPath(), "ProcessorEmulator_startup.log"); private static void Log(string line) diff --git a/AppThemeManager.cs b/AppThemeManager.cs index 80110063..39ccbf60 100644 --- a/AppThemeManager.cs +++ b/AppThemeManager.cs @@ -3,6 +3,7 @@ using System.Linq; using System.Windows; using System.Runtime.Versioning; +using Application = System.Windows.Application; namespace ProcessorEmulator { diff --git a/ArmHypervisor.cs b/ArmHypervisor.cs index 44c0d6d3..a7f8340c 100644 --- a/ArmHypervisor.cs +++ b/ArmHypervisor.cs @@ -162,6 +162,11 @@ private void ExecuteInstruction() { executed = ExecuteLoadStore(instruction); } + // Load/Store Multiple Instructions (bits 27-25 = 100) + else if ((instruction & 0x0E000000) == 0x08000000) + { + executed = ExecuteLoadStoreMultiple(instruction); + } // Branch Instructions (bits 27-25 = 101) else if ((instruction & 0x0E000000) == 0x0A000000) { @@ -210,15 +215,41 @@ private bool ExecuteDataProcessing(uint instruction) result = (uint)addResult; carryOut = addResult > 0xFFFFFFFF; break; + case 0x8: // TST + result = rnValue & operand2; + setFlags = true; + rd = 16; // Don't write to any register + break; + case 0x9: // TEQ + result = rnValue ^ operand2; + setFlags = true; + rd = 16; // Don't write to any register + break; case 0xA: // CMP (SUB but don't store result) result = rnValue - operand2; carryOut = rnValue >= operand2; setFlags = true; // CMP always sets flags rd = 16; // Don't write to any register break; + case 0xB: // CMN + long cmnResult = (long)rnValue + operand2; + result = (uint)cmnResult; + carryOut = cmnResult > 0xFFFFFFFF; + setFlags = true; + rd = 16; // Don't write to any register + break; + case 0xC: // ORR + result = rnValue | operand2; + break; case 0xD: // MOV result = operand2; break; + case 0xE: // BIC + result = rnValue & ~operand2; + break; + case 0xF: // MVN + result = ~operand2; + break; default: Debug.WriteLine($"[HV] Unhandled data processing opcode: 0x{opcode:X}"); return false; @@ -297,6 +328,79 @@ private bool ExecuteLoadStore(uint instruction) return true; } + private bool ExecuteLoadStoreMultiple(uint instruction) + { + bool isLoad = (instruction & 0x00100000) != 0; // L bit + bool writeBack = (instruction & 0x00200000) != 0; // W bit + bool increment = (instruction & 0x00800000) != 0; // U bit + bool preIndex = (instruction & 0x01000000) != 0; // P bit + uint rn = (instruction >> 16) & 0xF; // Base register + uint regList = instruction & 0xFFFF; + + uint address = registers[rn]; + int regCount = 0; + for (int i = 0; i < 16; i++) + { + if ((regList & (1 << i)) != 0) + { + regCount++; + } + } + + if (!increment) // Decrement + { + address -= (uint)(regCount * 4); + } + if (preIndex) + { + if (increment) + { + address += 4; + } + else + { + // address is already correct + } + } + + for (int i = 0; i < 16; i++) + { + if ((regList & (1 << i)) != 0) + { + if (isLoad) + { + registers[i] = ReadMemory32(address); + Debug.WriteLine($"[HV] LDM: R{i} = 0x{registers[i]:X8} from [0x{address:X8}]"); + } + else + { + WriteMemory32(address, registers[i]); + Debug.WriteLine($"[HV] STM: [0x{address:X8}] = 0x{registers[i]:X8}"); + } + address += 4; + } + } + + if (writeBack) + { + if (increment) + { + registers[rn] += (uint)(regCount * 4); + } + else + { + registers[rn] -= (uint)(regCount * 4); + } + } + + if (!isLoad || (regList & (1 << 15)) == 0) + { + registers[15] += 4; + } + + return true; + } + private bool ExecuteBranch(uint instruction) { bool isLink = (instruction & 0x01000000) != 0; // L bit @@ -452,6 +556,12 @@ private string DecodeInstruction(uint instruction) return isLoad ? $"LDR R{rd}, [R{rn}]" : $"STR R{rd}, [R{rn}]"; } + else if ((instruction & 0x0E000000) == 0x08000000) // Load/Store Multiple + { + bool isLoad = (instruction & 0x00100000) != 0; + uint rn = (instruction >> 16) & 0xF; + return isLoad ? $"LDM R{rn}" : $"STM R{rn}"; + } else if ((instruction & 0x0E000000) == 0x0A000000) // Branch { bool isLink = (instruction & 0x01000000) != 0; diff --git a/BoltDemo_Standalone/obj/BoltDemo.csproj.nuget.dgspec.json b/BoltDemo_Standalone/obj/BoltDemo.csproj.nuget.dgspec.json index a5c10c37..5ae41b4f 100644 --- a/BoltDemo_Standalone/obj/BoltDemo.csproj.nuget.dgspec.json +++ b/BoltDemo_Standalone/obj/BoltDemo.csproj.nuget.dgspec.json @@ -38,7 +38,7 @@ "auditLevel": "low", "auditMode": "direct" }, - "SdkAnalysisLevel": "9.0.300" + "SdkAnalysisLevel": "10.0.100" }, "frameworks": { "net6.0": { @@ -54,30 +54,12 @@ ], "assetTargetFallback": true, "warn": true, - "downloadDependencies": [ - { - "name": "Microsoft.AspNetCore.App.Ref", - "version": "[6.0.36, 6.0.36]" - }, - { - "name": "Microsoft.NETCore.App.Host.win-x64", - "version": "[6.0.36, 6.0.36]" - }, - { - "name": "Microsoft.NETCore.App.Ref", - "version": "[6.0.36, 6.0.36]" - }, - { - "name": "Microsoft.WindowsDesktop.App.Ref", - "version": "[6.0.36, 6.0.36]" - } - ], "frameworkReferences": { "Microsoft.NETCore.App": { "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.300\\RuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.102\\RuntimeIdentifierGraph.json" } } } diff --git a/BoltDemo_Standalone/obj/BoltDemo.csproj.nuget.g.props b/BoltDemo_Standalone/obj/BoltDemo.csproj.nuget.g.props index 4f31cb6c..f6a6ccb9 100644 --- a/BoltDemo_Standalone/obj/BoltDemo.csproj.nuget.g.props +++ b/BoltDemo_Standalone/obj/BoltDemo.csproj.nuget.g.props @@ -7,7 +7,7 @@ $(UserProfile)\.nuget\packages\ C:\Users\juler\.nuget\packages\ PackageReference - 6.14.0 + 7.0.0 diff --git a/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.AssemblyInfo.cs b/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.AssemblyInfo.cs index 3e48d297..951b0301 100644 --- a/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.AssemblyInfo.cs +++ b/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.AssemblyInfo.cs @@ -13,7 +13,7 @@ [assembly: System.Reflection.AssemblyCompanyAttribute("BoltDemo")] [assembly: System.Reflection.AssemblyConfigurationAttribute("Debug")] [assembly: System.Reflection.AssemblyFileVersionAttribute("1.0.0.0")] -[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+804bf781e2247bc2d60c687463eb00bdc6855b4e")] +[assembly: System.Reflection.AssemblyInformationalVersionAttribute("1.0.0+510fa6da2e9fa243e5b3fbb3a35ffc6d705bb3be")] [assembly: System.Reflection.AssemblyProductAttribute("BoltDemo")] [assembly: System.Reflection.AssemblyTitleAttribute("BoltDemo")] [assembly: System.Reflection.AssemblyVersionAttribute("1.0.0.0")] diff --git a/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.AssemblyInfoInputs.cache b/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.AssemblyInfoInputs.cache index 4c0f813c..e040be26 100644 --- a/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.AssemblyInfoInputs.cache +++ b/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.AssemblyInfoInputs.cache @@ -1 +1 @@ -c8b6793e0acd9d438065242c46ec4e0309651be119b1a9ed9ffd903db716a9c8 +86cc116b25dfe63cfdc820e94796b6d487fe6cc62d57683ae144a2c8144611c9 diff --git a/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.GeneratedMSBuildEditorConfig.editorconfig b/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.GeneratedMSBuildEditorConfig.editorconfig index 9d2239a4..62a2decb 100644 --- a/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.GeneratedMSBuildEditorConfig.editorconfig +++ b/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.GeneratedMSBuildEditorConfig.editorconfig @@ -1,5 +1,7 @@ is_global = true build_property.TargetFramework = net6.0 +build_property.TargetFrameworkIdentifier = .NETCoreApp +build_property.TargetFrameworkVersion = v6.0 build_property.TargetPlatformMinVersion = build_property.UsingMicrosoftNETSdkWeb = build_property.ProjectTypeGuids = diff --git a/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.GlobalUsings.g.cs b/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.GlobalUsings.g.cs index 8578f3d0..d12bcbc7 100644 --- a/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.GlobalUsings.g.cs +++ b/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.GlobalUsings.g.cs @@ -1,8 +1,8 @@ // -global using global::System; -global using global::System.Collections.Generic; -global using global::System.IO; -global using global::System.Linq; -global using global::System.Net.Http; -global using global::System.Threading; -global using global::System.Threading.Tasks; +global using System; +global using System.Collections.Generic; +global using System.IO; +global using System.Linq; +global using System.Net.Http; +global using System.Threading; +global using System.Threading.Tasks; diff --git a/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.assets.cache b/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.assets.cache index bdf2aca5..fa720486 100644 Binary files a/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.assets.cache and b/BoltDemo_Standalone/obj/Debug/net6.0/BoltDemo.assets.cache differ diff --git a/BoltDemo_Standalone/obj/project.assets.json b/BoltDemo_Standalone/obj/project.assets.json index 7af06466..2eee82a2 100644 --- a/BoltDemo_Standalone/obj/project.assets.json +++ b/BoltDemo_Standalone/obj/project.assets.json @@ -13,11 +13,11 @@ "project": { "version": "1.0.0", "restore": { - "projectUniqueName": "c:\\Users\\juler\\Documents\\GitHub\\Processor-Emulator\\BoltDemo_Standalone\\BoltDemo.csproj", + "projectUniqueName": "C:\\Users\\juler\\Documents\\GitHub\\Processor-Emulator\\BoltDemo_Standalone\\BoltDemo.csproj", "projectName": "BoltDemo", - "projectPath": "c:\\Users\\juler\\Documents\\GitHub\\Processor-Emulator\\BoltDemo_Standalone\\BoltDemo.csproj", + "projectPath": "C:\\Users\\juler\\Documents\\GitHub\\Processor-Emulator\\BoltDemo_Standalone\\BoltDemo.csproj", "packagesPath": "C:\\Users\\juler\\.nuget\\packages\\", - "outputPath": "c:\\Users\\juler\\Documents\\GitHub\\Processor-Emulator\\BoltDemo_Standalone\\obj\\", + "outputPath": "C:\\Users\\juler\\Documents\\GitHub\\Processor-Emulator\\BoltDemo_Standalone\\obj\\", "projectStyle": "PackageReference", "configFilePaths": [ "C:\\Users\\juler\\AppData\\Roaming\\NuGet\\NuGet.Config" @@ -44,7 +44,7 @@ "auditLevel": "low", "auditMode": "direct" }, - "SdkAnalysisLevel": "9.0.300" + "SdkAnalysisLevel": "10.0.100" }, "frameworks": { "net6.0": { @@ -60,30 +60,12 @@ ], "assetTargetFallback": true, "warn": true, - "downloadDependencies": [ - { - "name": "Microsoft.AspNetCore.App.Ref", - "version": "[6.0.36, 6.0.36]" - }, - { - "name": "Microsoft.NETCore.App.Host.win-x64", - "version": "[6.0.36, 6.0.36]" - }, - { - "name": "Microsoft.NETCore.App.Ref", - "version": "[6.0.36, 6.0.36]" - }, - { - "name": "Microsoft.WindowsDesktop.App.Ref", - "version": "[6.0.36, 6.0.36]" - } - ], "frameworkReferences": { "Microsoft.NETCore.App": { "privateAssets": "all" } }, - "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\9.0.300\\RuntimeIdentifierGraph.json" + "runtimeIdentifierGraphPath": "C:\\Program Files\\dotnet\\sdk\\10.0.102\\RuntimeIdentifierGraph.json" } } } diff --git a/BoltDemo_Standalone/obj/project.nuget.cache b/BoltDemo_Standalone/obj/project.nuget.cache index 2f2058ff..ee0d6e19 100644 --- a/BoltDemo_Standalone/obj/project.nuget.cache +++ b/BoltDemo_Standalone/obj/project.nuget.cache @@ -1,13 +1,8 @@ { "version": 2, - "dgSpecHash": "9EVy3SwQS1o=", + "dgSpecHash": "VrZ3Gz65KCg=", "success": true, "projectFilePath": "C:\\Users\\juler\\Documents\\GitHub\\Processor-Emulator\\BoltDemo_Standalone\\BoltDemo.csproj", - "expectedPackageFiles": [ - "C:\\Users\\juler\\.nuget\\packages\\microsoft.netcore.app.ref\\6.0.36\\microsoft.netcore.app.ref.6.0.36.nupkg.sha512", - "C:\\Users\\juler\\.nuget\\packages\\microsoft.windowsdesktop.app.ref\\6.0.36\\microsoft.windowsdesktop.app.ref.6.0.36.nupkg.sha512", - "C:\\Users\\juler\\.nuget\\packages\\microsoft.aspnetcore.app.ref\\6.0.36\\microsoft.aspnetcore.app.ref.6.0.36.nupkg.sha512", - "C:\\Users\\juler\\.nuget\\packages\\microsoft.netcore.app.host.win-x64\\6.0.36\\microsoft.netcore.app.host.win-x64.6.0.36.nupkg.sha512" - ], + "expectedPackageFiles": [], "logs": [] } \ No newline at end of file diff --git a/CP0.cs b/CP0.cs index 581df780..b0233f2b 100644 --- a/CP0.cs +++ b/CP0.cs @@ -5,6 +5,9 @@ namespace ProcessorEmulator.Emulation public class CP0 { private uint[] registers = new uint[32]; + private uint _entryHi; + private uint _entryLo0; + private uint _entryLo1; // MIPS CP0 Register Constants private const int IndexReg = 0; @@ -30,9 +33,25 @@ public class CP0 public uint EPC { get => registers[EPCReg]; set => registers[EPCReg] = value; } public uint Count { get => registers[CountReg]; set => registers[CountReg] = value; } public uint Compare { get => registers[CompareReg]; set => registers[CompareReg] = value; } + public uint EntryHi { get => _entryHi; set => _entryHi = value; } + public uint EntryLo0 { get => _entryLo0; set => _entryLo0 = value; } + public uint EntryLo1 { get => _entryLo1; set => _entryLo1 = value; } + public uint PRId { get; set; } + // TLB Entry structure + public struct TLBEntry + { + public uint EntryHi; // Virtual Page Number, ASID, VPN2 + public uint EntryLo0; // PFN, C, D, V, G for even page + public uint EntryLo1; // PFN, C, D, V, G for odd page + public uint PageMask; // Page size + } + + private const int TLB_ENTRIES = 16; // MIPS typically has 16 or 32 TLB entries + private TLBEntry[] _tlb = new TLBEntry[TLB_ENTRIES]; + // Status Register bits private const uint STATUS_IE_BIT = 1 << 0; // Interrupt Enable private const uint STATUS_EXL_BIT = 1 << 1; // Exception Level @@ -72,9 +91,26 @@ public void WriteRegister(int reg, uint value) registers[CauseReg] = (registers[CauseReg] & ~clearMask) | (value & clearMask); return; } - - Console.WriteLine($"CP0 Write: Reg {reg} = 0x{value:X8}"); - registers[reg] = value; + + switch(reg) + { + case EntryHiReg: + _entryHi = value; + Console.WriteLine($"CP0 Write: EntryHi = 0x{value:X8}"); + break; + case EntryLo0Reg: + _entryLo0 = value; + Console.WriteLine($"CP0 Write: EntryLo0 = 0x{value:X8}"); + break; + case EntryLo1Reg: + _entryLo1 = value; + Console.WriteLine($"CP0 Write: EntryLo1 = 0x{value:X8}"); + break; + default: + Console.WriteLine($"CP0 Write: Reg {reg} = 0x{value:X8}"); + registers[reg] = value; + break; + } } } @@ -88,7 +124,22 @@ public uint ReadRegister(int reg) if (reg >= 0 && reg < 32) { - uint value = registers[reg]; + uint value; + switch(reg) + { + case EntryHiReg: + value = _entryHi; + break; + case EntryLo0Reg: + value = _entryLo0; + break; + case EntryLo1Reg: + value = _entryLo1; + break; + default: + value = registers[reg]; + break; + } // Console.WriteLine($"CP0 Read: Reg {reg} returns 0x{value:X8}"); // Too noisy for timer return value; } @@ -128,5 +179,106 @@ public bool ShouldTriggerInterrupt() return interruptsEnabled && !inException && (interruptPending & interruptMask) != 0; } + + // MIPS TLB operations + + /// + /// Reads the TLB entry specified by IndexReg into EntryHi, EntryLo0, EntryLo1, and PageMask. + /// + public void ReadTLBEntry() + { + int index = (int)(registers[IndexReg] & 0x1F); // Index is 5 bits + if (index < TLB_ENTRIES) + { + EntryHi = _tlb[index].EntryHi; + EntryLo0 = _tlb[index].EntryLo0; + EntryLo1 = _tlb[index].EntryLo1; + registers[PageMaskReg] = _tlb[index].PageMask; + Console.WriteLine($"CP0 TLBR: Read TLB entry {index}"); + } + else + { + Console.WriteLine($"CP0 TLBR: Invalid TLB index {index}"); + // In a real CPU, this might cause an exception or return garbage. + } + } + + /// + /// Writes EntryHi, EntryLo0, EntryLo1, and PageMask to the TLB entry specified by IndexReg. + /// + public void WriteTLBEntryIndexed() + { + int index = (int)(registers[IndexReg] & 0x1F); // Index is 5 bits + if (index < TLB_ENTRIES) + { + _tlb[index].EntryHi = EntryHi; + _tlb[index].EntryLo0 = EntryLo0; + _tlb[index].EntryLo1 = EntryLo1; + _tlb[index].PageMask = registers[PageMaskReg]; + Console.WriteLine($"CP0 TLBWI: Wrote TLB entry {index}"); + } + else + { + Console.WriteLine($"CP0 TLBWI: Invalid TLB index {index}"); + } + } + + /// + /// Writes EntryHi, EntryLo0, EntryLo1, and PageMask to a random TLB entry. + /// (Simplified: for now, it's not truly random, just uses a fixed index after wired entries) + /// + public void WriteTLBEntryRandom() + { + // MIPS architecture has 'wired' entries that cannot be overwritten randomly. + // For simplicity, we'll assume a fixed set of wired entries (e.g., 0-7) + // and use a very simple 'random' for now (e.g., just write to index 8). + // A real Random register would decrement and wrap around. + int index = 8; // Example: Fixed non-wired entry + if (index < TLB_ENTRIES) + { + _tlb[index].EntryHi = EntryHi; + _tlb[index].EntryLo0 = EntryLo0; + _tlb[index].EntryLo1 = EntryLo1; + _tlb[index].PageMask = registers[PageMaskReg]; + Console.WriteLine($"CP0 TLBWR: Wrote TLB entry randomly at {index}"); + } + else + { + Console.WriteLine($"CP0 TLBWR: No random entry available (or TLB_ENTRIES is too small)"); + } + } + + /// + /// Searches the TLB for an entry matching EntryHi. If found, its index is written to IndexReg. + /// + public void ProbeTLB() + { + // MIPS TLBP instruction uses EntryHi to find a matching entry in the TLB. + // If found, Index register is updated with the entry's index. + // If not found, the P bit (bit 31) of Index register is set. + + uint vpn2 = (EntryHi >> 13) & 0x7FFFF; // VPN2 from EntryHi (bits 13-31) + uint asid = EntryHi & 0xFF; // ASID from EntryHi (bits 0-7) + + for (int i = 0; i < TLB_ENTRIES; i++) + { + // Extract VPN2 and ASID from stored TLB entry + uint tlbVpn2 = (_tlb[i].EntryHi >> 13) & 0x7FFFF; + uint tlbAsid = _tlb[i].EntryHi & 0xFF; + + // For simplicity, ignore ASID for now (assume all entries are global or ASID matches) + // A real implementation would check the G bit and ASID match + if (tlbVpn2 == vpn2) // && (tlbAsid == asid || (_tlb[i].EntryLo0 & 1) != 0 || (_tlb[i].EntryLo1 & 1) != 0)) // Check G bit + { + registers[IndexReg] = (uint)i; + Console.WriteLine($"CP0 TLBP: Found match at index {i}"); + return; + } + } + + // No match found, set P bit (bit 31) in IndexReg + registers[IndexReg] = 0x80000000; + Console.WriteLine("CP0 TLBP: No match found"); + } } } \ No newline at end of file diff --git a/Core/BaseIrExecutor.cs b/Core/BaseIrExecutor.cs index cd047a3b..7c7bc057 100644 --- a/Core/BaseIrExecutor.cs +++ b/Core/BaseIrExecutor.cs @@ -6,7 +6,8 @@ namespace ProcessorEmulator.Core.Backends { public class BaseIrExecutor : IExecutionEngine { - public void ExecuteStatement(IrStatement statement, ICpuState state, IMemoryManager memory) + // Primary implementation invoked by callers and overridable by backends + public virtual void ExecuteStatement(IrStatement statement, ICpuState state, IMemoryManager memory) { // Strict Privilege Check if ((statement.Metadata & 1) == 1 && state.PrivilegeLevel == 0) @@ -118,6 +119,12 @@ public void ExecuteStatement(IrStatement statement, ICpuState state, IMemoryMana } } + // Explicit interface implementation to ensure the class satisfies the IExecutionEngine contract + void IExecutionEngine.ExecuteStatement(IrStatement statement, ICpuState state, IMemoryManager memory) + { + ExecuteStatement(statement, state, memory); + } + private ulong GetOperandValue(IrOperand operand, ICpuState state) { if (operand.IsImmediate) diff --git a/Core/CpuState.cs b/Core/CpuState.cs index b36fdcee..cea3e37b 100644 --- a/Core/CpuState.cs +++ b/Core/CpuState.cs @@ -9,18 +9,18 @@ namespace ProcessorEmulator.Core public class CpuState : ICpuState, IMemoryManager { private readonly Dictionary _registers = new Dictionary(StringComparer.OrdinalIgnoreCase); - private readonly List _memoryMap; - private readonly Dictionary _ramRegions = new Dictionary(); + private readonly List _memoryMap; + private readonly Dictionary _ramRegions = new Dictionary(); public int PrivilegeLevel { get; set; } public ulong PC { get; set; } - public IReadOnlyList MemoryMap => _memoryMap; + public IReadOnlyList MemoryMap => _memoryMap; public bool IsLittleEndian { get; } public ulong? LinkedAddress { get; set; } - public CpuState(List memoryMap, bool isLittleEndian, ulong startPC = 0) + public CpuState(List memoryMap, bool isLittleEndian, ulong startPC = 0) { - _memoryMap = memoryMap ?? new List(); + _memoryMap = memoryMap ?? new List(); IsLittleEndian = isLittleEndian; PC = startPC; @@ -31,6 +31,31 @@ public CpuState(List memoryMap, bool isLittleEndian, ulong startPC } } + // Compatibility overload: accept legacy ProcessorEmulator.MemoryRegion array + public CpuState(ProcessorEmulator.MemoryRegion[] legacyMemoryMap, bool isLittleEndian, ulong startPC = 0) + { + var converted = new List(); + if (legacyMemoryMap != null) + { + foreach (var lm in legacyMemoryMap) + { + ulong start = lm.BaseAddress; + ulong size = lm.Size; + var name = $"LEGACY_{start:X}"; + converted.Add(new ProcessorEmulator.Core.Emulation.MemoryRegion(name, start, size, ProcessorEmulator.Core.Emulation.MemoryRegionType.RAM)); + } + } + + _memoryMap = converted; + IsLittleEndian = isLittleEndian; + PC = startPC; + + foreach (var ramRegion in _memoryMap.Where(r => r.Type == MemoryRegionType.RAM)) + { + _ramRegions[ramRegion] = new byte[ramRegion.Size]; + } + } + public ulong GetRegister(string name, BitWidth width) { _registers.TryGetValue(name, out ulong value); @@ -42,27 +67,27 @@ public void SetRegister(string name, ulong value, BitWidth width) _registers[name] = value; } - private MemoryRegion HandleLazyAllocation(ulong address) + private ProcessorEmulator.Core.Emulation.MemoryRegion HandleLazyAllocation(ulong address) { const ulong pageSize = 4096; ulong startAddress = address / pageSize * pageSize; // Align to 4KB boundary Console.WriteLine($"[ADAPTIVE BUS]: New memory region discovered at 0x{address:X}. Mapping temporary 4KB RAM at 0x{startAddress:X}."); - var newRegion = new MemoryRegion($"RAM_Lazy_{startAddress:X}", startAddress, pageSize, MemoryRegionType.RAM); + var newRegion = new ProcessorEmulator.Core.Emulation.MemoryRegion($"RAM_Lazy_{startAddress:X}", startAddress, pageSize, ProcessorEmulator.Core.Emulation.MemoryRegionType.RAM); _memoryMap.Add(newRegion); _ramRegions[newRegion] = new byte[pageSize]; return newRegion; } - private (MemoryRegion region, byte[] buffer) GetRegionAndBufferForAddress(ulong address) + private (ProcessorEmulator.Core.Emulation.MemoryRegion region, byte[] buffer) GetRegionAndBufferForAddress(ulong address) { foreach (var region in _memoryMap) { if (region.Contains(address)) { - if (region.Type == MemoryRegionType.RAM) + if (region.Type == ProcessorEmulator.Core.Emulation.MemoryRegionType.RAM) { return (region, _ramRegions[region]); } diff --git a/Core/IntermediateRepresentation.cs b/Core/IntermediateRepresentation.cs index e4458569..571c4874 100644 --- a/Core/IntermediateRepresentation.cs +++ b/Core/IntermediateRepresentation.cs @@ -2,8 +2,21 @@ using System.Collections.Generic; using System.Numerics; -namespace ProcessorEmulator.Core +namespace ProcessorEmulator.Core.IR { + /// + /// Minimal IR value representation used for immediates inside the IR. + /// This is a lightweight wrapper to allow the rest of the IR code to compile. + /// + public readonly struct IrValue + { + public readonly long SignedValue; + public readonly ulong UnsignedValue; + public IrValue(long v) { SignedValue = v; UnsignedValue = (ulong)v; } + public IrValue(ulong v) { UnsignedValue = v; SignedValue = (long)v; } + public override string ToString() => SignedValue.ToString(); + } + /// /// Defines the type of an IR operand. /// diff --git a/DiscoveryDevice.cs b/DiscoveryDevice.cs new file mode 100644 index 00000000..02c6ea9a --- /dev/null +++ b/DiscoveryDevice.cs @@ -0,0 +1,33 @@ +using System; +using ProcessorEmulator.Emulation; + +namespace ProcessorEmulator +{ + public class DiscoveryDevice : IBusDevice + { + public uint StartAddress => 0x1FC00100; // Just after the Reset Vector + public uint Size => 0x100; + + // A simple table defining the "Universe" of this machine + private readonly uint[] _configTable = new uint[] { + 0x4D495053, // Magic: "MIPS" + 0x10400000, // UART Base + 0x10000000, // Framebuffer Base + 640, // Screen Width + 480, // Screen Height + 0x0002A000 // Chipset ID (BCM7405) + }; + + public uint Read32(uint offset) + { + int index = (int)(offset / 4); + return (index < _configTable.Length) ? _configTable[index] : 0; + } + + public void Write32(uint offset, uint value) { /* Read Only */ } + public void Write16(uint address, ushort value) { /* Read Only */ } + public void Write8(uint address, byte value) { /* Read Only */ } + public ushort Read16(uint address) { return 0; } + public byte Read8(uint address) { return 0; } + } +} diff --git a/Emulation/DisplayWindow.cs b/Emulation/DisplayWindow.cs index c737a0a0..0c63c2bc 100644 --- a/Emulation/DisplayWindow.cs +++ b/Emulation/DisplayWindow.cs @@ -4,6 +4,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Windows.Threading; +using Image = System.Windows.Controls.Image; namespace ProcessorEmulator.Emulation { diff --git a/Emulation/EmulatorDisplay.cs b/Emulation/EmulatorDisplay.cs index 6e5ed576..52e94de4 100644 --- a/Emulation/EmulatorDisplay.cs +++ b/Emulation/EmulatorDisplay.cs @@ -60,6 +60,34 @@ public EmulatorDisplay(GenericFramebuffer vram) _backBuffer = new Bitmap(640, 480, PixelFormat.Format32bppRgb); _screen.Image = _backBuffer; + + InitializeHardwareMonitor(); + } + + private void InitializeHardwareMonitor() + { + // Creating a Windows 7 style 'Property Grid' or 'ListView' + ListView hardwareList = new ListView + { + //View = View.Details, + FullRowSelect = true, + GridLines = true, + Location = new Point(660, 12), + Size = new Size(250, 482), + //BackColor = SystemColors.Window, + //Font = new Font("Segoe UI", 9) // The Windows 7 standard font + }; + + hardwareList.Columns.Add("Component", 100); + hardwareList.Columns.Add("Address", 120); + + hardwareList.Items.Add(new ListViewItem(new[] { "CPU Core", "MIPS32 R2" })); + hardwareList.Items.Add(new ListViewItem(new[] { "UART (Serial)", "0x10400000" })); + hardwareList.Items.Add(new ListViewItem(new[] { "Video RAM", "0x10000000" })); + + this.Controls.Add(hardwareList); + // Expand the window to fit the monitor + this.Width += 270; } public void RenderFrame() diff --git a/Emulation/EmulatorWindow.xaml.cs b/Emulation/EmulatorWindow.xaml.cs index 9bd9c899..fee785ee 100644 --- a/Emulation/EmulatorWindow.xaml.cs +++ b/Emulation/EmulatorWindow.xaml.cs @@ -5,6 +5,7 @@ using System.Windows.Media; using System.Windows.Media.Imaging; using System.Diagnostics; +using Timer = System.Threading.Timer; namespace ProcessorEmulator.Emulation { diff --git a/Emulation/SyncEngine/SyncScheduler.cs b/Emulation/SyncEngine/SyncScheduler.cs index 7923a74a..96c179c0 100644 --- a/Emulation/SyncEngine/SyncScheduler.cs +++ b/Emulation/SyncEngine/SyncScheduler.cs @@ -16,7 +16,7 @@ public class SyncScheduler private readonly EntitlementManager entitlementManager; private readonly CMTSResponder cmtsResponder; - private readonly Timer syncTimer; + private readonly System.Threading.Timer syncTimer; private readonly List syncHistory; private bool isRunning; private CancellationTokenSource cancellationTokenSource; @@ -34,7 +34,7 @@ public SyncScheduler() cancellationTokenSource = new CancellationTokenSource(); // Start sync timer (runs every 30 minutes) - syncTimer = new Timer(OnSyncTimer, null, TimeSpan.Zero, TimeSpan.FromMinutes(30)); + syncTimer = new System.Threading.Timer(OnSyncTimer, null, TimeSpan.Zero, TimeSpan.FromMinutes(30)); Debug.WriteLine("[SyncScheduler] Sync scheduler initialized"); } diff --git a/EmulationLogPanel.cs b/EmulationLogPanel.cs index d2bd13bc..3a518ce0 100644 --- a/EmulationLogPanel.cs +++ b/EmulationLogPanel.cs @@ -5,10 +5,16 @@ using System.Windows.Controls; using System.Windows.Controls.Primitives; using System.Windows.Media; +using MessageBox = System.Windows.MessageBox; +using Brushes = System.Windows.Media.Brushes; using System.Diagnostics; using System.Threading; using System.Linq; using System.Text; +using UserControl = System.Windows.Controls.UserControl; +using ListView = System.Windows.Controls.ListView; +using Timer = System.Threading.Timer; +using Brush = System.Windows.Media.Brush; namespace ProcessorEmulator { @@ -24,7 +30,7 @@ public partial class EmulationLogPanel : UserControl, INotifyPropertyChanged private volatile bool isEnabled = true; private int maxLogEntries = 1000; - public event PropertyChangedEventHandler PropertyChanged; + public event PropertyChangedEventHandler? PropertyChanged; public ObservableCollection LogEntries => logEntries; diff --git a/EmulatorConsole.cs b/EmulatorConsole.cs index e4a3e6c5..56593d2b 100644 --- a/EmulatorConsole.cs +++ b/EmulatorConsole.cs @@ -1,7 +1,6 @@ using System; using System.Drawing; using System.Windows.Forms; - using System.Linq; namespace ProcessorEmulator @@ -12,9 +11,8 @@ public partial class EmulatorConsole : Form public EmulatorConsole() { - // Set the classic Win32 look this.Text = "MIPS System Console"; - this.BackColor = SystemColors.Control; // Classic Gray + this.BackColor = SystemColors.Control; this.Size = new Size(800, 600); _terminal = new RichTextBox @@ -42,22 +40,22 @@ public void AppendText(string text) _terminal.SelectionStart = _terminal.Text.Length; _terminal.ScrollToCaret(); } - + + // Append a single character (used as a callback from UART/OnCharReceived) public void AppendChar(char c) { AppendText(c.ToString()); } + // Intercepts key presses to send to the emulated UART. protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { - // Convert the key to an ASCII character char c = (char)0; if (keyData == Keys.Enter) c = '\r'; else if (keyData == Keys.Back) c = '\b'; else if (keyData >= Keys.A && keyData <= Keys.Z) { - // Handle Shift for uppercase/lowercase bool shift = (ModifierKeys & Keys.Shift) != 0; c = (char)(keyData.ToString()[0]); if (!shift) c = char.ToLower(c); @@ -66,21 +64,16 @@ protected override bool ProcessCmdKey(ref Message msg, Keys keyData) { c = keyData.ToString().Last(); } - // Add more cases for symbols/space as needed... else if (keyData == Keys.Space) c = ' '; - if (c != 0) + // Send the character to the UART if it's a valid one. + if (c != 0 && Program.CurrentUart != null) { - // Access your UART instance and send the key - // Assuming you have a reference to the active UART - if (Program.CurrentUart != null) - { - Program.CurrentUart.SendKey(c); - return true; // Mark as handled - } + Program.CurrentUart.SendKey(c); + return true; } return base.ProcessCmdKey(ref msg, keyData); } } -} +} \ No newline at end of file diff --git a/ErrorManager.cs b/ErrorManager.cs index 963a86b0..dd778bea 100644 --- a/ErrorManager.cs +++ b/ErrorManager.cs @@ -1,6 +1,7 @@ using System; using System.Collections.Generic; using System.Windows; +using MessageBox = System.Windows.MessageBox; namespace ProcessorEmulator { diff --git a/HypervisorWindow.cs b/HypervisorWindow.cs new file mode 100644 index 00000000..f1c3d96a --- /dev/null +++ b/HypervisorWindow.cs @@ -0,0 +1,9 @@ +// This file was a duplicate of the XAML-backed code-behind and has been +// emptied to avoid ambiguous partial-class definitions. The real implementation +// lives in HypervisorWindow.xaml.cs which is generated/maintained for the +// XAML-backed window. Keeping an empty placeholder file prevents accidental +// reintroduction of duplicate members while preserving repository history. + +// If you need to add non-XAML partial members for `HypervisorWindow`, add +// only methods or properties that don't duplicate generated members. + diff --git a/HypervisorWindow.xaml.cs b/HypervisorWindow.xaml.cs index a71b79e2..f5c13c23 100644 --- a/HypervisorWindow.xaml.cs +++ b/HypervisorWindow.xaml.cs @@ -2,6 +2,9 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Threading; +using System.Windows.Media; +using TextBox = System.Windows.Controls.TextBox; +using Button = System.Windows.Controls.Button; namespace ProcessorEmulator { @@ -9,13 +12,25 @@ public partial class HypervisorWindow : Window { private readonly RealMipsHypervisor hypervisor; private TextBox logBox; - private TextBlock statusText; + private TextBox statusText; private Button startButton; private Button stopButton; + // Parameterless constructor for XAML designer and tooling + public HypervisorWindow() + { + InitializeComponent(); + this.hypervisor = new RealMipsHypervisor(); + this.Title = "Real MIPS Hypervisor"; + + CreateUI(); + this.hypervisor.OnRealExecution += AppendLog; + } + public HypervisorWindow(RealMipsHypervisor hypervisor, string platformName) { - this.hypervisor = hypervisor; + InitializeComponent(); + this.hypervisor = hypervisor ?? new RealMipsHypervisor(); this.Title = $"Real MIPS Hypervisor - {platformName}"; CreateUI(); @@ -45,35 +60,44 @@ private void CreateUI() Content = mainStack; // Title - var titleText = new TextBlock + var titleText = new TextBox { Text = "REAL MIPS EMULATOR", FontSize = 36, FontWeight = FontWeights.Bold, Foreground = System.Windows.Media.Brushes.White, HorizontalAlignment = HorizontalAlignment.Center, - Margin = new Thickness(0, 20, 0, 0) + Margin = new Thickness(0, 20, 0, 0), + Background = System.Windows.Media.Brushes.Transparent, + BorderThickness = new Thickness(0), + IsReadOnly = true }; mainStack.Children.Add(titleText); // Subtitle - var subtitleText = new TextBlock + var subtitleText = new TextBox { Text = "AT&T U-verse / Microsoft Mediaroom", FontSize = 16, Foreground = System.Windows.Media.Brushes.Gray, - HorizontalAlignment = HorizontalAlignment.Center + HorizontalAlignment = HorizontalAlignment.Center, + Background = System.Windows.Media.Brushes.Transparent, + BorderThickness = new Thickness(0), + IsReadOnly = true }; mainStack.Children.Add(subtitleText); // Status - statusText = new TextBlock + statusText = new TextBox { Text = "Initializing...", FontSize = 14, Foreground = System.Windows.Media.Brushes.Yellow, HorizontalAlignment = HorizontalAlignment.Center, - Margin = new Thickness(0, 10, 0, 0) + Margin = new Thickness(0, 10, 0, 0), + Background = System.Windows.Media.Brushes.Transparent, + BorderThickness = new Thickness(0), + IsReadOnly = true }; mainStack.Children.Add(statusText); diff --git a/IChipsetEmulator.cs b/IChipsetEmulator.cs index dd4e3b5f..1e025f1c 100644 --- a/IChipsetEmulator.cs +++ b/IChipsetEmulator.cs @@ -1,4 +1,4 @@ -namespace ProcessorEmulator.Tools +namespace ProcessorEmulator { public interface IChipsetEmulator { diff --git a/MainWindow.Themes.cs b/MainWindow.Themes.cs index 87c6f8a5..1498d48f 100644 --- a/MainWindow.Themes.cs +++ b/MainWindow.Themes.cs @@ -2,17 +2,18 @@ using System.Windows; using System.Windows.Controls; using System.Windows.Media; +using MessageBox = System.Windows.MessageBox; namespace ProcessorEmulator { public partial class MainWindow { - private ComboBox runtimeThemeCombo; + private System.Windows.Controls.ComboBox runtimeThemeCombo; private TextBlock runtimeGlassText; private void ThemeCombo_SelectionChanged(object sender, SelectionChangedEventArgs e) { - if (sender is ComboBox cb && cb.SelectedItem is ComboBoxItem item) + if (sender is System.Windows.Controls.ComboBox cb && cb.SelectedItem is ComboBoxItem item) { string name = item.Content?.ToString() ?? "Win95"; SwitchTheme(name); diff --git a/MainWindow.cs b/MainWindow.cs new file mode 100644 index 00000000..d37cc2c3 --- /dev/null +++ b/MainWindow.cs @@ -0,0 +1,88 @@ +using System; +using System.Drawing; +using System.Windows; +using System.Windows.Forms; +using System.Windows.Threading; // Added for Dispatcher.Invoke +using ProcessorEmulator.Emulation; // Added for MipsCpuEmulator + +namespace ProcessorEmulator +{ + // Use the real MipsBus and CP0 implementations from ProcessorEmulator.Emulation + + public partial class MainWindow : Window + { + // Our WinForms hardware components + private RichTextBox _serialConsole; + private PictureBox _videoDisplay; + private MipsCpuEmulator _emulator; // Declared MipsCpuEmulator + + public MainWindow() + { + InitializeComponent(); + SetupClassicUI(); + + // Instantiate real CP0 and MipsBus, then create the emulator and subscribe to logs + var cp0 = new CP0(); + var bus = new MipsBus(cp0); + _emulator = new MipsCpuEmulator(bus, cp0); + _emulator.OnLogMessage += AppendToSerialConsole; + } + + private void SetupClassicUI() + { + // 1. Create the Video Tab (The XP/7 Pro look) + TabPage videoPage = new TabPage("Video Output"); + _videoDisplay = new PictureBox { + Dock = DockStyle.Fill, + BackColor = System.Drawing.Color.Black, + SizeMode = PictureBoxSizeMode.CenterImage + }; + videoPage.Controls.Add(_videoDisplay); + + // 2. Create the Serial Console Tab (The Terminal look) + TabPage consolePage = new TabPage("System Console"); + _serialConsole = new RichTextBox { + Dock = DockStyle.Fill, + BackColor = System.Drawing.Color.Black, + ForeColor = System.Drawing.Color.Lime, + Font = new System.Drawing.Font("Lucida Console", 9f), + ReadOnly = true + }; + consolePage.Controls.Add(_serialConsole); + + // Add them to the WindowsFormsHost container + MainTabs.TabPages.Add(videoPage); + MainTabs.TabPages.Add(consolePage); + } + + private void AppendToSerialConsole(string message) + { + // Ensure UI update is on the correct thread + // Use the WPF Dispatcher from this Window to avoid ambiguous 'Application' type + this.Dispatcher.Invoke(() => + { + _serialConsole.AppendText(message); + // WinForms.RichTextBox doesn't have ScrollToEnd; use ScrollToCaret after moving selection + _serialConsole.SelectionStart = _serialConsole.Text.Length; + _serialConsole.ScrollToCaret(); // Auto-scroll to the latest message + }); + } + + + private void LoadBinary_Click(object sender, RoutedEventArgs e) + { + // Trigger your Smart Loader logic here + StatusText.Text = "Loading Binary..."; + } + + private void Exit_Click(object sender, RoutedEventArgs e) => this.Close(); + + private void ShowMemoryMap_Click(object sender, RoutedEventArgs e) => + System.Windows.MessageBox.Show("Displaying Resource Map..."); + + private void ShowInterrupts_Click(object sender, RoutedEventArgs e) + { + System.Windows.MessageBox.Show("Displaying Interrupts..."); + } + } +} diff --git a/MainWindow.xaml b/MainWindow.xaml index f8477b91..75a8bda3 100644 --- a/MainWindow.xaml +++ b/MainWindow.xaml @@ -1,541 +1,34 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -