99 lines
3.6 KiB
C#
99 lines
3.6 KiB
C#
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace DotN64.CPU
|
|
{
|
|
public partial class VR4300
|
|
{
|
|
public partial class SystemControlUnit : ICoprocessor
|
|
{
|
|
#region Fields
|
|
private readonly VR4300 cpu;
|
|
private readonly IReadOnlyDictionary<OpCode, Action<Instruction>> operations;
|
|
private readonly IReadOnlyDictionary<FunctOpCode, Action<Instruction>> functOperations;
|
|
#endregion
|
|
|
|
#region Properties
|
|
public ulong[] Registers { get; } = new ulong[32];
|
|
|
|
public ConfigRegister Config { get; }
|
|
|
|
public StatusRegister Status { get; }
|
|
|
|
public CauseRegister Cause { get; }
|
|
#endregion
|
|
|
|
#region Constructors
|
|
public SystemControlUnit(VR4300 cpu)
|
|
{
|
|
this.cpu = cpu;
|
|
Config = new ConfigRegister(this);
|
|
Status = new StatusRegister(this);
|
|
Cause = new CauseRegister(this);
|
|
operations = new Dictionary<OpCode, Action<Instruction>>
|
|
{
|
|
[OpCode.MT] = i => Registers[i.RD] = cpu.GPR[i.RT],
|
|
[OpCode.MF] = i => cpu.GPR[i.RT] = (ulong)(int)Registers[i.RD],
|
|
[OpCode.CO] = i =>
|
|
{
|
|
if (functOperations.TryGetValue((FunctOpCode)i.Funct, out var operation))
|
|
operation(i);
|
|
else //if (i.Funct == 0b010000) // TODO: Uncomment once the operations are implemented.
|
|
ExceptionProcessing.ReservedInstruction(cpu, i);
|
|
}
|
|
};
|
|
functOperations = new Dictionary<FunctOpCode, Action<Instruction>>
|
|
{
|
|
[FunctOpCode.TLBWI] = i => { /* TODO. */ },
|
|
[FunctOpCode.ERET] = i =>
|
|
{
|
|
if (Status.ERL)
|
|
{
|
|
cpu.PC = Registers[(int)RegisterIndex.ErrorEPC];
|
|
Status.ERL = false;
|
|
}
|
|
else
|
|
{
|
|
cpu.PC = Registers[(int)RegisterIndex.EPC];
|
|
Status.EXL = false;
|
|
}
|
|
|
|
cpu.LLBit = false;
|
|
cpu.DelaySlot = null;
|
|
}
|
|
};
|
|
}
|
|
#endregion
|
|
|
|
#region Methods
|
|
/// <summary>
|
|
/// Translates a virtual address into a physical address.
|
|
/// See: datasheet#5.2.4 Table 5-3.
|
|
/// </summary>
|
|
public uint Translate(ulong address)
|
|
{
|
|
switch (address >> 29 & 0b111)
|
|
{
|
|
case 0b100: // kseg0.
|
|
return (uint)(address - 0xFFFFFFFF80000000);
|
|
case 0b101: // kseg1.
|
|
return (uint)(address - 0xFFFFFFFFA0000000);
|
|
default:
|
|
throw new Exception($"Unknown memory map segment for location 0x{address:X16}.");
|
|
}
|
|
}
|
|
|
|
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))
|
|
operation(instruction);
|
|
else
|
|
ExceptionProcessing.ReservedInstruction(cpu, instruction);
|
|
}
|
|
#endregion
|
|
}
|
|
}
|
|
}
|