mirror of
https://github.com/zeldaret/oot.git
synced 2024-11-10 19:20:13 +00:00
Documentation for audio_thread_manager.c (#1562)
* Documentation for audio_thread_manager.c * Fixes * Move AudioTask back to z64audio.h and include in audiomgr.h, adjust bug comment * Adjust AudioMgrDebugLevel enum
This commit is contained in:
parent
f2c06ce441
commit
5ce4670fd1
7 changed files with 118 additions and 44 deletions
33
include/audiomgr.h
Normal file
33
include/audiomgr.h
Normal file
|
@ -0,0 +1,33 @@
|
||||||
|
#ifndef AUDIOMGR_H
|
||||||
|
#define AUDIOMGR_H
|
||||||
|
|
||||||
|
#include "sched.h"
|
||||||
|
#include "z64audio.h"
|
||||||
|
|
||||||
|
typedef enum {
|
||||||
|
/* 0 */ AUDIOMGR_DEBUG_LEVEL_NONE,
|
||||||
|
/* 1 */ AUDIOMGR_DEBUG_LEVEL_NO_RSP,
|
||||||
|
/* 2 */ AUDIOMGR_DEBUG_LEVEL_NO_UPDATE
|
||||||
|
} AudioMgrDebugLevel;
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* 0x0000 */ IrqMgr* irqMgr;
|
||||||
|
/* 0x0004 */ Scheduler* sched;
|
||||||
|
/* 0x0008 */ OSScTask audioTask;
|
||||||
|
/* 0x0070 */ AudioTask* rspTask;
|
||||||
|
/* 0x0074 */ OSMesgQueue interruptQueue;
|
||||||
|
/* 0x008C */ OSMesg interruptMsgBuf[8];
|
||||||
|
/* 0x00AC */ OSMesgQueue taskDoneQueue;
|
||||||
|
/* 0x00C4 */ OSMesg taskDoneMsg;
|
||||||
|
/* 0x00C8 */ OSMesgQueue initQueue;
|
||||||
|
/* 0x00E0 */ OSMesg initMsg;
|
||||||
|
/* 0x00E8 */ OSThread thread;
|
||||||
|
} AudioMgr; // size = 0x298
|
||||||
|
|
||||||
|
void AudioMgr_Init(AudioMgr* audioMgr, void* stack, OSPri pri, OSId id, Scheduler* sched, IrqMgr* irqMgr);
|
||||||
|
|
||||||
|
void AudioMgr_WaitForInit(AudioMgr* audioMgr);
|
||||||
|
|
||||||
|
void AudioMgr_StopAllSfx(void);
|
||||||
|
|
||||||
|
#endif
|
|
@ -1295,13 +1295,6 @@ void func_800C213C(PreRender* this, Gfx** gfxP);
|
||||||
void PreRender_RestoreFramebuffer(PreRender* this, Gfx** gfxP);
|
void PreRender_RestoreFramebuffer(PreRender* this, Gfx** gfxP);
|
||||||
void PreRender_CopyImageRegion(PreRender* this, Gfx** gfxP);
|
void PreRender_CopyImageRegion(PreRender* this, Gfx** gfxP);
|
||||||
void PreRender_ApplyFilters(PreRender* this);
|
void PreRender_ApplyFilters(PreRender* this);
|
||||||
void AudioMgr_StopAllSfx(void);
|
|
||||||
void func_800C3C80(AudioMgr* audioMgr);
|
|
||||||
void AudioMgr_HandleRetrace(AudioMgr* audioMgr);
|
|
||||||
void AudioMgr_HandlePreNMI(AudioMgr* audioMgr);
|
|
||||||
void AudioMgr_ThreadEntry(void* arg0);
|
|
||||||
void AudioMgr_Unlock(AudioMgr* audioMgr);
|
|
||||||
void AudioMgr_Init(AudioMgr* audioMgr, void* stack, OSPri pri, OSId id, Scheduler* sched, IrqMgr* irqMgr);
|
|
||||||
void GameState_FaultPrint(void);
|
void GameState_FaultPrint(void);
|
||||||
void GameState_SetFBFilter(Gfx** gfxP);
|
void GameState_SetFBFilter(Gfx** gfxP);
|
||||||
void GameState_DrawInputDisplay(u16 input, Gfx** gfxP);
|
void GameState_DrawInputDisplay(u16 input, Gfx** gfxP);
|
||||||
|
|
|
@ -48,6 +48,7 @@
|
||||||
#define R_ENV_TIME_SPEED_OLD REG(15) // Most likely used during development. Unused in the final game.
|
#define R_ENV_TIME_SPEED_OLD REG(15) // Most likely used during development. Unused in the final game.
|
||||||
#define R_RUN_SPEED_LIMIT REG(45)
|
#define R_RUN_SPEED_LIMIT REG(45)
|
||||||
#define R_ENABLE_ARENA_DBG SREG(0)
|
#define R_ENABLE_ARENA_DBG SREG(0)
|
||||||
|
#define R_AUDIOMGR_DEBUG_LEVEL SREG(20)
|
||||||
#define R_ROOM_IMAGE_NODRAW_FLAGS SREG(25)
|
#define R_ROOM_IMAGE_NODRAW_FLAGS SREG(25)
|
||||||
#define R_ROOM_BG2D_FORCE_SCALEBG SREG(26)
|
#define R_ROOM_BG2D_FORCE_SCALEBG SREG(26)
|
||||||
#define R_UPDATE_RATE SREG(30)
|
#define R_UPDATE_RATE SREG(30)
|
||||||
|
|
|
@ -4,6 +4,7 @@
|
||||||
#include "ultra64.h"
|
#include "ultra64.h"
|
||||||
#include "ultra64/gs2dex.h"
|
#include "ultra64/gs2dex.h"
|
||||||
#include "attributes.h"
|
#include "attributes.h"
|
||||||
|
#include "audiomgr.h"
|
||||||
#include "z64save.h"
|
#include "z64save.h"
|
||||||
#include "z64light.h"
|
#include "z64light.h"
|
||||||
#include "z64bgcheck.h"
|
#include "z64bgcheck.h"
|
||||||
|
@ -695,20 +696,6 @@ typedef struct {
|
||||||
/* 0x10 */ u8 data[1];
|
/* 0x10 */ u8 data[1];
|
||||||
} Yaz0Header; // size = 0x10 ("data" is not part of the header)
|
} Yaz0Header; // size = 0x10 ("data" is not part of the header)
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
/* 0x0000 */ IrqMgr* irqMgr;
|
|
||||||
/* 0x0004 */ Scheduler* sched;
|
|
||||||
/* 0x0008 */ OSScTask audioTask;
|
|
||||||
/* 0x0070 */ AudioTask* rspTask;
|
|
||||||
/* 0x0074 */ OSMesgQueue interruptQueue;
|
|
||||||
/* 0x008C */ OSMesg interruptMsgBuf[8];
|
|
||||||
/* 0x00AC */ OSMesgQueue taskQueue;
|
|
||||||
/* 0x00C4 */ OSMesg taskMsgBuf[1];
|
|
||||||
/* 0x00C8 */ OSMesgQueue lockQueue;
|
|
||||||
/* 0x00E0 */ OSMesg lockMsgBuf[1];
|
|
||||||
/* 0x00E8 */ OSThread thread;
|
|
||||||
} AudioMgr; // size = 0x298
|
|
||||||
|
|
||||||
struct ArenaNode;
|
struct ArenaNode;
|
||||||
|
|
||||||
typedef struct Arena {
|
typedef struct Arena {
|
||||||
|
|
|
@ -814,13 +814,6 @@ typedef struct {
|
||||||
/* 0x10 */ AudioTableEntry entries[1]; // (dynamic size)
|
/* 0x10 */ AudioTableEntry entries[1]; // (dynamic size)
|
||||||
} AudioTable; // size >= 0x20
|
} AudioTable; // size >= 0x20
|
||||||
|
|
||||||
typedef struct {
|
|
||||||
/* 0x00 */ OSTask task;
|
|
||||||
/* 0x40 */ OSMesgQueue* msgQueue;
|
|
||||||
/* 0x44 */ void* unk_44; // probably a message that gets unused.
|
|
||||||
/* 0x48 */ char unk_48[0x8];
|
|
||||||
} AudioTask; // size = 0x50
|
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* 0x00 */ u8* ramAddr;
|
/* 0x00 */ u8* ramAddr;
|
||||||
/* 0x04 */ u32 devAddr;
|
/* 0x04 */ u32 devAddr;
|
||||||
|
@ -831,6 +824,13 @@ typedef struct {
|
||||||
/* 0x0E */ u8 ttl; // duration after which the DMA can be discarded
|
/* 0x0E */ u8 ttl; // duration after which the DMA can be discarded
|
||||||
} SampleDma; // size = 0x10
|
} SampleDma; // size = 0x10
|
||||||
|
|
||||||
|
typedef struct {
|
||||||
|
/* 0x00 */ OSTask task;
|
||||||
|
/* 0x40 */ OSMesgQueue* msgQueue;
|
||||||
|
/* 0x44 */ void* unk_44; // probably a message that gets unused.
|
||||||
|
/* 0x48 */ char unk_48[0x8];
|
||||||
|
} AudioTask; // size = 0x50
|
||||||
|
|
||||||
typedef struct {
|
typedef struct {
|
||||||
/* 0x0000 */ char unk_0000;
|
/* 0x0000 */ char unk_0000;
|
||||||
/* 0x0001 */ s8 numSynthesisReverbs;
|
/* 0x0001 */ s8 numSynthesisReverbs;
|
||||||
|
|
|
@ -1,82 +1,134 @@
|
||||||
|
/**
|
||||||
|
* @file audio_thread_manager.c
|
||||||
|
*
|
||||||
|
* This file implements basic thread features for the audio driver. It manages updating the driver on vertical retrace
|
||||||
|
* and sending the audio rsp tasks generated by the driver to the task scheduler.
|
||||||
|
*/
|
||||||
|
|
||||||
#include "global.h"
|
#include "global.h"
|
||||||
|
|
||||||
void func_800C3C80(AudioMgr* audioMgr) {
|
void AudioMgr_NotifyTaskDone(AudioMgr* audioMgr) {
|
||||||
AudioTask* task = audioMgr->rspTask;
|
AudioTask* task = audioMgr->rspTask;
|
||||||
|
|
||||||
|
// If the audio rsp task has a message queue to receive task done notifications, post a message to it.
|
||||||
if (audioMgr->rspTask->msgQueue != NULL) {
|
if (audioMgr->rspTask->msgQueue != NULL) {
|
||||||
osSendMesg(task->msgQueue, NULL, OS_MESG_BLOCK);
|
osSendMesg(task->msgQueue, NULL, OS_MESG_BLOCK);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle retrace event.
|
||||||
|
* Update the audio driver and schedule audio rsp tasks.
|
||||||
|
*/
|
||||||
void AudioMgr_HandleRetrace(AudioMgr* audioMgr) {
|
void AudioMgr_HandleRetrace(AudioMgr* audioMgr) {
|
||||||
AudioTask* rspTask;
|
AudioTask* rspTask;
|
||||||
|
|
||||||
if (SREG(20) > 0) {
|
if (R_AUDIOMGR_DEBUG_LEVEL > AUDIOMGR_DEBUG_LEVEL_NONE) {
|
||||||
|
// Inhibit audio rsp task processing
|
||||||
audioMgr->rspTask = NULL;
|
audioMgr->rspTask = NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (audioMgr->rspTask != NULL) {
|
if (audioMgr->rspTask != NULL) {
|
||||||
|
// Got an rsp task to process, build the OSScTask and forward it to the scheduler to run
|
||||||
|
|
||||||
audioMgr->audioTask.next = NULL;
|
audioMgr->audioTask.next = NULL;
|
||||||
audioMgr->audioTask.flags = OS_SC_NEEDS_RSP;
|
audioMgr->audioTask.flags = OS_SC_NEEDS_RSP;
|
||||||
audioMgr->audioTask.framebuffer = NULL;
|
audioMgr->audioTask.framebuffer = NULL;
|
||||||
|
|
||||||
audioMgr->audioTask.list = audioMgr->rspTask->task;
|
audioMgr->audioTask.list = audioMgr->rspTask->task;
|
||||||
audioMgr->audioTask.msgQueue = &audioMgr->taskQueue;
|
audioMgr->audioTask.msgQueue = &audioMgr->taskDoneQueue;
|
||||||
|
|
||||||
audioMgr->audioTask.msg = NULL;
|
audioMgr->audioTask.msg = NULL;
|
||||||
osSendMesg(&audioMgr->sched->cmdQueue, (OSMesg)&audioMgr->audioTask, OS_MESG_BLOCK);
|
osSendMesg(&audioMgr->sched->cmdQueue, (OSMesg)&audioMgr->audioTask, OS_MESG_BLOCK);
|
||||||
Sched_Notify(audioMgr->sched);
|
Sched_Notify(audioMgr->sched);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Update the audio driver
|
||||||
|
|
||||||
gAudioThreadUpdateTimeStart = osGetTime();
|
gAudioThreadUpdateTimeStart = osGetTime();
|
||||||
if (SREG(20) >= 2) {
|
|
||||||
|
if (R_AUDIOMGR_DEBUG_LEVEL >= AUDIOMGR_DEBUG_LEVEL_NO_UPDATE) {
|
||||||
|
// Skip update, no rsp task produced
|
||||||
rspTask = NULL;
|
rspTask = NULL;
|
||||||
} else {
|
} else {
|
||||||
rspTask = func_800E4FE0();
|
rspTask = func_800E4FE0();
|
||||||
}
|
}
|
||||||
|
|
||||||
gAudioThreadUpdateTimeAcc += osGetTime() - gAudioThreadUpdateTimeStart;
|
gAudioThreadUpdateTimeAcc += osGetTime() - gAudioThreadUpdateTimeStart;
|
||||||
gAudioThreadUpdateTimeStart = 0;
|
gAudioThreadUpdateTimeStart = 0;
|
||||||
|
|
||||||
if (audioMgr->rspTask != NULL) {
|
if (audioMgr->rspTask != NULL) {
|
||||||
osRecvMesg(&audioMgr->taskQueue, NULL, OS_MESG_BLOCK);
|
// Wait for the audio rsp task scheduled on the previous retrace to complete. This looks like it should wait
|
||||||
func_800C3C80(audioMgr);
|
// for the task scheduled on the current retrace, earlier in this function, but since the queue is initially
|
||||||
|
// filled in AudioMgr_Init this osRecvMesg call doesn't wait for the task scheduler to post a message for the
|
||||||
|
// most recent task as there is already a message waiting.
|
||||||
|
osRecvMesg(&audioMgr->taskDoneQueue, NULL, OS_MESG_BLOCK);
|
||||||
|
// Report task done
|
||||||
|
//! @bug As the above osRecvMesg is waiting for the previous task to complete rather than the current task,
|
||||||
|
//! the task done notification is sent to the task done queue for the current task as soon as the previous task
|
||||||
|
//! is completed, without waiting for the current task.
|
||||||
|
//! In practice, task done notifications are not used by the audio driver so this is inconsequential.
|
||||||
|
AudioMgr_NotifyTaskDone(audioMgr);
|
||||||
}
|
}
|
||||||
|
// Update rsp task to be scheduled on next retrace
|
||||||
audioMgr->rspTask = rspTask;
|
audioMgr->rspTask = rspTask;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Handle Pre-NMI event.
|
||||||
|
* Implemented by the audio driver.
|
||||||
|
*
|
||||||
|
* @see Audio_PreNMI
|
||||||
|
*/
|
||||||
void AudioMgr_HandlePreNMI(AudioMgr* audioMgr) {
|
void AudioMgr_HandlePreNMI(AudioMgr* audioMgr) {
|
||||||
// "Audio manager received OS_SC_PRE_NMI_MSG"
|
// "Audio manager received OS_SC_PRE_NMI_MSG"
|
||||||
osSyncPrintf("オーディオマネージャが OS_SC_PRE_NMI_MSG を受け取りました\n");
|
osSyncPrintf("オーディオマネージャが OS_SC_PRE_NMI_MSG を受け取りました\n");
|
||||||
Audio_PreNMI();
|
Audio_PreNMI();
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMgr_ThreadEntry(void* arg0) {
|
void AudioMgr_ThreadEntry(void* arg) {
|
||||||
AudioMgr* audioMgr = (AudioMgr*)arg0;
|
AudioMgr* audioMgr = (AudioMgr*)arg;
|
||||||
IrqMgrClient irqClient;
|
IrqMgrClient irqClient;
|
||||||
s16* msg = NULL;
|
s16* msg = NULL;
|
||||||
|
|
||||||
osSyncPrintf("オーディオマネージャスレッド実行開始\n"); // "Start running audio manager thread"
|
// "Start running audio manager thread"
|
||||||
|
osSyncPrintf("オーディオマネージャスレッド実行開始\n");
|
||||||
|
|
||||||
|
// Initialize audio driver
|
||||||
Audio_Init();
|
Audio_Init();
|
||||||
AudioLoad_SetDmaHandler(DmaMgr_AudioDmaHandler);
|
AudioLoad_SetDmaHandler(DmaMgr_AudioDmaHandler);
|
||||||
Audio_InitSound();
|
Audio_InitSound();
|
||||||
osSendMesg(&audioMgr->lockQueue, NULL, OS_MESG_BLOCK);
|
|
||||||
|
// Fill init queue to signal that the audio driver is initialized
|
||||||
|
osSendMesg(&audioMgr->initQueue, NULL, OS_MESG_BLOCK);
|
||||||
|
|
||||||
IrqMgr_AddClient(audioMgr->irqMgr, &irqClient, &audioMgr->interruptQueue);
|
IrqMgr_AddClient(audioMgr->irqMgr, &irqClient, &audioMgr->interruptQueue);
|
||||||
|
|
||||||
|
// Spin waiting for events
|
||||||
while (true) {
|
while (true) {
|
||||||
osRecvMesg(&audioMgr->interruptQueue, (OSMesg*)&msg, OS_MESG_BLOCK);
|
osRecvMesg(&audioMgr->interruptQueue, (OSMesg*)&msg, OS_MESG_BLOCK);
|
||||||
|
|
||||||
switch (*msg) {
|
switch (*msg) {
|
||||||
case OS_SC_RETRACE_MSG:
|
case OS_SC_RETRACE_MSG:
|
||||||
AudioMgr_HandleRetrace(audioMgr);
|
AudioMgr_HandleRetrace(audioMgr);
|
||||||
|
|
||||||
|
// Empty the interrupt queue
|
||||||
while (!MQ_IS_EMPTY(&audioMgr->interruptQueue)) {
|
while (!MQ_IS_EMPTY(&audioMgr->interruptQueue)) {
|
||||||
osRecvMesg(&audioMgr->interruptQueue, (OSMesg*)&msg, OS_MESG_BLOCK);
|
osRecvMesg(&audioMgr->interruptQueue, (OSMesg*)&msg, OS_MESG_BLOCK);
|
||||||
|
|
||||||
switch (*msg) {
|
switch (*msg) {
|
||||||
case OS_SC_RETRACE_MSG:
|
case OS_SC_RETRACE_MSG:
|
||||||
|
// Don't process a retrace more than once in quick succession
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OS_SC_PRE_NMI_MSG:
|
case OS_SC_PRE_NMI_MSG:
|
||||||
|
// Always handle Pre-NMI
|
||||||
AudioMgr_HandlePreNMI(audioMgr);
|
AudioMgr_HandlePreNMI(audioMgr);
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case OS_SC_PRE_NMI_MSG:
|
case OS_SC_PRE_NMI_MSG:
|
||||||
AudioMgr_HandlePreNMI(audioMgr);
|
AudioMgr_HandlePreNMI(audioMgr);
|
||||||
break;
|
break;
|
||||||
|
@ -84,8 +136,15 @@ void AudioMgr_ThreadEntry(void* arg0) {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMgr_Unlock(AudioMgr* audioMgr) {
|
/**
|
||||||
osRecvMesg(&audioMgr->lockQueue, NULL, OS_MESG_BLOCK);
|
* Stalls the current thread until the audio thread is sufficiently initialized.
|
||||||
|
*
|
||||||
|
* Note this function only works once. After the first call the message that the audio thread posted to the init queue
|
||||||
|
* will have been removed, subsequent calls to this function will block indefinitely as the audio thread does not refill
|
||||||
|
* the queue.
|
||||||
|
*/
|
||||||
|
void AudioMgr_WaitForInit(AudioMgr* audioMgr) {
|
||||||
|
osRecvMesg(&audioMgr->initQueue, NULL, OS_MESG_BLOCK);
|
||||||
}
|
}
|
||||||
|
|
||||||
void AudioMgr_Init(AudioMgr* audioMgr, void* stack, OSPri pri, OSId id, Scheduler* sched, IrqMgr* irqMgr) {
|
void AudioMgr_Init(AudioMgr* audioMgr, void* stack, OSPri pri, OSId id, Scheduler* sched, IrqMgr* irqMgr) {
|
||||||
|
@ -95,11 +154,12 @@ void AudioMgr_Init(AudioMgr* audioMgr, void* stack, OSPri pri, OSId id, Schedule
|
||||||
audioMgr->irqMgr = irqMgr;
|
audioMgr->irqMgr = irqMgr;
|
||||||
audioMgr->rspTask = NULL;
|
audioMgr->rspTask = NULL;
|
||||||
|
|
||||||
osCreateMesgQueue(&audioMgr->taskQueue, audioMgr->taskMsgBuf, ARRAY_COUNT(audioMgr->taskMsgBuf));
|
osCreateMesgQueue(&audioMgr->taskDoneQueue, &audioMgr->taskDoneMsg, 1);
|
||||||
osCreateMesgQueue(&audioMgr->interruptQueue, audioMgr->interruptMsgBuf, ARRAY_COUNT(audioMgr->interruptMsgBuf));
|
osCreateMesgQueue(&audioMgr->interruptQueue, audioMgr->interruptMsgBuf, ARRAY_COUNT(audioMgr->interruptMsgBuf));
|
||||||
osCreateMesgQueue(&audioMgr->lockQueue, audioMgr->lockMsgBuf, ARRAY_COUNT(audioMgr->lockMsgBuf));
|
osCreateMesgQueue(&audioMgr->initQueue, &audioMgr->initMsg, 1);
|
||||||
|
|
||||||
osSendMesg(&audioMgr->taskQueue, NULL, OS_MESG_BLOCK);
|
// Send a message to the task done queue so it is initially full
|
||||||
|
osSendMesg(&audioMgr->taskDoneQueue, NULL, OS_MESG_BLOCK);
|
||||||
|
|
||||||
osCreateThread(&audioMgr->thread, id, AudioMgr_ThreadEntry, audioMgr, stack, pri);
|
osCreateThread(&audioMgr->thread, id, AudioMgr_ThreadEntry, audioMgr, stack, pri);
|
||||||
osStartThread(&audioMgr->thread);
|
osStartThread(&audioMgr->thread);
|
||||||
|
|
|
@ -90,7 +90,7 @@ void Main(void* arg) {
|
||||||
StackCheck_Init(&sPadMgrStackInfo, sPadMgrStack, STACK_TOP(sPadMgrStack), 0, 0x100, "padmgr");
|
StackCheck_Init(&sPadMgrStackInfo, sPadMgrStack, STACK_TOP(sPadMgrStack), 0, 0x100, "padmgr");
|
||||||
PadMgr_Init(&gPadMgr, &sSerialEventQueue, &gIrqMgr, THREAD_ID_PADMGR, THREAD_PRI_PADMGR, STACK_TOP(sPadMgrStack));
|
PadMgr_Init(&gPadMgr, &sSerialEventQueue, &gIrqMgr, THREAD_ID_PADMGR, THREAD_PRI_PADMGR, STACK_TOP(sPadMgrStack));
|
||||||
|
|
||||||
AudioMgr_Unlock(&gAudioMgr);
|
AudioMgr_WaitForInit(&gAudioMgr);
|
||||||
|
|
||||||
StackCheck_Init(&sGraphStackInfo, sGraphStack, STACK_TOP(sGraphStack), 0, 0x100, "graph");
|
StackCheck_Init(&sGraphStackInfo, sGraphStack, STACK_TOP(sGraphStack), 0, 0x100, "graph");
|
||||||
osCreateThread(&sGraphThread, THREAD_ID_GRAPH, Graph_ThreadEntry, arg, STACK_TOP(sGraphStack), THREAD_PRI_GRAPH);
|
osCreateThread(&sGraphThread, THREAD_ID_GRAPH, Graph_ThreadEntry, arg, STACK_TOP(sGraphStack), THREAD_PRI_GRAPH);
|
||||||
|
|
Loading…
Reference in a new issue