diff --git a/librecomp/src/ultra_translation.cpp b/librecomp/src/ultra_translation.cpp index 24ea0bd..e0d367e 100644 --- a/librecomp/src/ultra_translation.cpp +++ b/librecomp/src/ultra_translation.cpp @@ -73,12 +73,21 @@ extern "C" void osGetCount_recomp(uint8_t * rdram, recomp_context * ctx) { ctx->r2 = osGetCount(); } +extern "C" void osSetCount_recomp(uint8_t * rdram, recomp_context * ctx) { + osSetCount(ctx->r4); +} + extern "C" void osGetTime_recomp(uint8_t * rdram, recomp_context * ctx) { uint64_t total_count = osGetTime(); ctx->r2 = (int32_t)(total_count >> 32); ctx->r3 = (int32_t)(total_count >> 0); } +extern "C" void osSetTime_recomp(uint8_t * rdram, recomp_context * ctx) { + uint64_t t = ((uint64_t)(ctx->r4) << 32) | ((ctx->r5) & 0xFFFFFFFFu); + osSetTime(t); +} + extern "C" void osSetTimer_recomp(uint8_t * rdram, recomp_context * ctx) { uint64_t countdown = ((uint64_t)(ctx->r6) << 32) | ((ctx->r7) & 0xFFFFFFFFu); uint64_t interval = load_doubleword(rdram, ctx->r29, 0x10); diff --git a/ultramodern/include/ultramodern/ultra64.h b/ultramodern/include/ultramodern/ultra64.h index 71a412f..31eb106 100644 --- a/ultramodern/include/ultramodern/ultra64.h +++ b/ultramodern/include/ultramodern/ultra64.h @@ -290,7 +290,9 @@ void osViSetYScale(float scale); PTR(void) osViGetNextFramebuffer(); PTR(void) osViGetCurrentFramebuffer(); u32 osGetCount(); +void osSetCount(u32 count); OSTime osGetTime(); +void osSetTime(OSTime t); int osSetTimer(RDRAM_ARG PTR(OSTimer) timer, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg); int osStopTimer(RDRAM_ARG PTR(OSTimer) timer); u32 osVirtualToPhysical(PTR(void) addr); diff --git a/ultramodern/src/events.cpp b/ultramodern/src/events.cpp index 7fb890b..46f63f1 100644 --- a/ultramodern/src/events.cpp +++ b/ultramodern/src/events.cpp @@ -28,6 +28,7 @@ struct SpTaskAction { }; struct ScreenUpdateAction { + ultramodern::renderer::ViRegs regs; }; struct UpdateConfigAction { @@ -55,6 +56,7 @@ static struct { int field; ViState states[2]; ultramodern::renderer::ViRegs regs; + ultramodern::renderer::ViRegs update_screen_regs; ViState* get_next_state() { return &states[cur_state ^ 1]; @@ -131,7 +133,7 @@ static struct { } events_context{}; ultramodern::renderer::ViRegs* ultramodern::renderer::get_vi_regs() { - return &events_context.vi.regs; + return &events_context.vi.update_screen_regs; } extern "C" void osSetEventMesg(RDRAM_ARG OSEvent event_id, PTR(OSMesgQueue) mq_, OSMesg msg) { @@ -198,8 +200,9 @@ void vi_thread_func() { next = std::chrono::high_resolution_clock::now(); } ultramodern::sleep_until(next); + auto time_now = ultramodern::time_since_start(); // Calculate how many VIs have passed - uint64_t new_total_vis = (ultramodern::time_since_start() * (60 * ultramodern::get_speed_multiplier()) / 1000ms) + 1; + uint64_t new_total_vis = (time_now * (60 * ultramodern::get_speed_multiplier()) / 1000ms) + 1; if (new_total_vis > total_vis + 1) { //printf("Skipped % " PRId64 " frames in VI interupt thread!\n", new_total_vis - total_vis - 1); } @@ -212,12 +215,13 @@ void vi_thread_func() { odd = !odd; } + // Queue a screen update for the graphics thread with the current VI register state. + // Doing this before the VI update is equivalent to updating the screen after the previous frame's scanout finished. + events_context.action_queue.enqueue(ScreenUpdateAction{ events_context.vi.regs }); + // Update VI registers and swap VI modes. events_context.vi.update_vi(); - // Queue a screen update for the graphics thread. - events_context.action_queue.enqueue(ScreenUpdateAction{ }); - // If the game has started, handle sending VI and AI events. if (ultramodern::is_game_started()) { remaining_retraces--; @@ -226,13 +230,12 @@ void vi_thread_func() { std::lock_guard lock{ events_context.message_mutex }; ViState* cur_state = events_context.vi.get_cur_state(); if (remaining_retraces == 0) { - remaining_retraces = cur_state->retrace_count; - if (cur_state->mq != NULLPTR) { if (osSendMesg(PASS_RDRAM cur_state->mq, cur_state->msg, OS_MESG_NOBLOCK) == -1) { //printf("Game skipped a VI frame!\n"); } } + remaining_retraces = cur_state->retrace_count; } if (events_context.ai.mq != NULLPTR) { if (osSendMesg(PASS_RDRAM events_context.ai.mq, events_context.ai.msg, OS_MESG_NOBLOCK) == -1) { @@ -370,7 +373,7 @@ void gfx_thread_func(uint8_t* rdram, moodycamel::LightweightSemaphore* thread_re // printf("Renderer ProcessDList time: %d us\n", static_cast(std::chrono::duration_cast(renderer_end - renderer_start).count())); } else if (const auto* screen_update_action = std::get_if(&action)) { - (void)screen_update_action; + events_context.vi.update_screen_regs = screen_update_action->regs; renderer_context->update_screen(); display_refresh_rate = renderer_context->get_display_framerate(); resolution_scale = renderer_context->get_resolution_scale(); diff --git a/ultramodern/src/timer.cpp b/ultramodern/src/timer.cpp index 1b4e9ae..d36ffcc 100644 --- a/ultramodern/src/timer.cpp +++ b/ultramodern/src/timer.cpp @@ -13,6 +13,8 @@ // Start time for the program static std::chrono::high_resolution_clock::time_point start_time = std::chrono::high_resolution_clock::now(); +// Offset of the duration since program start used to calculate the value for osGetTime. +static int64_t ostime_offset = 0; // Game speed multiplier (1 means no speedup) constexpr uint32_t speed_multiplier = 1; // N64 CPU counter ticks per millisecond @@ -162,12 +164,20 @@ extern "C" u32 osGetCount() { return (uint32_t)total_count; } +extern "C" void osSetCount(u32 count) { + assert(false); +} + extern "C" OSTime osGetTime() { - uint64_t total_count = time_now(); + uint64_t total_count = time_now() - ostime_offset; return total_count; } +extern "C" void osSetTime(OSTime t) { + ostime_offset = time_now() - t; +} + extern "C" int osSetTimer(RDRAM_ARG PTR(OSTimer) t_, OSTime countdown, OSTime interval, PTR(OSMesgQueue) mq, OSMesg msg) { OSTimer* t = TO_PTR(OSTimer, t_);