diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index d021f947..83b8a3d1 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -46,7 +46,13 @@ jobs: cc: 'clang' cxx: 'clang++' - # Windows + - displayTargetName: 'macOS x86_64 (Clang)' + artifact: 'virtualjaguar_libretro.dylib' + os: macos-13 + cc: 'clang' + cxx: 'clang++' + + # Windows (MinGW) - displayTargetName: 'Windows x86_64 (MSYS2)' artifact: 'virtualjaguar_libretro.dll' os: windows-latest @@ -71,28 +77,38 @@ jobs: os: ubuntu-latest emscripten: true - # Android NDK (arm64-v8a) + # Android NDK - displayTargetName: 'Android arm64-v8a' artifact: 'libs/arm64-v8a/libretro.so' os: ubuntu-latest android: true android_abi: 'arm64-v8a' - # Android NDK (armeabi-v7a) - displayTargetName: 'Android armeabi-v7a' artifact: 'libs/armeabi-v7a/libretro.so' os: ubuntu-latest android: true android_abi: 'armeabi-v7a' - # iOS + - displayTargetName: 'Android x86_64' + artifact: 'libs/x86_64/libretro.so' + os: ubuntu-latest + android: true + android_abi: 'x86_64' + + - displayTargetName: 'Android x86' + artifact: 'libs/x86/libretro.so' + os: ubuntu-latest + android: true + android_abi: 'x86' + + # iOS / tvOS - displayTargetName: 'iOS arm64' artifact: 'virtualjaguar_libretro_ios.dylib' os: macos-latest make_platform: 'ios-arm64' cross: true - # tvOS - displayTargetName: 'tvOS arm64' artifact: 'virtualjaguar_libretro_tvos.dylib' os: macos-latest @@ -181,6 +197,79 @@ jobs: path: ${{ matrix.config.artifact }} if-no-files-found: error + msvc-check: + name: MSVC ${{ matrix.arch }} compilation check + runs-on: windows-latest + strategy: + fail-fast: false + matrix: + arch: [x64, x86] + steps: + - uses: actions/checkout@v4 + + - uses: ilammy/msvc-dev-cmd@v1 + with: + arch: ${{ matrix.arch }} + + - name: Compile all sources with cl.exe + shell: cmd + run: | + cl.exe /c /W3 /O2 /DNDEBUG /D_CRT_SECURE_NO_DEPRECATE ^ + /I. /Isrc /Isrc\m68000 /Ilibretro-common\include ^ + /D__LIBRETRO__ /DINLINE="_inline" ^ + libretro.c ^ + src\blitter.c src\dac.c src\dsp.c src\file.c ^ + src\gpu.c src\jaguar.c src\jerry.c src\tom.c src\op.c ^ + src\cdintf.c src\cdrom.c src\crc32.c src\event.c ^ + src\eeprom.c src\filedb.c src\joystick.c src\settings.c ^ + src\memtrack.c src\mmu.c src\vjag_memory.c ^ + src\universalhdr.c src\wavetable.c ^ + src\jagbios.c src\jagbios2.c ^ + src\jagcdbios.c src\jagdevcdbios.c ^ + src\jagstub1bios.c src\jagstub2bios.c ^ + src\m68000\m68kinterface.c ^ + src\blitter_simd_scalar.c ^ + src\blitter_simd_sse2.c + echo MSVC compilation check passed + + vita-build: + name: build-PS Vita + runs-on: ubuntu-latest + container: + image: vitasdk/vitasdk:latest + steps: + - uses: actions/checkout@v4 + + - name: Build + run: make -j4 platform=vita + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: PS Vita + path: virtualjaguar_libretro_vita.a + if-no-files-found: error + + switch-build: + name: build-Nintendo Switch + runs-on: ubuntu-latest + container: + image: devkitpro/devkita64:latest + steps: + - uses: actions/checkout@v4 + + - name: Build + run: make -j4 platform=libnx + env: + DEVKITPRO: /opt/devkitpro + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: Nintendo Switch + path: virtualjaguar_libretro_libnx.a + if-no-files-found: error + c89-lint: name: C89 compliance check runs-on: ubuntu-latest @@ -191,10 +280,9 @@ jobs: run: | echo "==> Checking C89 compliance (catches MSVC C89 errors)..." FAILED=0 - for f in libretro.c src/*.c; do - # Skip machine-generated files + for f in libretro.c src/*.c src/m68000/m68kinterface.c; do case "$f" in - src/m68000/*|src/jag*bios*.c|src/jagstub*bios.c|src/blitter_simd_neon.c|src/blitter_simd_sse2.c) continue ;; + src/m68000/cpu*.c|src/m68000/read*.c|src/jag*bios*.c|src/jagstub*bios.c|src/blitter_simd_neon.c|src/blitter_simd_sse2.c) continue ;; esac if ! gcc -fsyntax-only -std=gnu89 \ -Werror=declaration-after-statement \ @@ -209,3 +297,21 @@ jobs: exit 1 fi echo "==> All files pass C89 declaration check" + + - name: Check for stdbool.h usage (use boolean.h instead) + run: | + echo "==> Checking for direct stdbool.h includes..." + FAILED=0 + for f in libretro.c src/*.c src/*.h; do + case "$f" in + src/boolean.h) continue ;; + esac + if grep -n '#include.*' "$f" 2>/dev/null; then + echo "::error file=$f::Use instead of (MSVC 2005/2010 compat)" + FAILED=1 + fi + done + if [ "$FAILED" = "1" ]; then + exit 1 + fi + echo "==> No direct stdbool.h includes found" diff --git a/.github/workflows/release.yml b/.github/workflows/release.yml index 8ffa501c..64deb952 100644 --- a/.github/workflows/release.yml +++ b/.github/workflows/release.yml @@ -40,6 +40,12 @@ jobs: cc: clang cxx: clang++ + - platform: macos-x86_64 + artifact: virtualjaguar_libretro.dylib + os: macos-13 + cc: clang + cxx: clang++ + # Windows - platform: windows-x86_64 artifact: virtualjaguar_libretro.dll @@ -78,13 +84,24 @@ jobs: android: true android_abi: 'armeabi-v7a' - # iOS + - platform: android-x86_64 + artifact: libs/x86_64/libretro.so + os: ubuntu-latest + android: true + android_abi: 'x86_64' + + - platform: android-x86 + artifact: libs/x86/libretro.so + os: ubuntu-latest + android: true + android_abi: 'x86' + + # iOS / tvOS - platform: ios-arm64 artifact: virtualjaguar_libretro_ios.dylib os: macos-latest make_platform: 'ios-arm64' - # tvOS - platform: tvos-arm64 artifact: virtualjaguar_libretro_tvos.dylib os: macos-latest @@ -156,8 +173,56 @@ jobs: path: dist/ if-no-files-found: error + vita-build: + name: build-vita + runs-on: ubuntu-latest + container: + image: vitasdk/vitasdk:latest + steps: + - uses: actions/checkout@v4 + + - name: Build + run: make -j4 platform=vita + + - name: Package + run: | + mkdir -p dist + cp virtualjaguar_libretro_vita.a dist/virtualjaguar_libretro-vita.a + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: virtualjaguar_libretro-vita + path: dist/ + if-no-files-found: error + + switch-build: + name: build-switch + runs-on: ubuntu-latest + container: + image: devkitpro/devkita64:latest + steps: + - uses: actions/checkout@v4 + + - name: Build + run: make -j4 platform=libnx + env: + DEVKITPRO: /opt/devkitpro + + - name: Package + run: | + mkdir -p dist + cp virtualjaguar_libretro_libnx.a dist/virtualjaguar_libretro-switch.a + + - name: Upload artifact + uses: actions/upload-artifact@v4 + with: + name: virtualjaguar_libretro-switch + path: dist/ + if-no-files-found: error + release: - needs: build + needs: [build, vita-build, switch-build] runs-on: ubuntu-latest steps: - uses: actions/checkout@v4 diff --git a/src/blitter_simd.h b/src/blitter_simd.h index 15906373..bd2a66f6 100644 --- a/src/blitter_simd.h +++ b/src/blitter_simd.h @@ -13,7 +13,7 @@ #define BLITTER_SIMD_H #include -#include +#include typedef struct { diff --git a/src/blitter_simd_sse2.c b/src/blitter_simd_sse2.c index 67dc9dd8..d8e61882 100644 --- a/src/blitter_simd_sse2.c +++ b/src/blitter_simd_sse2.c @@ -12,9 +12,16 @@ #include /* SSE2 */ #include /* memcpy for type-punning extract */ -/* _mm_cvtsi128_si64 only exists on x86_64 (needs 64-bit GP register). - * memcpy from the __m128i is portable, alignment-safe, and compilers - * optimize it to a single register move. */ +/* _mm_set_epi64x doesn't exist in MSVC 2010 and earlier. + * Build from two 32-bit halves instead (pure SSE2). */ +#if defined(_MSC_VER) && _MSC_VER < 1700 +#define SSE2_SET64(hi, lo) \ + _mm_set_epi32((int)((uint64_t)(hi) >> 32), (int)(hi), \ + (int)((uint64_t)(lo) >> 32), (int)(lo)) +#else +#define SSE2_SET64(hi, lo) _mm_set_epi64x((int64_t)(hi), (int64_t)(lo)) +#endif + static uint64_t sse2_extract_u64(__m128i v) { uint64_t r; @@ -35,15 +42,15 @@ static uint64_t sse2_lfu(uint64_t srcd, uint64_t dstd, uint8_t lfu_func) uint64_t func2 = (lfu_func & 0x04) ? 0xFFFFFFFFFFFFFFFFULL : 0; uint64_t func3 = (lfu_func & 0x08) ? 0xFFFFFFFFFFFFFFFFULL : 0; - __m128i vs = _mm_set_epi64x(0, (int64_t)srcd); - __m128i vd = _mm_set_epi64x(0, (int64_t)dstd); + __m128i vs = SSE2_SET64(0, srcd); + __m128i vd = SSE2_SET64(0, dstd); __m128i vns = _mm_andnot_si128(vs, _mm_set1_epi32(-1)); /* ~srcd */ __m128i vnd = _mm_andnot_si128(vd, _mm_set1_epi32(-1)); /* ~dstd */ - __m128i vf0 = _mm_set_epi64x(0, (int64_t)func0); - __m128i vf1 = _mm_set_epi64x(0, (int64_t)func1); - __m128i vf2 = _mm_set_epi64x(0, (int64_t)func2); - __m128i vf3 = _mm_set_epi64x(0, (int64_t)func3); + __m128i vf0 = SSE2_SET64(0, func0); + __m128i vf1 = SSE2_SET64(0, func1); + __m128i vf2 = SSE2_SET64(0, func2); + __m128i vf3 = SSE2_SET64(0, func3); /* (~s & ~d & f0) | (~s & d & f1) | (s & ~d & f2) | (s & d & f3) */ __m128i t0 = _mm_and_si128(_mm_and_si128(vns, vnd), vf0); @@ -64,8 +71,8 @@ static uint64_t sse2_lfu(uint64_t srcd, uint64_t dstd, uint8_t lfu_func) static uint8_t sse2_dcomp(uint64_t patd, uint64_t srcd, uint64_t dstd, bool cmpdst) { uint64_t other = cmpdst ? dstd : srcd; - __m128i vp = _mm_set_epi64x(0, (int64_t)patd); - __m128i vo = _mm_set_epi64x(0, (int64_t)other); + __m128i vp = SSE2_SET64(0, patd); + __m128i vo = SSE2_SET64(0, other); __m128i vxor = _mm_xor_si128(vp, vo); /* Compare each byte against zero */ @@ -88,8 +95,8 @@ static uint8_t sse2_zcomp(uint64_t srcz, uint64_t dstz, uint8_t zmode) uint8_t result = 0; uint8_t packed = 0; - __m128i vs = _mm_set_epi64x(0, (int64_t)srcz); - __m128i vd = _mm_set_epi64x(0, (int64_t)dstz); + __m128i vs = SSE2_SET64(0, srcz); + __m128i vd = SSE2_SET64(0, dstz); /* Bias for unsigned comparison via signed instructions */ __m128i bias = _mm_set1_epi16((short)0x8000); @@ -150,9 +157,9 @@ static uint64_t sse2_byte_merge(uint64_t src, uint64_t dst, uint16_t mask) sel64 |= (uint64_t)((uint8_t)(-(int8_t)((mask >> 13) & 1))) << 48; sel64 |= (uint64_t)((uint8_t)(-(int8_t)((mask >> 14) & 1))) << 56; - vmask = _mm_set_epi64x(0, (int64_t)sel64); - vsrc = _mm_set_epi64x(0, (int64_t)src); - vdst = _mm_set_epi64x(0, (int64_t)dst); + vmask = SSE2_SET64(0, sel64); + vsrc = SSE2_SET64(0, src); + vdst = SSE2_SET64(0, dst); /* result = (src & mask) | (dst & ~mask) */ r = _mm_or_si128( diff --git a/src/eeprom.c b/src/eeprom.c index e96a997c..480f6424 100644 --- a/src/eeprom.c +++ b/src/eeprom.c @@ -16,7 +16,7 @@ #include "eeprom.h" #include -#include +#include #include // For memset uint16_t eeprom_ram[64]; diff --git a/src/m68000/m68kinterface.c b/src/m68000/m68kinterface.c index f119062e..ce45ba1c 100644 --- a/src/m68000/m68kinterface.c +++ b/src/m68000/m68kinterface.c @@ -411,6 +411,7 @@ void BuildCPUFunctionTable(void) size_t M68KStateSave(uint8_t *buf) { uint8_t *start = buf; + uint32_t pc_p_offset, pc_oldp_offset; /* Save register struct fields individually (not the raw struct, * because pc_p/pc_oldp are pointers that differ per session). */ @@ -435,8 +436,8 @@ size_t M68KStateSave(uint8_t *buf) STATE_SAVE_VAR(buf, regs.interruptCycles); /* Save pc_p and pc_oldp as offsets from jagMemSpace */ - uint32_t pc_p_offset = (uint32_t)(regs.pc_p ? (regs.pc_p - jagMemSpace) : 0xFFFFFFFF); - uint32_t pc_oldp_offset = (uint32_t)(regs.pc_oldp ? (regs.pc_oldp - jagMemSpace) : 0xFFFFFFFF); + pc_p_offset = (uint32_t)(regs.pc_p ? (regs.pc_p - jagMemSpace) : 0xFFFFFFFF); + pc_oldp_offset = (uint32_t)(regs.pc_oldp ? (regs.pc_oldp - jagMemSpace) : 0xFFFFFFFF); STATE_SAVE_VAR(buf, pc_p_offset); STATE_SAVE_VAR(buf, pc_oldp_offset); @@ -452,6 +453,7 @@ size_t M68KStateSave(uint8_t *buf) size_t M68KStateLoad(const uint8_t *buf) { const uint8_t *start = buf; + uint32_t pc_p_offset, pc_oldp_offset; STATE_LOAD_BUF(buf, regs.regs, sizeof(regs.regs)); STATE_LOAD_VAR(buf, regs.usp); @@ -474,7 +476,6 @@ size_t M68KStateLoad(const uint8_t *buf) STATE_LOAD_VAR(buf, regs.interruptCycles); /* Reconstruct pc_p and pc_oldp from offsets */ - uint32_t pc_p_offset, pc_oldp_offset; STATE_LOAD_VAR(buf, pc_p_offset); STATE_LOAD_VAR(buf, pc_oldp_offset); regs.pc_p = (pc_p_offset != 0xFFFFFFFF) ? (jagMemSpace + pc_p_offset) : NULL;