mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-27 18:52:11 -04:00
8mb support
This commit is contained in:
@@ -2629,11 +2629,23 @@ GeneratedFunction CodeGenerator::generate_function(
|
||||
}
|
||||
body_ss << "#endif\n";
|
||||
|
||||
// Cadence / RAM-prep mod hooks must run on CPS mid-function re-entry too:
|
||||
// AOT interior blocks still `lw` guest RAM (e.g. SimVblankGate skip). Other
|
||||
// entry hooks (debug log, widescreen) stay AFTER the switch so they only
|
||||
// fire on a true prologue entry (cpu->pc == 0).
|
||||
if (config_.mod_function_entry_funcs.count(func.start_addr)) {
|
||||
body_ss << config_.indent
|
||||
<< fmt::format(
|
||||
"psx_mod_function_entry(cpu, 0x{:08X}u);"
|
||||
" /* trusted opt-in game-mod hook (pre-CPS) */\n",
|
||||
func.start_addr);
|
||||
}
|
||||
|
||||
// CPS entry-switch: when the unified flat trampoline dispatches a
|
||||
// continuation address (a callee published cpu->pc = $ra back to us), route
|
||||
// into the owning block. Emitted BEFORE the entry hooks so a continuation
|
||||
// re-entry bypasses debug_server_log_call_entry / widescreen entry hooks
|
||||
// (those must run only on a true function entry, cpu->pc == 0).
|
||||
// into the owning block. Emitted before debug/widescreen entry hooks so a
|
||||
// continuation re-entry bypasses those (true-entry only). mod_function_entry
|
||||
// is intentionally above so cadence plugins still run.
|
||||
// Overlay mode must emit the cpu->pc guard even when the continuation set
|
||||
// is EMPTY: a range re-entry can still request an interior PC of a
|
||||
// single-block function, and without the guard the body runs from its top
|
||||
@@ -2711,13 +2723,6 @@ GeneratedFunction CodeGenerator::generate_function(
|
||||
" /* data-shard: replay or arm capture */\n",
|
||||
func.start_addr);
|
||||
}
|
||||
if (config_.mod_function_entry_funcs.count(func.start_addr)) {
|
||||
body_ss << config_.indent
|
||||
<< fmt::format(
|
||||
"psx_mod_function_entry(cpu, 0x{:08X}u);"
|
||||
" /* trusted opt-in game-mod hook */\n",
|
||||
func.start_addr);
|
||||
}
|
||||
if (config_.ws_sprite_tag_funcs.count(func.start_addr)) {
|
||||
body_ss << config_.indent
|
||||
<< "psx_ws_sprite_tag(cpu); /* widescreen: record prim ($a0) + anchor */\n";
|
||||
|
||||
@@ -43,6 +43,26 @@ void gpu_get_display_info(GpuDisplayInfo* out);
|
||||
* packed RGB888 lives in the CPU mirror; treating A0 rects as 1555 FBO uploads
|
||||
* both wastes bandwidth and force-flushes when UP_RECTS_MAX is hit (MotK FMV). */
|
||||
int gpu_display_is_depth24(void);
|
||||
/* GP1(08h) bit 3: 0 = NTSC, 1 = PAL. */
|
||||
int gpu_display_is_pal(void);
|
||||
/* Guest CPU cycles per CRTC frame (33.8688 MHz / 60 NTSC, / 50 PAL). */
|
||||
#define PSX_VBLANK_CYCLES_NTSC 564480u
|
||||
#define PSX_VBLANK_CYCLES_PAL 677376u
|
||||
uint32_t gpu_vblank_period_cycles(void);
|
||||
/*
|
||||
* Force guest cycles per VBlank regardless of GP1(08h) PAL/NTSC. 0 clears.
|
||||
* Used so NTSC display mode can keep a 50 Hz IRQ (25 fps with stock skip)
|
||||
* for audio-safe ports of PAL titles. Still divided by the Nx multiplier.
|
||||
*/
|
||||
void gpu_set_crtc_vblank_period_override(uint32_t cycles);
|
||||
uint32_t gpu_get_crtc_vblank_period_override(void);
|
||||
/*
|
||||
* Experimental enhancement: GooseStation-style Nx video timing. Multiplier 2
|
||||
* halves the guest VBlank period (NTSC 120 Hz / PAL 100 Hz CRTC). Default 1.
|
||||
* Host wall-clock pacing is separate (psx_mod_set_native_vblank_rate).
|
||||
*/
|
||||
void gpu_set_crtc_refresh_multiplier(uint32_t multiplier);
|
||||
uint32_t gpu_get_crtc_refresh_multiplier(void);
|
||||
void gpu_display_pixel_rgb(const GpuDisplayInfo* di, uint32_t x, uint32_t y,
|
||||
uint8_t* r, uint8_t* g, uint8_t* b);
|
||||
uint32_t gpu_display_pixel_argb(const GpuDisplayInfo* di, uint32_t x, uint32_t y);
|
||||
|
||||
@@ -76,7 +76,8 @@ void psx_check_interrupts_dispatch_entry(struct CPUState* cpu, uint32_t resume_p
|
||||
* Called from psx_advance_cycles() so the VBlank rate is gated on
|
||||
* guest cycles (correct PSX timing) rather than block-dispatch
|
||||
* count (which was 5-6x too fast and squeezed game-time to ~60% of
|
||||
* real). One real-PSX VBlank = 564480 cycles (33.8688 MHz / 60). */
|
||||
* real). Period follows GP1(08h) video mode: 564480 cycles NTSC
|
||||
* (33.8688 MHz / 60) or 677376 PAL (33.8688 MHz / 50). */
|
||||
void interrupts_advance_cycles(uint32_t cycles);
|
||||
void interrupts_service_scheduled_events(void);
|
||||
uint32_t interrupts_cycles_to_vblank(void);
|
||||
|
||||
@@ -103,6 +103,20 @@ int psx_mod_set_adaptive_display_aspect(uint32_t max_numerator,
|
||||
*/
|
||||
int psx_mod_set_native_vblank_rate(uint32_t frames_per_second);
|
||||
|
||||
/*
|
||||
* GooseStation-style Nx CRTC: divide guest cycles per VBlank by `multiplier`
|
||||
* (1 = stock, 2 = NTSC 120 Hz / PAL 100 Hz). Pair with
|
||||
* psx_mod_set_native_vblank_rate(base_hz * multiplier) so wall-clock and
|
||||
* guest time stay aligned; otherwise audio and realtime speed drift.
|
||||
*/
|
||||
int psx_mod_set_crtc_refresh_multiplier(uint32_t multiplier);
|
||||
/*
|
||||
* Force the guest VBlank IRQ period to a fixed CRTC rate (50 or 60 Hz), or
|
||||
* pass 0 to follow GP1(08h) again. Does not change the GPU video-mode bit —
|
||||
* NTSC display programming can keep a 50 Hz IRQ for audio-safe PAL ports.
|
||||
*/
|
||||
int psx_mod_set_crtc_vblank_hz(uint32_t hz);
|
||||
|
||||
/*
|
||||
* Enable presentation-only frame interpolation while leaving guest VBlank,
|
||||
* game logic, timers, and audio at their stock cadence. The OpenGL presenter
|
||||
|
||||
+4
-4
@@ -13,6 +13,7 @@
|
||||
#include "cdrom.h"
|
||||
#include "cdrom_irq.h"
|
||||
#include "dma.h"
|
||||
#include "gpu.h"
|
||||
#include "spu.h"
|
||||
#include "event_ring.h"
|
||||
#include "audio_trace.h"
|
||||
@@ -340,8 +341,7 @@ uint32_t g_cd_overwrite_last_frame = 0;
|
||||
* frames always advance. Paired with the per-source exception-progress
|
||||
* guarantee in interrupts.c (the real anti-starvation fix); this floor keeps
|
||||
* the exception VOLUME sane so the framerate doesn't collapse. Tunable via the
|
||||
* MAX_PER_FRAME knob. */
|
||||
#define VBLANK_CYCLES_NTSC 564480 /* 33.8688 MHz / 60 Hz; matches interrupts.c */
|
||||
* MAX_PER_FRAME knob. PAL uses the live CRTC period from gpu_vblank_period_cycles(). */
|
||||
#define CDROM_INSTANT_MAX_PER_FRAME_DEFAULT 32
|
||||
#define CDROM_SINGLE_SPEED_SECTOR_CYCLES 451584
|
||||
|
||||
@@ -511,14 +511,14 @@ void cdrom_set_instant_rate(int per_frame) {
|
||||
int cdrom_get_instant_rate(void) { return g_instant_max_per_frame; }
|
||||
|
||||
static int instant_period(void) {
|
||||
int p = VBLANK_CYCLES_NTSC / g_instant_max_per_frame;
|
||||
int p = (int)(gpu_vblank_period_cycles() / (uint32_t)g_instant_max_per_frame);
|
||||
return p < CDROM_MIN_DELAY ? CDROM_MIN_DELAY : p;
|
||||
}
|
||||
|
||||
static int warm_route_period(void) {
|
||||
const CDROMWarmRoute *route = warm_route_current();
|
||||
int rate = route ? route->rate : CDROM_INSTANT_MAX_PER_FRAME_DEFAULT;
|
||||
int p = VBLANK_CYCLES_NTSC / rate;
|
||||
int p = (int)(gpu_vblank_period_cycles() / (uint32_t)rate);
|
||||
return p < CDROM_MIN_DELAY ? CDROM_MIN_DELAY : p;
|
||||
}
|
||||
|
||||
|
||||
@@ -3095,6 +3095,43 @@ int gpu_display_is_depth24(void) {
|
||||
return (int)(display_depth & 1u);
|
||||
}
|
||||
|
||||
int gpu_display_is_pal(void) {
|
||||
return (int)(video_mode & 1u);
|
||||
}
|
||||
|
||||
/* Opt-in Nx CRTC (mod plugins). 1 = stock; 2 = half-period VBlank. */
|
||||
static uint32_t s_crtc_refresh_multiplier = 1u;
|
||||
/* 0 = derive from GP1 video_mode; else absolute period before Nx divide. */
|
||||
static uint32_t s_crtc_period_override = 0u;
|
||||
|
||||
void gpu_set_crtc_refresh_multiplier(uint32_t multiplier) {
|
||||
if (multiplier < 1u)
|
||||
multiplier = 1u;
|
||||
if (multiplier > 8u)
|
||||
multiplier = 8u;
|
||||
s_crtc_refresh_multiplier = multiplier;
|
||||
}
|
||||
|
||||
uint32_t gpu_get_crtc_refresh_multiplier(void) {
|
||||
return s_crtc_refresh_multiplier ? s_crtc_refresh_multiplier : 1u;
|
||||
}
|
||||
|
||||
void gpu_set_crtc_vblank_period_override(uint32_t cycles) {
|
||||
s_crtc_period_override = cycles;
|
||||
}
|
||||
|
||||
uint32_t gpu_get_crtc_vblank_period_override(void) {
|
||||
return s_crtc_period_override;
|
||||
}
|
||||
|
||||
uint32_t gpu_vblank_period_cycles(void) {
|
||||
const uint32_t base = s_crtc_period_override
|
||||
? s_crtc_period_override
|
||||
: (video_mode ? PSX_VBLANK_CYCLES_PAL : PSX_VBLANK_CYCLES_NTSC);
|
||||
const uint32_t mult = gpu_get_crtc_refresh_multiplier();
|
||||
return base / mult;
|
||||
}
|
||||
|
||||
void gpu_get_display_info(GpuDisplayInfo* out) {
|
||||
out->display_x = display_area_x;
|
||||
out->display_y = display_area_y;
|
||||
|
||||
+16
-10
@@ -177,8 +177,10 @@ void psx_irq_raise(uint32_t bit, uint32_t detail)
|
||||
|
||||
/* Dispatch counter for vblank scheduling. */
|
||||
#define VBLANK_INTERVAL 50000 /* legacy: dispatch-count fallback (unused for VBlank gating now) */
|
||||
#define VBLANK_CYCLES 564480u /* 33.8688 MHz / 60 Hz — real PSX NTSC VBlank period */
|
||||
#define VBLANK_DEFER_STALE_CYCLES (VBLANK_CYCLES * 10ull)
|
||||
static uint32_t vblank_cycles(void) {
|
||||
return gpu_vblank_period_cycles();
|
||||
}
|
||||
#define VBLANK_DEFER_STALE_CYCLES_FRAMES 10ull
|
||||
static uint32_t dispatch_count;
|
||||
static uint64_t total_checks;
|
||||
static uint32_t cycles_since_vblank; /* incremented by interrupts_advance_cycles */
|
||||
@@ -356,7 +358,7 @@ static int should_defer_vblank_for_sio(void) {
|
||||
uint64_t since_progress = now >= last_sio_progress_cycle
|
||||
? now - last_sio_progress_cycle
|
||||
: 0;
|
||||
return since_progress < VBLANK_DEFER_STALE_CYCLES;
|
||||
return since_progress < ((uint64_t)vblank_cycles() * VBLANK_DEFER_STALE_CYCLES_FRAMES);
|
||||
}
|
||||
|
||||
/* ---- Mid-dispatch audio pump -------------------------------------------
|
||||
@@ -390,13 +392,14 @@ static void fire_vblank_edge(void) {
|
||||
/* Subtract one VBlank period rather than reset to 0 so cycle overshoot
|
||||
* carries forward. Prevents long-running blocks from rounding multiple
|
||||
* VBlanks together. */
|
||||
cycles_since_vblank -= VBLANK_CYCLES;
|
||||
const uint32_t period = vblank_cycles();
|
||||
cycles_since_vblank -= period;
|
||||
dispatch_count = 0;
|
||||
/* DEQUEUE: this VBlank fired. ENQUEUE: next VBlank scheduled one period out. */
|
||||
event_ring_record_aux(EV_DEQ, (uint8_t)SRC_VBLANK,
|
||||
(uint32_t)psx_get_cycle_count());
|
||||
event_ring_record_aux(EV_ENQ, (uint8_t)SRC_VBLANK,
|
||||
(uint32_t)(psx_get_cycle_count() + VBLANK_CYCLES));
|
||||
(uint32_t)(psx_get_cycle_count() + period));
|
||||
psx_irq_raise(IRQ_VBLANK, 0);
|
||||
g_vblank_raise_count++;
|
||||
event_ring_record(EV_ISTAT_RAISE, IRQ_VBLANK);
|
||||
@@ -412,15 +415,17 @@ static void fire_vblank_edge(void) {
|
||||
void interrupts_service_scheduled_events(void) {
|
||||
note_sio_progress_cycle();
|
||||
if (in_exception) return;
|
||||
while (cycles_since_vblank >= VBLANK_CYCLES) {
|
||||
const uint32_t period = vblank_cycles();
|
||||
while (cycles_since_vblank >= period) {
|
||||
if (should_defer_vblank_for_sio()) return;
|
||||
fire_vblank_edge();
|
||||
}
|
||||
}
|
||||
|
||||
uint32_t interrupts_cycles_to_vblank(void) {
|
||||
if (cycles_since_vblank >= VBLANK_CYCLES) return 0;
|
||||
return VBLANK_CYCLES - cycles_since_vblank;
|
||||
const uint32_t period = vblank_cycles();
|
||||
if (cycles_since_vblank >= period) return 0;
|
||||
return period - cycles_since_vblank;
|
||||
}
|
||||
|
||||
uint32_t interrupts_get_cycles_since_vblank(void) {
|
||||
@@ -896,8 +901,9 @@ uint32_t cycles_to_next_event(void) {
|
||||
* card-SIO case only pushes VBlank LATER, so this estimate stays a safe
|
||||
* under-estimate. */
|
||||
if (i_mask & (1u << IRQ_VBLANK)) {
|
||||
uint32_t d = (cycles_since_vblank >= VBLANK_CYCLES)
|
||||
? 0u : (VBLANK_CYCLES - cycles_since_vblank);
|
||||
const uint32_t period = vblank_cycles();
|
||||
uint32_t d = (cycles_since_vblank >= period)
|
||||
? 0u : (period - cycles_since_vblank);
|
||||
if (d < best) best = d;
|
||||
}
|
||||
uint32_t t = timers_cycles_to_irq(i_mask); if (t < best) best = t;
|
||||
|
||||
+92
-19
@@ -1170,10 +1170,33 @@ static_assert((int)PSX_MOD_CONTROLLER_ANALOG ==
|
||||
static_assert((int)PSX_MOD_CONTROLLER_DIGITAL ==
|
||||
(int)PSXRecompV4::PAD_MODE_DIGITAL);
|
||||
static double g_host_refresh_hz = 0.0;
|
||||
static constexpr double PSX_FRAME_PERIOD_MS = 1000.0 / 59.94;
|
||||
static constexpr double PSX_FRAME_HZ_NTSC = 59.94;
|
||||
static constexpr double PSX_FRAME_HZ_PAL = 50.0;
|
||||
static constexpr double PSX_FRAME_PERIOD_MS = 1000.0 / PSX_FRAME_HZ_NTSC;
|
||||
static double g_frame_period_ms = PSX_FRAME_PERIOD_MS;
|
||||
static bool g_mod_native_vblank_rate = false;
|
||||
static uint32_t g_mod_native_vblank_fps = 0;
|
||||
|
||||
static double psx_crtc_frame_hz(void) {
|
||||
const uint32_t period = gpu_vblank_period_cycles();
|
||||
if (period == 0u)
|
||||
return PSX_FRAME_HZ_NTSC;
|
||||
/* 33.8688 MHz CPU clock / cycles-per-vblank. */
|
||||
return 33868800.0 / (double)period;
|
||||
}
|
||||
|
||||
static void refresh_frame_pacer_period(void) {
|
||||
if (g_mod_native_vblank_rate)
|
||||
return;
|
||||
const double psx_hz = psx_crtc_frame_hz();
|
||||
g_frame_period_ms = 1000.0 / psx_hz;
|
||||
if (g_host_refresh_hz > 0.0) {
|
||||
const double lo = psx_hz * 0.98;
|
||||
const double hi = psx_hz * 1.02;
|
||||
if (g_host_refresh_hz >= lo && g_host_refresh_hz <= hi)
|
||||
g_frame_period_ms = 1000.0 / g_host_refresh_hz;
|
||||
}
|
||||
}
|
||||
/* Activation-time request. -1 means no enabled mod owns load acceleration. */
|
||||
static int g_mod_load_wall_multiplier = -1;
|
||||
static int g_mod_load_release_frames = -1;
|
||||
@@ -1277,7 +1300,7 @@ extern "C" int psx_mod_set_adaptive_display_aspect(
|
||||
extern "C" int psx_mod_set_native_vblank_rate(
|
||||
uint32_t frames_per_second) {
|
||||
if (frames_per_second != 0 &&
|
||||
(frames_per_second < 60 || frames_per_second > 1000)) {
|
||||
(frames_per_second < 50 || frames_per_second > 1000)) {
|
||||
std::fprintf(stderr,
|
||||
"psxrecomp: mod rejected invalid native VBlank rate %u FPS\n",
|
||||
(unsigned)frames_per_second);
|
||||
@@ -1307,6 +1330,46 @@ extern "C" int psx_mod_set_native_vblank_rate(
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" int psx_mod_set_crtc_refresh_multiplier(
|
||||
uint32_t multiplier) {
|
||||
if (multiplier < 1u || multiplier > 8u) {
|
||||
std::fprintf(stderr,
|
||||
"psxrecomp: mod rejected invalid CRTC refresh multiplier %u\n",
|
||||
(unsigned)multiplier);
|
||||
return 0;
|
||||
}
|
||||
gpu_set_crtc_refresh_multiplier(multiplier);
|
||||
std::fprintf(stdout,
|
||||
"psxrecomp: mod selected CRTC refresh multiplier %u "
|
||||
"(guest VBlank period / %u)\n",
|
||||
(unsigned)multiplier, (unsigned)multiplier);
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" int psx_mod_set_crtc_vblank_hz(uint32_t hz) {
|
||||
if (hz == 0u) {
|
||||
gpu_set_crtc_vblank_period_override(0u);
|
||||
std::fprintf(stdout,
|
||||
"psxrecomp: mod cleared CRTC VBlank period override "
|
||||
"(follow GP1 video mode)\n");
|
||||
return 1;
|
||||
}
|
||||
if (hz != 50u && hz != 60u) {
|
||||
std::fprintf(stderr,
|
||||
"psxrecomp: mod rejected invalid CRTC VBlank rate %u Hz "
|
||||
"(use 50, 60, or 0)\n",
|
||||
(unsigned)hz);
|
||||
return 0;
|
||||
}
|
||||
const uint32_t cycles =
|
||||
(hz == 50u) ? PSX_VBLANK_CYCLES_PAL : PSX_VBLANK_CYCLES_NTSC;
|
||||
gpu_set_crtc_vblank_period_override(cycles);
|
||||
std::fprintf(stdout,
|
||||
"psxrecomp: mod forced CRTC VBlank to %u Hz (%u guest cycles)\n",
|
||||
(unsigned)hz, (unsigned)cycles);
|
||||
return 1;
|
||||
}
|
||||
|
||||
extern "C" int psx_mod_set_frame_interpolation(
|
||||
uint32_t frames_per_second) {
|
||||
if (frames_per_second != 0 &&
|
||||
@@ -2524,8 +2587,10 @@ static void apply_netplay_local_viewport_aspect(bool netplay_enabled) {
|
||||
* wall-clock pacer double-blocks the vblank callback (present is before the
|
||||
* guest resumes), which shows up as MotK FMV ~30–40 FPS in netplay vs ~50+
|
||||
* offline. Force immediate swaps for the session; restore on soft-exit. */
|
||||
static int host_refresh_is_approx_60hz(void) {
|
||||
return g_host_refresh_hz >= 58.8 && g_host_refresh_hz <= 61.2;
|
||||
static int host_refresh_matches_crtc(void) {
|
||||
const double psx_hz = psx_crtc_frame_hz();
|
||||
return g_host_refresh_hz >= psx_hz * 0.98 &&
|
||||
g_host_refresh_hz <= psx_hz * 1.02;
|
||||
}
|
||||
|
||||
static int present_vsync_owns_cadence(void) {
|
||||
@@ -2537,7 +2602,7 @@ static int present_vsync_owns_cadence(void) {
|
||||
return 0;
|
||||
if (g_netplay_vsync_forced_off || psx_netplay_active())
|
||||
return 0;
|
||||
return host_refresh_is_approx_60hz();
|
||||
return host_refresh_matches_crtc();
|
||||
}
|
||||
|
||||
static int present_effective_swap_interval(void) {
|
||||
@@ -5924,7 +5989,7 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
|
||||
} else if (frequency && now - s_fps_last_time >= frequency) {
|
||||
const double seconds = (double)(now - s_fps_last_time) / (double)frequency;
|
||||
const double fps = (double)(s_frame_count - s_fps_last_frame) / seconds;
|
||||
const double speed = fps / 59.94;
|
||||
const double speed = fps / psx_crtc_frame_hz();
|
||||
double display_fps = 0.0;
|
||||
if (g_frame_interpolation && g_gl_active) {
|
||||
display_fps = g_frame_interpolation_fps > 0
|
||||
@@ -6418,12 +6483,14 @@ static NetplayVblankEpilogue sdl_vblank_present_body(void) {
|
||||
}
|
||||
|
||||
/* Offline wall-clock pacing before present. Skipped when driver vsync
|
||||
* owns cadence (~60 Hz panel) so the two waits cannot double-block.
|
||||
* owns cadence (panel near the live CRTC rate) so the two waits cannot
|
||||
* double-block.
|
||||
* Netplay paces in the epilogue AFTER present so Swap overlaps the
|
||||
* peer's guest quantum. Self-check replay runs uncapped like netplay
|
||||
* resim. */
|
||||
if (!psx_netplay_active() && !psx_selfcheck_resim_active()) {
|
||||
uint64_t perf_start = runtime_perf_section_begin();
|
||||
refresh_frame_pacer_period();
|
||||
if (!manual_turbo_active && !turbo_load_paced && present_should_wall_pace())
|
||||
frame_pacer_wait(&s_frame_pacer, g_frame_period_ms);
|
||||
runtime_perf_section_end(perf_start, &g_runtime_perf.pacer_ticks);
|
||||
@@ -6944,6 +7011,7 @@ static void sdl_vblank_present(void) {
|
||||
return;
|
||||
}
|
||||
uint64_t perf_start = runtime_perf_section_begin();
|
||||
refresh_frame_pacer_period();
|
||||
if (present_should_wall_pace())
|
||||
frame_pacer_wait(&s_frame_pacer, g_frame_period_ms);
|
||||
runtime_perf_section_end(perf_start, &g_runtime_perf.pacer_ticks);
|
||||
@@ -12726,23 +12794,28 @@ session_reboot:
|
||||
}
|
||||
psx_apply_window_icon(sdl_window, argv[0]);
|
||||
|
||||
/* Host refresh: if the panel is within ~2% of 60 Hz, record it so driver
|
||||
* vsync can own cadence (pacer skipped). Non-~60 Hz and unknown refresh
|
||||
* (common on Wayland) keep PSX 59.94 Hz pacing and force swap interval 0
|
||||
* — vsync as the clock would run the sim at the panel rate. */
|
||||
/* Host refresh: if the panel is within ~2% of the live CRTC rate (NTSC
|
||||
* 59.94 / PAL 50), record it so driver vsync can own cadence. Otherwise
|
||||
* keep the PSX CRTC period and force swap interval 0. Mods that own
|
||||
* native VBlank keep their cadence. */
|
||||
{
|
||||
SDL_DisplayMode dm;
|
||||
int disp_idx = SDL_GetWindowDisplayIndex(sdl_window);
|
||||
if (disp_idx >= 0 && SDL_GetCurrentDisplayMode(disp_idx, &dm) == 0 && dm.refresh_rate > 0) {
|
||||
double host_hz = (double)dm.refresh_rate;
|
||||
g_host_refresh_hz = host_hz;
|
||||
if (host_hz >= 58.8 && host_hz <= 61.2) {
|
||||
g_frame_period_ms = 1000.0 / host_hz;
|
||||
if (disp_idx >= 0 && SDL_GetCurrentDisplayMode(disp_idx, &dm) == 0 && dm.refresh_rate > 0)
|
||||
g_host_refresh_hz = (double)dm.refresh_rate;
|
||||
refresh_frame_pacer_period();
|
||||
if (!g_mod_native_vblank_rate) {
|
||||
const double psx_hz = psx_crtc_frame_hz();
|
||||
if (host_refresh_matches_crtc()) {
|
||||
std::printf("psxrecomp: sync-to-host-refresh: pacing to %d Hz panel "
|
||||
"(%.4f ms/frame)\n", dm.refresh_rate, g_frame_period_ms);
|
||||
"(%.4f ms/frame, %s CRTC)\n",
|
||||
(int)(g_host_refresh_hz + 0.5), g_frame_period_ms,
|
||||
gpu_display_is_pal() ? "PAL" : "NTSC");
|
||||
} else {
|
||||
std::printf("psxrecomp: host panel %d Hz not ~60 Hz; keeping PSX "
|
||||
"59.94 Hz pacing\n", dm.refresh_rate);
|
||||
std::printf("psxrecomp: host panel %.0f Hz; keeping %s %.2f Hz pacing\n",
|
||||
g_host_refresh_hz,
|
||||
gpu_display_is_pal() ? "PAL" : "NTSC",
|
||||
psx_hz);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-5
@@ -17,6 +17,7 @@
|
||||
#include "memcard.h"
|
||||
#include "debug_server.h"
|
||||
#include "event_ring.h"
|
||||
#include "gpu.h"
|
||||
#include <stdlib.h>
|
||||
#include <string.h>
|
||||
|
||||
@@ -424,7 +425,7 @@ static int sio_txn_open = 0;
|
||||
static void sio_arm_card_ct_defer_guard(void) {
|
||||
extern uint64_t psx_get_cycle_count(void);
|
||||
uint64_t now = psx_get_cycle_count();
|
||||
uint64_t until = now + 564480ull * 2ull;
|
||||
uint64_t until = now + (uint64_t)gpu_vblank_period_cycles() * 2ull;
|
||||
if (until > s_card_ct_defer_until_cyc)
|
||||
s_card_ct_defer_until_cyc = until;
|
||||
}
|
||||
@@ -547,7 +548,7 @@ static void ape_card_unstick_maybe(int allow_b4e38_synth) {
|
||||
psx_irq_raise(IRQ_SIO0, 0);
|
||||
/* ~2ms between pulses — faster than one VB/8 so two pops can land
|
||||
* before BIOS clears I_MASK.7 for good. */
|
||||
s_ape_unstick_cool_cyc = now + 564480ull / 32ull;
|
||||
s_ape_unstick_cool_cyc = now + (uint64_t)gpu_vblank_period_cycles() / 32ull;
|
||||
if (s_card_handoff_armed)
|
||||
card_handoff_push(6, (uint8_t)(a6 & 0xffu));
|
||||
if (++s_ape_torn_pulses >= 128)
|
||||
@@ -597,15 +598,15 @@ int sio_card_handoff_cap(void) { return CARD_HANDOFF_CAP; }
|
||||
int sio_card_handoff_armed(void) { return s_card_handoff_armed; }
|
||||
|
||||
int sio_hold_present_for_card(void) {
|
||||
/* NTSC VBlank period — matches interrupts.c VBLANK_CYCLES. */
|
||||
/* Current CRTC VBlank period — matches interrupts.c. */
|
||||
enum { SIO_PRESENT_HOLD_STALE_VB = 10 };
|
||||
static const uint64_t stale_cycles =
|
||||
564480ull * (uint64_t)SIO_PRESENT_HOLD_STALE_VB;
|
||||
static uint32_t s_hold_seq;
|
||||
static uint64_t s_hold_progress_cyc;
|
||||
static int s_hold_armed;
|
||||
uint32_t seq;
|
||||
uint64_t now;
|
||||
const uint64_t stale_cycles =
|
||||
(uint64_t)gpu_vblank_period_cycles() * (uint64_t)SIO_PRESENT_HOLD_STALE_VB;
|
||||
|
||||
if (!sio_card_protocol_active()) {
|
||||
s_hold_armed = 0;
|
||||
|
||||
@@ -16,6 +16,7 @@
|
||||
|
||||
#include "timers.h"
|
||||
#include "event_ring.h"
|
||||
#include "gpu.h"
|
||||
#include <string.h>
|
||||
|
||||
/* Mode register bit definitions */
|
||||
@@ -210,7 +211,7 @@ void timers_advance(uint32_t cycles) {
|
||||
timer_advance_counts(t, cycles);
|
||||
} else if (t == 1) {
|
||||
/* Timer 1 HBlank clock. NTSC has about 263 HBlanks per frame. */
|
||||
timer_advance_divided(t, cycles, 2146);
|
||||
timer_advance_divided(t, cycles, gpu_display_is_pal() ? 2157u : 2146u);
|
||||
} else if (t == 0) {
|
||||
/* Timer 0 dotclock approximation; exact divider depends on GPU mode. */
|
||||
timer_advance_divided(t, cycles, 5);
|
||||
@@ -223,7 +224,7 @@ static uint32_t timer_divisor(int t) {
|
||||
int src = (timers[t].mode >> 8) & 3;
|
||||
if (t == 2 && (src == 2 || src == 3)) return 8; /* sysclk/8 */
|
||||
if (timer_uses_sysclk(t)) return 1; /* 1 cycle = 1 tick */
|
||||
if (t == 1) return 2146; /* HBlank approximation */
|
||||
if (t == 1) return gpu_display_is_pal() ? 2157u : 2146u;
|
||||
if (t == 0) return 5; /* dotclock approximation */
|
||||
return 1;
|
||||
}
|
||||
@@ -334,9 +335,10 @@ void timers_write(uint32_t addr, uint32_t value) {
|
||||
}
|
||||
|
||||
void timers_tick(int cycles) {
|
||||
/* Timer 1 in HBlank mode: ~263 HBlanks per NTSC frame */
|
||||
/* Timer 1 in HBlank mode: one pulse per scanline this frame. */
|
||||
if (!timer_uses_sysclk(1)) {
|
||||
for (int h = 0; h < 263; h++)
|
||||
const int lines = gpu_display_is_pal() ? 314 : 263;
|
||||
for (int h = 0; h < lines; h++)
|
||||
timer_tick_one(1);
|
||||
}
|
||||
|
||||
|
||||
Reference in New Issue
Block a user