mirror of
https://github.com/flyinghead/flycast.git
synced 2026-11-08 01:48:31 -10:00
Compare commits
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
a14c02e2bd | ||
|
|
802874e79d | ||
|
|
1fac3d3fef | ||
|
|
800d0ec2d8 | ||
|
|
6a6038f910 | ||
|
|
f86e4c6ea4 | ||
|
|
7b1df94c8e | ||
|
|
973e569a7d | ||
|
|
13cfe43858 | ||
|
|
e778e529be | ||
|
|
2c83abcce4 | ||
|
|
07535d22e8 | ||
|
|
98ceceed1e | ||
|
|
23d212f4f0 | ||
|
|
917a7552f9 | ||
|
|
697d15a133 | ||
|
|
34e1211871 | ||
|
|
cd89ae96ab | ||
|
|
01db8a203e | ||
|
|
6d49796a15 | ||
|
|
907d15155e | ||
|
|
e2f1484571 | ||
|
|
fe58ee8c20 | ||
|
|
7c22066aa3 | ||
|
|
3a498b1cb5 | ||
|
|
c52e6ccf56 | ||
|
|
de7597f0bc | ||
|
|
5f35ec7d72 | ||
|
|
db065c5700 | ||
|
|
620699865b | ||
|
|
9981e84912 | ||
|
|
b5acd1b533 | ||
|
|
91c3084d52 | ||
|
|
506b4efd04 | ||
|
|
7422d1b831 | ||
|
|
6dafd41eaa | ||
|
|
ec56b5f260 | ||
|
|
bc3b8c71da | ||
|
|
4dca1988da | ||
|
|
552a86086b | ||
|
|
9ec8b7e658 | ||
|
|
7d69c9cfd4 | ||
|
|
5ff3f974d9 | ||
|
|
f83fda7069 | ||
|
|
fe04dd6503 | ||
|
|
b1b80d8849 | ||
|
|
0a304f1396 | ||
|
|
2ac5c772f0 | ||
|
|
fff68b3f57 | ||
|
|
e6707469d2 | ||
|
|
a99c8667b4 | ||
|
|
49ad26a7e5 | ||
|
|
da5be1a0a7 | ||
|
|
36c975e391 | ||
|
|
bcad2d76d0 | ||
|
|
6838fbd642 | ||
|
|
34cd222e12 | ||
|
|
0ff538115f | ||
|
|
5aca173e80 | ||
|
|
2f0917bb7b | ||
|
|
f2013c652a | ||
|
|
230d83654f | ||
|
|
d3c90b0f84 | ||
|
|
fe4cf1d586 | ||
|
|
08ffbe6cf1 | ||
|
|
b35b829a12 | ||
|
|
62d7a0432a | ||
|
|
066c82ddfd | ||
|
|
1145248aa7 | ||
|
|
cc4a59b1f2 | ||
|
|
9bdb0ad323 | ||
|
|
5c96b69ff0 | ||
|
|
7db29cc2fd | ||
|
|
162bc02007 | ||
|
|
b4d7e75977 | ||
|
|
58b283fc02 | ||
|
|
453dcefc12 | ||
|
|
ce3abe5a0b | ||
|
|
bbe606c8f7 | ||
|
|
5803d8aa14 | ||
|
|
f831592f64 | ||
|
|
ba04273a25 | ||
|
|
804fcb4a9e | ||
|
|
d367284ddf | ||
|
|
105f760613 | ||
|
|
8c5446706e | ||
|
|
982f26a0da | ||
|
|
643374c91e | ||
|
|
33d5b38163 | ||
|
|
caa55c0297 | ||
|
|
a6242fd653 | ||
|
|
02c779ddc1 | ||
|
|
b0284ba9ba | ||
|
|
29a54e36ce | ||
|
|
7cc58de0c3 | ||
|
|
2550d10902 | ||
|
|
565f5174f7 | ||
|
|
f58719d9b7 | ||
|
|
8041f02984 | ||
|
|
41406fb31a | ||
|
|
e293832bef | ||
|
|
253970d8e5 | ||
|
|
63aecfd491 | ||
|
|
670b5001d4 | ||
|
|
2805df5484 | ||
|
|
0cd66cea5c | ||
|
|
d93612734b | ||
|
|
95e28f412f | ||
|
|
a749e51559 | ||
|
|
e82f38a8cc | ||
|
|
89bf160a9d | ||
|
|
e64b28a759 | ||
|
|
675ce1b63b | ||
|
|
63d19693bc | ||
|
|
8b5cb7513b | ||
|
|
940f5bb911 | ||
|
|
d70a265804 | ||
|
|
fc7b8c247d | ||
|
|
526b52b141 | ||
|
|
bcbc43fad6 | ||
|
|
d7c59e9ab5 | ||
|
|
14487d96ec | ||
|
|
f52c275c90 | ||
|
|
a27a81ba18 | ||
|
|
d1d7373d43 | ||
|
|
80cc71952e | ||
|
|
8c155b02c8 | ||
|
|
5ff2bc0c42 | ||
|
|
e435d72aa8 | ||
|
|
f68e60fe92 | ||
|
|
1e60ef78c7 | ||
|
|
a9c5ef8b41 | ||
|
|
d234bcf7d9 | ||
|
|
1e68c83356 | ||
|
|
436e125d59 | ||
|
|
444635372e | ||
|
|
abf284fc2d | ||
|
|
b4f89c9926 | ||
|
|
b043043568 | ||
|
|
8542ff2dc4 | ||
|
|
f6dead8c39 | ||
|
|
6de588bf57 | ||
|
|
1536d588f6 | ||
|
|
d3c64bf1f5 | ||
|
|
2f10d39910 | ||
|
|
7e917bc53a | ||
|
|
4a9ca07838 | ||
|
|
3e81e56499 | ||
|
|
39c991671f | ||
|
|
10dee60cda | ||
|
|
6925586fa2 | ||
|
|
d447d3d2db | ||
|
|
55a8d6094b | ||
|
|
1370b74fb9 | ||
|
|
46deb1dd7a | ||
|
|
7baf5c0a5b | ||
|
|
ce19baebc5 | ||
|
|
00892506d7 | ||
|
|
34bb486e54 | ||
|
|
1583b75dd7 | ||
|
|
569dc1cd1a | ||
|
|
8c0da03a15 | ||
|
|
3153ac762e | ||
|
|
b49dd9d184 | ||
|
|
4941fdf934 | ||
|
|
81d4756fe9 | ||
|
|
f35f0d51ac | ||
|
|
db8147e91f | ||
|
|
91d1430b42 | ||
|
|
cc1f7ca1c1 |
No files matched your search
@@ -24,8 +24,8 @@ jobs:
|
||||
version: '11.0'
|
||||
pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache clang cmake git libao libcdio libusb1 libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2
|
||||
- operating_system: openbsd
|
||||
version: '7.9'
|
||||
pkginstall: sudo pkg_add -u && sudo pkg_add ccache cmake git libao libcdio libusb1-1.0.29 libzip lua-5.4.8p0 miniupnpc ninja pulseaudio sdl2
|
||||
version: '8.0'
|
||||
pkginstall: sudo pkg_add -u && sudo pkg_add ccache cmake git libao libcdio libusb1-1.0.30 libzip lua-5.4.8p2 miniupnpc ninja pulseaudio sdl2
|
||||
exclude:
|
||||
- architecture: arm64
|
||||
|
||||
|
||||
+13
-10
@@ -24,6 +24,7 @@ jobs:
|
||||
- { name: i686-pc-windows-msvc, os: windows-2022, shell: cmd, arch: x86, cmakeArgs: -G Ninja, buildType: Release }
|
||||
- { name: apple-darwin, os: macos-latest, shell: sh, cmakeArgs: -G Xcode -DUSE_DISCORD=ON -DMOLTENVK_DYLIB=$MOLTENVK_DYLIB, destDir: osx, buildType: RelWithDebInfo, packagePath: artifact/flycast-apple-darwin.zip }
|
||||
- { name: x86_64-pc-linux-gnu, os: ubuntu-22.04, shell: sh, cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: linux, buildType: RelWithDebInfo }
|
||||
- { name: arm64-pc-linux-gnu, os: ubuntu-22.04-arm, shell: sh, cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: linux, buildType: RelWithDebInfo }
|
||||
- { name: x86_64-pc-windows-msvc, os: windows-2022, shell: cmd, arch: x64, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, buildType: Release }
|
||||
- { name: x86_64-w64-mingw32, os: windows-2022, shell: 'msys2 {0}', cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: win, buildType: RelWithDebInfo, packagePath: artifact/flycast-x86_64-w64-mingw32.zip }
|
||||
- { name: libretro-x86_64-pc-linux-gnu, os: ubuntu-22.04, shell: sh, cmakeArgs: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -G Ninja, buildType: Release }
|
||||
@@ -36,7 +37,6 @@ jobs:
|
||||
brew list -1 | grep python | while read formula; do brew unlink $formula; brew link --overwrite $formula; done
|
||||
brew update || :
|
||||
brew install libao ldid pulseaudio
|
||||
brew uninstall --ignore-dependencies zstd
|
||||
|
||||
moltenvk_tag="v1.4.2"
|
||||
moltenvk_root="$RUNNER_TEMP/MoltenVK-$moltenvk_tag"
|
||||
@@ -88,16 +88,16 @@ jobs:
|
||||
|
||||
- name: Compile universal dependencies (macOS)
|
||||
run: |
|
||||
for formula in ./shell/apple/libomp.rb ./shell/apple/libpng-flycast.rb; do
|
||||
for formula in ./shell/apple/libpng-flycast.rb; do
|
||||
libname=$(basename "$formula" .rb)
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_DEVELOPER=1 brew install --build-from-source --formula "$formula" || \
|
||||
HOMEBREW_NO_INSTALLED_DEPENDENTS_CHECK=1 HOMEBREW_DEVELOPER=1 brew reinstall --build-from-source --formula "$formula"
|
||||
|
||||
|
||||
prefix=$(brew --prefix $libname)
|
||||
# Find and verify the static library
|
||||
libfile=$(find "$prefix/lib" -name "*.a" | head -n1)
|
||||
lipo -info "$libfile"
|
||||
|
||||
|
||||
# Export prefix for subsequent CMake steps
|
||||
if [ -z "$CMAKE_PREFIX_PATH" ]; then
|
||||
CMAKE_PREFIX_PATH="$prefix"
|
||||
@@ -106,6 +106,8 @@ jobs:
|
||||
fi
|
||||
done
|
||||
echo "CMAKE_PREFIX_PATH=$CMAKE_PREFIX_PATH" >> $GITHUB_ENV
|
||||
# After the last brew download: brew's own curl links brew's zstd
|
||||
brew uninstall --ignore-dependencies zstd
|
||||
if: matrix.config.name == 'apple-darwin'
|
||||
|
||||
- uses: actions/cache@v6
|
||||
@@ -127,7 +129,7 @@ jobs:
|
||||
cmake -B build -DCMAKE_BUILD_TYPE=${{ matrix.config.buildType }} -DCMAKE_INSTALL_PREFIX=artifact -DENABLE_CTEST=ON ${{ matrix.config.cmakeArgs }}
|
||||
cmake --build build --config ${{ matrix.config.buildType }}
|
||||
./build/flycast
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu' || matrix.config.name == 'arm64-pc-linux-gnu'
|
||||
|
||||
- name: Dump symbols
|
||||
run: |
|
||||
@@ -142,7 +144,7 @@ jobs:
|
||||
./configure
|
||||
make
|
||||
sudo make install
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu' || matrix.config.name == 'arm64-pc-linux-gnu'
|
||||
|
||||
- name: Dump symbols and make AppImage (linux)
|
||||
run: |
|
||||
@@ -150,9 +152,10 @@ jobs:
|
||||
dump_syms artifact/bin/flycast > symbols/flycast.sym 2>/dev/null
|
||||
strip artifact/bin/flycast
|
||||
shell/linux/make-appimage.sh . artifact/bin appimage
|
||||
mv flycast-x86_64.AppImage artifact/bin
|
||||
ARCH=$(uname -m)
|
||||
mv flycast-${ARCH}.AppImage artifact/bin
|
||||
rm artifact/bin/flycast
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu'
|
||||
if: matrix.config.name == 'x86_64-pc-linux-gnu' || matrix.config.name == 'arm64-pc-linux-gnu'
|
||||
|
||||
- name: Package app (macos)
|
||||
run: |
|
||||
@@ -164,7 +167,7 @@ jobs:
|
||||
|
||||
- name: Package app (windows)
|
||||
run: |
|
||||
powershell Compress-Archive artifact/bin/flycast.exe artifact/bin/flycast.zip
|
||||
powershell Compress-Archive artifact/bin/flycast.exe artifact/bin/flycast.zip
|
||||
rm artifact/bin/flycast.exe
|
||||
cp artifact/bin/flycast.zip "${{ matrix.config.packagePath }}"
|
||||
if: matrix.config.name == 'x86_64-w64-mingw32'
|
||||
@@ -208,4 +211,4 @@ jobs:
|
||||
shell: bash
|
||||
env:
|
||||
SENTRY_TOKEN: ${{ secrets.SENTRY_TOKEN }}
|
||||
if: ${{ env.SENTRY_TOKEN != '' && (matrix.config.name == 'x86_64-w64-mingw32' || matrix.config.name == 'apple-darwin' || matrix.config.name == 'x86_64-pc-linux-gnu') && github.event_name == 'push' }}
|
||||
if: ${{ env.SENTRY_TOKEN != '' && (matrix.config.name == 'x86_64-w64-mingw32' || matrix.config.name == 'apple-darwin' || matrix.config.name == 'x86_64-pc-linux-gnu' || matrix.config.name == 'arm64-pc-linux-gnu') && github.event_name == 'push' }}
|
||||
+4
-1
@@ -49,7 +49,7 @@
|
||||
url = https://github.com/herumi/xbyak.git
|
||||
[submodule "core/deps/libjuice"]
|
||||
path = core/deps/libjuice
|
||||
url = https://github.com/paullouisageneau/libjuice.git
|
||||
url = https://github.com/flyinghead/libjuice.git
|
||||
[submodule "core/deps/websocketpp"]
|
||||
path = core/deps/websocketpp
|
||||
url = https://github.com/zaphoyd/websocketpp.git
|
||||
@@ -65,3 +65,6 @@
|
||||
[submodule "core/deps/gamesdk"]
|
||||
path = core/deps/gamesdk
|
||||
url = https://github.com/flyinghead/gamesdk.git
|
||||
[submodule "core/deps/basis_universal"]
|
||||
path = core/deps/basis_universal
|
||||
url = https://github.com/BinomialLLC/basis_universal.git
|
||||
+109
-62
@@ -87,6 +87,7 @@ option(USE_HOST_GLSLANG "Use host glslang" OFF)
|
||||
option(USE_HOST_LIBZIP "Use host libzip" ON)
|
||||
option(USE_HOST_SDL "Use host SDL library" ${USE_HOST_SDL_DEFAULT})
|
||||
option(USE_HOST_LIBCHDR "Use host libchdr" OFF)
|
||||
option(USE_HOST_LIBJUICE "Use host libjuice" OFF)
|
||||
option(USE_OPENMP "Use OpenMP if available" ON)
|
||||
option(USE_VULKAN "Build with Vulkan support" ON)
|
||||
set(MOLTENVK_DYLIB "" CACHE FILEPATH "Path to macOS MoltenVK dylib for macOS Vulkan builds")
|
||||
@@ -106,6 +107,7 @@ option(ENABLE_DC_PROFILER "Build with support for target machine (SH4) profiler"
|
||||
option(ENABLE_FC_PROFILER "Build with support for host app (Flycast) profiler" OFF)
|
||||
option(USE_DISCORD "Use Discord Presence API" OFF)
|
||||
option(USE_LIBCDIO "Use libcdio for CDROM access" OFF)
|
||||
option(USE_DREAMLINK_DEVICES "Use DreamLink devices" ON)
|
||||
|
||||
if(IOS AND NOT LIBRETRO)
|
||||
set(USE_VULKAN OFF CACHE BOOL "Force vulkan off" FORCE)
|
||||
@@ -121,6 +123,11 @@ if(WEBOS)
|
||||
set(USE_VULKAN OFF CACHE BOOL "Force vulkan off" FORCE)
|
||||
endif()
|
||||
|
||||
if(ANDROID OR IOS OR NINTENDO_SWITCH OR LIBRETRO)
|
||||
# DreamLink only available for non-mobile Linux, MacOS, and Windows
|
||||
set(USE_DREAMLINK_DEVICES OFF CACHE BOOL "Force DreamLink devices off" FORCE)
|
||||
endif()
|
||||
|
||||
include(GNUInstallDirs)
|
||||
include(CMakeRC)
|
||||
|
||||
@@ -223,7 +230,7 @@ elseif(LIBRETRO)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE ${FOUNDATION})
|
||||
endif()
|
||||
if(ANDROID OR USE_GLES)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE GLES GLES3 HAVE_OPENGLES HAVE_OPENGLES3 HAVE_OIT)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE GLES GLES3 HAVE_OPENGLES HAVE_OPENGLES3 HAVE_OPENGLES_3_1 HAVE_OIT)
|
||||
if(ANDROID)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE android)
|
||||
endif()
|
||||
@@ -279,6 +286,7 @@ elseif(WIN32)
|
||||
set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT ${PROJECT_NAME})
|
||||
endif()
|
||||
elseif(APPLE)
|
||||
set(USE_OPENMP OFF CACHE BOOL "Force openmp off" FORCE)
|
||||
add_executable(${PROJECT_NAME} MACOSX_BUNDLE core/emulator.cpp)
|
||||
else()
|
||||
add_executable(${PROJECT_NAME} core/emulator.cpp)
|
||||
@@ -288,6 +296,25 @@ set_target_properties(${PROJECT_NAME} PROPERTIES
|
||||
CMAKE_CXX_STANDARD 20
|
||||
CMAKE_CXX_STANDARD_REQUIRED ON)
|
||||
|
||||
add_library(flycast_basis_transcoder STATIC
|
||||
core/deps/basis_universal/transcoder/basisu_transcoder.cpp
|
||||
core/deps/basis_universal/zstd/zstddeclib.c)
|
||||
target_compile_features(flycast_basis_transcoder PRIVATE cxx_std_17)
|
||||
target_compile_definitions(flycast_basis_transcoder PRIVATE
|
||||
BASISD_SUPPORT_KTX2=1
|
||||
BASISD_SUPPORT_KTX2_ZSTD=1
|
||||
BASISD_SUPPORT_DXT1=1
|
||||
BASISD_SUPPORT_DXT5A=1
|
||||
BASISD_SUPPORT_ETC2_EAC_A8=1
|
||||
BASISD_SUPPORT_XUASTC=1)
|
||||
target_include_directories(flycast_basis_transcoder PUBLIC core/deps)
|
||||
set_target_properties(flycast_basis_transcoder PROPERTIES POSITION_INDEPENDENT_CODE ON)
|
||||
if(CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
|
||||
target_compile_options(flycast_basis_transcoder PRIVATE
|
||||
$<$<COMPILE_LANGUAGE:CXX>:-fno-strict-aliasing>)
|
||||
endif()
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE flycast_basis_transcoder)
|
||||
|
||||
if(WINDOWS_STORE)
|
||||
set(USE_OPENGL OFF)
|
||||
set(USE_VULKAN OFF)
|
||||
@@ -436,10 +463,6 @@ if(USE_BREAKPAD AND NOT LIBRETRO)
|
||||
endif()
|
||||
|
||||
if(USE_OPENMP)
|
||||
if(APPLE AND IS_DIRECTORY "/opt/homebrew/opt/libomp")
|
||||
list(PREPEND CMAKE_PREFIX_PATH "/opt/homebrew/opt/libomp")
|
||||
message(STATUS "Found and using Homebrew libomp hint.")
|
||||
endif()
|
||||
find_package(OpenMP)
|
||||
if(OpenMP_CXX_FOUND)
|
||||
if(MINGW)
|
||||
@@ -480,6 +503,37 @@ if(USE_VULKAN)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE glslang::glslang-default-resource-limits glslang::SPIRV)
|
||||
endif()
|
||||
|
||||
if(USE_DREAMLINK_DEVICES)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_DREAMLINK_DEVICES=1)
|
||||
|
||||
if (WINDOWS_STORE)
|
||||
# Use winrt implementation of the DreamPicoPort-API
|
||||
option(DREAMPICOPORT_WITH_LIBUSB "Use libusb to access DreamPicoPort-API" OFF)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|NetBSD|OpenBSD|Haiku")
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
|
||||
|
||||
set(DREAMPICOPORT_LIBUSB_LIBRARIES "${LIBUSB_LIBRARIES}" CACHE STRING "Name of the libusb library to link against")
|
||||
set(DREAMPICOPORT_LIBUSB_INCLUDE_DIRS "${LIBUSB_INCLUDE_DIRS}" CACHE STRING "Path to libusb include directory, when applicable")
|
||||
else()
|
||||
if(MINGW)
|
||||
# Shim 'windowsapp' to prevent libusb from linking against the UWP library
|
||||
# and link against standard desktop libraries instead for Windows 7 support.
|
||||
if(NOT TARGET windowsapp)
|
||||
add_library(windowsapp INTERFACE)
|
||||
target_link_libraries(windowsapp INTERFACE advapi32 ole32 shell32 user32)
|
||||
endif()
|
||||
endif()
|
||||
# libusb
|
||||
add_subdirectory(core/deps/DreamPicoPort-API/ext/libusb-cmake)
|
||||
endif()
|
||||
|
||||
# DreamPicoPort-API
|
||||
option(DREAMPICOPORT_ADD_LIBUSB "Add internal libusb library" OFF) # Already included above, when applicable
|
||||
add_subdirectory(core/deps/DreamPicoPort-API EXCLUDE_FROM_ALL)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE dream_pico_port_api)
|
||||
endif()
|
||||
|
||||
if(NOT LIBRETRO)
|
||||
if(USE_OSS)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_OSS)
|
||||
@@ -499,39 +553,6 @@ if(NOT LIBRETRO)
|
||||
endif()
|
||||
|
||||
if(NOT ANDROID AND NOT IOS)
|
||||
if (NOT NINTENDO_SWITCH)
|
||||
# DreamLink enabled for non-mobile Linux, MacOS, and Windows
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_DREAMLINK_DEVICES=1)
|
||||
set(USE_DREAMLINK_DEVICES ON) # Must be set before adding core/sdl
|
||||
|
||||
if (WINDOWS_STORE)
|
||||
# Use winrt implementation of the DreamPicoPort-API
|
||||
option(DREAMPICOPORT_WITH_LIBUSB "Use libusb to access DreamPicoPort-API" OFF)
|
||||
elseif (CMAKE_SYSTEM_NAME MATCHES "DragonFly|FreeBSD|NetBSD|OpenBSD|Haiku")
|
||||
find_package(PkgConfig REQUIRED)
|
||||
pkg_check_modules(LIBUSB REQUIRED libusb-1.0)
|
||||
|
||||
set(DREAMPICOPORT_LIBUSB_LIBRARIES "${LIBUSB_LIBRARIES}" CACHE STRING "Name of the libusb library to link against")
|
||||
set(DREAMPICOPORT_LIBUSB_INCLUDE_DIRS "${LIBUSB_INCLUDE_DIRS}" CACHE STRING "Path to libusb include directory, when applicable")
|
||||
else()
|
||||
if(MINGW)
|
||||
# Shim 'windowsapp' to prevent libusb from linking against the UWP library
|
||||
# and link against standard desktop libraries instead for Windows 7 support.
|
||||
if(NOT TARGET windowsapp)
|
||||
add_library(windowsapp INTERFACE)
|
||||
target_link_libraries(windowsapp INTERFACE advapi32 ole32 shell32 user32)
|
||||
endif()
|
||||
endif()
|
||||
# libusb
|
||||
add_subdirectory(core/deps/DreamPicoPort-API/ext/libusb-cmake)
|
||||
endif()
|
||||
|
||||
# DreamPicoPort-API
|
||||
option(DREAMPICOPORT_ADD_LIBUSB "Add internal libusb library" OFF) # Already included above, when applicable
|
||||
add_subdirectory(core/deps/DreamPicoPort-API EXCLUDE_FROM_ALL)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE dream_pico_port_api)
|
||||
endif()
|
||||
|
||||
if(USE_HOST_SDL)
|
||||
find_package(SDL2 2.0.18)
|
||||
endif()
|
||||
@@ -558,11 +579,6 @@ if(NOT LIBRETRO)
|
||||
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_SDL USE_SDL_AUDIO)
|
||||
add_subdirectory(core/sdl)
|
||||
|
||||
if((UNIX AND NOT APPLE) OR NINTENDO_SWITCH)
|
||||
find_package(CURL REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
find_package(ZLIB)
|
||||
@@ -582,6 +598,18 @@ if(NOT LIBRETRO)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# http
|
||||
if((UNIX AND NOT APPLE AND NOT ANDROID) OR NINTENDO_SWITCH)
|
||||
find_package(CURL REQUIRED)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE CURL::libcurl)
|
||||
elseif(LIBRETRO AND ANDROID)
|
||||
# libretro needs to enable prefab and add dependency 'implementation("io.github.vvb2060.ndk:curl:8.18.0")'
|
||||
find_package(curl REQUIRED CONFIG)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE curl::curl_static)
|
||||
elseif(LIBRETRO AND WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE wininet)
|
||||
endif()
|
||||
|
||||
find_package(PkgConfig)
|
||||
|
||||
if(NOT WITH_SYSTEM_ZLIB)
|
||||
@@ -797,18 +825,27 @@ if(WIN32)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/dirent)
|
||||
endif()
|
||||
|
||||
if(NOT LIBRETRO AND NOT NINTENDO_SWITCH)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_ICE)
|
||||
# libjuice
|
||||
# libjuice
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_ICE)
|
||||
if (USE_HOST_LIBJUICE AND UNIX AND NOT APPLE AND NOT ANDROID)
|
||||
find_package(LibJuice)
|
||||
endif()
|
||||
if(NOT TARGET LibJuice::LibJuiceStatic)
|
||||
option(BUILD_SHARED_LIBS "Build shared libraries" OFF)
|
||||
option(NO_SERVER "Disable server support" ON)
|
||||
option(NO_TESTS "Disable tests build" ON)
|
||||
add_subdirectory(core/deps/libjuice EXCLUDE_FROM_ALL)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE LibJuice::LibJuiceStatic)
|
||||
if(NINTENDO_SWITCH)
|
||||
target_include_directories(juice-static SYSTEM PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/shell/switch)
|
||||
target_compile_definitions(juice-static PRIVATE getnameinfo=getnameinfo_fixed)
|
||||
endif()
|
||||
endif()
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE LibJuice::LibJuiceStatic)
|
||||
|
||||
# websocket++
|
||||
# websocket++
|
||||
if(NOT LIBRETRO AND NOT NINTENDO_SWITCH)
|
||||
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/websocketpp)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE ASIO_STANDALONE)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE ASIO_STANDALONE USE_WEBSOCKET)
|
||||
if (MSVC)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE
|
||||
_WEBSOCKETPP_CPP11_FUNCTIONAL_
|
||||
@@ -823,6 +860,7 @@ if(NOT LIBRETRO AND NOT NINTENDO_SWITCH)
|
||||
endif()
|
||||
endif()
|
||||
|
||||
# tinygettext
|
||||
set(TINYGETTEXT_WO_ICONV ON)
|
||||
add_subdirectory(core/deps/tinygettext EXCLUDE_FROM_ALL)
|
||||
if(MSVC)
|
||||
@@ -987,7 +1025,9 @@ if(LIBRETRO)
|
||||
shell/libretro/storage.cpp
|
||||
shell/libretro/vmu_xhair.cpp)
|
||||
if(APPLE)
|
||||
target_sources(${PROJECT_NAME} PRIVATE shell/libretro/oslib_apple.mm)
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
shell/libretro/oslib_apple.mm
|
||||
shell/apple/common/http_client.mm)
|
||||
endif()
|
||||
if(WIN32)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE mswsock)
|
||||
@@ -1376,19 +1416,24 @@ if(NOT LIBRETRO)
|
||||
COMMAND ${CMAKE_COMMAND} -E tar "cfv" "${CMAKE_CURRENT_BINARY_DIR}/$<CONFIG>-${CMAKE_OSX_SYSROOT}/Flycast.ipa" --format=zip ${CMAKE_CURRENT_BINARY_DIR}/Payload)
|
||||
else()
|
||||
|
||||
add_subdirectory(core/deps/Syphon)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Syphon)
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${SYPHON_METALLIB}
|
||||
$<TARGET_FILE_DIR:flycast>/../Resources/)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE VIDEO_ROUTING)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
shell/apple/common/http_client.mm
|
||||
shell/apple/common/util.mm
|
||||
shell/apple/emulator-osx/emulator-osx/SDLApplicationDelegate.h
|
||||
shell/apple/emulator-osx/emulator-osx/SDLApplicationDelegate.mm
|
||||
shell/apple/emulator-osx/emulator-osx/osx-main.mm)
|
||||
shell/apple/common/util.mm)
|
||||
# The app's main() lives in SDLApplicationDelegate.mm; the test binary
|
||||
# gets its main() from gtest_main and stubs the rest (tests/src/test_stubs.cpp).
|
||||
if(NOT BUILD_TESTING)
|
||||
add_subdirectory(core/deps/Syphon)
|
||||
target_link_libraries(${PROJECT_NAME} PRIVATE Syphon)
|
||||
add_custom_command(TARGET ${PROJECT_NAME} POST_BUILD
|
||||
COMMAND ${CMAKE_COMMAND} -E copy ${SYPHON_METALLIB}
|
||||
$<TARGET_FILE_DIR:flycast>/../Resources/)
|
||||
target_compile_definitions(${PROJECT_NAME} PRIVATE VIDEO_ROUTING)
|
||||
|
||||
target_sources(${PROJECT_NAME} PRIVATE
|
||||
shell/apple/emulator-osx/emulator-osx/SDLApplicationDelegate.h
|
||||
shell/apple/emulator-osx/emulator-osx/SDLApplicationDelegate.mm
|
||||
shell/apple/emulator-osx/emulator-osx/osx-main.mm)
|
||||
endif()
|
||||
set(ASSETS shell/apple/emulator-osx/emulator-osx/Images.xcassets)
|
||||
target_sources(${PROJECT_NAME} PRIVATE ${ASSETS})
|
||||
source_group("Resources" FILES ${ASSETS})
|
||||
@@ -1509,7 +1554,7 @@ if(NINTENDO_SWITCH)
|
||||
COMMAND ${CMAKE_COMMAND} -E make_directory
|
||||
combined-objs/xxhash combined-objs/chdr combined-objs/zip
|
||||
combined-objs/zstd combined-objs/elf combined-objs/tinygettext
|
||||
combined-objs/res
|
||||
combined-objs/res combined-objs/libjuice
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir combined-objs/xxhash
|
||||
${CMAKE_AR} -x $<TARGET_FILE:xxHash::xxhash>
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir combined-objs/chdr
|
||||
@@ -1524,6 +1569,8 @@ if(NINTENDO_SWITCH)
|
||||
${CMAKE_AR} -x $<TARGET_FILE:tinygettext::tinygettext>
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir combined-objs/res
|
||||
${CMAKE_AR} -x $<TARGET_FILE:flycast::res>
|
||||
COMMAND ${CMAKE_COMMAND} -E chdir combined-objs/libjuice
|
||||
${CMAKE_AR} -x $<TARGET_FILE:LibJuice::LibJuiceStatic>
|
||||
COMMAND ${CMAKE_AR} -rs flycast_libretro_libnx.a combined-objs/*/*.o
|
||||
COMMAND ${CMAKE_COMMAND} -E rm -rf combined-objs
|
||||
WORKING_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_ARCHIVE_OUTPUT_DIRECTORY}
|
||||
|
||||
@@ -73,8 +73,10 @@ constexpr u32 SAMPLE_COUNT = 512; // AudioBackend::push() is always called with
|
||||
class RingBuffer
|
||||
{
|
||||
std::vector<u8> buffer;
|
||||
std::atomic_int readCursor { 0 };
|
||||
std::atomic_int writeCursor { 0 };
|
||||
// The producer and the audio callback each update one cursor: keep them on
|
||||
// separate cache lines.
|
||||
alignas(64) std::atomic_int readCursor { 0 };
|
||||
alignas(64) std::atomic_int writeCursor { 0 };
|
||||
|
||||
u32 readSize() {
|
||||
return (u32)((writeCursor - readCursor + buffer.size()) % buffer.size());
|
||||
|
||||
+1
-1
@@ -1,5 +1,5 @@
|
||||
#pragma once
|
||||
#include <stdint.h>
|
||||
#include <cstdint>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
|
||||
+14
-3
@@ -20,6 +20,8 @@
|
||||
#include "types.h"
|
||||
#include "stdclass.h"
|
||||
|
||||
#include <cctype>
|
||||
|
||||
namespace config {
|
||||
|
||||
const IniFile::Entry *IniFile::getEntry(const std::string& sectionName, const std::string& name) const
|
||||
@@ -71,7 +73,7 @@ bool IniFile::getBool(const std::string& section, const std::string& entry, bool
|
||||
static bool hasHexPrefix(const std::string& s)
|
||||
{
|
||||
size_t pos = 0;
|
||||
while (pos < s.length() - 1 && std::isspace((int8_t)s[pos]))
|
||||
while (pos + 1 < s.length() && std::isspace(static_cast<unsigned char>(s[pos])))
|
||||
pos++;
|
||||
if (pos + 1 >= s.length())
|
||||
return false;
|
||||
@@ -89,9 +91,18 @@ int IniFile::getInt(const std::string& section, const std::string& entry, int de
|
||||
ss.setf(std::ios_base::hex, std::ios_base::basefield);
|
||||
else
|
||||
ss.setf(std::ios_base::dec, std::ios_base::basefield);
|
||||
unsigned value = defaultValue;
|
||||
unsigned value = static_cast<unsigned>(defaultValue);
|
||||
ss >> value;
|
||||
return (int)value;
|
||||
if (ss)
|
||||
return static_cast<int>(value);
|
||||
// Compatibility for options migrated from Option<bool> to Option<int>.
|
||||
std::string token = *pValue;
|
||||
string_tolower(token);
|
||||
if (token == "yes" || token == "true" || token == "on")
|
||||
return 1;
|
||||
if (token == "no" || token == "false" || token == "off")
|
||||
return 0;
|
||||
return defaultValue;
|
||||
}
|
||||
|
||||
int64_t IniFile::getInt64(const std::string& section, const std::string& entry, int64_t defaultValue) const
|
||||
|
||||
@@ -21,7 +21,6 @@
|
||||
#include <cstdio>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <cstdint>
|
||||
#include <vector>
|
||||
#include <locale>
|
||||
#include <sstream>
|
||||
|
||||
+17
-1
@@ -77,11 +77,12 @@ Option<int> TextureUpscale("rend.TextureUpscale2", 1);
|
||||
Option<int> MaxFilteredTextureSize("rend.MaxFilteredTextureSize", 256);
|
||||
Option<float> ExtraDepthScale("rend.ExtraDepthScale", 1.f);
|
||||
Option<bool> CustomTextures("rend.CustomTextures");
|
||||
Option<bool> PreloadCustomTextures("rend.PreloadCustomTextures");
|
||||
Option<int> PreloadCustomTextures("rend.PreloadCustomTextures", 0);
|
||||
Option<bool> DumpTextures("rend.DumpTextures");
|
||||
Option<bool> DumpUniqueTextures("rend.DumpUniqueTextures");
|
||||
Option<bool> DumpReplacedTextures("rend.DumpReplacedTextures");
|
||||
Option<int> ScreenStretching("rend.ScreenStretching", 100);
|
||||
Option<bool> StretchToFill("rend.StretchToFill");
|
||||
Option<bool> Fog("rend.Fog", true);
|
||||
Option<bool> FloatVMUs("rend.FloatVMUs");
|
||||
Option<bool> Rotate90("rend.Rotate90");
|
||||
@@ -121,6 +122,20 @@ Option<bool> EmulateFramebuffer("rend.EmulateFramebuffer", false);
|
||||
Option<bool> FixUpscaleBleedingEdge("rend.FixUpscaleBleedingEdge", true);
|
||||
Option<bool> CustomGpuDriver("rend.CustomGpuDriver", false);
|
||||
Option<bool> FramePacing("rend.FramePacing", true);
|
||||
|
||||
CustomTexturePreloadMode customTexturePreloadMode()
|
||||
{
|
||||
const auto mode = static_cast<CustomTexturePreloadMode>(PreloadCustomTextures.get());
|
||||
switch (mode)
|
||||
{
|
||||
case CustomTexturePreloadMode::Off:
|
||||
case CustomTexturePreloadMode::SystemMemory:
|
||||
case CustomTexturePreloadMode::VideoMemory:
|
||||
return mode;
|
||||
default:
|
||||
return CustomTexturePreloadMode::Off;
|
||||
}
|
||||
}
|
||||
#ifdef VIDEO_ROUTING
|
||||
Option<bool, false> VideoRouting("rend.VideoRouting", false);
|
||||
Option<bool, false> VideoRoutingScale("rend.VideoRoutingScale", false);
|
||||
@@ -244,6 +259,7 @@ Option<std::string, false> LuaFileName("LuaFileName", "flycast.lua");
|
||||
// RetroAchievements
|
||||
|
||||
Option<bool> EnableAchievements("Enabled", false, "achievements");
|
||||
Option<bool> ShowAchievementPopups("Show", true, "achievements");
|
||||
Option<bool> AchievementsHardcoreMode("HardcoreMode", false, "achievements");
|
||||
OptionString AchievementsUserName("UserName", "", "achievements");
|
||||
OptionString AchievementsToken("Token", "", "achievements");
|
||||
|
||||
+15
-6
@@ -17,16 +17,16 @@
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <type_traits>
|
||||
#include "cfg.h"
|
||||
#include "hw/maple/maple_cfg.h"
|
||||
#ifdef LIBRETRO
|
||||
#include <libretro.h>
|
||||
#endif
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <string>
|
||||
#include <type_traits>
|
||||
#include <vector>
|
||||
|
||||
namespace config {
|
||||
|
||||
@@ -447,11 +447,19 @@ extern Option<int> PerPixelLayers;
|
||||
#endif
|
||||
extern Option<float> ExtraDepthScale;
|
||||
extern Option<bool> CustomTextures;
|
||||
extern Option<bool> PreloadCustomTextures;
|
||||
enum class CustomTexturePreloadMode : int
|
||||
{
|
||||
Off = 0,
|
||||
SystemMemory = 1,
|
||||
VideoMemory = 2,
|
||||
};
|
||||
extern Option<int> PreloadCustomTextures;
|
||||
CustomTexturePreloadMode customTexturePreloadMode();
|
||||
extern Option<bool> DumpTextures;
|
||||
extern Option<bool> DumpUniqueTextures;
|
||||
extern Option<bool> DumpReplacedTextures;
|
||||
extern Option<int> ScreenStretching; // in percent. 150 means stretch from 4/3 to 6/3
|
||||
extern Option<bool> StretchToFill;
|
||||
extern Option<bool> Fog;
|
||||
extern Option<bool> FloatVMUs;
|
||||
extern Option<bool> Rotate90;
|
||||
@@ -571,6 +579,7 @@ extern Option<std::string, false> LuaFileName;
|
||||
// RetroAchievements
|
||||
|
||||
extern Option<bool> EnableAchievements;
|
||||
extern Option<bool> ShowAchievementPopups;
|
||||
extern Option<bool> AchievementsHardcoreMode;
|
||||
extern OptionString AchievementsUserName;
|
||||
extern OptionString AchievementsToken;
|
||||
|
||||
@@ -29,6 +29,10 @@
|
||||
#include "emulator.h"
|
||||
#include "oslib/storage.h"
|
||||
#include "oslib/i18n.h"
|
||||
|
||||
#include <string>
|
||||
#include <vector>
|
||||
|
||||
using namespace i18n;
|
||||
|
||||
const WidescreenCheat CheatManager::widescreen_cheats[] =
|
||||
|
||||
@@ -24,7 +24,7 @@
|
||||
#include "hw/sh4/dyna/shil.h"
|
||||
#include "cfg/option.h"
|
||||
#include <array>
|
||||
#include <signal.h>
|
||||
#include <csignal>
|
||||
#include <map>
|
||||
|
||||
#ifndef SIGTRAP
|
||||
|
||||
+1
-1
Submodule core/deps/asio updated: d3402006e8...3321a1a753.
Submodule
+1
Submodule core/deps/basis_universal added at 9bebe16726.
+1
-1
Submodule core/deps/breakpad updated: 1ab24bcc81...6f3c4af788.
@@ -303,8 +303,6 @@ void ImGui_ImplOpenGL3_RenderDrawData(ImDrawData* draw_data)
|
||||
if (fb_width <= 0 || fb_height <= 0)
|
||||
return;
|
||||
|
||||
ImGui_ImplOpenGL3_Data* bd = ImGui_ImplOpenGL3_GetBackendData();
|
||||
|
||||
// Catch up with texture updates. Most of the times, the list will have 1 element with an OK status, aka nothing to do.
|
||||
// (This almost always points to ImGui::GetPlatformIO().Textures[] but is part of ImDrawData to allow overriding or disabling texture updates).
|
||||
if (draw_data->Textures != nullptr)
|
||||
|
||||
+1
-1
Submodule core/deps/libjuice updated: 77daa8befd...8f0918a716.
@@ -3008,7 +3008,10 @@ static bool glsm_state_ctx_init(glsm_ctx_params_t *params)
|
||||
return false;
|
||||
|
||||
#ifdef HAVE_OPENGLES
|
||||
hw_render.context_type = RETRO_HW_CONTEXT_OPENGLES2;
|
||||
hw_render.context_type = params->context_type != RETRO_HW_CONTEXT_NONE
|
||||
? params->context_type : RETRO_HW_CONTEXT_OPENGLES2;
|
||||
hw_render.version_major = params->major;
|
||||
hw_render.version_minor = params->minor;
|
||||
#else
|
||||
hw_render.context_type = RETRO_HW_CONTEXT_OPENGL;
|
||||
if (params->context_type != RETRO_HW_CONTEXT_NONE)
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
|
||||
*********************************************************************/
|
||||
#define MAX_BLOCK_SIZE 1600
|
||||
#define MAX_BLOCK_COUNT 16
|
||||
#define MAX_BLOCK_COUNT 1024 /* 16 blocks (~1,000 timers) ran out with a few dozen connections a second */
|
||||
|
||||
#define DECLARE_HEAP(type, orderby) \
|
||||
struct heap_ ## type { \
|
||||
|
||||
@@ -69,6 +69,8 @@ static void debug_q(struct pico_queue *q)
|
||||
|
||||
static inline int32_t pico_enqueue(struct pico_queue *q, struct pico_frame *p)
|
||||
{
|
||||
uint32_t size;
|
||||
|
||||
if ((q->max_frames) && (q->max_frames <= q->frames))
|
||||
return -1;
|
||||
|
||||
@@ -98,18 +100,19 @@ static inline int32_t pico_enqueue(struct pico_queue *q, struct pico_frame *p)
|
||||
q->size += p->buffer_len + q->overhead;
|
||||
q->frames++;
|
||||
debug_q(q);
|
||||
/* Read under the lock: once unlocked, another thread may dequeue (and free) the frame,
|
||||
* and a size of 0 would make the caller free it again. */
|
||||
size = q->size;
|
||||
|
||||
if (q->shared)
|
||||
PICOTCP_MUTEX_UNLOCK(q->mutex);
|
||||
|
||||
return (int32_t)q->size;
|
||||
return (int32_t)size;
|
||||
}
|
||||
|
||||
static inline struct pico_frame *pico_dequeue(struct pico_queue *q)
|
||||
{
|
||||
struct pico_frame *p = q->head;
|
||||
if (!p)
|
||||
return NULL;
|
||||
struct pico_frame *p;
|
||||
|
||||
if (q->frames < 1)
|
||||
return NULL;
|
||||
@@ -117,15 +120,19 @@ static inline struct pico_frame *pico_dequeue(struct pico_queue *q)
|
||||
if (q->shared)
|
||||
PICOTCP_MUTEX_LOCK(q->mutex);
|
||||
|
||||
q->head = p->next;
|
||||
q->frames--;
|
||||
q->size -= p->buffer_len - q->overhead;
|
||||
if (q->head == NULL)
|
||||
q->tail = NULL;
|
||||
p = q->head;
|
||||
if (p)
|
||||
{
|
||||
q->head = p->next;
|
||||
q->frames--;
|
||||
q->size -= p->buffer_len - q->overhead;
|
||||
if (q->head == NULL)
|
||||
q->tail = NULL;
|
||||
|
||||
debug_q(q);
|
||||
debug_q(q);
|
||||
|
||||
p->next = NULL;
|
||||
p->next = NULL;
|
||||
}
|
||||
if (q->shared)
|
||||
PICOTCP_MUTEX_UNLOCK(q->mutex);
|
||||
|
||||
@@ -160,6 +167,11 @@ static inline void pico_queue_empty(struct pico_queue *q)
|
||||
|
||||
static inline void pico_queue_protect(struct pico_queue *q)
|
||||
{
|
||||
#ifdef PICO_SUPPORT_MUTEX
|
||||
/* Create the mutex now: creating it lazily on first use races between threads */
|
||||
if (q->mutex == NULL)
|
||||
q->mutex = pico_mutex_init();
|
||||
#endif
|
||||
q->shared = 1;
|
||||
}
|
||||
|
||||
|
||||
@@ -1710,7 +1710,12 @@ int pico_socket_connect(struct pico_socket *s, const void *remote_addr, uint16_t
|
||||
return -1;
|
||||
}
|
||||
|
||||
pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0);
|
||||
/* A socket with the same quad is already in the tree: carrying on would leave this one
|
||||
* untracked, and deleting it later would unlink (and free) the other one. */
|
||||
if (pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0) != 0) {
|
||||
pico_err = PICO_ERR_EADDRINUSE;
|
||||
return -1;
|
||||
}
|
||||
|
||||
#ifdef PICO_SUPPORT_UDP
|
||||
if (PROTO(s) == PICO_PROTO_UDP) {
|
||||
|
||||
+40
-15
@@ -25,6 +25,7 @@
|
||||
#include "hw/naomi/naomi_cart.h"
|
||||
#include "reios/reios.h"
|
||||
#include "hw/sh4/modules/mmu.h"
|
||||
#include "hw/sh4/modules/modules.h"
|
||||
#include "hw/sh4/sh4_if.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
#include "hw/sh4/sh4_sched.h"
|
||||
@@ -42,15 +43,19 @@
|
||||
#include "hw/pvr/pvr.h"
|
||||
#include "profiler/fc_profiler.h"
|
||||
#include "oslib/storage.h"
|
||||
#include "wsi/context.h"
|
||||
#include <chrono>
|
||||
#ifndef LIBRETRO
|
||||
#include "ui/gui.h"
|
||||
#endif
|
||||
#include "hw/sh4/sh4_interpreter.h"
|
||||
#include "hw/sh4/dyna/ngen.h"
|
||||
#include "oslib/i18n.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <chrono>
|
||||
#include <exception>
|
||||
#include <future>
|
||||
#include <mutex>
|
||||
#include <string>
|
||||
#include <utility>
|
||||
|
||||
settings_t settings;
|
||||
constexpr char const *BIOS_TITLE = "Dreamcast BIOS";
|
||||
|
||||
@@ -259,7 +264,33 @@ static void loadSpecialSettings()
|
||||
|| prod_id == "T1235M" // Vampire Chronicle for Matching Service
|
||||
|| prod_id == "T22901N" // Roadsters (US)
|
||||
|| prod_id == "T28202M" // Shin Nihon Pro Wrestling 4
|
||||
|| prod_id == "T9512N")) // The Grinch (US)
|
||||
|| prod_id == "T9512N" // The Grinch (US)
|
||||
|| prod_id == "51034" // Generator Vol.1
|
||||
|| prod_id == "MK-51057" // Generator Vol.2
|
||||
|| prod_id == "T15128N" // Coaster Works
|
||||
|| prod_id == "T9505N" // ESPN-NBA 2 Night
|
||||
|| prod_id == "T-9703N" // NFL Blitz 2000
|
||||
|| prod_id == "T-8106N" // Shadow Man
|
||||
|| prod_id == "T15106N" // Slave Zero
|
||||
|| prod_id == "T40402N" // Tom Clancy's Rainbow Six - Rouge Spear
|
||||
|| prod_id == "T15125N" // Unreal Tournament
|
||||
|| prod_id == "6107140" // Dorimaga GD Vol.1
|
||||
|| prod_id == "T41202M" // Boku no tennis jinsei
|
||||
|| prod_id == "T9503M" // Eisei Meijin III
|
||||
|| prod_id == "T41201M" // Jet Coaster Dream
|
||||
|| prod_id == "T40902M" // Jet Coaster Dream 2
|
||||
|| prod_id == "T20105M" // Kanon
|
||||
|| prod_id == "T19702M" // Memories Off Complete
|
||||
|| prod_id == "T17001M" // Pen Pen Tri Iceron (JP)
|
||||
|| prod_id == "T1301M" // Revive - Sosei
|
||||
|| prod_id == "T15003M" // Seitai Heiki Expendable
|
||||
|| prod_id == "T38805M" // Sengoku Turb FID
|
||||
|| prod_id == "T20104M" // Sentimental Graffiti 2
|
||||
|| prod_id == "T16601M" // Shin Honkaku Hanafuda
|
||||
|| prod_id == "T1236M" // Super Street Fighter IIX For Matching Service
|
||||
|| prod_id == "T36501M" // Vigilante 8 (JP)
|
||||
|| prod_id == "T5301M" // World Neverland Plus - Olerud Kingdom Story
|
||||
|| prod_id == "HDR-0198")) // World Series Baseball 2K2 (JP)
|
||||
{
|
||||
NOTICE_LOG(BOOT, "Game doesn't support RGB. Using TV Composite instead");
|
||||
config::Cable.override(3);
|
||||
@@ -279,8 +310,7 @@ static void loadSpecialSettings()
|
||||
}
|
||||
else if (prod_id == "T17708N" // Stupid Invaders (US)
|
||||
|| prod_id == "T17711D" // Stupid Invaders (EU)
|
||||
|| prod_id == "T46509M" // Suika (JP)
|
||||
|| prod_id == "T36901M") // Cool Boarders Burrrn (JP)
|
||||
|| prod_id == "T46509M") // Suika (JP)
|
||||
{
|
||||
NOTICE_LOG(BOOT, "Forcing HLE BIOS");
|
||||
config::UseReios.override(true);
|
||||
@@ -555,7 +585,7 @@ int getGamePlatform(const std::string& filename)
|
||||
return DC_PLATFORM_DREAMCAST; // unknown
|
||||
if (extension == "zip" || extension == "7z")
|
||||
return naomi_cart_GetPlatform(filename.c_str());
|
||||
if (extension == "bin" || extension == "dat" || extension == "lst")
|
||||
if (extension == "bin" || extension == "dat")
|
||||
return DC_PLATFORM_NAOMI;
|
||||
|
||||
return DC_PLATFORM_DREAMCAST;
|
||||
@@ -867,7 +897,6 @@ void loadGameSpecificSettings()
|
||||
loadSpecialSettings();
|
||||
|
||||
config::Settings::instance().setGameId(settings.content.gameId);
|
||||
custom_texture.init();
|
||||
|
||||
// Reload per-game settings
|
||||
config::Settings::instance().load(true);
|
||||
@@ -899,11 +928,8 @@ void Emulator::stepRange(u32 from, u32 to)
|
||||
|
||||
void Emulator::loadstate(Deserializer& deser)
|
||||
{
|
||||
if (!custom_texture.preloaded())
|
||||
{
|
||||
custom_texture.terminate();
|
||||
custom_texture.init();
|
||||
}
|
||||
if (config::customTexturePreloadMode() == config::CustomTexturePreloadMode::Off)
|
||||
custom_texture.refresh();
|
||||
#if FEAT_AREC == DYNAREC_JIT
|
||||
aica::arm::recompiler::flush();
|
||||
#endif
|
||||
@@ -1125,7 +1151,6 @@ void Emulator::diskChange()
|
||||
{
|
||||
config::Settings::instance().reset();
|
||||
config::Settings::instance().load(false);
|
||||
custom_texture.terminate();
|
||||
if (!settings.content.path.empty())
|
||||
{
|
||||
hostfs::FileInfo info = hostfs::storage().getFileInfo(settings.content.path);
|
||||
|
||||
+1
-1
@@ -28,7 +28,7 @@
|
||||
#include <mutex>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
#include <ctime>
|
||||
|
||||
void loadGameSpecificSettings();
|
||||
void SaveSettings();
|
||||
|
||||
@@ -402,7 +402,8 @@ static void Write_SB_ADST(u32 addr, u32 data)
|
||||
SB_ADSUSP &= ~0x10;
|
||||
|
||||
// Schedule the end of DMA transfer interrupt
|
||||
int cycles = len * (SH4_MAIN_CLOCK / 2 / G2_BUS_CLOCK); // 16 bits @ 25 MHz
|
||||
// theoretical: 16 bits @ 25 MHz, actual: 11.3 MiB/s, here: 15.9 MiB/s
|
||||
int cycles = len * (SH4_MAIN_CLOCK / 2 / G2_BUS_CLOCK) * 3;
|
||||
if (cycles <= 512)
|
||||
dma_end_sched(0, 0, 0, nullptr);
|
||||
else
|
||||
@@ -544,6 +545,9 @@ void serialize(Serializer& ser)
|
||||
ser << aica_reg;
|
||||
|
||||
sgc::serialize(ser);
|
||||
sh4_sched_serialize(ser, aica_schid);
|
||||
sh4_sched_serialize(ser, rtc_schid);
|
||||
sh4_sched_serialize(ser, dma_sched_id);
|
||||
}
|
||||
|
||||
void deserialize(Deserializer& deser)
|
||||
@@ -559,10 +563,7 @@ void deserialize(Deserializer& deser)
|
||||
deser >> arm::armFiqEnable;
|
||||
deser >> arm::armMode;
|
||||
deser >> arm::Arm7Enabled;
|
||||
if (deser.version() >= Deserializer::V19)
|
||||
deser >> arm::arm7ClockTicks;
|
||||
else
|
||||
arm::arm7ClockTicks = 0;
|
||||
deser >> arm::arm7ClockTicks;
|
||||
|
||||
dsp::state.deserialize(deser);
|
||||
|
||||
@@ -573,11 +574,7 @@ void deserialize(Deserializer& deser)
|
||||
}
|
||||
|
||||
if (!deser.rollback())
|
||||
{
|
||||
aica_ram.deserialize(deser);
|
||||
if (settings.platform.isAtomiswave())
|
||||
deser.skip(6_MB, Deserializer::V30);
|
||||
}
|
||||
deser >> VREG;
|
||||
deser >> ARMRST;
|
||||
deser >> rtc_EN;
|
||||
@@ -586,6 +583,11 @@ void deserialize(Deserializer& deser)
|
||||
deser >> aica_reg;
|
||||
|
||||
sgc::deserialize(deser);
|
||||
if (deser.version() >= Deserializer::V62) {
|
||||
sh4_sched_deserialize(deser, aica_schid);
|
||||
sh4_sched_deserialize(deser, rtc_schid);
|
||||
sh4_sched_deserialize(deser, dma_sched_id);
|
||||
}
|
||||
}
|
||||
|
||||
} // namespace aica
|
||||
|
||||
@@ -6,6 +6,9 @@ namespace aica
|
||||
{
|
||||
|
||||
extern RamRegion aica_ram;
|
||||
extern int aica_schid;
|
||||
extern int rtc_schid;
|
||||
extern int dma_sched_id;
|
||||
u32 GetRTC_now();
|
||||
template<typename T> T readRtcReg(u32 addr);
|
||||
template<typename T> void writeRtcReg(u32 addr, T data);
|
||||
|
||||
@@ -41,16 +41,12 @@ struct DSPState
|
||||
|
||||
void deserialize(Deserializer& deser)
|
||||
{
|
||||
deser.skip(4096 * 8, Deserializer::V18); // DynCode
|
||||
deser >> TEMP;
|
||||
deser >> MEMS;
|
||||
deser >> MIXS;
|
||||
deser >> RBP;
|
||||
deser >> RBL;
|
||||
deser.skip(44, Deserializer::V18);
|
||||
deser >> MDEC_CT;
|
||||
deser.skip(33596 - 4096 * 8 - sizeof(TEMP) - sizeof(MEMS) - sizeof(MIXS) - 4 * 3 - 44,
|
||||
Deserializer::V18); // other dsp stuff
|
||||
if (!deser.rollback())
|
||||
dirty = true;
|
||||
}
|
||||
|
||||
+12
-20
@@ -202,12 +202,9 @@ public:
|
||||
{
|
||||
if (deser.version() < Deserializer::V49)
|
||||
{
|
||||
if (deser.version() >= Deserializer::V22)
|
||||
{
|
||||
deser.skip<int>(); // beepOn
|
||||
deser.skip<int>(); // beepPeriod
|
||||
deser.skip<int>(); // beepCounter
|
||||
}
|
||||
deser.skip<int>(); // beepOn
|
||||
deser.skip<int>(); // beepPeriod
|
||||
deser.skip<int>(); // beepCounter
|
||||
init();
|
||||
}
|
||||
else
|
||||
@@ -1099,13 +1096,8 @@ void ChannelEx::StepDecodeSample(u32 CA)
|
||||
case ADPCM_STREAM:
|
||||
{
|
||||
u8 ad1 = readAicaRam<u8>(SA + (CA >> 1));
|
||||
u8 ad2 = readAicaRam<u8>(SA + (next_addr >> 1));
|
||||
|
||||
ad1 >>= (CA & 1) * 4;
|
||||
ad2 >>= (next_addr & 1) * 4;
|
||||
|
||||
ad1 &= 0xF;
|
||||
ad2 &= 0xF;
|
||||
|
||||
s32 q = adpcm.last_quant;
|
||||
if (CA == loop.LSA)
|
||||
@@ -1126,6 +1118,9 @@ void ChannelEx::StepDecodeSample(u32 CA)
|
||||
adpcm.last_quant = q;
|
||||
if constexpr (Last)
|
||||
{
|
||||
u8 ad2 = readAicaRam<u8>(SA + (next_addr >> 1));
|
||||
ad2 >>= (next_addr & 1) * 4;
|
||||
ad2 &= 0xF;
|
||||
SampleType prev = s0;
|
||||
if (PCMS == ADPCM && next_addr == loop.LSA && adpcm.in_loop)
|
||||
{
|
||||
@@ -1721,16 +1716,13 @@ void deserialize(Deserializer& deser)
|
||||
deser >> cdda_sector;
|
||||
deser >> cdda_index;
|
||||
midiSendBuffer.clear();
|
||||
if (deser.version() >= Deserializer::V28)
|
||||
u32 size;
|
||||
deser >> size;
|
||||
for (u32 i = 0; i < size; i++)
|
||||
{
|
||||
u32 size;
|
||||
deser >> size;
|
||||
for (u32 i = 0; i < size; i++)
|
||||
{
|
||||
u8 b;
|
||||
deser >> b;
|
||||
midiSendBuffer.push_back(b);
|
||||
}
|
||||
u8 b;
|
||||
deser >> b;
|
||||
midiSendBuffer.push_back(b);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -28,6 +28,7 @@ using namespace Xbyak::util;
|
||||
#include "arm7_rec.h"
|
||||
#include "oslib/unwind_info.h"
|
||||
#include "oslib/virtmem.h"
|
||||
#include "stdclass.h"
|
||||
|
||||
namespace aica::arm
|
||||
{
|
||||
@@ -45,11 +46,11 @@ static UnwindInfo unwinder;
|
||||
class Arm7Compiler;
|
||||
|
||||
#ifdef _WIN32
|
||||
static const std::array<Xbyak::Reg32, 8> alloc_regs {
|
||||
static constexpr std::array<Xbyak::Reg32, 8> alloc_regs {
|
||||
ebx, ebp, edi, esi, r12d, r13d, r14d, r15d
|
||||
};
|
||||
#else
|
||||
static const std::array<Xbyak::Reg32, 6> alloc_regs {
|
||||
static constexpr std::array<Xbyak::Reg32, 6> alloc_regs {
|
||||
ebx, ebp, r12d, r13d, r14d, r15d
|
||||
};
|
||||
#endif
|
||||
@@ -1003,8 +1004,13 @@ void arm7backend_compile(const std::vector<ArmOp>& block_ops, u32 cycles)
|
||||
virtmem::jit_set_exec(protStart, protSize, false);
|
||||
|
||||
Arm7Compiler assembler;
|
||||
assembler.compile(block_ops, cycles);
|
||||
|
||||
try {
|
||||
assembler.compile(block_ops, cycles);
|
||||
} catch (const Xbyak::Error& e) {
|
||||
virtmem::jit_set_exec(protStart, protSize, true);
|
||||
ERROR_LOG(AICA_ARM, "Xbyak error: %s", e.what());
|
||||
throw FlycastException(strprintf("ARM recompilation error: %s", e.what()));
|
||||
}
|
||||
virtmem::jit_set_exec(protStart, protSize, true);
|
||||
}
|
||||
|
||||
@@ -1016,8 +1022,13 @@ void arm7backend_flush()
|
||||
unwinder.clear();
|
||||
|
||||
Arm7Compiler assembler;
|
||||
assembler.generateMainLoop();
|
||||
|
||||
try {
|
||||
assembler.generateMainLoop();
|
||||
} catch (const Xbyak::Error& e) {
|
||||
virtmem::jit_set_exec(protStart, protSize, true);
|
||||
ERROR_LOG(AICA_ARM, "Xbyak error: %s", e.what());
|
||||
throw FlycastException(strprintf("ARM recompilation error: %s", e.what()));
|
||||
}
|
||||
virtmem::jit_set_exec(protStart, protSize, true);
|
||||
}
|
||||
|
||||
|
||||
+19
-60
@@ -23,48 +23,9 @@
|
||||
#include "serialize.h"
|
||||
#include "hw/sh4/sh4_sched.h"
|
||||
#include "cfg/option.h"
|
||||
#include <atomic>
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
static RTL8139State *rtl8139device;
|
||||
|
||||
// The network backends (picotcp, DCNet) deliver frames from their own
|
||||
// threads, but the RTL8139 model and the SH-4 interrupt controller belong to
|
||||
// the emulation thread: raising the receive interrupt from another thread
|
||||
// races with SIIDRebuild() and can leave stray pending interrupts, which
|
||||
// the guest sees as an unhandled IRQ. Queue the frames here and hand them
|
||||
// to the chip from the SH-4 scheduler.
|
||||
static std::mutex rxMutex;
|
||||
static std::deque<std::vector<u8>> rxQueue;
|
||||
static std::atomic<bool> rxPending;
|
||||
static int rxSchedId = -1;
|
||||
constexpr size_t RX_QUEUE_MAX = 64;
|
||||
constexpr int RX_POLL_CYCLES = 1_sh4ms;
|
||||
|
||||
static int rxDeliver(int tag, int cycles, int jitter, void *arg)
|
||||
{
|
||||
if (rxPending.load(std::memory_order_acquire) && rtl8139device != nullptr)
|
||||
{
|
||||
std::lock_guard<std::mutex> _(rxMutex);
|
||||
while (!rxQueue.empty() && rtl8139_can_receive(rtl8139device))
|
||||
{
|
||||
rtl8139_receive(rtl8139device, rxQueue.front().data(), rxQueue.front().size());
|
||||
rxQueue.pop_front();
|
||||
}
|
||||
rxPending = !rxQueue.empty();
|
||||
}
|
||||
return RX_POLL_CYCLES;
|
||||
}
|
||||
|
||||
static void rxClear()
|
||||
{
|
||||
std::lock_guard<std::mutex> _(rxMutex);
|
||||
rxQueue.clear();
|
||||
rxPending = false;
|
||||
}
|
||||
|
||||
// 1400 - 1600 GAPSPCI bridge registers
|
||||
// 1600 - 1700 standard PCI config
|
||||
// 1700 - 1800 rtl8139c I/O ports
|
||||
@@ -91,6 +52,23 @@ static u8 GAPS_regs[GAPSPCI_REGS_SIZE];
|
||||
static u32 dmaOffset;
|
||||
static bool interruptPending;
|
||||
static constexpr u32 MAX_ETH_FRAME_SIZE = 1514;
|
||||
static int rxSchedId = -1;
|
||||
constexpr int RX_POLL_CYCLES = 5_sh4ms;
|
||||
|
||||
static int rxDeliver(int tag, int cycles, int jitter, void *arg)
|
||||
{
|
||||
u8 frame[MAX_ETH_FRAME_SIZE];
|
||||
while (rtl8139_can_receive(rtl8139device))
|
||||
{
|
||||
const u32 size = net::modbba::sendEthFrame(frame);
|
||||
if (size == 0)
|
||||
break;
|
||||
if (size < 6 || size > MAX_ETH_FRAME_SIZE)
|
||||
continue;
|
||||
rtl8139_receive(rtl8139device, frame, size);
|
||||
}
|
||||
return RX_POLL_CYCLES;
|
||||
}
|
||||
|
||||
static void setInterrupt()
|
||||
{
|
||||
@@ -116,7 +94,6 @@ static void bba_Init()
|
||||
memcpy(&GAPS_regs[0], "GAPSPCI_BRIDGE_2", 0x10);
|
||||
memcpy(&GAPS_regs[0x1c], "SEGA", 4);
|
||||
|
||||
rxClear();
|
||||
rxSchedId = sh4_sched_register(0, rxDeliver);
|
||||
sh4_sched_request(rxSchedId, RX_POLL_CYCLES);
|
||||
}
|
||||
@@ -129,12 +106,8 @@ void bba_Term()
|
||||
rtl8139_destroy(rtl8139device);
|
||||
rtl8139device = nullptr;
|
||||
}
|
||||
if (rxSchedId != -1)
|
||||
{
|
||||
sh4_sched_unregister(rxSchedId);
|
||||
rxSchedId = -1;
|
||||
}
|
||||
rxClear();
|
||||
sh4_sched_unregister(rxSchedId);
|
||||
rxSchedId = -1;
|
||||
}
|
||||
|
||||
void bba_Reset(bool hard)
|
||||
@@ -266,20 +239,6 @@ ssize_t qemu_send_packet(RTL8139State *s, const uint8_t *buf, int size)
|
||||
return size;
|
||||
}
|
||||
|
||||
// Called from the network threads
|
||||
int bba_recv_frame(const u8 *data, u32 len)
|
||||
{
|
||||
if (len < 6 || len > MAX_ETH_FRAME_SIZE)
|
||||
return 0;
|
||||
std::lock_guard<std::mutex> _(rxMutex);
|
||||
if (rxQueue.size() >= RX_QUEUE_MAX)
|
||||
return 0;
|
||||
rxQueue.emplace_back(data, data + len);
|
||||
rxPending.store(true, std::memory_order_release);
|
||||
|
||||
return 1;
|
||||
}
|
||||
|
||||
void pci_dma_read(PCIDevice *dev, dma_addr_t addr, void *buf, dma_addr_t len)
|
||||
{
|
||||
memcpy(buf, &GAPS_ram[addr & GAPSPCI_RAM_MASK], len);
|
||||
|
||||
@@ -25,4 +25,3 @@ u32 bba_ReadMem(u32 addr, u32 sz);
|
||||
void bba_WriteMem(u32 addr, u32 data, u32 sz);
|
||||
void bba_Serialize(Serializer& ser);
|
||||
void bba_Deserialize(Deserializer& deser);
|
||||
int bba_recv_frame(const u8 *data, u32 len);
|
||||
@@ -2491,8 +2491,7 @@ bool rtl8139_deserialize(RTL8139State *s, Deserializer& deser)
|
||||
deser >> s->TimerInt;
|
||||
deser >> s->TCTR_base;
|
||||
|
||||
if (deser.version() >= Deserializer::V38)
|
||||
sh4_sched_deserialize(deser, s->schedId);
|
||||
sh4_sched_deserialize(deser, s->schedId);
|
||||
|
||||
return s->bChipCmdState & CmdRxEnb;
|
||||
}
|
||||
@@ -18,6 +18,7 @@
|
||||
#include "oslib/oslib.h"
|
||||
#include "stdclass.h"
|
||||
#include "oslib/storage.h"
|
||||
#include "oslib/directory.h"
|
||||
|
||||
bool MemChip::Load(const std::string& file)
|
||||
{
|
||||
@@ -40,17 +41,8 @@ void MemChip::Load(const u8 *data, size_t size)
|
||||
memcpy(this->data + write_protect_size, data, this->size - write_protect_size);
|
||||
}
|
||||
|
||||
void WritableChip::Save(const std::string& file)
|
||||
{
|
||||
FILE *f = nowide::fopen(file.c_str(), "wb");
|
||||
if (f == nullptr)
|
||||
{
|
||||
ERROR_LOG(FLASHROM, "Cannot save flash/nvmem to file '%s'", file.c_str());
|
||||
return;
|
||||
}
|
||||
if (std::fwrite(data + write_protect_size, 1, size - write_protect_size, f) != size - write_protect_size)
|
||||
ERROR_LOG(FLASHROM, "Failed or truncated write to flash file '%s'", file.c_str());
|
||||
std::fclose(f);
|
||||
void WritableChip::Save(const std::string& file) {
|
||||
safeFullSave(file, data + write_protect_size, size - write_protect_size);
|
||||
}
|
||||
|
||||
bool MemChip::Load(const std::string &prefix, const std::string &names_ro, const std::string &title)
|
||||
|
||||
@@ -8,7 +8,7 @@
|
||||
*/
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include <string.h>
|
||||
#include <cstring>
|
||||
|
||||
class X76F100SerialFlash
|
||||
{
|
||||
|
||||
+69
-66
@@ -15,7 +15,9 @@
|
||||
#include "imgread/common.h"
|
||||
#include "serialize.h"
|
||||
|
||||
int gdrom_schid;
|
||||
int gdrom_schid = -1;
|
||||
static int statusSchid = -1;
|
||||
static int nextStatus = -1;
|
||||
|
||||
//Sense: ASC - ASCQ - Key
|
||||
int sns_asc;
|
||||
@@ -32,15 +34,6 @@ cdda_t cdda;
|
||||
|
||||
static gd_states gd_state;
|
||||
static DiscType gd_disk_type;
|
||||
/*
|
||||
GD rom reset -> GDS_WAITCMD
|
||||
|
||||
GDS_WAITCMD -> ATA/SPI command [Command code is on ata_command]
|
||||
SPI Command -> GDS_WAITPACKET -> GDS_SPI_* , depending on input
|
||||
|
||||
GDS_SPI_READSECTOR -> Depending on features , it can do quite a few things
|
||||
*/
|
||||
static u32 data_write_mode=0;
|
||||
|
||||
//Registers
|
||||
static u32 DriveSel;
|
||||
@@ -181,6 +174,17 @@ void PioBuffer::deserialize(Deserializer& deser)
|
||||
deser.deserialize(&_data[0], size);
|
||||
}
|
||||
|
||||
static void cddaTransition(int state, u64 duration = 20_sh4ms, int transientState = GD_BUSY)
|
||||
{
|
||||
SecNumber.Status = transientState;
|
||||
nextStatus = state;
|
||||
sh4_sched_request(statusSchid, duration);
|
||||
}
|
||||
|
||||
static void cddaCancelTransition() {
|
||||
sh4_sched_request(statusSchid, -1);
|
||||
}
|
||||
|
||||
static void gd_set_state(gd_states state)
|
||||
{
|
||||
gd_states prev=gd_state;
|
||||
@@ -261,6 +265,9 @@ static void gd_set_state(gd_states state)
|
||||
sector_count = maxSectors;
|
||||
next_state = gds_readsector_pio;
|
||||
}
|
||||
else {
|
||||
cddaTransition(GD_PAUSE, 67_sh4ms, GD_PLAY);
|
||||
}
|
||||
|
||||
u16 *buffer = pio_buff.fill(sector_count * read_params.sector_type);
|
||||
libGDR_ReadSector((u8*)buffer, read_params.start_sector, sector_count, read_params.sector_type);
|
||||
@@ -349,6 +356,7 @@ void gd_setdisc()
|
||||
|
||||
gd_disk_type = newd;
|
||||
SecNumber.DiscFormat = gd_disk_type >> 4;
|
||||
cddaCancelTransition();
|
||||
}
|
||||
|
||||
static void gd_reset()
|
||||
@@ -444,6 +452,7 @@ static void gd_process_ata_cmd()
|
||||
Error.full = 1;
|
||||
sns_key = 0;
|
||||
SecNumber.Status = GD_PAUSE;
|
||||
cddaCancelTransition();
|
||||
IntReason.full = 1;
|
||||
// DC Checker expects these values
|
||||
ByteCount.low = 0x14;
|
||||
@@ -753,7 +762,9 @@ static void gd_process_spi_cmd()
|
||||
{
|
||||
#define readcmd packet_cmd.GDReadBlock
|
||||
|
||||
SecNumber.Status = GD_PLAY;
|
||||
cdda.status = cdda_t::NoInfo;
|
||||
cddaCancelTransition();
|
||||
u32 sector_type = 2048;
|
||||
if (readcmd.head == 1 && readcmd.subh == 1 && readcmd.data == 1 && readcmd.expdtype == 3 && readcmd.other == 0)
|
||||
sector_type = 2340;
|
||||
@@ -915,7 +926,6 @@ static void gd_process_spi_cmd()
|
||||
case SPI_CD_PLAY:
|
||||
{
|
||||
const u32 param_type = packet_cmd.data_8[1] & 7;
|
||||
printf_spicmd("SPI_CD_PLAY param_type=%d", param_type);
|
||||
|
||||
if (param_type == 1 || param_type == 2)
|
||||
{
|
||||
@@ -931,30 +941,24 @@ static void gd_process_spi_cmd()
|
||||
cdda.EndAddr.FAD = ses_inf[3] << 16 | ses_inf[4] << 8 | ses_inf[5];
|
||||
}
|
||||
cdda.repeats = packet_cmd.data_8[6] & 0xF;
|
||||
cdda.status = cdda_t::Playing;
|
||||
SecNumber.Status = GD_PLAY;
|
||||
|
||||
GDStatus.DSC = 1;
|
||||
printf_spicmd("SPI_CD_PLAY %d -> %d repeat %d", cdda.CurrAddr.FAD, cdda.EndAddr.FAD, cdda.repeats);
|
||||
cddaTransition(GD_PLAY, 5_sh4ms, SecNumber.Status);
|
||||
}
|
||||
else if (param_type == 7)
|
||||
{
|
||||
printf_spicmd("SPI_CD_PLAY pause");
|
||||
if (cdda.status == cdda_t::Paused)
|
||||
{
|
||||
// Resume from previous pos unless we're at the end
|
||||
if (cdda.CurrAddr.FAD > cdda.EndAddr.FAD)
|
||||
{
|
||||
cdda.status = cdda_t::Terminated;
|
||||
SecNumber.Status = GD_STANDBY;
|
||||
}
|
||||
cddaTransition(GD_STANDBY);
|
||||
else
|
||||
{
|
||||
cdda.status = cdda_t::Playing;
|
||||
SecNumber.Status = GD_PLAY;
|
||||
}
|
||||
cddaTransition(GD_PLAY);
|
||||
}
|
||||
}
|
||||
else
|
||||
else {
|
||||
die("SPI_CD_PLAY: unknown parameter");
|
||||
}
|
||||
|
||||
DEBUG_LOG(GDROM, "CDDA StartAddr=%d EndAddr=%d repeats=%d status=%d CurrAddr=%d",cdda.StartAddr.FAD,
|
||||
cdda.EndAddr.FAD, cdda.repeats, cdda.status, cdda.CurrAddr.FAD);
|
||||
@@ -968,42 +972,30 @@ static void gd_process_spi_cmd()
|
||||
const u32 param_type = packet_cmd.data_8[1] & 7;
|
||||
printf_spicmd("SPI_CD_SEEK param_type=%d", param_type);
|
||||
|
||||
SecNumber.Status = GD_PAUSE;
|
||||
if (cdda.status == cdda_t::Playing)
|
||||
cdda.status = cdda_t::Paused;
|
||||
|
||||
if (param_type == 1 || param_type == 2)
|
||||
{
|
||||
bool min_sec_frame = param_type == 2;
|
||||
cdda.StartAddr.FAD = cdda.CurrAddr.FAD = GetFAD(&packet_cmd.data_8[2], min_sec_frame);
|
||||
#ifdef STRICT_MODE
|
||||
SecNumber.Status = GD_SEEK;
|
||||
GDStatus.DSC = 0;
|
||||
sh4_sched_request(gdrom_schid, SH4_MAIN_CLOCK / 50); // 20 ms
|
||||
#else
|
||||
GDStatus.DSC = 1;
|
||||
#endif
|
||||
cddaTransition(GD_PAUSE, 20_sh4ms, GD_SEEK);
|
||||
}
|
||||
else if (param_type == 3)
|
||||
{
|
||||
//stop audio , goto home
|
||||
cdda.StartAddr.FAD = cdda.CurrAddr.FAD = 150;
|
||||
cdda.status = cdda_t::NoInfo;
|
||||
#ifdef STRICT_MODE
|
||||
SecNumber.Status = GD_BUSY;
|
||||
GDStatus.DSC = 0;
|
||||
sh4_sched_request(gdrom_schid, SH4_MAIN_CLOCK / 50); // 20 ms
|
||||
#else
|
||||
SecNumber.Status = GD_STANDBY;
|
||||
GDStatus.DSC = 1;
|
||||
#endif
|
||||
cddaTransition(GD_STANDBY, 5_sh4ms);
|
||||
}
|
||||
else if (param_type == 4)
|
||||
{
|
||||
//pause audio -- nothing more
|
||||
SecNumber.Status = GD_PAUSE;
|
||||
cdda.status = cdda_t::Paused;
|
||||
cddaCancelTransition();
|
||||
}
|
||||
else
|
||||
else {
|
||||
die("SPI_CD_SEEK : not known parameter..");
|
||||
}
|
||||
|
||||
DEBUG_LOG(GDROM, "CDDA StartAddr=%d EndAddr=%d repeats=%d status=%d CurrAddr=%d",cdda.StartAddr.FAD,
|
||||
cdda.EndAddr.FAD, cdda.repeats, cdda.status, cdda.CurrAddr.FAD);
|
||||
@@ -1021,7 +1013,7 @@ static void gd_process_spi_cmd()
|
||||
|
||||
case SPI_GET_SCD:
|
||||
{
|
||||
printf_spicmd("SPI_GET_SCD");
|
||||
printf_spicmd("SPI_GET_SCD %d", packet_cmd.data_8[1] & 0xF);
|
||||
|
||||
const u32 format = packet_cmd.data_8[1] & 0xF;
|
||||
const u32 alloc_len = (packet_cmd.data_8[3] << 8) | packet_cmd.data_8[4];
|
||||
@@ -1234,16 +1226,6 @@ static int getGDROMTicks()
|
||||
//is this needed ?
|
||||
static int GDRomschd(int tag, int cycles, int jitter, void *arg)
|
||||
{
|
||||
if (SecNumber.Status == GD_SEEK)
|
||||
{
|
||||
SecNumber.Status = GD_PAUSE;
|
||||
GDStatus.DSC = 1;
|
||||
}
|
||||
else if (SecNumber.Status == GD_BUSY)
|
||||
{
|
||||
SecNumber.Status = GD_STANDBY;
|
||||
GDStatus.DSC = 1;
|
||||
}
|
||||
if (!(SB_GDST & 1) || !(SB_GDEN & 1) || (dma_buff.isEmpty() && read_params.remaining_sectors == 0))
|
||||
return 0;
|
||||
|
||||
@@ -1302,13 +1284,33 @@ static int GDRomschd(int tag, int cycles, int jitter, void *arg)
|
||||
asic_RaiseInterrupt(holly_GDROM_DMA);
|
||||
}
|
||||
// Read ALL sectors and all buffer
|
||||
if (read_params.remaining_sectors == 0 && dma_buff.isEmpty())
|
||||
//verify(!SB_GDST&1) -> dc can do multi read dma
|
||||
if (read_params.remaining_sectors == 0 && dma_buff.isEmpty()) {
|
||||
gd_set_state(gds_procpacketdone);
|
||||
cddaTransition(GD_PAUSE, 67_sh4ms, GD_PLAY);
|
||||
}
|
||||
|
||||
return getGDROMTicks();
|
||||
}
|
||||
|
||||
static int statusSched(int tag, int cycles, int jitter, void *arg)
|
||||
{
|
||||
if (nextStatus != -1)
|
||||
{
|
||||
GDStatus.DSC = 1;
|
||||
int prevCddaStatus = cdda.status;
|
||||
if (nextStatus == GD_PAUSE && cdda.status == cdda_t::Playing)
|
||||
cdda.status = cdda_t::Paused;
|
||||
else if (nextStatus == GD_STANDBY)
|
||||
cdda.status = cdda_t::Terminated;
|
||||
else if (nextStatus == GD_PLAY)
|
||||
cdda.status = cdda_t::Playing;
|
||||
DEBUG_LOG(GDROM, "statusSched: Status %d -> %d, cdda status %d -> %d", SecNumber.Status, nextStatus, prevCddaStatus, cdda.status);
|
||||
SecNumber.Status = nextStatus;
|
||||
nextStatus = -1;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
//DMA Start
|
||||
static void GDROM_DmaStart(u32 addr, u32 data)
|
||||
{
|
||||
@@ -1350,6 +1352,7 @@ static void GDROM_DmaEnable(u32 addr, u32 data)
|
||||
void gdrom_reg_Init()
|
||||
{
|
||||
gdrom_schid = sh4_sched_register(0, &GDRomschd);
|
||||
statusSchid = sh4_sched_register(0, &statusSched);
|
||||
gd_disc_change();
|
||||
}
|
||||
|
||||
@@ -1357,6 +1360,8 @@ void gdrom_reg_Term()
|
||||
{
|
||||
sh4_sched_unregister(gdrom_schid);
|
||||
gdrom_schid = -1;
|
||||
sh4_sched_unregister(statusSchid);
|
||||
statusSchid = -1;
|
||||
}
|
||||
|
||||
void gdrom_reg_Reset(bool hard)
|
||||
@@ -1394,7 +1399,6 @@ void gdrom_reg_Reset(bool hard)
|
||||
cdda = {};
|
||||
gd_disk_type = NoDisk;
|
||||
|
||||
data_write_mode = 0;
|
||||
DriveSel = 0xa0;
|
||||
Error = {};
|
||||
IntReason = {};
|
||||
@@ -1427,7 +1431,6 @@ void serialize(Serializer& ser)
|
||||
ser << cdda;
|
||||
ser << gd_state;
|
||||
ser << gd_disk_type;
|
||||
ser << data_write_mode;
|
||||
ser << DriveSel;
|
||||
ser << Error;
|
||||
|
||||
@@ -1437,6 +1440,8 @@ void serialize(Serializer& ser)
|
||||
ser << SecNumber;
|
||||
ser << GDStatus;
|
||||
ser << ByteCount;
|
||||
sh4_sched_serialize(ser, gdrom_schid);
|
||||
sh4_sched_serialize(ser, statusSchid);
|
||||
}
|
||||
|
||||
void deserialize(Deserializer& deser)
|
||||
@@ -1450,20 +1455,14 @@ void deserialize(Deserializer& deser)
|
||||
deser >> packet_cmd;
|
||||
deser >> set_mode_offset;
|
||||
deser >> read_params;
|
||||
if (deser.version() >= Deserializer::V17) {
|
||||
dma_buff.deserialize(deser);
|
||||
}
|
||||
else {
|
||||
deser >> packet_cmd;
|
||||
dma_buff.clear();
|
||||
}
|
||||
dma_buff.deserialize(deser);
|
||||
pio_buff.deserialize(deser);
|
||||
deser.skip<u32>(Deserializer::V44); // set_mode_offset (repeat)
|
||||
deser >> ata_command;
|
||||
deser >> cdda;
|
||||
deser >> gd_state;
|
||||
deser >> gd_disk_type;
|
||||
deser >> data_write_mode;
|
||||
deser.skip<u32>(Deserializer::V62); // data_write_mode
|
||||
deser >> DriveSel;
|
||||
deser >> Error;
|
||||
|
||||
@@ -1473,6 +1472,10 @@ void deserialize(Deserializer& deser)
|
||||
deser >> SecNumber;
|
||||
deser >> GDStatus;
|
||||
deser >> ByteCount;
|
||||
if (deser.version() >= Deserializer::V62) {
|
||||
sh4_sched_deserialize(deser, gdrom_schid);
|
||||
sh4_sched_deserialize(deser, statusSchid);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
+1
-15
@@ -705,19 +705,5 @@ void sb_serialize(Serializer& ser)
|
||||
void sb_deserialize(Deserializer& deser)
|
||||
{
|
||||
deser >> sb_regs;
|
||||
if (deser.version() < Deserializer::V33)
|
||||
deser >> SB_ISTNRM;
|
||||
if (deser.version() >= Deserializer::V24)
|
||||
deser >> SB_ISTNRM1;
|
||||
else
|
||||
SB_ISTNRM1 = 0;
|
||||
if (deser.version() < Deserializer::V33)
|
||||
{
|
||||
if (deser.version() < Deserializer::V30)
|
||||
{
|
||||
deser.skip<u32>(); // SB_FFST_rc;
|
||||
deser.skip<u32>(); // SB_FFST;
|
||||
}
|
||||
deser >> SB_ADST;
|
||||
}
|
||||
deser >> SB_ISTNRM1;
|
||||
}
|
||||
@@ -7,4 +7,5 @@ target_sources(${PROJECT_NAME} PRIVATE
|
||||
maple_helper.h
|
||||
maple_if.cpp
|
||||
maple_if.h
|
||||
maple_jvs.cpp)
|
||||
maple_jvs.cpp
|
||||
wccf_cam.cpp)
|
||||
+22
-21
@@ -4,6 +4,7 @@
|
||||
#include "hw/naomi/naomi_cart.h"
|
||||
#include "hw/naomi/card_reader.h"
|
||||
#include "hw/sh4/modules/modules.h"
|
||||
#include "hw/sh4/sh4_sched.h"
|
||||
#include "cfg/option.h"
|
||||
#include "stdclass.h"
|
||||
#include "serialize.h"
|
||||
@@ -14,6 +15,7 @@ MapleInputState mapleInputState[4];
|
||||
extern bool maple_ddt_pending_reset;
|
||||
extern std::vector<std::pair<u32, std::vector<u32>>> mapleDmaOut;
|
||||
extern bool SDCKBOccupied;
|
||||
extern int maple_schid;
|
||||
|
||||
void (*MapleConfigMap::UpdateVibration)(u32 port, float power, float inclination, u32 duration_ms);
|
||||
|
||||
@@ -258,6 +260,12 @@ static void createNaomiDevices()
|
||||
insertRfidCard(1);
|
||||
}
|
||||
}
|
||||
else if (gameId.substr(0, 4) == "WCCF")
|
||||
{
|
||||
if (config::MultiboardSlaves <= 1)
|
||||
// CCD cam
|
||||
mcfg_Create(MDT_WccfCamera, 1, 5, 0);
|
||||
}
|
||||
else if (gameId == "THE KING OF ROUTE66")
|
||||
{
|
||||
mcfg_Create(MDT_SegaController, 1, 5);
|
||||
@@ -503,6 +511,8 @@ void mcfg_SerializeDevices(Serializer& ser)
|
||||
{
|
||||
ser << maple_ddt_pending_reset;
|
||||
ser << SDCKBOccupied;
|
||||
sh4_sched_serialize(ser, maple_schid);
|
||||
|
||||
ser << (u32)mapleDmaOut.size();
|
||||
for (const auto& pair : mapleDmaOut)
|
||||
{
|
||||
@@ -527,30 +537,23 @@ void mcfg_DeserializeDevices(Deserializer& deser)
|
||||
{
|
||||
if (!deser.rollback())
|
||||
mcfg_DestroyDevices(false);
|
||||
u8 eeprom[128];
|
||||
if (deser.version() < Deserializer::V23)
|
||||
{
|
||||
deser >> eeprom;
|
||||
deser.skip(128); // Unused eeprom space
|
||||
deser.skip<bool>(); // EEPROM_loaded
|
||||
}
|
||||
deser >> maple_ddt_pending_reset;
|
||||
if (deser.version() >= Deserializer::V47)
|
||||
deser >> SDCKBOccupied;
|
||||
if (deser.version() >= Deserializer::V62)
|
||||
sh4_sched_deserialize(deser, maple_schid);
|
||||
|
||||
mapleDmaOut.clear();
|
||||
if (deser.version() >= Deserializer::V23)
|
||||
u32 size;
|
||||
deser >> size;
|
||||
for (u32 i = 0; i < size; i++)
|
||||
{
|
||||
u32 size;
|
||||
deser >> size;
|
||||
for (u32 i = 0; i < size; i++)
|
||||
{
|
||||
u32 address;
|
||||
deser >> address;
|
||||
u32 dataSize;
|
||||
deser >> dataSize;
|
||||
mapleDmaOut.emplace_back(address, std::vector<u32>(dataSize));
|
||||
deser.deserialize(mapleDmaOut.back().second.data(), dataSize);
|
||||
}
|
||||
u32 address;
|
||||
deser >> address;
|
||||
u32 dataSize;
|
||||
deser >> dataSize;
|
||||
mapleDmaOut.emplace_back(address, std::vector<u32>(dataSize));
|
||||
deser.deserialize(mapleDmaOut.back().second.data(), dataSize);
|
||||
}
|
||||
|
||||
for (int i = 0; i < MAPLE_PORTS; i++)
|
||||
@@ -565,8 +568,6 @@ void mcfg_DeserializeDevices(Deserializer& deser)
|
||||
MapleDevices[i][j]->deserialize(deser);
|
||||
}
|
||||
}
|
||||
if (deser.version() < Deserializer::V23 && EEPROM != nullptr)
|
||||
memcpy(EEPROM, eeprom, sizeof(eeprom));
|
||||
}
|
||||
|
||||
std::shared_ptr<MIE> getMieDevice()
|
||||
|
||||
@@ -25,6 +25,7 @@ enum MapleDeviceType
|
||||
MDT_Dreameye = 17,
|
||||
MDT_SegaControllerXL = 18,
|
||||
MDT_DreamParaParaController = 19,
|
||||
MDT_WccfCamera = 20,
|
||||
MDT_Count
|
||||
};
|
||||
|
||||
@@ -48,6 +49,11 @@ enum PlainJoystickTriggerId
|
||||
PJTI_Count = 4
|
||||
};
|
||||
|
||||
enum class MaplePortType {
|
||||
user_accessible,
|
||||
built_in,
|
||||
};
|
||||
|
||||
struct PlainJoystickState
|
||||
{
|
||||
PlainJoystickState()
|
||||
@@ -118,10 +124,12 @@ void mcfg_DestroyDevices(bool full = true);
|
||||
void mcfg_SerializeDevices(Serializer& ser);
|
||||
void mcfg_DeserializeDevices(Deserializer& deser);
|
||||
|
||||
constexpr int maple_getPortCount(MapleDeviceType type)
|
||||
constexpr int maple_getPortCount(MapleDeviceType type, MaplePortType port)
|
||||
{
|
||||
switch (type)
|
||||
{
|
||||
case MDT_FishingController:
|
||||
return port == MaplePortType::user_accessible ? 0 : 1;
|
||||
case MDT_SegaController:
|
||||
case MDT_SegaControllerXL:
|
||||
return 2;
|
||||
|
||||
@@ -841,7 +841,6 @@ struct maple_microphone: maple_base
|
||||
deser >> gain;
|
||||
deser >> sampling;
|
||||
deser >> eight_khz;
|
||||
deser.skip(480 - sizeof(u32) - sizeof(bool) * 2, Deserializer::V23);
|
||||
if (sampling)
|
||||
StartAudioRecording(eight_khz);
|
||||
}
|
||||
@@ -1888,6 +1887,7 @@ std::shared_ptr<maple_device> maple_Create(MapleDeviceType type)
|
||||
case MDT_SegaControllerXL: return std::make_shared<FullController>();
|
||||
case MDT_DreamParaParaController: return std::make_shared<maple_dreamparapara_controller>();
|
||||
case MDT_RFIDReaderWriter: return RFIDReaderWriter::Create();
|
||||
case MDT_WccfCamera: return WccfCamera::Create();
|
||||
|
||||
default:
|
||||
ERROR_LOG(MAPLE, "Invalid device type %d", type);
|
||||
|
||||
@@ -2,13 +2,12 @@
|
||||
#include "types.h"
|
||||
#include "maple_cfg.h"
|
||||
#include "maple_helper.h"
|
||||
#include <cmath>
|
||||
#include "input/gamepad.h"
|
||||
#include "serialize.h"
|
||||
#include "hw/hwreg.h"
|
||||
|
||||
#include <cmath>
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
enum MapleFunctionID
|
||||
{
|
||||
@@ -204,8 +203,6 @@ void limit_joystick_magnitude(s8& joyx, s8& joyy)
|
||||
}
|
||||
}
|
||||
|
||||
extern u8 *EEPROM;
|
||||
|
||||
#define SWAP32(a) ((((a) & 0xff) << 24) | (((a) & 0xff00) << 8) | (((a) >> 8) & 0xff00) | (((a) >> 24) & 0xff))
|
||||
|
||||
const char *GetCurrentGameButtonName(DreamcastKey key);
|
||||
@@ -305,6 +302,8 @@ struct BaseMIE : public maple_base
|
||||
u32 dma(u32 cmd) override;
|
||||
void reply(u8 code, u8 sizew = 0);
|
||||
|
||||
virtual u8 getExtDeviceMap() const { return maple_GetAttachedDevices(bus_id); }
|
||||
|
||||
virtual void handle_86_subcommand();
|
||||
virtual void firmwareLoaded(u32 hash) {}
|
||||
};
|
||||
@@ -318,3 +317,8 @@ struct RFIDReaderWriter : public BaseMIE
|
||||
{
|
||||
static std::shared_ptr<maple_device> Create();
|
||||
};
|
||||
|
||||
struct WccfCamera : public BaseMIE
|
||||
{
|
||||
static std::shared_ptr<maple_device> Create();
|
||||
};
|
||||
@@ -21,7 +21,7 @@ enum MaplePattern
|
||||
|
||||
std::shared_ptr<maple_device> MapleDevices[MAPLE_PORTS][6];
|
||||
|
||||
int maple_schid;
|
||||
int maple_schid = -1;
|
||||
|
||||
/*
|
||||
Maple host controller
|
||||
@@ -219,7 +219,7 @@ static void maple_DoDma()
|
||||
// and send everything to the main device.
|
||||
auto pDevice = MapleDevices[bus][5];
|
||||
if (pDevice != nullptr
|
||||
&& maple_getPortCount(pDevice->get_device_type()) != 0)
|
||||
&& maple_getPortCount(pDevice->get_device_type(), MaplePortType::built_in) != 0)
|
||||
{
|
||||
port = getPort(reci);
|
||||
if (port != 5)
|
||||
|
||||
+88
-45
@@ -22,11 +22,15 @@
|
||||
#include <xxhash.h>
|
||||
#include "oslib/oslib.h"
|
||||
#include "oslib/i18n.h"
|
||||
#include "oslib/directory.h"
|
||||
#include "stdclass.h"
|
||||
#include "cfg/option.h"
|
||||
#include "network/output.h"
|
||||
#include "hw/naomi/printer.h"
|
||||
#include "hw/naomi/card_reader.h"
|
||||
#include "hw/sh4/sh4_sched.h"
|
||||
#include "input/haptic.h"
|
||||
#include "hw/sh4/sh4_mem.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
@@ -635,10 +639,7 @@ public:
|
||||
}
|
||||
void deserialize(Deserializer& deser) override
|
||||
{
|
||||
if (deser.version() >= Deserializer::V31)
|
||||
deser >> out;
|
||||
else
|
||||
out = 0xff;
|
||||
deser >> out;
|
||||
jvs_837_13844::deserialize(deser);
|
||||
}
|
||||
|
||||
@@ -756,10 +757,7 @@ public:
|
||||
}
|
||||
void deserialize(Deserializer& deser) override
|
||||
{
|
||||
if (deser.version() >= Deserializer::V31)
|
||||
deser >> testMode;
|
||||
else
|
||||
testMode = false;
|
||||
deser >> testMode;
|
||||
if (deser.version() >= Deserializer::V51)
|
||||
{
|
||||
deser >> damper_high;
|
||||
@@ -942,8 +940,7 @@ public:
|
||||
}
|
||||
void deserialize(Deserializer& deser) override
|
||||
{
|
||||
if (deser.version() >= Deserializer::V31)
|
||||
deser >> gear;
|
||||
deser >> gear;
|
||||
jvs_837_13844_racing::deserialize(deser);
|
||||
}
|
||||
|
||||
@@ -1022,6 +1019,69 @@ public:
|
||||
}
|
||||
};
|
||||
|
||||
class jvs_837_13844_wccf : public jvs_837_13844
|
||||
{
|
||||
public:
|
||||
jvs_837_13844_wccf(u8 node_id, MIEImpl *parent, int first_player = 0)
|
||||
: jvs_837_13844(node_id, parent, first_player)
|
||||
{}
|
||||
|
||||
void write_digital_out(int count, const u8 *data) override
|
||||
{
|
||||
jvs_837_13844::write_digital_out(count, data);
|
||||
if (count >= 3)
|
||||
{
|
||||
if (data[2] & 0x80) {
|
||||
// solenoid is on
|
||||
solenoidOn = true;
|
||||
solenoidWait = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
// solenoid is off
|
||||
if (solenoidOn)
|
||||
{
|
||||
// wait 30 frames before ejecting the card
|
||||
if (++solenoidWait >= 30) {
|
||||
card_reader::ejectCard();
|
||||
solenoidOn = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
if (data[0] & 1)
|
||||
{
|
||||
// Player card dispenser
|
||||
if (cardDispenser == 0)
|
||||
NOTICE_LOG(JVS, "WCCF player card dispensed");
|
||||
cardDispenser = sh4_sched_now64();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void read_digital_in(const u32 *buttons, u32 *v) override
|
||||
{
|
||||
jvs_837_13844::read_digital_in(buttons, v);
|
||||
v[1] |= NAOMI_RIGHT_KEY | NAOMI_BTN1_KEY; // card dispenser sensor off, standby on
|
||||
if (cardDispenser != 0)
|
||||
{
|
||||
// idle: wait for owed cards > 0 && standby=on
|
||||
// payout: assert card vendor payout
|
||||
// payoutwait: wait for !sensor on, decrement owed cards
|
||||
// recvwait: wait for !sensor off -> idle
|
||||
u64 now = sh4_sched_now64();
|
||||
if (now - cardDispenser > 1000_sh4ms)
|
||||
cardDispenser = 0;
|
||||
else
|
||||
v[1] &= ~(NAOMI_RIGHT_KEY | NAOMI_BTN1_KEY); // card dispenser sensor on, standby off
|
||||
}
|
||||
}
|
||||
|
||||
private:
|
||||
bool solenoidOn = false;
|
||||
int solenoidWait = 0;
|
||||
u64 cardDispenser = 0;
|
||||
};
|
||||
|
||||
// Ninja assault
|
||||
class jvs_namco_jyu : public jvs_io_board
|
||||
{
|
||||
@@ -1309,8 +1369,11 @@ u32 BaseMIE::RawDma(const u32 *buffer_in, u32 buffer_in_len, u32 *buffer_out)
|
||||
u32 resp = dma(cmd);
|
||||
if (resp != MDRE_UnknownCmd)
|
||||
{
|
||||
const u32 reci = (buffer_in[0] >> 8) & 0xFF;
|
||||
u32 reci = (buffer_in[0] >> 8) & 0xFF;
|
||||
const u32 sender = (buffer_in[0] >> 16) & 0xFF;
|
||||
if (reci & 0x20)
|
||||
reci |= getExtDeviceMap();
|
||||
|
||||
buffer_out[0] = (resp << 0 ) | (sender << 8) | (reci << 16) | ((out_len / 4) << 24);
|
||||
return out_len + 4;
|
||||
}
|
||||
@@ -1383,7 +1446,7 @@ u32 BaseMIE::RawDma(const u32 *buffer_in, u32 buffer_in_len, u32 *buffer_out)
|
||||
|
||||
case MDC_JVSGetId:
|
||||
{
|
||||
DEBUG_LOG(JVS, "bus[%d] JVS Get Id", bus_id);
|
||||
LOGJVS("bus[%d] JVS Get Id", bus_id);
|
||||
static const char ID[56] = "315-6149 COPYRIGHT SEGA ENTERPRISES CO,LTD. 1998";
|
||||
reply(MDRS_JVSGetIdReply, 7);
|
||||
wptr(ID, 28);
|
||||
@@ -1395,7 +1458,7 @@ u32 BaseMIE::RawDma(const u32 *buffer_in, u32 buffer_in_len, u32 *buffer_out)
|
||||
break;
|
||||
|
||||
default:
|
||||
INFO_LOG(MAPLE, "BaseMIE: Unknown Maple command %x", cmd);
|
||||
INFO_LOG(MAPLE, "bus[%d] BaseMIE: Unknown Maple command %x", bus_id, cmd);
|
||||
reply(MDRE_UnknownCmd);
|
||||
break;;
|
||||
}
|
||||
@@ -1598,6 +1661,10 @@ MIEImpl::MIEImpl()
|
||||
{
|
||||
io_boards.push_back(std::make_unique<jvs_837_13844_samba>(1, this));
|
||||
}
|
||||
else if (gameId.substr(0, 4) == "WCCF" && config::MultiboardSlaves <= 1)
|
||||
{
|
||||
io_boards.push_back(std::make_unique<jvs_837_13844_wccf>(1, this));
|
||||
}
|
||||
else
|
||||
{
|
||||
if (gameId == "POKASUKA GHOST (JAPANESE)" // Manic Panic Ghosts
|
||||
@@ -1894,18 +1961,7 @@ void MIEImpl::handle_86_subcommand()
|
||||
|
||||
std::string eeprom_file = getEepromPath();
|
||||
if (!eeprom_file.empty())
|
||||
{
|
||||
FILE* f = nowide::fopen(eeprom_file.c_str(), "wb");
|
||||
if (f)
|
||||
{
|
||||
std::fwrite(eeprom, 1, sizeof(eeprom), f);
|
||||
std::fclose(f);
|
||||
INFO_LOG(MAPLE, "Saved EEPROM to %s", eeprom_file.c_str());
|
||||
}
|
||||
else {
|
||||
WARN_LOG(MAPLE, "EEPROM SAVE FAILED to %s", eeprom_file.c_str());
|
||||
}
|
||||
}
|
||||
safeFullSave(eeprom_file, eeprom, sizeof(eeprom));
|
||||
|
||||
reply(MDRS_JVSReply, 1);
|
||||
memcpy(dma_buffer_out, eeprom, 4);
|
||||
@@ -2038,18 +2094,13 @@ void MIEImpl::deserialize(Deserializer& deser)
|
||||
{
|
||||
maple_base::deserialize(deser);
|
||||
deser >> crazy_mode;
|
||||
if (deser.version() >= Deserializer::V35)
|
||||
deser >> hotd2p;
|
||||
else
|
||||
hotd2p = settings.content.gameId == "hotd2p";
|
||||
deser >> hotd2p;
|
||||
deser >> jvs_repeat_request;
|
||||
deser >> jvs_receive_length;
|
||||
deser >> jvs_receive_buffer;
|
||||
if (deser.version() >= Deserializer::V23)
|
||||
deser >> eeprom;
|
||||
deser >> eeprom;
|
||||
u32 board_count;
|
||||
deser >> board_count;
|
||||
deser.skip(sizeof(size_t) - sizeof(u32), Deserializer::V23);
|
||||
for (u32 i = 0; i < board_count; i++)
|
||||
io_boards[i]->deserialize(deser);
|
||||
}
|
||||
@@ -2638,7 +2689,7 @@ u32 RFIDReaderWriterImpl::dma(u32 cmd)
|
||||
return (MapleDeviceRV)0xfe;
|
||||
|
||||
case 0xA1: // read card data
|
||||
DEBUG_LOG(JVS, "RFID card read (data? %d)", d4Seen);
|
||||
LOGJVS("RFID card read (data? %d)", d4Seen);
|
||||
w32(getStatus());
|
||||
if (!d4Seen)
|
||||
// serial0 and serial1 only
|
||||
@@ -2667,7 +2718,7 @@ u32 RFIDReaderWriterImpl::dma(u32 cmd)
|
||||
u32 offset = r8() * 4;
|
||||
size_t size = r8() * 4;
|
||||
skip(2);
|
||||
DEBUG_LOG(JVS, "RFID card write: offset 0x%x len %d", offset, (int)size);
|
||||
LOGJVS("RFID card write: offset 0x%x len %d", offset, (int)size);
|
||||
rptr(cardData + offset, std::min(size, sizeof(cardData) - offset));
|
||||
saveCard();
|
||||
return (MapleDeviceRV)0xfe;
|
||||
@@ -2694,7 +2745,7 @@ u32 RFIDReaderWriterImpl::dma(u32 cmd)
|
||||
counter = 0;
|
||||
break;
|
||||
}
|
||||
DEBUG_LOG(JVS, "RFID decrement %d", counter);
|
||||
LOGJVS("RFID decrement %d", counter);
|
||||
cardData[19 - counter]--;
|
||||
saveCard();
|
||||
w32(getStatus());
|
||||
@@ -2802,17 +2853,9 @@ void RFIDReaderWriterImpl::saveCard() const
|
||||
{
|
||||
if (transientData)
|
||||
return;
|
||||
std::string path = getCardPath();
|
||||
FILE *fp = nowide::fopen(path.c_str(), "wb");
|
||||
if (fp == nullptr)
|
||||
{
|
||||
WARN_LOG(NAOMI, "Can't create card file %s: errno %d", path.c_str(), errno);
|
||||
return;
|
||||
}
|
||||
const std::string path = getCardPath();
|
||||
INFO_LOG(NAOMI, "Saving card file to %s", path.c_str());
|
||||
if (fwrite(cardData, 1, sizeof(cardData), fp) != sizeof(cardData))
|
||||
WARN_LOG(NAOMI, "Truncated write to file: %s", path.c_str());
|
||||
fclose(fp);
|
||||
safeFullSave(path, cardData, sizeof(cardData));
|
||||
}
|
||||
|
||||
void RFIDReaderWriterImpl::serialize(Serializer& ser) const
|
||||
|
||||
File diff suppressed because it is too large.
Load diff
+177
-36
@@ -59,7 +59,7 @@ static u8 dspram[0x1000];
|
||||
static_assert(sizeof(regs_write_mask) == sizeof(modem_regs));
|
||||
static_assert(sizeof(por_dspram) == sizeof(dspram));
|
||||
|
||||
static int modem_sched;
|
||||
static int modem_sched = -1;
|
||||
|
||||
enum ModemStates
|
||||
{
|
||||
@@ -82,13 +82,24 @@ enum ConnectState
|
||||
HANDSHAKING,
|
||||
PRE_CONNECTED,
|
||||
CONNECTED,
|
||||
NEGO_COMPLETE
|
||||
NEGO_COMPLETE,
|
||||
RINGING_IN,
|
||||
};
|
||||
static ConnectState connect_state = DISCONNECTED;
|
||||
static float txFifoSize = 0.f;
|
||||
static std::deque<u8> rxFifo;
|
||||
static int cyclesPerByte;
|
||||
static float txRxRatio = 1.f;
|
||||
static std::string numberDialed;
|
||||
static std::function<void()> onAnswerCallback;
|
||||
static std::function<void()> onHandshakeCallback;
|
||||
static std::function<void(const std::string&)> onDialCallback;
|
||||
static std::function<void()> periodicCallback;
|
||||
static u32 periodicCycles;
|
||||
static u64 lastDSPRamWrite;
|
||||
static u64 ringStart;
|
||||
static bool peer2peerRawMode;
|
||||
static bool keepRinging;
|
||||
|
||||
static void schedule_callback(int ms);
|
||||
static const char *getDSPRamLabel(u32 addr);
|
||||
@@ -166,6 +177,7 @@ static InStream *curInput = &netIn;
|
||||
static OutStream *curOutput = &netOut;
|
||||
|
||||
static V42Protocol v42Proto { netIn, netOut };
|
||||
static HdlcSyncAsync hdlcProto { netIn, netOut };
|
||||
|
||||
static bool v8bis;
|
||||
static V8bisProtocol v8bisProto;
|
||||
@@ -196,9 +208,16 @@ static void configureStreams()
|
||||
curInput = &v8bisProto;
|
||||
curOutput = &v8bisProto;
|
||||
}
|
||||
else if (modem_regs.reg08.ASYN == 0) {
|
||||
curInput = &v42Proto;
|
||||
curOutput = &v42Proto;
|
||||
else if (modem_regs.reg08.ASYN == 0)
|
||||
{
|
||||
if (peer2peerRawMode) {
|
||||
curInput = &hdlcProto;
|
||||
curOutput = &hdlcProto;
|
||||
}
|
||||
else {
|
||||
curInput = &v42Proto;
|
||||
curOutput = &v42Proto;
|
||||
}
|
||||
}
|
||||
else {
|
||||
curInput = &netIn;
|
||||
@@ -334,6 +353,16 @@ static bool setModemSpeedFromCONF()
|
||||
return speed != 0;
|
||||
}
|
||||
|
||||
static void hangUp()
|
||||
{
|
||||
INFO_LOG(MODEM, "Hanging up modem");
|
||||
modem_regs.ABCODE = 0x96;
|
||||
modem_regs.reg1f.NEWS = 1;
|
||||
modem_regs.CONF = 0xC0;
|
||||
dspram[0x2e4] = 0;
|
||||
connect_state = DISCONNECTED;
|
||||
}
|
||||
|
||||
static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
|
||||
{
|
||||
#ifndef NDEBUG
|
||||
@@ -362,6 +391,11 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
|
||||
break;
|
||||
case MS_END_DSP:
|
||||
DSPTestEnd();
|
||||
if (ringStart != 0) {
|
||||
// The game resets the modem before answering a call so restore the correct state
|
||||
connect_state = RINGING;
|
||||
ringStart = 0;
|
||||
}
|
||||
break;
|
||||
case MS_NORMAL:
|
||||
modem_regs.reg1f.NEWC = 0; // Not needed when state is CONNECTED but who cares
|
||||
@@ -369,18 +403,24 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
|
||||
switch (connect_state)
|
||||
{
|
||||
case DIALING:
|
||||
if (last_dial_time != 0 && sh4_sched_now64() - last_dial_time >= SH4_MAIN_CLOCK)
|
||||
if (last_dial_time == 0)
|
||||
last_dial_time = sh4_sched_now64();
|
||||
if (sh4_sched_now64() - last_dial_time >= SH4_MAIN_CLOCK)
|
||||
{
|
||||
LOG("Switching to RINGING state");
|
||||
connect_state = RINGING;
|
||||
schedule_callback(100);
|
||||
if (!numberDialed.empty())
|
||||
{
|
||||
NOTICE_LOG(MODEM, "Dialing %s", numberDialed.c_str());
|
||||
if (onDialCallback)
|
||||
onDialCallback(numberDialed);
|
||||
numberDialed.clear();
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
last_dial_time = sh4_sched_now64();
|
||||
|
||||
else {
|
||||
modem_regs.reg1e.TDBE = 1;
|
||||
schedule_callback(1000); // To switch to Ringing state
|
||||
callback_cycles = 1000_sh4ms; // To switch to Ringing state
|
||||
}
|
||||
break;
|
||||
case RINGING:
|
||||
@@ -388,11 +428,8 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
|
||||
last_dial_time = 0;
|
||||
LOG("\t\t *** %s STATE ***", connect_state == RINGING ? "RINGING" : "NEGO COMPLETE");
|
||||
modem_regs.reg1f.NEWS = 1;
|
||||
if (!modem_regs.reg09.DATA && connect_state == RINGING)
|
||||
{
|
||||
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RI, 1);
|
||||
if (!modem_regs.reg09.DATA && connect_state == RINGING && modem_regs.reg09.ORG)
|
||||
SET_STATUS_BIT(0x0b, modem_regs.reg0b.ATV25, 1);
|
||||
}
|
||||
break;
|
||||
case HANDSHAKING:
|
||||
LOG("\t\t *** HANDSHAKING STATE ***");
|
||||
@@ -410,8 +447,7 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
|
||||
break;
|
||||
|
||||
case PRE_CONNECTED:
|
||||
if (modem_regs.reg03.RLSDE)
|
||||
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RLSD, 1);
|
||||
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RLSD, 1);
|
||||
bool validConfig;
|
||||
if (modem_regs.CONF == 0xAA)
|
||||
{
|
||||
@@ -493,8 +529,11 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
|
||||
dspram[0x208] = 0xff; // 2.4 - 19.2 kpbs supported
|
||||
dspram[0x209] = 0xbf; // 21.6 - 33.6 kpbs supported, asymmetric supported
|
||||
|
||||
callback_cycles = SH4_MAIN_CLOCK / 1000000 * 238; // 238 us
|
||||
callback_cycles = 238_sh4us;
|
||||
v42Proto.reset();
|
||||
v42Proto.setOriginator(modem_regs.reg09.ORG);
|
||||
hdlcProto.reset();
|
||||
hdlcProto.setOriginator(modem_regs.reg09.ORG);
|
||||
|
||||
break;
|
||||
|
||||
@@ -519,14 +558,46 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
|
||||
}
|
||||
if (rxFifo.size() < 16)
|
||||
{
|
||||
int c = curInput->read();
|
||||
if (c >= 0) {
|
||||
rxFifo.push_back(c);
|
||||
updateRxFifoStatus();
|
||||
try {
|
||||
int c = curInput->read();
|
||||
if (c >= 0) {
|
||||
rxFifo.push_back(c);
|
||||
updateRxFifoStatus();
|
||||
}
|
||||
} catch (const net::modbba::ConnectionClosed& e) {
|
||||
hangUp();
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case DISCONNECTED:
|
||||
if (onAnswerCallback)
|
||||
{
|
||||
callback_cycles = SH4_MAIN_CLOCK;
|
||||
if (lastDSPRamWrite != 0
|
||||
&& sh4_sched_now64() - lastDSPRamWrite >= (u64)5 * SH4_MAIN_CLOCK)
|
||||
{
|
||||
INFO_LOG(MODEM, "Answer mode detected");
|
||||
onAnswerCallback();
|
||||
lastDSPRamWrite = 0;
|
||||
callback_cycles = 0;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case RINGING_IN:
|
||||
{
|
||||
// Japan standard: 1 s on, 2 s off
|
||||
u64 v = (sh4_sched_now64() - ringStart) % ((u64)3 * SH4_MAIN_CLOCK);
|
||||
bool ri = v < SH4_MAIN_CLOCK;
|
||||
if (ri != modem_regs.reg0f.RI) {
|
||||
modem_regs.reg1f.NEWS = 1;
|
||||
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RI, ri);
|
||||
}
|
||||
callback_cycles = SH4_MAIN_CLOCK;
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
@@ -537,6 +608,13 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
|
||||
}
|
||||
update_interrupt();
|
||||
|
||||
if (periodicCallback)
|
||||
{
|
||||
periodicCallback();
|
||||
if (callback_cycles == 0 || callback_cycles > (int)periodicCycles)
|
||||
callback_cycles = periodicCycles;
|
||||
}
|
||||
|
||||
return callback_cycles;
|
||||
}
|
||||
|
||||
@@ -548,6 +626,15 @@ void ModemInit()
|
||||
void ModemReset()
|
||||
{
|
||||
net::modbba::stop();
|
||||
onAnswerCallback = {};
|
||||
onHandshakeCallback = {};
|
||||
onDialCallback = {};
|
||||
periodicCallback = {};
|
||||
periodicCycles = 0;
|
||||
lastDSPRamWrite = 0;
|
||||
ringStart = 0;
|
||||
peer2peerRawMode = false;;
|
||||
keepRinging = false;
|
||||
}
|
||||
|
||||
void ModemTerm()
|
||||
@@ -660,6 +747,8 @@ static void modem_reset(u32 v)
|
||||
state = MS_RESET;
|
||||
LOG("Modem reset start ...");
|
||||
net::modbba::stop();
|
||||
lastDSPRamWrite = 0;
|
||||
keepRinging = false;
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -678,11 +767,14 @@ static void check_start_handshake()
|
||||
{
|
||||
if (modem_regs.reg09.DTR
|
||||
&& (modem_regs.reg09.DATA || modem_regs.reg15.AUTO)
|
||||
&& (connect_state == RINGING || connect_state == NEGO_COMPLETE))
|
||||
&& (connect_state == RINGING || connect_state == NEGO_COMPLETE)
|
||||
&& !keepRinging)
|
||||
{
|
||||
LOG("DTR asserted. starting handshaking");
|
||||
connect_state = HANDSHAKING;
|
||||
schedule_callback(1);
|
||||
if (onHandshakeCallback)
|
||||
onHandshakeCallback();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -748,7 +840,7 @@ static void ModemNormalWrite(u32 reg, u32 data)
|
||||
{
|
||||
download_crc = (download_crc << 1) + ((download_crc & 0x80) >> 7) + (data & 0xFF);
|
||||
}
|
||||
else if (connect_state == DISCONNECTED || connect_state == DIALING)
|
||||
else if ((connect_state == DISCONNECTED || connect_state == DIALING) && !modem_regs.reg09.DATA)
|
||||
{
|
||||
//LOG("ModemNormalWrite : TBUFFER = %X", data);
|
||||
if (connect_state == DISCONNECTED)
|
||||
@@ -756,7 +848,10 @@ static void ModemNormalWrite(u32 reg, u32 data)
|
||||
INFO_LOG(MODEM, "MODEM Dialing");
|
||||
connect_state = DIALING;
|
||||
modem_regs.SECRXB = 0;
|
||||
numberDialed.clear();
|
||||
}
|
||||
if (data <= 9)
|
||||
numberDialed += '0' + data;
|
||||
schedule_callback(100);
|
||||
}
|
||||
else if (connect_state == CONNECTED && modem_regs.reg08.RTS)
|
||||
@@ -765,7 +860,12 @@ static void ModemNormalWrite(u32 reg, u32 data)
|
||||
#ifndef NDEBUG
|
||||
sent_bytes++;
|
||||
#endif
|
||||
curOutput->write(data);
|
||||
try {
|
||||
curOutput->write(data);
|
||||
} catch (const net::modbba::ConnectionClosed& e) {
|
||||
hangUp();
|
||||
break;
|
||||
}
|
||||
|
||||
modem_regs.reg1e.TDBE = 0;
|
||||
txFifoSize += 1.f;
|
||||
@@ -860,6 +960,10 @@ static void ModemNormalWrite(u32 reg, u32 data)
|
||||
}
|
||||
if ((dspram_addr == 0x26B || dspram_addr == 0x26c) && (modem_regs.MEDA & 0xFF) != 0)
|
||||
dspram[0x26F] = 1; // Saved Filtered EQM
|
||||
if (connect_state == DISCONNECTED && onAnswerCallback) {
|
||||
lastDSPRamWrite = sh4_sched_now64();
|
||||
schedule_callback(1000);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
@@ -1048,19 +1152,17 @@ void ModemSerialize(Serializer& ser)
|
||||
}
|
||||
void ModemDeserialize(Deserializer& deser)
|
||||
{
|
||||
if (!config::EmulateBBA || deser.version() > Deserializer::V31)
|
||||
sh4_sched_deserialize(deser, modem_sched);
|
||||
if (deser.version() >= Deserializer::V20)
|
||||
{
|
||||
deser >> modem_regs;
|
||||
deser >> dspram;
|
||||
deser >> state;
|
||||
deser >> connect_state;
|
||||
deser >> last_dial_time;
|
||||
deser.skip<bool>(Deserializer::V58);
|
||||
}
|
||||
sh4_sched_deserialize(deser, modem_sched);
|
||||
deser >> modem_regs;
|
||||
deser >> dspram;
|
||||
deser >> state;
|
||||
deser >> connect_state;
|
||||
deser >> last_dial_time;
|
||||
deser.skip<bool>(Deserializer::V58);
|
||||
|
||||
rxFifo.clear();
|
||||
v42Proto.reset();
|
||||
hdlcProto.reset();
|
||||
v8bis = false;
|
||||
v8bisProto.reset();
|
||||
configureStreams();
|
||||
@@ -1211,3 +1313,42 @@ static const char *getDSPRamLabel(u32 addr)
|
||||
else
|
||||
return it->second;
|
||||
}
|
||||
|
||||
void modemOnAnswerMode(std::function<void()> callback) {
|
||||
onAnswerCallback = callback;
|
||||
}
|
||||
void modemOnDial(std::function<void(const std::string&)> callback) {
|
||||
onDialCallback = callback;
|
||||
}
|
||||
void modemOnHandshake(std::function<void()> callback) {
|
||||
onHandshakeCallback = callback;
|
||||
}
|
||||
|
||||
void modemPeriodicCallback(u32 cycles, std::function<void()> callback)
|
||||
{
|
||||
periodicCallback = callback;
|
||||
periodicCycles = cycles;
|
||||
if (cycles != 0 && !sh4_sched_is_scheduled(modem_sched))
|
||||
sh4_sched_request(modem_sched, cycles);
|
||||
}
|
||||
|
||||
void modemPeer2Peer(bool enabled) {
|
||||
peer2peerRawMode = enabled;
|
||||
configureStreams();
|
||||
}
|
||||
|
||||
void modemKeepRinging(bool enabled)
|
||||
{
|
||||
keepRinging = enabled;
|
||||
if (!enabled)
|
||||
check_start_handshake();
|
||||
}
|
||||
|
||||
void modemIncomingCall()
|
||||
{
|
||||
connect_state = RINGING_IN;
|
||||
modem_regs.reg1f.NEWS = 1;
|
||||
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RI, 1);
|
||||
ringStart = sh4_sched_now64();
|
||||
schedule_callback(1000);
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include <functional>
|
||||
|
||||
void ModemInit();
|
||||
void ModemReset();
|
||||
@@ -31,3 +32,10 @@ u32 ModemReadMem_A0_006(u32 addr,u32 size);
|
||||
void ModemWriteMem_A0_006(u32 addr,u32 data,u32 size);
|
||||
void ModemSerialize(Serializer& ser);
|
||||
void ModemDeserialize(Deserializer& deser);
|
||||
void modemOnAnswerMode(std::function<void()> callback);
|
||||
void modemOnHandshake(std::function<void()> callback);
|
||||
void modemOnDial(std::function<void(const std::string&)> callback);
|
||||
void modemPeer2Peer(bool enabled);
|
||||
void modemKeepRinging(bool enabled);
|
||||
void modemIncomingCall();
|
||||
void modemPeriodicCallback(u32 cycles, std::function<void()> callback);
|
||||
+369
-47
@@ -151,7 +151,7 @@ static u16 calcCrc16(const std::vector<u8>& data, size_t len = 0)
|
||||
return crc ^ 0xffff;
|
||||
}
|
||||
|
||||
void HdlcEncoder::sendFrame(const std::vector<u8>& data)
|
||||
void HdlcEncoder::sendFrame(const std::vector<u8>& data, int crc)
|
||||
{
|
||||
// opening flag
|
||||
sendFlag();
|
||||
@@ -159,7 +159,8 @@ void HdlcEncoder::sendFrame(const std::vector<u8>& data)
|
||||
for (u8 b : data)
|
||||
sendByte(b);
|
||||
// crc
|
||||
u16 crc = calcCrc16(data);
|
||||
if (crc < 0)
|
||||
crc = calcCrc16(data);
|
||||
sendByte(crc & 0xff);
|
||||
sendByte(crc >> 8);
|
||||
// closing flag
|
||||
@@ -221,7 +222,7 @@ void HdlcDecoder::write(u8 v)
|
||||
if (rxOnes == 6)
|
||||
{
|
||||
// we have a flag
|
||||
if (!rxFrame.empty())
|
||||
if (rxFrame.size() >= 4)
|
||||
{
|
||||
u16 crc = calcCrc16(rxFrame, rxFrame.size() - 2);
|
||||
if (crc != *(u16 *)(&rxFrame.back() - 1)) {
|
||||
@@ -229,12 +230,18 @@ void HdlcDecoder::write(u8 v)
|
||||
}
|
||||
else
|
||||
{
|
||||
rxFrame.pop_back();
|
||||
rxFrame.pop_back();
|
||||
if (!keepCrc) {
|
||||
rxFrame.pop_back();
|
||||
rxFrame.pop_back();
|
||||
}
|
||||
frames.push_back(std::move(rxFrame));
|
||||
}
|
||||
rxFrame.clear();
|
||||
}
|
||||
else if (!rxFrame.empty()) {
|
||||
WARN_LOG(MODEM, "Invalid ~empty frame: size %zd [%02x]", rxFrame.size(), rxFrame[0]);
|
||||
rxFrame.clear();
|
||||
}
|
||||
rxCurByte = 0;
|
||||
rxPosition = 0;
|
||||
}
|
||||
@@ -359,6 +366,8 @@ void V42Protocol::handleFrame()
|
||||
handleSabme(rxFrame[0], rxFrame[1]);
|
||||
break;
|
||||
case 0x0f: // DM
|
||||
WARN_LOG(MODEM, "Received DM");
|
||||
phase = Release;
|
||||
break;
|
||||
case 0x03: // UI
|
||||
WARN_LOG(MODEM, "Received UI");
|
||||
@@ -367,7 +376,17 @@ void V42Protocol::handleFrame()
|
||||
handleDisc(rxFrame[0]);
|
||||
break;
|
||||
case 0x63: // UA
|
||||
receiverReady = true;
|
||||
DEBUG_LOG(MODEM, "Received UA");
|
||||
if (!originator && phase == Establish) {
|
||||
phase = Connected;
|
||||
}
|
||||
else if (phase == DiscSent) {
|
||||
INFO_LOG(MODEM, "Received UA of DISC");
|
||||
phase = Release;
|
||||
}
|
||||
else {
|
||||
receiverReady = true;
|
||||
}
|
||||
break;
|
||||
case 0x87: // FRMR
|
||||
WARN_LOG(MODEM, "Received FRMR");
|
||||
@@ -393,6 +412,18 @@ void V42Protocol::handleFrame()
|
||||
}
|
||||
}
|
||||
|
||||
void V42Protocol::sendSabme() {
|
||||
std::vector<u8> sabme { 3, 0x7f };
|
||||
sendFrame(sabme);
|
||||
}
|
||||
|
||||
void V42Protocol::sendDisc()
|
||||
{
|
||||
std::vector<u8> disc { (u8)(1 | (!originator << 1)), 0x53 };
|
||||
sendFrame(disc);
|
||||
phase = DiscSent;
|
||||
}
|
||||
|
||||
void V42Protocol::handleSabme(u8 address, u8 control)
|
||||
{
|
||||
if (phase == Connected)
|
||||
@@ -442,10 +473,7 @@ void V42Protocol::handleIFrame(const std::vector<u8>& rxFrame)
|
||||
outputStream.write(c);
|
||||
}
|
||||
} catch (const v42b::Exception& e) {
|
||||
// send DISC
|
||||
std::vector<u8> disc { 1, 0x43 };
|
||||
sendFrame(disc);
|
||||
phase = Release;
|
||||
sendDisc();
|
||||
sentIFrames.clear();
|
||||
return;
|
||||
}
|
||||
@@ -464,6 +492,29 @@ void V42Protocol::handleIFrame(const std::vector<u8>& rxFrame)
|
||||
}
|
||||
}
|
||||
|
||||
void V42Protocol::sendXid()
|
||||
{
|
||||
// 03 af 82
|
||||
// [
|
||||
// gid, sz_msb, sz_lsb
|
||||
// [
|
||||
// paramId, valueSize, value...
|
||||
// ]
|
||||
// ]
|
||||
// As sent when originator
|
||||
std::vector<u8> xid { 0x03, 0xaf, 0x82,
|
||||
0x80, 0x00, 0x13,
|
||||
0x03, 0x03, 0x8a, 0x89, 0x00,
|
||||
0x05, 0x02, 0x02, 0x00,
|
||||
0x06, 0x02, 0x02, 0x00,
|
||||
0x07, 0x01, 0x0f,
|
||||
0x08, 0x01, 0x0f,
|
||||
0xff, 0x00, 0x05,
|
||||
0x01, 0x03, 0x4d, 0x4d, 0x41
|
||||
};
|
||||
sendFrame(xid);
|
||||
}
|
||||
|
||||
void V42Protocol::handleXid(std::vector<u8> rxFrame)
|
||||
{
|
||||
u32 maxCodeWords = 512;
|
||||
@@ -634,10 +685,12 @@ void V42Protocol::handleXid(std::vector<u8> rxFrame)
|
||||
i += paramSize;
|
||||
}
|
||||
}
|
||||
if (userDataOffset != 0)
|
||||
rxFrame.resize(userDataOffset);
|
||||
sendFrame(rxFrame);
|
||||
|
||||
if (originator)
|
||||
{
|
||||
if (userDataOffset != 0)
|
||||
rxFrame.resize(userDataOffset);
|
||||
sendFrame(rxFrame);
|
||||
}
|
||||
if (compressionEnabled)
|
||||
{
|
||||
NOTICE_LOG(MODEM, "V.42bis compression enabled: max dictionary size %d", maxCodeWords);
|
||||
@@ -722,8 +775,24 @@ void V42Protocol::sendIFrame()
|
||||
|
||||
int V42Protocol::read()
|
||||
{
|
||||
if (phase == None || phase == Detection) {
|
||||
v14Encoder.flush();
|
||||
if (phase == None || phase == Detection)
|
||||
{
|
||||
if (originator) {
|
||||
v14Encoder.flush();
|
||||
}
|
||||
else if (!v14Encoder.available())
|
||||
{
|
||||
if (odpCount <= 20)
|
||||
{
|
||||
// send ODP: DC1
|
||||
v14Encoder.setStopBits(9);
|
||||
v14Encoder.write(0x91);
|
||||
v14Encoder.write(0x11);
|
||||
v14Encoder.setStopBits(1);
|
||||
v14Encoder.flush();
|
||||
}
|
||||
v14Encoder.flush();
|
||||
}
|
||||
return v14Encoder.read();
|
||||
}
|
||||
else if (phase == V14)
|
||||
@@ -734,7 +803,7 @@ int V42Protocol::read()
|
||||
}
|
||||
if (hdlcEncoder.available() == 0)
|
||||
{
|
||||
if (phase == Connected)
|
||||
if (phase == Connected || phase == DiscSent)
|
||||
{
|
||||
sendIFrame();
|
||||
if (hdlcEncoder.available() != 0)
|
||||
@@ -748,7 +817,17 @@ int V42Protocol::read()
|
||||
return hdlcEncoder.read();
|
||||
}
|
||||
}
|
||||
sendFlag();
|
||||
else if (phase == Establish && !originator)
|
||||
{
|
||||
for (int i = 0; i < 16; i++)
|
||||
sendFlag();
|
||||
sendXid();
|
||||
sendSabme();
|
||||
if (hdlcEncoder.available() != 0)
|
||||
return hdlcEncoder.read();
|
||||
}
|
||||
if (phase != Release)
|
||||
sendFlag();
|
||||
}
|
||||
return hdlcEncoder.read();
|
||||
}
|
||||
@@ -757,12 +836,19 @@ void V42Protocol::write(u8 v)
|
||||
{
|
||||
if (phase == None || phase == Detection)
|
||||
{
|
||||
if (detectionTimer.expired()) {
|
||||
if (originator && detectionTimer.expired())
|
||||
{
|
||||
// V14 isn't currently used in answering mode. If needed, the detectionTimer should probably be reset
|
||||
// on first write since it's already expired by then.
|
||||
INFO_LOG(MODEM, "Switching to V.14 mode");
|
||||
phase = V14;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (!originator && phase == None) {
|
||||
phase = Detection;
|
||||
odpCount = 0;
|
||||
}
|
||||
v14Decoder.write(v);
|
||||
int c = v14Decoder.read();
|
||||
if (c == -1)
|
||||
@@ -770,49 +856,81 @@ void V42Protocol::write(u8 v)
|
||||
v = c;
|
||||
}
|
||||
}
|
||||
if (phase == None)
|
||||
switch (phase)
|
||||
{
|
||||
if ((v == 0x11 || v == 0x91)
|
||||
&& (v14Decoder.receivedStopBits() == 9 || v14Decoder.receivedStopBits() == 17))
|
||||
case None:
|
||||
if (originator)
|
||||
{
|
||||
phase = Detection;
|
||||
lastRx = v;
|
||||
odpCount = 1;
|
||||
if ((v == 0x11 || v == 0x91)
|
||||
&& (v14Decoder.receivedStopBits() >= 9 && v14Decoder.receivedStopBits() <= 17))
|
||||
{
|
||||
phase = Detection;
|
||||
lastRx = v;
|
||||
odpCount = 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (phase == Detection)
|
||||
{
|
||||
// V.42 ODP: DC1 with alternating parity followed by 8+1 or 16+1 ones
|
||||
if ((v == 0x11 || v == 0x91)
|
||||
&& (v14Decoder.receivedStopBits() == 9 || v14Decoder.receivedStopBits() == 17)
|
||||
&& v != lastRx)
|
||||
break;
|
||||
|
||||
case Detection:
|
||||
if (originator)
|
||||
{
|
||||
odpCount++;
|
||||
if (odpCount == 4)
|
||||
// V.42 ODP: DC1 with alternating parity followed by 8+1 or 16+1 ones
|
||||
if ((v == 0x11 || v == 0x91)
|
||||
&& (v14Decoder.receivedStopBits() >= 9 && v14Decoder.receivedStopBits() <= 17)
|
||||
&& v != lastRx)
|
||||
{
|
||||
odpCount++;
|
||||
if (odpCount == 4)
|
||||
{
|
||||
odpCount = 0;
|
||||
// send ADP
|
||||
v14Encoder.setStopBits(9);
|
||||
v14Encoder.write('E');
|
||||
v14Encoder.write('C');
|
||||
//v14Encoder.write(0); // switches to v14 only and start ppp nego
|
||||
v14Encoder.setStopBits(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
odpCount = 0;
|
||||
// send ADP
|
||||
v14Encoder.setStopBits(9);
|
||||
v14Encoder.write('E');
|
||||
v14Encoder.write('C');
|
||||
//v14Encoder.write(0); // switches to v14 only and start ppp nego
|
||||
v14Encoder.setStopBits(1);
|
||||
if (v == 0x7e)
|
||||
phase = Establish;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
odpCount = 0;
|
||||
if (v == 0x7e)
|
||||
phase = Establish;
|
||||
// Answerer
|
||||
if ((v == 'E' || v == 'C')
|
||||
&& (v14Decoder.receivedStopBits() >= 9 && v14Decoder.receivedStopBits() <= 17)
|
||||
&& v != lastRx)
|
||||
{
|
||||
++odpCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (odpCount >= 10)
|
||||
phase = Establish;
|
||||
else
|
||||
odpCount = 0;
|
||||
}
|
||||
}
|
||||
lastRx = v;
|
||||
}
|
||||
else if (phase == Establish || phase == Connected) {
|
||||
break;
|
||||
|
||||
case Establish:
|
||||
case Connected:
|
||||
case DiscSent:
|
||||
hdlcDecoder.write(v);
|
||||
handleFrame();
|
||||
}
|
||||
else if (phase == V14) {
|
||||
break;
|
||||
|
||||
case V14:
|
||||
v14PipeOut.write(v);
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -850,6 +968,210 @@ void V42Protocol::ackIFrame(int seqNum)
|
||||
}
|
||||
}
|
||||
|
||||
int HdlcSyncAsync::read()
|
||||
{
|
||||
if (phase == None || phase == Detection)
|
||||
{
|
||||
if (originator) {
|
||||
v14Encoder.flush();
|
||||
}
|
||||
else if (!v14Encoder.available())
|
||||
{
|
||||
if (odpCount <= 20)
|
||||
{
|
||||
// send ODP: DC1
|
||||
v14Encoder.setStopBits(9);
|
||||
v14Encoder.write(0x91);
|
||||
v14Encoder.write(0x11);
|
||||
v14Encoder.setStopBits(1);
|
||||
v14Encoder.flush();
|
||||
}
|
||||
v14Encoder.flush();
|
||||
}
|
||||
return v14Encoder.read();
|
||||
}
|
||||
if (hdlcEncoder.available() == 0)
|
||||
{
|
||||
if (phase == Connected)
|
||||
{
|
||||
if (hdlcEncoder.available() == 0)
|
||||
handleAsyncFrame();
|
||||
if (hdlcEncoder.available() != 0)
|
||||
return hdlcEncoder.read();
|
||||
}
|
||||
if (phase != Release)
|
||||
hdlcEncoder.sendFlag();
|
||||
}
|
||||
return hdlcEncoder.read();
|
||||
}
|
||||
|
||||
void HdlcSyncAsync::write(u8 v)
|
||||
{
|
||||
if (phase == None || phase == Detection)
|
||||
{
|
||||
if (!originator && phase == None) {
|
||||
phase = Detection;
|
||||
odpCount = 0;
|
||||
}
|
||||
v14Decoder.write(v);
|
||||
int c = v14Decoder.read();
|
||||
if (c == -1)
|
||||
return;
|
||||
v = c;
|
||||
}
|
||||
switch (phase)
|
||||
{
|
||||
case None:
|
||||
if (originator)
|
||||
{
|
||||
if ((v == 0x11 || v == 0x91)
|
||||
&& (v14Decoder.receivedStopBits() >= 9 && v14Decoder.receivedStopBits() <= 17))
|
||||
{
|
||||
phase = Detection;
|
||||
lastRx = v;
|
||||
odpCount = 1;
|
||||
}
|
||||
}
|
||||
break;
|
||||
|
||||
case Detection:
|
||||
if (originator)
|
||||
{
|
||||
// V.42 ODP: DC1 with alternating parity followed by 8+1 or 16+1 ones
|
||||
if ((v == 0x11 || v == 0x91)
|
||||
&& (v14Decoder.receivedStopBits() >= 9 && v14Decoder.receivedStopBits() <= 17)
|
||||
&& v != lastRx)
|
||||
{
|
||||
odpCount++;
|
||||
if (odpCount == 4)
|
||||
{
|
||||
odpCount = 0;
|
||||
// send ADP
|
||||
v14Encoder.setStopBits(9);
|
||||
v14Encoder.write('E');
|
||||
v14Encoder.write('C');
|
||||
v14Encoder.setStopBits(1);
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
odpCount = 0;
|
||||
if (v == 0x7e)
|
||||
phase = Connected;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// Answerer
|
||||
if ((v == 'E' || v == 'C')
|
||||
&& (v14Decoder.receivedStopBits() >= 9 && v14Decoder.receivedStopBits() <= 17)
|
||||
&& v != lastRx)
|
||||
{
|
||||
++odpCount;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (odpCount >= 10)
|
||||
phase = Connected;
|
||||
else
|
||||
odpCount = 0;
|
||||
}
|
||||
}
|
||||
lastRx = v;
|
||||
break;
|
||||
|
||||
case Connected:
|
||||
hdlcDecoder.write(v);
|
||||
handleSyncFrame();
|
||||
break;
|
||||
|
||||
default:
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void HdlcSyncAsync::handleSyncFrame()
|
||||
{
|
||||
if (!hdlcDecoder.frameAvailable())
|
||||
return;
|
||||
outputStream.write(FLAG);
|
||||
std::vector<u8> frame = hdlcDecoder.getFrame();
|
||||
for (u8 b : frame)
|
||||
{
|
||||
if (b == FLAG || b == ESCAPE) {
|
||||
outputStream.write(ESCAPE);
|
||||
b ^= 0x20;
|
||||
}
|
||||
outputStream.write(b);
|
||||
}
|
||||
outputStream.write(FLAG);
|
||||
}
|
||||
|
||||
void HdlcSyncAsync::handleAsyncFrame()
|
||||
{
|
||||
if (inputStream.available() == 0)
|
||||
return;
|
||||
bool frameComplete = false;
|
||||
for (;;)
|
||||
{
|
||||
int c = inputStream.read();
|
||||
if (c == -1)
|
||||
break;
|
||||
if (c == FLAG)
|
||||
{
|
||||
if (inFrame.empty())
|
||||
continue;
|
||||
frameComplete = true;
|
||||
break;
|
||||
}
|
||||
else if (c == ESCAPE) {
|
||||
escapeSeen = true;
|
||||
continue;
|
||||
}
|
||||
if (escapeSeen) {
|
||||
c ^= 0x20;
|
||||
escapeSeen = false;
|
||||
}
|
||||
inFrame.push_back(c & 0xff);
|
||||
}
|
||||
if (!frameComplete)
|
||||
return;
|
||||
|
||||
if (inFrame.size() >= 4)
|
||||
{
|
||||
u16 crc = *(u16 *)&inFrame[inFrame.size() - 2];
|
||||
inFrame.pop_back();
|
||||
inFrame.pop_back();
|
||||
/*
|
||||
if ((inFrame[0] & 0xfd) == 1)
|
||||
{
|
||||
if (inFrame[1] == 9)
|
||||
WARN_LOG(MODEM, "[%d] Received REJ %d", originator, inFrame[2] >> 1);
|
||||
else if (inFrame[1] == 0xd)
|
||||
WARN_LOG(MODEM, "[%d] Received SREJ %d", originator, inFrame[2] >> 1);
|
||||
}
|
||||
*/
|
||||
hdlcEncoder.sendFrame(inFrame, crc);
|
||||
}
|
||||
else if (!inFrame.empty()) {
|
||||
WARN_LOG(MODEM, "Small frame (%d) received", (int)inFrame.size());
|
||||
}
|
||||
inFrame.clear();
|
||||
}
|
||||
|
||||
void HdlcSyncAsync::reset()
|
||||
{
|
||||
phase = None;
|
||||
lastRx = 0;
|
||||
odpCount = 0;
|
||||
v14Encoder.reset();
|
||||
v14Decoder.reset();
|
||||
hdlcEncoder.reset();
|
||||
hdlcDecoder.reset();
|
||||
inFrame.clear();
|
||||
escapeSeen = false;
|
||||
}
|
||||
|
||||
u8 V8bisProtocol::detectTone()
|
||||
{
|
||||
switch (toneState)
|
||||
|
||||
+67
-2
@@ -20,7 +20,6 @@
|
||||
#include "types.h"
|
||||
#include "internal.h"
|
||||
#include "v42bis.h"
|
||||
#include "network/netservice.h"
|
||||
#include <vector>
|
||||
#include <deque>
|
||||
#include <unordered_map>
|
||||
@@ -71,6 +70,9 @@ private:
|
||||
class HdlcDecoder : public OutStream
|
||||
{
|
||||
public:
|
||||
HdlcDecoder(bool keepCrc = false)
|
||||
: keepCrc(keepCrc)
|
||||
{}
|
||||
void write(u8 v);
|
||||
bool frameAvailable() const { return !frames.empty(); }
|
||||
std::vector<u8> getFrame();
|
||||
@@ -82,12 +84,13 @@ private:
|
||||
int rxOnes = 0;
|
||||
int rxPosition = 0;
|
||||
u8 rxCurByte = 0;
|
||||
bool keepCrc;
|
||||
};
|
||||
|
||||
class HdlcEncoder : public BufferedTransformer
|
||||
{
|
||||
public:
|
||||
void sendFrame(const std::vector<u8>& data);
|
||||
void sendFrame(const std::vector<u8>& data, int crc = -1);
|
||||
void sendFlag();
|
||||
void reset() override;
|
||||
|
||||
@@ -114,15 +117,21 @@ public:
|
||||
return 1;
|
||||
}
|
||||
|
||||
void setOriginator(bool originator) {
|
||||
this->originator = originator;
|
||||
}
|
||||
|
||||
private:
|
||||
InStream& inputStream;
|
||||
OutStream& outputStream;
|
||||
bool originator = true;
|
||||
|
||||
enum Phase {
|
||||
None,
|
||||
Detection,
|
||||
Establish,
|
||||
Connected,
|
||||
DiscSent,
|
||||
Release,
|
||||
V14 // Bomberman Online, Power Smash, Pro Yakyu Team de asobou net!
|
||||
};
|
||||
@@ -181,10 +190,66 @@ private:
|
||||
|
||||
void sendIFrame();
|
||||
void ackIFrame(int seqNum);
|
||||
void sendDisc();
|
||||
// Answerer
|
||||
void sendSabme();
|
||||
void sendXid();
|
||||
|
||||
friend class V42Test;
|
||||
};
|
||||
|
||||
//
|
||||
// Converts between synchronous HDLC and HDLC-like asynchronous frames
|
||||
// as used by ppp. This is used for Capcom peer-to-peer communication.
|
||||
// Upper layer protocols such as V.42 (and potentially V.42bis) are implemented
|
||||
// natively by each peer. This way error correction works end to end.
|
||||
//
|
||||
class HdlcSyncAsync : public InStream, public OutStream
|
||||
{
|
||||
public:
|
||||
HdlcSyncAsync(InStream& in, OutStream& out)
|
||||
: inputStream(in), outputStream(out)
|
||||
{}
|
||||
int read() override;
|
||||
void write(u8 v) override;
|
||||
void reset();
|
||||
|
||||
int available() override {
|
||||
return 1;
|
||||
}
|
||||
|
||||
void setOriginator(bool originator) {
|
||||
this->originator = originator;
|
||||
}
|
||||
|
||||
private:
|
||||
void handleSyncFrame();
|
||||
void handleAsyncFrame();
|
||||
|
||||
InStream& inputStream;
|
||||
OutStream& outputStream;
|
||||
bool originator = true;
|
||||
|
||||
enum Phase {
|
||||
None,
|
||||
Detection,
|
||||
Connected,
|
||||
Release,
|
||||
};
|
||||
Phase phase = None;
|
||||
u8 lastRx = 0;
|
||||
int odpCount = 0;
|
||||
V14Encoder v14Encoder;
|
||||
V14Decoder v14Decoder;
|
||||
HdlcEncoder hdlcEncoder;
|
||||
HdlcDecoder hdlcDecoder {true};
|
||||
std::vector<uint8_t> inFrame;
|
||||
bool escapeSeen = false;
|
||||
|
||||
static constexpr uint8_t FLAG = 0x7e;
|
||||
static constexpr uint8_t ESCAPE = 0x7d;
|
||||
};
|
||||
|
||||
//
|
||||
// V.8 bis is used by older browsers (Planet Web 1.125, DreamKey 1.0/1.5 and the many games that include them)
|
||||
// and Windows CE games.
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include "internal.h"
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
#include <stdexcept>
|
||||
|
||||
@@ -154,10 +154,8 @@ void serialize(Serializer& ser)
|
||||
void deserialize(Deserializer& deser)
|
||||
{
|
||||
deser >> aw_maple_devs;
|
||||
if (deser.version() >= Deserializer::V20) {
|
||||
deser >> coin_chute_time;
|
||||
deser >> aw_ram_test_skipped;
|
||||
}
|
||||
deser >> coin_chute_time;
|
||||
deser >> aw_ram_test_skipped;
|
||||
}
|
||||
|
||||
} // namespace atomiswave
|
||||
@@ -18,10 +18,13 @@
|
||||
*/
|
||||
#include "card_reader.h"
|
||||
#include "oslib/oslib.h"
|
||||
#include "oslib/directory.h"
|
||||
#include "hw/sh4/modules/modules.h"
|
||||
#include "hw/maple/maple_cfg.h"
|
||||
#include "hw/maple/maple_devs.h"
|
||||
#include "oslib/i18n.h"
|
||||
#include "stdclass.h"
|
||||
#include "input/gamepad_device.h"
|
||||
#include <deque>
|
||||
#include <memory>
|
||||
#include <cerrno>
|
||||
@@ -40,6 +43,8 @@ public:
|
||||
INFO_LOG(NAOMI, "Card inserted");
|
||||
}
|
||||
|
||||
virtual void ejectCard() {}
|
||||
|
||||
protected:
|
||||
virtual bool loadCard() = 0;
|
||||
|
||||
@@ -61,16 +66,8 @@ protected:
|
||||
void saveCard(const u8 *cardData, u32 len)
|
||||
{
|
||||
std::string path = hostfs::getArcadeFlashPath() + ".card";
|
||||
FILE *fp = nowide::fopen(path.c_str(), "wb");
|
||||
if (fp == nullptr)
|
||||
{
|
||||
WARN_LOG(NAOMI, "Can't create card file %s: errno %d", path.c_str(), errno);
|
||||
return;
|
||||
}
|
||||
INFO_LOG(NAOMI, "Saving card file to %s", path.c_str());
|
||||
if (fwrite(cardData, 1, len, fp) != len)
|
||||
WARN_LOG(NAOMI, "Truncated write to file: %s", path.c_str());
|
||||
fclose(fp);
|
||||
safeFullSave(path, cardData, len);
|
||||
}
|
||||
|
||||
template<typename T>
|
||||
@@ -655,6 +652,343 @@ const u8 ClubKartCardReader::CommandBytes[][2]
|
||||
|
||||
static std::unique_ptr<CardReaderWriter> cardReader;
|
||||
|
||||
#define HWLOG(...) DEBUG_LOG(NAOMI, __VA_ARGS__)
|
||||
|
||||
// WCCF, Dragon Treasure IC card reader/writer (Saxa HW210), hooked to the SH4 SCIF port
|
||||
class SaxaHW210 : public CardReaderWriter, public SerialPort::Pipe
|
||||
{
|
||||
public:
|
||||
SaxaHW210() {
|
||||
SCIFSerialPort::Instance().setPipe(this);
|
||||
}
|
||||
~SaxaHW210() override {
|
||||
SCIFSerialPort::Instance().setPipe(nullptr);
|
||||
kcode[1] |= DC_DPAD_UP;
|
||||
}
|
||||
|
||||
void write(u8 b) override
|
||||
{
|
||||
inBuffer.push_back(b);
|
||||
if (inBuffer.size() < 6)
|
||||
return;
|
||||
|
||||
u32 size = (inBuffer[2] << 8) + inBuffer[3] + 5;
|
||||
if (inBuffer.size() < size)
|
||||
return;
|
||||
|
||||
u8 crc = calcCrc(inBuffer.begin(), inBuffer.begin() + size - 1);
|
||||
if (crc != inBuffer[size - 1]) {
|
||||
WARN_LOG(NAOMI, "Invalid CRC for command %02x size %d", inBuffer[1], size);
|
||||
}
|
||||
else
|
||||
{
|
||||
dumpPkt();
|
||||
size_t outStart = outBuffer.size();
|
||||
const u16 arg = (inBuffer[4] << 8) | inBuffer[5];
|
||||
switch (inBuffer[1])
|
||||
{
|
||||
case 0x10: // sz 0 arg 1000
|
||||
sendCmd(0x10); // returned arg should be != 0x20
|
||||
break;
|
||||
case 0x11: // sz 8 arg 6: 00 00 00 00 00 00
|
||||
sendCmd(0x11);
|
||||
break;
|
||||
case 0x14: // NFC field status?
|
||||
// sz 0 arg 1400:
|
||||
// called after returning arg=0x20 in cmd 10
|
||||
sendCmd(0x14, 0, cardInserted);
|
||||
break;
|
||||
case 0x20: // Select card slot
|
||||
// sz 8 arg 1: 00 00 00 00 00 00
|
||||
// arg is selected card slot#. Normally 1 but set to 0 to access other card
|
||||
// expects arg=0, no payload
|
||||
if (!cardInserted || (!cardExpired && arg == 0)) {
|
||||
sendCmd(0x20, 0, 0x8000);
|
||||
}
|
||||
else {
|
||||
cardIndex = arg;
|
||||
sendCmd(0x20);
|
||||
}
|
||||
break;
|
||||
case 0x21: // sz 10 arg 0: 00 00 00 00 00 00 00 00 00 00 00 00 00 00
|
||||
// Returning arg=1 indicates that 2 cards are in the reader
|
||||
// payload is some card identifier, sent back in cmd 22
|
||||
sendCmd(0x21, 8, cardExpired && cardIndex == 1);
|
||||
outBuffer.push_back(0);
|
||||
outBuffer.push_back(cardIndex);
|
||||
outBuffer.push_back(0);
|
||||
outBuffer.push_back(cardIndex);
|
||||
for (int i = 0; i < 4; i++)
|
||||
outBuffer.push_back(0);
|
||||
break;
|
||||
case 0x22: // sz 8 arg 0: 00 00 00 00 00 00
|
||||
// expects arg=0
|
||||
// payload[0-1] is used as cmd arg for all operations below
|
||||
sendCmd(0x22, 8);
|
||||
outBuffer.push_back(0);
|
||||
outBuffer.push_back(arg);
|
||||
for (int i = 0; i < 6; i++)
|
||||
outBuffer.push_back(0);
|
||||
break;
|
||||
|
||||
case 0x24: // Read single block
|
||||
{
|
||||
// sz 8 arg 0: 00 04 00 00 00 00
|
||||
if (!cardInserted) {
|
||||
sendCmd(0x24, 0, 0x8000);
|
||||
break;
|
||||
}
|
||||
sendCmd(0x24, 8);
|
||||
u32 offset = inBuffer[7] * 8;
|
||||
for (int i = 0; i < 8; i++)
|
||||
{
|
||||
if (offset + i < CARD_SZ)
|
||||
outBuffer.push_back(cardData[arg][offset + i]);
|
||||
else
|
||||
outBuffer.push_back(0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
case 0x25: // Write single block
|
||||
// sz 10 arg 0: 00 00 00 04 95 71 25 76 12 34 56 78 00 00
|
||||
if (!cardInserted) {
|
||||
sendCmd(0x25, 0, 0x8000);
|
||||
break;
|
||||
}
|
||||
if (inBuffer[9] < 6) {
|
||||
// write-protected
|
||||
HWLOG("HW210: Block %d write-protected", inBuffer[9]);
|
||||
sendCmd(0x25, 0, 0x8001);
|
||||
}
|
||||
else {
|
||||
writeCard(arg, inBuffer[9], 1, &inBuffer[10]);
|
||||
sendCmd(0x25);
|
||||
}
|
||||
break;
|
||||
|
||||
case 0x26: // Decrement remaining writes
|
||||
// sz 8 arg 800: 00 05 00 01 00 00
|
||||
// sz 8 arg 800: 00 05 ff fd 00 00 after resigning
|
||||
{
|
||||
if (!cardInserted) {
|
||||
sendCmd(0x26, 0, 0x8000);
|
||||
break;
|
||||
}
|
||||
u8 *p = &cardData[arg][inBuffer[7] * 8];
|
||||
u16 remain = (p[0] << 8) | p[1];
|
||||
const u16 count = (inBuffer[8] << 8) | inBuffer[9];
|
||||
remain -= count;
|
||||
p[0] = remain >> 8;
|
||||
p[1] = remain & 0xff;
|
||||
// After transferring an expired club card, the game will decrease
|
||||
// the source card write count to 0. This prevents the card from being
|
||||
// transferred again. In the Flycast case, the old card doesn't exist anymore
|
||||
// so we ignore it.
|
||||
saveCard(arg);
|
||||
sendCmd(0x26);
|
||||
break;
|
||||
}
|
||||
case 0x27: // sz 8 arg 800: 00 00 00 00 00 00
|
||||
sendCmd(0x27);
|
||||
break;
|
||||
case 0x31: // sz 8 arg 800: 00 00 00 06 00 00
|
||||
sendCmd(0x31);
|
||||
break;
|
||||
case 0x33: // Get remaining writes
|
||||
{
|
||||
// sz 8 arg 0: 00 05 00 00 00 00
|
||||
// expects arg=8008 or 0
|
||||
if (!cardInserted) {
|
||||
sendCmd(0x33, 0, 0x8000);
|
||||
break;
|
||||
}
|
||||
sendCmd(0x33, 8);
|
||||
// must be ffff for blank cards
|
||||
const u8 *p = &cardData[arg][inBuffer[7] * 8];
|
||||
outBuffer.push_back(p[0]);
|
||||
outBuffer.push_back(p[1]);
|
||||
for (int i = 0; i < 6; i++)
|
||||
outBuffer.push_back(0);
|
||||
|
||||
break;
|
||||
}
|
||||
|
||||
case 0x34: // Read card
|
||||
{
|
||||
// sz 8 arg 0: 00 08 00 1e 00 00
|
||||
// offset size (in 8-byte blocks)
|
||||
if (!cardInserted) {
|
||||
sendCmd(0x34, 0, 0x8000);
|
||||
break;
|
||||
}
|
||||
const u32 offset = ((inBuffer[6] << 8) | inBuffer[7]) * 8;
|
||||
u32 size = ((inBuffer[8] << 8) | inBuffer[9]) * 8;
|
||||
size = std::min(size, (u32)CARD_SZ - offset);
|
||||
sendCmd(0x34, size);
|
||||
for (u32 i = 0; i < size; i++)
|
||||
outBuffer.push_back(cardData[arg][offset + i]);
|
||||
break;
|
||||
}
|
||||
case 0x35: // Write card
|
||||
{
|
||||
if (!cardInserted) {
|
||||
sendCmd(0x35, 0, 0x8000);
|
||||
break;
|
||||
}
|
||||
const u32 block = (inBuffer[6] << 8) | inBuffer[7];
|
||||
if (block < 6) {
|
||||
// write-protected
|
||||
HWLOG("HW210: Block %d write-protected", block);
|
||||
sendCmd(0x35, 0, 0x8001);
|
||||
}
|
||||
else {
|
||||
writeCard(arg, block, (inBuffer[8] << 8) | inBuffer[9],
|
||||
&inBuffer[10]);
|
||||
sendCmd(0x35);
|
||||
}
|
||||
break;
|
||||
}
|
||||
|
||||
default:
|
||||
WARN_LOG(NAOMI, "HW210: unhandled command %02x", inBuffer[1]);
|
||||
break;
|
||||
}
|
||||
if (outBuffer.size() != outStart)
|
||||
outBuffer.push_back(calcCrc(outBuffer.begin() + outStart, outBuffer.end()));
|
||||
}
|
||||
inBuffer.erase(inBuffer.begin(), inBuffer.begin() + size);
|
||||
}
|
||||
|
||||
int available() override {
|
||||
return outBuffer.size();
|
||||
}
|
||||
|
||||
u8 read() override
|
||||
{
|
||||
if (outBuffer.empty())
|
||||
return 0;
|
||||
u8 b = outBuffer.front();
|
||||
outBuffer.pop_front();
|
||||
return b;
|
||||
}
|
||||
|
||||
void ejectCard() override
|
||||
{
|
||||
if (!cardInserted)
|
||||
return;
|
||||
NOTICE_LOG(NAOMI, "Card ejected");
|
||||
os_notify(i18n::T("Card ejected"), 2000);
|
||||
cardInserted = false;
|
||||
cardExpired = false;
|
||||
memset(cardData[0], 0, CARD_SZ);
|
||||
memset(cardData[1], 0, CARD_SZ);
|
||||
kcode[1] |= DC_DPAD_UP;
|
||||
}
|
||||
|
||||
protected:
|
||||
bool loadCard() override
|
||||
{
|
||||
memset(cardData[0], 0, CARD_SZ);
|
||||
memset(cardData[1], 0, CARD_SZ);
|
||||
bool ret = CardReaderWriter::loadCard(cardData[1], CARD_SZ);
|
||||
if (!ret) {
|
||||
initCard(1);
|
||||
}
|
||||
else
|
||||
{
|
||||
cardExpired = cardData[1][0x28] == 0 && cardData[1][0x29] == 1;
|
||||
if (cardExpired) {
|
||||
memcpy(cardData[0], cardData[1], CARD_SZ);
|
||||
initCard(1);
|
||||
}
|
||||
}
|
||||
kcode[1] &= ~DC_DPAD_UP; // card sensor
|
||||
return true;
|
||||
}
|
||||
|
||||
private:
|
||||
void dumpPkt()
|
||||
{
|
||||
std::string payload;
|
||||
u32 size = (inBuffer[2] << 8) + inBuffer[3];
|
||||
for (u32 i = 2; i < size; i++)
|
||||
payload += strprintf("%02x ", inBuffer[4 + i]);
|
||||
if (inBuffer[1] != 0x24 || inBuffer[6] != 0 || inBuffer[7] != 0)
|
||||
HWLOG("HW210: cmd %02x sz %x arg %x: %s", inBuffer[1], size, (inBuffer[4] << 8) + inBuffer[5], payload.c_str());
|
||||
}
|
||||
|
||||
void sendCmd(u8 cmd, u16 payloadSz = 0, u16 arg = 0)
|
||||
{
|
||||
outBuffer.push_back(0x10); // status = OK? FF seems to be accepted too
|
||||
outBuffer.push_back(cmd);
|
||||
outBuffer.push_back((payloadSz + 2) >> 8);
|
||||
outBuffer.push_back(payloadSz + 2);
|
||||
outBuffer.push_back(arg >> 8);
|
||||
outBuffer.push_back(arg);
|
||||
}
|
||||
|
||||
void initCard(int index)
|
||||
{
|
||||
u8 *card = cardData[index];
|
||||
memset(card, 0, CARD_SZ);
|
||||
|
||||
// Block 0: factory-set UID
|
||||
for (int i = 0; i < 8; i++)
|
||||
card[i] = rand() & 0xff;
|
||||
// Block 4 must start with 95 71 25 7x ?? ?? ?? ??
|
||||
// ? can be any digit [0-9]
|
||||
// x must be the sum of all ? digits % 10
|
||||
static constexpr u8 UID[] = { 0x95, 0x71, 0x25, 0x70 };
|
||||
const u32 offset = 4 * 8;
|
||||
memcpy(&card[offset], UID, sizeof(UID));
|
||||
int sum = 0;
|
||||
srand(time(nullptr) + index * 2);
|
||||
for (int i = 0; i < 4; i++)
|
||||
{
|
||||
const u8 n = rand() % 100;
|
||||
card[i + offset + 4] = ((n / 10) << 4) | (n % 10);
|
||||
sum += n / 10 + n % 10;
|
||||
}
|
||||
card[offset + 3] = (card[offset + 3] & 0xf0) | (sum % 10);
|
||||
// Block 5: write counter
|
||||
card[0x28] = 0xff;
|
||||
card[0x29] = 0xff;
|
||||
// Block 6: copy of block #0 UID
|
||||
memcpy(card + 0x30, card, 8);
|
||||
NOTICE_LOG(NAOMI, "WCCF IC card %d created: %02x %02x %02x %02x %02x", index, card[offset + 3],
|
||||
card[offset + 4], card[offset + 5],
|
||||
card[offset + 6], card[offset + 7]);
|
||||
}
|
||||
|
||||
void writeCard(int cardIndex, u32 offset, u32 blocks, const u8 *data)
|
||||
{
|
||||
offset *= 8; // in bytes
|
||||
if (offset >= CARD_SZ)
|
||||
return;
|
||||
|
||||
u32 size = blocks * 8;
|
||||
size = std::min(size, (u32)CARD_SZ - offset);
|
||||
if (memcmp(&cardData[cardIndex][offset], data, size) == 0)
|
||||
return;
|
||||
|
||||
memcpy(&cardData[cardIndex][offset], data, size);
|
||||
saveCard(cardIndex);
|
||||
}
|
||||
|
||||
void saveCard(int cardIndex)
|
||||
{
|
||||
if (cardIndex == 1)
|
||||
CardReaderWriter::saveCard(cardData[1], CARD_SZ);
|
||||
else
|
||||
WARN_LOG(NAOMI, "Card #0 not saved");
|
||||
}
|
||||
|
||||
static constexpr size_t CARD_SZ = 0x270;
|
||||
u8 cardData[2][CARD_SZ] {};
|
||||
int cardIndex = 1;
|
||||
bool cardExpired = false;
|
||||
};
|
||||
|
||||
void initdInit() {
|
||||
term();
|
||||
cardReader = std::make_unique<InitialDCardReader>();
|
||||
@@ -674,6 +1008,11 @@ void clubkInit() {
|
||||
cardReader = std::make_unique<ClubKartCardReader>();
|
||||
}
|
||||
|
||||
void wccfInit() {
|
||||
term();
|
||||
cardReader = std::make_unique<SaxaHW210>();
|
||||
}
|
||||
|
||||
void term() {
|
||||
cardReader.reset();
|
||||
}
|
||||
@@ -765,6 +1104,11 @@ void insertCard(int playerNum)
|
||||
else
|
||||
insertRfidCard(playerNum);
|
||||
}
|
||||
void ejectCard()
|
||||
{
|
||||
if (cardReader != nullptr)
|
||||
cardReader->ejectCard();
|
||||
}
|
||||
|
||||
bool readerAvailable()
|
||||
{
|
||||
|
||||
@@ -24,6 +24,7 @@ namespace card_reader {
|
||||
void initdInit();
|
||||
void derbyInit();
|
||||
void clubkInit();
|
||||
void wccfInit();
|
||||
void term();
|
||||
|
||||
void barcodeInit();
|
||||
@@ -33,6 +34,7 @@ std::string barcodeGetCard();
|
||||
void barcodeSetCard(const std::string& card);
|
||||
|
||||
void insertCard(int playerNum);
|
||||
void ejectCard();
|
||||
bool readerAvailable();
|
||||
|
||||
}
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "dinokich_card.h"
|
||||
#include "cfg/option.h"
|
||||
#include "oslib/oslib.h"
|
||||
#include "oslib/directory.h"
|
||||
#include <algorithm>
|
||||
|
||||
namespace systemsp
|
||||
@@ -385,16 +386,7 @@ DinokichCardReader::Reply DinokichCardReader::on_63(const std::vector<u8>& f) {
|
||||
void DinokichCardReader::saveCard(int slotId)
|
||||
{
|
||||
Slot& slot = slots_[slotId];
|
||||
FILE *f = nowide::fopen(getCardDataPath(index + slotId).c_str(), "wb");
|
||||
if (f != nullptr)
|
||||
{
|
||||
if (fwrite(slot.eeprom.data(), 1, slot.eeprom.size(), f) != slot.eeprom.size())
|
||||
WARN_LOG(NAOMI, "Rfid card %d: truncated write", index);
|
||||
fclose(f);
|
||||
}
|
||||
else {
|
||||
WARN_LOG(NAOMI, "Card saved failed to %s", getCardDataPath(index).c_str());
|
||||
}
|
||||
safeFullSave(getCardDataPath(index + slotId), slot.eeprom.data(), slot.eeprom.size());
|
||||
}
|
||||
|
||||
void DinokichCardReader::serialize(Serializer& ser) const
|
||||
@@ -445,12 +437,9 @@ void DinokichCardReader::deserialize(Deserializer& deser)
|
||||
deser.skip<u8>(); // expectedBytes
|
||||
deser >> size;
|
||||
deser.skip(size);
|
||||
if (deser.version() >= Deserializer::V41)
|
||||
{
|
||||
deser.skip<int>(); // state
|
||||
deser.skip<int>(); // rowCounter
|
||||
deser.skip(128); // cardData
|
||||
}
|
||||
deser.skip<int>(); // state
|
||||
deser.skip<int>(); // rowCounter
|
||||
deser.skip(128); // cardData
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@
|
||||
#include "naomi.h"
|
||||
#include "oslib/i18n.h"
|
||||
#include <algorithm>
|
||||
#include <zlib.h>
|
||||
|
||||
/*
|
||||
|
||||
@@ -539,26 +540,28 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector<u8> *digest)
|
||||
|
||||
// directory
|
||||
u8 dir_sector[2048];
|
||||
// find data of file
|
||||
u32 file_size;
|
||||
|
||||
if (netpic == 0) {
|
||||
// find data of file
|
||||
if (netpic == 0)
|
||||
{
|
||||
u32 dir = ((buffer[0x2 + 0] << 0) |
|
||||
(buffer[0x2 + 1] << 8) |
|
||||
(buffer[0x2 + 2] << 16) |
|
||||
(buffer[0x2 + 3] << 24));
|
||||
|
||||
read_gdrom(gdrom.get(), dir, dir_sector);
|
||||
find_file(name, dir_sector, file_start, file_size);
|
||||
find_file(name, dir_sector, file_start, fileSize);
|
||||
|
||||
if (file_start && (file_size == 0x100)) {
|
||||
if (file_start && fileSize == 0x100) {
|
||||
// read file
|
||||
read_gdrom(gdrom.get(), file_start, buffer);
|
||||
// get "rom" file name
|
||||
memset(name, '\0', 128);
|
||||
memcpy(name, buffer + 0xc0, FILENAME_LENGTH - 1);
|
||||
}
|
||||
} else {
|
||||
}
|
||||
else
|
||||
{
|
||||
bool found = false;
|
||||
u32 i = 0;
|
||||
while (i < 2048 && buffer[i] != 0)
|
||||
@@ -583,15 +586,15 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector<u8> *digest)
|
||||
}
|
||||
}
|
||||
|
||||
find_file(name, dir_sector, file_start, file_size);
|
||||
find_file(name, dir_sector, file_start, fileSize);
|
||||
if (file_start == 0)
|
||||
// mj1: filename in the pic is incorrect, probably because the disk isn't supposed to be run like this?
|
||||
// so grab the first .BIN and load it.
|
||||
find_file("*.BIN", dir_sector, file_start, file_size);
|
||||
find_file("*.BIN", dir_sector, file_start, fileSize);
|
||||
|
||||
if (file_start != 0)
|
||||
{
|
||||
u32 file_rounded_size = (file_size + 2047) & ~2048;
|
||||
u32 file_rounded_size = (fileSize + 2047) & ~2048;
|
||||
for (dimm_data_size = 4096; dimm_data_size < file_rounded_size; dimm_data_size <<= 1)
|
||||
;
|
||||
dimm_data = (u8 *)malloc(dimm_data_size);
|
||||
@@ -605,6 +608,20 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector<u8> *digest)
|
||||
loadedSegments.end(), true);
|
||||
|
||||
des_generate_subkeys(rev64(key), des_subkeys);
|
||||
|
||||
if (fullLoad)
|
||||
{
|
||||
for (unsigned offset = 0; offset < fileSize; offset += SEGMENT_SIZE)
|
||||
{
|
||||
if (progress != nullptr)
|
||||
{
|
||||
if (progress->cancelled)
|
||||
throw LoadCancelledException();
|
||||
progress->progress = (float)offset / fileSize;
|
||||
}
|
||||
loadSegments(offset, SEGMENT_SIZE);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (!dimm_data)
|
||||
@@ -620,13 +637,16 @@ void GDCartridge::loadSegments(u32 offset, u32 size)
|
||||
if (loadedSegments[segment])
|
||||
continue;
|
||||
DEBUG_LOG(NAOMI, "Loading segment %d", segment);
|
||||
const u32 start = segment * SEGMENT_SIZE;
|
||||
// load data
|
||||
read_gdrom(gdrom.get(), file_start + (segment * SEGMENT_SIZE) / 2048,
|
||||
dimm_data + segment * SEGMENT_SIZE,
|
||||
read_gdrom(gdrom.get(), file_start + start / 2048,
|
||||
dimm_data + start,
|
||||
SEGMENT_SIZE / 2048,
|
||||
nullptr);
|
||||
if (start < fileSize)
|
||||
crc = crc32(crc, dimm_data + start, std::min(SEGMENT_SIZE, fileSize - start));
|
||||
// decrypt loaded data
|
||||
u64 *pData = (u64 *)(dimm_data + segment * SEGMENT_SIZE);
|
||||
u64 *pData = (u64 *)(dimm_data + start);
|
||||
for (u32 i = 0; i < SEGMENT_SIZE; i += 8, pData++)
|
||||
*pData = des_encrypt_decrypt<true>(*pData, des_subkeys);
|
||||
loadedSegments[segment] = true;
|
||||
@@ -759,18 +779,18 @@ void GDCartridge::process()
|
||||
break;
|
||||
case 1: // network commands
|
||||
WARN_LOG(NAOMI, "Network command received cmd %x. Need full NetDIMM?", cmd);
|
||||
returnToNaomi(true, 0, -1);
|
||||
returnToNaomi(dimm_command & 0x7e00, true, 0, -1);
|
||||
break;
|
||||
default:
|
||||
WARN_LOG(NAOMI, "Unknown DIMM command group %d cmd %x", cmdGroup, cmd);
|
||||
returnToNaomi(true, 0, -1);
|
||||
returnToNaomi(dimm_command & 0x7e00, true, 0, -1);
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
void GDCartridge::returnToNaomi(bool failed, u16 offsetl, u32 parameter)
|
||||
void GDCartridge::returnToNaomi(u16 cmd, bool failed, u16 offsetl, u32 parameter)
|
||||
{
|
||||
dimm_command = ((dimm_command & 0x7e00) + 0x400) | (failed ? 0xff : 0x4);
|
||||
dimm_command = (cmd + 0x400) | (failed ? 0xff : 0x4);
|
||||
dimm_offsetl = offsetl;
|
||||
dimm_parameterh = parameter >> 16;
|
||||
dimm_parameterl = parameter;
|
||||
|
||||
@@ -33,10 +33,13 @@ public:
|
||||
void Serialize(Serializer &ser) const override;
|
||||
void Deserialize(Deserializer &deser) override;
|
||||
|
||||
u32 getFileSize() const { return fileSize; }
|
||||
u32 getCrc() const { return ~crc; }
|
||||
|
||||
protected:
|
||||
virtual void process();
|
||||
virtual int schedCallback();
|
||||
void returnToNaomi(bool failed, u16 offsetl, u32 parameter);
|
||||
void returnToNaomi(u16 cmd, bool failed, u16 offsetl, u32 parameter);
|
||||
|
||||
template<typename T>
|
||||
void peek(u32 address)
|
||||
@@ -69,6 +72,9 @@ protected:
|
||||
u16 dimm_parameterh;
|
||||
static constexpr u16 DIMM_STATUS = 0x111;
|
||||
int schedId;
|
||||
u32 fileSize = 0;
|
||||
u32 crc = 0;
|
||||
bool fullLoad = false;
|
||||
|
||||
private:
|
||||
const char *gdrom_name = nullptr;
|
||||
|
||||
@@ -323,18 +323,10 @@ private:
|
||||
deser >> maxBet;
|
||||
deser >> minBet;
|
||||
deser >> addBet;
|
||||
if (deser.version() >= Deserializer::V39)
|
||||
{
|
||||
deser >> paidAmount;
|
||||
deser >> wonAmount;
|
||||
deser >> betButton;
|
||||
deser >> curBase;
|
||||
}
|
||||
else
|
||||
{
|
||||
paidAmount = 0;
|
||||
wonAmount = 0;
|
||||
}
|
||||
deser >> paidAmount;
|
||||
deser >> wonAmount;
|
||||
deser >> betButton;
|
||||
deser >> curBase;
|
||||
}
|
||||
|
||||
u32 expectedBytes = 0;
|
||||
|
||||
+10
-22
@@ -269,30 +269,18 @@ void serialize(Serializer& ser)
|
||||
|
||||
void deserialize(Deserializer& deser)
|
||||
{
|
||||
if (deser.version() >= Deserializer::V27)
|
||||
{
|
||||
if (initialized) {
|
||||
deser >> midiTxBuf;
|
||||
deser >> midiTxBufIndex;
|
||||
}
|
||||
else if (deser.version() < Deserializer::V51) {
|
||||
deser.skip(4); // midiTxBuf
|
||||
deser.skip<u32>(); // midiTxBufIndex
|
||||
}
|
||||
if (initialized) {
|
||||
deser >> midiTxBuf;
|
||||
deser >> midiTxBufIndex;
|
||||
}
|
||||
else {
|
||||
midiTxBufIndex = 0;
|
||||
}
|
||||
if (deser.version() >= Deserializer::V34)
|
||||
{
|
||||
if (initialized)
|
||||
deser >> calibrating;
|
||||
else if (deser.version() < Deserializer::V51)
|
||||
deser.skip<bool>(); // calibrating
|
||||
}
|
||||
else {
|
||||
calibrating = false;
|
||||
else if (deser.version() < Deserializer::V51) {
|
||||
deser.skip(4); // midiTxBuf
|
||||
deser.skip<u32>(); // midiTxBufIndex
|
||||
}
|
||||
if (initialized)
|
||||
deser >> calibrating;
|
||||
else if (deser.version() < Deserializer::V51)
|
||||
deser.skip<bool>(); // calibrating
|
||||
if (initialized)
|
||||
{
|
||||
maxSpring = 0x7f;
|
||||
|
||||
+30
-41
@@ -25,6 +25,7 @@
|
||||
#include "naomi_cart.h"
|
||||
#include "naomi_regs.h"
|
||||
#include "naomi_m3comm.h"
|
||||
#include "naomi_flashrom.h"
|
||||
#include "multiboard.h"
|
||||
#include "serialize.h"
|
||||
#include "network/output.h"
|
||||
@@ -126,12 +127,21 @@ static int naomiDmaSched(int tag, int sch_cycl, int jitter, void *arg)
|
||||
void* ptr = CurrentCartridge->GetDmaPtr(block_len);
|
||||
if (block_len == 0)
|
||||
{
|
||||
INFO_LOG(NAOMI, "Aborted DMA transfer. Read past end of cart?");
|
||||
for (u32 i = 0; i < len; i += 8, start += 8)
|
||||
addrspace::write64(start, 0);
|
||||
if (SB_GDDIR == 1)
|
||||
{
|
||||
INFO_LOG(NAOMI, "Aborted DMA transfer. Read past end of cart?");
|
||||
for (u32 i = 0; i < len; i += 8, start += 8)
|
||||
addrspace::write64(start, 0);
|
||||
}
|
||||
break;
|
||||
}
|
||||
WriteMemBlock_nommu_ptr(start, (u32*)ptr, block_len);
|
||||
if (SB_GDDIR == 1) {
|
||||
WriteMemBlock_nommu_ptr(start, (u32*)ptr, block_len);
|
||||
}
|
||||
else {
|
||||
const void *src = GetMemPtr(start, block_len);
|
||||
memcpy(ptr, src, block_len);
|
||||
}
|
||||
CurrentCartridge->AdvancePtr(block_len);
|
||||
len -= block_len;
|
||||
start += block_len;
|
||||
@@ -165,7 +175,6 @@ static void Naomi_DmaStart(u32 addr, u32 data)
|
||||
else if ((m3comm == nullptr || !m3comm->DmaStart(addr, data)) && CurrentCartridge != nullptr)
|
||||
{
|
||||
DEBUG_LOG(NAOMI, "NAOMI-DMA start addr %08X len %x", SB_GDSTAR, SB_GDLEN);
|
||||
verify(1 == SB_GDDIR);
|
||||
SB_GDST = 1;
|
||||
SB_GDSTARD = SB_GDSTAR & 0x1FFFFFE0;
|
||||
SB_GDLEND = 0;
|
||||
@@ -215,7 +224,19 @@ void naomi_reg_Init()
|
||||
'0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0', '0'
|
||||
};
|
||||
romSerialId.setData(romSerialData);
|
||||
#if 0
|
||||
// Generate random main board serial id
|
||||
u8 mainSerialData[sizeof(romSerialData)];
|
||||
memcpy(mainSerialData, romSerialData, sizeof(romSerialData));
|
||||
for (unsigned i = 52; i < 52 + 16; i++)
|
||||
mainSerialData[i] = (rand() % 10) + '0';
|
||||
u16 crc = eeprom_crc(&mainSerialData[22], 46);
|
||||
mainSerialData[20] = crc >> 8;
|
||||
mainSerialData[21] = crc;
|
||||
mainSerialId.setData(mainSerialData);
|
||||
#else
|
||||
mainSerialId.setData(romSerialData);
|
||||
#endif
|
||||
if (dmaSchedId == -1)
|
||||
dmaSchedId = sh4_sched_register(0, naomiDmaSched);
|
||||
}
|
||||
@@ -236,11 +257,11 @@ const u8 *getGameSerialId()
|
||||
|
||||
void naomi_reg_Term()
|
||||
{
|
||||
naomi_cart_Close();
|
||||
multiboard.reset();
|
||||
m3comm.reset();
|
||||
networkOutput.term();
|
||||
if (dmaSchedId != -1)
|
||||
sh4_sched_unregister(dmaSchedId);
|
||||
sh4_sched_unregister(dmaSchedId);
|
||||
dmaSchedId = -1;
|
||||
midiffb::term();
|
||||
}
|
||||
@@ -289,40 +310,8 @@ void naomi_Serialize(Serializer& ser)
|
||||
}
|
||||
void naomi_Deserialize(Deserializer& deser)
|
||||
{
|
||||
if (deser.version() < Deserializer::V40)
|
||||
{
|
||||
deser.skip<u32>(); // GSerialBuffer
|
||||
deser.skip<u32>(); // BSerialBuffer
|
||||
deser.skip<int>(); // GBufPos
|
||||
deser.skip<int>(); // BBufPos
|
||||
deser.skip<int>(); // GState
|
||||
deser.skip<int>(); // BState
|
||||
deser.skip<int>(); // GOldClk
|
||||
deser.skip<int>(); // BOldClk
|
||||
deser.skip<int>(); // BControl
|
||||
deser.skip<int>(); // BCmd
|
||||
deser.skip<int>(); // BLastCmd
|
||||
deser.skip<int>(); // GControl
|
||||
deser.skip<int>(); // GCmd
|
||||
deser.skip<int>(); // GLastCmd
|
||||
deser.skip<int>(); // SerStep
|
||||
deser.skip<int>(); // SerStep2
|
||||
deser.skip(69); // BSerial
|
||||
deser.skip(69); // GSerial
|
||||
}
|
||||
else
|
||||
{
|
||||
mainSerialId.deserialize(deser);
|
||||
romSerialId.deserialize(deser);
|
||||
}
|
||||
if (deser.version() < Deserializer::V36)
|
||||
{
|
||||
deser.skip<u32>(); // reg_dimm_command;
|
||||
deser.skip<u32>(); // reg_dimm_offsetl;
|
||||
deser.skip<u32>(); // reg_dimm_parameterl;
|
||||
deser.skip<u32>(); // reg_dimm_parameterh;
|
||||
deser.skip<u32>(); // reg_dimm_status;
|
||||
}
|
||||
mainSerialId.deserialize(deser);
|
||||
romSerialId.deserialize(deser);
|
||||
atomiswave::deserialize(deser);
|
||||
midiffb::deserialize(deser);
|
||||
if (deser.version() >= Deserializer::V45)
|
||||
|
||||
+79
-122
@@ -278,7 +278,8 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo
|
||||
GDCartridge *gdcart;
|
||||
if (strncmp(game->name, "vf4", 3) == 0
|
||||
|| strcmp(game->name, "mj1") == 0
|
||||
|| strncmp(game->name, "wccf", 4) == 0)
|
||||
|| strncmp(game->name, "wccf", 4) == 0
|
||||
|| strncmp(game->name, "dragntr", 7) == 0)
|
||||
gdcart = new NetDimm(game->size);
|
||||
else
|
||||
gdcart = new GDCartridge(game->size);
|
||||
@@ -466,143 +467,59 @@ static void loadDecryptedRom(const std::string& path, const std::string& fileNam
|
||||
}
|
||||
}
|
||||
|
||||
std::string folder;
|
||||
std::vector<std::string> files;
|
||||
std::vector<u32> fstart;
|
||||
std::vector<u32> fsize;
|
||||
u32 romSize = 0;
|
||||
// BIN/DAT loading
|
||||
hostfs::File* fp = hostfs::storage().openFile(path, "rb");
|
||||
if (fp == nullptr)
|
||||
throw FlycastException(strprintf(T("Error: can't open %s"), path.c_str()));
|
||||
|
||||
std::string extension = get_file_extension(fileName);
|
||||
if (extension == "lst")
|
||||
{
|
||||
// LST file
|
||||
hostfs::File *fl = hostfs::storage().openFile(path, "r");
|
||||
if (!fl)
|
||||
throw FlycastException(strprintf(T("Error: can't open %s"), path.c_str()));
|
||||
folder = hostfs::storage().getParentPath(path);
|
||||
fp->seek(0, SEEK_END);
|
||||
const u32 romSize = (u32)fp->tell();
|
||||
fp->seek(0, SEEK_SET);
|
||||
|
||||
char t[512];
|
||||
char* line = fl->gets(t, sizeof(t));
|
||||
if (!line)
|
||||
{
|
||||
delete fl;
|
||||
throw FlycastException(Ts("Error: Invalid LST file"));
|
||||
}
|
||||
|
||||
char* eon = strstr(line, "\n");
|
||||
if (!eon)
|
||||
DEBUG_LOG(NAOMI, "+Loading naomi rom that has no name");
|
||||
else
|
||||
{
|
||||
*eon = 0;
|
||||
DEBUG_LOG(NAOMI, "+Loading naomi rom : %s", line);
|
||||
}
|
||||
|
||||
line = fl->gets(t, sizeof(t));
|
||||
if (!line)
|
||||
{
|
||||
delete fl;
|
||||
throw FlycastException(Ts("Error: Invalid LST file"));
|
||||
}
|
||||
|
||||
while (line)
|
||||
{
|
||||
char filename[512];
|
||||
u32 addr, sz;
|
||||
if (sscanf(line, "\"%[^\"]\",%x,%x", filename, &addr, &sz) == 3)
|
||||
{
|
||||
files.emplace_back(filename);
|
||||
fstart.push_back(addr);
|
||||
fsize.push_back(sz);
|
||||
romSize = std::max(romSize, (addr + sz));
|
||||
}
|
||||
else if (line[0] != 0 && line[0] != '\n' && line[0] != '\r')
|
||||
WARN_LOG(NAOMI, "Warning: invalid line in .lst file: %s", line);
|
||||
|
||||
line = fl->gets(t, sizeof(t));
|
||||
}
|
||||
delete fl;
|
||||
}
|
||||
else
|
||||
{
|
||||
// BIN loading
|
||||
hostfs::File* fp = hostfs::storage().openFile(path, "rb");
|
||||
if (fp == nullptr)
|
||||
throw FlycastException(strprintf(T("Error: can't open %s"), path.c_str()));
|
||||
|
||||
fp->seek(0, SEEK_END);
|
||||
u32 file_size = (u32)fp->tell();
|
||||
INFO_LOG(NAOMI, "raw: %.2f MB set address space", romSize / 1024.f / 1024.f);
|
||||
if (romSize == 0) {
|
||||
delete fp;
|
||||
files.emplace_back(path);
|
||||
fstart.push_back(0);
|
||||
fsize.push_back(file_size);
|
||||
romSize = file_size;
|
||||
}
|
||||
|
||||
INFO_LOG(NAOMI, "+%zd romfiles, %.2f MB set address space", files.size(), romSize / 1024.f / 1024.f);
|
||||
if (romSize == 0)
|
||||
throw FlycastException(Ts("Invalid empty ROM"));
|
||||
|
||||
MD5Sum md5;
|
||||
}
|
||||
|
||||
// Allocate space for the rom
|
||||
u8 *romBase = (u8 *)malloc(romSize);
|
||||
if (romBase == nullptr)
|
||||
if (romBase == nullptr) {
|
||||
delete fp;
|
||||
throw FlycastException(Ts("Out of memory"));
|
||||
|
||||
bool load_error = false;
|
||||
|
||||
for (size_t i = 0; i<files.size(); i++)
|
||||
{
|
||||
hostfs::File *fp = nullptr;
|
||||
|
||||
if (files[i] != "null")
|
||||
{
|
||||
std::string filePath;
|
||||
if (folder.empty())
|
||||
filePath = files[i];
|
||||
else
|
||||
filePath = hostfs::storage().getSubPath(folder, files[i]);
|
||||
|
||||
fp = hostfs::storage().openFile(filePath, "rb");
|
||||
if (fp == nullptr)
|
||||
{
|
||||
ERROR_LOG(NAOMI, "Unable to open file %s: error %d", filePath.c_str(), errno);
|
||||
load_error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
u8* romDest = romBase + fstart[i];
|
||||
|
||||
if (fp == nullptr)
|
||||
{
|
||||
//printf("-Reserving ram at 0x%08X, size 0x%08X\n", fstart[i], fsize[i]);
|
||||
memset(romDest, -1, fsize[i]);
|
||||
}
|
||||
else
|
||||
{
|
||||
//printf("-Mapping \"%s\" at 0x%08X, size 0x%08X\n", files[i].c_str(), fstart[i], fsize[i]);
|
||||
bool mapped = fp->read(romDest, 1, fsize[i]) == fsize[i];
|
||||
if (config::GGPOEnable)
|
||||
md5.add(fp);
|
||||
delete fp;
|
||||
if (!mapped)
|
||||
{
|
||||
ERROR_LOG(NAOMI, "Unable to read file %s @ %08x size %x", files[i].c_str(),
|
||||
fstart[i], fsize[i]);
|
||||
load_error = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (load_error)
|
||||
u32 offset = 0;
|
||||
bool error = false;
|
||||
while (offset < romSize && !error)
|
||||
{
|
||||
const u32 len = std::min(8192u, romSize - offset);
|
||||
error = fp->read(romBase + offset, len, 1) != 1;
|
||||
offset += len;
|
||||
if (progress != nullptr)
|
||||
{
|
||||
if (progress->cancelled)
|
||||
break;
|
||||
progress->progress = (float)offset / romSize;
|
||||
}
|
||||
}
|
||||
delete fp;
|
||||
if (progress->cancelled) {
|
||||
free(romBase);
|
||||
throw LoadCancelledException();
|
||||
}
|
||||
if (error)
|
||||
{
|
||||
ERROR_LOG(NAOMI, "Unable to read file %s size %d", path.c_str(), romSize);
|
||||
free(romBase);
|
||||
throw FlycastException(Ts("Error: Failed to load BIN/DAT file"));
|
||||
}
|
||||
if (config::GGPOEnable)
|
||||
{
|
||||
MD5Sum md5;
|
||||
md5.add(romBase, romSize);
|
||||
md5.getDigest(settings.network.md5.game);
|
||||
}
|
||||
|
||||
DEBUG_LOG(NAOMI, "Legacy ROM loaded successfully");
|
||||
|
||||
@@ -705,6 +622,11 @@ void naomi_cart_LoadRom(const std::string& path, const std::string& fileName, Lo
|
||||
{
|
||||
serialModemInit();
|
||||
}
|
||||
else if ((gameId.substr(0, 4) == "WCCF" && config::MultiboardSlaves <= 1)
|
||||
|| gameId.substr(0, 15) == "DRAGON TREASURE")
|
||||
{
|
||||
card_reader::wccfInit();
|
||||
}
|
||||
if (gameId == " TOUCH DE UNOH -------------"
|
||||
|| gameId == " TOUCH DE UNOH 2 -----------"
|
||||
|| (gameId == "F355 CHALLENGE JAPAN" && (config::MultiboardSlaves == 2 || romName == "f355"))
|
||||
@@ -753,6 +675,41 @@ void naomi_cart_LoadRom(const std::string& path, const std::string& fileName, Lo
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (gameId.substr(0, 4) == "WCCF" && config::MultiboardSlaves > 1)
|
||||
{
|
||||
// Main projectors are directly linked by a null-modem cable
|
||||
config::BattleCableEnable.override(true);
|
||||
if (!config::loadBool("naomi", "WCCFSlave"))
|
||||
{
|
||||
config::ActAsServer.override(true);
|
||||
config::NetworkServer.override("localhost:37392");
|
||||
config::LocalPort.override(37391);
|
||||
int x = config::loadInt("window", "left", (1920 - 640) / 2);
|
||||
int w = config::loadInt("window", "width", 640);
|
||||
std::string region = "config:Dreamcast.Region=" + std::to_string(config::Region);
|
||||
std::string left = "window:left=" + std::to_string(x + w);
|
||||
std::string title = "window:title=\"" + Ts("Right")
|
||||
+ " - " + settings.content.title + '"';
|
||||
std::string server = "naomi:WCCFServer=" + config::loadStr("naomi", "WCCFServer");
|
||||
|
||||
const char *args[] = {
|
||||
"-config", "naomi:WCCFSlave=yes",
|
||||
"-config", server.c_str(),
|
||||
"-config", region.c_str(),
|
||||
"-config", left.c_str(),
|
||||
"-config", title.c_str(),
|
||||
"-config", "network:MultiboardSlaves=2",
|
||||
settings.content.path.c_str()
|
||||
};
|
||||
os_RunInstance(std::size(args), args);
|
||||
}
|
||||
else
|
||||
{
|
||||
config::ActAsServer.override(false);
|
||||
config::NetworkServer.override("localhost:37391");
|
||||
config::LocalPort.override(37392);
|
||||
}
|
||||
}
|
||||
#endif
|
||||
}
|
||||
else
|
||||
|
||||
@@ -20,11 +20,14 @@
|
||||
*/
|
||||
#include "naomi_flashrom.h"
|
||||
#include "hw/flashrom/nvmem.h"
|
||||
#include "hw/maple/maple_devs.h"
|
||||
#include "cfg/option.h"
|
||||
|
||||
static u16 eeprom_crc(const u8 *buf, int size)
|
||||
extern u8 *EEPROM;
|
||||
|
||||
// CRC-16-CCITT variant
|
||||
u16 eeprom_crc(const u8 *buf, int size)
|
||||
{
|
||||
// custom seed
|
||||
int n = 0xdebdeb00;
|
||||
|
||||
for (int i = 0; i < size; i++)
|
||||
@@ -40,6 +43,7 @@ static u16 eeprom_crc(const u8 *buf, int size)
|
||||
n <<= 1;
|
||||
}
|
||||
}
|
||||
// extra round with a virtual null trailing byte
|
||||
for (int c = 0; c < 8; c++)
|
||||
{
|
||||
if (n & 0x80000000)
|
||||
|
||||
@@ -27,3 +27,4 @@ u8 read_naomi_eeprom(u32 offset);
|
||||
void write_naomi_eeprom(u32 offset, u8 value);
|
||||
void configure_naomi_eeprom(const RomBootID *bootId);
|
||||
void configure_maxspeed_flash(bool enableNetwork, bool master);
|
||||
u16 eeprom_crc(const u8 *buf, int size);
|
||||
@@ -6141,6 +6141,7 @@ const Game Games[] =
|
||||
{ "317-0329-jpn.pic", 0, 0x4000, 0x097f5f92 },
|
||||
},
|
||||
"cdp-10001c",
|
||||
&wccf_inputs,
|
||||
},
|
||||
// World Club Champion Football Serie A 2001-2002 DIMM FIRM Ver.3.03 (CDP-10003)
|
||||
{
|
||||
@@ -6173,6 +6174,7 @@ const Game Games[] =
|
||||
// SH4-based camera sensor board connected to MAPLE CN7
|
||||
},
|
||||
"cdv-10002",
|
||||
&wccf_inputs,
|
||||
},
|
||||
// World Club Champion Football Serie A 2002-2003 Ver.2.34 (Japan) (CDV-10008)
|
||||
{
|
||||
@@ -6188,6 +6190,7 @@ const Game Games[] =
|
||||
{ "317-0369-jpn.pic", 0, 0x4000, 0xfb3672ba },
|
||||
},
|
||||
"cdv-10008",
|
||||
&wccf_inputs,
|
||||
},
|
||||
// World Club Champion Football European Clubs 2004-2005 (Asia) (CDV-10013)
|
||||
{
|
||||
@@ -6203,6 +6206,7 @@ const Game Games[] =
|
||||
{ "317-0419-jpn.pic", 0, 0x4000, 0x04fae86b },
|
||||
},
|
||||
"cdv-10013",
|
||||
&wccf_inputs,
|
||||
},
|
||||
// World Club Champion Football European Clubs 2004-2005 Ver.1.1 (Export) (CDV-10015)
|
||||
{
|
||||
@@ -6218,6 +6222,7 @@ const Game Games[] =
|
||||
{ "317-0419-exp.pic", 0, 0x4000, 0x3f5e1445 },
|
||||
},
|
||||
"cdv-10015",
|
||||
&wccf_inputs,
|
||||
},
|
||||
// World Club Champion Football European Clubs 2004-2005 Ver.3.22 (Export) (CDV-10015P)
|
||||
{
|
||||
@@ -6233,6 +6238,7 @@ const Game Games[] =
|
||||
{ "317-0419-exp.pic", 0, 0x4000, 0x3f5e1445 },
|
||||
},
|
||||
"cdv-10015p",
|
||||
&wccf_inputs,
|
||||
},
|
||||
// World Club Champion Football European Clubs 2004-2005 Ver.1.1 (Japan) (CDV-10020)
|
||||
{
|
||||
@@ -6248,6 +6254,7 @@ const Game Games[] =
|
||||
{ "317-unk-jpn.pic", 0, 0x4000, 0xa4abd76b },
|
||||
},
|
||||
"cdv-10020",
|
||||
&wccf_inputs,
|
||||
},
|
||||
// World Club Champion Football European Clubs 2004-2005 Ver.1.2 (Japan) (CDV-10021)
|
||||
{
|
||||
@@ -6263,6 +6270,7 @@ const Game Games[] =
|
||||
{ "317-unk-jpn.pic", 0, 0x4000, 0xa4abd76b },
|
||||
},
|
||||
"cdv-10021",
|
||||
&wccf_inputs,
|
||||
},
|
||||
// World Club Champion Football European Clubs 2005-2006 (Japan) (CDV-10025)
|
||||
{
|
||||
@@ -6278,6 +6286,7 @@ const Game Games[] =
|
||||
{ "317-0456-jpn.pic", 0, 0x4000, 0xcf3bd834 },
|
||||
},
|
||||
"cdv-10025",
|
||||
&wccf_inputs,
|
||||
},
|
||||
// World Club Champion Football European Clubs 2005-2006 (Export) (CDV-10027)
|
||||
{
|
||||
@@ -6293,6 +6302,7 @@ const Game Games[] =
|
||||
{ "317-0456-exp.pic", 0, 0x4000, 0xfb7dd592 },
|
||||
},
|
||||
"cdv-10027",
|
||||
&wccf_inputs,
|
||||
},
|
||||
|
||||
// AtomisWave Roms
|
||||
|
||||
@@ -628,6 +628,33 @@ static InputDescriptors kick4csh_inputs = {
|
||||
NAO_BASE_BTN_DESC
|
||||
}
|
||||
};
|
||||
|
||||
static InputDescriptors wccf_inputs = {
|
||||
{
|
||||
// UDLR START
|
||||
// NAOMI_BTN0_KEY "PRESSURE"
|
||||
// NAOMI_BTN1_KEY "SHOOT"
|
||||
// NAOMI_BTN2_KEY card vendor error
|
||||
// NAOMI_BTN3_KEY card vendor empty
|
||||
// P2:
|
||||
// NAOMI_BTN0_KEY "KEEPER"
|
||||
// NAOMI_BTN1_KEY card vendor standby
|
||||
// NAOMI_BTN2_KEY "DATA"
|
||||
// UP club team card
|
||||
// DOWN SW1
|
||||
// LEFT SW2
|
||||
// RIGHT !card vendor sensor
|
||||
{ NAOMI_BTN0_KEY, "PRESSURE" },
|
||||
{ NAOMI_BTN1_KEY, "SHOOT" },
|
||||
{ NAOMI_BTN2_KEY, "SW1", 0, NAOMI_DOWN_KEY },
|
||||
{ NAOMI_BTN3_KEY, "KEEPER", 0, NAOMI_BTN0_KEY },
|
||||
{ NAOMI_BTN4_KEY, "DATA", 0, NAOMI_BTN2_KEY },
|
||||
NAO_DPAD_DESC
|
||||
NAO_START_DESC
|
||||
NAO_BASE_BTN_DESC
|
||||
}
|
||||
};
|
||||
|
||||
//
|
||||
// AtomisWave games
|
||||
//
|
||||
|
||||
+1055
-122
File diff suppressed because it is too large.
Load diff
+38
-25
@@ -19,11 +19,21 @@
|
||||
#pragma once
|
||||
#include "gdcartridge.h"
|
||||
#include "network/net_platform.h"
|
||||
#include "util/tsqueue.h"
|
||||
#include <memory>
|
||||
#include <functional>
|
||||
|
||||
//#define NET_TRACE
|
||||
|
||||
class NetDimmServer;
|
||||
|
||||
class NetDimm : public GDCartridge
|
||||
{
|
||||
public:
|
||||
enum ServerMsg { ControlRead, Reboot };
|
||||
|
||||
NetDimm(u32 size);
|
||||
~NetDimm();
|
||||
|
||||
void Init(LoadProgress *progress = nullptr, std::vector<u8> *digest = nullptr) override;
|
||||
|
||||
@@ -31,6 +41,15 @@ public:
|
||||
|
||||
void Deserialize(Deserializer &deser) override;
|
||||
|
||||
u32 getDimmSize() const { return dimm_data_size; }
|
||||
|
||||
u8 *getDimmData(u32 offset) {
|
||||
return &dimm_data[offset & (dimm_data_size - 1)];
|
||||
}
|
||||
|
||||
void controlRead(std::function<void(u32)> callback);
|
||||
void reboot();
|
||||
|
||||
protected:
|
||||
void process() override;
|
||||
int schedCallback() override;
|
||||
@@ -62,12 +81,7 @@ private:
|
||||
dimm_parameterh = value >> 16;
|
||||
}
|
||||
|
||||
sock_t getSocket(int idx)
|
||||
{
|
||||
if (idx < 1 || idx > (int)sockets.size())
|
||||
return INVALID_SOCKET;
|
||||
return sockets[idx - 1].fd;
|
||||
}
|
||||
sock_t getSocket(int idx);
|
||||
|
||||
bool isBusy() const
|
||||
{
|
||||
@@ -81,23 +95,7 @@ private:
|
||||
Socket() = default;
|
||||
Socket(sock_t fd) : fd(fd) {}
|
||||
|
||||
int close()
|
||||
{
|
||||
int rc = 0;
|
||||
if (fd != INVALID_SOCKET)
|
||||
rc = ::closesocket(fd);
|
||||
fd = INVALID_SOCKET;
|
||||
connecting = false;
|
||||
receiving = false;
|
||||
sending = false;
|
||||
connectTimeout = 0;
|
||||
connectTime = 0;
|
||||
sendTimeout = 0;
|
||||
sendTime = 0;
|
||||
recvTimeout = 0;
|
||||
recvTime = 0;
|
||||
return rc;
|
||||
}
|
||||
int close();
|
||||
|
||||
bool isClosed() const {
|
||||
return fd == INVALID_SOCKET;
|
||||
@@ -122,13 +120,28 @@ private:
|
||||
u64 recvTimeout = 0;
|
||||
u64 recvTime = 0;
|
||||
int lastError = 0;
|
||||
int port = 0;
|
||||
sockaddr *srcAddr = nullptr;
|
||||
socklen_t *addrLen = nullptr;
|
||||
#ifdef NET_TRACE
|
||||
void openTrace();
|
||||
void closeTrace();
|
||||
void traceRecv(const u8 *data, size_t len);
|
||||
void traceSend(const u8 *data, size_t len);
|
||||
|
||||
FILE *trcFile = nullptr;
|
||||
#endif
|
||||
};
|
||||
std::vector<Socket> sockets;
|
||||
int lastError = 0; // for socket() and select()
|
||||
bool dnsInProgress = false;
|
||||
u32 serverIp = 0; //0x0100007f for testing only
|
||||
bool finalTuned = false;
|
||||
bool wccf = false;
|
||||
|
||||
u32 dimmBufferOffset = 0x0f000000;
|
||||
std::unique_ptr<NetDimmServer> server;
|
||||
std::function<void(u32)> controlReadCallback;
|
||||
TsQueue<ServerMsg> serverQueue;
|
||||
|
||||
static constexpr int POLL_CYCLES = SH4_MAIN_CLOCK / 60;
|
||||
static constexpr int POLL_CYCLES = SH4_MAIN_CLOCK / 120;
|
||||
};
|
||||
@@ -1203,12 +1203,7 @@ void serialize(Serializer& ser)
|
||||
void deserialize(Deserializer& deser)
|
||||
{
|
||||
if (printer != nullptr)
|
||||
{
|
||||
if (deser.version() >= Deserializer::V35)
|
||||
printer->deserialize(deser);
|
||||
else
|
||||
init();
|
||||
}
|
||||
printer->deserialize(deser);
|
||||
}
|
||||
#endif
|
||||
|
||||
|
||||
@@ -30,6 +30,7 @@
|
||||
#include "hw/holly/sb.h"
|
||||
#include "oslib/storage.h"
|
||||
#include "oslib/oslib.h"
|
||||
#include "oslib/directory.h"
|
||||
#include "cfg/option.h"
|
||||
#include "card_reader.h"
|
||||
#include "naomi_roms.h"
|
||||
@@ -342,17 +343,9 @@ public:
|
||||
deser >> size;
|
||||
recvBuffer.resize(size);
|
||||
deser.deserialize(recvBuffer.data(), recvBuffer.size());
|
||||
if (deser.version() >= Deserializer::V41)
|
||||
{
|
||||
deser >> state;
|
||||
deser >> rowCounter;
|
||||
deser.deserialize(cardData.data(), cardData.size());
|
||||
}
|
||||
else
|
||||
{
|
||||
state = Off;
|
||||
rowCounter = 0;
|
||||
}
|
||||
deser >> state;
|
||||
deser >> rowCounter;
|
||||
deser.deserialize(cardData.data(), cardData.size());
|
||||
}
|
||||
|
||||
private:
|
||||
@@ -432,13 +425,7 @@ private:
|
||||
|
||||
void saveData()
|
||||
{
|
||||
FILE *f = nowide::fopen(getCardDataPath().c_str(), "wb");
|
||||
if (f == nullptr) {
|
||||
WARN_LOG(NAOMI, "Can't save RFID card: error %x", errno);
|
||||
return;
|
||||
}
|
||||
fwrite(cardData.data(), 1, cardData.size(), f);
|
||||
fclose(f);
|
||||
safeFullSave(getCardDataPath(), cardData.data(), cardData.size());
|
||||
}
|
||||
|
||||
u32 dinoShuffle(u32 v) {
|
||||
@@ -683,7 +670,7 @@ private:
|
||||
SerialPort *port;
|
||||
std::deque<u8> toSend;
|
||||
std::vector<u8> recvBuffer;
|
||||
int schedId = 0;
|
||||
int schedId;
|
||||
u32 lastPosX = ~0;
|
||||
u32 lastPosY = ~0;
|
||||
bool lastButton = false;
|
||||
|
||||
@@ -26,6 +26,7 @@
|
||||
#include "serialize.h"
|
||||
#include "network/net_platform.h"
|
||||
#include <memory>
|
||||
#include <vector>
|
||||
|
||||
namespace systemsp
|
||||
{
|
||||
|
||||
+63
-23
@@ -1,7 +1,9 @@
|
||||
#include "Renderer_if.h"
|
||||
#include "spg.h"
|
||||
#include "rend/CustomTexture.h"
|
||||
#include "rend/texconv.h"
|
||||
#include "rend/transform_matrix.h"
|
||||
#include "pvr_mem.h"
|
||||
#include "cfg/option.h"
|
||||
#include "emulator.h"
|
||||
#include "serialize.h"
|
||||
@@ -12,6 +14,7 @@
|
||||
#include "profiler/fc_profiler.h"
|
||||
#include "network/ggpo.h"
|
||||
|
||||
#include <cassert>
|
||||
#include <mutex>
|
||||
#include <deque>
|
||||
|
||||
@@ -30,9 +33,6 @@ static cResetEvent vramRollback;
|
||||
|
||||
// direct framebuffer write detection
|
||||
static bool render_called = false;
|
||||
u32 fb_watch_addr_start;
|
||||
u32 fb_watch_addr_end;
|
||||
bool fb_dirty;
|
||||
|
||||
static bool pend_rend;
|
||||
static bool rendererEnabled = true;
|
||||
@@ -199,6 +199,7 @@ private:
|
||||
TA_context *taContext = DequeueRender();
|
||||
if (taContext == nullptr)
|
||||
return;
|
||||
renderer->processGpuCleanupOperations();
|
||||
|
||||
// tile clipping is used to calculate framebuffer size in RTT below
|
||||
setTileClipping(taContext->rend);
|
||||
@@ -252,6 +253,7 @@ private:
|
||||
void renderFramebuffer(const FramebufferInfo& config)
|
||||
{
|
||||
FC_PROFILE_SCOPE;
|
||||
renderer->processGpuCleanupOperations();
|
||||
|
||||
#ifdef LIBRETRO
|
||||
int w, h;
|
||||
@@ -487,12 +489,65 @@ void rend_term_renderer()
|
||||
|
||||
if (renderer != nullptr)
|
||||
{
|
||||
custom_texture.invalidateGpuPreloads();
|
||||
renderer->Term();
|
||||
delete renderer;
|
||||
renderer = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
void rend_process_custom_texture_preloads()
|
||||
{
|
||||
if (renderer != nullptr)
|
||||
{
|
||||
renderer->processGpuCleanupOperations();
|
||||
renderer->processCustomTexturePreloads();
|
||||
}
|
||||
}
|
||||
|
||||
bool rend_supports_gpu_texture_preload()
|
||||
{
|
||||
return renderer != nullptr && renderer->supportsGpuTexturePreload();
|
||||
}
|
||||
|
||||
std::shared_ptr<GpuPreloadedTexture> Renderer::findGpuPreloadedTexture(
|
||||
u32 currentHash, u32 oldVqHash, u32 oldHash) const
|
||||
{
|
||||
const auto find = [this](u32 hash) -> std::shared_ptr<GpuPreloadedTexture> {
|
||||
const auto found = gpuPreloadedTextures.find(hash);
|
||||
return found == gpuPreloadedTextures.end() ? nullptr : found->second;
|
||||
};
|
||||
if (auto texture = find(currentHash))
|
||||
return texture;
|
||||
if (oldVqHash != 0)
|
||||
if (auto texture = find(oldVqHash))
|
||||
return texture;
|
||||
if (oldHash != 0)
|
||||
return find(oldHash);
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
void Renderer::addGpuPreloadedTexture(u32 hash, std::shared_ptr<GpuPreloadedTexture> texture)
|
||||
{
|
||||
assert(texture);
|
||||
gpuPreloadedTextures.emplace(hash, std::move(texture));
|
||||
}
|
||||
|
||||
void Renderer::clearGpuPreloadedTextures()
|
||||
{
|
||||
gpuPreloadedTextures.clear();
|
||||
}
|
||||
|
||||
void Renderer::processGpuCleanupOperations()
|
||||
{
|
||||
if (custom_texture.consumeGpuCleanupOperations())
|
||||
{
|
||||
clearTextureCache();
|
||||
clearGpuPreloadedTextures();
|
||||
resetTextureCache = false;
|
||||
}
|
||||
}
|
||||
|
||||
void rend_reset()
|
||||
{
|
||||
FinishRender(DequeueRender());
|
||||
@@ -611,7 +666,7 @@ int rend_end_render(int tag, int cycles, int jitter, void *arg)
|
||||
void rend_vblank()
|
||||
{
|
||||
if (config::EmulateFramebuffer
|
||||
|| (!render_called && fb_dirty && FB_R_CTRL.fb_enable))
|
||||
|| (!render_called && FB_R_CTRL.fb_enable && FramebufferWatcher::Instance().isDirty()))
|
||||
{
|
||||
if (rend_is_enabled())
|
||||
{
|
||||
@@ -622,21 +677,13 @@ void rend_vblank()
|
||||
if (!config::EmulateFramebuffer)
|
||||
DEBUG_LOG(PVR, "Direct framebuffer write detected");
|
||||
}
|
||||
fb_dirty = false;
|
||||
}
|
||||
|
||||
render_called = false;
|
||||
check_framebuffer_write();
|
||||
emu.vblank();
|
||||
swapIntervalDetector.vblank();
|
||||
}
|
||||
|
||||
void check_framebuffer_write()
|
||||
{
|
||||
u32 fb_size = (FB_R_SIZE.fb_y_size + 1) * (FB_R_SIZE.fb_x_size + FB_R_SIZE.fb_modulus) * 4;
|
||||
fb_watch_addr_start = (SPG_CONTROL.interlace ? FB_R_SOF2 : FB_R_SOF1) & VRAM_MASK;
|
||||
fb_watch_addr_end = fb_watch_addr_start + fb_size;
|
||||
}
|
||||
|
||||
void rend_cancel_emu_wait()
|
||||
{
|
||||
if (config::ThreadedRendering)
|
||||
@@ -693,20 +740,13 @@ void rend_serialize(Serializer& ser)
|
||||
{
|
||||
ser << fb_w_cur;
|
||||
ser << render_called;
|
||||
ser << fb_dirty;
|
||||
ser << fb_watch_addr_start;
|
||||
ser << fb_watch_addr_end;
|
||||
FramebufferWatcher::Instance().serialize(ser);
|
||||
}
|
||||
void rend_deserialize(Deserializer& deser)
|
||||
{
|
||||
deser >> fb_w_cur;
|
||||
if (deser.version() >= Deserializer::V20)
|
||||
{
|
||||
deser >> render_called;
|
||||
deser >> fb_dirty;
|
||||
deser >> fb_watch_addr_start;
|
||||
deser >> fb_watch_addr_end;
|
||||
}
|
||||
deser >> render_called;
|
||||
FramebufferWatcher::Instance().deserialize(deser);
|
||||
pend_rend = false;
|
||||
fbAddrHistory[0] = 1;
|
||||
fbAddrHistory[1] = 1;
|
||||
|
||||
@@ -2,8 +2,13 @@
|
||||
#include "types.h"
|
||||
#include "ta_ctx.h"
|
||||
#include "emulator.h"
|
||||
#include <string>
|
||||
#include <memory>
|
||||
#include <unordered_map>
|
||||
#include <vector>
|
||||
|
||||
struct GpuPreloadedTexture;
|
||||
|
||||
extern u32 FrameCount;
|
||||
|
||||
bool rend_init_renderer();
|
||||
@@ -21,6 +26,8 @@ void rend_start_rollback();
|
||||
void rend_allow_rollback();
|
||||
void rend_enable_renderer(bool enabled);
|
||||
bool rend_is_enabled();
|
||||
void rend_process_custom_texture_preloads();
|
||||
bool rend_supports_gpu_texture_preload();
|
||||
void rend_serialize(Serializer& ser);
|
||||
void rend_deserialize(Deserializer& deser);
|
||||
static void rend_updatePalette();
|
||||
@@ -75,10 +82,20 @@ struct Renderer
|
||||
virtual bool GetLastFrame(std::vector<u8>& data, int& width, int& height) { return false; }
|
||||
|
||||
virtual bool Present() { return true; }
|
||||
virtual void processCustomTexturePreloads() {}
|
||||
virtual bool supportsGpuTexturePreload() const { return false; }
|
||||
|
||||
virtual BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw, int area = 0) { return nullptr; }
|
||||
std::shared_ptr<GpuPreloadedTexture> findGpuPreloadedTexture(
|
||||
u32 currentHash, u32 oldVqHash, u32 oldHash) const;
|
||||
void processGpuCleanupOperations();
|
||||
|
||||
protected:
|
||||
virtual void clearTextureCache() {}
|
||||
void addGpuPreloadedTexture(u32 hash, std::shared_ptr<GpuPreloadedTexture> texture);
|
||||
void clearGpuPreloadedTextures();
|
||||
|
||||
std::unordered_map<u32, std::shared_ptr<GpuPreloadedTexture>> gpuPreloadedTextures;
|
||||
bool resetTextureCache = false;
|
||||
bool clearLastFrame = false;
|
||||
bool updatePalette = true;
|
||||
@@ -100,11 +117,6 @@ private:
|
||||
|
||||
extern Renderer* renderer;
|
||||
|
||||
extern u32 fb_watch_addr_start;
|
||||
extern u32 fb_watch_addr_end;
|
||||
extern bool fb_dirty;
|
||||
|
||||
void check_framebuffer_write();
|
||||
static inline void rend_updatePalette() {
|
||||
if (renderer != nullptr)
|
||||
renderer->updatePalette = true;
|
||||
|
||||
+3
-18
@@ -514,26 +514,13 @@ struct State
|
||||
void deserialize(Deserializer& deser)
|
||||
{
|
||||
projMatrixIdx = -1;
|
||||
if (deser.version() < Deserializer::V24)
|
||||
{
|
||||
reset();
|
||||
resetProjectionMatrix();
|
||||
return;
|
||||
}
|
||||
ta_parse_reset();
|
||||
u32 listType;
|
||||
deser >> listType;
|
||||
ta_set_list_type(listType);
|
||||
deser >> gmp;
|
||||
deser >> instance;
|
||||
if (deser.version() < Deserializer::V40)
|
||||
{
|
||||
deser.skip<u32>(); // projMatrix address
|
||||
resetProjectionMatrix();
|
||||
}
|
||||
else {
|
||||
deser >> projMatrix;
|
||||
}
|
||||
deser >> projMatrix;
|
||||
u32 tileclip;
|
||||
deser >> tileclip;
|
||||
ta_set_tileclip(tileclip);
|
||||
@@ -1795,10 +1782,8 @@ void reset(bool hard)
|
||||
|
||||
void term()
|
||||
{
|
||||
if (schedId != -1) {
|
||||
sh4_sched_unregister(schedId);
|
||||
schedId = -1;
|
||||
}
|
||||
sh4_sched_unregister(schedId);
|
||||
schedId = -1;
|
||||
}
|
||||
|
||||
void vmem_init()
|
||||
|
||||
+1
-4
@@ -97,10 +97,7 @@ void deserialize(Deserializer& deser)
|
||||
|
||||
deser >> ta_fsm[2048];
|
||||
deser >> ta_fsm_cl;
|
||||
if (deser.version() >= Deserializer::V29)
|
||||
deser >> taRenderPass;
|
||||
else
|
||||
taRenderPass = 0;
|
||||
deser >> taRenderPass;
|
||||
DeserializeTAContext(deser);
|
||||
|
||||
if (!deser.rollback())
|
||||
|
||||
+93
-26
@@ -11,8 +11,6 @@
|
||||
#include "hw/holly/holly_intc.h"
|
||||
#include "serialize.h"
|
||||
|
||||
static u32 pvr_map32(u32 offset32);
|
||||
|
||||
RamRegion vram;
|
||||
|
||||
// YUV converter code
|
||||
@@ -226,11 +224,14 @@ void DYNACALL pvr_write32p(u32 addr, T data)
|
||||
return;
|
||||
}
|
||||
addr &= ~(sizeof(T) - 1);
|
||||
u32 vaddr = addr & VRAM_MASK;
|
||||
if (vaddr >= fb_watch_addr_start && vaddr < fb_watch_addr_end)
|
||||
fb_dirty = true;
|
||||
|
||||
*(T *)&vram[pvr_map32(addr)] = data;
|
||||
T *pVram = (T *)&vram[pvr_map32(addr)];
|
||||
if constexpr (!Internal)
|
||||
{
|
||||
// check needed to avoid spurious FB write detections with VF3tb history videos
|
||||
if (data != *pVram)
|
||||
FramebufferWatcher::Instance().vramCheck(addr & VRAM_MASK);
|
||||
}
|
||||
*pVram = data;
|
||||
}
|
||||
template void pvr_write32p<u8, false>(u32 addr, u8 data);
|
||||
template void pvr_write32p<u8, true>(u32 addr, u8 data);
|
||||
@@ -284,25 +285,6 @@ void DYNACALL TAWriteSQ(u32 address, const SQBuffer *sqb)
|
||||
|
||||
//Misc interface
|
||||
|
||||
#define VRAM_BANK_BIT 0x400000
|
||||
|
||||
static u32 pvr_map32(u32 offset32)
|
||||
{
|
||||
//64b wide bus is achieved by interleaving the banks every 32 bits
|
||||
const u32 static_bits = VRAM_MASK - (VRAM_BANK_BIT * 2 - 1) + 3;
|
||||
const u32 offset_bits = (VRAM_BANK_BIT - 1) & ~3;
|
||||
|
||||
u32 bank = (offset32 & VRAM_BANK_BIT) / VRAM_BANK_BIT;
|
||||
|
||||
u32 rv = offset32 & static_bits;
|
||||
|
||||
rv |= (offset32 & offset_bits) * 2;
|
||||
|
||||
rv |= bank * 4;
|
||||
|
||||
return rv;
|
||||
}
|
||||
|
||||
template<typename T, bool upper>
|
||||
T DYNACALL pvr_read_area4(u32 addr)
|
||||
{
|
||||
@@ -334,3 +316,88 @@ template void pvr_write_area4<u32, false>(u32 addr, u32 data);
|
||||
template void pvr_write_area4<u8, true>(u32 addr, u8 data);
|
||||
template void pvr_write_area4<u16, true>(u32 addr, u16 data);
|
||||
template void pvr_write_area4<u32, true>(u32 addr, u32 data);
|
||||
|
||||
|
||||
FramebufferWatcher::FramebufferWatcher() {
|
||||
fbSizeUpdated();
|
||||
fbSofUpdated(FB_R_SOF1);
|
||||
}
|
||||
|
||||
void FramebufferWatcher::fbSizeUpdated()
|
||||
{
|
||||
size = (FB_R_SIZE.fb_y_size + 1) * (FB_R_SIZE.fb_x_size + FB_R_SIZE.fb_modulus) * 4;
|
||||
fbs[0].end = fbs[0].start + size;
|
||||
fbs[1].end = fbs[1].start + size;
|
||||
}
|
||||
|
||||
void FramebufferWatcher::fbSofUpdated(u32 sof)
|
||||
{
|
||||
u32 waddr = sof & VRAM_MASK;
|
||||
if (waddr == fbs[0].start || waddr == fbs[1].start)
|
||||
return;
|
||||
fbs[1] = fbs[0];
|
||||
fbs[0].start = waddr;
|
||||
fbs[0].end = waddr + size;
|
||||
fbs[0].dirty = false;
|
||||
DEBUG_LOG(PVR, "FramebufferWatcher: watching new %x old %x SOF1 %x", fbs[0].start, fbs[1].start, sof);
|
||||
}
|
||||
|
||||
void FramebufferWatcher::vramCheck(u32 vramOffset)
|
||||
{
|
||||
for (Framebuffer& fb : fbs)
|
||||
{
|
||||
if (vramOffset >= fb.start && vramOffset < fb.end)
|
||||
{
|
||||
if (!fb.dirty)
|
||||
DEBUG_LOG(PVR, "FB Write detected in [%x - %x]: %x", fb.start, fb.end, vramOffset);
|
||||
fb.dirty = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
bool FramebufferWatcher::isDirty()
|
||||
{
|
||||
for (Framebuffer& fb : fbs)
|
||||
if (fb.dirty) {
|
||||
fb.dirty = false;
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void FramebufferWatcher::serialize(Serializer& ser)
|
||||
{
|
||||
for (const Framebuffer& fb : fbs)
|
||||
{
|
||||
ser << fb.dirty;
|
||||
ser << fb.start;
|
||||
ser << fb.end;
|
||||
}
|
||||
}
|
||||
|
||||
void FramebufferWatcher::deserialize(Deserializer& deser)
|
||||
{
|
||||
if (deser.version() >= Deserializer::V61)
|
||||
{
|
||||
for (Framebuffer& fb : fbs)
|
||||
{
|
||||
deser >> fb.dirty;
|
||||
deser >> fb.start;
|
||||
deser >> fb.end;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
deser >> fbs[0].dirty;
|
||||
deser >> fbs[0].start;
|
||||
deser >> fbs[0].end;
|
||||
fbs[1] = {};
|
||||
}
|
||||
fbSizeUpdated();
|
||||
}
|
||||
|
||||
FramebufferWatcher& FramebufferWatcher::Instance() {
|
||||
static FramebufferWatcher instance;
|
||||
return instance;
|
||||
}
|
||||
@@ -19,8 +19,45 @@ void YUV_deserialize(Deserializer& deser);
|
||||
void YUV_reset();
|
||||
|
||||
// 32-bit vram path handlers
|
||||
// The 32-bit bus interleaves the two VRAM banks every 32 bits.
|
||||
constexpr u32 VRAM_32_BANK_BIT = 0x400000;
|
||||
inline u32 pvr_map32(u32 offset32)
|
||||
{
|
||||
const u32 static_bits = VRAM_MASK - (VRAM_32_BANK_BIT * 2 - 1) + 3;
|
||||
const u32 offset_bits = (VRAM_32_BANK_BIT - 1) & ~3;
|
||||
const u32 bank = (offset32 & VRAM_32_BANK_BIT) / VRAM_32_BANK_BIT;
|
||||
|
||||
u32 rv = offset32 & static_bits;
|
||||
rv |= (offset32 & offset_bits) * 2;
|
||||
rv |= bank * 4;
|
||||
return rv;
|
||||
}
|
||||
|
||||
template<typename T> T DYNACALL pvr_read32p(u32 addr);
|
||||
template<typename T, bool Internal = false> void DYNACALL pvr_write32p(u32 addr, T data);
|
||||
// Area 4 handlers
|
||||
template<typename T, bool upper> T DYNACALL pvr_read_area4(u32 addr);
|
||||
template<typename T, bool upper> void DYNACALL pvr_write_area4(u32 addr, T data);
|
||||
|
||||
class FramebufferWatcher
|
||||
{
|
||||
FramebufferWatcher();
|
||||
|
||||
struct Framebuffer {
|
||||
u32 start = 0;
|
||||
u32 end = 0;
|
||||
bool dirty = false;
|
||||
};
|
||||
Framebuffer fbs[2];
|
||||
u32 size = 0;
|
||||
|
||||
public:
|
||||
void fbSizeUpdated();
|
||||
void fbSofUpdated(u32 sof);
|
||||
void vramCheck(u32 vramOffset);
|
||||
bool isDirty();
|
||||
void serialize(Serializer& ser);
|
||||
void deserialize(Deserializer& deser);
|
||||
|
||||
static FramebufferWatcher& Instance();
|
||||
};
|
||||
@@ -171,11 +171,9 @@ void pvr_WriteReg(u32 paddr,u32 data)
|
||||
return;
|
||||
|
||||
case FB_R_SIZE_addr:
|
||||
if (PvrReg(addr, u32) != data)
|
||||
{
|
||||
if (PvrReg(addr, u32) != data) {
|
||||
PvrReg(addr, u32) = data;
|
||||
fb_dirty = false;
|
||||
check_framebuffer_write();
|
||||
FramebufferWatcher::Instance().fbSizeUpdated();
|
||||
}
|
||||
return;
|
||||
|
||||
@@ -192,6 +190,8 @@ void pvr_WriteReg(u32 paddr,u32 data)
|
||||
case FB_R_SOF1_addr:
|
||||
case FB_R_SOF2_addr:
|
||||
data &= 0x00fffffc;
|
||||
if (addr == FB_R_SOF1_addr)
|
||||
FramebufferWatcher::Instance().fbSofUpdated(data);
|
||||
rend_swap_frame(data);
|
||||
break;
|
||||
|
||||
|
||||
+9
-4
@@ -28,8 +28,8 @@ static u64 last_fps;
|
||||
constexpr int PIXEL_CLOCK = 27 * 1000 * 1000;
|
||||
static u32 Line_Cycles;
|
||||
static u32 Frame_Cycles;
|
||||
int render_end_schid;
|
||||
int vblank_schid;
|
||||
int render_end_schid = -1;
|
||||
int vblank_schid = -1;
|
||||
|
||||
static std::array<double, 4> real_times;
|
||||
static std::array<u64, 4> cpu_cycles;
|
||||
@@ -319,11 +319,12 @@ void spg_Serialize(Serializer& ser)
|
||||
ser << Frame_Cycles;
|
||||
ser << lightgun_line;
|
||||
ser << lightgun_hpos;
|
||||
sh4_sched_serialize(ser, vblank_schid);
|
||||
sh4_sched_serialize(ser, render_end_schid);
|
||||
}
|
||||
|
||||
void spg_Deserialize(Deserializer& deser)
|
||||
{
|
||||
if (deser.version() < Deserializer::V30)
|
||||
deser.skip<u32>(); // in_vblank
|
||||
deser >> clc_pvr_scanline;
|
||||
deser >> maple_int_pending;
|
||||
deser >> pvr_numscanlines;
|
||||
@@ -332,4 +333,8 @@ void spg_Deserialize(Deserializer& deser)
|
||||
deser >> Frame_Cycles;
|
||||
deser >> lightgun_line;
|
||||
deser >> lightgun_hpos;
|
||||
if (deser.version() >= Deserializer::V62) {
|
||||
sh4_sched_deserialize(deser, vblank_schid);
|
||||
sh4_sched_deserialize(deser, render_end_schid);
|
||||
}
|
||||
}
|
||||
+12
-30
@@ -245,12 +245,6 @@ static void deserializeContext(Deserializer& deser, TA_context **pctx)
|
||||
tad_context& tad = (*pctx)->tad;
|
||||
deser.deserialize(tad.thd_root, size);
|
||||
tad.thd_data = tad.thd_root + size;
|
||||
if (deser.version() < Deserializer::V26)
|
||||
{
|
||||
u32 render_pass_count;
|
||||
deser >> render_pass_count;
|
||||
deser.skip(sizeof(u32) * render_pass_count);
|
||||
}
|
||||
}
|
||||
|
||||
void SerializeTAContext(Serializer& ser)
|
||||
@@ -270,30 +264,18 @@ void DeserializeTAContext(Deserializer& deser)
|
||||
{
|
||||
if (::ta_ctx != nullptr)
|
||||
SetCurrentTARC(TACTX_NONE);
|
||||
if (deser.version() >= Deserializer::V25)
|
||||
u32 listSize;
|
||||
deser >> listSize;
|
||||
for (const auto& ctx : ctx_list)
|
||||
tactx_Recycle(ctx);
|
||||
ctx_list.clear();
|
||||
for (u32 i = 0; i < listSize; i++)
|
||||
{
|
||||
u32 listSize;
|
||||
deser >> listSize;
|
||||
for (const auto& ctx : ctx_list)
|
||||
tactx_Recycle(ctx);
|
||||
ctx_list.clear();
|
||||
for (u32 i = 0; i < listSize; i++)
|
||||
{
|
||||
TA_context *ctx;
|
||||
deserializeContext(deser, &ctx);
|
||||
}
|
||||
int curCtx;
|
||||
deser >> curCtx;
|
||||
if (curCtx >= 0 && curCtx < (int)ctx_list.size())
|
||||
SetCurrentTARC(ctx_list[curCtx]->Address);
|
||||
}
|
||||
else
|
||||
{
|
||||
TA_context *ta_cur_ctx;
|
||||
deserializeContext(deser, &ta_cur_ctx);
|
||||
if (ta_cur_ctx != nullptr)
|
||||
SetCurrentTARC(ta_cur_ctx->Address);
|
||||
if (deser.version() >= Deserializer::V20)
|
||||
deserializeContext(deser, &ta_cur_ctx);
|
||||
TA_context *ctx;
|
||||
deserializeContext(deser, &ctx);
|
||||
}
|
||||
int curCtx;
|
||||
deser >> curCtx;
|
||||
if (curCtx >= 0 && curCtx < (int)ctx_list.size())
|
||||
SetCurrentTARC(ctx_list[curCtx]->Address);
|
||||
}
|
||||
+57
-28
@@ -36,6 +36,31 @@ static f32 f16(u16 v)
|
||||
return *(f32*)&z;
|
||||
}
|
||||
|
||||
static void loadTexture(PolyParam *pp, int index)
|
||||
{
|
||||
if (pp->pcw.Texture == 0)
|
||||
return;
|
||||
if (index == 0)
|
||||
{
|
||||
pp->texture = renderer->GetTexture(pp->tsp, pp->tcw, 0);
|
||||
if (pp->texture == nullptr)
|
||||
pp->pcw.Texture = 0;
|
||||
}
|
||||
else
|
||||
{
|
||||
pp->texture1 = renderer->GetTexture(pp->tsp1, pp->tcw1, 1);
|
||||
if (pp->texture1 == nullptr)
|
||||
pp->pcw.Texture = 0;
|
||||
}
|
||||
}
|
||||
|
||||
static void loadTextures(PolyParam *pp)
|
||||
{
|
||||
loadTexture(pp, 0);
|
||||
if (pp->tsp1.full != (u32)-1)
|
||||
loadTexture(pp, 1);
|
||||
}
|
||||
|
||||
class BaseTAParser
|
||||
{
|
||||
static Ta_Dma *DYNACALL NullVertexData(Ta_Dma *data, Ta_Dma *data_end)
|
||||
@@ -549,8 +574,8 @@ private:
|
||||
d_pp->pcw = pp->pcw;
|
||||
d_pp->tileclip = tileclip_val;
|
||||
|
||||
if (d_pp->pcw.Texture && fetchTextures)
|
||||
d_pp->texture = renderer->GetTexture(d_pp->tsp, d_pp->tcw);
|
||||
if (fetchTextures)
|
||||
loadTexture(d_pp, 0);
|
||||
}
|
||||
|
||||
#define glob_param_bdc(pp) glob_param_bdc_( (TA_PolyParam0*)pp)
|
||||
@@ -609,8 +634,8 @@ private:
|
||||
|
||||
CurrentPP->tsp1.full = pp->tsp1.full;
|
||||
CurrentPP->tcw1.full = pp->tcw1.full;
|
||||
if (pp->pcw.Texture && fetchTextures)
|
||||
CurrentPP->texture1 = renderer->GetTexture(pp->tsp1, pp->tcw1, 1);
|
||||
if (fetchTextures)
|
||||
loadTexture(CurrentPP, 1);
|
||||
}
|
||||
|
||||
// Intensity, with Two Volumes
|
||||
@@ -622,8 +647,8 @@ private:
|
||||
|
||||
CurrentPP->tsp1.full = pp->tsp1.full;
|
||||
CurrentPP->tcw1.full = pp->tcw1.full;
|
||||
if (pp->pcw.Texture && fetchTextures)
|
||||
CurrentPP->texture1 = renderer->GetTexture(pp->tsp1, pp->tcw1, 1);
|
||||
if (fetchTextures)
|
||||
loadTexture(CurrentPP, 1);
|
||||
}
|
||||
|
||||
static void TACALL AppendPolyParam4B(void* vpp)
|
||||
@@ -981,8 +1006,8 @@ private:
|
||||
d_pp->pcw = spr->pcw;
|
||||
d_pp->tileclip = tileclip_val;
|
||||
|
||||
if (d_pp->pcw.Texture && fetchTextures)
|
||||
d_pp->texture = renderer->GetTexture(d_pp->tsp, d_pp->tcw);
|
||||
if (fetchTextures)
|
||||
loadTexture(d_pp, 0);
|
||||
|
||||
SFaceBaseColor = spr->BaseCol;
|
||||
SFaceOffsColor = spr->OffsCol;
|
||||
@@ -1226,8 +1251,7 @@ static void ta_parse_vdrc(TA_context* ctx, bool primRestart)
|
||||
ta_parse_reset();
|
||||
|
||||
PolyParam *bgpp = &vd_rc.global_param_op.front();
|
||||
if (bgpp->pcw.Texture)
|
||||
bgpp->texture = renderer->GetTexture(bgpp->tsp, bgpp->tcw);
|
||||
loadTexture(bgpp, 0);
|
||||
|
||||
TA_context *childCtx = ctx;
|
||||
int pass = 0;
|
||||
@@ -1276,6 +1300,26 @@ static void ta_parse_vdrc(TA_context* ctx, bool primRestart)
|
||||
childCtx = childCtx->nextContext;
|
||||
pass++;
|
||||
}
|
||||
if (vd_rc.global_param_mvo_tr.size() > vd_rc.global_param_mvo.size())
|
||||
{
|
||||
// Hack for Xtreme Sports. This game shares the same MVO list between opaque and translucent.
|
||||
// BUT the translucent list is the one to use, contrary to other games. Detect this here and
|
||||
// swap the arrays, which also allows non-oit renderers to display them.
|
||||
bool swapMVO = false;
|
||||
for (const RenderPass& renderPass : vd_rc.render_passes)
|
||||
{
|
||||
if (renderPass.mv_op_tr_shared) {
|
||||
swapMVO = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
if (swapMVO)
|
||||
{
|
||||
std::swap(vd_rc.global_param_mvo, vd_rc.global_param_mvo_tr);
|
||||
for (RenderPass& renderPass : vd_rc.render_passes)
|
||||
std::swap(renderPass.mvo_count, renderPass.mvo_tr_count);
|
||||
}
|
||||
}
|
||||
|
||||
vd_ctx = nullptr;
|
||||
}
|
||||
@@ -1283,26 +1327,11 @@ static void ta_parse_vdrc(TA_context* ctx, bool primRestart)
|
||||
static void ta_parse_naomi2(TA_context* ctx, bool primRestart)
|
||||
{
|
||||
for (PolyParam& pp : ctx->rend.global_param_op)
|
||||
{
|
||||
if (pp.pcw.Texture)
|
||||
pp.texture = renderer->GetTexture(pp.tsp, pp.tcw, 0);
|
||||
if (pp.tsp1.full != (u32)-1)
|
||||
pp.texture1 = renderer->GetTexture(pp.tsp1, pp.tcw1, 1);
|
||||
}
|
||||
loadTextures(&pp);
|
||||
for (PolyParam& pp : ctx->rend.global_param_pt)
|
||||
{
|
||||
if (pp.pcw.Texture)
|
||||
pp.texture = renderer->GetTexture(pp.tsp, pp.tcw, 0);
|
||||
if (pp.tsp1.full != (u32)-1)
|
||||
pp.texture1 = renderer->GetTexture(pp.tsp1, pp.tcw1, 1);
|
||||
}
|
||||
loadTextures(&pp);
|
||||
for (PolyParam& pp : ctx->rend.global_param_tr)
|
||||
{
|
||||
if (pp.pcw.Texture)
|
||||
pp.texture = renderer->GetTexture(pp.tsp, pp.tcw, 0);
|
||||
if (pp.tsp1.full != (u32)-1)
|
||||
pp.texture1 = renderer->GetTexture(pp.tsp1, pp.tcw1, 1);
|
||||
}
|
||||
loadTextures(&pp);
|
||||
|
||||
ctx->rend.newRenderPass();
|
||||
RenderPass previousPass{};
|
||||
|
||||
@@ -61,82 +61,88 @@ DynarecCodeEntryPtr DYNACALL bm_GetCodeByVAddr(u32 addr)
|
||||
|
||||
if (addr & 1)
|
||||
{
|
||||
switch (addr)
|
||||
if (!settings.content.windowsCE) {
|
||||
Do_Exception(addr, Sh4Ex_AddressErrorRead);
|
||||
}
|
||||
else
|
||||
{
|
||||
switch (addr)
|
||||
{
|
||||
#ifdef USE_WINCE_HACK
|
||||
case 0xfffffde7: // GetTickCount
|
||||
// This should make this syscall faster
|
||||
Sh4cntx.r[0] = sh4_sched_now64() * 1000 / SH4_MAIN_CLOCK;
|
||||
Sh4cntx.pc = Sh4cntx.pr;
|
||||
Sh4cntx.cycle_counter -= 100;
|
||||
break;
|
||||
case 0xfffffde7: // GetTickCount
|
||||
// This should make this syscall faster
|
||||
Sh4cntx.r[0] = sh4_sched_now64() * 1000 / SH4_MAIN_CLOCK;
|
||||
Sh4cntx.pc = Sh4cntx.pr;
|
||||
Sh4cntx.cycle_counter -= 100;
|
||||
break;
|
||||
|
||||
case 0xfffffd05: // QueryPerformanceCounter(u64 *)
|
||||
{
|
||||
bool isRam;
|
||||
u64 *ptr;
|
||||
u32 paddr;
|
||||
if (rdv_writeMemImmediate(Sh4cntx.r[4], sizeof(u64), (void*&)ptr, isRam, paddr) && isRam)
|
||||
case 0xfffffd05: // QueryPerformanceCounter(u64 *)
|
||||
{
|
||||
*ptr = sh4_sched_now64() >> 4;
|
||||
Sh4cntx.r[0] = 1;
|
||||
Sh4cntx.pc = Sh4cntx.pr;
|
||||
Sh4cntx.cycle_counter -= 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
Do_Exception(addr, Sh4Ex_AddressErrorRead);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xfffffded: // GetProcAddressW
|
||||
{
|
||||
// Intercept the dricas authentication function and replace it with a stub (Hundred Swords, Rune Jade)
|
||||
static constexpr u16 Function[] = { 'U', 'A', '_', 'Q', 'u', 'e', 'r', 'y', 'D', '\0' };
|
||||
bool success = false;
|
||||
u32 vaddr = Sh4cntx.r[5];
|
||||
if ((vaddr & 0xffff0000) != 0) // not an ordinal
|
||||
{
|
||||
u32 pa = 0;
|
||||
for (unsigned i = 0; ; i++)
|
||||
bool isRam;
|
||||
u64 *ptr;
|
||||
u32 paddr;
|
||||
if (rdv_writeMemImmediate(Sh4cntx.r[4], sizeof(u64), (void*&)ptr, isRam, paddr) && isRam)
|
||||
{
|
||||
if (pa == 0 && mmu_full_lookup(vaddr, nullptr, pa) != MmuError::NONE)
|
||||
break;
|
||||
u16 c = ReadMem16_nommu(pa);
|
||||
if (c != Function[i])
|
||||
break;
|
||||
if (c == 0) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
vaddr += 2;
|
||||
if ((vaddr & 0xfff) == 0)
|
||||
pa = 0;
|
||||
else
|
||||
pa += 2;
|
||||
*ptr = sh4_sched_now64() >> 4;
|
||||
Sh4cntx.r[0] = 1;
|
||||
Sh4cntx.pc = Sh4cntx.pr;
|
||||
Sh4cntx.cycle_counter -= 100;
|
||||
}
|
||||
else
|
||||
{
|
||||
Do_Exception(addr, Sh4Ex_AddressErrorRead);
|
||||
}
|
||||
}
|
||||
if (success)
|
||||
break;
|
||||
case 0xfffffded: // GetProcAddressW
|
||||
{
|
||||
INFO_LOG(DYNAREC, "GetProcAddressW('UA_QueryD') intercepted");
|
||||
Sh4cntx.r[0] = 0xffff0001;
|
||||
Sh4cntx.pc = Sh4cntx.pr;
|
||||
// Intercept the dricas authentication function and replace it with a stub (Hundred Swords, Rune Jade)
|
||||
static constexpr u16 Function[] = { 'U', 'A', '_', 'Q', 'u', 'e', 'r', 'y', 'D', '\0' };
|
||||
bool success = false;
|
||||
u32 vaddr = Sh4cntx.r[5];
|
||||
if ((vaddr & 0xffff0000) != 0) // not an ordinal
|
||||
{
|
||||
u32 pa = 0;
|
||||
for (unsigned i = 0; ; i++)
|
||||
{
|
||||
if (pa == 0 && mmu_full_lookup(vaddr, nullptr, pa) != MmuError::NONE)
|
||||
break;
|
||||
u16 c = ReadMem16_nommu(pa);
|
||||
if (c != Function[i])
|
||||
break;
|
||||
if (c == 0) {
|
||||
success = true;
|
||||
break;
|
||||
}
|
||||
vaddr += 2;
|
||||
if ((vaddr & 0xfff) == 0)
|
||||
pa = 0;
|
||||
else
|
||||
pa += 2;
|
||||
}
|
||||
}
|
||||
if (success)
|
||||
{
|
||||
INFO_LOG(DYNAREC, "GetProcAddressW('UA_QueryD') intercepted");
|
||||
Sh4cntx.r[0] = 0xffff0001;
|
||||
Sh4cntx.pc = Sh4cntx.pr;
|
||||
}
|
||||
else {
|
||||
Do_Exception(addr, Sh4Ex_AddressErrorRead);
|
||||
}
|
||||
}
|
||||
else {
|
||||
Do_Exception(addr, Sh4Ex_AddressErrorRead);
|
||||
}
|
||||
}
|
||||
break;
|
||||
case 0xffff0001: // UA_QueryD stub
|
||||
DEBUG_LOG(DYNAREC, "UA_QueryD called");
|
||||
Sh4cntx.r[0] = 0;
|
||||
Sh4cntx.pc = Sh4cntx.pr;
|
||||
break;
|
||||
break;
|
||||
case 0xffff0001: // UA_QueryD stub
|
||||
DEBUG_LOG(DYNAREC, "UA_QueryD called");
|
||||
Sh4cntx.r[0] = 0;
|
||||
Sh4cntx.pc = Sh4cntx.pr;
|
||||
break;
|
||||
#endif
|
||||
|
||||
default:
|
||||
Do_Exception(addr, Sh4Ex_AddressErrorRead);
|
||||
break;
|
||||
default:
|
||||
Do_Exception(addr, Sh4Ex_AddressErrorRead);
|
||||
break;
|
||||
}
|
||||
}
|
||||
addr = Sh4cntx.pc;
|
||||
}
|
||||
|
||||
@@ -24,6 +24,7 @@
|
||||
#include "hw/sh4/modules/mmu.h"
|
||||
#include "ssa.h"
|
||||
|
||||
#include <array>
|
||||
#include <deque>
|
||||
#include <map>
|
||||
#include <vector>
|
||||
@@ -37,19 +38,18 @@ public:
|
||||
RegAlloc() = default;
|
||||
virtual ~RegAlloc() = default;
|
||||
|
||||
void DoAlloc(RuntimeBlockInfo* block, const nreg_t* regs_avail, const nregf_t* regsf_avail)
|
||||
template<std::size_t N, std::size_t NF>
|
||||
void DoAlloc(RuntimeBlockInfo* block, const std::array<nreg_t, N>& regs_avail, const std::array<nregf_t, NF>& regsf_avail)
|
||||
{
|
||||
this->block = block;
|
||||
SSAOptimizer optim(block);
|
||||
optim.AddVersionPass();
|
||||
|
||||
verify(host_gregs.empty());
|
||||
while (*regs_avail != (nreg_t)-1)
|
||||
host_gregs.push_back(*regs_avail++);
|
||||
host_gregs.insert(host_gregs.end(), regs_avail.begin(), regs_avail.end());
|
||||
|
||||
verify(host_fregs.empty());
|
||||
while (*regsf_avail != (nregf_t)-1)
|
||||
host_fregs.push_back(*regsf_avail++);
|
||||
host_fregs.insert(host_fregs.end(), regsf_avail.begin(), regsf_avail.end());
|
||||
}
|
||||
|
||||
void OpBegin(shil_opcode* op, int opid)
|
||||
|
||||
@@ -926,7 +926,7 @@ sh4op(i1011_iiii_iiii_iiii)
|
||||
// trapa #<imm>
|
||||
sh4op(i1100_0011_iiii_iiii)
|
||||
{
|
||||
WARN_LOG(INTERPRETER, "TRAP #%X", GetImm8(op));
|
||||
INFO_LOG(INTERPRETER, "TRAP #%X", GetImm8(op));
|
||||
debugger::debugTrap(Sh4Ex_Trap);
|
||||
CCN_TRA = (GetImm8(op) << 2);
|
||||
Do_Exception(ctx->pc, Sh4Ex_Trap);
|
||||
|
||||
@@ -176,9 +176,35 @@ int main(int argc, char *argv[])
|
||||
}
|
||||
#endif
|
||||
|
||||
bool UTLB_Sync(u32 entry)
|
||||
bool fullMmuNeeded(int index)
|
||||
{
|
||||
const unsigned start = index < 0 ? 0 : index;
|
||||
const unsigned end = index < 0 ? std::size(UTLB) : index + 1;
|
||||
for (unsigned i = start; i < end; i++)
|
||||
{
|
||||
const TLB_Entry& entry = UTLB[i];
|
||||
if (entry.Data.V == 1
|
||||
// ignore many arcade and Visual Concepts games presumably bogus mappings
|
||||
&& entry.Address.VPN != 0x30040 && entry.Address.VPN != 0x30000
|
||||
// ignore identity mapping
|
||||
&& entry.Address.VPN != entry.Data.PPN
|
||||
// ignore store queue mapping
|
||||
&& (entry.Address.VPN & (0xFC000000 >> 10)) != (0xE0000000 >> 10))
|
||||
{
|
||||
INFO_LOG(SH4, "fullMmuNeeded[%d]: VPN %x PPN %x ASID %d sz %d", i,
|
||||
entry.Address.VPN << 10, entry.Data.PPN << 10,
|
||||
entry.Address.ASID, entry.Data.SZ1 * 2 + entry.Data.SZ0);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
void UTLB_Sync(u32 entry)
|
||||
{
|
||||
TLB_Entry& tlb_entry = UTLB[entry];
|
||||
if (tlb_entry.Data.V == 0)
|
||||
return;
|
||||
u32 sz = tlb_entry.Data.SZ1 * 2 + tlb_entry.Data.SZ0;
|
||||
|
||||
tlb_entry.Address.VPN &= mmu_mask[sz] >> 10;
|
||||
@@ -190,6 +216,8 @@ bool UTLB_Sync(u32 entry)
|
||||
|
||||
cache_entry(tlb_entry);
|
||||
|
||||
DEBUG_LOG(SH4, "UTLB_Sync[%d]: VPN %x PPN %x ASID %d sz %d", entry, tlb_entry.Address.VPN << 10, tlb_entry.Data.PPN << 10,
|
||||
tlb_entry.Address.ASID, sz);
|
||||
if (!mmu_enabled())
|
||||
{
|
||||
if ((tlb_entry.Address.VPN & (0xFC000000 >> 10)) == (0xE0000000 >> 10))
|
||||
@@ -198,20 +226,18 @@ bool UTLB_Sync(u32 entry)
|
||||
u32 vpn_sq = ((tlb_entry.Address.VPN & 0x7FFFF) >> 10) & 0x3F;//upper bits are always known [0xE0/E1/E2/E3]
|
||||
sq_remap[vpn_sq] = tlb_entry.Data.PPN << 10;
|
||||
}
|
||||
else if (CCN_MMUCR.AT == 1 && tlb_entry.Address.VPN != 0x30040 && tlb_entry.Address.VPN != 0x30000 && tlb_entry.Data.V == 1)
|
||||
else if (CCN_MMUCR.AT == 1 && fullMmuNeeded(entry))
|
||||
{
|
||||
// Enable Full MMU if not an expected store queue mapping
|
||||
// VPN checks to ignore many arcade and Visual Concepts games presumably bogus mappings
|
||||
mmuOn = true;
|
||||
NOTICE_LOG(SH4, "Enabling on-demand Full MMU support");
|
||||
mmu_set_state();
|
||||
}
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
void ITLB_Sync(u32 entry)
|
||||
{
|
||||
DEBUG_LOG(SH4, "ITLB_Sync[%d]", entry);
|
||||
}
|
||||
|
||||
//Do a full lookup on the UTLB entry's
|
||||
@@ -249,28 +275,31 @@ MmuError mmu_full_lookup(u32 va, const TLB_Entry** tlb_entry_ret, u32& rv)
|
||||
}
|
||||
|
||||
#ifdef USE_WINCE_HACK
|
||||
// WinCE hack
|
||||
TLB_Entry& entry = UTLB[CCN_MMUCR.URC];
|
||||
if (wince_resolve_address(va, entry))
|
||||
if (settings.content.windowsCE)
|
||||
{
|
||||
CCN_PTEL.reg_data = entry.Data.reg_data;
|
||||
CCN_PTEA.reg_data = entry.Assistance.reg_data;
|
||||
CCN_PTEH.reg_data = entry.Address.reg_data;
|
||||
// WinCE hack to avoid calling the page fault handler
|
||||
TLB_Entry& entry = UTLB[CCN_MMUCR.URC];
|
||||
if (wince_resolve_address(va, entry))
|
||||
{
|
||||
CCN_PTEL.reg_data = entry.Data.reg_data;
|
||||
CCN_PTEA.reg_data = entry.Assistance.reg_data;
|
||||
CCN_PTEH.reg_data = entry.Address.reg_data;
|
||||
|
||||
lru_entry = *tlb_entry_ret = &entry;
|
||||
lru_entry = *tlb_entry_ret = &entry;
|
||||
|
||||
u32 sz = entry.Data.SZ1 * 2 + entry.Data.SZ0;
|
||||
lru_mask = mmu_mask[sz];
|
||||
lru_address = va & mmu_mask[sz];
|
||||
entry.Data.PPN &= mmu_mask[sz] >> 10;
|
||||
u32 sz = entry.Data.SZ1 * 2 + entry.Data.SZ0;
|
||||
lru_mask = mmu_mask[sz];
|
||||
lru_address = va & mmu_mask[sz];
|
||||
entry.Data.PPN &= mmu_mask[sz] >> 10;
|
||||
|
||||
rv = (entry.Data.PPN << 10) | (va & ~mmu_mask[sz]);
|
||||
rv = (entry.Data.PPN << 10) | (va & ~mmu_mask[sz]);
|
||||
|
||||
cache_entry(entry);
|
||||
cache_entry(entry);
|
||||
|
||||
p_sh4rcb->cntx.cycle_counter -= 164;
|
||||
p_sh4rcb->cntx.cycle_counter -= 164;
|
||||
|
||||
return MmuError::NONE;
|
||||
return MmuError::NONE;
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -333,5 +362,10 @@ void mmu_flush_table()
|
||||
lru_entry = nullptr;
|
||||
flush_cache();
|
||||
mmuAddressLUTFlush(true);
|
||||
|
||||
for (TLB_Entry& entry : ITLB)
|
||||
entry.Data.V = 0;
|
||||
for (TLB_Entry& entry : UTLB)
|
||||
entry.Data.V = 0;
|
||||
}
|
||||
#endif // FAST_MMU
|
||||
+25
-17
@@ -55,24 +55,18 @@ constexpr u32 ITLB_LRU_AND[4] =
|
||||
|
||||
#ifndef FAST_MMU
|
||||
//sync mem mapping to mmu , suspend compiled blocks if needed.entry is a UTLB entry # , -1 is for full sync
|
||||
bool UTLB_Sync(u32 entry)
|
||||
void UTLB_Sync(u32 entry)
|
||||
{
|
||||
printf_mmu("UTLB MEM remap %d : 0x%X to 0x%X : %d asid %d size %d", entry, UTLB[entry].Address.VPN << 10, UTLB[entry].Data.PPN << 10, UTLB[entry].Data.V,
|
||||
UTLB[entry].Address.ASID, UTLB[entry].Data.SZ0 + UTLB[entry].Data.SZ1 * 2);
|
||||
if (UTLB[entry].Data.V == 0)
|
||||
return true;
|
||||
return;
|
||||
|
||||
if ((UTLB[entry].Address.VPN & (0xFC000000 >> 10)) == (0xE0000000 >> 10))
|
||||
{
|
||||
// Used when FullMMU is off
|
||||
u32 vpn_sq = ((UTLB[entry].Address.VPN & 0x7FFFF) >> 10) & 0x3F;//upper bits are always known [0xE0/E1/E2/E3]
|
||||
sq_remap[vpn_sq] = UTLB[entry].Data.PPN << 10;
|
||||
|
||||
return true;
|
||||
}
|
||||
else
|
||||
{
|
||||
return false;
|
||||
}
|
||||
}
|
||||
//sync mem mapping to mmu , suspend compiled blocks if needed.entry is a ITLB entry # , -1 is for full sync
|
||||
@@ -441,18 +435,27 @@ void mmu_set_state()
|
||||
{
|
||||
if (CCN_MMUCR.AT == 1)
|
||||
{
|
||||
// Detect if we're running Windows CE
|
||||
static const char magic[] = { 'S', 0, 'H', 0, '-', 0, '4', 0, ' ', 0, 'K', 0, 'e', 0, 'r', 0, 'n', 0, 'e', 0, 'l', 0 };
|
||||
if (memcmp(GetMemPtr(0x8c0110a8, 4), magic, sizeof(magic)) == 0
|
||||
|| memcmp(GetMemPtr(0x8c011118, 4), magic, sizeof(magic)) == 0)
|
||||
if (!settings.content.windowsCE)
|
||||
{
|
||||
mmuOn = true;
|
||||
NOTICE_LOG(SH4, "Enabling Full MMU support");
|
||||
// Detect if we're running Windows CE
|
||||
static const char magic[] = { 'S', 0, 'H', 0, '-', 0, '4', 0, ' ', 0, 'K', 0, 'e', 0, 'r', 0, 'n', 0, 'e', 0, 'l', 0 };
|
||||
if (memcmp(GetMemPtr(0x8c0110a8, 4), magic, sizeof(magic)) == 0
|
||||
|| memcmp(GetMemPtr(0x8c011118, 4), magic, sizeof(magic)) == 0)
|
||||
{
|
||||
mmuOn = true;
|
||||
settings.content.windowsCE = true;
|
||||
NOTICE_LOG(SH4, "Enabling Windows CE MMU support");
|
||||
}
|
||||
else if (fullMmuNeeded()) {
|
||||
mmuOn = true;
|
||||
NOTICE_LOG(SH4, "Enabling Full MMU support");
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
mmuOn = false;
|
||||
settings.content.windowsCE = false;
|
||||
}
|
||||
|
||||
SetMemoryHandlers();
|
||||
@@ -586,11 +589,16 @@ void mmu_serialize(Serializer& ser)
|
||||
|
||||
void mmu_deserialize(Deserializer& deser)
|
||||
{
|
||||
deser.skip(8, Deserializer::V33); // CCN_QACR_TR
|
||||
|
||||
deser >> UTLB;
|
||||
deser >> ITLB;
|
||||
|
||||
deser >> sq_remap;
|
||||
deser.skip(64 * 4, Deserializer::V23); // ITLB_LRU_USE
|
||||
|
||||
if (!settings.content.windowsCE)
|
||||
{
|
||||
for (unsigned i = 0; i < std::size(UTLB); i++)
|
||||
UTLB_Sync(i);
|
||||
for (unsigned i = 0; i < std::size(ITLB); i++)
|
||||
ITLB_Sync(i);
|
||||
}
|
||||
}
|
||||
@@ -55,13 +55,14 @@ constexpr u32 mmu_mask[4] =
|
||||
((0xFFFFFFFF) >> 20) << 20 //1 MB page
|
||||
};
|
||||
|
||||
bool UTLB_Sync(u32 entry);
|
||||
void UTLB_Sync(u32 entry);
|
||||
void ITLB_Sync(u32 entry);
|
||||
|
||||
bool mmu_match(u32 va, CCN_PTEH_type Address, CCN_PTEL_type Data);
|
||||
void mmu_set_state();
|
||||
void mmu_flush_table();
|
||||
[[noreturn]] void mmu_raise_exception(MmuError mmu_error, u32 address, u32 am);
|
||||
bool fullMmuNeeded(int index = -1);
|
||||
|
||||
static inline bool mmu_enabled()
|
||||
{
|
||||
@@ -122,12 +123,13 @@ extern u32 mmuAddressLUT[0x100000];
|
||||
|
||||
static inline void mmuAddressLUTFlush(bool full)
|
||||
{
|
||||
if (full)
|
||||
memset(mmuAddressLUT, 0, sizeof(mmuAddressLUT) / 2); // flush user memory
|
||||
if (full || !settings.content.windowsCE) {
|
||||
memset(mmuAddressLUT, 0, sizeof(mmuAddressLUT) / 2); // flush user memory (P0)
|
||||
}
|
||||
else
|
||||
{
|
||||
constexpr u32 slotPages = (32 * 1024 * 1024) >> 12;
|
||||
memset(mmuAddressLUT, 0, slotPages * sizeof(u32)); // flush slot 0
|
||||
memset(mmuAddressLUT, 0, slotPages * sizeof(u32)); // flush slot 0 (windows CE)
|
||||
}
|
||||
}
|
||||
#endif
|
||||
|
||||
@@ -375,14 +375,10 @@ void SCIFSerialPort::init()
|
||||
|
||||
void SCIFSerialPort::term()
|
||||
{
|
||||
if (schedId != -1) {
|
||||
sh4_sched_unregister(schedId);
|
||||
schedId = -1;
|
||||
}
|
||||
if (brkSchedId != -1) {
|
||||
sh4_sched_unregister(brkSchedId);
|
||||
brkSchedId = -1;
|
||||
}
|
||||
sh4_sched_unregister(schedId);
|
||||
schedId = -1;
|
||||
sh4_sched_unregister(brkSchedId);
|
||||
brkSchedId = -1;
|
||||
}
|
||||
|
||||
void SCIFSerialPort::reset()
|
||||
|
||||
@@ -17,7 +17,7 @@ static u64 tmu_mask[3];
|
||||
static u32 old_mode[3] = { 0xFFFF, 0xFFFF, 0xFFFF};
|
||||
|
||||
static constexpr InterruptID tmu_intID[3] = { sh4_TMU0_TUNI0, sh4_TMU1_TUNI1, sh4_TMU2_TUNI2 };
|
||||
int tmu_sched[3];
|
||||
int tmu_sched[3] { -1, -1, -1 };
|
||||
static s64 tmu_ch_base[3];
|
||||
|
||||
inline static u64 now() {
|
||||
|
||||
@@ -17,13 +17,13 @@
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include <array>
|
||||
#include "types.h"
|
||||
#include "sh4_mem.h"
|
||||
#include "modules/mmu.h"
|
||||
#include "hw/sh4/sh4_if.h"
|
||||
#include "serialize.h"
|
||||
#include "sh4_cycles.h"
|
||||
#include <array>
|
||||
|
||||
static bool cachedArea(u32 area)
|
||||
{
|
||||
|
||||
@@ -17,7 +17,6 @@
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include "sh4_cycles.h"
|
||||
#include "modules/mmu.h"
|
||||
|
||||
int Sh4Cycles::countCycles(u16 op)
|
||||
{
|
||||
@@ -65,7 +64,7 @@ int Sh4Cycles::countCycles(u16 op)
|
||||
if (isMemOp[opcode->ex_type])
|
||||
{
|
||||
if (++memOps < 4)
|
||||
cycles = mmu_enabled() ? 5 : 2;
|
||||
cycles = 2;
|
||||
}
|
||||
// TODO only for mem read?
|
||||
#endif
|
||||
|
||||
@@ -94,8 +94,9 @@ static void map_area4(u32 base)
|
||||
template <class T>
|
||||
T DYNACALL ReadMem_extdev_T(u32 addr)
|
||||
{
|
||||
INFO_LOG(SH4, "Read ext. device (Area 5) undefined @ %08x", addr);
|
||||
return (T)0;
|
||||
INFO_LOG(SH4, "Read<%d> ext. device (Area 5) undefined @ %08x", (int)sizeof(T), addr);
|
||||
// This is what real hw returns
|
||||
return (T)((addr >> 16) & 0x1fff);
|
||||
}
|
||||
|
||||
//Write Ext.Device
|
||||
|
||||
@@ -713,16 +713,10 @@ void deserialize(Deserializer& deser)
|
||||
|
||||
interrupts_deserialize(deser);
|
||||
|
||||
if (deser.version() <= Deserializer::V31)
|
||||
deser.skip<int>(); // do_sqw index
|
||||
CCN_QACR_write<0>(0, CCN_QACR0.reg_data);
|
||||
CCN_QACR_write<1>(0, CCN_QACR1.reg_data);
|
||||
|
||||
deser >> (*p_sh4rcb).cntx;
|
||||
if (deser.version() >= Deserializer::V19 && deser.version() < Deserializer::V21)
|
||||
deser.skip<u32>(); // sh4InterpCycles
|
||||
if (deser.version() < Deserializer::V21)
|
||||
p_sh4rcb->cntx.cycle_counter = SH4_TIMESLICE;
|
||||
|
||||
sh4_sched_deserialize(deser);
|
||||
// let the serial port reschedule its callback after the scheduler has been deserialized
|
||||
@@ -737,13 +731,6 @@ void serialize2(Serializer& ser)
|
||||
|
||||
void deserialize2(Deserializer& deser)
|
||||
{
|
||||
if (deser.version() <= Deserializer::V32)
|
||||
{
|
||||
deser >> SCIF_SCFSR2;
|
||||
deser >> SCIF_SCSCR2;
|
||||
deser >> BSC_PDTRA;
|
||||
}
|
||||
|
||||
tmu.deserialize(deser);
|
||||
mmu_deserialize(deser);
|
||||
}
|
||||
|
||||
@@ -1,6 +1,5 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include "sh4_if.h"
|
||||
#include "modules/modules.h"
|
||||
#include "modules/ccn.h"
|
||||
#include "modules/dmac.h"
|
||||
|
||||
+15
-28
@@ -2,6 +2,7 @@
|
||||
#include "sh4_if.h"
|
||||
#include "sh4_sched.h"
|
||||
#include "serialize.h"
|
||||
#include "hw/aica/aica_if.h"
|
||||
|
||||
#include <algorithm>
|
||||
#include <vector>
|
||||
@@ -214,14 +215,6 @@ void sh4_sched_deserialize(Deserializer& deser, int id)
|
||||
}
|
||||
|
||||
// FIXME modules should save their scheduling data so that it doesn't depend on their scheduler id
|
||||
namespace aica
|
||||
{
|
||||
// hw/aica/aica.cpp
|
||||
extern int aica_schid;
|
||||
extern int rtc_schid;
|
||||
// hw/aica/aica_if.cpp
|
||||
extern int dma_sched_id;
|
||||
}
|
||||
// hw/gdrom/gdromv3.cpp
|
||||
extern int gdrom_schid;
|
||||
// hw/maple/maple_if.cpp
|
||||
@@ -232,35 +225,29 @@ extern int vblank_schid;
|
||||
// hw/sh4/modules/tmu.cpp
|
||||
extern int tmu_sched[3];
|
||||
|
||||
void sh4_sched_serialize(Serializer& ser)
|
||||
{
|
||||
void sh4_sched_serialize(Serializer& ser) {
|
||||
ser << sh4_sched_ffb;
|
||||
|
||||
sh4_sched_serialize(ser, aica::aica_schid);
|
||||
sh4_sched_serialize(ser, aica::rtc_schid);
|
||||
sh4_sched_serialize(ser, gdrom_schid);
|
||||
sh4_sched_serialize(ser, maple_schid);
|
||||
sh4_sched_serialize(ser, aica::dma_sched_id);
|
||||
sh4_sched_serialize(ser, render_end_schid);
|
||||
sh4_sched_serialize(ser, vblank_schid);
|
||||
}
|
||||
|
||||
void sh4_sched_deserialize(Deserializer& deser)
|
||||
{
|
||||
deser >> sh4_sched_ffb;
|
||||
|
||||
if (deser.version() >= Deserializer::V19 && deser.version() <= Deserializer::V31)
|
||||
deser.skip<u32>(); // sh4_sched_next_id
|
||||
|
||||
sh4_sched_deserialize(deser, aica::aica_schid);
|
||||
sh4_sched_deserialize(deser, aica::rtc_schid);
|
||||
sh4_sched_deserialize(deser, gdrom_schid);
|
||||
sh4_sched_deserialize(deser, maple_schid);
|
||||
sh4_sched_deserialize(deser, aica::dma_sched_id);
|
||||
if (deser.version() < Deserializer::V62)
|
||||
{
|
||||
sh4_sched_deserialize(deser, aica::aica_schid);
|
||||
sh4_sched_deserialize(deser, aica::rtc_schid);
|
||||
sh4_sched_deserialize(deser, gdrom_schid);
|
||||
sh4_sched_deserialize(deser, maple_schid);
|
||||
sh4_sched_deserialize(deser, aica::dma_sched_id);
|
||||
}
|
||||
if (deser.version() < Deserializer::V58) {
|
||||
for (int id : tmu_sched)
|
||||
sh4_sched_deserialize(deser, id);
|
||||
}
|
||||
sh4_sched_deserialize(deser, render_end_schid);
|
||||
sh4_sched_deserialize(deser, vblank_schid);
|
||||
if (deser.version() < Deserializer::V62)
|
||||
{
|
||||
sh4_sched_deserialize(deser, render_end_schid);
|
||||
sh4_sched_deserialize(deser, vblank_schid);
|
||||
}
|
||||
}
|
||||
+32
-11
@@ -29,28 +29,42 @@ constexpr Disc* (*drivers[])(const char* path, std::vector<u8> *digest)
|
||||
|
||||
static u8 q_subchannel[96];
|
||||
|
||||
static bool convertSector(u8* in_buff , u8* out_buff , int from , int to,int sector)
|
||||
static u8 bin2bcd(u32 v) {
|
||||
return (u8)((v % 10) | ((v / 10) << 4));
|
||||
}
|
||||
|
||||
static bool convertSector(u8* in_buff, u8* out_buff, int from, int to, int sector)
|
||||
{
|
||||
//get subchannel data, if any
|
||||
if (from == 2448)
|
||||
{
|
||||
if (from == 2448) {
|
||||
memcpy(q_subchannel, in_buff + 2352, 96);
|
||||
from -= 96;
|
||||
}
|
||||
else
|
||||
else {
|
||||
memset(q_subchannel, 0, sizeof(q_subchannel));
|
||||
}
|
||||
|
||||
//if no conversion
|
||||
if (to == from)
|
||||
{
|
||||
if (to == from) {
|
||||
memcpy(out_buff, in_buff, to);
|
||||
return true;
|
||||
}
|
||||
switch (to)
|
||||
{
|
||||
case 2340:
|
||||
verify(from == 2352);
|
||||
memcpy(out_buff, &in_buff[12], 2340);
|
||||
verify(from == 2352 || from == 2336);
|
||||
if (from == 2336)
|
||||
{
|
||||
// 2336 formats have no header, synthesize one from the FAD
|
||||
out_buff[0] = bin2bcd(sector / 75 / 60);
|
||||
out_buff[1] = bin2bcd((sector / 75) % 60);
|
||||
out_buff[2] = bin2bcd(sector % 75);
|
||||
out_buff[3] = 2;
|
||||
memcpy(&out_buff[4], in_buff, 2336);
|
||||
}
|
||||
else {
|
||||
memcpy(out_buff, &in_buff[12], 2340);
|
||||
}
|
||||
break;
|
||||
case 2328:
|
||||
verify(from == 2352);
|
||||
@@ -314,6 +328,10 @@ u32 Disc::ReadSectors(u32 FAD, u32 count, u8* dst, u32 fmt, bool stopOnMiss, Loa
|
||||
else if (fmt == 2048 && secfmt == SECFMT_2336_MODE2) {
|
||||
memcpy(dst, temp + 8, 2048);
|
||||
}
|
||||
else if (fmt == 2340 && secfmt == SECFMT_2336_MODE2) {
|
||||
// Bleemcast
|
||||
convertSector(temp, dst, 2336, fmt, FAD);
|
||||
}
|
||||
else if (fmt == 2048 && (secfmt == SECFMT_2048_MODE1 || secfmt == SECFMT_2048_MODE2_FORM1)) {
|
||||
memcpy(dst, temp, 2048);
|
||||
}
|
||||
@@ -388,9 +406,12 @@ void libGDR_init()
|
||||
}
|
||||
void libGDR_term()
|
||||
{
|
||||
gdr::termDrive();
|
||||
sh4_sched_unregister(schedId);
|
||||
schedId = -1;
|
||||
if (schedId != -1)
|
||||
{
|
||||
gdr::termDrive();
|
||||
sh4_sched_unregister(schedId);
|
||||
schedId = -1;
|
||||
}
|
||||
}
|
||||
|
||||
void libGDR_serialize(Serializer& ser)
|
||||
|
||||
@@ -1,10 +1,9 @@
|
||||
#pragma once
|
||||
#include "types.h"
|
||||
#include <vector>
|
||||
|
||||
#include "emulator.h"
|
||||
#include "hw/gdrom/gdrom_if.h"
|
||||
#include "oslib/storage.h"
|
||||
#include <vector>
|
||||
|
||||
/*
|
||||
Mode2 Subheader:
|
||||
|
||||
@@ -92,6 +92,7 @@ Disc* cue_parse(const char* file, std::vector<u8> *digest)
|
||||
std::string track_type;
|
||||
u32 track_secsize = 0;
|
||||
std::string track_isrc;
|
||||
u32 pregap = 0;
|
||||
|
||||
std::string line;
|
||||
while (std::getline(istream, line))
|
||||
@@ -198,6 +199,15 @@ Disc* cue_parse(const char* file, std::vector<u8> *digest)
|
||||
track_isrc.clear();
|
||||
track_secsize = 0;
|
||||
}
|
||||
else if (token == "PREGAP")
|
||||
{
|
||||
cuesheet >> token;
|
||||
int min = 0, sec = 0, frame = 0;
|
||||
if (sscanf(token.c_str(), "%d:%d:%d", &min, &sec, &frame) == 3)
|
||||
pregap = frame + 75 * (sec + 60 * min);
|
||||
else
|
||||
throw FlycastException(i18n::T("Invalid PREGAP in CUE file"));
|
||||
}
|
||||
else if (token == "TRACK")
|
||||
{
|
||||
cuesheet >> track_number;
|
||||
@@ -245,6 +255,14 @@ Disc* cue_parse(const char* file, std::vector<u8> *digest)
|
||||
t.CTRL = (track_type == "AUDIO" || track_type == "CDG") ? 0 : 4;
|
||||
t.EndFAD = currentFAD - 1;
|
||||
t.isrc = track_isrc;
|
||||
|
||||
if (pregap > 0)
|
||||
{
|
||||
t.StartFAD += pregap;
|
||||
t.EndFAD += pregap;
|
||||
pregap = 0; // reset pregap after track is added
|
||||
}
|
||||
|
||||
DEBUG_LOG(GDROM, "file[%zd] \"%s\": session %d type %s FAD:%d -> %d %s", disc->tracks.size() + 1, track_filename.c_str(),
|
||||
session_number, track_type.c_str(), t.StartFAD, t.EndFAD, t.isrc.empty() ? "" : ("ISRC " + t.isrc).c_str());
|
||||
hostfs::File *track_file = hostfs::storage().openFile(track_filename, "rb");
|
||||
|
||||
@@ -207,7 +207,7 @@ void update()
|
||||
auto& potato = Potatoes[bus];
|
||||
if (config::NetworkExpansionDevices[bus][0] == 1
|
||||
&& config::MapleExpansionDevices[bus][0] == MDT_SegaVMU
|
||||
&& maple_getPortCount(config::MapleMainDevices[bus]) >= 1)
|
||||
&& maple_getPortCount(config::MapleMainDevices[bus], MaplePortType::built_in) >= 1)
|
||||
{
|
||||
if (potato == nullptr) {
|
||||
potato = std::make_shared<DreamPotato>(bus);
|
||||
|
||||
+1
-1
@@ -17,7 +17,7 @@
|
||||
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#pragma once
|
||||
#include "build.h"
|
||||
#include "types.h"
|
||||
|
||||
#ifdef USE_SDL
|
||||
void sdl_setTorque(int, float);
|
||||
|
||||
@@ -16,12 +16,12 @@
|
||||
You should have received a copy of the GNU General Public License
|
||||
along with reicast. If not, see <https://www.gnu.org/licenses/>.
|
||||
*/
|
||||
#include <cmath>
|
||||
#include "mapping.h"
|
||||
#include "cfg/ini.h"
|
||||
#include "stdclass.h"
|
||||
#include "cfg/option.h"
|
||||
#include "oslib/storage.h"
|
||||
#include <cmath>
|
||||
|
||||
static struct
|
||||
{
|
||||
|
||||
@@ -96,7 +96,12 @@ void SystemMouse::setWheel(int delta) {
|
||||
static void screenToNative(int& x, int& y, int width, int height)
|
||||
{
|
||||
float fx, fy;
|
||||
if (!config::Rotate90)
|
||||
if (config::StretchToFill)
|
||||
{
|
||||
fx = x * 640.f / width;
|
||||
fy = y * 480.f / height;
|
||||
}
|
||||
else if (!config::Rotate90)
|
||||
{
|
||||
float scale = 480.f / height;
|
||||
fy = y * scale;
|
||||
|
||||
@@ -19,7 +19,6 @@
|
||||
#pragma once
|
||||
|
||||
#include "LogManager.h"
|
||||
#include <deque>
|
||||
#include <mutex>
|
||||
#include <vector>
|
||||
|
||||
|
||||
@@ -121,7 +121,7 @@ LogManager::LogManager()
|
||||
m_log[LogTypes::SH4] = {"SH4", "SH4 Modules"};
|
||||
|
||||
// Set up log listeners
|
||||
int verbosity = config::loadInt("log", "Verbosity", LogTypes::LDEBUG);
|
||||
int verbosity = config::loadInt("log", "Verbosity", LogTypes::LINFO);
|
||||
|
||||
// Ensure the verbosity level is valid
|
||||
if (verbosity < 1)
|
||||
|
||||
Loaded 100 of 267 files, more files were not shown because too many files have changed in this diff.
Show more
Reference in new issue
Block a user