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