GBA.Unity/Assets/emulator/Ipc.cs

207 lines
7.3 KiB
C#

using System;
using static OptimeGBA.Bits;
namespace OptimeGBA
{
public sealed class Ipc
{
Nds Nds;
byte Id;
public Ipc(Nds nds, byte id)
{
Nds = nds;
Id = id;
}
public CircularBuffer<uint> RecvFifo = new CircularBuffer<uint>(16, 0);
public uint LastSendValue;
public uint LastRecvValue;
public bool SendFifoEmptyIrqLevel;
public bool RecvFifoPendingIrqLevel;
public byte IpcSyncDataOut;
// IPCSYNC
public bool EnableRemoteIrq;
// IPCFIFOCNT
public bool EnableSendFifoEmptyIrq;
public bool EnableRecvFifoPendingIrq;
public bool FifoError;
public bool EnableFifos;
public byte ReadHwio8(uint addr)
{
byte val = 0;
switch (addr)
{
case 0x4000180: // IPCSYNC B0
val |= GetRemote().IpcSyncDataOut;
break;
case 0x4000181: // IPCSYNC B1
val |= IpcSyncDataOut;
if (EnableRemoteIrq) val = BitSet(val, 14 - 8);
break;
case 0x4000184: // IPCFIFOCNT B0
if (GetRemote().RecvFifo.Entries == 0) val = BitSet(val, 0); // Send FIFO empty
if (GetRemote().RecvFifo.Entries == 16) val = BitSet(val, 1); // Send FIFO full
if (EnableSendFifoEmptyIrq) val = BitSet(val, 2);
CheckSendFifoEmptyIrq("IPCFIFOCNT bit enable");
break;
case 0x4000185: // IPCFIFOCNT B1
if (RecvFifo.Entries == 0) val = BitSet(val, 0); // Receive FIFO empty
if (RecvFifo.Entries == 16) val = BitSet(val, 1); // Receive FIFO full
if (EnableRecvFifoPendingIrq) val = BitSet(val, 2);
CheckRecvFifoPendingIrq("IPCFIFOCNT bit enable");
if (FifoError) val = BitSet(val, 6);
if (EnableFifos) val = BitSet(val, 7);
break;
case 0x4100000: // IPCFIFORECV B0
if (RecvFifo.Entries > 0)
{
if (EnableFifos)
{
LastRecvValue = RecvFifo.Pop();
GetRemote().CheckSendFifoEmptyIrq("remote pop");
}
}
else
{
FifoError = true;
}
val = GetByteIn(LastRecvValue, addr & 3);
break;
case 0x4100001: // IPCFIFORECV B1
case 0x4100002: // IPCFIFORECV B2
case 0x4100003: // IPCFIFORECV B3
val = GetByteIn(LastRecvValue, addr & 3);
break;
}
return val;
}
public void WriteHwio8(uint addr, byte val)
{
switch (addr)
{
case 0x4000180: // IPCSYNC B0
break;
case 0x4000181: // IPCSYNC B1
IpcSyncDataOut = (byte)(val & 0xF);
// send IRQ to remote
if (BitTest(val, 13 - 8) && GetRemote().EnableRemoteIrq)
{
// Console.WriteLine($"[{Id}] Sending IRQ");
switch (Id)
{
case 0:
Nds.HwControl7.FlagInterrupt((uint)InterruptNds.IpcSync);
break;
case 1:
Nds.HwControl9.FlagInterrupt((uint)InterruptNds.IpcSync);
break;
}
}
EnableRemoteIrq = BitTest(val, 14 - 8);
break;
case 0x4000184: // IPCFIFOCNT B0
EnableSendFifoEmptyIrq = BitTest(val, 2);
if (BitTest(val, 3))
{
GetRemote().RecvFifo.Reset();
}
break;
case 0x4000185: // IPCFIFOCNT B1
EnableRecvFifoPendingIrq = BitTest(val, 2);
if (BitTest(val, 6))
{
FifoError = false;
}
EnableFifos = BitTest(val, 7);
break;
case 0x4000188: // IPCFIFOSEND B0
case 0x4000189: // IPCFIFOSEND B1
case 0x400018A: // IPCFIFOSEND B2
LastSendValue = SetByteIn(LastSendValue, val, addr & 3);
break;
case 0x400018B: // IPCFIFOSEND B3
LastSendValue = SetByteIn(LastSendValue, val, addr & 3);
if (EnableFifos)
{
GetRemote().RecvFifo.Insert(LastSendValue);
bool eligible = true;
// if ((LastSendValue >> 28) == 0x8) eligible = false;
// if ((LastSendValue >> 28) == 0x4) eligible = false;
// if ((LastSendValue >> 28) == 0xC) eligible = false;
// // if ((LastSendValue >> 28) == 0x0) eligible = false;
// if (eligible)
// {
// if (Id == 0) Console.WriteLine("ARM9 to ARM7 " + Util.Hex(LastSendValue, 8));
// // else Console.WriteLine("ARM7 to ARM9 " + Util.Hex(LastSendValue, 8));
// }
unsafe
{
GetRemote().CheckRecvFifoPendingIrq("remote insert R15: " + Util.Hex(Nds.Cpu7.R[15], 8));
}
}
break;
}
}
public Ipc GetRemote()
{
return Nds.Ipcs[Id ^ 1];
}
public void CheckSendFifoEmptyIrq(string from)
{
var prev = SendFifoEmptyIrqLevel;
SendFifoEmptyIrqLevel = GetRemote().RecvFifo.Entries == 0 && EnableSendFifoEmptyIrq;
if (!prev && SendFifoEmptyIrqLevel)
{
// Console.WriteLine($"Flagging ARM{(Id == 0 ? 7 : 9)} IPC Send FIFO Empty IRQ from " + from);
FlagSourceInterrupt(InterruptNds.IpcSendFifoEmpty);
}
}
public void CheckRecvFifoPendingIrq(string from)
{
var prev = RecvFifoPendingIrqLevel;
RecvFifoPendingIrqLevel = RecvFifo.Entries > 0 && EnableRecvFifoPendingIrq;
if (!prev && RecvFifoPendingIrqLevel)
{
// Console.WriteLine($"Flagging ARM{(Id == 0 ? 7 : 9)} IPC Recv FIFO Pending Irq from " + from);
FlagSourceInterrupt(InterruptNds.IpcRecvFifoPending);
}
}
public void FlagSourceInterrupt(InterruptNds interrupt)
{
switch (Id)
{
case 0:
Nds.HwControl9.FlagInterrupt((uint)interrupt);
break;
case 1:
Nds.HwControl7.FlagInterrupt((uint)interrupt);
break;
}
}
}
}