CPU: refactored control flow and load/store methods, added new ops.

master
Nabile Rahmani 2018-06-09 17:40:52 +02:00
parent dca7725258
commit eb41337dce
8 changed files with 172 additions and 51 deletions

View File

@ -0,0 +1,13 @@
namespace DotN64.CPU
{
public partial class VR4300
{
private enum AccessSize : byte
{
Byte,
HalfWord,
Word,
DoubleWord
}
}
}

View File

@ -55,7 +55,11 @@
/// <summary>Store Halfword.</summary>
SH = 0b101001,
/// <summary>Load Halfword Unsigned.</summary>
LHU = 0b100101
LHU = 0b100101,
/// <summary>Jump.</summary>
J = 0b000010,
/// <summary>Load Byte.</summary>
LB = 0b100000
}
}
}

View File

@ -7,7 +7,11 @@
/// <summary>Branch On Greater Than Or Equal To Zero And Link.</summary>
BGEZAL = 0b10001,
/// <summary>Branch On Greater Than Or Equal To Zero Likely.</summary>
BGEZL = 0b00011
BGEZL = 0b00011,
/// <summary>Branch On Less Than Zero.</summary>
BLTZ = 0b00000,
/// <summary>Branch On Greater Than Or Equal To Zero.</summary>
BGEZ = 0b00001
}
}
}

View File

@ -49,7 +49,9 @@
/// <summary>Move To LO.</summary>
MTLO = 0b010011,
/// <summary>Move To HI.</summary>
MTHI = 0b010001
MTHI = 0b010001,
/// <summary>Jump And Link Register.</summary>
JALR = 0b001001
}
}
}

View File

@ -135,11 +135,11 @@ namespace DotN64.CPU
{
[Instruction.FromOpCode(OpCode.LUI)] = i => GPR[i.RT] = (ulong)(i.Immediate << 16),
[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.LW)] = i => Load(i, AccessSize.Word),
[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]),
[Instruction.FromOpCode(OpCode.SW)] = i => Store(i, AccessSize.Word),
[Instruction.FromOpCode(OpCode.BNEL)] = i => BranchLikely(i, (rs, rt) => rs != rt),
[Instruction.FromOpCode(OpCode.BNE)] = i => Branch(i, (rs, rt) => rs != rt),
[Instruction.FromOpCode(OpCode.BEQ)] = i => Branch(i, (rs, rt) => rs == rt),
@ -147,41 +147,24 @@ namespace DotN64.CPU
[Instruction.FromOpCode(OpCode.CACHE)] = i => { /* TODO: Implement and compare the performance if it's a concern. */ },
[Instruction.FromOpCode(OpCode.JAL)] = i =>
{
GPR[31] = PC + Instruction.Size;
DelaySlot = PC;
PC = (PC & ~((ulong)(1 << 28) - 1)) | (i.Target << 2);
StoreLink();
Jump((PC & ~((ulong)(1 << 28) - 1)) | (i.Target << 2));
},
[Instruction.FromOpCode(OpCode.SLTI)] = i => GPR[i.RT] = (long)GPR[i.RS] < (long)(short)i.Immediate ? (ulong)1 : 0,
[Instruction.FromOpCode(OpCode.XORI)] = i => GPR[i.RT] = GPR[i.RS] ^ i.Immediate,
[Instruction.FromOpCode(OpCode.BLEZL)] = i => BranchLikely(i, (rs, rt) => rs <= 0),
[Instruction.FromOpCode(OpCode.SB)] = i =>
{
var address = (ulong)(short)i.Immediate + GPR[i.RS];
WriteWord(address, (ReadWord(address) & ~((uint)(1 << 8) - 1)) | (byte)GPR[i.RT]);
},
[Instruction.FromOpCode(OpCode.LBU)] = i => GPR[i.RT] = (byte)ReadWord((ulong)(short)i.Immediate + GPR[i.RS]),
[Instruction.FromOpCode(OpCode.SB)] = i => Store(i, AccessSize.Byte),
[Instruction.FromOpCode(OpCode.LBU)] = i => LoadUnsigned(i, AccessSize.Byte),
[Instruction.FromOpCode(OpCode.COP3)] = i => ExceptionProcessing.ReservedInstruction(this, i), // CP3 access throws a reserved instruction for this CPU.
[Instruction.FromOpCode(OpCode.BLEZ)] = i => Branch(i, (rs, rt) => rs <= 0),
[Instruction.FromOpCode(OpCode.LD)] = i =>
{
var address = (ulong)(short)i.Immediate + GPR[i.RS];
GPR[i.RT] = ReadWord(address) << 32 | ReadWord(address + sizeof(uint));
},
[Instruction.FromOpCode(OpCode.LD)] = i => Load(i, AccessSize.DoubleWord),
[Instruction.FromOpCode(OpCode.SLTIU)] = i => GPR[i.RT] = GPR[i.RS] < (ulong)(short)i.Immediate ? (ulong)1 : 0,
[Instruction.FromOpCode(OpCode.SH)] = i =>
{
var address = (ulong)(short)i.Immediate + GPR[i.RS];
WriteWord(address, (ReadWord(address) & ~((uint)(1 << 16) - 1)) | (ushort)GPR[i.RT]);
},
[Instruction.FromOpCode(OpCode.LHU)] = i => GPR[i.RT] = (ushort)ReadWord((ulong)(short)i.Immediate + GPR[i.RS]),
[Instruction.FromOpCode(OpCode.SH)] = i => Store(i, AccessSize.HalfWord),
[Instruction.FromOpCode(OpCode.LHU)] = i => LoadUnsigned(i, AccessSize.HalfWord),
[Instruction.FromOpCode(OpCode.J)] = i => Jump((PC & ~((ulong)(1 << 28) - 1)) | (i.Target << 2)),
[Instruction.FromOpCode(OpCode.LB)] = i => Load(i, AccessSize.Byte),
[Instruction.FromOpCode(SpecialOpCode.ADD)] = i => GPR[i.RD] = (ulong)((int)GPR[i.RS] + (int)GPR[i.RT]),
[Instruction.FromOpCode(SpecialOpCode.JR)] = i =>
{
DelaySlot = PC;
PC = GPR[i.RS];
},
[Instruction.FromOpCode(SpecialOpCode.JR)] = i => Jump(GPR[i.RS]),
[Instruction.FromOpCode(SpecialOpCode.SRL)] = i => GPR[i.RD] = (ulong)((int)GPR[i.RT] >> i.SA),
[Instruction.FromOpCode(SpecialOpCode.OR)] = i => GPR[i.RD] = GPR[i.RS] | GPR[i.RT],
[Instruction.FromOpCode(SpecialOpCode.MULTU)] = i =>
@ -224,8 +207,19 @@ namespace DotN64.CPU
[Instruction.FromOpCode(SpecialOpCode.SRA)] = i => GPR[i.RD] = (ulong)((int)GPR[i.RT] >> i.SA),
[Instruction.FromOpCode(SpecialOpCode.MTLO)] = i => LO = GPR[i.RS],
[Instruction.FromOpCode(SpecialOpCode.MTHI)] = i => HI = GPR[i.RS],
[Instruction.FromOpCode(RegImmOpCode.BGEZAL)] = i => Branch(i, (rs, rt) => rs >= 0, true),
[Instruction.FromOpCode(RegImmOpCode.BGEZL)] = i => BranchLikely(i, (rs, rt) => rs >= 0)
[Instruction.FromOpCode(SpecialOpCode.JALR)] = i =>
{
StoreLink((GPRIndex)i.RD);
Jump(GPR[i.RS]);
},
[Instruction.FromOpCode(RegImmOpCode.BGEZAL)] = i =>
{
StoreLink();
Branch(i, (rs, rt) => rs >= 0);
},
[Instruction.FromOpCode(RegImmOpCode.BGEZL)] = i => BranchLikely(i, (rs, rt) => rs >= 0),
[Instruction.FromOpCode(RegImmOpCode.BLTZ)] = i => Branch(i, (rs, rt) => rs < 0),
[Instruction.FromOpCode(RegImmOpCode.BGEZ)] = i => Branch(i, (rs, rt) => rs >= 0)
};
}
#endregion
@ -289,18 +283,12 @@ namespace DotN64.CPU
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private bool Branch(Instruction instruction, BranchCondition condition, bool storeLink = false)
private bool Branch(Instruction instruction, BranchCondition condition)
{
var result = condition(GPR[instruction.RS], GPR[instruction.RT]);
if (storeLink)
GPR[31] = PC + Instruction.Size;
if (result)
{
DelaySlot = PC;
PC += (ulong)(short)instruction.Immediate << 2;
}
Jump(PC + ((ulong)(short)instruction.Immediate << 2));
return result;
}
@ -313,10 +301,92 @@ namespace DotN64.CPU
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private uint ReadWord(ulong address) => ReadSysAD(CP0.Translate(address));
private void Jump(ulong address)
{
DelaySlot = PC;
PC = address;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void WriteWord(ulong address, uint value) => WriteSysAD(CP0.Translate(address), value);
private void StoreLink(GPRIndex index = GPRIndex.RA) => GPR[(int)index] = PC + Instruction.Size;
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private uint ReadWord(ulong address) => (uint)Read(address, AccessSize.Word);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private ulong Read(ulong address, AccessSize size)
{
var physicalAddress = CP0.Translate(address);
switch (size)
{
case AccessSize.Byte:
return (byte)ReadSysAD(physicalAddress);
case AccessSize.HalfWord:
return (ushort)ReadSysAD(physicalAddress);
case AccessSize.Word:
return ReadSysAD(physicalAddress);
case AccessSize.DoubleWord:
return ReadSysAD(physicalAddress) << 32 | ReadSysAD(physicalAddress + sizeof(uint));
default:
throw new ArgumentException("Invalid system bus access size.", nameof(size));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Write(ulong address, ulong value, AccessSize size)
{
var physicalAddress = CP0.Translate(address);
switch (size)
{
case AccessSize.Byte:
WriteSysAD(physicalAddress, (ReadWord(address) & ~((uint)(1 << 8) - 1)) | (byte)value);
break;
case AccessSize.HalfWord:
WriteSysAD(physicalAddress, (ReadWord(address) & ~((uint)(1 << 16) - 1)) | (ushort)value);
break;
case AccessSize.Word:
WriteSysAD(physicalAddress, (uint)value);
break;
case AccessSize.DoubleWord:
WriteSysAD(physicalAddress, (uint)(value >> 32));
WriteSysAD(physicalAddress + sizeof(uint), (uint)value);
break;
default:
throw new ArgumentException("Invalid system bus access size.", nameof(size));
}
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Load(Instruction instruction, AccessSize size, bool signExtend = true)
{
var value = Read((ulong)(short)instruction.Immediate + GPR[instruction.RS], size);
if (signExtend)
{
switch (size)
{
case AccessSize.Byte:
value = (ulong)(sbyte)value;
break;
case AccessSize.HalfWord:
value = (ulong)(short)value;
break;
case AccessSize.Word:
value = (ulong)(int)value;
break;
}
}
GPR[instruction.RT] = value;
}
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void LoadUnsigned(Instruction instruction, AccessSize size) => Load(instruction, size, false);
[MethodImpl(MethodImplOptions.AggressiveInlining)]
private void Store(Instruction instruction, AccessSize size) => Write((ulong)(short)instruction.Immediate + GPR[instruction.RS], GPR[instruction.RT], size);
#endregion
}
}

View File

@ -67,6 +67,8 @@
{
case VR4300.RegImmOpCode.BGEZAL:
case VR4300.RegImmOpCode.BGEZL:
case VR4300.RegImmOpCode.BLTZ:
case VR4300.RegImmOpCode.BGEZ:
return Format(instruction, FormatRegister(instruction.RS, cpu), (short)instruction.Immediate);
}
@ -92,6 +94,8 @@
{
switch (instruction.Special)
{
case VR4300.SpecialOpCode.JALR:
return Format(instruction, FormatRegister(instruction.RD, cpu), FormatRegister(instruction.RS, cpu));
case VR4300.SpecialOpCode.JR:
case VR4300.SpecialOpCode.MTLO:
case VR4300.SpecialOpCode.MTHI:
@ -133,7 +137,7 @@
/// <summary>
/// Jump type.
/// </summary>
public static string J(VR4300.Instruction instruction, VR4300 cpu) => Format(instruction, $"0x{instruction.Target:X8}");
public static string J(VR4300.Instruction instruction, VR4300 cpu) => Format(instruction, $"0x{((cpu != null ? (cpu.DelaySlot ?? cpu.PC) : 0) & ~(ulong)((1 << 28) - 1)) | (instruction.Target << 2):X8}");
public static string Unknown(VR4300.Instruction instruction) => FormatOpCode(instruction);
#endregion

View File

@ -29,6 +29,7 @@ namespace DotN64.Diagnostics
[VR4300.Instruction.FromOpCode(VR4300.OpCode.CACHE)] = null,
[VR4300.Instruction.FromOpCode(VR4300.OpCode.COP0)] = InstructionFormat.R, // FIXME: all CP0 ops are treated as such at the moment.
[VR4300.Instruction.FromOpCode(VR4300.OpCode.JAL)] = InstructionFormat.J,
[VR4300.Instruction.FromOpCode(VR4300.OpCode.J)] = InstructionFormat.J,
[VR4300.Instruction.FromOpCode(VR4300.OpCode.LBU)] = InstructionFormat.I,
[VR4300.Instruction.FromOpCode(VR4300.OpCode.LUI)] = InstructionFormat.I,
[VR4300.Instruction.FromOpCode(VR4300.OpCode.LD)] = InstructionFormat.I,
@ -41,6 +42,7 @@ namespace DotN64.Diagnostics
[VR4300.Instruction.FromOpCode(VR4300.OpCode.SLTIU)] = InstructionFormat.I,
[VR4300.Instruction.FromOpCode(VR4300.OpCode.SW)] = InstructionFormat.I,
[VR4300.Instruction.FromOpCode(VR4300.OpCode.XORI)] = InstructionFormat.I,
[VR4300.Instruction.FromOpCode(VR4300.OpCode.LB)] = InstructionFormat.I,
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.ADD)] = InstructionFormat.R,
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.ADDU)] = InstructionFormat.R,
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.AND)] = InstructionFormat.R,
@ -49,6 +51,7 @@ namespace DotN64.Diagnostics
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.DSLL32)] = InstructionFormat.R,
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.DSRA32)] = InstructionFormat.R,
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.JR)] = InstructionFormat.R,
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.JALR)] = InstructionFormat.R,
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.MFHI)] = InstructionFormat.R,
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.MFLO)] = InstructionFormat.R,
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.MTHI)] = InstructionFormat.R,
@ -65,7 +68,9 @@ namespace DotN64.Diagnostics
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.SUBU)] = InstructionFormat.R,
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.XOR)] = InstructionFormat.R,
[VR4300.Instruction.FromOpCode(VR4300.RegImmOpCode.BGEZAL)] = InstructionFormat.I,
[VR4300.Instruction.FromOpCode(VR4300.RegImmOpCode.BGEZL)] = InstructionFormat.I
[VR4300.Instruction.FromOpCode(VR4300.RegImmOpCode.BGEZL)] = InstructionFormat.I,
[VR4300.Instruction.FromOpCode(VR4300.RegImmOpCode.BLTZ)] = InstructionFormat.I,
[VR4300.Instruction.FromOpCode(VR4300.RegImmOpCode.BGEZ)] = InstructionFormat.I
};
#endregion
@ -92,8 +97,24 @@ namespace DotN64.Diagnostics
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)),
new Command(new[] { "disassemble", "d" }, "Disassembles instructions from the current PC.", args =>
new Command(new[] { "goto", "g" }, "Sets the CPU's PC to the specified address or register value.", args =>
{
var target = args.First();
const char RegisterPrefix = '$';
if (target[0] == RegisterPrefix)
{
target = target.Substring(1);
if (Enum.TryParse<VR4300.GPRIndex>(target, true, out var gprIndex))
nintendo64.CPU.PC = nintendo64.CPU.GPR[(int)gprIndex];
else if (Enum.TryParse<VR4300.SystemControlUnit.RegisterIndex>(target, true, out var cp0Index))
nintendo64.CPU.PC = nintendo64.CPU.CP0.Registers[(int)cp0Index];
}
else
nintendo64.CPU.PC = ulong.Parse(target, NumberStyles.HexNumber);
}),
new Command(new[] { "disassemble", "disasm", "d" }, "Disassembles instructions from the current PC.", args =>
{
var count = args.Length > 0 ? BigInteger.Parse(args.First()) : 1;
@ -127,14 +148,14 @@ namespace DotN64.Diagnostics
{
foreach (var pair in labels)
{
Console.WriteLine($".{pair.Value}: {pair.Key:X16}");
Console.WriteLine($"{pair.Value}: {pair.Key:X16}");
}
break;
}
var address = ulong.Parse(args[index++], NumberStyles.HexNumber);
Console.WriteLine($".{labels[address]}: {address:X16}");
Console.WriteLine($"{labels[address]}: {address:X16}");
}
break;
}
@ -203,7 +224,7 @@ namespace DotN64.Diagnostics
{
Console.ForegroundColor = ConsoleColor.Green;
Console.WriteLine($".{label}:");
Console.WriteLine($"{label}:");
Console.ResetColor();
}

View File

@ -8,6 +8,8 @@
<RootNamespace>DotN64</RootNamespace>
<AssemblyName>DotN64</AssemblyName>
<TargetFrameworkVersion>v4.6.1</TargetFrameworkVersion>
<ReleaseVersion>0.0.0.*</ReleaseVersion>
<SynchReleaseVersion>false</SynchReleaseVersion>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<DebugSymbols>true</DebugSymbols>
@ -93,6 +95,7 @@
<Compile Include="CPU\VR4300\CP1\VR4300.FloatingPointUnit.ImplementationRevisionRegister.cs" />
<Compile Include="CPU\VR4300\CP1\VR4300.FloatingPointUnit.ControlStatusRegister.cs" />
<Compile Include="CPU\VR4300\CP0\VR4300.SystemControlUnit.FunctOpCode.cs" />
<Compile Include="CPU\VR4300\VR4300.AccessSize.cs" />
</ItemGroup>
<ItemGroup>
<Folder Include="CPU\" />