sm64

A Super Mario 64 decompilation
Log | Files | Refs | README | LICENSE

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 }