Skip to content
Closed
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
20 commits
Select commit Hold shift + click to select a range
51a0fe9
enhancement: Added fast Span<T>-based APIs to IReaderWriter<T>.
assumenothing May 19, 2026
1042a0f
enhancement: Update indexer classes to utilize Span<T> methods in IBy…
assumenothing May 19, 2026
e7dcfe7
enhancement: Various improvements to Indexable class.
assumenothing May 19, 2026
9e5a832
fix: Return correct length span in ArrayReaderWriter<T>.TryGetSpan me…
assumenothing May 20, 2026
5223641
fix: Various fixes to ByteArrayBasedIndexable methods.
assumenothing May 20, 2026
8a56b0b
enhancement: Improvements to AbstractMemoryBasedDataStructure.
assumenothing May 20, 2026
b03a4ea
enhancement: Added MemoryAccess parameter and ReadOnlySpan<T> overloa…
assumenothing May 20, 2026
db66677
fix: Restore Indexable.SetSpacePaddedString method to original trunca…
assumenothing May 20, 2026
56b672f
enhancement: Added new span-based APIs from Indexable to IIndexable.
assumenothing May 21, 2026
8f145d3
fix: Prevent exception from being thrown when Indexable.SetZeroTermin…
assumenothing May 22, 2026
4ffd71a
enhancement: Change access modifiers for MemoryIndexer methods ReadSe…
assumenothing May 22, 2026
ece7469
fix: Fixed bug in ByteArrayBasedIndexable and AbstractMemoryBasedData…
assumenothing May 23, 2026
2e26663
refactor: Optimized RealModeMmu386.IsValidAccess method.
assumenothing May 23, 2026
3156b15
enhancement: Added virtual method IMmu.TryTranslateAddressRange.
assumenothing May 23, 2026
201b95e
chore: Consolidated and simplified Indexable string/memory helper fun…
assumenothing May 24, 2026
af67201
Use Indexable.DisableSpanAccess property on the various Indexer classes.
assumenothing May 24, 2026
14e12fe
fix: Changed default implementation of IReaderWriter.TryGetSpan.
assumenothing May 25, 2026
ed77d55
feature: Implemented basic span support for Memory and IMemoryDevice.
assumenothing May 25, 2026
feed0dd
fix: Misc. updates/fixes to Span-related code and Indexable.
assumenothing May 25, 2026
8b3ea82
feature: Split span access methods between Indexable methods and Memo…
assumenothing May 25, 2026
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
7 changes: 3 additions & 4 deletions src/Spice86.Core/Emulator/Devices/Video/VideoMemory.cs
Original file line number Diff line number Diff line change
@@ -1,13 +1,12 @@
namespace Spice86.Core.Emulator.Devices.Video;

using System;
using System.Runtime.InteropServices;
using System.Threading;

using Spice86.Core.Emulator.Devices.Video.Registers;
using Spice86.Core.Emulator.Devices.Video.Registers.Graphics;
using Spice86.Shared.Interfaces;

using System;
using System.Threading;

/// <summary>
/// A wrapper class for the video card that implements the IMemoryDevice interface.
/// </summary>
Expand Down
24 changes: 24 additions & 0 deletions src/Spice86.Core/Emulator/InterruptHandlers/Dos/Ems/EmmPage.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,28 @@ public EmmPage(uint size) {

/// <inheritdoc />
public IList<byte> GetSlice(int address, int length) => _pageMemory.GetSlice(address, length);

/// <inheritdoc/>
public bool TryGetSpan(out uint startAddress, out Span<byte> span, MemoryAccess access)
=> _pageMemory.TryGetSpan(out startAddress, out span, access);

/// <inheritdoc/>
public bool TryGetSpan(out uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access)
=> _pageMemory.TryGetSpan(out startAddress, out span, access);

/// <inheritdoc/>
public bool TryGetSpan(uint startAddress, out Span<byte> span, MemoryAccess access)
=> _pageMemory.TryGetSpan(startAddress, out span, access);

/// <inheritdoc/>
public bool TryGetSpan(uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access)
=> _pageMemory.TryGetSpan(startAddress, out span, access);

/// <inheritdoc/>
public bool TryGetSpan(uint startAddress, int length, out Span<byte> span, MemoryAccess access)
=> _pageMemory.TryGetSpan(startAddress, length, out span, access);

/// <inheritdoc/>
public bool TryGetSpan(uint startAddress, int length, out ReadOnlySpan<byte> span, MemoryAccess access)
=> _pageMemory.TryGetSpan(startAddress, length, out span, access);
}
26 changes: 26 additions & 0 deletions src/Spice86.Core/Emulator/InterruptHandlers/Dos/Ems/EmmRegister.cs
Original file line number Diff line number Diff line change
Expand Up @@ -45,4 +45,30 @@ public void Write(uint address, byte value) {
public IList<byte> GetSlice(int address, int length) {
return PhysicalPage.GetSlice((int)(address - Offset), length);
}

/// <inheritdoc/>
public bool TryGetSpan(uint startAddress, int length, out Span<byte> span, MemoryAccess access) {
if (length >= 0) {
long pageAddress = (long)startAddress - Offset;
if (pageAddress >= 0 && length <= ExpandedMemoryManager.EmmPageSize - pageAddress) {
return PhysicalPage.TryGetSpan((uint)pageAddress, length, out span, access);
}
}

span = [];
return false;
}

/// <inheritdoc/>
public bool TryGetSpan(uint startAddress, int length, out ReadOnlySpan<byte> span, MemoryAccess access) {
if (length >= 0) {
long pageAddress = (long)startAddress - Offset;
if (pageAddress >= 0 && length <= ExpandedMemoryManager.EmmPageSize - pageAddress) {
return PhysicalPage.TryGetSpan((uint)pageAddress, length, out span, access);
}
}

span = [];
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -1393,15 +1393,15 @@ private void ClearScreen(VgaMode vgaMode) {
switch (vgaMode.MemoryModel) {
case MemoryModel.Text:
// Write white (0x07) spaces (0x20) to memory.
MemSet16(vgaMode.StartSegment, 0, 0x0720, 32 * 1024);
MemSet16(vgaMode.StartSegment, 0, 0x0720, 16 * 1024);
break;
case MemoryModel.Cga:
MemSet16(vgaMode.StartSegment, 0, 0x0000, 32 * 1024);
MemSet16(vgaMode.StartSegment, 0, 0x0000, 16 * 1024);
break;
case MemoryModel.Planar:
case MemoryModel.Packed:
default:
MemSet16(vgaMode.StartSegment, 0, 0x0000, 64 * 1024);
MemSet16(vgaMode.StartSegment, 0, 0x0000, 32 * 1024);
break;
}
}
Expand Down
84 changes: 84 additions & 0 deletions src/Spice86.Core/Emulator/InterruptHandlers/VGA/VgaRom.cs
Original file line number Diff line number Diff line change
Expand Up @@ -77,6 +77,90 @@ public void Write(uint address, byte value) {

/// <inheritdoc />
public IList<byte> GetSlice(int address, int length) {
// Note that this bypasses the write limitations and allows memory to be modified.
return _storage.GetSlice(address - BaseAddress, length);
}

/// <inheritdoc/>
public bool TryGetSpan(out uint startAddress, out Span<byte> span, MemoryAccess access) {
// Only allow write access to VGA ROM if no read/write access is requested (bypass mode).
if ((access & MemoryAccess.ReadWrite) == MemoryAccess.None
&& MemoryDeviceUtils.TryGetSpan(_storage, out _, out span)) {
startAddress = BaseAddress;
return true;
}

startAddress = 0;
span = [];
return false;
}

/// <inheritdoc/>
public bool TryGetSpan(out uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access) {
if ((access & MemoryAccess.ReadWrite) == MemoryAccess.None
&& MemoryDeviceUtils.TryGetSpan(_storage, out _, out span)) {
startAddress = BaseAddress;
return true;
}

startAddress = 0;
span = [];
return false;
}

/// <inheritdoc/>
public bool TryGetSpan(uint startAddress, out Span<byte> span, MemoryAccess access) {
// Only allow write access to VGA ROM if no read/write access is requested (bypass mode).
if ((access & MemoryAccess.ReadWrite) == MemoryAccess.None) {
long romAddress = (long)startAddress - BaseAddress;
if (romAddress >= 0) {
return MemoryDeviceUtils.TryGetSpan(_storage, (uint)romAddress, out span);
}
}

span = [];
return false;
}

/// <inheritdoc/>
public bool TryGetSpan(uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access) {
if (!access.HasFlag(MemoryAccess.Write)) {
long romAddress = (long)startAddress - BaseAddress;
if (romAddress >= 0) {
return MemoryDeviceUtils.TryGetSpan(_storage, (uint)romAddress, out span);
}
}

span = [];
return false;
}

/// <inheritdoc/>
public bool TryGetSpan(uint startAddress, int length, out Span<byte> span, MemoryAccess access) {
// Only allow write access to VGA ROM if no read/write access is requested (bypass mode).
if ((access & MemoryAccess.ReadWrite) == MemoryAccess.None) {
long romAddress = (long)startAddress - BaseAddress;
if (romAddress >= 0) {
return MemoryDeviceUtils.TryGetSpan(_storage, (uint)romAddress, length, out span);
}
}

startAddress = 0;
span = [];
return false;
}

/// <inheritdoc/>
public bool TryGetSpan(uint startAddress, int length, out ReadOnlySpan<byte> span, MemoryAccess access) {
if (!access.HasFlag(MemoryAccess.Write)) {
long romAddress = (long)startAddress - BaseAddress;
if (romAddress >= 0) {
return MemoryDeviceUtils.TryGetSpan(_storage, (uint)romAddress, length, out span);
}
}

startAddress = 0;
span = [];
return false;
}
}
96 changes: 96 additions & 0 deletions src/Spice86.Core/Emulator/Memory/IMemoryDevice.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,4 +32,100 @@ uint Size {
/// <param name="length">The length of the slice</param>
/// <returns>A byte list</returns>
public IList<byte> GetSlice(int address, int length);

/// <summary>
/// Attempts to get a span from the reader/writer.
/// </summary>
/// <param name="startAddress">The starting address for the returned span.</param>
/// <param name="span">A span containing all the bytes in the requested memory.</param>
/// <param name="access">Specifies the type of memory access that is requested for the span.</param>
/// <returns><see langword="true"/> if the span was successfully retrieved; otherwise, <see langword="false"/>.</returns>
public virtual bool TryGetSpan(out uint startAddress, out Span<byte> span, MemoryAccess access) {
startAddress = 0;
if ((int)Size >= 0) {
return TryGetSpan(0, (int)Size, out span, access);
}

span = [];
return false;
}

/// <summary>
/// Attempts to get a read only span from the reader/writer.
/// </summary>
/// <param name="startAddress">The starting address for the returned span.</param>
/// <param name="span">A read only span containing all the bytes in the requested memory.</param>
/// <param name="access">Specifies the type of memory access that is requested for the span.</param>
/// <returns><see langword="true"/> if the span was successfully retrieved; otherwise, <see langword="false"/>.</returns>
public virtual bool TryGetSpan(out uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access) {
bool result = TryGetSpan(out startAddress, out Span<byte> mutableSpan, access);
span = mutableSpan;
return result;
}

/// <summary>
/// Attempts to get a span from a portion of the reader/writer.
/// </summary>
/// <param name="startAddress">The starting address to request bytes from.</param>
/// <param name="span">A span containing the remaining bytes starting at the given address.</param>
/// <param name="access">Specifies the type of memory access that is requested for the span.</param>
/// <returns><see langword="true"/> if the span was successfully retrieved; otherwise, <see langword="false"/>.</returns>
public virtual bool TryGetSpan(uint startAddress, out Span<byte> span, MemoryAccess access) {
if ((long)Size - startAddress is >= 0 and <= int.MaxValue) {
return TryGetSpan(startAddress, (int)(Size - startAddress), out span, access);
}

span = [];
return false;
}

/// <summary>
/// Attempts to get a read only span from a portion of the reader/writer.
/// </summary>
/// <param name="startAddress">The starting address to request bytes from.</param>
/// <param name="span">A span containing the remaining bytes starting at the given address.</param>
/// <param name="access">Specifies the type of memory access that is requested for the span.</param>
/// <returns><see langword="true"/> if the span was successfully retrieved; otherwise, <see langword="false"/>.</returns>
public virtual bool TryGetSpan(uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access) {
bool result = TryGetSpan(startAddress, out Span<byte> mutableSpan, access);
span = mutableSpan;
return result;
}

/// <summary>
/// Attempts to get a span from a portion of the reader/writer.
/// </summary>
/// <param name="startAddress">The starting address to request bytes from.</param>
/// <param name="length">The number of bytes requested. A negative length should always result in a failure.</param>
/// <param name="span">A span containing the number of requested bytes starting at the given address.</param>
/// <param name="access">Specifies the type of memory access that is requested for the span.</param>
/// <returns><see langword="true"/> if the span was successfully retrieved; otherwise, <see langword="false"/>.</returns>
/// <remarks>
/// Implementors should always return a span with <paramref name="length"/> bytes if successful. Callers should
/// only assume that all implementors will return a span with <em>at least</em> <paramref name="length"/> bytes
/// on success.
/// </remarks>
public virtual bool TryGetSpan(uint startAddress, int length, out Span<byte> span, MemoryAccess access) {
span = [];
return false;
}

/// <summary>
/// Attempts to get a read only span from a portion of the reader/writer.
/// </summary>
/// <param name="startAddress">The starting address to request bytes from.</param>
/// <param name="length">The number of bytes requested. A negative length should always result in a failure.</param>
/// <param name="span">A span containing the number of requested bytes starting at the given address.</param>
/// <param name="access">Specifies the type of memory access that is requested for the span.</param>
/// <returns><see langword="true"/> if the span was successfully retrieved; otherwise, <see langword="false"/>.</returns>
/// <remarks>
/// Implementors should always return a span with <paramref name="length"/> bytes if successful. Callers should
/// only assume that all implementors will return a span with <em>at least</em> <paramref name="length"/> bytes
/// on success.
/// </remarks>
public virtual bool TryGetSpan(uint startAddress, int length, out ReadOnlySpan<byte> span, MemoryAccess access) {
bool result = TryGetSpan(startAddress, length, out Span<byte> mutableSpan, access);
span = mutableSpan;
return result;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,7 @@ namespace Spice86.Core.Emulator.Memory.Indexable;
using Spice86.Core.Emulator.Memory.ReaderWriter;

/// <summary>
/// Implementation of Indexable over a byte array.
/// Implementation of <see cref="Indexable"/> over a byte array.
/// </summary>
public class ByteArrayBasedIndexable : Indexable {

Expand All @@ -20,32 +20,33 @@ public class ByteArrayBasedIndexable : Indexable {
public byte[] Array { get => ReaderWriter.Array; }

/// <inheritdoc/>
public override UInt8Indexer UInt8 { get; }
public sealed override UInt8Indexer UInt8 { get; }

/// <inheritdoc/>
public override UInt16Indexer UInt16 { get; }
public sealed override UInt16Indexer UInt16 { get; }

/// <inheritdoc/>
public override UInt32Indexer UInt32 { get; }
public sealed override UInt32Indexer UInt32 { get; }

/// <inheritdoc/>
public override Int8Indexer Int8 { get; }
public sealed override Int8Indexer Int8 { get; }

/// <inheritdoc/>
public override Int16Indexer Int16 { get; }
public sealed override Int16Indexer Int16 { get; }

/// <inheritdoc/>
public override UInt16BigEndianIndexer UInt16BigEndian { get; }
public sealed override UInt16BigEndianIndexer UInt16BigEndian { get; }

/// <inheritdoc/>
public override Int32Indexer Int32 { get; }
public sealed override Int32Indexer Int32 { get; }

/// <inheritdoc/>
public override SegmentedAddress16Indexer SegmentedAddress16 {
public sealed override SegmentedAddress16Indexer SegmentedAddress16 {
get;
}

/// <inheritdoc/>
public override SegmentedAddress32Indexer SegmentedAddress32 {
public sealed override SegmentedAddress32Indexer SegmentedAddress32 {
get;
}

Expand All @@ -55,6 +56,7 @@ public override SegmentedAddress32Indexer SegmentedAddress32 {
/// <param name="array">The byte array used as RAM storage.</param>
public ByteArrayBasedIndexable(byte[] array) {
ReaderWriter = new ByteArrayReaderWriter(array);
(UInt8, UInt16, UInt16BigEndian, UInt32, Int8, Int16, Int32, SegmentedAddress16, SegmentedAddress32) = InstantiateIndexersFromByteReaderWriter(ReaderWriter, new RealModeMmu8086());
(UInt8, UInt16, UInt16BigEndian, UInt32, Int8, Int16, Int32, SegmentedAddress16, SegmentedAddress32) =
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
Comment thread
github-advanced-security[bot] marked this conversation as resolved.
Fixed
InstantiateIndexersFromByteReaderWriter(ReaderWriter, new RealModeMmu8086());
}
}
}
Loading
Loading