- Added the FPU.
- Added an exception handler for the FPU. - Added constants for cartridges and fixed header properties. - PIF HLE determines the console region for the saved register that's used by the OS. - The disassembler shows CP1 opcodes.master
parent
9910f7ab0b
commit
1800179515
|
@ -55,6 +55,8 @@ namespace DotN64.CPU
|
|||
}
|
||||
}
|
||||
|
||||
public bool IsCoprocessorUsable(byte unit) => ((byte)Status.CU & 1 << unit) != 0 || (unit == 0 && Status.KSU == StatusRegister.Mode.Kernel);
|
||||
|
||||
public void Run(Instruction instruction)
|
||||
{
|
||||
if (operations.TryGetValue((OpCode)instruction.RS, out var operation))
|
||||
|
|
|
@ -0,0 +1,138 @@
|
|||
using System;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace DotN64.CPU
|
||||
{
|
||||
public partial class VR4300
|
||||
{
|
||||
public partial class FloatingPointUnit
|
||||
{
|
||||
/// <summary>
|
||||
/// See: datasheet#7.2.4.
|
||||
/// </summary>
|
||||
public class ControlStatusRegister : Register
|
||||
{
|
||||
#region Fields
|
||||
private static readonly BitVector32.Section rmSection = BitVector32.CreateSection((1 << 2) - 1),
|
||||
flagsSection = BitVector32.CreateSection((1 << 5) - 1, rmSection),
|
||||
enablesSection = BitVector32.CreateSection((1 << 5) - 1, flagsSection),
|
||||
causeSection = BitVector32.CreateSection((1 << 6) - 1, enablesSection),
|
||||
constant1 = BitVector32.CreateSection((1 << 5) - 1, causeSection),
|
||||
cSection = BitVector32.CreateSection(1, constant1),
|
||||
fsSection = BitVector32.CreateSection(1, cSection);
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
protected override uint Data
|
||||
{
|
||||
get => fpu.cpu.FCR31;
|
||||
set => fpu.cpu.FCR31 = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bits 1 and 0 in the FCR31 register constitute the Rounding Mode (RM) bits.
|
||||
/// These bits specify the rounding mode that FPU uses for all floating-point operations.
|
||||
/// </summary>
|
||||
public RoundingMode RM
|
||||
{
|
||||
get => (RoundingMode)this[rmSection];
|
||||
set => this[rmSection] = (byte)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The Flag bits are cumulative and indicate the exceptions that were raised after reset.
|
||||
/// Flag bits are set to 1 if an IEEE754 exception is raised but the occurrence of the exception is prohibited.
|
||||
/// Otherwise, they remain unchanged.
|
||||
/// The Flag bits are never cleared as a side effect of floating-point operations; however, they can be set or cleared by writing a new value into the FCR31, using a CTC1 instruction.
|
||||
/// </summary>
|
||||
public ExceptionFlags Flags
|
||||
{
|
||||
get => (ExceptionFlags)this[flagsSection];
|
||||
set => this[flagsSection] = (byte)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// A floating-point exception is generated any time a Cause bit and the corresponding Enable bit are set.
|
||||
/// As soon as the Cause bit enabled through the Floating-point operation, an exception occurs.
|
||||
/// When both Cause and Enable bits are set by the CTC1 instruction, an exception also occurs.
|
||||
/// </summary>
|
||||
public ExceptionFlags Enables
|
||||
{
|
||||
get => (ExceptionFlags)this[enablesSection];
|
||||
set => this[enablesSection] = (byte)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Bits 17:12 in the FCR31 contain Cause bits which reflect the results of the most recently executed floating-point instruction.
|
||||
/// The Cause bits are a logical extension of the CP0 Cause register; they identify the exceptions raised by the last floating-point operation; and generate exceptions if the corresponding Enable bit is set.
|
||||
/// If more than one exception occurs on a single instruction, each appropriate bit is set.
|
||||
/// </summary>
|
||||
public ExceptionFlags Cause
|
||||
{
|
||||
get => (ExceptionFlags)this[causeSection];
|
||||
set => this[causeSection] = (byte)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// When a floating-point Compare operation takes place, the result is stored at bit 23, the Condition bit.
|
||||
/// The C bit is set to 1 if the condition is true; the bit is cleared to 0 if the condition is false.
|
||||
/// Bit 23 is affected only by compare and CTC1 instructions.
|
||||
/// </summary>
|
||||
public bool C
|
||||
{
|
||||
get => Convert.ToBoolean(this[cSection]);
|
||||
set => this[cSection] = Convert.ToInt32(value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// The FS bit enables a value that cannot be normalized (denormalized number) to be flashed.
|
||||
/// When the FS bit is set and the enable bit is not set for the underflow exception and illegal exception, the result of the denormalized number does not cause the unimplemented operation exception, but is flushed.
|
||||
/// Whether the flushed result is 0 or the minimum normalized value is determined depending on the rounding mode (refer to Table 7-2).
|
||||
/// If the result is flushed, the Flag and Cause bits are set for the underflow and illegal exceptions.
|
||||
/// </summary>
|
||||
public bool FS
|
||||
{
|
||||
get => Convert.ToBoolean(this[fsSection]);
|
||||
set => this[fsSection] = Convert.ToInt32(value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public ControlStatusRegister(FloatingPointUnit fpu)
|
||||
: base(fpu) { }
|
||||
#endregion
|
||||
|
||||
#region Enumerations
|
||||
public enum RoundingMode : byte
|
||||
{
|
||||
/// <summary>Round result to nearest representable value; round to value with least-significant bit 0 when the two nearest representable values are equally near.</summary>
|
||||
RN = 0b00,
|
||||
/// <summary>Round toward 0: round to value closest to and not greater in magnitude than the infinitely precise result.</summary>
|
||||
RZ = 0b01,
|
||||
/// <summary>Round toward + ∞: round to value closest to and not less than the infinitely precise result.</summary>
|
||||
RP = 0b10,
|
||||
/// <summary>Round toward – ∞: round to value closest to and not greater than the infinitely precise result.</summary>
|
||||
RM = 0b11
|
||||
}
|
||||
|
||||
[Flags]
|
||||
public enum ExceptionFlags : byte
|
||||
{
|
||||
/// <summary>Inexact Operation.</summary>
|
||||
I = 1 << 0,
|
||||
/// <summary>Underflow.</summary>
|
||||
U = 1 << 1,
|
||||
/// <summary>Overflow.</summary>
|
||||
O = 1 << 2,
|
||||
/// <summary>Division by Zero.</summary>
|
||||
Z = 1 << 3,
|
||||
/// <summary>Invalid Operation.</summary>
|
||||
V = 1 << 4,
|
||||
/// <summary>Unimplemented Operation.</summary>
|
||||
E = 1 << 5
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,55 @@
|
|||
using System.Collections.Specialized;
|
||||
|
||||
namespace DotN64.CPU
|
||||
{
|
||||
public partial class VR4300
|
||||
{
|
||||
public partial class FloatingPointUnit
|
||||
{
|
||||
/// <summary>
|
||||
/// See: datasheet#7.2.5.
|
||||
/// </summary>
|
||||
public class ImplementationRevisionRegister : Register
|
||||
{
|
||||
#region Fields
|
||||
private static readonly BitVector32.Section revSection = BitVector32.CreateSection((1 << 8) - 1),
|
||||
impSection = BitVector32.CreateSection((1 << 8) - 1, revSection);
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
protected override uint Data
|
||||
{
|
||||
get => fpu.cpu.FCR0;
|
||||
set => fpu.cpu.FCR0 = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Revision number in the form of y.x.
|
||||
/// </summary>
|
||||
public byte Rev
|
||||
{
|
||||
get => (byte)this[revSection];
|
||||
set => this[revSection] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Implementation number (0x0B).
|
||||
/// </summary>
|
||||
public byte Imp
|
||||
{
|
||||
get => (byte)this[impSection];
|
||||
set => this[impSection] = value;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public ImplementationRevisionRegister(FloatingPointUnit fpu)
|
||||
: base(fpu)
|
||||
{
|
||||
Imp = 0x0B;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,16 @@
|
|||
namespace DotN64.CPU
|
||||
{
|
||||
public partial class VR4300
|
||||
{
|
||||
public partial class FloatingPointUnit
|
||||
{
|
||||
public enum OpCode : byte
|
||||
{
|
||||
/// <summary>Move Control Word From FPU.</summary>
|
||||
CF = 0b00010,
|
||||
/// <summary>Move Control Word To FPU.</summary>
|
||||
CT = 0b00110
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,54 @@
|
|||
using System.Collections.Specialized;
|
||||
|
||||
namespace DotN64.CPU
|
||||
{
|
||||
public partial class VR4300
|
||||
{
|
||||
public partial class FloatingPointUnit
|
||||
{
|
||||
public abstract class Register
|
||||
{
|
||||
#region Fields
|
||||
protected readonly FloatingPointUnit fpu;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
private BitVector32 Bits => new BitVector32((int)Data);
|
||||
|
||||
protected abstract uint Data { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Indexers
|
||||
protected int this[BitVector32.Section section]
|
||||
{
|
||||
get => Bits[section];
|
||||
set
|
||||
{
|
||||
var bits = Bits;
|
||||
bits[section] = value;
|
||||
Data = (uint)bits.Data;
|
||||
}
|
||||
}
|
||||
|
||||
protected bool this[int mask]
|
||||
{
|
||||
get => Bits[mask];
|
||||
set
|
||||
{
|
||||
var bits = Bits;
|
||||
bits[mask] = value;
|
||||
Data = (uint)bits.Data;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
protected Register(FloatingPointUnit fpu)
|
||||
{
|
||||
this.fpu = fpu;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,85 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DotN64.CPU
|
||||
{
|
||||
public partial class VR4300
|
||||
{
|
||||
public partial class FloatingPointUnit : ICoprocessor
|
||||
{
|
||||
#region Fields
|
||||
private readonly VR4300 cpu;
|
||||
private readonly IReadOnlyDictionary<OpCode, Action<Instruction>> operations;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public ImplementationRevisionRegister ImplementationRevision { get; }
|
||||
|
||||
public ControlStatusRegister ControlStatus { get; }
|
||||
|
||||
/// <summary>
|
||||
/// Condition signal.
|
||||
/// </summary>
|
||||
public bool CO { get; private set; }
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public FloatingPointUnit(VR4300 cpu)
|
||||
{
|
||||
this.cpu = cpu;
|
||||
ImplementationRevision = new ImplementationRevisionRegister(this);
|
||||
ControlStatus = new ControlStatusRegister(this);
|
||||
operations = new Dictionary<OpCode, Action<Instruction>>
|
||||
{
|
||||
[OpCode.CF] = i =>
|
||||
{
|
||||
switch (i.RD)
|
||||
{
|
||||
case 0:
|
||||
cpu.GPR[i.RT] = (ulong)(int)cpu.FCR0;
|
||||
break;
|
||||
case 31:
|
||||
cpu.GPR[i.RT] = (ulong)(int)cpu.FCR31;
|
||||
break;
|
||||
default:
|
||||
ExceptionProcessing.ReservedInstruction(cpu, i);
|
||||
return;
|
||||
}
|
||||
},
|
||||
[OpCode.CT] = i =>
|
||||
{
|
||||
switch (i.RD)
|
||||
{
|
||||
case 0:
|
||||
return; // Read-only register.
|
||||
case 31:
|
||||
cpu.FCR31 = (uint)cpu.GPR[i.RT];
|
||||
CO = ControlStatus.C;
|
||||
|
||||
if ((ControlStatus.Cause & ControlStatus.Enables) != 0)
|
||||
{
|
||||
ExceptionProcessing.FloatingPoint(cpu);
|
||||
return;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
ExceptionProcessing.ReservedInstruction(cpu, i);
|
||||
return;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public void Run(Instruction instruction)
|
||||
{
|
||||
if (operations.TryGetValue((OpCode)instruction.RS, out var operation))
|
||||
operation(instruction);
|
||||
else
|
||||
ExceptionProcessing.ReservedInstruction(cpu, instruction);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
|
@ -71,6 +71,8 @@
|
|||
}
|
||||
|
||||
public static void CoprocessorUnusable(VR4300 cpu, byte unit) => HandleGeneral(cpu, SystemControlUnit.CauseRegister.ExceptionCode.CpU, unit);
|
||||
|
||||
public static void FloatingPoint(VR4300 cpu) => HandleGeneral(cpu, SystemControlUnit.CauseRegister.ExceptionCode.FPE);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -56,12 +56,12 @@ namespace DotN64.CPU
|
|||
/// <summary>
|
||||
/// 32-bit floating-point Implementation/Revision register.
|
||||
/// </summary>
|
||||
public float FCR0 { get; set; }
|
||||
public uint FCR0 { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// 32-bit floating-point Control/Status register.
|
||||
/// </summary>
|
||||
public float FCR31 { get; set; }
|
||||
public uint FCR31 { get; set; }
|
||||
|
||||
private byte divMode;
|
||||
/// <summary>
|
||||
|
@ -121,6 +121,8 @@ namespace DotN64.CPU
|
|||
|
||||
public SystemControlUnit CP0 => COP[0] as SystemControlUnit;
|
||||
|
||||
public FloatingPointUnit CP1 => COP[1] as FloatingPointUnit;
|
||||
|
||||
public ulong? DelaySlot { get; private set; }
|
||||
#endregion
|
||||
|
||||
|
@ -128,6 +130,7 @@ namespace DotN64.CPU
|
|||
public VR4300()
|
||||
{
|
||||
COP[0] = new SystemControlUnit(this);
|
||||
COP[1] = new FloatingPointUnit(this);
|
||||
operations = new Dictionary<Instruction, Action<Instruction>>
|
||||
{
|
||||
[Instruction.FromOpCode(OpCode.LUI)] = i => GPR[i.RT] = (ulong)(i.Immediate << 16),
|
||||
|
@ -206,7 +209,7 @@ namespace DotN64.CPU
|
|||
{
|
||||
var unit = instruction.COPz.Value;
|
||||
|
||||
if (((byte)CP0.Status.CU & 1 << unit) != 0 || (unit == 0 && CP0.Status.KSU == SystemControlUnit.StatusRegister.Mode.Kernel))
|
||||
if (CP0.IsCoprocessorUsable(unit))
|
||||
COP[unit].Run(instruction);
|
||||
else
|
||||
ExceptionProcessing.CoprocessorUnusable(this, unit);
|
||||
|
|
|
@ -0,0 +1,14 @@
|
|||
namespace DotN64
|
||||
{
|
||||
public partial class Cartridge
|
||||
{
|
||||
public enum CountryCode : byte
|
||||
{
|
||||
Germany = 0x44,
|
||||
USA = 0x45,
|
||||
Japan = 0x4A,
|
||||
Europe = 0x50,
|
||||
Australia = 0x55
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,12 @@
|
|||
namespace DotN64
|
||||
{
|
||||
public partial class Cartridge
|
||||
{
|
||||
public enum MediaFormat
|
||||
{
|
||||
Cartridge = 'N',
|
||||
Disk = 'D',
|
||||
ExpandableCartridge = 'E'
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,17 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Net;
|
||||
using System.Text;
|
||||
|
||||
namespace DotN64
|
||||
{
|
||||
using Helpers;
|
||||
|
||||
public class Cartridge
|
||||
public partial class Cartridge
|
||||
{
|
||||
#region Fields
|
||||
public const ushort HeaderSize = 0x40, BootstrapSize = 0x1000 - HeaderSize;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public byte[] ROM { get; set; }
|
||||
|
||||
|
@ -26,11 +29,13 @@ namespace DotN64
|
|||
|
||||
public string ImageName => Encoding.ASCII.GetString(ROM, 0x20, 0x34 - 0x20);
|
||||
|
||||
public byte ManufacturerID => ROM[0x3B];
|
||||
public MediaFormat Format => (MediaFormat)BitHelper.FromBigEndian(BitConverter.ToUInt32(ROM, 0x38));
|
||||
|
||||
public ushort ID => (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(ROM, 0x3C));
|
||||
public ushort ID => BitHelper.FromBigEndian(BitConverter.ToUInt16(ROM, 0x3C));
|
||||
|
||||
public byte CountryCode => ROM[0x3E];
|
||||
public CountryCode Country => (CountryCode)ROM[0x3E];
|
||||
|
||||
public byte Version => ROM[0x3F];
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
|
|
|
@ -33,6 +33,9 @@
|
|||
case VR4300.OpCode.COP0:
|
||||
opCode = ((VR4300.SystemControlUnit.OpCode)instruction.RS).ToString(); // Not accounting for RT, Func...
|
||||
break;
|
||||
case VR4300.OpCode.COP1:
|
||||
opCode = ((VR4300.FloatingPointUnit.OpCode)instruction.RS).ToString();
|
||||
break;
|
||||
}
|
||||
|
||||
return $"{instruction}.{opCode}";
|
||||
|
@ -103,8 +106,9 @@
|
|||
case VR4300.SystemControlUnit.OpCode.MT:
|
||||
case VR4300.SystemControlUnit.OpCode.MF:
|
||||
return Format(instruction, FormatRegister(instruction.RT, cpu), FormatCP0Register(instruction.RD, cpu));
|
||||
default:
|
||||
return FormatOpCode(instruction);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
return Format(instruction, FormatRegister(instruction.RD, cpu), FormatRegister(instruction.RS, cpu), FormatRegister(instruction.RT, cpu));
|
||||
|
@ -114,6 +118,8 @@
|
|||
/// Jump type.
|
||||
/// </summary>
|
||||
public static string J(VR4300.Instruction instruction, VR4300 cpu) => Format(instruction, $"0x{instruction.Target:X8}");
|
||||
|
||||
public static string Unknown(VR4300.Instruction instruction) => FormatOpCode(instruction);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -178,7 +178,7 @@ namespace DotN64.Diagnostics
|
|||
#endregion
|
||||
|
||||
#region Methods
|
||||
private string Disassemble(VR4300.Instruction instruction, bool withRegisterContents) => operationFormats.TryGetValue(instruction.ToOpCode(), out var format) && format != null ? format(instruction, withRegisterContents ? nintendo64.CPU : null) : instruction.ToString();
|
||||
private string Disassemble(VR4300.Instruction instruction, bool withRegisterContents) => operationFormats.TryGetValue(instruction.ToOpCode(), out var format) && format != null ? format(instruction, withRegisterContents ? nintendo64.CPU : null) : InstructionFormat.Unknown(instruction);
|
||||
|
||||
private void Disassemble(ulong? address = null, bool withRegisterContents = true)
|
||||
{
|
||||
|
|
|
@ -84,6 +84,14 @@
|
|||
<Compile Include="CPU\VR4300\VR4300.ExceptionProcessing.cs" />
|
||||
<Compile Include="CPU\VR4300\VR4300.ICoprocessor.cs" />
|
||||
<Compile Include="CPU\VR4300\CP0\VR4300.SystemControlUnit.OpCode.cs" />
|
||||
<Compile Include="CPU\VR4300\CP1\VR4300.FloatingPointUnit.cs" />
|
||||
<Compile Include="PIF\PeripheralInterface.TVType.cs" />
|
||||
<Compile Include="Cartridge.MediaFormat.cs" />
|
||||
<Compile Include="Cartridge.CountryCode.cs" />
|
||||
<Compile Include="CPU\VR4300\CP1\VR4300.FloatingPointUnit.OpCode.cs" />
|
||||
<Compile Include="CPU\VR4300\CP1\VR4300.FloatingPointUnit.Register.cs" />
|
||||
<Compile Include="CPU\VR4300\CP1\VR4300.FloatingPointUnit.ImplementationRevisionRegister.cs" />
|
||||
<Compile Include="CPU\VR4300\CP1\VR4300.FloatingPointUnit.ControlStatusRegister.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="CPU\" />
|
||||
|
@ -103,6 +111,7 @@
|
|||
<Folder Include="Helpers\" />
|
||||
<Folder Include="PIF\" />
|
||||
<Folder Include="RDRAM\" />
|
||||
<Folder Include="CPU\VR4300\CP1\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -35,9 +35,15 @@ namespace DotN64.Helpers
|
|||
}
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ushort FromBigEndian(ushort value) => (ushort)IPAddress.NetworkToHostOrder((short)value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint FromBigEndian(uint value) => (uint)IPAddress.NetworkToHostOrder((int)value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ushort ToBigEndian(ushort value) => (ushort)IPAddress.HostToNetworkOrder((short)value);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ToBigEndian(uint value) => (uint)IPAddress.HostToNetworkOrder((int)value);
|
||||
#endregion
|
||||
|
|
|
@ -35,7 +35,7 @@
|
|||
return c ^ 0xFFFFFFFF;
|
||||
}
|
||||
|
||||
public static uint GetSeed(byte[] data, int offset = 0x40, int length = 0x1000 - 0x40)
|
||||
public static uint GetSeed(byte[] data, int offset = Cartridge.HeaderSize, int length = Cartridge.BootstrapSize)
|
||||
{
|
||||
switch (ComputeCRC32(data, offset, length))
|
||||
{
|
||||
|
|
|
@ -0,0 +1,12 @@
|
|||
namespace DotN64
|
||||
{
|
||||
public partial class PeripheralInterface
|
||||
{
|
||||
private enum TVType : byte
|
||||
{
|
||||
PAL = 0,
|
||||
NTSC = 1,
|
||||
MPAL = 2
|
||||
}
|
||||
}
|
||||
}
|
|
@ -95,14 +95,14 @@ namespace DotN64
|
|||
|
||||
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.
|
||||
for (int i = Cartridge.HeaderSize; i < Cartridge.HeaderSize + Cartridge.BootstrapSize; i += sizeof(uint)) // Copying the bootstrap code from the cartridge to the RSP's DMEM.
|
||||
{
|
||||
BitHelper.Write(nintendo64.RCP.SP.DMEM, 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.S4] = (ulong)GetRegion();
|
||||
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;
|
||||
|
@ -115,15 +115,33 @@ namespace DotN64
|
|||
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;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Determines the console region from the loaded cartridge's header.
|
||||
/// </summary>
|
||||
private TVType GetRegion()
|
||||
{
|
||||
switch (nintendo64.Cartridge?.Country)
|
||||
{
|
||||
case Cartridge.CountryCode.Europe:
|
||||
case Cartridge.CountryCode.Germany:
|
||||
case Cartridge.CountryCode.Australia:
|
||||
return TVType.PAL;
|
||||
case Cartridge.CountryCode.Japan:
|
||||
case Cartridge.CountryCode.USA:
|
||||
return TVType.NTSC;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
public void Reset()
|
||||
{
|
||||
if (nintendo64.Cartridge?.ROM.Length >= 0x1000)
|
||||
if (nintendo64.Cartridge?.ROM.Length >= Cartridge.HeaderSize + Cartridge.BootstrapSize)
|
||||
DeviceStateFlags = CIC.GetSeed(nintendo64.Cartridge.ROM);
|
||||
|
||||
if (BootROM == null)
|
||||
|
|
Loading…
Reference in New Issue