nnsched.txt
7.93 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
==============================================================================
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 }