diff --git a/src/xenia/base/clock.cc b/src/xenia/base/clock.cc index 78f922b3b..e3cd39c12 100644 --- a/src/xenia/base/clock.cc +++ b/src/xenia/base/clock.cc @@ -9,13 +9,43 @@ #include "xenia/base/clock.h" +#include + +#include "xenia/base/assert.h" #include "xenia/base/platform.h" namespace xe { +// Time scalar applied to all time operations. double guest_time_scalar_ = 1.0; +// Tick frequency of guest. uint64_t guest_tick_frequency_ = Clock::host_tick_frequency(); +// Base FILETIME of the guest system from app start. uint64_t guest_system_time_base_ = Clock::QueryHostSystemTime(); +// Combined time and frequency scalar (computed by RecomputeGuestTickScalar). +double guest_tick_scalar_ = 1.0; +// Native guest ticks. +thread_local uint64_t guest_tick_count_ = 0; +// 100ns ticks, relative to guest_system_time_base_. +thread_local uint64_t guest_time_filetime_ = 0; +// Last sampled host tick count. +thread_local uint64_t last_host_tick_count_ = Clock::QueryHostTickCount(); + +void RecomputeGuestTickScalar() { + guest_tick_scalar_ = (guest_tick_frequency_ * guest_time_scalar_) / + double(Clock::host_tick_frequency()); +} + +void UpdateGuestClock() { + uint64_t host_tick_count = Clock::QueryHostTickCount(); + uint64_t host_tick_delta = host_tick_count > last_host_tick_count_ + ? host_tick_count - last_host_tick_count_ + : 0; + last_host_tick_count_ = host_tick_count; + uint64_t guest_tick_delta = uint64_t(host_tick_delta * guest_tick_scalar_); + guest_tick_count_ += guest_tick_delta; + guest_time_filetime_ += (guest_tick_delta * 10000000) / guest_tick_frequency_; +} uint64_t Clock::host_tick_frequency() { static LARGE_INTEGER frequency = {0}; @@ -46,12 +76,14 @@ double Clock::guest_time_scalar() { return guest_time_scalar_; } void Clock::set_guest_time_scalar(double scalar) { guest_time_scalar_ = scalar; + RecomputeGuestTickScalar(); } uint64_t Clock::guest_tick_frequency() { return guest_tick_frequency_; } void Clock::set_guest_tick_frequency(uint64_t frequency) { guest_tick_frequency_ = frequency; + RecomputeGuestTickScalar(); } uint64_t Clock::guest_system_time_base() { return guest_system_time_base_; } @@ -61,36 +93,56 @@ void Clock::set_guest_system_time_base(uint64_t time_base) { } uint64_t Clock::QueryGuestTickCount() { - // TODO(benvanik): adjust. - return QueryHostTickCount(); + UpdateGuestClock(); + return guest_tick_count_; } uint64_t Clock::QueryGuestSystemTime() { - // TODO(benvanik): adjust. - return QueryHostSystemTime(); + UpdateGuestClock(); + return guest_system_time_base_ + guest_time_filetime_; } uint32_t Clock::QueryGuestUptimeMillis() { - // TODO(benvanik): adjust. - return QueryHostUptimeMillis(); + UpdateGuestClock(); + uint64_t uptime_millis = guest_tick_count_ / (guest_tick_frequency_ / 1000); + uint32_t result = uint32_t(std::min(uptime_millis, uint64_t(UINT_MAX))); + return result; } uint32_t Clock::ScaleGuestDurationMillis(uint32_t guest_ms) { - // TODO(benvanik): adjust. - // if -1, return -1 - // if 0, return 0 - return guest_ms; + if (guest_ms == UINT_MAX) { + return UINT_MAX; + } else if (!guest_ms) { + return 0; + } + uint64_t scaled_ms = uint64_t(uint64_t(guest_ms) * guest_time_scalar_); + return uint32_t(std::min(scaled_ms, uint64_t(UINT_MAX))); } int64_t Clock::ScaleGuestDurationFileTime(int64_t guest_file_time) { - // TODO(benvanik): adjust. // negative = relative times // positive = absolute times - return guest_file_time; + // TODO(benvanik): support absolute times. + assert_true(guest_file_time <= 0); + if (!guest_file_time) { + return 0; + } + uint64_t scaled_file_time = + uint64_t(uint64_t(guest_file_time) * guest_time_scalar_); + // TODO(benvanik): check for overflow? + return scaled_file_time; } void Clock::ScaleGuestDurationTimeval(long* tv_sec, long* tv_usec) { - // TODO(benvanik): adjust. + uint64_t scaled_sec = uint64_t(uint64_t(*tv_sec) * guest_tick_scalar_); + uint64_t scaled_usec = uint64_t(uint64_t(*tv_usec) * guest_time_scalar_); + if (scaled_usec > UINT_MAX) { + uint64_t overflow_sec = scaled_usec / 1000000; + scaled_usec -= overflow_sec * 1000000; + scaled_sec += overflow_sec; + } + *tv_sec = long(scaled_sec); + *tv_usec = long(scaled_usec); } } // namespace xe diff --git a/src/xenia/emulator.cc b/src/xenia/emulator.cc index 0de7c9fab..9ed480a2a 100644 --- a/src/xenia/emulator.cc +++ b/src/xenia/emulator.cc @@ -72,7 +72,7 @@ X_STATUS Emulator::Setup() { // Initialize clock. // 360 uses a 50MHz clock. - // Clock::set_guest_tick_frequency(50000000); + Clock::set_guest_tick_frequency(50000000); // We could reset this with save state data/constant value to help replays. Clock::set_guest_system_time_base(Clock::QueryHostSystemTime()); // This can be adjusted dynamically, as well. diff --git a/src/xenia/ui/main_window.cc b/src/xenia/ui/main_window.cc index 139dc7f62..835e3637c 100644 --- a/src/xenia/ui/main_window.cc +++ b/src/xenia/ui/main_window.cc @@ -9,6 +9,7 @@ #include "xenia/ui/main_window.h" +#include "xenia/base/clock.h" #include "xenia/base/logging.h" #include "xenia/base/threading.h" #include "xenia/gpu/graphics_system.h" @@ -18,8 +19,10 @@ namespace xe { namespace ui { +const std::wstring kBaseTitle = L"xenia"; + MainWindow::MainWindow(Emulator* emulator) - : PlatformWindow(L"xenia"), + : PlatformWindow(kBaseTitle), emulator_(emulator), main_menu_(MenuItem::Type::kNormal) {} @@ -48,6 +51,7 @@ bool MainWindow::Initialize() { return false; } Resize(1280, 720); + UpdateTitle(); on_key_down.AddListener([this](KeyEvent& e) { bool handled = true; switch (e.key_code()) { @@ -59,6 +63,21 @@ bool MainWindow::Initialize() { emulator()->graphics_system()->ClearCaches(); break; } + case 0x6D: { // numpad minus + Clock::set_guest_time_scalar(Clock::guest_time_scalar() / 2.0); + UpdateTitle(); + break; + } + case 0x6B: { // numpad plus + Clock::set_guest_time_scalar(Clock::guest_time_scalar() * 2.0); + UpdateTitle(); + break; + } + case 0x0D: { // numpad enter + Clock::set_guest_time_scalar(1.0); + UpdateTitle(); + break; + } default: { handled = false; break; @@ -83,6 +102,16 @@ bool MainWindow::Initialize() { return true; } +void MainWindow::UpdateTitle() { + std::wstring title(kBaseTitle); + if (Clock::guest_time_scalar() != 1.0) { + title += L" (@"; + title += xe::to_wstring(std::to_string(Clock::guest_time_scalar())); + title += L"x)"; + } + set_title(title); +} + void MainWindow::OnClose() { loop_.Quit(); diff --git a/src/xenia/ui/main_window.h b/src/xenia/ui/main_window.h index fe746051d..7fdf82e9e 100644 --- a/src/xenia/ui/main_window.h +++ b/src/xenia/ui/main_window.h @@ -44,6 +44,8 @@ class MainWindow : public PlatformWindow { private: bool Initialize(); + void UpdateTitle(); + void OnClose() override; void OnCommand(int id) override;