mirror of
https://github.com/zeldaret/mm.git
synced 2026-08-06 01:48:09 -04:00
Irqmgr + scheduler Docs (#1527)
* irqmgr docs * Sched_FlushTaskQueue * scheduler docs
This commit is contained in:
+2
-2
@@ -2,9 +2,11 @@
|
||||
#include "buffers.h"
|
||||
#include "irqmgr.h"
|
||||
#include "main.h"
|
||||
#include "segment_symbols.h"
|
||||
#include "stack.h"
|
||||
#include "stackcheck.h"
|
||||
#include "viconfig.h"
|
||||
#include "z64dma.h"
|
||||
#include "z64thread.h"
|
||||
|
||||
// Variables are put before most headers as a hacky way to bypass bss reordering
|
||||
@@ -19,8 +21,6 @@ OSMesgQueue gPiMgrCmdQueue;
|
||||
OSViMode gViConfigMode;
|
||||
u8 gViConfigModeType;
|
||||
|
||||
#include "global.h"
|
||||
#include "buffers.h"
|
||||
#include "idle.h"
|
||||
|
||||
u8 D_80096B20 = 1;
|
||||
|
||||
+159
-89
@@ -1,47 +1,94 @@
|
||||
#include "global.h"
|
||||
/**
|
||||
* @file irqMgr.c
|
||||
*
|
||||
* This file implements a manager for forwarding three key system interrupt events to
|
||||
* registered clients.
|
||||
* Together with sched.c, these systems implement the libultra video and task scheduling
|
||||
* model from the libultra "sched" module, with improved functionality in the handling of
|
||||
* Pre-NMI related events.
|
||||
*
|
||||
* The interrupts the IRQ manager deals with are:
|
||||
* - Vertical Retrace
|
||||
* This event is sent to the IRQ manager by the OS VI manager which only supports
|
||||
* the forwarding of VI events to a single message queue. The IRQ manager will
|
||||
* forward these events to every registered client. Vertical retrace events are
|
||||
* received when the Video Interface has reached the start of the vertical blanking
|
||||
* interval, happening at approximately 60Hz on NTSC and 50Hz on PAL. Many threads
|
||||
* sit idle until a vertical retrace event wakes them up, at which point they will
|
||||
* perform their task and then return to idle to await the next retrace.
|
||||
*
|
||||
* - Pre-NMI
|
||||
* This event is sent to the IRQ manager by the OS Interrupt Handler when the reset
|
||||
* button on the N64 control deck is pressed. This event is forwarded to clients to
|
||||
* inform them that a reset will occur in at least 0.5s / 500ms so they may begin any
|
||||
* shutdown procedures.
|
||||
*
|
||||
* - NMI
|
||||
* This event is sent at 450ms into the Pre-NMI phase, informing clients that the
|
||||
* mandatory 0.5s of Pre-NMI is almost over and a reset may be imminent. This event
|
||||
* is not to be confused with the hardware NMI interrupt signalled when the CPU is
|
||||
* to fully reset, as by the time that interrupt is received there is no time left
|
||||
* to do anything.
|
||||
*
|
||||
* @see sched.c
|
||||
*/
|
||||
#include "irqmgr.h"
|
||||
|
||||
#include "libc/stdbool.h"
|
||||
#include "macros.h"
|
||||
#include "stackcheck.h"
|
||||
#include "z64thread.h"
|
||||
|
||||
vs32 gIrqMgrResetStatus = 0;
|
||||
vs32 gIrqMgrResetStatus = IRQ_RESET_STATUS_IDLE;
|
||||
volatile OSTime sIrqMgrResetTime = 0;
|
||||
volatile OSTime gIrqMgrRetraceTime = 0;
|
||||
s32 sIrqMgrRetraceCount = 0;
|
||||
|
||||
void IrqMgr_AddClient(IrqMgr* irqmgr, IrqMgrClient* client, OSMesgQueue* msgQueue) {
|
||||
u32 saveMask;
|
||||
// Internal messages
|
||||
#define IRQ_RETRACE_MSG 666
|
||||
#define IRQ_PRENMI_MSG 669
|
||||
#define IRQ_PRENMI450_MSG 671
|
||||
#define IRQ_PRENMI480_MSG 672
|
||||
#define IRQ_PRENMI500_MSG 673
|
||||
|
||||
saveMask = osSetIntMask(1);
|
||||
/**
|
||||
* Registers a client and an associated message queue with the IRQ manager. When an
|
||||
* interrupt event is received by the IRQ manager, these clients will be notified of
|
||||
* the event.
|
||||
*
|
||||
* @param irqMgr the IrqMgr instance to register with.
|
||||
* @param client client to register.
|
||||
* @param msgQueue message queue to send notifications of interrupts to, associated with the client.
|
||||
*/
|
||||
void IrqMgr_AddClient(IrqMgr* irqMgr, IrqMgrClient* client, OSMesgQueue* msgQueue) {
|
||||
u32 saveMask = osSetIntMask(OS_IM_NONE);
|
||||
|
||||
client->queue = msgQueue;
|
||||
client->next = irqmgr->callbacks;
|
||||
irqmgr->callbacks = client;
|
||||
client->next = irqMgr->clients;
|
||||
irqMgr->clients = client;
|
||||
|
||||
osSetIntMask(saveMask);
|
||||
|
||||
if (irqmgr->prenmiStage > 0) {
|
||||
osSendMesg(client->queue, &irqmgr->prenmiMsg.type, OS_MESG_NOBLOCK);
|
||||
if (irqMgr->resetStatus >= IRQ_RESET_STATUS_PRENMI) {
|
||||
osSendMesg(client->queue, (OSMesg)&irqMgr->prenmiMsg, OS_MESG_NOBLOCK);
|
||||
}
|
||||
if (irqmgr->prenmiStage > 1) {
|
||||
osSendMesg(client->queue, &irqmgr->nmiMsg.type, OS_MESG_NOBLOCK);
|
||||
|
||||
if (irqMgr->resetStatus >= IRQ_RESET_STATUS_NMI) {
|
||||
osSendMesg(client->queue, (OSMesg)&irqMgr->nmiMsg, OS_MESG_NOBLOCK);
|
||||
}
|
||||
}
|
||||
|
||||
void IrqMgr_RemoveClient(IrqMgr* irqmgr, IrqMgrClient* remove) {
|
||||
IrqMgrClient* iter;
|
||||
IrqMgrClient* last;
|
||||
u32 saveMask;
|
||||
|
||||
iter = irqmgr->callbacks;
|
||||
last = NULL;
|
||||
|
||||
saveMask = osSetIntMask(1);
|
||||
void IrqMgr_RemoveClient(IrqMgr* irqMgr, IrqMgrClient* client) {
|
||||
IrqMgrClient* iter = irqMgr->clients;
|
||||
IrqMgrClient* last = NULL;
|
||||
u32 saveMask = osSetIntMask(OS_IM_NONE);
|
||||
|
||||
while (iter != NULL) {
|
||||
if (iter == remove) {
|
||||
if (iter == client) {
|
||||
if (last != NULL) {
|
||||
last->next = remove->next;
|
||||
last->next = client->next;
|
||||
} else {
|
||||
irqmgr->callbacks = remove->next;
|
||||
irqMgr->clients = client->next;
|
||||
}
|
||||
break;
|
||||
}
|
||||
@@ -52,119 +99,142 @@ void IrqMgr_RemoveClient(IrqMgr* irqmgr, IrqMgrClient* remove) {
|
||||
osSetIntMask(saveMask);
|
||||
}
|
||||
|
||||
void IrqMgr_SendMesgForClient(IrqMgr* irqmgr, OSMesg msg) {
|
||||
IrqMgrClient* iter = irqmgr->callbacks;
|
||||
/**
|
||||
* Send `msg` to every registered client if the message queue is not full. The message is
|
||||
* appended to the back of the queue.
|
||||
*/
|
||||
void IrqMgr_SendMesgToClients(IrqMgr* irqMgr, OSMesg msg) {
|
||||
IrqMgrClient* client = irqMgr->clients;
|
||||
|
||||
while (iter != NULL) {
|
||||
osSendMesg(iter->queue, msg, OS_MESG_NOBLOCK);
|
||||
iter = iter->next;
|
||||
while (client != NULL) {
|
||||
osSendMesg(client->queue, msg, OS_MESG_NOBLOCK);
|
||||
client = client->next;
|
||||
}
|
||||
}
|
||||
|
||||
void IrqMgr_JamMesgForClient(IrqMgr* irqmgr, OSMesg msg) {
|
||||
IrqMgrClient* iter = irqmgr->callbacks;
|
||||
/**
|
||||
* Send `msg` to every registered client if the message queue is not full. This appears to be for
|
||||
* high-priority messages that should be jammed to the front of the queue, however a bug prevents
|
||||
* this from working in this way and the message is appended to the back of the queue as in
|
||||
* `IrqMgr_SendMesgToClients`.
|
||||
*
|
||||
* @see IrqMgr_SendMesgToClients
|
||||
*/
|
||||
void IrqMgr_JamMesgToClients(IrqMgr* irqMgr, OSMesg msg) {
|
||||
IrqMgrClient* client = irqMgr->clients;
|
||||
|
||||
while (iter != NULL) {
|
||||
if (iter->queue->validCount < iter->queue->msgCount) {
|
||||
osSendMesg(iter->queue, msg, OS_MESG_NOBLOCK);
|
||||
while (client != NULL) {
|
||||
if (!MQ_IS_FULL(client->queue)) {
|
||||
//! @bug The function's name suggests this would use osJamMesg rather than osSendMesg, using the
|
||||
//! latter makes this function no different than IrqMgr_SendMesgToClients.
|
||||
osSendMesg(client->queue, msg, OS_MESG_NOBLOCK);
|
||||
}
|
||||
iter = iter->next;
|
||||
client = client->next;
|
||||
}
|
||||
}
|
||||
|
||||
void IrqMgr_HandlePreNMI(IrqMgr* irqmgr) {
|
||||
gIrqMgrResetStatus = 1;
|
||||
irqmgr->prenmiStage = 1;
|
||||
/**
|
||||
* Runs when the Pre-NMI OS Event is received. This indicates that the console will reset in at least
|
||||
* 0.5s / 500ms. Updates the reset status and time before forwarding the Pre-NMI message to registered
|
||||
* clients so they may begin shutting down in advance of the reset.
|
||||
*/
|
||||
void IrqMgr_HandlePreNMI(IrqMgr* irqMgr) {
|
||||
gIrqMgrResetStatus = IRQ_RESET_STATUS_PRENMI;
|
||||
irqMgr->resetStatus = IRQ_RESET_STATUS_PRENMI;
|
||||
|
||||
sIrqMgrResetTime = irqmgr->lastPrenmiTime = osGetTime();
|
||||
sIrqMgrResetTime = irqMgr->resetTime = osGetTime();
|
||||
|
||||
// Wait .45 seconds then generate a stage 2 prenmi interrupt
|
||||
osSetTimer(&irqmgr->prenmiTimer, OS_USEC_TO_CYCLES(450000), 0, &irqmgr->irqQueue, (OSMesg)0x29F);
|
||||
osSetTimer(&irqMgr->timer, OS_MSEC_TO_CYCLES(450), 0, &irqMgr->queue, (OSMesg)IRQ_PRENMI450_MSG);
|
||||
|
||||
IrqMgr_JamMesgForClient(irqmgr, &irqmgr->prenmiMsg.type);
|
||||
IrqMgr_JamMesgToClients(irqMgr, (OSMesg)&irqMgr->prenmiMsg);
|
||||
}
|
||||
|
||||
void IrqMgr_CheckStack(void) {
|
||||
void IrqMgr_CheckStacks(void) {
|
||||
StackCheck_Check(NULL);
|
||||
}
|
||||
|
||||
void IrqMgr_HandlePRENMI450(IrqMgr* irqmgr) {
|
||||
gIrqMgrResetStatus = 2;
|
||||
irqmgr->prenmiStage = 2;
|
||||
void IrqMgr_HandlePRENMI450(IrqMgr* irqMgr) {
|
||||
gIrqMgrResetStatus = IRQ_RESET_STATUS_NMI;
|
||||
irqMgr->resetStatus = IRQ_RESET_STATUS_NMI;
|
||||
|
||||
// Wait .03 seconds then generate a stage 3 prenmi interrupt
|
||||
osSetTimer(&irqmgr->prenmiTimer, OS_USEC_TO_CYCLES(30000), 0, &irqmgr->irqQueue, (OSMesg)0x2A0);
|
||||
osSetTimer(&irqMgr->timer, OS_MSEC_TO_CYCLES(30), 0, &irqMgr->queue, (OSMesg)IRQ_PRENMI480_MSG);
|
||||
|
||||
IrqMgr_SendMesgForClient(irqmgr, &irqmgr->nmiMsg.type);
|
||||
IrqMgr_SendMesgToClients(irqMgr, (OSMesg)&irqMgr->nmiMsg);
|
||||
}
|
||||
|
||||
void IrqMgr_HandlePRENMI480(IrqMgr* irqmgr) {
|
||||
void IrqMgr_HandlePRENMI480(IrqMgr* irqMgr) {
|
||||
// Wait .52 seconds. After this we will have waited an entire second
|
||||
osSetTimer(&irqmgr->prenmiTimer, OS_USEC_TO_CYCLES(520000), 0, &irqmgr->irqQueue, (OSMesg)0x2A1);
|
||||
osSetTimer(&irqMgr->timer, OS_MSEC_TO_CYCLES(520), 0, &irqMgr->queue, (OSMesg)IRQ_PRENMI500_MSG);
|
||||
|
||||
osAfterPreNMI();
|
||||
}
|
||||
|
||||
void IrqMgr_HandlePRENMI500(IrqMgr* irqmgr) {
|
||||
IrqMgr_CheckStack();
|
||||
void IrqMgr_HandlePRENMI500(IrqMgr* irqMgr) {
|
||||
IrqMgr_CheckStacks();
|
||||
}
|
||||
void IrqMgr_HandleRetrace(IrqMgr* irqmgr) {
|
||||
|
||||
void IrqMgr_HandleRetrace(IrqMgr* irqMgr) {
|
||||
if (gIrqMgrRetraceTime == 0) {
|
||||
if (irqmgr->lastFrameTime == 0) {
|
||||
irqmgr->lastFrameTime = osGetTime();
|
||||
if (irqMgr->retraceTime == 0) {
|
||||
irqMgr->retraceTime = osGetTime();
|
||||
} else {
|
||||
gIrqMgrRetraceTime = osGetTime() - irqmgr->lastFrameTime;
|
||||
gIrqMgrRetraceTime = osGetTime() - irqMgr->retraceTime;
|
||||
}
|
||||
}
|
||||
|
||||
sIrqMgrRetraceCount += 1;
|
||||
IrqMgr_SendMesgForClient(irqmgr, irqmgr);
|
||||
sIrqMgrRetraceCount++;
|
||||
IrqMgr_SendMesgToClients(irqMgr, (OSMesg)&irqMgr->retraceMsg);
|
||||
}
|
||||
|
||||
void IrqMgr_ThreadEntry(IrqMgr* irqmgr) {
|
||||
u32 interrupt;
|
||||
u32 stop;
|
||||
void IrqMgr_ThreadEntry(void* arg) {
|
||||
s32 msg = 0;
|
||||
IrqMgr* irqMgr = (IrqMgr*)arg;
|
||||
s32 exit = false;
|
||||
|
||||
interrupt = 0;
|
||||
stop = 0;
|
||||
while (stop == 0) {
|
||||
if (stop) {
|
||||
;
|
||||
}
|
||||
while (!exit) {
|
||||
osRecvMesg(&irqMgr->queue, (OSMesg*)&msg, OS_MESG_BLOCK);
|
||||
|
||||
osRecvMesg(&irqmgr->irqQueue, (OSMesg*)&interrupt, OS_MESG_BLOCK);
|
||||
switch (interrupt) {
|
||||
case 0x29A:
|
||||
IrqMgr_HandleRetrace(irqmgr);
|
||||
switch (msg) {
|
||||
case IRQ_RETRACE_MSG:
|
||||
IrqMgr_HandleRetrace(irqMgr);
|
||||
break;
|
||||
case 0x29D:
|
||||
IrqMgr_HandlePreNMI(irqmgr);
|
||||
|
||||
case IRQ_PRENMI_MSG:
|
||||
IrqMgr_HandlePreNMI(irqMgr);
|
||||
break;
|
||||
case 0x29F:
|
||||
IrqMgr_HandlePRENMI450(irqmgr);
|
||||
|
||||
case IRQ_PRENMI450_MSG:
|
||||
IrqMgr_HandlePRENMI450(irqMgr);
|
||||
break;
|
||||
case 0x2A0:
|
||||
IrqMgr_HandlePRENMI480(irqmgr);
|
||||
|
||||
case IRQ_PRENMI480_MSG:
|
||||
IrqMgr_HandlePRENMI480(irqMgr);
|
||||
break;
|
||||
case 0x2A1:
|
||||
IrqMgr_HandlePRENMI500(irqmgr);
|
||||
|
||||
case IRQ_PRENMI500_MSG:
|
||||
IrqMgr_HandlePRENMI500(irqMgr);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void IrqMgr_Init(IrqMgr* irqmgr, void* stack, OSPri pri, u8 retraceCount) {
|
||||
irqmgr->callbacks = NULL;
|
||||
irqmgr->verticalRetraceMesg.type = 1;
|
||||
irqmgr->prenmiMsg.type = 4;
|
||||
irqmgr->nmiMsg.type = 3;
|
||||
irqmgr->prenmiStage = 0;
|
||||
irqmgr->lastPrenmiTime = 0;
|
||||
void IrqMgr_Init(IrqMgr* irqMgr, void* stack, OSPri pri, u8 retraceCount) {
|
||||
irqMgr->clients = NULL;
|
||||
irqMgr->retraceMsg.type = OS_SC_RETRACE_MSG;
|
||||
irqMgr->prenmiMsg.type = OS_SC_PRE_NMI_MSG;
|
||||
irqMgr->nmiMsg.type = OS_SC_NMI_MSG;
|
||||
irqMgr->resetStatus = IRQ_RESET_STATUS_IDLE;
|
||||
irqMgr->resetTime = 0;
|
||||
|
||||
osCreateMesgQueue(&irqmgr->irqQueue, (OSMesg*)irqmgr->irqBuffer, ARRAY_COUNT(irqmgr->irqBuffer));
|
||||
osSetEventMesg(OS_EVENT_PRENMI, &irqmgr->irqQueue, (OSMesg)0x29D);
|
||||
osViSetEvent(&irqmgr->irqQueue, (OSMesg)0x29A, retraceCount);
|
||||
osCreateMesgQueue(&irqMgr->queue, irqMgr->msgBuf, ARRAY_COUNT(irqMgr->msgBuf));
|
||||
osSetEventMesg(OS_EVENT_PRENMI, &irqMgr->queue, (OSMesg)IRQ_PRENMI_MSG);
|
||||
osViSetEvent(&irqMgr->queue, (OSMesg)IRQ_RETRACE_MSG, retraceCount);
|
||||
|
||||
osCreateThread(&irqmgr->thread, Z_THREAD_ID_IRQMGR, IrqMgr_ThreadEntry, irqmgr, stack, pri);
|
||||
osStartThread(&irqmgr->thread);
|
||||
osCreateThread(&irqMgr->thread, Z_THREAD_ID_IRQMGR, IrqMgr_ThreadEntry, irqMgr, stack, pri);
|
||||
osStartThread(&irqMgr->thread);
|
||||
}
|
||||
|
||||
@@ -240,7 +240,7 @@ void DmaMgr_ThreadEntry(void* arg) {
|
||||
*/
|
||||
s32 DmaMgr_RequestAsync(DmaRequest* req, void* ram, uintptr_t vrom, size_t size, UNK_TYPE4 unused, OSMesgQueue* queue,
|
||||
OSMesg msg) {
|
||||
if (gIrqMgrResetStatus >= 2) {
|
||||
if (gIrqMgrResetStatus >= IRQ_RESET_STATUS_NMI) {
|
||||
return -2;
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user