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