|
|
1.1 root 1: //
2: // nono
1.1.1.4 root 3: // Copyright (C) 2020 nono project
4: // Licensed under nono-license.txt
1.1 root 5: //
6:
1.1.1.4 root 7: #include "scheduler.h"
8: #include "config.h"
1.1.1.3 root 9: #include "mainapp.h"
1.1 root 10: #include "mfp.h"
1.1.1.4 root 11: #include "mpu680x0.h"
12: #include "mpu88xx0.h"
13: #include "mythread.h"
1.1 root 14: #include "rtc.h"
1.1.1.11! root 15: #include "uimessage.h"
1.1 root 16: #include "vm.h"
17:
18: // VM スケジューラは、仮想時間とイベントを管理する。
19: //
1.1.1.9 root 20: // 仮想時間は電源オン時からの nsec で表し、MPU デバイス (実際はその向こうの
1.1.1.7 root 21: // CPU コア) が管理している。VM 界からは gMPU->GetVirtTime() で取得する。
1.1.1.9 root 22: //
23: // 実時間はストップウォッチ gRealtime を基準とし、こちらも nsec で表す。
24: // このストップウォッチ自体は Scheduler 初期化時から常に単調増加する。
25: // 電源オフ中でも RTC へのパルス供給のために単調増加し続けることに注意。
26: //
27: // 「実経過時間」は、GetRealTime() (中身は gRealtime.Elapsed()) で取得でき
28: // o (たぶん)アプリケーション起動時から、常に増加。
29: // o (たぶん)ホストの時刻変更やサスペンドの影響を受けず、常に増加。
30: // o VM 電源オフ時でも進行する。
31: // o デバッガプロンプトで停止中は進行が停止する。
32: // という性質を持つ。
33: // 「仮想経過時間」は、gMPU->GetVirtTime() で取得でき
34: // o 電源オン時のみ有効。
35: // o (今のところ) MPU の命令境界で増加する
36: // という性質を持つ。
37: //
38: // 同期走行モードにおいては rtime_epoch, vtime_epoch をそれぞれの基準点とし
39: // これらと現在の実経過時間、仮想経過時間の差が同期するよう調整する。
40: // 高速走行モードにおいてはこれらは必要ないので特に参照しない。
41: //
42: // 一定間隔で行うパフォーマンス測定は、前回計測時の実および仮想経過時間を
43: // last_perf_rtime, last_perf_vtime として持っているので、これと現在の
44: // 経過時間との比で求める。同期走行/高速走行の影響は受けない。
1.1 root 45:
46: static void *scheduler_run(void *arg);
47:
1.1.1.3 root 48: std::unique_ptr<Scheduler> gScheduler;
1.1 root 49:
1.1.1.9 root 50: // パフォーマンス測定を行う実時間間隔
51: static constexpr uint64 PERF_INTERVAL = 1000_msec;
52:
53: // 実時間の基準となるストップウォッチ。
54: Stopwatch gRealtime;
55:
56: // 実経過時間 [nsec] を返す
57: static inline uint64 GetRealTime()
58: {
59: return gRealtime.Elapsed();
60: }
61:
62: #define FORMAT_FULL 0
63: #define FORMAT_SEC 1
64:
65: // 経過時間 t を文字列にして返す。
1.1.1.10 root 66: // 文字列長は、t の大きさ(とフォーマット指定)によって以下の通り。
67: //
68: // 0 1 2
69: // 01234567890123456789012345
70: // "0.mmm'uuu'nnn" 10秒未満、または FORMAT_SEC なら13桁
71: // "59.mmm'uuu'nnn" 1分未満なら14桁
72: // " 9:59.mmm'uuu'nnn" 1時間未満なら17桁
73: // " 9:59:59.mmm'uuu'nnn" 24時間未満なら20桁
74: // "999d 23:59:59.mmm'uuu'nnn" 1日以上なら25桁。これが最大幅。
75: //
76: // FORMAT_SEC は1桁秒以下の場合 (実際には1秒未満の場合) に用いる。
77: // FORMAT_FULL は 1000日経過すると桁がずれるけど、それはもういいだろう。
78: //
79: // 10秒未満の場合だけ %2d ではなく %d で1桁切り詰めているが、これは
80: // FORMAT_SEC との互換性のため。その必要のない10分未満と10時間未満は
81: // どちらも %2d で表記し桁数を維持することに努める。
1.1.1.9 root 82: static const std::string
83: TimeToStrF(uint64 t, int format)
84: {
85: char buf[32];
86: char *p;
87: size_t len;
88: int n;
89:
90: uint ns = t % 1000;
91: t /= 1000;
92: uint us = t % 1000;
93: t /= 1000;
94: uint ms = t % 1000;
95: t /= 1000;
96:
97: uint s, m, h, d;
98: if (format == FORMAT_FULL) {
99: s = t % 60;
100: t /= 60;
101: m = t % 60;
102: t /= 60;
103: h = t % 24;
104: t /= 24;
105: d = t;
106: } else {
107: s = t;
108: m = 0;
109: h = 0;
110: d = 0;
111: }
112:
113: p = buf;
114: len = sizeof(buf);
115: if (d) {
116: n = snprintf(p, len, "%3dd %02u:%02u:%02u", d, h, m, s);
117: p += n;
118: len -= n;
119: } else if (h) {
120: n = snprintf(p, len, "%2u:%02u:%02u", h, m, s);
121: p += n;
122: len -= n;
123: } else if (m) {
124: n = snprintf(p, len, "%2u:%02u", m, s);
125: p += n;
126: len -= n;
127: } else {
128: n = snprintf(p, len, "%u", s);
129: p += n;
130: len -= n;
131: }
132: n = snprintf(p, len, ".%03u'%03u'%03u", ms, us, ns);
133: p += n;
134: len -= n;
135:
136: return std::string(buf, p - buf);
137: }
138:
1.1.1.10 root 139: const std::string
1.1.1.9 root 140: TimeToStr(uint64 t)
141: {
142: return TimeToStrF(t, FORMAT_FULL);
143: }
144:
145: static const std::string
146: SecToStr(uint64 t)
147: {
148: return TimeToStrF(t, FORMAT_SEC);
149: }
150:
151: //
152: // スケジューラ
153: //
154:
1.1 root 155: // コンストラクタ
156: Scheduler::Scheduler()
1.1.1.10 root 157: : inherited("Scheduler")
1.1 root 158: {
1.1.1.9 root 159: for (int i = 0; i < perfq.Capacity(); i++) {
160: perfq.Enqueue(1000);
161: }
162:
163: sync_event.func = (DeviceCallback_t)&Scheduler::SyncCallback;
164: sync_event.time = 50_msec;
165: sync_event.SetName("Scheduler Sync");
1.1.1.10 root 166:
167: monitor.func = (MonitorCallback_t)&Scheduler::MonitorUpdate;
168: // サイズは Init で決まる
169: monitor.Regist(ID_MONITOR_SCHEDULER);
1.1 root 170: }
171:
172: // デストラクタ
173: Scheduler::~Scheduler()
174: {
1.1.1.3 root 175: Terminate();
1.1 root 176: }
177:
178: // 初期化
179: bool
180: Scheduler::Init()
181: {
1.1.1.10 root 182: // この時点でイベントの個数は確定しているはず(してなければならない)
183: monitor.SetSize(72, 10 + gEvents.size());
184:
1.1.1.3 root 185: // 起動時引数指定の高速モード
186: SetFullSpeed(gMainApp.fast_mode);
1.1 root 187:
188: // スケジューラ(VM)スレッド起動
1.1.1.3 root 189: pthread_create(&thread, NULL, scheduler_run, NULL);
190: thread_created = true;
1.1 root 191:
192: return true;
193: }
194:
195: // スレッドエントリ関数
196: void *
197: scheduler_run(void *arg)
198: {
1.1.1.4 root 199: PTHREAD_SETNAME("VM");
1.1 root 200:
201: gScheduler->ThreadRun();
202: return NULL;
203: }
204:
205: // スレッドエントリ関数(の実体)
206: void
207: Scheduler::ThreadRun()
208: {
1.1.1.9 root 209: // ここが rtime の基準点。
210: // Stopwatch.Restart() は .Net 由来なので
211: // Restart() は経過時間をリセットしてからタイマーを開始の意。
212: gRealtime.Restart();
213: rtime = 0;
214: next_rtc_rtime = 31.25_msec;
1.1 root 215:
1.1.1.3 root 216: for (;;) {
1.1.1.9 root 217: if (RunOff() == false)
218: return;
219: if (RunOn() == false)
1.1.1.3 root 220: return;
1.1.1.9 root 221: }
222: }
223:
224: // 電源オフ時のスケジューラメインループ。
225: // false ならこのスレッドの終了指示。
226: bool
227: Scheduler::RunOff()
228: {
229: uint32 oldreq;
230:
231: ispower = false;
232:
233: oldreq = -1;
234: for (;;) {
235: // RunOn() と違ってこっちは処理したフラグだけを下ろす。
236:
237: // デバッグ表示も前回から変化した時だけ。
238: uint32 req = atomic_reqflag.load();
239: if (__predict_false(req != oldreq)) {
240: if (loglevel >= 2) {
241: PutlogReqFlag(req);
242: }
243: oldreq = req;
1.1.1.3 root 244: }
1.1.1.9 root 245:
246: // 終了
247: if ((atomic_reqflag & REQ_EXIT)) {
248: return false;
1.1.1.3 root 249: }
1.1.1.9 root 250:
251: if ((atomic_reqflag & REQ_RESTART)) {
252: // 電源オフ中に RESTART は通常来るはずはないが、来てしまったら
253: // メニューから RESTART 操作中に VM が電源オフしてしまったのかも
254: // 知れないので、単に電源を入れる。
255: atomic_reqflag &= ~REQ_RESTART;
256: atomic_reqflag |= REQ_POWER_ON;
257: }
258:
259: // 電源オン指示
260: if ((atomic_reqflag & REQ_POWER_ON)) {
261: // 電源オン要求の場合
262:
263: // 全デバイス電源オン
264: for (const auto& d : gDevices) {
265: if (!d->PowerOn()) {
266: return false;
267: }
268: }
269:
270: // 続いて全デバイスをリセット
271: for (const auto& d : gDevices) {
272: d->ResetHard();
273: }
274:
275: // POWER_ON は処理したので下ろす。他のフラグは触らない。
276: atomic_reqflag &= ~REQ_POWER_ON;
277: // 電源オンは RESET も兼ねる (RESET は RunOn() 内で行う)
278: atomic_reqflag |= REQ_RESET;
279:
280: return true;
281: }
282:
283: // RTC はホスト時間で 32Hz (31.25msec) ごとにカウントアップするが、
284: // usleep() はそこまでの精度がないことと、RunRTC() は前回カウント
285: // アップ時刻からの差で処理しており入力が多少ぶれても構わないので、
286: // 適当にその周期の半分強で sleep してみる。
287: usleep(16_msec / 1_usec);
288:
289: // RTC をホスト時間で進める。
290: // (電源オンするまでは gRealTime は進み続けている)
291: rtime = GetRealTime();
292: RunRTC();
1.1 root 293: }
1.1.1.9 root 294: __unreachable();
295: }
1.1 root 296:
1.1.1.9 root 297: // 電源オン時のスケジューラメインループ。
298: // false ならこのスレッドの終了指示。
299: bool
300: Scheduler::RunOn()
301: {
302: uint64 last_event_vtime; // 直近のイベント発生時刻 (統計用)
303: Statistics<uint64> event_stat;
304: uint64 last_sync_rtime;
305:
306: ispower = true;
307:
308: // パフォーマンス計測は電源オン時のみなので、ここで初期化
309: last_perf_rtime = rtime;
310: last_perf_vtime = gMPU->GetVirtTime();
311: next_perf_rtime = rtime + PERF_INTERVAL;
312: // 最初の集計が行われる起動後1秒までは、この値が表示されるので
313: // インチキして 100% にしておく。
314: perf_counter = 100;
315:
316: last_event_vtime = last_perf_vtime;
317:
318: // 実時間系の初期化
319: rtime = GetRealTime();
320: last_sync_rtime = 0;
321: overslept = 0;
322: sync_event.Start();
1.1 root 323:
324: for (;;) {
1.1.1.9 root 325: vtime = gMPU->GetVirtTime();
326:
327: uint32 req = atomic_reqflag.exchange(0);
1.1.1.8 root 328: if (__predict_false(req != 0)) {
1.1.1.9 root 329:
330: // デバッグ表示
331: if (loglevel >= 2) {
332: PutlogReqFlag(req);
333: }
334:
1.1.1.8 root 335: uint32 newmode = mode;
336:
337: // 動作モード変更要因を一つずつ調べる
338: if ((req & REQ_CPU_MODE)) {
339: // CPU 状態が new_cpumode に変更になった
340: newmode &= ~SCHED_CPU_MASK;
341: newmode |= new_cpumode;
342: req &= ~REQ_CPU_MODE;
343: }
344: if ((req & REQ_USER_MODE)) {
345: // ユーザからの高速/同期モード指示が変更になった
346: newmode &= ~SCHED_SYNC;
347: newmode |= new_usermode;
348: req &= ~REQ_USER_MODE;
349: }
1.1.1.9 root 350: if ((req & REQ_BOOT_MODE)) {
351: // ブートページが変更になった
352: newmode &= ~SCHED_BOOT;
353: newmode |= new_bootmode;
354: req &= ~REQ_BOOT_MODE;
355: }
356: if ((req & REQ_KEY_MODE)) {
357: // キー入力中かどうか
358: newmode &= ~SCHED_KEY;
359: newmode |= new_keymode;
360: req &= ~REQ_KEY_MODE;
361: }
362:
363: // いずれかでも変化していれば..
364: if (mode != newmode) {
365: // 動作モード変更要因を全部調べた結果、変更が必要か。
366: // mode、newmode どちらもゼロ(高速モード)か非ゼロ(通常モード)か
367: // なので、等値比較ではなく二値にしてから比較。
368: if ((bool)mode != (bool)newmode) {
369: // モードが変わるのでここで一旦同期をとる
370: auto over = Sync();
371: if (overslept < over) {
372: overslept = over;
373: }
374:
375: if (newmode == 0) {
376: // 高速モードに
377: putlog(1, "高速モード");
378:
379: // 同期モードを抜けた時刻
380: last_sync_rtime = rtime;
381:
382: // 高速モードでは RAM は直接アクセス
383: ::direct_ram_size = gRAM->GetSize();
384:
385: } else {
386: // 同期モードに
387: putlog(1, "同期モード");
388:
389: // 同期モードでは RAM アクセスはデバイス経由
390: ::direct_ram_size = 0;
391:
392: // 同期用の基準点を設置。
393: // ゲストが STOP 命令による定常状態にいる時は
394: // 割り込みなど短期間だけ高速モードになるのを繰り返す。
395: // スリープ粒度の粗い NetBSD ホストでは同期モードに
396: // 切り替えるたびに基準点を移動していると、スリープ
397: // しすぎてしまうので、短期間だけ高速モードを出入り
398: // した分は基準点を移動せず無視する。
399: if (rtime - last_sync_rtime > overslept) {
400: rtime_epoch = rtime;
401: vtime_epoch = vtime;
402: }
403: }
404: }
405:
406: // ブール比較の後で、全ビットを維持したまま代入。
1.1.1.8 root 407: mode = newmode;
1.1.1.9 root 408: }
1.1.1.8 root 409:
1.1.1.9 root 410: if ((req & REQ_RESTART)) {
411: // 電源オン中に RESTART が来たら電源オンを予約して電源オフ。
1.1.1.11! root 412: // UIMessage::POWEROFF は GUI にアプリケーションを終了させる
! 413: // ためのものなのでここでは呼ばない。
1.1.1.9 root 414: atomic_reqflag |= REQ_POWER_ON;
1.1.1.8 root 415:
1.1.1.9 root 416: // 全デバイス電源オフ
417: for (const auto& d : gDevices) {
418: if (!d->PowerOff()) {
419: return false;
420: }
1.1.1.8 root 421: }
1.1 root 422:
1.1.1.9 root 423: return true;
1.1 root 424: }
425:
426: if ((req & REQ_POWER_OFF)) {
427: // 電源オフ要求の場合
428:
429: // 全デバイス電源オフ
1.1.1.9 root 430: for (const auto& d : gDevices) {
431: if (!d->PowerOff()) {
432: return false;
433: }
434: }
435:
436: // GUI に通知
1.1.1.11! root 437: UIMessage::Post(UIMessage::POWEROFF);
1.1 root 438:
1.1.1.9 root 439: return true;
1.1 root 440: }
441:
1.1.1.6 root 442: if ((req & REQ_RESET)) {
443: // リセット要求の場合
444:
445: // 全デバイスをリセット
1.1.1.9 root 446: for (const auto& d : gDevices) {
447: d->ResetHard();
448: }
1.1.1.6 root 449:
450: req &= ~REQ_RESET;
451: }
452:
1.1 root 453: if ((req & REQ_EXIT)) {
454: // 終了要求
1.1.1.9 root 455: return false;
1.1 root 456: }
457:
1.1.1.9 root 458: // これいるかな?
1.1.1.3 root 459: assertmsg(req == 0, "req=$%x", req);
1.1 root 460: }
461:
1.1.1.9 root 462: // パフォーマンス表示
463: if (__predict_false(rtime >= next_perf_rtime)) {
464: uint64 rperf = rtime - last_perf_rtime;
465: uint64 vperf = vtime - last_perf_vtime;
466:
467: // 百分率で小数以下1桁分までとなるよう整数で計算する
468: perfq.EnqueueForce((uint32)(vperf * 1000 / rperf));
469: // 移動平均を求める (perfq は常に要素数分埋めてある)
470: int ma = 0;
471: int qlen = perfq.Length();
472: for (int i = 0; i < qlen; i++) {
473: auto v = perfq.Peek(i);
474: ma += v;
475: }
476: ma /= qlen;
477: // 最下位を四捨五入して捨てる (整数%になる)
478: ma = (ma + 5) / 10;
479: perf_counter = ma;
480:
481: // 直近1秒の Sync 回数
482: last_sync_count = sync_count;
483: sync_count = 0;
484:
485: // 直近1秒のイベント統計
486: last_event_stat = event_stat;
487: event_stat.Clear();
488:
489: last_perf_rtime = rtime;
490: last_perf_vtime = vtime;
491: next_perf_rtime = rtime + PERF_INTERVAL;
1.1 root 492: }
493:
1.1.1.6 root 494: // イベントリストを調べ現時点から最も早く発生するイベントまでの必要
1.1.1.7 root 495: // 時間を求めて、この時間分だけ MPU を駆動する。
1.1.1.6 root 496: // MPU 実行中にデバイスアクセスなどによってスケジューラにイベントが
497: // 登録されると、その命令の終了とともに処理を打ち切って戻ってくる。
1.1.1.7 root 498:
499: // delta_vtime が今回進める仮想時間。
500: // とりあえず初期値を仮置きしとくけど、イベントは通常 20msec 以内には
501: // 必ず起きるので (例えば LUNA の sysclk は 10 or 16 msec 周期)、
502: // この初期値は実質上限値。20msec 程度より大きければいくらでもよくて
503: // 100msec とかでいいんだけど、二進数で置数しやすいよう
504: // 0x200'0000 (約33.5msec) にしておく。
505: uint64 delta_vtime = 0x200'0000;
1.1 root 506:
507: // イベント
508: for (;;) {
1.1.1.6 root 509: Event *e;
510: // ロック区間
511: {
512: std::lock_guard<std::mutex> lock(evcs);
513:
1.1.1.9 root 514: if (eventq.empty()) {
1.1.1.6 root 515: break;
516: }
1.1.1.9 root 517: e = eventq.top();
1.1.1.7 root 518: if (e->vtime > vtime) {
1.1.1.6 root 519: // まだ時刻に到達していない
1.1.1.7 root 520: delta_vtime = e->vtime - vtime;
1.1.1.6 root 521: break;
522: }
523:
524: // 到達したのでこのイベントをリストから削除
525: e->active = false;
1.1.1.9 root 526: eventq.pop();
1.1 root 527: }
528:
1.1.1.9 root 529: // イベント発生間隔 (統計情報)
530: // t は前回と今回のイベント予定時刻の間隔。
531: uint64 t = e->vtime - last_event_vtime;
532: event_stat.Add(t);
533: last_event_vtime = e->vtime;
534:
1.1 root 535: // コールバック
1.1.1.8 root 536: e->dev->putlogf(3, lstr("イベント '%s' 時刻到達",
537: e->GetName().c_str()));
1.1.1.6 root 538: ((e->dev)->*(e->func))(*e);
1.1 root 539:
540: // 同時刻のイベントがあるかも知れないのでなくなるまで調べる
541: }
1.1.1.6 root 542:
1.1.1.9 root 543: // RTC をホスト時間で進める
544: // (同期イベントによって sleep したかもしれないので、その後がよかろう)
545: RunRTC();
546:
1.1.1.7 root 547: // CPU を駆動
1.1.1.8 root 548: new_cpumode = gMPU->Run(delta_vtime);
1.1.1.7 root 549:
1.1.1.9 root 550: // CPU 側の走行状態が変わったら、このループに通知
551: if (__predict_false(((mode ^ new_cpumode) & SCHED_CPU_MASK))) {
552: atomic_reqflag |= REQ_CPU_MODE;
553: }
554: }
555: __unreachable();
556: }
1.1 root 557:
1.1.1.9 root 558: // 同期イベント
559: void
560: Scheduler::SyncCallback(Event& ev)
561: {
562: Sync();
563:
564: // 次回のイベントを再登録
565: ev.Start();
566: }
1.1 root 567:
1.1.1.9 root 568: // 同期処理
569: // ここで仮想時間を実時間と同期させる。
570: // スリープしすぎた実時間を返す。
571: uint64
572: Scheduler::Sync()
573: {
574: uint64 over = 0;
575:
576: // 実時間をここで更新
577: rtime = GetRealTime();
578:
579: // 統計情報
580: sync_count++;
581:
582: // 同期モードなら..
583: if (mode != 0) {
584: uint64 rspan = rtime - rtime_epoch;
585: uint64 vspan = vtime - vtime_epoch;
586: if (vspan > rspan) {
587: // 仮想時間のほうが進んでいれば、スリープして待つ
588: uint64 diff = vspan - rspan;
589: struct timespec ts;
590: ts.tv_sec = diff / (1000 * 1000 * 1000);
591: ts.tv_nsec = diff % (1000 * 1000 * 1000);
592: nanosleep(&ts, NULL);
593:
594: // スリープしすぎた時間を計測。
595: // この時間にはスリープ時間を計算するための時間も含む。
596: uint64 prev = rtime;
597: rtime = GetRealTime();
598: uint64 realslept = rtime - prev;
599: if (realslept > diff) {
600: over = realslept - diff;
1.1 root 601: }
1.1.1.9 root 602: } else {
603: // 実時間のほうが進んでいれば、間に合ってない
1.1 root 604:
1.1.1.9 root 605: // XXX ここで間引き運転とか
1.1 root 606: }
607: }
1.1.1.9 root 608: return over;
609: }
610:
611: // RTC は常にホスト時刻で動いており
612: // 32Hz = 31.25msec ごとにパルスを入れる。
613: // VM の電源がオフでも実行する。
614: void
615: Scheduler::RunRTC()
616: {
617: while (rtime >= next_rtc_rtime) {
618: gRTC->ClockIn();
619: next_rtc_rtime += 31.25_msec;
620: }
1.1 root 621: }
622:
1.1.1.3 root 623: // 必要ならスレッドの終了を指示して完了まで待つ。
1.1 root 624: void
625: Scheduler::Terminate()
626: {
1.1.1.3 root 627: if (thread_created) {
1.1.1.8 root 628: RequestExit();
1.1.1.10 root 629: Wait();
630: }
631: }
632:
633: // スレッドの完了まで待つ。
634: void
635: Scheduler::Wait()
636: {
637: if (thread_created) {
1.1.1.3 root 638: pthread_join(thread, NULL);
639: thread_created = false;
640: }
1.1 root 641: }
642:
1.1.1.8 root 643: // スレッド終了を指示する。
644: void
645: Scheduler::RequestExit()
646: {
647: atomic_reqflag |= REQ_EXIT;
648: }
649:
1.1 root 650: // 動作モードを設定する。
651: // true なら高速モード、false なら同期モード。
652: void
653: Scheduler::SetFullSpeed(bool enable)
654: {
655: if (enable) {
1.1.1.8 root 656: new_usermode = 0;
1.1 root 657: } else {
1.1.1.8 root 658: new_usermode = SCHED_SYNC;
1.1 root 659: }
1.1.1.8 root 660: atomic_reqflag |= REQ_USER_MODE;
1.1 root 661: }
662:
1.1.1.9 root 663: // ブートページモードを設定する。
664: // 戻り値は現在の動作モードがブートモード(ROM)なら true。
665: // (VM::SwitchBootPage から呼ぶので個別には呼ばないこと)
666: void
667: Scheduler::RequestBootPageMode(bool isrom)
668: {
669: if (isrom) {
670: new_bootmode = SCHED_BOOT;
671: } else {
672: new_bootmode = 0;
673: }
674: atomic_reqflag |= REQ_BOOT_MODE;
675: }
676:
677: // キー入力中かどうかを設定する。
678: // true ならキー入力中、false なら入力中でない。
679: void
680: Scheduler::RequestKeyPressed(bool pressed)
681: {
682: if (pressed) {
683: new_keymode = SCHED_KEY;
684: } else {
685: new_keymode = 0;
686: }
687: atomic_reqflag |= REQ_KEY_MODE;
688: }
689:
690: // REQ_* フラグのデバッグ表示
691: void
692: Scheduler::PutlogReqFlag(uint32 req) const
693: {
694: static const char * const cpumode_str[] = {
695: "Normal",
696: "STOP",
697: "HALT",
698: "HALTXXX",
699: };
700: char buf[128];
701: int len = 0;
702:
703: buf[0] = '\0';
704:
705: #define SET(FL, MSG) \
706: if ((req & (FL))) len += snprintf(buf + len, sizeof(buf) - len, ",%s", MSG)
707:
708: SET(REQ_EXIT, "EXIT");
709: SET(REQ_RESTART, "RESTART");
710: SET(REQ_RESET, "RESET");
711: SET(REQ_POWER_OFF, "POWER_OFF");
712: SET(REQ_POWER_ON, "POWER_ON");
713: if ((req & REQ_USER_MODE)) {
714: len += snprintf(buf + len, sizeof(buf) - len, ",USER:%s",
715: (new_usermode == 0) ? "Fast" : "Sync");
716: }
717: if ((req & REQ_CPU_MODE)) {
718: len += snprintf(buf + len, sizeof(buf) - len, ",CPU:%s",
719: cpumode_str[new_cpumode & 3]);
720: }
721: if ((req & REQ_BOOT_MODE)) {
722: len += snprintf(buf + len, sizeof(buf) - len, ",BOOT:%s",
723: (new_bootmode == 0) ? "Normal" : "Boot");
724: }
725: if ((req & REQ_KEY_MODE)) {
726: len += snprintf(buf + len, sizeof(buf) - len, ",KEY:%s",
727: (new_keymode == 0) ? "-" : "Pressed");
728: }
729:
730: if (buf[0] != '\0') {
731: buf[0] = '<';
732: strlcat(buf, ">", sizeof(buf));
733: }
734: putlogn("%sreq=$%04x%s", (ispower ? "" : "Off "), req, buf);
735: }
736:
1.1.1.3 root 737: // 指定のイベントを開始する。
1.1 root 738: // すでに同イベントが登録されている場合は古いイベントを削除してから
739: // 新しいイベントを再登録となる。
740: // イベントはワンショットのみ。
741: void
1.1.1.3 root 742: Scheduler::StartEvent(Event *newev)
1.1 root 743: {
744: bool updated = false;
745:
1.1.1.7 root 746: // time は仮想相対時間 [nsec]、vtime は仮想絶対時刻 [nsec]
747: newev->vtime = gMPU->GetVirtTime() + newev->time;
1.1 root 748:
749: evcs.lock();
1.1.1.9 root 750: // すでにあれば削除して...
1.1 root 751: if (newev->active) {
1.1.1.9 root 752: updated = eventq.Erase(newev);
1.1 root 753: }
1.1.1.9 root 754: // 自身を追加
755: eventq.push(newev);
1.1 root 756: newev->active = true;
757:
1.1.1.9 root 758: // 先頭なら再スケジュールのため CPU 実行をここで打ち切り
759: if (eventq.top() == newev) {
760: gMPU->Release();
761: }
762: evcs.unlock();
1.1 root 763:
1.1.1.8 root 764: newev->dev->putlogf(3, lstr("イベント '%s' %s %d.%03d usec 後",
1.1.1.2 root 765: newev->GetName().c_str(),
1.1.1.3 root 766: updated ? "更新" : "開始",
1.1 root 767: (int)(newev->time / 1000),
1.1.1.8 root 768: (int)(newev->time % 1000)));
1.1 root 769: }
770:
1.1.1.3 root 771: // 指定のイベントを停止する。
1.1 root 772: // 指定されたイベントが登録されていなければ何もしない。
773: void
1.1.1.3 root 774: Scheduler::StopEvent(Event *event)
1.1 root 775: {
776: bool found = false;
777:
778: evcs.lock();
1.1.1.9 root 779: found = eventq.Erase(event);
1.1 root 780: evcs.unlock();
1.1.1.9 root 781: event->active = false;
1.1 root 782:
1.1.1.3 root 783: // イベントを停止した場合は MPU の実行中断はしなくてよい。
1.1 root 784: // 1msec 後にイベントを追加した後、やっぱりそのイベントを取り消した
785: // 場合 (LUNA の電源オフとか) はここで MPU 処理を中断するよりも
786: // そのまま 1msec 走って問題ない。
787:
788: if (found) {
1.1.1.8 root 789: event->dev->putlogf(3, lstr("イベント '%s' 停止",
790: event->GetName().c_str()));
1.1 root 791: }
792: }
793:
1.1.1.5 root 794: void
1.1.1.10 root 795: Scheduler::MonitorUpdate(Monitor *, TextScreen& screen)
1.1 root 796: {
797: int x, y;
798:
1.1.1.10 root 799: screen.Clear();
1.1 root 800: x = 0;
801: y = 0;
802:
1.1.1.7 root 803: int clock_khz = gMPU->GetClockSpeed();
804:
1.1.1.10 root 805: screen.Print(0, y, "Mode: 0x%04x (", mode);
1.1.1.8 root 806: if ((mode & SCHED_SYNC)) {
1.1.1.10 root 807: screen.Puts(14, y, TA::On, "Sync");
1.1.1.8 root 808: } else {
1.1.1.10 root 809: screen.Puts(14, y, "Full");
1.1.1.8 root 810: }
811: switch (mode & SCHED_CPU_MASK) {
812: case SCHED_CPU_NORMAL:
1.1.1.10 root 813: screen.Puts(19, y, "Run");
1.1.1.8 root 814: break;
815: case SCHED_CPU_STOP:
1.1.1.10 root 816: screen.Puts(19, y, TA::On, "STOP");
1.1.1.8 root 817: break;
818: case SCHED_CPU_HALT:
1.1.1.10 root 819: screen.Puts(19, y, TA::On, "HALT");
1.1.1.8 root 820: break;
821: }
1.1.1.10 root 822: screen.Puts(24, y, TA::OnOff((mode & SCHED_BOOT)), "Boot");
823: screen.Puts(29, y, TA::OnOff((mode & SCHED_KEY)), "Key");
824: screen.Puts(33, y, ")");
1.1.1.8 root 825: y++;
1.1.1.10 root 826: screen.Print(0, y++, "Req: %08x", (uint32)atomic_reqflag);
827: screen.Print(0, y++, "MPU Speed: %d.%03dMHz",
1.1.1.7 root 828: (clock_khz / 1000), (clock_khz % 1000));
1.1.1.9 root 829: uint64 rt = rtime;
830: uint64 vt = vtime;
831:
832: auto s_rt = TimeToStr(rt);
833: auto s_vt = TimeToStr(vt);
834: std::string s_sync_rt {};
835: std::string s_sync_vt {};
836: if (mode != 0) {
837: uint64 sync_rt = rtime - rtime_epoch;
838: uint64 sync_vt = vtime - vtime_epoch;
839: s_sync_rt = TimeToStr(sync_rt);
840: s_sync_vt = TimeToStr(sync_vt);
841: }
842: // 日 まで表示しようとするときは桁位置を動かす
1.1.1.10 root 843: int width = std::max(rt, vt) < 86400_sec ? 20 : 25;
1.1.1.9 root 844:
1.1.1.10 root 845: screen.Print(0, y++, "Total Real Time: %*s", width, s_rt.c_str());
846: screen.Print(4, y++, "Virtual Time: %*s", width, s_vt.c_str());
847: screen.Print(0, y++, "Sync Real Time: %*s", width, s_sync_rt.c_str());
848: screen.Print(4, y++, "Virtual Time: %*s", width, s_sync_vt.c_str());
1.1.1.9 root 849:
1.1.1.10 root 850: screen.Print(0, y++, "Performance : %4d%%", perf_counter);
1.1.1.9 root 851:
852: x = 44;
853: y = 0;
1.1.1.10 root 854: screen.Print(x, y++, "Max OverSlept: %13s", SecToStr(overslept).c_str());
855: screen.Print(x, y++, "Sync per sec : %4u", last_sync_count);
856: screen.Print(x, y++, "Event per sec: %8u",
1.1.1.9 root 857: (uint)last_event_stat.Count());
1.1.1.10 root 858: screen.Print(x, y++, "Evt Intvl Min: %13s",
1.1.1.9 root 859: SecToStr(last_event_stat.Min()).c_str());
1.1.1.10 root 860: screen.Print(x, y++, "Evt Intvl Avg: %13s",
1.1.1.9 root 861: SecToStr(last_event_stat.Average()).c_str());
1.1.1.10 root 862: screen.Print(x, y++, "Evt Intvl Max: %13s",
1.1.1.9 root 863: SecToStr(last_event_stat.Max()).c_str());
1.1 root 864:
865: // 0 1 2 3 4 5 6
1.1.1.9 root 866: // 01234567890123456789012345678901234567890123456789012345678901234568
867: // Set Time Remain Time Description
868: // 3.123'456'789 3.123'456'789 012345678901234567890123456789
1.1 root 869: x = 0;
1.1.1.9 root 870: y = 9;
1.1.1.10 root 871: screen.Puts(x + 0, y, "Event Time");
872: screen.Puts(x + 15, y, "Remain Time");
873: screen.Puts(x + 30, y, "Description");
1.1 root 874: y++;
1.1.1.6 root 875:
1.1 root 876: for (const auto& e : gEvents) {
877: uint64 rem;
1.1.1.4 root 878: TA attr;
1.1 root 879: if (e->active) {
1.1.1.4 root 880: attr = TA::Normal;
1.1.1.7 root 881: if (e->vtime > vtime) {
882: rem = e->vtime - vtime;
1.1.1.6 root 883: } else {
884: // XXX 実際には起きないけど、この表示をするにあたって
885: // スケジューラスレッドと一切調停していないので、仮想時刻が
886: // e->cycle (イベント発生時刻) をすぎていることがある。
887: // すぎてるので残り時間 0 と表示しておく。
888: rem = 0;
889: }
1.1 root 890: } else {
891: attr = TA::Disable;
892: rem = 0;
893: }
1.1.1.10 root 894: screen.Print(x, y++, attr,
1.1.1.9 root 895: "%13s %13s %-30s",
896: SecToStr(e->time).c_str(),
897: SecToStr(rem).c_str(),
898: e->GetName().c_str());
1.1 root 899: }
900: }
This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.