mirror of
https://github.com/libretro/virtualjaguar-libretro.git
synced 2026-11-07 04:36:00 -10:00
master
9
Commits
| Author | SHA1 | Message | Date | |
|---|---|---|---|---|
|
|
e873883e33 |
feat(debug): core side of the app debug interface (vjdebug.c) with socket-less halt pump
src/debug/vjdebug.c implements struct vj_debug_iface: side-effect-free peek (JaguarReadByte DEBUGGER), policy-checked poke, both RISC register banks, RISC disassembly, attach/halt-pump/breakpoints on the existing GDB halt machinery (a host can attach without a socket; GDBHalt pumps it and never blocks when nothing could resume it), a one-walk OP display-list snapshot taken before the OP rewrites HEIGHT/DATA, a blitter launch log ring, TexReplaceReload, and the frame counter. The interface is handed over once via VJ_ENV_SET_DEBUG_IFACE, only to a frontend that set VJ_CAP_DEBUG (RetroArch never does). Detached cost: one global test per OPProcessList call and one per blitter launch. Statics reset on unload and deinit. test/test_debug_iface.c drives it on yarc.j64: who gets the interface, peek == jaguarMainRAM, the poke policy, halt/step/continue/detach through a scripted host, breakpoint re-hit, OP snapshot, blit log, and an identical frame + RAM hash with everything switched on. Refs #652 Refs #833 Co-Authored-By: Claude Sonnet 5.5 <[email protected]> |
||
|
|
8b9065d86e |
feat(core): free-form clock scale, gdb timeout clamp, private frontend event channel
Parser-only emulation change: virtualjaguar_{m68k,risc}_clock_scale also accept
a percentage ("175" or "175%", clamped 25..400) after the listed values
(0.5x/1.5x/2x/3x), which map exactly as before; anything else the old ladder
did not know still resolves to stock. virtualjaguar_gdb_halt_timeout is clamped
to 3600 s.
New src/core/vj_frontend_ext.{h,c}: a RETRO_ENVIRONMENT_PRIVATE HELLO/EVENT
channel (ABI 1). retro_init asks once; a frontend that does not answer (RetroArch)
latches it off so no event is ever sent; retro_deinit resets the latch. Events
(LINK_UP/DOWN/STATS, WIRE_SPEED, CRASH_WATCHDOG) are emitted from retro_run next
to the existing log lines, which are unchanged. JLinkStatsLast() exposes the
numbers behind the stats line.
A typed link host that was never a picker row no longer draws the 'dropped off
the LAN' warning.
Refs #833 Refs #888
Co-Authored-By: Claude Sonnet 5.5 <[email protected]>
|
||
|
|
958d859ba2 |
fix(spm): exclude app/ from the SwiftPM core target
The target path is "." and SPM scans it for resources even with an explicit sources list. The standalone app's CMake build unpacks SDL3 and Dear ImGui under app/build*/_deps, which carry .metal shaders, .storyboard files and an .lproj, so `swift build` at the repo root would fail on any checkout that has built the app in-tree (same class as the tools/vendor .lproj exclusion). app/ holds no core sources; check-package-sources.sh still passes. Refs #833 Co-Authored-By: Claude Sonnet 5.5 <[email protected]> |
||
|
|
dc34531117 |
feat(jaggd): GameDrive SD-card file API against a host folder (#783)
The GameDrive's file/dir functions were stubs returning -1, so software that keeps its data on the SD card (JagOS 'Spot', streamed assets, screenshots) could not run. Blob functions 9-16, 20-23 and 26 now work against <save dir>/jaggd-sd (system dir fallback), per the new core option virtualjaguar_jgd_sd (Read/write default, Read-only, Disabled). - Call path: each of those blob slots is one backdoor write, $C000 | function. The core services the call during that write: it reads the 68K's d0/d1/a0/a1 and leaves the result in d0 via m68k_set_reg. Safe because the 68K's JERRY writes are synchronous and the stub's move.w #imm,abs.l never touches d0; only a 68K writer triggers it. Paths and buffers move through JaguarRead/WriteByte, bounds-checked. - src/core/jaggd_fs.c: FatFs semantics, matching RetroHQ's published gdbios.h (facts only, none of their files vendored): FA_* open dispositions, write/create needs a writable card, short reads succeed, FILINFO records big-endian (22/278 bytes). End of directory returns -1 with a zeroed record. Sandbox: .., drive letters, FAT-invalid chars and symlinks refused; components matched case-insensitively. - Savestate v17 (first bump of the v3.8.0 cycle): trailing "JGF1" chunk with card-relative path, access and position per handle. Restore reopens with the original access only (never create/truncate/append), re-seeks a handle already open on the same file, closes deleted ones. - Blob grows 208 -> 338 bytes (limit 510). Tests: test_jgd's probe now drives the file API through the real blob (size 7 and the read buffer are values only the host can produce; sandbox escape returns -1); new host test test_jaggd_fs covers every disposition, read-only cards, sandbox refusals, FILINFO layout, directories, the handle limit and savestate restore. Co-Authored-By: Claude Opus 5.5 <[email protected]> |
||
|
|
5656c1516f |
feat(hooks): user-supplied hook file + one patching router doc (#637) (#716)
* feat(hooks): user-supplied hook file + one patching router doc (#637) We shipped three overlapping ways to modify a game with no single door, and the question that prompted this ("can users put their own patch files in?") came from someone who works on this core -- so discoverability was the bug. Answer: yes, two ways already (cheats, soft patching); the one thing a user could NOT author was a hook. Both halves fixed. docs/patching-a-game.md routes the four cases -- cheat code, IPS/BPS/UPS, byte patch, "different emulator settings for one game" (which is a per-title default, not a patch at all). Cross-linked from README and the top of rom-patches.md / enhancement-hooks.md so any entry point reaches the router. src/core/hookfile.c parses <system_dir>/vj_hooks.txt: crc=DC187F82 hook=my-patch 0x0012A4 4E71 4E75 Line-based, NOT JSON: libretro-common vendors no JSON parser, and hand-rolling one adds a large parsing surface to a feature whose whole job is writing bytes into a ROM. Same shape as vj_netlink.txt, which this core already reads. Field splitting is hand-rolled rather than strtok_r -- that is POSIX, and msvc05/msvc10 on the GitLab buildbot is exactly where our C89 lint is structurally blind. Every fence from docs/enhancement-hooks.md is inherited, none relaxed: - Same gate, read raw via environ_cb BEFORE the file is parsed, so a file can never switch on its own gate. - expect[] mandatory; all-or-nothing per title in the applier. - Additionally all-or-nothing per FILE: any malformed line discards every hook parsed. A half-understood patch set is worse than none, and the applier's own all-or-nothing rule would otherwise be enforced over hooks we only partly parsed. - Applied once at content load, so nothing enters the savestate blob -- no new field, no version bump. - User file wins over a built-in row for the same CRC, with a LOG_WRN naming both, matching the DB's "user-set values always win" rule. - Nothing allocates. Every bound is a compile-time constant, so a file cannot drive allocation or overrun by declaring a count. - Malformed or absent file never refuses to load the game. 13 new assertions in test/test_titlehook, including the ones that matter: a malformed line discards the whole file, expect/patch length mismatch is refused, over-long hex does not overrun the arena, and >TITLEDB_MAX_HOOKS refuses the file. The parser is pure, so they drive it directly and then feed the parsed set through the real applier against a real buffer. Verified: make TEST_EXPORTS=1 test exit 0, zero FAILs; c89-lint clean on all four touched .c files. Refs #637 Co-Authored-By: Claude Opus 5 <[email protected]> * fix(build): add hookfile.c to Package.swift; guard test fopen results (#637) Three CI failures on the first push, all mine: - Package.swift repeats Makefile.common's source list (SPM cannot read a Makefile), and I updated only one side -- which is exactly what the "duplicated lists in sync" guard exists to catch, and what broke the SwiftPM build job too. One line, both green. - clang-tidy flagged four clang-analyzer-nullability warnings in the new test block: fopen results used without a NULL check. Every fixture write is now guarded, and the first one bails out rather than pretending to test a file it could not create. Verified: check-package-sources.sh OK (70 core sources agree), clang-tidy clean on all four changed files, make TEST_EXPORTS=1 test exit 0. Refs #637 Co-Authored-By: Claude Opus 5 <[email protected]> * fix(hooks): bounded name copy, not strcpy (#637) clang-analyzer-security.insecureAPI.strcpy on the CI runner. The length was already validated one branch earlier, so this was safe in fact -- but the check is right that strcpy is unbounded at the call site regardless of what the caller checked, and a parser reading an attacker-editable file is the last place to argue with that. Local clang-tidy (llvm 21) did not raise it; the CI runner's did. Worth knowing the two disagree. Verified: test_titlehook OK (0 failures), make TEST_EXPORTS=1 test exit 0, c89-lint clean. Refs #637 Co-Authored-By: Claude Opus 5 <[email protected]> --------- Co-authored-by: Claude Opus 5 <[email protected]> |
||
|
|
0d451c4e03 |
feat(debug): GDB remote stub Phase 1 — protocol engine, 68K target, loopback transport (#662)
* feat(debug): RSP packet framing for the GDB stub Refs #652 * feat(debug): RSP run-length decode and command dispatch Refs #652 * feat(debug): bounds-checked m packet for the GDB stub Every guest address is validated by the target op before any read; the engine turns refusal into E01 and never guesses a range. Refs #652 * feat(debug): 68000 register and memory target ops Refs #652 * feat(debug): loopback-only TCP transport for the GDB stub Binds 127.0.0.1 only, never INADDR_ANY. Verified by observation: netstat shows 127.0.0.1.<port> LISTEN, never *.<port>. Refs #652 * feat(debug): core options and lifecycle for the GDB stub Two Restart-gated core options (virtualjaguar_gdb_stub, virtualjaguar_gdb_port) read raw via environ_cb in retro_load_game, matching the enhancement-hook gate's reasoning: a developer-facing option must not be settable by a per-title DB row. On enable, opens the loopback listener, inits the RSP session against the Jaguar target ops, and shows a one-shot SET_MESSAGE_EXT banner. Serviced once per frame from the top of retro_run() -- one poll() and two comparisons when disabled or idle. All new statics (including the packet accumulation buffer) are reset in both retro_unload_game() and retro_deinit(), since iOS cannot dlclose the core. Verified: savestate digests are byte-identical across stub-disabled and stub-enabled-idle runs of test/tools/hires_state_digest (three checkpoints, SHA-identical output). netstat confirms the live core binds 127.0.0.1.<port>, never *.<port>. Also fixes two build-system gaps this change exposed: scripts/c89-lint.sh was missing -Isrc/debug (libretro.c now includes gdbstub.h), and Package.swift's coreSources/headerSearchPath arrays had drifted from Makefile.common per the exact class of bug scripts/check-package-sources.sh exists to catch -- it now reports clean. `swift build` itself still fails in this environment on an unrelated, pre-existing manifest error (defaultLocalization not set) reproducible on the unmodified manifest, so full SPM compilation could not be verified end-to-end here. Refs #652 * test(debug): end-to-end GDB attach probe Scripted RSP client (test/tools/gdb_attach_probe.py) since the #581 toolchain does not ship a GDB. Run live against the actual core (not just the fake-target unit tests): attach, qSupported, halt reason, 144-hex register read, memory read, and bounds refusal on a wild address -- all passed against ./virtualjaguar_libretro.dylib with the stub enabled. Answers both of docs/gdb-stub-design.md's open questions, recorded there with evidence: - #581 toolchain ships no m68k-elf-gdb (checked the built tree after `make jaguar-toolchain-build`; PIN lists five tools, none a debugger). - rln emits no DWARF: its output is classic mc68k COFF (not ELF), `-g` produced a byte-identical link to the same build without it, and no toolchain on this host can even parse the format as an object file. Every "source-level debugging" claim in the design doc is corrected to symbol-level (rln -m's load map) accordingly. Refs #652 * fix(debug): correct overclaims and harden the GDB stub's Phase 1 edges Post-review accuracy pass, three fixes: - gdbtarget.c's non-perturbing comment overclaimed: it verified every who-gated side effect but said nothing about unconditional MMIO read side effects (e.g. CD BUTCH+2's DSCNTRL ack, reached only via the 16/32-bit path this file's byte-granular reads never take). Narrowed to state exactly what was checked and what a debugger read through MMIO can still legitimately perturb, same as any processor touching it. - docs/gdb-stub-design.md: the scripted RSP probe passes without ever sending the low-level +/- ack byte real GDB requires before QStartNoAckMode is negotiated -- a real m68k-elf-gdb would stall on its first qSupported. Documented as a named Phase 2 gap rather than left implicit in a probe that doesn't check for it. - gdbsock.c: added the same #pragma comment(lib, "ws2_32.lib") under _MSC_VER that jlink_tcp.c (the file this one deliberately mirrors) already carries, for parity on a raw MSVC compile outside the Makefile's own -lws2_32 link line. - libretro.c: a hand-edited config with a non-numeric gdb_port value used to silently ask the OS for an ephemeral port while the startup banner kept reporting the bogus requested number; now rejected in favour of the documented default. Also confirmed jni/Android.mk and the MSVC buildbot targets both build through Makefile.common's own SOURCES_C/INCFLAGS rather than a third hand-maintained list, so neither needed the src/debug update Package.swift did in the previous commit. No behavior change to the wire protocol or the packet-framing engine. Determinism re-verified byte-identical after these edits. Refs #652 * ci(msvc): compile src/debug in the MSVC check libretro.c now includes gdbstub.h, but the MSVC job's hand-maintained source list and include flags had no src\\debug -- so cl.exe failed to open the header on both x64 and x86 while every other target built fine. Same class of drift as the SIMD blitter include-path miss: Makefile.common carries the real source list, and these by-hand CI compiles silently fall behind it. Only the first cl.exe invocation needs it; the second compiles just the blitter for SSE2. Refs #652 |
||
|
|
9439e50947 |
fix(spm): exclude tools/ so a fetched Jaguar toolchain doesn't break the manifest
`swift build --target virtualjaguar` failed with
manifest property 'defaultLocalization' not set; it is required in the
presence of localized resources
but only on machines that had run tools/jaguar-toolchain/setup.sh. The fetched
toolchain lands at
tools/vendor/jaguar-toolchain/new_bjl/macos_helper/launcher.app/
Contents/Resources/English.lproj
and SPM reads that .lproj as a localized resource, which hard-errors the
manifest before a single file compiles.
The load-bearing fact, because the comment this replaces got it wrong: an
explicit `sources:` array does NOT stop SPM's resource scan. SPM walks the
entire target `path` for resources independently of `sources:`, and `exclude:`
is the only thing that stops it. With `path: "."` that scan covers the whole
repo, so any .lproj anywhere outside the exclude list breaks the build.
Fixed by excluding tools/ rather than by adding `defaultLocalization: "en"`.
Verified that the resource scan does respect `exclude`, which makes
defaultLocalization not merely redundant but worse: it would make SPM *accept*
the toolchain's InfoPlist.strings as a resource of the C library instead of
ignoring it, giving the core a resource bundle it has no business carrying.
Kept to the one demonstrated offender -- a sweep of docs/, site/, test/ etc.
would add unexplained entries without closing the class anyway, since a .lproj
under src/ would still break it and src/ cannot be excluded.
Verified on macOS with the Xcode toolchain, each run with a fresh
--cache-path/--scratch-path so nothing came from a cached package graph:
- offending path present, before fix -> the defaultLocalization error
- offending path present, after fix -> build complete
- offending path absent, after fix -> build complete
- scripts/check-package-sources.sh -> OK, 66 core + 13 libretro-common
Note that CI cannot catch this class: /tools/vendor/ is gitignored and the
spm-consumer job resolves the package by version from a clean tree, so the
directory that triggers it never exists there. It surfaces only locally, and
only for developers who have fetched the assembler toolchain.
Co-Authored-By: Claude Opus 5 <[email protected]>
|
||
|
|
5f0925f058 |
build(spm): drop unsafeFlags -- they make the package unconsumable by version
Kimi review on #625, and it is a real defect that defeats the point of the PR. SwiftPM REFUSES to build a VERSIONED dependency (`from: "x.y.z"`) on any package whose targets carry unsafe flags: error: 'app': the target 'virtualjaguar-libchdr' in product 'VirtualJaguar' contains unsafe build flags Only path and branch/revision dependencies are exempt. So a frontend doing the natural thing -- pinning a release tag of this repo -- could not build, which is exactly the "depend on it as a package instead of vendoring it" workflow this manifest exists to enable. My verification could not have caught it: `swift build` inside the repo builds this as the ROOT package, and the root is exempt from the restriction. The spm-build CI job had the same blind spot. Same shape as the boolean.h correction earlier on this branch -- a test that structurally cannot observe the failure it is cited as evidence against. The flags only silenced unused-function/variable warnings in libchdr's unity TU. Not worth making the package unusable; removed. Added a CI job that consumes the package the way a frontend actually would -- a throwaway tagged clone plus a tiny consumer depending on it by version -- so the restriction is tested rather than remembered. Two things about that job were established by experiment rather than assumed: - It must run `swift build`, NOT `swift package resolve`. Resolve SUCCEEDS with unsafe flags present; the graph is only validated for a build. - The clone goes in a directory named after the repo, because SwiftPM derives package identity from the URL's last path component and not from `name:` in the manifest. Also from the same review: check-package-sources.sh now uses `set -eu` rather than `set -e`, and `command rm` in its trap. An unset variable expanding to empty is precisely how a drift guard starts silently passing on nothing, and this repo's shells alias rm to `rm -i`, which blocks forever with no tty. These guard scripts get copied from, so the precedent matters. swift build (root) clean scripts/check-package-sources.sh 66 core + 13 libretro-common agree Co-Authored-By: Claude Opus 5 <[email protected]> |
||
|
|
534ded45f9 |
build: ship a Package.swift so Apple frontends stop vendoring this repo
Closes the last item on #614. Apple consumption of this core currently requires a fork branch plus a downstream manifest that re-declares every source file. That second copy of the file list is the problem: the Provenance wrapper kept one and drifted by 27 files over seven releases before anyone noticed, and a missing -DBLITTER_SIMD_<ARCH> in it meant every arm64 build inlined the SCALAR blitter while the linked vtable was NEON (#612). This manifest builds the C core only -- no Swift, no frontend glue -- so a frontend depends on it as a package instead of vendoring it. It is inert for every other build system: make, jni/Android.mk and the MSVC projects never read it, and nothing here changes a flag they use. Two guards, because a second copy of a list that changes every release is what rots: scripts/check-package-sources.sh diffs Package.swift's arrays against Makefile.common, which stays the single source of truth. It fails loudly and names the files, never auto-fixes -- same posture as the cd-boot-matrix and version_fallback guards. Verified it catches both directions: a file added to Makefile.common but forgotten here, and a stale entry here that Makefile.common dropped. A new spm-build CI job actually runs `swift build`. Nothing else in CI reads this file, so without it a manifest that does not even parse would merge unnoticed and only break the frontend. That job also asserts exactly one blitter_simd_ops definition survives when all three SIMD variants are compiled together -- the invariant #617's arch guards exist to hold, which no Makefile target can check because it compiles only one variant. Target names are prefixed (virtualjaguar-libretro-common, virtualjaguar-libchdr). SPM requires them unique across the consuming app's whole package graph, and generic names collide: a frontend carrying two cores that each vendor CHD cannot build if both call the target "libchdr". That is not hypothetical -- it broke the Provenance build this week. path "." for the core target because libretro.c sits at the repo root and SPM requires sources under the target path. The explicit sources list means only named files compile, so docs/, test/ and scripts/ are ignored; the two subdirectories owned by other targets are excluded so SPM does not reject overlapping paths. HAVE_COCOATOUCH and __GCCUNIX__, which the downstream manifest defines, are deliberately absent: neither appears anywhere in the core sources. swift build clean, 80 files scripts/check-package-sources.sh 66 core + 13 libretro-common agree make -j 0 errors (unaffected) make TEST_EXPORTS=1 test 0 test(s) failed one vtable, and it is NEON on arm64 verified via nm on the SPM objects blitter hot path 47 NEON vector instructions Co-Authored-By: Claude Opus 5 <[email protected]> |