summaryrefslogtreecommitdiff
path: root/arm9/lib/src/SND_command.c
diff options
context:
space:
mode:
authorred031000 <rubenru09@aol.com>2020-05-29 18:45:08 +0100
committerred031000 <rubenru09@aol.com>2020-05-29 18:45:08 +0100
commit0d8876b37adebcda571659089dcff343fccdfc8b (patch)
tree0fc796c2d9a711cd8ba6f1919391a57174aa5b3f /arm9/lib/src/SND_command.c
parent83d33f36fcf46dec8ea99b86563a19885be4bd8e (diff)
parentd6fd8d1ed926feebffab4154a84eb70a84083bfc (diff)
Merge branch 'master' of https://github.com/martmists/pokediamond
Diffstat (limited to 'arm9/lib/src/SND_command.c')
-rw-r--r--arm9/lib/src/SND_command.c333
1 files changed, 333 insertions, 0 deletions
diff --git a/arm9/lib/src/SND_command.c b/arm9/lib/src/SND_command.c
new file mode 100644
index 00000000..4a867ce3
--- /dev/null
+++ b/arm9/lib/src/SND_command.c
@@ -0,0 +1,333 @@
+#include "SND_command.h"
+#include "SND_work.h"
+#include "OS_system.h"
+
+#define SND_CMD_WAIT_QUEUE_COUNT 8
+
+static struct SNDCommand sCommandArray[SND_CMD_COUNT];
+static struct SNDSharedWork sSharedWork;
+static struct SNDCommand *sWaitingCommandListQueue[SND_CMD_WAIT_QUEUE_COUNT + 1]; // not sure why this is one element to large
+static struct SNDCommand *sReserveList;
+static struct SNDCommand *sReserveListEnd;
+static struct SNDCommand *sFreeListEnd;
+static s32 sWaitingCommandListQueueRead;
+static s32 sWaitingCommandListQueueWrite;
+static s32 sWaitingCommandListCount;
+static u32 sCurrentTag;
+static u32 sFinishedTag;
+static struct SNDCommand *sFreeList;
+
+// TODO remove these function declarations once they are in the headers
+extern s32 PXI_SendWordByFifo(u32, u32, u32);
+extern void PXI_SetFifoRecvCallback(u32, void (*)(s32, s32));
+extern BOOL PXI_IsCallbackReady(u32, u32);
+
+static void InitPXI(void);
+static void RequestCommandProc(void);
+static struct SNDCommand *AllocCommand(void);
+static BOOL IsCommandAvailable(void);
+
+ARM_FUNC void SND_CommandInit(void) {
+ InitPXI();
+ sFreeList = sCommandArray;
+ for (int i = 0; i < SND_CMD_COUNT - 1; i++) {
+ sCommandArray[i].llNext = &sCommandArray[i+1];
+ }
+ sCommandArray[SND_CMD_COUNT - 1].llNext = NULL;
+ sFreeListEnd = &sCommandArray[SND_CMD_COUNT - 1];
+ sReserveList = NULL;
+ sReserveListEnd = NULL;
+ sWaitingCommandListCount = 0;
+ sWaitingCommandListQueueRead = 0;
+ sWaitingCommandListQueueWrite = 0;
+ sCurrentTag = 1;
+ sFinishedTag = 0;
+ SNDi_SharedWork = &sSharedWork;
+ SNDi_InitSharedWork(SNDi_SharedWork);
+
+ struct SNDCommand *cmd = SND_AllocCommand(SND_CMD_FLAG_BLOCK);
+ if (cmd == NULL)
+ return;
+
+ cmd->id = SND_CMD_SET_SHARED_WORK;
+ cmd->arg[0] = (u32)SNDi_SharedWork;
+ SND_PushCommand(cmd);
+ SND_FlushCommand(SND_CMD_FLAG_BLOCK);
+}
+
+ARM_FUNC const struct SNDCommand *SND_RecvCommandReply(u32 flags) {
+ OSIntrMode oldirq = OS_DisableInterrupts();
+
+ if (flags & SND_CMD_FLAG_BLOCK) {
+ u32 tag = SNDi_GetFinishedCommandTag();
+ while (sFinishedTag == tag) {
+ OS_RestoreInterrupts(oldirq);
+ OS_SpinWait(100);
+ oldirq = OS_DisableInterrupts();
+ tag = SNDi_GetFinishedCommandTag();
+ }
+ } else {
+ u32 tag = SNDi_GetFinishedCommandTag();
+ if (sFinishedTag == tag) {
+ OS_RestoreInterrupts(oldirq);
+ return NULL;
+ }
+ }
+
+ struct SNDCommand *queueRead = sWaitingCommandListQueue[sWaitingCommandListQueueRead];
+
+ if (++sWaitingCommandListQueueRead > SND_CMD_WAIT_QUEUE_COUNT)
+ sWaitingCommandListQueueRead = 0;
+
+ struct SNDCommand *cur = queueRead;
+ while (cur->llNext != NULL)
+ cur = cur->llNext;
+
+ if (sFreeListEnd != NULL) {
+ sFreeListEnd->llNext = queueRead;
+ } else {
+ sFreeList = queueRead;
+ }
+
+ sFreeListEnd = cur;
+ sWaitingCommandListCount--;
+ sFinishedTag++;
+
+ OS_RestoreInterrupts(oldirq);
+ return queueRead;
+}
+
+ARM_FUNC struct SNDCommand *SND_AllocCommand(u32 flags) {
+ struct SNDCommand *cmd;
+ if (!IsCommandAvailable())
+ return NULL;
+
+ cmd = AllocCommand();
+ if (cmd != NULL)
+ return cmd;
+
+ if ((flags & SND_CMD_FLAG_BLOCK) == 0)
+ return NULL;
+
+ if (SND_CountWaitingCommand() > 0) {
+ while (SND_RecvCommandReply(SND_CMD_FLAG_NOBLOCK) != NULL) { }
+
+ cmd = AllocCommand();
+ if (cmd != NULL)
+ return cmd;
+ } else {
+ SND_FlushCommand(SND_CMD_FLAG_BLOCK);
+ }
+
+ RequestCommandProc();
+
+ do {
+ SND_RecvCommandReply(SND_CMD_FLAG_BLOCK);
+ cmd = AllocCommand();
+ } while (cmd == NULL);
+ return cmd;
+}
+
+ARM_FUNC void SND_PushCommand(struct SNDCommand *cmd) {
+ OSIntrMode oldirq = OS_DisableInterrupts();
+
+ struct SNDCommand *newend = cmd;
+ if (sReserveListEnd == NULL) {
+ sReserveList = cmd;
+ sReserveListEnd = cmd;
+ } else {
+ sReserveListEnd->llNext = cmd;
+ sReserveListEnd = cmd;
+ }
+
+ cmd->llNext = NULL;
+
+ OS_RestoreInterrupts(oldirq);
+}
+
+ARM_FUNC BOOL SND_FlushCommand(u32 flags) {
+ OSIntrMode oldirq = OS_DisableInterrupts();
+
+ if (sReserveList == NULL) {
+ OS_RestoreInterrupts(oldirq);
+ return TRUE;
+ }
+
+ if (sWaitingCommandListCount >= SND_CMD_WAIT_QUEUE_COUNT) {
+ if ((flags & SND_CMD_FLAG_BLOCK) == 0) {
+ OS_RestoreInterrupts(oldirq);
+ return FALSE;
+ }
+
+ do {
+ SND_RecvCommandReply(SND_CMD_FLAG_BLOCK);
+ } while (sWaitingCommandListCount >= SND_CMD_WAIT_QUEUE_COUNT);
+ }
+
+ DC_FlushRange(sCommandArray, sizeof(sCommandArray));
+
+ s32 result = PXI_SendWordByFifo(7, (u32)sReserveList, 0);
+ if (result < 0) {
+ if ((flags & SND_CMD_FLAG_BLOCK) == 0) {
+ OS_RestoreInterrupts(oldirq);
+ return FALSE;
+ }
+
+ result = PXI_SendWordByFifo(7, (u32)sReserveList, 0);
+ while (result < 0) {
+ OS_RestoreInterrupts(oldirq);
+ OS_SpinWait(100);
+ oldirq = OS_DisableInterrupts();
+ result = PXI_SendWordByFifo(7, (u32)sReserveList, 0);
+ }
+ }
+
+ if ((flags & SND_CMD_FLAG_IMMEDIATE) != 0) {
+ RequestCommandProc();
+ }
+
+ sWaitingCommandListQueue[sWaitingCommandListQueueWrite] = sReserveList;
+
+ if (++sWaitingCommandListQueueWrite > SND_CMD_WAIT_QUEUE_COUNT) {
+ sWaitingCommandListQueueWrite = 0;
+ }
+
+ sReserveList = NULL;
+ sReserveListEnd = NULL;
+ sWaitingCommandListCount++;
+ sCurrentTag++;
+
+ OS_RestoreInterrupts(oldirq);
+ return TRUE;
+}
+
+ARM_FUNC void SND_WaitForCommandProc(u32 tag) {
+ if (SND_IsFinishedCommandTag(tag))
+ return;
+
+ while (SND_RecvCommandReply(SND_CMD_FLAG_NOBLOCK) != NULL) { }
+
+ if (SND_IsFinishedCommandTag(tag))
+ return;
+
+ RequestCommandProc();
+
+ if (SND_IsFinishedCommandTag(tag))
+ return;
+
+ do {
+ SND_RecvCommandReply(SND_CMD_FLAG_BLOCK);
+ } while (SND_IsFinishedCommandTag(tag) == 0);
+}
+
+ARM_FUNC u32 SND_GetCurrentCommandTag(void) {
+ OSIntrMode oldirq = OS_DisableInterrupts();
+
+ u32 retval;
+ if (sReserveList == NULL)
+ retval = sFinishedTag;
+ else
+ retval = sCurrentTag;
+
+ OS_RestoreInterrupts(oldirq);
+ return retval;
+}
+
+ARM_FUNC BOOL SND_IsFinishedCommandTag(u32 tag) {
+ OSIntrMode oldirq = OS_DisableInterrupts();
+
+ BOOL result;
+ if (tag > sFinishedTag) {
+ if (tag - sFinishedTag < 0x80000000)
+ result = FALSE;
+ else
+ result = TRUE;
+ } else {
+ if (sFinishedTag - tag < 0x80000000)
+ result = TRUE;
+ else
+ result = FALSE;
+ }
+
+ OS_RestoreInterrupts(oldirq);
+ return result;
+}
+
+ARM_FUNC s32 SND_CountFreeCommand(void) {
+ OSIntrMode oldirq = OS_DisableInterrupts();
+
+ s32 count = 0;
+ for (struct SNDCommand *cmd = sFreeList; cmd != NULL; cmd = cmd->llNext)
+ count++;
+
+ OS_RestoreInterrupts(oldirq);
+ return count;
+}
+
+ARM_FUNC s32 SND_CountReservedCommand(void) {
+ OSIntrMode oldirq = OS_DisableInterrupts();
+
+ s32 count = 0;
+ for (struct SNDCommand *cmd = sReserveList; cmd != NULL; cmd = cmd->llNext)
+ count++;
+
+ OS_RestoreInterrupts(oldirq);
+ return count;
+}
+
+ARM_FUNC s32 SND_CountWaitingCommand(void) {
+ return SND_CMD_COUNT - SND_CountFreeCommand() - SND_CountReservedCommand();
+}
+
+ARM_FUNC static void PxiFifoCallback(s32 a, s32 b) {
+ OSIntrMode oldirq = OS_DisableInterrupts();
+ SNDi_CallAlarmHandler(b);
+ OS_RestoreInterrupts(oldirq);
+}
+
+ARM_FUNC static void InitPXI(void) {
+ PXI_SetFifoRecvCallback(7, PxiFifoCallback);
+
+ if (!IsCommandAvailable())
+ return;
+
+ if (PXI_IsCallbackReady(7, 1))
+ return;
+
+ do {
+ OS_SpinWait(100);
+ } while (!PXI_IsCallbackReady(7, 1));
+}
+
+ARM_FUNC static void RequestCommandProc(void) {
+ while (PXI_SendWordByFifo(7, 0, 0) < 0) { }
+}
+
+ARM_FUNC static struct SNDCommand *AllocCommand(void) {
+ OSIntrMode oldirq = OS_DisableInterrupts();
+ if (sFreeList == NULL) {
+ OS_RestoreInterrupts(oldirq);
+ return NULL;
+ }
+
+ struct SNDCommand *retval = sFreeList;
+
+ sFreeList = sFreeList->llNext;
+ if (sFreeList == NULL)
+ sFreeListEnd = NULL;
+ OS_RestoreInterrupts(oldirq);
+ return retval;
+}
+
+ARM_FUNC static BOOL IsCommandAvailable(void) {
+ if (!OS_IsRunOnEmulator())
+ return TRUE;
+
+ OSIntrMode oldirq = OS_DisableInterrupts();
+ // TODO use proper register names here
+ // is this some kind of debug or ensata register?
+ *(vu32 *)0x4FFF200 = 0x10;
+ u32 resp = *(vu32 *)0x4FFF200;
+ OS_RestoreInterrupts(oldirq);
+ return resp != 0;
+}