diff --git a/src/xenia/kernel/objects/xtimer.cc b/src/xenia/kernel/objects/xtimer.cc index 988fe6d6d..63f2bf17a 100644 --- a/src/xenia/kernel/objects/xtimer.cc +++ b/src/xenia/kernel/objects/xtimer.cc @@ -9,6 +9,8 @@ #include +#include + using namespace xe; using namespace xe::kernel; @@ -44,14 +46,36 @@ void XTimer::Initialize(uint32_t timer_type) { handle_ = CreateWaitableTimer(NULL, manual_reset, NULL); } -X_STATUS XTimer::SetTimer(int64_t due_time, uint32_t period_ms) { +X_STATUS XTimer::SetTimer( + int64_t due_time, uint32_t period_ms, + uint32_t routine, uint32_t routine_arg, bool resume) { + // Stash routine for callback. + current_routine_ = routine; + current_routine_arg_ = routine_arg; + LARGE_INTEGER due_time_li; due_time_li.QuadPart = due_time; - if (SetWaitableTimer(handle_, &due_time_li, period_ms, NULL, NULL, TRUE)) { - return X_STATUS_SUCCESS; - } else { - return X_STATUS_UNSUCCESSFUL; + BOOL result = SetWaitableTimer( + handle_, &due_time_li, period_ms, + routine ? (PTIMERAPCROUTINE)CompletionRoutine : NULL, this, + resume ? TRUE : FALSE); + + // Caller is checking for STATUS_TIMER_RESUME_IGNORED. + // This occurs if result == TRUE but error is set. + if (!result && GetLastError() == ERROR_NOT_SUPPORTED) { + return X_STATUS_TIMER_RESUME_IGNORED; } + + return result ? X_STATUS_SUCCESS : X_STATUS_UNSUCCESSFUL; +} + +void XTimer::CompletionRoutine( + XTimer* timer, DWORD timer_low, DWORD timer_high) { + XEASSERT(timer->current_routine_); + + // Queue APC to call back routine with (arg, low, high). + // TODO(benvanik): APC dispatch. + XELOGE("Timer needs APC!"); } X_STATUS XTimer::Cancel() { diff --git a/src/xenia/kernel/objects/xtimer.h b/src/xenia/kernel/objects/xtimer.h index d25774b22..586c45e41 100644 --- a/src/xenia/kernel/objects/xtimer.h +++ b/src/xenia/kernel/objects/xtimer.h @@ -27,13 +27,20 @@ public: void Initialize(uint32_t timer_type); // completion routine, arg to completion routine - X_STATUS SetTimer(int64_t due_time, uint32_t period_ms); + X_STATUS SetTimer(int64_t due_time, uint32_t period_ms, + uint32_t routine, uint32_t routine_arg, bool resume); X_STATUS Cancel(); virtual void* GetWaitHandle() { return handle_; } private: HANDLE handle_; + + uint32_t current_routine_; + uint32_t current_routine_arg_; + + static void CompletionRoutine( + XTimer* timer, DWORD timer_low, DWORD timer_high); }; diff --git a/src/xenia/kernel/xboxkrnl_debug.cc b/src/xenia/kernel/xboxkrnl_debug.cc index bd3e25615..8127d45b0 100644 --- a/src/xenia/kernel/xboxkrnl_debug.cc +++ b/src/xenia/kernel/xboxkrnl_debug.cc @@ -282,7 +282,7 @@ SHIM_CALL RtlRaiseException_shim( // TODO(benvanik): unwinding. // This is going to suck. - XEASSERTALWAYS(); + DebugBreak(); } diff --git a/src/xenia/kernel/xboxkrnl_threading.cc b/src/xenia/kernel/xboxkrnl_threading.cc index 39bf3abba..fae066aea 100644 --- a/src/xenia/kernel/xboxkrnl_threading.cc +++ b/src/xenia/kernel/xboxkrnl_threading.cc @@ -977,24 +977,23 @@ SHIM_CALL NtCreateTimer_shim( SHIM_CALL NtSetTimerEx_shim( PPCContext* ppc_state, KernelState* state) { uint32_t timer_handle = SHIM_GET_ARG_32(0); - uint32_t info_class = SHIM_GET_ARG_32(1); - uint32_t info_ptr = SHIM_GET_ARG_32(2); - uint32_t info_length = SHIM_GET_ARG_32(3); + uint32_t due_time_ptr = SHIM_GET_ARG_32(1); + uint32_t routine = SHIM_GET_ARG_32(2); // PTIMERAPCROUTINE + uint32_t unk_one = SHIM_GET_ARG_32(3); + uint32_t routine_arg = SHIM_GET_ARG_32(4); + uint32_t resume = SHIM_GET_ARG_32(5); + uint32_t period_ms = SHIM_GET_ARG_32(6); + uint32_t unk_zero = SHIM_GET_ARG_32(7); - // UNVERIFIED - XEASSERTALWAYS(); + XEASSERT(unk_one == 1); + XEASSERT(unk_zero == 0); + + uint64_t due_time = SHIM_MEM_64(due_time_ptr); XELOGD( - "NtSetTimerEx(%.8X, %.8X, %.8X, %d)", - timer_handle, info_class, info_ptr, info_length); - - // TIMER_BASIC_INFORMATION - XEASSERT(info_class == 0); - XEASSERT(info_length == 12); - - uint64_t due_time = SHIM_MEM_64(info_ptr + 0); - uint32_t timer_state = SHIM_MEM_32(info_ptr + 8); // unused? - uint32_t period_ms = 0; // Not repeating. + "NtSetTimerEx(%.8X, %.8X(%lld), %.8X, %.8X, %.8X, %.1X, %d, %.8X)", + timer_handle, due_time_ptr, due_time, routine, unk_one, + routine_arg, resume, period_ms, unk_zero); X_STATUS result = X_STATUS_SUCCESS; @@ -1002,7 +1001,8 @@ SHIM_CALL NtSetTimerEx_shim( result = state->object_table()->GetObject( timer_handle, (XObject**)&timer); if (XSUCCEEDED(result)) { - result = timer->SetTimer(due_time, period_ms); + result = timer->SetTimer( + due_time, period_ms, routine, routine_arg, resume ? true : false); timer->Release(); } @@ -1016,7 +1016,7 @@ SHIM_CALL NtCancelTimer_shim( uint32_t current_state_ptr = SHIM_GET_ARG_32(1); // UNVERIFIED - XEASSERTALWAYS(); + DebugBreak(); XELOGD( "NtCancelTimer(%.8X, %.8X)", diff --git a/src/xenia/xbox.h b/src/xenia/xbox.h index fa622ebb8..837e5dc89 100644 --- a/src/xenia/xbox.h +++ b/src/xenia/xbox.h @@ -33,6 +33,7 @@ typedef uint32_t X_STATUS; #define X_STATUS_ALERTED ((X_STATUS)0x00000101L) #define X_STATUS_TIMEOUT ((X_STATUS)0x00000102L) #define X_STATUS_PENDING ((X_STATUS)0x00000103L) +#define X_STATUS_TIMER_RESUME_IGNORED ((X_STATUS)0x40000025L) #define X_STATUS_UNSUCCESSFUL ((X_STATUS)0xC0000001L) #define X_STATUS_NOT_IMPLEMENTED ((X_STATUS)0xC0000002L) #define X_STATUS_INFO_LENGTH_MISMATCH ((X_STATUS)0xC0000004L)