mirror of
https://github.com/ApfelTeeSaft/DotNesJit.git
synced 2026-08-26 19:23:35 +00:00
Updated memory mapping to allow more flexible routing
This commit is contained in:
@@ -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);
|
||||
}
|
||||
}
|
||||
@@ -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;
|
||||
|
||||
|
||||
@@ -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(() =>
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user