--- nono/vm/scheduler.cpp 2026/04/29 17:04:45 1.1.1.7 +++ nono/vm/scheduler.cpp 2026/04/29 17:04:50 1.1.1.8 @@ -86,8 +86,8 @@ Scheduler::ThreadRun() if ((req & REQ_EXIT)) { return; } - // REQ_POWER_ON なら他のフラグを書き戻して抜ける。 - // REQ_{SYNC|FAST} は以降のメインループで処理するため。 + // REQ_POWER_ON ならこれだけ下げて抜ける。 + // 他のフラグはメインループで処理するため維持。 if ((req & REQ_POWER_ON)) { req &= ~REQ_POWER_ON; atomic_reqflag.store(req); @@ -103,43 +103,46 @@ Scheduler::ThreadRun() mode = 0; for (;;) { req = atomic_reqflag.exchange(0); - if (req) { - if ((req & REQ_SYNC)) { - // スケジューラを同期モードに - mode |= SCHED_SYNC; - // 同期モードでは RAM アクセスはデバイス経由 - ::direct_ram_size = 0; - } - if ((req & REQ_FAST)) { - // スケジューラを高速モードに - mode &= ~SCHED_SYNC; - // 高速モードでは RAM は直接アクセス - ::direct_ram_size = ::ram_size; - } - if ((req & REQ_RUN)) { - // MPU が通常状態になった - mode &= ~SCHED_STOP; - } - if ((req & REQ_STOP)) { - // MPU が STOP 状態になった - mode |= SCHED_STOP; - } - if ((req & REQ_MODEMASK)) { - // mode がどれかにでも変わったら - static const char *modestr[] = { - "高速", - "高速 & STOP状態", - "通常", - "通常 & STOP状態", - }; - putlog(1, "モード変更 mode=%d (%s)", mode, modestr[mode]); + if (__predict_false(req != 0)) { + uint32 newmode = mode; + + // 動作モード変更要因を一つずつ調べる + if ((req & REQ_CPU_MODE)) { + // CPU 状態が new_cpumode に変更になった + newmode &= ~SCHED_CPU_MASK; + newmode |= new_cpumode; + req &= ~REQ_CPU_MODE; + } + if ((req & REQ_USER_MODE)) { + // ユーザからの高速/同期モード指示が変更になった + newmode &= ~SCHED_SYNC; + newmode |= new_usermode; + req &= ~REQ_USER_MODE; + } + // 動作モード変更要因を全部調べた結果、変更が必要か。 + // mode、newmode どちらもゼロ(高速モード)か非ゼロ(通常モード)か + // なので、等値比較ではなく二値にしてから比較。 + if ((bool)newmode != (bool)mode) { + // 動作モード変更 (ここは全ビットを維持して代入) + mode = newmode; + + if (mode == 0) { + // 高速モードに + putlog(1, "高速モード"); + + // 高速モードでは RAM は直接アクセス + ::direct_ram_size = gRAM->GetSize(); + } else { + // 同期モードに + putlog(1, "同期モード"); + + // 同期モードでは RAM アクセスはデバイス経由 + ::direct_ram_size = 0; + } // 基準時刻をリセット rtimebase = ::GetRealTime(); vtimebase = gMPU->GetVirtTime(); - - // 該当ビットを落としておく - req &= ~REQ_MODEMASK; } if ((req & REQ_POWER_OFF)) { @@ -218,15 +221,15 @@ Scheduler::ThreadRun() } // コールバック - e->dev->putlog(3, "イベント '%s' 時刻到達", e->GetName().c_str()); + e->dev->putlogf(3, lstr("イベント '%s' 時刻到達", + e->GetName().c_str())); ((e->dev)->*(e->func))(*e); // 同時刻のイベントがあるかも知れないのでなくなるまで調べる } // CPU を駆動 - uint32 outer; - outer = gMPU->Run(delta_vtime); + new_cpumode = gMPU->Run(delta_vtime); // CPU 駆動によって進んだ仮想時刻を再取得 vtime = gMPU->GetVirtTime(); @@ -256,16 +259,9 @@ Scheduler::ThreadRun() } } - // CPU の STOP 状態をこっちのモードに反映。 - // このために SCHED_STOP と CPU_REQ_STOP は同じビット位置にしてある。 - // CPU の HALT はスケジューラ的には STOP と同じ扱いでいい。 - if (((mode ^ outer) & SCHED_STOP)) { - // 状態変更要求 - if ((outer & (CPU_REQ_STOP | CPU_REQ_HALT)) != 0) { - atomic_reqflag |= REQ_STOP; - } else { - atomic_reqflag |= REQ_RUN; - } + // CPU 側の走行状態が変わったら、このループに通知 + if (__predict_false(((mode ^ new_cpumode) & SCHED_CPU_MASK))) { + atomic_reqflag |= REQ_CPU_MODE; } } } @@ -275,22 +271,32 @@ void Scheduler::Terminate() { if (thread_created) { - atomic_reqflag |= REQ_EXIT; + RequestExit(); pthread_join(thread, NULL); thread_created = false; } } +// スレッド終了を指示する。 +void +Scheduler::RequestExit() +{ + std::unique_lock lock(cvmtx); + atomic_reqflag |= REQ_EXIT; + cv.notify_one(); +} + // 動作モードを設定する。 // true なら高速モード、false なら同期モード。 void Scheduler::SetFullSpeed(bool enable) { if (enable) { - atomic_reqflag |= REQ_FAST; + new_usermode = 0; } else { - atomic_reqflag |= REQ_SYNC; + new_usermode = SCHED_SYNC; } + atomic_reqflag |= REQ_USER_MODE; } // 指定のイベントを開始する。 @@ -337,11 +343,11 @@ Scheduler::StartEvent(Event *newev) gMPU->Release(); - newev->dev->putlog(3, "イベント '%s' %s %d.%03d usec 後", + newev->dev->putlogf(3, lstr("イベント '%s' %s %d.%03d usec 後", newev->GetName().c_str(), updated ? "更新" : "開始", (int)(newev->time / 1000), - (int)(newev->time % 1000)); + (int)(newev->time % 1000))); } // 指定のイベントを停止する。 @@ -369,7 +375,8 @@ Scheduler::StopEvent(Event *event) // そのまま 1msec 走って問題ない。 if (found) { - event->dev->putlog(3, "イベント '%s' 停止", event->GetName().c_str()); + event->dev->putlogf(3, lstr("イベント '%s' 停止", + event->GetName().c_str())); } } @@ -436,9 +443,25 @@ Scheduler::MonitorUpdate(TextScreen& mon int clock_khz = gMPU->GetClockSpeed(); - monitor.Print(0, y++, "Mode: %s %s", - (mode & SCHED_SYNC) ? "Sync" : "Full", - (mode & SCHED_STOP) ? "STOP" : "Run "); + monitor.Print(0, y, "Mode: 0x%04x (", mode); + if ((mode & SCHED_SYNC)) { + monitor.Puts(14, y, TA::On, "Sync"); + } else { + monitor.Puts(14, y, "Full"); + } + switch (mode & SCHED_CPU_MASK) { + case SCHED_CPU_NORMAL: + monitor.Puts(19, y, "Run"); + break; + case SCHED_CPU_STOP: + monitor.Puts(19, y, TA::On, "STOP"); + break; + case SCHED_CPU_HALT: + monitor.Puts(19, y, TA::On, "HALT"); + break; + } + monitor.Puts(23, y, ")"); + y++; monitor.Print(0, y++, "Req: %08x", (uint32)atomic_reqflag); monitor.Print(0, y++, "MPU Speed: %d.%03dMHz", (clock_khz / 1000), (clock_khz % 1000));