nnsched.txt
7.84 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
188
189
190
191
192
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
==============================================================================
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 }