core: stdint typedefs, LE optimizations, frame determinism
Three follow-up audit passes on top of the memory-safety / leak / savestate-portability work in1185db8. ============================================================== 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 in1185db8(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.
This commit is contained in:
@@ -50,7 +50,7 @@
|
||||
#define RED_EXPAND 3
|
||||
#define GREEN_EXPAND 2
|
||||
#define BLUE_EXPAND 3
|
||||
typedef uint16 bpp_t;
|
||||
typedef uint16_t bpp_t;
|
||||
#elif defined (FRONTEND_SUPPORTS_ABGR1555)
|
||||
#define RED_SHIFT 0
|
||||
#define GREEN_SHIFT 5
|
||||
@@ -61,7 +61,7 @@ typedef uint16 bpp_t;
|
||||
#define RED_MASK 0x1F
|
||||
#define GREEN_MASK 0x3E0
|
||||
#define BLUE_MASK 0x7C00
|
||||
typedef uint16 bpp_t;
|
||||
typedef uint16_t bpp_t;
|
||||
#elif defined (FRONTEND_SUPPORTS_RGB888)
|
||||
#define RED_SHIFT 16
|
||||
#define GREEN_SHIFT 8
|
||||
@@ -72,7 +72,7 @@ typedef uint16 bpp_t;
|
||||
#define RED_MASK 0xFF0000
|
||||
#define GREEN_MASK 0x00FF00
|
||||
#define BLUE_MASK 0x0000FF
|
||||
typedef uint32 bpp_t;
|
||||
typedef uint32_t bpp_t;
|
||||
#elif defined (FRONTEND_SUPPORTS_RGB565)
|
||||
#define RED_SHIFT 11
|
||||
#define GREEN_SHIFT 5
|
||||
@@ -83,7 +83,7 @@ typedef uint32 bpp_t;
|
||||
#define RED_MASK 0xF800
|
||||
#define GREEN_MASK 0x7e0
|
||||
#define BLUE_MASK 0x1f
|
||||
typedef uint16 bpp_t;
|
||||
typedef uint16_t bpp_t;
|
||||
#else
|
||||
#define RED_SHIFT 10
|
||||
#define GREEN_SHIFT 5
|
||||
@@ -91,7 +91,7 @@ typedef uint16 bpp_t;
|
||||
#define RED_EXPAND 3
|
||||
#define GREEN_EXPAND 3
|
||||
#define BLUE_EXPAND 3
|
||||
typedef uint16 bpp_t;
|
||||
typedef uint16_t bpp_t;
|
||||
#endif
|
||||
|
||||
#define MAX_PLAYERS 4 /* max supported players */
|
||||
@@ -255,7 +255,7 @@ static unsigned libretro_msg_interface_version = 0;
|
||||
|
||||
const size_t PPU_BIT = 1ULL << 31ULL;
|
||||
|
||||
extern uint8 NTARAM[0x800], PALRAM[0x20], SPRAM[0x100], PPU[4];
|
||||
extern uint8_t NTARAM[0x800], PALRAM[0x20], SPRAM[0x100], PPU[4];
|
||||
|
||||
/* overclock the console by adding dummy scanlines to PPU loop
|
||||
* disables DMC DMA and WaveHi filling for these dummies
|
||||
@@ -298,7 +298,7 @@ static unsigned serialize_size;
|
||||
|
||||
/* extern forward decls.*/
|
||||
extern FCEUGI *GameInfo;
|
||||
extern uint8 *XBuf;
|
||||
extern uint8_t *XBuf;
|
||||
extern CartInfo iNESCart;
|
||||
extern CartInfo UNIFCart;
|
||||
extern int show_crosshair;
|
||||
@@ -315,9 +315,9 @@ const char * GetKeyboard(void)
|
||||
|
||||
#define BUILD_PIXEL_RGB565(R,G,B) (((int) ((R)&0x1f) << RED_SHIFT) | ((int) ((G)&0x3f) << GREEN_SHIFT) | ((int) ((B)&0x1f) << BLUE_SHIFT))
|
||||
|
||||
void FCEUD_SetPalette(uint16 index, uint8_t r, uint8_t g, uint8_t b)
|
||||
void FCEUD_SetPalette(uint16_t index, uint8_t r, uint8_t g, uint8_t b)
|
||||
{
|
||||
uint16 index_to_write = index;
|
||||
uint16_t index_to_write = index;
|
||||
#if defined(RENDER_GSKIT_PS2)
|
||||
/* Index correction for PS2 GS */
|
||||
int modi = index & 63;
|
||||
@@ -418,7 +418,7 @@ void FCEUD_SoundToggle (void)
|
||||
#define PAL_CUSTOM (PAL_INTERNAL + 3)
|
||||
#define PAL_TOTAL PAL_CUSTOM
|
||||
|
||||
static uint8 external_palette_exist = false;
|
||||
static uint8_t external_palette_exist = false;
|
||||
|
||||
/* table for currently loaded palette */
|
||||
static uint8_t base_palette[192];
|
||||
@@ -2455,7 +2455,7 @@ static void check_variables(bool startup)
|
||||
update_option_visibility();
|
||||
}
|
||||
|
||||
void add_powerpad_input(unsigned port, uint32 variant, uint32_t *ppdata) {
|
||||
void add_powerpad_input(unsigned port, uint32_t variant, uint32_t *ppdata) {
|
||||
unsigned k;
|
||||
const uint32_t* map = powerpadmap;
|
||||
for (k = 0 ; k < 12 ; k++)
|
||||
@@ -2463,7 +2463,7 @@ void add_powerpad_input(unsigned port, uint32 variant, uint32_t *ppdata) {
|
||||
*ppdata |= (1 << k);
|
||||
}
|
||||
|
||||
void add_fkb_input(unsigned port, uint32 variant, uint8_t *fkbkeys) {
|
||||
void add_fkb_input(unsigned port, uint32_t variant, uint8_t *fkbkeys) {
|
||||
unsigned k;
|
||||
const uint32_t* map = fkbmap;
|
||||
for (k = 0 ; k < 0x48 ; k++)
|
||||
@@ -2473,7 +2473,7 @@ void add_fkb_input(unsigned port, uint32 variant, uint8_t *fkbkeys) {
|
||||
fkbkeys[k]=0;
|
||||
}
|
||||
|
||||
void add_suborkey_input(unsigned port, uint32 variant, uint8_t *suborkeys) {
|
||||
void add_suborkey_input(unsigned port, uint32_t variant, uint8_t *suborkeys) {
|
||||
unsigned k;
|
||||
const uint32_t* map = suborkbmap;
|
||||
for (k = 0 ; k < 0x65 ; k++)
|
||||
@@ -2485,7 +2485,7 @@ void add_suborkey_input(unsigned port, uint32 variant, uint8_t *suborkeys) {
|
||||
|
||||
static int mzx = 0, mzy = 0;
|
||||
|
||||
void get_mouse_input(unsigned port, uint32 variant, uint32_t *mousedata)
|
||||
void get_mouse_input(unsigned port, uint32_t variant, uint32_t *mousedata)
|
||||
{
|
||||
int min_width, min_height, max_width, max_height;
|
||||
|
||||
@@ -2565,7 +2565,7 @@ void get_mouse_input(unsigned port, uint32 variant, uint32_t *mousedata)
|
||||
|
||||
if (_x != 0 || _y != 0)
|
||||
{
|
||||
int32 raw = (_x + (0x7FFF + offset_x)) * max_width / ((0x7FFF + offset_x) * 2);
|
||||
int32_t raw = (_x + (0x7FFF + offset_x)) * max_width / ((0x7FFF + offset_x) * 2);
|
||||
if (arkanoidmode == RetroArkanoidAbsMouse) {
|
||||
/* remap so full screen movement ends up within the encoder range 0-240
|
||||
game board: 176 wide
|
||||
@@ -2907,7 +2907,7 @@ static void FCEUD_UpdateInput(void)
|
||||
palette_switch_counter = 0;
|
||||
}
|
||||
|
||||
void FCEUD_Update(uint8 *XBuf, int32 *Buffer, int Count)
|
||||
void FCEUD_Update(uint8_t *XBuf, int32_t *Buffer, int Count)
|
||||
{
|
||||
}
|
||||
|
||||
@@ -3039,7 +3039,7 @@ static void retro_run_blit(uint8_t *gfx)
|
||||
}
|
||||
else
|
||||
{
|
||||
uint8 pixel_mask = use_raw_palette ? 0x3F : 0xFF;
|
||||
uint8_t pixel_mask = use_raw_palette ? 0x3F : 0xFF;
|
||||
fceu_video_out[y * width + x] = retro_palette[*gfx & pixel_mask];
|
||||
}
|
||||
}
|
||||
@@ -3131,7 +3131,7 @@ static int checkGG(char c)
|
||||
static int GGisvalid(const char *code)
|
||||
{
|
||||
size_t len = strlen(code);
|
||||
uint32 i;
|
||||
uint32_t i;
|
||||
|
||||
if (len != 6 && len != 8)
|
||||
return 0;
|
||||
@@ -3152,8 +3152,8 @@ void retro_cheat_set(unsigned index, bool enabled, const char *code)
|
||||
char name[256];
|
||||
char temp[1024];
|
||||
char *codepart;
|
||||
uint16 a;
|
||||
uint8 v;
|
||||
uint16_t a;
|
||||
uint8_t v;
|
||||
int c;
|
||||
int type = 1;
|
||||
|
||||
|
||||
@@ -19,7 +19,7 @@ extern CartInfo iNESCart;
|
||||
|
||||
typedef struct {
|
||||
const char *name;
|
||||
uint8 value;
|
||||
uint8_t value;
|
||||
} SETTING;
|
||||
|
||||
typedef struct {
|
||||
@@ -1077,7 +1077,7 @@ static char *core_key[MAX_CORE_OPTIONS];
|
||||
static unsigned dipswitch_type = DPSW_NONE;
|
||||
static unsigned numCoreOptions = 0;
|
||||
static unsigned numValues[MAX_VALUES] = {0};
|
||||
static uint8 dipswitchPreset = 0;
|
||||
static uint8_t dipswitchPreset = 0;
|
||||
|
||||
static const char *str_to_corekey(char *s)
|
||||
{
|
||||
@@ -1199,8 +1199,8 @@ static VSUNIGAME *get_vsuni_dipswitch(unsigned id)
|
||||
static void update_dipswitch_vsuni(void)
|
||||
{
|
||||
unsigned index_key;
|
||||
uint8 vsdip_new = 0;
|
||||
uint8 last_vsdip = FCEUI_VSUniGetDIPs();
|
||||
uint8_t vsdip_new = 0;
|
||||
uint8_t last_vsdip = FCEUI_VSUniGetDIPs();
|
||||
|
||||
for (index_key = 0; index_key < numCoreOptions; index_key++)
|
||||
{
|
||||
@@ -1215,7 +1215,7 @@ static void update_dipswitch_vsuni(void)
|
||||
for (index_value = 0; index_value < numValues[index_key]; index_value++)
|
||||
{
|
||||
const char *var_value = vscoreopt[index_key].values[index_value].value;
|
||||
uint8 value = 0;
|
||||
uint8_t value = 0;
|
||||
|
||||
if (strcmp(var.value, var_value) != 0)
|
||||
continue;
|
||||
|
||||
Reference in New Issue
Block a user