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)
This commit is contained in:
Shane Michael Mathews (Personal Account)
2026-07-10 13:10:23 -04:00
committed by GitHub
parent d50ed4e300
commit c4355c8cda
2 changed files with 55 additions and 4 deletions
+3 -3
View File
@@ -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;
+52 -1
View File
@@ -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<uint32_t>(getRegU32(&ctx, 2)), static_cast<uint32_t>(7), "scePadInfoMode CURID should return DualShock");
t.Equals(static_cast<uint32_t>(getRegU32(&ctx, 2)), static_cast<uint32_t>(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<uint8_t> 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<uint32_t>(getRegU32(&ctx, 2)), static_cast<uint32_t>(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<uint32_t>(getRegU32(&ctx, 2)), static_cast<uint32_t>(4), "scePadInfoMode CURID should return digital at open");
runPadRead(ctx, rdram);
const uint8_t *data = rdram.data() + kPadDataAddr;
t.Equals(data[1], static_cast<uint8_t>(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<uint32_t>(getRegU32(&ctx, 2)), static_cast<uint32_t>(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<uint32_t>(getRegU32(&ctx, 2)), static_cast<uint32_t>(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<uint8_t>(0x73), "mode byte should be 0x73 (analog) after SetMainMode");
closePadPort(ctx, rdram);
});
tc.Run("pad setters return success", [](TestCase &t)
{
std::vector<uint8_t> rdram(PS2_RAM_SIZE, 0);