joeylib2/scripts/lib/pspGate.sh
2026-10-13 14:01:46 -05:00

94 lines
3.4 KiB
Bash

# Part of JoeyLib, Copyright (c) 2026 Scott Duensing, under the MIT License.
# See LICENSE.md for license information.
# SPDX-License-Identifier: MIT
#
# Shared by the PSP gates (scripts/bench-psp.sh, scripts/verify-psp-*.sh) and
# joey-run: run a PSP program (make psp: bin/NAME/EBOOT.PBP and its DATA)
# under PPSSPPHeadless, the installer's patched headless PPSSPP (ppsspp
# component, installer/patches/ppsspp). Source after joeyEnv.sh.
#
# PPSSPPHeadless keeps its memory stick in $HOME/.ppsspp, so it always runs
# with HOME pointing at a work folder: the program folder goes on that
# memory stick where a PSP keeps it, PSP/GAME/NAME, and runs from there
# (ms0:/PSP/GAME/NAME is its current directory, as on a PSP). Every run
# checks every memory access as a PSP does (--strict-mem, on the IR JIT,
# the core that checks alignment): an unaligned or wild access stops the
# program, and pspStrictCheck reports it.
#
# pspNeed exit 2 unless PPSSPPHeadless is installed
# pspStage WORK DIR copy the program folder DIR onto WORK's
# memory stick; prints the folder's path there
# pspRun WORK FOLDER SECS ARGS start the program in FOLDER (pspStage's) in
# the background for at most SECS seconds of
# real time, with PPSSPPHeadless's ARGS too;
# its output goes to WORK/ppsspp.log; sets
# PSP_PID
# pspWait wait for the run to end by itself
# pspStrictCheck WORK DIR 0 unless the run of the program folder DIR
# stopped at a bad memory access; then prints
# where, and the line psp-addr2line finds for
# it in the program's ELF (make/psp.mk keeps
# bin/NAME's in elf/NAME.elf)
# pspStop stop the run
PSP_PID=""
# Real seconds a run is given beyond SECS before it is killed outright.
PSP_KILL_GRACE=30
pspNeed() {
joeyNeed PPSSPP_HEADLESS emulator:psp
}
pspStage() {
local folder
folder="$1/.ppsspp/PSP/GAME/$(basename "$2")"
mkdir -p "$folder"
cp -r "$2/." "$folder/"
printf '%s\n' "$folder"
}
pspRun() {
local work="$1"
local folder="$2"
local seconds="$3"
shift 3
HOME="$work" timeout -s KILL "$((seconds + PSP_KILL_GRACE))" "$PPSSPP_HEADLESS" "$folder/EBOOT.PBP" --strict-mem --jit-ir --timeout="$seconds" "$@" > "$work/ppsspp.log" 2>&1 &
PSP_PID=$!
}
pspWait() {
wait "$PSP_PID" 2> /dev/null || true
PSP_PID=""
}
pspStrictCheck() {
local line
local pc
local elf
line="$(grep -m1 '^STRICT-MEM: ' "$1/ppsspp.log" 2> /dev/null)" || return 0
echo "$(basename "$0"): the program stopped at a bad memory access, as a PSP would:" >&2
sed -n '/^STRICT-MEM: /,/^$/p' "$1/ppsspp.log" >&2
pc="$(sed -n 's/.*, pc \([0-9a-f]*\)$/\1/p' <<< "$line")"
elf="$(dirname "$(dirname "$2")")/elf/$(basename "$2").elf"
if [ -n "$pc" ] && [ -f "$elf" ] && [ -n "${PSPDEV:-}" ]; then
echo " pc $pc: $("$PSPDEV/bin/psp-addr2line" -f -e "$elf" "0x$pc" | tr '\n' ' ')" >&2
fi
return 1
}
pspStop() {
local child
if [ -n "$PSP_PID" ]; then
if child="$(joeyChild "$PSP_PID")"; then
joeyStop "$child"
fi
joeyStop "$PSP_PID"
fi
PSP_PID=""
}