mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-26 02:11:36 -04:00
Compose analog routes with port and post-load admission; regenerate BIOS dispatcher
This commit is contained in:
@@ -1 +1 @@
|
||||
19ca6a92f84105422cbcdf584777ddc955cf76d80f574891d090a88b88780eed
|
||||
e2bf436a77fe1e353ebd47bde9c1a99231b577c4ffd84170295866a3c8133699
|
||||
|
||||
@@ -36,6 +36,13 @@ extern int (*g_psx_bios_hle_hook)(CPUState* cpu, uint32_t phys);
|
||||
extern uint32_t g_psx_mod_guest_functions;
|
||||
extern int psx_mod_dispatch_guest_function(CPUState*, uint32_t);
|
||||
|
||||
/* Game function-override tier (runtime/include/func_override.h):
|
||||
* hand-written C registered against a guest address. NULL (the
|
||||
* default) = no overrides, dispatch identical to a build without
|
||||
* the tier. Returns 1 when the override handled the call; the
|
||||
* guest resumes at $ra exactly as if the original ran jr $ra. */
|
||||
extern int (*g_psx_func_override_hook)(CPUState* cpu, uint32_t phys);
|
||||
|
||||
#ifdef PSX_HAS_GAME_DISPATCH
|
||||
extern int psx_game_address_in_text(uint32_t addr);
|
||||
#endif
|
||||
@@ -9111,6 +9118,18 @@ static void psx_dispatch_impl(CPUState* cpu, uint32_t addr, uint32_t stop_addr)
|
||||
/* Byte-guarded A0/B0/C0 call-vector tail stubs. */
|
||||
if (!found && psx_bios_try_native_call_stub(cpu, addr))
|
||||
found = 1;
|
||||
/* Function-override tier: AFTER the BIOS tiers (an override
|
||||
* can never shadow a kernel service vector), BEFORE every
|
||||
* game code backend, so one address-keyed hook covers the
|
||||
* static EXE, runtime-loaded overlays and dirty RAM alike.
|
||||
* Handled (rc 1) => the override completed against guest
|
||||
* state; resume at $ra via the trampoline's normal
|
||||
* return/tail contract. rc 0 => fall through untouched. */
|
||||
if (!found && g_psx_func_override_hook &&
|
||||
g_psx_func_override_hook(cpu, addr & 0x1FFFFFFFu)) {
|
||||
cpu->pc = cpu->gpr[31];
|
||||
found = 1;
|
||||
}
|
||||
#ifdef PSX_HAS_GAME_DISPATCH
|
||||
/* Game EXEs can overlap the BIOS shell copy window at
|
||||
* physical 0x30000-0x5AFFF. If the target belongs to the
|
||||
|
||||
@@ -8416,10 +8416,8 @@ static void handle_clear_input(int id, const char *json)
|
||||
}
|
||||
if(port>=3) {send_ok(id);return;}
|
||||
if (port == 0 || port == (int)s_input_route_port) {
|
||||
s_input_route_active = 0;
|
||||
input_route_stop(&s_route);
|
||||
s_input_route_wait_load = 0;
|
||||
s_input_route_index = 0;
|
||||
s_input_route_remaining = 0;
|
||||
}
|
||||
if (port == 0 || port == 2) {
|
||||
s_pad_connected_override[1] = -1;
|
||||
@@ -8502,10 +8500,10 @@ static void handle_input_route_status(int id, const char *json)
|
||||
uint8_t ax[4] = { 0x80, 0x80, 0x80, 0x80 };
|
||||
const int axes_live = input_route_frame_axes(&s_route, ax);
|
||||
send_fmt("{\"id\":%d,\"ok\":true,\"active\":%s,\"steps\":%u,"
|
||||
"\"index\":%u,\"remaining\":%u,\"axes_live\":%s,"
|
||||
"\"port\":%u,\"waiting_for_load\":%s,\"index\":%u,\"remaining\":%u,\"axes_live\":%s,"
|
||||
"\"axes\":[%u,%u,%u,%u]}\n",
|
||||
id, s_route.active ? "true" : "false",
|
||||
(unsigned)s_route.count, (unsigned)s_route.index,
|
||||
(unsigned)s_route.count, s_input_route_port, s_input_route_wait_load?"true":"false", (unsigned)s_route.index,
|
||||
(unsigned)s_route.remaining, axes_live ? "true" : "false",
|
||||
ax[0], ax[1], ax[2], ax[3]);
|
||||
}
|
||||
@@ -17867,6 +17865,13 @@ int debug_server_get_input_override_port2(void)
|
||||
return current;
|
||||
}
|
||||
|
||||
int debug_server_get_input_override_extra(unsigned slot) {
|
||||
if(slot<2 || slot>=8 || !s_extra_active[slot])return -1;
|
||||
int value=s_extra_buttons[slot];
|
||||
if(s_extra_frames[slot]>0 && !--s_extra_frames[slot])s_extra_active[slot]=0;
|
||||
return value;
|
||||
}
|
||||
|
||||
int debug_server_get_axis_override(unsigned char st[4])
|
||||
{
|
||||
/* A route step with axes owns the sticks for the poll it drove. */
|
||||
|
||||
@@ -11,7 +11,7 @@ import tempfile
|
||||
|
||||
|
||||
def function(source, name):
|
||||
found = re.search(r"^(?:static )?(?:int|void) " + name + r"\([^\n]*\)\n\{.*?^\}",
|
||||
found = re.search(r"^(?:static )?(?:int|void) " + name + r"\([^\n]*\)\s*\{.*?^\}",
|
||||
source, re.M | re.S)
|
||||
if not found:
|
||||
raise ValueError("Missing source function: " + name)
|
||||
@@ -23,10 +23,9 @@ def main():
|
||||
parser.add_argument('--cc', required=True)
|
||||
args = parser.parse_args()
|
||||
source = (Path(__file__).resolve().parents[1] / 'src/debug_server.c').read_text()
|
||||
declarations = source.split('#define INPUT_ROUTE_MAX_STEPS', 1)[1].split(
|
||||
'static uint64_t s_input_route_start_frame = 0;', 1)[0]
|
||||
preamble = r'''
|
||||
#include <assert.h>
|
||||
#include "input_route.h"
|
||||
#include <stdint.h>
|
||||
#include <stdio.h>
|
||||
#include <stdlib.h>
|
||||
@@ -43,6 +42,9 @@ static int s_input_override=-1,s_input_override_p2=-1;
|
||||
static int s_input_frames,s_input_frames_p2,s_axis_override,s_pad_type_override=-1;
|
||||
static int s_pad_connected_override[2]={-1,-1};
|
||||
static int s_extra_active[8],s_extra_frames[8];
|
||||
static InputRoute s_route;
|
||||
static unsigned s_input_route_port=1;
|
||||
static int s_input_route_wait_load;
|
||||
static unsigned char s_axis_st[4],s_host_axes[4],s_host_lt,s_host_rt;
|
||||
static int s_host_layer;
|
||||
static uint16_t s_host_buttons;
|
||||
@@ -60,7 +62,7 @@ static int json_get_int(const char *json,const char *key,int fallback){
|
||||
'handle_clear_input', 'handle_input_route_clear', 'handle_input_route_append',
|
||||
'handle_input_route_start', 'handle_input_route_stop', 'handle_input_route_status',
|
||||
'input_route_loaded', 'debug_server_note_savestate_loaded',
|
||||
'input_route_next', 'debug_server_get_input_override',
|
||||
'input_route_poll_port', 'debug_server_get_input_override',
|
||||
'debug_server_get_input_override_port2'))
|
||||
checks = r'''
|
||||
static void queue(void){
|
||||
@@ -75,62 +77,62 @@ int main(void){
|
||||
assert(s_input_override==123&&s_input_override_p2==-1);
|
||||
s_frame_count=9;
|
||||
assert(debug_server_get_input_override_port2()==-1);
|
||||
assert(s_input_route_remaining==2);
|
||||
assert(s_route.remaining==2);
|
||||
s_frame_count=10;
|
||||
assert(debug_server_get_input_override()==123);
|
||||
assert(s_input_route_remaining==2);
|
||||
assert(s_route.remaining==2);
|
||||
assert(debug_server_get_input_override_port2()==32767);
|
||||
assert(s_input_route_remaining==1);
|
||||
assert(s_route.remaining==1);
|
||||
++s_frame_count;
|
||||
assert(debug_server_get_input_override()==123);
|
||||
assert(debug_server_get_input_override_port2()==32767);
|
||||
assert(s_input_route_index==1&&s_input_route_remaining==1);
|
||||
assert(s_route.index==1&&s_route.remaining==1);
|
||||
++s_frame_count;
|
||||
assert(debug_server_get_input_override_port2()==65535);
|
||||
assert(!s_input_route_active);
|
||||
assert(!s_route.active);
|
||||
assert(debug_server_get_input_override_port2()==-1);
|
||||
queue();handle_input_route_start(1,"{\"port\":2}");
|
||||
handle_clear_input(1,"{\"port\":1}");assert(s_input_route_active);
|
||||
handle_clear_input(1,"{\"port\":2}");assert(!s_input_route_active);
|
||||
handle_clear_input(1,"{\"port\":1}");assert(s_route.active);
|
||||
handle_clear_input(1,"{\"port\":2}");assert(!s_route.active);
|
||||
queue();handle_input_route_start(1,"{}");
|
||||
assert(s_input_route_port==1);s_input_override_p2=456;
|
||||
assert(debug_server_get_input_override_port2()==456&&s_input_route_remaining==2);
|
||||
assert(debug_server_get_input_override_port2()==456&&s_route.remaining==2);
|
||||
assert(debug_server_get_input_override()==32767);
|
||||
handle_clear_input(1,"{\"port\":2}");assert(s_input_route_active);
|
||||
handle_clear_input(1,"{\"port\":2}");assert(s_route.active);
|
||||
handle_input_route_status(1,"{}");assert(strstr(response,"\"port\":1"));
|
||||
handle_input_route_append(1,"{\"frames\":1,\"buttons\":0}");
|
||||
assert(strstr(response,"ERROR"));
|
||||
handle_input_route_stop(1,"{}");assert(!s_input_route_active);
|
||||
handle_input_route_stop(1,"{}");assert(!s_route.active);
|
||||
handle_input_route_start(1,"{\"port\":3}");
|
||||
assert(strstr(response,"ERROR")&&!s_input_route_active);
|
||||
assert(strstr(response,"ERROR")&&!s_route.active);
|
||||
handle_input_route_start(1,"{\"port\":2}");
|
||||
handle_clear_input(1,"{}");assert(!s_input_route_active);
|
||||
handle_clear_input(1,"{}");assert(!s_route.active);
|
||||
handle_input_route_clear(1,"{}");handle_input_route_start(1,"{}");
|
||||
assert(strstr(response,"empty"));
|
||||
queue();handle_input_route_start(1,"{\"port\":2,\"after_load\":1}");
|
||||
assert(s_input_route_active&&s_input_route_wait_load);
|
||||
assert(s_route.active&&s_input_route_wait_load);
|
||||
assert(strstr(response,"\"waiting_for_load\":true"));
|
||||
for(unsigned n=0;n<20;++n){++s_frame_count;assert(debug_server_get_input_override_port2()==-1);}
|
||||
assert(s_input_route_remaining==2);
|
||||
assert(s_route.remaining==2);
|
||||
handle_input_route_status(1,"{}");assert(strstr(response,"\"waiting_for_load\":true"));
|
||||
/* Route admission is independent of optional fingerprint reset. */
|
||||
debug_server_note_savestate_loaded();assert(!s_input_route_wait_load);
|
||||
assert(debug_server_get_input_override_port2()==32767&&s_input_route_remaining==1);
|
||||
assert(debug_server_get_input_override_port2()==32767&&s_route.remaining==1);
|
||||
debug_server_note_savestate_loaded(); /* An ordinary later load does not restart it. */
|
||||
assert(s_input_route_remaining==1);
|
||||
assert(s_route.remaining==1);
|
||||
assert(debug_server_get_input_override_port2()==32767);
|
||||
assert(debug_server_get_input_override_port2()==65535&&!s_input_route_active);
|
||||
assert(debug_server_get_input_override_port2()==65535&&!s_route.active);
|
||||
for(unsigned port=1;port<=2;++port){
|
||||
queue();handle_input_route_start(1,port==1?"{\"after_load\":1}":"{\"port\":2,\"after_load\":1}");
|
||||
handle_clear_input(1,port==1?"{\"port\":2}":"{\"port\":1}");assert(s_input_route_wait_load);
|
||||
handle_clear_input(1,port==1?"{\"port\":1}":"{\"port\":2}");assert(!s_input_route_wait_load&&!s_input_route_active);
|
||||
handle_clear_input(1,port==1?"{\"port\":1}":"{\"port\":2}");assert(!s_input_route_wait_load&&!s_route.active);
|
||||
}
|
||||
queue();handle_input_route_start(1,"{\"after_load\":1}");
|
||||
handle_input_route_stop(1,"{}");debug_server_note_savestate_loaded();assert(!s_input_route_active);
|
||||
handle_input_route_stop(1,"{}");debug_server_note_savestate_loaded();assert(!s_route.active);
|
||||
queue();handle_input_route_start(1,"{\"after_load\":1}");
|
||||
handle_input_route_clear(1,"{}");debug_server_note_savestate_loaded();assert(!s_input_route_active);
|
||||
queue();handle_input_route_start(1,"{\"after_load\":2}");assert(strstr(response,"ERROR")&&!s_input_route_active);
|
||||
handle_input_route_start(1,"{\"after_load\":1,\"at_frame\":10}");assert(strstr(response,"ERROR")&&!s_input_route_active);
|
||||
handle_input_route_clear(1,"{}");debug_server_note_savestate_loaded();assert(!s_route.active);
|
||||
queue();handle_input_route_start(1,"{\"after_load\":2}");assert(strstr(response,"ERROR")&&!s_route.active);
|
||||
handle_input_route_start(1,"{\"after_load\":1,\"at_frame\":10}");assert(strstr(response,"ERROR")&&!s_route.active);
|
||||
handle_input_route_start(1,"{\"after_load\":1}");s_fp_reset_on_load=1;s_fp_head=4;s_fp_total=5;
|
||||
debug_server_note_savestate_loaded();assert(!s_input_route_wait_load&&!s_fp_reset_on_load&&!s_fp_head&&!s_fp_total);
|
||||
puts("debug input route: P2 timing, isolation, deferred start, clearing and legacy P1 pass");
|
||||
@@ -139,11 +141,11 @@ int main(void){
|
||||
'''
|
||||
with tempfile.TemporaryDirectory(prefix='psx-input-route-') as directory:
|
||||
path = Path(directory) / 'route.c'
|
||||
path.write_text(preamble + '\n#define INPUT_ROUTE_MAX_STEPS' + declarations +
|
||||
'static uint64_t s_input_route_start_frame = 0;\n' + body + checks)
|
||||
path.write_text(preamble + body + checks,encoding='utf-8',newline='\n')
|
||||
executable = Path(directory) / 'route.exe'
|
||||
subprocess.run([args.cc, '-std=c11', '-Wall', '-Wextra', '-Werror', str(path),
|
||||
'-o', str(executable)], check=True)
|
||||
'-I',str(Path(__file__).resolve().parents[1]/'include'),
|
||||
str(Path(__file__).resolve().parents[1]/'src/input_route.c'),'-o', str(executable)], check=True)
|
||||
subprocess.run([str(executable)], check=True)
|
||||
|
||||
|
||||
|
||||
Reference in New Issue
Block a user