Skip to content

Commit

Permalink
chore: Merge branch 'master' into feature/dos_process_managment_init
Browse files Browse the repository at this point in the history
Signed-off-by: Maximilien Noal <noal.maximilien@gmail.com>
  • Loading branch information
maximilien-noal committed Dec 29, 2024
2 parents 0a778e5 + ae581f1 commit 2141bc9
Show file tree
Hide file tree
Showing 46 changed files with 1,077 additions and 599 deletions.
10 changes: 5 additions & 5 deletions .github/workflows/nuget.yml
Original file line number Diff line number Diff line change
Expand Up @@ -34,20 +34,20 @@ jobs:

- name: Upload NuGet Bufdio.Spice86
working-directory: ./src/Bufdio.Spice86/bin/Release
run: nuget push Bufdio.Spice86.7.0.0.nupkg ${{secrets.NUGET_API_KEY}} -Source 'https://api.nuget.org/v3/index.json' -SkipDuplicate
run: nuget push Bufdio.Spice86.8.0.2.nupkg ${{secrets.NUGET_API_KEY}} -Source 'https://api.nuget.org/v3/index.json' -SkipDuplicate

- name: Upload NuGet Spice86.Shared
working-directory: ./src/Spice86.Shared/bin/Release
run: nuget push Spice86.Shared.7.0.0.nupkg ${{secrets.NUGET_API_KEY}} -Source 'https://api.nuget.org/v3/index.json' -SkipDuplicate
run: nuget push Spice86.Shared.8.0.2.nupkg ${{secrets.NUGET_API_KEY}} -Source 'https://api.nuget.org/v3/index.json' -SkipDuplicate

- name: Upload NuGet Spice86.Logging
working-directory: ./src/Spice86.Logging/bin/Release
run: nuget push Spice86.Logging.7.0.0.nupkg ${{secrets.NUGET_API_KEY}} -Source 'https://api.nuget.org/v3/index.json' -SkipDuplicate
run: nuget push Spice86.Logging.8.0.2.nupkg ${{secrets.NUGET_API_KEY}} -Source 'https://api.nuget.org/v3/index.json' -SkipDuplicate

- name: Upload NuGet Spice86.Core
working-directory: ./src/Spice86.Core/bin/Release
run: nuget push Spice86.Core.7.0.0.nupkg ${{secrets.NUGET_API_KEY}} -Source 'https://api.nuget.org/v3/index.json' -SkipDuplicate
run: nuget push Spice86.Core.8.0.2.nupkg ${{secrets.NUGET_API_KEY}} -Source 'https://api.nuget.org/v3/index.json' -SkipDuplicate

- name: Upload NuGet Spice86
working-directory: ./src/Spice86/bin/Release
run: nuget push Spice86.7.0.0.nupkg ${{secrets.NUGET_API_KEY}} -Source 'https://api.nuget.org/v3/index.json' -SkipDuplicate
run: nuget push Spice86.8.0.2.nupkg ${{secrets.NUGET_API_KEY}} -Source 'https://api.nuget.org/v3/index.json' -SkipDuplicate
51 changes: 33 additions & 18 deletions README.md
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ If there is already data there the emulator will load it first and complete it,
## More command line options

```
--Debug (Default: false) Starts the program paused.
--Ems (Default: false) Enables EMS memory. EMS adds 8 MB of memory accessible to DOS programs through the EMM Page Frame.
--A20Gate (Default: false) Disables the 20th address line to support programs relying on the rollover of memory addresses above the HMA (slightly above 1 MB).
-m, --Mt32RomsPath Zip file or directory containing the MT-32 ROM files
Expand Down Expand Up @@ -80,7 +81,11 @@ Spice86 speaks the [GDB](https://www.gnu.org/software/gdb/) remote protocol:
- it supports most of the commands you need to debug.
- it also provides custom GDB commands to do dynamic analysis.

### Connecting
### Alternative to GDB

Alternatively, Spice86 has a [home-grown debugger](https://github.com/OpenRakis/Spice86/wiki/Spice86-internal-debugger).

### Connecting to GDB
You need to specify a port for the GDB server to start when launching Spice86:
```
Spice86 --GdbPort=10000
Expand Down Expand Up @@ -121,7 +126,7 @@ GDB does not support x86 real mode segmented addressing, so pointers need to ref

Similarly, The $pc variable in GDB will be exposed by Spice86 as the physical address pointed by CS:IP.

### Custom commands (where the magic happens)
### Custom GDB commands (where the magic happens)
The list of custom commands can be displayed like this:
```
(gdb) monitor help
Expand Down Expand Up @@ -154,6 +159,8 @@ Break at the end of the emulated program:

For a pleasing and productive experience with GDB, the [seerGDB](https://github.com/epasveer/seer) client is highly recommended.



## Reverse engineering process
Concrete example with Cryo Dune [here](https://github.com/OpenRakis/Cryogenic).

Expand Down Expand Up @@ -199,9 +206,9 @@ public class MyProgramOverrideSupplier : IOverrideSupplier {
public class MyOverrides : CSharpOverrideHelper {
private MyOverridesGlobalsOnDs globalsOnDs;

public MyOverrides(IDictionary<SegmentedAddress, FunctionInformation> functionInformations, int segment, Machine machine) {
public MyOverrides(IDictionary<SegmentedAddress, FunctionInformation> functionInformations, ushort entrySegment, Machine machine, ILoggerService loggerService, Configuration configuration) {
// "myOverides" is a prefix that will be appended to all the function names defined in this class
base(functionInformations, "myOverides", machine);
base(functionInformations, machine, loggerService, configuration);
globalsOnDs = new MyOverridesGlobalsOnDs(machine);
// incUnknown47A8_0x1ED_0xA1E8_0xC0B8 will get executed instead of the assembly code when a call to 1ED:A1E8 is performed.
// Also when dumping functions, the name myOverides.incUnknown47A8 or instead of unknown
Expand Down Expand Up @@ -229,18 +236,26 @@ public class MyOverrides : CSharpOverrideHelper {
}

// Memory accesses can be encapsulated into classes like this to give names to addresses and make the code shorter.
public class MyOverridesGlobalsOnDs : MemoryBasedDataStructureWithDsBaseAddress {
public DialoguesGlobalsOnDs(Machine machine) {
base(machine);
}

public void SetDialogueCount47A8(int value) {
this.SetUint8(0x47A8, value);
}

public int GetDialogueCount47A8() {
return this.GetUint8(0x47A8);
}
public class GlobalsOnDs : MemoryBasedDataStructureWithDsBaseAddress {
public GlobalsOnDs(IByteReaderWriter memory, SegmentRegisters segmentRegisters) : base(memory, segmentRegisters) {
}

// Getters and Setters for address 0x1DD:0x2/0x1DD2.
// Was accessed via the following registers: DS
public int Get01DD_0002_Word16() {
return UInt16[0x2];
}

// Operation not registered by running code
public void Set01DD_0002_Word16(byte value) {
UInt16[0x2] = value;
}

// Getters and Setters for address 0x1138:0x0/0x11380.
// Operation not registered by running code
public int Get1138_0000_Word16() {
return UInt16[0x0];
}
}
```

Expand Down Expand Up @@ -329,7 +344,7 @@ Compatibility list available [here](COMPATIBILITY.md).

### How to build on your machine

- Install the .NET 8 SDK (once)
- Install the [.NET 9 SDK](https://dotnet.microsoft.com/en-us/download/dotnet/9.0) (once)
- clone the repo
- run this where Spice86.sln is located:

Expand Down Expand Up @@ -390,7 +405,7 @@ Stunts:

Betrayal at Krondor:

![](doc/BaK.PNG)
![](doc/BaK.png)

## Credits

Expand Down
4 changes: 2 additions & 2 deletions src/Directory.Build.props
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
</PropertyGroup>
<!-- Nuget package info-->
<PropertyGroup>
<Version>7.0.0</Version>
<PackageReleaseNotes>Some breaking API changes (SegmentRegisters.cs), WIP new CFG_CPU, addtionnal memory/disasm views to the internal debugger, replaced UI DI framework with Microsoft.DI.</PackageReleaseNotes>
<Version>8.0.2</Version>
<PackageReleaseNotes>fix: Off by one error generating an IndexOutOfRangeException in the internal debugger Memory tab.</PackageReleaseNotes>
<Authors>Kevin Ferrare, Maximilien Noal, Joris van Eijden, Artjom Vejsel</Authors>
<PackageTags>reverse-engineering;avalonia;debugger;assembly;emulator;cross-platform</PackageTags>
<Description>Reverse engineer and rewrite real mode dos programs</Description>
Expand Down
29 changes: 15 additions & 14 deletions src/Directory.Packages.props
Original file line number Diff line number Diff line change
Expand Up @@ -4,25 +4,26 @@
<CentralPackageTransitivePinningEnabled>true</CentralPackageTransitivePinningEnabled>
</PropertyGroup>
<ItemGroup>
<PackageVersion Include="Avalonia" Version="11.2.1" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.1" />
<PackageVersion Include="Avalonia" Version="11.2.3" />
<PackageVersion Include="Avalonia.Controls.DataGrid" Version="11.2.3" />
<PackageVersion Include="Avalonia.Controls.PanAndZoom" Version="11.2.0" />
<PackageVersion Include="Avalonia.Controls.TreeDataGrid" Version="11.0.10" />
<PackageVersion Include="Avalonia.Desktop" Version="11.2.1" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.1" />
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.2.1" />
<PackageVersion Include="Avalonia.Xaml.Behaviors" Version="11.2.0" />
<PackageVersion Include="Avalonia.Controls.TreeDataGrid" Version="11.1.0" />
<PackageVersion Include="Avalonia.Desktop" Version="11.2.3" />
<PackageVersion Include="Avalonia.Diagnostics" Version="11.2.3" />
<PackageVersion Include="Avalonia.Fonts.Inter" Version="11.2.3" />
<PackageVersion Include="Avalonia.Xaml.Behaviors" Version="11.2.0.1" />
<PackageVersion Include="AvaloniaGraphControl" Version="0.6.1" />
<PackageVersion Include="AvaloniaHex" Version="0.1.5" />
<PackageVersion Include="AvaloniaHex" Version="0.1.6" />
<PackageVersion Include="bodong.Avalonia.PropertyGrid" Version="11.1.4.2" />
<PackageVersion Include="bodong.PropertyModels" Version="11.1.4.2" />
<PackageVersion Include="CommandLineParser" Version="2.9.1" />
<PackageVersion Include="CommunityToolkit.HighPerformance" Version="8.3.2" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.3.2" />
<PackageVersion Include="CommunityToolkit.HighPerformance" Version="8.4.0" />
<PackageVersion Include="CommunityToolkit.Mvvm" Version="8.4.0" />
<PackageVersion Include="coverlet.collector" Version="6.0.2" />
<PackageVersion Include="Deadpikle.AvaloniaProgressRing" Version="0.10.10" />
<PackageVersion Include="DialogHost.Avalonia" Version="0.8.1" />
<PackageVersion Include="ErrorProne.NET.CoreAnalyzers" Version="0.1.2" />
<PackageVersion Include="FluentIcons.Avalonia.Fluent" Version="1.1.265" />
<PackageVersion Include="FluentIcons.Avalonia.Fluent" Version="1.1.271" />
<PackageVersion Include="Iced" Version="1.21.0" />
<PackageVersion Include="JetBrains.Annotations" Version="2024.3.0" />
<PackageVersion Include="JvE.Structurizer" Version="1.0.1" />
Expand All @@ -34,10 +35,10 @@
<PackageVersion Include="NSubstitute.Analyzers.CSharp" Version="1.0.17" />
<PackageVersion Include="Roslynator.Analyzers" Version="4.12.9" />
<PackageVersion Include="Roslynator.CodeAnalysis.Analyzers" Version="4.12.9" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1" />
<PackageVersion Include="Semi.Avalonia" Version="11.2.1.2" />
<PackageVersion Include="Semi.Avalonia.DataGrid" Version="11.2.1.2" />
<PackageVersion Include="Semi.Avalonia.TreeDataGrid" Version="11.0.10.1" />
<PackageVersion Include="Serilog" Version="4.1.0" />
<PackageVersion Include="Serilog" Version="4.2.0" />
<PackageVersion Include="Serilog.Sinks.Console" Version="6.0.0" />
<PackageVersion Include="Serilog.Sinks.Debug" Version="3.0.0" />
<PackageVersion Include="Serilog.Sinks.File" Version="6.0.0" />
Expand Down
20 changes: 14 additions & 6 deletions src/Spice86.Core/Emulator/Devices/Sound/Midi/GeneralMidiDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
using Spice86.Core.Emulator.VM;
using Spice86.Shared.Interfaces;

using System.Linq;

using Windows;

using OperatingSystem = System.OperatingSystem;
Expand All @@ -17,7 +19,7 @@
/// </summary>
public sealed class GeneralMidiDevice : MidiDevice {
private readonly SoundChannel? _soundChannel;
private readonly Synthesizer _synthesizer;
private readonly Synthesizer? _synthesizer;

private bool _disposed;
private bool _threadStarted;
Expand All @@ -33,6 +35,7 @@ public sealed class GeneralMidiDevice : MidiDevice {
/// The file name of the soundfont we load and use for all General MIDI preset sounds.
/// </summary>
public const string SoundFont = "2MGM.sf2";
private const string SoundFontResourceName = "Spice86.Core.2MGM.sf2";

private IntPtr _midiOutHandle;

Expand All @@ -43,7 +46,12 @@ public sealed class GeneralMidiDevice : MidiDevice {
/// <param name="loggerService">The service used to log messages.</param>
/// <param name="pauseHandler">The service for handling pause/resume of emulation.</param>
public GeneralMidiDevice(SoftwareMixer softwareMixer, ILoggerService loggerService, IPauseHandler pauseHandler) {
_synthesizer = new Synthesizer(new SoundFont(SoundFont), 48000);
if (GetType().Assembly.GetManifestResourceNames().Any(x => x == SoundFontResourceName)) {
Stream? resource = GetType().Assembly.GetManifestResourceStream(SoundFontResourceName);
if (resource is not null) {
_synthesizer = new Synthesizer(new SoundFont(resource), 48000);
}
}
_pauseHandler = pauseHandler;
_loggerService = loggerService;
if (!OperatingSystem.IsWindows()) {
Expand Down Expand Up @@ -88,9 +96,9 @@ private void RenderThreadMethod() {
}
}

private void FillBuffer(Synthesizer synthesizer, Span<float> data) {
private void FillBuffer(Synthesizer? synthesizer, Span<float> data) {
ExtractAndProcessMidiMessage(_message, synthesizer);
synthesizer.RenderInterleaved(data);
synthesizer?.RenderInterleaved(data);
}

protected override void PlayShortMessage(uint message) {
Expand All @@ -109,7 +117,7 @@ private void WakeUpRenderThread() {
}
}

private static void ExtractAndProcessMidiMessage(uint packedMessage, Synthesizer synthesizer) {
private static void ExtractAndProcessMidiMessage(uint packedMessage, Synthesizer? synthesizer) {
byte[] bytes = BitConverter.GetBytes(packedMessage);

// Extract MIDI status from the low word, low-order byte
Expand All @@ -131,7 +139,7 @@ private static void ExtractAndProcessMidiMessage(uint packedMessage, Synthesizer
// find the channel by masking off all but the low 4 bits
byte channel = (byte)(midiStatus & 0x0F);

synthesizer.ProcessMidiMessage(channel, command, data1, data2);
synthesizer?.ProcessMidiMessage(channel, command, data1, data2);
}

/// <summary>
Expand Down
7 changes: 5 additions & 2 deletions src/Spice86.Core/Emulator/Devices/Video/Renderer.cs
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@ namespace Spice86.Core.Emulator.Devices.Video;
/// <inheritdoc cref="IVgaRenderer" />
public class Renderer : IVgaRenderer {
private static readonly object RenderLock = new();
private readonly IVideoMemory _memory;
private readonly VideoMemory _memory;
private readonly IVideoState _state;

/// <summary>
Expand Down Expand Up @@ -194,7 +194,10 @@ private MemoryWidth DetermineMemoryWidthMode() {
return memoryWidthMode;
}

private (byte plane0, byte plane1, byte plane2, byte plane3) ReadVideoMemory(MemoryWidth memoryWidthMode, int memoryAddressCounter, bool scanLineBit0ForAddressBit13, int scanline, bool scanLineBit0ForAddressBit14, IReadOnlyList<bool> planesEnabled) {
private (byte plane0, byte plane1, byte plane2, byte plane3) ReadVideoMemory(
MemoryWidth memoryWidthMode, int memoryAddressCounter,
bool scanLineBit0ForAddressBit13, int scanline,
bool scanLineBit0ForAddressBit14, ReadOnlySpan<bool> planesEnabled) {
// Convert logical address to physical address.
ushort physicalAddress = memoryWidthMode switch {

Check warning on line 202 in src/Spice86.Core/Emulator/Devices/Video/Renderer.cs

View workflow job for this annotation

GitHub Actions / Analyze (csharp)

The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. For example, the pattern '(Spice86.Core.Emulator.Devices.Video.MemoryWidth)4' is not covered.

Check warning on line 202 in src/Spice86.Core/Emulator/Devices/Video/Renderer.cs

View workflow job for this annotation

GitHub Actions / build (ubuntu-latest)

The switch expression does not handle some values of its input type (it is not exhaustive) involving an unnamed enum value. For example, the pattern '(Spice86.Core.Emulator.Devices.Video.MemoryWidth)4' is not covered.
MemoryWidth.Byte => (ushort)memoryAddressCounter,
Expand Down
5 changes: 3 additions & 2 deletions src/Spice86.Core/Emulator/Devices/Video/VideoMemory.cs
Original file line number Diff line number Diff line change
Expand Up @@ -142,7 +142,7 @@ private void HandleWriteMode2(byte value, Register8 planeEnable, bool[] writePla
}
}

private void HandleWriteMode1(Register8 planeEnable, IReadOnlyList<bool> writePlane, uint offset) {
private void HandleWriteMode1(Register8 planeEnable, ReadOnlySpan<bool> writePlane, uint offset) {
// Foreach plane
for (int plane = 0; plane < 4; plane++) {
if (planeEnable[plane] && writePlane[plane]) {
Expand All @@ -151,7 +151,8 @@ private void HandleWriteMode1(Register8 planeEnable, IReadOnlyList<bool> writePl
}
}

private void HandleWriteMode0(byte value, Register8 planeEnable, IReadOnlyList<bool> writePlane, Register8 setResetEnable, Register8 setReset, uint offset) {
private void HandleWriteMode0(byte value, Register8 planeEnable,
ReadOnlySpan<bool> writePlane, Register8 setResetEnable, Register8 setReset, uint offset) {
Debug.Assert(offset < 0x10000);
if (_state.GraphicsControllerRegisters.DataRotateRegister.RotateCount != 0) {
value.Ror(_state.GraphicsControllerRegisters.DataRotateRegister.RotateCount);
Expand Down
8 changes: 5 additions & 3 deletions src/Spice86.Core/Emulator/Gdb/GdbCommandBreakPointHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -39,9 +39,11 @@ public GdbCommandBreakpointHandler(EmulatorBreakpointsManager emulatorBreakpoint
/// <returns>A response string to send back to GDB.</returns>
public string AddBreakpoint(string commandContent) {
BreakPoint? breakPoint = ParseBreakPoint(commandContent);
_emulatorBreakpointsManager.ToggleBreakPoint(breakPoint, true);
if (_loggerService.IsEnabled(LogEventLevel.Debug)) {
_loggerService.Debug("Breakpoint added!\n{@BreakPoint}", breakPoint);
if(breakPoint is not null) {
_emulatorBreakpointsManager.ToggleBreakPoint(breakPoint, true);
if (_loggerService.IsEnabled(LogEventLevel.Debug)) {
_loggerService.Debug("Breakpoint added!\n{@BreakPoint}", breakPoint);
}
}
return _gdbIo.GenerateResponse("OK");
}
Expand Down
10 changes: 5 additions & 5 deletions src/Spice86.Core/Emulator/Gdb/GdbCommandHandler.cs
Original file line number Diff line number Diff line change
Expand Up @@ -135,12 +135,12 @@ private string Kill() {

private Tuple<string, object> ParseSupportedQuery(string item) {
Tuple<string, object> res;
if (item.EndsWith("+")) {
if (item.EndsWith('+')) {
res = Tuple.Create(item[0..^1], (object)true);
} else if (item.EndsWith("-")) {
} else if (item.EndsWith('-')) {
res = Tuple.Create(item[0..^1], (object)false);
} else {
string[] split = item.Split("=");
string[] split = item.Split('=');
res = Tuple.Create(split[0], new object());
if (split.Length == 2) {
res = Tuple.Create(split[0], (object)split[1]);
Expand Down Expand Up @@ -171,12 +171,12 @@ private string QueryVariable(string command) {
return _gdbIo.GenerateResponse("");
}

if (command.StartsWith("L")) {
if (command.StartsWith('L')) {
string nextthread = command[4..];
return _gdbIo.GenerateResponse($"qM011{nextthread}00000001");
}

if (command.StartsWith("P")) {
if (command.StartsWith('P')) {
return _gdbIo.GenerateResponse("");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -30,7 +30,7 @@ public MemoryAsmWriter(IIndexable memory, SegmentedAddress beginningAddress, Cal
/// <param name="callbackNumber">Callback index</param>
/// <param name="runnable">Action to run when this callback is executed by the CPU</param>
public void RegisterAndWriteCallback(byte callbackNumber, Action runnable) {
ICallback callback = new Callback(callbackNumber, runnable, GetCurrentAddressCopy());
Callback callback = new Callback(callbackNumber, runnable, GetCurrentAddressCopy());
_callbackHandler.AddCallback(callback);
WriteCallback(callback.Index);
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ public class DosInt21Handler : InterruptHandler {

private StringBuilder _displayOutputBuilder = new();
private readonly DosFileManager _dosFileManager;
private readonly IList<IVirtualDevice> _devices;
private readonly List<IVirtualDevice> _devices;
private readonly Dos _dos;
private readonly KeyboardInt16Handler _keyboardInt16Handler;
private readonly IVgaFunctionality _vgaFunctionality;
Expand Down
Loading

0 comments on commit 2141bc9

Please sign in to comment.