--- nono/vm/scheduler.h 2026/04/29 17:04:50 1.1.1.7 +++ nono/vm/scheduler.h 2026/04/29 17:04:59 1.1.1.10 @@ -8,7 +8,12 @@ #include "device.h" #include "event.h" +#include "eventqueue.h" +#include "fixedqueue.h" +#include "monitor.h" #include "mpu.h" +#include "statistics.h" +#include "stopwatch.h" #include #include #include @@ -26,9 +31,11 @@ class Scheduler : public Device // ビットがすべて %0 なら高速モード、どれかでも %1 なら通常モードになる。 // // | b7 | b6 | b5 | b4 | b3 | b2 | b1 | b0 | - // mode | |SYNC| CPUSTAT | - // | +---+---- CPU の状態 - // +-------------- 高速モード抑制 + // mode | |KEY |BOOT|SYNC| CPUSTAT | + // | | | +---+---- CPU の状態 + // | | +-------------- 高速モード抑制 + // | +------------------- ブートページ実行中 + // +------------------------ キー入力中 // // bit1,0 (CPUSTAT) は CPU の状態を示す。%00 の時だけ高速走行が可能。 // これは MPUDevice::Run() の戻り値でもある。 @@ -44,6 +51,8 @@ class Scheduler : public Device static const uint32 SCHED_CPU_STOP = 0x0001; // CPU が STOP 状態 static const uint32 SCHED_CPU_HALT = 0x0002; // CPU が HALT 状態 static const uint32 SCHED_SYNC = 0x0004; // 高速モード抑制指示(UI) + static const uint32 SCHED_BOOT = 0x0008; // ブートページ実行中 + static const uint32 SCHED_KEY = 0x0010; // キー入力中 // 便宜上用意しておく static const uint32 SCHED_CPU_NORMAL= 0x0000; // CPU が通常状態 @@ -57,10 +66,13 @@ class Scheduler : public Device // REQ_USER_MODE と new_usermode も同様。 static const uint REQ_CPU_MODE = 0x0001; // CPU 状態を変更 static const uint REQ_USER_MODE = 0x0002; // 同期/高速モード変更 + static const uint REQ_BOOT_MODE = 0x0004; // ブートページモード変更 + static const uint REQ_KEY_MODE = 0x0008; // キー入力モード変更 static const uint REQ_POWER_ON = 0x0010; // 電源オン static const uint REQ_POWER_OFF = 0x0020; // 電源オフ static const uint REQ_EXIT = 0x0040; // アプリケーション終了 static const uint REQ_RESET = 0x0080; // ハードウェアリセット + static const uint REQ_RESTART = 0x0100; // 電源入れ直し public: Scheduler(); @@ -68,19 +80,29 @@ class Scheduler : public Device bool Init() override; - nnSize GetMonitorSize() override; - void MonitorUpdate(TextScreen&) override; - // スレッド開始 void ThreadRun(); // スレッド終了 void Terminate(); - // スケジューラを開始 (電源オン時に vm から呼ばれる) - void Run(); + // スレッド終了を待機する + void Wait(); + + // スレッド終了を指示する + void RequestExit(); + + // 電源オン要求 + // 当然電源オフ時に呼ぶので VM スレッド外から呼ばれることになる。 + void RequestPowerOn() { + atomic_reqflag |= REQ_POWER_ON; + } // 電源オフ要求 + // → ここでフラグを立ててスケジューラスレッドに通知、 + // → スケジューラスレッドが各デバイスの PowerOff() をコール、 + // → 電源オフ状態に移行、 + // → (コールバックが登録されていれば) UI スレッドに通知。 void RequestPowerOff() { atomic_reqflag |= REQ_POWER_OFF; } @@ -90,41 +112,70 @@ class Scheduler : public Device atomic_reqflag |= REQ_RESET; } + // 電源入れ直し要求 + void RequestRestartVM() { + atomic_reqflag |= REQ_RESTART; + } + + // 現在の実行モードを返す。 + uint32 GetRunMode() const { return mode; } + + // 電源オンなら true を返す。 + bool IsPower() const { return ispower; } - // スケジューラが高速モードなら true を返す。 + // 高速モード(UIによる指示)なら true を返す。 // ここでいう高速モードは STOP 状態中でも高速モード。 bool GetFullSpeed() const { return ((mode & SCHED_SYNC) == 0); } - // スケジューラの動作モードを設定する。 + // 高速モードを指示する。 // true なら高速モード、false なら同期モード。 void SetFullSpeed(bool enable); + // ブートページモードの設定。 + void RequestBootPageMode(bool isrom); + + // キー入力中モードの設定。 + void RequestKeyPressed(bool pressed); + // イベントを開始する void StartEvent(Event *newev); // イベントを停止する void StopEvent(Event *event); - std::list eventlist {}; // 有効なイベントリスト - std::mutex evcs {}; // イベントリストのロック + // パフォーマンスカウンタの値取得 (UI 表示用) + int GetPerfCounter() const { return perf_counter; } - // 状態変更要求 - // スケジューラループの状態を変更したい場合はこのフラグを立てる。 - // REQ_MODE_* の相反するビット以外は同時に複数立ててもいいはず。 - // 処理されると再び 0 になる。 - std::atomic atomic_reqflag {}; - std::condition_variable cv {}; - std::mutex cvmtx {}; + EventQueue eventq {}; // 有効なイベントキュー + std::mutex evcs {}; // イベントリストのロック private: - // リセットイベントコールバック - void ResetCallback(Event& ev); + // 同期イベント + void SyncCallback(Event& ev); - // スレッド終了を指示する - void RequestExit(); + DECLARE_MONITOR_CALLBACK(MonitorUpdate); + + // メインループ + bool RunOff(); + bool RunOn(); + + // 同期処理 + uint64 Sync(); + + // RTC を進める。 + void RunRTC(); + + // REQ_* フラグのデバッグ表示 + void PutlogReqFlag(uint32 req) const; pthread_t thread {}; bool thread_created {}; // スレッドが作成されたら true + // スケジューラスレッドに処理を要求する場合はこのフラグを立てる + std::atomic atomic_reqflag {}; + + // 電源オンなら true + bool ispower {}; + // スケジューラの動作モード。SCHED_* uint32 mode {}; @@ -134,20 +185,49 @@ class Scheduler : public Device // REQ_USER_MODE フラグで変更するモード uint32 new_usermode {}; - uint64 rtimestart {}; // スケジューラ開始実時刻 - uint64 rtimebase {}; // 実時間の基準時刻 - uint64 vtimebase {}; // 仮想時間の基準時刻 + // REQ_BOOT_MODE フラグで変更するモード + uint32 new_bootmode {}; + + // REQ_KEY_MODE フラグで変更するモード + uint32 new_keymode {}; + + uint64 rtime {}; // 現在の実経過時間 + uint64 vtime {}; // 現在の仮想経過時間 + + // 同期調整用 + uint64 rtime_epoch {}; + uint64 vtime_epoch {}; + + // sleep しすぎた時間の最大値 + uint64 overslept {}; - // 前回 RTC へ 32Hz 入力パルスを入れた時刻 (実時間の絶対時刻系) - uint64 rtc_last_clock {}; + // 同期回数 + uint sync_count {}; // 現在の1秒間での同期回数 + uint last_sync_count {}; // 前回の1秒間での同期回数 + + // 次回 RTC へ 32Hz 入力パルスを入れる予定の実経過時間 + uint64 next_rtc_rtime {}; + + // パフォーマンスカウンタ + // perfq が移動平均用のバッファ。四捨五入用に10倍値を持っておく + // (100% なら 1000)。perf_counter は perfq から求めた実行率で + // 小数以下を四捨五入する (のでこっちは 100% なら 100)。 + int perf_counter {}; // 実行率 [%] + FixedQueue perfq {}; // 移動平均用のバッファ + uint64 next_perf_rtime {}; // 次回測定予定の実経過時間 + uint64 last_perf_rtime {}; // 前回測定時の実経過時間 + uint64 last_perf_vtime {}; // 前回測定時の仮想経過時間 + + Statistics last_event_stat {}; // 直近実1秒のイベント統計 + + // 同期イベント + Event sync_event { this }; + + Monitor monitor { this }; }; -extern std::unique_ptr gScheduler; +extern const std::string TimeToStr(uint64 t); -// 現在時刻 [nsec] を取得する -static inline uint64 GetRealTime() -{ - struct timeval tv; - gettimeofday(&tv, NULL); - return (uint64)(tv.tv_sec * 1000 * 1000 + tv.tv_usec) * 1000; -} +extern Stopwatch gRealtime; + +extern std::unique_ptr gScheduler;