* VR4300.Exceptions.cs: Added a custom exception.
* VR4300.cs: Fixed SLLV/SRLV not downcasting a register source. * Debugger.InstructionFormat.cs: Added some more formatting exceptions. * Program.cs: * Cartridge.cs: * DotN64.csproj: * RDRAM.cs: * PeripheralInterface.cs: * RDRAM.ConfigIndex.cs: * RDRAM.ConfigRegister.cs: * PeripheralInterface.CIC.cs: * PeripheralInterface.CICStatus.cs: * PeripheralInterface.DeviceState.cs: * RealityCoprocessor.RDRAMInterface.cs: * RealityCoprocessor.PeripheralInterface.cs: * Nintendo64.cs: Added peripherals and rewiring. * RealityCoprocessor.PeripheralInterface.StatusRegister.cs: Moved write register constants to an enum, too. * BitHelper.cs: Better method signatures.master
parent
23b06e69b5
commit
17dbe71cef
|
@ -0,0 +1,28 @@
|
|||
using System;
|
||||
|
||||
namespace DotN64.CPU
|
||||
{
|
||||
public partial class VR4300
|
||||
{
|
||||
public class UnimplementedOperationException : Exception
|
||||
{
|
||||
#region Properties
|
||||
public Instruction Instruction { get; }
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public UnimplementedOperationException(Instruction instruction)
|
||||
: base($"Unimplemented opcode ({GetOpCodeType(instruction).Name} {FormatOpCodeBits(instruction)}) from instruction 0x{(uint)instruction:X8}.")
|
||||
{
|
||||
Instruction = instruction;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
private static Type GetOpCodeType(Instruction instruction) => instruction.Special?.GetType() ?? instruction.RegImm?.GetType() ?? instruction.OP.GetType();
|
||||
|
||||
private static string FormatOpCodeBits(Instruction instruction) => "0b" + Convert.ToString((byte?)instruction.Special ?? (byte?)instruction.RegImm ?? (byte)instruction.OP, 2);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
|
@ -94,7 +94,7 @@ namespace DotN64.CPU
|
|||
[Instruction.FromOpCode(OpCode.MTC0)] = i => CP0.Registers[i.RD] = GPR[i.RT],
|
||||
[Instruction.FromOpCode(OpCode.ORI)] = i => GPR[i.RT] = GPR[i.RS] | i.Immediate,
|
||||
[Instruction.FromOpCode(OpCode.LW)] = i => GPR[i.RT] = (ulong)(int)ReadWord((ulong)(short)i.Immediate + GPR[i.RS]),
|
||||
[Instruction.FromOpCode(OpCode.ANDI)] = i => GPR[i.RT] = (ulong)(i.Immediate & (ushort)GPR[i.RS]),
|
||||
[Instruction.FromOpCode(OpCode.ANDI)] = i => GPR[i.RT] = i.Immediate & GPR[i.RS],
|
||||
[Instruction.FromOpCode(OpCode.BEQL)] = i => BranchLikely(i, (rs, rt) => rs == rt),
|
||||
[Instruction.FromOpCode(OpCode.ADDIU)] = i => GPR[i.RT] = (ulong)(int)(GPR[i.RS] + (ulong)(short)i.Immediate),
|
||||
[Instruction.FromOpCode(OpCode.SW)] = i => WriteWord((ulong)(short)i.Immediate + GPR[i.RS], (uint)GPR[i.RT]),
|
||||
|
@ -140,8 +140,8 @@ namespace DotN64.CPU
|
|||
[Instruction.FromOpCode(SpecialOpCode.MFHI)] = i => GPR[i.RD] = HI,
|
||||
[Instruction.FromOpCode(SpecialOpCode.ADDU)] = i => GPR[i.RD] = (ulong)((int)GPR[i.RS] + (int)GPR[i.RT]),
|
||||
[Instruction.FromOpCode(SpecialOpCode.SLTU)] = i => GPR[i.RD] = (ulong)(GPR[i.RS] < GPR[i.RT] ? 1 : 0),
|
||||
[Instruction.FromOpCode(SpecialOpCode.SLLV)] = i => GPR[i.RD] = (ulong)(int)(GPR[i.RT] << (int)(GPR[i.RS] & ((1 << 5) - 1))),
|
||||
[Instruction.FromOpCode(SpecialOpCode.SRLV)] = i => GPR[i.RD] = (ulong)(int)(GPR[i.RT] >> (int)(GPR[i.RS] & ((1 << 5) - 1))),
|
||||
[Instruction.FromOpCode(SpecialOpCode.SLLV)] = i => GPR[i.RD] = (ulong)(int)((uint)GPR[i.RT] << (int)(GPR[i.RS] & ((1 << 5) - 1))),
|
||||
[Instruction.FromOpCode(SpecialOpCode.SRLV)] = i => GPR[i.RD] = (ulong)(int)((uint)GPR[i.RT] >> (int)(GPR[i.RS] & ((1 << 5) - 1))),
|
||||
[Instruction.FromOpCode(SpecialOpCode.AND)] = i => GPR[i.RD] = GPR[i.RS] & GPR[i.RT],
|
||||
[Instruction.FromOpCode(SpecialOpCode.SLT)] = i => GPR[i.RD] = GPR[i.RS] < GPR[i.RT] ? (ulong)1 : 0,
|
||||
[Instruction.FromOpCode(RegImmOpCode.BGEZAL)] = i => Branch(i, (rs, rt) => rs >= 0, true),
|
||||
|
@ -179,7 +179,7 @@ namespace DotN64.CPU
|
|||
if (operations.TryGetValue(instruction.ToOpCode(), out var operation))
|
||||
operation(instruction);
|
||||
else
|
||||
throw new Exception($"Unknown opcode from instruction 0x{(uint)instruction:X8}.");
|
||||
throw new UnimplementedOperationException(instruction);
|
||||
}
|
||||
|
||||
public void Step()
|
||||
|
|
|
@ -5,21 +5,23 @@ using System.Text;
|
|||
|
||||
namespace DotN64
|
||||
{
|
||||
using Helpers;
|
||||
|
||||
public class Cartridge
|
||||
{
|
||||
#region Properties
|
||||
public byte[] ROM { get; set; }
|
||||
|
||||
public uint ClockRate => (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(ROM, 0x04));
|
||||
public uint ClockRate => BitHelper.FromBigEndian(BitConverter.ToUInt32(ROM, 0x04));
|
||||
|
||||
public uint BootAddressOffset => (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(ROM, 0x08));
|
||||
public uint BootAddressOffset => BitHelper.FromBigEndian(BitConverter.ToUInt32(ROM, 0x08));
|
||||
|
||||
public uint ReleaseOffset => (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(ROM, 0x0C));
|
||||
public uint ReleaseOffset => BitHelper.FromBigEndian(BitConverter.ToUInt32(ROM, 0x0C));
|
||||
|
||||
public uint[] CRC => new[]
|
||||
{
|
||||
(uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(ROM, 0x10)),
|
||||
(uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(ROM, 0x14))
|
||||
BitHelper.FromBigEndian(BitConverter.ToUInt32(ROM, 0x10)),
|
||||
BitHelper.FromBigEndian(BitConverter.ToUInt32(ROM, 0x14))
|
||||
};
|
||||
|
||||
public string ImageName => Encoding.ASCII.GetString(ROM, 0x20, 0x34 - 0x20);
|
||||
|
|
|
@ -60,9 +60,17 @@
|
|||
{
|
||||
case VR4300.SpecialOpCode.JR:
|
||||
return Format(instruction, FormatRegister(instruction.RS, cpu));
|
||||
case VR4300.SpecialOpCode.MFHI:
|
||||
case VR4300.SpecialOpCode.MFLO:
|
||||
return Format(instruction, FormatRegister(instruction.RD, cpu));
|
||||
case VR4300.SpecialOpCode.MULTU:
|
||||
return Format(instruction, FormatRegister(instruction.RS, cpu), FormatRegister(instruction.RT, cpu));
|
||||
case VR4300.SpecialOpCode.SLLV:
|
||||
case VR4300.SpecialOpCode.SRLV:
|
||||
return Format(instruction, FormatRegister(instruction.RD, cpu), FormatRegister(instruction.RT, cpu), FormatRegister(instruction.RS, cpu));
|
||||
case VR4300.SpecialOpCode.SLL:
|
||||
case VR4300.SpecialOpCode.SRL:
|
||||
return Format(instruction, FormatRegister(instruction.RD, cpu), FormatRegister(instruction.RT, cpu), (sbyte)instruction.SA);
|
||||
}
|
||||
|
||||
switch (instruction.OP)
|
||||
|
|
|
@ -43,7 +43,6 @@
|
|||
<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" />
|
||||
|
@ -72,8 +71,14 @@
|
|||
<Compile Include="Helpers\BitHelper.cs" />
|
||||
<Compile Include="RCP\DP\RealityCoprocessor.DisplayProcessor.StatusRegister.cs" />
|
||||
<Compile Include="RCP\SP\RealityCoprocessor.SignalProcessor.StatusRegister.cs" />
|
||||
<Compile Include="RCP\RI\RealityCoprocessor.RDRAMInterface.RDRAMConfigRegister.cs" />
|
||||
<Compile Include="RCP\RI\RealityCoprocessor.RDRAMInterface.RDRAMConfigIndex.cs" />
|
||||
<Compile Include="CPU\VR4300\VR4300.Exceptions.cs" />
|
||||
<Compile Include="PIF\PeripheralInterface.cs" />
|
||||
<Compile Include="PIF\PeripheralInterface.CICStatus.cs" />
|
||||
<Compile Include="PIF\PeripheralInterface.DeviceState.cs" />
|
||||
<Compile Include="RDRAM\RDRAM.cs" />
|
||||
<Compile Include="RDRAM\RDRAM.ConfigIndex.cs" />
|
||||
<Compile Include="RDRAM\RDRAM.ConfigRegister.cs" />
|
||||
<Compile Include="PIF\PeripheralInterface.CIC.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="CPU\" />
|
||||
|
@ -91,6 +96,8 @@
|
|||
<Folder Include="CPU\VR4300\CP0\" />
|
||||
<Folder Include="Diagnostics\" />
|
||||
<Folder Include="Helpers\" />
|
||||
<Folder Include="PIF\" />
|
||||
<Folder Include="RDRAM\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -1,4 +1,5 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
using System.Net;
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DotN64.Helpers
|
||||
{
|
||||
|
@ -25,6 +26,7 @@ namespace DotN64.Helpers
|
|||
data |= (value & size) << shift;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static unsafe void Write(byte[] data, int index, uint value)
|
||||
{
|
||||
fixed (byte* pointer = &data[index])
|
||||
|
@ -32,6 +34,12 @@ namespace DotN64.Helpers
|
|||
*(uint*)pointer = value;
|
||||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint FromBigEndian(uint value) => (uint)IPAddress.NetworkToHostOrder((int)value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ToBigEndian(uint value) => (uint)IPAddress.HostToNetworkOrder((int)value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -15,7 +15,9 @@ namespace DotN64
|
|||
|
||||
public RealityCoprocessor RCP { get; }
|
||||
|
||||
public byte[] RAM { get; } = new byte[0x00400000]; // The base system has 4 MB of RAM installed.
|
||||
public RDRAM RAM { get; } = new RDRAM(new byte[0x00400000]); // The base system has 4 MB of RAM installed.
|
||||
|
||||
public PeripheralInterface PIF { get; }
|
||||
|
||||
public Cartridge Cartridge { get; set; }
|
||||
#endregion
|
||||
|
@ -24,17 +26,18 @@ namespace DotN64
|
|||
public Nintendo64()
|
||||
{
|
||||
RCP = new RealityCoprocessor(this);
|
||||
PIF = new PeripheralInterface(this);
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x1FC00000, 0x1FC007BF, false) // PIF Boot ROM.
|
||||
{
|
||||
Read = RCP.PI.MemoryMaps.ReadWord,
|
||||
Write = RCP.PI.MemoryMaps.WriteWord
|
||||
Read = PIF.MemoryMaps.ReadWord,
|
||||
Write = PIF.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x1FC007C0, 0x1FC007FF, false) // PIF (JoyChannel) RAM.
|
||||
{
|
||||
Read = RCP.PI.MemoryMaps.ReadWord,
|
||||
Write = RCP.PI.MemoryMaps.WriteWord
|
||||
Read = PIF.MemoryMaps.ReadWord,
|
||||
Write = PIF.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x04600000, 0x046FFFFF, false) // Peripheral interface (PI) registers.
|
||||
{
|
||||
|
@ -82,13 +85,13 @@ namespace DotN64
|
|||
},
|
||||
new MappingEntry(0x00000000, 0x03EFFFFF, false) // RDRAM memory.
|
||||
{
|
||||
Read = RCP.RI.MemoryMaps.ReadWord,
|
||||
Write = RCP.RI.MemoryMaps.WriteWord
|
||||
Read = RAM.MemoryMaps.ReadWord,
|
||||
Write = RAM.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x03F00000, 0x03FFFFFF, false) // RDRAM registers.
|
||||
{
|
||||
Read = RCP.RI.MemoryMaps.ReadWord,
|
||||
Write = RCP.RI.MemoryMaps.WriteWord
|
||||
Read = RAM.MemoryMaps.ReadWord,
|
||||
Write = RAM.MemoryMaps.WriteWord
|
||||
}
|
||||
};
|
||||
CPU = new VR4300
|
||||
|
@ -104,9 +107,7 @@ namespace DotN64
|
|||
public void PowerOn()
|
||||
{
|
||||
CPU.Reset();
|
||||
|
||||
if (RCP.PI.BootROM == null)
|
||||
RCP.PI.EmulateBootROM();
|
||||
PIF.Reset();
|
||||
}
|
||||
|
||||
public void Run()
|
||||
|
|
|
@ -0,0 +1,62 @@
|
|||
namespace DotN64
|
||||
{
|
||||
public partial class PeripheralInterface
|
||||
{
|
||||
public static class CIC
|
||||
{
|
||||
#region Methods
|
||||
private static uint ComputeCRC32(byte[] data, int offset, int length)
|
||||
{
|
||||
var table = new uint[256];
|
||||
uint c;
|
||||
|
||||
for (uint i = 0; i < table.Length; i++)
|
||||
{
|
||||
c = i;
|
||||
|
||||
for (uint j = 0; j < 8; j++)
|
||||
{
|
||||
if ((c & 1) != 0)
|
||||
c = 0xEDB88320 ^ (c >> 1);
|
||||
else
|
||||
c >>= 1;
|
||||
}
|
||||
|
||||
table[i] = c;
|
||||
}
|
||||
|
||||
c = 0 ^ 0xFFFFFFFF;
|
||||
|
||||
for (int i = 0; i < length; i++)
|
||||
{
|
||||
c = table[(c ^ (data[offset + i])) & 0xFF] ^ (c >> 8);
|
||||
}
|
||||
|
||||
return c ^ 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
public static uint GetSeed(byte[] data, int offset = 0x40, int length = 0x1000 - 0x40)
|
||||
{
|
||||
switch (ComputeCRC32(data, offset, length))
|
||||
{
|
||||
case 0x6170A4A1: // NUS-6101.
|
||||
case 0x009E9EA3: // NUS-7102.
|
||||
return 0x00043F3F;
|
||||
case 0x90BB6CB5: // NUS-6102.
|
||||
return 0x00003F3F;
|
||||
case 0x0B050EE0: // NUS-6103.
|
||||
return 0x0000783F;
|
||||
case 0x98BC2C86: // NUS-6105.
|
||||
return 0x0000913F;
|
||||
case 0xACC8580A: // NUS-6106.
|
||||
return 0x0000853F;
|
||||
case 0x0E018159: // NUS-8303.
|
||||
return 0x0000DD00;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,22 @@
|
|||
namespace DotN64
|
||||
{
|
||||
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,72 @@
|
|||
using System.Collections.Specialized;
|
||||
|
||||
namespace DotN64
|
||||
{
|
||||
public partial class PeripheralInterface
|
||||
{
|
||||
private struct DeviceState
|
||||
{
|
||||
#region Fields
|
||||
private BitVector32 bits;
|
||||
|
||||
private static readonly BitVector32.Section ipl2SeedSection = BitVector32.CreateSection(0xFF),
|
||||
ipl3SeedSection = BitVector32.CreateSection(0xFF, ipl2SeedSection),
|
||||
resetSection = BitVector32.CreateSection(0x02, ipl3SeedSection),
|
||||
versionSection = BitVector32.CreateSection(0x02, resetSection),
|
||||
romSection = BitVector32.CreateSection(0x04, versionSection);
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public byte IPL2Seed
|
||||
{
|
||||
get => (byte)bits[ipl2SeedSection];
|
||||
set => bits[ipl2SeedSection] = value;
|
||||
}
|
||||
|
||||
public byte IPL3Seed
|
||||
{
|
||||
get => (byte)bits[ipl3SeedSection];
|
||||
set => bits[ipl3SeedSection] = value;
|
||||
}
|
||||
|
||||
public ResetType Reset
|
||||
{
|
||||
get => (ResetType)bits[resetSection];
|
||||
set => bits[resetSection] = (byte)value;
|
||||
}
|
||||
|
||||
public byte Version
|
||||
{
|
||||
get => (byte)bits[versionSection];
|
||||
set => bits[versionSection] = value;
|
||||
}
|
||||
|
||||
public ROMType ROM
|
||||
{
|
||||
get => (ROMType)bits[romSection];
|
||||
set => bits[romSection] = (byte)value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
public static implicit operator DeviceState(uint data) => new DeviceState { bits = new BitVector32((int)data) };
|
||||
|
||||
public static implicit operator uint(DeviceState state) => (uint)state.bits.Data;
|
||||
#endregion
|
||||
|
||||
#region Enumerations
|
||||
public enum ResetType : byte
|
||||
{
|
||||
ColdReset = 0,
|
||||
NMI = 1
|
||||
}
|
||||
|
||||
public enum ROMType : byte
|
||||
{
|
||||
Cartridge = 0,
|
||||
DiskDrive = 1
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,134 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DotN64
|
||||
{
|
||||
using CPU;
|
||||
using Extensions;
|
||||
using Helpers;
|
||||
|
||||
public partial class PeripheralInterface
|
||||
{
|
||||
#region Fields
|
||||
private readonly Nintendo64 nintendo64;
|
||||
|
||||
private const byte CICStatusOffset = 0x3C, DeviceStateOffset = 0x24;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public IReadOnlyList<MappingEntry> MemoryMaps { get; }
|
||||
|
||||
public byte[] BootROM { get; set; }
|
||||
|
||||
public byte[] RAM { get; } = new byte[64];
|
||||
|
||||
private DeviceState DeviceStateFlags
|
||||
{
|
||||
get => BitConverter.ToUInt32(RAM, DeviceStateOffset);
|
||||
set => BitHelper.Write(RAM, DeviceStateOffset, value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public PeripheralInterface(Nintendo64 nintendo64)
|
||||
{
|
||||
this.nintendo64 = nintendo64;
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x1FC00000, 0x1FC007BF) // PIF Boot ROM.
|
||||
{
|
||||
Read = o => BitHelper.FromBigEndian(BitConverter.ToUInt32(BootROM, (int)o))
|
||||
},
|
||||
new MappingEntry(0x1FC007C0, 0x1FC007FF) // PIF (JoyChannel) RAM.
|
||||
{
|
||||
Read = o => BitConverter.ToUInt32(RAM, (int)o),
|
||||
Write = (o, v) =>
|
||||
{
|
||||
BitHelper.Write(RAM, (int)o, v);
|
||||
OnRAMWritten((int)o);
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
/// <summary>
|
||||
/// Called when a word is written at a specified index in the RAM.
|
||||
/// Handles actions sent through memory writes.
|
||||
/// </summary>
|
||||
private void OnRAMWritten(int index)
|
||||
{
|
||||
switch (index)
|
||||
{
|
||||
case CICStatusOffset:
|
||||
if (RAM[index] == (byte)CICStatus.Waiting) // The boot ROM waits for the PIF's CIC check to be OK.
|
||||
RAM[index] = (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.
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
private void EmulateBootROM()
|
||||
{
|
||||
// Replicating the memory writes to properly initialise the subsystems.
|
||||
var writes = new uint[,]
|
||||
{
|
||||
{ 0x4040010, 0xA },
|
||||
{ 0x4600010, 0x3 },
|
||||
{ 0x440000C, 0x3FF },
|
||||
{ 0x4400024, 0x0 },
|
||||
{ 0x4400010, 0x0 },
|
||||
{ 0x4500000, 0x0 },
|
||||
{ 0x4500004, 0x0 },
|
||||
{ 0x4600014, 0x40 }, // These four are likely cartridge-specific (PI domain 1 values).
|
||||
{ 0x4600018, 0xFF803712 },
|
||||
{ 0x460001C, 0xFFFF8037 },
|
||||
{ 0x4600020, 0xFFFFF803 },
|
||||
// Omitted the CIC results.
|
||||
{ 0x1FC007FC, 0xC0 }
|
||||
};
|
||||
|
||||
for (int i = 0; i < writes.GetLength(0); i++)
|
||||
{
|
||||
nintendo64.MemoryMaps.WriteWord(writes[i, 0], writes[i, 1]);
|
||||
}
|
||||
|
||||
if (DeviceStateFlags.ROM == DeviceState.ROMType.Cartridge && nintendo64.Cartridge != null)
|
||||
{
|
||||
for (int i = 0x40; i < 0x1000; i += sizeof(uint)) // Copying the bootstrap code from the cartridge to the RSP's DMEM.
|
||||
{
|
||||
nintendo64.MemoryMaps.WriteWord((ulong)(0x04000000 + i), BitHelper.FromBigEndian(BitConverter.ToUInt32(nintendo64.Cartridge.ROM, i)));
|
||||
}
|
||||
}
|
||||
|
||||
nintendo64.CPU.GPR[(int)VR4300.GPRIndex.s3] = (ulong)DeviceStateFlags.ROM;
|
||||
// TODO: $s4 is the video mode (0 being PAL settings). This value is hard-coded into regional boot ROMs.
|
||||
nintendo64.CPU.GPR[(int)VR4300.GPRIndex.s5] = (ulong)DeviceStateFlags.Reset;
|
||||
nintendo64.CPU.GPR[(int)VR4300.GPRIndex.s6] = DeviceStateFlags.IPL3Seed;
|
||||
nintendo64.CPU.GPR[(int)VR4300.GPRIndex.s7] = DeviceStateFlags.Version;
|
||||
|
||||
// Restoring CPU state.
|
||||
nintendo64.CPU.CP0.Registers[12] = 0x34000000;
|
||||
nintendo64.CPU.CP0.Registers[16] = 0x6E463;
|
||||
nintendo64.CPU.GPR[6] = 0xFFFFFFFFA4001F0C;
|
||||
nintendo64.CPU.GPR[7] = 0xFFFFFFFFA4001F08;
|
||||
nintendo64.CPU.GPR[8] = 0xC0;
|
||||
nintendo64.CPU.GPR[10] = 0x40;
|
||||
nintendo64.CPU.GPR[11] = 0xFFFFFFFFA4000040;
|
||||
nintendo64.CPU.GPR[20] = 0x1;
|
||||
nintendo64.CPU.GPR[29] = 0xFFFFFFFFA4001FF0;
|
||||
nintendo64.CPU.GPR[31] = 0xFFFFFFFFA4001550;
|
||||
nintendo64.CPU.PC = 0xFFFFFFFFA4000040;
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
if (nintendo64.Cartridge?.ROM.Length >= 0x1000)
|
||||
DeviceStateFlags = CIC.GetSeed(nintendo64.Cartridge.ROM);
|
||||
|
||||
if (BootROM == null)
|
||||
EmulateBootROM();
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -18,7 +18,7 @@ namespace DotN64
|
|||
switch (arg)
|
||||
{
|
||||
case "--pif-rom":
|
||||
nintendo64.RCP.PI.BootROM = File.ReadAllBytes(args[++i]);
|
||||
nintendo64.PIF.BootROM = File.ReadAllBytes(args[++i]);
|
||||
break;
|
||||
case "--debug":
|
||||
case "-d":
|
||||
|
|
|
@ -1,25 +0,0 @@
|
|||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,16 +1,25 @@
|
|||
namespace DotN64.RCP
|
||||
using System;
|
||||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class PeripheralInterface
|
||||
{
|
||||
[System.Flags]
|
||||
[Flags]
|
||||
public enum StatusRegister
|
||||
{
|
||||
DMABusy = 1 << 0,
|
||||
IOBusy = 1 << 1,
|
||||
Error = 1 << 2
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum WriteStatusRegister : byte
|
||||
{
|
||||
ResetController = 1 << 0,
|
||||
ClearInterrupt = 1 << 1
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Net;
|
||||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
|
@ -10,18 +9,9 @@ namespace DotN64.RCP
|
|||
{
|
||||
public partial class PeripheralInterface : Interface
|
||||
{
|
||||
#region Fields
|
||||
private const byte CICStatusOffset = 60;
|
||||
private const byte ResetControllerStatus = 1 << 0, ClearInterruptStatus = 1 << 1;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public StatusRegister Status { get; set; }
|
||||
|
||||
public byte[] BootROM { get; set; }
|
||||
|
||||
public byte[] RAM { get; } = new byte[64];
|
||||
|
||||
public Domain[] Domains { get; } = new[]
|
||||
{
|
||||
new Domain(),
|
||||
|
@ -50,29 +40,16 @@ namespace DotN64.RCP
|
|||
{
|
||||
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) =>
|
||||
{
|
||||
BitHelper.Write(RAM, (int)o, 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) { /* TODO. */ }
|
||||
var status = (WriteStatusRegister)v;
|
||||
|
||||
if ((v & ClearInterruptStatus) != 0) { /* TODO. */ }
|
||||
if ((status & WriteStatusRegister.ResetController) != 0) { /* TODO. */ }
|
||||
|
||||
if ((status & WriteStatusRegister.ClearInterrupt) != 0) { /* TODO. */ }
|
||||
}
|
||||
},
|
||||
new MappingEntry(0x04600014, 0x04600017) // PI dom1 latency.
|
||||
|
@ -118,61 +95,11 @@ namespace DotN64.RCP
|
|||
},
|
||||
new MappingEntry(0x10000000, 0x1FBFFFFF) // Cartridge Domain 1 Address 2.
|
||||
{
|
||||
Read = o => (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(rcp.Nintendo64.Cartridge.ROM, (int)o))
|
||||
Read = o => BitHelper.FromBigEndian(BitConverter.ToUInt32(rcp.Nintendo64.Cartridge.ROM, (int)o))
|
||||
}
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public void EmulateBootROM()
|
||||
{
|
||||
// Replicating the memory writes to properly initialise the subsystems.
|
||||
var writes = new uint[,]
|
||||
{
|
||||
{ 0x4040010, 0xA },
|
||||
{ 0x4600010, 0x3 },
|
||||
{ 0x440000C, 0x3FF },
|
||||
{ 0x4400024, 0x0 },
|
||||
{ 0x4400010, 0x0 },
|
||||
{ 0x4500000, 0x0 },
|
||||
{ 0x4500004, 0x0 },
|
||||
{ 0x4600014, 0x40 }, // These four are likely cartridge-specific (PI domain 1 values).
|
||||
{ 0x4600018, 0xFF803712 },
|
||||
{ 0x460001C, 0xFFFF8037 },
|
||||
{ 0x4600020, 0xFFFFF803 },
|
||||
// Omitted the CIC results.
|
||||
{ 0x1FC007FC, 0xC0 }
|
||||
};
|
||||
|
||||
for (int i = 0; i < writes.GetLength(0); i++)
|
||||
{
|
||||
var address = (ulong)writes[i, 0];
|
||||
|
||||
rcp.Nintendo64.MemoryMaps.WriteWord(address, writes[i, 1]);
|
||||
}
|
||||
|
||||
for (int i = 0x40; i < 0x1000; i += sizeof(uint)) // Copying the bootstrap code from the cartridge to the RSP's DMEM.
|
||||
{
|
||||
var address = (ulong)(0x04000000 + i);
|
||||
|
||||
rcp.Nintendo64.MemoryMaps.WriteWord(address, (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(rcp.Nintendo64.Cartridge.ROM, i)));
|
||||
}
|
||||
|
||||
// Restoring CPU state.
|
||||
rcp.Nintendo64.CPU.CP0.Registers[12] = 0x34000000;
|
||||
rcp.Nintendo64.CPU.CP0.Registers[16] = 0x6E463;
|
||||
rcp.Nintendo64.CPU.GPR[6] = 0xFFFFFFFFA4001F0C;
|
||||
rcp.Nintendo64.CPU.GPR[7] = 0xFFFFFFFFA4001F08;
|
||||
rcp.Nintendo64.CPU.GPR[8] = 0xC0;
|
||||
rcp.Nintendo64.CPU.GPR[10] = 0x40;
|
||||
rcp.Nintendo64.CPU.GPR[11] = 0xFFFFFFFFA4000040;
|
||||
rcp.Nintendo64.CPU.GPR[20] = 0x1;
|
||||
rcp.Nintendo64.CPU.GPR[29] = 0xFFFFFFFFA4001FF0;
|
||||
rcp.Nintendo64.CPU.GPR[31] = 0xFFFFFFFFA4001550;
|
||||
rcp.Nintendo64.CPU.PC = 0xFFFFFFFFA4000040;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,15 +0,0 @@
|
|||
namespace DotN64.RCP
|
||||
{
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class RDRAMInterface
|
||||
{
|
||||
public enum RDRAMConfigIndex : byte
|
||||
{
|
||||
Zero,
|
||||
One,
|
||||
Global
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,27 +0,0 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class RDRAMInterface
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct RDRAMConfigRegister
|
||||
{
|
||||
#region Fields
|
||||
public uint DeviceType;
|
||||
public uint DeviceID;
|
||||
public uint Delay;
|
||||
public uint Mode;
|
||||
public uint RefInterval;
|
||||
public uint RefRow;
|
||||
public uint RasInterval;
|
||||
public uint MinInterval;
|
||||
public uint AddressSelect;
|
||||
public uint DeviceManufacturer;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,9 +1,5 @@
|
|||
using System;
|
||||
|
||||
namespace DotN64.RCP
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
using Helpers;
|
||||
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class RDRAMInterface : Interface
|
||||
|
@ -16,8 +12,6 @@ namespace DotN64.RCP
|
|||
public ModeRegister Mode { get; set; }
|
||||
|
||||
public RefreshRegister Refresh { get; set; }
|
||||
|
||||
public RDRAMConfigRegister[] RDRAMConfigs { get; } = new RDRAMConfigRegister[Enum.GetNames(typeof(RDRAMConfigIndex)).Length];
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
@ -47,69 +41,10 @@ namespace DotN64.RCP
|
|||
{
|
||||
Read = a => Refresh,
|
||||
Write = (a, v) => Refresh = v
|
||||
},
|
||||
new MappingEntry(0x00000000, 0x03EFFFFF) // RDRAM memory.
|
||||
{
|
||||
Read = o => BitConverter.ToUInt32(rcp.Nintendo64.RAM, (int)o),
|
||||
Write = (o, v) => BitHelper.Write(rcp.Nintendo64.RAM, (int)o, v)
|
||||
},
|
||||
new MappingEntry(0x03F00000, 0x03FFFFFF) // RDRAM registers.
|
||||
{
|
||||
Read = o =>
|
||||
{
|
||||
if (!GetRDRAMRegisterInfo((uint)o, out var register, out var index))
|
||||
return 0;
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (void* pointer = &RDRAMConfigs[index.Value])
|
||||
{
|
||||
return *((uint*)pointer + register / sizeof(uint));
|
||||
}
|
||||
}
|
||||
},
|
||||
Write = (o, v) =>
|
||||
{
|
||||
if (!GetRDRAMRegisterInfo((uint)o, out var register, out var index))
|
||||
return;
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (void* pointer = &RDRAMConfigs[index.Value])
|
||||
{
|
||||
*((uint*)pointer + register / sizeof(uint)) = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
private bool GetRDRAMRegisterInfo(uint offset, out uint register, out int? index)
|
||||
{
|
||||
register = offset & ((1 << 8) - 1);
|
||||
|
||||
switch (offset >> 8)
|
||||
{
|
||||
case 0x0000:
|
||||
index = (int)RDRAMConfigIndex.Zero;
|
||||
break;
|
||||
case 0x0004:
|
||||
index = (int)RDRAMConfigIndex.One;
|
||||
break;
|
||||
case 0x0800:
|
||||
index = (int)RDRAMConfigIndex.Global;
|
||||
break;
|
||||
default:
|
||||
index = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
namespace DotN64
|
||||
{
|
||||
public partial class RDRAM
|
||||
{
|
||||
public enum ConfigIndex : byte
|
||||
{
|
||||
Zero,
|
||||
One,
|
||||
Global
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,24 @@
|
|||
using System.Runtime.InteropServices;
|
||||
|
||||
namespace DotN64
|
||||
{
|
||||
public partial class RDRAM
|
||||
{
|
||||
[StructLayout(LayoutKind.Sequential)]
|
||||
public struct ConfigRegister
|
||||
{
|
||||
#region Fields
|
||||
public uint DeviceType;
|
||||
public uint DeviceID;
|
||||
public uint Delay;
|
||||
public uint Mode;
|
||||
public uint RefInterval;
|
||||
public uint RefRow;
|
||||
public uint RasInterval;
|
||||
public uint MinInterval;
|
||||
public uint AddressSelect;
|
||||
public uint DeviceManufacturer;
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,87 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DotN64
|
||||
{
|
||||
using Helpers;
|
||||
|
||||
public partial class RDRAM
|
||||
{
|
||||
#region Properties
|
||||
public IReadOnlyList<MappingEntry> MemoryMaps { get; }
|
||||
|
||||
public byte[] Memory { get; }
|
||||
|
||||
public ConfigRegister[] Configs { get; } = new ConfigRegister[Enum.GetNames(typeof(ConfigIndex)).Length];
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public RDRAM(byte[] memory)
|
||||
{
|
||||
Memory = memory;
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x00000000, 0x03EFFFFF) // RDRAM memory.
|
||||
{
|
||||
Read = o => BitConverter.ToUInt32(Memory, (int)o),
|
||||
Write = (o, v) => BitHelper.Write(Memory, (int)o, v)
|
||||
},
|
||||
new MappingEntry(0x03F00000, 0x03FFFFFF) // RDRAM registers.
|
||||
{
|
||||
Read = o =>
|
||||
{
|
||||
if (!GetRegisterInfo((uint)o, out var register, out var index))
|
||||
return 0;
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (void* pointer = &Configs[index.Value])
|
||||
{
|
||||
return *((uint*)pointer + register);
|
||||
}
|
||||
}
|
||||
},
|
||||
Write = (o, v) =>
|
||||
{
|
||||
if (!GetRegisterInfo((uint)o, out var register, out var index))
|
||||
return;
|
||||
|
||||
unsafe
|
||||
{
|
||||
fixed (void* pointer = &Configs[index.Value])
|
||||
{
|
||||
*((uint*)pointer + register) = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
private bool GetRegisterInfo(uint offset, out uint register, out int? index)
|
||||
{
|
||||
register = (offset & ((1 << 8) - 1)) / sizeof(uint);
|
||||
|
||||
switch (offset >> 8)
|
||||
{
|
||||
case 0x0000:
|
||||
index = (int)ConfigIndex.Zero;
|
||||
break;
|
||||
case 0x0004:
|
||||
index = (int)ConfigIndex.One;
|
||||
break;
|
||||
case 0x0800:
|
||||
index = (int)ConfigIndex.Global;
|
||||
break;
|
||||
default:
|
||||
index = null;
|
||||
return false;
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue