Skip to content

Commit ed77d55

Browse files
committed
feature: Implemented basic span support for Memory and IMemoryDevice.
- This also extends span support to Ram, VideoMemory, EmmPage, EmmRegister, and VgaRom. - Added HasActiveBreakPoint to BreakPointHolder and AddressReadWriteBreakpoints (necessary for span access through Memory). - This appears to result in a performance regression when running the unit tests (likely due to heavy use of breakpoints during emulation--resulting in possibly doubling the number of expensive breakpoint checks per read/write operation). Could also be partially due to the extra defensive checks that are required for validating span memory ranges.
1 parent 14e12fe commit ed77d55

10 files changed

Lines changed: 522 additions & 15 deletions

File tree

src/Spice86.Core/Emulator/Devices/Video/VideoMemory.cs

Lines changed: 28 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,13 +1,13 @@
11
namespace Spice86.Core.Emulator.Devices.Video;
22

3-
using System;
4-
using System.Runtime.InteropServices;
5-
using System.Threading;
6-
73
using Spice86.Core.Emulator.Devices.Video.Registers;
84
using Spice86.Core.Emulator.Devices.Video.Registers.Graphics;
5+
using Spice86.Core.Emulator.Memory;
96
using Spice86.Shared.Interfaces;
107

8+
using System;
9+
using System.Threading;
10+
1111
/// <summary>
1212
/// A wrapper class for the video card that implements the IMemoryDevice interface.
1313
/// </summary>
@@ -338,4 +338,28 @@ private void WriteValue(int plane, uint offset, byte value) {
338338

339339
return (planes, offset);
340340
}
341+
342+
/// <inheritdoc/>
343+
public bool TryGetSpan(out uint startAddress, out Span<byte> span, MemoryAccess access)
344+
=> MemoryDeviceUtils.TryGetSpan(VRam, out startAddress, out span);
345+
346+
/// <inheritdoc/>
347+
public bool TryGetSpan(out uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access)
348+
=> MemoryDeviceUtils.TryGetSpan(VRam, out startAddress, out span);
349+
350+
/// <inheritdoc/>
351+
public bool TryGetSpan(uint startAddress, out Span<byte> span, MemoryAccess access)
352+
=> MemoryDeviceUtils.TryGetSpan(VRam, startAddress, out span);
353+
354+
/// <inheritdoc/>
355+
public bool TryGetSpan(uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access)
356+
=> MemoryDeviceUtils.TryGetSpan(VRam, startAddress, out span);
357+
358+
/// <inheritdoc/>
359+
public bool TryGetSpan(uint startAddress, int length, out Span<byte> span, MemoryAccess access)
360+
=> MemoryDeviceUtils.TryGetSpan(VRam, startAddress, length, out span);
361+
362+
/// <inheritdoc/>
363+
public bool TryGetSpan(uint startAddress, int length, out ReadOnlySpan<byte> span, MemoryAccess access)
364+
=> MemoryDeviceUtils.TryGetSpan(VRam, startAddress, length, out span);
341365
}

src/Spice86.Core/Emulator/InterruptHandlers/Dos/Ems/EmmPage.cs

Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,28 @@ public EmmPage(uint size) {
3232

3333
/// <inheritdoc />
3434
public IList<byte> GetSlice(int address, int length) => _pageMemory.GetSlice(address, length);
35+
36+
/// <inheritdoc/>
37+
public bool TryGetSpan(out uint startAddress, out Span<byte> span, MemoryAccess access)
38+
=> _pageMemory.TryGetSpan(out startAddress, out span, access);
39+
40+
/// <inheritdoc/>
41+
public bool TryGetSpan(out uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access)
42+
=> _pageMemory.TryGetSpan(out startAddress, out span, access);
43+
44+
/// <inheritdoc/>
45+
public bool TryGetSpan(uint startAddress, out Span<byte> span, MemoryAccess access)
46+
=> _pageMemory.TryGetSpan(startAddress, out span, access);
47+
48+
/// <inheritdoc/>
49+
public bool TryGetSpan(uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access)
50+
=> _pageMemory.TryGetSpan(startAddress, out span, access);
51+
52+
/// <inheritdoc/>
53+
public bool TryGetSpan(uint startAddress, int length, out Span<byte> span, MemoryAccess access)
54+
=> _pageMemory.TryGetSpan(startAddress, length, out span, access);
55+
56+
/// <inheritdoc/>
57+
public bool TryGetSpan(uint startAddress, int length, out ReadOnlySpan<byte> span, MemoryAccess access)
58+
=> _pageMemory.TryGetSpan(startAddress, length, out span, access);
3559
}

src/Spice86.Core/Emulator/InterruptHandlers/Dos/Ems/EmmRegister.cs

Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -45,4 +45,26 @@ public void Write(uint address, byte value) {
4545
public IList<byte> GetSlice(int address, int length) {
4646
return PhysicalPage.GetSlice((int)(address - Offset), length);
4747
}
48+
49+
/// <inheritdoc/>
50+
public bool TryGetSpan(uint startAddress, int length, out Span<byte> span, MemoryAccess access) {
51+
long pageAddress = (long)startAddress - Offset;
52+
if (pageAddress >= 0 && length >= 0 && length <= ExpandedMemoryManager.EmmPageSize - pageAddress) {
53+
return PhysicalPage.TryGetSpan((uint)pageAddress, length, out span, access);
54+
}
55+
56+
span = [];
57+
return false;
58+
}
59+
60+
/// <inheritdoc/>
61+
public bool TryGetSpan(uint startAddress, int length, out ReadOnlySpan<byte> span, MemoryAccess access) {
62+
long pageAddress = (long)startAddress - Offset;
63+
if (pageAddress >= 0 && length >= 0 && length <= ExpandedMemoryManager.EmmPageSize - pageAddress) {
64+
return PhysicalPage.TryGetSpan((uint)pageAddress, length, out span, access);
65+
}
66+
67+
span = [];
68+
return false;
69+
}
4870
}

src/Spice86.Core/Emulator/InterruptHandlers/VGA/VgaRom.cs

Lines changed: 68 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -77,6 +77,74 @@ public void Write(uint address, byte value) {
7777

7878
/// <inheritdoc />
7979
public IList<byte> GetSlice(int address, int length) {
80+
// Note that this bypasses the write limitations and allows memory to be modified.
8081
return _storage.GetSlice(address - BaseAddress, length);
8182
}
83+
84+
/// <inheritdoc/>
85+
public bool TryGetSpan(out uint startAddress, out Span<byte> span, MemoryAccess access) {
86+
// Only allow write access to VGA ROM if no read/write access is requested (bypass mode).
87+
if ((access & MemoryAccess.ReadWrite) == MemoryAccess.None) {
88+
return MemoryDeviceUtils.TryGetSpan(_storage, out startAddress, out span);
89+
}
90+
91+
startAddress = 0;
92+
span = [];
93+
return false;
94+
}
95+
96+
/// <inheritdoc/>
97+
public bool TryGetSpan(out uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access) {
98+
if (!access.HasFlag(MemoryAccess.Write)) {
99+
return MemoryDeviceUtils.TryGetSpan(_storage, out startAddress, out span);
100+
}
101+
102+
startAddress = 0;
103+
span = [];
104+
return false;
105+
}
106+
107+
/// <inheritdoc/>
108+
public bool TryGetSpan(uint startAddress, out Span<byte> span, MemoryAccess access) {
109+
// Only allow write access to VGA ROM if no read/write access is requested (bypass mode).
110+
if ((access & MemoryAccess.ReadWrite) == MemoryAccess.None) {
111+
return MemoryDeviceUtils.TryGetSpan(_storage, startAddress, out span);
112+
}
113+
114+
span = [];
115+
return false;
116+
}
117+
118+
/// <inheritdoc/>
119+
public bool TryGetSpan(uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access) {
120+
if (!access.HasFlag(MemoryAccess.Write)) {
121+
return MemoryDeviceUtils.TryGetSpan(_storage, startAddress, out span);
122+
}
123+
124+
span = [];
125+
return false;
126+
}
127+
128+
/// <inheritdoc/>
129+
public bool TryGetSpan(uint startAddress, int length, out Span<byte> span, MemoryAccess access) {
130+
// Only allow write access to VGA ROM if no read/write access is requested (bypass mode).
131+
if ((access & MemoryAccess.ReadWrite) == MemoryAccess.None) {
132+
return MemoryDeviceUtils.TryGetSpan(_storage, startAddress, length, out span);
133+
}
134+
135+
startAddress = 0;
136+
span = [];
137+
return false;
138+
}
139+
140+
/// <inheritdoc/>
141+
public bool TryGetSpan(uint startAddress, int length, out ReadOnlySpan<byte> span, MemoryAccess access) {
142+
if (!access.HasFlag(MemoryAccess.Write)) {
143+
return MemoryDeviceUtils.TryGetSpan(_storage, startAddress, length, out span);
144+
}
145+
146+
startAddress = 0;
147+
span = [];
148+
return false;
149+
}
82150
}

src/Spice86.Core/Emulator/Memory/IMemoryDevice.cs

Lines changed: 96 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,4 +32,100 @@ uint Size {
3232
/// <param name="length">The length of the slice</param>
3333
/// <returns>A byte list</returns>
3434
public IList<byte> GetSlice(int address, int length);
35+
36+
/// <summary>
37+
/// Attempts to get a span from the reader/writer.
38+
/// </summary>
39+
/// <param name="startAddress">The starting address for the returned span.</param>
40+
/// <param name="span">A span containing all the bytes in the requested memory.</param>
41+
/// <param name="access">Specifies the type of memory access that is requested for the span.</param>
42+
/// <returns><see langword="true"/> if the span was successfully retrieved; otherwise, <see langword="false"/>.</returns>
43+
public virtual bool TryGetSpan(out uint startAddress, out Span<byte> span, MemoryAccess access) {
44+
startAddress = 0;
45+
if ((int)Size >= 0) {
46+
return TryGetSpan(0, (int)Size, out span, access);
47+
}
48+
49+
span = [];
50+
return false;
51+
}
52+
53+
/// <summary>
54+
/// Attempts to get a read only span from the reader/writer.
55+
/// </summary>
56+
/// <param name="startAddress">The starting address for the returned span.</param>
57+
/// <param name="span">A read only span containing all the bytes in the requested memory.</param>
58+
/// <param name="access">Specifies the type of memory access that is requested for the span.</param>
59+
/// <returns><see langword="true"/> if the span was successfully retrieved; otherwise, <see langword="false"/>.</returns>
60+
public virtual bool TryGetSpan(out uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access) {
61+
bool result = TryGetSpan(out startAddress, out Span<byte> mutableSpan, access);
62+
span = mutableSpan;
63+
return result;
64+
}
65+
66+
/// <summary>
67+
/// Attempts to get a span from a portion of the reader/writer.
68+
/// </summary>
69+
/// <param name="startAddress">The starting address to request bytes from.</param>
70+
/// <param name="span">A span containing the remaining bytes starting at the given address.</param>
71+
/// <param name="access">Specifies the type of memory access that is requested for the span.</param>
72+
/// <returns><see langword="true"/> if the span was successfully retrieved; otherwise, <see langword="false"/>.</returns>
73+
public virtual bool TryGetSpan(uint startAddress, out Span<byte> span, MemoryAccess access) {
74+
if ((long)Size - startAddress is >= 0 and <= int.MaxValue) {
75+
return TryGetSpan(startAddress, (int)(Size - startAddress), out span, access);
76+
}
77+
78+
span = [];
79+
return false;
80+
}
81+
82+
/// <summary>
83+
/// Attempts to get a read only span from a portion of the reader/writer.
84+
/// </summary>
85+
/// <param name="startAddress">The starting address to request bytes from.</param>
86+
/// <param name="span">A span containing the remaining bytes starting at the given address.</param>
87+
/// <param name="access">Specifies the type of memory access that is requested for the span.</param>
88+
/// <returns><see langword="true"/> if the span was successfully retrieved; otherwise, <see langword="false"/>.</returns>
89+
public virtual bool TryGetSpan(uint startAddress, out ReadOnlySpan<byte> span, MemoryAccess access) {
90+
bool result = TryGetSpan(startAddress, out Span<byte> mutableSpan, access);
91+
span = mutableSpan;
92+
return result;
93+
}
94+
95+
/// <summary>
96+
/// Attempts to get a span from a portion of the reader/writer.
97+
/// </summary>
98+
/// <param name="startAddress">The starting address to request bytes from.</param>
99+
/// <param name="length">The number of bytes requested. A negative length should always result in a failure.</param>
100+
/// <param name="span">A span containing the number of requested bytes starting at the given address.</param>
101+
/// <param name="access">Specifies the type of memory access that is requested for the span.</param>
102+
/// <returns><see langword="true"/> if the span was successfully retrieved; otherwise, <see langword="false"/>.</returns>
103+
/// <remarks>
104+
/// Implementors should always return a span with <paramref name="length"/> bytes if successful. Callers should
105+
/// only assume that all implementors will return a span with <em>at least</em> <paramref name="length"/> bytes
106+
/// on success.
107+
/// </remarks>
108+
public virtual bool TryGetSpan(uint startAddress, int length, out Span<byte> span, MemoryAccess access) {
109+
span = [];
110+
return false;
111+
}
112+
113+
/// <summary>
114+
/// Attempts to get a read only span from a portion of the reader/writer.
115+
/// </summary>
116+
/// <param name="startAddress">The starting address to request bytes from.</param>
117+
/// <param name="length">The number of bytes requested. A negative length should always result in a failure.</param>
118+
/// <param name="span">A span containing the number of requested bytes starting at the given address.</param>
119+
/// <param name="access">Specifies the type of memory access that is requested for the span.</param>
120+
/// <returns><see langword="true"/> if the span was successfully retrieved; otherwise, <see langword="false"/>.</returns>
121+
/// <remarks>
122+
/// Implementors should always return a span with <paramref name="length"/> bytes if successful. Callers should
123+
/// only assume that all implementors will return a span with <em>at least</em> <paramref name="length"/> bytes
124+
/// on success.
125+
/// </remarks>
126+
public virtual bool TryGetSpan(uint startAddress, int length, out ReadOnlySpan<byte> span, MemoryAccess access) {
127+
bool result = TryGetSpan(startAddress, length, out Span<byte> mutableSpan, access);
128+
span = mutableSpan;
129+
return result;
130+
}
35131
}

0 commit comments

Comments
 (0)