Files
psxrecomp/tools/tests/test_overlay_widescreen_callbacks.py
Shokunin 709e04d228 Overlay shards: define psx_ws_cull_bgez and psx_ws_clip_edge_x
The emitter writes both helpers at a listed bgez_sites or
clip_edge_x_load_sites address whose instruction matches in overlay code
too, but overlay_dispatch_preamble.c.inc did not define them, so such a
shard failed to link (macOS, Windows) or to load (Linux dlopen RTLD_NOW)
and its overlay stayed interpreted.

The preamble now defines both over psx_ws_x_margin(), with the same pure
math gpu.c uses (ws_cull_edge.h, stdint-only, staged with the other
runtime headers). ws_cull_edge.h joins the shard compile surface in
codegen_hash_sources.cmake. overlay_shim_compile_contract links a shim
that calls both with the emitter's extern declarations, and
overlay_widescreen_callbacks runs them in an executable: identity with no
margin callback, widened at margin 120, interior clip edges unchanged.
Both fail without the definitions.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
2026-09-28 22:00:17 -07:00

75 lines
3.1 KiB
Python

import os
from pathlib import Path
import shutil
import subprocess
import tempfile
import unittest
class OverlayWidescreenCallbacks(unittest.TestCase):
def test_native_module_forwards_screen_bounds_and_entry_hooks(self):
root = Path(__file__).resolve().parents[2]
gcc = shutil.which('gcc') or ('C:/msys64/mingw64/bin/gcc.exe' if os.name == 'nt' else None)
if not gcc or not Path(gcc).exists():
self.skipTest('C compiler unavailable')
source = '''
#define PSX_OVERLAY_DLL_BUILD 1
#define PSX_OVERLAY_EXPORT
#include "overlay_dispatch_preamble.c.inc"
#include <assert.h>
static CPUState *seen_cpu;
static uint32_t seen_address, cycles;
static int32_t bound(int32_t x) { return x * 2; }
static int margin120(void) { return 120; }
static void advance(uint32_t n) { cycles += n; }
static void entry(CPUState *cpu, uint32_t address) {
assert(cycles == 17); seen_cpu = cpu; seen_address = address;
}
int main(void) {
CPUState cpu = {0};
OverlayCallbacks callbacks = {0};
callbacks.ws_screen_x_bound = bound;
callbacks.mod_function_entry = entry;
callbacks.advance_cycles = advance;
overlay_init(&callbacks);
assert(overlay_abi() == PSX_OVERLAY_ABI_TAG);
assert(psx_ws_screen_x_bound(-256) == -512);
assert(psx_ws_screen_x_bound(256) == 512);
psx_advance_cycles(17);
psx_mod_function_entry(&cpu, 0x80045770);
assert(seen_cpu == &cpu && seen_address == 0x80045770);
callbacks.ws_screen_x_bound = 0; callbacks.mod_function_entry = 0;
overlay_init(&callbacks);
assert(psx_ws_screen_x_bound(-256) == -256);
psx_mod_function_entry(&cpu, 0);
assert(seen_address == 0x80045770);
/* bgez_sites / clip_edge_x_load_sites in overlay code: identity with no
* margin callback (4:3 host), widened by the live margin with one. */
assert(psx_ws_cull_bgez(0u) == 1 && psx_ws_cull_bgez((uint32_t)-1) == 0);
assert(psx_ws_clip_edge_x(0u, 0x140u) == 0u);
assert(psx_ws_clip_edge_x(0x140u, 0x140u) == 0x140u);
callbacks.ws_x_margin = margin120;
overlay_init(&callbacks);
assert(psx_ws_cull_bgez((uint32_t)-120) == 1);
assert(psx_ws_cull_bgez((uint32_t)-121) == 0);
assert(psx_ws_clip_edge_x(0u, 0x140u) == (uint32_t)-120);
assert(psx_ws_clip_edge_x(0x140u, 0x140u) == 0x140u + 120u);
assert(psx_ws_clip_edge_x(98u, 0x140u) == 98u); /* interior viewport edge */
assert(psx_ws_clip_edge_x(222u, 0x140u) == 222u);
return 0;
}
'''
with tempfile.TemporaryDirectory() as tmp:
path = Path(tmp)
(path/'test.c').write_text(source, encoding='utf-8', newline='\n')
executable = path / ('test.exe' if os.name == 'nt' else 'test')
subprocess.run([gcc, '-std=c11', '-DPSX_ENABLE_BLOCK_CYCLES=1',
'-DPSX_NO_DEBUG_TOOLS', '-I'+str(root/'runtime/include'),
str(path/'test.c'), '-o', str(executable), '-lm'], check=True,
stdout=subprocess.PIPE, stderr=subprocess.PIPE)
subprocess.run([str(executable)], check=True)
if __name__ == '__main__':
unittest.main()