6 Commits
Author SHA1 Message Date
github-actions[bot] 7adbf3caef Fetch translations & Recreate libretro_core_options_intl.h 2026-10-31 01:33:35 +00:00
Joseph MattielloandClaude Opus 5.5 1b63c8a367 dreamcast: Default broadcast follows the region
Broadcast has a Default entry in the settings (value 4), but
fixUpDCFlash() only writes values in range, so Default left the flash
byte alone: NTSC from the factory block on a fresh flash, or whatever
the last explicit choice had written. A PAL game booted in NTSC with
Broadcast on Default.

Region's own Default is resolved from IP.BIN just above; resolve
Broadcast the same way: PAL for Europe, NTSC otherwise. Applied with
override() at load, like the region, so the saved setting stays Default.

Testing: compiles on macOS arm64.

Co-Authored-By: Claude Opus 5.5 <[email protected]>
2026-10-27 11:48:03 +01:00
Chris Dixon 11ba75f843 libretro: add a Vblank Pacing option (#2562)
Without threaded rendering a run ends when a frame is presented, or
after 50 ms of emulated time without one. Frontends that pace retro_run
by the frame rate instead of blocking on audio, Kodi among them, then
get several vblanks per run through loading screens and have to drop
the extra audio. With the option on, every run ends at the next vblank.
[ci skip]
2026-10-27 11:47:43 +01:00
github-actions[bot] e88b0ced22 Fetch translations & Recreate libretro_core_options_intl.h 2026-10-24 00:37:48 +00:00
Flyinghead 6d81b99816 add pull request template. Fix issue templates. 2026-10-23 11:20:53 +02:00
Flyinghead 53efedbbe7 libretro: don't call the frontend on the emulator thread
Fixes crash with many WinCE games. Issue #2442
2026-10-23 11:20:08 +02:00
271 changed files with 8151 additions and 17784 deletions

No files matched your search

+8 -9
View File
@@ -7,8 +7,8 @@ assignees: ''
---
**Please Note: This form is the minimum required information for submitting bugs.**
**Removing this form may lead to your issue being closed until it is completed.**
<!-- Please Note: This form is the minimum required information for submitting bugs. -->
<!-- Removing this form may lead to your issue being closed until it is completed. -->
Platform / OS / Hardware:
@@ -18,24 +18,23 @@ Hardware:
**_Description of the Issue_**
Add a short, concise description of the issue here
<!-- Add a short, concise description of the issue here -->
**_Debugging Steps Tested_**
* Fill in any steps already tried here
* Begin each new line with an asterisk
* If no steps are required, please skip
<!-- Fill in any steps already tried here -->
<!-- Begin each new line with an asterisk -->
<!-- If no steps are required, please skip -->
**_Logs Gathered_**
```
Please paste the contents of the log / logs here
You may leave this field blank if you have none.
Delete this field if you have none.
```
**_Screenshots_**
(Replace this line with any issue screenshots)
<!-- Replace this line with any issue screenshots -->
+4 -4
View File
@@ -8,13 +8,13 @@ assignees: ''
---
**Is your feature request related to a problem? Please describe.**
A clear and concise description of what the problem is. Ex. I'm always frustrated when [...]
<!-- A clear and concise description of what the problem is. Ex. I'm always frustrated when [...] -->
**Describe the solution you'd like**
A clear and concise description of what you want to happen.
<!-- A clear and concise description of what you want to happen. -->
**Describe alternatives you've considered**
A clear and concise description of any alternative solutions or features you've considered.
<!-- A clear and concise description of any alternative solutions or features you've considered. -->
**Additional context**
Add any other context or screenshots about the feature request here.
<!-- Add any other context or screenshots about the feature request here. -->
+11
View File
@@ -0,0 +1,11 @@
## Description
<!-- Summarize the change and its motivation. -->
<!-- Use "Fixes #123" for an issue this PR closes, or "Related to #123" otherwise. -->
## Checklist
- [ ] This PR targets `dev`, unless it is a critical, low-risk bug fix.
- [ ] I linked the relevant issue or discussion, if one exists.
- [ ] I added or updated tests where appropriate.
- [ ] I tested the change on the relevant platform(s) and noted any limitations.
- [ ] For UI changes, I included screenshots or a recording where helpful.
- [ ] I kept the change focused, followed existing project patterns and avoided code duplication.
+2 -2
View File
@@ -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: '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
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
exclude:
- architecture: arm64
+10 -13
View File
@@ -24,7 +24,6 @@ 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 }
@@ -37,6 +36,7 @@ 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/libpng-flycast.rb; do
for formula in ./shell/apple/libomp.rb ./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,8 +106,6 @@ 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
@@ -129,7 +127,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' || matrix.config.name == 'arm64-pc-linux-gnu'
if: matrix.config.name == 'x86_64-pc-linux-gnu'
- name: Dump symbols
run: |
@@ -144,7 +142,7 @@ jobs:
./configure
make
sudo make install
if: matrix.config.name == 'x86_64-pc-linux-gnu' || matrix.config.name == 'arm64-pc-linux-gnu'
if: matrix.config.name == 'x86_64-pc-linux-gnu'
- name: Dump symbols and make AppImage (linux)
run: |
@@ -152,10 +150,9 @@ jobs:
dump_syms artifact/bin/flycast > symbols/flycast.sym 2>/dev/null
strip artifact/bin/flycast
shell/linux/make-appimage.sh . artifact/bin appimage
ARCH=$(uname -m)
mv flycast-${ARCH}.AppImage artifact/bin
mv flycast-x86_64.AppImage artifact/bin
rm artifact/bin/flycast
if: matrix.config.name == 'x86_64-pc-linux-gnu' || matrix.config.name == 'arm64-pc-linux-gnu'
if: matrix.config.name == 'x86_64-pc-linux-gnu'
- name: Package app (macos)
run: |
@@ -167,7 +164,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'
@@ -211,4 +208,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' || matrix.config.name == 'arm64-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') && github.event_name == 'push' }}
+1 -4
View File
@@ -49,7 +49,7 @@
url = https://github.com/herumi/xbyak.git
[submodule "core/deps/libjuice"]
path = core/deps/libjuice
url = https://github.com/flyinghead/libjuice.git
url = https://github.com/paullouisageneau/libjuice.git
[submodule "core/deps/websocketpp"]
path = core/deps/websocketpp
url = https://github.com/zaphoyd/websocketpp.git
@@ -65,6 +65,3 @@
[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
+62 -109
View File
@@ -87,7 +87,6 @@ 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")
@@ -107,7 +106,6 @@ 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)
@@ -123,11 +121,6 @@ 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)
@@ -230,7 +223,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_OPENGLES_3_1 HAVE_OIT)
target_compile_definitions(${PROJECT_NAME} PRIVATE GLES GLES3 HAVE_OPENGLES HAVE_OPENGLES3 HAVE_OIT)
if(ANDROID)
target_link_libraries(${PROJECT_NAME} PRIVATE android)
endif()
@@ -286,7 +279,6 @@ 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)
@@ -296,25 +288,6 @@ 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)
@@ -463,6 +436,10 @@ 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)
@@ -503,37 +480,6 @@ 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)
@@ -553,6 +499,39 @@ 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()
@@ -579,6 +558,11 @@ 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)
@@ -598,18 +582,6 @@ 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)
@@ -825,27 +797,18 @@ if(WIN32)
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/dirent)
endif()
# 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)
if(NOT LIBRETRO AND NOT NINTENDO_SWITCH)
target_compile_definitions(${PROJECT_NAME} PRIVATE USE_ICE)
# libjuice
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)
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)
target_link_libraries(${PROJECT_NAME} PRIVATE LibJuice::LibJuiceStatic)
# websocket++
if(NOT LIBRETRO AND NOT NINTENDO_SWITCH)
# websocket++
target_include_directories(${PROJECT_NAME} PRIVATE core/deps/websocketpp)
target_compile_definitions(${PROJECT_NAME} PRIVATE ASIO_STANDALONE USE_WEBSOCKET)
target_compile_definitions(${PROJECT_NAME} PRIVATE ASIO_STANDALONE)
if (MSVC)
target_compile_definitions(${PROJECT_NAME} PRIVATE
_WEBSOCKETPP_CPP11_FUNCTIONAL_
@@ -860,7 +823,6 @@ 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)
@@ -1025,9 +987,7 @@ if(LIBRETRO)
shell/libretro/storage.cpp
shell/libretro/vmu_xhair.cpp)
if(APPLE)
target_sources(${PROJECT_NAME} PRIVATE
shell/libretro/oslib_apple.mm
shell/apple/common/http_client.mm)
target_sources(${PROJECT_NAME} PRIVATE shell/libretro/oslib_apple.mm)
endif()
if(WIN32)
target_link_libraries(${PROJECT_NAME} PRIVATE mswsock)
@@ -1416,24 +1376,19 @@ 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)
# 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()
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)
set(ASSETS shell/apple/emulator-osx/emulator-osx/Images.xcassets)
target_sources(${PROJECT_NAME} PRIVATE ${ASSETS})
source_group("Resources" FILES ${ASSETS})
@@ -1554,7 +1509,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/libjuice
combined-objs/res
COMMAND ${CMAKE_COMMAND} -E chdir combined-objs/xxhash
${CMAKE_AR} -x $<TARGET_FILE:xxHash::xxhash>
COMMAND ${CMAKE_COMMAND} -E chdir combined-objs/chdr
@@ -1569,8 +1524,6 @@ 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}
+2 -4
View File
@@ -73,10 +73,8 @@ constexpr u32 SAMPLE_COUNT = 512; // AudioBackend::push() is always called with
class RingBuffer
{
std::vector<u8> buffer;
// 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 };
std::atomic_int readCursor { 0 };
std::atomic_int writeCursor { 0 };
u32 readSize() {
return (u32)((writeCursor - readCursor + buffer.size()) % buffer.size());
+1 -1
View File
@@ -1,5 +1,5 @@
#pragma once
#include <cstdint>
#include <stdint.h>
#include <string>
#include <vector>
+3 -14
View File
@@ -20,8 +20,6 @@
#include "types.h"
#include "stdclass.h"
#include <cctype>
namespace config {
const IniFile::Entry *IniFile::getEntry(const std::string& sectionName, const std::string& name) const
@@ -73,7 +71,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 + 1 < s.length() && std::isspace(static_cast<unsigned char>(s[pos])))
while (pos < s.length() - 1 && std::isspace((int8_t)s[pos]))
pos++;
if (pos + 1 >= s.length())
return false;
@@ -91,18 +89,9 @@ 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 = static_cast<unsigned>(defaultValue);
unsigned value = defaultValue;
ss >> 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;
return (int)value;
}
int64_t IniFile::getInt64(const std::string& section, const std::string& entry, int64_t defaultValue) const
+1
View File
@@ -21,6 +21,7 @@
#include <cstdio>
#include <map>
#include <string>
#include <cstdint>
#include <vector>
#include <locale>
#include <sstream>
+2 -17
View File
@@ -77,12 +77,11 @@ 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<int> PreloadCustomTextures("rend.PreloadCustomTextures", 0);
Option<bool> PreloadCustomTextures("rend.PreloadCustomTextures");
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");
@@ -111,6 +110,7 @@ Option<int64_t> PixelBufferSize("rend.PixelBufferSize", 512_MB);
Option<int> AnisotropicFiltering("rend.AnisotropicFiltering", 1);
Option<int> TextureFiltering("rend.TextureFiltering", 0); // Default
Option<bool> ThreadedRendering("rend.ThreadedRendering", true);
Option<bool> VblankPacing("");
Option<bool> DupeFrames("rend.DupeFrames", false);
Option<int> PerPixelLayers("rend.PerPixelLayers", 32);
#ifdef TARGET_UWP
@@ -122,20 +122,6 @@ 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);
@@ -259,7 +245,6 @@ 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");
+7 -15
View File
@@ -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,19 +447,11 @@ extern Option<int> PerPixelLayers;
#endif
extern Option<float> ExtraDepthScale;
extern Option<bool> CustomTextures;
enum class CustomTexturePreloadMode : int
{
Off = 0,
SystemMemory = 1,
VideoMemory = 2,
};
extern Option<int> PreloadCustomTextures;
CustomTexturePreloadMode customTexturePreloadMode();
extern Option<bool> PreloadCustomTextures;
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;
@@ -479,6 +471,7 @@ extern Option<int64_t> PixelBufferSize;
extern Option<int> AnisotropicFiltering;
extern Option<int> TextureFiltering; // 0: default, 1: force nearest, 2: force linear
extern Option<bool> ThreadedRendering;
extern Option<bool> VblankPacing;
extern Option<bool> DupeFrames;
extern Option<bool> NativeDepthInterpolation;
extern Option<bool> EmulateFramebuffer;
@@ -579,7 +572,6 @@ 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;
-4
View File
@@ -29,10 +29,6 @@
#include "emulator.h"
#include "oslib/storage.h"
#include "oslib/i18n.h"
#include <string>
#include <vector>
using namespace i18n;
const WidescreenCheat CheatManager::widescreen_cheats[] =
+1 -1
View File
@@ -24,7 +24,7 @@
#include "hw/sh4/dyna/shil.h"
#include "cfg/option.h"
#include <array>
#include <csignal>
#include <signal.h>
#include <map>
#ifndef SIGTRAP
@@ -303,6 +303,8 @@ 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 -4
View File
@@ -3008,10 +3008,7 @@ static bool glsm_state_ctx_init(glsm_ctx_params_t *params)
return false;
#ifdef HAVE_OPENGLES
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;
hw_render.context_type = RETRO_HW_CONTEXT_OPENGLES2;
#else
hw_render.context_type = RETRO_HW_CONTEXT_OPENGL;
if (params->context_type != RETRO_HW_CONTEXT_NONE)
+1 -1
View File
@@ -4,7 +4,7 @@
*********************************************************************/
#define MAX_BLOCK_SIZE 1600
#define MAX_BLOCK_COUNT 1024 /* 16 blocks (~1,000 timers) ran out with a few dozen connections a second */
#define MAX_BLOCK_COUNT 16
#define DECLARE_HEAP(type, orderby) \
struct heap_ ## type { \
+11 -23
View File
@@ -69,8 +69,6 @@ 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;
@@ -100,19 +98,18 @@ 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)size;
return (int32_t)q->size;
}
static inline struct pico_frame *pico_dequeue(struct pico_queue *q)
{
struct pico_frame *p;
struct pico_frame *p = q->head;
if (!p)
return NULL;
if (q->frames < 1)
return NULL;
@@ -120,19 +117,15 @@ static inline struct pico_frame *pico_dequeue(struct pico_queue *q)
if (q->shared)
PICOTCP_MUTEX_LOCK(q->mutex);
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;
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);
@@ -167,11 +160,6 @@ 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;
}
+1 -6
View File
@@ -1710,12 +1710,7 @@ int pico_socket_connect(struct pico_socket *s, const void *remote_addr, uint16_t
return -1;
}
/* 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;
}
pico_socket_alter_state(s, PICO_SOCKET_STATE_BOUND, 0, 0);
#ifdef PICO_SUPPORT_UDP
if (PROTO(s) == PICO_PROTO_UDP) {
+23 -41
View File
@@ -25,7 +25,6 @@
#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"
@@ -43,19 +42,15 @@
#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";
@@ -243,6 +238,13 @@ static void loadSpecialSettings()
}
else
WARN_LOG(BOOT, "No region specified in IP.BIN");
// Broadcast left on Default follows the region, the way Region's own
// Default follows IP.BIN above: PAL for Europe, NTSC otherwise.
// fixUpDCFlash() skips the out-of-range Default value, which left the
// flash byte at the factory setting (NTSC) or at whatever the last
// explicit choice wrote.
if (config::Broadcast == 4)
config::Broadcast.override(config::Region == 2 ? 1 : 0);
if (config::Cable <= 1 && (!ip_meta.supportsVGA()
|| prod_id == "T-12504N" // Caesar's Palace (NTSC)
|| prod_id == "12502D-50")) // Caesar's Palace (PAL)
@@ -264,33 +266,7 @@ 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 == "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)
|| prod_id == "T9512N")) // The Grinch (US)
{
NOTICE_LOG(BOOT, "Game doesn't support RGB. Using TV Composite instead");
config::Cable.override(3);
@@ -310,7 +286,8 @@ static void loadSpecialSettings()
}
else if (prod_id == "T17708N" // Stupid Invaders (US)
|| prod_id == "T17711D" // Stupid Invaders (EU)
|| prod_id == "T46509M") // Suika (JP)
|| prod_id == "T46509M" // Suika (JP)
|| prod_id == "T36901M") // Cool Boarders Burrrn (JP)
{
NOTICE_LOG(BOOT, "Forcing HLE BIOS");
config::UseReios.override(true);
@@ -585,7 +562,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")
if (extension == "bin" || extension == "dat" || extension == "lst")
return DC_PLATFORM_NAOMI;
return DC_PLATFORM_DREAMCAST;
@@ -897,6 +874,7 @@ void loadGameSpecificSettings()
loadSpecialSettings();
config::Settings::instance().setGameId(settings.content.gameId);
custom_texture.init();
// Reload per-game settings
config::Settings::instance().load(true);
@@ -928,8 +906,11 @@ void Emulator::stepRange(u32 from, u32 to)
void Emulator::loadstate(Deserializer& deser)
{
if (config::customTexturePreloadMode() == config::CustomTexturePreloadMode::Off)
custom_texture.refresh();
if (!custom_texture.preloaded())
{
custom_texture.terminate();
custom_texture.init();
}
#if FEAT_AREC == DYNAREC_JIT
aica::arm::recompiler::flush();
#endif
@@ -1114,7 +1095,7 @@ void Emulator::vblank()
EventManager::event(Event::VBlank);
runner.execTasks();
// Time out if a frame hasn't been rendered for 50 ms
if (sh4_sched_now64() - startTime <= 50_sh4ms)
if (!config::VblankPacing && sh4_sched_now64() - startTime <= 50_sh4ms)
return;
if (ggpo::active())
ggpo::endOfFrame();
@@ -1151,6 +1132,7 @@ 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
View File
@@ -28,7 +28,7 @@
#include <mutex>
#include <utility>
#include <vector>
#include <ctime>
#include <time.h>
void loadGameSpecificSettings();
void SaveSettings();
+9 -11
View File
@@ -402,8 +402,7 @@ static void Write_SB_ADST(u32 addr, u32 data)
SB_ADSUSP &= ~0x10;
// Schedule the end of DMA transfer interrupt
// 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;
int cycles = len * (SH4_MAIN_CLOCK / 2 / G2_BUS_CLOCK); // 16 bits @ 25 MHz
if (cycles <= 512)
dma_end_sched(0, 0, 0, nullptr);
else
@@ -545,9 +544,6 @@ 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)
@@ -563,7 +559,10 @@ void deserialize(Deserializer& deser)
deser >> arm::armFiqEnable;
deser >> arm::armMode;
deser >> arm::Arm7Enabled;
deser >> arm::arm7ClockTicks;
if (deser.version() >= Deserializer::V19)
deser >> arm::arm7ClockTicks;
else
arm::arm7ClockTicks = 0;
dsp::state.deserialize(deser);
@@ -574,7 +573,11 @@ 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;
@@ -583,11 +586,6 @@ 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
-3
View File
@@ -6,9 +6,6 @@ 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);
+4
View File
@@ -41,12 +41,16 @@ 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;
}
+20 -12
View File
@@ -202,9 +202,12 @@ public:
{
if (deser.version() < Deserializer::V49)
{
deser.skip<int>(); // beepOn
deser.skip<int>(); // beepPeriod
deser.skip<int>(); // beepCounter
if (deser.version() >= Deserializer::V22)
{
deser.skip<int>(); // beepOn
deser.skip<int>(); // beepPeriod
deser.skip<int>(); // beepCounter
}
init();
}
else
@@ -1096,8 +1099,13 @@ 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)
@@ -1118,9 +1126,6 @@ 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)
{
@@ -1716,13 +1721,16 @@ void deserialize(Deserializer& deser)
deser >> cdda_sector;
deser >> cdda_index;
midiSendBuffer.clear();
u32 size;
deser >> size;
for (u32 i = 0; i < size; i++)
if (deser.version() >= Deserializer::V28)
{
u8 b;
deser >> b;
midiSendBuffer.push_back(b);
u32 size;
deser >> size;
for (u32 i = 0; i < size; i++)
{
u8 b;
deser >> b;
midiSendBuffer.push_back(b);
}
}
}
+6 -17
View File
@@ -28,7 +28,6 @@ using namespace Xbyak::util;
#include "arm7_rec.h"
#include "oslib/unwind_info.h"
#include "oslib/virtmem.h"
#include "stdclass.h"
namespace aica::arm
{
@@ -46,11 +45,11 @@ static UnwindInfo unwinder;
class Arm7Compiler;
#ifdef _WIN32
static constexpr std::array<Xbyak::Reg32, 8> alloc_regs {
static const std::array<Xbyak::Reg32, 8> alloc_regs {
ebx, ebp, edi, esi, r12d, r13d, r14d, r15d
};
#else
static constexpr std::array<Xbyak::Reg32, 6> alloc_regs {
static const std::array<Xbyak::Reg32, 6> alloc_regs {
ebx, ebp, r12d, r13d, r14d, r15d
};
#endif
@@ -1004,13 +1003,8 @@ void arm7backend_compile(const std::vector<ArmOp>& block_ops, u32 cycles)
virtmem::jit_set_exec(protStart, protSize, false);
Arm7Compiler assembler;
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()));
}
assembler.compile(block_ops, cycles);
virtmem::jit_set_exec(protStart, protSize, true);
}
@@ -1022,13 +1016,8 @@ void arm7backend_flush()
unwinder.clear();
Arm7Compiler assembler;
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()));
}
assembler.generateMainLoop();
virtmem::jit_set_exec(protStart, protSize, true);
}
+60 -19
View File
@@ -23,9 +23,48 @@
#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
@@ -52,23 +91,6 @@ 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()
{
@@ -94,6 +116,7 @@ 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);
}
@@ -106,8 +129,12 @@ void bba_Term()
rtl8139_destroy(rtl8139device);
rtl8139device = nullptr;
}
sh4_sched_unregister(rxSchedId);
rxSchedId = -1;
if (rxSchedId != -1)
{
sh4_sched_unregister(rxSchedId);
rxSchedId = -1;
}
rxClear();
}
void bba_Reset(bool hard)
@@ -239,6 +266,20 @@ 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);
+1
View File
@@ -25,3 +25,4 @@ 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);
+2 -1
View File
@@ -2491,7 +2491,8 @@ bool rtl8139_deserialize(RTL8139State *s, Deserializer& deser)
deser >> s->TimerInt;
deser >> s->TCTR_base;
sh4_sched_deserialize(deser, s->schedId);
if (deser.version() >= Deserializer::V38)
sh4_sched_deserialize(deser, s->schedId);
return s->bChipCmdState & CmdRxEnb;
}
+11 -3
View File
@@ -18,7 +18,6 @@
#include "oslib/oslib.h"
#include "stdclass.h"
#include "oslib/storage.h"
#include "oslib/directory.h"
bool MemChip::Load(const std::string& file)
{
@@ -41,8 +40,17 @@ 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) {
safeFullSave(file, data + write_protect_size, 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);
}
bool MemChip::Load(const std::string &prefix, const std::string &names_ro, const std::string &title)
+1 -1
View File
@@ -8,7 +8,7 @@
*/
#pragma once
#include "types.h"
#include <cstring>
#include <string.h>
class X76F100SerialFlash
{
+66 -69
View File
@@ -15,9 +15,7 @@
#include "imgread/common.h"
#include "serialize.h"
int gdrom_schid = -1;
static int statusSchid = -1;
static int nextStatus = -1;
int gdrom_schid;
//Sense: ASC - ASCQ - Key
int sns_asc;
@@ -34,6 +32,15 @@ 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;
@@ -174,17 +181,6 @@ 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;
@@ -265,9 +261,6 @@ 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);
@@ -356,7 +349,6 @@ void gd_setdisc()
gd_disk_type = newd;
SecNumber.DiscFormat = gd_disk_type >> 4;
cddaCancelTransition();
}
static void gd_reset()
@@ -452,7 +444,6 @@ 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;
@@ -762,9 +753,7 @@ 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;
@@ -926,6 +915,7 @@ 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)
{
@@ -941,24 +931,30 @@ 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;
printf_spicmd("SPI_CD_PLAY %d -> %d repeat %d", cdda.CurrAddr.FAD, cdda.EndAddr.FAD, cdda.repeats);
cddaTransition(GD_PLAY, 5_sh4ms, SecNumber.Status);
cdda.status = cdda_t::Playing;
SecNumber.Status = GD_PLAY;
GDStatus.DSC = 1;
}
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)
cddaTransition(GD_STANDBY);
{
cdda.status = cdda_t::Terminated;
SecNumber.Status = GD_STANDBY;
}
else
cddaTransition(GD_PLAY);
{
cdda.status = cdda_t::Playing;
SecNumber.Status = 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);
@@ -972,30 +968,42 @@ 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;
cddaTransition(GD_PAUSE, 20_sh4ms, GD_SEEK);
sh4_sched_request(gdrom_schid, SH4_MAIN_CLOCK / 50); // 20 ms
#else
GDStatus.DSC = 1;
#endif
}
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;
cddaTransition(GD_STANDBY, 5_sh4ms);
sh4_sched_request(gdrom_schid, SH4_MAIN_CLOCK / 50); // 20 ms
#else
SecNumber.Status = GD_STANDBY;
GDStatus.DSC = 1;
#endif
}
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);
@@ -1013,7 +1021,7 @@ static void gd_process_spi_cmd()
case SPI_GET_SCD:
{
printf_spicmd("SPI_GET_SCD %d", packet_cmd.data_8[1] & 0xF);
printf_spicmd("SPI_GET_SCD");
const u32 format = packet_cmd.data_8[1] & 0xF;
const u32 alloc_len = (packet_cmd.data_8[3] << 8) | packet_cmd.data_8[4];
@@ -1226,6 +1234,16 @@ 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;
@@ -1284,33 +1302,13 @@ 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()) {
if (read_params.remaining_sectors == 0 && dma_buff.isEmpty())
//verify(!SB_GDST&1) -> dc can do multi read dma
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)
{
@@ -1352,7 +1350,6 @@ 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();
}
@@ -1360,8 +1357,6 @@ void gdrom_reg_Term()
{
sh4_sched_unregister(gdrom_schid);
gdrom_schid = -1;
sh4_sched_unregister(statusSchid);
statusSchid = -1;
}
void gdrom_reg_Reset(bool hard)
@@ -1399,6 +1394,7 @@ void gdrom_reg_Reset(bool hard)
cdda = {};
gd_disk_type = NoDisk;
data_write_mode = 0;
DriveSel = 0xa0;
Error = {};
IntReason = {};
@@ -1431,6 +1427,7 @@ void serialize(Serializer& ser)
ser << cdda;
ser << gd_state;
ser << gd_disk_type;
ser << data_write_mode;
ser << DriveSel;
ser << Error;
@@ -1440,8 +1437,6 @@ 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)
@@ -1455,14 +1450,20 @@ void deserialize(Deserializer& deser)
deser >> packet_cmd;
deser >> set_mode_offset;
deser >> read_params;
dma_buff.deserialize(deser);
if (deser.version() >= Deserializer::V17) {
dma_buff.deserialize(deser);
}
else {
deser >> packet_cmd;
dma_buff.clear();
}
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.skip<u32>(Deserializer::V62); // data_write_mode
deser >> data_write_mode;
deser >> DriveSel;
deser >> Error;
@@ -1472,10 +1473,6 @@ 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);
}
}
}
+15 -1
View File
@@ -705,5 +705,19 @@ void sb_serialize(Serializer& ser)
void sb_deserialize(Deserializer& deser)
{
deser >> sb_regs;
deser >> SB_ISTNRM1;
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;
}
}
+1 -2
View File
@@ -7,5 +7,4 @@ target_sources(${PROJECT_NAME} PRIVATE
maple_helper.h
maple_if.cpp
maple_if.h
maple_jvs.cpp
wccf_cam.cpp)
maple_jvs.cpp)
+21 -22
View File
@@ -4,7 +4,6 @@
#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"
@@ -15,7 +14,6 @@ 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);
@@ -260,12 +258,6 @@ 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);
@@ -511,8 +503,6 @@ 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)
{
@@ -537,23 +527,30 @@ 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();
u32 size;
deser >> size;
for (u32 i = 0; i < size; i++)
if (deser.version() >= Deserializer::V23)
{
u32 address;
deser >> address;
u32 dataSize;
deser >> dataSize;
mapleDmaOut.emplace_back(address, std::vector<u32>(dataSize));
deser.deserialize(mapleDmaOut.back().second.data(), dataSize);
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);
}
}
for (int i = 0; i < MAPLE_PORTS; i++)
@@ -568,6 +565,8 @@ 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()
+1 -9
View File
@@ -25,7 +25,6 @@ enum MapleDeviceType
MDT_Dreameye = 17,
MDT_SegaControllerXL = 18,
MDT_DreamParaParaController = 19,
MDT_WccfCamera = 20,
MDT_Count
};
@@ -49,11 +48,6 @@ enum PlainJoystickTriggerId
PJTI_Count = 4
};
enum class MaplePortType {
user_accessible,
built_in,
};
struct PlainJoystickState
{
PlainJoystickState()
@@ -124,12 +118,10 @@ void mcfg_DestroyDevices(bool full = true);
void mcfg_SerializeDevices(Serializer& ser);
void mcfg_DeserializeDevices(Deserializer& deser);
constexpr int maple_getPortCount(MapleDeviceType type, MaplePortType port)
constexpr int maple_getPortCount(MapleDeviceType type)
{
switch (type)
{
case MDT_FishingController:
return port == MaplePortType::user_accessible ? 0 : 1;
case MDT_SegaController:
case MDT_SegaControllerXL:
return 2;
+1 -1
View File
@@ -841,6 +841,7 @@ 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);
}
@@ -1887,7 +1888,6 @@ 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);
+4 -8
View File
@@ -2,12 +2,13 @@
#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
{
@@ -203,6 +204,8 @@ 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);
@@ -302,8 +305,6 @@ 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) {}
};
@@ -317,8 +318,3 @@ struct RFIDReaderWriter : public BaseMIE
{
static std::shared_ptr<maple_device> Create();
};
struct WccfCamera : public BaseMIE
{
static std::shared_ptr<maple_device> Create();
};
+2 -2
View File
@@ -21,7 +21,7 @@ enum MaplePattern
std::shared_ptr<maple_device> MapleDevices[MAPLE_PORTS][6];
int maple_schid = -1;
int maple_schid;
/*
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(), MaplePortType::built_in) != 0)
&& maple_getPortCount(pDevice->get_device_type()) != 0)
{
port = getPort(reci);
if (port != 5)
+45 -88
View File
@@ -22,15 +22,11 @@
#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>
@@ -639,7 +635,10 @@ public:
}
void deserialize(Deserializer& deser) override
{
deser >> out;
if (deser.version() >= Deserializer::V31)
deser >> out;
else
out = 0xff;
jvs_837_13844::deserialize(deser);
}
@@ -757,7 +756,10 @@ public:
}
void deserialize(Deserializer& deser) override
{
deser >> testMode;
if (deser.version() >= Deserializer::V31)
deser >> testMode;
else
testMode = false;
if (deser.version() >= Deserializer::V51)
{
deser >> damper_high;
@@ -940,7 +942,8 @@ public:
}
void deserialize(Deserializer& deser) override
{
deser >> gear;
if (deser.version() >= Deserializer::V31)
deser >> gear;
jvs_837_13844_racing::deserialize(deser);
}
@@ -1019,69 +1022,6 @@ 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
{
@@ -1369,11 +1309,8 @@ u32 BaseMIE::RawDma(const u32 *buffer_in, u32 buffer_in_len, u32 *buffer_out)
u32 resp = dma(cmd);
if (resp != MDRE_UnknownCmd)
{
u32 reci = (buffer_in[0] >> 8) & 0xFF;
const 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;
}
@@ -1446,7 +1383,7 @@ u32 BaseMIE::RawDma(const u32 *buffer_in, u32 buffer_in_len, u32 *buffer_out)
case MDC_JVSGetId:
{
LOGJVS("bus[%d] JVS Get Id", bus_id);
DEBUG_LOG(JVS, "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);
@@ -1458,7 +1395,7 @@ u32 BaseMIE::RawDma(const u32 *buffer_in, u32 buffer_in_len, u32 *buffer_out)
break;
default:
INFO_LOG(MAPLE, "bus[%d] BaseMIE: Unknown Maple command %x", bus_id, cmd);
INFO_LOG(MAPLE, "BaseMIE: Unknown Maple command %x", cmd);
reply(MDRE_UnknownCmd);
break;;
}
@@ -1661,10 +1598,6 @@ 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
@@ -1961,7 +1894,18 @@ void MIEImpl::handle_86_subcommand()
std::string eeprom_file = getEepromPath();
if (!eeprom_file.empty())
safeFullSave(eeprom_file, eeprom, sizeof(eeprom));
{
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());
}
}
reply(MDRS_JVSReply, 1);
memcpy(dma_buffer_out, eeprom, 4);
@@ -2094,13 +2038,18 @@ void MIEImpl::deserialize(Deserializer& deser)
{
maple_base::deserialize(deser);
deser >> crazy_mode;
deser >> hotd2p;
if (deser.version() >= Deserializer::V35)
deser >> hotd2p;
else
hotd2p = settings.content.gameId == "hotd2p";
deser >> jvs_repeat_request;
deser >> jvs_receive_length;
deser >> jvs_receive_buffer;
deser >> eeprom;
if (deser.version() >= Deserializer::V23)
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);
}
@@ -2689,7 +2638,7 @@ u32 RFIDReaderWriterImpl::dma(u32 cmd)
return (MapleDeviceRV)0xfe;
case 0xA1: // read card data
LOGJVS("RFID card read (data? %d)", d4Seen);
DEBUG_LOG(JVS, "RFID card read (data? %d)", d4Seen);
w32(getStatus());
if (!d4Seen)
// serial0 and serial1 only
@@ -2718,7 +2667,7 @@ u32 RFIDReaderWriterImpl::dma(u32 cmd)
u32 offset = r8() * 4;
size_t size = r8() * 4;
skip(2);
LOGJVS("RFID card write: offset 0x%x len %d", offset, (int)size);
DEBUG_LOG(JVS, "RFID card write: offset 0x%x len %d", offset, (int)size);
rptr(cardData + offset, std::min(size, sizeof(cardData) - offset));
saveCard();
return (MapleDeviceRV)0xfe;
@@ -2745,7 +2694,7 @@ u32 RFIDReaderWriterImpl::dma(u32 cmd)
counter = 0;
break;
}
LOGJVS("RFID decrement %d", counter);
DEBUG_LOG(JVS, "RFID decrement %d", counter);
cardData[19 - counter]--;
saveCard();
w32(getStatus());
@@ -2853,9 +2802,17 @@ void RFIDReaderWriterImpl::saveCard() const
{
if (transientData)
return;
const std::string path = getCardPath();
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;
}
INFO_LOG(NAOMI, "Saving card file to %s", path.c_str());
safeFullSave(path, cardData, sizeof(cardData));
if (fwrite(cardData, 1, sizeof(cardData), fp) != sizeof(cardData))
WARN_LOG(NAOMI, "Truncated write to file: %s", path.c_str());
fclose(fp);
}
void RFIDReaderWriterImpl::serialize(Serializer& ser) const
File diff suppressed because it is too large. Load diff
+36 -177
View File
@@ -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 = -1;
static int modem_sched;
enum ModemStates
{
@@ -82,24 +82,13 @@ enum ConnectState
HANDSHAKING,
PRE_CONNECTED,
CONNECTED,
NEGO_COMPLETE,
RINGING_IN,
NEGO_COMPLETE
};
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);
@@ -177,7 +166,6 @@ static InStream *curInput = &netIn;
static OutStream *curOutput = &netOut;
static V42Protocol v42Proto { netIn, netOut };
static HdlcSyncAsync hdlcProto { netIn, netOut };
static bool v8bis;
static V8bisProtocol v8bisProto;
@@ -208,16 +196,9 @@ static void configureStreams()
curInput = &v8bisProto;
curOutput = &v8bisProto;
}
else if (modem_regs.reg08.ASYN == 0)
{
if (peer2peerRawMode) {
curInput = &hdlcProto;
curOutput = &hdlcProto;
}
else {
curInput = &v42Proto;
curOutput = &v42Proto;
}
else if (modem_regs.reg08.ASYN == 0) {
curInput = &v42Proto;
curOutput = &v42Proto;
}
else {
curInput = &netIn;
@@ -353,16 +334,6 @@ 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
@@ -391,11 +362,6 @@ 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
@@ -403,24 +369,18 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
switch (connect_state)
{
case DIALING:
if (last_dial_time == 0)
last_dial_time = sh4_sched_now64();
if (sh4_sched_now64() - last_dial_time >= SH4_MAIN_CLOCK)
if (last_dial_time != 0 && 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 {
else
{
last_dial_time = sh4_sched_now64();
modem_regs.reg1e.TDBE = 1;
callback_cycles = 1000_sh4ms; // To switch to Ringing state
schedule_callback(1000); // To switch to Ringing state
}
break;
case RINGING:
@@ -428,8 +388,11 @@ 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 && modem_regs.reg09.ORG)
if (!modem_regs.reg09.DATA && connect_state == RINGING)
{
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RI, 1);
SET_STATUS_BIT(0x0b, modem_regs.reg0b.ATV25, 1);
}
break;
case HANDSHAKING:
LOG("\t\t *** HANDSHAKING STATE ***");
@@ -447,7 +410,8 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
break;
case PRE_CONNECTED:
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RLSD, 1);
if (modem_regs.reg03.RLSDE)
SET_STATUS_BIT(0x0f, modem_regs.reg0f.RLSD, 1);
bool validConfig;
if (modem_regs.CONF == 0xAA)
{
@@ -529,11 +493,8 @@ 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 = 238_sh4us;
callback_cycles = SH4_MAIN_CLOCK / 1000000 * 238; // 238 us
v42Proto.reset();
v42Proto.setOriginator(modem_regs.reg09.ORG);
hdlcProto.reset();
hdlcProto.setOriginator(modem_regs.reg09.ORG);
break;
@@ -558,46 +519,14 @@ static int modem_sched_func(int tag, int cycles, int jitter, void *arg)
}
if (rxFifo.size() < 16)
{
try {
int c = curInput->read();
if (c >= 0) {
rxFifo.push_back(c);
updateRxFifoStatus();
}
} catch (const net::modbba::ConnectionClosed& e) {
hangUp();
int c = curInput->read();
if (c >= 0) {
rxFifo.push_back(c);
updateRxFifoStatus();
}
}
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;
}
@@ -608,13 +537,6 @@ 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;
}
@@ -626,15 +548,6 @@ void ModemInit()
void ModemReset()
{
net::modbba::stop();
onAnswerCallback = {};
onHandshakeCallback = {};
onDialCallback = {};
periodicCallback = {};
periodicCycles = 0;
lastDSPRamWrite = 0;
ringStart = 0;
peer2peerRawMode = false;;
keepRinging = false;
}
void ModemTerm()
@@ -747,8 +660,6 @@ static void modem_reset(u32 v)
state = MS_RESET;
LOG("Modem reset start ...");
net::modbba::stop();
lastDSPRamWrite = 0;
keepRinging = false;
}
else
{
@@ -767,14 +678,11 @@ 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)
&& !keepRinging)
&& (connect_state == RINGING || connect_state == NEGO_COMPLETE))
{
LOG("DTR asserted. starting handshaking");
connect_state = HANDSHAKING;
schedule_callback(1);
if (onHandshakeCallback)
onHandshakeCallback();
}
}
@@ -840,7 +748,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) && !modem_regs.reg09.DATA)
else if (connect_state == DISCONNECTED || connect_state == DIALING)
{
//LOG("ModemNormalWrite : TBUFFER = %X", data);
if (connect_state == DISCONNECTED)
@@ -848,10 +756,7 @@ 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)
@@ -860,12 +765,7 @@ static void ModemNormalWrite(u32 reg, u32 data)
#ifndef NDEBUG
sent_bytes++;
#endif
try {
curOutput->write(data);
} catch (const net::modbba::ConnectionClosed& e) {
hangUp();
break;
}
curOutput->write(data);
modem_regs.reg1e.TDBE = 0;
txFifoSize += 1.f;
@@ -960,10 +860,6 @@ 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
{
@@ -1152,17 +1048,19 @@ void ModemSerialize(Serializer& ser)
}
void ModemDeserialize(Deserializer& deser)
{
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);
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);
}
rxFifo.clear();
v42Proto.reset();
hdlcProto.reset();
v8bis = false;
v8bisProto.reset();
configureStreams();
@@ -1313,42 +1211,3 @@ 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);
}
-8
View File
@@ -23,7 +23,6 @@
#pragma once
#include "types.h"
#include <functional>
void ModemInit();
void ModemReset();
@@ -32,10 +31,3 @@ 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);
+47 -369
View File
@@ -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, int crc)
void HdlcEncoder::sendFrame(const std::vector<u8>& data)
{
// opening flag
sendFlag();
@@ -159,8 +159,7 @@ void HdlcEncoder::sendFrame(const std::vector<u8>& data, int crc)
for (u8 b : data)
sendByte(b);
// crc
if (crc < 0)
crc = calcCrc16(data);
u16 crc = calcCrc16(data);
sendByte(crc & 0xff);
sendByte(crc >> 8);
// closing flag
@@ -222,7 +221,7 @@ void HdlcDecoder::write(u8 v)
if (rxOnes == 6)
{
// we have a flag
if (rxFrame.size() >= 4)
if (!rxFrame.empty())
{
u16 crc = calcCrc16(rxFrame, rxFrame.size() - 2);
if (crc != *(u16 *)(&rxFrame.back() - 1)) {
@@ -230,18 +229,12 @@ void HdlcDecoder::write(u8 v)
}
else
{
if (!keepCrc) {
rxFrame.pop_back();
rxFrame.pop_back();
}
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;
}
@@ -366,8 +359,6 @@ 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");
@@ -376,17 +367,7 @@ void V42Protocol::handleFrame()
handleDisc(rxFrame[0]);
break;
case 0x63: // UA
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;
}
receiverReady = true;
break;
case 0x87: // FRMR
WARN_LOG(MODEM, "Received FRMR");
@@ -412,18 +393,6 @@ 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)
@@ -473,7 +442,10 @@ void V42Protocol::handleIFrame(const std::vector<u8>& rxFrame)
outputStream.write(c);
}
} catch (const v42b::Exception& e) {
sendDisc();
// send DISC
std::vector<u8> disc { 1, 0x43 };
sendFrame(disc);
phase = Release;
sentIFrames.clear();
return;
}
@@ -492,29 +464,6 @@ 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;
@@ -685,12 +634,10 @@ void V42Protocol::handleXid(std::vector<u8> rxFrame)
i += paramSize;
}
}
if (originator)
{
if (userDataOffset != 0)
rxFrame.resize(userDataOffset);
sendFrame(rxFrame);
}
if (userDataOffset != 0)
rxFrame.resize(userDataOffset);
sendFrame(rxFrame);
if (compressionEnabled)
{
NOTICE_LOG(MODEM, "V.42bis compression enabled: max dictionary size %d", maxCodeWords);
@@ -775,24 +722,8 @@ void V42Protocol::sendIFrame()
int V42Protocol::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();
}
if (phase == None || phase == Detection) {
v14Encoder.flush();
return v14Encoder.read();
}
else if (phase == V14)
@@ -803,7 +734,7 @@ int V42Protocol::read()
}
if (hdlcEncoder.available() == 0)
{
if (phase == Connected || phase == DiscSent)
if (phase == Connected)
{
sendIFrame();
if (hdlcEncoder.available() != 0)
@@ -817,17 +748,7 @@ int V42Protocol::read()
return hdlcEncoder.read();
}
}
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();
sendFlag();
}
return hdlcEncoder.read();
}
@@ -836,19 +757,12 @@ void V42Protocol::write(u8 v)
{
if (phase == None || phase == Detection)
{
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.
if (detectionTimer.expired()) {
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)
@@ -856,81 +770,49 @@ void V42Protocol::write(u8 v)
v = c;
}
}
switch (phase)
if (phase == None)
{
case None:
if (originator)
if ((v == 0x11 || v == 0x91)
&& (v14Decoder.receivedStopBits() == 9 || v14Decoder.receivedStopBits() == 17))
{
if ((v == 0x11 || v == 0x91)
&& (v14Decoder.receivedStopBits() >= 9 && v14Decoder.receivedStopBits() <= 17))
{
phase = Detection;
lastRx = v;
odpCount = 1;
}
phase = Detection;
lastRx = v;
odpCount = 1;
}
break;
case Detection:
if (originator)
}
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)
{
// 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++;
if (odpCount == 4)
{
odpCount = 0;
if (v == 0x7e)
phase = Establish;
// 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
{
// Answerer
if ((v == 'E' || v == 'C')
&& (v14Decoder.receivedStopBits() >= 9 && v14Decoder.receivedStopBits() <= 17)
&& v != lastRx)
{
++odpCount;
}
else
{
if (odpCount >= 10)
phase = Establish;
else
odpCount = 0;
}
odpCount = 0;
if (v == 0x7e)
phase = Establish;
}
lastRx = v;
break;
case Establish:
case Connected:
case DiscSent:
}
else if (phase == Establish || phase == Connected) {
hdlcDecoder.write(v);
handleFrame();
break;
case V14:
}
else if (phase == V14) {
v14PipeOut.write(v);
break;
default:
break;
}
}
@@ -968,210 +850,6 @@ 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)
+2 -67
View File
@@ -20,6 +20,7 @@
#include "types.h"
#include "internal.h"
#include "v42bis.h"
#include "network/netservice.h"
#include <vector>
#include <deque>
#include <unordered_map>
@@ -70,9 +71,6 @@ 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();
@@ -84,13 +82,12 @@ private:
int rxOnes = 0;
int rxPosition = 0;
u8 rxCurByte = 0;
bool keepCrc;
};
class HdlcEncoder : public BufferedTransformer
{
public:
void sendFrame(const std::vector<u8>& data, int crc = -1);
void sendFrame(const std::vector<u8>& data);
void sendFlag();
void reset() override;
@@ -117,21 +114,15 @@ 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!
};
@@ -190,66 +181,10 @@ 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.
+1
View File
@@ -19,6 +19,7 @@
#pragma once
#include "types.h"
#include "internal.h"
#include <deque>
#include <map>
#include <vector>
#include <stdexcept>
+4 -2
View File
@@ -154,8 +154,10 @@ void serialize(Serializer& ser)
void deserialize(Deserializer& deser)
{
deser >> aw_maple_devs;
deser >> coin_chute_time;
deser >> aw_ram_test_skipped;
if (deser.version() >= Deserializer::V20) {
deser >> coin_chute_time;
deser >> aw_ram_test_skipped;
}
}
} // namespace atomiswave
+9 -353
View File
@@ -18,13 +18,10 @@
*/
#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>
@@ -43,8 +40,6 @@ public:
INFO_LOG(NAOMI, "Card inserted");
}
virtual void ejectCard() {}
protected:
virtual bool loadCard() = 0;
@@ -66,8 +61,16 @@ 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());
safeFullSave(path, cardData, len);
if (fwrite(cardData, 1, len, fp) != len)
WARN_LOG(NAOMI, "Truncated write to file: %s", path.c_str());
fclose(fp);
}
template<typename T>
@@ -652,343 +655,6 @@ 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>();
@@ -1008,11 +674,6 @@ void clubkInit() {
cardReader = std::make_unique<ClubKartCardReader>();
}
void wccfInit() {
term();
cardReader = std::make_unique<SaxaHW210>();
}
void term() {
cardReader.reset();
}
@@ -1104,11 +765,6 @@ void insertCard(int playerNum)
else
insertRfidCard(playerNum);
}
void ejectCard()
{
if (cardReader != nullptr)
cardReader->ejectCard();
}
bool readerAvailable()
{
-2
View File
@@ -24,7 +24,6 @@ namespace card_reader {
void initdInit();
void derbyInit();
void clubkInit();
void wccfInit();
void term();
void barcodeInit();
@@ -34,7 +33,6 @@ std::string barcodeGetCard();
void barcodeSetCard(const std::string& card);
void insertCard(int playerNum);
void ejectCard();
bool readerAvailable();
}
+16 -5
View File
@@ -23,7 +23,6 @@
#include "dinokich_card.h"
#include "cfg/option.h"
#include "oslib/oslib.h"
#include "oslib/directory.h"
#include <algorithm>
namespace systemsp
@@ -386,7 +385,16 @@ DinokichCardReader::Reply DinokichCardReader::on_63(const std::vector<u8>& f) {
void DinokichCardReader::saveCard(int slotId)
{
Slot& slot = slots_[slotId];
safeFullSave(getCardDataPath(index + slotId), slot.eeprom.data(), slot.eeprom.size());
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());
}
}
void DinokichCardReader::serialize(Serializer& ser) const
@@ -437,9 +445,12 @@ void DinokichCardReader::deserialize(Deserializer& deser)
deser.skip<u8>(); // expectedBytes
deser >> size;
deser.skip(size);
deser.skip<int>(); // state
deser.skip<int>(); // rowCounter
deser.skip(128); // cardData
if (deser.version() >= Deserializer::V41)
{
deser.skip<int>(); // state
deser.skip<int>(); // rowCounter
deser.skip(128); // cardData
}
}
}
}
+16 -36
View File
@@ -23,7 +23,6 @@
#include "naomi.h"
#include "oslib/i18n.h"
#include <algorithm>
#include <zlib.h>
/*
@@ -540,28 +539,26 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector<u8> *digest)
// directory
u8 dir_sector[2048];
// find data of file
if (netpic == 0)
{
u32 file_size;
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, fileSize);
find_file(name, dir_sector, file_start, file_size);
if (file_start && fileSize == 0x100) {
if (file_start && (file_size == 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)
@@ -586,15 +583,15 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector<u8> *digest)
}
}
find_file(name, dir_sector, file_start, fileSize);
find_file(name, dir_sector, file_start, file_size);
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, fileSize);
find_file("*.BIN", dir_sector, file_start, file_size);
if (file_start != 0)
{
u32 file_rounded_size = (fileSize + 2047) & ~2048;
u32 file_rounded_size = (file_size + 2047) & ~2048;
for (dimm_data_size = 4096; dimm_data_size < file_rounded_size; dimm_data_size <<= 1)
;
dimm_data = (u8 *)malloc(dimm_data_size);
@@ -608,20 +605,6 @@ 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)
@@ -637,16 +620,13 @@ 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 + start / 2048,
dimm_data + start,
read_gdrom(gdrom.get(), file_start + (segment * SEGMENT_SIZE) / 2048,
dimm_data + segment * SEGMENT_SIZE,
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 + start);
u64 *pData = (u64 *)(dimm_data + segment * SEGMENT_SIZE);
for (u32 i = 0; i < SEGMENT_SIZE; i += 8, pData++)
*pData = des_encrypt_decrypt<true>(*pData, des_subkeys);
loadedSegments[segment] = true;
@@ -779,18 +759,18 @@ void GDCartridge::process()
break;
case 1: // network commands
WARN_LOG(NAOMI, "Network command received cmd %x. Need full NetDIMM?", cmd);
returnToNaomi(dimm_command & 0x7e00, true, 0, -1);
returnToNaomi(true, 0, -1);
break;
default:
WARN_LOG(NAOMI, "Unknown DIMM command group %d cmd %x", cmdGroup, cmd);
returnToNaomi(dimm_command & 0x7e00, true, 0, -1);
returnToNaomi(true, 0, -1);
break;
}
}
void GDCartridge::returnToNaomi(u16 cmd, bool failed, u16 offsetl, u32 parameter)
void GDCartridge::returnToNaomi(bool failed, u16 offsetl, u32 parameter)
{
dimm_command = (cmd + 0x400) | (failed ? 0xff : 0x4);
dimm_command = ((dimm_command & 0x7e00) + 0x400) | (failed ? 0xff : 0x4);
dimm_offsetl = offsetl;
dimm_parameterh = parameter >> 16;
dimm_parameterl = parameter;
+1 -7
View File
@@ -33,13 +33,10 @@ 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(u16 cmd, bool failed, u16 offsetl, u32 parameter);
void returnToNaomi(bool failed, u16 offsetl, u32 parameter);
template<typename T>
void peek(u32 address)
@@ -72,9 +69,6 @@ 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;
+12 -4
View File
@@ -323,10 +323,18 @@ private:
deser >> maxBet;
deser >> minBet;
deser >> addBet;
deser >> paidAmount;
deser >> wonAmount;
deser >> betButton;
deser >> curBase;
if (deser.version() >= Deserializer::V39)
{
deser >> paidAmount;
deser >> wonAmount;
deser >> betButton;
deser >> curBase;
}
else
{
paidAmount = 0;
wonAmount = 0;
}
}
u32 expectedBytes = 0;
+22 -10
View File
@@ -269,18 +269,30 @@ void serialize(Serializer& ser)
void deserialize(Deserializer& deser)
{
if (initialized) {
deser >> midiTxBuf;
deser >> midiTxBufIndex;
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
}
}
else if (deser.version() < Deserializer::V51) {
deser.skip(4); // midiTxBuf
deser.skip<u32>(); // 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;
}
if (initialized)
deser >> calibrating;
else if (deser.version() < Deserializer::V51)
deser.skip<bool>(); // calibrating
if (initialized)
{
maxSpring = 0x7f;
+41 -30
View File
@@ -25,7 +25,6 @@
#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"
@@ -127,21 +126,12 @@ static int naomiDmaSched(int tag, int sch_cycl, int jitter, void *arg)
void* ptr = CurrentCartridge->GetDmaPtr(block_len);
if (block_len == 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);
}
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;
}
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);
}
WriteMemBlock_nommu_ptr(start, (u32*)ptr, block_len);
CurrentCartridge->AdvancePtr(block_len);
len -= block_len;
start += block_len;
@@ -175,6 +165,7 @@ 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;
@@ -224,19 +215,7 @@ 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);
}
@@ -257,11 +236,11 @@ const u8 *getGameSerialId()
void naomi_reg_Term()
{
naomi_cart_Close();
multiboard.reset();
m3comm.reset();
networkOutput.term();
sh4_sched_unregister(dmaSchedId);
if (dmaSchedId != -1)
sh4_sched_unregister(dmaSchedId);
dmaSchedId = -1;
midiffb::term();
}
@@ -310,8 +289,40 @@ void naomi_Serialize(Serializer& ser)
}
void naomi_Deserialize(Deserializer& deser)
{
mainSerialId.deserialize(deser);
romSerialId.deserialize(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;
}
atomiswave::deserialize(deser);
midiffb::deserialize(deser);
if (deser.version() >= Deserializer::V45)
+119 -76
View File
@@ -278,8 +278,7 @@ 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, "dragntr", 7) == 0)
|| strncmp(game->name, "wccf", 4) == 0)
gdcart = new NetDimm(game->size);
else
gdcart = new GDCartridge(game->size);
@@ -467,59 +466,143 @@ static void loadDecryptedRom(const std::string& path, const std::string& fileNam
}
}
// 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 folder;
std::vector<std::string> files;
std::vector<u32> fstart;
std::vector<u32> fsize;
u32 romSize = 0;
fp->seek(0, SEEK_END);
const u32 romSize = (u32)fp->tell();
fp->seek(0, SEEK_SET);
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);
INFO_LOG(NAOMI, "raw: %.2f MB set address space", romSize / 1024.f / 1024.f);
if (romSize == 0) {
delete fp;
throw FlycastException(Ts("Invalid empty ROM"));
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();
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) {
delete fp;
if (romBase == nullptr)
throw FlycastException(Ts("Out of memory"));
}
u32 offset = 0;
bool error = false;
while (offset < romSize && !error)
bool load_error = false;
for (size_t i = 0; i<files.size(); i++)
{
const u32 len = std::min(8192u, romSize - offset);
error = fp->read(romBase + offset, len, 1) != 1;
offset += len;
if (progress != nullptr)
hostfs::File *fp = nullptr;
if (files[i] != "null")
{
if (progress->cancelled)
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;
progress->progress = (float)offset / romSize;
}
}
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;
}
}
}
delete fp;
if (progress->cancelled) {
free(romBase);
throw LoadCancelledException();
}
if (error)
if (load_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");
@@ -622,11 +705,6 @@ 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"))
@@ -675,41 +753,6 @@ 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
+2 -6
View File
@@ -20,14 +20,11 @@
*/
#include "naomi_flashrom.h"
#include "hw/flashrom/nvmem.h"
#include "hw/maple/maple_devs.h"
#include "cfg/option.h"
extern u8 *EEPROM;
// CRC-16-CCITT variant
u16 eeprom_crc(const u8 *buf, int size)
static u16 eeprom_crc(const u8 *buf, int size)
{
// custom seed
int n = 0xdebdeb00;
for (int i = 0; i < size; i++)
@@ -43,7 +40,6 @@ 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)
-1
View File
@@ -27,4 +27,3 @@ 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);
-10
View File
@@ -6141,7 +6141,6 @@ 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)
{
@@ -6174,7 +6173,6 @@ 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)
{
@@ -6190,7 +6188,6 @@ 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)
{
@@ -6206,7 +6203,6 @@ 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)
{
@@ -6222,7 +6218,6 @@ 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)
{
@@ -6238,7 +6233,6 @@ 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)
{
@@ -6254,7 +6248,6 @@ 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)
{
@@ -6270,7 +6263,6 @@ 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)
{
@@ -6286,7 +6278,6 @@ 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)
{
@@ -6302,7 +6293,6 @@ const Game Games[] =
{ "317-0456-exp.pic", 0, 0x4000, 0xfb7dd592 },
},
"cdv-10027",
&wccf_inputs,
},
// AtomisWave Roms
-27
View File
@@ -628,33 +628,6 @@ 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
//
+122 -1055
View File
File diff suppressed because it is too large. Load diff
+25 -38
View File
@@ -19,21 +19,11 @@
#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;
@@ -41,15 +31,6 @@ 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;
@@ -81,7 +62,12 @@ private:
dimm_parameterh = value >> 16;
}
sock_t getSocket(int idx);
sock_t getSocket(int idx)
{
if (idx < 1 || idx > (int)sockets.size())
return INVALID_SOCKET;
return sockets[idx - 1].fd;
}
bool isBusy() const
{
@@ -95,7 +81,23 @@ private:
Socket() = default;
Socket(sock_t fd) : fd(fd) {}
int close();
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;
}
bool isClosed() const {
return fd == INVALID_SOCKET;
@@ -120,28 +122,13 @@ 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 wccf = false;
bool finalTuned = 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 / 120;
static constexpr int POLL_CYCLES = SH4_MAIN_CLOCK / 60;
};
+6 -1
View File
@@ -1203,7 +1203,12 @@ void serialize(Serializer& ser)
void deserialize(Deserializer& deser)
{
if (printer != nullptr)
printer->deserialize(deser);
{
if (deser.version() >= Deserializer::V35)
printer->deserialize(deser);
else
init();
}
}
#endif
+19 -6
View File
@@ -30,7 +30,6 @@
#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"
@@ -343,9 +342,17 @@ public:
deser >> size;
recvBuffer.resize(size);
deser.deserialize(recvBuffer.data(), recvBuffer.size());
deser >> state;
deser >> rowCounter;
deser.deserialize(cardData.data(), cardData.size());
if (deser.version() >= Deserializer::V41)
{
deser >> state;
deser >> rowCounter;
deser.deserialize(cardData.data(), cardData.size());
}
else
{
state = Off;
rowCounter = 0;
}
}
private:
@@ -425,7 +432,13 @@ private:
void saveData()
{
safeFullSave(getCardDataPath(), cardData.data(), cardData.size());
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);
}
u32 dinoShuffle(u32 v) {
@@ -670,7 +683,7 @@ private:
SerialPort *port;
std::deque<u8> toSend;
std::vector<u8> recvBuffer;
int schedId;
int schedId = 0;
u32 lastPosX = ~0;
u32 lastPosY = ~0;
bool lastButton = false;
-1
View File
@@ -26,7 +26,6 @@
#include "serialize.h"
#include "network/net_platform.h"
#include <memory>
#include <vector>
namespace systemsp
{
+24 -64
View File
@@ -1,9 +1,7 @@
#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"
@@ -14,7 +12,6 @@
#include "profiler/fc_profiler.h"
#include "network/ggpo.h"
#include <cassert>
#include <mutex>
#include <deque>
@@ -33,6 +30,9 @@ 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,7 +199,6 @@ 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);
@@ -253,7 +252,6 @@ private:
void renderFramebuffer(const FramebufferInfo& config)
{
FC_PROFILE_SCOPE;
renderer->processGpuCleanupOperations();
#ifdef LIBRETRO
int w, h;
@@ -270,7 +268,7 @@ private:
if (renderer->Present())
{
presented = true;
if (!config::ThreadedRendering && !ggpo::active())
if (!config::ThreadedRendering && !config::VblankPacing && !ggpo::active())
emu.getSh4Executor()->Stop();
#ifdef LIBRETRO
retro_rend_present();
@@ -489,65 +487,12 @@ 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());
@@ -666,7 +611,7 @@ int rend_end_render(int tag, int cycles, int jitter, void *arg)
void rend_vblank()
{
if (config::EmulateFramebuffer
|| (!render_called && FB_R_CTRL.fb_enable && FramebufferWatcher::Instance().isDirty()))
|| (!render_called && fb_dirty && FB_R_CTRL.fb_enable))
{
if (rend_is_enabled())
{
@@ -677,13 +622,21 @@ 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)
@@ -740,13 +693,20 @@ void rend_serialize(Serializer& ser)
{
ser << fb_w_cur;
ser << render_called;
FramebufferWatcher::Instance().serialize(ser);
ser << fb_dirty;
ser << fb_watch_addr_start;
ser << fb_watch_addr_end;
}
void rend_deserialize(Deserializer& deser)
{
deser >> fb_w_cur;
deser >> render_called;
FramebufferWatcher::Instance().deserialize(deser);
if (deser.version() >= Deserializer::V20)
{
deser >> render_called;
deser >> fb_dirty;
deser >> fb_watch_addr_start;
deser >> fb_watch_addr_end;
}
pend_rend = false;
fbAddrHistory[0] = 1;
fbAddrHistory[1] = 1;
+5 -17
View File
@@ -2,13 +2,8 @@
#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();
@@ -26,8 +21,6 @@ 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();
@@ -82,20 +75,10 @@ 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;
@@ -117,6 +100,11 @@ 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;
+18 -3
View File
@@ -514,13 +514,26 @@ 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;
deser >> projMatrix;
if (deser.version() < Deserializer::V40)
{
deser.skip<u32>(); // projMatrix address
resetProjectionMatrix();
}
else {
deser >> projMatrix;
}
u32 tileclip;
deser >> tileclip;
ta_set_tileclip(tileclip);
@@ -1782,8 +1795,10 @@ void reset(bool hard)
void term()
{
sh4_sched_unregister(schedId);
schedId = -1;
if (schedId != -1) {
sh4_sched_unregister(schedId);
schedId = -1;
}
}
void vmem_init()
+4 -1
View File
@@ -97,7 +97,10 @@ void deserialize(Deserializer& deser)
deser >> ta_fsm[2048];
deser >> ta_fsm_cl;
deser >> taRenderPass;
if (deser.version() >= Deserializer::V29)
deser >> taRenderPass;
else
taRenderPass = 0;
DeserializeTAContext(deser);
if (!deser.rollback())
+26 -93
View File
@@ -11,6 +11,8 @@
#include "hw/holly/holly_intc.h"
#include "serialize.h"
static u32 pvr_map32(u32 offset32);
RamRegion vram;
// YUV converter code
@@ -224,14 +226,11 @@ void DYNACALL pvr_write32p(u32 addr, T data)
return;
}
addr &= ~(sizeof(T) - 1);
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;
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;
}
template void pvr_write32p<u8, false>(u32 addr, u8 data);
template void pvr_write32p<u8, true>(u32 addr, u8 data);
@@ -285,6 +284,25 @@ 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)
{
@@ -316,88 +334,3 @@ 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;
}
-37
View File
@@ -19,45 +19,8 @@ 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();
};
+4 -4
View File
@@ -171,9 +171,11 @@ 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;
FramebufferWatcher::Instance().fbSizeUpdated();
fb_dirty = false;
check_framebuffer_write();
}
return;
@@ -190,8 +192,6 @@ 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;
+6 -11
View File
@@ -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 = -1;
int vblank_schid = -1;
int render_end_schid;
int vblank_schid;
static std::array<double, 4> real_times;
static std::array<u64, 4> cpu_cycles;
@@ -42,7 +42,7 @@ static u32 lightgun_hpos;
static bool maple_int_pending;
#ifdef LIBRETRO
extern bool retro_refresh_av_info(double fps);
extern void retro_setfps(double fps);
#endif
void CalculateSync()
@@ -64,7 +64,7 @@ void CalculateSync()
sh4_sched_request(vblank_schid, Line_Cycles);
#ifdef LIBRETRO
retro_refresh_av_info((double)SH4_MAIN_CLOCK / ((double)Line_Cycles * (double)pvr_numscanlines));
retro_setfps((double)SH4_MAIN_CLOCK / ((double)Line_Cycles * (double)pvr_numscanlines));
#endif
}
@@ -319,12 +319,11 @@ 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;
@@ -333,8 +332,4 @@ 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);
}
}
+30 -12
View File
@@ -245,6 +245,12 @@ 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)
@@ -264,18 +270,30 @@ void DeserializeTAContext(Deserializer& deser)
{
if (::ta_ctx != nullptr)
SetCurrentTARC(TACTX_NONE);
u32 listSize;
deser >> listSize;
for (const auto& ctx : ctx_list)
tactx_Recycle(ctx);
ctx_list.clear();
for (u32 i = 0; i < listSize; i++)
if (deser.version() >= Deserializer::V25)
{
TA_context *ctx;
deserializeContext(deser, &ctx);
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);
}
int curCtx;
deser >> curCtx;
if (curCtx >= 0 && curCtx < (int)ctx_list.size())
SetCurrentTARC(ctx_list[curCtx]->Address);
}
+28 -57
View File
@@ -36,31 +36,6 @@ 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)
@@ -574,8 +549,8 @@ private:
d_pp->pcw = pp->pcw;
d_pp->tileclip = tileclip_val;
if (fetchTextures)
loadTexture(d_pp, 0);
if (d_pp->pcw.Texture && fetchTextures)
d_pp->texture = renderer->GetTexture(d_pp->tsp, d_pp->tcw);
}
#define glob_param_bdc(pp) glob_param_bdc_( (TA_PolyParam0*)pp)
@@ -634,8 +609,8 @@ private:
CurrentPP->tsp1.full = pp->tsp1.full;
CurrentPP->tcw1.full = pp->tcw1.full;
if (fetchTextures)
loadTexture(CurrentPP, 1);
if (pp->pcw.Texture && fetchTextures)
CurrentPP->texture1 = renderer->GetTexture(pp->tsp1, pp->tcw1, 1);
}
// Intensity, with Two Volumes
@@ -647,8 +622,8 @@ private:
CurrentPP->tsp1.full = pp->tsp1.full;
CurrentPP->tcw1.full = pp->tcw1.full;
if (fetchTextures)
loadTexture(CurrentPP, 1);
if (pp->pcw.Texture && fetchTextures)
CurrentPP->texture1 = renderer->GetTexture(pp->tsp1, pp->tcw1, 1);
}
static void TACALL AppendPolyParam4B(void* vpp)
@@ -1006,8 +981,8 @@ private:
d_pp->pcw = spr->pcw;
d_pp->tileclip = tileclip_val;
if (fetchTextures)
loadTexture(d_pp, 0);
if (d_pp->pcw.Texture && fetchTextures)
d_pp->texture = renderer->GetTexture(d_pp->tsp, d_pp->tcw);
SFaceBaseColor = spr->BaseCol;
SFaceOffsColor = spr->OffsCol;
@@ -1251,7 +1226,8 @@ static void ta_parse_vdrc(TA_context* ctx, bool primRestart)
ta_parse_reset();
PolyParam *bgpp = &vd_rc.global_param_op.front();
loadTexture(bgpp, 0);
if (bgpp->pcw.Texture)
bgpp->texture = renderer->GetTexture(bgpp->tsp, bgpp->tcw);
TA_context *childCtx = ctx;
int pass = 0;
@@ -1300,26 +1276,6 @@ 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;
}
@@ -1327,11 +1283,26 @@ 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)
loadTextures(&pp);
{
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);
}
for (PolyParam& pp : ctx->rend.global_param_pt)
loadTextures(&pp);
{
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);
}
for (PolyParam& pp : ctx->rend.global_param_tr)
loadTextures(&pp);
{
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);
}
ctx->rend.newRenderPass();
RenderPass previousPass{};
+64 -70
View File
@@ -61,88 +61,82 @@ DynarecCodeEntryPtr DYNACALL bm_GetCodeByVAddr(u32 addr)
if (addr & 1)
{
if (!settings.content.windowsCE) {
Do_Exception(addr, Sh4Ex_AddressErrorRead);
}
else
switch (addr)
{
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 *)
case 0xfffffd05: // QueryPerformanceCounter(u64 *)
{
bool isRam;
u64 *ptr;
u32 paddr;
if (rdv_writeMemImmediate(Sh4cntx.r[4], sizeof(u64), (void*&)ptr, isRam, paddr) && isRam)
{
bool isRam;
u64 *ptr;
u32 paddr;
if (rdv_writeMemImmediate(Sh4cntx.r[4], sizeof(u64), (void*&)ptr, isRam, paddr) && isRam)
{
*ptr = sh4_sched_now64() >> 4;
Sh4cntx.r[0] = 1;
Sh4cntx.pc = Sh4cntx.pr;
Sh4cntx.cycle_counter -= 100;
}
else
{
Do_Exception(addr, Sh4Ex_AddressErrorRead);
}
*ptr = sh4_sched_now64() >> 4;
Sh4cntx.r[0] = 1;
Sh4cntx.pc = Sh4cntx.pr;
Sh4cntx.cycle_counter -= 100;
}
break;
case 0xfffffded: // GetProcAddressW
else
{
// 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
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++)
{
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 (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;
}
}
if (success)
{
INFO_LOG(DYNAREC, "GetProcAddressW('UA_QueryD') intercepted");
Sh4cntx.r[0] = 0xffff0001;
Sh4cntx.pc = Sh4cntx.pr;
}
else {
Do_Exception(addr, Sh4Ex_AddressErrorRead);
vaddr += 2;
if ((vaddr & 0xfff) == 0)
pa = 0;
else
pa += 2;
}
}
break;
case 0xffff0001: // UA_QueryD stub
DEBUG_LOG(DYNAREC, "UA_QueryD called");
Sh4cntx.r[0] = 0;
Sh4cntx.pc = Sh4cntx.pr;
break;
if (success)
{
INFO_LOG(DYNAREC, "GetProcAddressW('UA_QueryD') intercepted");
Sh4cntx.r[0] = 0xffff0001;
Sh4cntx.pc = Sh4cntx.pr;
}
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;
#endif
default:
Do_Exception(addr, Sh4Ex_AddressErrorRead);
break;
}
default:
Do_Exception(addr, Sh4Ex_AddressErrorRead);
break;
}
addr = Sh4cntx.pc;
}
+5 -5
View File
@@ -24,7 +24,6 @@
#include "hw/sh4/modules/mmu.h"
#include "ssa.h"
#include <array>
#include <deque>
#include <map>
#include <vector>
@@ -38,18 +37,19 @@ public:
RegAlloc() = default;
virtual ~RegAlloc() = default;
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)
void DoAlloc(RuntimeBlockInfo* block, const nreg_t* regs_avail, const nregf_t* regsf_avail)
{
this->block = block;
SSAOptimizer optim(block);
optim.AddVersionPass();
verify(host_gregs.empty());
host_gregs.insert(host_gregs.end(), regs_avail.begin(), regs_avail.end());
while (*regs_avail != (nreg_t)-1)
host_gregs.push_back(*regs_avail++);
verify(host_fregs.empty());
host_fregs.insert(host_fregs.end(), regsf_avail.begin(), regsf_avail.end());
while (*regsf_avail != (nregf_t)-1)
host_fregs.push_back(*regsf_avail++);
}
void OpBegin(shil_opcode* op, int opid)
+1 -1
View File
@@ -926,7 +926,7 @@ sh4op(i1011_iiii_iiii_iiii)
// trapa #<imm>
sh4op(i1100_0011_iiii_iiii)
{
INFO_LOG(INTERPRETER, "TRAP #%X", GetImm8(op));
WARN_LOG(INTERPRETER, "TRAP #%X", GetImm8(op));
debugger::debugTrap(Sh4Ex_Trap);
CCN_TRA = (GetImm8(op) << 2);
Do_Exception(ctx->pc, Sh4Ex_Trap);
+20 -54
View File
@@ -176,35 +176,9 @@ int main(int argc, char *argv[])
}
#endif
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)
bool 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;
@@ -216,8 +190,6 @@ void 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))
@@ -226,18 +198,20 @@ void 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 && fullMmuNeeded(entry))
else if (CCN_MMUCR.AT == 1 && tlb_entry.Address.VPN != 0x30040 && tlb_entry.Address.VPN != 0x30000 && tlb_entry.Data.V == 1)
{
// 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
@@ -275,31 +249,28 @@ MmuError mmu_full_lookup(u32 va, const TLB_Entry** tlb_entry_ret, u32& rv)
}
#ifdef USE_WINCE_HACK
if (settings.content.windowsCE)
// WinCE hack
TLB_Entry& entry = UTLB[CCN_MMUCR.URC];
if (wince_resolve_address(va, entry))
{
// 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;
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
@@ -362,10 +333,5 @@ 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
+17 -25
View File
@@ -55,18 +55,24 @@ 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
void UTLB_Sync(u32 entry)
bool 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;
return true;
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
@@ -435,27 +441,18 @@ void mmu_set_state()
{
if (CCN_MMUCR.AT == 1)
{
if (!settings.content.windowsCE)
// 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)
{
// 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");
}
mmuOn = true;
NOTICE_LOG(SH4, "Enabling Full MMU support");
}
}
else
{
mmuOn = false;
settings.content.windowsCE = false;
}
SetMemoryHandlers();
@@ -589,16 +586,11 @@ 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;
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);
}
deser.skip(64 * 4, Deserializer::V23); // ITLB_LRU_USE
}
+4 -6
View File
@@ -55,14 +55,13 @@ constexpr u32 mmu_mask[4] =
((0xFFFFFFFF) >> 20) << 20 //1 MB page
};
void UTLB_Sync(u32 entry);
bool 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()
{
@@ -123,13 +122,12 @@ extern u32 mmuAddressLUT[0x100000];
static inline void mmuAddressLUTFlush(bool full)
{
if (full || !settings.content.windowsCE) {
memset(mmuAddressLUT, 0, sizeof(mmuAddressLUT) / 2); // flush user memory (P0)
}
if (full)
memset(mmuAddressLUT, 0, sizeof(mmuAddressLUT) / 2); // flush user memory
else
{
constexpr u32 slotPages = (32 * 1024 * 1024) >> 12;
memset(mmuAddressLUT, 0, slotPages * sizeof(u32)); // flush slot 0 (windows CE)
memset(mmuAddressLUT, 0, slotPages * sizeof(u32)); // flush slot 0
}
}
#endif
+8 -4
View File
@@ -375,10 +375,14 @@ void SCIFSerialPort::init()
void SCIFSerialPort::term()
{
sh4_sched_unregister(schedId);
schedId = -1;
sh4_sched_unregister(brkSchedId);
brkSchedId = -1;
if (schedId != -1) {
sh4_sched_unregister(schedId);
schedId = -1;
}
if (brkSchedId != -1) {
sh4_sched_unregister(brkSchedId);
brkSchedId = -1;
}
}
void SCIFSerialPort::reset()
+1 -1
View File
@@ -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] { -1, -1, -1 };
int tmu_sched[3];
static s64 tmu_ch_base[3];
inline static u64 now() {
+1 -1
View File
@@ -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)
{
+2 -1
View File
@@ -17,6 +17,7 @@
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#include "sh4_cycles.h"
#include "modules/mmu.h"
int Sh4Cycles::countCycles(u16 op)
{
@@ -64,7 +65,7 @@ int Sh4Cycles::countCycles(u16 op)
if (isMemOp[opcode->ex_type])
{
if (++memOps < 4)
cycles = 2;
cycles = mmu_enabled() ? 5 : 2;
}
// TODO only for mem read?
#endif
+2 -3
View File
@@ -94,9 +94,8 @@ static void map_area4(u32 base)
template <class T>
T DYNACALL ReadMem_extdev_T(u32 addr)
{
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);
INFO_LOG(SH4, "Read ext. device (Area 5) undefined @ %08x", addr);
return (T)0;
}
//Write Ext.Device
+13
View File
@@ -713,10 +713,16 @@ 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
@@ -731,6 +737,13 @@ 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
View File
@@ -1,5 +1,6 @@
#pragma once
#include "types.h"
#include "sh4_if.h"
#include "modules/modules.h"
#include "modules/ccn.h"
#include "modules/dmac.h"
+28 -15
View File
@@ -2,7 +2,6 @@
#include "sh4_if.h"
#include "sh4_sched.h"
#include "serialize.h"
#include "hw/aica/aica_if.h"
#include <algorithm>
#include <vector>
@@ -215,6 +214,14 @@ 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
@@ -225,29 +232,35 @@ 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::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::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::V58) {
for (int id : tmu_sched)
sh4_sched_deserialize(deser, id);
}
if (deser.version() < Deserializer::V62)
{
sh4_sched_deserialize(deser, render_end_schid);
sh4_sched_deserialize(deser, vblank_schid);
}
sh4_sched_deserialize(deser, render_end_schid);
sh4_sched_deserialize(deser, vblank_schid);
}
+11 -32
View File
@@ -29,42 +29,28 @@ constexpr Disc* (*drivers[])(const char* path, std::vector<u8> *digest)
static u8 q_subchannel[96];
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)
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 || 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);
}
verify(from == 2352);
memcpy(out_buff, &in_buff[12], 2340);
break;
case 2328:
verify(from == 2352);
@@ -328,10 +314,6 @@ 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);
}
@@ -406,12 +388,9 @@ void libGDR_init()
}
void libGDR_term()
{
if (schedId != -1)
{
gdr::termDrive();
sh4_sched_unregister(schedId);
schedId = -1;
}
gdr::termDrive();
sh4_sched_unregister(schedId);
schedId = -1;
}
void libGDR_serialize(Serializer& ser)
+2 -1
View File
@@ -1,9 +1,10 @@
#pragma once
#include "types.h"
#include <vector>
#include "emulator.h"
#include "hw/gdrom/gdrom_if.h"
#include "oslib/storage.h"
#include <vector>
/*
Mode2 Subheader:
-18
View File
@@ -92,7 +92,6 @@ 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))
@@ -199,15 +198,6 @@ 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;
@@ -255,14 +245,6 @@ 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");
+1 -1
View File
@@ -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], MaplePortType::built_in) >= 1)
&& maple_getPortCount(config::MapleMainDevices[bus]) >= 1)
{
if (potato == nullptr) {
potato = std::make_shared<DreamPotato>(bus);
+1 -1
View File
@@ -17,7 +17,7 @@
along with Flycast. If not, see <https://www.gnu.org/licenses/>.
*/
#pragma once
#include "types.h"
#include "build.h"
#ifdef USE_SDL
void sdl_setTorque(int, float);
+1 -1
View File
@@ -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
{
Loaded 100 of 271 files, more files were not shown because too many files have changed in this diff. Show more