mirror of
https://github.com/ApfelTeeSaft/DotNesJit.git
synced 2026-08-26 19:23:35 +00:00
Moving towards Vic-II implementation
This commit is contained in:
@@ -1,4 +1,3 @@
|
||||
using Dotnet6502.C64.Emulation;
|
||||
using Dotnet6502.Common.Hardware;
|
||||
|
||||
namespace Dotnet6502.C64.Hardware;
|
||||
@@ -30,7 +29,7 @@ public class C64Hal : Base6502Hal
|
||||
|
||||
for (var x = 0; x < count; x++)
|
||||
{
|
||||
_vic2.RunSingleCpuCycle();
|
||||
_vic2.RunSingleCycle();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -4,12 +4,21 @@ namespace Dotnet6502.C64.Hardware;
|
||||
|
||||
public class IoMemoryArea : IMemoryDevice
|
||||
{
|
||||
private readonly byte[] _data = new byte[0xdfff - 0xd000 + 1];
|
||||
private const int TotalSize = 0xdfff - 0xd000 + 1;
|
||||
private readonly byte[] _data = new byte[TotalSize];
|
||||
|
||||
public uint Size => 0xdfff - 0xd000 + 1;
|
||||
public uint Size => TotalSize;
|
||||
|
||||
public ReadOnlyMemory<byte>? RawBlockFromZero => null;
|
||||
|
||||
public Memory<byte> Vic2Registers => _data.AsMemory()[..0x400];
|
||||
public Memory<byte> SidRegisters => _data.AsMemory()[0x400..0x400];
|
||||
public Memory<byte> ColorRam => _data.AsMemory()[0x800..0x400];
|
||||
public Memory<byte> Cia1 => _data.AsMemory()[0xc00..0x100];
|
||||
public Memory<byte> Cia2 => _data.AsMemory()[0xd00..0x100];
|
||||
public Memory<byte> Io1 => _data.AsMemory()[0xe00..0x100];
|
||||
public Memory<byte> Io2 => _data.AsMemory()[0xf00..0x100];
|
||||
|
||||
public void Write(ushort offset, byte value)
|
||||
{
|
||||
_data[offset] = value;
|
||||
|
||||
@@ -21,7 +21,7 @@ public class ProgrammableLogicArray : IMemoryDevice
|
||||
private readonly BasicRamMemoryDevice _ramE000ToFfff;
|
||||
private readonly IoMemoryArea _ioMemoryArea;
|
||||
|
||||
public ProgrammableLogicArray()
|
||||
public ProgrammableLogicArray(IoMemoryArea ioMemoryArea)
|
||||
{
|
||||
BasicRom = new BasicRamMemoryDevice(0xBFFF - 0xA000 + 1);
|
||||
KernelRom = new BasicRamMemoryDevice(0xFFFF - 0xE000 + 1);
|
||||
@@ -29,7 +29,7 @@ public class ProgrammableLogicArray : IMemoryDevice
|
||||
_ramD000ToDfff = new BasicRamMemoryDevice(0xDFFF - 0xD000 + 1);
|
||||
_ramE000ToFfff = new BasicRamMemoryDevice(0xFFFF - 0xE000 + 1);
|
||||
CharacterRom = new BasicRamMemoryDevice(0xDFFF - 0xD000 + 1);
|
||||
_ioMemoryArea = new IoMemoryArea();
|
||||
_ioMemoryArea = ioMemoryArea;
|
||||
|
||||
_a000ToBfffDevice = new SwappableMemoryDevice(_ramA000ToBfff);
|
||||
_d000ToDfffDevice = new SwappableMemoryDevice(_ramD000ToDfff);
|
||||
|
||||
@@ -7,34 +7,129 @@ namespace Dotnet6502.C64.Hardware;
|
||||
/// </summary>
|
||||
public class Vic2
|
||||
{
|
||||
// Reference: https://zimmers.net/cbmpics/cbm/c64/vic-ii.txt
|
||||
private const int DotsPerCpuCycle = 8;
|
||||
private const int DotsPerScanline = 520;
|
||||
private const int VisibleDotsPerScanline = DotsPerScanline - 56;
|
||||
private const int VisibleDotsPerScanLine = 464;
|
||||
private const int TotalScanlines = 263;
|
||||
private const int VisibleScanlines = 235;
|
||||
private const int NonVisibleScanlines = TotalScanlines - VisibleScanlines;
|
||||
private const int FirstVisibleScanline = 16;
|
||||
private const int LastVisibleScanLine = 262;
|
||||
private const int FirstDisplayWindowScanLine = 51;
|
||||
private const int LastDisplayWindowScanLine = 250;
|
||||
private const int VisibleScanLines = LastVisibleScanLine - FirstVisibleScanline;
|
||||
|
||||
private readonly IC64Display _c64Display;
|
||||
private int _dotCount;
|
||||
private int _scanlineCount;
|
||||
private readonly Memory<byte> _vic2Registers;
|
||||
private int _lineCycleCount;
|
||||
private int _lineDotCount;
|
||||
private int _currentScanLine;
|
||||
|
||||
public Vic2(IC64Display c64Display)
|
||||
/// <summary>
|
||||
/// AKA VC, 10-bit counter that tracks which character/sprite data to fetch from memory. It acts as the main
|
||||
/// "position" counter for the current display line
|
||||
/// </summary>
|
||||
private ushort _videoCounter;
|
||||
|
||||
/// <summary>
|
||||
/// AKA RC, 3-bit counter that tracks which scan line within a character row is currently being rendered
|
||||
/// </summary>
|
||||
private byte _rasterCounter;
|
||||
|
||||
/// <summary>
|
||||
/// AKA VMLI, Serves as the base value for teh video counter. Stores the VC value at the beginning of each character row
|
||||
/// </summary>
|
||||
private ushort _videoMatrixLineIndex;
|
||||
|
||||
public Vic2(IC64Display c64Display, IoMemoryArea ioMemoryArea)
|
||||
{
|
||||
_c64Display = c64Display;
|
||||
_vic2Registers = ioMemoryArea.Vic2Registers;
|
||||
}
|
||||
|
||||
public void RunSingleCpuCycle()
|
||||
public void RunSingleCycle()
|
||||
{
|
||||
_dotCount += DotsPerCpuCycle;
|
||||
if (_dotCount >= DotsPerScanline)
|
||||
var vic2RegisterSpan = _vic2Registers.Span;
|
||||
|
||||
_lineCycleCount++;
|
||||
_lineDotCount += DotsPerCpuCycle;
|
||||
if (_lineDotCount >= DotsPerCpuCycle)
|
||||
{
|
||||
_scanlineCount++;
|
||||
_dotCount = 0;
|
||||
if (_scanlineCount > TotalScanlines)
|
||||
{
|
||||
_scanlineCount = 0;
|
||||
_c64Display.RenderFrame(new RgbColor[VisibleDotsPerScanline * VisibleScanlines]);
|
||||
}
|
||||
AdvanceToNextScanLine(vic2RegisterSpan);
|
||||
}
|
||||
}
|
||||
|
||||
private void AdvanceToNextScanLine(Span<byte> registers)
|
||||
{
|
||||
_currentScanLine++;
|
||||
_lineCycleCount = 0;
|
||||
_lineDotCount = 0;
|
||||
|
||||
if (_currentScanLine > LastVisibleScanLine)
|
||||
{
|
||||
// Vblank starts
|
||||
_c64Display.RenderFrame(new RgbColor[VisibleDotsPerScanLine * VisibleScanLines]);
|
||||
_currentScanLine = 0; // vblank period comes before first visible scanlines
|
||||
}
|
||||
|
||||
// Update the raster counter
|
||||
var scanLineMsbIsSet = (_currentScanLine & 0b1_0000_0000) > 0;
|
||||
registers[Vic2Registers.ControlRegister1] = scanLineMsbIsSet
|
||||
? (byte)(registers[Vic2Registers.ControlRegister1] & 0b1111_1111)
|
||||
: (byte)(registers[Vic2Registers.ControlRegister1] & 0b0111_1111);
|
||||
|
||||
registers[Vic2Registers.Raster] = (byte)(_currentScanLine & 0xFF);
|
||||
|
||||
// Reset internal registers
|
||||
_videoCounter = 0;
|
||||
_videoMatrixLineIndex = 0;
|
||||
_rasterCounter = 0;
|
||||
}
|
||||
|
||||
private void RunMemoryAccessPhase(Span<byte> registers)
|
||||
{
|
||||
if (_lineCycleCount < 10)
|
||||
{
|
||||
// TODO: Sprites (p-access for sprite pointers, s-access for sprite data)
|
||||
}
|
||||
|
||||
else if (_lineCycleCount < 14)
|
||||
{
|
||||
// TODO: Idle
|
||||
}
|
||||
|
||||
else if (_lineCycleCount < 54)
|
||||
{
|
||||
// TOOD: If badline, perform c-access (read screen memory for character codes)
|
||||
if (IsBadline(registers))
|
||||
{
|
||||
// TODO: implement
|
||||
}
|
||||
}
|
||||
|
||||
else if (_lineCycleCount < 58)
|
||||
{
|
||||
// TODO: Graphics fetch - perform g-access (fetch character/bitmap data) using the character pointers
|
||||
// from c-access
|
||||
}
|
||||
|
||||
else
|
||||
{
|
||||
// TODO: Refresh cycles, more sprite checks
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines if the current scanline is considered a badline. Badlines are lines where the Vic-II halts the
|
||||
/// CPU for 40 cycles in order to pull in new character and graphics data from memory.
|
||||
/// </summary>
|
||||
/// <returns></returns>
|
||||
private bool IsBadline(Span<byte> registers)
|
||||
{
|
||||
var displayEnable = (registers[Vic2Registers.ControlRegister1] & 0b0001_0000) > 0;
|
||||
var yScroll = registers[Vic2Registers.ControlRegister1] & 0b0000_0111;
|
||||
return displayEnable &&
|
||||
_currentScanLine >= 48 &&
|
||||
_currentScanLine <= 247 &&
|
||||
(_currentScanLine & 0b111) == yScroll;
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
namespace Dotnet6502.C64.Hardware;
|
||||
|
||||
public static class Vic2Registers
|
||||
{
|
||||
/// <summary>
|
||||
/// - 7 = Raster bit 8
|
||||
/// - 6 = ECM
|
||||
/// - 5 = BMM
|
||||
/// - 4 = Display Enable
|
||||
/// - 3 = RSEL
|
||||
/// - 2-0 = Yscroll
|
||||
/// </summary>
|
||||
public const ushort ControlRegister1 = 0x011;
|
||||
|
||||
/// <summary>
|
||||
/// Raster bits 7-0
|
||||
/// </summary>
|
||||
public const ushort Raster = 0x012;
|
||||
|
||||
/// <summary>
|
||||
/// - 7-6 = Unused
|
||||
/// - 5 = RES
|
||||
/// - 4 = MCM
|
||||
/// - 3 = CSEL
|
||||
/// - 2-0 = XScroll
|
||||
/// </summary>
|
||||
public const ushort ControlRegister2 = 0x016;
|
||||
|
||||
/// <summary>
|
||||
/// - 7-4 = Screen pointer (A13-A10) (aka video screen matrix?)
|
||||
/// - 3-1 = Bitmapt/Charset pointer (A13-A11)
|
||||
/// - 0 = unused
|
||||
/// </summary>
|
||||
public const ushort VmCb = 0x018;
|
||||
}
|
||||
@@ -9,10 +9,11 @@ var cancellationTokenSource = new CancellationTokenSource();
|
||||
var cliArgs = CommandLineHandler.Parse(args);
|
||||
|
||||
Console.WriteLine("Starting Dotnet6502 Commodore64 emulator");
|
||||
var ioMemoryArea = new IoMemoryArea();
|
||||
var pla = await SetupPla(cliArgs);
|
||||
var memoryBus = SetupMemoryBus();
|
||||
var app = new MonogameApp(false);
|
||||
var vic2 = new Vic2(app);
|
||||
var vic2 = new Vic2(app, ioMemoryArea);
|
||||
var hal = new C64Hal(memoryBus, cancellationTokenSource.Token, vic2);
|
||||
var jitCustomizer = new C64JitCustomizer();
|
||||
var jitCompiler = new JitCompiler(hal, jitCustomizer, memoryBus);
|
||||
@@ -54,7 +55,7 @@ async Task<ProgrammableLogicArray> SetupPla(CommandLineHandler.Values values)
|
||||
var kernelRomContents = await File.ReadAllBytesAsync(values.KernelRom.FullName);
|
||||
var charRomContents = await File.ReadAllBytesAsync(values.CharacterRom.FullName);
|
||||
|
||||
var programmableLogicArray = new ProgrammableLogicArray();
|
||||
var programmableLogicArray = new ProgrammableLogicArray(ioMemoryArea);
|
||||
programmableLogicArray.BasicRom.SetContent(basicRomContents);
|
||||
programmableLogicArray.KernelRom.SetContent(kernelRomContents);
|
||||
programmableLogicArray.CharacterRom.SetContent(charRomContents);
|
||||
|
||||
@@ -5,6 +5,7 @@ namespace Dotnet6502.Common.Compilation;
|
||||
/// </summary>
|
||||
public static class Ir6502
|
||||
{
|
||||
// TODO: Should probably refactor this to not create so much garbage
|
||||
public abstract record Instruction;
|
||||
|
||||
public abstract record JumpTarget;
|
||||
|
||||
Reference in New Issue
Block a user