Updated memory mapping to allow more flexible routing

This commit is contained in:
KallDrexx
2026-01-06 22:57:24 -05:00
parent 3431402417
commit ccb2aaa7ed
9 changed files with 247 additions and 200 deletions
+3 -4
View File
@@ -12,16 +12,15 @@ public class C64Hal : Base6502Hal
private readonly bool _debugModeEnabled;
public C64Hal(
MemoryBus memoryBus,
C64MemoryConfig memoryConfig,
CancellationToken cancellationToken,
Vic2 vic2,
IoMemoryArea ioMemoryArea,
DebugWriter? debugWriter,
bool debugModeEnabled) : base(memoryBus)
bool debugModeEnabled) : base(memoryConfig.CpuMemoryBus)
{
_cancellationToken = cancellationToken;
_vic2 = vic2;
_ioMemoryArea = ioMemoryArea;
_ioMemoryArea = memoryConfig.IoMemoryArea;
_debugWriter = debugWriter;
_debugModeEnabled = debugModeEnabled;
}
@@ -0,0 +1,30 @@
using Dotnet6502.Common.Hardware;
namespace Dotnet6502.C64.Hardware;
public class C64MemoryConfig
{
public BasicRamMemoryDevice FullRam { get; } = new(0xFFFF + 1);
public BasicRamMemoryDevice KernelRom { get; } = new(0xFFFF - 0xE000 + 1);
public BasicRamMemoryDevice CharRom { get; } = new(0xDFFF - 0xD000 + 1);
public BasicRamMemoryDevice BasicRom { get; } = new(0xBFFF - 0xA000 + 1);
public IoMemoryArea IoMemoryArea { get; } = new();
public MemoryBus CpuMemoryBus { get; } = new(0xFFFF + 1);
public MemoryBus Vic2MemoryBus { get; } = new(0xFFFF + 1);
public C64MemoryConfig()
{
CpuMemoryBus.Attach(FullRam, 0x0000);
var pla = new ProgrammableLogicArray(this);
// Set the default map configuration value
pla.Write(1, 0b00110111);
// The Vi2 sees the full RAM area, except character rom at 0x1000 and 0x9000.
Vic2MemoryBus.Attach(FullRam, 0x0000);
Vic2MemoryBus.Attach(CharRom, 0x1000, true);
Vic2MemoryBus.Attach(CharRom, 0x9000, true);
}
}
@@ -9,62 +9,54 @@ namespace Dotnet6502.C64.Hardware;
public class ProgrammableLogicArray : IMemoryDevice
{
private readonly byte[] _cpuIoPort = [0b00101111, 0b00110111];
private readonly SwappableMemoryDevice _a000ToBfffDevice;
private readonly SwappableMemoryDevice _d000ToDfffDevice;
private readonly SwappableMemoryDevice _e000ToFfffDevice;
private readonly C64MemoryConfig _memoryConfig;
public BasicRamMemoryDevice BasicRom { get; }
public BasicRamMemoryDevice KernelRom { get; }
public BasicRamMemoryDevice CharacterRom { get; }
private readonly BasicRamMemoryDevice _ramA000ToBfff;
private readonly BasicRamMemoryDevice _ramD000ToDfff;
private readonly BasicRamMemoryDevice _ramE000ToFfff;
private readonly IoMemoryArea _ioMemoryArea;
private readonly RoutableMemoryDevice _a000ToBfffDevice = new(0xBFFF - 0xA000 + 1);
private readonly RoutableMemoryDevice _d000ToDfffDevice = new(0xDFFF - 0xD000 + 1);
private readonly RoutableMemoryDevice _e000ToFfffDevice = new(0xFFFF - 0xE000 + 1);
public ProgrammableLogicArray(IoMemoryArea ioMemoryArea)
public ProgrammableLogicArray(C64MemoryConfig memoryConfig)
{
BasicRom = new BasicRamMemoryDevice(0xBFFF - 0xA000 + 1);
KernelRom = new BasicRamMemoryDevice(0xFFFF - 0xE000 + 1);
_ramA000ToBfff = new BasicRamMemoryDevice(0xBFFF - 0xA000 + 1);
_ramD000ToDfff = new BasicRamMemoryDevice(0xDFFF - 0xD000 + 1);
_ramE000ToFfff = new BasicRamMemoryDevice(0xFFFF - 0xE000 + 1);
CharacterRom = new BasicRamMemoryDevice(0xDFFF - 0xD000 + 1);
_ioMemoryArea = ioMemoryArea;
_memoryConfig = memoryConfig;
_a000ToBfffDevice = new SwappableMemoryDevice(_ramA000ToBfff);
_d000ToDfffDevice = new SwappableMemoryDevice(_ramD000ToDfff);
_e000ToFfffDevice = new SwappableMemoryDevice(_ramE000ToFfff);
_a000ToBfffDevice.Add(memoryConfig.FullRam, 0xA000);
_a000ToBfffDevice.Add(memoryConfig.BasicRom, 0x0000);
_d000ToDfffDevice.Add(memoryConfig.FullRam, 0xD000);
_d000ToDfffDevice.Add(memoryConfig.IoMemoryArea, 0x0000);
_d000ToDfffDevice.Add(memoryConfig.CharRom, 0x0000);
_e000ToFfffDevice.Add(memoryConfig.FullRam, 0xE000);
_e000ToFfffDevice.Add(memoryConfig.KernelRom, 0x0000);
_memoryConfig.CpuMemoryBus.Attach(this, 0x0000, true);
_memoryConfig.CpuMemoryBus.Attach(_a000ToBfffDevice, 0xa000, true);
_memoryConfig.CpuMemoryBus.Attach(_d000ToDfffDevice, 0xd000, true);
_memoryConfig.CpuMemoryBus.Attach(_e000ToFfffDevice, 0xe000, true);
UpdateDevices();
}
public void AttachToBus(MemoryBus memoryBus)
{
memoryBus.Attach(_a000ToBfffDevice, 0xa000);
memoryBus.Attach(_d000ToDfffDevice, 0xd000);
memoryBus.Attach(_e000ToFfffDevice, 0xe000);
}
private void UpdateDevices()
{
var section = _cpuIoPort[1] & 0b111;
if ((section & 0b11) == 0)
{
// RAM visible in all 3 sections
_a000ToBfffDevice.MakeVisible(_ramA000ToBfff, SwappableMemoryDevice.Mode.Read);
_d000ToDfffDevice.MakeVisible(_ramD000ToDfff, SwappableMemoryDevice.Mode.Read);
_e000ToFfffDevice.MakeVisible(_ramA000ToBfff, SwappableMemoryDevice.Mode.Read);
_a000ToBfffDevice.SetRoutableDevice(_memoryConfig.FullRam, RoutableMemoryDevice.RoutableDirection.Read);
_d000ToDfffDevice.SetRoutableDevice(_memoryConfig.FullRam, RoutableMemoryDevice.RoutableDirection.Read);
_e000ToFfffDevice.SetRoutableDevice(_memoryConfig.FullRam, RoutableMemoryDevice.RoutableDirection.Read);
return;
}
IMemoryDevice a0Device = (section & 0b011) == 0b11 ? BasicRom : _ramA000ToBfff;
IMemoryDevice d0Device = (section & 0b100) > 0 ? _ioMemoryArea : CharacterRom;
IMemoryDevice e0Device = (section & 0b011) == 0b01 ? _ramE000ToFfff : KernelRom;
IMemoryDevice a0Device = (section & 0b011) == 0b11 ? _memoryConfig.BasicRom : _memoryConfig.FullRam;
IMemoryDevice d0Device = (section & 0b100) > 0 ? _memoryConfig.IoMemoryArea : _memoryConfig.CharRom;
IMemoryDevice e0Device = (section & 0b011) == 0b01 ? _memoryConfig.FullRam : _memoryConfig.KernelRom;
_a000ToBfffDevice.MakeVisible(a0Device, SwappableMemoryDevice.Mode.Read);
_d000ToDfffDevice.MakeVisible(d0Device, SwappableMemoryDevice.Mode.Read);
_e000ToFfffDevice.MakeVisible(e0Device, SwappableMemoryDevice.Mode.Read);
_a000ToBfffDevice.SetRoutableDevice(a0Device, RoutableMemoryDevice.RoutableDirection.Read);
_d000ToDfffDevice.SetRoutableDevice(d0Device, RoutableMemoryDevice.RoutableDirection.Read);
_e000ToFfffDevice.SetRoutableDevice(e0Device, RoutableMemoryDevice.RoutableDirection.Read);
}
public uint Size => (uint) _cpuIoPort.Length;
@@ -0,0 +1,96 @@
using Dotnet6502.Common.Hardware;
namespace Dotnet6502.C64.Hardware;
/// <summary>
/// A memory device that's routable between different memory devices
/// </summary>
public class RoutableMemoryDevice : IMemoryDevice
{
public enum RoutableDirection { Read, Write, ReadAndWrite }
private readonly Dictionary<IMemoryDevice, ushort> _deviceToOffsetAdjustmentMap = new();
private IMemoryDevice? _routableDeviceForRead;
private IMemoryDevice? _routableDeviceForWrite;
public uint Size { get; }
public ReadOnlyMemory<byte>? RawBlockFromZero => null;
public RoutableMemoryDevice(uint size)
{
Size = size;
}
public void Write(ushort offset, byte value)
{
if (_routableDeviceForWrite == null)
{
const string message = "Attempted to write to a device, but no device has been set as the routable target";
throw new InvalidOperationException(message);
}
var adjustment = _deviceToOffsetAdjustmentMap[_routableDeviceForWrite];
_routableDeviceForWrite.Write((ushort)(offset + adjustment), value);
}
public byte Read(ushort offset)
{
if (_routableDeviceForRead == null)
{
const string message = "Attempted to read from a device, but no device has been set as the routable target";
throw new InvalidOperationException(message);
}
var adjustment = _deviceToOffsetAdjustmentMap[_routableDeviceForRead];
return _routableDeviceForRead.Read((ushort)(offset + adjustment));
}
/// <summary>
/// Adds a device that can be routed to, (but is not yet routed). The device must be at least the size
/// of this RoutableMemoryDevice from the offset adjustment to the end.
/// </summary>
public void Add(IMemoryDevice memoryDevice, ushort offsetAdjustment)
{
var deviceSize = memoryDevice.Size - offsetAdjustment;
if (deviceSize < Size)
{
var message = $"Attempted to add a memory device with a size of {memoryDevice.Size:X4} at an offset " +
$"adjustment of {offsetAdjustment:X4}, which ends up with a visible size of {deviceSize:X4}. " +
$"This is less than this routable memory device's size of {Size}";
throw new ArgumentException(message);
}
_deviceToOffsetAdjustmentMap.Add(memoryDevice, offsetAdjustment);
}
/// <summary>
/// Sets the device with the specified id as routable in the specified direction
/// </summary>
public void SetRoutableDevice(IMemoryDevice device, RoutableDirection direction)
{
if (!_deviceToOffsetAdjustmentMap.ContainsKey(device))
{
const string message = "Attempted to set routable device with a device that has not been added yet";
throw new ArgumentException(message);
}
switch (direction)
{
case RoutableDirection.Read:
_routableDeviceForRead = device;
break;
case RoutableDirection.Write:
_routableDeviceForWrite = device;
break;
case RoutableDirection.ReadAndWrite:
_routableDeviceForRead = device;
_routableDeviceForWrite = device;
break;
default:
throw new NotSupportedException(direction.ToString());
}
}
}
@@ -1,71 +0,0 @@
using Dotnet6502.Common.Hardware;
namespace Dotnet6502.C64.Hardware;
/// <summary>
/// A memory mapped device that can be swapped out to different memory devices.
/// </summary>
public class SwappableMemoryDevice : IMemoryDevice
{
/// <summary>
/// The mode a memory device should be made visible with
/// </summary>
public enum Mode { Read, Write, ReadAndWrite }
private IMemoryDevice _readDevice;
private IMemoryDevice _writeDevice;
public uint Size => _readDevice.Size;
public ReadOnlyMemory<byte>? RawBlockFromZero => _readDevice.RawBlockFromZero;
public SwappableMemoryDevice(IMemoryDevice readWriteDevice)
{
_readDevice = readWriteDevice;
_writeDevice = readWriteDevice;
}
/// <summary>
/// Makes the specified device responsible for read or write calls based on the mode. The modes not specified
/// will retain their routes to the previous devices.
/// </summary>
public void MakeVisible(IMemoryDevice memoryDevice, Mode mode)
{
if (memoryDevice.Size != _readDevice.Size)
{
var message = $"Attempted to swap memory device from one with {_readDevice.Size} " +
$"to one with {memoryDevice.Size}. Sizes must match";
throw new ArgumentException(message);
}
switch (mode)
{
case Mode.ReadAndWrite:
_readDevice = memoryDevice;
_writeDevice = memoryDevice;
break;
case Mode.Read:
_readDevice = memoryDevice;
break;
case Mode.Write:
_writeDevice = memoryDevice;
break;
default:
throw new NotSupportedException(mode.ToString());
}
}
public void Write(ushort offset, byte value)
{
_writeDevice.Write(offset, value);
}
public byte Read(ushort offset)
{
return _readDevice.Read(offset);
}
}
+32 -30
View File
@@ -30,9 +30,9 @@ public class Vic2
public const int VisibleScanLines = LastVisibleScanLine - FirstVisibleScanline + 1;
private readonly IC64Display _c64Display;
private readonly BasicRamMemoryDevice _vic2Registers;
private readonly BasicRamMemoryDevice _colorRam;
private readonly ReadOnlyMemory<byte> _screenRam;
private readonly Vic2RegisterData _vic2Registers;
private readonly MemoryBus _ramView;
private readonly ComplexInterfaceAdapter _cia2;
private readonly RgbColor[] _frameBuffer = new RgbColor[VisibleDotsPerScanLine * VisibleScanLines];
private readonly RgbColor[] _palette = new RgbColor[16];
private int _lineCycleCount;
@@ -61,12 +61,12 @@ public class Vic2
/// </summary>
private ushort _videoMatrixLineIndex;
public Vic2(IC64Display c64Display, IoMemoryArea ioMemoryArea, ReadOnlyMemory<byte> screenRam)
public Vic2(IC64Display c64Display, C64MemoryConfig memoryConfig)
{
_c64Display = c64Display;
_screenRam = screenRam;
_vic2Registers = ioMemoryArea.Vic2Registers;
_colorRam = ioMemoryArea.ColorRam;
_vic2Registers = new Vic2RegisterData(memoryConfig.IoMemoryArea.Vic2Registers);
_cia2 = memoryConfig.IoMemoryArea.Cia2;
_ramView = memoryConfig.Vic2MemoryBus;
// Colors from https://www.c64-wiki.com/wiki/Color
_palette[0] = new RgbColor(0, 0, 0); // Black
@@ -89,20 +89,18 @@ public class Vic2
public void RunSingleCycle()
{
var registers = new Vic2RegisterData(_vic2Registers);
_lineCycleCount++;
_lineDotCount += DotsPerCpuCycle;
if (_lineDotCount >= DotsPerScanline)
{
AdvanceToNextScanLine(registers);
AdvanceToNextScanLine();
}
RunMemoryAccessPhase(registers);
UpdateFramebuffer(registers);
RunMemoryAccessPhase();
UpdateFramebuffer();
}
private void AdvanceToNextScanLine(Vic2RegisterData registers)
private void AdvanceToNextScanLine()
{
_currentScanLine++;
_lineCycleCount = 0;
@@ -121,7 +119,7 @@ public class Vic2
}
// Update the raster counter
registers.RasterCounter = _currentScanLine;
_vic2Registers.RasterCounter = _currentScanLine;
// Reset internal registers
_videoCounter = 0;
@@ -129,7 +127,7 @@ public class Vic2
_rowCounter = 0;
}
private void RunMemoryAccessPhase(Vic2RegisterData registerData)
private void RunMemoryAccessPhase()
{
if (_lineCycleCount < 10)
{
@@ -146,7 +144,7 @@ public class Vic2
_videoCounter = _videoCounterBase;
_videoMatrixLineIndex = 0;
if (IsBadline(registerData))
if (IsBadline())
{
_rowCounter = 0;
@@ -179,7 +177,7 @@ public class Vic2
}
}
private void UpdateFramebuffer(Vic2RegisterData registerData)
private void UpdateFramebuffer()
{
if (_currentScanLine < FirstVisibleScanline ||
_currentScanLine > LastVisibleScanLine || // just to be safe
@@ -189,20 +187,13 @@ public class Vic2
return;
}
var csel = registerData.CSel;
var rsel = registerData.RSel;
var csel = _vic2Registers.CSel;
var rsel = _vic2Registers.RSel;
var lastBorderLeftDot = csel ? 24 : 31;
var firstBorderRightDot = csel ? 344 : 335;
var lastBorderTopLine = rsel ? 51 : 55;
var firstBorderBottomLine = rsel ? 247 : 251;
var borderColor = registerData.BorderColor;
// var colorRam = _colorRam.Span;
var screenRam = _screenRam.Span;
var row = (_currentScanLine - FirstVisibleScanline) / 8;
var rowInChar = (_currentScanLine - FirstVisibleScanline) % 8;
var column = _lineCycleCount - 17;
var borderColor = _vic2Registers.BorderColor;
// Write the next 8 dots
for (var x = 0; x < DotsPerCpuCycle; x++)
@@ -235,12 +226,12 @@ public class Vic2
/// CPU for 40 cycles in order to pull in new character and graphics data from memory.
/// </summary>
/// <returns></returns>
private bool IsBadline(Vic2RegisterData registerData)
private bool IsBadline()
{
return registerData.DisplayEnable &&
return _vic2Registers.DisplayEnable &&
_currentScanLine >= 48 &&
_currentScanLine <= 247 &&
(_currentScanLine & 0b111) == registerData.YScroll;
(_currentScanLine & 0b111) == _vic2Registers.YScroll;
}
/// <summary>
@@ -264,4 +255,15 @@ public class Vic2
_ => GraphicsMode.Invalid,
};
}
private byte ReadRam(ushort address)
{
// The VIC only knows about the first 14 bits of the address. The 2 MSB are set by CIA2 to determine
// what bank the VIC ends up reading. The CIA2 has the two bits inverted, so 0b01 translates to
// 0b10 in the address call.
var bank = (ushort)(((_cia2.DataPortA & 0b11) ^ 0b11) << 14);
address = (ushort)(bank | (address & 0b0011_1111_1111_1111));
return _ramView.Read(address);
}
}
@@ -2,7 +2,7 @@ using Dotnet6502.Common.Hardware;
namespace Dotnet6502.C64.Hardware;
public readonly ref struct Vic2RegisterData
public class Vic2RegisterData
{
private readonly BasicRamMemoryDevice _registerBytes;
+17 -42
View File
@@ -1,9 +1,7 @@
using Dotnet6502.C64;
using Dotnet6502.C64.Emulation;
using Dotnet6502.C64.Hardware;
using Dotnet6502.C64.Integration;
using Dotnet6502.Common.Compilation;
using Dotnet6502.Common.Hardware;
var cancellationTokenSource = new CancellationTokenSource();
var cliArgs = CommandLineHandler.Parse(args);
@@ -16,22 +14,20 @@ if (cliArgs.LogFile != null)
logWriter = new DebugWriter(cliArgs.LogFile);
}
var ioMemoryArea = new IoMemoryArea();
var pla = await SetupPla(cliArgs);
var (memoryBus, screenRam) = SetupMemoryBus();
var memoryConfig = await SetupMemory();
var app = new MonogameApp(false);
var vic2 = new Vic2(app, ioMemoryArea, screenRam);
var hal = new C64Hal(memoryBus, cancellationTokenSource.Token, vic2, ioMemoryArea, logWriter, cliArgs.InDebugMode);
var vic2 = new Vic2(app, memoryConfig);
var hal = new C64Hal(memoryConfig, cancellationTokenSource.Token, vic2, logWriter, cliArgs.InDebugMode);
var jitCustomizer = new C64JitCustomizer();
var jitCompiler = new JitCompiler(hal, jitCustomizer, memoryBus);
var jitCompiler = new JitCompiler(hal, jitCustomizer, memoryConfig.CpuMemoryBus);
await RunSystem();
Console.WriteLine("Done");
return 0;
async Task<ProgrammableLogicArray> SetupPla(CommandLineHandler.Values values)
async Task<C64MemoryConfig> SetupMemory()
{
if (values.KernelRom == null)
if (cliArgs.KernelRom == null)
{
Console.WriteLine("Error: No kernel rom specified");
Console.WriteLine();
@@ -40,7 +36,7 @@ async Task<ProgrammableLogicArray> SetupPla(CommandLineHandler.Values values)
Environment.Exit(1);
}
if (values.BasicRom == null)
if (cliArgs.BasicRom == null)
{
Console.WriteLine("Error: No basic rom specified");
Console.WriteLine();
@@ -49,7 +45,7 @@ async Task<ProgrammableLogicArray> SetupPla(CommandLineHandler.Values values)
Environment.Exit(1);
}
if (values.CharacterRom == null)
if (cliArgs.CharacterRom == null)
{
Console.WriteLine("Error: No character rom specified");
Console.WriteLine();
@@ -58,42 +54,21 @@ async Task<ProgrammableLogicArray> SetupPla(CommandLineHandler.Values values)
Environment.Exit(1);
}
var basicRomContents = await File.ReadAllBytesAsync(values.BasicRom.FullName);
var kernelRomContents = await File.ReadAllBytesAsync(values.KernelRom.FullName);
var charRomContents = await File.ReadAllBytesAsync(values.CharacterRom.FullName);
var basicRomContents = await File.ReadAllBytesAsync(cliArgs.BasicRom.FullName);
var kernelRomContents = await File.ReadAllBytesAsync(cliArgs.KernelRom.FullName);
var charRomContents = await File.ReadAllBytesAsync(cliArgs.CharacterRom.FullName);
var programmableLogicArray = new ProgrammableLogicArray(ioMemoryArea);
programmableLogicArray.BasicRom.SetContent(basicRomContents);
programmableLogicArray.KernelRom.SetContent(kernelRomContents);
programmableLogicArray.CharacterRom.SetContent(charRomContents);
var config = new C64MemoryConfig();
config.KernelRom.SetContent(kernelRomContents);
config.BasicRom.SetContent(basicRomContents);
config.CharRom.SetContent(charRomContents);
// Set the default map configuration value
programmableLogicArray.Write(1, 0b00110111);
return programmableLogicArray;
}
(MemoryBus, ReadOnlyMemory<byte> screenRam) SetupMemoryBus()
{
var bus = new MemoryBus(0xFFFF + 1);
bus.Attach(pla, 0x0000);
pla.AttachToBus(bus);
// fill in the rest with ram
var screenRamArea = new BasicRamMemoryDevice(0x07ff - 0x0400 + 1);
bus.Attach(new BasicRamMemoryDevice(0x03ff - 0x0002 + 1), 0x0002);
bus.Attach(screenRamArea, 0x0400);
bus.Attach(new BasicRamMemoryDevice(0x7fff - 0x0800 + 1), 0x0800);
bus.Attach(new BasicRamMemoryDevice(0xcfff - 0xc000 + 1), 0xc000);
// TODO: Add cartridge rom low swapping to this region
bus.Attach(new BasicRamMemoryDevice(0x9fff - 0x8000 + 1), 0x8000);
return (bus, screenRamArea.RawBlockFromZero!.Value);
return config;
}
async Task RunSystem()
{
var resetVector = (ushort)((memoryBus.Read(0xFFFD) << 8) | memoryBus.Read(0xFFFC));
var resetVector = (ushort)((memoryConfig.CpuMemoryBus.Read(0xFFFD) << 8) | memoryConfig.CpuMemoryBus.Read(0xFFFC));
Console.WriteLine($"Starting at reset vector {resetVector:X4}");
var c64Task = Task.Run(() =>
+39 -15
View File
@@ -22,7 +22,20 @@ public class MemoryBus
_deviceIndexMap = new ushort[memorySize];
}
public void Attach(IMemoryDevice device, ushort baseAddress)
/// <summary>
/// Attaches a memory device to the bus at a specific address
/// </summary>
/// <param name="device">The device to add</param>
/// <param name="baseAddress">The base address this device is visible starting from</param>
/// <param name="allowsOverriding">
/// If true and the memory space is already occupied by another device, this device will take over
/// responding to memory requests from the specified base address onto the size of the device being
/// attached. This allows segmenting a portion of memory without needing to subdivide memory devices.
///
/// If this is false, an exception will be thrown if any device has already claimed space between this
/// device's base address and end address.
/// </param>
public void Attach(IMemoryDevice device, ushort baseAddress, bool allowsOverriding = false)
{
if (_devices.Count == ushort.MaxValue - 1)
{
@@ -30,29 +43,40 @@ public class MemoryBus
}
// Make sure this doesn't overlap with an existing device
var newDeviceEnd = baseAddress + device.Size;
foreach (var (existingStart, memoryDevice) in _devices)
if (!allowsOverriding)
{
var existingEnd = existingStart + memoryDevice.Size;
if (existingEnd > baseAddress && existingStart < newDeviceEnd)
var newDeviceEnd = baseAddress + device.Size;
foreach (var (existingStart, memoryDevice) in _devices)
{
var message = $"Cannot attach device {device.GetType().Name} at address 0x{baseAddress:X4}-" +
$"0x{newDeviceEnd:X4} as it overlaps with an already attached device of type " +
$"{memoryDevice.GetType().Name} is using addresses 0x{existingStart:X4}-" +
$"0x{existingEnd:X4}";
var existingEnd = existingStart + memoryDevice.Size;
throw new InvalidOperationException(message);
if (existingEnd > baseAddress && existingStart < newDeviceEnd)
{
var message = $"Cannot attach device {device.GetType().Name} at address 0x{baseAddress:X4}-" +
$"0x{newDeviceEnd:X4} as it overlaps with an already attached device of type " +
$"{memoryDevice.GetType().Name} is using addresses 0x{existingStart:X4}-" +
$"0x{existingEnd:X4}";
throw new InvalidOperationException(message);
}
}
}
// If we got here we can attach it
_devices.Add(new AttachedDevice(baseAddress, device));
// If we got here we can attach it.
// Is this a new device or a respecification of an existing one?
var deviceIndex = _devices.FindIndex(x => x.Device == device);
if (deviceIndex < 0)
{
_devices.Add(new AttachedDevice(baseAddress, device));
deviceIndex = (ushort)_devices.Count - 1;
}
// index is incremented so that 0 represents unmapped memory
deviceIndex++;
var index = (ushort)_devices.Count; // index is incremented so that 0 represents unmapped memory
for (var x = 0; x < device.Size; x++)
{
_deviceIndexMap[baseAddress + x] = index;
_deviceIndexMap[baseAddress + x] = (ushort)deviceIndex;
}
}