Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
43 changes: 43 additions & 0 deletions docs/regional/low-input-latency.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
# Low Input Latency

Optional, default-off fresher resolved digital P1 input. The original pad reader,
button-edge logic, dialogue locks, actions and rumble remain in control; no extra
simulation or rendering is introduced. US/Japan retain 60 Hz and Europe 50 Hz.

Verified original-byte profiles cover USA, all five EU1 languages, both EU2
revisions and their five languages, and Japan. European routines differ only in
verified data relocations. Japan retains its original packed P1/P2 BIOS producer;
the checked cached-P1 store is observed in both original loop callers. Its
legacy ReadPad button bytes are converted to the canonical host layout before
priming and back to the original layout before the guest store. P2 stays original.

Requires the companion psxrecomp digital-pad snapshot API. Rebuild the runtime
and overlays with the added mod_instruction_sites. Never rename old native DLLs
into a new configuration namespace. Whole-function contracts and device checks
fall back to original input when a profile or controller mode is unsupported.

Short controller-only LOAD checks pass for USA and English EU2 original revision:
walking, direction changes, jump/landing, and observed input edges. English EU1
also has an owner-confirmed responsiveness improvement. The other European
languages and EU2 Rev 1 have original-byte contract coverage, not new gameplay
acceptance. Japan's original menu lists the save, but loading into controllable
gameplay remains unconfirmed after the bounded follow-up, including the byte-
layout correction. These checks are not campaign certification.

Coverage is offline single-player digital P1. Analog/Hybrid, registered controller
transforms or sources, configured multiple pads, multitap, netplay, replay and
input guards retain original input. Unknown guest-only multiplayer patches are
not validated by this API.
Short observed taps are latched; discontinuities re-prime without replaying old
edges. No host API can recover input that the operating system never delivered.

The engine regression is digital_pad_snapshot_test. The optional title fixture
eu/tests/test_low_input_latency.c accepts owner-extracted regional executables
and checks original-byte contracts, preserved registers and fallback paths.
Retail bytes are never distributed. Internal timings are not controller-to-TV
measurements. Frame interpolation is independent and is not part of this change.

The validated US/EU production paths are unchanged; Japan only received the
necessary legacy-button byte-layout correction. Re-polling SDL inside the guest hook
or changing presentation timing needs separate correctness work; neither is an
additional proven safe latency reduction in this change.
20 changes: 20 additions & 0 deletions eu/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -96,6 +96,7 @@ if(TOMBI_PC_FEATURES)
endif()
# Address-free host policy; guest widescreen hooks use checked EU adapters.
target_sources(${tombi_target} PRIVATE
"${TOMBA_ROOT}/src/mods/tomba_low_input_latency_plugin.c"
"${TOMBA_ROOT}/src/mods/tomba_hybrid_controller_plugin.c"
"${TOMBA_ROOT}/src/mods/tomba_skip_fmv_plugin.c")
if(TARGET ${tombi_target}-pgxp)
Expand All @@ -105,6 +106,25 @@ if(TOMBI_PC_FEATURES)
endif()
include(CTest)
if(BUILD_TESTING)
add_executable(tombi-low-input-tests tests/test_low_input_latency.c)
target_include_directories(tombi-low-input-tests PRIVATE
"${PSXRECOMP_ROOT}/runtime/include")
target_compile_options(tombi-low-input-tests PRIVATE
$<$<C_COMPILER_ID:MSVC>:/UNDEBUG>
$<$<NOT:$<C_COMPILER_ID:MSVC>>:-UNDEBUG>)
set(TOMBI_INPUT_TEST_MAIN_DIR "" CACHE PATH
"Optional owner-extracted EU1 MAIN0..4 directory")
if(TOMBI_INPUT_TEST_MAIN_DIR)
add_test(NAME tombi_low_input COMMAND tombi-low-input-tests
"${TOMBI_INPUT_TEST_MAIN_DIR}/MAIN0.EXE"
"${TOMBI_INPUT_TEST_MAIN_DIR}/MAIN1.EXE"
"${TOMBI_INPUT_TEST_MAIN_DIR}/MAIN2.EXE"
"${TOMBI_INPUT_TEST_MAIN_DIR}/MAIN3.EXE"
"${TOMBI_INPUT_TEST_MAIN_DIR}/MAIN4.EXE")
else()
add_test(NAME tombi_low_input COMMAND tombi-low-input-tests)
endif()
set_tests_properties(tombi_low_input PROPERTIES SKIP_RETURN_CODE 77)
add_executable(tombi-widescreen-identity-tests tests/test_widescreen_identity.c
"${tombi_ws}/eu_widescreen.c")
target_include_directories(tombi-widescreen-identity-tests PRIVATE
Expand Down
2 changes: 2 additions & 0 deletions eu/low_input_latency.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
/* Regional input implementation is shared; original readers still execute. */
#include "../src/mods/tomba_low_input_latency_plugin.c"
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
format_version = 5
id = "tombi.enhancement.low-input-latency"
version = "0.1.0"
name = "Low Input Latency"
author = "TombiRecomp contributors"
description = "Uses fresher digital input without changing the original simulation rate."
resolver = "declarative"
save_compatibility = "shared"

[[target]]
game_id = "SCES-01330"

[[feature]]
id = "low-input-latency"
name = "Low Input Latency"
description = "Reduces the original digital controller packet delay. Currently verified for English EU1 Rev 1, offline single-player D-pad. Other languages and controller modes retain original input. Applies at the next game start."
group = "Gameplay"
default_enabled = false

[[plugin]]
feature = "low-input-latency"
id = "tombi.low-input-latency"
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
format_version = 5
id = "tombi.enhancement.low-input-latency"
version = "0.1.1"
name = "Low Input Latency"
author = "TombiRecomp contributors"
description = "Uses fresher digital input without changing the original simulation rate."
resolver = "declarative"
save_compatibility = "shared"

[[target]]
game_id = "SCUS-94236"

[[target]]
game_id = "SCES-01330"

[[target]]
game_id = "SCES-01331"

[[target]]
game_id = "SLPS-01144"

[[feature]]
id = "low-input-latency"
name = "Low Input Latency"
description = "Reduces digital input packet delay. Verified bindings for USA, both European language sets and Japan. Offline single-player D-pad; other controller modes keep original input. Applies at the next game start."
group = "Gameplay"
default_enabled = false
[[plugin]]
feature = "low-input-latency"
id = "tombi.low-input-latency"
1 change: 1 addition & 0 deletions eu/pc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ known_md5 = ["5029ec8fee56ec6bb6316bd16ee77d54"]
known_sha1 = ["2505d958b4439d04844cadd3f968fc7432a48e00"]

[recompiler]
mod_instruction_sites = ["0x800263B4"]
bios_config = "../psxrecomp/bios/SCPH1001.toml" # ABI/address metadata only; runtime uses bundled OpenBIOS.
seeds = "seeds/boot.txt"
out_dir = "../build-eu-prototype/generated"
Expand Down
1 change: 1 addition & 0 deletions eu/release/game.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ known_md5 = ["5029ec8fee56ec6bb6316bd16ee77d54"]
known_sha1 = ["2505d958b4439d04844cadd3f968fc7432a48e00"]

[recompiler]
mod_instruction_sites = ["0x800263B4"]
seeds = "seeds/boot.txt"
out_dir = "cache/generated"
strict = true
Expand Down
1 change: 1 addition & 0 deletions eu/seamless/game.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ known_md5 = ["5029ec8fee56ec6bb6316bd16ee77d54"]
known_sha1 = ["2505d958b4439d04844cadd3f968fc7432a48e00"]

[recompiler]
mod_instruction_sites = ["0x800263B4"]
bios_config = "../../psxrecomp/bios/SCPH1001.toml" # ABI/address metadata only; runtime uses bundled OpenBIOS.
seeds = "../seeds/boot.txt"
out_dir = "../../build-eu-seamless/generated/boot"
Expand Down
79 changes: 79 additions & 0 deletions eu/tests/test_low_input_latency.c
Original file line number Diff line number Diff line change
@@ -0,0 +1,79 @@
/* Optional original-disc contract test: pass owner-extracted executables.
* No retail bytes, cards, rendering or network are needed in this fixture. */
#include <assert.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "../low_input_latency.c"
static unsigned char image[0x200000];
static uint32_t sample,last_legacy;
static int allowed,reads;
uint8_t psx_mod_read_byte(uint32_t pc) {return (pc&0x1fffffffu)<sizeof image?image[pc&0x1fffffffu]:0;}
uint16_t psx_mod_read_half(uint32_t pc) {return psx_mod_read_byte(pc)|(uint16_t)psx_mod_read_byte(pc+1)<<8;}
uint32_t psx_mod_read_word(uint32_t pc) {return psx_mod_read_half(pc)|(uint32_t)psx_mod_read_half(pc+2)<<16;}
static void put(uint32_t a,uint32_t v) {for(int i=0;i<4;++i)image[(a&0x1fffffu)+i]=(uint8_t)(v>>(8*i));}
int psx_mod_read_digital_pad(uint32_t player,PSXModDigitalPadReader *r,uint32_t old,uint32_t *out) {
(void)r;last_legacy=old;assert(player==0);++reads;*out=sample;return allowed;
}
int psx_mod_register_session_reset_plugin(const char *id,PSXModActivationCallback cb) {(void)id;(void)cb;return 1;}
int psx_mod_register_activation_plugin(const char *id,PSXModActivationCallback cb) {(void)id;(void)cb;return 1;}
int psx_mod_register_savestate_plugin(const char *id,PSXModVBlankCallback cb) {(void)id;(void)cb;return 1;}
int psx_mod_register_instruction_plugin(const char *id,uint32_t pc,uint32_t word,PSXModFunctionEntryCallback cb) {
(void)id;(void)cb;
for(unsigned i=0;i<sizeof input_profiles/sizeof *input_profiles;++i)
if(input_profiles[i].hook==pc && input_profiles[i].instruction==word)return 1;
assert(0);return 0;
}
int main(int argc,char **argv) {
if(argc<2)return 77;
for(int input=1;input<argc;++input) {
FILE *f=fopen(argv[input],"rb");assert(f);assert(!fseek(f,2048,SEEK_SET));
memset(image,0,sizeof image);assert(fread(image+0x10000,1,sizeof image-0x10000,f)>0x20000);fclose(f);
const TombaInputProfile *p=0;
for(unsigned i=0;i<sizeof input_profiles/sizeof *input_profiles;++i) {
const TombaInputProfile *q=&input_profiles[i];
if(body_hash(q->start,q->size)==q->hash && body_hash(q->extra,q->extra_size)==q->extra_hash) {p=q;break;}
}
assert(p);assert(psx_mod_read_word(p->hook)==p->instruction);
if(p->kind)put(p->field1,1);
else {image[p->field0&0x1fffffu]=0;put(p->field1,4);}
CPUState before;memset(&before,0x5a,sizeof before);before.gpr[31]=p->caller;
if(p->kind)before.gpr[4]=p->field0;
allowed=1;reads=0;
for(unsigned bit=0;bit<18;++bit) {
sample=bit<16?1u<<bit:bit==16?0:65535;
CPUState cpu=before,expected=before;
expected.gpr[2]=p->kind?((sample>>8)&0xffu)|((sample&0xffu)<<8):(uint32_t)(int32_t)(int16_t)(uint16_t)sample;
read_return(&cpu,p->hook);assert(!memcmp(&cpu,&expected,sizeof cpu));
}
assert(reads==18);
/* Both verified Japanese callers reach the same original P1 store.
* Exercise each, not just the first matching regional profile. */
if(p->kind) {
for(unsigned i=0;i<sizeof input_profiles/sizeof *input_profiles;++i) {
const TombaInputProfile *q=&input_profiles[i];
if(q->kind!=p->kind || q->hash!=p->hash || q->start!=p->start)continue;
CPUState call=before,expected=before;
call.gpr[31]=expected.gpr[31]=q->caller;
sample=0xa55au;expected.gpr[2]=0x5aa5u;
int count=reads;read_return(&call,q->hook);
assert(reads==count+1 && !memcmp(&call,&expected,sizeof call));
}
}
/* Priming/fallback receives canonical buttons while preserving the
* original guest result when the host reader declines. */
CPUState raw=before;raw.gpr[2]=0x1234u;CPUState original=raw;
allowed=0;read_return(&raw,p->hook);
assert(last_legacy==(p->kind?0x3412u:0x1234u));
assert(!memcmp(&raw,&original,sizeof raw));
CPUState cpu=before;allowed=0;read_return(&cpu,p->hook);assert(!memcmp(&cpu,&before,sizeof cpu));
allowed=1;cpu=before;cpu.gpr[31]=0;CPUState bad=cpu;reads=0;
read_return(&cpu,p->hook);assert(!reads && !memcmp(&cpu,&bad,sizeof cpu));
if(p->kind)put(p->field1,0);else put(p->field1,7);
cpu=before;reads=0;read_return(&cpu,p->hook);assert(!reads && !memcmp(&cpu,&before,sizeof cpu));
if(p->kind)put(p->field1,1);else put(p->field1,4);
image[p->start&0x1fffffu]^=1;cpu=before;reads=0;
read_return(&cpu,p->hook);assert(!reads && !memcmp(&cpu,&before,sizeof cpu));
printf("PASS original input ABI: %s\n",argv[input]);
}
}
7 changes: 6 additions & 1 deletion eu2/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,10 @@ option(TOMBI_EU2_GEOMETRY_VALIDATE "Private original-vs-HLE differential checks"
if(TOMBI_EU2_PLAYER_RELEASE AND PSX_DEBUG_TOOLS)
message(FATAL_ERROR "Player releases must use PSX_DEBUG_TOOLS=OFF")
endif()
set(eu2_mods NONE)
set(input_mods "${CMAKE_CURRENT_BINARY_DIR}/input-preloaded")
file(COPY "${TOMBA_ROOT}/mods/preloaded/packages/tombi.enhancement.low-input-latency"
DESTINATION "${input_mods}/packages")
set(eu2_mods "${input_mods}")
if(TOMBI_EU2_SEAMLESS)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
execute_process(COMMAND "${Python3_EXECUTABLE}" -I -B
Expand Down Expand Up @@ -112,3 +115,5 @@ if(TOMBI_EU2_SEAMLESS)
endif()
endif()
endif()

target_sources(tombi-eu2 PRIVATE "${TOMBA_ROOT}/src/mods/tomba_low_input_latency_plugin.c")
1 change: 1 addition & 0 deletions eu2/game.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ known_md5 = ["5cbbd8800409d992ce4b6d0df4bd7fab"]
known_sha1 = ["684dbf0ad03f872511046d40d3cc85b17d839dbf"]

[recompiler]
mod_instruction_sites = ["0x80026368"]
bios_config = "../psxrecomp/bios/SCPH1001.toml"
seeds = "seeds/boot.txt"
out_dir = "../build-eu2/generated"
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
format_version = 5
id = "tombi.enhancement.low-input-latency"
version = "0.1.1"
name = "Low Input Latency"
author = "TombiRecomp contributors"
description = "Uses fresher digital input without changing the original simulation rate."
resolver = "declarative"
save_compatibility = "shared"

[[target]]
game_id = "SCUS-94236"

[[target]]
game_id = "SCES-01330"

[[target]]
game_id = "SCES-01331"

[[target]]
game_id = "SLPS-01144"

[[feature]]
id = "low-input-latency"
name = "Low Input Latency"
description = "Reduces digital input packet delay. Verified bindings for USA, both European language sets and Japan. Offline single-player D-pad; other controller modes keep original input. Applies at the next game start."
group = "Gameplay"
default_enabled = false
[[plugin]]
feature = "low-input-latency"
id = "tombi.low-input-latency"
1 change: 1 addition & 0 deletions eu2/pc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ known_md5 = ["5cbbd8800409d992ce4b6d0df4bd7fab"]
known_sha1 = ["684dbf0ad03f872511046d40d3cc85b17d839dbf"]

[recompiler]
mod_instruction_sites = ["0x80026368"]
bios_config = "../psxrecomp/bios/SCPH1001.toml"
seeds = "seeds/boot.txt"
out_dir = "../build-eu2-seamless/generated/boot"
Expand Down
1 change: 1 addition & 0 deletions eu2/release/game.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ known_md5 = ["5cbbd8800409d992ce4b6d0df4bd7fab"]
known_sha1 = ["684dbf0ad03f872511046d40d3cc85b17d839dbf"]

[recompiler]
mod_instruction_sites = ["0x80026368"]
seeds = "seeds/boot.txt"
out_dir = "cache/generated"
strict = true
Expand Down
1 change: 1 addition & 0 deletions eu2/rev1/game.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ known_md5 = ["2456298c5fc4c7686e739f02ee165363"]
known_sha1 = ["ec50074b6fd98f611bae11e3de68d6e76ffdd6d4"]

[recompiler]
mod_instruction_sites = ["0x800263B4"]
bios_config = "../psxrecomp/bios/SCPH1001.toml"
seeds = "seeds/boot.txt"
out_dir = "../build-eu2/generated"
Expand Down
1 change: 1 addition & 0 deletions eu2/rev1/pc.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ known_md5 = ["2456298c5fc4c7686e739f02ee165363"]
known_sha1 = ["ec50074b6fd98f611bae11e3de68d6e76ffdd6d4"]

[recompiler]
mod_instruction_sites = ["0x800263B4"]
bios_config = "../psxrecomp/bios/SCPH1001.toml"
seeds = "seeds/boot.txt"
out_dir = "../build-eu2-seamless/generated/boot"
Expand Down
1 change: 1 addition & 0 deletions eu2/rev1/release/game.toml
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ known_md5 = ["2456298c5fc4c7686e739f02ee165363"]
known_sha1 = ["ec50074b6fd98f611bae11e3de68d6e76ffdd6d4"]

[recompiler]
mod_instruction_sites = ["0x800263B4"]
seeds = "seeds/boot.txt"
out_dir = "cache/generated"
strict = true
Expand Down
1 change: 1 addition & 0 deletions eu2/seamless.toml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ known_md5 = ["5cbbd8800409d992ce4b6d0df4bd7fab"]
known_sha1 = ["684dbf0ad03f872511046d40d3cc85b17d839dbf"]

[recompiler]
mod_instruction_sites = ["0x80026368"]
bios_config = "../psxrecomp/bios/SCPH1001.toml"
seeds = "seeds/boot.txt"
out_dir = "../build-eu2-seamless/generated/boot"
Expand Down
1 change: 1 addition & 0 deletions game.toml
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,7 @@ known_sha1 = [
]

[recompiler]
mod_instruction_sites = ["0x80028EEC"]
seeds = "seeds/ghidra_funcs.txt"
out_dir = "generated"
strict = true
Expand Down
7 changes: 6 additions & 1 deletion jp/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,10 @@ include("${PSXRECOMP_ROOT}/runtime/runtime.cmake")
# adapter, jp/widescreen_profile.py).
option(TOMBA_JP_ENHANCEMENTS "Seamless Loading, FMV Skip and Widescreen" OFF)
set(TOMBA_JP_GENERATED_DIR "${TOMBA_ROOT}/build-jp/generated" CACHE PATH "Japanese boot code directory")
set(jp_mods NONE)
set(input_mods "${CMAKE_CURRENT_BINARY_DIR}/input-preloaded")
file(COPY "${TOMBA_ROOT}/mods/preloaded/packages/tombi.enhancement.low-input-latency"
DESTINATION "${input_mods}/packages")
set(jp_mods "${input_mods}")
if(TOMBA_JP_ENHANCEMENTS)
find_package(Python3 REQUIRED COMPONENTS Interpreter)
get_filename_component(jp_generated "${TOMBA_JP_GENERATED_DIR}" DIRECTORY)
Expand Down Expand Up @@ -57,3 +60,5 @@ endif()
# The shared launcher already embeds the UTF-8 application manifest
# (recomp-ui windows/recomp_utf8.manifest); a second manifest resource would
# collide with it at link time.

target_sources(tomba-jp PRIVATE "${TOMBA_ROOT}/src/mods/tomba_low_input_latency_plugin.c")
1 change: 1 addition & 0 deletions jp/game.toml
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@ known_md5 = ["788922f281da3337e03c7113f9bd9bf5"]
known_sha1 = ["c86baf702303e7e0963b99aff307b1c40e40478a"]

[recompiler]
mod_instruction_sites = ["0x800207C8"]
seeds = "seeds/boot.txt"
out_dir = "../build-jp/generated"
strict = true
Expand Down
Loading