Annotation of nono/vm/scheduler.cpp, revision 1.1.1.10

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: }

unix.superglobalmegacorp.com

This archive runs on limited infrastructure. Preserving old code on modern bandwidth. Automated agents are requested to crawl responsibly.