mysched.c 5.81 KB
/*
 *  NINTENDO64 SAMPLE PROGRAM
 *
 *  FILE : mysched.c
 *
 *  Copyright (C) 1997, NINTENDO Co,Ltd.
 */


#include  <ultra64.h>
#include  "system.h"
#include  "mysched.h"


/*
 *  スレッド用のスタックの確保
 */
u64  myScStack        [MY_SC_STACKSIZE / 8];
u64  myScAudioStack   [MY_SC_STACKSIZE / 8];
u64  myScGraphicsStack[MY_SC_STACKSIZE / 8];


/*
 *  スケジューラの作成
 */
void  myScCreateScheduler(MYSched *sc, u8 videoMode, u8 numFields)
{

  /* 変数の初期化 */
  sc->clientList      = 0;
  sc->firstTime       = 1; 
  sc->retraceMsg      = MY_SC_RETRACE_MSG;
  sc->prenmiMsg       = MY_SC_PRE_NMI_MSG;


  /*
   *  メッセージキューの作成
   */
  /* リトレース */
  osCreateMesgQueue(&sc->retraceMQ, sc->retraceMsgBuf, MY_SC_MAX_MESGS);
  /* RSP の終了 */
  osCreateMesgQueue(&sc->rspMQ, &sc->rspMsgBuf, 1);
  /* RDP の終了 */
  osCreateMesgQueue(&sc->rdpMQ, &sc->rdpMsgBuf, 1);
  /* グラフィックスタスク */
  osCreateMesgQueue(&sc->graphicsRequestMQ, &sc->graphicsRequestBuf, 1);
  /* オーディオタスク */
  osCreateMesgQueue(&sc->audioRequestMQ, &sc->audioRequestBuf, 1);
  /* グラフィックスフレーム */
  osCreateMesgQueue(&sc->graphicsFrameMQ, &sc->graphicsFrameBuf, 1);
  /* オーディオフレーム */
  osCreateMesgQueue(&sc->audioFrameMQ, &sc->audioFrameBuf, 1);


  /*
   *  ビデオモードの設定
   */
  osCreateViManager(OS_PRIORITY_VIMGR);    
  osViSetMode(&osViModeTable[videoMode]);
  osViBlack(TRUE);


  /*
   *  イベントハンドラの登録
   */
  /* リトレース */
  osViSetEvent(&sc->retraceMQ, (OSMesg)VIDEO_MSG, numFields);    
  /* NMI */
  osSetEventMesg(OS_EVENT_PRENMI, &sc->retraceMQ, (OSMesg)PRE_NMI_MSG);   
  /* RSP の終了 */
  osSetEventMesg(OS_EVENT_SP, &sc->rspMQ, (OSMesg)RSP_DONE_MSG);
  /* RDP の終了 */
  osSetEventMesg(OS_EVENT_DP, &sc->rdpMQ, (OSMesg)RDP_DONE_MSG);


  /*
   *  スケジューラサブスレッドの起動
   */
  /* スケジューラ */
  osCreateThread(&sc->schedulerThread, 19, (void(*)(void*))myScEventHandler,
		 (void *)sc, myScStack+MY_SC_STACKSIZE/sizeof(u64),
		 MY_SC_PRI);
  osStartThread(&sc->schedulerThread);
  /* グラフィックスタスクスレッド */
  osCreateThread(&sc->graphicsThread, 18,(void(*)(void*))myScExecuteGraphics,
		 (void *)sc, myScGraphicsStack+MY_SC_STACKSIZE/sizeof(u64),
		 MY_SC_GRAPHICS_PRI);
  osStartThread(&sc->graphicsThread);
  /* オーディオタスクスレッド */
  osCreateThread(&sc->audioThread, 17, (void(*)(void *))myScExecuteAudio,
		 (void *)sc, myScAudioStack+MY_SC_STACKSIZE/sizeof(u64),
		 MY_SC_AUDIO_PRI);
  osStartThread(&sc->audioThread);
}


/*
 *  システムイベントの処理
 */
void  myScEventHandler(MYSched *sc)
{

  OSMesg msg = (OSMesg)0;


  while(1) {

    /*
     *  イベントの取得
     */
    osRecvMesg(&sc->retraceMQ, &msg, OS_MESG_BLOCK);

    switch ( (s32)msg ) {

      /*
       * リトレース
       */
      case VIDEO_MSG:
        myScEventBroadcast( sc, &sc->retraceMsg );
	break;

      /*
       *  NMI
       */
      case PRE_NMI_MSG:
      default:
        myScEventBroadcast( sc, &sc->prenmiMsg );
	break;
    }
  }
}


/*
 *  クライアントにメッセージを転送
 */
void  myScEventBroadcast(MYSched *sc, MYScMsg *msg)
{

  MYScClient *client;
  

  /*
   *  リトレースメッセージをクライアントに通知
   */
  for (client = sc->clientList; client != 0; client = client->next) {
    osSendMesg(client->msgQ, (OSMesg *)msg, OS_MESG_NOBLOCK);
  }
}


/*
 *  クライアントの登録
 */
void  myScAddClient(MYSched *sc, MYScClient *c, OSMesgQueue *msgQ)
{

  OSIntMask mask;


  mask = osSetIntMask(OS_IM_NONE);
  c->msgQ = msgQ;
  c->next = sc->clientList;
  sc->clientList = c;
  osSetIntMask(mask);
}


/*
 *  オーディオタスクの実行
 */
void  myScExecuteAudio(MYSched *sc)
{

  u32       address = 0;
  MYScTask *task = (MYScTask *)0;


  while(1) {

    /*
     *  オーディオの実行要求を待ち
     *
     *  前フレーム、CPU のオーディオ処理修了時、下
     *  のメッセージキューにタスクリストが渡される。
     */
    osRecvMesg(&sc->audioRequestMQ, (OSMesg *)&task, OS_MESG_BLOCK);


    /*
     *  グラフィックスタスクの終了待ち
     *
     *  先にグラフィックスタスクが実行される
     */
    osRecvMesg(&sc->audioFrameMQ, (OSMesg *)&address, OS_MESG_BLOCK);


    /*
     *  オーディオタスクの実行
     *
     *  <仮定>
     *  RSP のグラフィックスタスクは十分な時間を残して、必ず、終了する。
     */

#ifdef TIMER_BAR
    time_aud_rsp_start = osGetCount();
#endif

    osSpTaskStart(&task->list);
    osRecvMesg(&sc->rspMQ, NULL, OS_MESG_BLOCK);

#ifdef TIMER_BAR
    time_aud_rsp_end = osGetCount();
#endif


    /*
     *  RDP の終了待ち
     */
    osRecvMesg(&sc->rdpMQ, NULL, OS_MESG_BLOCK);

#ifdef TIMER_BAR
    time_gfx_rdp_end = osGetCount();
#endif


    /*
     *  初回だけ VI Black を無効にする。
     */
    if (sc->firstTime > 0)
      sc->firstTime --;
    else if(!sc->firstTime){
      osViBlack(FALSE);
      sc->firstTime --;
    }


    /*
     *  次に表示されるフレームバッファの指定
     */
    osViSwapBuffer((void *)address);

  }
}


/*
 *  グラフィックスタスクの実行
 */
void  myScExecuteGraphics(MYSched *sc)
{

  MYScTask *task = (MYScTask *)0;


  while(1) {

    /*
     * グラフィックスタスクの実行要求を待ち
     *
     *  前フレーム、CPU のグラフィックス処理修了時、下
     *  のメッセージキューにタスクリストが渡される。
     */
    osRecvMesg(&sc->graphicsRequestMQ, (OSMesg *)&task, OS_MESG_BLOCK);


    /*
     *  オーディオマネージャからのリトレース待ち
     */
    osRecvMesg(&sc->graphicsFrameMQ, NULL, OS_MESG_NOBLOCK);
    osRecvMesg(&sc->graphicsFrameMQ, NULL, OS_MESG_BLOCK);


    /*
     * グラフィックスタスクの実行
     */

#ifdef TIMER_BAR
    time_gfx_rsp_start = osGetCount();
#endif

    osWritebackDCacheAll();
    osSpTaskStart(&task->list);
    osRecvMesg(&sc->rspMQ, NULL, OS_MESG_BLOCK);

#ifdef TIMER_BAR
    time_gfx_rsp_end = osGetCount();
#endif


    /*
     *  オーディオタスクスレッドに終了を報せる
     */
    osSendMesg(&sc->audioFrameMQ, (OSMesg)task->framebuffer, OS_MESG_BLOCK);

  }
}