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 RecvFifo = new CircularBuffer(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; } } } }