------------------------------------------------------------------
		NINTENDO64 TECHNICAL SUPPORT CENTER 
		    NINTENDO64 SAMPLE PROGRAM 1
		Copyright (C) 1997, NINTENDO Co., Ltd.
------------------------------------------------------------------


The NTSC scheduler was specially developed for 'fast3D and 
'fast3D_fifo' microcode.  The NTSC scheduler comprises a main 
thread, an audio thread and a graphics thread.  The functions of 
each of these three threads is described below.

* Main thread
	The scheduler task gets a retrace signal and NMI signal 
	from the system and sends it to the client.

* Audio thread
	The audio task manages execution of a task to sound one 
	frame's worth of audio data created by the audio manager 
	upon receipt of a retrace signal.  The priority of the 
	audio task is set higher than the priority of the graphic 
	task, so it is executed in every frame.  If a graphic 
	task is being executed on the RSP at the time that an 
	audio task is started, the audio thread halts the graphic 
	task with the osTaskYield function and restarts it after 
	the audio task is done.

* Graphics thread
	Manages execution of the graphics task.  The graphics 
	task gets the RSP display list and takes charge of the 
	draw to the frame buffer.


In order to realize these above functions, the order of priority 
of the threads in set as follows:

	scheduler --> audio --> graphics

The thread priority is defined in nnSched.h as follows:

	#define NN_SC_PRI          120  /* scheduler */
	#define NN_SC_AUDIO_PRI    110  /* audio */
	#define NN_SC_GRAPHICS_PRI 100  /* graphics */


------------------------------------------------------------------
nnScEventHandler()  -  Main thread (managing system events)


The main thread receives system event in block mode (line 82) and 
broadcasts system event to every registered client.  Two types of 
events are handled here: a retrace event (line 85) and an NMI 
event (line 88).


75 void
  76 nnScEventHandler(NNSched *sc)
  77 {
  78   OSMesg msg = (OSMesg)0;
  79 
  80   while(1) {
  81     /* receive event  */
  82     osRecvMesg(&sc->retraceMQ, &msg, OS_MESG_BLOCK);
  83 
  84     switch ( (int)msg ) {
  85     case VIDEO_MSG:	/* retrace signal process  */
  86       nnScEventBroadcast( sc, &sc->retraceMsg );
  87       break;
  88     case PRE_NMI_MSG:	/*  NMI signal process */
  89       nnScEventBroadcast( sc, &sc->prenmiMsg );
  90       break;
  91     }
  92   }
  93 }

------------------------------------------------------------------
nnScExecuteAudio()  -   Executing an audio task


In line 164, the audio thread is waiting for a  request for 
execution of an audio task from the application.  When an 
execution request comes, the first action (line 166) is to flush 
all cache lines and update the contents of memory.  The next step 
is to verify that a graphics task is not occupying the RSP.  If a 
graphics task is being executed, then line 174 tells the RSP task 
to yield, and the system waits for an RSP task done message.  The 
received message can have one of two meanings.  Line 178 checks 
whether the graphics task has actually yielded: TRUE means the 
graphics has temporarily halted, FALSE means the graphic task 
process was completed before the task yield process conducted by 
line 174.  Either way, at this point the RSP can now be used, and 
line 183 executes the audio task and line 184 waits for end.   As 
the final process, if a graphics task is yielding, it is 
restarted in line 188.  If  the graphics task was already done 
when the yield process was conducted (yieldFlag = 2), then the 
RSP task done message received in line 175 is  returned to line 
192.  Once all processes are completed, line 194 informs the 
application that the audio task is done and then the thread waits 
for the next request.


 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     /* wait for request to execute audio task */
 164     osRecvMesg(&sc->audioRequestMQ, (OSMesg *)&task, 
		OS_MESG_BLOCK);
 165 
 166     osWritebackDCacheAll();	/* flush cache */
 167   
 168     /* check current RSP status */
 169     yieldFlag = 0;
 170     gfxTask = sc->curGraphicsTask;
 171     if( gfxTask ) {
 172 
 173       /* wait for end (yield) of graphics task */
 174       osSpTaskYield();		/* yield task */
 175       osRecvMesg(&sc->rspMQ, &msg, OS_MESG_BLOCK);
 176 
 177       /* check is task actually yielding */
 178       if (osSpTaskYielded(&gfxTask->list))
 179 	        yieldFlag =1;
 180 	   else yieldFlag =2;
 181     }
 182     /* wait for end of RSP task */
 183     osSpTaskStart(&task->list);        /* execute task */
 184     osRecvMesg(&sc->rspMQ, &msg, OS_MESG_BLOCK);
 185 
 186     /* restart yielding graphics task */
 187     if( yieldFlag == 1) {
 188       osSpTaskStart(&gfxTask->list);    
 189     }
 190     else if( yieldFlag == 2 )
 191       osSendMesg(&sc->rspMQ, &msg, OS_MESG_BLOCK);
 192 
 193     /* inform thread which started audio task that task is 
		done */
 194     osSendMesg(task->msgQ, task->msg, OS_MESG_BLOCK);
 195   }
 196 }

------------------------------------------------------------------
nnScExecuteGraphics() --  Executing a graphics task


In line 210, the graphics thread is waiting for a  request for 
execution of a graphics task from the application.  When an 
execution request comes, line 213 performs wait until frame 
buffer can be used.  Once frame buffer becomes available, the 
graphics task is executed.  Line 219 receives RSP task done 
message, and line 223 receives RDP process done message.


 201 void
 202 nnScExecuteGraphics(NNSched *sc)
 203 {
 204   OSMesg msg = (OSMesg)0;
 205   NNScTask *task;
 206 
 207   while(1) {
 208 
 209     /* wait for request to execute graphics task */
 210     osRecvMesg(&sc->graphicsRequestMQ, (OSMesg *)&task, 
		OS_MESG_BLOCK);
 211 
 212     /* wait for frame buffer to become available for use */
 213     nnScWaitTaskReady(sc, task);
 214 
 215     osSpTaskStart(&task->list);        /*  execute task */
 216     sc->curGraphicsTask = task;
 217 
 218     /*  wait for end of RSP task */
 219     osRecvMesg(&sc->rspMQ, &msg, OS_MESG_BLOCK);
 220     sc->curGraphicsTask = (NNScTask *)0;
 221 
 222     /* wait for end of RDP task */
 223     osRecvMesg(&sc->rdpMQ, &msg, OS_MESG_BLOCK);
 224 
 225     /* invalidate video blackout first time only */
 226     if (sc->firstTime) {
 227       osViBlack(FALSE);
 228       sc->firstTime = 0;
 229     }
 230 
 231     /* specify next frame buffer to display */
 232     if ( task->flags & NN_SC_SWAPBUFFER )
 233       osViSwapBuffer(task->framebuffer);
 234 
 235     /* inform thread which started graphics task that task 
		is done */
 236     osSendMesg(task->msgQ, task->msg, OS_MESG_BLOCK);
 237   }
 238 }


------------------------------------------------------------------
nnScWaitTaskReady() -  Wait for frame buffer to become available 
for use


This function in line 251 checks whether frame buffer is being 
used (either displaying current frame or set to display next 
frame) and, if so, waits in block mode for next update. In order 
to perform this process, line 252 prepares to receive a retrace 
signal as its own client by registering a wait message queue in 
the scheduler.  In line 253 the next retrace signal can be 
received, so in line 254 it deletes itself from the clients.  
Here an idle loop is used so other threads cannot execute.



 243 void
 244 nnScWaitTaskReady(NNSched *sc, NNScTask *task)
 245 {
 246   OSMesg msg = (OSMesg)0;
 247   NNScClient client;
 248   void *fb = task->framebuffer;
 249 
 250   /* If frame buffer is not empty wait until next retrace */
 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 } 