From c4355c8cdac0f6bbcdb6bd8b51da537e92984287 Mon Sep 17 00:00:00 2001 From: "Shane Michael Mathews (Personal Account)" Date: Fri, 10 Jul 2026 13:10:23 -0400 Subject: [PATCH] Pads open in DIGITAL mode, not analog (spec 06) (#153) Real PS2 pads power up in DIGITAL mode (CURID 4, mode byte 0x41) and only switch to analog (CURID 7, 0x73) when the game calls scePadSetMainMode. The three open-time sites in Pad.cpp incorrectly set analogMode=true, so scePadInfoMode(PAD_MODECURID) reported analog from frame 0. Games that gate logic on the analog transition (e.g. DQ8 fn_165b20) saw an impossible state. - PadPortState default, initializePadPortLocked, scePadPortOpen: analogMode=false - scePadSetMainMode remains the only path into analog mode (unchanged) - Updated the existing pad-info test's at-open CURID expectation (7 -> 4) - Added a regression test: open reports digital (CURID 4, 0x41), then scePadSetMainMode(mode=1) switches to analog (CURID 7, 0x73) --- ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp | 6 +-- ps2xTest/src/pad_input_tests.cpp | 53 +++++++++++++++++++++++- 2 files changed, 55 insertions(+), 4 deletions(-) diff --git a/ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp b/ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp index b40edd5..681f068 100644 --- a/ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp +++ b/ps2xRuntime/src/lib/Kernel/Stubs/Pad.cpp @@ -45,7 +45,7 @@ namespace ps2_stubs struct PadPortState { bool open = false; - bool analogMode = true; + bool analogMode = false; // real pads power up DIGITAL (CURID=4, mode 0x41) bool pressureEnabled = false; bool lastUsedOverride = false; bool lastUsedBackend = false; @@ -216,7 +216,7 @@ namespace ps2_stubs void initializePadPortLocked(PadPortState &portState, uint32_t dmaAddr) { portState.open = true; - portState.analogMode = true; + portState.analogMode = false; // real pads open DIGITAL portState.pressureEnabled = false; portState.buttonMask = 0xFFFFu; portState.dmaAddr = dmaAddr; @@ -610,7 +610,7 @@ namespace ps2_stubs } portState->open = true; - portState->analogMode = true; + portState->analogMode = false; // real pads open DIGITAL portState->pressureEnabled = false; portState->buttonMask = 0xFFFFu; portState->dmaAddr = dmaAddr; diff --git a/ps2xTest/src/pad_input_tests.cpp b/ps2xTest/src/pad_input_tests.cpp index 3a3c2aa..07f9b7a 100644 --- a/ps2xTest/src/pad_input_tests.cpp +++ b/ps2xTest/src/pad_input_tests.cpp @@ -258,7 +258,7 @@ void register_pad_input_tests() setRegU32(ctx, 6, 1); setRegU32(ctx, 7, 0); ps2_stubs::scePadInfoMode(rdram.data(), &ctx, nullptr); - t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(7), "scePadInfoMode CURID should return DualShock"); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(4), "scePadInfoMode CURID should return digital at open"); setRegU32(ctx, 4, 0u); setRegU32(ctx, 5, 0u); @@ -303,6 +303,57 @@ void register_pad_input_tests() closePadPort(ctx, rdram); }); + tc.Run("pads open in digital mode and switch to analog on scePadSetMainMode", [](TestCase &t) + { + std::vector rdram(PS2_RAM_SIZE, 0); + R5900Context ctx; + + ps2_stubs::scePadInit(rdram.data(), &ctx, nullptr); + openPadPort(ctx, rdram); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + ps2_stubs::scePadGetState(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(6), "freshly opened port should report STABLE"); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, 1); + setRegU32(ctx, 7, 0); + ps2_stubs::scePadInfoMode(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(4), "scePadInfoMode CURID should return digital at open"); + + runPadRead(ctx, rdram); + const uint8_t *data = rdram.data() + kPadDataAddr; + t.Equals(data[1], static_cast(0x41), "mode byte should be 0x41 (digital) at open"); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, 1); + setRegU32(ctx, 7, 3); + ps2_stubs::scePadSetMainMode(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(1), "scePadSetMainMode should succeed switching to analog"); + + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + setRegU32(ctx, 6, 1); + setRegU32(ctx, 7, 0); + ps2_stubs::scePadInfoMode(rdram.data(), &ctx, nullptr); + t.Equals(static_cast(getRegU32(&ctx, 2)), static_cast(7), "scePadInfoMode CURID should return analog after SetMainMode"); + + // scePadSetMainMode queues a one-shot EXECCMD transient state; pump scePadGetState + // once so the port settles back to STABLE before reading, mirroring the existing + // "pad command state reports EXECCMD once before returning STABLE" test. + setRegU32(ctx, 4, 0u); + setRegU32(ctx, 5, 0u); + ps2_stubs::scePadGetState(rdram.data(), &ctx, nullptr); + + runPadRead(ctx, rdram); + t.Equals(data[1], static_cast(0x73), "mode byte should be 0x73 (analog) after SetMainMode"); + + closePadPort(ctx, rdram); + }); + tc.Run("pad setters return success", [](TestCase &t) { std::vector rdram(PS2_RAM_SIZE, 0);