Moved RCP interfaces inside itself.

master
Nabile Rahmani 2017-11-04 15:24:26 +01:00
parent 467ea2a07d
commit ea629a6b16
38 changed files with 846 additions and 802 deletions

View File

@ -1,49 +0,0 @@
using System.Collections.Generic;
namespace DotN64.AI
{
public class AudioInterface : Interface
{
#region Fields
private readonly IReadOnlyList<MappingEntry> memoryMaps;
#endregion
#region Properties
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
private uint dramAddress;
/// <summary>
/// Starting RDRAM address (8B-aligned).
/// </summary>
public uint DRAMAddress
{
get => dramAddress;
set => dramAddress = value & ((1 << 24) - 1);
}
private uint transferLength;
public uint TransferLength
{
get => transferLength;
set => transferLength = (uint)(value & ((1 << 18) - 1) & ~((1 << 3) - 1)); // "v2.0".
}
#endregion
#region Constructors
public AudioInterface()
{
memoryMaps = new[]
{
new MappingEntry(0x04500000, 0x04500003) // AI DRAM address.
{
Write = (o, v) => DRAMAddress = v
},
new MappingEntry(0x04500004, 0x04500007) // AI length.
{
Write = (o, v) => TransferLength = v
}
};
}
#endregion
}
}

View File

@ -37,7 +37,6 @@
<Compile Include="CPU\VR4300.Instruction.cs" />
<Compile Include="CPU\VR4300.OpCode.cs" />
<Compile Include="RCP\RealityCoprocessor.cs" />
<Compile Include="RCP\RealityCoprocessor.SignalProcessor.cs" />
<Compile Include="CPU\CP0\VR4300.SystemControlUnit.cs" />
<Compile Include="CPU\CP0\VR4300.SystemControlUnit.ConfigRegister.cs" />
<Compile Include="CPU\CP0\VR4300.SystemControlUnit.Register.cs" />
@ -45,37 +44,40 @@
<Compile Include="CPU\CP0\VR4300.SystemControlUnit.StatusRegister.cs" />
<Compile Include="CPU\VR4300.SpecialOpCode.cs" />
<Compile Include="MappingEntry.cs" />
<Compile Include="AI\AudioInterface.cs" />
<Compile Include="PI\PeripheralInterface.cs" />
<Compile Include="PI\PeripheralInterface.StatusRegister.cs" />
<Compile Include="SI\SerialInterface.cs" />
<Compile Include="SI\SerialInterface.StatusRegister.cs" />
<Compile Include="VI\VideoInterface.cs" />
<Compile Include="VI\VideoInterface.HorizontalVideoRegister.cs" />
<Compile Include="PI\PeripheralInterface.Domain.cs" />
<Compile Include="RCP\RealityCoprocessor.DisplayProcessor.cs" />
<Compile Include="MI\MIPSInterface.cs" />
<Compile Include="CPU\VR4300.RegImmOpCode.cs" />
<Compile Include="PI\PeripheralInterface.CICStatus.cs" />
<Compile Include="Extensions\MappingEntryExtensions.cs" />
<Compile Include="RI\RDRAMInterface.cs" />
<Compile Include="RI\RDRAMInterface.ConfigRegister.cs" />
<Compile Include="RI\RDRAMInterface.ModeRegister.cs" />
<Compile Include="MI\MIPSInterface.InitModeRegister.cs" />
<Compile Include="RI\RDRAMInterface.RefreshRegister.cs" />
<Compile Include="Interface.cs" />
<Compile Include="RCP\DP\RealityCoprocessor.DisplayProcessor.cs" />
<Compile Include="RCP\SP\RealityCoprocessor.SignalProcessor.cs" />
<Compile Include="RCP\RealityCoprocessor.Interface.cs" />
<Compile Include="RCP\AI\RealityCoprocessor.AudioInterface.cs" />
<Compile Include="RCP\MI\RealityCoprocessor.MIPSInterface.cs" />
<Compile Include="RCP\MI\RealityCoprocessor.MIPSInterface.InitModeRegister.cs" />
<Compile Include="RCP\PI\RealityCoprocessor.PeripheralInterface.cs" />
<Compile Include="RCP\PI\RealityCoprocessor.PeripheralInterface.CICStatus.cs" />
<Compile Include="RCP\PI\RealityCoprocessor.PeripheralInterface.Domain.cs" />
<Compile Include="RCP\PI\RealityCoprocessor.PeripheralInterface.StatusRegister.cs" />
<Compile Include="RCP\RI\RealityCoprocessor.RDRAMInterface.cs" />
<Compile Include="RCP\RI\RealityCoprocessor.RDRAMInterface.ConfigRegister.cs" />
<Compile Include="RCP\RI\RealityCoprocessor.RDRAMInterface.ModeRegister.cs" />
<Compile Include="RCP\RI\RealityCoprocessor.RDRAMInterface.RefreshRegister.cs" />
<Compile Include="RCP\SI\RealityCoprocessor.SerialInterface.cs" />
<Compile Include="RCP\SI\RealityCoprocessor.SerialInterface.StatusRegister.cs" />
<Compile Include="RCP\VI\RealityCoprocessor.VideoInterface.cs" />
<Compile Include="RCP\VI\RealityCoprocessor.VideoInterface.HorizontalVideoRegister.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="CPU\" />
<Folder Include="RCP\" />
<Folder Include="CPU\CP0\" />
<Folder Include="AI\" />
<Folder Include="PI\" />
<Folder Include="SI\" />
<Folder Include="VI\" />
<Folder Include="MI\" />
<Folder Include="Extensions\" />
<Folder Include="RI\" />
<Folder Include="RCP\AI\" />
<Folder Include="RCP\MI\" />
<Folder Include="RCP\PI\" />
<Folder Include="RCP\RI\" />
<Folder Include="RCP\SI\" />
<Folder Include="RCP\VI\" />
<Folder Include="RCP\DP\" />
<Folder Include="RCP\SP\" />
</ItemGroup>
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
</Project>

View File

@ -1,19 +0,0 @@
using System.Collections.Generic;
namespace DotN64
{
using Extensions;
public abstract class Interface
{
#region Properties
protected abstract IReadOnlyList<MappingEntry> MemoryMaps { get; }
#endregion
#region Methods
public uint ReadWord(ulong address) => MemoryMaps.GetEntry(address).ReadWord(address);
public void WriteWord(ulong address, uint value) => MemoryMaps.GetEntry(address).WriteWord(address, value);
#endregion
}
}

View File

@ -1,59 +0,0 @@
using System;
using System.Collections.Specialized;
namespace DotN64.MI
{
public partial class MIPSInterface
{
public struct InitModeRegister
{
#region Fields
private BitVector32 bits;
public static readonly BitVector32.Section InitLengthSection = BitVector32.CreateSection((1 << 7) - 1),
ClearInitModeSection = BitVector32.CreateSection(1, InitLengthSection),
SetInitModeSection = BitVector32.CreateSection(1, ClearInitModeSection),
ClearEBusTestModeSection = BitVector32.CreateSection(1, SetInitModeSection),
SetEBusTestModeSection = BitVector32.CreateSection(1, ClearEBusTestModeSection),
ClearDPInterruptSection = BitVector32.CreateSection(1, SetEBusTestModeSection),
ClearRDRAMRegSection = BitVector32.CreateSection(1, ClearDPInterruptSection),
SetRDRAMRegModeSection = BitVector32.CreateSection(1, ClearRDRAMRegSection);
private static readonly BitVector32.Section initModeSection = BitVector32.CreateSection(1, InitLengthSection),
eBusTestModeSection = BitVector32.CreateSection(1, initModeSection),
rdramRegModeSection = BitVector32.CreateSection(1, eBusTestModeSection);
#endregion
#region Properties
public byte InitLength
{
get => (byte)bits[InitLengthSection];
set => bits[InitLengthSection] = value;
}
public bool InitMode
{
get => Convert.ToBoolean(bits[initModeSection]);
set => bits[initModeSection] = Convert.ToInt32(value);
}
public bool EBusTestMode
{
get => Convert.ToBoolean(bits[eBusTestModeSection]);
set => bits[eBusTestModeSection] = Convert.ToInt32(value);
}
public bool RDRAMRegMode
{
get => Convert.ToBoolean(bits[rdramRegModeSection]);
set => bits[rdramRegModeSection] = Convert.ToInt32(value);
}
#endregion
#region Operators
public static implicit operator InitModeRegister(uint data) => new InitModeRegister { bits = new BitVector32((int)data) };
public static implicit operator uint(InitModeRegister mode) => (uint)mode.bits.Data;
#endregion
}
}
}

View File

@ -1,52 +0,0 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace DotN64.MI
{
public partial class MIPSInterface : Interface
{
#region Fields
private readonly IReadOnlyList<MappingEntry> memoryMaps;
#endregion
#region Properties
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
public InitModeRegister InitMode { get; set; }
#endregion
#region Constructors
public MIPSInterface()
{
memoryMaps = new[]
{
new MappingEntry(0x04300000, 0x04300003) // MI init mode.
{
Write = (o, v) =>
{
var bits = new BitVector32((int)v);
var mode = InitMode;
mode.InitLength = (byte)bits[InitModeRegister.InitLengthSection];
mode.InitMode &= bits[InitModeRegister.ClearInitModeSection] == 0;
mode.InitMode |= bits[InitModeRegister.SetInitModeSection] != 0;
mode.EBusTestMode &= bits[InitModeRegister.ClearEBusTestModeSection] == 0;
mode.EBusTestMode |= bits[InitModeRegister.SetEBusTestModeSection] != 0;
if (bits[InitModeRegister.ClearDPInterruptSection] != 0)
throw new NotImplementedException("Clear DP interrupt.");
mode.RDRAMRegMode &= bits[InitModeRegister.ClearRDRAMRegSection] == 0;
mode.RDRAMRegMode |= bits[InitModeRegister.SetRDRAMRegModeSection] != 0;
InitMode = mode;
}
}
};
}
#endregion
}
}

View File

@ -3,14 +3,8 @@ using System.Net;
namespace DotN64
{
using AI;
using CPU;
using MI;
using PI;
using RCP;
using RI;
using SI;
using VI;
public class Nintendo64
{
@ -19,18 +13,6 @@ namespace DotN64
public RealityCoprocessor RCP { get; } = new RealityCoprocessor();
public PeripheralInterface PI { get; } = new PeripheralInterface();
public SerialInterface SI { get; } = new SerialInterface();
public AudioInterface AI { get; } = new AudioInterface();
public VideoInterface VI { get; } = new VideoInterface();
public MIPSInterface MI { get; } = new MIPSInterface();
public RDRAMInterface RI { get; } = new RDRAMInterface();
public Cartridge Cartridge { get; set; }
#endregion
@ -41,18 +23,18 @@ namespace DotN64
{
new MappingEntry(0x1FC00000, 0x1FC007BF, false) // PIF Boot ROM.
{
Read = PI.ReadWord,
Write = PI.WriteWord
Read = RCP.PI.ReadWord,
Write = RCP.PI.WriteWord
},
new MappingEntry(0x1FC007C0, 0x1FC007FF, false) // PIF (JoyChannel) RAM.
{
Read = PI.ReadWord,
Write = PI.WriteWord
Read = RCP.PI.ReadWord,
Write = RCP.PI.WriteWord
},
new MappingEntry(0x04600000, 0x046FFFFF, false) // Peripheral interface (PI) registers.
{
Read = PI.ReadWord,
Write = PI.WriteWord
Read = RCP.PI.ReadWord,
Write = RCP.PI.WriteWord
},
new MappingEntry(0x04000000, 0x040FFFFF, false) // SP registers.
{
@ -61,23 +43,23 @@ namespace DotN64
},
new MappingEntry(0x04400000, 0x044FFFFF, false) // Video interface (VI) registers.
{
Read = VI.ReadWord,
Write = VI.WriteWord
Read = RCP.VI.ReadWord,
Write = RCP.VI.WriteWord
},
new MappingEntry(0x04500000, 0x045FFFFF, false) // Audio interface (AI) registers.
{
Read = AI.ReadWord,
Write = AI.WriteWord
Read = RCP.AI.ReadWord,
Write = RCP.AI.WriteWord
},
new MappingEntry(0x04300000, 0x043FFFFF, false) // MIPS interface (MI) registers.
{
Read = MI.ReadWord,
Write = MI.WriteWord
Read = RCP.MI.ReadWord,
Write = RCP.MI.WriteWord
},
new MappingEntry(0x04800000, 0x048FFFFF, false) // Serial interface (SI) registers.
{
Read = SI.ReadWord,
Write = SI.WriteWord
Read = RCP.SI.ReadWord,
Write = RCP.SI.WriteWord
},
new MappingEntry(0x10000000, 0x1FBFFFFF) // Cartridge Domain 1 Address 2.
{
@ -90,13 +72,13 @@ namespace DotN64
},
new MappingEntry(0x04700000, 0x047FFFFF, false) // RDRAM interface (RI) registers.
{
Read = RI.ReadWord,
Write = RI.WriteWord
Read = RCP.RI.ReadWord,
Write = RCP.RI.WriteWord
},
new MappingEntry(0x00000000, 0x03EFFFFF, false) // RDRAM memory.
{
Read = RI.ReadWord,
Write = RI.WriteWord
Read = RCP.RI.ReadWord,
Write = RCP.RI.WriteWord
}
};
CPU = new VR4300(memoryMaps)
@ -170,7 +152,7 @@ namespace DotN64
{
CPU.Reset();
if (PI.BootROM == null)
if (RCP.PI.BootROM == null)
EmulatePIFBootROM();
while (true)

View File

@ -1,22 +0,0 @@
namespace DotN64.PI
{
public partial class PeripheralInterface
{
private enum CICStatus : byte
{
/// <summary>
/// The boot ROM copies the cartridge's bootstrap code [0x40, 0x1000[ into the RSP's DMEM while computing the CIC algorithm on the data.
/// When done, it stores the result in the PIF RAM at [48, 56[.
/// </summary>
Computing = 16,
/// <summary>
/// The CPU waits for the PIF CPU to validate that the cartridge's CIC computed the same result stored in the PIF RAM.
/// </summary>
Waiting = 48,
/// <summary>
/// The CIC check matched, continue booting.
/// </summary>
OK = 128
}
}
}

View File

@ -1,40 +0,0 @@
namespace DotN64.PI
{
public partial class PeripheralInterface
{
public class Domain
{
#region Properties
/// <summary>
/// Device latency.
/// </summary>
public byte Latency { get; set; }
/// <summary>
/// Device R/W strobe pulse width.
/// </summary>
public byte PulseWidth { get; set; }
private byte pageSize;
/// <summary>
/// Device page size.
/// </summary>
public byte PageSize
{
get => pageSize;
set => pageSize = (byte)(value & ((1 << 4) - 1));
}
private byte release;
/// <summary>
/// Device R/W release duration.
/// </summary>
public byte Release
{
get => release;
set => release = (byte)(value & ((1 << 2) - 1));
}
#endregion
}
}
}

View File

@ -1,13 +0,0 @@
namespace DotN64.PI
{
public partial class PeripheralInterface
{
[System.Flags]
public enum StatusRegister
{
DMABusy = 1 << 0,
IOBusy = 1 << 1,
Error = 1 << 2
}
}
}

View File

@ -1,123 +0,0 @@
using System;
using System.Collections.Generic;
using System.Net;
namespace DotN64.PI
{
public partial class PeripheralInterface : Interface
{
#region Fields
private readonly IReadOnlyList<MappingEntry> memoryMaps;
private const byte CICStatusOffset = 60;
private const byte ResetControllerStatus = 1 << 0, ClearInterruptStatus = 1 << 1;
#endregion
#region Properties
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
public StatusRegister Status { get; set; }
public byte[] BootROM { get; set; }
public byte[] RAM { get; } = new byte[64];
public Domain[] Domains { get; } = new[]
{
new Domain(),
new Domain()
};
/// <summary>
/// Starting RDRAM address.
/// </summary>
public uint DRAMAddress { get; set; }
/// <summary>
/// Starting AD16 address.
/// </summary>
public uint PBusAddress { get; set; }
/// <summary>
/// Write data length.
/// </summary>
public uint WriteLength { get; set; }
#endregion
#region Constructors
public PeripheralInterface()
{
memoryMaps = new[]
{
new MappingEntry(0x1FC00000, 0x1FC007BF) // PIF Boot ROM.
{
Read = o => (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(BootROM, (int)o))
},
new MappingEntry(0x1FC007C0, 0x1FC007FF) // PIF (JoyChannel) RAM.
{
Read = o => BitConverter.ToUInt32(RAM, (int)o),
Write = (o, v) =>
{
unsafe
{
fixed (byte* data = &RAM[(int)o])
{
*(uint*)data = v;
}
}
if (o == CICStatusOffset && RAM[o] == (byte)CICStatus.Waiting) // The boot ROM waits for the PIF's CIC check to be OK.
RAM[o] = (byte)CICStatus.OK; // We tell it it's OK by having the loaded word that gets ANDI'd match the immediate value 128, storing non-zero which allows us to exit the BEQL loop.
}
},
new MappingEntry(0x04600010, 0x04600013) // PI status.
{
Read = o => (uint)Status,
Write = (o, v) =>
{
if ((v & ResetControllerStatus) != 0)
ResetController();
if ((v & ClearInterruptStatus) != 0)
ClearInterrupt();
}
},
new MappingEntry(0x04600014, 0x04600017) // PI dom1 latency.
{
Write = (o, v) => Domains[0].Latency = (byte)v
},
new MappingEntry(0x04600018, 0x0460001B) // PI dom1 pulse width.
{
Write = (o, v) => Domains[0].PulseWidth = (byte)v
},
new MappingEntry(0x0460001C, 0x0460001F) // PI dom1 page size.
{
Write = (o, v) => Domains[0].PageSize = (byte)v
},
new MappingEntry(0x04600020, 0x04600023) // PI dom1 release.
{
Write = (o, v) => Domains[0].Release = (byte)v
},
new MappingEntry(0x04600000, 0x04600003) // PI DRAM address.
{
Write = (o, v) => DRAMAddress = v & ((1 << 24) - 1)
},
new MappingEntry(0x04600004, 0x04600007) // PI pbus (cartridge) address.
{
Write = (o, v) => PBusAddress = v
},
new MappingEntry(0x0460000C, 0x0460000F) // PI write length.
{
Write = (o, v) => WriteLength = v & ((1 << 24) - 1)
}
};
}
#endregion
#region Methods
private void ClearInterrupt() { /* TODO: Implement. */ }
private void ResetController() { /* TODO: Implement. */ }
#endregion
}
}

View File

@ -15,7 +15,7 @@ namespace DotN64
switch (arg)
{
case "--pif-rom":
nintendo64.PI.BootROM = File.ReadAllBytes(args[++i]);
nintendo64.RCP.PI.BootROM = File.ReadAllBytes(args[++i]);
break;
default:
nintendo64.Cartridge = Cartridge.FromFile(new FileInfo(arg));

View File

@ -0,0 +1,52 @@
using System.Collections.Generic;
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public class AudioInterface : Interface
{
#region Fields
private readonly IReadOnlyList<MappingEntry> memoryMaps;
#endregion
#region Properties
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
private uint dramAddress;
/// <summary>
/// Starting RDRAM address (8B-aligned).
/// </summary>
public uint DRAMAddress
{
get => dramAddress;
set => dramAddress = value & ((1 << 24) - 1);
}
private uint transferLength;
public uint TransferLength
{
get => transferLength;
set => transferLength = (uint)(value & ((1 << 18) - 1) & ~((1 << 3) - 1)); // "v2.0".
}
#endregion
#region Constructors
public AudioInterface()
{
memoryMaps = new[]
{
new MappingEntry(0x04500000, 0x04500003) // AI DRAM address.
{
Write = (o, v) => DRAMAddress = v
},
new MappingEntry(0x04500004, 0x04500007) // AI length.
{
Write = (o, v) => TransferLength = v
}
};
}
#endregion
}
}
}

View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Specialized;
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class MIPSInterface
{
public struct InitModeRegister
{
#region Fields
private BitVector32 bits;
public static readonly BitVector32.Section InitLengthSection = BitVector32.CreateSection((1 << 7) - 1),
ClearInitModeSection = BitVector32.CreateSection(1, InitLengthSection),
SetInitModeSection = BitVector32.CreateSection(1, ClearInitModeSection),
ClearEBusTestModeSection = BitVector32.CreateSection(1, SetInitModeSection),
SetEBusTestModeSection = BitVector32.CreateSection(1, ClearEBusTestModeSection),
ClearDPInterruptSection = BitVector32.CreateSection(1, SetEBusTestModeSection),
ClearRDRAMRegSection = BitVector32.CreateSection(1, ClearDPInterruptSection),
SetRDRAMRegModeSection = BitVector32.CreateSection(1, ClearRDRAMRegSection);
private static readonly BitVector32.Section initModeSection = BitVector32.CreateSection(1, InitLengthSection),
eBusTestModeSection = BitVector32.CreateSection(1, initModeSection),
rdramRegModeSection = BitVector32.CreateSection(1, eBusTestModeSection);
#endregion
#region Properties
public byte InitLength
{
get => (byte)bits[InitLengthSection];
set => bits[InitLengthSection] = value;
}
public bool InitMode
{
get => Convert.ToBoolean(bits[initModeSection]);
set => bits[initModeSection] = Convert.ToInt32(value);
}
public bool EBusTestMode
{
get => Convert.ToBoolean(bits[eBusTestModeSection]);
set => bits[eBusTestModeSection] = Convert.ToInt32(value);
}
public bool RDRAMRegMode
{
get => Convert.ToBoolean(bits[rdramRegModeSection]);
set => bits[rdramRegModeSection] = Convert.ToInt32(value);
}
#endregion
#region Operators
public static implicit operator InitModeRegister(uint data) => new InitModeRegister { bits = new BitVector32((int)data) };
public static implicit operator uint(InitModeRegister mode) => (uint)mode.bits.Data;
#endregion
}
}
}
}

View File

@ -0,0 +1,55 @@
using System;
using System.Collections.Generic;
using System.Collections.Specialized;
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class MIPSInterface : Interface
{
#region Fields
private readonly IReadOnlyList<MappingEntry> memoryMaps;
#endregion
#region Properties
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
public InitModeRegister InitMode { get; set; }
#endregion
#region Constructors
public MIPSInterface()
{
memoryMaps = new[]
{
new MappingEntry(0x04300000, 0x04300003) // MI init mode.
{
Write = (o, v) =>
{
var bits = new BitVector32((int)v);
var mode = InitMode;
mode.InitLength = (byte)bits[InitModeRegister.InitLengthSection];
mode.InitMode &= bits[InitModeRegister.ClearInitModeSection] == 0;
mode.InitMode |= bits[InitModeRegister.SetInitModeSection] != 0;
mode.EBusTestMode &= bits[InitModeRegister.ClearEBusTestModeSection] == 0;
mode.EBusTestMode |= bits[InitModeRegister.SetEBusTestModeSection] != 0;
if (bits[InitModeRegister.ClearDPInterruptSection] != 0)
throw new NotImplementedException("Clear DP interrupt.");
mode.RDRAMRegMode &= bits[InitModeRegister.ClearRDRAMRegSection] == 0;
mode.RDRAMRegMode |= bits[InitModeRegister.SetRDRAMRegModeSection] != 0;
InitMode = mode;
}
}
};
}
#endregion
}
}
}

View File

@ -0,0 +1,25 @@
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class PeripheralInterface
{
private enum CICStatus : byte
{
/// <summary>
/// The boot ROM copies the cartridge's bootstrap code [0x40, 0x1000[ into the RSP's DMEM while computing the CIC algorithm on the data.
/// When done, it stores the result in the PIF RAM at [48, 56[.
/// </summary>
Computing = 16,
/// <summary>
/// The CPU waits for the PIF CPU to validate that the cartridge's CIC computed the same result stored in the PIF RAM.
/// </summary>
Waiting = 48,
/// <summary>
/// The CIC check matched, continue booting.
/// </summary>
OK = 128
}
}
}
}

View File

@ -0,0 +1,43 @@
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class PeripheralInterface
{
public class Domain
{
#region Properties
/// <summary>
/// Device latency.
/// </summary>
public byte Latency { get; set; }
/// <summary>
/// Device R/W strobe pulse width.
/// </summary>
public byte PulseWidth { get; set; }
private byte pageSize;
/// <summary>
/// Device page size.
/// </summary>
public byte PageSize
{
get => pageSize;
set => pageSize = (byte)(value & ((1 << 4) - 1));
}
private byte release;
/// <summary>
/// Device R/W release duration.
/// </summary>
public byte Release
{
get => release;
set => release = (byte)(value & ((1 << 2) - 1));
}
#endregion
}
}
}
}

View File

@ -0,0 +1,16 @@
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class PeripheralInterface
{
[System.Flags]
public enum StatusRegister
{
DMABusy = 1 << 0,
IOBusy = 1 << 1,
Error = 1 << 2
}
}
}
}

View File

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Net;
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class PeripheralInterface : Interface
{
#region Fields
private readonly IReadOnlyList<MappingEntry> memoryMaps;
private const byte CICStatusOffset = 60;
private const byte ResetControllerStatus = 1 << 0, ClearInterruptStatus = 1 << 1;
#endregion
#region Properties
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
public StatusRegister Status { get; set; }
public byte[] BootROM { get; set; }
public byte[] RAM { get; } = new byte[64];
public Domain[] Domains { get; } = new[]
{
new Domain(),
new Domain()
};
/// <summary>
/// Starting RDRAM address.
/// </summary>
public uint DRAMAddress { get; set; }
/// <summary>
/// Starting AD16 address.
/// </summary>
public uint PBusAddress { get; set; }
/// <summary>
/// Write data length.
/// </summary>
public uint WriteLength { get; set; }
#endregion
#region Constructors
public PeripheralInterface()
{
memoryMaps = new[]
{
new MappingEntry(0x1FC00000, 0x1FC007BF) // PIF Boot ROM.
{
Read = o => (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(BootROM, (int)o))
},
new MappingEntry(0x1FC007C0, 0x1FC007FF) // PIF (JoyChannel) RAM.
{
Read = o => BitConverter.ToUInt32(RAM, (int)o),
Write = (o, v) =>
{
unsafe
{
fixed (byte* data = &RAM[(int)o])
{
*(uint*)data = v;
}
}
if (o == CICStatusOffset && RAM[o] == (byte)CICStatus.Waiting) // The boot ROM waits for the PIF's CIC check to be OK.
RAM[o] = (byte)CICStatus.OK; // We tell it it's OK by having the loaded word that gets ANDI'd match the immediate value 128, storing non-zero which allows us to exit the BEQL loop.
}
},
new MappingEntry(0x04600010, 0x04600013) // PI status.
{
Read = o => (uint)Status,
Write = (o, v) =>
{
if ((v & ResetControllerStatus) != 0)
ResetController();
if ((v & ClearInterruptStatus) != 0)
ClearInterrupt();
}
},
new MappingEntry(0x04600014, 0x04600017) // PI dom1 latency.
{
Write = (o, v) => Domains[0].Latency = (byte)v
},
new MappingEntry(0x04600018, 0x0460001B) // PI dom1 pulse width.
{
Write = (o, v) => Domains[0].PulseWidth = (byte)v
},
new MappingEntry(0x0460001C, 0x0460001F) // PI dom1 page size.
{
Write = (o, v) => Domains[0].PageSize = (byte)v
},
new MappingEntry(0x04600020, 0x04600023) // PI dom1 release.
{
Write = (o, v) => Domains[0].Release = (byte)v
},
new MappingEntry(0x04600000, 0x04600003) // PI DRAM address.
{
Write = (o, v) => DRAMAddress = v & ((1 << 24) - 1)
},
new MappingEntry(0x04600004, 0x04600007) // PI pbus (cartridge) address.
{
Write = (o, v) => PBusAddress = v
},
new MappingEntry(0x0460000C, 0x0460000F) // PI write length.
{
Write = (o, v) => WriteLength = v & ((1 << 24) - 1)
}
};
}
#endregion
#region Methods
private void ClearInterrupt() { /* TODO: Implement. */ }
private void ResetController() { /* TODO: Implement. */ }
#endregion
}
}
}

View File

@ -0,0 +1,41 @@
using System;
using System.Collections.Specialized;
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class RDRAMInterface
{
public struct ConfigRegister
{
#region Fields
private BitVector32 bits;
private static readonly BitVector32.Section currentControlInput = BitVector32.CreateSection((1 << 6) - 1),
currentControlEnable = BitVector32.CreateSection(1, currentControlInput);
#endregion
#region Properties
public byte CurrentControlInput
{
get => (byte)bits[currentControlInput];
set => bits[currentControlInput] = value;
}
public bool CurrentControlEnable
{
get => Convert.ToBoolean(bits[currentControlEnable]);
set => bits[currentControlEnable] = Convert.ToInt32(value);
}
#endregion
#region Operators
public static implicit operator ConfigRegister(uint data) => new ConfigRegister { bits = new BitVector32((int)data) };
public static implicit operator uint(ConfigRegister register) => (uint)register.bits.Data;
#endregion
}
}
}
}

View File

@ -0,0 +1,48 @@
using System;
using System.Collections.Specialized;
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class RDRAMInterface
{
public struct ModeRegister
{
#region Fields
private BitVector32 bits;
private static readonly BitVector32.Section operatingMode = BitVector32.CreateSection((1 << 2) - 1),
stopTActive = BitVector32.CreateSection(1, operatingMode),
stopRActive = BitVector32.CreateSection(1, stopTActive);
#endregion
#region Properties
public byte OperatingMode
{
get => (byte)bits[operatingMode];
set => bits[operatingMode] = value;
}
public bool StopTActive
{
get => Convert.ToBoolean(bits[stopTActive]);
set => bits[stopTActive] = Convert.ToInt32(value);
}
public bool StopRActive
{
get => Convert.ToBoolean(bits[stopRActive]);
set => bits[stopRActive] = Convert.ToInt32(value);
}
#endregion
#region Operators
public static implicit operator ModeRegister(uint data) => new ModeRegister { bits = new BitVector32((int)data) };
public static implicit operator uint(ModeRegister register) => (uint)register.bits.Data;
#endregion
}
}
}
}

View File

@ -0,0 +1,62 @@
using System;
using System.Collections.Specialized;
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class RDRAMInterface
{
public struct RefreshRegister
{
#region Fields
private BitVector32 bits;
private static readonly BitVector32.Section cleanRefreshDelaySection = BitVector32.CreateSection((1 << 8) - 1),
dirtyRefreshDelaySection = BitVector32.CreateSection((1 << 8) - 1, cleanRefreshDelaySection),
refreshBankSection = BitVector32.CreateSection(1, dirtyRefreshDelaySection),
refreshEnableSection = BitVector32.CreateSection(1, refreshBankSection),
refreshOptimiseSection = BitVector32.CreateSection(1, refreshEnableSection);
#endregion
#region Properties
public byte CleanRefreshDelay
{
get => (byte)bits[cleanRefreshDelaySection];
set => bits[cleanRefreshDelaySection] = value;
}
public byte DirtyRefreshDelay
{
get => (byte)bits[dirtyRefreshDelaySection];
set => bits[dirtyRefreshDelaySection] = value;
}
public bool RefreshBank
{
get => Convert.ToBoolean(bits[refreshBankSection]);
set => bits[refreshBankSection] = Convert.ToInt32(value);
}
public bool RefreshEnable
{
get => Convert.ToBoolean(bits[refreshEnableSection]);
set => bits[refreshEnableSection] = Convert.ToInt32(value);
}
public bool RefreshOptimise
{
get => Convert.ToBoolean(bits[refreshOptimiseSection]);
set => bits[refreshOptimiseSection] = Convert.ToInt32(value);
}
#endregion
#region Operators
public static implicit operator RefreshRegister(uint data) => new RefreshRegister { bits = new BitVector32((int)data) };
public static implicit operator uint(RefreshRegister register) => (uint)register.bits.Data;
#endregion
}
}
}
}

View File

@ -0,0 +1,72 @@
using System;
using System.Collections.Generic;
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class RDRAMInterface : Interface
{
#region Fields
private readonly IReadOnlyList<MappingEntry> memoryMaps;
#endregion
#region Properties
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
public byte Select { get; set; } = 0x14;
public ConfigRegister Config { get; set; } = 0x40;
public ModeRegister Mode { get; set; } = 0x0E;
public RefreshRegister Refresh { get; set; } = 0x00063634;
public byte[] RAM { get; } = new byte[0x00400000]; // The base system has 4 MB of RAM installed.
#endregion
#region Constructors
public RDRAMInterface()
{
memoryMaps = new[]
{
new MappingEntry(0x0470000C, 0x0470000F) // RI select.
{
Read = o => Select,
Write = (o, v) => Select = (byte)(v & (1 << 3) - 1)
},
new MappingEntry(0x04700004, 0x04700007) // RI config.
{
Write = (o, v) => Config = v
},
new MappingEntry(0x04700008, 0x0470000B) // RI current load.
{
Write = (o, v) => { /* TODO: Any write updates current control register. */ }
},
new MappingEntry(0x04700000, 0x04700003) // RI mode.
{
Write = (o, v) => Mode = v
},
new MappingEntry(0x04700010, 0x04700013) // RI refresh.
{
},
new MappingEntry(0x00000000, 0x03EFFFFF) // RDRAM memory.
{
Read = o => BitConverter.ToUInt32(RAM, (int)o),
Write = (o, v) =>
{
unsafe
{
fixed (byte* data = &RAM[(int)o])
{
*(uint*)data = v;
}
}
}
}
};
}
#endregion
}
}
}

View File

@ -0,0 +1,22 @@
using System.Collections.Generic;
namespace DotN64.RCP
{
using Extensions;
public partial class RealityCoprocessor
{
public abstract class Interface
{
#region Properties
protected abstract IReadOnlyList<MappingEntry> MemoryMaps { get; }
#endregion
#region Methods
public uint ReadWord(ulong address) => MemoryMaps.GetEntry(address).ReadWord(address);
public void WriteWord(ulong address, uint value) => MemoryMaps.GetEntry(address).WriteWord(address, value);
#endregion
}
}
}

View File

@ -6,6 +6,18 @@
public SignalProcessor SP { get; } = new SignalProcessor();
public DisplayProcessor DP { get; } = new DisplayProcessor();
public PeripheralInterface PI { get; } = new PeripheralInterface();
public SerialInterface SI { get; } = new SerialInterface();
public AudioInterface AI { get; } = new AudioInterface();
public VideoInterface VI { get; } = new VideoInterface();
public MIPSInterface MI { get; } = new MIPSInterface();
public RDRAMInterface RI { get; } = new RDRAMInterface();
#endregion
}
}

View File

@ -0,0 +1,17 @@
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class SerialInterface
{
[System.Flags]
public enum StatusRegister
{
DMABusy = 1 << 0,
IOReadBusy = 1 << 1,
DMAError = 1 << 3,
Interrupt = 1 << 12
}
}
}
}

View File

@ -0,0 +1,34 @@
using System.Collections.Generic;
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class SerialInterface : Interface
{
#region Fields
private readonly IReadOnlyList<MappingEntry> memoryMaps;
#endregion
#region Properties
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
public StatusRegister Status { get; set; }
#endregion
#region Constructors
public SerialInterface()
{
memoryMaps = new[]
{
new MappingEntry(0x04800018, 0x0480001B) // SI status.
{
Read = o => (uint)Status,
Write = (o, v) => Status &= ~StatusRegister.Interrupt
}
};
}
#endregion
}
}
}

View File

@ -0,0 +1,47 @@
using System.Collections.Specialized;
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class VideoInterface
{
public struct HorizontalVideoRegister
{
#region Fields
private BitVector32 bits;
private static readonly BitVector32.Section activeVideoEnd = BitVector32.CreateSection((1 << 10) - 1),
unknown1 = BitVector32.CreateSection((1 << 6) - 1, activeVideoEnd),
activeVideoStart = BitVector32.CreateSection((1 << 10) - 1, unknown1);
#endregion
#region Properties
/// <summary>
/// End of active video in screen pixels.
/// </summary>
public ushort ActiveVideoEnd
{
get => (ushort)bits[activeVideoEnd];
set => bits[activeVideoEnd] = value;
}
/// <summary>
/// Start of active video in screen pixels.
/// </summary>
public ushort ActiveVideoStart
{
get => (ushort)bits[activeVideoStart];
set => bits[activeVideoStart] = value;
}
#endregion
#region Operators
public static implicit operator HorizontalVideoRegister(uint data) => new HorizontalVideoRegister { bits = new BitVector32((int)data) };
public static implicit operator uint(HorizontalVideoRegister register) => (uint)register.bits.Data;
#endregion
}
}
}
}

View File

@ -0,0 +1,66 @@
using System.Collections.Generic;
namespace DotN64.RCP
{
public partial class RealityCoprocessor
{
public partial class VideoInterface : Interface
{
#region Fields
private readonly IReadOnlyList<MappingEntry> memoryMaps;
#endregion
#region Properties
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
private ushort verticalInterrupt;
/// <summary>
/// Interrupt when current half-line = V_INTR.
/// </summary>
public ushort VerticalInterrupt
{
get => verticalInterrupt;
set => verticalInterrupt = (ushort)(value & ((1 << 10) - 1));
}
public HorizontalVideoRegister HorizontalVideo { get; set; }
private ushort currentVerticalLine;
/// <summary>
/// Current half line, sampled once per line (the lsb of V_CURRENT is constant within a field, and in interlaced modes gives the field number - which is constant for non-interlaced modes).
/// </summary>
public ushort CurrentVerticalLine
{
get => currentVerticalLine;
set
{
currentVerticalLine = (ushort)(value & ((1 << 10) - 1));
// TODO: Clear interrupt line.
}
}
#endregion
#region Constructors
public VideoInterface()
{
memoryMaps = new[]
{
new MappingEntry(0x0440000C, 0x0440000F) // VI vertical intr.
{
Write = (o, v) => VerticalInterrupt = (ushort)v
},
new MappingEntry(0x04400024, 0x04400027) // VI horizontal video.
{
Write = (o, v) => HorizontalVideo = v
},
new MappingEntry(0x04400010, 0x04400013) // VI current vertical line.
{
Write = (o, v) => CurrentVerticalLine = (ushort)v
}
};
}
#endregion
}
}
}

View File

@ -1,38 +0,0 @@
using System;
using System.Collections.Specialized;
namespace DotN64.RI
{
public partial class RDRAMInterface
{
public struct ConfigRegister
{
#region Fields
private BitVector32 bits;
private static readonly BitVector32.Section currentControlInput = BitVector32.CreateSection((1 << 6) - 1),
currentControlEnable = BitVector32.CreateSection(1, currentControlInput);
#endregion
#region Properties
public byte CurrentControlInput
{
get => (byte)bits[currentControlInput];
set => bits[currentControlInput] = value;
}
public bool CurrentControlEnable
{
get => Convert.ToBoolean(bits[currentControlEnable]);
set => bits[currentControlEnable] = Convert.ToInt32(value);
}
#endregion
#region Operators
public static implicit operator ConfigRegister(uint data) => new ConfigRegister { bits = new BitVector32((int)data) };
public static implicit operator uint(ConfigRegister register) => (uint)register.bits.Data;
#endregion
}
}
}

View File

@ -1,45 +0,0 @@
using System;
using System.Collections.Specialized;
namespace DotN64.RI
{
public partial class RDRAMInterface
{
public struct ModeRegister
{
#region Fields
private BitVector32 bits;
private static readonly BitVector32.Section operatingMode = BitVector32.CreateSection((1 << 2) - 1),
stopTActive = BitVector32.CreateSection(1, operatingMode),
stopRActive = BitVector32.CreateSection(1, stopTActive);
#endregion
#region Properties
public byte OperatingMode
{
get => (byte)bits[operatingMode];
set => bits[operatingMode] = value;
}
public bool StopTActive
{
get => Convert.ToBoolean(bits[stopTActive]);
set => bits[stopTActive] = Convert.ToInt32(value);
}
public bool StopRActive
{
get => Convert.ToBoolean(bits[stopRActive]);
set => bits[stopRActive] = Convert.ToInt32(value);
}
#endregion
#region Operators
public static implicit operator ModeRegister(uint data) => new ModeRegister { bits = new BitVector32((int)data) };
public static implicit operator uint(ModeRegister register) => (uint)register.bits.Data;
#endregion
}
}
}

View File

@ -1,59 +0,0 @@
using System;
using System.Collections.Specialized;
namespace DotN64.RI
{
public partial class RDRAMInterface
{
public struct RefreshRegister
{
#region Fields
private BitVector32 bits;
private static readonly BitVector32.Section cleanRefreshDelaySection = BitVector32.CreateSection((1 << 8) - 1),
dirtyRefreshDelaySection = BitVector32.CreateSection((1 << 8) - 1, cleanRefreshDelaySection),
refreshBankSection = BitVector32.CreateSection(1, dirtyRefreshDelaySection),
refreshEnableSection = BitVector32.CreateSection(1, refreshBankSection),
refreshOptimiseSection = BitVector32.CreateSection(1, refreshEnableSection);
#endregion
#region Properties
public byte CleanRefreshDelay
{
get => (byte)bits[cleanRefreshDelaySection];
set => bits[cleanRefreshDelaySection] = value;
}
public byte DirtyRefreshDelay
{
get => (byte)bits[dirtyRefreshDelaySection];
set => bits[dirtyRefreshDelaySection] = value;
}
public bool RefreshBank
{
get => Convert.ToBoolean(bits[refreshBankSection]);
set => bits[refreshBankSection] = Convert.ToInt32(value);
}
public bool RefreshEnable
{
get => Convert.ToBoolean(bits[refreshEnableSection]);
set => bits[refreshEnableSection] = Convert.ToInt32(value);
}
public bool RefreshOptimise
{
get => Convert.ToBoolean(bits[refreshOptimiseSection]);
set => bits[refreshOptimiseSection] = Convert.ToInt32(value);
}
#endregion
#region Operators
public static implicit operator RefreshRegister(uint data) => new RefreshRegister { bits = new BitVector32((int)data) };
public static implicit operator uint(RefreshRegister register) => (uint)register.bits.Data;
#endregion
}
}
}

View File

@ -1,69 +0,0 @@
using System;
using System.Collections.Generic;
namespace DotN64.RI
{
public partial class RDRAMInterface : Interface
{
#region Fields
private readonly IReadOnlyList<MappingEntry> memoryMaps;
#endregion
#region Properties
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
public byte Select { get; set; } = 0x14;
public ConfigRegister Config { get; set; } = 0x40;
public ModeRegister Mode { get; set; } = 0x0E;
public RefreshRegister Refresh { get; set; } = 0x00063634;
public byte[] RAM { get; } = new byte[0x00400000]; // The base system has 4 MB of RAM installed.
#endregion
#region Constructors
public RDRAMInterface()
{
memoryMaps = new[]
{
new MappingEntry(0x0470000C, 0x0470000F) // RI select.
{
Read = o => Select,
Write = (o, v) => Select = (byte)(v & (1 << 3) - 1)
},
new MappingEntry(0x04700004, 0x04700007) // RI config.
{
Write = (o, v) => Config = v
},
new MappingEntry(0x04700008, 0x0470000B) // RI current load.
{
Write = (o, v) => { /* TODO: Any write updates current control register. */ }
},
new MappingEntry(0x04700000, 0x04700003) // RI mode.
{
Write = (o, v) => Mode = v
},
new MappingEntry(0x04700010, 0x04700013) // RI refresh.
{
},
new MappingEntry(0x00000000, 0x03EFFFFF) // RDRAM memory.
{
Read = o => BitConverter.ToUInt32(RAM, (int)o),
Write = (o, v) =>
{
unsafe
{
fixed (byte* data = &RAM[(int)o])
{
*(uint*)data = v;
}
}
}
}
};
}
#endregion
}
}

View File

@ -1,14 +0,0 @@
namespace DotN64.SI
{
public partial class SerialInterface
{
[System.Flags]
public enum StatusRegister
{
DMABusy = 1 << 0,
IOReadBusy = 1 << 1,
DMAError = 1 << 3,
Interrupt = 1 << 12
}
}
}

View File

@ -1,31 +0,0 @@
using System.Collections.Generic;
namespace DotN64.SI
{
public partial class SerialInterface : Interface
{
#region Fields
private readonly IReadOnlyList<MappingEntry> memoryMaps;
#endregion
#region Properties
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
public StatusRegister Status { get; set; }
#endregion
#region Constructors
public SerialInterface()
{
memoryMaps = new[]
{
new MappingEntry(0x04800018, 0x0480001B) // SI status.
{
Read = o => (uint)Status,
Write = (o, v) => Status &= ~StatusRegister.Interrupt
}
};
}
#endregion
}
}

View File

@ -1,44 +0,0 @@
using System.Collections.Specialized;
namespace DotN64.VI
{
public partial class VideoInterface
{
public struct HorizontalVideoRegister
{
#region Fields
private BitVector32 bits;
private static readonly BitVector32.Section activeVideoEnd = BitVector32.CreateSection((1 << 10) - 1),
unknown1 = BitVector32.CreateSection((1 << 6) - 1, activeVideoEnd),
activeVideoStart = BitVector32.CreateSection((1 << 10) - 1, unknown1);
#endregion
#region Properties
/// <summary>
/// End of active video in screen pixels.
/// </summary>
public ushort ActiveVideoEnd
{
get => (ushort)bits[activeVideoEnd];
set => bits[activeVideoEnd] = value;
}
/// <summary>
/// Start of active video in screen pixels.
/// </summary>
public ushort ActiveVideoStart
{
get => (ushort)bits[activeVideoStart];
set => bits[activeVideoStart] = value;
}
#endregion
#region Operators
public static implicit operator HorizontalVideoRegister(uint data) => new HorizontalVideoRegister { bits = new BitVector32((int)data) };
public static implicit operator uint(HorizontalVideoRegister register) => (uint)register.bits.Data;
#endregion
}
}
}

View File

@ -1,63 +0,0 @@
using System.Collections.Generic;
namespace DotN64.VI
{
public partial class VideoInterface : Interface
{
#region Fields
private readonly IReadOnlyList<MappingEntry> memoryMaps;
#endregion
#region Properties
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
private ushort verticalInterrupt;
/// <summary>
/// Interrupt when current half-line = V_INTR.
/// </summary>
public ushort VerticalInterrupt
{
get => verticalInterrupt;
set => verticalInterrupt = (ushort)(value & ((1 << 10) - 1));
}
public HorizontalVideoRegister HorizontalVideo { get; set; }
private ushort currentVerticalLine;
/// <summary>
/// Current half line, sampled once per line (the lsb of V_CURRENT is constant within a field, and in interlaced modes gives the field number - which is constant for non-interlaced modes).
/// </summary>
public ushort CurrentVerticalLine
{
get => currentVerticalLine;
set
{
currentVerticalLine = (ushort)(value & ((1 << 10) - 1));
// TODO: Clear interrupt line.
}
}
#endregion
#region Constructors
public VideoInterface()
{
memoryMaps = new[]
{
new MappingEntry(0x0440000C, 0x0440000F) // VI vertical intr.
{
Write = (o, v) => VerticalInterrupt = (ushort)v
},
new MappingEntry(0x04400024, 0x04400027) // VI horizontal video.
{
Write = (o, v) => HorizontalVideo = v
},
new MappingEntry(0x04400010, 0x04400013) // VI current vertical line.
{
Write = (o, v) => CurrentVerticalLine = (ushort)v
}
};
}
#endregion
}
}