Huge changes (too lazy to separate).
* VR4300.SystemControlUnit.StatusRegister.cs: Removed unneeded value. * VR4300.SystemControlUnit.cs: Renamed method. * VR4300.Instruction.cs: Refactored instruction contents. Now individual parts can be written to as well. It is also possible to strip an instruction to its bare opcode identifier, or even create one from a specified opcode, so this can be used as a key in a dictionary of operations. A basic ToString implementation displays the opcode of the instruction. * VR4300.OpCode.cs: * VR4300.RegImmOpCode.cs: * VR4300.SpecialOpCode.cs: Added opcodes. * VR4300.cs: Unified operations into a single dictionary thanks to the instruction refactoring. Added ops: JAL, SLTI, XORI, BLEZL, SB, LBU, SLT, BGEZL. * Debugger.Command.cs: Basic display of what the command is about. * Debugger.InstructionFormat.cs: First pass of the formatter. * Debugger.cs: Proper disassembly of instructions. Stepping also displays the contents of registers. ~Infinite~ count argument for stepping/disassembling. Refactored instruction fetch into using the CPU's SysAD bus (no need to manually access the N64's memory maps). * DotN64.csproj: * RealityCoprocessor.SignalProcessor.StatusRegister.cs: * RealityCoprocessor.DisplayProcessor.StatusRegister.cs: * RealityCoprocessor.RDRAMInterface.RDRAMConfigIndex.cs: * RealityCoprocessor.RDRAMInterface.RDRAMConfigRegister.cs: * MappingEntryExtensions.cs: Saves some typing. * BitHelper.cs: Reusable methods. * Nintendo64.cs: More memory maps. * Program.cs: Minor changes. * RealityCoprocessor.Interface.cs: * RealityCoprocessor.AudioInterface.cs: * RealityCoprocessor.VideoInterface.cs: * RealityCoprocessor.SerialInterface.cs: Simplified mapping accesses. * RealityCoprocessor.DisplayProcessor.cs: Added an actual register. Simplified mapping accesses. * RealityCoprocessor.MIPSInterface.cs: Added dummy version register read. Simplified mapping accesses. * RealityCoprocessor.PeripheralInterface.cs: Basic DMA. Simplified mapping accesses. * RealityCoprocessor.RDRAMInterface.cs: Removed constants that get set in the boot process. Added RDRAM registers. Simplified mapping accesses. * RealityCoprocessor.SignalProcessor.cs: Better types for existing registers and handle status writes. Simplified mapping accesses.master
parent
8905d75136
commit
23b06e69b5
|
@ -259,7 +259,6 @@ namespace DotN64.CPU
|
|||
[Flags]
|
||||
public enum CoprocessorUsabilities : byte
|
||||
{
|
||||
None = 0,
|
||||
CP0 = 1 << 0,
|
||||
CP1 = 1 << 1,
|
||||
CP2 = 1 << 2,
|
||||
|
|
|
@ -27,7 +27,7 @@ namespace DotN64.CPU
|
|||
/// Translates a virtual address into a physical address.
|
||||
/// See: datasheet#5.2.4 Table 5-3.
|
||||
/// </summary>
|
||||
public ulong Map(ulong address)
|
||||
public ulong Translate(ulong address)
|
||||
{
|
||||
switch (address >> 29 & 0b111)
|
||||
{
|
||||
|
|
|
@ -1,12 +1,27 @@
|
|||
namespace DotN64.CPU
|
||||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DotN64.CPU
|
||||
{
|
||||
using static Helpers.BitHelper;
|
||||
|
||||
public partial class VR4300
|
||||
{
|
||||
/// <summary>
|
||||
/// See: datasheet#3.1.
|
||||
/// </summary>
|
||||
public struct Instruction
|
||||
{
|
||||
#region Fields
|
||||
private uint data;
|
||||
|
||||
private const int OPShift = 26, OPSize = (1 << 6) - 1;
|
||||
private const int RSShift = 21, RSSize = (1 << 5) - 1;
|
||||
private const int RTShift = 16, RTSize = (1 << 5) - 1;
|
||||
private const int ImmediateShift = 0, ImmediateSize = (1 << 16) - 1;
|
||||
private const int TargetShift = 0, TargetSize = (1 << 26) - 1;
|
||||
private const int RDShift = 11, RDSize = (1 << 5) - 1;
|
||||
private const int SAShift = 6, SASize = (1 << 5) - 1;
|
||||
private const int FunctShift = 0, FunctSize = (1 << 6) - 1;
|
||||
public const int Size = sizeof(uint);
|
||||
#endregion
|
||||
|
||||
|
@ -14,42 +29,137 @@
|
|||
/// <summary>
|
||||
/// 6-bit operation code.
|
||||
/// </summary>
|
||||
public OpCode OP => (OpCode)(data >> 26);
|
||||
public OpCode OP
|
||||
{
|
||||
get => (OpCode)Get(data, OPShift, OPSize);
|
||||
set => Set(ref data, OPShift, OPSize, (uint)value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 5-bit source register number.
|
||||
/// </summary>
|
||||
public byte RS => (byte)(data >> 21 & ((1 << 5) - 1));
|
||||
public byte RS
|
||||
{
|
||||
get => (byte)Get(data, RSShift, RSSize);
|
||||
set => Set(ref data, RSShift, RSSize, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 5-bit target (source/destination) register number or branch condition.
|
||||
/// </summary>
|
||||
public byte RT => (byte)(data >> 16 & ((1 << 5) - 1));
|
||||
public byte RT
|
||||
{
|
||||
get => (byte)Get(data, RTShift, RTSize);
|
||||
set => Set(ref data, RTShift, RTSize, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 16-bit immediate value, branch displacement or address displacement.
|
||||
/// </summary>
|
||||
public ushort Immediate => (ushort)(data & ((1 << 16) - 1));
|
||||
public ushort Immediate
|
||||
{
|
||||
get => (ushort)Get(data, ImmediateShift, ImmediateSize);
|
||||
set => Set(ref data, ImmediateShift, ImmediateSize, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 26-bit unconditional branch target address.
|
||||
/// </summary>
|
||||
public uint Target => data & ((1 << 26) - 1);
|
||||
public uint Target
|
||||
{
|
||||
get => Get(data, TargetShift, TargetSize);
|
||||
set => Set(ref data, TargetShift, TargetSize, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 5-bit destination register number.
|
||||
/// </summary>
|
||||
public byte RD => (byte)(data >> 11 & ((1 << 5) - 1));
|
||||
public byte RD
|
||||
{
|
||||
get => (byte)Get(data, RDShift, RDSize);
|
||||
set => Set(ref data, RDShift, RDSize, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 5-bit shift amount.
|
||||
/// </summary>
|
||||
public byte SA => (byte)(data >> 6 & ((1 << 5) - 1));
|
||||
public byte SA
|
||||
{
|
||||
get => (byte)Get(data, SAShift, SASize);
|
||||
set => Set(ref data, SAShift, SASize, value);
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// 6-bit function field.
|
||||
/// </summary>
|
||||
public byte Funct => (byte)(data & ((1 << 6) - 1));
|
||||
public byte Funct
|
||||
{
|
||||
get => (byte)Get(data, FunctShift, FunctSize);
|
||||
set => Set(ref data, FunctShift, FunctSize, value);
|
||||
}
|
||||
|
||||
public SpecialOpCode? Special
|
||||
{
|
||||
get => OP == OpCode.SPECIAL ? (SpecialOpCode?)Funct : null;
|
||||
set
|
||||
{
|
||||
OP = OpCode.SPECIAL;
|
||||
Funct = (byte)value;
|
||||
}
|
||||
}
|
||||
|
||||
public RegImmOpCode? RegImm
|
||||
{
|
||||
get => OP == OpCode.REGIMM ? (RegImmOpCode?)RT : null;
|
||||
set
|
||||
{
|
||||
OP = OpCode.REGIMM;
|
||||
RT = (byte)value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public static Instruction FromOpCode(OpCode op) => new Instruction { OP = op };
|
||||
|
||||
public static Instruction FromOpCode(SpecialOpCode op) => new Instruction { Special = op };
|
||||
|
||||
public static Instruction FromOpCode(RegImmOpCode op) => new Instruction { RegImm = op };
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public Instruction ToOpCode()
|
||||
{
|
||||
switch (OP)
|
||||
{
|
||||
case OpCode.SPECIAL:
|
||||
return new Instruction
|
||||
{
|
||||
OP = OP,
|
||||
Funct = Funct
|
||||
};
|
||||
case OpCode.REGIMM:
|
||||
return new Instruction
|
||||
{
|
||||
OP = OP,
|
||||
RT = RT
|
||||
};
|
||||
default:
|
||||
return new Instruction { OP = OP };
|
||||
}
|
||||
}
|
||||
|
||||
public override string ToString()
|
||||
{
|
||||
switch (OP)
|
||||
{
|
||||
case OpCode.SPECIAL:
|
||||
return Special.ToString();
|
||||
case OpCode.REGIMM:
|
||||
return RegImm.ToString();
|
||||
default:
|
||||
return OP.ToString();
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Operators
|
||||
|
|
|
@ -31,7 +31,19 @@
|
|||
/// <summary>Add Immediate.</summary>
|
||||
ADDI = 0b001000,
|
||||
/// <summary>Cache Operation.</summary>
|
||||
CACHE = 0b101111
|
||||
CACHE = 0b101111,
|
||||
/// <summary>Jump And Link.</summary>
|
||||
JAL = 0b000011,
|
||||
/// <summary>Set On Less Than Immediate.</summary>
|
||||
SLTI = 0b001010,
|
||||
/// <summary>Exclusive Or Immediate.</summary>
|
||||
XORI = 0b001110,
|
||||
/// <summary>Branch On Less Than Or Equal To Zero Likely.</summary>
|
||||
BLEZL = 0b010110,
|
||||
/// <summary>Store Byte.</summary>
|
||||
SB = 0b101000,
|
||||
/// <summary>Load Byte Unsigned.</summary>
|
||||
LBU = 0b100100
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,7 +5,9 @@
|
|||
public enum RegImmOpCode : byte
|
||||
{
|
||||
/// <summary>Branch On Greater Than Or Equal To Zero And Link.</summary>
|
||||
BGEZAL = 0b10001
|
||||
BGEZAL = 0b10001,
|
||||
/// <summary>Branch On Greater Than Or Equal To Zero Likely.</summary>
|
||||
BGEZL = 0b00011
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -33,7 +33,9 @@
|
|||
/// <summary>Shift Right Logical Variable.</summary>
|
||||
SRLV = 0b000110,
|
||||
/// <summary>And.</summary>
|
||||
AND = 0b100100
|
||||
AND = 0b100100,
|
||||
/// <summary>Set On Less Than.</summary>
|
||||
SLT = 0b101010
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -11,9 +11,7 @@ namespace DotN64.CPU
|
|||
public partial class VR4300
|
||||
{
|
||||
#region Fields
|
||||
private readonly IReadOnlyDictionary<OpCode, Action<Instruction>> operations;
|
||||
private readonly IReadOnlyDictionary<SpecialOpCode, Action<Instruction>> specialOperations;
|
||||
private readonly IReadOnlyDictionary<RegImmOpCode, Action<Instruction>> regImmOperations;
|
||||
private readonly IReadOnlyDictionary<uint, Action<Instruction>> operations;
|
||||
|
||||
private const ulong ResetVector = 0xFFFFFFFFBFC00000;
|
||||
|
||||
|
@ -90,66 +88,64 @@ namespace DotN64.CPU
|
|||
#region Constructors
|
||||
public VR4300()
|
||||
{
|
||||
operations = new Dictionary<OpCode, Action<Instruction>>
|
||||
operations = new Dictionary<uint, Action<Instruction>>
|
||||
{
|
||||
[OpCode.SPECIAL] = i =>
|
||||
[Instruction.FromOpCode(OpCode.LUI)] = i => GPR[i.RT] = (ulong)(i.Immediate << 16),
|
||||
[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.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.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),
|
||||
[Instruction.FromOpCode(OpCode.ADDI)] = i => GPR[i.RT] = (ulong)(int)(GPR[i.RS] + (ulong)(short)i.Immediate),
|
||||
[Instruction.FromOpCode(OpCode.CACHE)] = i => { /* TODO: Implement and compare the performance if it's a concern. */ },
|
||||
[Instruction.FromOpCode(OpCode.JAL)] = i =>
|
||||
{
|
||||
if (specialOperations.TryGetValue((SpecialOpCode)i.Funct, out var operation))
|
||||
operation(i);
|
||||
else
|
||||
throw new Exception($"Unknown special opcode (0b{Convert.ToString(i.Funct, 2)}) from instruction 0x{(uint)i:X8}.");
|
||||
GPR[31] = PC + Instruction.Size;
|
||||
DelaySlot = PC;
|
||||
PC = (PC & ~((ulong)(1 << 28) - 1)) | (i.Target << 2);
|
||||
},
|
||||
[OpCode.REGIMM] = i =>
|
||||
[Instruction.FromOpCode(OpCode.SLTI)] = i => GPR[i.RT] = GPR[i.RS] < (ulong)(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 =>
|
||||
{
|
||||
if (regImmOperations.TryGetValue((RegImmOpCode)i.RT, out var operation))
|
||||
operation(i);
|
||||
else
|
||||
throw new Exception($"Unknown reg imm opcode (0b{Convert.ToString(i.RT, 2)}) from instruction 0x{(uint)i:X8}.");
|
||||
var address = (ulong)(short)i.Immediate + GPR[i.RS];
|
||||
|
||||
WriteWord(address, (ReadWord(address) & ~((uint)(1 << 8) - 1)) | (byte)GPR[i.RT]);
|
||||
},
|
||||
[OpCode.LUI] = i => GPR[i.RT] = (ulong)(i.Immediate << 16),
|
||||
[OpCode.MTC0] = i => CP0.Registers[i.RD] = GPR[i.RT],
|
||||
[OpCode.ORI] = i => GPR[i.RT] = GPR[i.RS] | i.Immediate,
|
||||
[OpCode.LW] = i => GPR[i.RT] = (ulong)(int)ReadWord((ulong)(short)i.Immediate + GPR[i.RS]),
|
||||
[OpCode.ANDI] = i => GPR[i.RT] = (ulong)(i.Immediate & (ushort)GPR[i.RS]),
|
||||
[OpCode.BEQL] = i => BranchLikely(i, (rs, rt) => rs == rt),
|
||||
[OpCode.ADDIU] = i => GPR[i.RT] = (ulong)(int)(GPR[i.RS] + (ulong)(short)i.Immediate),
|
||||
[OpCode.SW] = i => WriteWord((ulong)(short)i.Immediate + GPR[i.RS], (uint)GPR[i.RT]),
|
||||
[OpCode.BNEL] = i => BranchLikely(i, (rs, rt) => rs != rt),
|
||||
[OpCode.BNE] = i => Branch(i, (rs, rt) => rs != rt),
|
||||
[OpCode.BEQ] = i => Branch(i, (rs, rt) => rs == rt),
|
||||
[OpCode.ADDI] = i => GPR[i.RT] = (ulong)(int)(GPR[i.RS] + (ulong)(short)i.Immediate),
|
||||
[OpCode.CACHE] = i => { /* TODO: Implement and compare the performance if it's a concern. */ }
|
||||
};
|
||||
specialOperations = new Dictionary<SpecialOpCode, Action<Instruction>>
|
||||
{
|
||||
[SpecialOpCode.ADD] = i => GPR[i.RD] = (ulong)((int)GPR[i.RS] + (int)GPR[i.RT]),
|
||||
[SpecialOpCode.JR] = i =>
|
||||
[Instruction.FromOpCode(OpCode.LBU)] = i => GPR[i.RT] = (byte)ReadWord((ulong)(short)i.Immediate + GPR[i.RS]),
|
||||
[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];
|
||||
},
|
||||
[SpecialOpCode.SRL] = i => GPR[i.RD] = (ulong)((int)GPR[i.RT] >> i.SA),
|
||||
[SpecialOpCode.OR] = i => GPR[i.RD] = GPR[i.RS] | GPR[i.RT],
|
||||
[SpecialOpCode.MULTU] = i =>
|
||||
[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 =>
|
||||
{
|
||||
var result = (ulong)(uint)GPR[i.RS] * (ulong)(uint)GPR[i.RT];
|
||||
LO = (ulong)(int)result;
|
||||
HI = (ulong)(int)(result >> 32);
|
||||
},
|
||||
[SpecialOpCode.MFLO] = i => GPR[i.RD] = LO,
|
||||
[SpecialOpCode.SLL] = i => GPR[i.RD] = (ulong)((int)GPR[i.RT] << i.SA),
|
||||
[SpecialOpCode.SUBU] = i => GPR[i.RD] = (ulong)(int)(GPR[i.RS] - GPR[i.RT]),
|
||||
[SpecialOpCode.XOR] = i => GPR[i.RD] = GPR[i.RS] ^ GPR[i.RT],
|
||||
[SpecialOpCode.MFHI] = i => GPR[i.RD] = HI,
|
||||
[SpecialOpCode.ADDU] = i => GPR[i.RD] = (ulong)((int)GPR[i.RS] + (int)GPR[i.RT]),
|
||||
[SpecialOpCode.SLTU] = i => GPR[i.RD] = (ulong)(GPR[i.RS] < GPR[i.RT] ? 1 : 0),
|
||||
[SpecialOpCode.SLLV] = i => GPR[i.RD] = (ulong)(int)(GPR[i.RT] << (int)(GPR[i.RS] & ((1 << 5) - 1))),
|
||||
[SpecialOpCode.SRLV] = i => GPR[i.RD] = (ulong)(int)(GPR[i.RT] >> (int)(GPR[i.RS] & ((1 << 5) - 1))),
|
||||
[SpecialOpCode.AND] = i => GPR[i.RD] = GPR[i.RS] & GPR[i.RT]
|
||||
};
|
||||
regImmOperations = new Dictionary<RegImmOpCode, Action<Instruction>>
|
||||
{
|
||||
[RegImmOpCode.BGEZAL] = i => Branch(i, (rs, rt) => rs >= 0, true)
|
||||
[Instruction.FromOpCode(SpecialOpCode.MFLO)] = i => GPR[i.RD] = LO,
|
||||
[Instruction.FromOpCode(SpecialOpCode.SLL)] = i => GPR[i.RD] = (ulong)((int)GPR[i.RT] << i.SA),
|
||||
[Instruction.FromOpCode(SpecialOpCode.SUBU)] = i => GPR[i.RD] = (ulong)(int)(GPR[i.RS] - GPR[i.RT]),
|
||||
[Instruction.FromOpCode(SpecialOpCode.XOR)] = i => GPR[i.RD] = GPR[i.RS] ^ GPR[i.RT],
|
||||
[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.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),
|
||||
[Instruction.FromOpCode(RegImmOpCode.BGEZL)] = i => BranchLikely(i, (rs, rt) => rs >= 0)
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
@ -180,10 +176,10 @@ namespace DotN64.CPU
|
|||
|
||||
public void Run(Instruction instruction)
|
||||
{
|
||||
if (operations.TryGetValue(instruction.OP, out var operation))
|
||||
if (operations.TryGetValue(instruction.ToOpCode(), out var operation))
|
||||
operation(instruction);
|
||||
else
|
||||
throw new Exception($"Unknown opcode (0b{Convert.ToString((byte)instruction.OP, 2)}) from instruction 0x{(uint)instruction:X8}.");
|
||||
throw new Exception($"Unknown opcode from instruction 0x{(uint)instruction:X8}.");
|
||||
}
|
||||
|
||||
public void Step()
|
||||
|
@ -227,10 +223,10 @@ namespace DotN64.CPU
|
|||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private uint ReadWord(ulong address) => ReadSysAD(CP0.Map(address));
|
||||
private uint ReadWord(ulong address) => ReadSysAD(CP0.Translate(address));
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
private void WriteWord(ulong address, uint value) => WriteSysAD(CP0.Map(address), value);
|
||||
private void WriteWord(ulong address, uint value) => WriteSysAD(CP0.Translate(address), value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -23,6 +23,10 @@ namespace DotN64.Diagnostics
|
|||
Action = action;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public override string ToString() => $"{string.Join(", ", Names)}: {Description}";
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,84 @@
|
|||
namespace DotN64.Diagnostics
|
||||
{
|
||||
using CPU;
|
||||
|
||||
public partial class Debugger
|
||||
{
|
||||
// TODO: Double check for possibly missing exceptions in formatting according to the CPU's documentation, as I started with help from a MIPS I (?) general documentation (https://en.wikibooks.org/wiki/MIPS_Assembly/Instruction_Formats).
|
||||
// NOTE: Perhaps this should be moved beside the CPU. It would probably be nice to make it overridable from a more general MIPS format class, as well as make it instanciable for changing display variables (register prefix, etc.).
|
||||
// NOTE: If assembling input has to be implemented, make an enum Type, and move the currently used methods inside a Disassembler class, and the new ones into Assembler. The caller would look for the type associated with a given instruction and choose from here.
|
||||
/// <summary>
|
||||
/// Formatting helpers for disassembling instructions.
|
||||
/// </summary>
|
||||
private static class InstructionFormat
|
||||
{
|
||||
#region Fields
|
||||
private const string Separator = ", ", RegisterPrefix = "$";
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
private static string Format(VR4300.Instruction instruction, params object[] values) => $"{instruction} {string.Join(Separator, values)}";
|
||||
|
||||
private static string FormatRegister(int index, VR4300 cpu) => RegisterPrefix + (VR4300.GPRIndex)index + (cpu != null ? FormatRegisterContents(cpu.GPR[index]) : string.Empty);
|
||||
|
||||
private static string FormatCP0Register(int index, VR4300 cpu) => RegisterPrefix + (VR4300.SystemControlUnit.RegisterIndex)index + (cpu != null ? FormatRegisterContents(cpu.CP0.Registers[index]) : string.Empty);
|
||||
|
||||
private static string FormatRegisterContents(ulong value) => $"(0x{value:X})";
|
||||
|
||||
/// <summary>
|
||||
/// Immediate type.
|
||||
/// </summary>
|
||||
public static string I(VR4300.Instruction instruction, VR4300 cpu)
|
||||
{
|
||||
switch (instruction.RegImm)
|
||||
{
|
||||
case VR4300.RegImmOpCode.BGEZAL:
|
||||
case VR4300.RegImmOpCode.BGEZL:
|
||||
return Format(instruction, FormatRegister(instruction.RS, cpu), (short)instruction.Immediate);
|
||||
}
|
||||
|
||||
switch (instruction.OP)
|
||||
{
|
||||
case VR4300.OpCode.BEQ:
|
||||
case VR4300.OpCode.BNE:
|
||||
case VR4300.OpCode.BEQL:
|
||||
case VR4300.OpCode.BNEL:
|
||||
return Format(instruction, FormatRegister(instruction.RS, cpu), FormatRegister(instruction.RT, cpu), (short)instruction.Immediate);
|
||||
case VR4300.OpCode.BLEZL:
|
||||
return Format(instruction, FormatRegister(instruction.RS, cpu), (short)instruction.Immediate);
|
||||
default:
|
||||
return Format(instruction, FormatRegister(instruction.RT, cpu), FormatRegister(instruction.RS, cpu), (short)instruction.Immediate);
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Register type.
|
||||
/// </summary>
|
||||
public static string R(VR4300.Instruction instruction, VR4300 cpu)
|
||||
{
|
||||
switch (instruction.Special)
|
||||
{
|
||||
case VR4300.SpecialOpCode.JR:
|
||||
return Format(instruction, FormatRegister(instruction.RS, cpu));
|
||||
case VR4300.SpecialOpCode.SLLV:
|
||||
case VR4300.SpecialOpCode.SRLV:
|
||||
return Format(instruction, FormatRegister(instruction.RD, cpu), FormatRegister(instruction.RT, cpu), FormatRegister(instruction.RS, cpu));
|
||||
}
|
||||
|
||||
switch (instruction.OP)
|
||||
{
|
||||
case VR4300.OpCode.MTC0:
|
||||
return Format(instruction, FormatRegister(instruction.RT, cpu), FormatCP0Register(instruction.RD, cpu));
|
||||
default:
|
||||
return Format(instruction, FormatRegister(instruction.RD, cpu), FormatRegister(instruction.RS, cpu), FormatRegister(instruction.RT, cpu));
|
||||
}
|
||||
}
|
||||
|
||||
/// <summary>
|
||||
/// Jump type.
|
||||
/// </summary>
|
||||
public static string J(VR4300.Instruction instruction, VR4300 cpu) => Format(instruction, $"0x{instruction.Target:X8}");
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,11 +2,11 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Globalization;
|
||||
using System.Linq;
|
||||
using System.Numerics;
|
||||
|
||||
namespace DotN64.Diagnostics
|
||||
{
|
||||
using CPU;
|
||||
using Extensions;
|
||||
|
||||
public partial class Debugger
|
||||
{
|
||||
|
@ -15,6 +15,46 @@ namespace DotN64.Diagnostics
|
|||
private readonly IReadOnlyCollection<Command> commands;
|
||||
private readonly IDictionary<ulong, string> labels = new Dictionary<ulong, string>();
|
||||
private readonly IList<ulong> breakpoints = new List<ulong>();
|
||||
private readonly IDictionary<uint, Func<VR4300.Instruction, VR4300, string>> operationFormats = new Dictionary<uint, Func<VR4300.Instruction, VR4300, string>>
|
||||
{
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.ADDI)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.ADDIU)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.ANDI)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.BEQ)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.BEQL)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.BLEZL)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.BNE)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.BNEL)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.CACHE)] = null,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.JAL)] = InstructionFormat.J,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.LBU)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.LUI)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.LW)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.MTC0)] = InstructionFormat.R,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.ORI)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.SB)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.SLTI)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.SW)] = InstructionFormat.I,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.OpCode.XORI)] = 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,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.JR)] = InstructionFormat.R,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.MFHI)] = InstructionFormat.R,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.MFLO)] = InstructionFormat.R,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.MULTU)] = InstructionFormat.R,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.OR)] = InstructionFormat.R,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.SLL)] = InstructionFormat.R,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.SLLV)] = InstructionFormat.R,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.SLT)] = InstructionFormat.R,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.SLTU)] = InstructionFormat.R,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.SRL)] = InstructionFormat.R,
|
||||
[VR4300.Instruction.FromOpCode(VR4300.SpecialOpCode.SRLV)] = InstructionFormat.R,
|
||||
[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
|
||||
};
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
|
@ -30,9 +70,9 @@ namespace DotN64.Diagnostics
|
|||
new Command(new[] { "continue", "c" }, "Continues execution of the CPU.", args => DebuggerStatus = Status.Running),
|
||||
new Command(new[] { "step", "s" }, "Steps the CPU a specified amount of times.", args =>
|
||||
{
|
||||
var count = args.Length > 0 ? int.Parse(args.First()) : 1;
|
||||
var count = args.Length > 0 ? BigInteger.Parse(args.First()) : 1;
|
||||
|
||||
for (var i = 0; i < count; i++)
|
||||
for (var i = BigInteger.Zero; i < count; i++)
|
||||
{
|
||||
Disassemble();
|
||||
nintendo64.CPU.Step();
|
||||
|
@ -41,18 +81,18 @@ namespace DotN64.Diagnostics
|
|||
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 =>
|
||||
{
|
||||
var count = args.Length > 0 ? ulong.Parse(args.First()) : 1;
|
||||
var count = args.Length > 0 ? BigInteger.Parse(args.First()) : 1;
|
||||
|
||||
for (var i = 0ul; i < count; i++)
|
||||
for (var i = BigInteger.Zero; i < count; i++)
|
||||
{
|
||||
Disassemble(nintendo64.CPU.PC + i * VR4300.Instruction.Size);
|
||||
Disassemble(nintendo64.CPU.PC + (ulong)(i * VR4300.Instruction.Size), false);
|
||||
}
|
||||
}),
|
||||
new Command(new[] { "label", "labels", "l" }, "Shows, adds or removes a label attached to an address.", args =>
|
||||
{
|
||||
var index = 0;
|
||||
|
||||
switch (args.Length == 0 ? "show" : args[index++])
|
||||
switch (args.Length > 0 ? args[index++] : "show")
|
||||
{
|
||||
case "add":
|
||||
case "a":
|
||||
|
@ -75,7 +115,6 @@ namespace DotN64.Diagnostics
|
|||
{
|
||||
Console.WriteLine($".{pair.Value}: {pair.Key:X16}");
|
||||
}
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
|
@ -90,7 +129,7 @@ namespace DotN64.Diagnostics
|
|||
{
|
||||
var index = 0;
|
||||
|
||||
switch (args.Length == 0 ? "show" : args[index++])
|
||||
switch (args.Length > 0 ? args[index++] : "show")
|
||||
{
|
||||
case "add":
|
||||
case "a":
|
||||
|
@ -120,7 +159,7 @@ namespace DotN64.Diagnostics
|
|||
{
|
||||
foreach (var command in commands)
|
||||
{
|
||||
Console.WriteLine($"- {string.Join(", ", command.Names)}: {command.Description}");
|
||||
Console.WriteLine("- " + command);
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -128,7 +167,7 @@ namespace DotN64.Diagnostics
|
|||
var commandName = args.First();
|
||||
var command = commands.First(c => c.Names.Contains(commandName));
|
||||
|
||||
Console.WriteLine($"{string.Join(", ", command.Names)}: {command.Description}");
|
||||
Console.WriteLine(command);
|
||||
}
|
||||
}),
|
||||
new Command(new[] { "clear" }, "Clears the terminal.", args => Console.Clear())
|
||||
|
@ -137,32 +176,20 @@ namespace DotN64.Diagnostics
|
|||
#endregion
|
||||
|
||||
#region Methods
|
||||
private string Disassemble(VR4300.Instruction instruction)
|
||||
{
|
||||
switch (instruction.OP)
|
||||
{
|
||||
case VR4300.OpCode.SPECIAL:
|
||||
return ((VR4300.SpecialOpCode)instruction.Funct).ToString();
|
||||
case VR4300.OpCode.REGIMM:
|
||||
return ((VR4300.RegImmOpCode)instruction.RT).ToString();
|
||||
default:
|
||||
return instruction.OP.ToString();
|
||||
}
|
||||
}
|
||||
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 void Disassemble(ulong? address = null)
|
||||
private void Disassemble(ulong? address = null, bool withRegisterContents = true)
|
||||
{
|
||||
if (!address.HasValue)
|
||||
address = nintendo64.CPU.DelaySlot ?? nintendo64.CPU.PC;
|
||||
|
||||
var physicalAddress = nintendo64.CPU.CP0.Map(address.Value);
|
||||
var instruction = nintendo64.MemoryMaps.GetEntry(physicalAddress).ReadWord(physicalAddress);
|
||||
var instruction = nintendo64.CPU.ReadSysAD(nintendo64.CPU.CP0.Translate(address.Value));
|
||||
|
||||
if (labels.ContainsKey(address.Value))
|
||||
if (labels.TryGetValue(address.Value, out var label))
|
||||
{
|
||||
Console.ForegroundColor = ConsoleColor.Green;
|
||||
|
||||
Console.WriteLine($".{labels[address.Value]}:");
|
||||
Console.WriteLine($".{label}:");
|
||||
Console.ResetColor();
|
||||
}
|
||||
|
||||
|
@ -174,7 +201,7 @@ namespace DotN64.Diagnostics
|
|||
Console.ForegroundColor = ConsoleColor.White;
|
||||
}
|
||||
|
||||
Console.WriteLine($"{(hasBreakpoint ? '●' : ' ')} {address.Value:X16}: {instruction:X8} {Disassemble(instruction)}");
|
||||
Console.WriteLine($"{(hasBreakpoint ? '●' : ' ')} {address.Value:X16}: {instruction:X8} {Disassemble(instruction, withRegisterContents)}");
|
||||
|
||||
if (hasBreakpoint)
|
||||
Console.ResetColor();
|
||||
|
|
|
@ -27,6 +27,7 @@
|
|||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
<Reference Include="System.Core" />
|
||||
<Reference Include="System.Numerics" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Compile Include="Program.cs" />
|
||||
|
@ -67,6 +68,12 @@
|
|||
<Compile Include="Diagnostics\Debugger.cs" />
|
||||
<Compile Include="Diagnostics\Debugger.Status.cs" />
|
||||
<Compile Include="Diagnostics\Debugger.Command.cs" />
|
||||
<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\RI\RealityCoprocessor.RDRAMInterface.RDRAMConfigRegister.cs" />
|
||||
<Compile Include="RCP\RI\RealityCoprocessor.RDRAMInterface.RDRAMConfigIndex.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="CPU\" />
|
||||
|
@ -83,6 +90,7 @@
|
|||
<Folder Include="CPU\VR4300\" />
|
||||
<Folder Include="CPU\VR4300\CP0\" />
|
||||
<Folder Include="Diagnostics\" />
|
||||
<Folder Include="Helpers\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -20,6 +20,12 @@ namespace DotN64.Extensions
|
|||
|
||||
throw new Exception($"Unknown physical address: 0x{address:X16}.");
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint ReadWord(this IReadOnlyList<MappingEntry> memoryMaps, ulong address) => memoryMaps.GetEntry(address).ReadWord(address);
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void WriteWord(this IReadOnlyList<MappingEntry> memoryMaps, ulong address, uint value) => memoryMaps.GetEntry(address).WriteWord(address, value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,37 @@
|
|||
using System.Runtime.CompilerServices;
|
||||
|
||||
namespace DotN64.Helpers
|
||||
{
|
||||
public static class BitHelper
|
||||
{
|
||||
#region Methods
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static uint Get(uint data, int shift, uint size) => data >> shift & size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static ulong Get(ulong data, int shift, ulong size) => data >> shift & size;
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Set(ref uint data, int shift, uint size, uint value)
|
||||
{
|
||||
data &= ~(size << shift);
|
||||
data |= (value & size) << shift;
|
||||
}
|
||||
|
||||
[MethodImpl(MethodImplOptions.AggressiveInlining)]
|
||||
public static void Set(ref ulong data, int shift, ulong size, ulong value)
|
||||
{
|
||||
data &= ~(size << shift);
|
||||
data |= (value & size) << shift;
|
||||
}
|
||||
|
||||
public static unsafe void Write(byte[] data, int index, uint value)
|
||||
{
|
||||
fixed (byte* pointer = &data[index])
|
||||
{
|
||||
*(uint*)pointer = value;
|
||||
}
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -28,69 +28,74 @@ namespace DotN64
|
|||
{
|
||||
new MappingEntry(0x1FC00000, 0x1FC007BF, false) // PIF Boot ROM.
|
||||
{
|
||||
Read = RCP.PI.ReadWord,
|
||||
Write = RCP.PI.WriteWord
|
||||
Read = RCP.PI.MemoryMaps.ReadWord,
|
||||
Write = RCP.PI.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x1FC007C0, 0x1FC007FF, false) // PIF (JoyChannel) RAM.
|
||||
{
|
||||
Read = RCP.PI.ReadWord,
|
||||
Write = RCP.PI.WriteWord
|
||||
Read = RCP.PI.MemoryMaps.ReadWord,
|
||||
Write = RCP.PI.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x04600000, 0x046FFFFF, false) // Peripheral interface (PI) registers.
|
||||
{
|
||||
Read = RCP.PI.ReadWord,
|
||||
Write = RCP.PI.WriteWord
|
||||
Read = RCP.PI.MemoryMaps.ReadWord,
|
||||
Write = RCP.PI.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x04000000, 0x040FFFFF, false) // SP registers.
|
||||
{
|
||||
Read = RCP.SP.ReadWord,
|
||||
Write = RCP.SP.WriteWord
|
||||
Read = RCP.SP.MemoryMaps.ReadWord,
|
||||
Write = RCP.SP.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x04400000, 0x044FFFFF, false) // Video interface (VI) registers.
|
||||
{
|
||||
Read = RCP.VI.ReadWord,
|
||||
Write = RCP.VI.WriteWord
|
||||
Read = RCP.VI.MemoryMaps.ReadWord,
|
||||
Write = RCP.VI.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x04500000, 0x045FFFFF, false) // Audio interface (AI) registers.
|
||||
{
|
||||
Read = RCP.AI.ReadWord,
|
||||
Write = RCP.AI.WriteWord
|
||||
Read = RCP.AI.MemoryMaps.ReadWord,
|
||||
Write = RCP.AI.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x04300000, 0x043FFFFF, false) // MIPS interface (MI) registers.
|
||||
{
|
||||
Read = RCP.MI.ReadWord,
|
||||
Write = RCP.MI.WriteWord
|
||||
Read = RCP.MI.MemoryMaps.ReadWord,
|
||||
Write = RCP.MI.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x04800000, 0x048FFFFF, false) // Serial interface (SI) registers.
|
||||
{
|
||||
Read = RCP.SI.ReadWord,
|
||||
Write = RCP.SI.WriteWord
|
||||
Read = RCP.SI.MemoryMaps.ReadWord,
|
||||
Write = RCP.SI.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x10000000, 0x1FBFFFFF, false) // Cartridge Domain 1 Address 2.
|
||||
{
|
||||
Read = RCP.PI.ReadWord
|
||||
Read = RCP.PI.MemoryMaps.ReadWord
|
||||
},
|
||||
new MappingEntry(0x04100000, 0x041FFFFF, false) // DP command registers.
|
||||
{
|
||||
Read = RCP.DP.ReadWord,
|
||||
Write = RCP.DP.WriteWord
|
||||
Read = RCP.DP.MemoryMaps.ReadWord,
|
||||
Write = RCP.DP.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x04700000, 0x047FFFFF, false) // RDRAM interface (RI) registers.
|
||||
{
|
||||
Read = RCP.RI.ReadWord,
|
||||
Write = RCP.RI.WriteWord
|
||||
Read = RCP.RI.MemoryMaps.ReadWord,
|
||||
Write = RCP.RI.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x00000000, 0x03EFFFFF, false) // RDRAM memory.
|
||||
{
|
||||
Read = RCP.RI.ReadWord,
|
||||
Write = RCP.RI.WriteWord
|
||||
Read = RCP.RI.MemoryMaps.ReadWord,
|
||||
Write = RCP.RI.MemoryMaps.WriteWord
|
||||
},
|
||||
new MappingEntry(0x03F00000, 0x03FFFFFF, false) // RDRAM registers.
|
||||
{
|
||||
Read = RCP.RI.MemoryMaps.ReadWord,
|
||||
Write = RCP.RI.MemoryMaps.WriteWord
|
||||
}
|
||||
};
|
||||
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.
|
||||
ReadSysAD = a => MemoryMaps.GetEntry(a).ReadWord(a),
|
||||
WriteSysAD = (a, v) => MemoryMaps.GetEntry(a).WriteWord(a, v)
|
||||
ReadSysAD = MemoryMaps.ReadWord,
|
||||
WriteSysAD = MemoryMaps.WriteWord
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
|
|
@ -9,11 +9,11 @@ namespace DotN64
|
|||
private static void Main(string[] args)
|
||||
{
|
||||
var nintendo64 = new Nintendo64();
|
||||
var debugger = default(Debugger);
|
||||
Debugger debugger = null;
|
||||
|
||||
for (int i = 0; i < args.Length; i++)
|
||||
{
|
||||
string arg = args[i];
|
||||
var arg = args[i];
|
||||
|
||||
switch (arg)
|
||||
{
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace DotN64.RCP
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public class AudioInterface : Interface
|
||||
{
|
||||
#region Fields
|
||||
private readonly IReadOnlyList<MappingEntry> memoryMaps;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
|
||||
|
||||
private uint dramAddress;
|
||||
/// <summary>
|
||||
/// Starting RDRAM address (8B-aligned).
|
||||
|
@ -35,7 +27,7 @@ namespace DotN64.RCP
|
|||
public AudioInterface(RealityCoprocessor rcp)
|
||||
: base(rcp)
|
||||
{
|
||||
memoryMaps = new[]
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x04500000, 0x04500003) // AI DRAM address.
|
||||
{
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
namespace DotN64.RCP
|
||||
{
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class DisplayProcessor
|
||||
{
|
||||
[System.Flags]
|
||||
public enum StatusRegister : ushort
|
||||
{
|
||||
XBusDMemDMA = 1 << 0,
|
||||
Freeze = 1 << 1,
|
||||
Flush = 1 << 2,
|
||||
StartGClk = 1 << 3,
|
||||
TMemBusy = 1 << 4,
|
||||
PipeBusy = 1 << 5,
|
||||
CmdBusy = 1 << 6,
|
||||
CBufReady = 1 << 7,
|
||||
DMABusy = 1 << 8,
|
||||
EndValid = 1 << 9,
|
||||
StartValid = 1 << 10
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -2,34 +2,28 @@
|
|||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
using Extensions;
|
||||
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public class DisplayProcessor
|
||||
public partial class DisplayProcessor
|
||||
{
|
||||
#region Fields
|
||||
private readonly IReadOnlyList<MappingEntry> memoryMaps;
|
||||
#region Properties
|
||||
public IReadOnlyList<MappingEntry> MemoryMaps { get; }
|
||||
|
||||
public StatusRegister Status { get; set; }
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public DisplayProcessor()
|
||||
{
|
||||
memoryMaps = new[]
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x0410000C, 0x0410000F) // DP CMD status.
|
||||
{
|
||||
Read = o => 0
|
||||
Read = o => (uint)Status
|
||||
}
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public uint ReadWord(ulong address) => memoryMaps.GetEntry(address).ReadWord(address);
|
||||
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.GetEntry(address).WriteWord(address, value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,5 +1,4 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
|
||||
namespace DotN64.RCP
|
||||
|
@ -8,13 +7,7 @@ namespace DotN64.RCP
|
|||
{
|
||||
public partial class MIPSInterface : Interface
|
||||
{
|
||||
#region Fields
|
||||
private readonly IReadOnlyList<MappingEntry> memoryMaps;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
|
||||
|
||||
public InitModeRegister InitMode { get; set; }
|
||||
#endregion
|
||||
|
||||
|
@ -22,7 +15,7 @@ namespace DotN64.RCP
|
|||
public MIPSInterface(RealityCoprocessor rcp)
|
||||
: base(rcp)
|
||||
{
|
||||
memoryMaps = new[]
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x04300000, 0x04300003) // MI init mode.
|
||||
{
|
||||
|
@ -47,6 +40,10 @@ namespace DotN64.RCP
|
|||
|
||||
InitMode = mode;
|
||||
}
|
||||
},
|
||||
new MappingEntry(0x04300004, 0x04300007) // MI version.
|
||||
{
|
||||
Read = o => 0 // TODO.
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,25 +1,21 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Net;
|
||||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
using Extensions;
|
||||
using Helpers;
|
||||
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class PeripheralInterface : Interface
|
||||
{
|
||||
#region Fields
|
||||
private readonly IReadOnlyList<MappingEntry> memoryMaps;
|
||||
|
||||
private const byte CICStatusOffset = 60;
|
||||
private const byte ResetControllerStatus = 1 << 0, ClearInterruptStatus = 1 << 1;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
|
||||
|
||||
public StatusRegister Status { get; set; }
|
||||
|
||||
public byte[] BootROM { get; set; }
|
||||
|
@ -52,7 +48,7 @@ namespace DotN64.RCP
|
|||
public PeripheralInterface(RealityCoprocessor rcp)
|
||||
: base(rcp)
|
||||
{
|
||||
memoryMaps = new[]
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x1FC00000, 0x1FC007BF) // PIF Boot ROM.
|
||||
{
|
||||
|
@ -63,13 +59,7 @@ namespace DotN64.RCP
|
|||
Read = o => BitConverter.ToUInt32(RAM, (int)o),
|
||||
Write = (o, v) =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* data = &RAM[(int)o])
|
||||
{
|
||||
*(uint*)data = 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.
|
||||
|
@ -80,11 +70,9 @@ namespace DotN64.RCP
|
|||
Read = o => (uint)Status,
|
||||
Write = (o, v) =>
|
||||
{
|
||||
if ((v & ResetControllerStatus) != 0)
|
||||
ResetController();
|
||||
if ((v & ResetControllerStatus) != 0) { /* TODO. */ }
|
||||
|
||||
if ((v & ClearInterruptStatus) != 0)
|
||||
ClearInterrupt();
|
||||
if ((v & ClearInterruptStatus) != 0) { /* TODO. */ }
|
||||
}
|
||||
},
|
||||
new MappingEntry(0x04600014, 0x04600017) // PI dom1 latency.
|
||||
|
@ -113,7 +101,20 @@ namespace DotN64.RCP
|
|||
},
|
||||
new MappingEntry(0x0460000C, 0x0460000F) // PI write length.
|
||||
{
|
||||
Write = (o, v) => WriteLength = v & ((1 << 24) - 1)
|
||||
Write = (o, v) =>
|
||||
{
|
||||
WriteLength = v & ((1 << 24) - 1);
|
||||
Status |= StatusRegister.DMABusy;
|
||||
var maps = rcp.Nintendo64.MemoryMaps;
|
||||
|
||||
for (uint i = 0; i < WriteLength + 1; i += sizeof(uint))
|
||||
{
|
||||
maps.WriteWord(DRAMAddress + i, maps.ReadWord(PBusAddress + i));
|
||||
}
|
||||
|
||||
Status &= ~StatusRegister.DMABusy;
|
||||
// TODO: Set interrupt.
|
||||
}
|
||||
},
|
||||
new MappingEntry(0x10000000, 0x1FBFFFFF) // Cartridge Domain 1 Address 2.
|
||||
{
|
||||
|
@ -124,10 +125,6 @@ namespace DotN64.RCP
|
|||
#endregion
|
||||
|
||||
#region Methods
|
||||
private void ClearInterrupt() { /* TODO: Implement. */ }
|
||||
|
||||
private void ResetController() { /* TODO: Implement. */ }
|
||||
|
||||
public void EmulateBootROM()
|
||||
{
|
||||
// Replicating the memory writes to properly initialise the subsystems.
|
||||
|
@ -152,14 +149,14 @@ namespace DotN64.RCP
|
|||
{
|
||||
var address = (ulong)writes[i, 0];
|
||||
|
||||
rcp.Nintendo64.MemoryMaps.GetEntry(address).WriteWord(address, writes[i, 1]);
|
||||
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.GetEntry(address).WriteWord(address, (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(rcp.Nintendo64.Cartridge.ROM, i)));
|
||||
rcp.Nintendo64.MemoryMaps.WriteWord(address, (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(rcp.Nintendo64.Cartridge.ROM, i)));
|
||||
}
|
||||
|
||||
// Restoring CPU state.
|
||||
|
|
|
@ -0,0 +1,15 @@
|
|||
namespace DotN64.RCP
|
||||
{
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class RDRAMInterface
|
||||
{
|
||||
public enum RDRAMConfigIndex : byte
|
||||
{
|
||||
Zero,
|
||||
One,
|
||||
Global
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -0,0 +1,27 @@
|
|||
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,33 +1,30 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
using Helpers;
|
||||
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class RDRAMInterface : Interface
|
||||
{
|
||||
#region Fields
|
||||
private readonly IReadOnlyList<MappingEntry> memoryMaps;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
|
||||
public byte Select { get; set; }
|
||||
|
||||
public byte Select { get; set; } = 0x14;
|
||||
public ConfigRegister Config { get; set; }
|
||||
|
||||
public ConfigRegister Config { get; set; } = 0x40;
|
||||
public ModeRegister Mode { get; set; }
|
||||
|
||||
public ModeRegister Mode { get; set; } = 0x0E;
|
||||
public RefreshRegister Refresh { get; set; }
|
||||
|
||||
public RefreshRegister Refresh { get; set; } = 0x00063634;
|
||||
public RDRAMConfigRegister[] RDRAMConfigs { get; } = new RDRAMConfigRegister[Enum.GetNames(typeof(RDRAMConfigIndex)).Length];
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public RDRAMInterface(RealityCoprocessor rcp)
|
||||
: base(rcp)
|
||||
{
|
||||
memoryMaps = new[]
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x0470000C, 0x0470000F) // RI select.
|
||||
{
|
||||
|
@ -48,17 +45,39 @@ namespace DotN64.RCP
|
|||
},
|
||||
new MappingEntry(0x04700010, 0x04700013) // RI refresh.
|
||||
{
|
||||
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) =>
|
||||
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 (byte* data = &rcp.Nintendo64.RAM[(int)o])
|
||||
fixed (void* pointer = &RDRAMConfigs[index.Value])
|
||||
{
|
||||
*(uint*)data = v;
|
||||
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;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -66,6 +85,31 @@ namespace DotN64.RCP
|
|||
};
|
||||
}
|
||||
#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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -2,8 +2,6 @@
|
|||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
using Extensions;
|
||||
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public abstract class Interface
|
||||
|
@ -13,7 +11,7 @@ namespace DotN64.RCP
|
|||
#endregion
|
||||
|
||||
#region Properties
|
||||
protected abstract IReadOnlyList<MappingEntry> MemoryMaps { get; }
|
||||
public IReadOnlyList<MappingEntry> MemoryMaps { get; protected set; }
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
|
@ -22,12 +20,6 @@ namespace DotN64.RCP
|
|||
this.rcp = rcp;
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public uint ReadWord(ulong address) => MemoryMaps.GetEntry(address).ReadWord(address);
|
||||
|
||||
public void WriteWord(ulong address, uint value) => MemoryMaps.GetEntry(address).WriteWord(address, value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,18 +1,10 @@
|
|||
using System.Collections.Generic;
|
||||
|
||||
namespace DotN64.RCP
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class SerialInterface : Interface
|
||||
{
|
||||
#region Fields
|
||||
private readonly IReadOnlyList<MappingEntry> memoryMaps;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
|
||||
|
||||
public StatusRegister Status { get; set; }
|
||||
#endregion
|
||||
|
||||
|
@ -20,7 +12,7 @@ namespace DotN64.RCP
|
|||
public SerialInterface(RealityCoprocessor rcp)
|
||||
: base(rcp)
|
||||
{
|
||||
memoryMaps = new[]
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x04800018, 0x0480001B) // SI status.
|
||||
{
|
||||
|
|
|
@ -0,0 +1,60 @@
|
|||
using System;
|
||||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public partial class SignalProcessor
|
||||
{
|
||||
[Flags]
|
||||
public enum StatusRegister : ushort
|
||||
{
|
||||
Halt = 1 << 0,
|
||||
Broke = 1 << 1,
|
||||
DMABusy = 1 << 2,
|
||||
DMAFull = 1 << 3,
|
||||
IOFull = 1 << 4,
|
||||
SingleStep = 1 << 5,
|
||||
InterruptOnBreak = 1 << 6,
|
||||
Signal0 = 1 << 7,
|
||||
Signal1 = 1 << 8,
|
||||
Signal2 = 1 << 9,
|
||||
Signal3 = 1 << 10,
|
||||
Signal4 = 1 << 11,
|
||||
Signal5 = 1 << 12,
|
||||
Signal6 = 1 << 13,
|
||||
Signal7 = 1 << 14
|
||||
}
|
||||
|
||||
[Flags]
|
||||
private enum WriteStatusRegister
|
||||
{
|
||||
ClearHalt = 1 << 0,
|
||||
SetHalt = 1 << 1,
|
||||
ClearBroke = 1 << 2,
|
||||
ClearInterrupt = 1 << 3,
|
||||
SetInterrupt = 1 << 4,
|
||||
ClearSingleStep = 1 << 5,
|
||||
SetSingleStep = 1 << 6,
|
||||
ClearInterruptOnBreak = 1 << 7,
|
||||
SetInterruptOnBreak = 1 << 8,
|
||||
ClearSignal0 = 1 << 9,
|
||||
SetSignal0 = 1 << 10,
|
||||
ClearSignal1 = 1 << 11,
|
||||
SetSignal1 = 1 << 12,
|
||||
ClearSignal2 = 1 << 13,
|
||||
SetSignal2 = 1 << 14,
|
||||
ClearSignal3 = 1 << 15,
|
||||
SetSignal3 = 1 << 16,
|
||||
ClearSignal4 = 1 << 17,
|
||||
SetSignal4 = 1 << 18,
|
||||
ClearSignal5 = 1 << 19,
|
||||
SetSignal5 = 1 << 20,
|
||||
ClearSignal6 = 1 << 21,
|
||||
SetSignal6 = 1 << 22,
|
||||
ClearSignal7 = 1 << 23,
|
||||
SetSignal7 = 1 << 24,
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -3,77 +3,133 @@ using System.Collections.Generic;
|
|||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
using Extensions;
|
||||
using Helpers;
|
||||
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public class SignalProcessor
|
||||
public partial class SignalProcessor
|
||||
{
|
||||
#region Fields
|
||||
private readonly IReadOnlyList<MappingEntry> memoryMaps;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
public uint Status { get; set; } = 1;
|
||||
public IReadOnlyList<MappingEntry> MemoryMaps { get; }
|
||||
|
||||
public uint DMABusy { get; set; }
|
||||
public StatusRegister Status { get; set; } = StatusRegister.Halt;
|
||||
|
||||
public bool DMABusy { get; set; }
|
||||
|
||||
/// <summary>
|
||||
/// Instruction memory.
|
||||
/// </summary>
|
||||
public byte[] IMEM { get; } = new byte[0x1000];
|
||||
|
||||
/// <summary>
|
||||
/// Data memory.
|
||||
/// </summary>
|
||||
public byte[] DMEM { get; } = new byte[0x1000];
|
||||
#endregion
|
||||
|
||||
#region Constructors
|
||||
public SignalProcessor()
|
||||
{
|
||||
memoryMaps = new[]
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x04001000, 0x04001FFF) // SP_IMEM read/write.
|
||||
{
|
||||
Read = o => BitConverter.ToUInt32(IMEM, (int)o),
|
||||
Write = (o, v) =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* data = &IMEM[(int)o])
|
||||
{
|
||||
*(uint*)data = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
Write = (o, v) => BitHelper.Write(IMEM, (int)o, v)
|
||||
},
|
||||
new MappingEntry(0x04040010, 0x04040013) // SP status.
|
||||
{
|
||||
Read = o => Status,
|
||||
Write = (o, v) => Status = v
|
||||
Read = o => (uint)Status,
|
||||
Write = (o, v) =>
|
||||
{
|
||||
var status = (WriteStatusRegister)v;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearHalt))
|
||||
Status &= ~StatusRegister.Halt;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetHalt))
|
||||
Status |= StatusRegister.Halt;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearBroke))
|
||||
Status &= ~StatusRegister.Broke;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearInterrupt)) { /* TODO. */ }
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetInterrupt)) { /* TODO. */ }
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSingleStep))
|
||||
Status &= ~StatusRegister.SingleStep;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSingleStep))
|
||||
Status |= StatusRegister.SingleStep;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearInterruptOnBreak))
|
||||
Status &= ~StatusRegister.InterruptOnBreak;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetInterruptOnBreak))
|
||||
Status |= StatusRegister.InterruptOnBreak;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal0))
|
||||
Status &= ~StatusRegister.Signal0;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal0))
|
||||
Status |= StatusRegister.Signal0;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal1))
|
||||
Status &= ~StatusRegister.Signal1;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal1))
|
||||
Status |= StatusRegister.Signal1;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal2))
|
||||
Status &= ~StatusRegister.Signal2;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal2))
|
||||
Status |= StatusRegister.Signal2;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal3))
|
||||
Status &= ~StatusRegister.Signal3;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal3))
|
||||
Status |= StatusRegister.Signal3;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal4))
|
||||
Status &= ~StatusRegister.Signal4;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal4))
|
||||
Status |= StatusRegister.Signal4;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal5))
|
||||
Status &= ~StatusRegister.Signal5;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal5))
|
||||
Status |= StatusRegister.Signal5;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal6))
|
||||
Status &= ~StatusRegister.Signal6;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal6))
|
||||
Status |= StatusRegister.Signal6;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.ClearSignal7))
|
||||
Status &= ~StatusRegister.Signal7;
|
||||
|
||||
if (status.HasFlag(WriteStatusRegister.SetSignal7))
|
||||
Status |= StatusRegister.Signal7;
|
||||
}
|
||||
},
|
||||
new MappingEntry(0x04040018, 0x0404001B) // SP DMA busy.
|
||||
{
|
||||
Read = o => DMABusy
|
||||
Read = o => Convert.ToUInt32(DMABusy)
|
||||
},
|
||||
new MappingEntry(0x04000000, 0x04000FFF) // SP_DMEM read/write.
|
||||
{
|
||||
Read = o => BitConverter.ToUInt32(DMEM, (int)o),
|
||||
Write = (o, v) =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* data = &DMEM[(int)o])
|
||||
{
|
||||
*(uint*)data = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
Write = (o, v) => BitHelper.Write(DMEM, (int)o, v)
|
||||
}
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public uint ReadWord(ulong address) => memoryMaps.GetEntry(address).ReadWord(address);
|
||||
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.GetEntry(address).WriteWord(address, value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,13 +6,7 @@ namespace DotN64.RCP
|
|||
{
|
||||
public partial class VideoInterface : Interface
|
||||
{
|
||||
#region Fields
|
||||
private readonly IReadOnlyList<MappingEntry> memoryMaps;
|
||||
#endregion
|
||||
|
||||
#region Properties
|
||||
protected override IReadOnlyList<MappingEntry> MemoryMaps => memoryMaps;
|
||||
|
||||
private ushort verticalInterrupt;
|
||||
/// <summary>
|
||||
/// Interrupt when current half-line = V_INTR.
|
||||
|
@ -45,7 +39,7 @@ namespace DotN64.RCP
|
|||
public VideoInterface(RealityCoprocessor rcp)
|
||||
: base(rcp)
|
||||
{
|
||||
memoryMaps = new[]
|
||||
MemoryMaps = new[]
|
||||
{
|
||||
new MappingEntry(0x0440000C, 0x0440000F) // VI vertical intr.
|
||||
{
|
||||
|
|
Loading…
Reference in New Issue