mirror of
https://github.com/libretro/parallel-n64.git
synced 2026-11-07 12:37:50 -10:00
* Rename flat-memory dynarec shim memory.{c,h} -> m64p_memory.{c,h}
The flat-memory shim (rdword/readmem/fast_mem_access, the hacktarux/ari64
flat-memory ABI bridge) is renamed so its filename matches its role and no
longer collides with mupen64plus-next's handler-based memory.{c,h}. Sweeps the
memory.h -> m64p_memory.h include across the tree, plus the residual
exception.h -> cp0.h include fixups. Pure mechanical rename; no behavioural
change.
Author: LibretroAdmin <[email protected]>
* Relocate gb/transferpak subsystem to device/gb/ (next layout)
Moves the Game Boy cart + MBC3 RTC + GB camera (m64282fp) sources from src/gb/
to src/device/gb/ to match the mupen64plus-next tree layout, keeping the
baseline pn64 gb_cart content/API (has_rtc field, 3-arg read/write). Fixes the
transferpak + savestates include paths to the new location. transferpak savestate
format is unchanged.
Author: LibretroAdmin <[email protected]>
* Gate hacktarux JIT behind WANT_HACKTARUX (default off); ari64-only for now
Removes the hacktarux dyna_start entry stubs from the default build and adjusts
hacktarux_dynarec.c for the gated path. The Makefile gating (WANT_HACKTARUX ?= 0,
which drops -DHAVE_DYNAREC_HACKTARUX + assemble/regcache/hacktarux_dynarec.c +
the dyna_start nasm) lands with the build-wiring commit. SSE2 CPUFLAGS and the
entire new_dynarec/ari64 path are retained. hacktarux is restorable with
WANT_HACKTARUX=1 and is the planned follow-up backport.
Author: LibretroAdmin <[email protected]>
* Wire ari64-only build; restore exception.c + rdram_detection_hack; gate PASS
Makefile.common: gate hacktarux behind WANT_HACKTARUX (default 0), re-add
exception.c and rdram_detection_hack.c to SOURCES_C (the next-adoption sweep had
dropped them), and the device/gb paths. Adds the generated asm_defines headers
(x64 used by new_dynarec_64.c; arm/arm64/x86 for cross builds) and the ari64
arm64 linkage rename. Restores exception.c to the build.
Net architecture: all-pn64 r4300/cp0/cp1/interrupt/RCP/memory/device for the
new_dynarec (ari64) core, plus next's gb/transferpak. Removes the dead
next-adoption scaffolding (x86/, x86_64/, cp2, idec, vru, workqueue,
plugins_compat, file_storage) left over from the exploration.
Verified: Excitebike 64 frame-150 framebuffer MD5 d223b153cb11b48d6067ea6ec4fe4fff
under ari64, byte-identical to baseline 525ffac. 640x240 render, clean build,
0 undefined references.
Author: LibretroAdmin <[email protected]>
* r4300: include exception.h where exception_general/TLB_refill are called
The earlier include sweep rewrote exception.h -> cp0.h in cp0.c, tlb.c,
interrupt.c, cached_interp.c and pure_interp.c, but on this tree cp0.h does
not declare exception_general()/TLB_refill_exception() -- those stay in
exception.h. That produced implicit-declaration errors (cp0.c, tlb.c,
interrupt.c, and mips_instructions.def via cached_interp/pure_interp).
Add #include "exception.h" alongside cp0.h in the five callers. Builds clean
(0 errors, 0 undefined refs); Excitebike 64 frame-150 MD5
d223b153cb11b48d6067ea6ec4fe4fff under ari64.
Author: LibretroAdmin <[email protected]>
* Replace parallel-n64 emulation core with mupen64plus-next's
This replaces parallel-n64's emulation code wholesale with the actual
emulation core from mupen64plus-libretro-nx (develop), rather than the
prior approach of keeping pn64's code under next-flavoured filenames.
What changed:
Emulation core (genuine next source):
- r4300: cached_interp, cp0, cp1, cp2, idec, instr_counters, interrupt,
r4300_core, recomp, tlb, pure_interp, new_dynarec (struct-based, NEW_DYNAREC=2
with x64/linkage_x64.asm). Replaces pn64's r4300.c/reset.c/exception.c/
empty_dynarec.c (folded into device.c/cp0.c upstream) and the flat-memory
new_dynarec_64.c.
- RCP: ai/mi/pi/rdp(+fb)/ri/rsp/si/vi controllers; device.c/.h.
- memory: next's handler-based model in m64p_memory.c/.h (filename kept,
content is next's memory.c). Single contiguous g_mem_base allocation
(init_mem_base / mem_base_u32) replacing pn64's flat g_rdram[]/g_rom.
- cart/controllers/paks/dd/gb/pif/rdram, backends (file_storage,
clock_ctime_plus_delta, plugins_compat), api headers (m64p_types/plugin
extended).
- main layer: next's libretro-integrated main.c/rom.c/util.c/cheat.c/plugin.c/
frontend.c (g_mem_base, __LIBRETRO__ paths). savestates.c kept as the fork's
libretro filestream version, reconciled field-by-field to next's structs.
Fork reconciliation:
- g_mem_base = init_mem_base() in CoreStartup; ROM loads into MM_CART_ROM.
- Plugin connection: map fork gfx_plugin/rsp_plugin enums -> next
current_rdp_type/current_rsp_type (next's plugin_connect_all dispatches on
these; without the mapping the plugin tables stayed NULL).
- Define HAVE_LLE so the always-built cxd4 LLE RSP actually connects.
- Wire parallel-n64-cpucore -> r4300_emumode so the dynarec is selectable
(was defaulting to the pure interpreter).
- Per-frame threading without libco: the fork drives one frame per retro_run
via frame_break. Made run_pure_interpreter and new_dyna_start re-enterable
(new_dyna_start resumes at hot_state.pcaddr via get_addr_ht instead of
restarting the boot vector); run_r4300 only tears the core down on a real
stop, not on a per-frame yield.
- audio bridge (audio_out_backend_libretro), config globals, md5/xxhash
subprojects, dummy plugins, osal headers.
Validated: builds clean (MSVC-C89 discipline preserved for C sources), links,
boots, and renders. Excitebike 64 renders the N64 SPORTS boot logo, the Left
Field Productions 3D logo, and the Controller Paks UI correctly under both the
pure interpreter and the dynamic recompiler, with byte-identical output between
the two cores and deterministic frames across runs. Pokémon Snap also renders.
Author: LibretroAdmin <[email protected]>
* Fix new_core compile errors + remove __LIBRETRO__ ifdefs
- savestates.h: declare savestates_save_m64p/load_m64p unconditionally (were
under #ifndef __LIBRETRO__ with empty #else -> implicit-declaration errors in
the -D__LIBRETRO__ build).
- savestates.c: cp1 accessors take struct cp1* -> pass &g_dev.r4300.cp1 (7
sites); update_x86_rounding_mode(&g_dev.r4300.cp1); gb_cart is a pointer ->
gb = tpk->gb_cart (2 sites).
- main.c: saved_memory.mempack is [4][0x8000] -> .mempack[0] for uint8_t*;
load_dd_disk save_filename made const char* (get_dd_disk_save_path returns
const char*).
- Remove __LIBRETRO__ ifdefs (always a libretro core): unwrap includes in
device.c/main.c/plugin.c, delete the dead non-libretro plugin_connect_all,
collapse gliden64 GetPluginDir() to return slash, drop dlfcn include.
- Makefile: HAVE_THR_AL?=1 by default.
Author: LibretroAdmin <[email protected]>
* FIX: re-enterable cached interpreter (was restarting boot every frame -> hang)
The cached interpreter (parallel-n64-cpucore=cached_interpreter) had the same
non-re-enterable bug the dynarec did before it was fixed: run_r4300's
EMUMODE_INTERPRETER path ran init_blocks + cached_interpreter_jump_to(
start_address) on every call, so each per-frame slice restarted from the boot
vector, and it freed the block cache on the frame_break yield. Under RetroArch
this presents as a startup hang -- the core is frozen on the boot screen with
'Starting R4300 emulator: Cached Interpreter' repeating every frame.
Make it re-enterable, matching the existing pure_interp/dynarec handling: set up
the block cache and jump to the boot address once (l_cached_inited guard);
run_cached_interpreter then resumes from the live PC each slice (run_r4300
already clears the stop flag at entry); only free_blocks on a real stop, not on
the per-frame frame_break yield.
cached_interpreter now boots once and renders byte-identically to pure_interp
and dynarec.
Author: LibretroAdmin <[email protected]>
* Fix crashes in the new_core path: AI dangling pointer + angrylion reload
Three crash fixes for the libco-free per-frame execution model, where main_run()
returns after every frame instead of running the whole game on a coroutine stack.
1. AI-controller dangling pointer (the reported quick-menu / -O0 crash):
init_device stores the addresses of several main_run() locals into g_dev
(audio_out_backend_libretro, the eep/fla/sra/mpk file_storage structs,
dd_disk, and the joybus device arrays) and dereferences them throughout
emulation. Because main_run() now returns after each frame, those stack
locals die and g_dev points at reused stack -- ai->iaout->set_frequency()
jumps to a stale address (the call to 0x3 in the backtrace) on the first
audio-register write. Make them static so they outlive the per-frame returns.
The save-storage structs point into the global saved_memory (not malloc'd),
so this neither leaks nor double-frees; dd_disk is freed by close_dd_disk on
a real stop and re-zeroed on the next load.
Also gate the post-run_device teardown on a real stop (not the per-frame
frame_break yield) and reset the one-time init guards (l_setup_done,
l_dyna_inited, l_cached_inited, l_pi_started) on a real stop so the next ROM
re-initializes. pure_interp no longer clears frame_break itself (so main_run
sees the yield), and its boot-PC reseed guard only resets on a real stop.
2. angrylion n64video_update_screen NULL gfx.vi_reg (reload crash, fault 0x50):
on reload, initiateGFX's n64video_config_init() memsets config (clearing
gfx.vi_reg) before romOpen restores it. The resumed CPU can take a VI
interrupt in that window and call updateScreen() with gfx.vi_reg still NULL.
Bail out when it is NULL.
3. angrylion n64video_process_list NULL gfx.dp_reg (reload crash, fault 0x10):
same root cause via the RSP RDP-list path (Pokemon Snap). Bail out when
gfx.dp_reg is NULL.
VERIFIED (angrylion SW renderer, headless): all three CPU cores boot and render
byte-identically (008aee68 @ f60, ce41ca3a @ f300); 300-frame audio DMA no
longer crashes; load/run/unload reload cycles survive across
Excitebike/Pokemon Snap/Banjo-Tooie/DK64 x pure/cached/dynarec with stable RSS.
Author: LibretroAdmin <[email protected]>
* Bake the ROM settings database into the build (fixes Excitebike 64 + others)
parallel-n64's romdatabase_open() fopen'd mupen64plus.ini via
ConfigGetSharedDataFilepath, but the fork ships no such file and never
installed one, so the open failed and the database was empty -- every game fell
back to DEFAULT_COUNT_PER_OP and the generic save type. For timing-sensitive
titles this is fatal: Excitebike 64 needs CountPerOp=1, and at the default count
the CPU/RSP fall out of step, so the display list written to RDRAM is malformed
and every renderer (angrylion, parallel-RDP) and every RSP (cxd4, parallel)
faithfully draws the same garbage geometry on the title screen. This is why the
breakage was identical across all graphics/RSP combinations and identical to the
old core -- the missing per-game override, not the CPU, was always the cause.
(The tree already carried a half-finished attempt at this in main/rom_luts.c --
static per-game tables including '{ "NMX", 1 } / Excitebike 64' -- but that file
is not in the Makefile, not #included anywhere, and has no functions, so it was
dead and applied nothing.)
Bake the full upstream database in instead of depending on an external file:
- add mupen64plus.ini.h (the mupen64plus.ini catalog as a C string, ~3000
titles), matching next's generated header.
- parse romdatabase_open() straight from that in-memory string: no
ConfigGetSharedDataFilepath, no fopen, no fclose, no disk access and no
install step. The INI_SECTION/INI_PROPERTY parser and the CRC/MD5 indexing
are unchanged; only the line source changed from fgets(FILE*) to walking the
embedded buffer.
The ROM lookup is by MD5 with a CRC fallback (ini_search_by_crc on the header
CRCs), so byte-swapped .v64 images still match. Verified: Excitebike 64 (USA)
now matches its entry and applies countperop=1, savetype=Eeprom 16KB; all three
CPU cores still boot and render.
Author: LibretroAdmin <[email protected]>
* FIX: guard NULL gb_cart in savestate save/load (crash on every save without a transferpak)
The transferpak block in savestates_save_m64p / savestates_load_m64p
unconditionally dereferenced tpk->gb_cart:
struct gb_cart* gb = tpk->gb_cart;
struct mbc3_rtc* rtc = &gb->rtc; /* NULL deref */
...
PUTDATA(curr, uint32_t, gb->rom_bank);
But in the next-derived device model gb_cart is a pointer that is NULL unless a
transferpak with a GB cartridge is inserted -- i.e. for essentially every game,
including Excitebike 64. Saving a state (RetroArch quick menu -> Save State)
therefore crashed in savestates_save_m64p; loading would crash the same way.
Guard the NULL case in both directions. The block is still always written and
fixed-size (zeros when no cart is present) and read back the same way (GETDATA
into void), so the layout stays seekable and states remain compatible whether or
not a transferpak is attached.
VERIFIED: save + load round-trips on all three CPU cores (pure/cached/dynarec)
for a game with no transferpak; state file is the expected 16789372 bytes and
reloads without crashing.
Author: LibretroAdmin <[email protected]>
* FIX: retro_serialize_size was 208 bytes too small -> savestate heap overflow
retro_serialize_size() returned 16788348 + 1024, but savestates_save_m64p
actually writes 16789580 bytes: once the NULL-gb_cart guard makes the v1.3
Transfer Pak / MBC3 RTC block write unconditionally for all four controllers
(50 bytes each = 200) plus the v1.4 RSP DMA FIFO block, the real trailer is 1232
bytes, not the 1024 that was budgeted. retro_serialize() therefore wrote 208
bytes past the buffer the frontend allocated from retro_serialize_size(),
corrupting the heap -- which is the crash on Save State (RetroArch quick menu)
that persisted after the gb_cart NULL-deref fix.
The state is deterministic and the same size for every ROM and CPU core
(measured 16789580 across Excitebike/Pokemon Snap/Banjo-Tooie x
cached/dynarec/pure), so return that actual size plus a real 1KB margin. The
serialized byte layout is unchanged, so existing save states still load (only
the buffer allocation grew); verified loading a pre-fix state with the new
build.
VERIFIED: save no longer overflows; save+load round-trips on all three CPU
cores; old-format states still load.
Author: LibretroAdmin <[email protected]>
* Add runtime-selectable Hacktarux dynarec alongside the ari64 new dynarec
Make the classic Hacktarux dynarec and the ari64 new dynarec both compile into
one x86_64 binary and selectable at runtime via the CPU Core core option, instead
of the upstream compile-time NEW_DYNAREC switch that builds exactly one of them.
ari64 remains the default; selecting "Dynamic Recompiler (Hacktarux)" switches the
backend for that session.
Why this is non-trivial: upstream mupen64plus-core (and the next core this fork
tracks) pick the dynarec at compile time with #ifdef NEW_DYNAREC. The two dynarecs
keep CPU state in different places -- Hacktarux in the base struct
(r4300->regs/hi/lo/stop, cp0->regs, cp1 fcr/regs, cp2 latch), ari64 in
new_dynarec_hot_state -- and use mutually exclusive struct layouts. There is no
runtime-selectable version anywhere upstream to copy, so both layouts are made to
coexist and dispatch is done at runtime on a new r4300_jit_backend global
(R4300_JIT_ARI64=0 default, R4300_JIT_HACKTARUX=1), selected at load time so the
per-access dispatch is a cheap branch.
Structure / coexistence:
- The Hacktarux backend files are replaced with next's clean struct-based
x86_64/ versions (assemble, regcache, dynarec, dyna_start.asm), which match the
imported recomp.c; the old classic-hybrid files referenced removed globals and
did not build.
- r4300_core / cp0 / cp1 / cp2 structs: the register/state members each dynarec
needs and the new_dynarec_hot_state pointer are all made always-present
(extra_memory and new_dynarec_hot_state too). Offset macros target the struct
fields for Hacktarux codegen; ari64 keeps its own generated offsets.
- The r4300_regs/mult_hi/mult_lo/pc/stop and cp0/cp1/cp2 accessors dispatch at
runtime via a small "uses ari64 hot state" helper
((emumode==DYNAREC) && (backend==ARI64)).
- cp0_update_count, exception_epilog, reset_hard_handler, gen_interrupt's
dyna_stop, the soft-reset dyna_interp clear, tlb exception handling, the
cached-interp ADD_TO_PC macro, run_r4300's dynarec branch,
invalidate_r4300_cached_code and generic_jump_to are converted from
compile-time NEW_DYNAREC branches to runtime branches on the active backend,
each preserving the original path's exact entry conditions.
Build / asm_defines:
- asm_defines.c is ungated so the generator emits BOTH offset sets; the committed
nasm/gas headers in src/asm_defines and new_dynarec/x64 are regenerated (45
defines each) and auto-adjust to the coexisting layout (regs=0x0,
recomp=0x900238, new_dynarec_hot_state past the 32MB extra_memory).
- Makefile.common builds ari64 (HAVE_ARI64_X64=1) and Hacktarux
(WANT_HACKTARUX=1) together for WITH_DYNAREC=x86_64; the Hacktarux nasm
dyna_start goes through the same SOURCES_NASM / %.o:%.asm path as ari64's
linkage. The single link collision (dynarec_gen_interrupt defined by both) is
resolved by renaming the Hacktarux copy to dynarec_gen_interrupt_hacktarux; no
other symbols collide.
- libretro_core_options.h offers both dynarec choices when both are built
(default ari64); libretro.c parses the cpucore option into r4300_jit_backend.
Per-frame re-entry (this fork has no libco; the CPU is yielded each frame at the
VI interrupt via retro_return/frame_break):
- main_run's re-entry clears the active backend's stop via the accessor AND
clears ari64's hot-state stop directly, because retro_return early-outs on
mupencorestop which r4300.h hardcodes to ari64's hot-state stop -- otherwise in
Hacktarux mode it stays latched after the first frame, the frame-break is never
armed, gen_interrupt never sees stop, and the CPU spins.
- retro_return also sets *r4300_stop() so the Hacktarux gen_interrupt/dyna_jump
path actually yields at the VI boundary.
- run_r4300's Hacktarux branch guards the trailing pc++ on !frame_break (it is
run-to-completion cleanup that would corrupt the resume PC under per-frame
yield), and dynarec_setup_code resumes at skip_jump (latched by the VI
exception_epilog) or the live PC instead of always restarting at the boot
vector, via a new recomp.dyna_setup_started flag reset on real stop/poweron.
Memory-map hooks that were NEW_DYNAREC-gated and therefore compiled out in the
dual build, now dispatched at runtime for the Hacktarux backend:
- rdram.c map_corrupt_rdram(): drop recomp.fast_memory + invalidate cached code
during RDRAM control calibration so Hacktarux's generated loads route through
the corrupted-RDRAM handler, which is how IPL3 sizes memory. Without this IPL3
detected 0 MB, leaving osMemSize=0 and a broken heap (games froze on the boot
logo).
- fb.c protect_framebuffers(): drop recomp.fast_memory so generated framebuffer
writes route through the dirty-page handler the RDP observes.
ari64 manages both of these in its own memory map and must not have
recomp.fast_memory touched.
Result: HAVE_ARI64_X64=1 WANT_HACKTARUX=1 WITH_DYNAREC=x86_64 links into one
binary. ari64 is the default and unchanged (no regression). The Hacktarux backend
boots, runs and renders without hang or crash; on Excitebike it is byte-identical
to ari64 and the cached interpreter across the tested frames.
Known characteristic (not a regression): the upstream Hacktarux x86_64 dynarec
emits x87 FPU instructions (extended-precision intermediates) while the
interpreter and ari64 use SSE, so FP-heavy titles (e.g. Pokemon Snap) are not
bit-identical between the Hacktarux and SSE backends. This matches upstream
Hacktarux semantics; bit-exactness would require an SSE FP codegen rewrite.
* r4300: guard Hacktarux dynarec calls so ari64-only builds link
The runtime-selectable Hacktarux commit converted cp0.c / interrupt.c /
r4300_core.c to call the Hacktarux dynarec entry points (dyna_jump, dyna_stop,
dyna_start/dynarec_setup_code, the dynarec_*_block cached-interp callbacks, and
dynarec_jump_to) at runtime, but those functions are only compiled when
WANT_HACKTARUX=1 (they live in recomp.c / hacktarux_dynarec.c). A build with
HAVE_ARI64_X64=1 and no WANT_HACKTARUX -- the historical default and the
Windows/MSYS2 MinGW configuration -- therefore failed to link with undefined
references to all of those symbols.
Wrap every call into a Hacktarux-only symbol in #ifdef HAVE_DYNAREC_HACKTARUX
and force the hacktarux / hacktarux_dynarec_active selector flags to compile-time
0 when it is undefined. In an ari64-only build the runtime backend can never be
Hacktarux (libretro.c only exposes the Hacktarux core-option value when both
backends are compiled), so the guarded branches are unreachable and now compile
out, while the dual build is unchanged. invalidate_cached_code_hacktarux is left
unguarded on purpose: despite its name it is defined in cached_interp.c and is
always built (it is the shared cached-interp invalidator).
Verified linking and rendering in both the ari64-only and the dual
(WANT_HACKTARUX=1) x86_64 configurations.
* Makefile: default both x86/x86_64 dynarecs on for a bare build
A bare `make HAVE_PARALLEL=1 HAVE_PARALLEL_RSP=1` auto-detects WITH_DYNAREC from
the host ARCH (x86_64 -> x86_64) and emitted -DDYNAREC, but added NO dynarec
backend: the ari64 new dynarec was gated behind HAVE_ARI64_X64=1 and the
Hacktarux dynarec behind WANT_HACKTARUX=1, neither of which defaulted on. The
result was a -DDYNAREC binary with no recompiler compiled in.
Default HAVE_ARI64_X64 and WANT_HACKTARUX to 1 inside the x86_64 block (and
HAVE_ARI64_X86 to 1 inside the x86 block) with ?=, so a bare build now compiles
both backends -- ari64 as the default, Hacktarux runtime-selectable alongside it.
Explicit overrides still win: pass HAVE_ARI64_X64=0 / WANT_HACKTARUX=0 to opt a
backend out.
Verified: `make HAVE_PARALLEL=1 HAVE_PARALLEL_RSP=1` resolves
DYNAFLAGS = -DDYNAREC -D_M_X64 -DNEW_DYNAREC=2 -DHAVE_DYNAREC_HACKTARUX, links one
binary, and both backends boot and render (Excitebike 008aee68). WANT_HACKTARUX=0
and HAVE_ARI64_X64=0 overrides confirmed to drop the respective sources.
* libretro: apply CPU core / dynarec backend at startup only (fix on-the-fly switch crash)
Changing the CPU Core option in the Quick Menu while content was running switched
the active dynarec on the fly and crashed: update_variables() parsed
parallel-n64-cpucore and reassigned r4300_emumode and r4300_jit_backend
unconditionally, including on the runtime options-changed path
(update_variables(false)). Flipping r4300_jit_backend mid-run points the per-
dispatch backend selection at the other dynarec while the live one's recompiled
blocks, register/hot state and dyna_setup_started flag are still in use, so the
next dispatch runs against state that is invalid for it.
Gate the whole cpucore block behind if (startup), matching the upstream next
core (which parses -cpucore only inside its startup branch). The CPU core and JIT
backend are now locked in at retro_load_game time (update_variables(true), before
device/dynarec init) and a Quick Menu change takes effect on the next core
restart instead of being applied live. r4300_jit_backend keeps its zero (ari64)
default for the unset case.
Verified: both backends still select correctly at startup (Excitebike 008aee68
under each) and the runtime update_variables(false) path no longer reassigns the
backend.
---------
Co-authored-by: LibretroAdmin <[email protected]>