Files
ci-libretro-fceumm/src/filter.c

174 lines
4.1 KiB
C
Raw Normal View History

#include <math.h>
#include "fceu-types.h"
#include "sound.h"
#include "x6502.h"
#include "fceu.h"
#include "filter.h"
#include "fcoeffs.h"
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
static uint32_t mrindex;
static uint32_t mrratio;
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
void SexyFilter2(int32_t *in, int32_t count) {
#ifdef moo
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
static int64_t acc = 0;
double x, p;
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
int64_t c;
x = 2 * M_PI * 6000 / FSettings.SndRate;
p = ((double)2 - cos(x)) - sqrt(pow((double)2 - cos(x), 2) - 1);
c = p * 0x100000;
#endif
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
static int64_t acc = 0;
while (count--) {
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
int64_t dropcurrent;
dropcurrent = ((*in << 16) - acc) >> 3;
acc += dropcurrent;
*in = acc >> 16;
in++;
2017-10-15 03:13:11 +08:00
#if 0
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
acc=((int64_t)0x100000-c)* *in + ((c*acc)>>20);
2017-10-15 03:13:11 +08:00
*in=acc>>20;
in++;
#endif
}
}
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
int64_t sexyfilter_acc1 = 0, sexyfilter_acc2 = 0;
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
void SexyFilter(int32_t *in, int32_t *out, int32_t count) {
int32_t mul1, mul2, vmul;
mul1 = (94 << 16) / FSettings.SndRate;
mul2 = (24 << 16) / FSettings.SndRate;
vmul = (FSettings.SoundVolume << 16) * 3 / 4 / 100;
if (FSettings.soundq)
vmul /= 4;
else
vmul *= 2; /* TODO: Increase volume in low quality sound rendering code itself */
while (count) {
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
int64_t ino = (int64_t) * in * vmul;
sexyfilter_acc1 += ((ino - sexyfilter_acc1) * mul1) >> 16;
sexyfilter_acc2 += ((ino - sexyfilter_acc1 - sexyfilter_acc2) * mul2) >> 16;
*in = 0;
{
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
int32_t t = (sexyfilter_acc1 - ino + sexyfilter_acc2) >> 16;
if (t > 32767) t = 32767;
if (t < -32768) t = -32768;
*out = t;
}
in++;
out++;
count--;
}
}
/* Returns number of samples written to out. */
/* leftover is set to the number of samples that need to be copied
from the end of in to the beginning of in.
*/
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
/* static uint32_t mva=1000; */
/* This filtering code assumes that almost all input values stay below 32767.
Do not adjust the volume in the wlookup tables and the expansion sound
code to be higher, or you *might* overflow the FIR code.
*/
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
int32_t NeoFilterSound(int32_t *in, int32_t *out, uint32_t inlen, int32_t *leftover) {
uint32_t x;
int32_t *outsave = out;
int32_t count = 0;
uint32_t max = (inlen - 1) << 16;
if (FSettings.soundq == 2) {
for (x = mrindex; x < max; x += mrratio) {
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
int32_t acc = 0, acc2 = 0;
uint32_t c;
int32_t *S, *D;
for (c = SQ2NCOEFFS, S = &in[(x >> 16) - SQ2NCOEFFS], D = sq2coeffs; c; c--, D++) {
acc += (S[c] * *D) >> 6;
acc2 += (S[1 + c] * *D) >> 6;
}
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
acc = ((int64_t)acc * (65536 - (x & 65535)) + (int64_t)acc2 * (x & 65535)) >> (16 + 11);
*out = acc;
out++;
count++;
}
} else {
for (x = mrindex; x < max; x += mrratio) {
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
int32_t acc = 0, acc2 = 0;
uint32_t c;
int32_t *S, *D;
for (c = NCOEFFS, S = &in[(x >> 16) - NCOEFFS], D = coeffs; c; c--, D++) {
acc += (S[c] * *D) >> 6;
acc2 += (S[1 + c] * *D) >> 6;
}
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
acc = ((int64_t)acc * (65536 - (x & 65535)) + (int64_t)acc2 * (x & 65535)) >> (16 + 11);
*out = acc;
out++;
count++;
}
}
mrindex = x - max;
if (FSettings.soundq == 2) {
mrindex += SQ2NCOEFFS * 65536;
*leftover = SQ2NCOEFFS + 1;
} else {
mrindex += NCOEFFS * 65536;
*leftover = NCOEFFS + 1;
}
if (GameExpSound.NeoFill)
GameExpSound.NeoFill(outsave, count);
SexyFilter(outsave, outsave, count);
if (FSettings.lowpass)
SexyFilter2(outsave, count);
return(count);
}
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
void MakeFilters(int32_t rate) {
int32_t *tabs[6] = { C44100NTSC, C44100PAL, C48000NTSC, C48000PAL, C96000NTSC,
C96000PAL };
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
int32_t *sq2tabs[6] = { SQ2C44100NTSC, SQ2C44100PAL, SQ2C48000NTSC, SQ2C48000PAL,
SQ2C96000NTSC, SQ2C96000PAL };
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
int32_t *tmp;
int32_t x;
uint32_t nco;
if (FSettings.soundq == 2)
nco = SQ2NCOEFFS;
else
nco = NCOEFFS;
mrindex = (nco + 1) << 16;
core: stdint typedefs, LE optimizations, frame determinism Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in 1185db8. ============================================================== Pass 1: replace custom typedefs with C99 stdint types throughout ============================================================== The custom uint8 / uint16 / uint32 / uint64 / int8 / int16 / int32 / int64 typedefs in src/fceu-types.h were just simple aliases for the C99 stdint.h types. Replace them with the standard names directly. - 498 files modified - ~3,400 token replacements (uint8 -> uint8_t, etc) - fceu-types.h slimmed down to just INLINE / GINLINE / FASTAPASS macros and the readfunc / writefunc function-pointer typedefs (those now use uint8_t / uint32_t natively) - Build clean on `make platform=unix` with zero new warnings - Output binary size unchanged - confirming semantic equivalence Mechanical replacement done with a Python script that uses word- boundary regex to avoid false positives (e.g. 'uint32_t' was correctly left alone because '_' is a word character so 'uint32' is not a complete word inside it). ================================================================ Pass 2: prefer memcpy on LE hosts for endian read/write helpers ================================================================ fceu-endian.c's write32le_mem, FCEU_en32lsb, and FCEU_de32lsb performed bytewise composition/decomposition unconditionally. On LE hosts the in-memory representation already matches the desired LE on-disk format, so a single memcpy is equivalent and lets the compiler emit a single load/store rather than four byte ops. - The bytewise path is kept inside #ifdef MSB_FIRST for BE hosts where it implements the actual byte swap - Both forms produce identical results; this is a code-clarity change more than a performance one (the optimizer was already merging the shifts on LE), but it documents the intent and removes a strict-aliasing-flavoured cast through *(uint32_t*)Bufo - Added missing #include <string.h> in fceu-endian.c which was relying on transitive includes for memcpy Other MSB_FIRST sites in the codebase (state.c FlipByteOrder guards, ppu.c sprite-line rendering, boards/unrom512.c flash-write- counter access) were already optimized for LE; they were verified correct rather than changed. ================================================================ Pass 3: frame determinism for replay and netplay ================================================================ Two libc rand() sites in core were replaced with a local xorshift32 PRNG so that NES games which read uninitialised memory or hit hardware "weak bit" emulation produce reproducible behaviour across runs. NES titles routinely read uninitialised RAM (variables not zeroed before use, sprite Y-position set by junk-on-stack), so the RAM contents at power-on subtly affect game behaviour. With libc rand(), those contents depend on whether anyone else seeded rand() in the same process - a different libretro frontend, a different audio backend init order, or any frontend that does srand(time(0)) all break replay / netplay frame-determinism. 1. fceu.c FCEU_MemoryRand. Used to fill RAM (PowerNES) and CHR-RAM (iNES_Init) at power-on when option_ramstate=2 (random init). Replaced with a local xorshift32 PRNG, exposed via a new FCEU_MemoryRand_Reseed(uint32_t) function called once per power-on: - PowerNES seeds from the first 4 bytes of GameInfo->MD5 (set by all loaders before PowerNES runs) so identical ROMs produce identical RAM, different ROMs differ - iNES_Init seeds from iNESCart.PRGCRC32 before the CHR-RAM fill so two builds of the same ROM get the same CHR-RAM - The PRNG state advances across multiple FCEU_MemoryRand calls within one power-on so RAM and CHR-RAM get different content (matching NES hardware reality) 2. boards/rt-01.c UNLRT01Read. The RT-01 board has 'weak bit' protected EPROM regions; reads of 0xCE80-0xCEFF and 0xFE80- 0xFEFF return 0xF2 with the low 3 bits randomised. Replaced libc rand() with a local xorshift32 seeded at power-on, and added the PRNG state to the savestate via AddExState with key "WBKS" so save / load / rewind / netplay rollback all stay deterministic. In addition, two long-double-to-int truncations were changed to double for cross-platform FP determinism: - sound.c SetSoundVariables: soundtsinc - boards/n106.c DoNamcoSound: inc long double has platform-dependent precision (80-bit on x87, 64-bit with -mfpmath=sse, 128-bit on PowerPC), so the truncated integer result varied across these platforms. double is guaranteed 64-bit IEEE-754 portably. After this pass, the core has no time(), clock(), gettimeofday(), clock_gettime(), getpid(), getuid(), getgid(), getenv(), gethostid(), pthread, std::thread, OpenMP, signal handler, or non-deterministic- malloc dependency. Verified with a Python scanner that greps the source for these patterns; runs clean. The PPU / APU / CPU power-on already explicitly memset all state buffers to 0 (deterministic), and ROM/CHR-ROM allocation already memsets to 0xFF before partial fread (deterministic regardless of file truncation). Combined with the memory-safety hardening in 1185db8 (which prevents savestate-loaded indices from going out-of-bounds and producing unpredictable behaviour), the core now offers genuine frame-deterministic replay across runs, builds, and host endian.
2026-05-04 02:46:34 +02:00
mrratio = (PAL ? (int64_t)(PAL_CPU * 65536) : (int64_t)(NTSC_CPU * 65536)) / rate;
if (FSettings.soundq == 2)
tmp = sq2tabs[(PAL ? 1 : 0) | (rate == 48000 ? 2 : 0) | (rate == 96000 ? 4 : 0)];
else
tmp = tabs[(PAL ? 1 : 0) | (rate == 48000 ? 2 : 0) | (rate == 96000 ? 4 : 0)];
if (FSettings.soundq == 2)
for (x = 0; x < (SQ2NCOEFFS >> 1); x++)
sq2coeffs[x] = sq2coeffs[SQ2NCOEFFS - 1 - x] = tmp[x];
else
for (x = 0; x < (NCOEFFS >> 1); x++)
coeffs[x] = coeffs[NCOEFFS - 1 - x] = tmp[x];
}