Expose analog input through debug controls

Co-authored-by: douglasjv <vanderveerdouglas@gmail.com>
This commit is contained in:
Codex
2026-07-14 10:00:51 -07:00
committed by Matthew Stanley
parent 7085721afe
commit dbaebaf37a
5 changed files with 60 additions and 6 deletions
@@ -0,0 +1,9 @@
# Analog debug input and observability
Source: [PSXRecomp PR #15](https://github.com/mstan/psxrecomp/pull/15),
commits [`df7d1faa`](https://github.com/douglasjv/psxrecomp-tweaks/commit/df7d1faa5f27be0ba357463a763c77efc43e1f91)
and [`31cdfb4f`](https://github.com/douglasjv/psxrecomp-tweaks/commit/31cdfb4f4dac305e98661dfdbf8e685533fd7ca4).
This branch lets a debug `press` carry analog axes and reports live and
overridden stick values through `pad_status`. Widescreen, overlay discovery,
and unrelated debug commands are excluded.
+1
View File
@@ -109,6 +109,7 @@ uint8_t sio_peek_rx_data(void);
/* Debug accessors: is a pad connected on the slot, and is it in analog mode. */
int sio_get_pad_connected(int slot);
int sio_get_pad_analog(int slot);
void sio_get_pad_sticks(int slot, uint8_t out[4]);
/* ---- SIO byte-level trace ring buffer ----
* 1M entries × ~28 B ≈ 32 MB. At ~600 byte/sec that's ~30 min of history. */
+20 -4
View File
@@ -6739,6 +6739,13 @@ static void handle_press(int id, const char *json)
if (buttons < 0) { send_err(id, "missing buttons"); return; }
s_input_override = buttons;
s_input_frames = frames;
int ax[4] = { json_get_int(json, "lx", -1), json_get_int(json, "ly", -1),
json_get_int(json, "rx", -1), json_get_int(json, "ry", -1) };
s_axis_override = (ax[0] >= 0 || ax[1] >= 0 || ax[2] >= 0 || ax[3] >= 0);
for (int i = 0; i < 4; i++) {
int v = ax[i] < 0 ? 0x80 : (ax[i] > 255 ? 255 : ax[i]);
s_axis_st[i] = (uint8_t)v;
}
send_ok(id);
}
@@ -6748,19 +6755,28 @@ extern uint16_t sio_get_pad_buttons(void);
extern uint16_t sio_get_pad_buttons_slot(int slot);
extern int sio_get_pad_connected(int slot);
extern int sio_get_pad_analog(int slot);
extern void sio_get_pad_sticks(int slot, uint8_t out[4]);
static void handle_pad_status(int id, const char *json)
{
(void)json;
uint16_t pad0 = sio_get_pad_buttons_slot(0);
uint16_t pad1 = sio_get_pad_buttons_slot(1);
uint8_t sticks0[4], sticks1[4];
sio_get_pad_sticks(0, sticks0);
sio_get_pad_sticks(1, sticks1);
send_fmt("{\"id\":%d,\"ok\":true,\"pad\":\"0x%04X\","
"\"slot0\":{\"buttons\":\"0x%04X\",\"connected\":%s,\"analog\":%s},"
"\"slot1\":{\"buttons\":\"0x%04X\",\"connected\":%s,\"analog\":%s},"
"\"override\":%d,\"override_frames\":%d}\n",
"\"slot0\":{\"buttons\":\"0x%04X\",\"connected\":%s,\"analog\":%s,\"sticks\":[%u,%u,%u,%u]},"
"\"slot1\":{\"buttons\":\"0x%04X\",\"connected\":%s,\"analog\":%s,\"sticks\":[%u,%u,%u,%u]},"
"\"override\":%d,\"override_frames\":%d,"
"\"override_axes\":[%u,%u,%u,%u],\"override_axes_valid\":%s}\n",
id, pad0,
pad0, sio_get_pad_connected(0) ? "true" : "false", sio_get_pad_analog(0) ? "true" : "false",
sticks0[0], sticks0[1], sticks0[2], sticks0[3],
pad1, sio_get_pad_connected(1) ? "true" : "false", sio_get_pad_analog(1) ? "true" : "false",
s_input_override, s_input_frames);
sticks1[0], sticks1[1], sticks1[2], sticks1[3],
s_input_override, s_input_frames,
s_axis_st[0], s_axis_st[1], s_axis_st[2], s_axis_st[3],
s_axis_override ? "true" : "false");
}
static void handle_clear_input(int id, const char *json)
+10
View File
@@ -675,6 +675,16 @@ int sio_get_pad_analog(int slot) {
return (slot >= 0 && slot <= 1) ? pad_analog[slot] : 0;
}
void sio_get_pad_sticks(int slot, uint8_t out[4]) {
if (!out) return;
if (slot < 0 || slot > 1) {
out[0] = out[1] = out[2] = out[3] = 0x80;
return;
}
out[0] = pad_stick[slot][0]; out[1] = pad_stick[slot][1];
out[2] = pad_stick[slot][2]; out[3] = pad_stick[slot][3];
}
/* ── LEGACY pad-config compatibility (Tomba "Hybrid" controller) ─────────────
*
* Why this exists, and why it is explicitly LEGACY:
+20 -2
View File
@@ -261,8 +261,26 @@ def build_cmd(args):
return {"cmd": "get_snapshots"}, pretty_json
elif cmd == "input":
if len(args) < 2:
return None, lambda _: "Usage: input <buttons_hex>"
return {"cmd": "set_input", "buttons": args[1]}, pretty_json
return None, lambda _: "Usage: input <buttons_hex> [lx=N ly=N rx=N ry=N]"
d = {"cmd": "set_input", "buttons": args[1]}
for item in args[2:]:
key, sep, value = item.partition("=")
if not sep or key not in ("lx", "ly", "rx", "ry"):
return None, lambda _, item=item: f"Invalid analog axis '{item}'"
d[key] = int(value, 0)
return d, pretty_json
elif cmd == "press":
if len(args) < 2:
return None, lambda _: "Usage: press <buttons_hex> [frames] [lx=N ly=N rx=N ry=N]"
has_frames = len(args) > 2 and "=" not in args[2]
d = {"cmd": "press", "buttons": int(args[1], 0),
"frames": int(args[2]) if has_frames else 1}
for item in args[3 if has_frames else 2:]:
key, sep, value = item.partition("=")
if not sep or key not in ("lx", "ly", "rx", "ry"):
return None, lambda _, item=item: f"Invalid analog axis '{item}'"
d[key] = int(value, 0)
return d, pretty_json
elif cmd == "clear_input":
return {"cmd": "clear_input"}, pretty_json
elif cmd == "ws_margin":