- Reduced memory allocation and CPU usage by not using LINQ, foreach in hot paths and unsafe writes instead of array copies.
- 64-bit addressing in mapping entries instead of down-casting. - Fixed cartridge field length. - Adjusted ops.master
parent
f938e58c2e
commit
9da7b68741
|
@ -1,8 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DotN64.AI
|
||||
{
|
||||
using Extensions;
|
||||
|
||||
public class AudioInterface
|
||||
{
|
||||
#region Fields
|
||||
|
@ -46,9 +47,9 @@ namespace DotN64.AI
|
|||
#endregion
|
||||
|
||||
#region Methods
|
||||
public uint ReadWord(ulong address) => memoryMaps.First(e => e.Contains(address)).ReadWord(address);
|
||||
public uint ReadWord(ulong address) => memoryMaps.GetEntry(address).ReadWord(address);
|
||||
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.First(e => e.Contains(address)).WriteWord(address, value);
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.GetEntry(address).WriteWord(address, value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -6,105 +6,55 @@
|
|||
{
|
||||
public enum RegisterIndex : byte
|
||||
{
|
||||
/// <summary>
|
||||
/// Programmable pointer into TLB array.
|
||||
/// </summary>
|
||||
/// <summary>Programmable pointer into TLB array.</summary>
|
||||
Index = 0,
|
||||
/// <summary>
|
||||
/// Pseudorandom pointer into TLB array (read only).
|
||||
/// </summary>
|
||||
/// <summary>Pseudorandom pointer into TLB array (read only).</summary>
|
||||
Random = 1,
|
||||
/// <summary>
|
||||
/// Low half of TLB entry for even virtual address (VPN).
|
||||
/// </summary>
|
||||
/// <summary>Low half of TLB entry for even virtual address (VPN).</summary>
|
||||
EntryLo0 = 2,
|
||||
/// <summary>
|
||||
/// Low half of TLB entry for odd virtual address (VPN).
|
||||
/// </summary>
|
||||
/// <summary>Low half of TLB entry for odd virtual address (VPN).</summary>
|
||||
EntryLo1 = 3,
|
||||
/// <summary>
|
||||
/// Pointer to kernel virtual page table entry (PTE) in 32-bit mode.
|
||||
/// </summary>
|
||||
/// <summary>Pointer to kernel virtual page table entry (PTE) in 32-bit mode.</summary>
|
||||
Context = 4,
|
||||
/// <summary>
|
||||
/// Page size specification.
|
||||
/// </summary>
|
||||
/// <summary>Page size specification.</summary>
|
||||
PageMask = 5,
|
||||
/// <summary>
|
||||
/// Number of wired TLB entries.
|
||||
/// </summary>
|
||||
/// <summary>Number of wired TLB entries.</summary>
|
||||
Wired = 6,
|
||||
/// <summary>
|
||||
/// Display of virtual address that occurred an error last.
|
||||
/// </summary>
|
||||
/// <summary>Display of virtual address that occurred an error last.</summary>
|
||||
BadVAddr = 8,
|
||||
/// <summary>
|
||||
/// Timer Count.
|
||||
/// </summary>
|
||||
/// <summary>Timer Count.</summary>
|
||||
Count = 9,
|
||||
/// <summary>
|
||||
/// High half of TLB entry (including ASID).
|
||||
/// </summary>
|
||||
/// <summary>High half of TLB entry (including ASID).</summary>
|
||||
EntryHi = 10,
|
||||
/// <summary>
|
||||
/// Timer Compare Value.
|
||||
/// </summary>
|
||||
/// <summary>Timer Compare Value.</summary>
|
||||
Compare = 11,
|
||||
/// <summary>
|
||||
/// Operation status setting.
|
||||
/// </summary>
|
||||
/// <summary>Operation status setting.</summary>
|
||||
Status = 12,
|
||||
/// <summary>
|
||||
/// Display of cause of last exception.
|
||||
/// </summary>
|
||||
/// <summary>Display of cause of last exception.</summary>
|
||||
Cause = 13,
|
||||
/// <summary>
|
||||
/// Exception Program Counter.
|
||||
/// </summary>
|
||||
/// <summary>Exception Program Counter.</summary>
|
||||
EPC = 14,
|
||||
/// <summary>
|
||||
/// Processor Revision Identifier.
|
||||
/// </summary>
|
||||
/// <summary>Processor Revision Identifier.</summary>
|
||||
PRId = 15,
|
||||
/// <summary>
|
||||
/// Memory system mode setting.
|
||||
/// </summary>
|
||||
/// <summary>Memory system mode setting.</summary>
|
||||
Config = 16,
|
||||
/// <summary>
|
||||
/// Load Linked instruction address display.
|
||||
/// </summary>
|
||||
/// <summary>Load Linked instruction address display.</summary>
|
||||
LLAddr = 17,
|
||||
/// <summary>
|
||||
/// Memory reference trap address low bits.
|
||||
/// </summary>
|
||||
/// <summary>Memory reference trap address low bits.</summary>
|
||||
WatchLo = 18,
|
||||
/// <summary>
|
||||
/// Memory reference trap address high bits.
|
||||
/// </summary>
|
||||
/// <summary>Memory reference trap address high bits.</summary>
|
||||
WatchHi = 19,
|
||||
/// <summary>
|
||||
/// Pointer to Kernel virtual PTE table in 64-bit mode.
|
||||
/// </summary>
|
||||
/// <summary>Pointer to Kernel virtual PTE table in 64-bit mode.</summary>
|
||||
XContext = 20,
|
||||
/// <summary>
|
||||
/// Cache parity bits.
|
||||
/// </summary>
|
||||
/// <summary>Cache parity bits.</summary>
|
||||
ParityError = 26,
|
||||
/// <summary>
|
||||
/// Cache Error and Status register.
|
||||
/// </summary>
|
||||
/// <summary>Cache Error and Status register.</summary>
|
||||
CacheError = 27,
|
||||
/// <summary>
|
||||
/// Cache Tag register low.
|
||||
/// </summary>
|
||||
/// <summary>Cache Tag register low.</summary>
|
||||
TagLo = 28,
|
||||
/// <summary>
|
||||
/// Cache Tag register high.
|
||||
/// </summary>
|
||||
/// <summary>Cache Tag register high.</summary>
|
||||
TagHi = 29,
|
||||
/// <summary>
|
||||
/// Error Exception Program Counter.
|
||||
/// </summary>
|
||||
/// <summary>Error Exception Program Counter.</summary>
|
||||
ErrorEPC = 30
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DotN64.CPU
|
||||
{
|
||||
using Extensions;
|
||||
|
||||
public partial class VR4300
|
||||
{
|
||||
public partial class SystemControlUnit
|
||||
|
@ -56,16 +57,7 @@ namespace DotN64.CPU
|
|||
throw new Exception($"Unknown memory map segment for location 0x{address:X}.");
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
var value = address;
|
||||
|
||||
return memoryMaps.First(e => e.Contains(value));
|
||||
}
|
||||
catch (InvalidOperationException e)
|
||||
{
|
||||
throw new Exception($"Unknown physical address: 0x{address:X}.", e);
|
||||
}
|
||||
return memoryMaps.GetEntry(address);
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
|
|
|
@ -4,14 +4,19 @@
|
|||
{
|
||||
public enum GPRIndex : byte
|
||||
{
|
||||
r0,
|
||||
/// <summary>Hardwired to zero.</summary>
|
||||
zero,
|
||||
/// <summary>Assembler temporary (used for pseudo-ops).</summary>
|
||||
at,
|
||||
/// <summary>Function return values.</summary>
|
||||
v0,
|
||||
v1,
|
||||
/// <summary>Function arguments.</summary>
|
||||
a0,
|
||||
a1,
|
||||
a2,
|
||||
a3,
|
||||
/// <summary>Temporaries.</summary>
|
||||
t0,
|
||||
t1,
|
||||
t2,
|
||||
|
@ -20,6 +25,7 @@
|
|||
t5,
|
||||
t6,
|
||||
t7,
|
||||
/// <summary>Saved temporaries.</summary>
|
||||
s0,
|
||||
s1,
|
||||
s2,
|
||||
|
@ -28,13 +34,19 @@
|
|||
s5,
|
||||
s6,
|
||||
s7,
|
||||
/// <summary>Temporaries.</summary>
|
||||
t8,
|
||||
t9,
|
||||
/// <summary>Reserved for kernel.</summary>
|
||||
k0,
|
||||
k1,
|
||||
/// <summary>Global pointer.</summary>
|
||||
gp,
|
||||
/// <summary>Stack pointer.</summary>
|
||||
sp,
|
||||
s8,
|
||||
/// <summary>Frame pointer or saved temporary.</summary>
|
||||
fp,
|
||||
/// <summary>Return address.</summary>
|
||||
ra
|
||||
}
|
||||
}
|
||||
|
|
|
@ -82,38 +82,38 @@ namespace DotN64.CPU
|
|||
[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] = 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] = GPR[i.RS] + (ulong)(short)i.Immediate,
|
||||
[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] = GPR[i.RS] + (ulong)(short)i.Immediate
|
||||
[OpCode.ADDI] = i => GPR[i.RT] = (ulong)(int)(GPR[i.RS] + (ulong)(short)i.Immediate)
|
||||
};
|
||||
specialOperations = new Dictionary<SpecialOpCode, Action<Instruction>>
|
||||
{
|
||||
[SpecialOpCode.ADD] = i => GPR[i.RD] = GPR[i.RS] + GPR[i.RT], // Should we discard the upper word and extend the lower one ?
|
||||
[SpecialOpCode.ADD] = i => GPR[i.RD] = (ulong)((int)GPR[i.RS] + (int)GPR[i.RT]),
|
||||
[SpecialOpCode.JR] = i =>
|
||||
{
|
||||
delaySlot = PC;
|
||||
PC = GPR[i.RS];
|
||||
},
|
||||
[SpecialOpCode.SRL] = i => GPR[i.RD] = (ulong)(int)(GPR[i.RT] >> i.SA),
|
||||
[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 =>
|
||||
{
|
||||
var result = (uint)GPR[i.RS] * (uint)GPR[i.RT];
|
||||
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.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] + GPR[i.RT]),
|
||||
[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))),
|
||||
|
|
|
@ -22,7 +22,7 @@ namespace DotN64
|
|||
(uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(ROM, 0x14))
|
||||
};
|
||||
|
||||
public string ImageName => Encoding.ASCII.GetString(ROM, 0x20, 0x33 - 0x20);
|
||||
public string ImageName => Encoding.ASCII.GetString(ROM, 0x20, 0x34 - 0x20);
|
||||
|
||||
public byte ManufacturerID => ROM[0x3B];
|
||||
|
||||
|
|
|
@ -16,11 +16,13 @@
|
|||
<OutputPath>bin\Debug</OutputPath>
|
||||
<DefineConstants>DEBUG;</DefineConstants>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
|
||||
<Optimize>true</Optimize>
|
||||
<OutputPath>bin\Release</OutputPath>
|
||||
<WarningLevel>4</WarningLevel>
|
||||
<AllowUnsafeBlocks>true</AllowUnsafeBlocks>
|
||||
</PropertyGroup>
|
||||
<ItemGroup>
|
||||
<Reference Include="System" />
|
||||
|
@ -55,9 +57,9 @@
|
|||
<Compile Include="MI\MIPSInterface.cs" />
|
||||
<Compile Include="CPU\VR4300.RegImmOpCode.cs" />
|
||||
<Compile Include="PI\PeripheralInterface.CICStatus.cs" />
|
||||
<Compile Include="Extensions\MappingEntryExtensions.cs" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Folder Include="Documentation\" />
|
||||
<Folder Include="CPU\" />
|
||||
<Folder Include="RCP\" />
|
||||
<Folder Include="CPU\CP0\" />
|
||||
|
@ -66,9 +68,7 @@
|
|||
<Folder Include="SI\" />
|
||||
<Folder Include="VI\" />
|
||||
<Folder Include="MI\" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<None Include="Documentation\U10504EJ7V0UMJ1.pdf" />
|
||||
<Folder Include="Extensions\" />
|
||||
</ItemGroup>
|
||||
<Import Project="$(MSBuildBinPath)\Microsoft.CSharp.targets" />
|
||||
</Project>
|
|
@ -0,0 +1,23 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace DotN64.Extensions
|
||||
{
|
||||
public static class MappingEntryExtensions
|
||||
{
|
||||
#region Methods
|
||||
public static MappingEntry GetEntry(this IReadOnlyList<MappingEntry> memoryMaps, ulong address)
|
||||
{
|
||||
for (int i = 0; i < memoryMaps.Count; i++)
|
||||
{
|
||||
var entry = memoryMaps[i];
|
||||
|
||||
if (entry.Contains(address))
|
||||
return entry;
|
||||
}
|
||||
|
||||
throw new Exception($"Unknown physical address: 0x{address:X}.");
|
||||
}
|
||||
#endregion
|
||||
}
|
||||
}
|
|
@ -1,8 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DotN64.MI
|
||||
{
|
||||
using Extensions;
|
||||
|
||||
public class MIPSInterface
|
||||
{
|
||||
#region Fields
|
||||
|
@ -23,9 +24,9 @@ namespace DotN64.MI
|
|||
#endregion
|
||||
|
||||
#region Methods
|
||||
public uint ReadWord(ulong address) => memoryMaps.First(e => e.Contains(address)).ReadWord(address);
|
||||
public uint ReadWord(ulong address) => memoryMaps.GetEntry(address).ReadWord(address);
|
||||
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.First(e => e.Contains(address)).WriteWord(address, value);
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.GetEntry(address).WriteWord(address, value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -5,9 +5,9 @@ namespace DotN64
|
|||
public struct MappingEntry
|
||||
{
|
||||
#region Properties
|
||||
public uint StartAddress { get; set; }
|
||||
public ulong StartAddress { get; set; }
|
||||
|
||||
public uint EndAddress { get; set; }
|
||||
public ulong EndAddress { get; set; }
|
||||
|
||||
public Func<ulong, uint> Read { get; set; }
|
||||
|
||||
|
@ -27,7 +27,7 @@ namespace DotN64
|
|||
#endregion
|
||||
|
||||
#region Methods
|
||||
public bool Contains(ulong address) => (uint)address >= StartAddress && (uint)address <= EndAddress;
|
||||
public bool Contains(ulong address) => address >= StartAddress && address <= EndAddress;
|
||||
|
||||
public uint ReadWord(ulong address) => Read(OffsetAddress ? address - StartAddress : address);
|
||||
|
||||
|
|
|
@ -28,8 +28,6 @@ namespace DotN64
|
|||
|
||||
public MIPSInterface MI { get; } = new MIPSInterface();
|
||||
|
||||
public byte[] RAM { get; } = new byte[4 * 1024 * 1024]; // 4 MB of base memory (excludes the expansion pack).
|
||||
|
||||
public Cartridge Cartridge { get; set; }
|
||||
#endregion
|
||||
|
||||
|
@ -123,9 +121,9 @@ namespace DotN64
|
|||
CPU.CP0.Map(ref address).WriteWord(address, writes[i, 1]);
|
||||
}
|
||||
|
||||
for (int i = 0; i < 0x1000; i += sizeof(uint)) // Copying the bootstrap code from the cartridge to the RSP's DMEM.
|
||||
for (int i = 0x40; i < 0x1000; i += sizeof(uint)) // Copying the bootstrap code from the cartridge to the RSP's DMEM.
|
||||
{
|
||||
var dmemAddress = (ulong)(0xA4000000 + i);
|
||||
var dmemAddress = 0xFFFFFFFFA4000000 + (uint)i;
|
||||
|
||||
CPU.CP0.Map(ref dmemAddress).WriteWord(dmemAddress, (uint)IPAddress.NetworkToHostOrder(BitConverter.ToInt32(Cartridge.ROM, i)));
|
||||
}
|
||||
|
|
|
@ -1,11 +1,12 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Collections.Specialized;
|
||||
using System.Linq;
|
||||
using System.Net;
|
||||
|
||||
namespace DotN64.PI
|
||||
{
|
||||
using Extensions;
|
||||
|
||||
public partial class PeripheralInterface
|
||||
{
|
||||
#region Fields
|
||||
|
@ -42,7 +43,13 @@ namespace DotN64.PI
|
|||
Read = o => BitConverter.ToUInt32(RAM, (int)o),
|
||||
Write = (o, v) =>
|
||||
{
|
||||
Array.Copy(BitConverter.GetBytes(v), 0, RAM, (int)o, sizeof(uint));
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* data = &RAM[(int)o])
|
||||
{
|
||||
*(uint*)data = 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.
|
||||
|
@ -87,9 +94,9 @@ namespace DotN64.PI
|
|||
|
||||
private void ResetController() { /* TODO: Implement. */ }
|
||||
|
||||
public uint ReadWord(ulong address) => memoryMaps.First(e => e.Contains(address)).ReadWord(address);
|
||||
public uint ReadWord(ulong address) => memoryMaps.GetEntry(address).ReadWord(address);
|
||||
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.First(e => e.Contains(address)).WriteWord(address, value);
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.GetEntry(address).WriteWord(address, value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
using Extensions;
|
||||
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public class DisplayProcessor
|
||||
|
@ -24,10 +25,10 @@ namespace DotN64.RCP
|
|||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public uint ReadWord(ulong address) => memoryMaps.First(e => e.Contains(address)).ReadWord(address);
|
||||
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.First(e => e.Contains(address)).WriteWord(address, value);
|
||||
#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,9 +1,10 @@
|
|||
using System;
|
||||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DotN64.RCP
|
||||
{
|
||||
using Extensions;
|
||||
|
||||
public partial class RealityCoprocessor
|
||||
{
|
||||
public class SignalProcessor
|
||||
|
@ -13,9 +14,9 @@ namespace DotN64.RCP
|
|||
#endregion
|
||||
|
||||
#region Properties
|
||||
public uint StatusRegister { get; set; } = 1;
|
||||
public uint Status { get; set; } = 1;
|
||||
|
||||
public uint DMABusyRegister { get; set; }
|
||||
public uint DMABusy { get; set; }
|
||||
|
||||
public byte[] IMEM { get; } = new byte[0x1000];
|
||||
|
||||
|
@ -30,30 +31,48 @@ namespace DotN64.RCP
|
|||
new MappingEntry(0x04001000, 0x04001FFF) // SP_IMEM read/write.
|
||||
{
|
||||
Read = o => BitConverter.ToUInt32(IMEM, (int)o),
|
||||
Write = (o, v) => Array.Copy(BitConverter.GetBytes(v), 0, IMEM, (int)o, sizeof(uint))
|
||||
Write = (o, v) =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* data = &IMEM[(int)o])
|
||||
{
|
||||
*(uint*)data = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
new MappingEntry(0x04040010, 0x04040013) // SP status.
|
||||
{
|
||||
Read = o => StatusRegister,
|
||||
Write = (o, v) => StatusRegister = v
|
||||
Read = o => Status,
|
||||
Write = (o, v) => Status = v
|
||||
},
|
||||
new MappingEntry(0x04040018, 0x0404001B) // SP DMA busy.
|
||||
{
|
||||
Read = o => DMABusyRegister
|
||||
Read = o => DMABusy
|
||||
},
|
||||
new MappingEntry(0x04000000, 0x04000FFF) // SP_DMEM read/write.
|
||||
{
|
||||
Read = o => BitConverter.ToUInt32(DMEM, (int)o),
|
||||
Write = (o, v) => Array.Copy(BitConverter.GetBytes(v), 0, DMEM, (int)o, sizeof(uint))
|
||||
Write = (o, v) =>
|
||||
{
|
||||
unsafe
|
||||
{
|
||||
fixed (byte* data = &DMEM[(int)o])
|
||||
{
|
||||
*(uint*)data = v;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
};
|
||||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public uint ReadWord(ulong address) => memoryMaps.First(e => e.Contains(address)).ReadWord(address);
|
||||
public uint ReadWord(ulong address) => memoryMaps.GetEntry(address).ReadWord(address);
|
||||
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.First(e => e.Contains(address)).WriteWord(address, value);
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.GetEntry(address).WriteWord(address, value);
|
||||
#endregion
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,8 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DotN64.SI
|
||||
{
|
||||
using Extensions;
|
||||
|
||||
public partial class SerialInterface
|
||||
{
|
||||
#region Fields
|
||||
|
@ -27,10 +28,10 @@ namespace DotN64.SI
|
|||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public uint ReadWord(ulong address) => memoryMaps.First(e => e.Contains(address)).ReadWord(address);
|
||||
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.First(e => e.Contains(address)).WriteWord(address, value);
|
||||
#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,8 +1,9 @@
|
|||
using System.Collections.Generic;
|
||||
using System.Linq;
|
||||
|
||||
namespace DotN64.VI
|
||||
{
|
||||
using Extensions;
|
||||
|
||||
public partial class VideoInterface
|
||||
{
|
||||
#region Fields
|
||||
|
@ -59,10 +60,10 @@ namespace DotN64.VI
|
|||
}
|
||||
#endregion
|
||||
|
||||
#region Methods
|
||||
public uint ReadWord(ulong address) => memoryMaps.First(e => e.Contains(address)).ReadWord(address);
|
||||
|
||||
public void WriteWord(ulong address, uint value) => memoryMaps.First(e => e.Contains(address)).WriteWord(address, value);
|
||||
#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
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue