Moved RCP interfaces inside itself.
parent
467ea2a07d
commit
ea629a6b16
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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>
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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));
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
||||
}
|
|
@ -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
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue