mirror of
https://github.com/RetroPortingToolKit/psxrecomp
synced 2026-10-28 10:59:16 -04:00
Bind full RAM diagnostics to the declared TAS return count
(cherry picked from commit 6f75d55234d1f901548a0a7c2eaf8cd2cce30feb)
This commit is contained in:
@@ -789,6 +789,11 @@ if(BUILD_TESTING)
|
||||
add_test(NAME tas_replay_recipe
|
||||
COMMAND ${Python3_EXECUTABLE}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../tools/tasreplays/test_tekken3.py")
|
||||
# The Python driver compiles test_source_ram_page_probe.c at O0/O2.
|
||||
add_test(NAME tas_ram_long_route
|
||||
COMMAND ${Python3_EXECUTABLE}
|
||||
"${CMAKE_CURRENT_SOURCE_DIR}/../runtime/tests/test_source_ram_page_probe.py"
|
||||
--cc ${CMAKE_C_COMPILER})
|
||||
endif()
|
||||
endif()
|
||||
|
||||
|
||||
@@ -17,17 +17,31 @@ static uint64_t source_ram_page_hash(const uint8_t *bytes, unsigned count) {
|
||||
static void source_ram_page_probe(unsigned frame, uint64_t cycle) {
|
||||
static int initialized, enabled;
|
||||
static FILE *stream;
|
||||
static unsigned snapshots[32], snapshot_count;
|
||||
static unsigned snapshots[32], snapshot_count, max_frames = 20000;
|
||||
if (!initialized) {
|
||||
initialized = 1;
|
||||
const char *setting = getenv("PSX_SOURCE_RAM_PAGE_PROBE");
|
||||
enabled = setting && strcmp(setting, "1") == 0;
|
||||
const char *limit = getenv("PSX_SOURCE_RAM_MAX_FRAMES");
|
||||
if (enabled && limit) {
|
||||
/* A declared route may exceed the historical short-TAS bound.
|
||||
* Parse without strtoul overflow or accepting signs/whitespace. */
|
||||
unsigned value = 0;
|
||||
if (!*limit) abort();
|
||||
for (const char *p = limit; *p; ++p) {
|
||||
if (*p < '0' || *p > '9' || value > 100000) abort();
|
||||
value = value * 10 + (unsigned)(*p - '0');
|
||||
if (value > 1000000) abort();
|
||||
}
|
||||
if (!value) abort();
|
||||
max_frames = value;
|
||||
}
|
||||
const char *cursor = getenv("PSX_SOURCE_RAM_SNAPSHOT_FRAMES");
|
||||
while (enabled && cursor && *cursor) {
|
||||
if (*cursor < '0' || *cursor > '9') abort();
|
||||
char *end;
|
||||
unsigned long value = strtoul(cursor, &end, 10);
|
||||
if (end == cursor || value < 1 || value > 20000 || snapshot_count == 32 ||
|
||||
if (end == cursor || value < 1 || value > max_frames || snapshot_count == 32 ||
|
||||
(*end && *end != ',')) abort();
|
||||
for (unsigned i = 0; i < snapshot_count; ++i)
|
||||
if (snapshots[i] == value) abort();
|
||||
@@ -37,7 +51,7 @@ static void source_ram_page_probe(unsigned frame, uint64_t cycle) {
|
||||
}
|
||||
}
|
||||
if (!enabled) return;
|
||||
if (frame < 1 || frame > 20000) abort();
|
||||
if (frame < 1 || frame > max_frames) abort();
|
||||
extern uint8_t *memory_get_ram_ptr(void);
|
||||
const uint8_t *ram = memory_get_ram_ptr();
|
||||
char path[4096];
|
||||
|
||||
@@ -0,0 +1,14 @@
|
||||
/* Exercise the production diagnostic with synthetic RAM, including long movies. */
|
||||
#include "source_ram_page_probe.h"
|
||||
#include <signal.h>
|
||||
static uint8_t test_ram[2097152];
|
||||
uint8_t *memory_get_ram_ptr(void) { return test_ram; }
|
||||
static void expected_abort(int ignored) { (void)ignored; exit(4); }
|
||||
int main(int argc, char **argv) {
|
||||
signal(SIGABRT, expected_abort);
|
||||
if (argc != 2) return 2;
|
||||
test_ram[0] = 0x34;
|
||||
test_ram[2097151] = 0xab;
|
||||
source_ram_page_probe((unsigned)strtoul(argv[1], NULL, 10), UINT64_C(40500000000));
|
||||
return 0;
|
||||
}
|
||||
@@ -0,0 +1,60 @@
|
||||
"""Long-route coverage and fail-closed bounds using the actual RAM observer."""
|
||||
import argparse
|
||||
import os
|
||||
from pathlib import Path
|
||||
import subprocess
|
||||
import tempfile
|
||||
|
||||
def fnv(data):
|
||||
h = 14695981039346656037
|
||||
for byte in data:
|
||||
h = ((h ^ byte) * 1099511628211) & 0xffffffffffffffff
|
||||
return f'{h:016X}'
|
||||
|
||||
def main():
|
||||
p = argparse.ArgumentParser()
|
||||
p.add_argument('--cc', default='gcc')
|
||||
a = p.parse_args()
|
||||
here = Path(__file__).resolve().parent
|
||||
ram = bytearray(2097152)
|
||||
ram[0], ram[-1] = 0x34, 0xab
|
||||
zero = fnv(bytes(4096))
|
||||
expected = [fnv(ram[:4096])] + [zero]*510 + [fnv(ram[-4096:])]
|
||||
cases = [('legacy',None,20000,None,0),
|
||||
('legacy_limit',None,20001,None,4),
|
||||
('pepsiman','71806',71806,'71806',0),
|
||||
('re1','227202',227202,'227202',0),
|
||||
('bound','71806',71807,None,4),
|
||||
('snapshot_bound','71806',1,'71807',4),
|
||||
('duplicate','71806',1,'71806,71806',4),
|
||||
('zero','0',1,None,4), ('negative','-1',1,None,4),
|
||||
('junk','71806x',1,None,4), ('overflow','999999999999999999999',1,None,4),
|
||||
('space',' 71806',1,None,4), ('over_cap','1000001',1,None,4)]
|
||||
with tempfile.TemporaryDirectory() as temporary:
|
||||
root=Path(temporary)
|
||||
for opt in ['-O0','-O2']:
|
||||
exe=root/('probe'+opt+('.exe' if os.name=='nt' else ''))
|
||||
subprocess.run([a.cc,'-std=c11',opt,'-Wall','-Wextra','-Werror',
|
||||
'-I'+str(here.parent/'include'),str(here/'test_source_ram_page_probe.c'),
|
||||
'-o',str(exe)],check=True)
|
||||
for name,limit,frame,snapshot,code in cases:
|
||||
out=root/(name+opt)
|
||||
out.mkdir()
|
||||
env={k:v for k,v in os.environ.items() if not k.startswith('PSX_')}
|
||||
env.update(PSX_SOURCE_RAM_PAGE_PROBE='1',PSX_INPUT_ROUTE_CAPTURE_DIR=str(out))
|
||||
if limit is not None: env['PSX_SOURCE_RAM_MAX_FRAMES']=limit
|
||||
if snapshot: env['PSX_SOURCE_RAM_SNAPSHOT_FRAMES']=snapshot
|
||||
result=subprocess.run([str(exe),str(frame)],env=env,capture_output=True,timeout=20)
|
||||
assert result.returncode==code,(opt,name,result.returncode,code,result.stderr)
|
||||
if code==0:
|
||||
rows=(out/'ram-pages.tsv').read_text().splitlines()
|
||||
assert len(rows)==3,(name,len(rows))
|
||||
row=rows[2].split('\t')
|
||||
assert row[:2]==[str(frame),'40500000000'] and row[2:]==expected,name
|
||||
if snapshot:
|
||||
assert (out/f'ram-frame-{frame:06d}.bin').read_bytes()==ram,name
|
||||
else:
|
||||
assert not (out/'ram-pages.tsv').exists(),name
|
||||
print(f'PASS: {len(cases)*2} production RAM observer cases; all 512 pages, clocks and late raw snapshots')
|
||||
|
||||
if __name__=='__main__': main()
|
||||
@@ -149,8 +149,8 @@ def main():
|
||||
raise ValueError('RAM page probe requires source raster frontend boundaries')
|
||||
if args.ram_snapshot_frame and (not args.ram_page_probe or len(args.ram_snapshot_frame)>32 or
|
||||
len(set(args.ram_snapshot_frame))!=len(args.ram_snapshot_frame) or
|
||||
any(f<1 or f>20000 for f in args.ram_snapshot_frame)):
|
||||
raise ValueError('RAM snapshots require page probe and1..32 unique frames in1..20000')
|
||||
any(f<1 for f in args.ram_snapshot_frame)):
|
||||
raise ValueError('RAM snapshots require page probe and 1..32 unique positive frames')
|
||||
if args.timer2_model!="default" and args.timer1_model!="octoshock-2.2.2":
|
||||
raise ValueError("timer2 model requires source timer1 model")
|
||||
if args.timer1_model!="default" and args.field_model!="octoshock-2.2.2-ntsc-raster":
|
||||
@@ -187,6 +187,13 @@ def main():
|
||||
if clock_tape:
|
||||
paths['cd_source_clock_tape'] = Path(clock_tape['path'])
|
||||
identity = route_identity(paths["route"])
|
||||
# The completion hook exits before the final return observer. Bind every
|
||||
# enabled RAM capture to the exact declared number of observed returns.
|
||||
ram_returns = identity['frames'] + args.neutral_tail - 1
|
||||
if args.ram_page_probe and not 1 <= ram_returns <= 1000000:
|
||||
raise ValueError('RAM capture requires 1..1000000 declared completed returns')
|
||||
if any(frame > ram_returns for frame in args.ram_snapshot_frame):
|
||||
raise ValueError('RAM snapshot exceeds the declared completed-return boundary')
|
||||
update_profile = None
|
||||
update_contexts = None
|
||||
context_values = None
|
||||
@@ -328,6 +335,7 @@ p2_mode = "digital"
|
||||
selected_env['PSX_SOURCE_CPU_RETURN_PROBE']='1'
|
||||
if args.ram_page_probe:
|
||||
selected_env['PSX_SOURCE_RAM_PAGE_PROBE']='1'
|
||||
selected_env['PSX_SOURCE_RAM_MAX_FRAMES']=str(ram_returns)
|
||||
if args.ram_snapshot_frame:
|
||||
selected_env['PSX_SOURCE_RAM_SNAPSHOT_FRAMES']=','.join(map(str,args.ram_snapshot_frame))
|
||||
if args.update_predictor != "gate":
|
||||
|
||||
Reference in New Issue
Block a user