Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
MorsGames
GitHub Repository: MorsGames/sm64plus
Path: blob/master/src/game/main.c
7858 views
1
#include <ultra64.h>
2
#include <stdio.h>
3
4
#include "sm64.h"
5
#include "audio/external.h"
6
#include "game_init.h"
7
#include "memory.h"
8
#include "sound_init.h"
9
#include "profiler.h"
10
#include "buffers/buffers.h"
11
#include "segments.h"
12
#include "main.h"
13
#include "rumble_init.h"
14
15
// Message IDs
16
#define MESG_SP_COMPLETE 100
17
#define MESG_DP_COMPLETE 101
18
#define MESG_VI_VBLANK 102
19
#define MESG_START_GFX_SPTASK 103
20
#define MESG_NMI_REQUEST 104
21
22
OSThread D_80339210; // unused?
23
OSThread gIdleThread;
24
OSThread gMainThread;
25
OSThread gGameLoopThread;
26
OSThread gSoundThread;
27
28
OSIoMesg gDmaIoMesg;
29
OSMesg gMainReceivedMesg;
30
31
OSMesgQueue gDmaMesgQueue;
32
OSMesgQueue gSIEventMesgQueue;
33
OSMesgQueue gPIMesgQueue;
34
OSMesgQueue gIntrMesgQueue;
35
OSMesgQueue gSPTaskMesgQueue;
36
37
OSMesg gDmaMesgBuf[1];
38
OSMesg gPIMesgBuf[32];
39
OSMesg gSIEventMesgBuf[1];
40
OSMesg gIntrMesgBuf[16];
41
OSMesg gUnknownMesgBuf[16];
42
43
struct VblankHandler *gVblankHandler1 = NULL;
44
struct VblankHandler *gVblankHandler2 = NULL;
45
struct SPTask *gActiveSPTask = NULL;
46
struct SPTask *sCurrentAudioSPTask = NULL;
47
struct SPTask *sCurrentDisplaySPTask = NULL;
48
struct SPTask *sNextAudioSPTask = NULL;
49
struct SPTask *sNextDisplaySPTask = NULL;
50
s8 sAudioEnabled = TRUE;
51
u32 gNumVblanks = 0;
52
s8 gResetTimer = 0;
53
s8 gNmiResetBarsTimer = 0;
54
s8 gDebugLevelSelect = FALSE;
55
s8 D_8032C650 = 0;
56
57
s8 gShowProfiler = FALSE;
58
s8 gShowDebugText = FALSE;
59
60
// unused
61
void handle_debug_key_sequences(void) {
62
static u16 sProfilerKeySequence[] = {
63
U_JPAD, U_JPAD, D_JPAD, D_JPAD, L_JPAD, R_JPAD, L_JPAD, R_JPAD
64
};
65
static u16 sDebugTextKeySequence[] = { D_JPAD, D_JPAD, U_JPAD, U_JPAD,
66
L_JPAD, R_JPAD, L_JPAD, R_JPAD };
67
static s16 sProfilerKey = 0;
68
static s16 sDebugTextKey = 0;
69
if (gPlayer3Controller->buttonPressed != 0) {
70
if (sProfilerKeySequence[sProfilerKey++] == gPlayer3Controller->buttonPressed) {
71
if (sProfilerKey == ARRAY_COUNT(sProfilerKeySequence)) {
72
sProfilerKey = 0, gShowProfiler ^= 1;
73
}
74
} else {
75
sProfilerKey = 0;
76
}
77
78
if (sDebugTextKeySequence[sDebugTextKey++] == gPlayer3Controller->buttonPressed) {
79
if (sDebugTextKey == ARRAY_COUNT(sDebugTextKeySequence)) {
80
sDebugTextKey = 0, gShowDebugText ^= 1;
81
}
82
} else {
83
sDebugTextKey = 0;
84
}
85
}
86
}
87
88
void unknown_main_func(void) {
89
// uninitialized
90
OSTime time;
91
u32 b;
92
#ifdef AVOID_UB
93
time = 0;
94
b = 0;
95
#endif
96
97
osSetTime(time);
98
osMapTLB(0, b, NULL, 0, 0, 0);
99
osUnmapTLBAll();
100
101
#pragma GCC diagnostic push
102
#pragma GCC diagnostic ignored "-Wnonnull"
103
sprintf(NULL, NULL);
104
#pragma GCC diagnostic pop
105
}
106
107
void stub_main_1(void) {
108
}
109
110
void stub_main_2(void) {
111
}
112
113
void stub_main_3(void) {
114
}
115
116
void setup_mesg_queues(void) {
117
osCreateMesgQueue(&gDmaMesgQueue, gDmaMesgBuf, ARRAY_COUNT(gDmaMesgBuf));
118
osCreateMesgQueue(&gSIEventMesgQueue, gSIEventMesgBuf, ARRAY_COUNT(gSIEventMesgBuf));
119
osSetEventMesg(OS_EVENT_SI, &gSIEventMesgQueue, NULL);
120
121
osCreateMesgQueue(&gSPTaskMesgQueue, gUnknownMesgBuf, ARRAY_COUNT(gUnknownMesgBuf));
122
osCreateMesgQueue(&gIntrMesgQueue, gIntrMesgBuf, ARRAY_COUNT(gIntrMesgBuf));
123
osViSetEvent(&gIntrMesgQueue, (OSMesg) MESG_VI_VBLANK, 1);
124
125
osSetEventMesg(OS_EVENT_SP, &gIntrMesgQueue, (OSMesg) MESG_SP_COMPLETE);
126
osSetEventMesg(OS_EVENT_DP, &gIntrMesgQueue, (OSMesg) MESG_DP_COMPLETE);
127
osSetEventMesg(OS_EVENT_PRENMI, &gIntrMesgQueue, (OSMesg) MESG_NMI_REQUEST);
128
}
129
130
void alloc_pool(void) {
131
void *start = (void *) SEG_POOL_START;
132
void *end = (void *) SEG_POOL_END;
133
134
main_pool_init(start, end);
135
gEffectsMemoryPool = mem_pool_init(0x4000, MEMORY_POOL_LEFT);
136
}
137
138
void create_thread(OSThread *thread, OSId id, void (*entry)(void *), void *arg, void *sp, OSPri pri) {
139
thread->next = NULL;
140
thread->queue = NULL;
141
osCreateThread(thread, id, entry, arg, sp, pri);
142
}
143
144
#ifdef VERSION_SH
145
extern void func_sh_802f69cc(void);
146
#endif
147
148
void handle_nmi_request(void) {
149
gResetTimer = 1;
150
gNmiResetBarsTimer = 0;
151
stop_sounds_in_continuous_banks();
152
sound_banks_disable(SEQ_PLAYER_SFX, SOUND_BANKS_BACKGROUND);
153
fadeout_music(90);
154
#ifdef VERSION_SH
155
func_sh_802f69cc();
156
#endif
157
}
158
159
void receive_new_tasks(void) {
160
struct SPTask *spTask;
161
162
while (osRecvMesg(&gSPTaskMesgQueue, (OSMesg *) &spTask, OS_MESG_NOBLOCK) != -1) {
163
spTask->state = SPTASK_STATE_NOT_STARTED;
164
switch (spTask->task.t.type) {
165
case 2:
166
sNextAudioSPTask = spTask;
167
break;
168
case 1:
169
sNextDisplaySPTask = spTask;
170
break;
171
}
172
}
173
174
if (sCurrentAudioSPTask == NULL && sNextAudioSPTask != NULL) {
175
sCurrentAudioSPTask = sNextAudioSPTask;
176
sNextAudioSPTask = NULL;
177
}
178
179
if (sCurrentDisplaySPTask == NULL && sNextDisplaySPTask != NULL) {
180
sCurrentDisplaySPTask = sNextDisplaySPTask;
181
sNextDisplaySPTask = NULL;
182
}
183
}
184
185
void start_sptask(s32 taskType) {
186
UNUSED s32 pad; // needed to pad the stack
187
188
if (taskType == M_AUDTASK) {
189
gActiveSPTask = sCurrentAudioSPTask;
190
} else {
191
gActiveSPTask = sCurrentDisplaySPTask;
192
}
193
194
osSpTaskLoad(&gActiveSPTask->task);
195
osSpTaskStartGo(&gActiveSPTask->task);
196
gActiveSPTask->state = SPTASK_STATE_RUNNING;
197
}
198
199
void interrupt_gfx_sptask(void) {
200
if (gActiveSPTask->task.t.type == M_GFXTASK) {
201
gActiveSPTask->state = SPTASK_STATE_INTERRUPTED;
202
osSpTaskYield();
203
}
204
}
205
206
void start_gfx_sptask(void) {
207
if (gActiveSPTask == NULL && sCurrentDisplaySPTask != NULL
208
&& sCurrentDisplaySPTask->state == SPTASK_STATE_NOT_STARTED) {
209
profiler_log_gfx_time(TASKS_QUEUED);
210
start_sptask(M_GFXTASK);
211
}
212
}
213
214
void pretend_audio_sptask_done(void) {
215
gActiveSPTask = sCurrentAudioSPTask;
216
gActiveSPTask->state = SPTASK_STATE_RUNNING;
217
osSendMesg(&gIntrMesgQueue, (OSMesg) MESG_SP_COMPLETE, OS_MESG_NOBLOCK);
218
}
219
220
void handle_vblank(void) {
221
UNUSED s32 pad; // needed to pad the stack
222
223
stub_main_3();
224
gNumVblanks++;
225
#ifdef VERSION_SH
226
if (gResetTimer > 0 && gResetTimer < 100) {
227
gResetTimer++;
228
}
229
#else
230
if (gResetTimer > 0) {
231
gResetTimer++;
232
}
233
#endif
234
235
receive_new_tasks();
236
237
// First try to kick off an audio task. If the gfx task is currently
238
// running, we need to asynchronously interrupt it -- handle_sp_complete
239
// will pick up on what we're doing and start the audio task for us.
240
// If there is already an audio task running, there is nothing to do.
241
// If there is no audio task available, try a gfx task instead.
242
if (sCurrentAudioSPTask != NULL) {
243
if (gActiveSPTask != NULL) {
244
interrupt_gfx_sptask();
245
} else {
246
profiler_log_vblank_time();
247
if (sAudioEnabled) {
248
start_sptask(M_AUDTASK);
249
} else {
250
pretend_audio_sptask_done();
251
}
252
}
253
} else {
254
if (gActiveSPTask == NULL && sCurrentDisplaySPTask != NULL
255
&& sCurrentDisplaySPTask->state != SPTASK_STATE_FINISHED) {
256
profiler_log_gfx_time(TASKS_QUEUED);
257
start_sptask(M_GFXTASK);
258
}
259
}
260
#if ENABLE_RUMBLE
261
rumble_thread_update_vi();
262
#endif
263
264
// Notify the game loop about the vblank.
265
if (gVblankHandler1 != NULL) {
266
osSendMesg(gVblankHandler1->queue, gVblankHandler1->msg, OS_MESG_NOBLOCK);
267
}
268
if (gVblankHandler2 != NULL) {
269
osSendMesg(gVblankHandler2->queue, gVblankHandler2->msg, OS_MESG_NOBLOCK);
270
}
271
}
272
273
void handle_sp_complete(void) {
274
struct SPTask *curSPTask = gActiveSPTask;
275
276
gActiveSPTask = NULL;
277
278
if (curSPTask->state == SPTASK_STATE_INTERRUPTED) {
279
// handle_vblank tried to start an audio task while there was already a
280
// gfx task running, so it had to interrupt the gfx task. That interruption
281
// just finished.
282
if (osSpTaskYielded(&curSPTask->task) == 0) {
283
// The gfx task completed before we had time to interrupt it.
284
// Mark it finished, just like below.
285
curSPTask->state = SPTASK_STATE_FINISHED;
286
profiler_log_gfx_time(RSP_COMPLETE);
287
}
288
289
// Start the audio task, as expected by handle_vblank.
290
profiler_log_vblank_time();
291
if (sAudioEnabled) {
292
start_sptask(M_AUDTASK);
293
} else {
294
pretend_audio_sptask_done();
295
}
296
} else {
297
curSPTask->state = SPTASK_STATE_FINISHED;
298
if (curSPTask->task.t.type == M_AUDTASK) {
299
// After audio tasks come gfx tasks.
300
profiler_log_vblank_time();
301
if (sCurrentDisplaySPTask != NULL
302
&& sCurrentDisplaySPTask->state != SPTASK_STATE_FINISHED) {
303
if (sCurrentDisplaySPTask->state != SPTASK_STATE_INTERRUPTED) {
304
profiler_log_gfx_time(TASKS_QUEUED);
305
}
306
start_sptask(M_GFXTASK);
307
}
308
sCurrentAudioSPTask = NULL;
309
if (curSPTask->msgqueue != NULL) {
310
osSendMesg(curSPTask->msgqueue, curSPTask->msg, OS_MESG_NOBLOCK);
311
}
312
} else {
313
// The SP process is done, but there is still a Display Processor notification
314
// that needs to arrive before we can consider the task completely finished and
315
// null out sCurrentDisplaySPTask. That happens in handle_dp_complete.
316
profiler_log_gfx_time(RSP_COMPLETE);
317
}
318
}
319
}
320
321
void handle_dp_complete(void) {
322
// Gfx SP task is completely done.
323
if (sCurrentDisplaySPTask->msgqueue != NULL) {
324
osSendMesg(sCurrentDisplaySPTask->msgqueue, sCurrentDisplaySPTask->msg, OS_MESG_NOBLOCK);
325
}
326
profiler_log_gfx_time(RDP_COMPLETE);
327
sCurrentDisplaySPTask->state = SPTASK_STATE_FINISHED_DP;
328
sCurrentDisplaySPTask = NULL;
329
}
330
331
void thread3_main(UNUSED void *arg) {
332
setup_mesg_queues();
333
alloc_pool();
334
load_engine_code_segment();
335
336
create_thread(&gSoundThread, 4, thread4_sound, NULL, gThread4Stack + 0x2000, 20);
337
osStartThread(&gSoundThread);
338
339
create_thread(&gGameLoopThread, 5, thread5_game_loop, NULL, gThread5Stack + 0x2000, 10);
340
osStartThread(&gGameLoopThread);
341
342
while (TRUE) {
343
OSMesg msg;
344
345
osRecvMesg(&gIntrMesgQueue, &msg, OS_MESG_BLOCK);
346
switch ((uintptr_t) msg) {
347
case MESG_VI_VBLANK:
348
handle_vblank();
349
break;
350
case MESG_SP_COMPLETE:
351
handle_sp_complete();
352
break;
353
case MESG_DP_COMPLETE:
354
handle_dp_complete();
355
break;
356
case MESG_START_GFX_SPTASK:
357
start_gfx_sptask();
358
break;
359
case MESG_NMI_REQUEST:
360
handle_nmi_request();
361
break;
362
}
363
stub_main_2();
364
}
365
}
366
367
void set_vblank_handler(s32 index, struct VblankHandler *handler, OSMesgQueue *queue, OSMesg *msg) {
368
handler->queue = queue;
369
handler->msg = msg;
370
371
switch (index) {
372
case 1:
373
gVblankHandler1 = handler;
374
break;
375
case 2:
376
gVblankHandler2 = handler;
377
break;
378
}
379
}
380
381
void send_sp_task_message(OSMesg *msg) {
382
osWritebackDCacheAll();
383
osSendMesg(&gSPTaskMesgQueue, msg, OS_MESG_NOBLOCK);
384
}
385
386
void dispatch_audio_sptask(struct SPTask *spTask) {
387
if (sAudioEnabled && spTask != NULL) {
388
osWritebackDCacheAll();
389
osSendMesg(&gSPTaskMesgQueue, spTask, OS_MESG_NOBLOCK);
390
}
391
}
392
393
void exec_display_list(struct SPTask *spTask) {
394
if (spTask != NULL) {
395
osWritebackDCacheAll();
396
spTask->state = SPTASK_STATE_NOT_STARTED;
397
if (sCurrentDisplaySPTask == NULL) {
398
sCurrentDisplaySPTask = spTask;
399
sNextDisplaySPTask = NULL;
400
osSendMesg(&gIntrMesgQueue, (OSMesg) MESG_START_GFX_SPTASK, OS_MESG_NOBLOCK);
401
} else {
402
sNextDisplaySPTask = spTask;
403
}
404
}
405
}
406
407
void turn_on_audio(void) {
408
sAudioEnabled = TRUE;
409
}
410
411
void turn_off_audio(void) {
412
sAudioEnabled = FALSE;
413
while (sCurrentAudioSPTask != NULL) {
414
;
415
}
416
}
417
418
/**
419
* Initialize hardware, start main thread, then idle.
420
*/
421
void thread1_idle(UNUSED void *arg) {
422
#if defined(VERSION_US) || defined(VERSION_SH)
423
s32 sp24 = osTvType;
424
#endif
425
426
osCreateViManager(OS_PRIORITY_VIMGR);
427
#if defined(VERSION_US) || defined(VERSION_SH)
428
if (sp24 == TV_TYPE_NTSC) {
429
osViSetMode(&osViModeTable[OS_VI_NTSC_LAN1]);
430
} else {
431
osViSetMode(&osViModeTable[OS_VI_PAL_LAN1]);
432
}
433
#elif defined(VERSION_JP)
434
osViSetMode(&osViModeTable[OS_VI_NTSC_LAN1]);
435
#else // VERSION_EU
436
osViSetMode(&osViModeTable[OS_VI_PAL_LAN1]);
437
#endif
438
osViBlack(TRUE);
439
osViSetSpecialFeatures(OS_VI_DITHER_FILTER_ON);
440
osViSetSpecialFeatures(OS_VI_GAMMA_OFF);
441
osCreatePiManager(OS_PRIORITY_PIMGR, &gPIMesgQueue, gPIMesgBuf, ARRAY_COUNT(gPIMesgBuf));
442
create_thread(&gMainThread, 3, thread3_main, NULL, gThread3Stack + 0x2000, 100);
443
if (D_8032C650 == 0) {
444
osStartThread(&gMainThread);
445
}
446
osSetThreadPri(NULL, 0);
447
448
// halt
449
while (TRUE) {
450
;
451
}
452
}
453
454
void main_func(void) {
455
UNUSED u8 pad[64]; // needed to pad the stack
456
457
osInitialize();
458
stub_main_1();
459
create_thread(&gIdleThread, 1, thread1_idle, NULL, gIdleThreadStack + 0x800, 100);
460
osStartThread(&gIdleThread);
461
}
462
463