Exception processing, interrupts, registers.
parent
08853d495f
commit
158f01ccf9
|
@ -0,0 +1,119 @@
|
|||
using System;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace DotN64.CPU
|
||||
{
|
||||
public partial class VR4300
|
||||
{
|
||||
public partial class SystemControlUnit
|
||||
{
|
||||
/// <summary>
|
||||
/// See: datasheet#6.3.6.
|
||||
/// </summary>
|
||||
public class CauseRegister : Register
|
||||
{
|
||||
#region Fields
|
||||
private static readonly BitVector32.Section constant0 = BitVector32.CreateSection((1 << 2) - 1),
|
||||
excCode = BitVector32.CreateSection((1 << 5) - 1, constant0),
|
||||
constant1 = BitVector32.CreateSection(1, excCode),
|
||||
ip = BitVector32.CreateSection((1 << 8) - 1, constant1),
|
||||
constant2 = BitVector32.CreateSection((1 << 12) - 1, ip),
|
||||
ce = BitVector32.CreateSection((1 << 2) - 1, constant2),
|
||||
constant3 = BitVector32.CreateSection(1, ce),
|
||||
bd = BitVector32.CreateSection(1, constant3);
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
protected override RegisterIndex Index => RegisterIndex.Cause;
|
||||
|
||||
/// <summary>
|
||||
/// Exception code field (refer to Table 6-2 for details.)
|
||||
/// </summary>
|
||||
public ExceptionCode ExcCode
|
||||
{
|
||||
get => (ExceptionCode)this[excCode];
|
||||
set => this[excCode] = (byte)value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates an interrupt is pending.
|
||||
/// 1 → interrupt pending
|
||||
/// 0 → no interrupt
|
||||
/// IP(7) : Timer interrupt
|
||||
/// IP(6:2) : External normal interrupts. Controlled by Int[4:0], or external write requests
|
||||
/// IP(1:0) : Software interrupts. Only these bits can cause interrupt exception when they are set to 1 by software.
|
||||
/// </summary>
|
||||
public byte IP
|
||||
{
|
||||
get => (byte)this[ip];
|
||||
set => this[ip] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Coprocessor unit number referenced when a Coprocessor Unusable exception has occurred.
|
||||
/// If this exception does not occur, undefined.
|
||||
/// </summary>
|
||||
public byte CE
|
||||
{
|
||||
get => (byte)this[ce];
|
||||
set => this[ce] = value;
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Indicates whether the last exception occurred has been executed in a branch delay slot.
|
||||
/// 1 → delay slot
|
||||
/// 0 → normal
|
||||
/// </summary>
|
||||
public bool BD
|
||||
{
|
||||
get => Convert.ToBoolean(this[bd]);
|
||||
set => this[bd] = Convert.ToInt32(value);
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public CauseRegister(SystemControlUnit cp0)
|
||||
: base(cp0) { }
|
||||
#endregion
|
||||
|
||||
#region Enumerations
|
||||
public enum ExceptionCode : byte
|
||||
{
|
||||
/// <summary>Interrupt.</summary>
|
||||
Int = 0,
|
||||
/// <summary>TLB Modification exception.</summary>
|
||||
Mod = 1,
|
||||
/// <summary>TLB Miss exception (load or instruction fetch).</summary>
|
||||
TLBL = 2,
|
||||
/// <summary>TLB Miss exception (store).</summary>
|
||||
TLBS = 3,
|
||||
/// <summary>Address Error exception (load or instruction fetch).</summary>
|
||||
AdEL = 4,
|
||||
/// <summary>Address Error exception (store).</summary>
|
||||
AdES = 5,
|
||||
/// <summary>Bus Error exception (instruction fetch).</summary>
|
||||
IBE = 6,
|
||||
/// <summary>Bus Error exception (data reference: load or store).</summary>
|
||||
DBE = 7,
|
||||
/// <summary>Syscall exception.</summary>
|
||||
Sys = 8,
|
||||
/// <summary>Breakpoint exception.</summary>
|
||||
Bp = 9,
|
||||
/// <summary>Reserved Instruction exception.</summary>
|
||||
RI = 10,
|
||||
/// <summary>Coprocessor Unusable exception.</summary>
|
||||
CpU = 11,
|
||||
/// <summary>Arithmetic Overflow exception.</summary>
|
||||
Ov = 12,
|
||||
/// <summary>Trap exception.</summary>
|
||||
Tr = 13,
|
||||
/// <summary>Floating-Point exception.</summary>
|
||||
FPE = 15,
|
||||
/// <summary>Watch exception.</summary>
|
||||
WATCH = 23
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -12,6 +12,8 @@ namespace DotN64.CPU
|
|||
public ConfigRegister Config { get; }
|
||||
|
||||
public StatusRegister Status { get; }
|
||||
|
||||
public CauseRegister Cause { get; }
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
@ -19,6 +21,7 @@ namespace DotN64.CPU
|
|||
{
|
||||
Config = new ConfigRegister(this);
|
||||
Status = new StatusRegister(this);
|
||||
Cause = new CauseRegister(this);
|
||||
}
|
||||
#endregion
|
||||
|
||||
|
|
|
@ -0,0 +1,68 @@
|
|||
namespace DotN64.CPU
|
||||
{
|
||||
public partial class VR4300
|
||||
{
|
||||
/// <summary>
|
||||
/// See: datasheet#6.4.
|
||||
/// </summary>
|
||||
private static class ExceptionProcessing
|
||||
{
|
||||
#region Fields
|
||||
private const ulong ResetVector = 0xFFFFFFFFBFC00000,
|
||||
GeneralVector = 0xFFFFFFFF80000000,
|
||||
GeneralVectorBEV = 0xFFFFFFFFBFC00200;
|
||||
private const ushort GeneralVectorOffset = 0x0180;
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
private static void HandleReset(VR4300 cpu)
|
||||
{
|
||||
cpu.CP0.Registers[(int)SystemControlUnit.RegisterIndex.ErrorEPC] = cpu.PC;
|
||||
cpu.PC = ResetVector;
|
||||
}
|
||||
|
||||
private static void HandleGeneral(VR4300 cpu, SystemControlUnit.CauseRegister.ExceptionCode excCode, byte? ce = null)
|
||||
{
|
||||
cpu.CP0.Cause.ExcCode = excCode;
|
||||
|
||||
if (ce.HasValue)
|
||||
cpu.CP0.Cause.CE = ce.Value;
|
||||
|
||||
cpu.CP0.Registers[(int)SystemControlUnit.RegisterIndex.BadVAddr] = cpu.PC;
|
||||
|
||||
if (!cpu.CP0.Status.EXL)
|
||||
{
|
||||
if (cpu.CP0.Cause.BD = cpu.DelaySlot.HasValue) // FIXME: Step() nullifies the delay slot before executing it.
|
||||
cpu.CP0.Registers[(int)SystemControlUnit.RegisterIndex.EPC] = cpu.PC - Instruction.Size;
|
||||
else
|
||||
cpu.CP0.Registers[(int)SystemControlUnit.RegisterIndex.EPC] = cpu.PC;
|
||||
}
|
||||
|
||||
cpu.CP0.Status.EXL = true;
|
||||
cpu.PC = (cpu.CP0.Status.DS.BEV ? GeneralVectorBEV : GeneralVector) + GeneralVectorOffset;
|
||||
}
|
||||
|
||||
public static void ColdReset(VR4300 cpu)
|
||||
{
|
||||
var ds = cpu.CP0.Status.DS;
|
||||
|
||||
ds.TS = ds.SR = cpu.CP0.Status.RP = false;
|
||||
cpu.CP0.Config.EP = 0;
|
||||
|
||||
cpu.CP0.Status.ERL = ds.BEV = true;
|
||||
cpu.CP0.Config.BE = (SystemControlUnit.ConfigRegister.Endianness)1;
|
||||
|
||||
cpu.CP0.Registers[(int)SystemControlUnit.RegisterIndex.Random] = 31;
|
||||
|
||||
cpu.CP0.Config.EC = cpu.DivMode;
|
||||
|
||||
cpu.CP0.Status.DS = ds;
|
||||
|
||||
HandleReset(cpu);
|
||||
}
|
||||
|
||||
public static void Interrupt(VR4300 cpu) => HandleGeneral(cpu, SystemControlUnit.CauseRegister.ExceptionCode.Int);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
|
@ -4,6 +4,8 @@ using System.Runtime.CompilerServices;
|
|||
|
||||
namespace DotN64.CPU
|
||||
{
|
||||
using Helpers;
|
||||
|
||||
/// <summary>
|
||||
/// The NEC VR4300 CPU, designed by MIPS Technologies.
|
||||
/// Datasheet: http://datasheets.chipdb.org/NEC/Vr-Series/Vr43xx/U10504EJ7V0UMJ1.pdf
|
||||
|
@ -12,8 +14,14 @@ namespace DotN64.CPU
|
|||
{
|
||||
#region Fields
|
||||
private readonly IReadOnlyDictionary<uint, Action<Instruction>> operations;
|
||||
private readonly IReadOnlyDictionary<byte, float> divModeMultipliers = new Dictionary<byte, float>
|
||||
{
|
||||
[0b01] = 1.5f,
|
||||
[0b10] = 2.0f,
|
||||
[0b11] = 3.0f
|
||||
};
|
||||
|
||||
private const ulong ResetVector = 0xFFFFFFFFBFC00000;
|
||||
private const byte IntShift = 2, IntSize = (1 << 5) - 1;
|
||||
|
||||
private delegate bool BranchCondition(ulong rs, ulong rt);
|
||||
#endregion
|
||||
|
@ -70,6 +78,39 @@ namespace DotN64.CPU
|
|||
set => divMode = (byte)(value & ((1 << 2) - 1));
|
||||
}
|
||||
|
||||
public double MasterClock { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Pipeline clock.
|
||||
/// </summary>
|
||||
private double PClock => MasterClock * divModeMultipliers[DivMode] * (CP0.Status.RP ? 0.25 : 1.0);
|
||||
|
||||
/// <summary>
|
||||
/// System interface clock.
|
||||
/// </summary>
|
||||
private double SClock => PClock / divModeMultipliers[DivMode];
|
||||
|
||||
/// <summary>
|
||||
/// Transmit/receive clock.
|
||||
/// </summary>
|
||||
public double TClock => SClock;
|
||||
|
||||
private double TicksPerCycle => 1.0 / PClock * TimeSpan.TicksPerSecond;
|
||||
|
||||
/// <summary>
|
||||
/// Interrupt request acknowledge.
|
||||
/// </summary>
|
||||
public byte Int
|
||||
{
|
||||
get => (byte)BitHelper.Get(CP0.Cause.IP, IntShift, IntSize);
|
||||
set
|
||||
{
|
||||
var ip = (uint)CP0.Cause.IP;
|
||||
BitHelper.Set(ref ip, IntShift, IntSize, value);
|
||||
CP0.Cause.IP = (byte)ip;
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// System address/data bus.
|
||||
/// </summary>
|
||||
|
@ -155,25 +196,9 @@ namespace DotN64.CPU
|
|||
/// Cold reset.
|
||||
/// See: datasheet#6.4.4.
|
||||
/// </summary>
|
||||
public void Reset()
|
||||
{
|
||||
PC = ResetVector;
|
||||
|
||||
var ds = CP0.Status.DS;
|
||||
|
||||
ds.TS = ds.SR = CP0.Status.RP = false;
|
||||
CP0.Config.EP = 0;
|
||||
|
||||
CP0.Status.ERL = ds.BEV = true;
|
||||
CP0.Config.BE = (SystemControlUnit.ConfigRegister.Endianness)1;
|
||||
|
||||
CP0.Registers[(int)SystemControlUnit.RegisterIndex.Random] = 31;
|
||||
|
||||
CP0.Config.EC = DivMode;
|
||||
|
||||
CP0.Status.DS = ds;
|
||||
}
|
||||
public void Reset() => ExceptionProcessing.ColdReset(this);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Run(Instruction instruction)
|
||||
{
|
||||
if (operations.TryGetValue(instruction.ToOpCode(), out var operation))
|
||||
|
@ -182,7 +207,8 @@ namespace DotN64.CPU
|
|||
throw new UnimplementedOperationException(instruction);
|
||||
}
|
||||
|
||||
public void Step()
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void Step()
|
||||
{
|
||||
Instruction instruction;
|
||||
|
||||
|
@ -200,6 +226,19 @@ namespace DotN64.CPU
|
|||
Run(instruction);
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public void Cycle()
|
||||
{
|
||||
if ((uint)++CP0.Registers[(int)SystemControlUnit.RegisterIndex.Count] == (uint)CP0.Registers[(int)SystemControlUnit.RegisterIndex.Compare])
|
||||
CP0.Cause.IP |= 1 << 7; // Set the Timer interrupt.
|
||||
|
||||
if (CP0.Status.IE && !CP0.Status.EXL && !CP0.Status.ERL && (CP0.Status.IM & CP0.Cause.IP) != 0)
|
||||
ExceptionProcessing.Interrupt(this);
|
||||
|
||||
Step();
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private bool Branch(Instruction instruction, BranchCondition condition, bool storeLink = false)
|
||||
{
|
||||
var result = condition(GPR[instruction.RS], GPR[instruction.RT]);
|
||||
|
@ -216,6 +255,7 @@ namespace DotN64.CPU
|
|||
return result;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void BranchLikely(Instruction instruction, BranchCondition condition)
|
||||
{
|
||||
if (!Branch(instruction, condition))
|
||||
|
|
|
@ -75,7 +75,7 @@ namespace DotN64.Diagnostics
|
|||
for (var i = BigInteger.Zero; i < count; i++)
|
||||
{
|
||||
Disassemble();
|
||||
nintendo64.CPU.Step();
|
||||
nintendo64.CPU.Cycle();
|
||||
}
|
||||
}),
|
||||
new Command(new[] { "goto", "g" }, "Sets the CPU's PC to the specified address.", args => nintendo64.CPU.PC = ulong.Parse(args.First(), NumberStyles.HexNumber)),
|
||||
|
@ -275,7 +275,7 @@ namespace DotN64.Diagnostics
|
|||
|
||||
try
|
||||
{
|
||||
nintendo64.CPU.Step();
|
||||
nintendo64.CPU.Cycle();
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
|
|
|
@ -44,7 +44,7 @@
|
|||
<Compile Include="RCP\MI\RealityCoprocessor.MIPSInterface.InitModeRegister.cs" />
|
||||
<Compile Include="RCP\PI\RealityCoprocessor.ParallelInterface.cs" />
|
||||
<Compile Include="RCP\PI\RealityCoprocessor.ParallelInterface.Domain.cs" />
|
||||
<Compile Include="RCP\PI\RealityCoprocessor.ParallelInterface.StatusRegister.cs" />
|
||||
<Compile Include="RCP\PI\RealityCoprocessor.ParallelInterface.Statuses.cs" />
|
||||
<Compile Include="RCP\RI\RealityCoprocessor.RDRAMInterface.cs" />
|
||||
<Compile Include="RCP\RI\RealityCoprocessor.RDRAMInterface.ConfigRegister.cs" />
|
||||
<Compile Include="RCP\RI\RealityCoprocessor.RDRAMInterface.ModeRegister.cs" />
|
||||
|
@ -70,7 +70,7 @@
|
|||
<Compile Include="Diagnostics\Debugger.InstructionFormat.cs" />
|
||||
<Compile Include="Helpers\BitHelper.cs" />
|
||||
<Compile Include="RCP\DP\RealityCoprocessor.DisplayProcessor.StatusRegister.cs" />
|
||||
<Compile Include="RCP\SP\RealityCoprocessor.SignalProcessor.StatusRegister.cs" />
|
||||
<Compile Include="RCP\SP\RealityCoprocessor.SignalProcessor.Statuses.cs" />
|
||||
<Compile Include="CPU\VR4300\VR4300.Exceptions.cs" />
|
||||
<Compile Include="PIF\PeripheralInterface.cs" />
|
||||
<Compile Include="PIF\PeripheralInterface.CICStatus.cs" />
|
||||
|
@ -79,6 +79,9 @@
|
|||
<Compile Include="RDRAM\RDRAM.ConfigIndex.cs" />
|
||||
<Compile Include="RDRAM\RDRAM.ConfigRegister.cs" />
|
||||
<Compile Include="PIF\PeripheralInterface.CIC.cs" />
|
||||
<Compile Include="RCP\MI\RealityCoprocessor.MIPSInterface.Interrupts.cs" />
|
||||
<Compile Include="CPU\VR4300\CP0\VR4300.SystemControlUnit.CauseRegister.cs" />
|
||||
<Compile Include="CPU\VR4300\VR4300.ExceptionProcessing.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="CPU\" />
|
||||
|
|
|
@ -1,4 +1,6 @@
|
|||
namespace DotN64
|
||||
using System;
|
||||
|
||||
namespace DotN64
|
||||
{
|
||||
using CPU;
|
||||
using Extensions;
|
||||
|
@ -25,7 +27,8 @@
|
|||
RCP = new RealityCoprocessor(this);
|
||||
CPU = new VR4300
|
||||
{
|
||||
DivMode = 0b01, // Assuming this value as the CPU is clocked at 93.75 MHz, and the RCP would be clocked at 93.75 / 3 * 2 = 62.5 MHz.
|
||||
DivMode = 0b01,
|
||||
MasterClock = 62.5 * Math.Pow(10, 6),
|
||||
ReadSysAD = RCP.MemoryMaps.ReadWord,
|
||||
WriteSysAD = RCP.MemoryMaps.WriteWord
|
||||
};
|
||||
|
@ -43,7 +46,7 @@
|
|||
{
|
||||
while (true)
|
||||
{
|
||||
CPU.Step();
|
||||
CPU.Cycle();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
|
|
@ -36,6 +36,10 @@
|
|||
new MappingEntry(0x04500004, 0x04500007) // AI length.
|
||||
{
|
||||
Write = (o, v) => TransferLength = v
|
||||
},
|
||||
new MappingEntry(0x0450000C, 0x0450000F) // AI status.
|
||||
{
|
||||
Write = (o, v) => rcp.MI.Interrupt &= ~MIPSInterface.Interrupts.AI
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,6 +6,10 @@ namespace DotN64.RCP
|
|||
{
|
||||
public partial class DisplayProcessor
|
||||
{
|
||||
#region Fields
|
||||
private readonly RealityCoprocessor rcp;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public IReadOnlyList<MappingEntry> MemoryMaps { get; }
|
||||
|
||||
|
@ -13,8 +17,9 @@ namespace DotN64.RCP
|
|||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public DisplayProcessor()
|
||||
public DisplayProcessor(RealityCoprocessor rcp)
|
||||
{
|
||||
this.rcp = rcp;
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x0410000C, 0x0410000F) // DP CMD status.
|
||||
|
|
|
@ -0,0 +1,38 @@
|
|||
using System;
|
||||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class MIPSInterface
|
||||
{
|
||||
[Flags]
|
||||
public enum Interrupts : byte
|
||||
{
|
||||
SP = 1 << 0,
|
||||
SI = 1 << 1,
|
||||
AI = 1 << 2,
|
||||
VI = 1 << 3,
|
||||
PI = 1 << 4,
|
||||
DP = 1 << 5
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum InterruptMaskWrites : ushort
|
||||
{
|
||||
ClearSP = 1 << 0,
|
||||
SetSP = 1 << 1,
|
||||
ClearSI = 1 << 2,
|
||||
SetSI = 1 << 3,
|
||||
ClearAI = 1 << 4,
|
||||
SetAI = 1 << 5,
|
||||
ClearVI = 1 << 6,
|
||||
SetVI = 1 << 7,
|
||||
ClearPI = 1 << 8,
|
||||
SetPI = 1 << 9,
|
||||
ClearDP = 1 << 10,
|
||||
SetDP = 1 << 11
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -1,14 +1,35 @@
|
|||
using System;
|
||||
using System.Collections.Specialized;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
using CPU;
|
||||
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class MIPSInterface : Interface
|
||||
{
|
||||
#region Fields
|
||||
private static readonly byte interruptPin = 1 << 0;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
private VR4300 CPU => rcp.nintendo64.CPU;
|
||||
|
||||
public InitModeRegister InitMode { get; set; }
|
||||
|
||||
public Interrupts InterruptMask { get; set; }
|
||||
|
||||
private Interrupts interrupt;
|
||||
public Interrupts Interrupt
|
||||
{
|
||||
get => interrupt;
|
||||
set
|
||||
{
|
||||
interrupt = value;
|
||||
|
||||
UpdateInterrupt();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
@ -33,7 +54,7 @@ namespace DotN64.RCP
|
|||
mode.EBusTestMode |= bits[InitModeRegister.SetEBusTestModeSection] != 0;
|
||||
|
||||
if (bits[InitModeRegister.ClearDPInterruptSection] != 0)
|
||||
throw new NotImplementedException("Clear DP interrupt.");
|
||||
Interrupt &= ~Interrupts.DP;
|
||||
|
||||
mode.RDRAMRegMode &= bits[InitModeRegister.ClearRDRAMRegSection] == 0;
|
||||
mode.RDRAMRegMode |= bits[InitModeRegister.SetRDRAMRegModeSection] != 0;
|
||||
|
@ -44,10 +65,65 @@ namespace DotN64.RCP
|
|||
new MappingEntry(0x04300004, 0x04300007) // MI version.
|
||||
{
|
||||
Read = o => 0 // TODO.
|
||||
},
|
||||
new MappingEntry(0x0430000C, 0x0430000F) // MI interrupt mask.
|
||||
{
|
||||
Write = (o, v) =>
|
||||
{
|
||||
var mask = (InterruptMaskWrites)v;
|
||||
|
||||
if ((mask & InterruptMaskWrites.ClearSP) != 0)
|
||||
InterruptMask &= ~Interrupts.SP;
|
||||
|
||||
if ((mask & InterruptMaskWrites.SetSP) != 0)
|
||||
InterruptMask |= Interrupts.SP;
|
||||
|
||||
if ((mask & InterruptMaskWrites.ClearSI) != 0)
|
||||
InterruptMask &= ~Interrupts.SI;
|
||||
|
||||
if ((mask & InterruptMaskWrites.SetSI) != 0)
|
||||
InterruptMask |= Interrupts.SI;
|
||||
|
||||
if ((mask & InterruptMaskWrites.ClearAI) != 0)
|
||||
InterruptMask &= ~Interrupts.AI;
|
||||
|
||||
if ((mask & InterruptMaskWrites.SetAI) != 0)
|
||||
InterruptMask |= Interrupts.AI;
|
||||
|
||||
if ((mask & InterruptMaskWrites.ClearVI) != 0)
|
||||
InterruptMask &= ~Interrupts.VI;
|
||||
|
||||
if ((mask & InterruptMaskWrites.SetVI) != 0)
|
||||
InterruptMask |= Interrupts.VI;
|
||||
|
||||
if ((mask & InterruptMaskWrites.ClearPI) != 0)
|
||||
InterruptMask &= ~Interrupts.PI;
|
||||
|
||||
if ((mask & InterruptMaskWrites.SetPI) != 0)
|
||||
InterruptMask |= Interrupts.PI;
|
||||
|
||||
if ((mask & InterruptMaskWrites.ClearDP) != 0)
|
||||
InterruptMask &= ~Interrupts.DP;
|
||||
|
||||
if ((mask & InterruptMaskWrites.SetDP) != 0)
|
||||
InterruptMask |= Interrupts.DP;
|
||||
|
||||
UpdateInterrupt();
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
private void UpdateInterrupt()
|
||||
{
|
||||
if ((Interrupt & InterruptMask) != 0)
|
||||
CPU.Int |= interruptPin;
|
||||
else
|
||||
CPU.Int &= (byte)~interruptPin;
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace DotN64.RCP
|
|||
public partial class ParallelInterface
|
||||
{
|
||||
[Flags]
|
||||
public enum StatusRegister
|
||||
public enum Statuses
|
||||
{
|
||||
DMABusy = 1 << 0,
|
||||
IOBusy = 1 << 1,
|
||||
|
@ -15,7 +15,7 @@ namespace DotN64.RCP
|
|||
}
|
||||
|
||||
[Flags]
|
||||
private enum WriteStatusRegister : byte
|
||||
private enum StatusWrites : byte
|
||||
{
|
||||
ResetController = 1 << 0,
|
||||
ClearInterrupt = 1 << 1
|
|
@ -10,7 +10,7 @@ namespace DotN64.RCP
|
|||
public partial class ParallelInterface : Interface
|
||||
{
|
||||
#region Properties
|
||||
public StatusRegister Status { get; set; }
|
||||
public Statuses Status { get; set; }
|
||||
|
||||
public Domain[] Domains { get; } = new[]
|
||||
{
|
||||
|
@ -45,11 +45,12 @@ namespace DotN64.RCP
|
|||
Read = o => (uint)Status,
|
||||
Write = (o, v) =>
|
||||
{
|
||||
var status = (WriteStatusRegister)v;
|
||||
var status = (StatusWrites)v;
|
||||
|
||||
if ((status & WriteStatusRegister.ResetController) != 0) { /* TODO. */ }
|
||||
if ((status & StatusWrites.ResetController) != 0) { /* TODO. */ }
|
||||
|
||||
if ((status & WriteStatusRegister.ClearInterrupt) != 0) { /* TODO. */ }
|
||||
if ((status & StatusWrites.ClearInterrupt) != 0)
|
||||
rcp.MI.Interrupt &= ~MIPSInterface.Interrupts.PI;
|
||||
}
|
||||
},
|
||||
new MappingEntry(0x04600014, 0x04600017) // PI dom1 latency.
|
||||
|
@ -81,7 +82,7 @@ namespace DotN64.RCP
|
|||
Write = (o, v) =>
|
||||
{
|
||||
WriteLength = v & ((1 << 24) - 1);
|
||||
Status |= StatusRegister.DMABusy;
|
||||
Status |= Statuses.DMABusy;
|
||||
var maps = rcp.MemoryMaps;
|
||||
|
||||
for (uint i = 0; i < WriteLength + 1; i += sizeof(uint))
|
||||
|
@ -89,13 +90,13 @@ namespace DotN64.RCP
|
|||
maps.WriteWord(DRAMAddress + i, maps.ReadWord(PBusAddress + i));
|
||||
}
|
||||
|
||||
Status &= ~StatusRegister.DMABusy;
|
||||
// TODO: Set interrupt.
|
||||
Status &= ~Statuses.DMABusy;
|
||||
rcp.MI.Interrupt |= MIPSInterface.Interrupts.PI;
|
||||
}
|
||||
},
|
||||
new MappingEntry(0x10000000, 0x1FBFFFFF) // Cartridge Domain 1 Address 2.
|
||||
{
|
||||
Read = o => BitHelper.FromBigEndian(BitConverter.ToUInt32(rcp.Nintendo64.Cartridge.ROM, (int)o))
|
||||
Read = o => BitHelper.FromBigEndian(BitConverter.ToUInt32(rcp.nintendo64.Cartridge.ROM, (int)o))
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -6,14 +6,16 @@ namespace DotN64.RCP
|
|||
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
#region Fields
|
||||
private readonly Nintendo64 nintendo64;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public IReadOnlyList<MappingEntry> MemoryMaps { get; }
|
||||
|
||||
public Nintendo64 Nintendo64 { get; }
|
||||
public SignalProcessor SP { get; }
|
||||
|
||||
public SignalProcessor SP { get; } = new SignalProcessor();
|
||||
|
||||
public DisplayProcessor DP { get; } = new DisplayProcessor();
|
||||
public DisplayProcessor DP { get; }
|
||||
|
||||
public ParallelInterface PI { get; }
|
||||
|
||||
|
@ -31,7 +33,9 @@ namespace DotN64.RCP
|
|||
#region Constructors
|
||||
public RealityCoprocessor(Nintendo64 nintendo64)
|
||||
{
|
||||
Nintendo64 = nintendo64;
|
||||
this.nintendo64 = nintendo64;
|
||||
SP = new SignalProcessor(this);
|
||||
DP = new DisplayProcessor(this);
|
||||
PI = new ParallelInterface(this);
|
||||
SI = new SerialInterface(this);
|
||||
AI = new AudioInterface(this);
|
||||
|
@ -42,13 +46,13 @@ namespace DotN64.RCP
|
|||
{
|
||||
new MappingEntry(0x1FC00000, 0x1FC007BF, false) // PIF Boot ROM.
|
||||
{
|
||||
Read = Nintendo64.PIF.MemoryMaps.ReadWord,
|
||||
Write = Nintendo64.PIF.MemoryMaps.WriteWord
|
||||
Read = nintendo64.PIF.MemoryMaps.ReadWord,
|
||||
Write = nintendo64.PIF.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x1FC007C0, 0x1FC007FF, false) // PIF (JoyChannel) RAM.
|
||||
{
|
||||
Read = Nintendo64.PIF.MemoryMaps.ReadWord,
|
||||
Write = Nintendo64.PIF.MemoryMaps.WriteWord
|
||||
Read = nintendo64.PIF.MemoryMaps.ReadWord,
|
||||
Write = nintendo64.PIF.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x04600000, 0x046FFFFF, false) // Peripheral interface (PI) registers.
|
||||
{
|
||||
|
@ -96,13 +100,13 @@ namespace DotN64.RCP
|
|||
},
|
||||
new MappingEntry(0x00000000, 0x03EFFFFF, false) // RDRAM memory.
|
||||
{
|
||||
Read = Nintendo64.RAM.MemoryMaps.ReadWord,
|
||||
Write = Nintendo64.RAM.MemoryMaps.WriteWord
|
||||
Read = nintendo64.RAM.MemoryMaps.ReadWord,
|
||||
Write = nintendo64.RAM.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x03F00000, 0x03FFFFFF, false) // RDRAM registers.
|
||||
{
|
||||
Read = Nintendo64.RAM.MemoryMaps.ReadWord,
|
||||
Write = Nintendo64.RAM.MemoryMaps.WriteWord
|
||||
Read = nintendo64.RAM.MemoryMaps.ReadWord,
|
||||
Write = nintendo64.RAM.MemoryMaps.WriteWord
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace DotN64.RCP
|
|||
public partial class SignalProcessor
|
||||
{
|
||||
[Flags]
|
||||
public enum StatusRegister : ushort
|
||||
public enum Statuses : ushort
|
||||
{
|
||||
Halt = 1 << 0,
|
||||
Broke = 1 << 1,
|
||||
|
@ -27,7 +27,7 @@ namespace DotN64.RCP
|
|||
}
|
||||
|
||||
[Flags]
|
||||
private enum WriteStatusRegister
|
||||
private enum StatusWrites
|
||||
{
|
||||
ClearHalt = 1 << 0,
|
||||
SetHalt = 1 << 1,
|
|
@ -9,10 +9,14 @@ namespace DotN64.RCP
|
|||
{
|
||||
public partial class SignalProcessor
|
||||
{
|
||||
#region Fields
|
||||
private readonly RealityCoprocessor rcp;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public IReadOnlyList<MappingEntry> MemoryMaps { get; }
|
||||
|
||||
public StatusRegister Status { get; set; } = StatusRegister.Halt;
|
||||
public Statuses Status { get; set; } = Statuses.Halt;
|
||||
|
||||
public bool DMABusy { get; set; }
|
||||
|
||||
|
@ -25,11 +29,17 @@ namespace DotN64.RCP
|
|||
/// Data memory.
|
||||
/// </summary>
|
||||
public byte[] DMEM { get; } = new byte[0x1000];
|
||||
|
||||
/// <summary>
|
||||
/// Program counter.
|
||||
/// </summary>
|
||||
public ushort PC { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public SignalProcessor()
|
||||
public SignalProcessor(RealityCoprocessor rcp)
|
||||
{
|
||||
this.rcp = rcp;
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x04001000, 0x04001FFF) // SP_IMEM read/write.
|
||||
|
@ -42,80 +52,82 @@ namespace DotN64.RCP
|
|||
Read = o => (uint)Status,
|
||||
Write = (o, v) =>
|
||||
{
|
||||
var status = (WriteStatusRegister)v;
|
||||
var status = (StatusWrites)v;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearHalt))
|
||||
Status &= ~StatusRegister.Halt;
|
||||
if ((status & StatusWrites.ClearHalt) != 0)
|
||||
Status &= ~Statuses.Halt;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetHalt))
|
||||
Status |= StatusRegister.Halt;
|
||||
if ((status & StatusWrites.SetHalt) != 0)
|
||||
Status |= Statuses.Halt;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearBroke))
|
||||
Status &= ~StatusRegister.Broke;
|
||||
if ((status & StatusWrites.ClearBroke) != 0)
|
||||
Status &= ~Statuses.Broke;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearInterrupt)) { /* TODO. */ }
|
||||
if ((status & StatusWrites.ClearInterrupt) != 0)
|
||||
rcp.MI.Interrupt &= ~MIPSInterface.Interrupts.SP;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetInterrupt)) { /* TODO. */ }
|
||||
if ((status & StatusWrites.SetInterrupt) != 0)
|
||||
rcp.MI.Interrupt |= MIPSInterface.Interrupts.SP;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSingleStep))
|
||||
Status &= ~StatusRegister.SingleStep;
|
||||
if ((status & StatusWrites.ClearSingleStep) != 0)
|
||||
Status &= ~Statuses.SingleStep;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSingleStep))
|
||||
Status |= StatusRegister.SingleStep;
|
||||
if ((status & StatusWrites.SetSingleStep) != 0)
|
||||
Status |= Statuses.SingleStep;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearInterruptOnBreak))
|
||||
Status &= ~StatusRegister.InterruptOnBreak;
|
||||
if ((status & StatusWrites.ClearInterruptOnBreak) != 0)
|
||||
Status &= ~Statuses.InterruptOnBreak;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetInterruptOnBreak))
|
||||
Status |= StatusRegister.InterruptOnBreak;
|
||||
if ((status & StatusWrites.SetInterruptOnBreak) != 0)
|
||||
Status |= Statuses.InterruptOnBreak;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal0))
|
||||
Status &= ~StatusRegister.Signal0;
|
||||
if ((status & StatusWrites.ClearSignal0) != 0)
|
||||
Status &= ~Statuses.Signal0;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal0))
|
||||
Status |= StatusRegister.Signal0;
|
||||
if ((status & StatusWrites.SetSignal0) != 0)
|
||||
Status |= Statuses.Signal0;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal1))
|
||||
Status &= ~StatusRegister.Signal1;
|
||||
if ((status & StatusWrites.ClearSignal1) != 0)
|
||||
Status &= ~Statuses.Signal1;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal1))
|
||||
Status |= StatusRegister.Signal1;
|
||||
if ((status & StatusWrites.SetSignal1) != 0)
|
||||
Status |= Statuses.Signal1;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal2))
|
||||
Status &= ~StatusRegister.Signal2;
|
||||
if ((status & StatusWrites.ClearSignal2) != 0)
|
||||
Status &= ~Statuses.Signal2;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal2))
|
||||
Status |= StatusRegister.Signal2;
|
||||
if ((status & StatusWrites.SetSignal2) != 0)
|
||||
Status |= Statuses.Signal2;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal3))
|
||||
Status &= ~StatusRegister.Signal3;
|
||||
if ((status & StatusWrites.ClearSignal3) != 0)
|
||||
Status &= ~Statuses.Signal3;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal3))
|
||||
Status |= StatusRegister.Signal3;
|
||||
if ((status & StatusWrites.SetSignal3) != 0)
|
||||
Status |= Statuses.Signal3;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal4))
|
||||
Status &= ~StatusRegister.Signal4;
|
||||
if ((status & StatusWrites.ClearSignal4) != 0)
|
||||
Status &= ~Statuses.Signal4;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal4))
|
||||
Status |= StatusRegister.Signal4;
|
||||
if ((status & StatusWrites.SetSignal4) != 0)
|
||||
Status |= Statuses.Signal4;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal5))
|
||||
Status &= ~StatusRegister.Signal5;
|
||||
if ((status & StatusWrites.ClearSignal5) != 0)
|
||||
Status &= ~Statuses.Signal5;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal5))
|
||||
Status |= StatusRegister.Signal5;
|
||||
if ((status & StatusWrites.SetSignal5) != 0)
|
||||
Status |= Statuses.Signal5;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal6))
|
||||
Status &= ~StatusRegister.Signal6;
|
||||
if ((status & StatusWrites.ClearSignal6) != 0)
|
||||
Status &= ~Statuses.Signal6;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal6))
|
||||
Status |= StatusRegister.Signal6;
|
||||
if ((status & StatusWrites.SetSignal6) != 0)
|
||||
Status |= Statuses.Signal6;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal7))
|
||||
Status &= ~StatusRegister.Signal7;
|
||||
if ((status & StatusWrites.ClearSignal7) != 0)
|
||||
Status &= ~Statuses.Signal7;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal7))
|
||||
Status |= StatusRegister.Signal7;
|
||||
if ((status & StatusWrites.SetSignal7) != 0)
|
||||
Status |= Statuses.Signal7;
|
||||
}
|
||||
},
|
||||
new MappingEntry(0x04040018, 0x0404001B) // SP DMA busy.
|
||||
|
@ -126,6 +138,10 @@ namespace DotN64.RCP
|
|||
{
|
||||
Read = o => BitConverter.ToUInt32(DMEM, (int)o),
|
||||
Write = (o, v) => BitHelper.Write(DMEM, (int)o, v)
|
||||
},
|
||||
new MappingEntry(0x04080000, 0x04080003) // SP PC.
|
||||
{
|
||||
Read = o => PC
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,38 +1,21 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace DotN64.RCP
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class VideoInterface : Interface
|
||||
{
|
||||
#region Properties
|
||||
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 ushort VerticalInterrupt { get; set; }
|
||||
|
||||
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.
|
||||
}
|
||||
}
|
||||
public ushort CurrentVerticalLine { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
@ -43,7 +26,7 @@ namespace DotN64.RCP
|
|||
{
|
||||
new MappingEntry(0x0440000C, 0x0440000F) // VI vertical intr.
|
||||
{
|
||||
Write = (o, v) => VerticalInterrupt = (ushort)v
|
||||
Write = (o, v) => VerticalInterrupt = (ushort)(v & ((1 << 10) - 1))
|
||||
},
|
||||
new MappingEntry(0x04400024, 0x04400027) // VI horizontal video.
|
||||
{
|
||||
|
@ -51,7 +34,11 @@ namespace DotN64.RCP
|
|||
},
|
||||
new MappingEntry(0x04400010, 0x04400013) // VI current vertical line.
|
||||
{
|
||||
Write = (o, v) => CurrentVerticalLine = (ushort)v
|
||||
Write = (o, v) =>
|
||||
{
|
||||
CurrentVerticalLine = (ushort)(v & ((1 << 10) - 1));
|
||||
rcp.MI.Interrupt &= ~MIPSInterface.Interrupts.VI;
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue