Fold the debug host layer's left stick onto the D-pad for digital pads

set_input layer=host is a default-mapped gamepad; a real one folds ls_* onto the D-pad in digital mode, the layer dropped the stick.

Co-Authored-By: Claude Opus 5.5 (1M context) <noreply@anthropic.com>
This commit is contained in:
Matthew Stanley
2026-10-24 18:42:06 -07:00
parent 4242bd10fd
commit 7ef29ad112
4 changed files with 66 additions and 1 deletions
+3
View File
@@ -900,6 +900,9 @@ if(BUILD_TESTING)
add_executable(psx_hotkey_pad_test tests/test_psx_hotkey_pad.c)
target_include_directories(psx_hotkey_pad_test PRIVATE include)
add_test(NAME psx_hotkey_pad_test COMMAND psx_hotkey_pad_test)
add_executable(psx_stick_fold_test tests/test_psx_stick_fold.c)
target_include_directories(psx_stick_fold_test PRIVATE include)
add_test(NAME psx_stick_fold_test COMMAND psx_stick_fold_test)
add_executable(psx_trigger_values_test tests/test_psx_trigger_values.c)
target_include_directories(psx_trigger_values_test PRIVATE include)
add_test(NAME psx_trigger_values_test COMMAND psx_trigger_values_test)
+23
View File
@@ -0,0 +1,23 @@
#pragma once
/* The default controller map's digital stick fold for a pad expressed as PSX
* stick bytes. main.cpp's default map binds ls_up/down/left/right to
* lefty-/lefty+/leftx-/leftx+ and folds them onto the D-pad (fold_bit) while
* the pad presents as DIGITAL, with the same per-axis test as
* controller_source_pressed_h (v > dz, v < -dz). The debug host input layer
* (set_input {"layer":"host"}) is a default-mapped gamepad, so its left stick
* must press the D-pad exactly as a physical controller's would. */
#include <stdint.h>
/* A PSX stick byte (0x00..0xFF, centre 0x80) in SDL axis units. */
static inline int psx_stick_byte_to_axis(uint8_t v) { return ((int)v - 0x80) * 256; }
/* Press (clear, active-low) the D-pad bits the left stick folds onto. */
static inline uint16_t psx_fold_left_stick_to_dpad(uint16_t word, uint8_t lx,
uint8_t ly, int deadzone_raw) {
const int x = psx_stick_byte_to_axis(lx), y = psx_stick_byte_to_axis(ly);
if (y < -deadzone_raw) word &= (uint16_t)~(1u << 4); /* Up */
if (x > deadzone_raw) word &= (uint16_t)~(1u << 5); /* Right */
if (y > deadzone_raw) word &= (uint16_t)~(1u << 6); /* Down */
if (x < -deadzone_raw) word &= (uint16_t)~(1u << 7); /* Left */
return word;
}
+7 -1
View File
@@ -94,6 +94,7 @@ extern "C" uint32_t g_guest_last_store_addr, g_guest_last_store_val;
#include "psx_trigger.h" /* continuous SDL trigger -> 0..255 magnitude */
#include "mod_pad_transform.h"
#include "psx_hotkey_pad.h" /* host shortcut bindings, direct claims */
#include "psx_stick_fold.h" /* default-map digital stick fold (host layer) */
#include "psx_controller_type.h" /* mapped wheel-name classification */
#include "psx_netplay_rb.h"
#include "psx_selfcheck.h"
@@ -6795,7 +6796,9 @@ static uint16_t host_layer_release_claimed(uint16_t word, uint32_t claimed) {
/* Stage 1 for P1 while the host layer is armed: buttons less any claimed
* shortcut button; DualShock with the layer's sticks unless P1 is digital
* (host_view, the transform's host pad: the sticks in either mode). */
* (host_view, the transform's host pad: the sticks in either mode). A digital
* P1 folds the left stick onto the D-pad like a default-mapped controller
* (psx_stick_fold.h), so a digital-only game moves from the stick here too. */
static int capture_host_layer_pad(PsxNetPad* out, uint16_t word,
const uint8_t st[4], bool guarded,
bool host_view, int present_sio_slot = 0) {
@@ -6805,6 +6808,9 @@ static int capture_host_layer_pad(PsxNetPad* out, uint16_t word,
const bool analog = mode != PSXRecompV4::PAD_MODE_DIGITAL;
const bool sticks = analog || host_view;
out->buttons = host_layer_release_claimed(word, g_p1_claimed_buttons);
if (!analog && !host_view)
out->buttons = psx_fold_left_stick_to_dpad(out->buttons, st[0], st[1],
kDefaultDeadzoneRaw);
out->lx = sticks ? st[0] : 0x80; out->ly = sticks ? st[1] : 0x80;
out->rx = sticks ? st[2] : 0x80; out->ry = sticks ? st[3] : 0x80;
out->analog = analog ? SIO_PAD_DUALSHOCK : SIO_PAD_DIGITAL;
+33
View File
@@ -0,0 +1,33 @@
/* psx_stick_fold.h: the default map's digital left-stick fold onto the D-pad. */
#include "psx_stick_fold.h"
#include <stdio.h>
static int failures = 0;
#define CHECK(c) do { if (!(c)) { fprintf(stderr, "FAIL %s:%d %s\n", __FILE__, __LINE__, #c); failures++; } } while (0)
enum { UP = 1 << 4, RIGHT = 1 << 5, DOWN = 1 << 6, LEFT = 1 << 7, CROSS = 1 << 14 };
static const int dz = 3277; /* main.cpp kDefaultDeadzoneRaw */
int main(void) {
/* Centred stick: nothing pressed, other buttons untouched. */
CHECK(psx_fold_left_stick_to_dpad(0xFFFF, 0x80, 0x80, dz) == 0xFFFF);
CHECK(psx_fold_left_stick_to_dpad((uint16_t)~CROSS, 0x80, 0x80, dz) == (uint16_t)~CROSS);
/* Full deflections press exactly one direction each. */
CHECK(psx_fold_left_stick_to_dpad(0xFFFF, 0xFF, 0x80, dz) == (uint16_t)~RIGHT);
CHECK(psx_fold_left_stick_to_dpad(0xFFFF, 0x00, 0x80, dz) == (uint16_t)~LEFT);
CHECK(psx_fold_left_stick_to_dpad(0xFFFF, 0x80, 0x00, dz) == (uint16_t)~UP);
CHECK(psx_fold_left_stick_to_dpad(0xFFFF, 0x80, 0xFF, dz) == (uint16_t)~DOWN);
/* Diagonal presses both axes. */
CHECK(psx_fold_left_stick_to_dpad(0xFFFF, 0xFF, 0x00, dz) == (uint16_t)~(RIGHT | UP));
/* Per-axis deadzone: a strong right push with small Y drift is Right only. */
CHECK(psx_fold_left_stick_to_dpad(0xFFFF, 0xFF, 0x80 + 12, dz) == (uint16_t)~RIGHT);
/* Threshold: 13 steps (3328) passes 3277, 12 steps (3072) does not. */
CHECK(psx_fold_left_stick_to_dpad(0xFFFF, 0x80 + 13, 0x80, dz) == (uint16_t)~RIGHT);
CHECK(psx_fold_left_stick_to_dpad(0xFFFF, 0x80 + 12, 0x80, dz) == 0xFFFF);
CHECK(psx_fold_left_stick_to_dpad(0xFFFF, 0x80 - 13, 0x80, dz) == (uint16_t)~LEFT);
/* An already-held D-pad bit stays held. */
CHECK(psx_fold_left_stick_to_dpad((uint16_t)~UP, 0xFF, 0x80, dz) == (uint16_t)~(UP | RIGHT));
if (failures) return 1;
puts("PASS psx_stick_fold");
return 0;
}