nnsched.txt 7.84 KB
==============================================================================

		NINTENDO64 TECHNICAL SUPPORT CENTER 
		
		    NINTENDO64 SAMPLE PROGRAM 1

		Copyright (C) 1997, NINTENDO Co,Ltd.

==============================================================================

NTSCスケジューラは`fast3D'および`fast3D_fifo'マイクロコードに特化した
スケジューラです。NTSCスケジューラはメインスレッド、オーディオスレッド、
グラフィックススレッドの3つのスレッドから構成されており、次のような機
能を実現しています。

◎メインスレッド
スケジューラタスクはシステムからリトレース信号、NMI信号を受取り、クラ
イアントに転送します。  

◎オーディオスレッド
オーディオタスクはリトレース信号を受信したオーディオマネージャが作成し
た1フレーム分のオーディオデータを鳴らすためのタスクの実行を管理します。
オーディオタスクはグラフィックスタスクより優先順位が高く設定されていま
すので必ず毎フレーム実行されます。もし、オーディオタスクが起動された時
点でRSP上でグラフィックスタスクが実行中であれば、オーディオスレッドは
グラフィックスタスクを`osTaskYield関数'で休止させ、オーディオタスクの
終了後に再開処理を行います。 

◎グラフィックススレッド
グラフィックスタスクの実行の管理を行います。グラフィックスタスクはRSP
ディスプレイリストを受取り、フレームバッファに描画する処理を受け持ちま
す。 

以上の機能を実現するためにスレッドの優先順位を次のように設定しています。

 スケジューラ > オーディオ > グラフィックス

このスレッドの優先順位は`nnSched.h'に次のように定義されています。

  #define NN_SC_PRI          120  /* スケジューラ */
  #define NN_SC_AUDIO_PRI    110  /* オーディオ */
  #define NN_SC_GRAPHICS_PRI 100  /* グラフィックス */


--------------------------------------------------------------------------------
nnScEventHandler() -- メインスレッド(システムイベントの管理)

メインスレッドでは82行目でブロックモードでシステムイベントを受信し、登
録されているすべてのクライアントに対してシステムイベントを配信します。
ここで扱われるイベントは85行目からのリトレースイベントと88行目からの
NMIイベントの2種類です。

  75 void
  76 nnScEventHandler(NNSched *sc)
  77 {
  78   OSMesg msg = (OSMesg)0;
  79 
  80   while(1) {
  81     /* イベントの取得 */
  82     osRecvMesg(&sc->retraceMQ, &msg, OS_MESG_BLOCK);
  83 
  84     switch ( (int)msg ) {
  85     case VIDEO_MSG:		/* リトレース信号の処理 */
  86       nnScEventBroadcast( sc, &sc->retraceMsg );
  87       break;
  88     case PRE_NMI_MSG:		/* NMI信号の処理 */
  89       nnScEventBroadcast( sc, &sc->prenmiMsg );
  90       break;
  91     }
  92   }
  93 }

--------------------------------------------------------------------------------
nnScExecuteAudio() -- オーディオスタスクの実行

オーディオスレッドでは、164行目でアプリケーションからオーディオタスク
の実行要求を待ちます。実行要求を受信した場合、まず最初に166行目ですべ
てのキャッシュラインをフラッシュしてメモリの内容を更新します。次に、既
にグラフィックスタスクがRSPを占有していないかを検査します。
グラフィックスタスクが実行されている場合には174行目でRSPタスクをyield
し、RSPタスクの終了メッセージを待ちます。この場合受信したメッセージに
は2通りの意味があります。178行目の実際にグラフィックスタスクがyieldし
ているかの検査が真の場合はグラフィックスタスクの一時中断を表しており、
偽の場合は174行目のタスクの中断処理より前にグラフィックス処理が完了し
ていたことになります。
以上でRSPが使用可能になるので、183行目でオーディオタスクを実行し184行
目で終了を待っています。最後に後処理としてグラフィックスタスクをyield
している場合188行目でグラフィックスタスクを再開します。yieldの処理をし
たが既にグラフィックスタスクが終了していた場合(yieldFlag==2)、175行目
で受信したRSPタスク終了のメッセージを192行目で戻しています。
すべての処理が終わると194行目でアプリケーションに対してオーディオタス
クが完了したことを通知して次の要求を待ちます。

 153 void
 154 nnScExecuteAudio(NNSched *sc)
 155 {
 156   OSMesg msg = (OSMesg)0;
 157   NNScTask *task = (NNScTask *)0;
 158   NNScTask *gfxTask = (NNScTask *)0;
 159   u32 yieldFlag = 0;
 160 
 161   while(1) {
 162 
 163     /* オーディオの実行要求を待つ。*/
 164     osRecvMesg(&sc->audioRequestMQ, (OSMesg *)&task, OS_MESG_BLOCK);
 165 
 166     osWritebackDCacheAll();	/* キャッシュのフラッシュ */
 167   
 168     /* 現在のRSPステータスの検査。*/
 169     yieldFlag = 0;
 170     gfxTask = sc->curGraphicsTask;
 171     if( gfxTask ) {
 172 
 173       /* グラフィックスタスクの終了(yield)を待つ */
 174       osSpTaskYield();		/* タスクのyield */
 175       osRecvMesg(&sc->rspMQ, &msg, OS_MESG_BLOCK);
 176 
 177       /* 実際にタスクがyieldされたかを検査 */
 178       if (osSpTaskYielded(&gfxTask->list))
 179 	        yieldFlag =1;
 180 	   else yieldFlag =2;
 181     }
 182     /* RSPタスクの終了を待つ。 */
 183     osSpTaskStart(&task->list);        /* タスクの実行。*/
 184     osRecvMesg(&sc->rspMQ, &msg, OS_MESG_BLOCK);
 185 
 186     /* `yield'したグラフィックスタスクの再開 */
 187     if( yieldFlag == 1) {
 188       osSpTaskStart(&gfxTask->list);    
 189     }
 190     else if( yieldFlag == 2 )
 191       osSendMesg(&sc->rspMQ, &msg, OS_MESG_BLOCK);
 192 
 193     /* オーディオタスクを起動したスレッドにタスクの終了を通知 */
 194     osSendMesg(task->msgQ, task->msg, OS_MESG_BLOCK);
 195   }
 196 }

--------------------------------------------------------------------------------
nnScExecuteGraphics() -- グラフィックタスクの実行

グラフィックススレッドでは、210行目でアプリケーションからグラフィック
スタスクの実行要求を待ちます。実行要求を受信した場合、213行目でフレー
ムバッファが使用可能になるまで待機する処理を行います。フレームバッファ
が利用可能になった後、グラフィックスタスクを実行し、219行目でRSPタスク
の終了メッセージ、223行目でRDP処理の終了メッセージを受信します。

 201 void
 202 nnScExecuteGraphics(NNSched *sc)
 203 {
 204   OSMesg msg = (OSMesg)0;
 205   NNScTask *task;
 206 
 207   while(1) {
 208 
 209     /* グラフィックタスクの実行要求を待つ。*/
 210     osRecvMesg(&sc->graphicsRequestMQ, (OSMesg *)&task, OS_MESG_BLOCK);
 211 
 212     /* フレームバッファが利用可能になるのを待つ。 */
 213     nnScWaitTaskReady(sc, task);
 214 
 215     osSpTaskStart(&task->list);        /* タスクの実行。*/
 216     sc->curGraphicsTask = task;
 217 
 218     /* RSPタスクの終了を待つ。 */
 219     osRecvMesg(&sc->rspMQ, &msg, OS_MESG_BLOCK);
 220     sc->curGraphicsTask = (NNScTask *)0;
 221 
 222     /* RDPタスクの終了を待つ。 */
 223     osRecvMesg(&sc->rdpMQ, &msg, OS_MESG_BLOCK);
 224 
 225     /* 初回だけビデオのブラックアウトを無効にする。 */
 226     if (sc->firstTime) {
 227       osViBlack(FALSE);
 228       sc->firstTime = 0;
 229     }
 230 
 231     /* 次に表示されるフレームバッファの指定 */
 232     if ( task->flags & NN_SC_SWAPBUFFER )
 233       osViSwapBuffer(task->framebuffer);
 234 
 235     /* グラフィックスタスクを起動したスレッドにタスクの終了を通知 */
 236     osSendMesg(task->msgQ, task->msg, OS_MESG_BLOCK);
 237   }
 238 }


--------------------------------------------------------------------------------
nnScWaitTaskReady() -- フレームバッファが利用可能になるまで待機

この関数は251行目でフレームバッファが使用中(現在のフレームで表示され
ているか、次のフレームで表示される)であるかを検査し、もし使用中であれ
ば次の更新までブロックモードで待機します。この処理を行うために252行目
で待機用のメッセージキューをスケジューラに登録することで自らクライアン
トとなりリトレース信号をもらう準備をします。253行目で次のリトレース信
号を受信できますので、254行目で自分自身をクライアントから削除していま
す。ここでアイドルループを使用すると他のスレッドが実行できなくなるので
このような手法を使っています。

 243 void
 244 nnScWaitTaskReady(NNSched *sc, NNScTask *task)
 245 {
 246   OSMesg msg = (OSMesg)0;
 247   NNScClient client;
 248   void *fb = task->framebuffer;
 249 
 250   /* フレームバッファが空いていなければ次のリトレースまで待つ。 */
 251   while( osViGetCurrentFramebuffer() == fb || osViGetNextFramebuffer() == fb ) {
 252     nnScAddClient( sc, &client, &sc->waitMQ );  
 253     osRecvMesg( &sc->waitMQ, &msg, OS_MESG_BLOCK );
 254     nnScRemoveClient( sc, &client );
 255   }
 256 }