From ff01e3521b6a4b695c7bfc1c401412e4d68a3f08 Mon Sep 17 00:00:00 2001 From: Flyinghead Date: Thu, 11 Oct 2018 10:09:28 +0200 Subject: [PATCH] Better NAOMI JVS emulation --- core/core.mk | 2 +- core/deps/xxhash/xxhash.c | 1030 +++++++++++++++++++++++++++++++ core/deps/xxhash/xxhash.h | 328 ++++++++++ core/hw/maple/maple_devs.cpp | 1131 ++++++++++++++++++++++++---------- core/hw/maple/maple_devs.h | 3 +- core/hw/maple/maple_if.cpp | 44 +- core/hw/naomi/naomi_cart.cpp | 4 +- core/linux-dist/x11.cpp | 8 + 8 files changed, 2219 insertions(+), 331 deletions(-) create mode 100644 core/deps/xxhash/xxhash.c create mode 100644 core/deps/xxhash/xxhash.h diff --git a/core/core.mk b/core/core.mk index 8d2832211..df6ef786b 100755 --- a/core/core.mk +++ b/core/core.mk @@ -10,7 +10,7 @@ RZDCY_MODULES := cfg/ hw/arm7/ hw/aica/ hw/holly/ hw/ hw/gdrom/ hw/maple/ hw/mod hw/mem/ hw/pvr/ hw/sh4/ hw/sh4/interpr/ hw/sh4/modules/ plugins/ profiler/ oslib/ \ hw/extdev/ hw/arm/ hw/naomi/ imgread/ ./ deps/coreio/ deps/zlib/ deps/chdr/ deps/crypto/ \ deps/libelf/ deps/chdpsr/ arm_emitter/ rend/ reios/ deps/libpng/ deps/xbrz/ \ - deps/picotcp/modules/ deps/picotcp/stack/ + deps/picotcp/modules/ deps/picotcp/stack/ deps/xxhash/ ifdef WEBUI diff --git a/core/deps/xxhash/xxhash.c b/core/deps/xxhash/xxhash.c new file mode 100644 index 000000000..ff28749e3 --- /dev/null +++ b/core/deps/xxhash/xxhash.c @@ -0,0 +1,1030 @@ +/* +* xxHash - Fast Hash algorithm +* Copyright (C) 2012-2016, Yann Collet +* +* BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) +* +* Redistribution and use in source and binary forms, with or without +* modification, are permitted provided that the following conditions are +* met: +* +* * Redistributions of source code must retain the above copyright +* notice, this list of conditions and the following disclaimer. +* * Redistributions in binary form must reproduce the above +* copyright notice, this list of conditions and the following disclaimer +* in the documentation and/or other materials provided with the +* distribution. +* +* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS +* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT +* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR +* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT +* OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, +* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT +* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, +* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY +* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT +* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE +* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. +* +* You can contact the author at : +* - xxHash homepage: http://www.xxhash.com +* - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + + +/* ************************************* +* Tuning parameters +***************************************/ +/*!XXH_FORCE_MEMORY_ACCESS : + * By default, access to unaligned memory is controlled by `memcpy()`, which is safe and portable. + * Unfortunately, on some target/compiler combinations, the generated assembly is sub-optimal. + * The below switch allow to select different access method for improved performance. + * Method 0 (default) : use `memcpy()`. Safe and portable. + * Method 1 : `__packed` statement. It depends on compiler extension (ie, not portable). + * This method is safe if your compiler supports it, and *generally* as fast or faster than `memcpy`. + * Method 2 : direct access. This method doesn't depend on compiler but violate C standard. + * It can generate buggy code on targets which do not support unaligned memory accesses. + * But in some circumstances, it's the only known way to get the most performance (ie GCC + ARMv6) + * See http://stackoverflow.com/a/32095106/646947 for details. + * Prefer these methods in priority order (0 > 1 > 2) + */ +#ifndef XXH_FORCE_MEMORY_ACCESS /* can be defined externally, on command line for example */ +# if defined(__GNUC__) && ( defined(__ARM_ARCH_6__) || defined(__ARM_ARCH_6J__) \ + || defined(__ARM_ARCH_6K__) || defined(__ARM_ARCH_6Z__) \ + || defined(__ARM_ARCH_6ZK__) || defined(__ARM_ARCH_6T2__) ) +# define XXH_FORCE_MEMORY_ACCESS 2 +# elif (defined(__INTEL_COMPILER) && !defined(_WIN32)) || \ + (defined(__GNUC__) && ( defined(__ARM_ARCH_7__) || defined(__ARM_ARCH_7A__) \ + || defined(__ARM_ARCH_7R__) || defined(__ARM_ARCH_7M__) \ + || defined(__ARM_ARCH_7S__) )) +# define XXH_FORCE_MEMORY_ACCESS 1 +# endif +#endif + +/*!XXH_ACCEPT_NULL_INPUT_POINTER : + * If input pointer is NULL, xxHash default behavior is to dereference it, triggering a segfault. + * When this macro is enabled, xxHash actively checks input for null pointer. + * It it is, result for null input pointers is the same as a null-length input. + */ +#ifndef XXH_ACCEPT_NULL_INPUT_POINTER /* can be defined externally */ +# define XXH_ACCEPT_NULL_INPUT_POINTER 0 +#endif + +/*!XXH_FORCE_NATIVE_FORMAT : + * By default, xxHash library provides endian-independent Hash values, based on little-endian convention. + * Results are therefore identical for little-endian and big-endian CPU. + * This comes at a performance cost for big-endian CPU, since some swapping is required to emulate little-endian format. + * Should endian-independence be of no importance for your application, you may set the #define below to 1, + * to improve speed for Big-endian CPU. + * This option has no impact on Little_Endian CPU. + */ +#ifndef XXH_FORCE_NATIVE_FORMAT /* can be defined externally */ +# define XXH_FORCE_NATIVE_FORMAT 0 +#endif + +/*!XXH_FORCE_ALIGN_CHECK : + * This is a minor performance trick, only useful with lots of very small keys. + * It means : check for aligned/unaligned input. + * The check costs one initial branch per hash; + * set it to 0 when the input is guaranteed to be aligned, + * or when alignment doesn't matter for performance. + */ +#ifndef XXH_FORCE_ALIGN_CHECK /* can be defined externally */ +# if defined(__i386) || defined(_M_IX86) || defined(__x86_64__) || defined(_M_X64) +# define XXH_FORCE_ALIGN_CHECK 0 +# else +# define XXH_FORCE_ALIGN_CHECK 1 +# endif +#endif + + +/* ************************************* +* Includes & Memory related functions +***************************************/ +/*! Modify the local functions below should you wish to use some other memory routines +* for malloc(), free() */ +#include +static void* XXH_malloc(size_t s) { return malloc(s); } +static void XXH_free (void* p) { free(p); } +/*! and for memcpy() */ +#include +static void* XXH_memcpy(void* dest, const void* src, size_t size) { return memcpy(dest,src,size); } + +#include /* assert */ + +#define XXH_STATIC_LINKING_ONLY +#include "xxhash.h" + + +/* ************************************* +* Compiler Specific Options +***************************************/ +#ifdef _MSC_VER /* Visual Studio */ +# pragma warning(disable : 4127) /* disable: C4127: conditional expression is constant */ +# define FORCE_INLINE static __forceinline +#else +# if defined (__cplusplus) || defined (__STDC_VERSION__) && __STDC_VERSION__ >= 199901L /* C99 */ +# ifdef __GNUC__ +# define FORCE_INLINE static inline __attribute__((always_inline)) +# else +# define FORCE_INLINE static inline +# endif +# else +# define FORCE_INLINE static +# endif /* __STDC_VERSION__ */ +#endif + + +/* ************************************* +* Basic Types +***************************************/ +#ifndef MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint8_t BYTE; + typedef uint16_t U16; + typedef uint32_t U32; +# else + typedef unsigned char BYTE; + typedef unsigned short U16; + typedef unsigned int U32; +# endif +#endif + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U32 XXH_read32(const void* memPtr) { return *(const U32*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; } __attribute__((packed)) unalign; +static U32 XXH_read32(const void* ptr) { return ((const unalign*)ptr)->u32; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ +static U32 XXH_read32(const void* memPtr) +{ + U32 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + + +/* **************************************** +* Compiler-specific Functions and Macros +******************************************/ +#define XXH_GCC_VERSION (__GNUC__ * 100 + __GNUC_MINOR__) + +/* Note : although _rotl exists for minGW (GCC under windows), performance seems poor */ +#if defined(_MSC_VER) +# define XXH_rotl32(x,r) _rotl(x,r) +# define XXH_rotl64(x,r) _rotl64(x,r) +#else +# define XXH_rotl32(x,r) ((x << r) | (x >> (32 - r))) +# define XXH_rotl64(x,r) ((x << r) | (x >> (64 - r))) +#endif + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap32 _byteswap_ulong +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap32 __builtin_bswap32 +#else +static U32 XXH_swap32 (U32 x) +{ + return ((x << 24) & 0xff000000 ) | + ((x << 8) & 0x00ff0000 ) | + ((x >> 8) & 0x0000ff00 ) | + ((x >> 24) & 0x000000ff ); +} +#endif + + +/* ************************************* +* Architecture Macros +***************************************/ +typedef enum { XXH_bigEndian=0, XXH_littleEndian=1 } XXH_endianess; + +/* XXH_CPU_LITTLE_ENDIAN can be defined externally, for example on the compiler command line */ +#ifndef XXH_CPU_LITTLE_ENDIAN +static int XXH_isLittleEndian(void) +{ + const union { U32 u; BYTE c[4]; } one = { 1 }; /* don't use static : performance detrimental */ + return one.c[0]; +} +# define XXH_CPU_LITTLE_ENDIAN XXH_isLittleEndian() +#endif + + +/* *************************** +* Memory reads +*****************************/ +typedef enum { XXH_aligned, XXH_unaligned } XXH_alignment; + +FORCE_INLINE U32 XXH_readLE32_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read32(ptr) : XXH_swap32(XXH_read32(ptr)); + else + return endian==XXH_littleEndian ? *(const U32*)ptr : XXH_swap32(*(const U32*)ptr); +} + +FORCE_INLINE U32 XXH_readLE32(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE32_align(ptr, endian, XXH_unaligned); +} + +static U32 XXH_readBE32(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap32(XXH_read32(ptr)) : XXH_read32(ptr); +} + + +/* ************************************* +* Macros +***************************************/ +#define XXH_STATIC_ASSERT(c) { enum { XXH_sa = 1/(int)(!!(c)) }; } /* use after variable declarations */ +XXH_PUBLIC_API unsigned XXH_versionNumber (void) { return XXH_VERSION_NUMBER; } + + +/* ******************************************************************* +* 32-bit hash functions +*********************************************************************/ +static const U32 PRIME32_1 = 2654435761U; +static const U32 PRIME32_2 = 2246822519U; +static const U32 PRIME32_3 = 3266489917U; +static const U32 PRIME32_4 = 668265263U; +static const U32 PRIME32_5 = 374761393U; + +static U32 XXH32_round(U32 seed, U32 input) +{ + seed += input * PRIME32_2; + seed = XXH_rotl32(seed, 13); + seed *= PRIME32_1; + return seed; +} + +/* mix all bits */ +static U32 XXH32_avalanche(U32 h32) +{ + h32 ^= h32 >> 15; + h32 *= PRIME32_2; + h32 ^= h32 >> 13; + h32 *= PRIME32_3; + h32 ^= h32 >> 16; + return(h32); +} + +#define XXH_get32bits(p) XXH_readLE32_align(p, endian, align) + +static U32 +XXH32_finalize(U32 h32, const void* ptr, size_t len, + XXH_endianess endian, XXH_alignment align) + +{ + const BYTE* p = (const BYTE*)ptr; + +#define PROCESS1 \ + h32 += (*p++) * PRIME32_5; \ + h32 = XXH_rotl32(h32, 11) * PRIME32_1 ; + +#define PROCESS4 \ + h32 += XXH_get32bits(p) * PRIME32_3; \ + p+=4; \ + h32 = XXH_rotl32(h32, 17) * PRIME32_4 ; + + switch(len&15) /* or switch(bEnd - p) */ + { + case 12: PROCESS4; + /* fallthrough */ + case 8: PROCESS4; + /* fallthrough */ + case 4: PROCESS4; + return XXH32_avalanche(h32); + + case 13: PROCESS4; + /* fallthrough */ + case 9: PROCESS4; + /* fallthrough */ + case 5: PROCESS4; + PROCESS1; + return XXH32_avalanche(h32); + + case 14: PROCESS4; + /* fallthrough */ + case 10: PROCESS4; + /* fallthrough */ + case 6: PROCESS4; + PROCESS1; + PROCESS1; + return XXH32_avalanche(h32); + + case 15: PROCESS4; + /* fallthrough */ + case 11: PROCESS4; + /* fallthrough */ + case 7: PROCESS4; + /* fallthrough */ + case 3: PROCESS1; + /* fallthrough */ + case 2: PROCESS1; + /* fallthrough */ + case 1: PROCESS1; + /* fallthrough */ + case 0: return XXH32_avalanche(h32); + } + assert(0); + return h32; /* reaching this point is deemed impossible */ +} + + +FORCE_INLINE U32 +XXH32_endian_align(const void* input, size_t len, U32 seed, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U32 h32; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)16; + } +#endif + + if (len>=16) { + const BYTE* const limit = bEnd - 15; + U32 v1 = seed + PRIME32_1 + PRIME32_2; + U32 v2 = seed + PRIME32_2; + U32 v3 = seed + 0; + U32 v4 = seed - PRIME32_1; + + do { + v1 = XXH32_round(v1, XXH_get32bits(p)); p+=4; + v2 = XXH32_round(v2, XXH_get32bits(p)); p+=4; + v3 = XXH32_round(v3, XXH_get32bits(p)); p+=4; + v4 = XXH32_round(v4, XXH_get32bits(p)); p+=4; + } while (p < limit); + + h32 = XXH_rotl32(v1, 1) + XXH_rotl32(v2, 7) + + XXH_rotl32(v3, 12) + XXH_rotl32(v4, 18); + } else { + h32 = seed + PRIME32_5; + } + + h32 += (U32)len; + + return XXH32_finalize(h32, p, len&15, endian, align); +} + + +XXH_PUBLIC_API unsigned int XXH32 (const void* input, size_t len, unsigned int seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH32_state_t state; + XXH32_reset(&state, seed); + XXH32_update(&state, input, len); + return XXH32_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 3) == 0) { /* Input is 4-bytes aligned, leverage the speed benefit */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH32_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + + + +/*====== Hash streaming ======*/ + +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void) +{ + return (XXH32_state_t*)XXH_malloc(sizeof(XXH32_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dstState, const XXH32_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH32_reset(XXH32_state_t* statePtr, unsigned int seed) +{ + XXH32_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME32_1 + PRIME32_2; + state.v2 = seed + PRIME32_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME32_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + + +FORCE_INLINE XXH_errorcode +XXH32_update_endian(XXH32_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + state->total_len_32 += (unsigned)len; + state->large_len |= (len>=16) | (state->total_len_32>=16); + + if (state->memsize + len < 16) { /* fill in tmp buffer */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, len); + state->memsize += (unsigned)len; + return XXH_OK; + } + + if (state->memsize) { /* some data left from previous update */ + XXH_memcpy((BYTE*)(state->mem32) + state->memsize, input, 16-state->memsize); + { const U32* p32 = state->mem32; + state->v1 = XXH32_round(state->v1, XXH_readLE32(p32, endian)); p32++; + state->v2 = XXH32_round(state->v2, XXH_readLE32(p32, endian)); p32++; + state->v3 = XXH32_round(state->v3, XXH_readLE32(p32, endian)); p32++; + state->v4 = XXH32_round(state->v4, XXH_readLE32(p32, endian)); + } + p += 16-state->memsize; + state->memsize = 0; + } + + if (p <= bEnd-16) { + const BYTE* const limit = bEnd - 16; + U32 v1 = state->v1; + U32 v2 = state->v2; + U32 v3 = state->v3; + U32 v4 = state->v4; + + do { + v1 = XXH32_round(v1, XXH_readLE32(p, endian)); p+=4; + v2 = XXH32_round(v2, XXH_readLE32(p, endian)); p+=4; + v3 = XXH32_round(v3, XXH_readLE32(p, endian)); p+=4; + v4 = XXH32_round(v4, XXH_readLE32(p, endian)); p+=4; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem32, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + + +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH32_update_endian(state_in, input, len, XXH_bigEndian); +} + + +FORCE_INLINE U32 +XXH32_digest_endian (const XXH32_state_t* state, XXH_endianess endian) +{ + U32 h32; + + if (state->large_len) { + h32 = XXH_rotl32(state->v1, 1) + + XXH_rotl32(state->v2, 7) + + XXH_rotl32(state->v3, 12) + + XXH_rotl32(state->v4, 18); + } else { + h32 = state->v3 /* == seed */ + PRIME32_5; + } + + h32 += state->total_len_32; + + return XXH32_finalize(h32, state->mem32, state->memsize, endian, XXH_aligned); +} + + +XXH_PUBLIC_API unsigned int XXH32_digest (const XXH32_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH32_digest_endian(state_in, XXH_littleEndian); + else + return XXH32_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +/*! Default XXH result types are basic unsigned 32 and 64 bits. +* The canonical representation follows human-readable write convention, aka big-endian (large digits first). +* These functions allow transformation of hash result into and from its canonical format. +* This way, hash values can be written into a file or buffer, remaining comparable across different systems. +*/ + +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH32_canonical_t) == sizeof(XXH32_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap32(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src) +{ + return XXH_readBE32(src); +} + + +#ifndef XXH_NO_LONG_LONG + +/* ******************************************************************* +* 64-bit hash functions +*********************************************************************/ + +/*====== Memory access ======*/ + +#ifndef MEM_MODULE +# define MEM_MODULE +# if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + typedef uint64_t U64; +# else + /* if compiler doesn't support unsigned long long, replace by another 64-bit type */ + typedef unsigned long long U64; +# endif +#endif + + +#if (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==2)) + +/* Force direct memory access. Only works on CPU which support unaligned memory access in hardware */ +static U64 XXH_read64(const void* memPtr) { return *(const U64*) memPtr; } + +#elif (defined(XXH_FORCE_MEMORY_ACCESS) && (XXH_FORCE_MEMORY_ACCESS==1)) + +/* __pack instructions are safer, but compiler specific, hence potentially problematic for some compilers */ +/* currently only defined for gcc and icc */ +typedef union { U32 u32; U64 u64; } __attribute__((packed)) unalign64; +static U64 XXH_read64(const void* ptr) { return ((const unalign64*)ptr)->u64; } + +#else + +/* portable and safe solution. Generally efficient. + * see : http://stackoverflow.com/a/32095106/646947 + */ + +static U64 XXH_read64(const void* memPtr) +{ + U64 val; + memcpy(&val, memPtr, sizeof(val)); + return val; +} + +#endif /* XXH_FORCE_DIRECT_MEMORY_ACCESS */ + +#if defined(_MSC_VER) /* Visual Studio */ +# define XXH_swap64 _byteswap_uint64 +#elif XXH_GCC_VERSION >= 403 +# define XXH_swap64 __builtin_bswap64 +#else +static U64 XXH_swap64 (U64 x) +{ + return ((x << 56) & 0xff00000000000000ULL) | + ((x << 40) & 0x00ff000000000000ULL) | + ((x << 24) & 0x0000ff0000000000ULL) | + ((x << 8) & 0x000000ff00000000ULL) | + ((x >> 8) & 0x00000000ff000000ULL) | + ((x >> 24) & 0x0000000000ff0000ULL) | + ((x >> 40) & 0x000000000000ff00ULL) | + ((x >> 56) & 0x00000000000000ffULL); +} +#endif + +FORCE_INLINE U64 XXH_readLE64_align(const void* ptr, XXH_endianess endian, XXH_alignment align) +{ + if (align==XXH_unaligned) + return endian==XXH_littleEndian ? XXH_read64(ptr) : XXH_swap64(XXH_read64(ptr)); + else + return endian==XXH_littleEndian ? *(const U64*)ptr : XXH_swap64(*(const U64*)ptr); +} + +FORCE_INLINE U64 XXH_readLE64(const void* ptr, XXH_endianess endian) +{ + return XXH_readLE64_align(ptr, endian, XXH_unaligned); +} + +static U64 XXH_readBE64(const void* ptr) +{ + return XXH_CPU_LITTLE_ENDIAN ? XXH_swap64(XXH_read64(ptr)) : XXH_read64(ptr); +} + + +/*====== xxh64 ======*/ + +static const U64 PRIME64_1 = 11400714785074694791ULL; +static const U64 PRIME64_2 = 14029467366897019727ULL; +static const U64 PRIME64_3 = 1609587929392839161ULL; +static const U64 PRIME64_4 = 9650029242287828579ULL; +static const U64 PRIME64_5 = 2870177450012600261ULL; + +static U64 XXH64_round(U64 acc, U64 input) +{ + acc += input * PRIME64_2; + acc = XXH_rotl64(acc, 31); + acc *= PRIME64_1; + return acc; +} + +static U64 XXH64_mergeRound(U64 acc, U64 val) +{ + val = XXH64_round(0, val); + acc ^= val; + acc = acc * PRIME64_1 + PRIME64_4; + return acc; +} + +static U64 XXH64_avalanche(U64 h64) +{ + h64 ^= h64 >> 33; + h64 *= PRIME64_2; + h64 ^= h64 >> 29; + h64 *= PRIME64_3; + h64 ^= h64 >> 32; + return h64; +} + + +#define XXH_get64bits(p) XXH_readLE64_align(p, endian, align) + +static U64 +XXH64_finalize(U64 h64, const void* ptr, size_t len, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)ptr; + +#define PROCESS1_64 \ + h64 ^= (*p++) * PRIME64_5; \ + h64 = XXH_rotl64(h64, 11) * PRIME64_1; + +#define PROCESS4_64 \ + h64 ^= (U64)(XXH_get32bits(p)) * PRIME64_1; \ + p+=4; \ + h64 = XXH_rotl64(h64, 23) * PRIME64_2 + PRIME64_3; + +#define PROCESS8_64 { \ + U64 const k1 = XXH64_round(0, XXH_get64bits(p)); \ + p+=8; \ + h64 ^= k1; \ + h64 = XXH_rotl64(h64,27) * PRIME64_1 + PRIME64_4; \ +} + + switch(len&31) { + case 24: PROCESS8_64; + /* fallthrough */ + case 16: PROCESS8_64; + /* fallthrough */ + case 8: PROCESS8_64; + return XXH64_avalanche(h64); + + case 28: PROCESS8_64; + /* fallthrough */ + case 20: PROCESS8_64; + /* fallthrough */ + case 12: PROCESS8_64; + /* fallthrough */ + case 4: PROCESS4_64; + return XXH64_avalanche(h64); + + case 25: PROCESS8_64; + /* fallthrough */ + case 17: PROCESS8_64; + /* fallthrough */ + case 9: PROCESS8_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 29: PROCESS8_64; + /* fallthrough */ + case 21: PROCESS8_64; + /* fallthrough */ + case 13: PROCESS8_64; + /* fallthrough */ + case 5: PROCESS4_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 26: PROCESS8_64; + /* fallthrough */ + case 18: PROCESS8_64; + /* fallthrough */ + case 10: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 30: PROCESS8_64; + /* fallthrough */ + case 22: PROCESS8_64; + /* fallthrough */ + case 14: PROCESS8_64; + /* fallthrough */ + case 6: PROCESS4_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 27: PROCESS8_64; + /* fallthrough */ + case 19: PROCESS8_64; + /* fallthrough */ + case 11: PROCESS8_64; + PROCESS1_64; + PROCESS1_64; + PROCESS1_64; + return XXH64_avalanche(h64); + + case 31: PROCESS8_64; + /* fallthrough */ + case 23: PROCESS8_64; + /* fallthrough */ + case 15: PROCESS8_64; + /* fallthrough */ + case 7: PROCESS4_64; + /* fallthrough */ + case 3: PROCESS1_64; + /* fallthrough */ + case 2: PROCESS1_64; + /* fallthrough */ + case 1: PROCESS1_64; + /* fallthrough */ + case 0: return XXH64_avalanche(h64); + } + + /* impossible to reach */ + assert(0); + return 0; /* unreachable, but some compilers complain without it */ +} + +FORCE_INLINE U64 +XXH64_endian_align(const void* input, size_t len, U64 seed, + XXH_endianess endian, XXH_alignment align) +{ + const BYTE* p = (const BYTE*)input; + const BYTE* bEnd = p + len; + U64 h64; + +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + if (p==NULL) { + len=0; + bEnd=p=(const BYTE*)(size_t)32; + } +#endif + + if (len>=32) { + const BYTE* const limit = bEnd - 32; + U64 v1 = seed + PRIME64_1 + PRIME64_2; + U64 v2 = seed + PRIME64_2; + U64 v3 = seed + 0; + U64 v4 = seed - PRIME64_1; + + do { + v1 = XXH64_round(v1, XXH_get64bits(p)); p+=8; + v2 = XXH64_round(v2, XXH_get64bits(p)); p+=8; + v3 = XXH64_round(v3, XXH_get64bits(p)); p+=8; + v4 = XXH64_round(v4, XXH_get64bits(p)); p+=8; + } while (p<=limit); + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + + } else { + h64 = seed + PRIME64_5; + } + + h64 += (U64) len; + + return XXH64_finalize(h64, p, len, endian, align); +} + + +XXH_PUBLIC_API unsigned long long XXH64 (const void* input, size_t len, unsigned long long seed) +{ +#if 0 + /* Simple version, good for code maintenance, but unfortunately slow for small inputs */ + XXH64_state_t state; + XXH64_reset(&state, seed); + XXH64_update(&state, input, len); + return XXH64_digest(&state); +#else + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if (XXH_FORCE_ALIGN_CHECK) { + if ((((size_t)input) & 7)==0) { /* Input is aligned, let's leverage the speed advantage */ + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_aligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_aligned); + } } + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_endian_align(input, len, seed, XXH_littleEndian, XXH_unaligned); + else + return XXH64_endian_align(input, len, seed, XXH_bigEndian, XXH_unaligned); +#endif +} + +/*====== Hash Streaming ======*/ + +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void) +{ + return (XXH64_state_t*)XXH_malloc(sizeof(XXH64_state_t)); +} +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr) +{ + XXH_free(statePtr); + return XXH_OK; +} + +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dstState, const XXH64_state_t* srcState) +{ + memcpy(dstState, srcState, sizeof(*dstState)); +} + +XXH_PUBLIC_API XXH_errorcode XXH64_reset(XXH64_state_t* statePtr, unsigned long long seed) +{ + XXH64_state_t state; /* using a local state to memcpy() in order to avoid strict-aliasing warnings */ + memset(&state, 0, sizeof(state)); + state.v1 = seed + PRIME64_1 + PRIME64_2; + state.v2 = seed + PRIME64_2; + state.v3 = seed + 0; + state.v4 = seed - PRIME64_1; + /* do not write into reserved, planned to be removed in a future version */ + memcpy(statePtr, &state, sizeof(state) - sizeof(state.reserved)); + return XXH_OK; +} + +FORCE_INLINE XXH_errorcode +XXH64_update_endian (XXH64_state_t* state, const void* input, size_t len, XXH_endianess endian) +{ + if (input==NULL) +#if defined(XXH_ACCEPT_NULL_INPUT_POINTER) && (XXH_ACCEPT_NULL_INPUT_POINTER>=1) + return XXH_OK; +#else + return XXH_ERROR; +#endif + + { const BYTE* p = (const BYTE*)input; + const BYTE* const bEnd = p + len; + + state->total_len += len; + + if (state->memsize + len < 32) { /* fill in tmp buffer */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, len); + state->memsize += (U32)len; + return XXH_OK; + } + + if (state->memsize) { /* tmp buffer is full */ + XXH_memcpy(((BYTE*)state->mem64) + state->memsize, input, 32-state->memsize); + state->v1 = XXH64_round(state->v1, XXH_readLE64(state->mem64+0, endian)); + state->v2 = XXH64_round(state->v2, XXH_readLE64(state->mem64+1, endian)); + state->v3 = XXH64_round(state->v3, XXH_readLE64(state->mem64+2, endian)); + state->v4 = XXH64_round(state->v4, XXH_readLE64(state->mem64+3, endian)); + p += 32-state->memsize; + state->memsize = 0; + } + + if (p+32 <= bEnd) { + const BYTE* const limit = bEnd - 32; + U64 v1 = state->v1; + U64 v2 = state->v2; + U64 v3 = state->v3; + U64 v4 = state->v4; + + do { + v1 = XXH64_round(v1, XXH_readLE64(p, endian)); p+=8; + v2 = XXH64_round(v2, XXH_readLE64(p, endian)); p+=8; + v3 = XXH64_round(v3, XXH_readLE64(p, endian)); p+=8; + v4 = XXH64_round(v4, XXH_readLE64(p, endian)); p+=8; + } while (p<=limit); + + state->v1 = v1; + state->v2 = v2; + state->v3 = v3; + state->v4 = v4; + } + + if (p < bEnd) { + XXH_memcpy(state->mem64, p, (size_t)(bEnd-p)); + state->memsize = (unsigned)(bEnd-p); + } + } + + return XXH_OK; +} + +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* state_in, const void* input, size_t len) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_update_endian(state_in, input, len, XXH_littleEndian); + else + return XXH64_update_endian(state_in, input, len, XXH_bigEndian); +} + +FORCE_INLINE U64 XXH64_digest_endian (const XXH64_state_t* state, XXH_endianess endian) +{ + U64 h64; + + if (state->total_len >= 32) { + U64 const v1 = state->v1; + U64 const v2 = state->v2; + U64 const v3 = state->v3; + U64 const v4 = state->v4; + + h64 = XXH_rotl64(v1, 1) + XXH_rotl64(v2, 7) + XXH_rotl64(v3, 12) + XXH_rotl64(v4, 18); + h64 = XXH64_mergeRound(h64, v1); + h64 = XXH64_mergeRound(h64, v2); + h64 = XXH64_mergeRound(h64, v3); + h64 = XXH64_mergeRound(h64, v4); + } else { + h64 = state->v3 /*seed*/ + PRIME64_5; + } + + h64 += (U64) state->total_len; + + return XXH64_finalize(h64, state->mem64, (size_t)state->total_len, endian, XXH_aligned); +} + +XXH_PUBLIC_API unsigned long long XXH64_digest (const XXH64_state_t* state_in) +{ + XXH_endianess endian_detected = (XXH_endianess)XXH_CPU_LITTLE_ENDIAN; + + if ((endian_detected==XXH_littleEndian) || XXH_FORCE_NATIVE_FORMAT) + return XXH64_digest_endian(state_in, XXH_littleEndian); + else + return XXH64_digest_endian(state_in, XXH_bigEndian); +} + + +/*====== Canonical representation ======*/ + +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash) +{ + XXH_STATIC_ASSERT(sizeof(XXH64_canonical_t) == sizeof(XXH64_hash_t)); + if (XXH_CPU_LITTLE_ENDIAN) hash = XXH_swap64(hash); + memcpy(dst, &hash, sizeof(*dst)); +} + +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src) +{ + return XXH_readBE64(src); +} + +#endif /* XXH_NO_LONG_LONG */ diff --git a/core/deps/xxhash/xxhash.h b/core/deps/xxhash/xxhash.h new file mode 100644 index 000000000..d6bad9433 --- /dev/null +++ b/core/deps/xxhash/xxhash.h @@ -0,0 +1,328 @@ +/* + xxHash - Extremely Fast Hash algorithm + Header File + Copyright (C) 2012-2016, Yann Collet. + + BSD 2-Clause License (http://www.opensource.org/licenses/bsd-license.php) + + Redistribution and use in source and binary forms, with or without + modification, are permitted provided that the following conditions are + met: + + * Redistributions of source code must retain the above copyright + notice, this list of conditions and the following disclaimer. + * Redistributions in binary form must reproduce the above + copyright notice, this list of conditions and the following disclaimer + in the documentation and/or other materials provided with the + distribution. + + THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS + "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT + LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR + A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT + OWNER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, + SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT + LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, + DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY + THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT + (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE + OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. + + You can contact the author at : + - xxHash source repository : https://github.com/Cyan4973/xxHash +*/ + +/* Notice extracted from xxHash homepage : + +xxHash is an extremely fast Hash algorithm, running at RAM speed limits. +It also successfully passes all tests from the SMHasher suite. + +Comparison (single thread, Windows Seven 32 bits, using SMHasher on a Core 2 Duo @3GHz) + +Name Speed Q.Score Author +xxHash 5.4 GB/s 10 +CrapWow 3.2 GB/s 2 Andrew +MumurHash 3a 2.7 GB/s 10 Austin Appleby +SpookyHash 2.0 GB/s 10 Bob Jenkins +SBox 1.4 GB/s 9 Bret Mulvey +Lookup3 1.2 GB/s 9 Bob Jenkins +SuperFastHash 1.2 GB/s 1 Paul Hsieh +CityHash64 1.05 GB/s 10 Pike & Alakuijala +FNV 0.55 GB/s 5 Fowler, Noll, Vo +CRC32 0.43 GB/s 9 +MD5-32 0.33 GB/s 10 Ronald L. Rivest +SHA1-32 0.28 GB/s 10 + +Q.Score is a measure of quality of the hash function. +It depends on successfully passing SMHasher test set. +10 is a perfect score. + +A 64-bit version, named XXH64, is available since r35. +It offers much better speed, but for 64-bit applications only. +Name Speed on 64 bits Speed on 32 bits +XXH64 13.8 GB/s 1.9 GB/s +XXH32 6.8 GB/s 6.0 GB/s +*/ + +#ifndef XXHASH_H_5627135585666179 +#define XXHASH_H_5627135585666179 1 + +#if defined (__cplusplus) +extern "C" { +#endif + + +/* **************************** +* Definitions +******************************/ +#include /* size_t */ +typedef enum { XXH_OK=0, XXH_ERROR } XXH_errorcode; + + +/* **************************** + * API modifier + ******************************/ +/** XXH_INLINE_ALL (and XXH_PRIVATE_API) + * This is useful to include xxhash functions in `static` mode + * in order to inline them, and remove their symbol from the public list. + * Inlining can offer dramatic performance improvement on small keys. + * Methodology : + * #define XXH_INLINE_ALL + * #include "xxhash.h" + * `xxhash.c` is automatically included. + * It's not useful to compile and link it as a separate module. + */ +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# ifndef XXH_STATIC_LINKING_ONLY +# define XXH_STATIC_LINKING_ONLY +# endif +# if defined(__GNUC__) +# define XXH_PUBLIC_API static __inline __attribute__((unused)) +# elif defined (__cplusplus) || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) +# define XXH_PUBLIC_API static inline +# elif defined(_MSC_VER) +# define XXH_PUBLIC_API static __inline +# else + /* this version may generate warnings for unused static functions */ +# define XXH_PUBLIC_API static +# endif +#else +# define XXH_PUBLIC_API /* do nothing */ +#endif /* XXH_INLINE_ALL || XXH_PRIVATE_API */ + +/*! XXH_NAMESPACE, aka Namespace Emulation : + * + * If you want to include _and expose_ xxHash functions from within your own library, + * but also want to avoid symbol collisions with other libraries which may also include xxHash, + * + * you can use XXH_NAMESPACE, to automatically prefix any public symbol from xxhash library + * with the value of XXH_NAMESPACE (therefore, avoid NULL and numeric values). + * + * Note that no change is required within the calling program as long as it includes `xxhash.h` : + * regular symbol name will be automatically translated by this header. + */ +#ifdef XXH_NAMESPACE +# define XXH_CAT(A,B) A##B +# define XXH_NAME2(A,B) XXH_CAT(A,B) +# define XXH_versionNumber XXH_NAME2(XXH_NAMESPACE, XXH_versionNumber) +# define XXH32 XXH_NAME2(XXH_NAMESPACE, XXH32) +# define XXH32_createState XXH_NAME2(XXH_NAMESPACE, XXH32_createState) +# define XXH32_freeState XXH_NAME2(XXH_NAMESPACE, XXH32_freeState) +# define XXH32_reset XXH_NAME2(XXH_NAMESPACE, XXH32_reset) +# define XXH32_update XXH_NAME2(XXH_NAMESPACE, XXH32_update) +# define XXH32_digest XXH_NAME2(XXH_NAMESPACE, XXH32_digest) +# define XXH32_copyState XXH_NAME2(XXH_NAMESPACE, XXH32_copyState) +# define XXH32_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH32_canonicalFromHash) +# define XXH32_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH32_hashFromCanonical) +# define XXH64 XXH_NAME2(XXH_NAMESPACE, XXH64) +# define XXH64_createState XXH_NAME2(XXH_NAMESPACE, XXH64_createState) +# define XXH64_freeState XXH_NAME2(XXH_NAMESPACE, XXH64_freeState) +# define XXH64_reset XXH_NAME2(XXH_NAMESPACE, XXH64_reset) +# define XXH64_update XXH_NAME2(XXH_NAMESPACE, XXH64_update) +# define XXH64_digest XXH_NAME2(XXH_NAMESPACE, XXH64_digest) +# define XXH64_copyState XXH_NAME2(XXH_NAMESPACE, XXH64_copyState) +# define XXH64_canonicalFromHash XXH_NAME2(XXH_NAMESPACE, XXH64_canonicalFromHash) +# define XXH64_hashFromCanonical XXH_NAME2(XXH_NAMESPACE, XXH64_hashFromCanonical) +#endif + + +/* ************************************* +* Version +***************************************/ +#define XXH_VERSION_MAJOR 0 +#define XXH_VERSION_MINOR 6 +#define XXH_VERSION_RELEASE 5 +#define XXH_VERSION_NUMBER (XXH_VERSION_MAJOR *100*100 + XXH_VERSION_MINOR *100 + XXH_VERSION_RELEASE) +XXH_PUBLIC_API unsigned XXH_versionNumber (void); + + +/*-********************************************************************** +* 32-bit hash +************************************************************************/ +typedef unsigned int XXH32_hash_t; + +/*! XXH32() : + Calculate the 32-bit hash of sequence "length" bytes stored at memory address "input". + The memory between input & input+length must be valid (allocated and read-accessible). + "seed" can be used to alter the result predictably. + Speed on Core 2 Duo @ 3 GHz (single thread, SMHasher benchmark) : 5.4 GB/s */ +XXH_PUBLIC_API XXH32_hash_t XXH32 (const void* input, size_t length, unsigned int seed); + +/*====== Streaming ======*/ +typedef struct XXH32_state_s XXH32_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH32_state_t* XXH32_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH32_freeState(XXH32_state_t* statePtr); +XXH_PUBLIC_API void XXH32_copyState(XXH32_state_t* dst_state, const XXH32_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH32_reset (XXH32_state_t* statePtr, unsigned int seed); +XXH_PUBLIC_API XXH_errorcode XXH32_update (XXH32_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH32_hash_t XXH32_digest (const XXH32_state_t* statePtr); + +/* + * Streaming functions generate the xxHash of an input provided in multiple segments. + * Note that, for small input, they are slower than single-call functions, due to state management. + * For small inputs, prefer `XXH32()` and `XXH64()`, which are better optimized. + * + * XXH state must first be allocated, using XXH*_createState() . + * + * Start a new hash by initializing state with a seed, using XXH*_reset(). + * + * Then, feed the hash state by calling XXH*_update() as many times as necessary. + * The function returns an error code, with 0 meaning OK, and any other value meaning there is an error. + * + * Finally, a hash value can be produced anytime, by using XXH*_digest(). + * This function returns the nn-bits hash as an int or long long. + * + * It's still possible to continue inserting input into the hash state after a digest, + * and generate some new hashes later on, by calling again XXH*_digest(). + * + * When done, free XXH state space if it was allocated dynamically. + */ + +/*====== Canonical representation ======*/ + +typedef struct { unsigned char digest[4]; } XXH32_canonical_t; +XXH_PUBLIC_API void XXH32_canonicalFromHash(XXH32_canonical_t* dst, XXH32_hash_t hash); +XXH_PUBLIC_API XXH32_hash_t XXH32_hashFromCanonical(const XXH32_canonical_t* src); + +/* Default result type for XXH functions are primitive unsigned 32 and 64 bits. + * The canonical representation uses human-readable write convention, aka big-endian (large digits first). + * These functions allow transformation of hash result into and from its canonical format. + * This way, hash values can be written into a file / memory, and remain comparable on different systems and programs. + */ + + +#ifndef XXH_NO_LONG_LONG +/*-********************************************************************** +* 64-bit hash +************************************************************************/ +typedef unsigned long long XXH64_hash_t; + +/*! XXH64() : + Calculate the 64-bit hash of sequence of length "len" stored at memory address "input". + "seed" can be used to alter the result predictably. + This function runs faster on 64-bit systems, but slower on 32-bit systems (see benchmark). +*/ +XXH_PUBLIC_API XXH64_hash_t XXH64 (const void* input, size_t length, unsigned long long seed); + +/*====== Streaming ======*/ +typedef struct XXH64_state_s XXH64_state_t; /* incomplete type */ +XXH_PUBLIC_API XXH64_state_t* XXH64_createState(void); +XXH_PUBLIC_API XXH_errorcode XXH64_freeState(XXH64_state_t* statePtr); +XXH_PUBLIC_API void XXH64_copyState(XXH64_state_t* dst_state, const XXH64_state_t* src_state); + +XXH_PUBLIC_API XXH_errorcode XXH64_reset (XXH64_state_t* statePtr, unsigned long long seed); +XXH_PUBLIC_API XXH_errorcode XXH64_update (XXH64_state_t* statePtr, const void* input, size_t length); +XXH_PUBLIC_API XXH64_hash_t XXH64_digest (const XXH64_state_t* statePtr); + +/*====== Canonical representation ======*/ +typedef struct { unsigned char digest[8]; } XXH64_canonical_t; +XXH_PUBLIC_API void XXH64_canonicalFromHash(XXH64_canonical_t* dst, XXH64_hash_t hash); +XXH_PUBLIC_API XXH64_hash_t XXH64_hashFromCanonical(const XXH64_canonical_t* src); +#endif /* XXH_NO_LONG_LONG */ + + + +#ifdef XXH_STATIC_LINKING_ONLY + +/* ================================================================================================ + This section contains declarations which are not guaranteed to remain stable. + They may change in future versions, becoming incompatible with a different version of the library. + These declarations should only be used with static linking. + Never use them in association with dynamic linking ! +=================================================================================================== */ + +/* These definitions are only present to allow + * static allocation of XXH state, on stack or in a struct for example. + * Never **ever** use members directly. */ + +#if !defined (__VMS) \ + && (defined (__cplusplus) \ + || (defined (__STDC_VERSION__) && (__STDC_VERSION__ >= 199901L) /* C99 */) ) +# include + +struct XXH32_state_s { + uint32_t total_len_32; + uint32_t large_len; + uint32_t v1; + uint32_t v2; + uint32_t v3; + uint32_t v4; + uint32_t mem32[4]; + uint32_t memsize; + uint32_t reserved; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +struct XXH64_state_s { + uint64_t total_len; + uint64_t v1; + uint64_t v2; + uint64_t v3; + uint64_t v4; + uint64_t mem64[4]; + uint32_t memsize; + uint32_t reserved[2]; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ + +# else + +struct XXH32_state_s { + unsigned total_len_32; + unsigned large_len; + unsigned v1; + unsigned v2; + unsigned v3; + unsigned v4; + unsigned mem32[4]; + unsigned memsize; + unsigned reserved; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH32_state_t */ + +# ifndef XXH_NO_LONG_LONG /* remove 64-bit support */ +struct XXH64_state_s { + unsigned long long total_len; + unsigned long long v1; + unsigned long long v2; + unsigned long long v3; + unsigned long long v4; + unsigned long long mem64[4]; + unsigned memsize; + unsigned reserved[2]; /* never read nor write, might be removed in a future version */ +}; /* typedef'd to XXH64_state_t */ +# endif + +# endif + + +#if defined(XXH_INLINE_ALL) || defined(XXH_PRIVATE_API) +# include "xxhash.c" /* include xxhash function bodies as `static`, for inlining */ +#endif + +#endif /* XXH_STATIC_LINKING_ONLY */ + + +#if defined (__cplusplus) +} +#endif + +#endif /* XXHASH_H_5627135585666179 */ diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index 80ebd77c0..610f8de2a 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -15,8 +15,14 @@ #define LOGW printf #define LOGI printf #endif +#ifndef RELEASE +#define LOGJVS(...) LOGI(__VA_ARGS__) +#else +#define LOGJVS(...) +#endif #include "deps/zlib/zlib.h" +#include "deps/xxhash/xxhash.h" const char* maple_sega_controller_name = "Dreamcast Controller"; const char* maple_sega_vmu_name = "Visual Memory"; @@ -64,10 +70,16 @@ enum MapleDeviceCommand MDCF_SetCondition = 0x0E, //FT,data ... MDCF_MICControl = 0x0F, //FT,MIC data ... MDCF_ARGunControl = 0x10, //FT,AR-Gun data ... + + MDC_JVSUploadFirmware = 0x80, // JVS bridge firmware + MDC_JVSGetId = 0x82, + MDC_JVSCommand = 0x86, // JVS I/O }; enum MapleDeviceRV { + MDRS_JVSNone = 0x00, // No reply, used for multiple JVS I/O boards + MDRS_DeviceStatus = 0x05, //28 words MDRS_DeviceStatusAll = 0x06, //28 words + device dependent data MDRS_DeviceReply = 0x07, //0 words @@ -75,10 +87,12 @@ enum MapleDeviceRV MDRE_UnknownFunction = 0xFE, //0 words MDRE_UnknownCmd = 0xFD, //0 words - MDRE_TransminAgain = 0xFC, //0 words + MDRE_TransmitAgain = 0xFC, //1 word, 1 or 2? MDRE_FileError = 0xFB, //1 word, bitfield MDRE_LCDError = 0xFA, //1 word, bitfield MDRE_ARGunError = 0xF9, //1 word, bitfield + + MDRS_JVSReply = 0x87, // JVS I/O }; NaomiInputMapping Naomi_Mapping; @@ -145,7 +159,7 @@ struct maple_base: maple_device } u32 r_count() { return dma_count_in; } - virtual u32 Dma(u32 Command,u32* buffer_in,u32 buffer_in_len,u32* buffer_out,u32& buffer_out_len) + u32 Dma(u32 Command,u32* buffer_in,u32 buffer_in_len,u32* buffer_out,u32& buffer_out_len) { dma_buffer_out=(u8*)buffer_out; dma_count_out=&buffer_out_len; @@ -156,6 +170,25 @@ struct maple_base: maple_device return dma(Command); } virtual u32 dma(u32 cmd)=0; + + virtual u32 RawDma(u32* buffer_in, u32 buffer_in_len, u32* buffer_out) + { + u32 command=buffer_in[0] &0xFF; + //Recipient address + u32 reci = (buffer_in[0] >> 8) & 0xFF; + //Sender address + u32 send = (buffer_in[0] >> 16) & 0xFF; + u32 outlen = 0; + u32 resp = Dma(command, &buffer_in[1], buffer_in_len - 4, &buffer_out[1], outlen); + + if (reci & 0x20) + reci |= maple_GetAttachedDevices(maple_GetBusId(reci)); + + verify(u8(outlen/4)*4==outlen); + buffer_out[0] = (resp <<0 ) | (send << 8) | (reci << 16) | ((outlen / 4) << 24); + + return outlen + 4; + } }; /* @@ -523,7 +556,7 @@ struct maple_sega_vmu: maple_base if (r32()!=0) { printf("VMU: Block read: MFID_3_Clock : invalid params \n"); - return MDRE_TransminAgain; //invalid params + return MDRE_TransmitAgain; //invalid params } else { @@ -669,7 +702,7 @@ struct maple_sega_vmu: maple_base case MFID_3_Clock: { if (r32()!=0 || r_count()!=8) - return MDRE_TransminAgain; //invalid params ... + return MDRE_TransmitAgain; //invalid params ... else { u8 timebuf[8]; @@ -897,7 +930,7 @@ struct maple_microphone: maple_base case 0x03: LOGI("maple_microphone::dma MDCF_MICControl set gain %#010x\n",secondword); return MDRS_DeviceReply; - case MDRE_TransminAgain: + case MDRE_TransmitAgain: LOGW("maple_microphone::dma MDCF_MICControl MDRE_TransminAgain"); //apparently this doesnt matter //wptr(micdata, SIZE_OF_MIC_DATA); @@ -1366,380 +1399,852 @@ static string get_eeprom_file_path() #endif /* -Sega Dreamcast Controller -No error checking of any kind, but works just fine + * Sega JVS I/O board */ +bool coin_chute; +static int coin_count; + +struct maple_naomi_jamma; + +class jvs_io_board +{ +public: + jvs_io_board(u8 node_id, bool last_node, maple_naomi_jamma *parent) + { + this->node_id = node_id; + this->last_node = last_node; + this->parent = parent; + } + + MapleDeviceRV handle_jvs_command(u8* data); + bool maple_serialize(void **data, unsigned int *total_size); + bool maple_unserialize(void **data, unsigned int *total_size); + + bool rotary_encoders = false; + bool lightgun_as_analog = false; + +private: + void make_jvs_reply_buffer(bool empty = false); + void finish_jvs_reply_buffer(); + + const u8 ALL_NODES = 0xff; + u8 node_id = 0; + bool last_node; + maple_naomi_jamma *parent; + + u8 jvs_repeat_request[256]; + u32 jvs_repeat_length = 0; + u8 jvs_request[256]; + u32 jvs_length = 0; + u8 *dma_out = NULL; +}; + struct maple_naomi_jamma : maple_sega_controller { + class jvs_io_board *io_boards[1]; + bool crazy_mode = false; + + maple_naomi_jamma() + { + io_boards[0] = new jvs_io_board(1, true, this); +// io_boards[1] = new jvs_io_board(2, true, this); +// io_boards[0]->rotary_encoders = true; + } + virtual MapleDeviceType get_device_type() { return MDT_NaomiJamma; } - virtual u32 dma(u32 cmd) + virtual u32 RawDma(u32* buffer_in, u32 buffer_in_len, u32* buffer_out) { - u32* buffer_in = (u32*)dma_buffer_in; - u32* buffer_out = (u32*)dma_buffer_out; + /* + printf("JVS IN: "); + u8 *p = (u8*)buffer_in; + for (int i = 0; i < buffer_in_len; i++) printf("%02x ", *p++); + printf("\n"); + */ - u8* buffer_in_b = dma_buffer_in; - u8* buffer_out_b = dma_buffer_out; + u32 out_len = 0; + dma_buffer_out = (u8 *)buffer_out; + dma_count_out = &out_len; - u32& buffer_out_len = *dma_count_out; - u32 buffer_in_len = dma_count_in; + dma_buffer_in = (u8 *)buffer_in + 4; + dma_count_in = buffer_in_len - 4; + u32 cmd = *(u8*)buffer_in; switch (cmd) { - case 0x86: - { - u32 subcode = *(u8*)buffer_in; - //printf("Naomi 0x86 : %x\n",SubCode); - switch (subcode) + case MDC_JVSCommand: { - case 0x15: - case 0x33: - { - PlainJoystickState pjs; - config->GetInput(&pjs); + u32 subcode = dma_buffer_in[0]; - buffer_out[0] = 0xffffffff; - buffer_out[1] = 0xffffffff; - u32 keycode = ~kcode[0]; - u32 keycode2 = ~kcode[1]; - - if (keycode&NAOMI_SERVICE_KEY_2) //Service - buffer_out[0] &= ~(1 << 0x1b); - - if (keycode&NAOMI_TEST_KEY_2) //Test - buffer_out[0] &= ~(1 << 0x1a); - - if (State.Mode == 0 && subcode != 0x33) //Get Caps + switch (subcode) // Sub-command { - buffer_out_b[0x11 + 1] = 0x8E; //Valid data check - buffer_out_b[0x11 + 2] = 0x01; - buffer_out_b[0x11 + 3] = 0x00; - buffer_out_b[0x11 + 4] = 0xFF; - buffer_out_b[0x11 + 5] = 0xE0; - buffer_out_b[0x11 + 8] = 0x01; - - switch (State.Cmd) - { - //Reset, in : 2 bytes, out : 0 case 0xF0: - break; - - //Find nodes? - //In addressing Slave address, in : 2 bytes, out : 1 case 0xF1: + case 0x13: + case 0x15: + case 0x17: + case 0x19: + case 0x21: + case 0x27: + case 0x33: { - buffer_out_len = 4 * 4; - } - break; - - //Speed Change, in : 2 bytes, out : 0 - case 0xF2: - break; - - //Name - //"In the I / O ID" "Reading each slave ID data" - //"NAMCO LTD.; I / O PCB-1000; ver1.0; for domestic only, no analog input" - //in : 1 byte, out : max 102 - case 0x10: - { - static char ID1[102] = "nullDC Team; I/O Plugin-1; ver0.2; for nullDC or other emus"; - buffer_out_b[0x8 + 0x10] = (u8)strlen(ID1) + 3; - for (int i = 0; ID1[i] != 0; ++i) + MapleDeviceRV rc; + for (int i = 0; i < sizeof(io_boards)/sizeof(void*); i++) { - buffer_out_b[0x8 + 0x13 + i] = ID1[i]; + rc = io_boards[i]->handle_jvs_command(dma_buffer_in); + if (rc != MDRS_JVSNone) + break; } } break; - //CMD Version - //REV in command|Format command to read the (revision)|One|Two - //in : 1 byte, out : 2 bytes - case 0x11: + case 0x0B: //EEPROM write { - buffer_out_b[0x8 + 0x13] = 0x13; + int address = dma_buffer_in[1]; + int size = dma_buffer_in[2]; + //printf("EEprom write %08X %08X\n",address,size); + //printState(Command,buffer_in,buffer_in_len); + memcpy(EEPROM + address, dma_buffer_in + 4, size); + +#ifdef SAVE_EPPROM + string eeprom_file = get_eeprom_file_path(); + FILE* f = fopen(eeprom_file.c_str(), "wb"); + if (f) + { + fwrite(EEPROM, 1, 0x80, f); + fclose(f); + printf("Saved EEPROM to %s\n", eeprom_file.c_str()); + } + else + printf("EEPROM SAVE FAILED to %s\n", eeprom_file.c_str()); +#endif + w8(MDRS_JVSReply); + w8(0x20); + w8(0x00); + w8(0x01); + memcpy(dma_buffer_out, EEPROM, 4); + out_len += 4; } break; - //JVS Version - //In JV REV|JAMMA VIDEO standard reading (revision)|One|Two - //in : 1 byte, out : 2 bytes - case 0x12: + case 0x3: //EEPROM read { - buffer_out_b[0x8 + 0x13] = 0x30; +#ifdef SAVE_EPPROM + if (!EEPROM_loaded) + { + EEPROM_loaded = true; + string eeprom_file = get_eeprom_file_path(); + FILE* f = fopen(eeprom_file.c_str(), "rb"); + if (f) + { + fread(EEPROM, 1, 0x80, f); + fclose(f); + printf("Loaded EEPROM from %s\n", eeprom_file.c_str()); + } + else + printf("EEPROM file not found at %s\n", eeprom_file.c_str()); + } +#endif + //printf("EEprom READ\n"); + int address = dma_buffer_in[1]; + //printState(Command,buffer_in,buffer_in_len); + w8(MDRS_JVSReply); + w8(0x20); + w8(0x00); + w8(0x20); + memcpy(dma_buffer_out, EEPROM + address, 0x80); + out_len += 0x80; } break; - //COM Version - //VER in the communication system|Read a communication system compliant version of |One|Two - //in : 1 byte, out : 2 bytes - case 0x13: + case 0x31: // DIP switches { - buffer_out_b[0x8 + 0x13] = 0x10; + w8(MDRS_JVSReply); + w8(0x00); + w8(0x20); + w8(0x05); + + w8(0x32); + w8(0xff); + w8(0xff); + w8(0xff); + w32(0xffffffff); // bit16: 1=VGA, 0=NTSCi + w32(0xffffffff); + w32(0xffffffff); + w32(0xffffffff); } break; - //Features - //Check in feature |Each features a slave to read |One |6 to - //in : 1 byte, out : 6 + (?) - case 0x14: - { - unsigned char *FeatPtr = buffer_out_b + 0x8 + 0x13; - buffer_out_b[0x8 + 0x9 + 0x3] = 0x0; - buffer_out_b[0x8 + 0x9 + 0x9] = 0x1; -#define ADDFEAT(Feature,Count1,Count2,Count3) *FeatPtr++=Feature; *FeatPtr++=Count1; *FeatPtr++=Count2; *FeatPtr++=Count3; - ADDFEAT(1, 2, 12, 0); //Feat 1=Digital Inputs. 2 Players. 12 bits - ADDFEAT(2, 2, 0, 0); //Feat 2=Coin inputs. 2 Inputs - ADDFEAT(3, 4, 0, 0); //Feat 3=Analog. 4 Chans + //case 0x3: + // break; - ADDFEAT(0, 0, 0, 0); //End of list - } - break; + case 0x1: + w8(MDRS_JVSReply); + w8(0x00); + w8(0x20); + w8(0x02); + + w8(0x2); + w8(0x0); + w8(0x0); + w8(0x0); + + w8(0x0); + w8(0x0); + w8(0x0); + w8(0x0); + break; default: - printf("unknown CAP %X\n", State.Cmd); - return 0; - } - buffer_out_len = 4 * 4; + printf("JVS: Unknown 0x86 sub-command %x\n", subcode); + break; } - else if (State.Mode == 1 || State.Mode == 2 || subcode == 0x33) //Get Data + } + break; + + + case MDC_JVSUploadFirmware: + { + static FILE *fw_dump; + static XXH32_state_t* state; + + if (state == NULL) { - unsigned char glbl = 0x00; - unsigned char p1_1 = 0x00; - unsigned char p1_2 = 0x00; - unsigned char p2_1 = 0x00; - unsigned char p2_2 = 0x00; - static unsigned char LastKey[256]; - static unsigned short coin1 = 0x0000; - static unsigned short coin2 = 0x0000; - unsigned char Key[256] = { 0 }; -#if HOST_OS == OS_WINDOWS - GetKeyboardState(Key); -#endif - u8 *buttons = &buffer_out_b[8 + 0x12 + 2]; - for (int i = 0; i < 4; i++) - buttons[i] = 0; - for (int i = 0; i < 16; i++) - if (keycode & (1 << i)) - { - buttons[Naomi_Mapping.button_mapping_byte[i]] |= Naomi_Mapping.button_mapping_mask[i]; - } - u16 axis_values[4]; - for (int i = 0; i < 4; i++) - { - if (Naomi_Mapping.axis[i] != NULL) - axis_values[i] = Naomi_Mapping.axis[i](); - else - { - switch (i) { - case 0: - axis_values[i] = (joyx[bus_id * 2 + 0] + 128) << 8; - break; - case 1: - axis_values[i] = (joyy[bus_id * 2 + 0] + 128) << 8; - break; - case 2: - axis_values[i] = rt[bus_id * 2] << 8; - break; - case 3: - axis_values[i] = lt[bus_id * 2] << 8; - break; - } - } - } - - if (keycode&NAOMI_SERVICE_KEY_1) //Service ? - glbl |= 0x80; - - static bool old_coin = false; - static bool old_coin2 = false; + state = XXH32_createState(); + XXH32_reset(state, 0); + } - if ((old_coin == false) && (keycode&NAOMI_COIN_KEY)) - coin1++; - old_coin = (keycode&NAOMI_COIN_KEY) ? true : false; - - if ((old_coin2 == false) && (keycode2&NAOMI_COIN_KEY)) - coin2++; - old_coin2 = (keycode2&NAOMI_COIN_KEY) ? true : false; - - buffer_out_b[0x11 + 0] = 0x00; - buffer_out_b[0x11 + 1] = 0x8E; //Valid data check - buffer_out_b[0x11 + 2] = 0x01; - buffer_out_b[0x11 + 3] = 0x00; - buffer_out_b[0x11 + 4] = 0xFF; - buffer_out_b[0x11 + 5] = 0xE0; - buffer_out_b[0x11 + 8] = 0x01; - - //memset(OutData+8+0x11,0x00,0x100); - - buffer_out_b[8 + 0x12 + 0] = 1; - buffer_out_b[8 + 0x12 + 1] = glbl; -// buffer_out_b[8 + 0x12 + 2] = p1_1; -// buffer_out_b[8 + 0x12 + 3] = p1_2; -// buffer_out_b[8 + 0x12 + 4] = p2_1; -// buffer_out_b[8 + 0x12 + 5] = p2_2; - buffer_out_b[8 + 0x12 + 6] = 1; - buffer_out_b[8 + 0x12 + 7] = coin1 >> 8; - buffer_out_b[8 + 0x12 + 8] = coin1 & 0xff; - buffer_out_b[8 + 0x12 + 9] = coin2 >> 8; - buffer_out_b[8 + 0x12 + 10] = coin2 & 0xff; - buffer_out_b[8 + 0x12 + 11] = 1; - buffer_out_b[8 + 0x12 + 12] = axis_values[0] >> 8; - buffer_out_b[8 + 0x12 + 13] = axis_values[0]; - buffer_out_b[8 + 0x12 + 14] = axis_values[1] >> 8; - buffer_out_b[8 + 0x12 + 15] = axis_values[1]; - buffer_out_b[8 + 0x12 + 16] = axis_values[2] >> 8; - buffer_out_b[8 + 0x12 + 17] = axis_values[2]; - buffer_out_b[8 + 0x12 + 18] = axis_values[3] >> 8; - buffer_out_b[8 + 0x12 + 19] = axis_values[3]; - buffer_out_b[8 + 0x12 + 20] = 0x00; - - memcpy(LastKey, Key, sizeof(Key)); - - if (State.Mode == 1) - { - buffer_out_b[0x11 + 0x7] = 19; - buffer_out_b[0x11 + 0x4] = 19 + 5; - } + if (dma_buffer_in[1] == 0xff) + { + XXH32_hash_t hash = XXH32_digest(state); + LOGJVS("JVS Firmware hash %08x\n", hash); + if (hash == 0xEF29B145) + crazy_mode = true; else - { - buffer_out_b[0x11 + 0x7] = 17; - buffer_out_b[0x11 + 0x4] = 17 - 1; - } + crazy_mode = false; + XXH32_freeState(state); + state = NULL; - //OutLen=8+0x11+16; - buffer_out_len = 8 + 0x12 + 22; + if (fw_dump) + fclose(fw_dump); + fw_dump = NULL; + + return MDRS_DeviceReply; } - /*ID.Keys=0xFFFFFFFF; - if(GetKeyState(VK_F1)&0x8000) //Service - ID.Keys&=~(1<<0x1b); - if(GetKeyState(VK_F2)&0x8000) //Test - ID.Keys&=~(1<<0x1a); - memcpy(OutData,&ID,sizeof(ID)); - OutData[0x12]=0x8E; - OutLen=sizeof(ID); - */ - } - return 8; - - case 0x17: //Select Subdevice - { - State.Mode = 0; - State.Cmd = buffer_in_b[8]; - State.Node = buffer_in_b[9]; - buffer_out_len = 0; - } - return (7); - - case 0x27: //Transfer request - { - State.Mode = 1; - State.Cmd = buffer_in_b[8]; - State.Node = buffer_in_b[9]; - buffer_out_len = 0; - } - return (7); - case 0x21: //Transfer request with repeat - { - State.Mode = 2; - State.Cmd = buffer_in_b[8]; - State.Node = buffer_in_b[9]; - buffer_out_len = 0; - } - return (7); - - case 0x0B: //EEPROM write - { - int address = buffer_in_b[1]; - int size = buffer_in_b[2]; - //printf("EEprom write %08X %08X\n",address,size); - //printState(Command,buffer_in,buffer_in_len); - memcpy(EEPROM + address, buffer_in_b + 4, size); - -#ifdef SAVE_EPPROM - string eeprom_file = get_eeprom_file_path(); - FILE* f = fopen(eeprom_file.c_str(), "wb"); - if (f) +#ifdef DUMP_JVS_FW + if (fw_dump == NULL) { - fwrite(EEPROM, 1, 0x80, f); - fclose(f); - printf("Saved EEPROM to %s\n", eeprom_file.c_str()); + char filename[128]; + for (int i = 0; ; i++) + { + sprintf(filename, "z80_fw_%d.bin", i); + fw_dump = fopen(filename, "r"); + if (fw_dump == NULL) + { + fw_dump = fopen(filename, "w"); + printf("Saving JVS firmware to %s\n", filename); + break; + } + } } +#endif + int xfer_bytes; + if (dma_buffer_in[0] == 0xff) + xfer_bytes = 0x1C; else - printf("EEPROM SAVE FAILED to %s\n", eeprom_file.c_str()); -#endif - } - return (7); - case 0x3: //EEPROM read - { -#ifdef SAVE_EPPROM - if (!EEPROM_loaded) + xfer_bytes = 0x18; + u16 addr = (dma_buffer_in[2] << 8) + dma_buffer_in[3]; + XXH32_update(state, &dma_buffer_in[4], xfer_bytes); +#ifdef DUMP_JVS_FW + if (fw_dump) { - EEPROM_loaded = true; - string eeprom_file = get_eeprom_file_path(); - FILE* f = fopen(eeprom_file.c_str(), "rb"); - if (f) - { - fread(EEPROM, 1, 0x80, f); - fclose(f); - printf("Loaded EEPROM from %s\n", eeprom_file.c_str()); - } - else - printf("EEPROM file not found at %s\n", eeprom_file.c_str()); + fseek(fw_dump, addr, SEEK_SET); + fwrite(&dma_buffer_in[4], 1, xfer_bytes, fw_dump); } #endif - //printf("EEprom READ ?\n"); - int address = buffer_in_b[1]; - //printState(Command,buffer_in,buffer_in_len); - memcpy(buffer_out, EEPROM + address, 0x80); - buffer_out_len = 0x80; + u8 sum = 0; + for (int i = 0; i < 0x1C; i++) + sum += dma_buffer_in[i]; + + w8(0x80); // or 0x81 on bootrom? + w8(0); + w8(0x20); + w8(0x01); + w8(sum); + w8(0); + w8(0); + w8(0); + + w8(MDRS_DeviceReply); + w8(0x00); + w8(0x20); + w8(0x00); } - return 8; - //IF I return all FF, then board runs in low res - case 0x31: + break; + + case MDC_JVSGetId: { - buffer_out[0] = 0xffffffff; - buffer_out[1] = 0xffffffff; + const char ID1[] = "315-6149 COPYRIGHT SEGA E"; + const char ID2[] = "NTERPRISES CO,LTD. 1998 "; + w8(0x83); + w8(0x00); + w8(0x20); + w8(0x07); + wstr(ID1, 28); + + w8(0x83); + w8(0x00); + w8(0x20); + w8(0x05); + wstr(ID2, 28); } - return (8); + break; - //case 0x3: - // break; + case MDC_DeviceRequest: + w8(MDRS_DeviceStatus); + w8(0x00); + w8(0x20); + w8(0x01); - //case 0x1: - // break; - default: - printf("Unknown 0x86 : SubCommand 0x%X - State: Cmd 0x%X Mode : 0x%X Node : 0x%X\n", subcode, State.Cmd, State.Mode, State.Node); - printState(cmd, buffer_in, buffer_in_len); - } + w32(2); - return 8;//MAPLE_RESPONSE_DATATRF - } - break; - case 0x82: - { - const char *ID = "315-6149 COPYRIGHT SEGA E\x83\x00\x20\x05NTERPRISES CO,LTD. "; - memset(buffer_out_b, 0x20, 256); - memcpy(buffer_out_b, ID, 0x38 - 4); - buffer_out_len = 256; - return (0x83); - } + break; - case 1: - case 9: - return maple_sega_controller::dma(cmd); + case MDC_DeviceReset: + w8(MDRS_DeviceReply); + w8(0x00); + w8(0x20); + w8(0x00); + break; default: - printf("unknown MAPLE Frame\n"); - //printState(Command, buffer_in, buffer_in_len); + printf("Unknown Maple command %x\n", cmd); + w8(MDRE_UnknownCmd); + w8(0x00); + w8(0x00); + w8(0x00); + break; } - return MDRE_UnknownFunction; + + return out_len; + } + + virtual bool maple_serialize(void **data, unsigned int *total_size) + { + // FIXME Should serialize count and rebuild dynamically + for (int i = 0; i < sizeof(io_boards)/sizeof(void*); i++) + io_boards[i]->maple_serialize(data, total_size); + + return true ; + } + + virtual bool maple_unserialize(void **data, unsigned int *total_size) + { + for (int i = 0; i < sizeof(io_boards)/sizeof(void*); i++) + io_boards[i]->maple_unserialize(data, total_size); + + return true ; } }; + + +MapleDeviceRV jvs_io_board::handle_jvs_command(u8* data) +{ + MapleDeviceRV rc; + + u8 req = data[0]; + if (req == 0x15 || req == 0x33) // Receive + { + if (req == 0x15 && jvs_length == 0) + { + return MDRS_JVSNone; + } + + if (jvs_length > 0) + { + switch (jvs_request[0]) + { + case 0xF0: // board reset + // We shouln't get there + rc = MDRS_JVSNone; + break; + + case 0xF1: // set board address + if (jvs_request[1] == node_id) + { + // That's our node number + make_jvs_reply_buffer(); + parent->w8(1); // report byte + finish_jvs_reply_buffer(); + LOGJVS("JVS Node %d address assigned\n", node_id); + rc = MDRS_JVSReply; + } + else + rc = MDRS_JVSNone; + break; + + case 0x10: // Read ID data + make_jvs_reply_buffer(); + parent->w8(1); // report byte + static char ID[] = "SEGA ENTERPRISES,LTD.;I/O BD JVS;837-13551 ;Ver1.00;98/10"; + for (char *p = ID; *p != 0; ) + parent->w8(*p++); + parent->w8(0); + finish_jvs_reply_buffer(); + rc = MDRS_JVSReply; + break; + + case 0x11: // Get command format version + make_jvs_reply_buffer(); + parent->w8(1); // report byte + parent->w8(0x13); // 1.3 + finish_jvs_reply_buffer(); + rc = MDRS_JVSReply; + break; + + case 0x12: // Get JVS versionjamma + make_jvs_reply_buffer(); + parent->w8(1); // report byte + parent->w8(0x30); // 3.0 + finish_jvs_reply_buffer(); + rc = MDRS_JVSReply; + break; + + case 0x13: // Get communication version + make_jvs_reply_buffer(); + parent->w8(1); // report byte + parent->w8(0x10); // 1.0 + finish_jvs_reply_buffer(); + rc = MDRS_JVSReply; + break; + + case 0x14: // Get slave features + make_jvs_reply_buffer(); + parent->w8(1); // report byte + + parent->w8(1); // Digital inputs + parent->w8(2); // 2 players + parent->w8(13); // 13 bits + parent->w8(0); + + parent->w8(2); // Coin inputs + parent->w8(2); // 2 inputs + parent->w8(0); + parent->w8(0); + + parent->w8(3); // Analog inputs + parent->w8(4); // 4 channels + parent->w8(0); // bits per channel, 0: unknown + parent->w8(0); + + if (rotary_encoders) + { + parent->w8(4); // Rotary encoders + parent->w8(2); // 2 channels + parent->w8(0); + parent->w8(0); + } + +// parent->w8(6); // Light gun +// parent->w8(16); // X bits +// parent->w8(16); // Y bits +// parent->w8(1); // 1 channel + + parent->w8(0); // End of list + parent->w8(0); + parent->w8(0); + parent->w8(0); + finish_jvs_reply_buffer(); + rc = MDRS_JVSReply; + break; + + case 0x15: // Master board ID + make_jvs_reply_buffer(); + parent->w8(1); // report byte + finish_jvs_reply_buffer(); + rc = MDRS_JVSReply; + break; + + default: + if (jvs_request[0] >= 0x20 && jvs_request[0] <= 0x38) // Read inputs and more + { + LOGJVS("JVS Node %d: ", node_id); + PlainJoystickState pjs; + parent->config->GetInput(&pjs); + u32 keycode = ~kcode[0]; + u32 keycode2 = ~kcode[1]; + + make_jvs_reply_buffer(); + for (int cmdi = 0; cmdi < jvs_length; ) + { + switch (jvs_request[cmdi]) + { + case 0x20: // Read digital input + { + parent->w8(1); // report byte + + LOGJVS("btns "); + parent->w8((kcode[0] & 1) ? 0 : 0x80); // test, tilt1, tilt2, tilt3, unused, unused, unused, unused + // FIXME in-lst mapping + u8 buttons[8] = { 0 }; + u32 keycode = ~kcode[0]; + for (int i = 0; i < 16; i++) + if ((keycode & (1 << i)) != 0) + { + buttons[Naomi_Mapping.button_mapping_byte[i]] |= Naomi_Mapping.button_mapping_mask[i]; + } + for (int player = 0; player < jvs_request[cmdi + 1]; player++) + { + LOGJVS("P%d %02x ", player + 1, buttons[player * 2]); + parent->w8(buttons[player * 2]); + if (jvs_request[cmdi + 2] == 2) + { + LOGJVS("%02x ", buttons[player * 2 + 1]); + parent->w8(buttons[player * 2 + 1]); + } + } +// for (int player = 0; player < jvs_request[cmdi + 1]; player++) +// { +// u32 keycode = ~kcode[player]; +// if (keycode & DC_BTN_C) +// keycode |= 0xFFff; +// +// if (jvs_request[cmdi + 2] == 1) +// w8(keycode); +// else +// w16(keycode); +// } + cmdi += 3; + } + break; + + case 0x21: // Read coins + { + if (coin_chute) + { + coin_chute = false; + coin_count++; + } + parent->w8(1); // report byte + LOGJVS("coins "); + for (int slot = 0; slot < jvs_request[cmdi + 1]; slot++) + { + if (slot == 0) + { + LOGJVS("0:%d ", coin_count); + parent->w8((coin_count >> 8) & 0x3F); // status (2 highest bits, 0: normal), coin count MSB + parent->w8(coin_count); // coin count LSB + } + else + { + LOGJVS("%d:0 ", slot); + parent->w8(0); + parent->w8(0); + } + } + cmdi += 2; + } + break; + + case 0x22: // Read analog inputs + { + parent->w8(1); // report byte + int axis = 0; + + LOGJVS("ana "); + if (lightgun_as_analog) + { + // Death Crimson / Confidential Mission + u16 x = mo_x_abs * 0xFFFF / 639; + u16 y = mo_y_abs * 0xFFFF / 479; + if (mo_x_abs < 0 || mo_x_abs > 639 || mo_y_abs < 0 || mo_y_abs > 479) + { + x = 0xffff; + y = 0xffff; + } + LOGJVS("x,y:%4x,%4x ", x, y); + parent->w8(x >> 8); // X, MSB + parent->w8(x); // X, LSB + parent->w8(y >> 8); // Y, MSB + parent->w8(y); // Y, LSB + axis = 2; + } + + for (; axis < jvs_request[cmdi + 1]; axis++) + { + u16 axis_value; + if (Naomi_Mapping.axis[axis] != NULL) + axis_value = Naomi_Mapping.axis[axis](); + else + { + switch (axis) { + case 0: + axis_value = (joyx[axis / 4] + 128) << 8; + break; + case 1: + axis_value = (joyy[axis / 4] + 128) << 8; + break; + case 2: + axis_value = rt[axis / 4] << 8; + break; + case 3: + axis_value = lt[axis / 4] << 8; + break; + } + } + LOGJVS("%d:%4x ", axis, axis_value); + parent->w8(axis_value >> 8); + parent->w8(axis_value); + } + cmdi += 2; + } + break; + + case 0x23: // Read rotary encoders + { + parent->w8(1); // report byte + static s16 rotx = 0; + static s16 roty = 0; + rotx += mo_x_delta * 10; + roty += mo_y_delta * 10; + mo_x_delta = 0; + mo_y_delta = 0; + LOGJVS("rotenc "); + for (int chan = 0; chan < jvs_request[cmdi + 1]; chan++) + { + if (chan == 0) + { + LOGJVS("%d:%4x ", chan, rotx); + parent->w8(rotx >> 8); // MSB + parent->w8(rotx); // LSB + } + else if (chan == 1) + { + LOGJVS("%d:%4x ", chan, roty); + parent->w8(roty >> 8); // MSB + parent->w8(roty); // LSB + } + else + { + LOGJVS("%d:%4x ", chan, 0); + parent->w8(0x80); // MSB + parent->w8(0x80); // LSB + } + } + cmdi += 2; + } + break; + + case 0x25: // Read screen pos inputs + { + parent->w8(1); // report byte + // Channel number is jvs_request[cmdi + 1] + u16 x = mo_x_abs * 0xFFFF / 639; + u16 y = (479 - mo_y_abs) * 0xFFFF / 479; + LOGJVS("lightgun %4x,%4x ", x, y); + parent->w8(x >> 8); // X, MSB + parent->w8(x); // X, LSB + parent->w8(y >> 8); // Y, MSB + parent->w8(y); // Y, LSB + cmdi += 2; + } + break; + + case 0x32: // switched outputs + case 0x33: + parent->w8(1); // report byte + cmdi += jvs_request[cmdi + 1] + 2; + break; + + // case 0x30, 0x31: add coin + default: + printf("JVS: Unknown input type %x\n", jvs_request[cmdi]); + parent->w8(2); // report byte: command error + cmdi = jvs_length; // Ignore subsequent commands + break; + } + } + LOGJVS("\n"); + finish_jvs_reply_buffer(); + rc = MDRS_JVSReply; + } + break; + } + + jvs_length = 0; + } + } + if (req != 0x15) // Send + { + u8 node; + u8 *cmd; + u32 len; + u8 channel = 0; + if (data[1] > 31 && data[1] != 0xff) // TODO what is this? + { + node = data[6]; + len = data[7]; + cmd = &data[8]; + channel = data[5]; + } + else + { + node = data[1]; + len = data[2]; + cmd = &data[3]; + } + if (node != ALL_NODES && node != node_id) + return MDRS_JVSNone; + + if (len > 0 && *cmd == 0xf0) // reset + return MDRS_JVSNone; + + if (req == 0x27) + { + // FIXME hack + node = cmd[-1]; + len = cmd[0]; + cmd++; + } + + // CT fw uses 13 as a 17, and 17 as 13 and also uses 19 + if (parent->crazy_mode) + { + switch (req) + { + case 0x13: + req = 0x17; + break; + case 0x17: + req = 0x13; + break; + } + } + // Store repeat request + if (req == 0x13) + { + if (node == node_id) + { + if (len > 0) + { + printf("JVS node %d: Storing %d cmd bytes\n", node_id, len); + memcpy(jvs_repeat_request, cmd, len); + jvs_repeat_length = len; + } + parent->w8(MDRS_JVSReply); + parent->w8(channel); // node id? + parent->w8(0x20); + parent->w8(0x01); + parent->w8(data[0] + 1); // subcommand + 1 + parent->w8(0); + parent->w8(len + 1); + parent->w8(0); + + return MDRS_JVSReply; + } + else + return MDRS_JVSNone; + } + memcpy(jvs_request, cmd, len); + jvs_length = len; + + if (node == node_id && (req == 0x21 || req == 0x27 || req == 0x33 || req == 0x19)) + { + // Use repeat + memcpy(jvs_request + jvs_length, jvs_repeat_request, jvs_repeat_length); + jvs_length += jvs_repeat_length; + } + // FIXME this is for 0x17 and 0x21 + parent->w8(MDRS_JVSReply); + parent->w8(channel); + parent->w8(0x20); + parent->w8(0x01); + parent->w8(0x18); + parent->w8(0x10); // FIXME bytes received? >0 required for 0x19 + parent->w8(0); // in(91) + parent->w8(0); + if (node != ALL_NODES) + rc = MDRS_JVSReply; + else + rc = MDRS_JVSNone; // Allow other boards to respond to the message + //printf("JVS send node %x len %d cmd %x repeat_length %d dma_count_out %d\n", node, jvs_length, *cmd, jvs_repeat_length, *(parent->dma_count_out)); + + } + return rc; +} + +void jvs_io_board::make_jvs_reply_buffer(bool empty) +{ + dma_out = parent->dma_buffer_out; + parent->w8(MDRS_JVSReply); + parent->w8(0x00); // channel id? + parent->w8(0x20); + parent->w8(0x00); // length in dword + parent->w32(0xffffff16); + parent->w32(0xffffffff); + if (!empty) + { + *(parent->dma_count_out) += 9; + parent->dma_buffer_out += 9; + parent->w8(0x00); + parent->w8(last_node ? 0x8E : 0x8F); // bit 0 is sense line level. If set during F1 , more I/O boards need addressing + parent->w8(node_id); // node number + parent->w8(0x00); + parent->w8(0x00); // len of following data + parent->w8(0xE0); // sync + parent->w8(0x00); // master node + parent->w8(0x00); // jvs payload len + parent->w8(0x01); // status byte + } + else + { + *(parent->dma_count_out) += 8; + parent->dma_buffer_out += 8; + } +} + +void jvs_io_board::finish_jvs_reply_buffer() +{ + dma_out[25] = parent->dma_buffer_out - dma_out - 26; + dma_out[28] = parent->dma_buffer_out - dma_out - 29; + while ((*(parent->dma_count_out)) & 3) + { + *(parent->dma_count_out) += 1; + parent->dma_buffer_out++; + } + dma_out[3] = (parent->dma_buffer_out - dma_out) / 4 - 1; +// printf("dma_count_out=%d\n", *(parent->dma_count_out)); +} + +bool jvs_io_board::maple_serialize(void **data, unsigned int *total_size) +{ + REICAST_S(node_id); + REICAST_S(last_node); + REICAST_S(rotary_encoders); + REICAST_S(jvs_length); + REICAST_SA(jvs_request, sizeof(jvs_request)); + REICAST_S(jvs_repeat_length); + REICAST_SA(jvs_repeat_request, sizeof(jvs_repeat_request)); + return true ; +} + +bool jvs_io_board::maple_unserialize(void **data, unsigned int *total_size) +{ + REICAST_US(node_id); + REICAST_US(last_node); + REICAST_US(rotary_encoders); + REICAST_US(jvs_length); + REICAST_USA(jvs_request, sizeof(jvs_request)); + REICAST_US(jvs_repeat_length); + REICAST_USA(jvs_repeat_request, sizeof(jvs_repeat_request)); + return true ; +} + maple_device* maple_Create(MapleDeviceType type) { maple_device* rv=0; diff --git a/core/hw/maple/maple_devs.h b/core/hw/maple/maple_devs.h index e6a691262..7e9d3d7aa 100755 --- a/core/hw/maple/maple_devs.h +++ b/core/hw/maple/maple_devs.h @@ -33,7 +33,8 @@ struct maple_device virtual void OnSetup(){}; virtual ~maple_device(); - virtual u32 Dma(u32 Command,u32* buffer_in,u32 buffer_in_len,u32* buffer_out,u32& buffer_out_len)=0; + u32 Dma(u32 Command,u32* buffer_in,u32 buffer_in_len,u32* buffer_out,u32& buffer_out_len); + virtual u32 RawDma(u32* buffer_in, u32 buffer_in_len, u32* buffer_out) = 0; virtual bool maple_serialize(void **data, unsigned int *total_size){return true;}; virtual bool maple_unserialize(void **data, unsigned int *total_size){return true;}; virtual MapleDeviceType get_device_type() = 0; diff --git a/core/hw/maple/maple_if.cpp b/core/hw/maple/maple_if.cpp index 058cd0f48..2fc0db764 100644 --- a/core/hw/maple/maple_if.cpp +++ b/core/hw/maple/maple_if.cpp @@ -135,7 +135,9 @@ void maple_DoDma() //this is kinda wrong .. but meh //really need to properly process the commands at some point - if (maple_op == MP_Start) + switch (maple_op) + { + case MP_Start: { if (!IsOnSh4Ram(header_2)) { @@ -163,14 +165,14 @@ void maple_DoDma() if (MapleDevices[bus][5] && MapleDevices[bus][port]) { - resp=MapleDevices[bus][port]->Dma(command,&p_data[1],inlen,&p_out[1],outlen); - - if(reci&0x20) - reci|=maple_GetAttachedDevices(bus); - - verify(u8(outlen/4)*4==outlen); - p_out[0]=(resp<<0)|(send<<8)|(reci<<16)|((outlen/4)<<24); - xfer_count+=outlen+4; + u32 outlen = MapleDevices[bus][port]->RawDma(&p_data[0], inlen + 4, &p_out[0]); + xfer_count =+ outlen; + /* + printf("JVS OUT: "); + u8 *p = (u8 *)&p_out[0]; + for (int i = 0; i < outlen + 4; i++) printf("%02x ", p[i]); + printf("\n"); + */ } else { @@ -183,7 +185,9 @@ void maple_DoDma() //goto next command addr += 2 * 4 + plen * 4; } - else if (maple_op == MP_SDCKBOccupy) + break; + + case MP_SDCKBOccupy: { u32 bus = (header_1 >> 16) & 3; if (MapleDevices[bus][5]) @@ -191,10 +195,22 @@ void maple_DoDma() addr += 1 * 4; } - else - { - if (maple_op != MP_NOP) - printf("MAPLE: maple_op == %d length %d\n", maple_op, plen * 4); + break; + + case MP_SDCKBOccupyCancel: + addr += 1 * 4; + break; + + case MP_Reset: + addr += 1 * 4; + break; + + case MP_NOP: + addr += 1 * 4; + break; + + default: + printf("MAPLE: Unknown maple_op == %d length %d\n", maple_op, plen * 4); addr += 1 * 4; } } diff --git a/core/hw/naomi/naomi_cart.cpp b/core/hw/naomi/naomi_cart.cpp index f15767d64..6a923e27f 100644 --- a/core/hw/naomi/naomi_cart.cpp +++ b/core/hw/naomi/naomi_cart.cpp @@ -58,8 +58,8 @@ static u16 getRightTriggerAxis() static NaomiInputMapping naomi_default_mapping = { { getJoystickXAxis, getJoystickYAxis, getRightTriggerAxis, getLeftTriggerAxis }, - { 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1 }, - { 1, 2, 4, 8, 16, 32, 64,128, 1, 2, 4, 8 }, + { 0, 0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 0, 0 }, + { 0x40, 0x01, 0x02, 0x80, 0x20, 0x10, 0x08, 0x04, 0, 0x80, 0x40, 0, 0 }, }; static void parse_comment(const char *line) diff --git a/core/linux-dist/x11.cpp b/core/linux-dist/x11.cpp index ec59e4388..36126e74b 100644 --- a/core/linux-dist/x11.cpp +++ b/core/linux-dist/x11.cpp @@ -242,6 +242,8 @@ extern s32 mo_y_abs; static bool capturing_mouse; static Cursor empty_cursor = None; +extern bool coin_chute; + static Cursor create_empty_cursor() { if (empty_cursor == None) @@ -433,6 +435,12 @@ void input_x11_handle() printf("OpenGL: renderer changed to %d\n", settings.pvr.rend); renderer_changed = true; } +#if DC_PLATFORM == DC_PLATFORM_NAOMI + else if (e.type == KeyRelease && e.xkey.keycode == KEY_F8) + { + coin_chute = true; + } +#endif else { int dc_key = x11_keymap[e.xkey.keycode];