diff --git a/.github/workflows/android.yml b/.github/workflows/android.yml index e09b39298..e10f9cb92 100644 --- a/.github/workflows/android.yml +++ b/.github/workflows/android.yml @@ -4,6 +4,7 @@ on: [push, pull_request] jobs: build: + name: Android runs-on: ubuntu-latest steps: diff --git a/.github/workflows/bsd.yml b/.github/workflows/bsd.yml index 71a6218a1..7beecc583 100644 --- a/.github/workflows/bsd.yml +++ b/.github/workflows/bsd.yml @@ -13,14 +13,14 @@ jobs: architecture: [ arm64, x86-64 ] include: - operating_system: freebsd - version: '14.1' - pkginstall: sudo pkg install -y alsa-lib ccache cmake evdev-proto git libao libevdev libudev-devd libzip lua54 miniupnpc ninja pkgconf pulseaudio sdl2 + version: '14.2' + pkginstall: sudo pkg install -y alsa-lib ccache cmake evdev-proto git libao libevdev libudev-devd libzip lua54 miniupnpc ninja pkgconf pulseaudio sdl2 libcdio - operating_system: netbsd version: '10.0' - pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache cmake gcc12 git libao libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2 && export PATH=/usr/pkg/gcc12/bin:$PATH + pkginstall: sudo pkgin update && sudo pkgin -y install alsa-lib ccache cmake gcc12 git libao libzip lua54 miniupnpc ninja-build pkgconf pulseaudio SDL2 libcdio && export PATH=/usr/pkg/gcc12/bin:$PATH - operating_system: openbsd - version: '7.5' - pkginstall: sudo pkg_add ccache cmake git libao libzip miniupnpc ninja pkgconf pulseaudio sdl2 + version: '7.6' + pkginstall: sudo pkg_add ccache cmake git libao libzip miniupnpc ninja pkgconf pulseaudio sdl2 libcdio exclude: - architecture: arm64 @@ -36,7 +36,7 @@ jobs: key: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}-${{ github.sha }} restore-keys: ccache-${{ matrix.operating_system }}-${{ matrix.architecture }}- - - uses: cross-platform-actions/action@v0.25.0 + - uses: cross-platform-actions/action@v0.26.0 with: operating_system: ${{ matrix.operating_system }} architecture: ${{ matrix.architecture }} @@ -44,5 +44,5 @@ jobs: environment_variables: CCACHE_DIR run: | ${{ matrix.pkginstall }} - cmake -B build -DCMAKE_BUILD_TYPE=Release -G Ninja + cmake -B build -DUSE_LIBCDIO=ON -DCMAKE_BUILD_TYPE=Release -G Ninja cmake --build build --config Release diff --git a/.github/workflows/c-cpp.yml b/.github/workflows/c-cpp.yml index 3954dee58..79ec624ff 100644 --- a/.github/workflows/c-cpp.yml +++ b/.github/workflows/c-cpp.yml @@ -20,11 +20,11 @@ jobs: - {name: i686-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x86, cmakeArgs: -G Ninja, buildType: Release} - {name: apple-darwin, os: macos-latest, shell: sh, cmakeArgs: -G Xcode -DUSE_DISCORD=ON, destDir: osx, buildType: RelWithDebInfo} - {name: apple-ios, os: macos-latest, shell: sh, cmakeArgs: -DCMAKE_SYSTEM_NAME=iOS -G Xcode, destDir: ios, buildType: Release} - - {name: x86_64-pc-linux-gnu, os: ubuntu-20.04, shell: sh, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, destDir: linux, buildType: RelWithDebInfo} + - {name: x86_64-pc-linux-gnu, os: ubuntu-20.04, shell: sh, cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: linux, buildType: RelWithDebInfo} - {name: x86_64-pc-windows-msvc, os: windows-latest, shell: cmd, arch: x64, cmakeArgs: -G Ninja -DUSE_DISCORD=ON, buildType: Release} - - {name: x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -G Ninja -DUSE_DISCORD=ON, destDir: win, buildType: RelWithDebInfo} - - {name: libretro-x86_64-pc-linux-gnu, os: ubuntu-latest, shell: sh, cmakeArgs: -DLIBRETRO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -G Ninja, buildType: Release} - - {name: libretro-x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -DLIBRETRO=ON -G Ninja, buildType: Release} + - {name: x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -G Ninja -DUSE_DISCORD=ON -DUSE_LIBCDIO=ON, destDir: win, buildType: RelWithDebInfo} + - {name: libretro-x86_64-pc-linux-gnu, os: ubuntu-latest, shell: sh, cmakeArgs: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -G Ninja, buildType: Release} + - {name: libretro-x86_64-w64-mingw32, os: windows-latest, shell: 'msys2 {0}', cmakeArgs: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -G Ninja, buildType: Release} steps: - name: Set up build environment (macOS) @@ -45,7 +45,7 @@ jobs: run: | sudo add-apt-repository ppa:christianrauch/libdecoration sudo apt-get update - sudo apt-get -y install ccache libao-dev libasound2-dev libevdev-dev libgl1-mesa-dev liblua5.3-dev libminiupnpc-dev libpulse-dev libsdl2-dev libudev-dev libzip-dev ninja-build libcurl4-openssl-dev + sudo apt-get -y install ccache libao-dev libasound2-dev libevdev-dev libgl1-mesa-dev liblua5.3-dev libminiupnpc-dev libpulse-dev libsdl2-dev libudev-dev libzip-dev ninja-build libcurl4-openssl-dev libcdio-dev sudo apt-get -y install libwayland-dev libdecor-0-dev libaudio-dev libjack-dev libsndio-dev libsamplerate0-dev libx11-dev libxext-dev libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxkbcommon-dev libdrm-dev libgbm-dev libgles2-mesa-dev libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev fcitx-libs-dev if: runner.os == 'Linux' @@ -53,7 +53,7 @@ jobs: uses: msys2/setup-msys2@v2 with: msystem: MINGW64 - install: git make mingw-w64-x86_64-ccache mingw-w64-x86_64-cmake mingw-w64-x86_64-lua mingw-w64-x86_64-ninja mingw-w64-x86_64-SDL2 mingw-w64-x86_64-toolchain + install: git make mingw-w64-x86_64-ccache mingw-w64-x86_64-cmake mingw-w64-x86_64-lua mingw-w64-x86_64-ninja mingw-w64-x86_64-SDL2 mingw-w64-x86_64-toolchain mingw-w64-x86_64-libcdio if: matrix.config.shell == 'msys2 {0}' - name: Set up build environment (Windows, Visual Studio) diff --git a/.github/workflows/switch.yml b/.github/workflows/switch.yml index 4d6398e44..07f873a57 100644 --- a/.github/workflows/switch.yml +++ b/.github/workflows/switch.yml @@ -4,7 +4,7 @@ on: [push, pull_request] jobs: build: - name: ${{ matrix.config.name }} + name: Switch ${{ matrix.config.name }} runs-on: ubuntu-latest container: devkitpro/devkita64:latest diff --git a/.github/workflows/uwp.yml b/.github/workflows/uwp.yml index 8c7450a1b..485827325 100644 --- a/.github/workflows/uwp.yml +++ b/.github/workflows/uwp.yml @@ -4,6 +4,7 @@ on: [push, pull_request] jobs: build: + name: UWP runs-on: windows-latest steps: diff --git a/.gitlab-ci.yml b/.gitlab-ci.yml index d2f50b97e..b3cc580a8 100644 --- a/.gitlab-ci.yml +++ b/.gitlab-ci.yml @@ -9,12 +9,12 @@ variables: GIT_SUBMODULE_STRATEGY: recursive CORENAME: flycast - CORE_ARGS: -DLIBRETRO=ON -DCMAKE_BUILD_TYPE=Release + CORE_ARGS: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_BUILD_TYPE=Release .core-defs-linux: extends: .core-defs variables: - CORE_ARGS: -DLIBRETRO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -DCMAKE_BUILD_TYPE=Release + CORE_ARGS: -DLIBRETRO=ON -DUSE_LIBCDIO=ON -DCMAKE_POSITION_INDEPENDENT_CODE=TRUE -DCMAKE_BUILD_TYPE=Release .core-defs-osx-x64: extends: .core-defs @@ -36,7 +36,7 @@ .core-defs-android: extends: .core-defs script: - - cmake $CORE_ARGS -DANDROID_PLATFORM=android-$API_LEVEL -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DANDROID_ABI=$ANDROID_ABI -DANDROID_ARM_MODE=arm "$CMAKE_SOURCE_ROOT" -B$BUILD_DIR + - cmake -DLIBRETRO=ON -DCMAKE_BUILD_TYPE=Release -DANDROID_PLATFORM=android-$API_LEVEL -DCMAKE_TOOLCHAIN_FILE=$NDK_ROOT/build/cmake/android.toolchain.cmake -DANDROID_STL=c++_static -DANDROID_ABI=$ANDROID_ABI -DANDROID_ARM_MODE=arm "$CMAKE_SOURCE_ROOT" -B$BUILD_DIR - cmake --build $BUILD_DIR --target ${CORENAME}_libretro --config Release -- -j $NUMPROC - mv $BUILD_DIR/${CORENAME}_libretro.so $LIBNAME - if [ $STRIP_CORE_LIB -eq 1 ]; then $NDK_ROOT/toolchains/llvm/prebuilt/linux-x86_64/bin/llvm-strip $LIBNAME; fi diff --git a/.gitmodules b/.gitmodules index 14fe5f2ac..fbec754c6 100644 --- a/.gitmodules +++ b/.gitmodules @@ -41,3 +41,6 @@ [submodule "core/deps/googletest"] path = core/deps/googletest url = https://github.com/google/googletest.git +[submodule "core/deps/asio"] + path = core/deps/asio + url = https://github.com/chriskohlhoff/asio.git diff --git a/CMakeLists.txt b/CMakeLists.txt index df7f51d38..8dd949e61 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -76,6 +76,7 @@ option(ENABLE_GDB_SERVER "Build with GDB debugging support" OFF) option(ENABLE_DC_PROFILER "Build with support for target machine (SH4) profiler" OFF) 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) if(IOS AND NOT LIBRETRO) set(USE_VULKAN OFF CACHE BOOL "Force vulkan off" FORCE) @@ -264,7 +265,7 @@ if(NOT "${SENTRY_UPLOAD_URL}" STREQUAL "") target_compile_definitions(${PROJECT_NAME} PRIVATE SENTRY_UPLOAD="${SENTRY_UPLOAD_URL}") endif() -target_include_directories(${PROJECT_NAME} PRIVATE core core/deps core/deps/stb core/deps/json) +target_include_directories(${PROJECT_NAME} PRIVATE core core/deps core/deps/stb core/deps/json core/deps/asio/asio/include) if(LIBRETRO) target_include_directories(${PROJECT_NAME} PRIVATE shell/libretro) endif() @@ -460,7 +461,14 @@ if(NOT LIBRETRO) endif() target_compile_definitions(${PROJECT_NAME} PRIVATE USE_SDL USE_SDL_AUDIO) - target_sources(${PROJECT_NAME} PRIVATE core/sdl/sdl.cpp core/sdl/sdl.h core/sdl/sdl_gamepad.h core/sdl/sdl_keyboard.h) + target_sources(${PROJECT_NAME} PRIVATE + core/sdl/sdl.cpp + core/sdl/sdl.h + core/sdl/sdl_gamepad.h + core/sdl/sdl_keyboard.h + core/sdl/sdl_keyboard_mac.h + core/sdl/dreamconn.cpp + core/sdl/dreamconn.h) if((UNIX AND NOT APPLE) OR NINTENDO_SWITCH) find_package(CURL REQUIRED) @@ -672,6 +680,28 @@ if(NOT LIBZIP_FOUND OR NINTENDO_SWITCH) target_link_libraries(${PROJECT_NAME} PRIVATE libzip::zip) endif() +if(USE_LIBCDIO) + if(PKG_CONFIG_FOUND) + pkg_check_modules(CDIO IMPORTED_TARGET libcdio) + if(CDIO_FOUND) + target_compile_definitions(${PROJECT_NAME} PRIVATE USE_LIBCDIO) + if(MINGW) + # Force static link + target_link_libraries(${PROJECT_NAME} PRIVATE "-l:libcdio.a -l:libiconv.a -lwinmm") + else() + target_link_libraries(${PROJECT_NAME} PRIVATE PkgConfig::CDIO) + endif() + endif() + endif() + if(NOT CDIO_FOUND) + find_package(libcdio) + if(TARGET libcdio::libcdio) + target_compile_definitions(${PROJECT_NAME} PRIVATE USE_LIBCDIO) + target_link_libraries(${PROJECT_NAME} PRIVATE libcdio::libcdio) + endif() + endif() +endif() + if(WIN32) target_include_directories(${PROJECT_NAME} PRIVATE core/deps/dirent) endif() @@ -1052,22 +1082,23 @@ if(NOT LIBRETRO) fonts/Roboto-Medium.ttf.zip fonts/Roboto-Regular.ttf.zip fonts/fa-solid-900.ttf.zip) - if(ANDROID) + if(ANDROID OR IOS) cmrc_add_resources(flycast-resources WHENCE resources - resources/picture/buttons.png) + resources/picture/buttons.png + resources/picture/buttons-arcade.png) endif() endif() target_sources(${PROJECT_NAME} PRIVATE core/imgread/cdi.cpp + core/imgread/cdio.cpp core/imgread/chd.cpp core/imgread/common.cpp core/imgread/common.h core/imgread/cue.cpp core/imgread/gdi.cpp core/imgread/ImgReader.cpp - core/imgread/ioctl.cpp core/imgread/iso9660.h core/imgread/isofs.cpp core/imgread/isofs.h) @@ -1324,6 +1355,8 @@ target_sources(${PROJECT_NAME} PRIVATE core/rend/tileclip.h core/rend/TexCache.cpp core/rend/TexCache.h + core/rend/texconv.cpp + core/rend/texconv.h core/rend/norend/norend.cpp) if(NOT LIBRETRO) target_sources(${PROJECT_NAME} PRIVATE @@ -1333,8 +1366,8 @@ if(NOT LIBRETRO) core/ui/gui.cpp core/ui/gui.h core/ui/gui_achievements.cpp - core/ui/gui_android.cpp - core/ui/gui_android.h + core/ui/vgamepad.cpp + core/ui/vgamepad.h core/ui/gui_chat.h core/ui/gui_cheats.cpp core/ui/gui_util.cpp @@ -1640,6 +1673,10 @@ if(NOT LIBRETRO) target_sources(${PROJECT_NAME} PRIVATE shell/android-studio/flycast/src/main/jni/src/Android.cpp shell/android-studio/flycast/src/main/jni/src/android_gamepad.h + shell/android-studio/flycast/src/main/jni/src/android_storage.h + shell/android-studio/flycast/src/main/jni/src/http_client.h + shell/android-studio/flycast/src/main/jni/src/jni_util.h + shell/android-studio/flycast/src/main/jni/src/android_input.cpp shell/android-studio/flycast/src/main/jni/src/android_keyboard.h) target_link_libraries(${PROJECT_NAME} PRIVATE android log) @@ -1664,6 +1701,8 @@ if(NOT LIBRETRO) shell/apple/emulator-ios/emulator/FlycastViewController.mm shell/apple/emulator-ios/emulator/PadViewController.h shell/apple/emulator-ios/emulator/PadViewController.mm + shell/apple/emulator-ios/emulator/EditPadViewController.h + shell/apple/emulator-ios/emulator/EditPadViewController.mm shell/apple/emulator-ios/emulator/EmulatorView.h shell/apple/emulator-ios/emulator/EmulatorView.mm shell/apple/emulator-ios/emulator/main.m @@ -1678,7 +1717,8 @@ if(NOT LIBRETRO) shell/apple/emulator-ios/emulator/Images.xcassets shell/apple/emulator-ios/emulator/FlycastStoryboard.storyboard shell/apple/emulator-ios/emulator/LaunchScreen.storyboard - shell/apple/emulator-ios/emulator/PadViewController.xib) + shell/apple/emulator-ios/emulator/PadViewController.xib + shell/apple/emulator-ios/emulator/EditPadViewController.xib) target_sources(${PROJECT_NAME} PRIVATE ${IOS_RESOURCES}) source_group("Resources" FILES ${IOS_RESOURCES}) set_source_files_properties(${IOS_RESOURCES} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") diff --git a/core/achievements/achievements.cpp b/core/achievements/achievements.cpp index abdd54979..b2ff0ed63 100644 --- a/core/achievements/achievements.cpp +++ b/core/achievements/achievements.cpp @@ -779,8 +779,7 @@ std::string Achievements::getGameHash() { if (settings.platform.isConsole()) { - const u32 diskType = libGDR_GetDiscType(); - if (diskType == NoDisk || diskType == Open) + if (!gdr::isLoaded()) return {}; // Reopen the disk locally to avoid threading issues (CHD) try { @@ -964,7 +963,8 @@ void Achievements::unloadGame() void Achievements::diskChange() { - if (!active) + if (!active || settings.content.path.empty()) + // Don't unload the game when the lid is open while swapping disks return; std::string hash = getGameHash(); if (hash == "") { diff --git a/core/audio/audiobackend_null.cpp b/core/audio/audiobackend_null.cpp index 67c935a7b..95a6b5647 100644 --- a/core/audio/audiobackend_null.cpp +++ b/core/audio/audiobackend_null.cpp @@ -19,17 +19,20 @@ class NullAudioBackend : public AudioBackend u32 push(const void* frame, u32 samples, bool wait) override { - if (wait) + if (wait && last_time.time_since_epoch() != the_clock::duration::zero()) { - if (last_time.time_since_epoch() != the_clock::duration::zero()) - { - auto fduration = std::chrono::nanoseconds(1000000000L * samples / 44100); - auto duration = fduration - (the_clock::now() - last_time); + auto fduration = std::chrono::nanoseconds(1'000'000'000LL * samples / 44100); + auto duration = fduration - (the_clock::now() - last_time); + if (duration > std::chrono::nanoseconds::zero()) std::this_thread::sleep_for(duration); - last_time += fduration; - } - else + if (duration < -std::chrono::milliseconds(67)) + // if ~4 frames ahead, reset time (fast forward detection) last_time = the_clock::now(); + else + last_time += fduration; + } + else { + last_time = the_clock::now(); } return 1; } diff --git a/core/audio/audiostream.cpp b/core/audio/audiostream.cpp index 4a6694edf..15f9a3965 100644 --- a/core/audio/audiostream.cpp +++ b/core/audio/audiostream.cpp @@ -1,5 +1,8 @@ #include "audiostream.h" #include "cfg/option.h" +#include "emulator.h" + +static void registerForEvents(); struct SoundFrame { s16 l; s16 r; }; @@ -60,6 +63,7 @@ void WriteSample(s16 r, s16 l) void InitAudio() { + registerForEvents(); TermAudio(); std::string slug = config::AudioBackend; @@ -138,3 +142,19 @@ void StopAudioRecording() currentBackend->termRecord(); audio_recording_started = false; } + +static void registerForEvents() +{ + static bool done; + if (done) + return; + done = true; + // Empty the audio buffer when loading a state or terminating the game + const auto& callback = [](Event, void *) { + writePtr = 0; + }; + EventManager::listen(Event::Terminate, callback); + EventManager::listen(Event::LoadState, callback); +} + + diff --git a/core/build.h b/core/build.h index eddcecf13..183669cba 100755 --- a/core/build.h +++ b/core/build.h @@ -42,6 +42,11 @@ #define HOST_CPU CPU_GENERIC #endif +#if defined(TARGET_IPHONE) && !defined(__aarch64__) +// iOS simulator +#define TARGET_NO_REC +#endif + #if defined(TARGET_NO_REC) #define FEAT_SHREC DYNAREC_NONE #define FEAT_AREC DYNAREC_NONE @@ -100,7 +105,7 @@ #endif #endif -#if !defined(LIBRETRO) && !defined(TARGET_NO_EXCEPTIONS) +#if !defined(LIBRETRO) #define USE_GGPO #endif diff --git a/core/cfg/cfg.cpp b/core/cfg/cfg.cpp index 4a464b845..021a51418 100644 --- a/core/cfg/cfg.cpp +++ b/core/cfg/cfg.cpp @@ -130,3 +130,12 @@ void cfgSetAutoSave(bool autoSave) if (autoSave) saveConfigFile(); } + +void cfgSaveFloat(const std::string& section, const std::string& key, float value) +{ + cfgdb.set_float(section, key, value); +} +float cfgLoadFloat(const std::string& section, const std::string& key, float def) +{ + return cfgdb.get_float(section, key, def); +} diff --git a/core/cfg/cfg.h b/core/cfg/cfg.h index f13279e54..d90a307f7 100644 --- a/core/cfg/cfg.h +++ b/core/cfg/cfg.h @@ -11,6 +11,8 @@ std::string cfgLoadStr(const std::string& section, const std::string& key, const void cfgSaveStr(const std::string& section, const std::string& key, const std::string& value); void cfgSaveBool(const std::string& section, const std::string& key, bool value); bool cfgLoadBool(const std::string& section, const std::string& key, bool def); +void cfgSaveFloat(const std::string& section, const std::string& key, float value); +float cfgLoadFloat(const std::string& section, const std::string& key, float def); void cfgSetVirtual(const std::string& section, const std::string& key, const std::string& value); bool cfgIsVirtual(const std::string& section, const std::string& key); diff --git a/core/cfg/ini.cpp b/core/cfg/ini.cpp index c5879572c..a5ea7aad0 100644 --- a/core/cfg/ini.cpp +++ b/core/cfg/ini.cpp @@ -47,6 +47,11 @@ bool ConfigEntry::get_bool() } } +float ConfigEntry::get_float() +{ + return atof(this->value.c_str()); +} + /* ConfigSection */ bool ConfigSection::has_entry(const std::string& name) @@ -192,6 +197,15 @@ bool ConfigFile::get_bool(const std::string& section_name, const std::string& en } } +float ConfigFile::get_float(const std::string& section_name, const std::string& entry_name, float default_value) +{ + ConfigEntry* entry = get_entry(section_name, entry_name); + if (entry == nullptr) + return default_value; + else + return entry->get_float(); +} + void ConfigFile::set(const std::string& section_name, const std::string& entry_name, const std::string& value, bool is_virtual) { ConfigSection* section = this->get_section(section_name, is_virtual); @@ -222,6 +236,13 @@ void ConfigFile::set_bool(const std::string& section_name, const std::string& en this->set(section_name, entry_name, str_value, is_virtual); } +void ConfigFile::set_float(const std::string& section_name, const std::string& entry_name, float value, bool is_virtual) +{ + std::stringstream str_value; + str_value << value; + this->set(section_name, entry_name, str_value.str(), is_virtual); +} + void ConfigFile::parse(FILE* file) { if (file == nullptr) diff --git a/core/cfg/ini.h b/core/cfg/ini.h index ab3edd3f8..e4bd8a532 100644 --- a/core/cfg/ini.h +++ b/core/cfg/ini.h @@ -13,6 +13,7 @@ struct ConfigEntry { int get_int(); bool get_bool(); int64_t get_int64(); + float get_float(); }; struct ConfigSection { @@ -45,11 +46,13 @@ struct ConfigFile { int get_int(const std::string& section_name, const std::string& entry_name, int default_value = 0); int64_t get_int64(const std::string& section_name, const std::string& entry_name, int64_t default_value = 0); bool get_bool(const std::string& section_name, const std::string& entry_name, bool default_value = false); + float get_float(const std::string& section_name, const std::string& entry_name, float default_value = 0.f); /* setting values */ void set(const std::string& section_name, const std::string& entry_name, const std::string& value, bool is_virtual = false); void set_int(const std::string& section_name, const std::string& entry_name, int value, bool is_virtual = false); void set_int64(const std::string& section_name, const std::string& entry_name, int64_t value, bool is_virtual = false); void set_bool(const std::string& section_name, const std::string& entry_name, bool value, bool is_virtual = false); + void set_float(const std::string& section_name, const std::string& entry_name, float value, bool is_virtual = false); void delete_section(const std::string& section_name); void delete_entry(const std::string& section_name, const std::string& entry_name); diff --git a/core/cfg/option.h b/core/cfg/option.h index 1e520ecd0..c3ba52786 100644 --- a/core/cfg/option.h +++ b/core/cfg/option.h @@ -202,11 +202,7 @@ class Option : public BaseOption { std::enable_if_t, T> doLoad(const std::string& section, const std::string& name) const { - std::string strValue = cfgLoadStr(section, name, ""); - if (strValue.empty()) - return value; - else - return (float)atof(strValue.c_str()); + return cfgLoadFloat(section, name, value); } template @@ -301,9 +297,7 @@ class Option : public BaseOption { std::enable_if_t> doSave(const std::string& section, const std::string& name) const { - char buf[64]; - snprintf(buf, sizeof(buf), "%f", value); - cfgSaveStr(section, name, buf); + cfgSaveFloat(section, name, value); } template diff --git a/core/cheats.cpp b/core/cheats.cpp index bf43ccbd4..999be2002 100644 --- a/core/cheats.cpp +++ b/core/cheats.cpp @@ -481,6 +481,15 @@ void CheatManager::reset(const std::string& gameId) else if (gameId == "SAMURAI SPIRITS 6" || gameId == "T0002M") { cheats.emplace_back(Cheat::Type::setValue, "fix depth", true, 16, 0x0003e602, 0x0009, true); // nop (shift by 8 bits instead of 10) } + else if (gameId == "T-8107N") { // Fur Fighters (US) + // force logging on to use more cycles + cheats.emplace_back(Cheat::Type::setValue, "enable logging", true, 32, 0x00314248, 1, true); + } + else if (gameId == "T-8113D-50") { // Fur Fighters (EU) + // force logging on to use more cycles + cheats.emplace_back(Cheat::Type::setValue, "enable logging", true, 32, 0x00314228, 1, true); + } + if (cheats.size() > cheatCount) setActive(true); } diff --git a/core/debug/debug_agent.h b/core/debug/debug_agent.h index 9783edc56..d2e03310c 100644 --- a/core/debug/debug_agent.h +++ b/core/debug/debug_agent.h @@ -21,7 +21,7 @@ #include "emulator.h" #include "hw/sh4/sh4_if.h" #include "hw/sh4/sh4_mem.h" -#include "hw/sh4/sh4_interpreter.h" +#include "hw/sh4/dyna/shil.h" #include "cfg/option.h" #include #include @@ -122,9 +122,9 @@ class DebugAgent for (u32 i = 0; i < Sh4RegList.size(); i++) { if (Sh4RegList[i] == reg_sr_status) - allregs[i] = sh4_sr_GetFull(); + allregs[i] = Sh4cntx.sr.getFull(); else if (Sh4RegList[i] != NoReg) - allregs[i] = *GetRegPtr(Sh4RegList[i]); + allregs[i] = *GetRegPtr(Sh4cntx, Sh4RegList[i]); } *regs = &allregs[0]; return allregs.size(); @@ -134,7 +134,7 @@ class DebugAgent { for (u32 i = 0; i < Sh4RegList.size(); i++) if (Sh4RegList[i] != NoReg) - *GetRegPtr(Sh4RegList[i]) = regs[i]; + *GetRegPtr(Sh4cntx, Sh4RegList[i]) = regs[i]; } u32 readReg(u32 regNum) @@ -143,9 +143,9 @@ class DebugAgent return 0; Sh4RegType reg = Sh4RegList[regNum]; if (reg == reg_sr_status) - return sh4_sr_GetFull(); + return Sh4cntx.sr.getFull(); if (reg != NoReg) - return *GetRegPtr(reg); + return *GetRegPtr(Sh4cntx, reg); return 0; } void writeReg(u32 regNum, u32 value) @@ -154,9 +154,9 @@ class DebugAgent return; Sh4RegType reg = Sh4RegList[regNum]; if (reg == reg_sr_status) - sh4_sr_SetFull(value); + Sh4cntx.sr.setFull(value); else if (reg != NoReg) - *GetRegPtr(reg) = value; + *GetRegPtr(Sh4cntx, reg) = value; } const u8 *readMem(u32 addr, u32 len) diff --git a/core/debug/gdb_server.cpp b/core/debug/gdb_server.cpp index 4141a7d20..35bd947b0 100644 --- a/core/debug/gdb_server.cpp +++ b/core/debug/gdb_server.cpp @@ -21,85 +21,168 @@ #ifdef GDB_SERVER #include "gdb_server.h" #include "debug_agent.h" -#include "network/net_platform.h" #include "cfg/option.h" #include "oslib/oslib.h" +#include "util/shared_this.h" +#include #include #include #include -#include #include - -#define MAX_PACKET_LEN 4096 +#include namespace debugger { -static void emuEventCallback(Event event, void *); +constexpr u32 MAX_PACKET_LEN = 4096; -class GdbServer +static u8 unpack(char c) +{ + c = std::tolower(c); + if (c <= '9') + return c - '0'; + else + return c - 'a' + 10; +} + +u32 unpack(const char *s, int l) +{ + u32 r = 0; + for (int i = 0; i < l && *s != '\0'; i += 2, s += 2) { + r |= (unpack(s[0]) << 4 | unpack(s[1])) << (i * 4); + } + return r; +} + +class GdbServer; + +class Connection : public SharedThis { public: - struct Error : public std::runtime_error { - Error(const char *reason) : std::runtime_error(reason) {} - }; + asio::ip::tcp::socket& getSocket() { + return socket; + } - void init(int port) + void start() { + asio::async_read_until(socket, asio::dynamic_string_buffer(message, MAX_PACKET_LEN), packetMatcher, + std::bind(&Connection::handlePacket, shared_from_this(), + asio::placeholders::error, + asio::placeholders::bytes_transferred)); + } + +private: + Connection(GdbServer& server, asio::io_context& io_context) + : server(server), io_context(io_context), socket(io_context) { + } + + using iterator = asio::buffers_iterator; + + std::pair + static packetMatcher(iterator begin, iterator end) { - if (VALID(serverSocket)) - return; + if (begin == end) + return std::make_pair(begin, false); + iterator i = begin; + if (*i == '\03') + // break + return std::make_pair(i + 1, true); + if (*i != '$') { + // unexpected, or ack/nack ('+', '-') + return std::make_pair(i + 1, true); + } + ++i; + while (i != end && *i != '#') + ++i; + if (i + 3 <= end) + // 2 chars for CRC + return std::make_pair(i + 3, true); - serverSocket = socket(AF_INET, SOCK_STREAM, IPPROTO_TCP); - if (!VALID(serverSocket)) - throw Error("gdb: Cannot create server socket"); + return std::make_pair(begin, false); + } - int option = 1; - setsockopt(serverSocket, SOL_SOCKET, SO_REUSEADDR, (const char *)&option, sizeof(option)); + void handlePacket(const std::error_code& ec, size_t len); - struct sockaddr_in serveraddr; - memset(&serveraddr, 0, sizeof(serveraddr)); - serveraddr.sin_family = AF_INET; - serveraddr.sin_port = htons(port); + void send(const std::string& msg) + { + if (msg.empty()) + start(); + else + asio::async_write(socket, asio::buffer(msg), + std::bind(&Connection::writeDone, shared_from_this(), + asio::placeholders::error, + asio::placeholders::bytes_transferred)); - if (::bind(serverSocket, (struct sockaddr *)&serveraddr, sizeof(serveraddr)) < 0) - { - closesocket(serverSocket); - throw Error("gdb: bind() failed"); - } - if (::listen(serverSocket, 5) < 0) - { - closesocket(serverSocket); - throw Error("gdb: listen() failed"); - } - EventManager::listen(Event::Resume, emuEventCallback); - EventManager::listen(Event::Terminate, emuEventCallback); + } + + void writeDone(const std::error_code& ec, size_t len) + { + if (ec) + WARN_LOG(COMMON, "Write error: %s", ec.message().c_str()); + else + start(); + } + + GdbServer& server; + asio::io_context& io_context; + asio::ip::tcp::socket socket; + std::string message; + friend super; +}; + +class TcpAcceptor +{ +public: + TcpAcceptor(GdbServer& server, asio::io_context& io_context, u16 port) + : server(server), io_context(io_context), + acceptor(asio::ip::tcp::acceptor(io_context, + asio::ip::tcp::endpoint(asio::ip::tcp::v4(), port))) + { + asio::socket_base::reuse_address option(true); + acceptor.set_option(option); + start(); + } + +private: + void start() + { + Connection::Ptr newConnection = Connection::create(server, io_context); - initialised = true; + acceptor.async_accept(newConnection->getSocket(), + std::bind(&TcpAcceptor::handleAccept, this, newConnection, asio::placeholders::error)); + } + void handleAccept(Connection::Ptr newConnection, const std::error_code& error); + + GdbServer& server; + asio::io_context& io_context; + asio::ip::tcp::acceptor acceptor; +}; + +class GdbServer +{ +public: + struct Error : public std::runtime_error { + Error(const char *reason) : std::runtime_error(reason) {} + }; + + void init(int port) + { + this->port = port; + EventManager::listen(Event::Resume, emuEventCallback, this); + EventManager::listen(Event::Terminate, emuEventCallback, this); } void term() { - if (!initialised) - return; - EventManager::unlisten(Event::Resume, emuEventCallback); - EventManager::unlisten(Event::Terminate, emuEventCallback); + EventManager::unlisten(Event::Resume, emuEventCallback, this); + EventManager::unlisten(Event::Terminate, emuEventCallback, this); stop(); - if (VALID(clientSocket)) - { - closesocket(clientSocket); - clientSocket = INVALID_SOCKET; - } - if (VALID(serverSocket)) - { - closesocket(serverSocket); - serverSocket = INVALID_SOCKET; - } } void run() { - if (!initialised || thread.joinable()) + if (thread.joinable()) return; DEBUG_LOG(COMMON, "GdbServer starting"); + io_context = std::make_unique(); thread = std::thread(&GdbServer::serverThread, this); if (config::GDBWaitForConnection) { @@ -110,19 +193,18 @@ class GdbServer void stop() { - if (!initialised) - return; if (thread.joinable()) { DEBUG_LOG(COMMON, "GdbServer stopping"); agent.resetAgent(); - stopRequested = true; + io_context->stop(); thread.join(); + io_context.reset(); } } bool isRunning() const { - return initialised && thread.joinable(); + return thread.joinable(); } // called on the emu thread @@ -142,69 +224,19 @@ class GdbServer void serverThread() { ThreadName _("GdbServer"); - while (!stopRequested) + try { - fd_set fds; - FD_ZERO(&fds); - sock_t max_fd = serverSocket; - FD_SET(serverSocket, &fds); - if (VALID(clientSocket)) - { - max_fd = std::max(max_fd, clientSocket); - FD_SET(clientSocket, &fds); - } - timeval tv; - tv.tv_sec = 0; - tv.tv_usec = 100 * 1000; - if (::select(max_fd + 1, &fds, nullptr, nullptr, &tv) > 0) - { - if (FD_ISSET(serverSocket, &fds)) - { - try { - acceptClientConnection(); - } catch (const Error& e) { - ERROR_LOG(COMMON, "%s", e.what()); - closesocket(serverSocket); - serverSocket = INVALID_SOCKET; - break; - } - } - else if (FD_ISSET(clientSocket, &fds)) - { - readCommand(); - } - } + TcpAcceptor server(*this, *io_context, port); + io_context->run(); } - if (VALID(clientSocket)) + catch (const std::exception& e) { - closesocket(clientSocket); - clientSocket = INVALID_SOCKET; + ERROR_LOG(COMMON, "Gdb server exception: %s", e.what()); } attached = false; - stopRequested = false; - } - - void acceptClientConnection() - { - if (VALID(clientSocket)) - closesocket(clientSocket); - sockaddr_in src_addr{}; - socklen_t addr_len = sizeof(src_addr); - clientSocket = ::accept(serverSocket, (sockaddr *)&src_addr, &addr_len); - if (!VALID(clientSocket)) - { - if (get_last_error() != L_EAGAIN && get_last_error() != L_EWOULDBLOCK) - throw Error("accept failed"); - } - else - { - NOTICE_LOG(NETWORK, "gdb: client connection"); - attached = true; - agentInterrupt(); - } } - void readCommand() + std::string handleCommand(const std::string& packet) { try { if (postDebugTrapNeeded) @@ -216,21 +248,21 @@ class GdbServer throw Error(e.what()); } } - std::string packet = recvPacket(); if (packet.empty()) - return; + return ""; DEBUG_LOG(NETWORK, "gdb: recv %s", packet.c_str()); + std::vector replies; switch (packet[0]) { case '!': // Enable extended mode - sendPacket("OK"); + replies.push_back("OK"); break; case '?': // Sent when connection is first established to query the reason the target halted - reportException(); + replies.push_back(reportException()); break; case 'A': // Initialized argv[] array passed into program. not supported - sendPacket("E01"); + replies.push_back("E01"); break;; case 'b': // Change the serial line speed to baud. deprecated break; @@ -238,53 +270,59 @@ class GdbServer break; case 'c': // Continue at addr, which is the address to resume. // If addr is omitted, resume at current address - sendContinue(packet); + doContinue(packet); break; case 'C': // Continue with signal sig - sendContinue(packet); + doContinue(packet); break; case 'd': // Toggle debug flag. deprecated break; case 'D': // Detach GDB from the remote system - sendPacket("OK"); + replies.push_back("OK"); agent.detach(); break; case 'F': // File-I/O protocol extension not currently supported break;; case 'g': // Read general registers - readAllRegs(); + replies.push_back(readAllRegs()); break; case 'G': // Write general registers - writeAllRegs(packet); + replies.push_back(writeAllRegs(packet)); break; case 'H': // Set thread for subsequent operations - sendPacket("OK"); + replies.push_back("OK"); break; case 'i': // Step the remote target by a single clock cycle case 'I': // Signal, then cycle step // not supported - sendPacket(""); + replies.push_back(""); break; case 'k': // Kill request. Stop process/system agent.kill(); break; case 'm': // Read length addressable memory units - readMem(packet); + replies.push_back(readMem(packet)); break; case 'M': // Write length addressable memory units - writeMem(packet); + replies.push_back(writeMem(packet)); break; case 'p': // Read the value of register - readReg(packet); + replies.push_back(readReg(packet)); break; case 'P': // Write register - writeReg(packet); + replies.push_back(writeReg(packet)); break; case 'q': // General query packets - query(packet); + { + auto v = query(packet); + replies.insert(replies.end(), v.begin(), v.end()); + } break; case 'Q': // General set packets - set(packet); + { + auto v = set(packet); + replies.insert(replies.end(), v.begin(), v.end()); + } break; case 'r': // Reset the entire system. Deprecated (use 'R' instead) break; @@ -292,52 +330,78 @@ class GdbServer restart(); break; case 's': // Single step - step(EXCEPT_NONE); + replies.push_back(step(EXCEPT_NONE)); break; case 'S': // Step with signal - step(); + replies.push_back(step()); break; case 't': // Search backwards. unsupported break; case 'T': // Find out if the thread is alive - sendPacket("OK"); + replies.push_back("OK"); break; case 'v': // 'v' packets to control execution - vpacket(packet); + { + auto v = vpacket(packet); + replies.insert(replies.end(), v.begin(), v.end()); + } break; case 'X': // Write binary data to memory - writeMemBin(packet); + replies.push_back(writeMemBin(packet)); break; case 'z': // Remove a breakpoint/watchpoint. - removeMatchpoint(packet); + replies.push_back(removeMatchpoint(packet)); break; case 'Z': // Insert a breakpoint/watchpoint. - insertMatchpoint(packet); + replies.push_back(insertMatchpoint(packet)); break; case 3: - interrupt(); + replies.push_back(interrupt()); break; default: // Unknown commands are ignored WARN_LOG(COMMON, "Unknown gdb command: %s", packet.c_str()); break;; } + std::string data; + for (const std::string& pkt : replies) + { + data.push_back('$'); + u8 checksum = 0; + for (char c : pkt) + { + if (c == '$' || c == '#' || c == '*' || c == '}') + { + c ^= 0x20; + checksum += (u8)'}'; + data.push_back('}'); + } + checksum += (u8)c; + data.push_back(c); + } + data.push_back('#'); + char s[9]; + sprintf(s, "%02x", checksum); + data += s; + } + DEBUG_LOG(NETWORK, "gdb: sent %s", data.c_str()); + + return data; } catch (const Error& e) { ERROR_LOG(COMMON, "%s", e.what()); - closesocket(clientSocket); - clientSocket = INVALID_SOCKET; attached = false; + throw e; } } - void reportException() + std::string reportException() { char s[4]; sprintf(s, "S%02X", agent.currentException()); - sendPacket(s); + return s; } - void sendContinue(const std::string& pkt) + void doContinue(const std::string& pkt) { if (pkt[0] != 'c') { WARN_LOG(COMMON, "Continue with signal not supported"); @@ -359,35 +423,34 @@ class GdbServer } } - void readAllRegs() + std::string readAllRegs() { u32 *regs; int c = agent.readAllRegs(®s); std::string outpkt; for (int i = 0; i < c; i++) outpkt += pack(regs[i]); - sendPacket(outpkt); + return outpkt; } - void writeAllRegs(const std::string& pkt) + std::string writeAllRegs(const std::string& pkt) { std::vector regs; for (auto it = pkt.begin() + 1; it <= pkt.end() - 8; it += 8) regs.push_back(unpack(&*it, 8)); agent.writeAllRegs(regs); - sendPacket("OK"); + return "OK"; } - void readMem(const std::string& pkt) + std::string readMem(const std::string& pkt) { u32 addr; u32 len; if (sscanf(pkt.c_str(), "m%x,%x:", &addr, &len) != 2) { WARN_LOG(COMMON, "readMem: invalid packet %s", pkt.c_str()); - sendPacket("E01"); - return; + return "E01"; } const u8 *mem = agent.readMem(addr, len); std::string outpkt; @@ -397,18 +460,17 @@ class GdbServer sprintf(s,"%02x", mem[i]); outpkt += s; } - sendPacket(outpkt); + return outpkt; } - void writeMem(const std::string& pkt) + std::string writeMem(const std::string& pkt) { u32 addr; u32 len; if (sscanf(pkt.c_str(), "M%x,%x:", &addr, &len) != 2) { WARN_LOG(COMMON, "writeMem: invalid packet %s", pkt.c_str()); - sendPacket("E01"); - return; + return "E01"; } std::vector data(len); const char *p = &pkt[pkt.find(':')] + 1; @@ -419,18 +481,17 @@ class GdbServer data[i] = (u8)b; } agent.writeMem(addr, data); - sendPacket("OK"); + return "OK"; } - void writeMemBin(const std::string& pkt) + std::string writeMemBin(const std::string& pkt) { u32 addr; u32 len; if (sscanf(pkt.c_str(), "X%x,%x:", &addr, &len) != 2) { WARN_LOG(COMMON, "writeMemBin invalid command: %s", pkt.c_str()); - sendPacket("E01"); - return; + return "E01"; } const char *p = &pkt[pkt.find(':')] + 1; std::vector data; @@ -445,64 +506,62 @@ class GdbServer data.push_back(b); } agent.writeMem(addr, data); - sendPacket("OK"); + return "OK"; } - void readReg(const std::string& pkt) + std::string readReg(const std::string& pkt) { u32 regNum; if (sscanf(pkt.c_str(), "p%x", ®Num) != 1) { WARN_LOG(COMMON, "readReg: invalid packet %s", pkt.c_str()); - sendPacket("E01"); - return; + return "E01"; } u32 v = agent.readReg(regNum); - sendPacket(pack(v)); + return pack(v); } - void writeReg(const std::string& pkt) + std::string writeReg(const std::string& pkt) { u32 regNum; char vstr[9]; if (sscanf(pkt.c_str(), "P%x=%8s", ®Num, vstr) != 2) { WARN_LOG(COMMON, "writeReg: invalid packet %s", pkt.c_str()); - sendPacket("E01"); - return; + return "E01"; } agent.writeReg(regNum, unpack(vstr, 8)); - sendPacket("OK"); + return "OK"; } - void query(const std::string& pkt) + std::vector query(const std::string& pkt) { if (pkt == "qC") // Return the current thread ID. 0 is "any thread" - sendPacket("QC0.01"); + return { "QC0.01" }; else if (pkt.rfind("qCRC", 0) == 0) { WARN_LOG(COMMON, "CRC compute not supported %s", pkt.c_str()); - sendPacket("E01"); + return { "E01" }; } else if (pkt == "qfThreadInfo") // Obtain a list of all active thread IDs (first call) - sendPacket("m0"); + return { "m0" }; else if (pkt == "qsThreadInfo") // Obtain a list of all active thread IDs (subsequent calls -> 'l' == end of list) - sendPacket("l"); + return { "l" }; else if (pkt.rfind("qGetTLSAddr:", 0) == 0) // Fetch the address associated with thread local storage - sendPacket(""); + return { "" }; else if (pkt.rfind("qL", 0) == 0) // Obtain thread information. deprecated - sendPacket("qM001"); + return { "qM001" }; else if (pkt == "qOffsets") // Get section offsets. Not supported - sendPacket(""); + return { "" }; else if (pkt.rfind("qP", 0) == 0) // Returns information on thread. deprecated - sendPacket(""); + return { "" }; else if (pkt.rfind("qRcmd,", 0) == 0) { std::string customCmd; @@ -535,10 +594,10 @@ class GdbServer } } *r = 0; - sendPacket(reply); + return { reply }; } else - sendPacket(""); + return { "" }; } else if (pkt.rfind("qSupported", 0) == 0) { @@ -546,43 +605,43 @@ class GdbServer // and query the stub for features it supports char qsupported[128]; snprintf(qsupported, 128, "PacketSize=%i;vContSupported+", MAX_PACKET_LEN); - sendPacket(qsupported); + return { qsupported }; } else if (pkt.rfind("qSymbol:", 0) == 0) // Notify the target that GDB is prepared to serve symbol lookup requests - sendPacket("OK"); + return { "OK" }; else if (pkt.rfind("qThreadExtraInfo,", 0) == 0) { // Obtain from the target OS a printable string description of thread attributes char s[19]; sprintf(s, "%02x%02x%02x%02x%02x%02x%02x%02x%02x", 'R', 'u', 'n', 'n', 'a', 'b', 'l', 'e', 0); - sendPacket(std::string(s, 18)); + return { std::string(s, 18) }; } else if (pkt.rfind("qXfer:", 0) == 0) // Read uninterpreted bytes from the target’s special data area identified by the keyword object - sendPacket(""); + return { "" }; else if (pkt.rfind("qAttached", 0) == 0) // Return an indication of whether the remote server attached to an existing process // or created a new process - sendPacket("1"); // existing process + return { "1" }; // existing process else if (pkt.rfind("qTfV", 0) == 0) // request data about trace state variables - sendPacket(""); + return { "" }; else if (pkt.rfind("qTfP", 0) == 0) // request data about tracepoints - sendPacket(""); + return { "" }; else if (pkt.rfind("qTStatus", 0) == 0) // Ask the stub if there is a trace experiment running right now - sendPacket(""); - else - WARN_LOG(COMMON, "query not supported %s", pkt.c_str()); + return { "" }; + WARN_LOG(COMMON, "query not supported %s", pkt.c_str()); + return {}; } - void set(const std::string& pkt) + std::vector set(const std::string& pkt) { if (pkt.rfind("QPassSignals:", 0) == 0) // Passing signals not supported - sendPacket(""); + return { "" }; else if (pkt.rfind("QTDP", 0) == 0 || pkt.rfind("QFrame", 0) == 0 || pkt.rfind("QTStart", 0) == 0 @@ -590,210 +649,184 @@ class GdbServer || pkt.rfind("QTinit", 0) == 0 || pkt.rfind("QTro", 0) == 0) // No tracepoint feature supported - sendPacket(""); - else - WARN_LOG(COMMON, "set not supported %s", pkt.c_str()); + return { "" }; + WARN_LOG(COMMON, "set not supported %s", pkt.c_str()); + return {}; } - void vpacket(const std::string& pkt) + std::vector vpacket(const std::string& pkt) { if (pkt.rfind("vAttach;", 0) == 0) - sendPacket("S05"); + return { "S05" }; else if (pkt.rfind("vCont?", 0) == 0) // supported vCont actions - (c)ontinue, (C)ontinue with signal, (s)tep, (S)tep with signal, (r)ange-step - sendPacket("vCont;c;C;s;S;t;r"); + return { "vCont;c;C;s;S;t;r" }; else if (pkt.rfind("vCont", 0) == 0) { std::string vContCmd = pkt.substr(strlen("vCont;")); + std::vector replies; switch (vContCmd[0]) { case 'c': case 'C': - sendContinue(vContCmd); - break; + doContinue(vContCmd); + return {}; case 's': - step(EXCEPT_NONE); - break; + return { step(EXCEPT_NONE) }; case 'S': - step(); + replies.push_back(step()); + [[fallthrough]]; case 'r': { u32 from, to; if (sscanf(vContCmd.c_str(), "r%x,%x", &from, &to) == 2) { - stepRange(from, to); + auto v = stepRange(from, to); + replies.insert(replies.end(), v.begin(), v.end()); } else { WARN_LOG(COMMON, "Unsupported vCont:r format %s", pkt.c_str()); - sendContinue("c"); + doContinue("c"); } - - break; + return replies; } default: WARN_LOG(COMMON, "vCont action not supported %s", pkt.c_str()); + return {}; } } else if (pkt.rfind("vFile:", 0) == 0) // not supported - sendPacket(""); + return { "" }; else if (pkt.rfind("vFlashErase:", 0) == 0) // not supported - sendPacket("E01"); + return { "E01" }; else if (pkt.rfind("vFlashWrite:", 0) == 0) // not supported - sendPacket("E01"); + return { "E01" }; else if (pkt.rfind("vFlashDone:", 0) == 0) // not supported - sendPacket("E01"); + return { "E01" }; else if (pkt.rfind("vRun;", 0) == 0) { if (pkt != "vRun;") WARN_LOG(COMMON, "unexpected vRun args ignored: %s", pkt.c_str()); agent.restart(); - sendPacket("S05"); + return { "S05" }; } else if (pkt.rfind("vKill", 0) == 0) { - sendPacket("OK"); agent.kill(); + return { "OK" }; } else { WARN_LOG(COMMON, "unknown v packet: %s", pkt.c_str()); - sendPacket(""); + return { "" }; } } - void restart() - { + void restart() { agent.restart(); } - void step(u32 what = 0) + std::string step(u32 what = 0) { try { agent.step(); - sendPacket("S05"); + return "S05"; } catch (const FlycastException& e) { throw Error(e.what()); } } - void stepRange(u32 from, u32 to) + std::vector stepRange(u32 from, u32 to) { try { - sendPacket("OK"); agent.stepRange(from, to); - sendPacket("S05"); + return { "OK", "S05" }; } catch (const FlycastException& e) { throw Error(e.what()); } } - void insertMatchpoint(const std::string& pkt) + std::string insertMatchpoint(const std::string& pkt) { u32 type; u32 addr; u32 len; if (sscanf(pkt.c_str(), "Z%1d,%x,%1d", &type, &addr, &len) != 3) { WARN_LOG(COMMON, "insertMatchpoint: unknown packet: %s", pkt.c_str()); - sendPacket("E01"); + return "E01"; } switch (type) { case DebugAgent::Breakpoint::BP_TYPE_SOFTWARE_BREAK: // soft bp if (agent.insertMatchpoint(DebugAgent::Breakpoint::BP_TYPE_SOFTWARE_BREAK, addr, len)) - sendPacket("OK"); + return "OK"; else - sendPacket("E01"); + return "E01"; break; case DebugAgent::Breakpoint::BP_TYPE_HARDWARE_BREAK: // hardware bp - sendPacket(""); + return ""; break; case DebugAgent::Breakpoint::BP_TYPE_WRITE_WATCHPOINT: // write watchpoint - sendPacket(""); + return ""; break; case DebugAgent::Breakpoint::BP_TYPE_READ_WATCHPOINT: // read watchpoint - sendPacket(""); + return ""; break; case DebugAgent::Breakpoint::BP_TYPE_ACCESS_WATCHPOINT: // access watchpoint - sendPacket(""); + return ""; break; default: - sendPacket(""); + return ""; break; } } - void removeMatchpoint(const std::string& pkt) + std::string removeMatchpoint(const std::string& pkt) { u32 type; u32 addr; u32 len; if (sscanf(pkt.c_str(), "z%1d,%x,%1d", &type, &addr, &len) != 3) { WARN_LOG(COMMON, "removeMatchpoint: unknown packet: %s", pkt.c_str()); - sendPacket("E01"); + return "E01"; } switch (type) { case 0: // soft bp if (agent.removeMatchpoint(DebugAgent::Breakpoint::BP_TYPE_SOFTWARE_BREAK, addr, len)) - sendPacket("OK"); + return "OK"; else - sendPacket("E01"); + return "E01"; break; case 1: // hardware bp - sendPacket(""); + return ""; break; case 2: // write watchpoint - sendPacket(""); + return ""; break; case 3: // read watchpoint - sendPacket(""); + return ""; break; case 4: // access watchpoint - sendPacket(""); + return ""; break; default: - sendPacket(""); + return ""; break; } } - void interrupt() + std::string interrupt() { u32 signal = agentInterrupt(); char s[10]; sprintf(s, "S%02x", signal); - sendPacket(s); - } - - char recvChar() - { - char c; - int rc = ::recv(clientSocket, &c, 1, 0); - if (rc <= 0) - throw Error("gdb: I/O error"); - return c; - } - - void sendChar(char c) - { - std::unique_lock lock(outMutex); - int rc = ::send(clientSocket, &c, 1, 0); - if (rc <= 0) - throw Error("gdb: I/O error"); - } - - u8 unpack(char c) - { - c = std::tolower(c); - if (c <= '9') - return c - '0'; - else - return c - 'a' + 10; + return s; } char packnb(u8 b) @@ -811,122 +844,109 @@ class GdbServer return s; } - std::string pack(u32 v) - { + std::string pack(u32 v) { return packb(v & 0xff) + packb((v >> 8) & 0xff) + packb((v >> 16) & 0xff) + packb((v >> 24) & 0xff); } - u32 unpack(const char *s, int l) + u32 agentInterrupt() { - u32 r = 0; - for (int i = 0; i < l && *s != '\0'; i += 2, s += 2) - { - r |= (unpack(s[0]) << 4 | unpack(s[1])) << (i * 4); + try { + return agent.interrupt(); + } catch (const FlycastException& e) { + throw Error(e.what()); } - return r; } - std::string recvPacket() - { - std::string pkt; - // look for start character ('$') or BREAK - char c = recvChar(); - if (c == 3) - return std::string("\03"); - if (c != '$') - return pkt; - - // read until '#' - u8 checksum = 0; - while (!stopRequested) - { - c = recvChar(); - if (c == '$') - { - checksum = 0; - pkt.clear(); - - continue; - } - - if (c == '#') - break; - - checksum += (u8)c; - pkt.push_back(c); - } - if (stopRequested) - { - pkt.clear(); - return pkt; - } - u8 recvchk = unpack(recvChar()) << 4; - recvchk |= unpack(recvChar()); - - // If the checksums don't match print a warning, and put the - // negative ack back to the client. Otherwise put a positive ack. - if (checksum != recvchk) - { - sendChar('-'); // Failed checksum - return ""; - } - else - { - sendChar('+'); // Successful transfer - return pkt; - } + void clientConnected() { + attached = true; + agentInterrupt(); } - void sendPacket(const std::string& pkt) - { DEBUG_LOG(NETWORK, "gdb: sending pkt"); - std::unique_lock lock(outMutex); - std::string data{'$'}; - u8 checksum = 0; - for (char c : pkt) + static void emuEventCallback(Event event, void *arg) + { + GdbServer *gdbServer = static_cast(arg); + switch (event) { - if (c == '$' || c == '#' || c == '*' || c == '}') - { - c ^= 0x20; - checksum += (u8)'}'; - data.push_back('}'); + case Event::Resume: + try { + if (!gdbServer->isRunning()) + gdbServer->run(); + } catch (const GdbServer::Error& e) { + ERROR_LOG(COMMON, "%s", e.what()); } - checksum += (u8)c; - data.push_back(c); + break; + case Event::Terminate: + gdbServer->stop(); + break; + default: + break; } - data.push_back('#'); - char s[9]; - sprintf(s, "%02x", checksum); - data += s; - DEBUG_LOG(NETWORK, "gdb: sent %s", data.c_str()); - int ret = ::send(clientSocket, data.c_str(), data.length(), 0); - if (ret < (int)data.length()) - throw Error("I/O error"); } - u32 agentInterrupt() - { - try { - return agent.interrupt(); - } catch (const FlycastException& e) { - throw Error(e.what()); - } - } - - bool initialised = false; - bool stopRequested = false; bool attached = false; bool postDebugTrapNeeded = false; - sock_t serverSocket = INVALID_SOCKET; - sock_t clientSocket = INVALID_SOCKET; std::thread thread; - std::mutex outMutex; + std::unique_ptr io_context; + int port = DEFAULT_PORT; + friend class TcpAcceptor; + friend class Connection; + public: DebugAgent agent; }; static GdbServer gdbServer; +void TcpAcceptor::handleAccept(Connection::Ptr newConnection, const std::error_code& error) +{ + if (!error) { + server.clientConnected(); + newConnection->start(); + } + start(); +} + +void Connection::handlePacket(const std::error_code& ec, size_t len) +{ + std::string msg = message.substr(0, len); + message = message.substr(len); + if (ec || len == 0) + { + // terminate the connection + if (ec != asio::error::eof && ec != asio::error::operation_aborted) + WARN_LOG(NETWORK, "Read error %s", ec.message().c_str()); + return; + } + try { + if (msg[0] == '\03') { // break + send(server.handleCommand(msg)); + return; + } + if (msg[0] != '$') { + // Ignore unexpected chars + send(""); + return; + } + u8 cksum = 0; + for (unsigned i = 1; i < len - 3; i++) + cksum += (u8)msg[i]; + if (cksum != (unpack(msg[len - 2]) << 4 | unpack(msg[len - 1]))) { + // Invalid checksum + WARN_LOG(COMMON, "Connection::handlePacket: invalid checksum: [%s]", msg.c_str()); + send("-"); + } + else { + // Positive ack + std::string reply = "+"; + reply += server.handleCommand(msg.substr(1, msg.length() - 4)); + send(reply); + } + } catch (...) { + // terminate the connection + } +} + void init(int port) { gdbServer.init(port); @@ -952,25 +972,5 @@ void subroutineReturn() gdbServer.agent.subroutineReturn(); } -static void emuEventCallback(Event event, void *) -{ - switch (event) - { - case Event::Resume: - try { - if (!gdbServer.isRunning()) - gdbServer.run(); - } catch (const GdbServer::Error& e) { - ERROR_LOG(COMMON, "%s", e.what()); - } - break; - case Event::Terminate: - gdbServer.stop(); - break; - default: - break; - } -} - } #endif diff --git a/core/deps/SDL b/core/deps/SDL index c98c4fbff..9c821dc21 160000 --- a/core/deps/SDL +++ b/core/deps/SDL @@ -1 +1 @@ -Subproject commit c98c4fbff6d8f3016a3ce6685bf8f43433c3efcc +Subproject commit 9c821dc21ccbd69b2bda421fdb35cb4ae2da8f5e diff --git a/core/deps/asio b/core/deps/asio new file mode 160000 index 000000000..03ae834ed --- /dev/null +++ b/core/deps/asio @@ -0,0 +1 @@ +Subproject commit 03ae834edbace31a96157b89bf50e5ee464e5ef9 diff --git a/core/deps/libretro-common/compat/fopen_utf8.c b/core/deps/libretro-common/compat/fopen_utf8.c index 52b481e7e..d3d467798 100644 --- a/core/deps/libretro-common/compat/fopen_utf8.c +++ b/core/deps/libretro-common/compat/fopen_utf8.c @@ -31,7 +31,7 @@ #endif #endif -#ifdef _WIN32 +#if defined(_WIN32) && !defined(USE_LIBCDIO) // libcdio has its equivalent version #undef fopen void *fopen_utf8(const char * filename, const char * mode) diff --git a/core/deps/picotcp/include/pico_socket.h b/core/deps/picotcp/include/pico_socket.h index 1c70610f1..9ca446e16 100644 --- a/core/deps/picotcp/include/pico_socket.h +++ b/core/deps/picotcp/include/pico_socket.h @@ -170,6 +170,7 @@ struct pico_ip_mreq_source { #define PICO_SOCK_EV_CLOSE 8u #define PICO_SOCK_EV_FIN 0x10u #define PICO_SOCK_EV_ERR 0x80u +#define PICO_SOCK_EV_DEL 0x100u struct pico_msginfo { struct pico_device *dev; diff --git a/core/deps/picotcp/modules/pico_ethernet.c b/core/deps/picotcp/modules/pico_ethernet.c index cc86e8562..9e246daa8 100644 --- a/core/deps/picotcp/modules/pico_ethernet.c +++ b/core/deps/picotcp/modules/pico_ethernet.c @@ -295,7 +295,7 @@ static int pico_ethernet_ipv6_dst(struct pico_frame *f, struct pico_eth *const d /* Ethernet send, first attempt: try our own address. * Returns 0 if the packet is not for us. - * Returns 1 if the packet is cloned to our own receive queue, so the caller can discard the original frame. + * Returns 1 if the packet is cloned to our own receive queue and the original frame is dicarded. * */ static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr) { @@ -308,7 +308,9 @@ static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr dbg("sending out packet destined for our own mac\n"); if (pico_ethernet_receive(clone) < 0) { dbg("pico_ethernet_receive() failed\n"); + return 0; } + pico_frame_discard(f); return 1; } @@ -317,13 +319,12 @@ static int32_t pico_ethsend_local(struct pico_frame *f, struct pico_eth_hdr *hdr /* Ethernet send, second attempt: try bcast. * Returns 0 if the packet is not bcast, so it will be handled somewhere else. - * Returns 1 if the packet is handled by the pico_device_broadcast() function, so it can be discarded. + * Returns 1 if the packet is handled by the pico_device_broadcast() function and is discarded. * */ static int32_t pico_ethsend_bcast(struct pico_frame *f) { if (IS_LIMITED_BCAST(f)) { - (void)pico_device_broadcast(f); /* We can discard broadcast even if it's not sent. */ - return 1; + return (pico_device_broadcast(f) > 0); // Return 1 on success, ret > 0 } return 0; diff --git a/core/deps/picotcp/modules/pico_icmp4.c b/core/deps/picotcp/modules/pico_icmp4.c index 25c75b68f..e2250ae25 100644 --- a/core/deps/picotcp/modules/pico_icmp4.c +++ b/core/deps/picotcp/modules/pico_icmp4.c @@ -242,6 +242,10 @@ static int8_t pico_icmp4_send_echo(struct pico_icmp4_ping_cookie *cookie) if (!dev) return -1; + // prevent overflow + if (cookie->size > PICO_ICMP_MAXCOOKIE) + return -1; + echo = pico_proto_ipv4.alloc(&pico_proto_ipv4, dev, (uint16_t)(PICO_ICMPHDR_UN_SIZE + cookie->size)); if (!echo) return -1; diff --git a/core/deps/picotcp/modules/pico_icmp4.h b/core/deps/picotcp/modules/pico_icmp4.h index 1d677fb6c..965b9551e 100644 --- a/core/deps/picotcp/modules/pico_icmp4.h +++ b/core/deps/picotcp/modules/pico_icmp4.h @@ -92,7 +92,7 @@ PACKED_STRUCT_DEF pico_icmp4_hdr { #define PICO_ICMP_MASKREPLY 18 #define PICO_ICMP_MAXTYPE 18 - +#define PICO_ICMP_MAXCOOKIE 65528 #define PICO_ICMP_UNREACH_NET 0 #define PICO_ICMP_UNREACH_HOST 1 diff --git a/core/deps/picotcp/modules/pico_tcp.c b/core/deps/picotcp/modules/pico_tcp.c index 3a06599e0..b03037d46 100644 --- a/core/deps/picotcp/modules/pico_tcp.c +++ b/core/deps/picotcp/modules/pico_tcp.c @@ -826,6 +826,8 @@ static void tcp_rcv_sack(struct pico_socket_tcp *t, uint8_t *opt, int len) static int tcpopt_len_check(uint32_t *idx, uint8_t len, uint8_t expected) { if (len != expected) { + if (len < 2) + return -1; *idx = *idx + len - 2; return -1; } @@ -880,7 +882,7 @@ static inline void tcp_parse_option_timestamp(struct pico_socket_tcp *t, struct t->ts_nxt = long_be(tsval); } -static void tcp_parse_options(struct pico_frame *f) +static int tcp_parse_options(struct pico_frame *f) { struct pico_socket_tcp *t = (struct pico_socket_tcp *)f->sock; uint8_t *opt = f->transport_hdr + PICO_SIZE_TCPHDR; @@ -897,6 +899,10 @@ static void tcp_parse_options(struct pico_frame *f) if (f->payload && ((opt + i) > f->payload)) break; + if (len == 0) { + return -1; + } + tcp_dbg_options("Received option '%d', len = %d \n", type, len); switch (type) { case PICO_TCP_OPTION_NOOP: @@ -924,6 +930,7 @@ static void tcp_parse_options(struct pico_frame *f) i = i + len - 2; } } + return 0; } static inline void tcp_send_add_tcpflags(struct pico_socket_tcp *ts, struct pico_frame *f) @@ -1755,7 +1762,8 @@ static int tcp_data_in(struct pico_socket *s, struct pico_frame *f) (void)hdr; if (((hdr->len & 0xf0u) >> 2u) <= f->transport_len) { - tcp_parse_options(f); + if (tcp_parse_options(f) < 0) + return -1; f->payload = f->transport_hdr + ((hdr->len & 0xf0u) >> 2u); f->payload_len = payload_len; tcp_dbg("TCP> Received segment. (exp: %x got: %x)\n", t->rcv_nxt, SEQN(f)); @@ -2130,7 +2138,8 @@ static int tcp_ack(struct pico_socket *s, struct pico_frame *f) tcp_ack_dbg(s, f); #endif - tcp_parse_options(f); + if (tcp_parse_options(f) < 0) + return -1; t->recv_wnd = short_be(hdr->rwnd); acked = (uint16_t)tcp_ack_advance_una(t, f, &acked_timestamp); @@ -2446,7 +2455,8 @@ static int tcp_syn(struct pico_socket *s, struct pico_frame *f) f->sock = &new->sock; mtu = (uint16_t)pico_socket_get_mss(&new->sock); new->mss = (uint16_t)(mtu - PICO_SIZE_TCPHDR); - tcp_parse_options(f); + if (tcp_parse_options(f) < 0) + return -1; new->tcpq_in.max_size = PICO_DEFAULT_SOCKETQ; new->tcpq_out.max_size = PICO_DEFAULT_SOCKETQ; new->tcpq_hold.max_size = 2u * mtu; @@ -2793,7 +2803,7 @@ static uint8_t invalid_flags(struct pico_socket *s, uint8_t flags) { /* PICO_SOCKET_STATE_TCP_UNDEF */ 0, }, { /* PICO_SOCKET_STATE_TCP_CLOSED */ 0, }, { /* PICO_SOCKET_STATE_TCP_LISTEN */ PICO_TCP_SYN, PICO_TCP_SYN | PICO_TCP_PSH }, - { /* PICO_SOCKET_STATE_TCP_SYN_SENT */ PICO_TCP_SYNACK, PICO_TCP_RST, PICO_TCP_RSTACK}, + { /* PICO_SOCKET_STATE_TCP_SYN_SENT */ PICO_TCP_SYNACK, PICO_TCP_RST, PICO_TCP_RSTACK, PICO_TCP_SYNACK | PICO_TCP_PSH }, { /* PICO_SOCKET_STATE_TCP_SYN_RECV */ PICO_TCP_SYN, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST}, { /* PICO_SOCKET_STATE_TCP_ESTABLISHED*/ PICO_TCP_SYN, PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST, PICO_TCP_RSTACK}, { /* PICO_SOCKET_STATE_TCP_CLOSE_WAIT */ PICO_TCP_SYNACK, PICO_TCP_ACK, PICO_TCP_PSH, PICO_TCP_PSHACK, PICO_TCP_FIN, PICO_TCP_FINACK, PICO_TCP_FINPSHACK, PICO_TCP_RST}, @@ -2864,6 +2874,11 @@ int pico_tcp_input(struct pico_socket *s, struct pico_frame *f) // tcp_dbg("[sam] TCP> s->state >> 8 = %u\n", s->state >> 8); tcp_dbg("[tcp input] socket: %p state: %d <-- local port:%u remote port: %u seq: 0x%08x ack: 0x%08x flags: 0x%02x t_len: %u, hdr: %u payload: %d\n", s, s->state >> 8, short_be(hdr->trans.dport), short_be(hdr->trans.sport), SEQN(f), ACKN(f), hdr->flags, f->transport_len, (hdr->len & 0xf0) >> 2, f->payload_len ); + if ((f->payload + f->payload_len) > (f->buffer + f->buffer_len)) { + tcp_dbg("TCP> Invalid payload len %04x\n", f->payload_len); + pico_frame_discard(f); + return -1; + } /* This copy of the frame has the current socket as owner */ f->sock = s; s->timestamp = TCP_TIME; @@ -2874,7 +2889,9 @@ int pico_tcp_input(struct pico_socket *s, struct pico_frame *f) } else if (flags == PICO_TCP_SYN || flags == (PICO_TCP_SYN | PICO_TCP_PSH)) { tcp_action_call(action->syn, s, f); - } else if (flags == (PICO_TCP_SYN | PICO_TCP_ACK)) { + } else if (flags == (PICO_TCP_SYN | PICO_TCP_ACK) + // Windows CE / DirectPlay sets the PSH flag on SYN|ACK packets, which is normally invalid + || flags == (PICO_TCP_SYN | PICO_TCP_ACK | PICO_TCP_PSH)) { tcp_action_call(action->synack, s, f); } else { ret = tcp_action_by_flags(action, s, f, flags); diff --git a/core/deps/picotcp/stack/pico_device.c b/core/deps/picotcp/stack/pico_device.c index e0fb8d1c6..ccd2f928b 100644 --- a/core/deps/picotcp/stack/pico_device.c +++ b/core/deps/picotcp/stack/pico_device.c @@ -451,6 +451,7 @@ int32_t pico_device_broadcast(struct pico_frame *f) { struct pico_tree_node *index; int32_t ret = -1; + int sent = 0; pico_tree_foreach(index, &Device_tree) { @@ -463,14 +464,28 @@ int32_t pico_device_broadcast(struct pico_frame *f) break; copy->dev = dev; - copy->dev->send(copy->dev, copy->start, (int)copy->len); + ret = copy->dev->send(copy->dev, copy->start, (int)copy->len); + /* FIXME: If a device driver returns zero (which means the device + * driver is currently busy) there is no means to retry the + * broadcast operation later. */ pico_frame_discard(copy); } else { ret = f->dev->send(f->dev, f->start, (int)f->len); + /* FIXME: If a device driver returns zero (which means the device + * driver is currently busy) there is no means to retry the + * broadcast operation later. */ + } + + /* FIXME: If at least one device driver was able to sent the frame on + * the wire, the broadcast operation will be considered successful. */ + if (ret > 0) { + sent = 1; } } + ret = sent ? f->len : -1; + pico_frame_discard(f); return ret; } diff --git a/core/deps/picotcp/stack/pico_socket.c b/core/deps/picotcp/stack/pico_socket.c index d5a8adf68..60e0a814e 100644 --- a/core/deps/picotcp/stack/pico_socket.c +++ b/core/deps/picotcp/stack/pico_socket.c @@ -473,6 +473,8 @@ static void socket_garbage_collect(pico_time now, void *arg) struct pico_socket *s = (struct pico_socket *) arg; IGNORE_PARAMETER(now); + if (s->wakeup) + s->wakeup(PICO_SOCK_EV_DEL, s); socket_clean_queues(s); PICO_FREE(s); } diff --git a/core/emulator.cpp b/core/emulator.cpp index 181bb619d..0e716e770 100644 --- a/core/emulator.cpp +++ b/core/emulator.cpp @@ -47,8 +47,11 @@ #ifndef LIBRETRO #include "ui/gui.h" #endif +#include "hw/sh4/sh4_interpreter.h" +#include "hw/sh4/dyna/ngen.h" settings_t settings; +constexpr char const *BIOS_TITLE = "Dreamcast BIOS"; static void loadSpecialSettings() { @@ -402,7 +405,7 @@ static void loadSpecialSettings() } } -void dc_reset(bool hard) +void Emulator::dc_reset(bool hard) { if (hard) { @@ -413,7 +416,7 @@ void dc_reset(bool hard) sh4_sched_reset(hard); pvr::reset(hard); aica::reset(hard); - sh4_cpu.Reset(true); + getSh4Executor()->Reset(true); mem_Reset(hard); } @@ -489,22 +492,28 @@ void Emulator::init() // the recompiler may start generating code at this point and needs a fully configured machine #if FEAT_SHREC != DYNAREC_NONE - Get_Sh4Recompiler(&sh4_cpu); - sh4_cpu.Init(); // Also initialize the interpreter + recompiler = Get_Sh4Recompiler(); + recompiler->Init(); if(config::DynarecEnabled) - { INFO_LOG(DYNAREC, "Using Recompiler"); - } else #endif - { - Get_Sh4Interpreter(&sh4_cpu); - sh4_cpu.Init(); INFO_LOG(INTERPRETER, "Using Interpreter"); - } + interpreter = Get_Sh4Interpreter(); + interpreter->Init(); state = Init; } +Sh4Executor *Emulator::getSh4Executor() +{ +#if FEAT_SHREC != DYNAREC_NONE + if(config::DynarecEnabled) + return recompiler; + else +#endif + return interpreter; +} + int getGamePlatform(const std::string& filename) { if (settings.naomi.slave) @@ -567,14 +576,14 @@ void Emulator::loadGame(const char *path, LoadProgress *progress) // Boot BIOS if (!nvmem::loadFiles()) throw FlycastException("No BIOS file found in " + hostfs::getFlashSavePath("", "")); - InitDrive(""); + gdr::initDrive(""); } else { std::string extension = get_file_extension(settings.content.path); if (extension != "elf") { - if (InitDrive(settings.content.path)) + if (gdr::initDrive(settings.content.path)) { loadGameSpecificSettings(); if (config::UseReios || !nvmem::loadFiles()) @@ -591,18 +600,18 @@ void Emulator::loadGame(const char *path, LoadProgress *progress) settings.content.path.clear(); if (!nvmem::loadFiles()) throw FlycastException("This media cannot be loaded"); - InitDrive(""); + gdr::initDrive(""); } } else { // Elf only supported with HLE BIOS nvmem::loadHle(); - InitDrive(""); + gdr::initDrive(""); } } if (settings.content.path.empty()) - settings.content.title = "Dreamcast BIOS"; + settings.content.title = BIOS_TITLE; if (progress) progress->progress = 1.0f; @@ -673,13 +682,13 @@ void Emulator::runInternal() { if (singleStep) { - sh4_cpu.Step(); + getSh4Executor()->Step(); singleStep = false; } else if(stepRangeTo != 0) { while (Sh4cntx.pc >= stepRangeFrom && Sh4cntx.pc <= stepRangeTo) - sh4_cpu.Step(); + getSh4Executor()->Step(); stepRangeFrom = 0; stepRangeTo = 0; @@ -689,7 +698,7 @@ void Emulator::runInternal() do { resetRequested = false; - sh4_cpu.Run(); + getSh4Executor()->Run(); if (resetRequested) { @@ -738,7 +747,18 @@ void Emulator::term() if (state == Init) { debugger::term(); - sh4_cpu.Term(); + if (interpreter != nullptr) + { + interpreter->Term(); + delete interpreter; + interpreter = nullptr; + } + if (recompiler != nullptr) + { + recompiler->Term(); + delete recompiler; + recompiler = nullptr; + } custom_texture.Terminate(); // lr: avoid deadlock on exit (win32) reios_term(); aica::term(); @@ -762,7 +782,7 @@ void Emulator::stop() const std::lock_guard _(mutex); // must be updated after GGPO is stopped since it may run some rollback frames state = Loaded; - sh4_cpu.Stop(); + getSh4Executor()->Stop(); } if (config::ThreadedRendering) { @@ -796,7 +816,7 @@ void Emulator::requestReset() resetRequested = true; if (config::GGPOEnable) NetworkHandshake::term(); - sh4_cpu.Stop(); + getSh4Executor()->Stop(); } void loadGameSpecificSettings() @@ -843,7 +863,7 @@ void Emulator::stepRange(u32 from, u32 to) stop(); } -void dc_loadstate(Deserializer& deser) +void Emulator::loadstate(Deserializer& deser) { custom_texture.Terminate(); #if FEAT_AREC == DYNAREC_JIT @@ -859,8 +879,8 @@ void dc_loadstate(Deserializer& deser) dc_deserialize(deser); mmu_set_state(); - sh4_cpu.ResetCache(); - KillTex = true; + getSh4Executor()->ResetCache(); + EventManager::event(Event::LoadState); } void Emulator::setNetworkState(bool online) @@ -873,7 +893,7 @@ void Emulator::setNetworkState(bool online) && config::Sh4Clock != 200) { config::Sh4Clock.override(200); - sh4_cpu.ResetCache(); + getSh4Executor()->ResetCache(); } EventManager::event(Event::Network); } @@ -908,7 +928,7 @@ void Emulator::run() startTime = sh4_sched_now64(); renderTimeout = false; if (!singleStep && stepRangeTo == 0) - sh4_cpu.Start(); + getSh4Executor()->Start(); try { runInternal(); if (ggpo::active()) @@ -916,7 +936,7 @@ void Emulator::run() } catch (...) { setNetworkState(false); state = Error; - sh4_cpu.Stop(); + getSh4Executor()->Stop(); EventManager::event(Event::Pause); throw; } @@ -932,18 +952,6 @@ void Emulator::start() if (config::GGPOEnable && config::ThreadedRendering) // Not supported with GGPO config::EmulateFramebuffer.override(false); -#if FEAT_SHREC != DYNAREC_NONE - if (config::DynarecEnabled) - { - Get_Sh4Recompiler(&sh4_cpu); - INFO_LOG(DYNAREC, "Using Recompiler"); - } - else -#endif - { - Get_Sh4Interpreter(&sh4_cpu); - INFO_LOG(DYNAREC, "Using Interpreter"); - } setupPtyPipe(); memwatch::protect(); @@ -951,7 +959,7 @@ void Emulator::start() if (config::ThreadedRendering) { const std::lock_guard lock(mutex); - sh4_cpu.Start(); + getSh4Executor()->Start(); threadResult = std::async(std::launch::async, [this] { ThreadName _("Flycast-emu"); InitAudio(); @@ -968,7 +976,7 @@ void Emulator::start() TermAudio(); } catch (...) { setNetworkState(false); - sh4_cpu.Stop(); + getSh4Executor()->Stop(); TermAudio(); throw; } @@ -1046,7 +1054,7 @@ void Emulator::vblank() if (ggpo::active()) ggpo::endOfFrame(); else if (!config::ThreadedRendering) - sh4_cpu.Stop(); + getSh4Executor()->Stop(); } bool Emulator::restartCpu() @@ -1054,8 +1062,47 @@ bool Emulator::restartCpu() const std::lock_guard _(mutex); if (state != Running) return false; - sh4_cpu.Start(); + getSh4Executor()->Start(); return true; } +void Emulator::insertGdrom(const std::string& path) +{ + if (settings.platform.isArcade()) + return; + gdr::insertDisk(path); + diskChange(); +} + +void Emulator::openGdrom() +{ + if (settings.platform.isArcade()) + return; + gdr::openLid(); + diskChange(); +} + +void Emulator::diskChange() +{ + config::Settings::instance().reset(); + config::Settings::instance().load(false); + if (!settings.content.path.empty()) + { + hostfs::FileInfo info = hostfs::storage().getFileInfo(settings.content.path); + settings.content.fileName = info.name; + loadGameSpecificSettings(); + } + else + { + settings.content.fileName.clear(); + settings.content.gameId.clear(); + settings.content.title = BIOS_TITLE; + } + cheatManager.reset(settings.content.gameId); + if (cheatManager.isWidescreen()) + config::ScreenStretching.override(134); // 4:3 -> 16:9 + custom_texture.Terminate(); + EventManager::event(Event::DiskChange); +} + Emulator emu; diff --git a/core/emulator.h b/core/emulator.h index 354a491db..4afd195a0 100644 --- a/core/emulator.h +++ b/core/emulator.h @@ -33,12 +33,10 @@ void loadGameSpecificSettings(); void SaveSettings(); int flycast_init(int argc, char* argv[]); -void dc_reset(bool hard); // for tests only void flycast_term(); void dc_exit(); void dc_savestate(int index = 0, const u8 *pngData = nullptr, u32 pngSize = 0); void dc_loadstate(int index = 0); -void dc_loadstate(Deserializer& deser); time_t dc_getStateCreationDate(int index); void dc_getStateScreenshot(int index, std::vector& pngData); @@ -98,6 +96,8 @@ struct LoadProgress std::atomic progress; }; +class Sh4Executor; + class Emulator { public: @@ -170,10 +170,27 @@ class Emulator * Returns true if the cpu was started */ bool restartCpu(); + /* + * Load the machine state from the passed deserializer + */ + void loadstate(Deserializer& deser); + /** + * Insert a new media in the GD-ROM drive. + */ + void insertGdrom(const std::string& path); + /** + * Open the GD-ROM drive lid. + */ + void openGdrom(); + + Sh4Executor *getSh4Executor(); + + void dc_reset(bool hard); // for tests only private: bool checkStatus(bool wait = false); void runInternal(); + void diskChange(); enum State { Uninitialized = 0, @@ -193,6 +210,8 @@ class Emulator u32 stepRangeTo = 0; bool stopRequested = false; std::mutex mutex; + Sh4Executor *interpreter = nullptr; + Sh4Executor *recompiler = nullptr; }; extern Emulator emu; diff --git a/core/hw/flashrom/x76f100.h b/core/hw/flashrom/x76f100.h index bb8496a02..b27d5ae13 100644 --- a/core/hw/flashrom/x76f100.h +++ b/core/hw/flashrom/x76f100.h @@ -62,6 +62,11 @@ class X76F100SerialFlash memcpy(this->data, &data[index], sizeof(this->data)); } + const u8 *getProtectedData() const + { + return data; + } + void serialize(Serializer& ser) const; void deserialize(Deserializer& deser); diff --git a/core/hw/gdrom/gdromv3.cpp b/core/hw/gdrom/gdromv3.cpp index 1a29572ea..494f8adec 100644 --- a/core/hw/gdrom/gdromv3.cpp +++ b/core/hw/gdrom/gdromv3.cpp @@ -9,7 +9,6 @@ #include "hw/holly/holly_intc.h" #include "hw/holly/sb.h" #include "hw/sh4/modules/dmac.h" -#include "hw/sh4/sh4_interpreter.h" #include "hw/sh4/sh4_mem.h" #include "hw/sh4/sh4_mmr.h" #include "hw/sh4/sh4_sched.h" @@ -26,9 +25,9 @@ int sns_key; static u32 set_mode_offset; static read_params_t read_params; static packet_cmd_t packet_cmd; -static read_buff_t read_buff; -static pio_buff_t pio_buff; -static ata_cmd_t ata_cmd; +static DmaBuffer dma_buff; +static PioBuffer pio_buff; +static u8 ata_command; cdda_t cdda; static gd_states gd_state; @@ -36,7 +35,7 @@ static DiscType gd_disk_type; /* GD rom reset -> GDS_WAITCMD - GDS_WAITCMD -> ATA/SPI command [Command code is on ata_cmd] + 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 @@ -66,32 +65,39 @@ GD_HardwareInfo_t GD_HardwareInfo; void libCore_CDDA_Sector(s16* sector) { - //silence ! :p if (cdda.status == cdda_t::Playing) { - libGDR_ReadSector((u8*)sector,cdda.CurrAddr.FAD,1,2352); - cdda.CurrAddr.FAD++; - if (cdda.CurrAddr.FAD >= cdda.EndAddr.FAD) + if (libGDR_ReadSector((u8*)sector, cdda.CurrAddr.FAD, 1, 2352, true) == 0) { - if (cdda.repeats==0) - { - //stop - cdda.status = cdda_t::Terminated; - SecNumber.Status = GD_PAUSE; - } - else + // Stop + cdda.CurrAddr.FAD--; // should stay on the last sector read (reported by subcode with cdda status=terminated) + cdda.status = cdda_t::Terminated; + SecNumber.Status = GD_PAUSE; + memset(sector, 0, 2352); + } + else + { + cdda.CurrAddr.FAD++; + if (cdda.CurrAddr.FAD >= cdda.EndAddr.FAD) { - //Repeat ;) - if (cdda.repeats!=0xf) - cdda.repeats--; - - cdda.CurrAddr.FAD=cdda.StartAddr.FAD; + if (cdda.repeats == 0) + { + // Stop + cdda.status = cdda_t::Terminated; + SecNumber.Status = GD_PAUSE; + } + else + { + // Repeat + if (cdda.repeats != 15) + cdda.repeats--; + cdda.CurrAddr.FAD = cdda.StartAddr.FAD; + } } } } - else - { - memset(sector,0,2352); + else { + memset(sector, 0, 2352); } } @@ -99,21 +105,70 @@ static void gd_spi_pio_end(const u8* buffer, u32 len, gd_states next_state = gds static void gd_process_spi_cmd(); static void gd_process_ata_cmd(); -static void FillReadBuffer() +void DmaBuffer::fill(read_params_t& params) { - read_buff.cache_index=0; - u32 count = read_params.remaining_sectors; + if (!isEmpty()) + return; + index = 0; + verify(params.remaining_sectors > 0); + u32 count = std::min(params.remaining_sectors, NSECT); + size = count * params.sector_type; + + libGDR_ReadSector(cache, params.start_sector, count, params.sector_type); + params.start_sector += count; + params.remaining_sectors -= count; +} - if (count > 32) - count = 32; +const u8 *DmaBuffer::read(u32 len) +{ + verify(len <= size); + const u8 *p = &cache[index]; + index += len; + size -= len; + return p; +} - read_buff.cache_size=count*read_params.sector_type; +void DmaBuffer::serialize(Serializer& ser) const +{ + ser << size; + ser.serialize(&cache[index], size); +} - libGDR_ReadSector(read_buff.cache,read_params.start_sector,count,read_params.sector_type); - read_params.start_sector+=count; - read_params.remaining_sectors-=count; +void DmaBuffer::deserialize(Deserializer& deser) +{ + if (deser.version() < Deserializer::V54) + { + deser >> index; + deser >> size; + deser >> cache; + deser.skip(2352 * 16); + } + else + { + index = 0; + deser >> size; + deser.deserialize(&cache[0], size); + } } +void PioBuffer::serialize(Serializer& ser) const +{ + ser << next_state; + ser << index; + ser << size; + ser.serialize(&_data[0], size); +} + +void PioBuffer::deserialize(Deserializer& deser) +{ + deser >> next_state; + deser >> index; + deser >> size; + if (deser.version() < Deserializer::V54) + deser >> _data; + else + deser.deserialize(&_data[0], size); +} static void gd_set_state(gd_states state) { @@ -164,7 +219,7 @@ static void gd_set_state(gd_states state) case gds_pio_send_data: // When preparations are complete, the following steps are carried out at the device. //(1) Number of bytes to be read is set in "Byte Count" register. - ByteCount.full =(u16)(pio_buff.size<<1); + ByteCount.full = (u16)pio_buff.getSize(); //(2) IO bit is set and CoD bit is cleared. IntReason.IO=1; IntReason.CoD=0; @@ -190,22 +245,24 @@ static void gd_set_state(gd_states state) u32 sector_count = read_params.remaining_sectors; gd_states next_state=gds_pio_end; - if (sector_count > 27) - { - sector_count = 27; + const u32 maxSectors = (PioBuffer::Capacity - 1) / read_params.sector_type; + if (sector_count > maxSectors) { + sector_count = maxSectors; next_state = gds_readsector_pio; } - libGDR_ReadSector((u8*)&pio_buff.data[0],read_params.start_sector,sector_count, read_params.sector_type); - read_params.start_sector+=sector_count; - read_params.remaining_sectors-=sector_count; + 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); + read_params.start_sector += sector_count; + read_params.remaining_sectors -= sector_count; - gd_spi_pio_end(0,sector_count*read_params.sector_type,next_state); + gd_spi_pio_end(nullptr, 0, next_state); } break; case gds_readsector_dma: - FillReadBuffer(); + dma_buff.clear(); + dma_buff.fill(read_params); break; case gds_pio_end: @@ -237,7 +294,7 @@ static void gd_set_state(gd_states state) break; case gds_process_set_mode: - memcpy((u8 *)&GD_HardwareInfo + set_mode_offset, pio_buff.data, pio_buff.size << 1); + memcpy((u8 *)&GD_HardwareInfo + set_mode_offset, pio_buff.data(), pio_buff.getSize()); //end pio transfer ;) gd_set_state(gds_pio_end); break; @@ -305,36 +362,32 @@ static void gd_disc_change() read_params = { 0 }; set_mode_offset = 0; packet_cmd = { 0 }; - memset(&read_buff, 0, sizeof(read_buff)); - pio_buff = { gds_waitcmd, 0 }; - ata_cmd = { 0 }; + dma_buff.clear(); + pio_buff.clear(); + ata_command = 0; cdda = { cdda_t::NoInfo, 0 }; } //This handles the work of setting up the pio regs/state :) static void gd_spi_pio_end(const u8* buffer, u32 len, gd_states next_state) { - verify(len<0xFFFF); - pio_buff.index=0; - pio_buff.size=len>>1; - pio_buff.next_state=next_state; - - if (buffer!=0) - memcpy(pio_buff.data,buffer,len); - - if (len==0) + if (buffer != nullptr) { + verify(len < 0xFFFF); // TODO shouldn't this be <= 0xFFFF ? + memcpy(pio_buff.fill(len), buffer, len); + } + pio_buff.next_state = next_state; + if (pio_buff.isEmpty()) gd_set_state(next_state); else gd_set_state(gds_pio_send_data); } static void gd_spi_pio_read_end(u32 len, gd_states next_state) { - verify(len<0xFFFF); - pio_buff.index=0; - pio_buff.size=len>>1; - pio_buff.next_state=next_state; + verify(len < 0xFFFF); // TODO see above + pio_buff.resetSize(len); + pio_buff.next_state = next_state; - if (len==0) + if (len == 0) gd_set_state(next_state); else gd_set_state(gds_pio_get_data); @@ -351,7 +404,7 @@ static void gd_process_ata_cmd() else GDStatus.CHECK=1; - switch(ata_cmd.command) + switch (ata_command) { case ATA_NOP: printf_ata("ATA_NOP"); @@ -448,7 +501,7 @@ static void gd_process_ata_cmd() break; default: - WARN_LOG(GDROM, "Unknown ATA command %x", ata_cmd.command); + WARN_LOG(GDROM, "Unknown ATA command %x", ata_command); Error.ABRT = 1; Error.Sense = 5; // illegal request GDStatus.BSY = 0; @@ -707,12 +760,12 @@ static void gd_process_spi_cmd() read_params.sector_type = sector_type;//yeah i know , not really many types supported... printf_spicmd("SPI_CD_READ - Sector=%d Size=%d/%d DMA=%d",read_params.start_sector,read_params.remaining_sectors,read_params.sector_type,Features.CDRead.DMA); - if (Features.CDRead.DMA == 1) - { + if (Features.CDRead.DMA == 1) { + pio_buff.clear(); gd_set_state(gds_readsector_dma); } - else - { + else { + dma_buff.clear(); gd_set_state(gds_readsector_pio); } } @@ -1009,16 +1062,15 @@ u32 ReadMem_gdrom(u32 Addr, u32 sz) //if (gd_state == gds_pio_send_data) //{ - if (pio_buff.index == pio_buff.size) + if (pio_buff.atEnd()) { INFO_LOG(GDROM, "GDROM: Illegal Read From DATA (underflow)"); } else { - u32 rv= pio_buff.data[pio_buff.index]; - pio_buff.index+=1; - ByteCount.full-=2; - if (pio_buff.index==pio_buff.size) + u32 rv = pio_buff.read(); + ByteCount.full -= sizeof(u16); + if (pio_buff.atEnd()) { verify(pio_buff.next_state != gds_pio_send_data); //end of pio transfer ! @@ -1086,11 +1138,10 @@ void WriteMem_gdrom(u32 Addr, u32 data, u32 sz) } else if (gd_state == gds_pio_get_data) { - pio_buff.data[pio_buff.index]=(u16)data; - pio_buff.index+=1; - if (pio_buff.size==pio_buff.index) + pio_buff.write((u16)data); + if (pio_buff.atEnd()) { - verify(pio_buff.next_state!=gds_pio_get_data); + verify(pio_buff.next_state != gds_pio_get_data); gd_set_state(pio_buff.next_state); } } @@ -1137,7 +1188,7 @@ void WriteMem_gdrom(u32 Addr, u32 data, u32 sz) { if (data != ATA_NOP && data != ATA_SOFT_RESET) verify(gd_state == gds_waitcmd); - ata_cmd.command = (u8)data; + ata_command = (u8)data; gd_set_state(gds_procata); } else @@ -1179,7 +1230,7 @@ static int GDRomschd(int tag, int cycles, int jitter, void *arg) SecNumber.Status = GD_STANDBY; GDStatus.DSC = 1; } - if(!(SB_GDST&1) || !(SB_GDEN &1) || (read_buff.cache_size==0 && read_params.remaining_sectors==0)) + if (!(SB_GDST & 1) || !(SB_GDEN & 1) || (dma_buff.isEmpty() && read_params.remaining_sectors == 0)) return 0; u32 src = SB_GDSTARD; @@ -1194,7 +1245,7 @@ static int GDRomschd(int tag, int cycles, int jitter, void *arg) //if we don't have any more sectors to read if (read_params.remaining_sectors == 0) //make sure we don't underrun the cache :) - len = std::min(len, read_buff.cache_size); + len = std::min(len, dma_buff.getSize()); len = std::min(len, (u32)10240); // do we need to do this for GDROM DMA? @@ -1212,25 +1263,15 @@ static int GDRomschd(int tag, int cycles, int jitter, void *arg) u32 len_backup = len; if(1 == SB_GDDIR) { - while(len) + while (len) { - u32 buff_size =read_buff.cache_size; - if (buff_size==0) - { - verify(read_params.remaining_sectors>0); - //buffer is empty , fill it :) - FillReadBuffer(); - continue; - } + dma_buff.fill(read_params); + // transfer up to len bytes + const u32 buff_size = std::min(dma_buff.getSize(), len); - //transfer up to len bytes - if (buff_size>len) - buff_size=len; - WriteMemBlock_nommu_ptr(src,(u32*)&read_buff.cache[read_buff.cache_index], buff_size); - read_buff.cache_index+=buff_size; - read_buff.cache_size-=buff_size; - src+=buff_size; - len-=buff_size; + WriteMemBlock_nommu_ptr(src, (const u32 *)dma_buff.read(buff_size), buff_size); + src += buff_size; + len -= buff_size; } } else @@ -1246,16 +1287,10 @@ static int GDRomschd(int tag, int cycles, int jitter, void *arg) SB_GDST = 0; asic_RaiseInterrupt(holly_GDROM_DMA); } - //Read ALL sectors - if (read_params.remaining_sectors==0) - { - //And all buffer :p - if (read_buff.cache_size==0) - { - //verify(!SB_GDST&1) -> dc can do multi read dma - gd_set_state(gds_procpacketdone); - } - } + // Read ALL sectors and all buffer + if (read_params.remaining_sectors == 0 && dma_buff.isEmpty()) + //verify(!SB_GDST&1) -> dc can do multi read dma + gd_set_state(gds_procpacketdone); return getGDROMTicks(); } @@ -1327,7 +1362,7 @@ void gdrom_reg_Reset(bool hard) memcpy(GD_HardwareInfo.drive_info, "SE ", sizeof(GD_HardwareInfo.drive_info)); memcpy(GD_HardwareInfo.system_version, "Rev 6.43", sizeof(GD_HardwareInfo.system_version)); memcpy(GD_HardwareInfo.system_date, "990408", sizeof(GD_HardwareInfo.system_date)); - TermDrive(); + gdr::termDrive(); } SB_GDST = 0; SB_GDEN = 0; @@ -1339,9 +1374,9 @@ void gdrom_reg_Reset(bool hard) set_mode_offset = 0; read_params = {}; packet_cmd = {}; - read_buff = {}; - pio_buff = {}; - ata_cmd = {}; + dma_buff.clear(); + pio_buff.clear(); + ata_command = 0; cdda = {}; gd_disk_type = NoDisk; @@ -1372,9 +1407,9 @@ void serialize(Serializer& ser) ser << packet_cmd; ser << set_mode_offset; ser << read_params; - ser << read_buff; - ser << pio_buff; - ser << ata_cmd; + dma_buff.serialize(ser); + pio_buff.serialize(ser); + ser << ata_command; ser << cdda; ser << gd_state; ser << gd_disk_type; @@ -1401,16 +1436,16 @@ void deserialize(Deserializer& deser) deser >> packet_cmd; deser >> set_mode_offset; deser >> read_params; - if (deser.version() >= Deserializer::V17) - deser >> read_buff; - else - { + if (deser.version() >= Deserializer::V17) { + dma_buff.deserialize(deser); + } + else { deser >> packet_cmd; - read_buff.cache_size = 0; + dma_buff.clear(); } - deser >> pio_buff; + pio_buff.deserialize(deser); deser.skip(Deserializer::V44); // set_mode_offset (repeat) - deser >> ata_cmd; + deser >> ata_command; deser >> cdda; deser >> gd_state; deser >> gd_disk_type; diff --git a/core/hw/gdrom/gdromv3.h b/core/hw/gdrom/gdromv3.h index e462a9df9..164c3c9e8 100644 --- a/core/hw/gdrom/gdromv3.h +++ b/core/hw/gdrom/gdromv3.h @@ -164,26 +164,89 @@ struct packet_cmd_t }; }; -//Buffer for sector reads [dma] -struct read_buff_t +class DmaBuffer { - u32 cache_index; - u32 cache_size; - u8 cache[2352 * 32]; +public: + DmaBuffer() { + clear(); + } + bool isEmpty() const { + return size == 0; + } + u32 getSize() const { + return size; + } + void clear() { + size = 0; + index = 0; + } + + // Fill the cache with up to NSECT sectors if empty, using the passed read parameters + void fill(read_params_t& params); + // Return a pointer to the cache and advance the index by len bytes. len *must* be <= getSize() + const u8 *read(u32 len); + void serialize(Serializer& ser) const; + void deserialize(Deserializer& deser); + +private: + static constexpr u32 NSECT = 16; + u32 index; + u32 size; + u8 cache[2352 * NSECT] {}; }; -//pio buffer -struct pio_buff_t +class PioBuffer { +public: + PioBuffer() { + clear(); + } + bool isEmpty() const { + return size == 0; + } + // Returns true if the buffer has reached its capacity and can't be read from or written to + bool atEnd() const { + return index == size; + } + // in bytes + u32 getSize() const { + return size * sizeof(u16); + } + void clear() + { + next_state = gds_waitcmd; + size = 0; + index = 0; + } + u16 read() { + return _data[index++]; + } + const u16 *data() const { + return _data; + } + void write(u16 v) { + _data[index++] = v; + } + // Returns a pointer to fill the buffer and sets its size. Index is reset. + u16 *fill(u32 size) { + resetSize(size); + return &_data[0]; + } + // Sets the buffer capacity and resets the index. + void resetSize(u32 size) { + this->index = 0; + this->size = size / sizeof(u16); + } + void serialize(Serializer& ser) const; + void deserialize(Deserializer& deser); + gd_states next_state; + static constexpr u32 Capacity = 64_KB; + +private: u32 index; u32 size; - u16 data[0x10000>>1]; //64 kb -}; - -struct ata_cmd_t -{ - u8 command; + u16 _data[Capacity / 2] {}; }; struct cdda_t diff --git a/core/hw/maple/maple_cfg.cpp b/core/hw/maple/maple_cfg.cpp index b006a9b20..72245b9ec 100644 --- a/core/hw/maple/maple_cfg.cpp +++ b/core/hw/maple/maple_cfg.cpp @@ -215,10 +215,7 @@ static void mcfg_Create(MapleDeviceType type, u32 bus, u32 port, s32 player_num { delete MapleDevices[bus][port]; maple_device* dev = maple_Create(type); - dev->Setup(maple_GetAddress(bus, port), player_num); - dev->config = new MapleConfigMap(dev); - dev->OnSetup(); - MapleDevices[bus][port] = dev; + dev->Setup(bus, port, player_num); } static void createNaomiDevices() @@ -319,7 +316,8 @@ static void createDreamcastDevices() switch (config::MapleMainDevices[bus]) { case MDT_SegaController: - mcfg_Create(MDT_SegaController, bus, 5); + case MDT_SegaControllerXL: + mcfg_Create(config::MapleMainDevices[bus], bus, 5); if (config::MapleExpansionDevices[bus][0] != MDT_None) mcfg_Create(config::MapleExpansionDevices[bus][0], bus, 0); if (config::MapleExpansionDevices[bus][1] != MDT_None) @@ -404,12 +402,6 @@ void mcfg_CreateDevices() die("Unknown system"); break; } - if (settings.platform.isArcade() && !settings.input.fourPlayerGames) - { - // No known 4-player lightgun/touchscreen game so far - config::CrosshairColor[2].override(0); - config::CrosshairColor[3].override(0); - } vmuDigest(); } diff --git a/core/hw/maple/maple_cfg.h b/core/hw/maple/maple_cfg.h index 2332c7fcf..404d7e86f 100644 --- a/core/hw/maple/maple_cfg.h +++ b/core/hw/maple/maple_cfg.h @@ -22,6 +22,7 @@ enum MapleDeviceType MDT_RacingController = 15, MDT_DenshaDeGoController = 16, MDT_Dreameye = 17, + MDT_SegaControllerXL = 18, MDT_Count }; diff --git a/core/hw/maple/maple_devs.cpp b/core/hw/maple/maple_devs.cpp index 70332305e..33e76b772 100755 --- a/core/hw/maple/maple_devs.cpp +++ b/core/hw/maple/maple_devs.cpp @@ -5,6 +5,7 @@ #include "hw/pvr/spg.h" #include "audio/audiostream.h" #include "oslib/oslib.h" +#include "oslib/storage.h" #include "hw/aica/sgc_if.h" #include "cfg/option.h" #include @@ -31,15 +32,19 @@ const char* maple_densha_controller_name = "TAITO 001 Controller"; const char* maple_sega_brand = "Produced By or Under License From SEGA ENTERPRISES,LTD."; //fill in the info -void maple_device::Setup(u32 port, int playerNum) +void maple_device::Setup(u32 bus, u32 port, int playerNum) { - maple_port = port; - bus_port = maple_GetPort(port); - bus_id = maple_GetBusId(port); + maple_port = (bus << 6) | (1 << port); + bus_port = port; + bus_id = bus; logical_port[0] = 'A' + bus_id; logical_port[1] = bus_port == 5 ? 'x' : '1' + bus_port; logical_port[2] = 0; player_num = playerNum == -1 ? bus_id : playerNum; + + config = new MapleConfigMap(this); + OnSetup(); + MapleDevices[bus][port] = this; } maple_device::~maple_device() { @@ -331,10 +336,11 @@ u8 vmu_default[] = { struct maple_sega_vmu: maple_base { - FILE* file; + FILE *file = nullptr; u8 flash_data[128_KB]; u8 lcd_data[192]; u8 lcd_data_decoded[48*32]; + bool fullSaveNeeded = false; MapleDeviceType get_device_type() override { @@ -360,11 +366,28 @@ struct maple_sega_vmu: maple_base config->SetImage(lcd_data_decoded); break; } + fullSaveNeeded = true; + } + + bool fullSave() + { + if (file == nullptr) + return false; + if (std::fseek(file, 0, SEEK_SET) != 0) { + ERROR_LOG(MAPLE, "VMU %s: I/O error", logical_port); + return false; + } + if (std::fwrite(flash_data, sizeof(flash_data), 1, file) != 1) { + ERROR_LOG(MAPLE, "Failed to write the VMU %s to disk", logical_port); + return false; + } + fullSaveNeeded = false; + return true; } void initializeVmu() { - INFO_LOG(MAPLE, "Initialising empty VMU..."); + INFO_LOG(MAPLE, "Initialising empty VMU %s...", logical_port); uLongf dec_sz = sizeof(flash_data); int rv = uncompress(flash_data, &dec_sz, vmu_default, sizeof(vmu_default)); @@ -372,34 +395,44 @@ struct maple_sega_vmu: maple_base verify(rv == Z_OK); verify(dec_sz == sizeof(flash_data)); - if (file != nullptr) - { - if (std::fwrite(flash_data, sizeof(flash_data), 1, file) != 1) - WARN_LOG(MAPLE, "Failed to write the VMU to disk"); - if (std::fseek(file, 0, SEEK_SET) != 0) - WARN_LOG(MAPLE, "VMU: I/O error"); - } + fullSave(); } void OnSetup() override { memset(flash_data, 0, sizeof(flash_data)); memset(lcd_data, 0, sizeof(lcd_data)); - std::string apath = hostfs::getVmuPath(logical_port); - - file = nowide::fopen(apath.c_str(), "rb+"); - if (file == nullptr) - { - INFO_LOG(MAPLE, "Unable to open VMU save file \"%s\", creating new file", apath.c_str()); - file = nowide::fopen(apath.c_str(), "wb+"); - if (file == nullptr) - ERROR_LOG(MAPLE, "Failed to create VMU save file \"%s\"", apath.c_str()); - initializeVmu(); - } - - if (file != nullptr) - if (std::fread(flash_data, sizeof(flash_data), 1, file) != 1) - WARN_LOG(MAPLE, "Failed to read the VMU from disk"); + + // Load existing vmu file if found + std::string rpath = hostfs::getVmuPath(logical_port, false); + // this might be a storage url + FILE *rfile = hostfs::storage().openFile(rpath, "rb"); + if (rfile == nullptr) { + INFO_LOG(MAPLE, "Unable to open VMU file \"%s\", creating new file", rpath.c_str()); + } + else + { + if (std::fread(flash_data, sizeof(flash_data), 1, rfile) != 1) + WARN_LOG(MAPLE, "Failed to read the VMU file \"%s\" from disk", rpath.c_str()); + std::fclose(rfile); + } + // Open or create the vmu file to save to + std::string wpath = hostfs::getVmuPath(logical_port, true); + file = nowide::fopen(wpath.c_str(), "rb+"); + if (file == nullptr) + { + file = nowide::fopen(wpath.c_str(), "wb+"); + if (file == nullptr) { + ERROR_LOG(MAPLE, "Failed to create VMU save file \"%s\"", wpath.c_str()); + } + else if (rfile != nullptr) + { + // VMU file is being renamed so save it fully now + // and delete the old file + if (fullSave()) + nowide::remove(rpath.c_str()); + } + } u8 sum = 0; for (u32 i = 0; i < sizeof(flash_data); i++) @@ -408,6 +441,7 @@ struct maple_sega_vmu: maple_base if (sum == 0) // This means the existing VMU file is completely empty and needs to be recreated initializeVmu(); + fullSaveNeeded = false; } ~maple_sega_vmu() override @@ -633,17 +667,19 @@ struct maple_sega_vmu: maple_base if (file != nullptr) { - if (std::fseek(file, write_adr, SEEK_SET) != 0 + if (fullSaveNeeded) { + if (!fullSave()) + return MDRE_FileError; + } + else if (std::fseek(file, write_adr, SEEK_SET) != 0 || std::fwrite(&flash_data[write_adr], write_len, 1, file) != 1) { - WARN_LOG(MAPLE, "Failed to save VMU %s: I/O error", logical_port); + ERROR_LOG(MAPLE, "Failed to save VMU %s: I/O error", logical_port); return MDRE_FileError; // I/O error } - std::fflush(file); } - else - { - INFO_LOG(MAPLE, "Failed to save VMU %s data", logical_port); + else { + WARN_LOG(MAPLE, "Failed to save VMU %s data", logical_port); } return MDRS_DeviceReply; } @@ -651,7 +687,7 @@ struct maple_sega_vmu: maple_base case MFID_2_LCD: { DEBUG_LOG(MAPLE, "VMU %s LCD write", logical_port); - r32(); + r32(); // PT, phase, block# rptr(lcd_data,192); u8 white=0xff,black=0x00; @@ -978,7 +1014,15 @@ struct maple_sega_purupuru : maple_base //2 w16(0x0640); // 160 mA - return cmd == MDC_DeviceRequest ? MDRS_DeviceStatus : MDRS_DeviceStatusAll; + if (cmd == MDC_AllStatusReq) + { + const char *extra = "Version 1.000,1998/11/10,315-6211-AH ,Vibration Motor:1,Fm:4 - 30Hz,Pow:7 "; + wptr(extra, strlen(extra)); + return MDRS_DeviceStatusAll; + } + else { + return MDRS_DeviceStatus; + } //get last vibration case MDCF_GetCondition: @@ -1598,6 +1642,51 @@ struct maple_densha_controller: maple_sega_controller } }; +struct FullController : maple_sega_controller +{ + u32 get_capabilities() override + { + // byte 0: 0 0 0 0 0 0 0 0 + // byte 1: 0 0 a5 a4 a3 a2 a1 a0 + // byte 2: R2 L2 D2 U2 D X Y Z + // byte 3: R L D U St A B C + return 0xffff3f00; // 6 axes, all buttons + } + + u16 getButtonState(const PlainJoystickState &pjs) override + { + u32 kcode = pjs.kcode; + mutualExclusion(kcode, DC_DPAD_UP | DC_DPAD_DOWN); + mutualExclusion(kcode, DC_DPAD_LEFT | DC_DPAD_RIGHT); + mutualExclusion(kcode, DC_DPAD2_UP | DC_DPAD2_DOWN); + mutualExclusion(kcode, DC_DPAD2_LEFT | DC_DPAD2_RIGHT); + return kcode; + } + + u32 getAnalogAxis(int index, const PlainJoystickState &pjs) override + { + if (index == 4 || index == 5) + { + // Limit the magnitude of the analog axes to 128 + s8 xaxis = pjs.joy[PJAI_X2] - 128; + s8 yaxis = pjs.joy[PJAI_Y2] - 128; + limit_joystick_magnitude<128>(xaxis, yaxis); + if (index == 4) + return xaxis + 128; + else + return yaxis + 128; + } + return maple_sega_controller::getAnalogAxis(index, pjs); + } + + const char *get_device_name() override { + return "Dreamcast Controller XL"; + } + + MapleDeviceType get_device_type() override { + return MDT_SegaControllerXL; + } +}; // Emulates a 838-14245-92 maple to RS232 converter // wired to a 838-14243 RFID reader/writer (apparently Saxa HW210) @@ -1634,7 +1723,7 @@ struct RFIDReaderWriter : maple_base u32 resp = Dma(command, &buffer_in[1], buffer_in_len - 4, &buffer_out[1], outlen); if (reci & 0x20) - reci |= maple_GetAttachedDevices(maple_GetBusId(reci)); + reci |= maple_GetAttachedDevices(bus_id); verify(u8(outlen / 4) * 4 == outlen); buffer_out[0] = (resp << 0 ) | (reci << 8) | (send << 16) | ((outlen / 4) << 24); @@ -1981,84 +2070,134 @@ const u8 *getRfidCardData(int playerNum) maple_device* maple_Create(MapleDeviceType type) { - maple_device* rv=0; switch(type) { case MDT_SegaController: if (!settings.platform.isAtomiswave()) - rv = new maple_sega_controller(); + return new maple_sega_controller(); else - rv = new maple_atomiswave_controller(); - break; - - case MDT_Microphone: - rv=new maple_microphone(); - break; - - case MDT_SegaVMU: - rv = new maple_sega_vmu(); - break; - - case MDT_PurupuruPack: - rv = new maple_sega_purupuru(); - break; - - case MDT_Keyboard: - rv = new maple_keyboard(); - break; - - case MDT_Mouse: - rv = new maple_mouse(); - break; - + return new maple_atomiswave_controller(); + case MDT_Microphone: return new maple_microphone(); + case MDT_SegaVMU: return new maple_sega_vmu(); + case MDT_PurupuruPack: return new maple_sega_purupuru(); + case MDT_Keyboard: return new maple_keyboard(); + case MDT_Mouse: return new maple_mouse(); case MDT_LightGun: if (!settings.platform.isAtomiswave()) - rv = new maple_lightgun(); + return new maple_lightgun(); else - rv = new atomiswave_lightgun(); - break; + return new atomiswave_lightgun(); + case MDT_NaomiJamma: return new maple_naomi_jamma(); + case MDT_TwinStick: return new maple_sega_twinstick(); + case MDT_AsciiStick: return new maple_ascii_stick(); + case MDT_MaracasController: return new maple_maracas_controller(); + case MDT_FishingController: return new maple_fishing_controller(); + case MDT_PopnMusicController: return new maple_popnmusic_controller(); + case MDT_RacingController: return new maple_racing_controller(); + case MDT_DenshaDeGoController: return new maple_densha_controller(); + case MDT_SegaControllerXL: return new FullController(); + case MDT_RFIDReaderWriter: return new RFIDReaderWriter(); - case MDT_NaomiJamma: - rv = new maple_naomi_jamma(); + default: + ERROR_LOG(MAPLE, "Invalid device type %d", type); + die("Invalid maple device type"); break; + } + return nullptr; +} - case MDT_TwinStick: - rv = new maple_sega_twinstick(); - break; +#if defined(_WIN32) && !defined(TARGET_UWP) && defined(USE_SDL) && !defined(LIBRETRO) +#include "sdl/dreamconn.h" - case MDT_AsciiStick: - rv = new maple_ascii_stick(); - break; +struct DreamConnVmu : public maple_sega_vmu +{ + std::shared_ptr dreamconn; - case MDT_MaracasController: - rv = new maple_maracas_controller(); - break; + DreamConnVmu(std::shared_ptr dreamconn) : dreamconn(dreamconn) { + } - case MDT_FishingController: - rv = new maple_fishing_controller(); - break; + u32 dma(u32 cmd) override + { + if (dma_count_in >= 4) + { + const u32 functionId = *(u32 *)dma_buffer_in; + if ((cmd == MDCF_BlockWrite && functionId == MFID_2_LCD) // LCD screen + || (cmd == MDCF_SetCondition && functionId == MFID_3_Clock)) // Buzzer + { + const MapleMsg *msg = reinterpret_cast(dma_buffer_in - 4); + dreamconn->send(*msg); + } + } + return maple_sega_vmu::dma(cmd); + } - case MDT_PopnMusicController: - rv = new maple_popnmusic_controller(); - break; + void copy(maple_sega_vmu *other) + { + memcpy(flash_data, other->flash_data, sizeof(flash_data)); + memcpy(lcd_data, other->lcd_data, sizeof(lcd_data)); + memcpy(lcd_data_decoded, other->lcd_data_decoded, sizeof(lcd_data_decoded)); + fullSaveNeeded = other->fullSaveNeeded; + } - case MDT_RacingController: - rv = new maple_racing_controller(); - break; + void updateScreen() + { + MapleMsg msg; + msg.command = MDCF_BlockWrite; + msg.destAP = maple_port; + msg.originAP = bus_id << 6; + msg.size = 2 + sizeof(lcd_data) / 4; + *(u32 *)&msg.data[0] = MFID_2_LCD; + *(u32 *)&msg.data[4] = 0; // PT, phase, block# + memcpy(&msg.data[8], lcd_data, sizeof(lcd_data)); + dreamconn->send(msg); + } +}; - case MDT_DenshaDeGoController: - rv = new maple_densha_controller(); - break; +struct DreamConnPurupuru : public maple_sega_purupuru +{ + std::shared_ptr dreamconn; - case MDT_RFIDReaderWriter: - rv = new RFIDReaderWriter(); - break; + DreamConnPurupuru(std::shared_ptr dreamconn) : dreamconn(dreamconn) { + } - default: - ERROR_LOG(MAPLE, "Invalid device type %d", type); - die("Invalid maple device type"); - break; + u32 dma(u32 cmd) override + { + if (cmd == MDCF_BlockWrite || cmd == MDCF_SetCondition) { + const MapleMsg *msg = reinterpret_cast(dma_buffer_in - 4); + dreamconn->send(*msg); + } + return maple_sega_purupuru::dma(cmd); } +}; - return rv; +void createDreamConnDevices(std::shared_ptr dreamconn, bool gameStart) +{ + const int bus = dreamconn->getBus(); + if (dreamconn->hasVmu()) + { + maple_device *dev = MapleDevices[bus][0]; + if (gameStart || (dev != nullptr && dev->get_device_type() == MDT_SegaVMU)) + { + DreamConnVmu *vmu = new DreamConnVmu(dreamconn); + vmu->Setup(bus, 0); + if (!gameStart) { + // if loading a state, copy data from the regular vmu and send a screen update + vmu->copy(static_cast(dev)); + vmu->updateScreen(); + } + delete dev; + } + } + if (dreamconn->hasRumble()) + { + maple_device *dev = MapleDevices[bus][1]; + if (gameStart || (dev != nullptr && dev->get_device_type() == MDT_PurupuruPack)) + { + delete dev; + DreamConnPurupuru *rumble = new DreamConnPurupuru(dreamconn); + rumble->Setup(bus, 1); + } + } } + +#endif diff --git a/core/hw/maple/maple_devs.h b/core/hw/maple/maple_devs.h index 7f97e5af4..89f1e5836 100755 --- a/core/hw/maple/maple_devs.h +++ b/core/hw/maple/maple_devs.h @@ -131,7 +131,7 @@ struct maple_device MapleConfigMap* config; //fill in the info - void Setup(u32 port, int playerNum = -1); + void Setup(u32 bus, u32 port = 5, int playerNum = -1); virtual void OnSetup() {}; virtual ~maple_device(); @@ -246,7 +246,7 @@ struct maple_base: maple_device u32 resp = Dma(command, &buffer_in[1], buffer_in_len - 4, &buffer_out[1], outlen); if (reci & 0x20) - reci |= maple_GetAttachedDevices(maple_GetBusId(reci)); + reci |= maple_GetAttachedDevices(bus_id); verify(u8(outlen / 4) * 4 == outlen); buffer_out[0] = (resp << 0 ) | (send << 8) | (reci << 16) | ((outlen / 4) << 24); diff --git a/core/hw/maple/maple_helper.cpp b/core/hw/maple/maple_helper.cpp index 03c509103..6e683d972 100644 --- a/core/hw/maple/maple_helper.cpp +++ b/core/hw/maple/maple_helper.cpp @@ -1,15 +1,6 @@ #include "maple_helper.h" #include "maple_if.h" -u32 maple_GetPort(u32 addr) -{ - for (int i=0;i<6;i++) - { - if ((1<> 6; -} - -u32 maple_GetPort(u32 addr); u32 maple_GetAttachedDevices(u32 bus); - -//device : 0 .. 4 -> subdevice , 5 -> main device :) -static inline u32 maple_GetAddress(u32 bus, u32 port) -{ - u32 rv = bus << 6; - rv |= 1 << port; - - return rv; -} diff --git a/core/hw/maple/maple_if.cpp b/core/hw/maple/maple_if.cpp index 168edeb35..e446057a9 100644 --- a/core/hw/maple/maple_if.cpp +++ b/core/hw/maple/maple_if.cpp @@ -124,6 +124,14 @@ static void maple_SB_MDSTAR_Write(u32 addr, u32 data) } #endif +static u32 getPort(u32 addr) +{ + for (int i = 0; i < 6; i++) + if ((1 << i) & addr) + return i; + return 5; +} + static void maple_DoDma() { verify(SB_MDEN & 1); @@ -202,8 +210,8 @@ static void maple_DoDma() //Number of additional words in frame u32 inlen = (frame_header >> 24) & 0xFF; - u32 port = maple_GetPort(reci); - u32 bus = maple_GetBusId(reci); + u32 port = getPort(reci); + u32 bus = reci >> 6; if (MapleDevices[bus][5] && MapleDevices[bus][port]) { diff --git a/core/hw/maple/maple_jvs.cpp b/core/hw/maple/maple_jvs.cpp index 284bd16b7..2cd83cc9d 100644 --- a/core/hw/maple/maple_jvs.cpp +++ b/core/hw/maple/maple_jvs.cpp @@ -190,6 +190,15 @@ class jvs_io_board continue; if (keycode & NAOMI_RELOAD_KEY) keycode |= NAOMI_BTN0_KEY; + if (lightgun_as_analog && (keycode & NAOMI_BTN0_KEY)) + { + const MapleInputState& inputState = mapleInputState[player]; + if (inputState.absPos.x < 0 || inputState.absPos.x > 639 + || inputState.absPos.y < 0 || inputState.absPos.y > 479 + || (keycode & NAOMI_RELOAD_KEY)) { + keycode |= NAOMI_BTN1_KEY; // offscreen sensor, not used by deathcox + } + } // P1 mapping (only for P2) if (player == 1) @@ -271,6 +280,7 @@ class jvs_io_board u32 output_count = 0; bool init_in_progress = false; maple_naomi_jamma *parent; + u8 first_player; private: void init_mappings() @@ -302,7 +312,6 @@ class jvs_io_board } u8 node_id; - u8 first_player; std::array cur_mapping; std::array p1_mapping; @@ -1095,15 +1104,16 @@ class jvs_namco_v226_pcb : public jvs_io_board void read_digital_in(const u32 *buttons, u32 *v) override { jvs_io_board::read_digital_in(buttons, v); - for (u32 player = 0; player < player_count; player++) + for (u32 player = first_player; player < first_player + player_count && player < 4; player++) { u8 trigger = mapleInputState[player].halfAxes[PJTI_R] >> 10; + const u32 idx = player - first_player; // Ball button - v[player] = ((trigger & 0x20) << 3) | ((trigger & 0x10) << 5) | ((trigger & 0x08) << 7) + v[idx] = ((trigger & 0x20) << 3) | ((trigger & 0x10) << 5) | ((trigger & 0x08) << 7) | ((trigger & 0x04) << 9) | ((trigger & 0x02) << 11) | ((trigger & 0x01) << 13) // other buttons - | (v[player] & (NAOMI_SERVICE_KEY | NAOMI_TEST_KEY | NAOMI_START_KEY)) - | ((v[player] & NAOMI_BTN0_KEY) >> 4); // remap button4 to button0 (change button) + | (v[idx] & (NAOMI_SERVICE_KEY | NAOMI_TEST_KEY | NAOMI_START_KEY)) + | ((v[idx] & NAOMI_BTN0_KEY) >> 4); // remap button4 to button0 (change button) } } @@ -1120,17 +1130,31 @@ class jvs_namco_v226_pcb : public jvs_io_board return std::min(0xff, 0x80 - axis_y) << 8; } - u16 read_analog_axis(int player_num, int player_axis, bool inverted) override { + u16 read_analog_axis(int player_num, int player_axis, bool inverted) override + { switch (player_axis) { + // P1 case 0: - return read_joystick_x(0); + return read_joystick_x(player_num + 0); case 1: - return read_joystick_y(0); + return read_joystick_y(player_num + 0); + // P2 & P4 case 4: - return read_joystick_x(1); + return read_joystick_x(player_num + 1); case 5: - return read_joystick_y(1); + return read_joystick_y(player_num + 1); + // P3 + case 8: + if (player_num == 0) + return read_joystick_x(player_num + 2); + else + return 0x8000; + case 9: + if (player_num == 0) + return read_joystick_y(player_num + 2); + else + return 0x8000; default: return 0x8000; } diff --git a/core/hw/mem/addrspace.cpp b/core/hw/mem/addrspace.cpp index fde6c74b4..a0e8f1810 100644 --- a/core/hw/mem/addrspace.cpp +++ b/core/hw/mem/addrspace.cpp @@ -380,16 +380,10 @@ bool bm_lockedWrite(u8* address) bool reserve() { - static_assert((sizeof(Sh4RCB) % PAGE_SIZE) == 0, "sizeof(Sh4RCB) not multiple of PAGE_SIZE"); - if (ram_base != nullptr) return true; - // Use vmem only if settings mandate so, and if we have proper exception handlers. -#if !defined(TARGET_NO_EXCEPTIONS) - if (!settings.dynarec.disable_nvmem) - virtmem::init((void**)&ram_base, (void**)&p_sh4rcb, RAM_SIZE_MAX + VRAM_SIZE_MAX + ARAM_SIZE_MAX + elan::ERAM_SIZE_MAX); -#endif + virtmem::init((void**)&ram_base, (void**)&p_sh4rcb, RAM_SIZE_MAX + VRAM_SIZE_MAX + ARAM_SIZE_MAX + elan::ERAM_SIZE_MAX); return true; } diff --git a/core/hw/naomi/gdcartridge.cpp b/core/hw/naomi/gdcartridge.cpp index 5a2b3176a..7c1a7a07a 100644 --- a/core/hw/naomi/gdcartridge.cpp +++ b/core/hw/naomi/gdcartridge.cpp @@ -14,6 +14,14 @@ #include "stdclass.h" #include "emulator.h" #include "oslib/storage.h" +#include "naomi_regs.h" +#include "hw/holly/sb.h" +#include "hw/holly/holly_intc.h" +#include "hw/mem/addrspace.h" +#include "serialize.h" +#include "hw/sh4/sh4_sched.h" +#include "naomi.h" +#include /* @@ -437,7 +445,7 @@ void GDCartridge::find_file(const char *name, const u8 *dir_sector, u32 &file_st void GDCartridge::read_gdrom(Disc *gdrom, u32 sector, u8* dst, u32 count, LoadProgress *progress) { - gdrom->ReadSectors(sector + 150, count, dst, 2048, progress); + gdrom->ReadSectors(sector + 150, count, dst, 2048, false, progress); } void GDCartridge::device_start(LoadProgress *progress, std::vector *digest) @@ -448,6 +456,7 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector *digest) dimm_data = NULL; } dimm_data_size = 0; + loadedSegments.clear(); char name[128]; memset(name,'\0',128); @@ -494,7 +503,6 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector *digest) u8 buffer[2048]; std::string parent = hostfs::storage().getParentPath(settings.content.path); std::string gdrom_path = get_file_basename(settings.content.fileName) + "/" + gdrom_name; - std::unique_ptr gdrom; try { gdrom_path = hostfs::storage().getSubPath(parent, gdrom_path); gdrom = std::unique_ptr(OpenDisc(gdrom_path + ".chd", digest)); @@ -529,7 +537,7 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector *digest) // directory u8 dir_sector[2048]; // find data of file - u32 file_start, file_size; + u32 file_size; if (netpic == 0) { u32 dir = ((buffer[0x2 + 0] << 0) | @@ -589,25 +597,11 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector *digest) if (dimm_data_size != file_rounded_size) memset(dimm_data + file_rounded_size, 0, dimm_data_size - file_rounded_size); - // read encrypted data into dimm_data - u32 sectors = file_rounded_size / 2048; - read_gdrom(gdrom.get(), file_start, dimm_data, sectors, progress); + loadedSegments.resize(dimm_data_size / SEGMENT_SIZE); + std::fill(loadedSegments.begin() + (file_rounded_size + SEGMENT_SIZE - 1) / SEGMENT_SIZE, + loadedSegments.end(), true); - // decrypt loaded data - u32 des_subkeys[32]; des_generate_subkeys(rev64(key), des_subkeys); - - for (u32 i = 0; i < file_rounded_size; i += 8) - { - if ((i & 0xfff) == 0 && progress != nullptr) - { - if (progress->cancelled) - throw LoadCancelledException(); - progress->label = "Decrypting..."; - progress->progress = (float)(i + 8) / file_rounded_size; - } - *(u64 *)(dimm_data + i) = des_encrypt_decrypt(*(u64 *)(dimm_data + i), des_subkeys); - } } if (!dimm_data) @@ -615,6 +609,27 @@ void GDCartridge::device_start(LoadProgress *progress, std::vector *digest) } } +void GDCartridge::loadSegments(u32 offset, u32 size) +{ + const u32 lastSegment = (offset + size - 1) / SEGMENT_SIZE; + for (u32 segment = offset / SEGMENT_SIZE; segment <= lastSegment; segment++) + { + if (loadedSegments[segment]) + continue; + DEBUG_LOG(NAOMI, "Loading segment %d", segment); + // load data + read_gdrom(gdrom.get(), file_start + (segment * SEGMENT_SIZE) / 2048, + dimm_data + segment * SEGMENT_SIZE, + SEGMENT_SIZE / 2048, + nullptr); + // decrypt loaded data + u64 *pData = (u64 *)(dimm_data + segment * SEGMENT_SIZE); + for (u32 i = 0; i < SEGMENT_SIZE; i += 8, pData++) + *pData = des_encrypt_decrypt(*pData, des_subkeys); + loadedSegments[segment] = true; + } +} + void GDCartridge::device_reset() { dimm_cur_address = 0; @@ -628,6 +643,7 @@ void *GDCartridge::GetDmaPtr(u32 &size) } dimm_cur_address = DmaOffset & (dimm_data_size-1); size = std::min(size, dimm_data_size - dimm_cur_address); + loadSegments(dimm_cur_address, size); return dimm_data + dimm_cur_address; } @@ -638,7 +654,241 @@ bool GDCartridge::Read(u32 offset, u32 size, void *dst) *(u32 *)dst = 0; return true; } - u32 addr = offset & (dimm_data_size-1); - memcpy(dst, &dimm_data[addr], std::min(size, dimm_data_size - addr)); + u32 addr = offset & (dimm_data_size - 1); + size = std::min(size, dimm_data_size - addr); + loadSegments(addr, size); + memcpy(dst, &dimm_data[addr], size); return true; } + +GDCartridge::GDCartridge(u32 size) : NaomiCartridge(size) +{ + schedId = sh4_sched_register(0, [](int tag, int sch_cycl, int jitter, void *arg){ + return ((GDCartridge *)arg)->schedCallback(); + }, this); +} + +GDCartridge::~GDCartridge() +{ + free(dimm_data); + sh4_sched_unregister(schedId); +} + +u32 GDCartridge::ReadMem(u32 address, u32 size) +{ + switch (address) + { + case NAOMI_DIMM_COMMAND: + DEBUG_LOG(NAOMI, "DIMM COMMAND read -> %x", dimm_command); + return dimm_command; + case NAOMI_DIMM_OFFSETL: + DEBUG_LOG(NAOMI, "DIMM OFFSETL read -> %x", dimm_offsetl); + return dimm_offsetl; + case NAOMI_DIMM_PARAMETERL: + DEBUG_LOG(NAOMI, "DIMM PARAMETERL read -> %x", dimm_parameterl); + return dimm_parameterl; + case NAOMI_DIMM_PARAMETERH: + DEBUG_LOG(NAOMI, "DIMM PARAMETERH read -> %x", dimm_parameterh); + return dimm_parameterh; + case NAOMI_DIMM_STATUS: + { + u32 rc = DIMM_STATUS & ~(((SB_ISTEXT >> 3) & 1) << 8); + static u32 lastRc; + if (rc != lastRc) + DEBUG_LOG(NAOMI, "DIMM STATUS read -> %x", rc); + lastRc = rc; + return rc; + } + default: + return NaomiCartridge::ReadMem(address, size); + } +} + +void GDCartridge::WriteMem(u32 address, u32 data, u32 size) +{ + switch (address) + { + case NAOMI_DIMM_COMMAND: + dimm_command = data; + DEBUG_LOG(NAOMI, "DIMM COMMAND Write<%d>: %x", size, data); + return; + + case NAOMI_DIMM_OFFSETL: + dimm_offsetl = data; + DEBUG_LOG(NAOMI, "DIMM OFFSETL Write<%d>: %x", size, data); + return; + case NAOMI_DIMM_PARAMETERL: + dimm_parameterl = data; + DEBUG_LOG(NAOMI, "DIMM PARAMETERL Write<%d>: %x", size, data); + return; + case NAOMI_DIMM_PARAMETERH: + dimm_parameterh = data; + DEBUG_LOG(NAOMI, "DIMM PARAMETERH Write<%d>: %x", size, data); + return; + + case NAOMI_DIMM_STATUS: + DEBUG_LOG(NAOMI, "DIMM STATUS Write<%d>: %x", size, data); + if (data & 0x100) + // write 0 seems ignored + asic_CancelInterrupt(holly_EXP_PCI); + if ((data & 1) == 0) + // irq to dimm + process(); + return; + + default: + NaomiCartridge::WriteMem(address, data, size); + return; + } +} + +void GDCartridge::process() +{ + INFO_LOG(NAOMI, "NetDIMM cmd %04x sock %d offset %04x paramh/l %04x %04x", (dimm_command >> 9) & 0x3f, + dimm_command & 0xff, dimm_offsetl, dimm_parameterh, dimm_parameterl); + + int cmdGroup = (dimm_command >> 13) & 3; + int cmd = (dimm_command >> 9) & 0xf; + switch (cmdGroup) + { + case 0: // system commands + systemCmd(cmd); + break; + case 1: // network commands + WARN_LOG(NAOMI, "Network command received cmd %x. Need full NetDIMM?", cmd); + returnToNaomi(true, 0, -1); + break; + default: + WARN_LOG(NAOMI, "Unknown DIMM command group %d cmd %x", cmdGroup, cmd); + returnToNaomi(true, 0, -1); + break; + } +} + +void GDCartridge::returnToNaomi(bool failed, u16 offsetl, u32 parameter) +{ + dimm_command = ((dimm_command & 0x7e00) + 0x400) | (failed ? 0xff : 0x4); + dimm_offsetl = offsetl; + dimm_parameterh = parameter >> 16; + dimm_parameterl = parameter; + verify(((SB_ISTEXT >> 3) & 1) == 0); + asic_RaiseInterrupt(holly_EXP_PCI); +} + +void GDCartridge::systemCmd(int cmd) +{ + switch (cmd) + { + case 0xf: // startup + INFO_LOG(NAOMI, "NetDIMM startup"); + // bit 16,17: dimm0 size (none, 128, 256, 512) + // bit 18,19: dimm1 size + // bit 28: network enabled (network settings appear in bios menu) + // bit 29: set + // bit 30: gd-rom connected + // bit 31: mobile/ppp network? + // (| 30, 70, F0, 1F0, 3F0, 7F0) + // | offset >> 20 (dimm buffers offset @ size - 16MB) + // offset = (64MB << 0-5) - 16MB + // vf4 forces this value to 0f000000 (256MB) if != 1f000000 (512MB) + if (dimm_data_size == 512_MB) + addrspace::write32(0xc01fc04, (3 << 16) | 0x60000000 | (dimm_data_size >> 20)); // dimm board config 1 x 512 MB + else if (dimm_data_size == 256_MB) + addrspace::write32(0xc01fc04, (2 << 16) | 0x60000000 | (dimm_data_size >> 20)); // dimm board config 1 x 256 MB + else + addrspace::write32(0xc01fc04, (1 << 16) | 0x60000000 | (dimm_data_size >> 20)); // dimm board config 1 x 128 MB + addrspace::write32(0xc01fc0c, 0x1020000 | 0x264); // fw version 1.02 + // DIMM board serial Id + { + const u32 *serial = (u32 *)(getGameSerialId() + 0x20); // get only the serial id + addrspace::write32(0xc01fc40, *serial++); + addrspace::write32(0xc01fc44, *serial++); + addrspace::write32(0xc01fc48, *serial++); + addrspace::write32(0xc01fc4c, *serial++); + } + // SET_BASE_ADDRESS(0c000000, 0) + dimm_command = 0x8600; + dimm_offsetl = 0; + dimm_parameterl = 0; + dimm_parameterh = 0x0c00; + asic_RaiseInterrupt(holly_EXP_PCI); + sh4_sched_request(schedId, SH4_MAIN_CLOCK); + + break; + + case 0: // nop + case 1: // control read + case 3: // set base address + case 4: // peek8 + case 5: // peek16 + case 6: // peek32 + case 8: // poke8 + case 9: // poke16 + case 10: // poke32 + // These are callbacks from naomi + INFO_LOG(NAOMI, "System callback command %x", cmd); + break; + + default: + WARN_LOG(NAOMI, "Unknown system command %x", cmd); + break; + } +} + +void GDCartridge::Serialize(Serializer &ser) const +{ + NaomiCartridge::Serialize(ser); + ser << dimm_command; + ser << dimm_offsetl; + ser << dimm_parameterl; + ser << dimm_parameterh; + sh4_sched_serialize(ser, schedId); +} + +void GDCartridge::Deserialize(Deserializer &deser) +{ + NaomiCartridge::Deserialize(deser); + if (deser.version() >= Deserializer::V53) + { + deser >> dimm_command; + deser >> dimm_offsetl; + deser >> dimm_parameterl; + deser >> dimm_parameterh; + sh4_sched_deserialize(deser, schedId); + } +} + +int GDCartridge::schedCallback() +{ + if (SB_ISTEXT & 8) // holly_EXP_PCI + return SH4_MAIN_CLOCK; + + // regularly peek the test request address + peek(0xc01fc08); + asic_RaiseInterrupt(holly_EXP_PCI); + + u32 testRequest = addrspace::read32(0xc01fc08); + if (testRequest & 1) + { + // bios dimm (fake) test + addrspace::write32(0xc01fc08, testRequest & ~1); + bool isMem; + char *p = (char *)addrspace::writeConst(0xc01fd00, isMem, 4); + strcpy(p, "CHECKING DIMM BD"); + p = (char *)addrspace::writeConst(0xc01fd10, isMem, 4); + strcpy(p, "DIMM0 - GOOD"); + p = (char *)addrspace::writeConst(0xc01fd20, isMem, 4); + strcpy(p, "DIMM1 - GOOD"); + p = (char *)addrspace::writeConst(0xc01fd30, isMem, 4); + strcpy(p, "--- COMPLETED---"); + addrspace::write32(0xc01fc0c, 0x0102a264); + } + else if (testRequest != 0) + { + addrspace::write32(0xc01fc08, 0); + addrspace::write32(0xc01fc0c, 0x03170100); + INFO_LOG(NAOMI, "TEST REQUEST %x", testRequest); + } + + return SH4_MAIN_CLOCK; +} diff --git a/core/hw/naomi/gdcartridge.h b/core/hw/naomi/gdcartridge.h index e1c565675..7ec87e756 100644 --- a/core/hw/naomi/gdcartridge.h +++ b/core/hw/naomi/gdcartridge.h @@ -9,38 +9,69 @@ * // copyright-holders:Olivier Galibert * */ - -#ifndef CORE_HW_NAOMI_GDCARTRIDGE_H_ -#define CORE_HW_NAOMI_GDCARTRIDGE_H_ - +#pragma once #include "naomi_cart.h" #include "imgread/common.h" -class GDCartridge: public NaomiCartridge { +class GDCartridge: public NaomiCartridge +{ public: - GDCartridge(u32 size) : NaomiCartridge(size) - { - } - ~GDCartridge() override - { - free(dimm_data); - } - void Init(LoadProgress *progress = nullptr, std::vector *digest = nullptr) override - { + GDCartridge(u32 size); + ~GDCartridge() override; + + void Init(LoadProgress *progress = nullptr, std::vector *digest = nullptr) override { device_start(progress, digest); device_reset(); } void* GetDmaPtr(u32 &size) override; bool Read(u32 offset, u32 size, void* dst) override; + u32 ReadMem(u32 address, u32 size) override; + void WriteMem(u32 address, u32 data, u32 size) override; void SetGDRomName(const char *name, const char *parentName) { this->gdrom_name = name; this->gdrom_parent_name = parentName; } + void Serialize(Serializer &ser) const override; + void Deserialize(Deserializer &deser) override; + protected: + virtual void process(); + virtual int schedCallback(); + void returnToNaomi(bool failed, u16 offsetl, u32 parameter); + + template + void peek(u32 address) + { + static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4); + int size; + switch (sizeof(T)) + { + case 1: + size = 4; + break; + case 2: + size = 5; + break; + case 4: + size = 6; + break; + } + dimm_command = ((address >> 16) & 0x1ff) | (size << 9) | 0x8000; + dimm_offsetl = address & 0xffff; + dimm_parameterl = 0; + dimm_parameterh = 0; + } + u8 *dimm_data = nullptr; u32 dimm_data_size = 0; + u16 dimm_command; + u16 dimm_offsetl; + u16 dimm_parameterl; + u16 dimm_parameterh; + static constexpr u16 DIMM_STATUS = 0x111; + int schedId; private: - enum { FILENAME_LENGTH=24 }; + static constexpr int FILENAME_LENGTH = 24; const char *gdrom_name = nullptr; const char *gdrom_parent_name = nullptr; @@ -60,6 +91,12 @@ class GDCartridge: public NaomiCartridge { static const u32 DES_MASK_TABLE[]; static const u8 DES_ROTATE_TABLE[16]; + std::vector loadedSegments; + static constexpr u32 SEGMENT_SIZE = 16_KB; + std::unique_ptr gdrom; + u32 file_start = 0; + u32 des_subkeys[32]; + void device_start(LoadProgress *progress, std::vector *digest); void device_reset(); void find_file(const char *name, const u8 *dir_sector, u32 &file_start, u32 &file_size); @@ -70,6 +107,6 @@ class GDCartridge: public NaomiCartridge { u64 des_encrypt_decrypt(u64 src, const u32 *des_subkeys); u64 rev64(u64 src); void read_gdrom(Disc *gdrom, u32 sector, u8* dst, u32 count = 1, LoadProgress *progress = nullptr); + void loadSegments(u32 offset, u32 size); + void systemCmd(int cmd); }; - -#endif /* CORE_HW_NAOMI_GDCARTRIDGE_H_ */ diff --git a/core/hw/naomi/m1cartridge.cpp b/core/hw/naomi/m1cartridge.cpp index bd82664ce..e32dd46d5 100644 --- a/core/hw/naomi/m1cartridge.cpp +++ b/core/hw/naomi/m1cartridge.cpp @@ -39,7 +39,6 @@ void M1Cartridge::AdvancePtr(u32 size) has_history = true; buffer_actual_size = 0; } - enc_fill(); } else NaomiCartridge::AdvancePtr(size); @@ -147,11 +146,13 @@ void M1Cartridge::Serialize(Serializer& ser) const void M1Cartridge::Deserialize(Deserializer& deser) { deser >> buffer; + deser.skip(32768 - sizeof(buffer), Deserializer::V52); deser >> dict; deser >> hist; deser >> avail_val; deser >> rom_cur_address; deser >> buffer_actual_size; + buffer_actual_size = std::min(buffer_actual_size, sizeof(buffer)); deser >> avail_bits; deser >> stream_ended; deser >> has_history; diff --git a/core/hw/naomi/m1cartridge.h b/core/hw/naomi/m1cartridge.h index f35497806..1ba034fce 100644 --- a/core/hw/naomi/m1cartridge.h +++ b/core/hw/naomi/m1cartridge.h @@ -26,6 +26,7 @@ class M1Cartridge : public NaomiCartridge { if (encryption) { + enc_fill(); size = std::min(size, (u32)sizeof(buffer)); return buffer; } @@ -48,7 +49,6 @@ class M1Cartridge : public NaomiCartridge //printf("M1 ENCRYPTION ON @ %08x\n", dma_offset); encryption = true; enc_reset(); - enc_fill(); } else encryption = false; @@ -100,7 +100,7 @@ class M1Cartridge : public NaomiCartridge u16 actel_id; - u8 buffer[32768]; + u8 buffer[1024]; u8 dict[111], hist[2]; u64 avail_val; u32 rom_cur_address, buffer_actual_size, avail_bits; diff --git a/core/hw/naomi/m4cartridge.cpp b/core/hw/naomi/m4cartridge.cpp index 20f86ef89..4e1068505 100644 --- a/core/hw/naomi/m4cartridge.cpp +++ b/core/hw/naomi/m4cartridge.cpp @@ -321,8 +321,10 @@ void M4Cartridge::Serialize(Serializer& ser) const void M4Cartridge::Deserialize(Deserializer& deser) { deser >> buffer; + deser.skip(32768 - sizeof(buffer), Deserializer::V52); deser >> rom_cur_address; deser >> buffer_actual_size; + buffer_actual_size = std::min(buffer_actual_size, sizeof(buffer)); deser >> iv; deser >> counter; deser >> encryption; diff --git a/core/hw/naomi/m4cartridge.h b/core/hw/naomi/m4cartridge.h index 7f21974a0..614f273c6 100644 --- a/core/hw/naomi/m4cartridge.h +++ b/core/hw/naomi/m4cartridge.h @@ -69,7 +69,7 @@ class M4Cartridge: public NaomiCartridge { u16 subkey2 = 0; u16 one_round[0x10000]; - u8 buffer[32768]; + u8 buffer[2048]; u32 rom_cur_address, buffer_actual_size; u16 iv; u8 counter; @@ -80,6 +80,8 @@ class M4Cartridge: public NaomiCartridge { void enc_init(); void enc_fill(); u16 decrypt_one_round(u16 word, u16 subkey); + + static_assert(sizeof(RomBootID) <= sizeof(buffer)); }; #endif /* CORE_HW_NAOMI_M4CARTRIDGE_H_ */ diff --git a/core/hw/naomi/naomi.cpp b/core/hw/naomi/naomi.cpp index 797755570..9a6fa8fbd 100644 --- a/core/hw/naomi/naomi.cpp +++ b/core/hw/naomi/naomi.cpp @@ -180,8 +180,9 @@ static void Naomi_DmaStart(u32 addr, u32 data) void Naomi_setDmaDelay() { - if (settings.content.gameId == "FORCE FIVE") - // 7 MB/s + if (settings.platform.isAtomiswave() || settings.content.gameId == "FORCE FIVE" + || settings.content.gameId == "KENJU") + // 7 MB/s for Atomiwave games and conversions dmaXferDelay = 27; else dmaXferDelay = 10; @@ -216,11 +217,20 @@ void naomi_reg_Init() dmaSchedId = sh4_sched_register(0, naomiDmaSched); } +// Sets the full content of the rom board serial eeprom (132 bytes) +// including response to reset and read/write passwords. void setGameSerialId(const u8 *data) { romSerialId.setData(data); } +// Return the protected data from the rom board serial eeprom (112 bytes) +// excluding response to reset and passwords. +const u8 *getGameSerialId() +{ + return romSerialId.getProtectedData(); +} + void naomi_reg_Term() { if (multiboard != nullptr) diff --git a/core/hw/naomi/naomi.h b/core/hw/naomi/naomi.h index dc2ffeae3..4abb1a0e3 100644 --- a/core/hw/naomi/naomi.h +++ b/core/hw/naomi/naomi.h @@ -19,6 +19,7 @@ u16 NaomiBoardIDRead(); u16 NaomiGameIDRead(); void NaomiGameIDWrite(u16 data); void setGameSerialId(const u8 *data); +const u8 *getGameSerialId(); void initDriveSimSerialPipe(); void Naomi_setDmaDelay(); @@ -60,7 +61,7 @@ static inline u32 g2ext_readMem(u32 addr, u32 size) if (multiboard != nullptr) return multiboard->readG2Ext(addr, size); - INFO_LOG(NAOMI, "Unhandled G2 Ext read<%d> at %x", size, addr); + DEBUG_LOG(NAOMI, "Unhandled G2 Ext read<%d> at %x", size, addr); return 0; } @@ -71,5 +72,5 @@ static inline void g2ext_writeMem(u32 addr, u32 data, u32 size) else if (multiboard != nullptr) multiboard->writeG2Ext(addr, size, data); else - INFO_LOG(NAOMI, "Unhandled G2 Ext write<%d> at %x: %x", size, addr, data); + DEBUG_LOG(NAOMI, "Unhandled G2 Ext write<%d> at %x: %x", size, addr, data); } diff --git a/core/hw/naomi/naomi_cart.cpp b/core/hw/naomi/naomi_cart.cpp index 2cc79cb43..a9ee7f746 100644 --- a/core/hw/naomi/naomi_cart.cpp +++ b/core/hw/naomi/naomi_cart.cpp @@ -291,7 +291,9 @@ static void loadMameRom(const std::string& path, const std::string& fileName, Lo case GD: { GDCartridge *gdcart; - if (strncmp(game->name, "vf4", 3) == 0) + if (strncmp(game->name, "vf4", 3) == 0 + || strcmp(game->name, "mj1") == 0 + || strncmp(game->name, "wccf", 4) == 0) gdcart = new NetDimm(game->size); else gdcart = new GDCartridge(game->size); @@ -1060,7 +1062,9 @@ void NaomiCartridge::WriteMem(u32 address, u32 data, u32 size) } if (multiboard != nullptr) multiboard->writeG1(address, size, data); - else + else if (address != NAOMI_MBOARD_DATA_addr + && address != NAOMI_MBOARD_OFFSET_addr + && address != NAOMI_MBOARD_STATUS_addr) DEBUG_LOG(NAOMI, "naomiCart::WriteMem<%d>: unknown %08x <= %x", size, address, data); } diff --git a/core/hw/naomi/naomi_cart.h b/core/hw/naomi/naomi_cart.h index 350e0e0c8..ea7440c2a 100644 --- a/core/hw/naomi/naomi_cart.h +++ b/core/hw/naomi/naomi_cart.h @@ -171,7 +171,7 @@ struct AxisDescriptor struct InputDescriptors { ButtonDescriptor buttons[18]; - AxisDescriptor axes[8]; + AxisDescriptor axes[16]; }; extern InputDescriptors *NaomiGameInputs; diff --git a/core/hw/naomi/naomi_roms.cpp b/core/hw/naomi/naomi_roms.cpp index 799df9064..d8a0fdece 100644 --- a/core/hw/naomi/naomi_roms.cpp +++ b/core/hw/naomi/naomi_roms.cpp @@ -142,6 +142,43 @@ const BIOS_t BIOS[] = { 3, "epr-21576h_multi.ic27", 0x000000, 0x200000, 0xcce01f1f }, } }, + { + "naomidev", + { + //ROM_SYSTEM_BIOS( 21, "bios21", "Set4 Dev BIOS" ) + //{ 0, "boot_rom_64b8.ic606", 0x000000, 0x080000, 0x7a50fab9 }, + //ROM_SYSTEM_BIOS( 22, "bios22", "Dev BIOS v1.10" ) + { 0, "develop110.ic27", 0x000000, 0x200000, 0xde7cfdb0 }, + //ROM_SYSTEM_BIOS( 23, "bios23", "Dev BIOS (Nov 1998)" ) + //{ 0, "develop.ic27", 0x000000, 0x200000, 0x309a196a }, + }, + "naomi", + }, + { + "naomigd", + { + //ROM_SYSTEM_BIOS( 2, "bios2", "epr-21576h (Japan)" ) + { 0, "epr-21576h.ic27", 0x000000, 0x200000, 0xd4895685 }, + //ROM_SYSTEM_BIOS( 1, "bios1", "epr-21576g (Japan)" ) + { 0, "epr-21576g.ic27", 0x000000, 0x200000, 0xd2a1c6bf }, + //ROM_SYSTEM_BIOS( 0, "bios0", "epr-21576e (Japan)" ) + { 0, "epr-21576e.ic27", 0x000000, 0x200000, 0x08c0add7 }, + + //ROM_SYSTEM_BIOS( 3, "bios3", "epr-21578h (Export)" ) + { 2, "epr-21578h.ic27", 0x000000, 0x200000, 0x7b452946 }, + //ROM_SYSTEM_BIOS( 4, "bios4", "epr-21578g (Export)" ) + { 2, "epr-21578g.ic27", 0x000000, 0x200000, 0x55413214 }, + //ROM_SYSTEM_BIOS( 5, "bios5", "epr-21578e (Export)" ) + { 2, "epr-21578e.ic27", 0x000000, 0x200000, 0x087f09a3 }, + + //ROM_SYSTEM_BIOS( 6, "bios6", "epr-21577h (USA)" ) + { 1, "epr-21577h.ic27", 0x000000, 0x200000, 0xfdf17452 }, + //ROM_SYSTEM_BIOS( 7, "bios7", "epr-21577g (USA)" ) + { 1, "epr-21577g.ic27", 0x000000, 0x200000, 0x25f64af7 }, + //ROM_SYSTEM_BIOS( 8, "bios8", "epr-21577e (USA)" ) + { 1, "epr-21577e.ic27", 0x000000, 0x200000, 0xcf36e97b }, + }, + }, { "naomi2", { @@ -205,7 +242,7 @@ const Game Games[] = "Giant Gram 2000", 0x0b000000, 0x7f805c3f, - NULL, + "naomi", M1, ROT0, { @@ -238,7 +275,7 @@ const Game Games[] = // Kick '4' Cash (Export) { "kick4csh", - NULL, + nullptr, "Kick '4' Cash (Export)", 0x9000000, 0x820857c9, @@ -268,25 +305,22 @@ const Game Games[] = //ROM_REGION(0x200, "some_eeprom", 0) //ROM_LOAD( "25lc040.ic13s", 0, 0x200, CRC(1576366a) SHA1(3e8bf3dbc8a248a6863242b78d5c6e53a869e951) ) - // TODO Need emulation of 837-14438 board on serial port //ROM_REGION(0x220000, "hopper_board", 0) //ROM_LOAD( "fpr-24150.ic6", 0x0000000, 0x200000, CRC(3845c34c) SHA1(027b17bac64482ee152773d5fab30fcbc6e2bcb7) ) // SH4 code //ROM_LOAD( "6372a.ic3", 0x0200000, 0x020000, CRC(f30839ad) SHA1(ea1a32c4da1ed9745300bcdd7964a7c0964e3221) ) // FPGA config - - { NULL, 0, 0 }, }, - NULL, - NULL, + nullptr, + &kick4csh_inputs, kick4csh_eeprom_dump }, // Marvel vs. Capcom 2 New Age of Heroes (Export, Korea, Rev A) { "mvsc2", - NULL, + nullptr, "Marvel vs. Capcom 2 New Age of Heroes (Export, Korea)", 0x08800000, 0xc18b6e7c, - NULL, + "naomi", M1, ROT0, { @@ -314,21 +348,19 @@ const Game Games[] = //ROM_REGION(0x200, "some_eeprom", 0) //ROM_LOAD( "25lc040.ic13s", 0x000000, 0x200, CRC(dc449637) SHA1(6cab09f61be1498271a36bff6a114a4eeeb00e1a) ) - - { NULL, 0, 0 }, }, - NULL, + nullptr, &mvsc2_inputs, mvsc2_eeprom_dump, }, // Mushiking The King Of Beetle (2K3 2ND, World) { "mushike", - NULL, + nullptr, "Mushiking The King Of Beetle (2K3 2ND Ver. 1.003-, World)", 0x4000000, 0x3892fb3a, - NULL, + "naomi", M1, ROT0, { @@ -351,9 +383,9 @@ const Game Games[] = // note: this dump from "empty/dead" Management Chip with no game run count left //ROM_REGION( 0x80, "rf_tag", 0 ) //{ "mushi_type1.bin", 0, 0x80, CRC(8f36572b) SHA1(87e00e56d07a961e9180c7da02e35f7fd216dbae) ) - - { NULL, 0, 0 }, - } + }, + nullptr, + &mushik_inputs, }, // Quiz Ah Megamisama (JPN, USA, EXP, KOR, AUS) { @@ -383,7 +415,6 @@ const Game Games[] = { "mpr-23224.ic30s", 0x7000002, 0x0800000, 0x0000000, InterleavedWord }, { "mpr-23225.ic31", 0x8000000, 0x0800000, 0x0000000, InterleavedWord }, { "mpr-23226.ic32s", 0x8000002, 0x0800000, 0x0000000, InterleavedWord }, - { NULL, 0, 0 }, } }, // Shootout Pool @@ -497,11 +528,9 @@ const Game Games[] = //ROM_REGION(0x200, "some_eeprom", 0) //ROM_LOAD( "25lc040.ic13s", 0, 0x200, CRC(6291605c) SHA1(44f757da4814b08108d1a4f431c9a39c38acecb2) ) - - { NULL, 0, 0 }, }, nullptr, - nullptr, + &service_btns_inputs, tduno2_eeprom_dump, }, // Virtua Tennis 2 / Power Smash 2 (Rev A) @@ -535,7 +564,6 @@ const Game Games[] = { "mpr-22323.ic33", 0x9000000, 0x800000, 0x0000000, InterleavedWord }, { "mpr-22324.ic34s", 0x9000002, 0x800000, 0x0000000, InterleavedWord }, { "copy", 0x0400000, 0xc00000, 0x0000000, Copy, 0x1000000 }, // changed - { NULL, 0, 0 }, }, nullptr, &shot12_inputs @@ -612,7 +640,7 @@ const Game Games[] = "Mushiking The King Of Beetle (2K3 2ND Ver. 1.002-, World)", 0x04000000, 0x3892fb3a, - NULL, + "naomi", M1, ROT0, { @@ -635,9 +663,9 @@ const Game Games[] = // note: this dump from "empty/dead" Management Chip with no game run count left //ROM_REGION( 0x80, "rf_tag", 0 ) //{ "mushi_type1.bin", 0, 0x80, CRC(8f36572b) SHA1(87e00e56d07a961e9180c7da02e35f7fd216dbae) ) - - { NULL, 0, 0 }, - } + }, + nullptr, + &mushik_inputs, }, // Mushiking The King Of Beetle (2K3 2ND Ver. 1.000-, Korea) { @@ -646,7 +674,7 @@ const Game Games[] = "Mushiking The King Of Beetle (2K3 2ND Ver. 1.000-, Korea)", 0x04000000, 0x3892fb3a, - nullptr, + "naomi", M1, ROT0, { @@ -658,7 +686,9 @@ const Game Games[] = { "opr-24270.ic21s", 0x3000000, 0x0800000, 0x02a513ad, InterleavedWord }, { "opr-24271.ic22", 0x3000002, 0x0800000, 0x7e5c745c, InterleavedWord }, { "copy", 0x0400000, 0x0c00000, 0, Copy, 0x1000000 }, - } + }, + nullptr, + &mushik_inputs, }, // Mushiking The King Of Beetle (2K3 2ND Ver. 1.000-, China) { @@ -667,13 +697,15 @@ const Game Games[] = "Mushiking The King Of Beetle (2K3 2ND Ver. 1.000-, China)", 0x04000000, 0x5501, - nullptr, + "naomi", M4, ROT0, { { "ic8.bin", 0x00000000, 0x04000000, 0x5edc61fb }, { "317-0437-com.ic3", 0, 0x800, 0x3b6fcee8, Key }, - } + }, + nullptr, + &mushik_inputs, }, // Mushiking The King Of Beetles - Mushiking II / III / III+ (Ver. 1.000-) (Korea) // require rev.H Korea BIOS, not dumped @@ -683,14 +715,16 @@ const Game Games[] = "Mushiking The King Of Beetles - Mushiking II / III / III+ (Ver. 1.000-) (Korea)", 0x08000000, 0x5502, - nullptr, + "naomi", M4, ROT0, { { "fpr-24355.ic8", 0x00000000, 0x04000000, 0x8bd89229 }, { "fpr-24356.ic9", 0x04000000, 0x04000000, 0xed649c81 }, { "317-0437-com.ic3", 0, 0x800, 0x3b6fcee8, Key }, - } + }, + nullptr, + &mushik_inputs, }, // Naomi M2/M3 Roms // 18 Wheeler (deluxe) (Rev A) @@ -725,7 +759,6 @@ const Game Games[] = { "mpr-22181.ic18s", 0x9000000, 0x800000 }, { "mpr-22182.ic19s", 0x9800000, 0x800000 }, { "mpr-22183.ic20s", 0xa000000, 0x800000 }, - { NULL, 0, 0 }, }, NULL, &_18wheelr_inputs, @@ -777,8 +810,6 @@ const Game Games[] = // 18 Wheeler motor controller 838-13992, code is for a TMPZ84C015 which is Z80 compatible //ROM_REGION( 0x10000, "motorio", ROMREGION_ERASEFF) //ROM_LOAD( "epr-23000.ic8", 0x000000, 0x010000, CRC(e3b162f7) SHA1(52c7ad759c3c4a3148764e14d77ba5006bc8af48) ) - - { NULL, 0, 0 }, }, NULL, &_18wheelr_inputs, @@ -830,8 +861,6 @@ const Game Games[] = // 18 Wheeler motor controller 838-13992, code is for a TMPZ84C015 which is Z80 compatible //ROM_REGION( 0x10000, "motorio", ROMREGION_ERASEFF) //ROM_LOAD( "epr-23000.ic8", 0x000000, 0x010000, CRC(e3b162f7) SHA1(52c7ad759c3c4a3148764e14d77ba5006bc8af48) ) - - { NULL, 0, 0 }, }, NULL, &_18wheelr_inputs, @@ -880,8 +909,6 @@ const Game Games[] = // 18 Wheeler motor controller 838-13992, code is for a TMPZ84C015 which is Z80 compatible //ROM_REGION( 0x10000, "motorio", ROMREGION_ERASEFF) //ROM_LOAD( "epr-23000.ic8", 0x000000, 0x010000, CRC(e3b162f7) SHA1(52c7ad759c3c4a3148764e14d77ba5006bc8af48) ) - - { NULL, 0, 0 }, }, NULL, &_18wheelr_inputs, @@ -929,8 +956,6 @@ const Game Games[] = // 840-0023 2000 317-0273-COM Naomi //ROM_PARAMETER( ":rom_board:segam2crypt:key", "2807cf54" ) - - { NULL, 0, 0 }, }, NULL, &_18wheelr_inputs, // no issue with wheel range on this version @@ -991,7 +1016,6 @@ const Game Games[] = { "mpr-21736.ic9", 0x4800000, 0x800000 }, { "mpr-21737.ic10", 0x5000000, 0x800000 }, { "mpr-21738.ic11", 0x5800000, 0x800000 }, - { NULL, 0, 0 }, }, NULL, &alpilot_inputs, @@ -1015,7 +1039,6 @@ const Game Games[] = { "mpr-23583.ic3", 0x2800000, 0x1000000 }, { "mpr-23584.ic4", 0x3800000, 0x1000000 }, { "mpr-23585.ic5", 0x4800000, 0x1000000 }, - { NULL, 0, 0 }, }, NULL, &alienfnt_inputs, @@ -1038,7 +1061,6 @@ const Game Games[] = { "mpr-23583.ic3", 0x2800000, 0x1000000 }, { "mpr-23584.ic4", 0x3800000, 0x1000000 }, { "mpr-23585.ic5", 0x4800000, 0x1000000 }, - { NULL, 0, 0 }, }, NULL, &alienfnt_inputs, @@ -1211,7 +1233,6 @@ const Game Games[] = { "mpr-21681.ic13s", 0x6800000, 0x800000 }, { "mpr-21682.ic14s", 0x7000000, 0x800000 }, { "mpr-21683.ic15s", 0x7800000, 0x800000 }, - { NULL, 0, 0 }, }, NULL, &crzytaxi_inputs, @@ -1237,9 +1258,9 @@ const Game Games[] = { "mpr-23425.ic6", 0x3000000, 0x0800000 }, { "mpr-23426.ic7", 0x3800000, 0x0800000 }, { "mpr-23427.ic8", 0x4000000, 0x0800000 }, - { NULL, 0, 0 }, - } - // SMASH, JUMP, udlr + }, + nullptr, + &csmash_inputs, }, // Cosmic Smash { @@ -1261,8 +1282,9 @@ const Game Games[] = { "mpr-23425.ic6", 0x3000000, 0x0800000 }, { "mpr-23426.ic7", 0x3800000, 0x0800000 }, { "mpr-23427.ic8", 0x4000000, 0x0800000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &csmash_inputs, }, // Cannon Spike / Gun Spike { @@ -1288,7 +1310,6 @@ const Game Games[] = { "mpr-23207.ic10", 0x5000000, 0x0800000 }, { "mpr-23208.ic11", 0x5800000, 0x0800000 }, { "mpr-23209.ic12s", 0x6000000, 0x0800000 }, - { NULL, 0, 0 }, }, NULL, &cspike_inputs @@ -1315,10 +1336,9 @@ const Game Games[] = { "mpr-23521.ic8", 0x4000000, 0x0800000 }, { "mpr-23522.ic9", 0x4800000, 0x0800000 }, { "mpr-23523.ic10", 0x5000000, 0x0800000 }, - { NULL, 0, 0 }, }, NULL, - &trigger_inputs + &lightgun_inputs }, // Death Crimson OX (USA) { @@ -1342,10 +1362,9 @@ const Game Games[] = { "mpr-23521.ic8", 0x4000000, 0x0800000, 0xcf8674b8 }, { "mpr-23522.ic9", 0x4800000, 0x0800000, 0x7ae6716e }, { "mpr-23523.ic10",0x5000000, 0x0800000, 0xc91efb67 }, - }, NULL, - &trigger_inputs + &lightgun_inputs }, // Death Crimson OX { @@ -1369,10 +1388,9 @@ const Game Games[] = { "mpr-23521.ic8", 0x4000000, 0x0800000 }, { "mpr-23522.ic9", 0x4800000, 0x0800000 }, { "mpr-23523.ic10", 0x5000000, 0x0800000 }, - { NULL, 0, 0 }, }, NULL, - &trigger_inputs + &lightgun_inputs }, // Derby Owners Club 2000 (Rev A) { @@ -1756,7 +1774,6 @@ const Game Games[] = { "mpr-22138.ic17s", 0x8800000, 0x0800000 }, { "mpr-22139.ic18s", 0x9000000, 0x0800000 }, { "mpr-22140.ic19s", 0x9800000, 0x0800000 }, - { NULL, 0, 0 }, }, nullptr, &wsbb_inputs, @@ -1795,7 +1812,6 @@ const Game Games[] = { "mpr-21572.ic19s", 0x9800000, 0x0800000 }, { "mpr-21573.ic20s", 0xa000000, 0x0800000 }, { "mpr-21574.ic21s", 0xa800000, 0x0800000 }, - { NULL, 0, 0 }, }, nullptr, &wsbb_inputs, @@ -1839,8 +1855,6 @@ const Game Games[] = //ROM_REGION( 0x10000, "drivebd", 0 ) // drive board ROM //ROM_LOAD( "epr-21867p.bin", 0x000000, 0x010000, CRC(6143b911) SHA1(360ebc53696da7a29e6404376c82947563274835) ) // prototype preview //ROM_LOAD( "epr-21867.bin", 0x000000, 0x010000, CRC(4f93a2a0) SHA1(875907e7fcfc44850e2c60c12268ac61c742f217) ) - - { NULL, 0, 0 }, }, nullptr, &f355_inputs, @@ -1880,7 +1894,6 @@ const Game Games[] = { "mpr-22845.ic19s", 0x9800000, 0x800000, 0x3327aed1 }, { "mpr-22846.ic20s", 0xa000000, 0x800000, 0xd4148f39 }, { "mpr-22847.ic21s", 0xa800000, 0x800000, 0x955ad42e }, - { NULL, 0, 0 }, }, nullptr, &f355_inputs, @@ -1920,7 +1933,6 @@ const Game Games[] = { "rom19.ic19s", 0x9800000, 0x800000, 0x3327aed1 }, { "rom20.ic20s", 0xa000000, 0x800000, 0xd4148f39 }, { "rom21.ic21s", 0xa800000, 0x800000, 0x955ad42e }, - { NULL, 0, 0 }, }, nullptr, &f355_inputs, @@ -1961,7 +1973,6 @@ const Game Games[] = { "mpr-23396.ic19s", 0x9800000, 0x800000 }, { "mpr-23397.ic20s", 0xa000000, 0x800000 }, { "mpr-23398.ic21s", 0xa800000, 0x800000 }, - { NULL, 0, 0 }, }, nullptr, &f355_inputs, @@ -1993,7 +2004,6 @@ const Game Games[] = { "mpr-21829.ic9", 0x4800000, 0x0800000 }, { "mpr-21830.ic10", 0x5000000, 0x0800000 }, { "mpr-21831.ic11", 0x5800000, 0x0800000 }, - { NULL, 0, 0 }, }, nullptr, &giant_gram_inputs, @@ -2024,7 +2034,6 @@ const Game Games[] = { "mpr-23353.ic12s", 0x6000000, 0x0800000 }, { "mpr-23354.ic13s", 0x6800000, 0x0800000 }, { "mpr-23355.ic14s", 0x7000000, 0x0800000 }, - { NULL, 0, 0 }, }, nullptr, &ggx_inputs, @@ -2052,7 +2061,6 @@ const Game Games[] = { "mpr-23635.ic8", 0x7800000, 0x1000000 }, { "mpr-23636.ic9", 0x8800000, 0x1000000 }, { "mpr-23637.ic10", 0x9800000, 0x1000000 }, - { NULL, 0, 0 }, }, NULL, &shot1234_inputs, @@ -2084,7 +2092,6 @@ const Game Games[] = { "bhf1ma13.6n", 0xd000000, 0x1000000 }, { "bhf1ma14.6m", 0xe000000, 0x1000000 }, { "bhf1ma15.6l", 0xf000000, 0x1000000 }, - { NULL, 0, 0 }, }, NULL, &gunsur2_inputs, @@ -2116,7 +2123,6 @@ const Game Games[] = { "bhf1ma13.6n", 0xd000000, 0x1000000 }, { "bhf1ma14.6m", 0xe000000, 0x1000000 }, { "bhf1ma15.6l", 0xf000000, 0x1000000 }, - { NULL, 0, 0 }, }, NULL, &gunsur2_inputs, @@ -2140,7 +2146,6 @@ const Game Games[] = { "mpr-22273.ic3", 0x2800000, 0x1000000 }, { "mpr-22274.ic4", 0x3800000, 0x1000000 }, { "mpr-22275.ic5", 0x4800000, 0x1000000 }, - { NULL, 0, 0 }, }, nullptr, &shot12_inputs, @@ -2168,7 +2173,6 @@ const Game Games[] = { "mpr-23713.ic9", 0x4800000, 0x0800000 }, { "mpr-23714.ic10", 0x5000000, 0x0800000 }, { "mpr-23715.ic11", 0x5800000, 0x0800000 }, - { NULL, 0, 0 }, }, NULL, &hmgeo_inputs, @@ -2207,10 +2211,9 @@ const Game Games[] = { "mpr-21403.ic18s", 0x9000000, 0x800000 }, { "mpr-21404.ic19s", 0x9800000, 0x800000 }, { "mpr-21405.ic20s", 0xa000000, 0x800000 }, - { NULL, 0, 0 }, }, nullptr, - nullptr, + &lightgun_inputs, // no free play with eeprom }, // The House of the Dead 2 @@ -2246,10 +2249,9 @@ const Game Games[] = { "mpr-21403.ic18s", 0x9000000, 0x800000 }, { "mpr-21404.ic19s", 0x9800000, 0x800000 }, { "mpr-21405.ic20s", 0xa000000, 0x800000 }, - { NULL, 0, 0 }, }, nullptr, - nullptr, + &lightgun_inputs, // no free play with eeprom }, // The House of the Dead 2 (Export) @@ -2285,10 +2287,9 @@ const Game Games[] = { "mpr-21403.ic18s", 0x9000000, 0x800000 }, { "mpr-21404.ic19s", 0x9800000, 0x800000 }, { "mpr-21405.ic20s", 0xa000000, 0x800000 }, - { NULL, 0, 0 }, }, nullptr, - nullptr, + &lightgun_inputs, // no free play with eeprom }, // The House of the Dead 2 (prototype) @@ -2327,7 +2328,7 @@ const Game Games[] = { "rom21.ic21s", 0xa800000, 0x800000, 0x256603d7 }, }, nullptr, - nullptr, + &lightgun_inputs, // no free play with eeprom }, // Inu No Osanpo / Dog Walking (Japan, Export, Rev A) @@ -2358,7 +2359,6 @@ const Game Games[] = { "rom14.ic14s", 0x7000000, 0x800000 }, { "rom15.ic15s", 0x7800000, 0x800000 }, { "rom16.ic16s", 0x8000000, 0x800000 }, - { NULL, 0, 0 }, } }, // Jambo! Safari (Rev A) @@ -2381,7 +2381,6 @@ const Game Games[] = { "mpr-22823.ic6", 0x3000000, 0x800000 }, { "mpr-22824.ic7", 0x3800000, 0x800000 }, { "mpr-22825.ic8", 0x4000000, 0x800000 }, - { NULL, 0, 0 }, }, NULL, &jambo_inputs @@ -2414,7 +2413,9 @@ const Game Games[] = { "mpr-22990.ic13s", 0x6800000, 0x800000 }, { "mpr-22991.ic14s", 0x7000000, 0x800000 }, { "mpr-22992.ic15s", 0x7800000, 0x800000 }, - } + }, + nullptr, + nullptr, // TODO }, // Mazan: Flash of the Blade (MAZ2 Ver. A) { @@ -2438,8 +2439,7 @@ const Game Games[] = { "maz1ma8.4d", 0x8000000, 0x1000000, 0xd46c9f40 }, }, nullptr, - // ENTER, START BUTTON - nullptr, + &mazan_inputs, // no free play with eeprom }, // Mazan: Flash of the Blade (US, MAZ3 Ver.A) @@ -2464,7 +2464,7 @@ const Game Games[] = { "maz1ma8.4d", 0x8000000, 0x1000000, 0xd46c9f40 }, }, nullptr, - nullptr, + &mazan_inputs, // no free play with eeprom }, // Mazan: Flash of the Blade (Japan, MAZ1 Ver.A) @@ -2489,17 +2489,17 @@ const Game Games[] = { "maz1ma8.4d", 0x8000000, 0x1000000, 0xd46c9f40 }, }, nullptr, - nullptr, + &mazan_inputs, // no free play with eeprom }, // Mushiking The King Of Beetles 2004 Second (Japan) { "mushi2k4", - NULL, + nullptr, "Mushiking The King Of Beetles 2004 Second (Japan)", 0x5800000, 0xffffffff, // not populated - NULL, + "naomi", M2, ROT0, { @@ -2509,17 +2509,18 @@ const Game Games[] = { "mpr-24244.ic3", 0x02800000, 0x01000000 }, { "mpr-24245.ic4", 0x03800000, 0x01000000 }, { "mpr-24246.ic5", 0x04800000, 0x01000000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &mushik_inputs, }, // Mushiking The King Of Beetles 2005 First (Japan) { "mushi2k5", - NULL, + nullptr, "Mushiking The King Of Beetles 2005 First (Japan)", 0x7800000, 0xffffffff, // not populated - NULL, + "naomi", M2, ROT0, { @@ -2531,8 +2532,9 @@ const Game Games[] = { "mpr-24280.ic5", 0x4800000, 0x1000000, 0x00000000 }, { "mpr-24281.ic6", 0x5800000, 0x1000000, 0x00000000 }, { "mpr-24282.ic7", 0x6800000, 0x1000000, 0x00000000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &mushik_inputs, }, // Mushiking The King Of Beetle (MUSHIUSA '04 1ST, Prototype) { @@ -2541,7 +2543,7 @@ const Game Games[] = "Mushiking The King Of Beetle (MUSHIUSA '04 1ST, Prototype)", 0x07800000, 0xffffffff, // not populated - NULL, + "naomi", M2, ROT0, { @@ -2552,8 +2554,9 @@ const Game Games[] = { "rom5.ic5s", 0x2800000, 0x800000, 0x7076a50e }, { "rom6.ic6s", 0x3000000, 0x800000, 0xd7143066 }, { "rom7.ic7s", 0x3800000, 0x800000, 0x98839bab }, - { NULL, 0, 0 }, - } + }, + nullptr, + &mushik_inputs, }, // Marvel vs. Capcom 2 New Age of Heroes (USA, Rev A) { @@ -2584,7 +2587,6 @@ const Game Games[] = //ROM_REGION(0x84, "some_eeprom", 0) //ROM_LOAD("sflash.ic37", 0x000000, 0x000084, CRC(37a66f3c) SHA1(df6cd2cdc2813caa5da4dc9f171998485bcbdc44)) - { NULL, 0, 0 }, }, NULL, &mvsc2_inputs, @@ -2615,7 +2617,6 @@ const Game Games[] = { "nja1ma10.4b", 0xa000000, 0x1000000 }, //ROM_REGION( 0x20000, "jyu_io", 0 ) // H8/3334-based I/O board ROM, eventually should be separated out //ROM_LOAD( "jyu1_prg0a.ic3", 0x000000, 0x020000, CRC(aec4dbc1) SHA1(bddd4f345baf7f594998a39c09da18b3834f0ac2) ) - { NULL, 0, 0 }, }, NULL, &ninjaslt_inputs, @@ -2647,7 +2648,6 @@ const Game Games[] = //ROM_REGION( 0x20000, "jyu_io", 0 ) // H8/3334-based I/O board ROM, eventually should be separated out //ROM_LOAD( "jyu1_prg0a.ic3", 0x000000, 0x020000, CRC(aec4dbc1) SHA1(bddd4f345baf7f594998a39c09da18b3834f0ac2) ) - { NULL, 0, 0 }, }, NULL, &ninjaslt_inputs, @@ -2679,8 +2679,6 @@ const Game Games[] = //ROM_REGION( 0x20000, "jyu_io", 0 ) // H8/3334-based I/O board ROM, eventually should be separated out //ROM_LOAD( "jyu1_prg0a.ic3", 0x000000, 0x020000, CRC(aec4dbc1) SHA1(bddd4f345baf7f594998a39c09da18b3834f0ac2) ) - - { NULL, 0, 0 }, }, NULL, &ninjaslt_inputs, @@ -2712,8 +2710,6 @@ const Game Games[] = //ROM_REGION( 0x20000, "jyu_io", 0 ) // H8/3334-based I/O board ROM, eventually should be separated out //ROM_LOAD( "jyu1_prg0a.ic3", 0x000000, 0x020000, CRC(aec4dbc1) SHA1(bddd4f345baf7f594998a39c09da18b3834f0ac2) ) - - { NULL, 0, 0 }, }, NULL, &ninjaslt_inputs, @@ -2736,7 +2732,6 @@ const Game Games[] = { "mpr-24056.ic3", 0x2800000, 0x1000000 }, { "mpr-24057.ic4", 0x3800000, 0x1000000 }, { "mpr-24058.ic5", 0x4800000, 0x1000000 }, - { NULL, 0, 0 }, } }, // OutTrigger (JPN, USA, EXP, KOR, AUS) @@ -2772,12 +2767,9 @@ const Game Games[] = { "mpr-22160.ic19s",0x9800000, 0x0800000 }, //ROM_REGION( 0x10000, "io_board", 0) //ROM_LOAD("epr-22084.ic3", 0x0000, 0x10000, CRC(18cf58bb) SHA1(1494f8215231929e41bbe2a133658d01882fbb0f) ) - - { NULL, 0, 0 }, }, nullptr, - // TRIGGER, CHANGE, JUMP - nullptr, + &otrigger_inputs, otrigger_eeprom_dump, }, // Moero! Justice Gakuen (JPN) / Project Justice (USA, EXP, KOR, AUS) (Rev A) @@ -2803,7 +2795,6 @@ const Game Games[] = { "mpr-23545.ic9", 0x8800000, 0x1000000 }, { "mpr-23546.ic10", 0x9800000, 0x1000000 }, { "mpr-23547.ic11", 0xa800000, 0x1000000 }, - { NULL, 0, 0 }, }, nullptr, &capcom_4btn_inputs, @@ -2829,7 +2820,6 @@ const Game Games[] = { "mpr-21594.ic6", 0x3000000, 0x0800000 }, { "mpr-21595.ic7", 0x3800000, 0x0800000 }, { "mpr-21596.ic8", 0x4000000, 0x0800000 }, - { NULL, 0, 0 }, }, nullptr, &pstone_inputs, @@ -2855,7 +2845,6 @@ const Game Games[] = { "mpr-23124.ic7", 0x3800000, 0x0800000 }, { "mpr-23125.ic8", 0x4000000, 0x0800000 }, { "mpr-23126.ic9", 0x4800000, 0x0800000 }, - { NULL, 0, 0 }, }, nullptr, &pstone2_inputs, @@ -2881,7 +2870,6 @@ const Game Games[] = { "07.ic8", 0x3800000, 0x0800000 }, { "08.ic9", 0x4000000, 0x0800000 }, { "09.ic10", 0x4800000, 0x0800000 }, - { NULL, 0, 0 }, }, nullptr, &pstone2_inputs, @@ -2918,8 +2906,9 @@ const Game Games[] = { "mpr-22203.ic18s", 0x9000000, 0x800000 }, { "mpr-22204.ic19s", 0x9800000, 0x800000 }, { "mpr-22205.ic20s", 0xa000000, 0x800000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &puyoda_inputs, }, // Ring Out 4x4 (Rev A) { @@ -2943,7 +2932,6 @@ const Game Games[] = { "mpr-21768.ic8", 0x4000000, 0x800000 }, { "mpr-21769.ic9", 0x4800000, 0x800000 }, { "mpr-21770.ic10", 0x5000000, 0x800000 }, - { NULL, 0, 0 }, }, NULL, &ringout_inputs, @@ -2970,7 +2958,6 @@ const Game Games[] = { "mpr-21768.ic8", 0x4000000, 0x800000 }, { "mpr-21769.ic9", 0x4800000, 0x800000 }, { "mpr-21770.ic10", 0x5000000, 0x800000 }, - { NULL, 0, 0 }, }, NULL, &ringout_inputs, @@ -3003,8 +2990,9 @@ const Game Games[] = { "mpr-22963.ic14s",0x7000000, 0x0800000 }, { "mpr-22964.ic15s",0x7800000, 0x0800000 }, { "mpr-22965.ic16s",0x8000000, 0x0800000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &samba_inputs, }, // Samba De Amigo (Rev A) { @@ -3034,8 +3022,9 @@ const Game Games[] = { "mpr-22963.ic14s", 0x7000000, 0x0800000 }, { "mpr-22964.ic15s", 0x7800000, 0x0800000 }, { "mpr-22965.ic16s", 0x8000000, 0x0800000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &samba_inputs, }, // Samba De Amigo (USA, prototype) { @@ -3063,8 +3052,9 @@ const Game Games[] = { "rom12.ic12s", 0x06000000, 0x00800000 }, { "rom13.ic13s", 0x06800000, 0x00800000 }, { "rom14.ic14s", 0x07000000, 0x00800000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &samba_inputs, }, //Samba de Amigo ver. 2000 (Japan) { @@ -3089,8 +3079,9 @@ const Game Games[] = { "mpr-23597.ic9", 0x08800000, 0x1000000 }, { "mpr-23598.ic10", 0x09800000, 0x1000000 }, { "mpr-23599.ic11", 0x0a800000, 0x1000000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &samba_inputs, }, // Sega Tetris { @@ -3111,9 +3102,9 @@ const Game Games[] = { "mpr-22913.ic4", 0x2000000, 0x800000 }, { "mpr-22914.ic5", 0x2800000, 0x800000 }, { "mpr-22915.ic6", 0x3000000, 0x800000 }, - { NULL, 0, 0 }, - } - // SW1/2 + }, + nullptr, + &sgtetris_inputs, }, // Dengen Tenshi Taisen Janshi Shangri-la (JPN, USA, EXP, KOR, AUS) { @@ -3139,7 +3130,6 @@ const Game Games[] = { "mpr-22070.ic10", 0x5000000, 0x0800000 }, { "mpr-22071.ic11", 0x5800000, 0x0800000 }, { "mpr-22072.ic12s", 0x6000000, 0x0800000 }, - { NULL, 0, 0 }, } }, // Star Horse (satellite) @@ -3164,8 +3154,6 @@ const Game Games[] = //ROM_REGION(0x84, "some_eeprom", 0) //ROM_LOAD( "sflash.ic46", 0x000000, 0x000084, CRC(4929e940) SHA1(f8c4277ca0ae5e36b2eed033cc731b8fc4fccafc) ) - - { NULL, 0, 0 }, } }, // Star Horse (live and backup) @@ -3202,8 +3190,6 @@ const Game Games[] = //ROM_REGION(0x84, "some_eeprom", 0) //ROM_LOAD( "sflash.ic37", 0x000000, 0x000084, CRC(1557297e) SHA1(41e8a7a8eaf5076b124d378afdf97e328d100e72) ) - - { NULL, 0, 0 }, } }, // Star Horse (main screens) @@ -3225,8 +3211,6 @@ const Game Games[] = //ROM_REGION(0x84, "some_eeprom", 0) //ROM_LOAD( "sflash.ic46", 0x000000, 0x000084, CRC(951684e4) SHA1(0beaf5827064252293223b946c04b8698e7207bb) ) - - { NULL, 0, 0 }, } }, // Star Horse Progress (satellite, Rev A) @@ -3248,7 +3232,6 @@ const Game Games[] = { "mpr-24127.ic5", 0x4800000, 0x1000000 }, { "mpr-24128.ic6", 0x5800000, 0x1000000 }, { "mpr-24129.ic7", 0x6800000, 0x1000000 }, - { NULL, 0, 0 }, } }, // Star Horse Progress (backup data) @@ -3267,8 +3250,6 @@ const Game Games[] = //ROM_REGION(0x84, "some_eeprom", 0) //ROM_LOAD( "sflash.ic37", 0x000000, 0x000084, CRC(fe8f8f5c) SHA1(839461ab736e0228dec7e2512e1692d6ecc4e664) ) - - { NULL, 0, 0 }, } }, // Star Horse Progress (live, Rev A) @@ -3294,7 +3275,6 @@ const Game Games[] = { "mpr-24116.ic9", 0x08800000, 0x01000000 }, { "mpr-24117.ic10", 0x09800000, 0x01000000 }, { "mpr-24118.ic11", 0x0a800000, 0x01000000 }, - { NULL, 0, 0 }, } }, // Star Horse Progress (main screens) @@ -3315,7 +3295,6 @@ const Game Games[] = { "mpr-24091.ic4", 0x03800000, 0x01000000 }, { "mpr-24092.ic5", 0x04800000, 0x01000000 }, { "mpr-24093.ic6", 0x05800000, 0x01000000 }, - { NULL, 0, 0 }, } }, // Star Horse Progress (sound & backup) @@ -3336,7 +3315,6 @@ const Game Games[] = { "mpr-24101.ic4", 0x03800000, 0x01000000 }, { "mpr-24102.ic5", 0x04800000, 0x01000000 }, { "mpr-24103.ic6", 0x05800000, 0x01000000 }, - { NULL, 0, 0 }, } }, // Star Horse 2001 (satellite, Rev B) @@ -3358,7 +3336,6 @@ const Game Games[] = { "mpr-23744.ic5", 0x04800000, 0x01000000 }, { "mpr-23745.ic6", 0x05800000, 0x01000000 }, { "mpr-23746.ic7", 0x06800000, 0x01000000 }, - { NULL, 0, 0 }, } }, // Star Horse 2002 (main screens) @@ -3478,7 +3455,6 @@ const Game Games[] = { "mpr-22056.ic19s", 0x9800000, 0x800000 }, { "mpr-22057.ic20s", 0xa000000, 0x800000 }, { "mpr-22058.ic21s", 0xa800000, 0x800000 }, - { NULL, 0, 0 }, }, NULL, &wsbb_inputs, @@ -3512,7 +3488,6 @@ const Game Games[] = { "mpr-23338.ic15s",0x7800000, 0x0800000 }, { "mpr-23339.ic16s",0x8000000, 0x0800000 }, { "mpr-23340.ic17s",0x8800000, 0x0800000 }, - { NULL, 0, 0 }, }, NULL, &slashout_inputs, @@ -3542,7 +3517,6 @@ const Game Games[] = { "mpr-22217.ic10", 0x5000000, 0x0800000 }, { "mpr-22218.ic11", 0x5800000, 0x0800000 }, { "mpr-22219.ic12s", 0x6000000, 0x0800000 }, - { NULL, 0, 0 }, }, NULL, &marine_fishing_inputs, @@ -3569,7 +3543,6 @@ const Game Games[] = { "mpr-22974.ic8", 0x4000000, 0x0800000 }, { "mpr-22975.ic9", 0x4800000, 0x0800000 }, { "mpr-22976.ic10", 0x5000000, 0x0800000 }, - { NULL, 0, 0 }, }, NULL, &shot1234_inputs, @@ -3607,7 +3580,6 @@ const Game Games[] = { "mpr-23319.ic18s", 0x9000000, 0x800000 }, { "mpr-23320.ic19s", 0x9800000, 0x800000 }, { "mpr-23321.ic20s", 0xa000000, 0x800000 }, - { NULL, 0, 0 }, }, nullptr, &sstrkfgt_inputs, @@ -3646,7 +3618,6 @@ const Game Games[] = { "mpr-23319.ic18s", 0x9000000, 0x800000 }, { "mpr-23320.ic19s", 0x9800000, 0x800000 }, { "mpr-23321.ic20s", 0xa000000, 0x800000 }, - { NULL, 0, 0 }, }, nullptr, &sstrkfgt_inputs, @@ -3680,7 +3651,6 @@ const Game Games[] = { "mpr-21991.ic12s",0x6000000, 0x0800000 }, { "mpr-21992.ic13s",0x6800000, 0x0800000 }, { "mpr-21993.ic14s",0x7000000, 0x0400000 }, - { NULL, 0, 0 }, } }, // Touch de Uno! / Unou Nouryoku Check Machine (Japan) @@ -3708,7 +3678,7 @@ const Game Games[] = { "x76f100.ic37", 0x000000, 0x000084, 0xc79251d5, Eeprom }, }, nullptr, - nullptr, + &service_btns_inputs, tduno_eeprom_dump, }, // Tokyo Bus (Rev A) @@ -3741,7 +3711,6 @@ const Game Games[] = { "rom16.ic16s", 0x8000000, 0x0800000 }, { "rom17.ic17s", 0x8800000, 0x0800000 }, { "rom18.ic18s", 0x9000000, 0x0800000 }, - { NULL, 0, 0 }, }, NULL, &tokyobus_inputs, @@ -3780,7 +3749,6 @@ const Game Games[] = { "mpr-23019.ic19s", 0x9800000, 0x0800000 }, { "mpr-23020.ic20s", 0xa000000, 0x0800000 }, // IC21s not populated - { NULL, 0, 0 }, }, nullptr, nullptr, @@ -3819,7 +3787,6 @@ const Game Games[] = { "mpr-23019.ic19s", 0x9800000, 0x0800000 }, { "mpr-23020.ic20s", 0xa000000, 0x0800000 }, // IC21S not populated - { NULL, 0, 0 }, }, nullptr, nullptr, @@ -3853,7 +3820,6 @@ const Game Games[] = { "trf1ma13.6n", 0xd000000, 0x1000000 }, { "trf1ma14.6m", 0xe000000, 0x1000000 }, { "trf1ma15.6l", 0xf000000, 0x1000000 }, - { NULL, 0, 0 }, }, nullptr, &toukon4_inputs, @@ -3880,7 +3846,6 @@ const Game Games[] = { "mpr-22032.ic8", 0x4000000, 0x0800000 }, { "mpr-22033.ic9", 0x4800000, 0x0800000 }, { "mpr-22034.ic10",0x5000000, 0x0800000 }, - { NULL, 0, 0 }, }, nullptr, &toyfight_inputs, @@ -3919,9 +3884,9 @@ const Game Games[] = { "mpr-22946.ic19s", 0x9800000, 0x0800000 }, { "mpr-22947.ic20s", 0xa000000, 0x0800000 }, { "mpr-22948.ic21s", 0xa800000, 0x0800000 }, - { NULL, 0, 0 }, - } - // PASS, SHOOT + }, + nullptr, + &virnba_inputs, }, // Virtua NBA { @@ -3956,8 +3921,9 @@ const Game Games[] = { "mpr-22946.ic19s", 0x9800000, 0x0800000 }, { "mpr-22947.ic20s", 0xa000000, 0x0800000 }, { "mpr-22948.ic21s", 0xa800000, 0x0800000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &virnba_inputs, }, // Virtua NBA (prototype) { @@ -3993,6 +3959,8 @@ const Game Games[] = { "rom20.ic20s", 0xa000000, 0x0800000, 0x9a08015e }, { "rom21.ic21s", 0xa800000, 0x0800000, 0x194594f2 }, }, + nullptr, + &virnba_inputs, }, // Virtua NBA (prototype, 15.11) { @@ -4027,8 +3995,9 @@ const Game Games[] = { "rom19.ic19s", 0x9800000, 0x0800000 }, { "rom20.ic20s", 0xa000000, 0x0800000 }, { "rom21.ic21s", 0xa800000, 0x0800000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &virnba_inputs, }, // Virtual On Oratorio Tangram M.S.B.S. ver5.66 2000 Edition { @@ -4055,7 +4024,6 @@ const Game Games[] = { "mpr-23192.ic11", 0x5800000, 0x800000 }, { "mpr-23193.ic12s", 0x6000000, 0x800000 }, { "mpr-23194.ic13s", 0x6800000, 0x800000 }, - { NULL, 0, 0 }, }, NULL, &vonot_inputs, @@ -4088,9 +4056,9 @@ const Game Games[] = { "mpr-21921.ic13s",0x6800000, 0x0800000 }, { "mpr-21922.ic14s",0x7000000, 0x0800000 }, { "mpr-21923.ic15s",0x7800000, 0x0400000 }, - { NULL, 0, 0 }, - } - // LONG PASS, SHOOT, SHORT PASS + }, + nullptr, + &vs2_2k_inputs, }, // Virtua Striker 2 Ver. 2000 { @@ -4119,8 +4087,9 @@ const Game Games[] = { "mpr-21921.ic13s", 0x6800000, 0x0800000 }, { "mpr-21922.ic14s", 0x7000000, 0x0800000 }, { "mpr-21923.ic15s", 0x7800000, 0x0400000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &vs2_2k_inputs, }, // Virtua Tennis (USA, EXP, KOR, AUS) / Power Smash (JPN) { @@ -4145,7 +4114,6 @@ const Game Games[] = { "mpr-22924.ic9", 0x4800000, 0x0800000 }, { "mpr-22925.ic10",0x5000000, 0x0800000 }, { "mpr-22926.ic11",0x5800000, 0x0800000 }, - { NULL, 0, 0 }, }, nullptr, &shot12_inputs, @@ -4169,7 +4137,6 @@ const Game Games[] = { "mpr-23722.ic4", 0x3800000, 0x1000000 }, { "mpr-23723.ic5", 0x4800000, 0x1000000 }, { "mpr-23724.ic6", 0x5800000, 0x1000000 }, - { NULL, 0, 0 }, }, NULL, &wrungp_inputs, @@ -4198,8 +4165,6 @@ const Game Games[] = // 838-12801 DRIVE BD WAVERUNNER //ROM_REGION( 0x10000, "motorio", ROMREGION_ERASEFF) //ROM_LOAD( "epr-19250.ic8", 0x000000, 0x010000, CRC(542d3836) SHA1(128cb0bfaf05791d219437653002f6bb948a4ad5) ) - - { NULL, 0, 0 }, }, NULL, &wrungp_inputs, @@ -4227,9 +4192,9 @@ const Game Games[] = { "wk1ma8.4e", 0x8000000, 0x1000000 }, { "wk1ma9.4d", 0x9000000, 0x1000000 }, { "wk1ma10.4c", 0xa000000, 0x1000000 }, - { NULL, 0, 0 }, - } - // BUTTON, (n/a, n/a, ENTER) + }, + nullptr, + &wldkicks_inputs, // TODO some default eeprom/nvram would help }, // World Kicks (Japan, WK1 Ver.A) { @@ -4253,8 +4218,9 @@ const Game Games[] = { "wk1ma8.4e", 0x8000000, 0x1000000 }, { "wk1ma9.4d", 0x9000000, 0x1000000 }, { "wk1ma10.4c", 0xa000000, 0x1000000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &wldkicks_inputs, }, // World Kicks (US, WK3 Ver.A) { @@ -4278,8 +4244,9 @@ const Game Games[] = { "wk1ma8.4e", 0x8000000, 0x1000000 }, { "wk1ma9.4d", 0x9000000, 0x1000000 }, { "wk1ma10.4c", 0xa000000, 0x1000000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &wldkicks_inputs, }, // World Kicks PCB (Japan, WKC1 Ver.A) { @@ -4303,8 +4270,9 @@ const Game Games[] = { "wk1ma8.4e", 0x8000000, 0x1000000 }, { "wk1ma9.4d", 0x9000000, 0x1000000 }, { "wk1ma10.4c", 0xa000000, 0x1000000 }, - { NULL, 0, 0 }, - } + }, + nullptr, + &wldkickspcb_inputs, // TODO some default eeprom/nvram would help }, // World Kicks PCB (World, WKC2 Ver.A) { @@ -4328,9 +4296,9 @@ const Game Games[] = { "wk1ma8.4e", 0x8000000, 0x1000000 }, { "wk1ma9.4d", 0x9000000, 0x1000000 }, { "wk1ma10.4c", 0xa000000, 0x1000000 }, - { NULL, 0, 0 }, - } - // C BUTTON + }, + nullptr, + &wldkickspcb_inputs, }, // WWF Royal Rumble (JPN, USA, EXP, KOR, AUS) { @@ -4352,9 +4320,9 @@ const Game Games[] = { "mpr-22267.ic6", 0x5800000, 0x1000000 }, { "mpr-22268.ic7", 0x6800000, 0x1000000 }, { "mpr-22269.ic8", 0x7800000, 0x1000000 }, - { NULL, 0, 0 }, - } - // ATTACK, GRAPPLE, SUPPORT + }, + nullptr, + &wwfroyal_inputs, }, // Zero Gunner 2 { @@ -4373,7 +4341,6 @@ const Game Games[] = { "mpr-23686.ic3", 0x2800000, 0x1000000 }, { "mpr-23687.ic4", 0x3800000, 0x1000000 }, { "mpr-23688.ic5", 0x4800000, 0x1000000 }, - { NULL, 0, 0 }, }, nullptr, &shot12_inputs, @@ -4410,7 +4377,6 @@ const Game Games[] = { "mpr-21724.ic17s", 0x8800000, 0x0800000 }, { "mpr-21725.ic18s", 0x9000000, 0x0800000 }, { "mpr-21726.ic19s", 0x9800000, 0x0800000 }, - { NULL, 0, 0 }, }, NULL, &zombie_inputs, @@ -4448,7 +4414,6 @@ const Game Games[] = { "mpr-21724.ic17s",0x8800000, 0x0800000 }, { "mpr-21725.ic18s",0x9000000, 0x0800000 }, { "mpr-21726.ic19s",0x9800000, 0x0800000 }, - { NULL, 0, 0 }, }, NULL, &zombie_inputs, @@ -4490,8 +4455,9 @@ const Game Games[] = { "rom9.ic9s", 0x04800000, 0x00800000, 0x032cca1a }, { "rom10.ic10s", 0x05000000, 0x00800000, 0x6d094477 }, { "rom11.ic11s", 0x05800000, 0x00800000, 0x6c803ca0 }, - // ic 12-21 populated, empty }, + nullptr, + &dygolf_inputs, }, // Nittere Shiki! Mirai Yosou Studio / NTV Future Forecast Studio (Japan) { @@ -4544,9 +4510,7 @@ const Game Games[] = { "ic9.bin", 0x4000000, 0x4000000 }, // IC10 and IC11 Populated, Empty { "317-05130-jpn.ic3", 0, 0x800, 0x0000000, Key }, - { NULL, 0, 0 }, }, - // ATTACK1/2/3 nullptr, &ausfache_inputs, ausfache_eeprom_dump, @@ -4567,9 +4531,9 @@ const Game Games[] = { "fpr-24384.ic10", 0x8000000, 0x4000000, 0x2e9116c4 }, { "fpr-24385.ic11", 0xc000000, 0x4000000, 0x2b79f45d }, { "317-0495-com.ic3", 0, 0x800, 0xc229a59b, Key }, - { NULL, 0, 0 }, - } - // PUNCH, KICK, JUMP + }, + nullptr, + &asndynmt_inputs, }, // Asian Dynamite / Dynamite Deka EX (older) { @@ -4587,10 +4551,9 @@ const Game Games[] = { "fpr-24384.ic10", 0x8000000, 0x4000000, 0x2e9116c4 }, { "fpr-24385.ic11", 0xc000000, 0x4000000, 0x2b79f45d }, { "317-0495-com.ic3", 0, 0x800, 0xc229a59b, Key }, - - { NULL, 0, 0 }, - } - // PUNCH, KICK, JUMP + }, + nullptr, + &asndynmt_inputs, }, // Illvelo (Illmatic Envelope) (Japan) { @@ -4607,10 +4570,9 @@ const Game Games[] = { "fpr-24438.ic9", 0x4000000, 0x4000000 }, { "fpr-24439.ic10", 0x8000000, 0x4000000 }, { "317-5131-jpn.ic3", 0, 0x800, 0x0000000, Key }, - { NULL, 0, 0 }, }, - NULL, - NULL, // SHOT/DOLL/SPECIAL + nullptr, + &illvelo_inputs, illvelo_eeprom_dump }, // Manic Panic Ghosts! *** BAD DUMP *** @@ -4653,10 +4615,9 @@ const Game Games[] = { "ic9.bin", 0x4000000, 0x4000000 }, { "ic10.bin", 0x8000000, 0x4000000 }, { "317-5132-jpn.ic3", 0, 0x800, 0x0000000, Key }, - { NULL, 0, 0 }, }, NULL, - &mamonoro_inputs, // SHOT(A)/(B) + &mamonoro_inputs, mamonoro_eeprom_dump }, // Melty Blood Actress Again Version A (Japan, Rev A) @@ -4678,7 +4639,6 @@ const Game Games[] = { "ic12.bin", 0x10000000, 0x4000000 }, { "ic13.bin", 0x14000000, 0x4000000 }, { "317-5133-jpn.ic3", 0, 0x800, 0x0000000, Key }, // pic_readout - { NULL, 0, 0 }, }, nullptr, &meltyb_inputs, @@ -4702,7 +4662,6 @@ const Game Games[] = { "ic12.bin", 0x10000000, 0x4000000 }, { "ic13.bin", 0x14000000, 0x4000000 }, { "317-5133-jpn.ic3", 0, 0x800, 0x0000000, Key }, // pic_readout - { NULL, 0, 0 }, }, nullptr, &meltyb_inputs, @@ -4722,13 +4681,14 @@ const Game Games[] = { "fpr-24333.ic8", 0x0000000, 0x4000000 }, { "fpr-24334.ic9", 0x4000000, 0x4000000 }, { "317-0437-com.ic3", 0, 0x800, 0x0000000, Key }, - { NULL, 0, 0 }, - } + }, + nullptr, + &mushik_inputs, }, // Mushiking The King Of Beetles - Mushiking II / III / III+ (World, Ver. 2.001) { "mushik2e", - NULL, + nullptr, "Mushiking The King Of Beetles - Mushiking II / III / III+ (World, Ver. 2.001)", 0x8000000, 0x5582, @@ -4743,16 +4703,16 @@ const Game Games[] = //ROM_REGION( 0x800, "pic_readout", 0 ) //ROM_LOAD( "317-0437-com.ic3", 0, 0x800, CRC(3b6fcee8) SHA1(65fbdd3b8c61a4b5ccb6389b25483a7ecdc0794d) ) { "317-0437-com.ic3", 0, 0x800, 0x0000000, Key }, - - { NULL, 0, 0 }, - } + }, + nullptr, + &mushik_inputs, }, // Mushiking The King Of Beetles - Mushiking IV / V / VI (World) // change game version (4/5/6): in BACKUP DATA CLEAR menu hold P1 and P2 buttons 1 for 3 seconds, then change version number in appeared menu and select YES(CLEAR) // ~equivalent of Japanese 2K6 versions { "mushik4e", - NULL, + nullptr, "Mushiking The King Of Beetles - Mushiking IV / V / VI (World)", 0x8000000, 0x5502, @@ -4765,7 +4725,9 @@ const Game Games[] = //ROM_REGION( 0x800, "pic_readout", 0 ) { "317-0437-com.ic3", 0, 0x800, 0x3b6fcee8, Key }, - } + }, + nullptr, + &mushik_inputs, }, // Mushiking The King Of Beetles - Mushiking IV / V / VI (Taiwan) // change game version (4/5/6): in BACKUP DATA CLEAR menu hold P1 and P2 buttons 1 for 3 seconds, then change version number in appeared menu and select YES(CLEAR) @@ -4785,7 +4747,9 @@ const Game Games[] = //ROM_REGION( 0x800, "pic_readout", 0 ) { "317-0437-com.ic3", 0, 0x800, 0x3b6fcee8, Key }, - } + }, + nullptr, + &mushik_inputs, }, { "mushi2k61", @@ -4802,6 +4766,8 @@ const Game Games[] = { "317-0444-jpn.ic3", 0, 0x800, 0x6ded35a2, Key }, }, + nullptr, + &mushik_inputs, }, { "mushi2k62", @@ -4818,6 +4784,8 @@ const Game Games[] = { "317-0444-jpn.ic3", 0, 0x800, 0x6ded35a2, Key }, }, + nullptr, + &mushik_inputs, }, // Pokasuka Ghost *** BAD DUMP *** { @@ -4858,11 +4826,9 @@ const Game Games[] = { "ic8.bin", 0x0000000, 0x4000000 }, { "ic9.bin", 0x4000000, 0x4000000 }, { "317-5138-jpn.ic3", 0, 0x800, 0x0000000, Key }, - { NULL, 0, 0 }, }, NULL, - &radirgyn_inputs, - // SHOT (A)/(B)/(C) + &radirgy_inputs, }, // Rhythm Tengoku { @@ -4880,9 +4846,9 @@ const Game Games[] = { "fpr-24425.ic10", 0x08000000, 0x4000000 }, { "fpr-24426.ic11", 0x0c000000, 0x4000000 }, { "317-0503-jpn.ic3", 0, 0x800, 0x0000000, Key }, - { NULL, 0, 0 }, - } - // SHOT A/B + }, + nullptr, + &rhytngk_inputs, }, // Shooting Love 2007 (Japan) { @@ -4900,10 +4866,9 @@ const Game Games[] = { "fpr-24415.ic10", 0x8000000, 0x4000000 }, { "fpr-24416.ic11", 0xc000000, 0x4000000 }, { "317-5129-jpn.ic3", 0, 0x800, 0x0000000, Key }, - { NULL, 0, 0 }, }, - NULL, - NULL, // PUSH1/2/3 + nullptr, + &sl2007_inputs, sl2007_eeprom_dump }, // Touch De Zunou (Rev A) *** BAD DUMP *** @@ -4923,8 +4888,9 @@ const Game Games[] = //ROM_REGION( 0x800, "pic_readout", 0 ) //ROM_LOAD( "317-0435-jpn.ic3", 0, 0x800, BAD_DUMP CRC(b553d900) SHA1(ed1c3c2053f2c0e98cb5c4d99f93143a66c29e5c) ) { "317-0435-jpn.ic3", 0, 0x800, 0x0000000, Key }, - { NULL, 0, 0 }, - } + }, + nullptr, + nullptr, }, // Star Horse Progress Returns (satellite) { @@ -4939,7 +4905,6 @@ const Game Games[] = { { "fpr-24489.ic8", 0x00000000, 0x4000000 }, { "fpr-24490.ic9", 0x04000000, 0x4000000 }, - { NULL, 0, 0 }, } }, // Naomi GD Roms @@ -4958,7 +4923,8 @@ const Game Games[] = { "317-5091-jpn.pic", 0, 0x4000, 0xb71ede16 }, }, "gdl-0018", - }, + &azumanga_inputs, + }, // Border Down (Rev A) { "bdrdown", @@ -4974,7 +4940,7 @@ const Game Games[] = { "bdrdown-default-eeprom.bin", 0, 0x80, 0x5b19727c, Eeprom }, }, "gdl-0023a", - // SHOT, LASER, SPEED + &bdrdown_inputs, }, // Chaos Field (Japan) { @@ -4991,7 +4957,7 @@ const Game Games[] = { "cfield-default-eeprom.bin", 0, 0x80, 0xa7acb6bf, Eeprom }, }, "gdl-0025", - // TRG1/2/3 + &cfield_inputs, }, // Musapey's Choco Marker (Rev A) { @@ -5007,7 +4973,7 @@ const Game Games[] = { "317-5085-jpn.pic", 0, 0x4000, 0x677fd544 }, }, "gdl-0014a", - // BUTTON A/B + &button12_inputs, }, // Cleopatra Fortune Plus { @@ -5023,7 +4989,7 @@ const Game Games[] = { "317-5083-com.pic", 0, 0x4000, 0x096a0fc2 }, }, "gdl-0012", - // BUTTON 1/2 + &button12_inputs, }, // Confidential Mission { @@ -5039,7 +5005,7 @@ const Game Games[] = { "317-0298-com.pic", 0, 0x4000, 0x15971bf6 }, }, "gds-0001", - nullptr, + &lightgun_inputs, confmiss_eeprom_dump, }, // Capcom vs. SNK Millennium Fight 2000 Pro (Japan) @@ -5174,6 +5140,7 @@ const Game Games[] = { "317-0308-com.pic", 0, 0x4000, 0x5e1ef2c4 }, }, "gds-0009a", + &dygolf_inputs, }, // Guilty Gear XX { @@ -5307,8 +5274,7 @@ const Game Games[] = { "317-5081-jpn.pic", 0, 0x4000, 0x72ca4579 }, }, "gdl-0010", - // SHOT, CHANGE - nullptr, + &ikaruga_inputs, nullptr, }, // Jingi Storm - The Arcade (Japan) @@ -5325,8 +5291,7 @@ const Game Games[] = { "317-5122-jpn.pic", 0, 0x4000, 0x88983220 }, }, "gdl-0037", - // GUARD, PUNCH, KICK - nullptr, + &jingystm_inputs, jingystm_eeprom_dump, }, // Karous (Japan) @@ -5344,7 +5309,7 @@ const Game Games[] = { "karous-default-eeprom.bin", 0, 0x80, 0xb017451c, Eeprom }, }, "gdl-0040", - // SHOT, SWORD, SPECIAL same as radirgy + &radirgy_inputs, }, // La Keyboard { @@ -5376,6 +5341,7 @@ const Game Games[] = { "317-5115-jpn.pic", 0, 0x4000, 0xe5435e85 }, }, "gdl-0034", + nullptr, // TODO }, // Lupin The Third - The Shooting (Rev A) { @@ -5391,7 +5357,7 @@ const Game Games[] = { "317-0325-jpn.pic", 0, 0x4000, 0xf71cb2fc }, }, "gds-0018a", - nullptr, + &lightgun_inputs, lupinsho_eeprom_dump, }, // Lupin The Third - The Shooting @@ -5408,7 +5374,7 @@ const Game Games[] = { "317-0325-jpn.pic", 0, 0x4000, 0xf71cb2fc }, }, "gds-0018", - nullptr, + &lightgun_inputs, lupinsho_eeprom_dump, }, // Lupin The Third - The Typing (Rev A) @@ -5511,6 +5477,7 @@ const Game Games[] = { "moeru-default-eeprom.bin", 0, 0x80, 0x50ca079f, Eeprom }, }, "gdl-0013", + nullptr, // TODO }, // The Maze of the Kings { @@ -5526,7 +5493,7 @@ const Game Games[] = { "317-0333-com.pic", 0, 0x4000, 0x15fb7792 }, }, "gds-0022", - nullptr, + &lightgun_inputs, mok_eeprom_dump, }, // Monkey Ball @@ -5561,8 +5528,7 @@ const Game Games[] = // { "psyvar2-default-eeprom.bin", 0, 0x80, 0x9d8661f3, Eeprom }, }, "gdl-0024", - // SHOT, BOMB - nullptr, + &psyvariar_inputs, psyvar2_eeprom_dump, }, // Puyo Pop Fever (World) @@ -5580,7 +5546,7 @@ const Game Games[] = // { "puyofev-default-eeprom.bin", 0, 0x80, 0x42e5fd40, Eeprom }, }, "gds-0034", - nullptr, + &puyofev_inputs, puyofev_eeprom_dump, }, // Puyo Puyo Fever (Japan) @@ -5598,7 +5564,7 @@ const Game Games[] = // { "puyofev-default-eeprom.bin", 0, 0x80, 0x42e5fd40, Eeprom }, }, "gds-0031", - nullptr, + &puyofev_inputs, puyofev_eeprom_dump, }, // Puyo Puyo Fever (Prototype) @@ -5637,7 +5603,7 @@ const Game Games[] = { "copy", 0x00400000, 0xc00000, 0x0000000, Copy, 0x1000000 }, }, nullptr, - nullptr, + &puyofev_inputs, puyofev_eeprom_dump, }, // Quiz Keitai Q mode @@ -5655,6 +5621,7 @@ const Game Games[] = { "quizqgd-default-eeprom.bin", 0, 0x80, 0x46c10aa3, Eeprom }, }, "gdl-0017", + nullptr, // TODO }, // Radirgy (Japan, Rev A) { @@ -5671,7 +5638,7 @@ const Game Games[] = { "radirgy-default-eeprom.bin", 0, 0x80, 0x8d60a282, Eeprom }, }, "gdl-0032a", - // SHOT, SWORD, SPECIAL same as karous + &radirgy_inputs, }, // Radirgy (Japan) { @@ -5688,6 +5655,7 @@ const Game Games[] = { "radirgy-default-eeprom.bin", 0, 0x80, 0x8d60a282, Eeprom }, }, "gdl-0032", + &radirgy_inputs, }, // Senko No Ronde (Japan, Rev A) { @@ -5704,7 +5672,7 @@ const Game Games[] = // { "senko-default-eeprom.bin", 0, 0x80, 0xb3d3be09, Eeprom }, }, "gdl-0030a", - nullptr, + &senko_inputs, senko_eeprom_dump, }, // Senko No Ronde (Japan) @@ -5722,7 +5690,7 @@ const Game Games[] = // { "senkoo-default-eeprom.bin", 0, 0x80, 0xa2203a7f, Eeprom }, }, "gdl-0030", - nullptr, + &senko_inputs, senko_eeprom_dump, }, // Senko No Ronde Special (Export, Japan) @@ -5823,6 +5791,7 @@ const Game Games[] = { "shikgam2-default-eeprom.bin", 0, 0x80, 0x5fb60e27, Eeprom }, }, "gdl-0021", + &sl2007_inputs, }, // Slashout { @@ -5855,7 +5824,7 @@ const Game Games[] = { "317-0303-com.pic", 0, 0x4000, 0xb42999dd }, }, "gds-0005", - nullptr, // BEAT, CHARGE ,JUMP, SHIFT + &spkrbtl_inputs, spkrbtl_eeprom_dump }, // Sports Jam @@ -5967,8 +5936,7 @@ const Game Games[] = // { "trgheart-default-eeprom.bin", 0, 0x80, 0x7faff313, Eeprom }, }, "gdl-0036a", - // SHOT, ANCHOR, BOMB - nullptr, + &trgheart_inputs, trgheart_eeprom_dump, }, // Trigger Heart Exelica (Japan) @@ -5986,7 +5954,7 @@ const Game Games[] = // { "trgheart-default-eeprom.bin", 0, 0x80, 0x7faff313, Eeprom }, }, "gdl-0036", - nullptr, + &trgheart_inputs, trgheart_eeprom_dump, }, // Trizeal (Japan) @@ -6004,8 +5972,7 @@ const Game Games[] = // { "trizeal-default-eeprom.bin", 0, 0x80, 0xac0847ce, Eeprom }, }, "gdl-0026", - // PUSH1/2/3 - nullptr, + &sl2007_inputs, trizeal_eeprom_dump, }, // Under Defeat (Japan) @@ -6023,8 +5990,7 @@ const Game Games[] = // { "undefeat-default-eeprom.bin", 0, 0x80, 0x9d2b071c, Eeprom }, }, "gdl-0035", - // SHOT, BOMB - nullptr, + &psyvariar_inputs, undefeat_eeprom_dump, }, // Usagi - Yamashiro Mahjong Hen (Japan) @@ -6057,7 +6023,7 @@ const Game Games[] = { "317-0330-com.pic", 0, 0x4000, 0x33ccf2d1 }, }, "gds-0019", - // RUN1, ACTION, RUN2 + &vathlete_inputs, }, // Virtua Tennis 2 / Power Smash 2 (Rev A) { @@ -6073,7 +6039,7 @@ const Game Games[] = { "317-0318-com.pic", 0, 0x4000, 0x83de4047 }, }, "gds-0015a", - nullptr, + &shot12_inputs, vtennis2_eeprom_dump, }, // Virtua Tennis / Power Smash @@ -6349,7 +6315,9 @@ const Game Games[] = { "vera.u17", 0x05000000, 0x01000000, 0xd78389a4 }, { "vera.u14", 0x06000000, 0x01000000, 0x35df044f }, { "vera.u16", 0x07000000, 0x01000000, 0x3590072d }, - } + }, + nullptr, + &basschal_inputs, }, // Sega Bass Fishing Challenge { @@ -6370,7 +6338,9 @@ const Game Games[] = { "610-0811.u17", 0x05000000, 0x01000000, 0xdb799f5a }, { "610-0811.u14", 0x06000000, 0x01000000, 0xf2769383 }, { "vera.u16", 0x07000000, 0x01000000, 0x3590072d }, - } + }, + nullptr, + &basschal_inputs, }, // Block Pong-Pong { @@ -6411,7 +6381,9 @@ const Game Games[] = { "608-2161.u17", 0x5000000, 0x1000100, 0x2f973eb4 }, { "608-2161.u14", 0x6000000, 0x1000100, 0x2e7d966f }, { "608-2161.u16", 0x7000000, 0x1000100, 0x14f8ca87 }, - } + }, + nullptr, + &aw_lightgun_inputs, }, // Demolish Fist { @@ -6455,7 +6427,9 @@ const Game Games[] = { "695-0014.u17", 0x5000000, 0x1000000, 0x16bb5992 }, { "695-0014.u14", 0x6000000, 0x1000000, 0x55470242 }, { "695-0014.u16", 0x7000000, 0x1000000, 0x730180a4 }, - } + }, + nullptr, + &aw_shot123_inputs, }, // Dolphin Blue { @@ -6765,7 +6739,9 @@ const Game Games[] = { "ax1603m01.ic13", 0x3000000, 0x1000000, 0x7c0aa241 }, { "ax1604m01.ic14", 0x4000000, 0x1000000, 0xd2369144 }, { "ax1605m01.ic15", 0x5000000, 0x1000000, 0x0c11c1f9 }, - } + }, + nullptr, + &aw_lightgun_inputs, }, // The Rumble Fish { @@ -6892,7 +6868,9 @@ const Game Games[] = { "ax1405m01.ic15", 0x5000000, 0x1000000, 0xb548446f }, { "ax1406m01.ic16", 0x6000000, 0x1000000, 0x437673e6 }, { "ax1407m01.ic17", 0x7000000, 0x1000000, 0x6b6acc0a }, - } + }, + nullptr, + nullptr, // TODO }, // Samurai Shodown VI / Samurai Spirits Tenkaichi Kenkakuden { @@ -6933,7 +6911,9 @@ const Game Games[] = { "ax0102m01.ic12", 0x2000000, 0x1000000, 0x700764d1 }, { "ax0103m01.ic13", 0x3000000, 0x1000000, 0x6144e7a8 }, { "ax0104m01.ic14", 0x4000000, 0x1000000, 0xccb72150 }, - } + }, + nullptr, + &aw_lightgun_inputs, // PUMP is mercury sensor when gun is pointing down }, // Sushi Bar { @@ -6991,11 +6971,6 @@ const Game Games[] = { { "u3", 0x0000000, 0x1000000, 0x7acfb499 }, { "u1", 0x1000000, 0x1000000, 0xb3c1c3bb }, - // garbage data not used by this game, match anmlbskta U4 - //{ "u4", 0x2000000, 0x1000000, 0x646e9773 }, - // garbage data not used by this game, match anmlbskta U2 - //{ "u2", 0x3000000, 0x1000000, 0xb9162d97 }, - // U14-U17 not populated } }, // Extreme Hunting @@ -7016,7 +6991,9 @@ const Game Games[] = { "ax2404m01.ic14", 0x4000000, 0x1000000, 0x759ef5cb }, { "ax2405m01.ic15", 0x5000000, 0x1000000, 0x940d77f1 }, { "ax2406m01.ic16", 0x6000000, 0x1000000, 0xcbcf2c5d }, - } + }, + nullptr, + &aw_lightgun_inputs, }, // Extreme Hunting 2 { @@ -7041,7 +7018,9 @@ const Game Games[] = //ROM_REGION( 0x1400000, "network", 0) // network board //ROM_LOAD( "fpr-24330a.ic2", 0x000000, 0x400000, CRC(8d89877e) SHA1(6caafc49114eb0358e217bc2d1a3ab58a93c8d19) ) //ROM_LOAD( "flash128.ic4s", 0x400000, 0x1000000, CRC(866ed675) SHA1(2c4c06935b7ab1876e640cede51713b841833567) ) - } + }, + nullptr, + &aw_lightgun_inputs, }, // // Naomi 2 @@ -7068,7 +7047,9 @@ const Game Games[] = { "mpr-23660.ic9", 0x8800000, 0x1000000, 0xe49e65f5 }, { "mpr-23661.ic10", 0x9800000, 0x1000000, 0x7d44dc74 }, { "mpr-23662.ic11", 0xa800000, 0x0800000, 0xd6ef7d68 }, - } + }, + nullptr, + &vs2_2k_inputs, }, { "vstrik3c", @@ -7092,7 +7073,9 @@ const Game Games[] = { "mpr-23660.ic9", 0x8800000, 0x1000000, 0xe49e65f5 }, { "mpr-23661.ic10", 0x9800000, 0x1000000, 0x7d44dc74 }, { "mpr-23662.ic11", 0xa800000, 0x0800000, 0xd6ef7d68 }, - } + }, + nullptr, + &vs2_2k_inputs, }, { "wldrider", @@ -7141,7 +7124,9 @@ const Game Games[] = { "mpr-23782.ic9", 0x8800000, 0x1000000, 0x4f72e901 }, { "mpr-23783.ic10", 0x9800000, 0x1000000, 0xc8d4f6f9 }, { "mpr-23784.ic11", 0xa800000, 0x1000000, 0xf74f2fee }, - } + }, + nullptr, + &vf4_inputs, }, { "kingrt66", @@ -7264,7 +7249,7 @@ const Game Games[] = { "copy", 0x400000, 0xc00000, 0x00000000, Copy, 0x1000000 }, }, nullptr, - nullptr, + &vf4_inputs, vf4evo_eeprom_dump, }, { @@ -7619,6 +7604,7 @@ const Game Games[] = { "317-0304-com.bin", 0, 0x4000, 0x8e82d17a }, }, "gds-0006", + &vs2_2k_inputs, }, { "vf4o", @@ -7633,7 +7619,7 @@ const Game Games[] = { "317-0314-com.pic", 0, 0x4000, 0xfa0b6c70 }, }, "gds-0012", - nullptr, + &vf4_inputs, vf4_eeprom_dump }, { @@ -7649,7 +7635,7 @@ const Game Games[] = { "317-0314-com.pic", 0, 0x4000, 0xfa0b6c70 }, }, "gds-0012b", - nullptr, + &vf4_inputs, vf4_eeprom_dump }, { @@ -7665,7 +7651,7 @@ const Game Games[] = { "317-0314-com.pic", 0, 0x4000, 0xfa0b6c70 }, }, "gds-0012c", - nullptr, + &vf4_inputs, vf4_eeprom_dump }, { @@ -7681,6 +7667,7 @@ const Game Games[] = { "317-0317-com.pic", 0, 0x4000, 0xef65fe73 }, }, "gds-0014", + &beachspi_inputs, }, { "initd", @@ -7725,7 +7712,7 @@ const Game Games[] = { "317-0338-jpn.pic", 0, 0x4000, 0xb177ba7d }, }, "gds-0024c", - nullptr, + &vf4_inputs, vf4evo_eeprom_dump, }, { @@ -7741,7 +7728,7 @@ const Game Games[] = { "317-0338-jpn.pic", 0, 0x4000, 0xb177ba7d }, }, "gds-0024b", - nullptr, + &vf4_inputs, vf4evo_eeprom_dump, }, { @@ -7757,7 +7744,7 @@ const Game Games[] = { "317-0338-jpn.pic", 0, 0x4000, 0xb177ba7d }, }, "gds-0024a", - nullptr, + &vf4_inputs, vf4evo_eeprom_dump, }, { @@ -7938,7 +7925,7 @@ const Game Games[] = { "317-0387-com.pic", 0, 0x4000, 0x8728aeaa }, }, "gds-0036f", - nullptr, + &vf4_inputs, vf4tuned_eeprom_dump, }, { @@ -7954,7 +7941,7 @@ const Game Games[] = { "317-0387-com.pic", 0, 0x4000, 0x8728aeaa }, }, "gds-0036d", - nullptr, + &vf4_inputs, vf4tuned_eeprom_dump, }, { @@ -7970,7 +7957,7 @@ const Game Games[] = { "317-0387-com.pic", 0, 0x4000, 0x8728aeaa }, }, "gds-0036a", - nullptr, + &vf4_inputs, vf4tuned_eeprom_dump, }, { @@ -8080,6 +8067,8 @@ const Game Games[] = { "ic69s", 0x07000000, 0x01000000, 0xc78e46c2 }, { "317-0408-com.ic15", 0, 0x800, 0xf77c49dc, Key }, }, + nullptr, + &dinok_inputs, }, { "galilfac", @@ -8149,6 +8138,8 @@ const Game Games[] = { "ic63", 0x04000000, 0x4000000, 0xd3870287 }, { "317-0446-com.ic15", 0, 0x800, 0x9e519dc6, Key }, }, + nullptr, + &lovebery_inputs, }, { "lovebero", @@ -8164,6 +8155,8 @@ const Game Games[] = { "ic63", 0x04000000, 0x4000000, 0xd3870287 }, { "317-0446-com.ic15", 0, 0x800, 0x9e519dc6, Key }, }, + nullptr, + &lovebery_inputs, }, { "magicpop", @@ -8261,6 +8254,8 @@ const Game Games[] = { "ic63", 0x04000000, 0x4000000, 0xcb946213 }, { "317-0604-com.ic15", 0, 0x800, 0xa46dfd47, Key }, }, + nullptr, + &tetgiant_inputs, }, { "unomedal", @@ -8307,6 +8302,7 @@ const Game Games[] = { "317-0408-com.ic15", 0, 0x800, 0xf77c49dc, Key }, }, "mda-c0021", + &dinok_inputs, }, { // Dinosaur King - D-Team VS. the Alpha Fortress (Export, Ver 2.500) @@ -8322,6 +8318,7 @@ const Game Games[] = { "317-0408-com.ic15", 0, 0x800, 0xf77c49dc, Key }, }, "mda-c0047", + &dinok_inputs, }, { // Konglongwang - D-Kids VS Alpha Yaosai (China, Ver 2.501) @@ -8338,6 +8335,7 @@ const Game Games[] = { "317-0408-com.ic15", 0, 0x800, 0xf77c49dc, Key }, }, "mda-c0081", + &dinok_inputs, }, { // Kodai Ouja Kyouryuu King - Mezame yo! Arata-naru Chikara!! (Japan, Ver 4.000) @@ -8354,6 +8352,7 @@ const Game Games[] = { "317-0408-com.ic15", 0, 0x800, 0xf77c49dc, Key }, }, "mda-c0061", + &dinok_inputs, }, { // Heat Up Hockey Image (Ver.1.003R) @@ -8399,6 +8398,7 @@ const Game Games[] = { "317-0446-com.ic15", 0, 0x800, 0x9e519dc6, Key }, }, "mda-c0042", + &lovebery_inputs, }, { // Love And Berry - 3rd-5th Collection (China, Ver 1.001) @@ -8415,6 +8415,7 @@ const Game Games[] = { "317-0446-com.ic15", 0, 0x800, 0x9e519dc6, Key }, }, "mda-c0071", + &lovebery_inputs, }, { "tetgiano", @@ -8429,6 +8430,7 @@ const Game Games[] = { "317-0604-com.ic15", 0, 0x800, 0xa46dfd47, Key }, }, "mda-c0076", + &tetgiant_inputs, }, { nullptr diff --git a/core/hw/naomi/naomi_roms_input.h b/core/hw/naomi/naomi_roms_input.h index 1e2245281..7151bfb4d 100644 --- a/core/hw/naomi/naomi_roms_input.h +++ b/core/hw/naomi/naomi_roms_input.h @@ -30,15 +30,25 @@ { NAOMI_TEST_KEY, "" }, \ { NAOMI_SERVICE_KEY, "" }, #define NAO_START_DESC { NAOMI_START_KEY, "" }, +#define NAO_DPAD_DESC { NAOMI_UP_KEY, "" }, \ + { NAOMI_DOWN_KEY, "" }, \ + { NAOMI_LEFT_KEY, "" }, \ + { NAOMI_RIGHT_KEY, "" }, \ + +#define INPUT_1_BUTTON(btn0) { \ + { \ + { NAOMI_BTN0_KEY, btn0 }, \ + NAO_DPAD_DESC \ + NAO_START_DESC \ + NAO_BASE_BTN_DESC \ + } \ +} #define INPUT_2_BUTTONS(btn0, btn1) { \ { \ { NAOMI_BTN0_KEY, btn0 }, \ { NAOMI_BTN1_KEY, btn1 }, \ - { NAOMI_UP_KEY, "" }, \ - { NAOMI_DOWN_KEY, "" }, \ - { NAOMI_LEFT_KEY, "" }, \ - { NAOMI_RIGHT_KEY, "" }, \ + NAO_DPAD_DESC \ NAO_START_DESC \ NAO_BASE_BTN_DESC \ } \ @@ -49,10 +59,7 @@ { NAOMI_BTN0_KEY, btn0 }, \ { NAOMI_BTN1_KEY, btn1 }, \ { NAOMI_BTN2_KEY, btn2 }, \ - { NAOMI_UP_KEY, "" }, \ - { NAOMI_DOWN_KEY, "" }, \ - { NAOMI_LEFT_KEY, "" }, \ - { NAOMI_RIGHT_KEY, "" }, \ + NAO_DPAD_DESC \ NAO_START_DESC \ NAO_BASE_BTN_DESC \ } \ @@ -64,10 +71,7 @@ { NAOMI_BTN1_KEY, btn1 }, \ { NAOMI_BTN2_KEY, btn2 }, \ { NAOMI_BTN3_KEY, btn3 }, \ - { NAOMI_UP_KEY, "" }, \ - { NAOMI_DOWN_KEY, "" }, \ - { NAOMI_LEFT_KEY, "" }, \ - { NAOMI_RIGHT_KEY, "" }, \ + NAO_DPAD_DESC \ NAO_START_DESC \ NAO_BASE_BTN_DESC \ } \ @@ -80,15 +84,18 @@ { NAOMI_BTN2_KEY, btn2 }, \ { NAOMI_BTN3_KEY, btn3 }, \ { NAOMI_BTN5_KEY, btn4, NAOMI_BTN4_KEY }, \ - { NAOMI_UP_KEY, "" }, \ - { NAOMI_DOWN_KEY, "" }, \ - { NAOMI_LEFT_KEY, "" }, \ - { NAOMI_RIGHT_KEY, "" }, \ + NAO_DPAD_DESC \ NAO_START_DESC \ NAO_BASE_BTN_DESC \ } \ } +static InputDescriptors service_btns_inputs = { + { + NAO_BASE_BTN_DESC + } +}; + static InputDescriptors _18wheelr_inputs = { { { NAOMI_BTN0_KEY, "HORN" }, @@ -145,10 +152,7 @@ static InputDescriptors capcom_4btn_inputs = { { NAOMI_BTN1_KEY, "HEAVY PUNCH" }, { NAOMI_BTN3_KEY, "LIGHT KICK" }, { NAOMI_BTN4_KEY, "HEAVY KICK" }, - { NAOMI_UP_KEY, "" }, - { NAOMI_DOWN_KEY, "" }, - { NAOMI_LEFT_KEY, "" }, - { NAOMI_RIGHT_KEY, "" }, + NAO_DPAD_DESC \ NAO_START_DESC NAO_BASE_BTN_DESC }, @@ -162,10 +166,7 @@ static InputDescriptors capcom_6btn_inputs = { { NAOMI_BTN3_KEY, "LIGHT KICK" }, { NAOMI_BTN4_KEY, "MEDIUM KICK" }, { NAOMI_BTN5_KEY, "HEAVY KICK" }, - { NAOMI_UP_KEY, "" }, - { NAOMI_DOWN_KEY, "" }, - { NAOMI_LEFT_KEY, "" }, - { NAOMI_RIGHT_KEY, "" }, + NAO_DPAD_DESC \ NAO_START_DESC NAO_BASE_BTN_DESC }, @@ -193,9 +194,10 @@ static InputDescriptors toyfight_inputs = INPUT_3_BUTTONS("Punch", "Kick", "Dodg static InputDescriptors ausfache_inputs = INPUT_3_BUTTONS("Weak Attack", "Medium Attack", "Strong Attack"); -static InputDescriptors trigger_inputs = { +static InputDescriptors lightgun_inputs = { { { NAOMI_BTN0_KEY, "TRIGGER" }, + { NAOMI_RELOAD_KEY, "" }, NAO_START_DESC NAO_BASE_BTN_DESC }, @@ -241,10 +243,7 @@ static InputDescriptors mvsc2_inputs = { { NAOMI_BTN3_KEY, "LIGHT KICK" }, { NAOMI_BTN4_KEY, "STRONG KICK" }, { NAOMI_BTN5_KEY, "ASSIST B" }, - { NAOMI_UP_KEY, "" }, - { NAOMI_DOWN_KEY, "" }, - { NAOMI_LEFT_KEY, "" }, - { NAOMI_RIGHT_KEY, "" }, + NAO_DPAD_DESC \ NAO_START_DESC NAO_BASE_BTN_DESC }, @@ -255,8 +254,19 @@ static InputDescriptors ninjaslt_inputs = { { NAOMI_BTN2_KEY, "ENTER", NAOMI_BTN0_KEY }, { NAOMI_START_KEY, "", NAOMI_BTN2_KEY, 0, NAOMI_BTN3_KEY }, { NAOMI_BTN0_KEY, "TRIGGER", NAOMI_BTN4_KEY, 0, NAOMI_BTN5_KEY }, + { NAOMI_RELOAD_KEY, "" }, + { NAOMI_UP_KEY, "SELECT UP" }, + { NAOMI_DOWN_KEY, "SELECT DOWN" }, + NAO_BASE_BTN_DESC + }, +}; + +static InputDescriptors mazan_inputs = { + { + { NAOMI_BTN0_KEY, "TRIGGER" }, { NAOMI_UP_KEY, "SELECT UP" }, { NAOMI_DOWN_KEY, "SELECT DOWN" }, + NAO_START_DESC NAO_BASE_BTN_DESC }, }; @@ -291,7 +301,7 @@ static InputDescriptors pstone2_inputs = INPUT_3_BUTTONS("Punch", "Jump", "Attac static InputDescriptors shot1234_inputs = INPUT_4_BUTTONS("SHOT1", "SHOT2", "SHOT3", "SHOT4"); -static InputDescriptors radirgyn_inputs = INPUT_3_BUTTONS("SHOOT", "SWORD", "SHIELD/SPECIAL"); +static InputDescriptors radirgy_inputs = INPUT_3_BUTTONS("SHOOT", "SWORD", "SHIELD/SPECIAL"); static InputDescriptors mamonoro_inputs = INPUT_2_BUTTONS("SHOOT", "SPECIAL"); @@ -396,10 +406,7 @@ static InputDescriptors zombie_inputs = { { NAOMI_BTN0_KEY, "L" }, { NAOMI_BTN1_KEY, "R" }, { NAOMI_BTN2_KEY, "G" }, - { NAOMI_UP_KEY, "" }, - { NAOMI_DOWN_KEY, "" }, - { NAOMI_LEFT_KEY, "" }, - { NAOMI_RIGHT_KEY, "" }, + NAO_DPAD_DESC \ NAO_START_DESC NAO_BASE_BTN_DESC }, @@ -460,6 +467,7 @@ static InputDescriptors guilty_gear_inputs = INPUT_5_BUTTONS("KICK", "SLASH", "H static InputDescriptors ggx_inputs = INPUT_4_BUTTONS("PUNCH", "KICK", "SLASH", "HSLASH"); +static InputDescriptors senko_inputs = INPUT_3_BUTTONS("ACTION", "MAIN", "SUB"); static InputDescriptors senkosp_inputs = INPUT_5_BUTTONS("MAIN", "SUB", "MAIN+SUB", "ACTION", "OVER DRIVE"); static InputDescriptors meltyb_inputs = INPUT_5_BUTTONS("LAttack", "MAttack", "HAttack", "Guard", "Quick Action"); @@ -484,6 +492,137 @@ static InputDescriptors shootout_inputs = { } }; +static InputDescriptors vf4_inputs = INPUT_3_BUTTONS("PUNCH", "KICK", "GUARD"); + +static InputDescriptors crackindj_inputs = { + { + NAO_START_DESC + NAO_BASE_BTN_DESC + }, + { + { "FADER", Full, 0, true }, + }, +}; + +static InputDescriptors shaktam_inputs = { + { + NAO_START_DESC + NAO_BASE_BTN_DESC + { NAOMI_BTN0_KEY, "SHAKE L" }, + { NAOMI_BTN1_KEY, "SHAKE R" }, + { NAOMI_BTN2_KEY, "KNOCK", NAOMI_DOWN_KEY }, + { NAOMI_DOWN_KEY, "DOWN", NAOMI_LEFT_KEY }, + { NAOMI_UP_KEY, "UP", NAOMI_RIGHT_KEY }, + }, + { + { "TAMBOURINE X", Full, 0 }, + { "TAMBOURINE Y", Full, 1 }, + { "", Full, 2 }, // unused but P2 starts at axis 4 + { "", Full, 3 }, // unused but P2 starts at axis 4 + }, +}; + +static InputDescriptors mushik_inputs = { + { + { NAOMI_BTN0_KEY, "HIT" }, + { NAOMI_BTN1_KEY, "PINCH" }, + { NAOMI_BTN2_KEY, "THROW" }, + NAO_BASE_BTN_DESC + }, +}; + +static InputDescriptors csmash_inputs = INPUT_2_BUTTONS("SMASH", "JUMP"); +static InputDescriptors otrigger_inputs = INPUT_3_BUTTONS("TRIGGER", "CHANGE", "JUMP"); +static InputDescriptors puyoda_inputs = INPUT_1_BUTTON("STAR"); +static InputDescriptors sgtetris_inputs = INPUT_2_BUTTONS("SW1", "SW2"); +static InputDescriptors virnba_inputs = INPUT_2_BUTTONS("PASS", "SHOOT"); +static InputDescriptors vs2_2k_inputs = INPUT_3_BUTTONS("LONG PASS", "SHOOT", "SHORT PASS"); +static InputDescriptors wwfroyal_inputs = INPUT_3_BUTTONS("ATTACK", "GRAPPLE", "SUPPORT"); +static InputDescriptors asndynmt_inputs = INPUT_3_BUTTONS("PUNCH", "KICK", "JUMP"); +static InputDescriptors illvelo_inputs = INPUT_3_BUTTONS("SHOT", "DOLL", "SPECIAL"); +static InputDescriptors rhytngk_inputs = INPUT_2_BUTTONS("SHOT A", "SHOT B"); +static InputDescriptors sl2007_inputs = INPUT_3_BUTTONS("PUSH 1", "PUSH 2", "PUSH 3"); +static InputDescriptors azumanga_inputs = INPUT_1_BUTTON("BUTTON A"); +static InputDescriptors bdrdown_inputs = INPUT_3_BUTTONS("SHOT", "LASER", "SPEED"); +static InputDescriptors cfield_inputs = INPUT_3_BUTTONS("TRG1", "TRG2", "TRG3"); +static InputDescriptors button12_inputs = INPUT_2_BUTTONS("BUTTON 1", "BUTTON 2"); +static InputDescriptors ikaruga_inputs = INPUT_2_BUTTONS("SHOT", "CHANGE"); +static InputDescriptors jingystm_inputs = INPUT_3_BUTTONS("GUARD", "PUNCH", "KICK"); +static InputDescriptors psyvariar_inputs = INPUT_2_BUTTONS("SHOT", "BOMB"); +static InputDescriptors puyofev_inputs = INPUT_2_BUTTONS("ROTATE1", "ROTATE2"); +static InputDescriptors spkrbtl_inputs = INPUT_4_BUTTONS("BEAT", "CHARGE", "JUMP", "SHIFT"); +static InputDescriptors trgheart_inputs = INPUT_3_BUTTONS("SHOT", "ANCHOR", "BOMB"); +static InputDescriptors vathlete_inputs = INPUT_3_BUTTONS("RUN1", "ACTION", "RUN2"); + +static InputDescriptors samba_inputs = { + { + { NAOMI_BTN0_KEY, "MARACAS R" }, + { NAOMI_BTN1_KEY, "MARACAS L" }, + NAO_START_DESC + NAO_BASE_BTN_DESC + }, + { + { "MARACAS R X", Full, 0 }, + { "MARACAS R Y", Full, 1 }, + { "MARACAS L X", Full, 2 }, + { "MARACAS L Y", Full, 3 }, + } +}; + +static InputDescriptors wldkicks_inputs = { + { + { NAOMI_BTN0_KEY, "BUTTON" }, + { NAOMI_BTN3_KEY, "ENTER" }, // service mode + { NAOMI_UP_KEY, "" }, // service mode + { NAOMI_DOWN_KEY, "" }, // service mode + NAO_BASE_BTN_DESC + }, + { + { "STICK L/R", Full, 0 }, // P1 + { "STICK U/D", Full, 1 }, + { "", Full, 2 }, // P2 + { "", Full, 3 }, + { "", Full, 4 }, // P3 + { "", Full, 5 }, + { "", Full, 6 }, // P4 + { "", Full, 7 }, + { "KICK", Full, 8 }, // P1 FIXME need to set Full here to have read_analog_axis() called but not seen as trigger + { "", Full, 9 }, // P2 + { "", Full, 10 }, // P3 + { "", Full, 11 }, // P4 + } +}; +static InputDescriptors wldkickspcb_inputs = { + { + { NAOMI_BTN0_KEY, "CHANGE" }, // original label: C BUTTON + NAO_START_DESC + NAO_BASE_BTN_DESC + }, + { + { "STICK L/R", Full, 0 }, + { "STICK U/D", Full, 1 }, + { "", Full, 2 }, +// { "", Full, 3 }, + { "BALL", Half, 4 }, // this is wrong, just to indicate RT is used + } +}; + +static InputDescriptors dygolf_inputs = { + { + NAO_DPAD_DESC + NAO_START_DESC + NAO_BASE_BTN_DESC + } +}; + +static InputDescriptors kick4csh_inputs = { + { + { NAOMI_BTN1_KEY, "VIEW" }, + { NAOMI_BTN2_KEY, "CHANCE" }, + { NAOMI_START_KEY, "START/DECIDE" }, + NAO_BASE_BTN_DESC + } +}; // // AtomisWave games // @@ -492,6 +631,11 @@ static InputDescriptors shootout_inputs = { { AWAVE_TEST_KEY, "" }, \ { AWAVE_SERVICE_KEY, "" }, #define AW_START_DESC { AWAVE_START_KEY, "" }, +#define AW_DPAD_DESC { AWAVE_UP_KEY, "" }, \ + { AWAVE_DOWN_KEY, "" }, \ + { AWAVE_LEFT_KEY, "" }, \ + { AWAVE_RIGHT_KEY, "" }, + #define AW_5_BUTTONS(btn0, btn1, btn2, btn3, btn4) { \ { \ @@ -500,10 +644,7 @@ static InputDescriptors shootout_inputs = { { AWAVE_BTN2_KEY, btn2 }, \ { AWAVE_BTN3_KEY, btn3 }, \ { AWAVE_BTN4_KEY, btn4 }, \ - { AWAVE_UP_KEY, "" }, \ - { AWAVE_DOWN_KEY, "" }, \ - { AWAVE_LEFT_KEY, "" }, \ - { AWAVE_RIGHT_KEY, "" }, \ + AW_DPAD_DESC \ AW_START_DESC \ AW_BASE_BTN_DESC \ } \ @@ -515,10 +656,7 @@ static InputDescriptors shootout_inputs = { { AWAVE_BTN1_KEY, btn1 }, \ { AWAVE_BTN2_KEY, btn2 }, \ { AWAVE_BTN3_KEY, btn3 }, \ - { AWAVE_UP_KEY, "" }, \ - { AWAVE_DOWN_KEY, "" }, \ - { AWAVE_LEFT_KEY, "" }, \ - { AWAVE_RIGHT_KEY, "" }, \ + AW_DPAD_DESC \ AW_START_DESC \ AW_BASE_BTN_DESC \ } \ @@ -529,10 +667,7 @@ static InputDescriptors shootout_inputs = { { AWAVE_BTN0_KEY, btn0 }, \ { AWAVE_BTN1_KEY, btn1 }, \ { AWAVE_BTN2_KEY, btn2 }, \ - { AWAVE_UP_KEY, "" }, \ - { AWAVE_DOWN_KEY, "" }, \ - { AWAVE_LEFT_KEY, "" }, \ - { AWAVE_RIGHT_KEY, "" }, \ + AW_DPAD_DESC \ AW_START_DESC \ AW_BASE_BTN_DESC \ } \ @@ -604,6 +739,32 @@ static InputDescriptors mslug6_inputs = AW_5_BUTTONS("SHOOT", "JUMP", "GRENADE", static InputDescriptors rumblef_inputs = AW_5_BUTTONS("LP", "SP", "Dodge", "LK", "SK"); +static InputDescriptors basschal_inputs = { + { + { AWAVE_BTN0_KEY, "ROTATE LEFT" }, + { AWAVE_BTN1_KEY, "ROTATE RIGHT" }, + { AWAVE_LEFT_KEY, "LEFT POINT" }, + { AWAVE_RIGHT_KEY, "RIGHT POINT" }, + AW_START_DESC + AW_BASE_BTN_DESC + }, +}; + +static InputDescriptors aw_lightgun_inputs = { + { + { AWAVE_BTN0_KEY, "TRIGGER" }, + { AWAVE_BTN1_KEY, "PUMP" }, + AW_START_DESC + AW_BASE_BTN_DESC + }, +}; + +static InputDescriptors aw_shot123_inputs = AW_3_BUTTONS("SHOT1", "SHOT2", "SHOT3"); + +// +// Naomi 2 +// + static InputDescriptors kingrt66_inputs = { { { NAOMI_BTN0_KEY, "HORN" }, @@ -724,30 +885,38 @@ static InputDescriptors drvsim_inputs = { }, }; -static InputDescriptors crackindj_inputs = { - { - NAO_START_DESC - NAO_BASE_BTN_DESC - }, +static InputDescriptors beachspi_inputs = INPUT_2_BUTTONS("A", "B"); + +// +// System SP games +// + +static InputDescriptors dinok_inputs = { { - { "FADER", Full, 0, true }, + { DC_BTN_A, "ROCK" }, + { DC_BTN_B, "SCISSORS" }, + { DC_BTN_C, "PAPER" }, + NAO_BASE_BTN_DESC }, }; -static InputDescriptors shaktam_inputs = { +static InputDescriptors lovebery_inputs = { { - NAO_START_DESC - NAO_BASE_BTN_DESC - { NAOMI_BTN0_KEY, "SHAKE L" }, - { NAOMI_BTN1_KEY, "SHAKE R" }, - { NAOMI_BTN2_KEY, "KNOCK", NAOMI_DOWN_KEY }, - { NAOMI_DOWN_KEY, "DOWN", NAOMI_LEFT_KEY }, - { NAOMI_UP_KEY, "UP", NAOMI_RIGHT_KEY }, + { DC_BTN_A, "P1 BUTTON" }, + { DC_BTN_B, "P2 BUTTON", 0, DC_BTN_A }, + NAO_BASE_BTN_DESC }, +}; + +static InputDescriptors tetgiant_inputs = { { - { "TAMBOURINE X", Full, 0 }, - { "TAMBOURINE Y", Full, 1 }, - { "", Full, 2 }, // unused but P2 starts at axis 4 - { "", Full, 3 }, // unused but P2 starts at axis 4 + { DC_BTN_A, "BUTTON L" }, + { DC_BTN_B, "BUTTON R" }, + { DC_DPAD_UP, "" }, + { DC_DPAD_DOWN, "" }, + { DC_DPAD_LEFT, "" }, + { DC_DPAD_RIGHT, "" }, + { DC_BTN_START, "" }, + NAO_BASE_BTN_DESC }, }; diff --git a/core/hw/naomi/netdimm.cpp b/core/hw/naomi/netdimm.cpp index 4acc2a562..128f81850 100644 --- a/core/hw/naomi/netdimm.cpp +++ b/core/hw/naomi/netdimm.cpp @@ -33,7 +33,6 @@ const char *SERVER_NAME = "vfnet.flyca.st"; NetDimm::NetDimm(u32 size) : GDCartridge(size) { - schedId = sh4_sched_register(0, schedCallback, this); if (serverIp == 0) { hostent *hp = gethostbyname(SERVER_NAME); @@ -44,11 +43,6 @@ NetDimm::NetDimm(u32 size) : GDCartridge(size) } } -NetDimm::~NetDimm() -{ - sh4_sched_unregister(schedId); -} - void NetDimm::Init(LoadProgress *progress, std::vector *digest) { GDCartridge::Init(progress, digest); @@ -56,74 +50,6 @@ void NetDimm::Init(LoadProgress *progress, std::vector *digest) finalTuned = strcmp(game->name, "vf4tuned") == 0; } -u32 NetDimm::ReadMem(u32 address, u32 size) -{ - switch (address) - { - case NAOMI_DIMM_COMMAND: - DEBUG_LOG(NAOMI, "DIMM COMMAND read -> %x", dimm_command); - return dimm_command; - case NAOMI_DIMM_OFFSETL: - DEBUG_LOG(NAOMI, "DIMM OFFSETL read -> %x", dimm_offsetl); - return dimm_offsetl; - case NAOMI_DIMM_PARAMETERL: - DEBUG_LOG(NAOMI, "DIMM PARAMETERL read -> %x", dimm_parameterl); - return dimm_parameterl; - case NAOMI_DIMM_PARAMETERH: - DEBUG_LOG(NAOMI, "DIMM PARAMETERH read -> %x", dimm_parameterh); - return dimm_parameterh; - case NAOMI_DIMM_STATUS: - { - u32 rc = DIMM_STATUS & ~(((SB_ISTEXT >> 3) & 1) << 8); - static u32 lastRc; - if (rc != lastRc) - DEBUG_LOG(NAOMI, "DIMM STATUS read -> %x", rc); - lastRc = rc; - return rc; - } - default: - return GDCartridge::ReadMem(address, size); - } -} - -void NetDimm::WriteMem(u32 address, u32 data, u32 size) -{ - switch (address) - { - case NAOMI_DIMM_COMMAND: - dimm_command = data; - DEBUG_LOG(NAOMI, "DIMM COMMAND Write<%d>: %x", size, data); - return; - - case NAOMI_DIMM_OFFSETL: - dimm_offsetl = data; - DEBUG_LOG(NAOMI, "DIMM OFFSETL Write<%d>: %x", size, data); - return; - case NAOMI_DIMM_PARAMETERL: - dimm_parameterl = data; - DEBUG_LOG(NAOMI, "DIMM PARAMETERL Write<%d>: %x", size, data); - return; - case NAOMI_DIMM_PARAMETERH: - dimm_parameterh = data; - DEBUG_LOG(NAOMI, "DIMM PARAMETERH Write<%d>: %x", size, data); - return; - - case NAOMI_DIMM_STATUS: - DEBUG_LOG(NAOMI, "DIMM STATUS Write<%d>: %x", size, data); - if (data & 0x100) - // write 0 seems ignored - asic_CancelInterrupt(holly_EXP_PCI); - if ((data & 1) == 0) - // irq to dimm - process(); - return; - - default: - GDCartridge::WriteMem(address, data, size); - return; - } -} - bool NetDimm::Write(u32 offset, u32 size, u32 data) { // u8 b0 = data; @@ -137,11 +63,6 @@ bool NetDimm::Write(u32 offset, u32 size, u32 data) return true; } -int NetDimm::schedCallback(int tag, int sch_cycl, int jitter, void *arg) -{ - return ((NetDimm *)arg)->schedCallback(); -} - int NetDimm::schedCallback() { fd_set readFds {}; @@ -356,16 +277,6 @@ int NetDimm::schedCallback() return SH4_MAIN_CLOCK; } -void NetDimm::returnToNaomi(bool failed, u16 offsetl, u32 parameter) -{ - dimm_command = ((dimm_command & 0x7e00) + 0x400) | (failed ? 0xff : 0x4); - dimm_offsetl = offsetl; - dimm_parameterh = parameter >> 16; - dimm_parameterl = parameter; - verify(((SB_ISTEXT >> 3) & 1) == 0); - asic_RaiseInterrupt(holly_EXP_PCI); -} - void NetDimm::systemCmd(int cmd) { switch (cmd) @@ -386,6 +297,8 @@ void NetDimm::systemCmd(int cmd) addrspace::write32(0xc01fc04, (3 << 16) | 0x70000000 | (dimmBufferOffset >> 20)); // dimm board config 1 x 512 MB else if (dimm_data_size == 256_MB) addrspace::write32(0xc01fc04, (2 << 16) | 0x70000000 | (dimmBufferOffset >> 20)); // dimm board config 1 x 256 MB + else if (dimm_data_size == 128_MB) + addrspace::write32(0xc01fc04, (1 << 16) | 0x70000000 | (dimmBufferOffset >> 20)); // dimm board config 1 x 128 MB else die("Unsupported dimm mem size"); addrspace::write32(0xc01fc0c, 0x3170000 | 0x264); // fw version | 100/264/364? @@ -397,10 +310,14 @@ void NetDimm::systemCmd(int cmd) addrspace::write32(0xc01fc24, 0x3e000a); addrspace::write32(0xc01fc28, 0x18077f); addrspace::write32(0xc01fc2c, 0x10014); - // PIC16? - //addrspace::write32(0xc01fc40, .); - // ... - //addrspace::write32(0xc01fc54, .); + // DIMM board serial Id + { + const u32 *serial = (u32 *)(getGameSerialId() + 0x20); // get only the serial id + addrspace::write32(0xc01fc40, *serial++); + addrspace::write32(0xc01fc44, *serial++); + addrspace::write32(0xc01fc48, *serial++); + addrspace::write32(0xc01fc4c, *serial++); + } addrspace::write32(0xc01fc18, 0x10002); // net mode (2 or 4 is mobile), bit 16 dhcp? // network order addrspace::write32(0xc01fc60, htonl(0xc0a80101)); // ip address (192.168.1.1) @@ -810,29 +727,20 @@ void NetDimm::process() netCmd(cmd); break; default: - WARN_LOG(NAOMI, "Unknown DIMM command group %d cmd %x\n", cmdGroup, cmd); + WARN_LOG(NAOMI, "Unknown DIMM command group %d cmd %x", cmdGroup, cmd); returnToNaomi(true, 0, -1); break; } } -void NetDimm::Serialize(Serializer &ser) const -{ - GDCartridge::Serialize(ser); - ser << dimm_command; - ser << dimm_offsetl; - ser << dimm_parameterl; - ser << dimm_parameterh; - sh4_sched_serialize(ser, schedId); -} - void NetDimm::Deserialize(Deserializer &deser) { GDCartridge::Deserialize(deser); for (Socket& socket : sockets) socket.close(); - if (deser.version() >= Deserializer::V36) + if (deser.version() >= Deserializer::V36 && deser.version() < Deserializer::V53) { + // moved to parent class in v53 deser >> dimm_command; deser >> dimm_offsetl; deser >> dimm_parameterl; diff --git a/core/hw/naomi/netdimm.h b/core/hw/naomi/netdimm.h index 436e592ae..e936c70e0 100644 --- a/core/hw/naomi/netdimm.h +++ b/core/hw/naomi/netdimm.h @@ -24,23 +24,18 @@ class NetDimm : public GDCartridge { public: NetDimm(u32 size); - ~NetDimm() override; void Init(LoadProgress *progress = nullptr, std::vector *digest = nullptr) override; - u32 ReadMem(u32 address, u32 size) override; - void WriteMem(u32 address, u32 data, u32 size) override; - bool Write(u32 offset, u32 size, u32 data) override; - void Serialize(Serializer &ser) const override; void Deserialize(Deserializer &deser) override; +protected: + void process() override; + int schedCallback() override; + private: - void returnToNaomi(bool failed, u16 offsetl, u32 parameter); - static int schedCallback(int tag, int sch_cycl, int jitter, void *arg); - int schedCallback(); - void process(); void systemCmd(int cmd); void netCmd(int cmd); @@ -67,29 +62,6 @@ class NetDimm : public GDCartridge dimm_parameterh = value >> 16; } - template - void peek(u32 address) - { - static_assert(sizeof(T) == 1 || sizeof(T) == 2 || sizeof(T) == 4); - int size; - switch (sizeof(T)) - { - case 1: - size = 4; - break; - case 2: - size = 5; - break; - case 4: - size = 6; - break; - } - dimm_command = ((address >> 16) & 0x1ff) | (size << 9) | 0x8000; - dimm_offsetl = address & 0xffff; - dimm_parameterl = 0; - dimm_parameterh = 0; - } - sock_t getSocket(int idx) { if (idx < 1 || idx > (int)sockets.size()) @@ -105,13 +77,6 @@ class NetDimm : public GDCartridge return false; } - u16 dimm_command; - u16 dimm_offsetl; - u16 dimm_parameterl; - u16 dimm_parameterh; - static constexpr u16 DIMM_STATUS = 0x111; - int schedId; - struct Socket { Socket() = default; Socket(sock_t fd) : fd(fd) {} diff --git a/core/hw/naomi/systemsp.cpp b/core/hw/naomi/systemsp.cpp index 02935dd10..7f1fb8e2b 100644 --- a/core/hw/naomi/systemsp.cpp +++ b/core/hw/naomi/systemsp.cpp @@ -988,8 +988,37 @@ class DefaultIOManager : public IOPortManager u64 startTime = 0; }; - void getInputState() { + void getInputState() + { ggpo::getInput(mapleInputState); + if (NaomiGameInputs != nullptr) + { + for (const ButtonDescriptor& bd : NaomiGameInputs->buttons) + { + if (bd.name == nullptr) + break; + if (bd.target != 0) + { + // remap P1 -> P1 and P2 -> P2 + if ((mapleInputState[0].kcode & bd.source) == 0) + mapleInputState[0].kcode &= ~bd.target; + if ((mapleInputState[1].kcode & bd.source) == 0) + mapleInputState[1].kcode &= ~bd.target; + } + else if (bd.p2_target != 0) + { + // remap P1 -> P2 + if ((mapleInputState[0].kcode & bd.source) == 0) + mapleInputState[1].kcode &= ~bd.p2_target; + } + else if (bd.p1_target != 0) + { + // remap P2 -> P1 + if ((mapleInputState[1].kcode & bd.source) == 0) + mapleInputState[0].kcode &= ~bd.p1_target; + } + } + } } MapleInputState mapleInputState[4]; diff --git a/core/hw/pvr/Renderer_if.cpp b/core/hw/pvr/Renderer_if.cpp index 86ce77c7d..7f669f3fd 100644 --- a/core/hw/pvr/Renderer_if.cpp +++ b/core/hw/pvr/Renderer_if.cpp @@ -1,12 +1,13 @@ #include "Renderer_if.h" #include "spg.h" -#include "rend/TexCache.h" +#include "rend/texconv.h" #include "rend/transform_matrix.h" #include "cfg/option.h" #include "emulator.h" #include "serialize.h" #include "hw/holly/holly_intc.h" #include "hw/sh4/sh4_if.h" +#include "hw/sh4/sh4_core.h" #include "profiler/fc_profiler.h" #include "network/ggpo.h" @@ -90,15 +91,12 @@ class PvrMessageQueue } else { - void setDefaultRoundingMode(); - void RestoreHostRoundingMode(); - setDefaultRoundingMode(); // drain the queue after switching to !threaded rendering while (!queue.empty()) waitAndExecute(); execute(msg); - RestoreHostRoundingMode(); + Sh4cntx.restoreHostRoundingMode(); } } @@ -237,7 +235,7 @@ class PvrMessageQueue { presented = true; if (!config::ThreadedRendering && !ggpo::active()) - sh4_cpu.Stop(); + emu.getSh4Executor()->Stop(); #ifdef LIBRETRO retro_rend_present(); #endif @@ -321,7 +319,7 @@ bool rend_init_renderer() rendererEnabled = true; if (renderer == nullptr) rend_create_renderer(); - bool success = renderer->Init(); + bool success = renderer != nullptr && renderer->Init(); if (!success) { delete renderer; renderer = rend_norend(); diff --git a/core/hw/pvr/Renderer_if.h b/core/hw/pvr/Renderer_if.h index d7f7f17a8..c9a5e29af 100644 --- a/core/hw/pvr/Renderer_if.h +++ b/core/hw/pvr/Renderer_if.h @@ -1,6 +1,7 @@ #pragma once #include "types.h" #include "ta_ctx.h" +#include "emulator.h" #include extern u32 FrameCount; @@ -22,6 +23,8 @@ void rend_enable_renderer(bool enabled); bool rend_is_enabled(); void rend_serialize(Serializer& ser); void rend_deserialize(Deserializer& deser); +static void rend_updatePalette(); +static void rend_updateFogTable(); /////// extern TA_context* _pvrrc; @@ -54,7 +57,14 @@ struct FramebufferInfo struct Renderer { - virtual ~Renderer() = default; + Renderer() { + EventManager::listen(Event::Terminate, onEvent, this); + EventManager::listen(Event::LoadState, onEvent, this); + } + virtual ~Renderer() { + EventManager::unlisten(Event::Terminate, onEvent, this); + EventManager::unlisten(Event::LoadState, onEvent, this); + } virtual bool Init() = 0; virtual void Term() = 0; @@ -71,9 +81,26 @@ struct Renderer virtual bool Present() { return true; } - virtual void DrawOSD(bool clear_screen) { } - virtual BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) { return nullptr; } + +protected: + bool resetTextureCache = false; + bool clearLastFrame = false; + bool updatePalette = true; + bool updateFogTable = true; + +private: + static void onEvent(Event event, void *arg) + { + Renderer *renderer = static_cast(arg); + renderer->resetTextureCache = true; + renderer->updatePalette = true; + renderer->updateFogTable = true; + if (event == Event::Terminate) + renderer->clearLastFrame = true; + } + friend void rend_updatePalette(); + friend void rend_updateFogTable(); }; extern Renderer* renderer; @@ -83,3 +110,11 @@ 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; +} +static inline void rend_updateFogTable() { + if (renderer != nullptr) + renderer->updateFogTable = true; +} diff --git a/core/hw/pvr/pvr.cpp b/core/hw/pvr/pvr.cpp index 22bead33e..285496d45 100644 --- a/core/hw/pvr/pvr.cpp +++ b/core/hw/pvr/pvr.cpp @@ -21,7 +21,6 @@ #include "pvr_regs.h" #include "Renderer_if.h" #include "ta_ctx.h" -#include "rend/TexCache.h" #include "serialize.h" #include "pvr_mem.h" #include "elan.h" @@ -31,7 +30,6 @@ extern u8 ta_fsm[2049]; //[2048] stores the current state extern u32 ta_fsm_cl; extern u32 taRenderPass; // pvr_regs.cpp -extern bool fog_needs_update; extern bool pal_needs_update; namespace pvr @@ -39,7 +37,6 @@ namespace pvr void reset(bool hard) { - KillTex = true; Regs_Reset(hard); spg_Reset(hard); if (hard) @@ -92,7 +89,7 @@ void deserialize(Deserializer& deser) YUV_deserialize(deser); deser >> pvr_regs; - fog_needs_update = true; + rend_updateFogTable(); spg_Deserialize(deser); diff --git a/core/hw/pvr/pvr_regs.cpp b/core/hw/pvr/pvr_regs.cpp index 235c728fb..67eb5e714 100644 --- a/core/hw/pvr/pvr_regs.cpp +++ b/core/hw/pvr/pvr_regs.cpp @@ -6,7 +6,6 @@ #include bool pal_needs_update=true; -bool fog_needs_update=true; u8 pvr_regs[pvr_RegSize]; @@ -221,7 +220,7 @@ void pvr_WriteReg(u32 paddr,u32 data) if (addr >= PALETTE_RAM_START_addr && PvrReg(addr,u32) != data) pal_needs_update = true; else if (addr >= FOG_TABLE_START_addr && addr <= FOG_TABLE_END_addr && PvrReg(addr,u32) != data) - fog_needs_update = true; + rend_updateFogTable(); break; } PvrReg(addr, u32) = data; diff --git a/core/hw/pvr/spg.cpp b/core/hw/pvr/spg.cpp index 5fd93bc86..0e2115c77 100755 --- a/core/hw/pvr/spg.cpp +++ b/core/hw/pvr/spg.cpp @@ -235,7 +235,7 @@ static int spg_line_sched(int tag, int cycles, int jitter, void *arg) void read_lightgun_position(int x, int y) { - static u8 flip; + static u32 flip; maple_int_pending = true; if (y < 0 || y >= 480 || x < 0 || x >= 640) { @@ -245,9 +245,11 @@ void read_lightgun_position(int x, int y) else { lightgun_line = y / (SPG_CONTROL.interlace ? 2 : 1) + SPG_VBLANK_INT.vblank_out_interrupt_line_number; - // For some reason returning the same position twice makes it register off screen - lightgun_hpos = (x + 286) ^ flip; - flip ^= 1; + // For some reason returning the same position twice makes it register off screen. + // Atomiswave Clay Challenge wants more wiggle than others for shots to register. + lightgun_line ^= (flip >> 1) & 1; + lightgun_hpos = (x + 286) ^ (flip & 1); + flip++; } } diff --git a/core/hw/sh4/dyna/blockmanager.cpp b/core/hw/sh4/dyna/blockmanager.cpp index 794119537..ea8cce072 100644 --- a/core/hw/sh4/dyna/blockmanager.cpp +++ b/core/hw/sh4/dyna/blockmanager.cpp @@ -40,7 +40,7 @@ static bm_Map blkmap; u32 protected_blocks; u32 unprotected_blocks; -#define FPCA(x) ((DynarecCodeEntryPtr&)sh4rcb.fpcb[(x>>1)&FPCB_MASK]) +#define FPCA(x) ((DynarecCodeEntryPtr&)p_sh4rcb->fpcb[(x>>1)&FPCB_MASK]) // addr must be a physical address // This returns an executable address @@ -65,8 +65,8 @@ DynarecCodeEntryPtr DYNACALL bm_GetCodeByVAddr(u32 addr) #ifdef USE_WINCE_HACK case 0xfffffde7: // GetTickCount // This should make this syscall faster - r[0] = sh4_sched_now64() * 1000 / SH4_MAIN_CLOCK; - next_pc = pr; + Sh4cntx.r[0] = sh4_sched_now64() * 1000 / SH4_MAIN_CLOCK; + Sh4cntx.pc = Sh4cntx.pr; Sh4cntx.cycle_counter -= 100; break; @@ -75,11 +75,11 @@ DynarecCodeEntryPtr DYNACALL bm_GetCodeByVAddr(u32 addr) bool isRam; u64 *ptr; u32 paddr; - if (rdv_writeMemImmediate(r[4], sizeof(u64), (void*&)ptr, isRam, paddr) && isRam) + if (rdv_writeMemImmediate(Sh4cntx.r[4], sizeof(u64), (void*&)ptr, isRam, paddr) && isRam) { *ptr = sh4_sched_now64() >> 4; - r[0] = 1; - next_pc = pr; + Sh4cntx.r[0] = 1; + Sh4cntx.pc = Sh4cntx.pr; Sh4cntx.cycle_counter -= 100; } else @@ -94,7 +94,7 @@ DynarecCodeEntryPtr DYNACALL bm_GetCodeByVAddr(u32 addr) Do_Exception(addr, Sh4Ex_AddressErrorRead); break; } - addr = next_pc; + addr = Sh4cntx.pc; } u32 paddr; @@ -102,7 +102,7 @@ DynarecCodeEntryPtr DYNACALL bm_GetCodeByVAddr(u32 addr) if (rv != MmuError::NONE) { DoMMUException(addr, rv, MMU_TT_IREAD); - mmu_instruction_translation(next_pc, paddr); + mmu_instruction_translation(Sh4cntx.pc, paddr); } return bm_GetCode(paddr); @@ -431,10 +431,6 @@ void RuntimeBlockInfo::Discard() void RuntimeBlockInfo::SetProtectedFlags() { -#ifdef TARGET_NO_EXCEPTIONS - this->read_only = false; - return; -#endif // Don't write protect rom and BIOS/IP.BIN (Grandia II) if (!IsOnRam(addr) || (addr & 0x1FFF0000) == 0x0c000000) { @@ -476,7 +472,7 @@ void bm_RamWriteAccess(u32 addr) std::vector list_copy; list_copy.insert(list_copy.begin(), block_list.begin(), block_list.end()); if (!list_copy.empty()) - DEBUG_LOG(DYNAREC, "bm_RamWriteAccess write access to %08x pc %08x", addr, next_pc); + DEBUG_LOG(DYNAREC, "bm_RamWriteAccess write access to %08x pc %08x", addr, Sh4cntx.pc); for (auto& block : list_copy) bm_DiscardBlock(block); verify(block_list.empty()); diff --git a/core/hw/sh4/dyna/blockmanager.h b/core/hw/sh4/dyna/blockmanager.h index 333190489..fd6445f25 100644 --- a/core/hw/sh4/dyna/blockmanager.h +++ b/core/hw/sh4/dyna/blockmanager.h @@ -16,8 +16,8 @@ struct RuntimeBlockInfo bool Setup(u32 pc,fpscr_t fpu_cfg); u32 addr; - DynarecCodeEntryPtr code; u32 vaddr; + DynarecCodeEntryPtr code; u32 host_code_size; //in bytes u32 sh4_code_size; //in bytes @@ -27,8 +27,8 @@ struct RuntimeBlockInfo u32 guest_opcodes; u32 host_opcodes; // set by host code generator, optional bool has_fpu_op; - u32 blockcheck_failures; bool temp_block; + u32 blockcheck_failures; u32 BranchBlock; //if not 0xFFFFFFFF then jump target u32 NextBlock; //if not 0xFFFFFFFF then next block (by position) @@ -42,8 +42,11 @@ struct RuntimeBlockInfo BlockEndType BlockType; bool has_jcond; + bool read_only; std::vector oplist; + //predecessors references + std::vector pre_refs; bool containsCode(const void *ptr) { @@ -56,16 +59,11 @@ struct RuntimeBlockInfo return 0; } - //predecessors references - std::vector pre_refs; - void AddRef(const RuntimeBlockInfoPtr& other); void RemRef(const RuntimeBlockInfoPtr& other); void Discard(); void SetProtectedFlags(); - - bool read_only; }; void bm_WriteBlockMap(const std::string& file); @@ -86,15 +84,31 @@ void bm_Init(); void bm_Term(); void bm_vmem_pagefill(void** ptr,u32 size_bytes); -bool bm_RamWriteAccess(void *p); -void bm_RamWriteAccess(u32 addr); static inline bool bm_IsRamPageProtected(u32 addr) { extern bool unprotected_pages[RAM_SIZE_MAX/PAGE_SIZE]; addr &= RAM_MASK; return !unprotected_pages[addr / PAGE_SIZE]; } + +#if FEAT_SHREC != DYNAREC_NONE + +bool bm_RamWriteAccess(void *p); +void bm_RamWriteAccess(u32 addr); void bm_LockPage(u32 addr, u32 size = PAGE_SIZE); void bm_UnlockPage(u32 addr, u32 size = PAGE_SIZE); u32 bm_getRamOffset(void *p); +#else + +inline static bool bm_RamWriteAccess(void *p) { + return false; +} +inline static void bm_RamWriteAccess(u32 addr) {} +inline static void bm_LockPage(u32 addr, u32 size = PAGE_SIZE) {} +inline static void bm_UnlockPage(u32 addr, u32 size = PAGE_SIZE) {} +inline static u32 bm_getRamOffset(void *p) { + return 0; +} + +#endif diff --git a/core/hw/sh4/dyna/decoder.cpp b/core/hw/sh4/dyna/decoder.cpp index 457996ee3..cc4129ca7 100644 --- a/core/hw/sh4/dyna/decoder.cpp +++ b/core/hw/sh4/dyna/decoder.cpp @@ -95,9 +95,6 @@ static void dec_End(u32 dst, BlockEndType flags, bool delaySlot) verify(state.JumpAddr != NullAddress); } -#define SR_STATUS_MASK STATUS_MASK -#define SR_T_MASK 1 - static u32 dec_jump_simm8(u32 op) { return state.cpu.rpc + GetSImm8(op)*2 + 4; @@ -114,8 +111,8 @@ static u32 dec_set_pr() } static void dec_write_sr(shil_param src) { - Emit(shop_and,mk_reg(reg_sr_status),src,mk_imm(SR_STATUS_MASK)); - Emit(shop_and,mk_reg(reg_sr_T),src,mk_imm(SR_T_MASK)); + Emit(shop_and, mk_reg(reg_sr_status), src, mk_imm(sr_t::MASK)); + Emit(shop_and, mk_reg(reg_sr_T), src, mk_imm(1)); } //bf sh4dec(i1000_1011_iiii_iiii) @@ -987,11 +984,11 @@ bool dec_DecodeBlock(RuntimeBlockInfo* rbi,u32 max_cycles) if (!blk->has_fpu_op && OpDesc[op]->IsFloatingPoint()) { - if (sr.FD == 1) + if (Sh4cntx.sr.FD == 1) { // We need to know FPSCR to compile the block, so let the exception handler run first // as it may change the fp registers - Do_Exception(next_pc, Sh4Ex_FpuDisabled); + Do_Exception(Sh4cntx.pc, Sh4Ex_FpuDisabled); return false; } blk->has_fpu_op = true; diff --git a/core/hw/sh4/dyna/decoder_opcodes.h b/core/hw/sh4/dyna/decoder_opcodes.h index 6eee9fed6..b44c34fc6 100644 --- a/core/hw/sh4/dyna/decoder_opcodes.h +++ b/core/hw/sh4/dyna/decoder_opcodes.h @@ -1,8 +1,10 @@ #pragma once #if FEAT_SHREC != DYNAREC_NONE #define sh4dec(str) void dec_##str (u32 op) +void dec_illegalOp(u32 op); #else #define sh4dec(str) static void dec_##str (u32 op) { } +inline static void dec_illegalOp(u32 op) {} #endif sh4dec(i1000_1011_iiii_iiii); @@ -33,5 +35,3 @@ sh4dec(i0100_nnnn_0110_1010); sh4dec(i0100_nnnn_0110_0110); sh4dec(i0100_nnnn_0001_1011); sh4dec(i0100_nnnn_0000_0011); - -void dec_illegalOp(u32 op); diff --git a/core/hw/sh4/dyna/driver.cpp b/core/hw/sh4/dyna/driver.cpp index 8c3a0c732..78009e77b 100644 --- a/core/hw/sh4/dyna/driver.cpp +++ b/core/hw/sh4/dyna/driver.cpp @@ -40,9 +40,9 @@ ptrdiff_t cc_rx_offset; static std::unordered_set smc_hotspots; -static sh4_if sh4Interp; static Sh4CodeBuffer codeBuffer; Sh4Dynarec *sh4Dynarec; +Sh4Recompiler *Sh4Recompiler::Instance; void *Sh4CodeBuffer::get() { @@ -83,32 +83,33 @@ void Sh4CodeBuffer::reset(bool temporary) lastAddr = 0; } -static void clear_temp_cache(bool full) +void Sh4Recompiler::clear_temp_cache(bool full) { //printf("recSh4:Temp Code Cache clear at %08X\n", curr_pc); codeBuffer.reset(true); bm_ResetTempCache(full); } -static void recSh4_ClearCache() +void Sh4Recompiler::ResetCache() { - INFO_LOG(DYNAREC, "recSh4:Dynarec Cache clear at %08X free space %d", next_pc, codeBuffer.getFreeSpace()); + INFO_LOG(DYNAREC, "recSh4:Dynarec Cache clear at %08X free space %d", getContext()->pc, codeBuffer.getFreeSpace()); codeBuffer.reset(false); bm_ResetCache(); smc_hotspots.clear(); clear_temp_cache(true); } -static void recSh4_Run() +void Sh4Recompiler::Run() { - RestoreHostRoundingMode(); + getContext()->restoreHostRoundingMode(); - u8 *sh4_dyna_rcb = (u8 *)&Sh4cntx + sizeof(Sh4cntx); - INFO_LOG(DYNAREC, "cntx // fpcb offset: %td // pc offset: %td // pc %08X", (u8*)&sh4rcb.fpcb - sh4_dyna_rcb, (u8*)&sh4rcb.cntx.pc - sh4_dyna_rcb, sh4rcb.cntx.pc); + u8 *sh4_dyna_rcb = (u8 *)getContext() + sizeof(Sh4Context); + INFO_LOG(DYNAREC, "cntx // fpcb offset: %td // pc offset: %td // pc %08X", (u8*)p_sh4rcb->fpcb - sh4_dyna_rcb, + (u8*)&getContext()->pc - sh4_dyna_rcb, getContext()->pc); sh4Dynarec->mainloop(sh4_dyna_rcb); - sh4_int_bCpuRun = false; + getContext()->CpuRunning = false; } void AnalyseBlock(RuntimeBlockInfo* blk); @@ -168,14 +169,14 @@ bool RuntimeBlockInfo::Setup(u32 rpc,fpscr_t rfpu_cfg) DynarecCodeEntryPtr rdv_CompilePC(u32 blockcheck_failures) { - const u32 pc = next_pc; + const u32 pc = Sh4cntx.pc; if (codeBuffer.getFreeSpace() < 32_KB || pc == 0x8c0000e0 || pc == 0xac010000 || pc == 0xac008300) - recSh4_ClearCache(); + Sh4Recompiler::Instance->ResetCache(); RuntimeBlockInfo* rbi = sh4Dynarec->allocateBlock(); - if (!rbi->Setup(pc, fpscr)) + if (!rbi->Setup(pc, Sh4cntx.fpscr)) { delete rbi; return nullptr; @@ -185,7 +186,7 @@ DynarecCodeEntryPtr rdv_CompilePC(u32 blockcheck_failures) { codeBuffer.useTempBuffer(true); if (codeBuffer.getFreeSpace() < 32_KB) - clear_temp_cache(false); + Sh4Recompiler::Instance->clear_temp_cache(false); rbi->temp_block = true; if (rbi->read_only) INFO_LOG(DYNAREC, "WARNING: temp block %x (%x) is protected!", rbi->vaddr, rbi->addr); @@ -204,16 +205,16 @@ DynarecCodeEntryPtr rdv_CompilePC(u32 blockcheck_failures) DynarecCodeEntryPtr DYNACALL rdv_FailedToFindBlock_pc() { - return rdv_FailedToFindBlock(next_pc); + return rdv_FailedToFindBlock(Sh4cntx.pc); } DynarecCodeEntryPtr DYNACALL rdv_FailedToFindBlock(u32 pc) { //DEBUG_LOG(DYNAREC, "rdv_FailedToFindBlock %08x", pc); - next_pc=pc; + Sh4cntx.pc=pc; DynarecCodeEntryPtr code = rdv_CompilePC(0); if (code == NULL) - code = bm_GetCodeByVAddr(next_pc); + code = bm_GetCodeByVAddr(Sh4cntx.pc); else code = (DynarecCodeEntryPtr)CC_RW2RX(code); return code; @@ -247,15 +248,15 @@ DynarecCodeEntryPtr DYNACALL rdv_BlockCheckFail(u32 addr) } else { - next_pc = addr; - recSh4_ClearCache(); + Sh4cntx.pc = addr; + Sh4Recompiler::Instance->ResetCache(); } return (DynarecCodeEntryPtr)CC_RW2RX(rdv_CompilePC(blockcheck_failures)); } DynarecCodeEntryPtr rdv_FindOrCompile() { - DynarecCodeEntryPtr rv = bm_GetCodeByVAddr(next_pc); // Returns exec addr + DynarecCodeEntryPtr rv = bm_GetCodeByVAddr(Sh4cntx.pc); // Returns exec addr if (rv == ngen_FailedToFindBlock) rv = (DynarecCodeEntryPtr)CC_RW2RX(rdv_CompilePC(0)); // Returns rw addr @@ -281,20 +282,20 @@ void* DYNACALL rdv_LinkBlock(u8* code,u32 dpc) if (bcls == BET_CLS_Static) { if (rbi->BlockType == BET_StaticIntr) - next_pc = rbi->NextBlock; + Sh4cntx.pc = rbi->NextBlock; else - next_pc = rbi->BranchBlock; + Sh4cntx.pc = rbi->BranchBlock; } else if (bcls == BET_CLS_Dynamic) { - next_pc = dpc; + Sh4cntx.pc = dpc; } else if (bcls == BET_CLS_COND) { if (dpc) - next_pc = rbi->BranchBlock; + Sh4cntx.pc = rbi->BranchBlock; else - next_pc = rbi->NextBlock; + Sh4cntx.pc = rbi->NextBlock; } DynarecCodeEntryPtr rv = rdv_FindOrCompile(); // Returns rx ptr @@ -313,17 +314,17 @@ void* DYNACALL rdv_LinkBlock(u8* code,u32 dpc) } else if (rbi->relink_data == 0) { - rbi->pBranchBlock = bm_GetBlock(next_pc).get(); + rbi->pBranchBlock = bm_GetBlock(Sh4cntx.pc).get(); rbi->pBranchBlock->AddRef(rbi); } } else { - RuntimeBlockInfo* nxt = bm_GetBlock(next_pc).get(); + RuntimeBlockInfo* nxt = bm_GetBlock(Sh4cntx.pc).get(); - if (rbi->BranchBlock == next_pc) + if (rbi->BranchBlock == Sh4cntx.pc) rbi->pBranchBlock = nxt; - if (rbi->NextBlock == next_pc) + if (rbi->NextBlock == Sh4cntx.pc) rbi->pNextBlock = nxt; nxt->AddRef(rbi); @@ -334,44 +335,28 @@ void* DYNACALL rdv_LinkBlock(u8* code,u32 dpc) } else { - INFO_LOG(DYNAREC, "null RBI: from %08X to %08X -- unlinked stale block -- code %p next %p", rbi->vaddr, next_pc, code, rv); + INFO_LOG(DYNAREC, "null RBI: from %08X to %08X -- unlinked stale block -- code %p next %p", rbi->vaddr, Sh4cntx.pc, code, rv); } return (void*)rv; } -static void recSh4_Start() +void Sh4Recompiler::Reset(bool hard) { - sh4Interp.Start(); -} - -static void recSh4_Stop() -{ - sh4Interp.Stop(); -} - -static void recSh4_Step() -{ - sh4Interp.Step(); -} - -static void recSh4_Reset(bool hard) -{ - sh4Interp.Reset(hard); - recSh4_ClearCache(); + super::Reset(hard); + ResetCache(); if (hard) bm_Reset(); } -static void recSh4_Init() +void Sh4Recompiler::Init() { - INFO_LOG(DYNAREC, "recSh4 Init"); - Get_Sh4Interpreter(&sh4Interp); - sh4Interp.Init(); + INFO_LOG(DYNAREC, "Sh4Recompiler::Init"); + super::Init(); bm_Init(); if (addrspace::virtmemEnabled()) - verify(&mem_b[0] == ((u8*)p_sh4rcb->sq_buffer + 512 + 0x0C000000)); + verify(&mem_b[0] == ((u8*)getContext()->sq_buffer + sizeof(Sh4Context) + 0x0C000000)); // Call the platform-specific magic to make the pages RWX CodeCache = nullptr; @@ -385,13 +370,13 @@ static void recSh4_Init() verify(CodeCache != nullptr); TempCodeCache = CodeCache + CODE_SIZE; - sh4Dynarec->init(codeBuffer); + sh4Dynarec->init(*getContext(), codeBuffer); bm_ResetCache(); } -static void recSh4_Term() +void Sh4Recompiler::Term() { - INFO_LOG(DYNAREC, "recSh4 Term"); + INFO_LOG(DYNAREC, "Sh4Recompiler::Term"); #ifdef FEAT_NO_RWX_PAGES if (CodeCache != nullptr) virtmem::release_jit_block(CodeCache, (u8 *)CodeCache + cc_rx_offset, FULL_SIZE); @@ -402,25 +387,12 @@ static void recSh4_Term() CodeCache = nullptr; TempCodeCache = nullptr; bm_Term(); - sh4Interp.Term(); -} - -static bool recSh4_IsCpuRunning() -{ - return sh4Interp.IsCpuRunning(); + super::Term(); } -void Get_Sh4Recompiler(sh4_if* cpu) +Sh4Executor *Get_Sh4Recompiler() { - cpu->Run = recSh4_Run; - cpu->Start = recSh4_Start; - cpu->Stop = recSh4_Stop; - cpu->Step = recSh4_Step; - cpu->Reset = recSh4_Reset; - cpu->Init = recSh4_Init; - cpu->Term = recSh4_Term; - cpu->IsCpuRunning = recSh4_IsCpuRunning; - cpu->ResetCache = recSh4_ClearCache; + return new Sh4Recompiler(); } static bool translateAddress(u32 addr, int size, u32 access, u32& outAddr, RuntimeBlockInfo* block) diff --git a/core/hw/sh4/dyna/ngen.h b/core/hw/sh4/dyna/ngen.h index 86b8ca7cb..852f8d15e 100644 --- a/core/hw/sh4/dyna/ngen.h +++ b/core/hw/sh4/dyna/ngen.h @@ -3,6 +3,7 @@ #pragma once #include "blockmanager.h" #include "oslib/host_context.h" +#include "../sh4_interpreter.h" // When NO_RWX is enabled there's two address-spaces, one executable and // one writtable. The emitter and most of the code in rec-* will work with @@ -41,13 +42,14 @@ extern void (*ngen_FailedToFindBlock)(); //Canonical callback interface enum CanonicalParamType { - CPT_u32, - CPT_u32rv, - CPT_u64rvL, - CPT_u64rvH, - CPT_f32, - CPT_f32rv, - CPT_ptr, + CPT_u32, // u32 param + CPT_u32rv, // u32 return value + CPT_u64rvL, // u64 return value lsb + CPT_u64rvH, // u64 return value msb + CPT_f32, // f32 param + CPT_f32rv, // f32 return value + CPT_ptr, // register pointer + CPT_sh4ctx, // Sh4Context pointer }; bool rdv_readMemImmediate(u32 addr, int size, void*& ptr, bool& isRam, u32& physAddr, RuntimeBlockInfo* block = nullptr); @@ -84,7 +86,7 @@ class Sh4Dynarec { public: // Initialize the dynarec, which should keep a reference to the passed code buffer to generate code later. - virtual void init(Sh4CodeBuffer& codeBuffer) = 0; + virtual void init(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) = 0; // Compile the given block. // If smc_checks is true, add self-modifying code detection. // If optimize is true, use fast memory accesses if possible, that will be rewritten if they fail. @@ -121,3 +123,25 @@ class Sh4Dynarec }; extern Sh4Dynarec *sh4Dynarec; + +class Sh4Recompiler : public Sh4Interpreter +{ + using super = Sh4Interpreter; + +public: + Sh4Recompiler() { + Instance = this; + } + ~Sh4Recompiler() { + Instance = nullptr; + } + void Run() override; + void ResetCache() override; + void Reset(bool hard) override; + void Init() override; + void Term() override; + + void clear_temp_cache(bool full); + + static Sh4Recompiler *Instance; +}; diff --git a/core/hw/sh4/dyna/shil.cpp b/core/hw/sh4/dyna/shil.cpp index 6c20e8b54..d5c161761 100644 --- a/core/hw/sh4/dyna/shil.cpp +++ b/core/hw/sh4/dyna/shil.cpp @@ -1,27 +1,10 @@ -#include - #include "types.h" +#if FEAT_SHREC != DYNAREC_NONE #include "hw/sh4/sh4_mem.h" #include "hw/sh4/sh4_mmr.h" - #include "ngen.h" -#include "hw/sh4/sh4_core.h" - -#define SHIL_MODE 1 -#include "shil_canonical.h" - -#define SHIL_MODE 4 -#include "shil_canonical.h" - -//#define SHIL_MODE 2 -//#include "shil_canonical.h" - -#if FEAT_SHREC != DYNAREC_NONE -#define SHIL_MODE 3 -#include "shil_canonical.h" -#endif - #include "ssa.h" +#include void AnalyseBlock(RuntimeBlockInfo* blk) { @@ -29,6 +12,56 @@ void AnalyseBlock(RuntimeBlockInfo* blk) optim.Optimize(); } +u32 getRegOffset(Sh4RegType reg) +{ + if (reg >= reg_r0 && reg <= reg_r15) { + const size_t regofs = (reg - reg_r0) * sizeof(u32); + return offsetof(Sh4Context, r[0]) + regofs; + } + if (reg >= reg_r0_Bank && reg <= reg_r7_Bank) { + const size_t regofs = (reg - reg_r0_Bank) * sizeof(u32); + return offsetof(Sh4Context, r_bank[0]) + regofs; + } + if (reg >= reg_fr_0 && reg <= reg_fr_15) { + const size_t regofs = (reg - reg_fr_0) * sizeof(float); + return offsetof(Sh4Context, fr[0]) + regofs; + } + if (reg >= reg_xf_0 && reg <= reg_xf_15) { + const size_t regofs = (reg - reg_xf_0) * sizeof(float); + return offsetof(Sh4Context, xf[0]) + regofs; + } + switch (reg) + { + case reg_gbr: return offsetof(Sh4Context, gbr); + case reg_vbr: return offsetof(Sh4Context, vbr); + case reg_ssr: return offsetof(Sh4Context, ssr); + case reg_spc: return offsetof(Sh4Context, spc); + case reg_sgr: return offsetof(Sh4Context, sgr); + case reg_dbr: return offsetof(Sh4Context, dbr); + case reg_mach: return offsetof(Sh4Context, mac.h); + case reg_macl: return offsetof(Sh4Context, mac.l); + case reg_pr: return offsetof(Sh4Context, pr); + case reg_fpul: return offsetof(Sh4Context, fpul); + case reg_nextpc: return offsetof(Sh4Context, pc); + case reg_sr_status: return offsetof(Sh4Context, sr.status); + case reg_sr_T: return offsetof(Sh4Context, sr.T); + case reg_old_fpscr: return offsetof(Sh4Context, old_fpscr.full); + case reg_fpscr: return offsetof(Sh4Context, fpscr.full); + case reg_pc_dyn: return offsetof(Sh4Context, jdyn); + case reg_temp: return offsetof(Sh4Context, temp_reg); + case reg_sq_buffer: return offsetof(Sh4Context, sq_buffer); + default: + ERROR_LOG(SH4, "Unknown register ID %d", reg); + die("Invalid reg"); + return 0; + } +} + +u32* GetRegPtr(Sh4Context& ctx, u32 reg) +{ + return (u32 *)((u8 *)&ctx + getRegOffset((Sh4RegType)reg)); +} + std::string name_reg(Sh4RegType reg) { std::stringstream ss; @@ -119,6 +152,22 @@ static std::string dissasm_param(const shil_param& prm, bool comma) return ss.str(); } +#include "hw/sh4/sh4_core.h" + +#define SHIL_MODE 1 +#include "shil_canonical.h" + +#define SHIL_MODE 4 +#include "shil_canonical.h" + +//#define SHIL_MODE 2 +//#include "shil_canonical.h" + +#if FEAT_SHREC != DYNAREC_NONE +#define SHIL_MODE 3 +#include "shil_canonical.h" +#endif + std::string shil_opcode::dissasm() const { std::stringstream ss; @@ -130,3 +179,5 @@ const char* shil_opcode_name(int op) { return shilop_str[op]; } + +#endif // FEAT_SHREC != DYNAREC_NONE diff --git a/core/hw/sh4/dyna/shil.h b/core/hw/sh4/dyna/shil.h index 9e2112bbf..787570855 100644 --- a/core/hw/sh4/dyna/shil.h +++ b/core/hw/sh4/dyna/shil.h @@ -5,6 +5,131 @@ struct shil_opcode; typedef void shil_chfp(shil_opcode* op); extern shil_chfp* shil_chf[]; +enum Sh4RegType +{ + //GPRs + reg_r0, + reg_r1, + reg_r2, + reg_r3, + reg_r4, + reg_r5, + reg_r6, + reg_r7, + reg_r8, + reg_r9, + reg_r10, + reg_r11, + reg_r12, + reg_r13, + reg_r14, + reg_r15, + + //FPU, bank 0 + reg_fr_0, + reg_fr_1, + reg_fr_2, + reg_fr_3, + reg_fr_4, + reg_fr_5, + reg_fr_6, + reg_fr_7, + reg_fr_8, + reg_fr_9, + reg_fr_10, + reg_fr_11, + reg_fr_12, + reg_fr_13, + reg_fr_14, + reg_fr_15, + + //FPU, bank 1 + reg_xf_0, + reg_xf_1, + reg_xf_2, + reg_xf_3, + reg_xf_4, + reg_xf_5, + reg_xf_6, + reg_xf_7, + reg_xf_8, + reg_xf_9, + reg_xf_10, + reg_xf_11, + reg_xf_12, + reg_xf_13, + reg_xf_14, + reg_xf_15, + + //GPR Interrupt bank + reg_r0_Bank, + reg_r1_Bank, + reg_r2_Bank, + reg_r3_Bank, + reg_r4_Bank, + reg_r5_Bank, + reg_r6_Bank, + reg_r7_Bank, + + //Misc regs + reg_gbr, + reg_ssr, + reg_spc, + reg_sgr, + reg_dbr, + reg_vbr, + reg_mach, + reg_macl, + reg_pr, + reg_fpul, + reg_nextpc, + reg_sr_status, //Only the status bits + reg_sr_T, //Only T + reg_old_fpscr, + reg_fpscr, + + reg_pc_dyn, //Write only, for dynarec only (dynamic block exit address) + reg_temp, + + sh4_reg_count, + + /* + These are virtual registers, used by the dynarec decoder + */ + regv_dr_0, + regv_dr_2, + regv_dr_4, + regv_dr_6, + regv_dr_8, + regv_dr_10, + regv_dr_12, + regv_dr_14, + + regv_xd_0, + regv_xd_2, + regv_xd_4, + regv_xd_6, + regv_xd_8, + regv_xd_10, + regv_xd_12, + regv_xd_14, + + regv_fv_0, + regv_fv_4, + regv_fv_8, + regv_fv_12, + + regv_xmtrx, + regv_fmtrx, + + reg_sq_buffer, + + NoReg=-1 +}; + +u32 getRegOffset(Sh4RegType reg); +u32* GetRegPtr(Sh4Context& ctx, u32 reg); + enum shil_param_type { FMT_NULL, @@ -27,7 +152,6 @@ enum shil_param_type param types: r32, r64 */ - #define SHIL_MODE 0 #include "shil_canonical.h" @@ -107,9 +231,9 @@ struct shil_param bool is_imm_s8() const { return is_imm() && (int8_t)_imm == (int32_t)_imm; } - u32* reg_ptr() const { verify(is_reg()); return GetRegPtr(_reg); } - s32 reg_nofs() const { verify(is_reg()); return (s32)((u8*)GetRegPtr(_reg) - (u8*)GetRegPtr(reg_xf_0)-sizeof(Sh4cntx)); } - u32 reg_aofs() const { return -reg_nofs(); } + u32* reg_ptr(Sh4Context& ctx) const { verify(is_reg()); return GetRegPtr(ctx, _reg); } + u32 reg_offset() const { verify(is_reg()); return getRegOffset(_reg); } + s32 reg_nofs() const { verify(is_reg()); return (int)getRegOffset(_reg) - sizeof(Sh4Context); } u32 imm_value() const { verify(is_imm()); return _imm; } diff --git a/core/hw/sh4/dyna/shil_canonical.h b/core/hw/sh4/dyna/shil_canonical.h index e041d7874..21e1a8230 100644 --- a/core/hw/sh4/dyna/shil_canonical.h +++ b/core/hw/sh4/dyna/shil_canonical.h @@ -34,6 +34,7 @@ #define shil_cf_arg_u32(x) sh4Dynarec->canonParam(op, &op->x, CPT_u32); #define shil_cf_arg_f32(x) sh4Dynarec->canonParam(op, &op->x, CPT_f32); #define shil_cf_arg_ptr(x) sh4Dynarec->canonParam(op, &op->x, CPT_ptr); + #define shil_cf_arg_sh4ctx() sh4Dynarec->canonParam(op, nullptr, CPT_sh4ctx); #define shil_cf_rv_u32(x) sh4Dynarec->canonParam(op, &op->x, CPT_u32rv); #define shil_cf_rv_f32(x) sh4Dynarec->canonParam(op, &op->x, CPT_f32rv); #define shil_cf_rv_u64(x) sh4Dynarec->canonParam(op, &op->rd, CPT_u64rvL); sh4Dynarec->canonParam(op, &op->rd2, CPT_u64rvH); @@ -227,11 +228,12 @@ shil_opc_end() shil_opc(sync_fpscr) shil_canonical ( -void, f1, (), - UpdateFPSCR(); +void, f1, (Sh4Context *ctx), + Sh4Context::UpdateFPSCR(ctx); ) shil_compile ( + shil_cf_arg_sh4ctx(); shil_cf(f1); ) shil_opc_end() @@ -660,7 +662,8 @@ shil_opc_end() shil_opc(div1) shil_canonical ( -u64,f1,(u32 a, s32 b, u32 T), +u64,f1,(u32 a, s32 b, u32 T, Sh4Context *ctx), + sr_t& sr = ctx->sr; bool qxm = sr.Q ^ sr.M; sr.Q = (int)a < 0; a = (a << 1) | T; @@ -674,6 +677,7 @@ u64,f1,(u32 a, s32 b, u32 T), ) shil_compile ( + shil_cf_arg_sh4ctx(); shil_cf_arg_u32(rs3); shil_cf_arg_u32(rs2); shil_cf_arg_u32(rs1); @@ -795,27 +799,15 @@ shil_opc_end() shil_opc(pref) shil_canonical ( -void,f1,(u32 r1), - if ((r1>>26) == 0x38) do_sqw_mmu(r1); -) - -shil_canonical -( -void,f2,(u32 r1), - if ((r1>>26) == 0x38) do_sqw_nommu(r1,sq_both); +void,f1,(u32 r1, Sh4Context *ctx), + if ((r1 >> 26) == 0x38) ctx->doSqWrite(r1, ctx); ) shil_compile ( + shil_cf_arg_sh4ctx(); shil_cf_arg_u32(rs1); - if (CCN_MMUCR.AT) - { - shil_cf(f1); - } - else - { - shil_cf(f2); - } + shil_cf(f1); ) shil_opc_end() diff --git a/core/hw/sh4/dyna/ssa.cpp b/core/hw/sh4/dyna/ssa.cpp index 11b641bf3..783874765 100644 --- a/core/hw/sh4/dyna/ssa.cpp +++ b/core/hw/sh4/dyna/ssa.cpp @@ -18,6 +18,8 @@ You should have received a copy of the GNU General Public License along with reicast. If not, see . */ +#include "build.h" +#if FEAT_SHREC != DYNAREC_NONE #include "blockmanager.h" #include "ssa.h" @@ -369,3 +371,4 @@ bool SSAOptimizer::ExecuteConstOp(shil_opcode* op) return false; } } +#endif // FEAT_SHREC != DYNAREC_NONE diff --git a/core/hw/sh4/interpr/sh4_fpu.cpp b/core/hw/sh4/interpr/sh4_fpu.cpp index 27bb2b175..231f45fe3 100644 --- a/core/hw/sh4/interpr/sh4_fpu.cpp +++ b/core/hw/sh4/interpr/sh4_fpu.cpp @@ -6,8 +6,6 @@ #include "hw/sh4/sh4_rom.h" #include "hw/sh4/sh4_mem.h" -#define sh4op(str) void DYNACALL str (u32 op) - static u32 GetN(u32 op) { return (op >> 8) & 0xf; } @@ -15,14 +13,14 @@ static u32 GetM(u32 op) { return (op >> 4) & 0xf; } -static double getDRn(u32 op) { - return GetDR((op >> 9) & 7); +static double getDRn(Sh4Context *ctx, u32 op) { + return ctx->getDR((op >> 9) & 7); } -static double getDRm(u32 op) { - return GetDR((op >> 5) & 7); +static double getDRm(Sh4Context *ctx, u32 op) { + return ctx->getDR((op >> 5) & 7); } -static void setDRn(u32 op, double d) { - SetDR((op >> 9) & 7, d); +static void setDRn(Sh4Context *ctx, u32 op, double d) { + ctx->setDR((op >> 9) & 7, d); } static void iNimp(const char *str); @@ -32,127 +30,127 @@ static void iNimp(const char *str); //fadd , sh4op(i1111_nnnn_mmmm_0000) { - if (fpscr.PR == 0) + if (ctx->fpscr.PR == 0) { u32 n = GetN(op); u32 m = GetM(op); - fr[n] += fr[m]; - CHECK_FPU_32(fr[n]); + ctx->fr[n] += ctx->fr[m]; + CHECK_FPU_32(ctx->fr[n]); } else { - double d = getDRn(op) + getDRm(op); + double d = getDRn(ctx, op) + getDRm(ctx, op); d = fixNaN64(d); - setDRn(op, d); + setDRn(ctx, op, d); } } //fsub , sh4op(i1111_nnnn_mmmm_0001) { - if (fpscr.PR == 0) + if (ctx->fpscr.PR == 0) { u32 n = GetN(op); u32 m = GetM(op); - fr[n] -= fr[m]; - CHECK_FPU_32(fr[n]); + ctx->fr[n] -= ctx->fr[m]; + CHECK_FPU_32(ctx->fr[n]); } else { - double d = getDRn(op) - getDRm(op); + double d = getDRn(ctx, op) - getDRm(ctx, op); d = fixNaN64(d); - setDRn(op, d); + setDRn(ctx, op, d); } } //fmul , sh4op(i1111_nnnn_mmmm_0010) { - if (fpscr.PR == 0) + if (ctx->fpscr.PR == 0) { u32 n = GetN(op); u32 m = GetM(op); - fr[n] *= fr[m]; - CHECK_FPU_32(fr[n]); + ctx->fr[n] *= ctx->fr[m]; + CHECK_FPU_32(ctx->fr[n]); } else { - double d = getDRn(op) * getDRm(op); + double d = getDRn(ctx, op) * getDRm(ctx, op); d = fixNaN64(d); - setDRn(op, d); + setDRn(ctx, op, d); } } //fdiv , sh4op(i1111_nnnn_mmmm_0011) { - if (fpscr.PR == 0) + if (ctx->fpscr.PR == 0) { u32 n = GetN(op); u32 m = GetM(op); - fr[n] /= fr[m]; + ctx->fr[n] /= ctx->fr[m]; - CHECK_FPU_32(fr[n]); + CHECK_FPU_32(ctx->fr[n]); } else { - double d = getDRn(op) / getDRm(op); + double d = getDRn(ctx, op) / getDRm(ctx, op); d = fixNaN64(d); - setDRn(op, d); + setDRn(ctx, op, d); } } //fcmp/eq , sh4op(i1111_nnnn_mmmm_0100) { - if (fpscr.PR == 0) + if (ctx->fpscr.PR == 0) { u32 n = GetN(op); u32 m = GetM(op); - sr.T = fr[m] == fr[n]; + ctx->sr.T = ctx->fr[m] == ctx->fr[n]; } else { - sr.T = getDRn(op) == getDRm(op); + ctx->sr.T = getDRn(ctx, op) == getDRm(ctx, op); } } //fcmp/gt , sh4op(i1111_nnnn_mmmm_0101) { - if (fpscr.PR == 0) + if (ctx->fpscr.PR == 0) { u32 n = GetN(op); u32 m = GetM(op); - if (fr[n] > fr[m]) - sr.T = 1; + if (ctx->fr[n] > ctx->fr[m]) + ctx->sr.T = 1; else - sr.T = 0; + ctx->sr.T = 0; } else { - sr.T = getDRn(op) > getDRm(op); + ctx->sr.T = getDRn(ctx, op) > getDRm(ctx, op); } } //All memory opcodes are here //fmov.s @(R0,), sh4op(i1111_nnnn_mmmm_0110) { - if (fpscr.SZ == 0) + if (ctx->fpscr.SZ == 0) { u32 n = GetN(op); u32 m = GetM(op); - fr_hex[n] = ReadMem32(r[m] + r[0]); + ctx->fr_hex(n) = ReadMem32(ctx->r[m] + ctx->r[0]); } else { u32 n = GetN(op)>>1; u32 m = GetM(op); if (((op >> 8) & 1) == 0) - dr_hex[n] = ReadMem64(r[m] + r[0]); + ctx->dr_hex(n) = ReadMem64(ctx->r[m] + ctx->r[0]); else - xd_hex[n] = ReadMem64(r[m] + r[0]); + ctx->xd_hex(n) = ReadMem64(ctx->r[m] + ctx->r[0]); } } @@ -160,21 +158,21 @@ sh4op(i1111_nnnn_mmmm_0110) //fmov.s ,@(R0,) sh4op(i1111_nnnn_mmmm_0111) { - if (fpscr.SZ == 0) + if (ctx->fpscr.SZ == 0) { u32 n = GetN(op); u32 m = GetM(op); - WriteMem32(r[0] + r[n], fr_hex[m]); + WriteMem32(ctx->r[0] + ctx->r[n], ctx->fr_hex(m)); } else { u32 n = GetN(op); u32 m = GetM(op)>>1; if (((op >> 4) & 0x1) == 0) - WriteMem64(r[n] + r[0], dr_hex[m]); + WriteMem64(ctx->r[n] + ctx->r[0], ctx->dr_hex(m)); else - WriteMem64(r[n] + r[0], xd_hex[m]); + WriteMem64(ctx->r[n] + ctx->r[0], ctx->xd_hex(m)); } } @@ -182,20 +180,20 @@ sh4op(i1111_nnnn_mmmm_0111) //fmov.s @, sh4op(i1111_nnnn_mmmm_1000) { - if (fpscr.SZ == 0) + if (ctx->fpscr.SZ == 0) { u32 n = GetN(op); u32 m = GetM(op); - fr_hex[n] = ReadMem32(r[m]); + ctx->fr_hex(n) = ReadMem32(ctx->r[m]); } else { u32 n = GetN(op)>>1; u32 m = GetM(op); if (((op >> 8) & 1) == 0) - dr_hex[n] = ReadMem64(r[m]); + ctx->dr_hex(n) = ReadMem64(ctx->r[m]); else - xd_hex[n] = ReadMem64(r[m]); + ctx->xd_hex(n) = ReadMem64(ctx->r[m]); } } @@ -203,23 +201,23 @@ sh4op(i1111_nnnn_mmmm_1000) //fmov.s @+, sh4op(i1111_nnnn_mmmm_1001) { - if (fpscr.SZ == 0) + if (ctx->fpscr.SZ == 0) { u32 n = GetN(op); u32 m = GetM(op); - fr_hex[n] = ReadMem32(r[m]); - r[m] += 4; + ctx->fr_hex(n) = ReadMem32(ctx->r[m]); + ctx->r[m] += 4; } else { u32 n = GetN(op)>>1; u32 m = GetM(op); if (((op >> 8) & 1) == 0) - dr_hex[n] = ReadMem64(r[m]); + ctx->dr_hex(n) = ReadMem64(ctx->r[m]); else - xd_hex[n] = ReadMem64(r[m]); - r[m] += 8; + ctx->xd_hex(n) = ReadMem64(ctx->r[m]); + ctx->r[m] += 8; } } @@ -227,11 +225,11 @@ sh4op(i1111_nnnn_mmmm_1001) //fmov.s ,@ sh4op(i1111_nnnn_mmmm_1010) { - if (fpscr.SZ == 0) + if (ctx->fpscr.SZ == 0) { u32 n = GetN(op); u32 m = GetM(op); - WriteMem32(r[n], fr_hex[m]); + WriteMem32(ctx->r[n], ctx->fr_hex(m)); } else { @@ -239,38 +237,38 @@ sh4op(i1111_nnnn_mmmm_1010) u32 m = GetM(op)>>1; if (((op >> 4) & 0x1) == 0) - WriteMem64(r[n], dr_hex[m]); + WriteMem64(ctx->r[n], ctx->dr_hex(m)); else - WriteMem64(r[n], xd_hex[m]); + WriteMem64(ctx->r[n], ctx->xd_hex(m)); } } //fmov.s ,@- sh4op(i1111_nnnn_mmmm_1011) { - if (fpscr.SZ == 0) + if (ctx->fpscr.SZ == 0) { u32 n = GetN(op); u32 m = GetM(op); - u32 addr = r[n] - 4; + u32 addr = ctx->r[n] - 4; - WriteMem32(addr, fr_hex[m]); + WriteMem32(addr, ctx->fr_hex(m)); - r[n] = addr; + ctx->r[n] = addr; } else { u32 n = GetN(op); u32 m = GetM(op)>>1; - u32 addr = r[n] - 8; + u32 addr = ctx->r[n] - 8; if (((op >> 4) & 0x1) == 0) - WriteMem64(addr, dr_hex[m]); + WriteMem64(addr, ctx->dr_hex(m)); else - WriteMem64(addr, xd_hex[m]); + WriteMem64(addr, ctx->xd_hex(m)); - r[n] = addr; + ctx->r[n] = addr; } } @@ -279,11 +277,11 @@ sh4op(i1111_nnnn_mmmm_1011) //fmov , sh4op(i1111_nnnn_mmmm_1100) { - if (fpscr.SZ == 0) + if (ctx->fpscr.SZ == 0) { u32 n = GetN(op); u32 m = GetM(op); - fr[n] = fr[m]; + ctx->fr[n] = ctx->fr[m]; } else { @@ -293,22 +291,22 @@ sh4op(i1111_nnnn_mmmm_1100) { case 0x00: //dr[n] = dr[m]; - dr_hex[n] = dr_hex[m]; + ctx->dr_hex(n) = ctx->dr_hex(m); break; case 0x01: //dr[n] = xd[m]; - dr_hex[n] = xd_hex[m]; + ctx->dr_hex(n) = ctx->xd_hex(m); break; case 0x10: //xd[n] = dr[m]; - xd_hex[n] = dr_hex[m]; + ctx->xd_hex(n) = ctx->dr_hex(m); break; case 0x11: //xd[n] = xd[m]; - xd_hex[n] = xd_hex[m]; + ctx->xd_hex(n) = ctx->xd_hex(m); break; } } @@ -320,10 +318,10 @@ sh4op(i1111_nnnn_0101_1101) { int n=GetN(op); - if (fpscr.PR ==0) - fr_hex[n]&=0x7FFFFFFF; + if (ctx->fpscr.PR == 0) + ctx->fr_hex(n) &= 0x7FFFFFFF; else - fr_hex[(n&0xE)]&=0x7FFFFFFF; + ctx->fr_hex(n & 0xE) &= 0x7FFFFFFF; } @@ -334,21 +332,21 @@ sh4op(i1111_nnn0_1111_1101) //cosine(x) = sine(pi/2 + x). - if (fpscr.PR==0) + if (ctx->fpscr.PR==0) { - u32 pi_index=fpul&0xFFFF; + u32 pi_index = ctx->fpul & 0xFFFF; #ifdef NATIVE_FSCA float rads = pi_index / (65536.0f / 2) * float(M_PI); - fr[n + 0] = sinf(rads); - fr[n + 1] = cosf(rads); + ctx->fr[n + 0] = sinf(rads); + ctx->fr[n + 1] = cosf(rads); - CHECK_FPU_32(fr[n]); - CHECK_FPU_32(fr[n+1]); + CHECK_FPU_32(ctx->fr[n]); + CHECK_FPU_32(ctx->fr[n + 1]); #else - fr[n + 0] = sin_table[pi_index].u[0]; - fr[n + 1] = sin_table[pi_index].u[1]; + ctx->fr[n + 0] = sin_table[pi_index].u[0]; + ctx->fr[n + 1] = sin_table[pi_index].u[1]; #endif } @@ -360,10 +358,10 @@ sh4op(i1111_nnn0_1111_1101) sh4op(i1111_nnnn_0111_1101) { u32 n = GetN(op); - if (fpscr.PR==0) + if (ctx->fpscr.PR==0) { - fr[n] = 1.f / sqrtf(fr[n]); - CHECK_FPU_32(fr[n]); + ctx->fr[n] = 1.f / sqrtf(ctx->fr[n]); + CHECK_FPU_32(ctx->fr[n]); } else iNimp("FSRRA : Double precision mode"); @@ -373,10 +371,10 @@ sh4op(i1111_nnnn_0111_1101) sh4op(i1111_nnnn_1011_1101) { - if (fpscr.PR == 1) + if (ctx->fpscr.PR == 1) { - u32 *p = &fpul; - *((float *)p) = (float)getDRn(op); + u32 *p = &ctx->fpul; + *((float *)p) = (float)getDRn(ctx, op); } else { @@ -388,10 +386,10 @@ sh4op(i1111_nnnn_1011_1101) //fcnvsd FPUL, sh4op(i1111_nnnn_1010_1101) { - if (fpscr.PR == 1) + if (ctx->fpscr.PR == 1) { - u32 *p = &fpul; - setDRn(op, (double)*((float *)p)); + u32 *p = &ctx->fpul; + setDRn(ctx, op, (double)*((float *)p)); } else { @@ -404,14 +402,14 @@ sh4op(i1111_nnmm_1110_1101) { int n=GetN(op)&0xC; int m=(GetN(op)&0x3)<<2; - if (fpscr.PR == 0) + if (ctx->fpscr.PR == 0) { - double idp = (double)fr[n + 0] * fr[m + 0]; - idp += (double)fr[n + 1] * fr[m + 1]; - idp += (double)fr[n + 2] * fr[m + 2]; - idp += (double)fr[n + 3] * fr[m + 3]; + double idp = (double)ctx->fr[n + 0] * ctx->fr[m + 0]; + idp += (double)ctx->fr[n + 1] * ctx->fr[m + 1]; + idp += (double)ctx->fr[n + 2] * ctx->fr[m + 2]; + idp += (double)ctx->fr[n + 3] * ctx->fr[m + 3]; - fr[n + 3] = fixNaN((float)idp); + ctx->fr[n + 3] = fixNaN((float)idp); } else { @@ -422,24 +420,24 @@ sh4op(i1111_nnmm_1110_1101) //fldi0 sh4op(i1111_nnnn_1000_1101) { - if (fpscr.PR!=0) + if (ctx->fpscr.PR!=0) return; u32 n = GetN(op); - fr[n] = 0.0f; + ctx->fr[n] = 0.0f; } //fldi1 sh4op(i1111_nnnn_1001_1101) { - if (fpscr.PR!=0) + if (ctx->fpscr.PR!=0) return; u32 n = GetN(op); - fr[n] = 1.0f; + ctx->fr[n] = 1.0f; } //flds ,FPUL @@ -447,27 +445,27 @@ sh4op(i1111_nnnn_0001_1101) { u32 n = GetN(op); - fpul = fr_hex[n]; + ctx->fpul = ctx->fr_hex(n); } //fsts FPUL, sh4op(i1111_nnnn_0000_1101) { u32 n = GetN(op); - fr_hex[n] = fpul; + ctx->fr_hex(n) = ctx->fpul; } //float FPUL, sh4op(i1111_nnnn_0010_1101) { - if (fpscr.PR == 0) + if (ctx->fpscr.PR == 0) { u32 n = GetN(op); - fr[n] = (float)(int)fpul; + ctx->fr[n] = (float)(int)ctx->fpul; } else { - setDRn(op, (double)(int)fpul); + setDRn(ctx, op, (double)(int)ctx->fpul); } } @@ -477,40 +475,40 @@ sh4op(i1111_nnnn_0100_1101) { u32 n = GetN(op); - if (fpscr.PR ==0) - fr_hex[n]^=0x80000000; + if (ctx->fpscr.PR == 0) + ctx->fr_hex(n) ^= 0x80000000; else - fr_hex[(n&0xE)]^=0x80000000; + ctx->fr_hex(n & 0xE) ^= 0x80000000; } //frchg sh4op(i1111_1011_1111_1101) { - fpscr.FR = 1 - fpscr.FR; + ctx->fpscr.FR = 1 - ctx->fpscr.FR; - UpdateFPSCR(); + Sh4Context::UpdateFPSCR(ctx); } //fschg sh4op(i1111_0011_1111_1101) { - fpscr.SZ = 1 - fpscr.SZ; + ctx->fpscr.SZ = 1 - ctx->fpscr.SZ; } //fsqrt sh4op(i1111_nnnn_0110_1101) { - if (fpscr.PR == 0) + if (ctx->fpscr.PR == 0) { u32 n = GetN(op); - fr[n] = sqrtf(fr[n]); - CHECK_FPU_32(fr[n]); + ctx->fr[n] = sqrtf(ctx->fr[n]); + CHECK_FPU_32(ctx->fr[n]); } else { - setDRn(op, fixNaN64(std::sqrt(getDRn(op)))); + setDRn(ctx, op, fixNaN64(sqrt(getDRn(ctx, op)))); } } @@ -518,31 +516,31 @@ sh4op(i1111_nnnn_0110_1101) //ftrc , FPUL sh4op(i1111_nnnn_0011_1101) { - if (fpscr.PR == 0) + if (ctx->fpscr.PR == 0) { u32 n = GetN(op); - fpul = (u32)(s32)fr[n]; + ctx->fpul = (u32)(s32)ctx->fr[n]; - if ((s32)fpul > 0x7fffff80) - fpul = 0x7fffffff; + if ((s32)ctx->fpul > 0x7fffff80) + ctx->fpul = 0x7fffffff; // Intel CPUs convert out of range float numbers to 0x80000000. Manually set the correct sign - else if (fpul == 0x80000000 && fr[n] == fr[n]) + else if (ctx->fpul == 0x80000000 && ctx->fr[n] == ctx->fr[n]) { - if (*(int *)&fr[n] > 0) // Using integer math to avoid issues with Inf and NaN - fpul--; + if (*(int *)&ctx->fr[n] > 0) // Using integer math to avoid issues with Inf and NaN + ctx->fpul--; } } else { - f64 f = getDRn(op); - fpul = (u32)(s32)f; + f64 f = getDRn(ctx, op); + ctx->fpul = (u32)(s32)f; // TODO saturate // Intel CPUs convert out of range float numbers to 0x80000000. Manually set the correct sign - if (fpul == 0x80000000 && f == f) + if (ctx->fpul == 0x80000000 && f == f) { if (*(s64 *)&f > 0) // Using integer math to avoid issues with Inf and NaN - fpul--; + ctx->fpul--; } } } @@ -551,13 +549,13 @@ sh4op(i1111_nnnn_0011_1101) //fmac ,, sh4op(i1111_nnnn_mmmm_1110) { - if (fpscr.PR==0) + if (ctx->fpscr.PR==0) { u32 n = GetN(op); u32 m = GetM(op); - fr[n] = std::fma(fr[0], fr[m], fr[n]); - CHECK_FPU_32(fr[n]); + ctx->fr[n] = std::fma(ctx->fr[0], ctx->fr[m], ctx->fr[n]); + CHECK_FPU_32(ctx->fr[n]); } else { @@ -578,32 +576,32 @@ sh4op(i1111_nn01_1111_1101) u32 n=GetN(op)&0xC; - if (fpscr.PR==0) + if (ctx->fpscr.PR==0) { - double v1 = (double)xf[0] * fr[n + 0] + - (double)xf[4] * fr[n + 1] + - (double)xf[8] * fr[n + 2] + - (double)xf[12] * fr[n + 3]; + double v1 = (double)ctx->xf[0] * ctx->fr[n + 0] + + (double)ctx->xf[4] * ctx->fr[n + 1] + + (double)ctx->xf[8] * ctx->fr[n + 2] + + (double)ctx->xf[12] * ctx->fr[n + 3]; - double v2 = (double)xf[1] * fr[n + 0] + - (double)xf[5] * fr[n + 1] + - (double)xf[9] * fr[n + 2] + - (double)xf[13] * fr[n + 3]; + double v2 = (double)ctx->xf[1] * ctx->fr[n + 0] + + (double)ctx->xf[5] * ctx->fr[n + 1] + + (double)ctx->xf[9] * ctx->fr[n + 2] + + (double)ctx->xf[13] * ctx->fr[n + 3]; - double v3 = (double)xf[2] * fr[n + 0] + - (double)xf[6] * fr[n + 1] + - (double)xf[10] * fr[n + 2] + - (double)xf[14] * fr[n + 3]; + double v3 = (double)ctx->xf[2] * ctx->fr[n + 0] + + (double)ctx->xf[6] * ctx->fr[n + 1] + + (double)ctx->xf[10] * ctx->fr[n + 2] + + (double)ctx->xf[14] * ctx->fr[n + 3]; - double v4 = (double)xf[3] * fr[n + 0] + - (double)xf[7] * fr[n + 1] + - (double)xf[11] * fr[n + 2] + - (double)xf[15] * fr[n + 3]; + double v4 = (double)ctx->xf[3] * ctx->fr[n + 0] + + (double)ctx->xf[7] * ctx->fr[n + 1] + + (double)ctx->xf[11] * ctx->fr[n + 2] + + (double)ctx->xf[15] * ctx->fr[n + 3]; - fr[n + 0] = fixNaN((float)v1); - fr[n + 1] = fixNaN((float)v2); - fr[n + 2] = fixNaN((float)v3); - fr[n + 3] = fixNaN((float)v4); + ctx->fr[n + 0] = fixNaN((float)v1); + ctx->fr[n + 1] = fixNaN((float)v2); + ctx->fr[n + 2] = fixNaN((float)v3); + ctx->fr[n + 3] = fixNaN((float)v4); } else { diff --git a/core/hw/sh4/interpr/sh4_interpreter.cpp b/core/hw/sh4/interpr/sh4_interpreter.cpp index 780f971aa..6b826463e 100644 --- a/core/hw/sh4/interpr/sh4_interpreter.cpp +++ b/core/hw/sh4/interpr/sh4_interpreter.cpp @@ -14,40 +14,34 @@ #include "debug/gdb_server.h" #include "../sh4_cycles.h" -// SH4 underclock factor when using the interpreter so that it's somewhat usable -#ifdef STRICT_MODE -constexpr int CPU_RATIO = 1; -#else -constexpr int CPU_RATIO = 8; -#endif - Sh4ICache icache; Sh4OCache ocache; -Sh4Cycles sh4cycles(CPU_RATIO); +Sh4Interpreter *Sh4Interpreter::Instance; -static void ExecuteOpcode(u16 op) +void Sh4Interpreter::ExecuteOpcode(u16 op) { - if (sr.FD == 1 && OpDesc[op]->IsFloatingPoint()) - RaiseFPUDisableException(); - OpPtr[op](op); + if (ctx->sr.FD == 1 && OpDesc[op]->IsFloatingPoint()) + throw SH4ThrownException(ctx->pc - 2, Sh4Ex_FpuDisabled); + OpPtr[op](ctx, op); sh4cycles.executeCycles(op); } -static u16 ReadNexOp() +u16 Sh4Interpreter::ReadNexOp() { - if (!mmu_enabled() && (next_pc & 1)) + u32 addr = ctx->pc; + if (!mmu_enabled() && (addr & 1)) // address error - throw SH4ThrownException(next_pc, Sh4Ex_AddressErrorRead); + throw SH4ThrownException(addr, Sh4Ex_AddressErrorRead); - u32 addr = next_pc; - next_pc += 2; + ctx->pc = addr + 2; return IReadMem16(addr); } -static void Sh4_int_Run() +void Sh4Interpreter::Run() { - RestoreHostRoundingMode(); + Instance = this; + ctx->restoreHostRoundingMode(); try { do @@ -58,36 +52,38 @@ static void Sh4_int_Run() u32 op = ReadNexOp(); ExecuteOpcode(op); - } while (p_sh4rcb->cntx.cycle_counter > 0); - p_sh4rcb->cntx.cycle_counter += SH4_TIMESLICE; + } while (ctx->cycle_counter > 0); + ctx->cycle_counter += SH4_TIMESLICE; UpdateSystem_INTC(); } catch (const SH4ThrownException& ex) { Do_Exception(ex.epc, ex.expEvn); // an exception requires the instruction pipeline to drain, so approx 5 cycles sh4cycles.addCycles(5 * CPU_RATIO); } - } while (sh4_int_bCpuRun); + } while (ctx->CpuRunning); } catch (const debugger::Stop&) { } - sh4_int_bCpuRun = false; + ctx->CpuRunning = false; + Instance = nullptr; } -static void Sh4_int_Start() +void Sh4Interpreter::Start() { - sh4_int_bCpuRun = true; + ctx->CpuRunning = true; } -static void Sh4_int_Stop() +void Sh4Interpreter::Stop() { - sh4_int_bCpuRun = false; + ctx->CpuRunning = false; } -static void Sh4_int_Step() +void Sh4Interpreter::Step() { - verify(!sh4_int_bCpuRun); + verify(!ctx->CpuRunning); + Instance = this; - RestoreHostRoundingMode(); + ctx->restoreHostRoundingMode(); try { u32 op = ReadNexOp(); ExecuteOpcode(op); @@ -97,48 +93,49 @@ static void Sh4_int_Step() sh4cycles.addCycles(5 * CPU_RATIO); } catch (const debugger::Stop&) { } + Instance = nullptr; } -static void Sh4_int_Reset(bool hard) +void Sh4Interpreter::Reset(bool hard) { - verify(!sh4_int_bCpuRun); + verify(!ctx->CpuRunning); if (hard) { - int schedNext = p_sh4rcb->cntx.sh4_sched_next; - memset(&p_sh4rcb->cntx, 0, sizeof(p_sh4rcb->cntx)); - p_sh4rcb->cntx.sh4_sched_next = schedNext; + int schedNext = ctx->sh4_sched_next; + memset(ctx, 0, sizeof(*ctx)); + ctx->sh4_sched_next = schedNext; } - next_pc = 0xA0000000; + ctx->pc = 0xA0000000; - memset(r,0,sizeof(r)); - memset(r_bank,0,sizeof(r_bank)); + memset(ctx->r, 0, sizeof(ctx->r)); + memset(ctx->r_bank, 0, sizeof(ctx->r_bank)); - gbr=ssr=spc=sgr=dbr=vbr=0; - mac.full=pr=fpul=0; + ctx->gbr = ctx->ssr = ctx->spc = ctx->sgr = ctx->dbr = ctx->vbr = 0; + ctx->mac.full = ctx->pr = ctx->fpul = 0; - sh4_sr_SetFull(0x700000F0); - old_sr.status=sr.status; + ctx->sr.setFull(0x700000F0); + ctx->old_sr.status = ctx->sr.status; UpdateSR(); - fpscr.full = 0x00040001; - old_fpscr = fpscr; + ctx->fpscr.full = 0x00040001; + ctx->old_fpscr = ctx->fpscr; icache.Reset(hard); ocache.Reset(hard); sh4cycles.reset(); - p_sh4rcb->cntx.cycle_counter = SH4_TIMESLICE; + ctx->cycle_counter = SH4_TIMESLICE; INFO_LOG(INTERPRETER, "Sh4 Reset"); } -static bool Sh4_int_IsCpuRunning() +bool Sh4Interpreter::IsCpuRunning() { - return sh4_int_bCpuRun; + return ctx->CpuRunning; } //TODO : Check for valid delayslot instruction -void ExecuteDelayslot() +void Sh4Interpreter::ExecuteDelayslot() { try { u32 op = ReadNexOp(); @@ -148,12 +145,12 @@ void ExecuteDelayslot() AdjustDelaySlotException(ex); throw ex; } catch (const debugger::Stop& e) { - next_pc -= 2; // break on previous instruction + ctx->pc -= 2; // break on previous instruction throw e; } } -void ExecuteDelayslot_RTE() +void Sh4Interpreter::ExecuteDelayslot_RTE() { try { // In an RTE delay slot, status register (SR) bits are referenced as follows. @@ -163,61 +160,45 @@ void ExecuteDelayslot_RTE() // instruction execution. The STC and STC.L SR instructions access all SR bits after modification. u32 op = ReadNexOp(); // Now restore all SR bits - sh4_sr_SetFull(ssr); + ctx->sr.setFull(ctx->ssr); // And execute ExecuteOpcode(op); } catch (const SH4ThrownException&) { throw FlycastException("Fatal: SH4 exception in RTE delay slot"); } catch (const debugger::Stop& e) { - next_pc -= 2; // break on previous instruction + ctx->pc -= 2; // break on previous instruction throw e; } } // every SH4_TIMESLICE cycles -int UpdateSystem() +int UpdateSystem_INTC() { Sh4cntx.sh4_sched_next -= SH4_TIMESLICE; if (Sh4cntx.sh4_sched_next < 0) sh4_sched_tick(SH4_TIMESLICE); - - return Sh4cntx.interrupt_pend; -} - -int UpdateSystem_INTC() -{ - if (UpdateSystem()) + if (Sh4cntx.interrupt_pend) return UpdateINTC(); else return 0; } -static void sh4_int_resetcache() { -} - -static void Sh4_int_Init() +void Sh4Interpreter::Init() { - static_assert(sizeof(Sh4cntx) == 448, "Invalid Sh4Cntx size"); - - memset(&p_sh4rcb->cntx, 0, sizeof(p_sh4rcb->cntx)); + ctx = &p_sh4rcb->cntx; + memset(ctx, 0, sizeof(*ctx)); + sh4cycles.init(ctx); + icache.init(ctx); + ocache.init(ctx); } -static void Sh4_int_Term() +void Sh4Interpreter::Term() { - Sh4_int_Stop(); + Stop(); INFO_LOG(INTERPRETER, "Sh4 Term"); } -void Get_Sh4Interpreter(sh4_if* cpu) +Sh4Executor *Get_Sh4Interpreter() { - cpu->Start = Sh4_int_Start; - cpu->Run = Sh4_int_Run; - cpu->Stop = Sh4_int_Stop; - cpu->Step = Sh4_int_Step; - cpu->Reset = Sh4_int_Reset; - cpu->Init = Sh4_int_Init; - cpu->Term = Sh4_int_Term; - cpu->IsCpuRunning = Sh4_int_IsCpuRunning; - - cpu->ResetCache = sh4_int_resetcache; + return new Sh4Interpreter(); } diff --git a/core/hw/sh4/interpr/sh4_opcodes.cpp b/core/hw/sh4/interpr/sh4_opcodes.cpp index 7495d077e..26df085e0 100644 --- a/core/hw/sh4/interpr/sh4_opcodes.cpp +++ b/core/hw/sh4/interpr/sh4_opcodes.cpp @@ -3,7 +3,6 @@ */ #include "types.h" -#include "hw/sh4/sh4_interpreter.h" #include "hw/sh4/sh4_mem.h" #include "hw/sh4/sh4_mmr.h" #include "hw/sh4/sh4_core.h" @@ -11,6 +10,7 @@ #include "hw/sh4/sh4_interrupts.h" #include "debug/gdb_server.h" #include "hw/sh4/dyna/decoder.h" +#include "emulator.h" #ifdef STRICT_MODE #include "hw/sh4/sh4_cache.h" @@ -44,7 +44,7 @@ sh4op(i0000_nnnn_0001_0010) { u32 n = GetN(op); - r[n] = gbr; + ctx->r[n] = ctx->gbr; } @@ -52,7 +52,7 @@ sh4op(i0000_nnnn_0001_0010) sh4op(i0000_nnnn_0010_0010) { u32 n = GetN(op); - r[n] = vbr; + ctx->r[n] = ctx->vbr; } @@ -60,21 +60,21 @@ sh4op(i0000_nnnn_0010_0010) sh4op(i0000_nnnn_0011_0010) { u32 n = GetN(op); - r[n] = ssr; + ctx->r[n] = ctx->ssr; } //stc SGR, sh4op(i0000_nnnn_0011_1010) { u32 n = GetN(op); - r[n] = sgr; + ctx->r[n] = ctx->sgr; } //stc SPC, sh4op(i0000_nnnn_0100_0010) { u32 n = GetN(op); - r[n] = spc; + ctx->r[n] = ctx->spc; } @@ -83,21 +83,21 @@ sh4op(i0000_nnnn_1mmm_0010) { u32 n = GetN(op); u32 m = GetM(op) & 0x7; - r[n] = r_bank[m]; + ctx->r[n] = ctx->r_bank[m]; } //sts FPUL, sh4op(i0000_nnnn_0101_1010) { u32 n = GetN(op); - r[n] = fpul; + ctx->r[n] = ctx->fpul; } //stc DBR, sh4op(i0000_nnnn_1111_1010) { u32 n = GetN(op); - r[n] = dbr; + ctx->r[n] = ctx->dbr; } @@ -105,7 +105,7 @@ sh4op(i0000_nnnn_1111_1010) sh4op(i0000_nnnn_0000_1010) { u32 n = GetN(op); - r[n] = mac.h; + ctx->r[n] = ctx->mac.h; } @@ -113,7 +113,7 @@ sh4op(i0000_nnnn_0000_1010) sh4op(i0000_nnnn_0001_1010) { u32 n = GetN(op); - r[n]=mac.l; + ctx->r[n] = ctx->mac.l; } @@ -121,7 +121,7 @@ sh4op(i0000_nnnn_0001_1010) sh4op(i0000_nnnn_0010_1010) { u32 n = GetN(op); - r[n] = pr; + ctx->r[n] = ctx->pr; } @@ -130,7 +130,7 @@ sh4op(i0000_nnnn_mmmm_1100) { u32 n = GetN(op); u32 m = GetM(op); - ReadMemBOS8(r[n],r[0],r[m]); + ReadMemBOS8(ctx->r[n], ctx->r[0], ctx->r[m]); } @@ -139,7 +139,7 @@ sh4op(i0000_nnnn_mmmm_1101) { u32 n = GetN(op); u32 m = GetM(op); - ReadMemBOS16(r[n],r[0],r[m]); + ReadMemBOS16(ctx->r[n], ctx->r[0], ctx->r[m]); } @@ -149,7 +149,7 @@ sh4op(i0000_nnnn_mmmm_1110) u32 n = GetN(op); u32 m = GetM(op); - ReadMemBOU32(r[n],r[0],r[m]); + ReadMemBOU32(ctx->r[n], ctx->r[0], ctx->r[m]); } //mov.b ,@(R0,) @@ -158,7 +158,7 @@ sh4op(i0000_nnnn_mmmm_0100) u32 n = GetN(op); u32 m = GetM(op); - WriteMemBOU8(r[0],r[n], r[m]); + WriteMemBOU8(ctx->r[0], ctx->r[n], ctx->r[m]); } @@ -167,7 +167,7 @@ sh4op(i0000_nnnn_mmmm_0101) { u32 n = GetN(op); u32 m = GetM(op); - WriteMemBOU16(r[0] , r[n], r[m]); + WriteMemBOU16(ctx->r[0] , ctx->r[n], ctx->r[m]); } @@ -176,11 +176,10 @@ sh4op(i0000_nnnn_mmmm_0110) { u32 n = GetN(op); u32 m = GetM(op); - WriteMemBOU32(r[0], r[n], r[m]); + WriteMemBOU32(ctx->r[0], ctx->r[n], ctx->r[m]); } - // // 1xxx @@ -190,7 +189,7 @@ sh4op(i0001_nnnn_mmmm_iiii) u32 n = GetN(op); u32 m = GetM(op); u32 disp = GetImm4(op); - WriteMemBOU32(r[n] , (disp <<2), r[m]); + WriteMemBOU32(ctx->r[n] , (disp <<2), ctx->r[m]); } // @@ -201,7 +200,7 @@ sh4op(i0010_nnnn_mmmm_0000) { u32 n = GetN(op); u32 m = GetM(op); - WriteMemU8(r[n],r[m] ); + WriteMemU8(ctx->r[n], ctx->r[m]); } // mov.w ,@ @@ -209,7 +208,7 @@ sh4op(i0010_nnnn_mmmm_0001) { u32 n = GetN(op); u32 m = GetM(op); - WriteMemU16(r[n],r[m]); + WriteMemU16(ctx->r[n], ctx->r[m]); } // mov.l ,@ @@ -217,7 +216,7 @@ sh4op(i0010_nnnn_mmmm_0010) { u32 n = GetN(op); u32 m = GetM(op); - WriteMemU32(r[n],r[m]); + WriteMemU32(ctx->r[n], ctx->r[m]); } // mov.b ,@- @@ -226,9 +225,9 @@ sh4op(i0010_nnnn_mmmm_0100) u32 n = GetN(op); u32 m = GetM(op); - u32 addr = r[n] - 1; - WriteMemBOU8(r[n], (u32)-1, r[m]); - r[n] = addr; + u32 addr = ctx->r[n] - 1; + WriteMemBOU8(ctx->r[n], (u32)-1, ctx->r[m]); + ctx->r[n] = addr; } //mov.w ,@- @@ -237,9 +236,9 @@ sh4op(i0010_nnnn_mmmm_0101) u32 n = GetN(op); u32 m = GetM(op); - u32 addr = r[n] - 2; - WriteMemU16(addr, r[m]); - r[n] = addr; + u32 addr = ctx->r[n] - 2; + WriteMemU16(addr, ctx->r[m]); + ctx->r[n] = addr; } //mov.l ,@- @@ -248,21 +247,21 @@ sh4op(i0010_nnnn_mmmm_0110) u32 n = GetN(op); u32 m = GetM(op); - u32 addr = r[n] - 4; - WriteMemU32(addr, r[m]); - r[n] = addr; + u32 addr = ctx->r[n] - 4; + WriteMemU32(addr, ctx->r[m]); + ctx->r[n] = addr; } - // +// // 4xxx //sts.l FPUL,@- sh4op(i0100_nnnn_0101_0010) { u32 n = GetN(op); - u32 addr = r[n] - 4; - WriteMemU32(addr, fpul); - r[n] = addr; + u32 addr = ctx->r[n] - 4; + WriteMemU32(addr, ctx->fpul); + ctx->r[n] = addr; } //sts.l MACH,@- @@ -270,9 +269,9 @@ sh4op(i0100_nnnn_0000_0010) { u32 n = GetN(op); - u32 addr = r[n] - 4; - WriteMemU32(addr, mac.h); - r[n] = addr; + u32 addr = ctx->r[n] - 4; + WriteMemU32(addr, ctx->mac.h); + ctx->r[n] = addr; } @@ -281,9 +280,9 @@ sh4op(i0100_nnnn_0001_0010) { u32 n = GetN(op); - u32 addr = r[n] - 4; - WriteMemU32(addr, mac.l); - r[n] = addr; + u32 addr = ctx->r[n] - 4; + WriteMemU32(addr, ctx->mac.l); + ctx->r[n] = addr; } @@ -292,9 +291,9 @@ sh4op(i0100_nnnn_0010_0010) { u32 n = GetN(op); - u32 addr = r[n] - 4; - WriteMemU32(addr,pr); - r[n] = addr; + u32 addr = ctx->r[n] - 4; + WriteMemU32(addr, ctx->pr); + ctx->r[n] = addr; } //sts.l DBR,@- @@ -302,9 +301,9 @@ sh4op(i0100_nnnn_1111_0010) { u32 n = GetN(op); - u32 addr = r[n] - 4; - WriteMemU32(addr,dbr); - r[n] = addr; + u32 addr = ctx->r[n] - 4; + WriteMemU32(addr, ctx->dbr); + ctx->r[n] = addr; } //stc.l GBR,@- @@ -312,9 +311,9 @@ sh4op(i0100_nnnn_0001_0011) { u32 n = GetN(op); - u32 addr = r[n] - 4; - WriteMemU32(addr, gbr); - r[n] = addr; + u32 addr = ctx->r[n] - 4; + WriteMemU32(addr, ctx->gbr); + ctx->r[n] = addr; } @@ -323,9 +322,9 @@ sh4op(i0100_nnnn_0010_0011) { u32 n = GetN(op); - u32 addr = r[n] - 4; - WriteMemU32(addr, vbr); - r[n] = addr; + u32 addr = ctx->r[n] - 4; + WriteMemU32(addr, ctx->vbr); + ctx->r[n] = addr; } @@ -334,18 +333,18 @@ sh4op(i0100_nnnn_0011_0011) { u32 n = GetN(op); - u32 addr = r[n] - 4; - WriteMemU32(addr, ssr); - r[n] = addr; + u32 addr = ctx->r[n] - 4; + WriteMemU32(addr, ctx->ssr); + ctx->r[n] = addr; } //stc.l SGR,@- sh4op(i0100_nnnn_0011_0010) { u32 n = GetN(op); - u32 addr = r[n] - 4; - WriteMemU32(addr, sgr); - r[n] = addr; + u32 addr = ctx->r[n] - 4; + WriteMemU32(addr, ctx->sgr); + ctx->r[n] = addr; } @@ -354,9 +353,9 @@ sh4op(i0100_nnnn_0100_0011) { u32 n = GetN(op); - u32 addr = r[n] - 4; - WriteMemU32(addr, spc); - r[n] = addr; + u32 addr = ctx->r[n] - 4; + WriteMemU32(addr, ctx->spc); + ctx->r[n] = addr; } //stc RM_BANK,@- @@ -365,20 +364,19 @@ sh4op(i0100_nnnn_1mmm_0011) u32 n = GetN(op); u32 m = GetM(op) & 0x07; - u32 addr = r[n] - 4; - WriteMemU32(addr, r_bank[m]); - r[n] = addr; + u32 addr = ctx->r[n] - 4; + WriteMemU32(addr, ctx->r_bank[m]); + ctx->r[n] = addr; } - //lds.l @+,MACH sh4op(i0100_nnnn_0000_0110) { u32 n = GetN(op); - ReadMemU32(mac.h,r[n]); + ReadMemU32(ctx->mac.h, ctx->r[n]); - r[n] += 4; + ctx->r[n] += 4; } @@ -386,9 +384,9 @@ sh4op(i0100_nnnn_0000_0110) sh4op(i0100_nnnn_0001_0110) { u32 n = GetN(op); - ReadMemU32(mac.l,r[n]); + ReadMemU32(ctx->mac.l, ctx->r[n]); - r[n] += 4; + ctx->r[n] += 4; } @@ -396,9 +394,9 @@ sh4op(i0100_nnnn_0001_0110) sh4op(i0100_nnnn_0010_0110) { u32 n = GetN(op); - ReadMemU32(pr,r[n]); + ReadMemU32(ctx->pr, ctx->r[n]); - r[n] += 4; + ctx->r[n] += 4; } @@ -407,8 +405,8 @@ sh4op(i0100_nnnn_0101_0110) { u32 n = GetN(op); - ReadMemU32(fpul,r[n]); - r[n] += 4; + ReadMemU32(ctx->fpul, ctx->r[n]); + ctx->r[n] += 4; } //lds.l @+,DBR @@ -416,8 +414,8 @@ sh4op(i0100_nnnn_1111_0110) { u32 n = GetN(op); - ReadMemU32(dbr,r[n]); - r[n] += 4; + ReadMemU32(ctx->dbr, ctx->r[n]); + ctx->r[n] += 4; } @@ -426,8 +424,8 @@ sh4op(i0100_nnnn_0001_0111) { u32 n = GetN(op); - ReadMemU32(gbr,r[n]); - r[n] += 4; + ReadMemU32(ctx->gbr, ctx->r[n]); + ctx->r[n] += 4; } @@ -436,8 +434,8 @@ sh4op(i0100_nnnn_0010_0111) { u32 n = GetN(op); - ReadMemU32(vbr,r[n]); - r[n] += 4; + ReadMemU32(ctx->vbr, ctx->r[n]); + ctx->r[n] += 4; } @@ -446,8 +444,8 @@ sh4op(i0100_nnnn_0011_0111) { u32 n = GetN(op); - ReadMemU32(ssr,r[n]); - r[n] += 4; + ReadMemU32(ctx->ssr, ctx->r[n]); + ctx->r[n] += 4; } //ldc.l @+,SGR @@ -455,8 +453,8 @@ sh4op(i0100_nnnn_0011_0110) { u32 n = GetN(op); - ReadMemU32(sgr,r[n]); - r[n] += 4; + ReadMemU32(ctx->sgr, ctx->r[n]); + ctx->r[n] += 4; } //ldc.l @+,SPC @@ -464,8 +462,8 @@ sh4op(i0100_nnnn_0100_0111) { u32 n = GetN(op); - ReadMemU32(spc,r[n]); - r[n] += 4; + ReadMemU32(ctx->spc, ctx->r[n]); + ctx->r[n] += 4; } @@ -475,15 +473,15 @@ sh4op(i0100_nnnn_1mmm_0111) u32 n = GetN(op); u32 m = GetM(op) & 7; - ReadMemU32(r_bank[m],r[n]); - r[n] += 4; + ReadMemU32(ctx->r_bank[m], ctx->r[n]); + ctx->r[n] += 4; } //lds ,MACH sh4op(i0100_nnnn_0000_1010) { u32 n = GetN(op); - mac.h = r[n]; + ctx->mac.h = ctx->r[n]; } @@ -491,7 +489,7 @@ sh4op(i0100_nnnn_0000_1010) sh4op(i0100_nnnn_0001_1010) { u32 n = GetN(op); - mac.l = r[n]; + ctx->mac.l = ctx->r[n]; } @@ -499,7 +497,7 @@ sh4op(i0100_nnnn_0001_1010) sh4op(i0100_nnnn_0010_1010) { u32 n = GetN(op); - pr = r[n]; + ctx->pr = ctx->r[n]; } @@ -507,27 +505,23 @@ sh4op(i0100_nnnn_0010_1010) sh4op(i0100_nnnn_0101_1010) { u32 n = GetN(op); - fpul =r[n]; + ctx->fpul = ctx->r[n]; } - - //ldc ,DBR sh4op(i0100_nnnn_1111_1010) { u32 n = GetN(op); - dbr = r[n]; + ctx->dbr = ctx->r[n]; } - - //ldc ,GBR sh4op(i0100_nnnn_0001_1110) { u32 n = GetN(op); - gbr = r[n]; + ctx->gbr = ctx->r[n]; } @@ -536,7 +530,7 @@ sh4op(i0100_nnnn_0010_1110) { u32 n = GetN(op); - vbr = r[n]; + ctx->vbr = ctx->r[n]; } @@ -545,7 +539,7 @@ sh4op(i0100_nnnn_0011_1110) { u32 n = GetN(op); - ssr = r[n]; + ctx->ssr = ctx->r[n]; } //ldc ,SGR @@ -553,7 +547,7 @@ sh4op(i0100_nnnn_0011_1010) { u32 n = GetN(op); - sgr = r[n]; + ctx->sgr = ctx->r[n]; } //ldc ,SPC @@ -561,7 +555,7 @@ sh4op(i0100_nnnn_0100_1110) { u32 n = GetN(op); - spc = r[n]; + ctx->spc = ctx->r[n]; } @@ -571,10 +565,10 @@ sh4op(i0100_nnnn_1mmm_1110) u32 n = GetN(op); u32 m = GetM(op) & 7; - r_bank[m] = r[n]; + ctx->r_bank[m] = ctx->r[n]; } - // +// // 5xxx //mov.l @(,), @@ -584,7 +578,7 @@ sh4op(i0101_nnnn_mmmm_iiii) u32 m = GetM(op); u32 disp = GetImm4(op) << 2; - ReadMemBOU32(r[n],r[m],disp); + ReadMemBOU32(ctx->r[n], ctx->r[m], disp); } // @@ -595,7 +589,7 @@ sh4op(i0110_nnnn_mmmm_0000) u32 n = GetN(op); u32 m = GetM(op); - ReadMemS8(r[n],r[m]); + ReadMemS8(ctx->r[n], ctx->r[m]); } @@ -604,7 +598,7 @@ sh4op(i0110_nnnn_mmmm_0001) { u32 n = GetN(op); u32 m = GetM(op); - ReadMemS16(r[n] ,r[m]); + ReadMemS16(ctx->r[n], ctx->r[m]); } @@ -614,7 +608,7 @@ sh4op(i0110_nnnn_mmmm_0010) u32 n = GetN(op); u32 m = GetM(op); - ReadMemU32(r[n],r[m]); + ReadMemU32(ctx->r[n], ctx->r[m]); } @@ -623,7 +617,7 @@ sh4op(i0110_nnnn_mmmm_0011) { u32 n = GetN(op); u32 m = GetM(op); - r[n] = r[m]; + ctx->r[n] = ctx->r[m]; } @@ -632,9 +626,9 @@ sh4op(i0110_nnnn_mmmm_0100) { u32 n = GetN(op); u32 m = GetM(op); - ReadMemS8(r[n],r[m]); + ReadMemS8(ctx->r[n], ctx->r[m]); if (n != m) - r[m] += 1; + ctx->r[m] += 1; } @@ -643,9 +637,9 @@ sh4op(i0110_nnnn_mmmm_0101) { u32 n = GetN(op); u32 m = GetM(op); - ReadMemS16(r[n],r[m]); + ReadMemS16(ctx->r[n], ctx->r[m]); if (n != m) - r[m] += 2; + ctx->r[m] += 2; } @@ -656,19 +650,19 @@ sh4op(i0110_nnnn_mmmm_0110) u32 m = GetM(op); - ReadMemU32(r[n],r[m]); + ReadMemU32(ctx->r[n], ctx->r[m]); if (n != m) - r[m] += 4; + ctx->r[m] += 4; } // //8xxx - // mov.b R0,@(,) +// mov.b R0,@(,) sh4op(i1000_0000_mmmm_iiii) { u32 n = GetM(op); u32 disp = GetImm4(op); - WriteMemBOU8(r[n],disp,r[0]); + WriteMemBOU8(ctx->r[n], disp, ctx->r[0]); } @@ -677,7 +671,7 @@ sh4op(i1000_0001_mmmm_iiii) { u32 disp = GetImm4(op); u32 m = GetM(op); - WriteMemBOU16(r[m] , (disp << 1),r[0]); + WriteMemBOU16(ctx->r[m], (disp << 1), ctx->r[0]); } @@ -686,7 +680,7 @@ sh4op(i1000_0100_mmmm_iiii) { u32 disp = GetImm4(op); u32 m = GetM(op); - ReadMemBOS8(r[0] ,r[m] , disp); + ReadMemBOS8(ctx->r[0], ctx->r[m] , disp); } @@ -695,10 +689,10 @@ sh4op(i1000_0101_mmmm_iiii) { u32 disp = GetImm4(op); u32 m = GetM(op); - ReadMemBOS16(r[0],r[m] , (disp << 1)); + ReadMemBOS16(ctx->r[0], ctx->r[m], (disp << 1)); } - // +// // 9xxx //mov.w @(,PC), @@ -706,17 +700,17 @@ sh4op(i1001_nnnn_iiii_iiii) { u32 n = GetN(op); u32 disp = GetImm8(op); - ReadMemS16(r[n],(disp<<1) + next_pc + 2); + ReadMemS16(ctx->r[n], (disp << 1) + ctx->pc + 2); } - // +// // Cxxx // mov.b R0,@(,GBR) sh4op(i1100_0000_iiii_iiii) { u32 disp = GetImm8(op); - WriteMemBOU8(gbr, disp, r[0]); + WriteMemBOU8(ctx->gbr, disp, ctx->r[0]); } @@ -724,7 +718,7 @@ sh4op(i1100_0000_iiii_iiii) sh4op(i1100_0001_iiii_iiii) { u32 disp = GetImm8(op); - WriteMemBOU16(gbr, (disp << 1), r[0]); + WriteMemBOU16(ctx->gbr, (disp << 1), ctx->r[0]); } @@ -732,14 +726,14 @@ sh4op(i1100_0001_iiii_iiii) sh4op(i1100_0010_iiii_iiii) { u32 disp = GetImm8(op); - WriteMemBOU32(gbr, (disp << 2), r[0]); + WriteMemBOU32(ctx->gbr, (disp << 2), ctx->r[0]); } // mov.b @(,GBR),R0 sh4op(i1100_0100_iiii_iiii) { u32 disp = GetImm8(op); - ReadMemBOS8(r[0],gbr,disp); + ReadMemBOS8(ctx->r[0], ctx->gbr, disp); } @@ -747,7 +741,7 @@ sh4op(i1100_0100_iiii_iiii) sh4op(i1100_0101_iiii_iiii) { u32 disp = GetImm8(op); - ReadMemBOS16(r[0],gbr,(disp<<1)); + ReadMemBOS16(ctx->r[0], ctx->gbr, (disp << 1)); } @@ -756,14 +750,14 @@ sh4op(i1100_0110_iiii_iiii) { u32 disp = GetImm8(op); - ReadMemBOU32(r[0],gbr,(disp<<2)); + ReadMemBOU32(ctx->r[0], ctx->gbr, (disp << 2)); } // mova @(,PC),R0 sh4op(i1100_0111_iiii_iiii) { - r[0] = ((next_pc+2)&0xFFFFFFFC)+(GetImm8(op)<<2); + ctx->r[0] = ((ctx->pc + 2) & 0xFFFFFFFC) + (GetImm8(op) << 2); } // @@ -773,10 +767,11 @@ sh4op(i1100_0111_iiii_iiii) sh4op(i1101_nnnn_iiii_iiii) { u32 n = GetN(op); - u32 disp = (GetImm8(op)); + u32 disp = GetImm8(op); - ReadMemU32(r[n],(disp<<2) + ((next_pc+2) & 0xFFFFFFFC)); + ReadMemU32(ctx->r[n], (disp << 2) + ((ctx->pc + 2) & 0xFFFFFFFC)); } + // // Exxx @@ -784,7 +779,7 @@ sh4op(i1101_nnnn_iiii_iiii) sh4op(i1110_nnnn_iiii_iiii) { u32 n = GetN(op); - r[n] = (u32)(s32)(s8)(GetSImm8(op)); + ctx->r[n] = (u32)(s32)(s8)GetSImm8(op); } @@ -792,35 +787,40 @@ sh4op(i1110_nnnn_iiii_iiii) sh4op(i0000_nnnn_1100_0011) { u32 n = GetN(op); - WriteMemU32(r[n],r[0]);//at r[n],r[0] + WriteMemU32(ctx->r[n], ctx->r[0]); // TODO ocache } //clrmac sh4op(i0000_0000_0010_1000) { - mac.full=0; + ctx->mac.full = 0; +} + +static void executeDelaySlot() { + Sh4Interpreter::Instance->ExecuteDelayslot(); } //braf sh4op(i0000_nnnn_0010_0011) { u32 n = GetN(op); - u32 newpc = r[n] + next_pc + 2;// - ExecuteDelayslot(); //WARN : r[n] can change here - next_pc = newpc; + u32 newpc = ctx->r[n] + ctx->pc + 2; + executeDelaySlot(); //WARN : r[n] can change here + ctx->pc = newpc; } + //bsrf sh4op(i0000_nnnn_0000_0011) { u32 n = GetN(op); - u32 newpc = r[n] + next_pc +2; - u32 newpr = next_pc + 2; + u32 newpc = ctx->r[n] + ctx->pc +2; + u32 newpr = ctx->pc + 2; - ExecuteDelayslot(); //WARN : pr and r[n] can change here + executeDelaySlot(); //WARN : pr and r[n] can change here - pr = newpr; - next_pc = newpc; + ctx->pr = newpr; + ctx->pc = newpc; debugger::subroutineCall(); } @@ -828,9 +828,9 @@ sh4op(i0000_nnnn_0000_0011) //rte sh4op(i0000_0000_0010_1011) { - u32 newpc = spc; - ExecuteDelayslot_RTE(); - next_pc = newpc; + u32 newpc = ctx->spc; + Sh4Interpreter::Instance->ExecuteDelayslot_RTE(); + ctx->pc = newpc; if (UpdateSR()) UpdateINTC(); debugger::subroutineReturn(); @@ -840,23 +840,24 @@ sh4op(i0000_0000_0010_1011) //rts sh4op(i0000_0000_0000_1011) { - u32 newpc=pr; - ExecuteDelayslot(); //WARN : pr can change here - next_pc=newpc; + u32 newpc = ctx->pr; + executeDelaySlot(); //WARN : pr can change here + ctx->pc = newpc; debugger::subroutineReturn(); } -u32 branch_target_s8(u32 op) +u32 branch_target_s8(Sh4Context *ctx, u32 op) { - return GetSImm8(op)*2 + 2 + next_pc; + return GetSImm8(op) * 2 + 2 + ctx->pc; } + // bf sh4op(i1000_1011_iiii_iiii) { - if (sr.T==0) + if (ctx->sr.T == 0) { //direct jump - next_pc = branch_target_s8(op); + ctx->pc = branch_target_s8(ctx, op); } } @@ -864,12 +865,12 @@ sh4op(i1000_1011_iiii_iiii) // bf.s sh4op(i1000_1111_iiii_iiii) { - if (sr.T==0) + if (ctx->sr.T == 0) { //delay 1 instruction - u32 newpc=branch_target_s8(op); - ExecuteDelayslot(); - next_pc = newpc; + u32 newpc = branch_target_s8(ctx, op); + executeDelaySlot(); + ctx->pc = newpc; } } @@ -877,10 +878,10 @@ sh4op(i1000_1111_iiii_iiii) // bt sh4op(i1000_1001_iiii_iiii) { - if (sr.T != 0) + if (ctx->sr.T != 0) { //direct jump - next_pc = branch_target_s8(op); + ctx->pc = branch_target_s8(ctx, op); } } @@ -888,37 +889,37 @@ sh4op(i1000_1001_iiii_iiii) // bt.s sh4op(i1000_1101_iiii_iiii) { - if (sr.T != 0) + if (ctx->sr.T != 0) { //delay 1 instruction - u32 newpc=branch_target_s8(op); - ExecuteDelayslot(); - next_pc = newpc; + u32 newpc = branch_target_s8(ctx, op); + executeDelaySlot(); + ctx->pc = newpc; } } -u32 branch_target_s12(u32 op) +static u32 branch_target_s12(Sh4Context *ctx, u32 op) { - return GetSImm12(op)*2 + 2 + next_pc; + return GetSImm12(op) * 2 + 2 + ctx->pc; } // bra sh4op(i1010_iiii_iiii_iiii) { - u32 newpc = branch_target_s12(op); - ExecuteDelayslot(); - next_pc=newpc; + u32 newpc = branch_target_s12(ctx, op); + executeDelaySlot(); + ctx->pc = newpc; } // bsr sh4op(i1011_iiii_iiii_iiii) { - u32 newpr = next_pc + 2; //return after delayslot - u32 newpc = branch_target_s12(op); - ExecuteDelayslot(); + u32 newpr = ctx->pc + 2; //return after delayslot + u32 newpc = branch_target_s12(ctx, op); + executeDelaySlot(); - pr = newpr; - next_pc = newpc; + ctx->pr = newpr; + ctx->pc = newpc; debugger::subroutineCall(); } @@ -928,7 +929,7 @@ sh4op(i1100_0011_iiii_iiii) WARN_LOG(INTERPRETER, "TRAP #%X", GetImm8(op)); debugger::debugTrap(Sh4Ex_Trap); CCN_TRA = (GetImm8(op) << 2); - Do_Exception(next_pc, Sh4Ex_Trap); + Do_Exception(ctx->pc, Sh4Ex_Trap); } //jmp @ @@ -936,9 +937,9 @@ sh4op(i0100_nnnn_0010_1011) { u32 n = GetN(op); - u32 newpc=r[n]; - ExecuteDelayslot(); //r[n] can change here - next_pc=newpc; + u32 newpc = ctx->r[n]; + executeDelaySlot(); //r[n] can change here + ctx->pc = newpc; } //jsr @ @@ -946,12 +947,12 @@ sh4op(i0100_nnnn_0000_1011) { u32 n = GetN(op); - u32 newpr = next_pc + 2; //return after delayslot - u32 newpc= r[n]; - ExecuteDelayslot(); //r[n]/pr can change here + u32 newpr = ctx->pc + 2; //return after delayslot + u32 newpc = ctx->r[n]; + executeDelaySlot(); //r[n]/pr can change here - pr = newpr; - next_pc = newpc; + ctx->pr = newpr; + ctx->pc = newpc; debugger::subroutineCall(); } @@ -959,8 +960,7 @@ sh4op(i0100_nnnn_0000_1011) sh4op(i0000_0000_0001_1011) { //just wait for an Interrupt - - int i=0,s=1; + int i = 0, s = 1; while (!UpdateSystem_INTC())//448 { @@ -971,9 +971,8 @@ sh4op(i0000_0000_0001_1011) } } //if not Interrupted , we must rexecute the sleep - if (s==0) - next_pc-=2;// re execute sleep - + if (s == 0) + ctx->pc -= 2;// re execute sleep } @@ -982,7 +981,7 @@ sh4op(i0011_nnnn_mmmm_1000) { u32 n = GetN(op); u32 m = GetM(op); - r[n] -=r[m]; + ctx->r[n] -= ctx->r[m]; } //add , @@ -990,10 +989,10 @@ sh4op(i0011_nnnn_mmmm_1100) { u32 n = GetN(op); u32 m = GetM(op); - r[n] +=r[m]; + ctx->r[n] += ctx->r[m]; } - // +// // 7xxx //add #, @@ -1001,7 +1000,7 @@ sh4op(i0111_nnnn_iiii_iiii) { u32 n = GetN(op); s32 stmp1 = GetSImm8(op); - r[n] +=stmp1; + ctx->r[n] += stmp1; } //Bitwise logical operations @@ -1012,7 +1011,7 @@ sh4op(i0010_nnnn_mmmm_1001) { u32 n = GetN(op); u32 m = GetM(op); - r[n] &= r[m]; + ctx->r[n] &= ctx->r[m]; } //xor , @@ -1020,7 +1019,7 @@ sh4op(i0010_nnnn_mmmm_1010) { u32 n = GetN(op); u32 m = GetM(op); - r[n] ^= r[m]; + ctx->r[n] ^= ctx->r[m]; } //or , @@ -1028,7 +1027,7 @@ sh4op(i0010_nnnn_mmmm_1011) { u32 n = GetN(op); u32 m = GetM(op); - r[n] |= r[m]; + ctx->r[n] |= ctx->r[m]; } @@ -1036,7 +1035,7 @@ sh4op(i0010_nnnn_mmmm_1011) sh4op(i0100_nnnn_0000_1000) { u32 n = GetN(op); - r[n] <<= 2; + ctx->r[n] <<= 2; } @@ -1044,7 +1043,7 @@ sh4op(i0100_nnnn_0000_1000) sh4op(i0100_nnnn_0001_1000) { u32 n = GetN(op); - r[n] <<= 8; + ctx->r[n] <<= 8; } @@ -1052,7 +1051,7 @@ sh4op(i0100_nnnn_0001_1000) sh4op(i0100_nnnn_0010_1000) { u32 n = GetN(op); - r[n] <<= 16; + ctx->r[n] <<= 16; } @@ -1060,7 +1059,7 @@ sh4op(i0100_nnnn_0010_1000) sh4op(i0100_nnnn_0000_1001) { u32 n = GetN(op); - r[n] >>= 2; + ctx->r[n] >>= 2; } @@ -1068,7 +1067,7 @@ sh4op(i0100_nnnn_0000_1001) sh4op(i0100_nnnn_0001_1001) { u32 n = GetN(op); - r[n] >>= 8; + ctx->r[n] >>= 8; } @@ -1076,22 +1075,22 @@ sh4op(i0100_nnnn_0001_1001) sh4op(i0100_nnnn_0010_1001) { u32 n = GetN(op); - r[n] >>= 16; + ctx->r[n] >>= 16; } // and #,R0 sh4op(i1100_1001_iiii_iiii) { u32 imm = GetImm8(op); - r[0] &= imm; + ctx->r[0] &= imm; } // xor #,R0 sh4op(i1100_1010_iiii_iiii) { - u32 imm = GetImm8(op); - r[0] ^= imm; + u32 imm = GetImm8(op); + ctx->r[0] ^= imm; } @@ -1099,7 +1098,7 @@ sh4op(i1100_1010_iiii_iiii) sh4op(i1100_1011_iiii_iiii) { u32 imm = GetImm8(op); - r[0] |= imm; + ctx->r[0] |= imm; } @@ -1123,7 +1122,7 @@ sh4op(i0000_0000_0011_1000) sh4op(i0000_nnnn_1001_0011) { #ifdef STRICT_MODE - ocache.WriteBack(r[GetN(op)], false, true); + ocache.WriteBack(ctx->r[GetN(op)], false, true); #endif } @@ -1131,7 +1130,7 @@ sh4op(i0000_nnnn_1001_0011) sh4op(i0000_nnnn_1010_0011) { #ifdef STRICT_MODE - ocache.WriteBack(r[GetN(op)], true, true); + ocache.WriteBack(ctx->r[GetN(op)], true, true); #endif } @@ -1139,7 +1138,7 @@ sh4op(i0000_nnnn_1010_0011) sh4op(i0000_nnnn_1011_0011) { #ifdef STRICT_MODE - ocache.WriteBack(r[GetN(op)], true, false); + ocache.WriteBack(ctx->r[GetN(op)], true, false); #endif } @@ -1147,14 +1146,11 @@ sh4op(i0000_nnnn_1011_0011) sh4op(i0000_nnnn_1000_0011) { u32 n = GetN(op); - u32 Dest = r[n]; + u32 Dest = ctx->r[n]; if ((Dest >> 26) == 0x38) // Store Queue { - if (CCN_MMUCR.AT) - do_sqw_mmu(Dest); - else - do_sqw_nommu(Dest, sq_both); + ctx->doSqWrite(Dest, ctx); } else { @@ -1169,32 +1165,32 @@ sh4op(i0000_nnnn_1000_0011) //sets sh4op(i0000_0000_0101_1000) { - sr.S = 1; + ctx->sr.S = 1; } //clrs sh4op(i0000_0000_0100_1000) { - sr.S = 0; + ctx->sr.S = 0; } //sett sh4op(i0000_0000_0001_1000) { - sr.T = 1; + ctx->sr.T = 1; } //clrt sh4op(i0000_0000_0000_1000) { - sr.T = 0; + ctx->sr.T = 0; } //movt sh4op(i0000_nnnn_0010_1001) { u32 n = GetN(op); - r[n] = sr.T; + ctx->r[n] = ctx->sr.T; } //************************ Reg Compares ************************ @@ -1203,30 +1199,30 @@ sh4op(i0100_nnnn_0001_0001) { u32 n = GetN(op); - if (((s32)r[n]) >= 0) - sr.T = 1; + if (((s32)ctx->r[n]) >= 0) + ctx->sr.T = 1; else - sr.T = 0; + ctx->sr.T = 0; } //cmp/pl sh4op(i0100_nnnn_0001_0101) { u32 n = GetN(op); - if ((s32)r[n] > 0) - sr.T = 1; + if ((s32)ctx->r[n] > 0) + ctx->sr.T = 1; else - sr.T = 0; + ctx->sr.T = 0; } //cmp/eq #,R0 sh4op(i1000_1000_iiii_iiii) { u32 imm = (u32)(s32)(GetSImm8(op)); - if (r[0] == imm) - sr.T =1; + if (ctx->r[0] == imm) + ctx->sr.T = 1; else - sr.T =0; + ctx->sr.T = 0; } //cmp/eq , @@ -1235,10 +1231,10 @@ sh4op(i0011_nnnn_mmmm_0000) u32 n = GetN(op); u32 m = GetM(op); - if (r[m] == r[n]) - sr.T = 1; + if (ctx->r[m] == ctx->r[n]) + ctx->sr.T = 1; else - sr.T = 0; + ctx->sr.T = 0; } //cmp/hs , @@ -1246,10 +1242,10 @@ sh4op(i0011_nnnn_mmmm_0010) { u32 n = GetN(op); u32 m = GetM(op); - if (r[n] >= r[m]) - sr.T=1; + if (ctx->r[n] >= ctx->r[m]) + ctx->sr.T = 1; else - sr.T=0; + ctx->sr.T = 0; } //cmp/ge , @@ -1257,10 +1253,10 @@ sh4op(i0011_nnnn_mmmm_0011) { u32 n = GetN(op); u32 m = GetM(op); - if ((s32)r[n] >= (s32)r[m]) - sr.T = 1; + if ((s32)ctx->r[n] >= (s32)ctx->r[m]) + ctx->sr.T = 1; else - sr.T = 0; + ctx->sr.T = 0; } //cmp/hi , @@ -1269,10 +1265,10 @@ sh4op(i0011_nnnn_mmmm_0110) u32 n = GetN(op); u32 m = GetM(op); - if (r[n] > r[m]) - sr.T=1; + if (ctx->r[n] > ctx->r[m]) + ctx->sr.T = 1; else - sr.T=0; + ctx->sr.T = 0; } //cmp/gt , @@ -1281,10 +1277,10 @@ sh4op(i0011_nnnn_mmmm_0111) u32 n = GetN(op); u32 m = GetM(op); - if (((s32)r[n]) > ((s32)r[m])) - sr.T = 1; + if ((s32)ctx->r[n] > (s32)ctx->r[m]) + ctx->sr.T = 1; else - sr.T = 0; + ctx->sr.T = 0; } //cmp/str , @@ -1297,27 +1293,27 @@ sh4op(i0010_nnnn_mmmm_1100) u32 temp; u32 HH, HL, LH, LL; - temp = r[n] ^ r[m]; + temp = ctx->r[n] ^ ctx->r[m]; - HH=(temp&0xFF000000)>>24; - HL=(temp&0x00FF0000)>>16; - LH=(temp&0x0000FF00)>>8; - LL=temp&0x000000FF; - HH=HH&&HL&&LH&&LL; - if (HH==0) - sr.T=1; + HH = (temp & 0xFF000000) >> 24; + HL = (temp & 0x00FF0000) >> 16; + LH = (temp & 0x0000FF00) >> 8; + LL = temp & 0x000000FF; + HH = HH && HL && LH && LL; + if (HH == 0) + ctx->sr.T = 1; else - sr.T=0; + ctx->sr.T = 0; } //tst #,R0 sh4op(i1100_1000_iiii_iiii) { - u32 utmp1 = r[0] & GetImm8(op); + u32 utmp1 = ctx->r[0] & GetImm8(op); if (utmp1 == 0) - sr.T = 1; + ctx->sr.T = 1; else - sr.T = 0; + ctx->sr.T = 0; } //tst , sh4op(i0010_nnnn_mmmm_1000) @@ -1325,10 +1321,10 @@ sh4op(i0010_nnnn_mmmm_1000) u32 n = GetN(op); u32 m = GetM(op); - if ((r[n] & r[m])!=0) - sr.T=0; + if ((ctx->r[n] & ctx->r[m]) != 0) + ctx->sr.T = 0; else - sr.T=1; + ctx->sr.T = 1; } //************************ mulls! ************************ @@ -1337,7 +1333,7 @@ sh4op(i0010_nnnn_mmmm_1110) { u32 n = GetN(op); u32 m = GetM(op); - mac.l = (u16)r[n] * (u16)r[m]; + ctx->mac.l = (u16)ctx->r[n] * (u16)ctx->r[m]; } //muls.w , @@ -1346,7 +1342,7 @@ sh4op(i0010_nnnn_mmmm_1111) u32 n = GetN(op); u32 m = GetM(op); - mac.l = (u32)((s16)r[n] * (s16)r[m]); + ctx->mac.l = (u32)((s16)ctx->r[n] * (s16)ctx->r[m]); } //dmulu.l , sh4op(i0011_nnnn_mmmm_0101) @@ -1354,7 +1350,7 @@ sh4op(i0011_nnnn_mmmm_0101) u32 n = GetN(op); u32 m = GetM(op); - mac.full = (u64)r[n] * (u64)r[m]; + ctx->mac.full = (u64)ctx->r[n] * (u64)ctx->r[m]; } //dmuls.l , @@ -1363,7 +1359,7 @@ sh4op(i0011_nnnn_mmmm_1101) u32 n = GetN(op); u32 m = GetM(op); - mac.full = (s64)(s32)r[n] * (s64)(s32)r[m]; + ctx->mac.full = (s64)(s32)ctx->r[n] * (s64)(s32)ctx->r[m]; } @@ -1372,7 +1368,7 @@ sh4op(i0100_nnnn_mmmm_1111) { u32 n = GetN(op); u32 m = GetM(op); - if (sr.S!=0) + if (ctx->sr.S != 0) { die("mac.w @+,@+ : S=1"); } @@ -1380,14 +1376,14 @@ sh4op(i0100_nnnn_mmmm_1111) { s32 rm,rn; - rn = (s32)(s16)ReadMem16(r[n]); - rm = (s32)(s16)ReadMem16(r[m] + (n == m ? 2 : 0)); + rn = (s32)(s16)ReadMem16(ctx->r[n]); + rm = (s32)(s16)ReadMem16(ctx->r[m] + (n == m ? 2 : 0)); - r[n]+=2; - r[m]+=2; + ctx->r[n] += 2; + ctx->r[m] += 2; - s32 mul=rm * rn; - mac.full+=(s64)mul; + s32 mul = rm * rn; + ctx->mac.full += (s64)mul; } } //mac.l @+,@+ @@ -1397,14 +1393,14 @@ sh4op(i0000_nnnn_mmmm_1111) u32 m = GetM(op); s32 rm, rn; - verify(sr.S==0); + verify(ctx->sr.S == 0); - ReadMemS32(rm,r[m]); - ReadMemS32(rn,r[n] + (n == m ? 4 : 0)); - r[m] += 4; - r[n] += 4; + ReadMemS32(rm, ctx->r[m]); + ReadMemS32(rn, ctx->r[n] + (n == m ? 4 : 0)); + ctx->r[m] += 4; + ctx->r[n] += 4; - mac.full += (s64)rm * (s64)rn; + ctx->mac.full += (s64)rm * (s64)rn; } //mul.l , @@ -1412,15 +1408,15 @@ sh4op(i0000_nnnn_mmmm_0111) { u32 n = GetN(op); u32 m = GetM(op); - mac.l = (u32)((((s32)r[n]) * ((s32)r[m]))); + ctx->mac.l = (u32)((((s32)ctx->r[n]) * ((s32)ctx->r[m]))); } //************************ Div ! ************************ //div0u sh4op(i0000_0000_0001_1001) { - sr.Q = 0; - sr.M = 0; - sr.T = 0; + ctx->sr.Q = 0; + ctx->sr.M = 0; + ctx->sr.T = 0; } //div0s , sh4op(i0010_nnnn_mmmm_0111) @@ -1428,9 +1424,9 @@ sh4op(i0010_nnnn_mmmm_0111) u32 n = GetN(op); u32 m = GetM(op); - sr.Q = r[n] >> 31; - sr.M = r[m] >> 31; - sr.T = sr.M ^ sr.Q; + ctx->sr.Q = ctx->r[n] >> 31; + ctx->sr.M = ctx->r[m] >> 31; + ctx->sr.T = ctx->sr.M ^ ctx->sr.Q; } //div1 , @@ -1439,46 +1435,46 @@ sh4op(i0011_nnnn_mmmm_0100) u32 n = GetN(op); u32 m = GetM(op); - const u8 old_q = sr.Q; - sr.Q = (u8)((0x80000000 & r[n]) != 0); + const u8 old_q = ctx->sr.Q; + ctx->sr.Q = (u8)((0x80000000 & ctx->r[n]) != 0); - const u32 old_rm = r[m]; - r[n] <<= 1; - r[n] |= sr.T; + const u32 old_rm = ctx->r[m]; + ctx->r[n] <<= 1; + ctx->r[n] |= ctx->sr.T; - const u32 old_rn = r[n]; + const u32 old_rn = ctx->r[n]; if (old_q == 0) { - if (sr.M == 0) + if (ctx->sr.M == 0) { - r[n] -= old_rm; - bool tmp1 = r[n] > old_rn; - sr.Q = sr.Q ^ tmp1; + ctx->r[n] -= old_rm; + bool tmp1 = ctx->r[n] > old_rn; + ctx->sr.Q = ctx->sr.Q ^ tmp1; } else { - r[n] += old_rm; - bool tmp1 = r[n] < old_rn; - sr.Q = !sr.Q ^ tmp1; + ctx->r[n] += old_rm; + bool tmp1 = ctx->r[n] < old_rn; + ctx->sr.Q = !ctx->sr.Q ^ tmp1; } } else { - if (sr.M == 0) + if (ctx->sr.M == 0) { - r[n] += old_rm; - bool tmp1 = r[n] < old_rn; - sr.Q = sr.Q ^ tmp1; + ctx->r[n] += old_rm; + bool tmp1 = ctx->r[n] < old_rn; + ctx->sr.Q = ctx->sr.Q ^ tmp1; } else { - r[n] -= old_rm; - bool tmp1 = r[n] > old_rn; - sr.Q = !sr.Q ^ tmp1; + ctx->r[n] -= old_rm; + bool tmp1 = ctx->r[n] > old_rn; + ctx->sr.Q = !ctx->sr.Q ^ tmp1; } } - sr.T = (sr.Q == sr.M); + ctx->sr.T = (ctx->sr.Q == ctx->sr.M); } //************************ Simple maths ************************ @@ -1487,18 +1483,18 @@ sh4op(i0011_nnnn_mmmm_1110) { u32 n = GetN(op); u32 m = GetM(op); - u32 tmp1 = r[n] + r[m]; - u32 tmp0 = r[n]; + u32 tmp1 = ctx->r[n] + ctx->r[m]; + u32 tmp0 = ctx->r[n]; - r[n] = tmp1 + sr.T; + ctx->r[n] = tmp1 + ctx->sr.T; if (tmp0 > tmp1) - sr.T = 1; + ctx->sr.T = 1; else - sr.T = 0; + ctx->sr.T = 0; - if (tmp1 > r[n]) - sr.T = 1; + if (tmp1 > ctx->r[n]) + ctx->sr.T = 1; } // addv , @@ -1507,16 +1503,16 @@ sh4op(i0011_nnnn_mmmm_1111) //Retail game "Twinkle Star Sprites" "uses" this opcode. u32 n = GetN(op); u32 m = GetM(op); - s64 br=(s64)(s32)r[n]+(s64)(s32)r[m]; + s64 br = (s64)(s32)ctx->r[n] + (s64)(s32)ctx->r[m]; if (br >=0x80000000) - sr.T=1; - else if (br < (s64) (0xFFFFFFFF80000000u)) - sr.T=1; + ctx->sr.T = 1; + else if (br < (s64)0xFFFFFFFF80000000u) + ctx->sr.T = 1; else - sr.T=0; + ctx->sr.T = 0; - r[n]+=r[m]; + ctx->r[n] += ctx->r[m]; } //subc , @@ -1525,17 +1521,17 @@ sh4op(i0011_nnnn_mmmm_1010) u32 n = GetN(op); u32 m = GetM(op); - u32 tmp1 = r[n] - r[m]; - u32 tmp0 = r[n]; - r[n] = tmp1 - sr.T; + u32 tmp1 = ctx->r[n] - ctx->r[m]; + u32 tmp0 = ctx->r[n]; + ctx->r[n] = tmp1 - ctx->sr.T; if (tmp0 < tmp1) - sr.T=1; + ctx->sr.T = 1; else - sr.T=0; + ctx->sr.T = 0; - if (tmp1 < r[n]) - sr.T=1; + if (tmp1 < ctx->r[n]) + ctx->sr.T = 1; } //subv , @@ -1544,26 +1540,27 @@ sh4op(i0011_nnnn_mmmm_1011) //Retail game "Twinkle Star Sprites" "uses" this opcode. u32 n = GetN(op); u32 m = GetM(op); - s64 br=(s64)(s32)r[n]-(s64)(s32)r[m]; + s64 br = (s64)(s32)ctx->r[n] - (s64)(s32)ctx->r[m]; - if (br >=0x80000000) - sr.T=1; + if (br >= 0x80000000) + ctx->sr.T = 1; else if (br < (s64) (0xFFFFFFFF80000000u)) - sr.T=1; + ctx->sr.T = 1; else - sr.T=0; + ctx->sr.T = 0; - r[n]-=r[m]; + ctx->r[n] -= ctx->r[m]; } + //dt sh4op(i0100_nnnn_0001_0000) { u32 n = GetN(op); - r[n]-=1; - if (r[n] == 0) - sr.T=1; + ctx->r[n] -= 1; + if (ctx->r[n] == 0) + ctx->sr.T = 1; else - sr.T=0; + ctx->sr.T = 0; } //negc , @@ -1573,16 +1570,16 @@ sh4op(i0110_nnnn_mmmm_1010) u32 m = GetM(op); //r[n]=-r[m]-sr.T; - u32 tmp=0 - r[m]; - r[n]=tmp - sr.T; + u32 tmp = 0 - ctx->r[m]; + ctx->r[n] = tmp - ctx->sr.T; - if (0sr.T = 1; else - sr.T=0; + ctx->sr.T = 0; - if (tmpr[n]) + ctx->sr.T = 1; } @@ -1591,7 +1588,7 @@ sh4op(i0110_nnnn_mmmm_1011) { u32 n = GetN(op); u32 m = GetM(op); - r[n] = -r[m]; + ctx->r[n] = -ctx->r[m]; } //not , @@ -1600,7 +1597,7 @@ sh4op(i0110_nnnn_mmmm_0111) u32 n = GetN(op); u32 m = GetM(op); - r[n] = ~r[m]; + ctx->r[n] = ~ctx->r[m]; } @@ -1610,15 +1607,15 @@ sh4op(i0100_nnnn_0000_0000) { u32 n = GetN(op); - sr.T = r[n] >> 31; - r[n] <<= 1; + ctx->sr.T = ctx->r[n] >> 31; + ctx->r[n] <<= 1; } //shal sh4op(i0100_nnnn_0010_0000) { - u32 n=GetN(op); - sr.T=r[n]>>31; - r[n]=((s32)r[n])<<1; + u32 n = GetN(op); + ctx->sr.T = ctx->r[n] >> 31; + ctx->r[n] = ((s32)ctx->r[n]) << 1; } @@ -1626,8 +1623,8 @@ sh4op(i0100_nnnn_0010_0000) sh4op(i0100_nnnn_0000_0001) { u32 n = GetN(op); - sr.T = r[n] & 0x1; - r[n] >>= 1; + ctx->sr.T = ctx->r[n] & 0x1; + ctx->r[n] >>= 1; } //shar @@ -1635,8 +1632,8 @@ sh4op(i0100_nnnn_0010_0001) { u32 n = GetN(op); - sr.T=r[n] & 1; - r[n]=((s32)r[n])>>1; + ctx->sr.T = ctx->r[n] & 1; + ctx->r[n] = ((s32)ctx->r[n]) >> 1; } //shad , @@ -1644,15 +1641,13 @@ sh4op(i0100_nnnn_mmmm_1100) { u32 n = GetN(op); u32 m = GetM(op); - u32 sgn = r[m] & 0x80000000; + u32 sgn = ctx->r[m] & 0x80000000; if (sgn == 0) - r[n] <<= (r[m] & 0x1F); - else if ((r[m] & 0x1F) == 0) - { - r[n]=((s32)r[n])>>31; - } + ctx->r[n] <<= ctx->r[m] & 0x1F; + else if ((ctx->r[m] & 0x1F) == 0) + ctx->r[n] = (s32)ctx->r[n] >> 31; else - r[n] = ((s32)r[n]) >> ((~r[m] & 0x1F) + 1); + ctx->r[n] = (s32)ctx->r[n] >> ((~ctx->r[m] & 0x1F) + 1); } @@ -1661,32 +1656,24 @@ sh4op(i0100_nnnn_mmmm_1101) { u32 n = GetN(op); u32 m = GetM(op); - u32 sgn = r[m] & 0x80000000; + u32 sgn = ctx->r[m] & 0x80000000; if (sgn == 0) - r[n] <<= (r[m] & 0x1F); - else if ((r[m] & 0x1F) == 0) - { - r[n] = 0; - } + ctx->r[n] <<= (ctx->r[m] & 0x1F); + else if ((ctx->r[m] & 0x1F) == 0) + ctx->r[n] = 0; else - r[n] = ((u32)r[n]) >> ((~r[m] & 0x1F) + 1); //isn't this the same as -r[m] ? + ctx->r[n] = ((u32)ctx->r[n]) >> ((~ctx->r[m] & 0x1F) + 1); //isn't this the same as -r[m] ? } - //rotcl sh4op(i0100_nnnn_0010_0100) { u32 n = GetN(op); - u32 t; - - t = sr.T; - - sr.T = r[n] >> 31; - - r[n] <<= 1; - - r[n]|=t; + u32 t = ctx->sr.T; + ctx->sr.T = ctx->r[n] >> 31; + ctx->r[n] <<= 1; + ctx->r[n] |= t; } @@ -1695,27 +1682,19 @@ sh4op(i0100_nnnn_0000_0100) { u32 n = GetN(op); - sr.T=r[n]>>31; - - r[n] <<= 1; - - r[n]|=sr.T; + ctx->sr.T = ctx->r[n] >> 31; + ctx->r[n] <<= 1; + ctx->r[n] |= ctx->sr.T; } //rotcr sh4op(i0100_nnnn_0010_0101) { u32 n = GetN(op); - u32 t; - - - t = r[n] & 0x1; - - r[n] >>= 1; - - r[n] |=(sr.T)<<31; - - sr.T = t; + u32 t = ctx->r[n] & 0x1; + ctx->r[n] >>= 1; + ctx->r[n] |= ctx->sr.T << 31; + ctx->sr.T = t; } @@ -1723,10 +1702,12 @@ sh4op(i0100_nnnn_0010_0101) sh4op(i0100_nnnn_0000_0101) { u32 n = GetN(op); - sr.T = r[n] & 0x1; - r[n] >>= 1; - r[n] |= ((sr.T) << 31); + + ctx->sr.T = ctx->r[n] & 0x1; + ctx->r[n] >>= 1; + ctx->r[n] |= ctx->sr.T << 31; } + //************************ byte reorder/sign ************************ //swap.b , sh4op(i0110_nnnn_mmmm_1000) @@ -1734,8 +1715,8 @@ sh4op(i0110_nnnn_mmmm_1000) u32 m = GetM(op); u32 n = GetN(op); - u32 rg=r[m]; - r[n] = (rg & 0xFFFF0000) | ((rg&0xFF)<<8) | ((rg>>8)&0xFF); + u32 rg = ctx->r[m]; + ctx->r[n] = (rg & 0xFFFF0000) | ((rg & 0xFF) << 8) | ((rg >> 8) & 0xFF); } @@ -1745,18 +1726,17 @@ sh4op(i0110_nnnn_mmmm_1001) u32 n = GetN(op); u32 m = GetM(op); - u16 t = (u16)(r[m]>>16); - r[n] = (r[m] << 16) | t; + u16 t = (u16)(ctx->r[m] >> 16); + ctx->r[n] = (ctx->r[m] << 16) | t; } - //extu.b , sh4op(i0110_nnnn_mmmm_1100) { u32 n = GetN(op); u32 m = GetM(op); - r[n] = (u32)(u8)r[m]; + ctx->r[n] = (u32)(u8)ctx->r[m]; } @@ -1765,7 +1745,7 @@ sh4op(i0110_nnnn_mmmm_1101) { u32 n = GetN(op); u32 m = GetM(op); - r[n] =(u32)(u16) r[m]; + ctx->r[n] = (u32)(u16)ctx->r[m]; } @@ -1775,8 +1755,7 @@ sh4op(i0110_nnnn_mmmm_1110) u32 n = GetN(op); u32 m = GetM(op); - r[n] = (u32)(s32)(s8)(u8)(r[m]); - + ctx->r[n] = (u32)(s32)(s8)(u8)ctx->r[m]; } @@ -1786,7 +1765,7 @@ sh4op(i0110_nnnn_mmmm_1111) u32 n = GetN(op); u32 m = GetM(op); - r[n] = (u32)(s32)(s16)(u16)(r[m]); + ctx->r[n] = (u32)(s32)(s16)(u16)ctx->r[m]; } @@ -1796,7 +1775,7 @@ sh4op(i0010_nnnn_mmmm_1101) u32 n = GetN(op); u32 m = GetM(op); - r[n] = ((r[n] >> 16) & 0xFFFF) | ((r[m] << 16) & 0xFFFF0000); + ctx->r[n] = ((ctx->r[n] >> 16) & 0xFFFF) | ((ctx->r[m] << 16) & 0xFFFF0000); } @@ -1805,57 +1784,56 @@ sh4op(i0010_nnnn_mmmm_1101) sh4op(i1100_1100_iiii_iiii) { //Retail game "Twinkle Star Sprites" "uses" this opcode. - u32 imm=GetImm8(op); + u32 imm = GetImm8(op); - u32 temp = (u8)ReadMem8(gbr+r[0]); + u32 temp = (u8)ReadMem8(ctx->gbr + ctx->r[0]); temp &= imm; - if (temp==0) - sr.T=1; + if (temp == 0) + ctx->sr.T = 1; else - sr.T=0; + ctx->sr.T = 0; } //and.b #,@(R0,GBR) sh4op(i1100_1101_iiii_iiii) { - u8 temp = (u8)ReadMem8(gbr+r[0]); + u8 temp = (u8)ReadMem8(ctx->gbr + ctx->r[0]); temp &= GetImm8(op); - WriteMem8(gbr +r[0], temp); + WriteMem8(ctx->gbr + ctx->r[0], temp); } //xor.b #,@(R0,GBR) sh4op(i1100_1110_iiii_iiii) { - u8 temp = (u8)ReadMem8(gbr+r[0]); + u8 temp = (u8)ReadMem8(ctx->gbr + ctx->r[0]); temp ^= GetImm8(op); - WriteMem8(gbr +r[0], temp); + WriteMem8(ctx->gbr + ctx->r[0], temp); } //or.b #,@(R0,GBR) sh4op(i1100_1111_iiii_iiii) { - u8 temp = (u8)ReadMem8(gbr+r[0]); + u8 temp = (u8)ReadMem8(ctx->gbr + ctx->r[0]); temp |= GetImm8(op); - WriteMem8(gbr+r[0], temp); + WriteMem8(ctx->gbr + ctx->r[0], temp); } + //tas.b @ sh4op(i0100_nnnn_0001_1011) { u32 n = GetN(op); - u8 val; - - val=(u8)ReadMem8(r[n]); + u8 val = (u8)ReadMem8(ctx->r[n]); u32 srT; if (val == 0) @@ -1865,9 +1843,9 @@ sh4op(i0100_nnnn_0001_1011) val |= 0x80; - WriteMem8(r[n], val); + WriteMem8(ctx->r[n], val); - sr.T=srT; + ctx->sr.T = srT; } //************************ Opcodes that read/write the status registers ************************ @@ -1875,30 +1853,30 @@ sh4op(i0100_nnnn_0001_1011) sh4op(i0000_nnnn_0000_0010)//0002 { u32 n = GetN(op); - r[n] = sh4_sr_GetFull(); + ctx->r[n] = ctx->sr.getFull(); } //sts FPSCR, sh4op(i0000_nnnn_0110_1010) { u32 n = GetN(op); - r[n] = fpscr.full; + ctx->r[n] = ctx->fpscr.full; } //sts.l FPSCR,@- sh4op(i0100_nnnn_0110_0010) { u32 n = GetN(op); - WriteMemU32(r[n] - 4, fpscr.full); - r[n] -= 4; + WriteMemU32(ctx->r[n] - 4, ctx->fpscr.full); + ctx->r[n] -= 4; } //stc.l SR,@- sh4op(i0100_nnnn_0000_0011) { u32 n = GetN(op); - WriteMemU32(r[n] - 4, sh4_sr_GetFull()); - r[n] -= 4; + WriteMemU32(ctx->r[n] - 4, ctx->sr.getFull()); + ctx->r[n] -= 4; } //lds.l @+,FPSCR @@ -1906,10 +1884,9 @@ sh4op(i0100_nnnn_0110_0110) { u32 n = GetN(op); - ReadMemU32(fpscr.full,r[n]); - - UpdateFPSCR(); - r[n] += 4; + ReadMemU32(ctx->fpscr.full, ctx->r[n]); + Sh4Context::UpdateFPSCR(ctx); + ctx->r[n] += 4; } //ldc.l @+,SR @@ -1918,10 +1895,10 @@ sh4op(i0100_nnnn_0000_0111) u32 n = GetN(op); u32 sr_t; - ReadMemU32(sr_t,r[n]); + ReadMemU32(sr_t, ctx->r[n]); - sh4_sr_SetFull(sr_t); - r[n] += 4; + ctx->sr.setFull(sr_t); + ctx->r[n] += 4; if (UpdateSR()) UpdateINTC(); } @@ -1930,15 +1907,15 @@ sh4op(i0100_nnnn_0000_0111) sh4op(i0100_nnnn_0110_1010) { u32 n = GetN(op); - fpscr.full = r[n]; - UpdateFPSCR(); + ctx->fpscr.full = ctx->r[n]; + Sh4Context::UpdateFPSCR(ctx); } //ldc ,SR sh4op(i0100_nnnn_0000_1110) { u32 n = GetN(op); - sh4_sr_SetFull(r[n]); + ctx->sr.setFull(ctx->r[n]); if (UpdateSR()) UpdateINTC(); } @@ -1948,6 +1925,6 @@ sh4op(iNotImplemented) INFO_LOG(INTERPRETER, "iNimp %04X", op); debugger::debugTrap(Sh4Ex_IllegalInstr); - throw SH4ThrownException(next_pc - 2, Sh4Ex_IllegalInstr); + throw SH4ThrownException(ctx->pc - 2, Sh4Ex_IllegalInstr); } diff --git a/core/hw/sh4/interpr/sh4_opcodes.h b/core/hw/sh4/interpr/sh4_opcodes.h index 3748685b4..f2a64275d 100644 --- a/core/hw/sh4/interpr/sh4_opcodes.h +++ b/core/hw/sh4/interpr/sh4_opcodes.h @@ -1,6 +1,6 @@ #pragma once #include "types.h" -#include "../sh4_interpreter.h" +#include "hw/sh4/sh4_opcode_list.h" /* Opcodes :) */ diff --git a/core/hw/sh4/modules/ccn.cpp b/core/hw/sh4/modules/ccn.cpp index 23e4ba51e..ae5003b5a 100644 --- a/core/hw/sh4/modules/ccn.cpp +++ b/core/hw/sh4/modules/ccn.cpp @@ -7,6 +7,8 @@ #include "hw/sh4/sh4_mmr.h" #include "hw/sh4/sh4_core.h" #include "hw/sh4/sh4_cache.h" +#include "cfg/option.h" +#include "emulator.h" CCNRegisters ccn; @@ -53,7 +55,7 @@ static void CCN_MMUCR_write(u32 addr, u32 value) { //printf("<*******>MMU Enabled , ONLY SQ remaps work<*******>\n"); mmu_set_state(); - sh4_cpu.ResetCache(); + emu.getSh4Executor()->ResetCache(); } } @@ -63,14 +65,14 @@ static void CCN_CCR_write(u32 addr, u32 value) temp.reg_data = value & 0x89AF; if (temp.ICI) { - DEBUG_LOG(SH4, "Sh4: i-cache invalidation %08X", curr_pc); + DEBUG_LOG(SH4, "Sh4: i-cache invalidation %08X", Sh4cntx.pc); //Shikigami No Shiro II uses ICI frequently if (!config::DynarecEnabled) icache.Invalidate(); temp.ICI = 0; } if (temp.OCI) { - DEBUG_LOG(SH4, "Sh4: o-cache invalidation %08X", curr_pc); + DEBUG_LOG(SH4, "Sh4: o-cache invalidation %08X", Sh4cntx.pc); if (!config::DynarecEnabled) ocache.Invalidate(); temp.OCI = 0; @@ -82,6 +84,7 @@ static void CCN_CCR_write(u32 addr, u32 value) static u32 CPU_VERSION_read(u32 addr) { return 0x040205c1; // this is what a real SH7091 in a Dreamcast returns - the later Naomi BIOSes check and care! + // 0x040204d0 on a PAL VA1 dreamcast, also works } static u32 CCN_PRR_read(u32 addr) diff --git a/core/hw/sh4/modules/mmu.cpp b/core/hw/sh4/modules/mmu.cpp index 483125fbb..af9de30e0 100644 --- a/core/hw/sh4/modules/mmu.cpp +++ b/core/hw/sh4/modules/mmu.cpp @@ -85,7 +85,7 @@ void ITLB_Sync(u32 entry) template static void mmuException(MmuError mmu_error, u32 address, u32 am, F raise) { - printf_mmu("MMU exception -> pc = 0x%X : ", next_pc); + printf_mmu("MMU exception -> pc = 0x%X : ", Sh4cntx.pc); CCN_TEA = address; CCN_PTEH.VPN = address >> 10; @@ -156,7 +156,7 @@ static void mmuException(MmuError mmu_error, u32 address, u32 am, F raise) mmuException(mmu_error, address, am, [](Sh4ExceptionCode event) { debugger::debugTrap(event); // FIXME CCN_TEA and CCN_PTEH have been updated already - throw SH4ThrownException(next_pc - 2, event); + throw SH4ThrownException(Sh4cntx.pc - 2, event); }); die("Unknown mmu_error"); } @@ -165,7 +165,7 @@ static void mmuException(MmuError mmu_error, u32 address, u32 am, F raise) void DoMMUException(u32 address, MmuError mmu_error, u32 access_type) { mmuException(mmu_error, address, access_type, [](Sh4ExceptionCode event) { - Do_Exception(next_pc, event); + Do_Exception(Sh4cntx.pc, event); }); } @@ -179,7 +179,7 @@ bool mmu_match(u32 va, CCN_PTEH_type Address, CCN_PTEL_type Data) if ((((Address.VPN << 10) & mask) == (va & mask))) { - bool needAsidMatch = Data.SH == 0 && (sr.MD == 0 || CCN_MMUCR.SV == 0); + bool needAsidMatch = Data.SH == 0 && (Sh4cntx.sr.MD == 0 || CCN_MMUCR.SV == 0); if (!needAsidMatch || Address.ASID == CCN_PTEH.ASID) return true; @@ -231,7 +231,7 @@ template MmuError mmu_full_SQ(u32 va, u32& rv) { - if ((va & 3) || (CCN_MMUCR.SQMD == 1 && sr.MD == 0)) + if ((va & 3) || (CCN_MMUCR.SQMD == 1 && Sh4cntx.sr.MD == 0)) //here, or after ? return MmuError::BADADDR; @@ -250,7 +250,7 @@ MmuError mmu_full_SQ(u32 va, u32& rv) u32 md = entry->Data.PR >> 1; //Priv mode protection - if (md == 0 && sr.MD == 0) + if (md == 0 && Sh4cntx.sr.MD == 0) return MmuError::PROTECTED; //Write Protection (Lock or FW) @@ -287,7 +287,7 @@ MmuError mmu_data_translation(u32 va, u32& rv) } } - if (sr.MD == 0 && (va & 0x80000000) != 0) + if (Sh4cntx.sr.MD == 0 && (va & 0x80000000) != 0) //if on kernel, and not SQ addr -> error return MmuError::BADADDR; @@ -326,7 +326,7 @@ MmuError mmu_data_translation(u32 va, u32& rv) //0X & User mode-> protection violation //Priv mode protection - if (md == 0 && sr.MD == 0) + if (md == 0 && Sh4cntx.sr.MD == 0) return MmuError::PROTECTED; //X0 -> read olny @@ -351,7 +351,7 @@ template MmuError mmu_data_translation(u32 va, u32& rv); MmuError mmu_instruction_translation(u32 va, u32& rv) { - if (sr.MD == 0 && (va & 0x80000000) != 0) + if (Sh4cntx.sr.MD == 0 && (va & 0x80000000) != 0) // User mode on kernel address return MmuError::BADADDR; @@ -375,7 +375,7 @@ MmuError mmu_instruction_translation(u32 va, u32& rv) //0X & User mode-> protection violation //Priv mode protection - if (md == 0 && sr.MD == 0) + if (md == 0 && Sh4cntx.sr.MD == 0) return MmuError::PROTECTED; return MmuError::NONE; @@ -396,7 +396,7 @@ MmuError mmu_instruction_lookup(u32 va, const TLB_Entry** tlb_entry_ret, u32& rv if ((((entry.Address.VPN << 10) & mask) == (va & mask))) { - bool needAsidMatch = entry.Data.SH == 0 && (sr.MD == 0 || CCN_MMUCR.SV == 0); + bool needAsidMatch = entry.Data.SH == 0 && (Sh4cntx.sr.MD == 0 || CCN_MMUCR.SV == 0); if (!needAsidMatch || entry.Address.ASID == CCN_PTEH.ASID) { diff --git a/core/hw/sh4/modules/serial.cpp b/core/hw/sh4/modules/serial.cpp index 3c1da16f2..7338af8f2 100644 --- a/core/hw/sh4/modules/serial.cpp +++ b/core/hw/sh4/modules/serial.cpp @@ -120,7 +120,6 @@ void SCIFSerialPort::rxSched() } } } - // TODO fifo might have been emptied since last rx else if (!rxFifo.empty()) { setStatusBit(DR); @@ -192,6 +191,8 @@ void SCIFSerialPort::writeStatus(u16 data) data |= RDF; if (isTDFE()) data |= TDFE; + if (!rxFifo.empty()) + data |= DR; SCIF_LOG("SCIF_SCFSR2.reset %s%s%s%s%s%s%s%s", (data & ER) ? "" : "ER ", (data & TEND) ? "" : "TEND ", @@ -439,6 +440,7 @@ void SCIFSerialPort::deserialize(Deserializer& deser) statusLastRead = 0; transmitting = false; } + SCIF_SCBRR2 &= 0xff; // work around previous issues with dynarecs updateBaudRate(); } diff --git a/core/hw/sh4/modules/wince.h b/core/hw/sh4/modules/wince.h index 3967488a9..dd2e8e662 100644 --- a/core/hw/sh4/modules/wince.h +++ b/core/hw/sh4/modules/wince.h @@ -363,7 +363,7 @@ static bool wince_resolve_address(u32 va, TLB_Entry &entry) u32 paddr = *(u32 *)&mem_b[(page_group + page) & ram_mask]; if (paddr & 0x80000000) { - u32 whatever = *(u32 *)&mem_b[(r_bank[4] + 0x14) & ram_mask]; + u32 whatever = *(u32 *)&mem_b[(p_sh4rcb->cntx.r_bank[4] + 0x14) & ram_mask]; if (whatever != *(u32 *)&mem_b[paddr & ram_mask]) { paddr += 12; diff --git a/core/hw/sh4/sh4_cache.h b/core/hw/sh4/sh4_cache.h index 5f9ba26e8..e99fe7f46 100644 --- a/core/hw/sh4/sh4_cache.h +++ b/core/hw/sh4/sh4_cache.h @@ -21,7 +21,7 @@ #include "types.h" #include "sh4_mem.h" #include "modules/mmu.h" -#include "hw/sh4/sh4_core.h" +#include "hw/sh4/sh4_if.h" #include "serialize.h" #include "sh4_cycles.h" @@ -55,6 +55,11 @@ static bool translatedArea(u32 area) class Sh4ICache { public: + void init(Sh4Context *ctx) { + this->ctx = ctx; + sh4cycles.init(ctx); + } + u16 ReadMem(u32 address) { bool cacheOn = false; @@ -177,7 +182,7 @@ class Sh4ICache return MmuError::BADADDR; const u32 area = address >> 29; - const bool userMode = sr.MD == 0; + const bool userMode = ctx->sr.MD == 0; if (userMode) { @@ -221,6 +226,8 @@ class Sh4ICache } std::array lines; + Sh4Cycles sh4cycles; + Sh4Context *ctx = nullptr; }; extern Sh4ICache icache; @@ -231,6 +238,11 @@ extern Sh4ICache icache; class Sh4OCache { public: + void init(Sh4Context *ctx) { + this->ctx = ctx; + sh4cycles.init(ctx); + } + template T ReadMem(u32 address) { @@ -514,7 +526,7 @@ class Sh4OCache return lookup; } const u32 area = address >> 29; - const bool userMode = sr.MD == 0; + const bool userMode = ctx->sr.MD == 0; // kernel mem protected in user mode if (userMode && (address & 0x80000000)) @@ -589,6 +601,8 @@ class Sh4OCache // TODO serialize u64 writeBackBufferCycles = 0; u64 writeThroughBufferCycles = 0; + Sh4Cycles sh4cycles; + Sh4Context *ctx = nullptr; }; extern Sh4OCache ocache; diff --git a/core/hw/sh4/sh4_core.h b/core/hw/sh4/sh4_core.h index c1994f3d3..868616dc3 100644 --- a/core/hw/sh4/sh4_core.h +++ b/core/hw/sh4/sh4_core.h @@ -1,100 +1,12 @@ #pragma once #include "types.h" #include "sh4_if.h" -#include "cfg/option.h" +#include -#define r Sh4cntx.r -#define r_bank Sh4cntx.r_bank -#define gbr Sh4cntx.gbr -#define ssr Sh4cntx.ssr -#define spc Sh4cntx.spc -#define sgr Sh4cntx.sgr -#define dbr Sh4cntx.dbr -#define vbr Sh4cntx.vbr -#define mac Sh4cntx.mac -#define pr Sh4cntx.pr -#define fpul Sh4cntx.fpul -#define next_pc Sh4cntx.pc -#define curr_pc (next_pc-2) -#define sr Sh4cntx.sr -#define fpscr Sh4cntx.fpscr -#define old_sr Sh4cntx.old_sr -#define old_fpscr Sh4cntx.old_fpscr -#define fr (&Sh4cntx.xffr[16]) -#define xf Sh4cntx.xffr -#define fr_hex ((u32*)fr) -#define xf_hex ((u32*)xf) -#define dr_hex ((u64*)fr) -#define xd_hex ((u64*)xf) -#define sh4_int_bCpuRun Sh4cntx.CpuRunning - -void UpdateFPSCR(); +int UpdateSystem_INTC(); bool UpdateSR(); -void RestoreHostRoundingMode(); void setDefaultRoundingMode(); -union DoubleReg -{ - f64 dbl; - f32 sgl[2]; -}; - -static inline f64 GetDR(u32 n) -{ -#ifdef TRACE - if (n>7) - INFO_LOG(SH4, "DR_r INDEX OVERRUN %d >7", n); -#endif - DoubleReg t; - - t.sgl[1]=fr[(n<<1) + 0]; - t.sgl[0]=fr[(n<<1) + 1]; - - return t.dbl; -} - -static inline f64 GetXD(u32 n) -{ -#ifdef TRACE - if (n>7) - INFO_LOG(SH4, "XD_r INDEX OVERRUN %d >7", n); -#endif - DoubleReg t; - - t.sgl[1]=xf[(n<<1) + 0]; - t.sgl[0]=xf[(n<<1) + 1]; - - return t.dbl; -} - -static inline void SetDR(u32 n,f64 val) -{ -#ifdef TRACE - if (n>7) - INFO_LOG(SH4, "DR_w INDEX OVERRUN %d >7", n); -#endif - DoubleReg t; - t.dbl=val; - - - fr[(n<<1) | 1]=t.sgl[0]; - fr[(n<<1) | 0]=t.sgl[1]; -} - -static inline void SetXD(u32 n,f64 val) -{ -#ifdef TRACE - if (n>7) - INFO_LOG(SH4, "XD_w INDEX OVERRUN %d >7", n); -#endif - - DoubleReg t; - t.dbl=val; - - xf[(n<<1) | 1]=t.sgl[0]; - xf[(n<<1) | 0]=t.sgl[1]; -} - struct SH4ThrownException { SH4ThrownException(u32 epc, Sh4ExceptionCode expEvn) : epc(epc), expEvn(expEvn) { } @@ -103,11 +15,6 @@ struct SH4ThrownException Sh4ExceptionCode expEvn; }; -static inline void RaiseFPUDisableException() -{ - throw SH4ThrownException(next_pc - 2, Sh4Ex_FpuDisabled); -} - static inline void AdjustDelaySlotException(SH4ThrownException& ex) { ex.epc -= 2; @@ -117,20 +24,13 @@ static inline void AdjustDelaySlotException(SH4ThrownException& ex) ex.expEvn = Sh4Ex_SlotIllegalInstr; } -// The SH4 sets the signaling bit to 0 for qNaN (unlike all recent CPUs). Some games rely on this. +// The SH4 sets the signaling bit to 0 for qNaN (unlike all recent CPUs). static inline f32 fixNaN(f32 f) { #ifdef STRICT_MODE u32& hex = *(u32 *)&f; -#ifdef __FAST_MATH__ - // fast-math - if ((hex & 0x7fffffff) > 0x7f800000) + if (std::isnan(f)) hex = 0x7fbfffff; -#else - // no fast-math - if (f != f) - hex = 0x7fbfffff; -#endif #endif return f; } @@ -139,15 +39,8 @@ static inline f64 fixNaN64(f64 f) { #ifdef STRICT_MODE u64& hex = *(u64 *)&f; -#ifdef __FAST_MATH__ - // fast-math - if ((hex & 0x7fffffffffffffffll) > 0x7ff0000000000000ll) + if (std::isnan(f)) hex = 0x7ff7ffffffffffffll; -#else - // no fast-math - if (f != f) - hex = 0x7ff7ffffffffffffll; -#endif #endif return f; } diff --git a/core/hw/sh4/sh4_core_regs.cpp b/core/hw/sh4/sh4_core_regs.cpp index 6c94093f5..7542f1388 100644 --- a/core/hw/sh4/sh4_core_regs.cpp +++ b/core/hw/sh4/sh4_core_regs.cpp @@ -10,35 +10,29 @@ #endif Sh4RCB* p_sh4rcb; -sh4_if sh4_cpu; static void ChangeGPR() { - std::swap((u32 (&)[8])r, r_bank); -} - -static void ChangeFP() -{ - std::swap((f32 (&)[16])Sh4cntx.xffr, *(f32 (*)[16])&Sh4cntx.xffr[16]); + std::swap((u32 (&)[8])Sh4cntx.r, Sh4cntx.r_bank); } //called when sr is changed and we must check for reg banks etc. //returns true if interrupt pending bool UpdateSR() { - if (sr.MD) + if (Sh4cntx.sr.MD) { - if (old_sr.RB != sr.RB) + if (Sh4cntx.old_sr.RB != Sh4cntx.sr.RB) ChangeGPR();//bank change } else { - if (old_sr.RB) + if (Sh4cntx.old_sr.RB) ChangeGPR();//switch } - old_sr.status = sr.status; - old_sr.RB &= sr.MD; + Sh4cntx.old_sr.status = Sh4cntx.sr.status; + Sh4cntx.old_sr.RB &= Sh4cntx.sr.MD; return SRdecode(); } @@ -47,21 +41,21 @@ bool UpdateSR() static u32 old_rm = 0xFF; static u32 old_dn = 0xFF; -static void setHostRoundingMode() +static void setHostRoundingMode(u32 roundingMode, u32 denorm2zero) { - if (old_rm != fpscr.RM || old_dn != fpscr.DN) + if (old_rm != roundingMode || old_dn != denorm2zero) { - old_rm = fpscr.RM; - old_dn = fpscr.DN; + old_rm = roundingMode; + old_dn = denorm2zero; //Correct rounding is required by some games (SOTB, etc) #ifdef _MSC_VER - if (fpscr.RM == 1) //if round to 0 , set the flag + if (roundingMode == 1) // if round to 0 , set the flag _controlfp(_RC_CHOP, _MCW_RC); else _controlfp(_RC_NEAR, _MCW_RC); - if (fpscr.DN) //denormals are considered 0 + if (denorm2zero == 1) // denormals are considered 0 _controlfp(_DN_FLUSH, _MCW_DN); else _controlfp(_DN_SAVE, _MCW_DN); @@ -71,20 +65,20 @@ static void setHostRoundingMode() u32 temp=0x1f80; //no flush to zero && round to nearest - if (fpscr.RM==1) //if round to 0 , set the flag + if (roundingMode==1) // if round to 0 , set the flag temp|=(3<<13); - if (fpscr.DN) //denormals are considered 0 + if (denorm2zero == 1) // denormals are considered 0 temp|=(1<<15); asm("ldmxcsr %0" : : "m"(temp)); #elif HOST_CPU==CPU_ARM static const unsigned int offMask = 0x04086060; unsigned int onMask = 0x02000000; - if (fpscr.RM == 1) //if round to 0 , set the flag + if (roundingMode == 1) onMask |= 3 << 22; - if (fpscr.DN) + if (denorm2zero == 1) onMask |= 1 << 24; #ifdef __ANDROID__ @@ -110,10 +104,10 @@ static void setHostRoundingMode() static const unsigned long off_mask = 0x04080000; unsigned long on_mask = 0x02000000; // DN=1 Any operation involving one or more NaNs returns the Default NaN - if (fpscr.RM == 1) // if round to 0, set the flag + if (roundingMode == 1) on_mask |= 3 << 22; - if (fpscr.DN) + if (denorm2zero == 1) on_mask |= 1 << 24; // flush denormalized numbers to zero asm volatile @@ -135,131 +129,24 @@ static void setHostRoundingMode() } //called when fpscr is changed and we must check for reg banks etc.. -void UpdateFPSCR() +void DYNACALL Sh4Context::UpdateFPSCR(Sh4Context *ctx) { - if (fpscr.FR !=old_fpscr.FR) - ChangeFP(); // FPU bank change + if (ctx->fpscr.FR != ctx->old_fpscr.FR) + // FPU bank change + std::swap(ctx->xf, ctx->fr); - old_fpscr=fpscr; - setHostRoundingMode(); + ctx->old_fpscr = ctx->fpscr; + setHostRoundingMode(ctx->fpscr.RM, ctx->fpscr.DN); } -void RestoreHostRoundingMode() +void Sh4Context::restoreHostRoundingMode() { old_rm = 0xFF; old_dn = 0xFF; - setHostRoundingMode(); + setHostRoundingMode(fpscr.RM, fpscr.DN); } void setDefaultRoundingMode() { - u32 savedRM = fpscr.RM; - u32 savedDN = fpscr.DN; - fpscr.RM = 0; - fpscr.DN = 0; - setHostRoundingMode(); - fpscr.RM = savedRM; - fpscr.DN = savedDN; -} - -static u32* Sh4_int_GetRegisterPtr(Sh4RegType reg) -{ - if ((reg>=reg_r0) && (reg<=reg_r15)) - { - return &r[reg-reg_r0]; - } - else if ((reg>=reg_r0_Bank) && (reg<=reg_r7_Bank)) - { - return &r_bank[reg-reg_r0_Bank]; - } - else if ((reg>=reg_fr_0) && (reg<=reg_fr_15)) - { - return &fr_hex[reg-reg_fr_0]; - } - else if ((reg>=reg_xf_0) && (reg<=reg_xf_15)) - { - return &xf_hex[reg-reg_xf_0]; - } - else - { - switch(reg) - { - case reg_gbr : - return &gbr; - break; - case reg_vbr : - return &vbr; - break; - - case reg_ssr : - return &ssr; - break; - - case reg_spc : - return &spc; - break; - - case reg_sgr : - return &sgr; - break; - - case reg_dbr : - return &dbr; - break; - - case reg_mach : - return &mac.h; - break; - - case reg_macl : - return &mac.l; - break; - - case reg_pr : - return ≺ - break; - - case reg_fpul : - return &fpul; - break; - - - case reg_nextpc : - return &next_pc; - break; - - case reg_sr_status : - return &sr.status; - break; - - case reg_sr_T : - return &sr.T; - break; - - case reg_old_fpscr : - return &old_fpscr.full; - break; - - case reg_fpscr : - return &fpscr.full; - break; - - case reg_pc_dyn: - return &Sh4cntx.jdyn; - - case reg_temp: - return &Sh4cntx.temp_reg; - - default: - ERROR_LOG(SH4, "Unknown register ID %d", reg); - die("Invalid reg"); - return 0; - break; - } - } -} - -u32* GetRegPtr(u32 reg) -{ - return Sh4_int_GetRegisterPtr((Sh4RegType)reg); + setHostRoundingMode(0, 0); } diff --git a/core/hw/sh4/sh4_cycles.cpp b/core/hw/sh4/sh4_cycles.cpp index 5588c9d6a..e55ca0760 100644 --- a/core/hw/sh4/sh4_cycles.cpp +++ b/core/hw/sh4/sh4_cycles.cpp @@ -17,6 +17,74 @@ along with Flycast. If not, see . */ #include "sh4_cycles.h" +#include "modules/mmu.h" + +int Sh4Cycles::countCycles(u16 op) +{ + sh4_opcodelistentry *opcode = OpDesc[op]; + int cycles = 0; +#ifndef STRICT_MODE + static const bool isMemOp[45] { + false, + false, + true, // all mem moves, ldtlb, sts.l FPUL/FPSCR, @-Rn, lds.l @Rn+,FPUL + true, // gbr-based load/store + false, + true, // tst.b #, @(R0,GBR) + true, // and/or/xor.b #, @(R0,GBR) + true, // tas.b @Rn + false, + false, + false, + false, + true, // movca.l R0, @Rn + false, + false, + false, + false, + true, // ldc.l @Rn+, VBR/SPC/SSR/Rn_Bank/DBR + true, // ldc.l @Rn+, GBR/SGR + true, // ldc.l @Rn+, SR + false, + false, + true, // stc.l DBR/SR/GBR/VBR/SSR/SPC/Rn_Bank, @-Rn + true, // stc.l SGR, @-Rn + false, + true, // lds.l @Rn+, PR + false, + true, // sts.l PR, @-Rn + false, + true, // lds.l @Rn+, MACH/MACL + false, + true, // sts.l MACH/MACL, @-Rn + false, + true, // lds.l @Rn+,FPSCR + false, + true, // mac.wl @Rm+,@Rn+ + }; + if (isMemOp[opcode->ex_type]) + { + if (++memOps < 4) + cycles = mmu_enabled() ? 5 : 2; + } + // TODO only for mem read? +#endif + + if (lastUnit == CO + || opcode->unit == CO + || (lastUnit == opcode->unit && lastUnit != MT)) + { + // cannot run in parallel + lastUnit = opcode->unit; + cycles += opcode->IssueCycles; + } + else + { + // can run in parallel + lastUnit = CO; + } + return cycles * cpuRatio; +} // TODO additional wait cycles depending on area?: // Area Wait cycles (not including external wait) diff --git a/core/hw/sh4/sh4_cycles.h b/core/hw/sh4/sh4_cycles.h index 1d517c675..246a1bd4d 100644 --- a/core/hw/sh4/sh4_cycles.h +++ b/core/hw/sh4/sh4_cycles.h @@ -21,99 +21,37 @@ #include "sh4_opcode_list.h" #include "sh4_if.h" #include "sh4_sched.h" -#include "modules/mmu.h" class Sh4Cycles { public: Sh4Cycles(int cpuRatio = 1) : cpuRatio(cpuRatio) {} + void init(Sh4Context *ctx) { + this->ctx = ctx; + } + void executeCycles(u16 op) { - Sh4cntx.cycle_counter -= countCycles(op); + ctx->cycle_counter -= countCycles(op); } void addCycles(int cycles) const { - Sh4cntx.cycle_counter -= cycles; + ctx->cycle_counter -= cycles; } void addReadAccessCycles(u32 addr, u32 size) const { - Sh4cntx.cycle_counter -= readAccessCycles(addr, size); + ctx->cycle_counter -= readAccessCycles(addr, size); } void addWriteAccessCycles(u32 addr, u32 size) const { - Sh4cntx.cycle_counter -= writeAccessCycles(addr, size); + ctx->cycle_counter -= writeAccessCycles(addr, size); } - int countCycles(u16 op) - { - sh4_opcodelistentry *opcode = OpDesc[op]; - int cycles = 0; -#ifndef STRICT_MODE - static const bool isMemOp[45] { - false, - false, - true, // all mem moves, ldtlb, sts.l FPUL/FPSCR, @-Rn, lds.l @Rn+,FPUL - true, // gbr-based load/store - false, - true, // tst.b #, @(R0,GBR) - true, // and/or/xor.b #, @(R0,GBR) - true, // tas.b @Rn - false, - false, - false, - false, - true, // movca.l R0, @Rn - false, - false, - false, - false, - true, // ldc.l @Rn+, VBR/SPC/SSR/Rn_Bank/DBR - true, // ldc.l @Rn+, GBR/SGR - true, // ldc.l @Rn+, SR - false, - false, - true, // stc.l DBR/SR/GBR/VBR/SSR/SPC/Rn_Bank, @-Rn - true, // stc.l SGR, @-Rn - false, - true, // lds.l @Rn+, PR - false, - true, // sts.l PR, @-Rn - false, - true, // lds.l @Rn+, MACH/MACL - false, - true, // sts.l MACH/MACL, @-Rn - false, - true, // lds.l @Rn+,FPSCR - false, - true, // mac.wl @Rm+,@Rn+ - }; - if (isMemOp[opcode->ex_type]) - { - if (++memOps < 4) - cycles = mmu_enabled() ? 5 : 2; - } - // TODO only for mem read? -#endif - - if (lastUnit == CO - || opcode->unit == CO - || (lastUnit == opcode->unit && lastUnit != MT)) - { - // cannot run in parallel - lastUnit = opcode->unit; - cycles += opcode->IssueCycles; - } - else - { - // can run in parallel - lastUnit = CO; - } - return cycles * cpuRatio; - } + int countCycles(u16 op); void reset() { @@ -121,8 +59,8 @@ class Sh4Cycles memOps = 0; } - static u64 now() { - return sh4_sched_now64() + SH4_TIMESLICE - Sh4cntx.cycle_counter; + u64 now() { + return sh4_sched_now64() + SH4_TIMESLICE - ctx->cycle_counter; } int readAccessCycles(u32 addr, u32 size) const { @@ -142,6 +80,5 @@ class Sh4Cycles sh4_eu lastUnit = CO; const int cpuRatio; int memOps = 0; + Sh4Context *ctx = nullptr; }; - -extern Sh4Cycles sh4cycles; diff --git a/core/hw/sh4/sh4_if.h b/core/hw/sh4/sh4_if.h index 096365c11..217e53071 100644 --- a/core/hw/sh4/sh4_if.h +++ b/core/hw/sh4/sh4_if.h @@ -1,126 +1,7 @@ #pragma once #include "types.h" #include "stdclass.h" - -enum Sh4RegType -{ - //GPRs - reg_r0, - reg_r1, - reg_r2, - reg_r3, - reg_r4, - reg_r5, - reg_r6, - reg_r7, - reg_r8, - reg_r9, - reg_r10, - reg_r11, - reg_r12, - reg_r13, - reg_r14, - reg_r15, - - //FPU, bank 0 - reg_fr_0, - reg_fr_1, - reg_fr_2, - reg_fr_3, - reg_fr_4, - reg_fr_5, - reg_fr_6, - reg_fr_7, - reg_fr_8, - reg_fr_9, - reg_fr_10, - reg_fr_11, - reg_fr_12, - reg_fr_13, - reg_fr_14, - reg_fr_15, - - //FPU, bank 1 - reg_xf_0, - reg_xf_1, - reg_xf_2, - reg_xf_3, - reg_xf_4, - reg_xf_5, - reg_xf_6, - reg_xf_7, - reg_xf_8, - reg_xf_9, - reg_xf_10, - reg_xf_11, - reg_xf_12, - reg_xf_13, - reg_xf_14, - reg_xf_15, - - //GPR Interrupt bank - reg_r0_Bank, - reg_r1_Bank, - reg_r2_Bank, - reg_r3_Bank, - reg_r4_Bank, - reg_r5_Bank, - reg_r6_Bank, - reg_r7_Bank, - - //Misc regs - reg_gbr, - reg_ssr, - reg_spc, - reg_sgr, - reg_dbr, - reg_vbr, - reg_mach, - reg_macl, - reg_pr, - reg_fpul, - reg_nextpc, - reg_sr_status, //Only the status bits - reg_sr_T, //Only T - reg_old_fpscr, - reg_fpscr, - - reg_pc_dyn, //Write only, for dynarec only (dynamic block exit address) - reg_temp, - - sh4_reg_count, - - /* - These are virtual registers, used by the dynarec decoder - */ - regv_dr_0, - regv_dr_2, - regv_dr_4, - regv_dr_6, - regv_dr_8, - regv_dr_10, - regv_dr_12, - regv_dr_14, - - regv_xd_0, - regv_xd_2, - regv_xd_4, - regv_xd_6, - regv_xd_8, - regv_xd_10, - regv_xd_12, - regv_xd_14, - - regv_fv_0, - regv_fv_4, - regv_fv_8, - regv_fv_12, - - regv_xmtrx, - regv_fmtrx, - - NoReg=-1 -}; +#include // SR (status register) @@ -145,8 +26,6 @@ union sr_status_t u32 status; }; -#define STATUS_MASK 0x700083F2 - // Status register with isolated T bit. // Used in place of the normal SR bitfield so that the T bit can be // handled as a regular register. This simplifies dynarec implementations. @@ -173,6 +52,17 @@ struct sr_t u32 status; }; u32 T; + + static constexpr u32 MASK = 0x700083F2; + + u32 getFull() const { + return (status & MASK) | T; + } + + void setFull(u32 v) { + status = v & MASK; + T = v & 1; + } }; // FPSCR (fpu status and control register) @@ -210,20 +100,28 @@ struct fpscr_t }; //sh4 interface -struct sh4_if +class Sh4Executor { - void (*Start)(); - void (*Run)(); - void (*Stop)(); - void (*Step)(); - void (*Reset)(bool hard); - void (*Init)(); - void (*Term)(); - void (*ResetCache)(); - bool (*IsCpuRunning)(); +public: + virtual ~Sh4Executor() = default; + virtual void Run() = 0; + virtual void Start() = 0; + virtual void Stop() = 0; + virtual void Step() = 0; + virtual void Reset(bool hard) = 0; + virtual void Init() = 0; + virtual void Term() = 0; + virtual void ResetCache() = 0; + virtual bool IsCpuRunning() = 0; }; -extern sh4_if sh4_cpu; +struct alignas(32) SQBuffer { + u8 data[32]; +}; + +void setSqwHandler(); +struct Sh4Context; +typedef void DYNACALL SQWriteFunc(u32 dst, Sh4Context *ctx); struct alignas(64) Sh4Context { @@ -231,7 +129,10 @@ struct alignas(64) Sh4Context { struct { - f32 xffr[32]; + SQBuffer sq_buffer[2]; + + float xf[16]; + float fr[16]; u32 r[16]; union @@ -264,20 +165,55 @@ struct alignas(64) Sh4Context u32 temp_reg; int cycle_counter; + + SQWriteFunc *doSqWrite; }; - u64 raw[64-8]; + u64 raw[64]; }; -}; -static_assert(sizeof(Sh4Context) == 448, "Invalid Sh4Context size"); -struct alignas(32) SQBuffer { - u8 data[32]; -}; + u32& fr_hex(int idx) { + assert(idx >= 0 && idx <= 15); + return reinterpret_cast(fr[idx]); + } + u64& dr_hex(int idx) { + assert(idx >= 0 && idx <= 7); + return *reinterpret_cast(&fr[idx * 2]); + } + u64& xd_hex(int idx) { + assert(idx >= 0 && idx <= 7); + return *reinterpret_cast(&xf[idx * 2]); + } + + double getDR(u32 n) + { + assert(n <= 7); + DoubleReg t; + t.sgl[1] = fr[n * 2]; + t.sgl[0] = fr[n * 2 + 1]; -void setSqwHandler(); -void DYNACALL do_sqw_mmu(u32 dst); + return t.dbl; + } -typedef void DYNACALL sqw_fp(u32 dst, const SQBuffer *sqb); + void setDR(u32 n, double val) + { + assert(n <= 7); + DoubleReg t; + t.dbl = val; + fr[n * 2] = t.sgl[1]; + fr[n * 2 + 1] = t.sgl[0]; + } + + static void DYNACALL UpdateFPSCR(Sh4Context *ctx); + void restoreHostRoundingMode(); + +private: + union DoubleReg + { + double dbl; + float sgl[2]; + }; +}; +static_assert(sizeof(Sh4Context) == 512, "Invalid Sh4Context size"); #define FPCB_SIZE (RAM_SIZE_MAX/2) #define FPCB_MASK (FPCB_SIZE -1) @@ -287,40 +223,23 @@ typedef void DYNACALL sqw_fp(u32 dst, const SQBuffer *sqb); // want to be an i8r4 value that can be substracted in one op (such as 0x4100000) #define FPCB_PAD 0x100000 #else -#define FPCB_PAD 0x10000 +// For other systems we could use PAGE_SIZE, except on windows that has a 64 KB granularity for memory mapping +#define FPCB_PAD 64_KB #endif struct alignas(PAGE_SIZE) Sh4RCB { void* fpcb[FPCB_SIZE]; - u8 _pad[FPCB_PAD - sizeof(Sh4Context) - sizeof(SQBuffer) * 2 - sizeof(void *)]; - sqw_fp* do_sqw_nommu; - SQBuffer sq_buffer[2]; + u8 _pad[FPCB_PAD - sizeof(Sh4Context)]; Sh4Context cntx; }; +static_assert((sizeof(Sh4RCB) % PAGE_SIZE) == 0, "sizeof(Sh4RCB) not multiple of PAGE_SIZE"); extern Sh4RCB* p_sh4rcb; - -static inline u32 sh4_sr_GetFull() -{ - return (p_sh4rcb->cntx.sr.status & STATUS_MASK) | p_sh4rcb->cntx.sr.T; -} - -static inline void sh4_sr_SetFull(u32 value) -{ - p_sh4rcb->cntx.sr.status=value & STATUS_MASK; - p_sh4rcb->cntx.sr.T=value&1; -} - -#define do_sqw_nommu sh4rcb.do_sqw_nommu - -#define sh4rcb (*p_sh4rcb) -#define Sh4cntx (sh4rcb.cntx) +#define Sh4cntx (p_sh4rcb->cntx) //Get an interface to sh4 interpreter -void Get_Sh4Interpreter(sh4_if* cpu); -void Get_Sh4Recompiler(sh4_if* cpu); - -u32* GetRegPtr(u32 reg); +Sh4Executor *Get_Sh4Interpreter(); +Sh4Executor *Get_Sh4Recompiler(); enum Sh4ExceptionCode : u16 { diff --git a/core/hw/sh4/sh4_interpreter.h b/core/hw/sh4/sh4_interpreter.h index 80605b04f..47dd2e444 100644 --- a/core/hw/sh4/sh4_interpreter.h +++ b/core/hw/sh4/sh4_interpreter.h @@ -1,42 +1,37 @@ #pragma once #include "types.h" +#include "sh4_cycles.h" -#undef sh4op -#define sh4op(str) void DYNACALL str (u32 op) -typedef void (DYNACALL OpCallFP) (u32 op); - -enum OpcodeType +class Sh4Interpreter : public Sh4Executor { - //basic - Normal = 0, // Heh , nothing special :P - ReadsPC = 1, // PC must be set upon calling it - WritesPC = 2, // It will write PC (branch) - Delayslot = 4, // Has a delayslot opcode , valid only when WritesPC is set - - WritesSR = 8, // Writes to SR , and UpdateSR needs to be called - WritesFPSCR = 16, // Writes to FPSCR , and UpdateSR needs to be called - Invalid = 128, // Invalid - - UsesFPU = 2048, // Floating point op - FWritesFPSCR = UsesFPU | WritesFPSCR, - - // Heh, not basic :P - ReadWritePC = ReadsPC|WritesPC, // Read and writes pc :P - WritesSRRWPC = WritesSR|ReadsPC|WritesPC, - - // Branches (not delay slot): - Branch_dir = ReadWritePC, // Direct (eg , pc=r[xx]) -- this one is ReadWritePC b/c the delayslot may use pc ;) - Branch_rel = ReadWritePC, // Relative (rg pc+=10); - - // Delay slot - Branch_dir_d = Delayslot|Branch_dir, // Direct (eg , pc=r[xx]) - Branch_rel_d = Delayslot|Branch_rel, // Relative (rg pc+=10); +public: + void Run() override; + void ResetCache() override {} + void Start() override; + void Stop() override; + void Step() override; + void Reset(bool hard) override; + void Init() override; + void Term() override; + bool IsCpuRunning() override; + void ExecuteDelayslot(); + void ExecuteDelayslot_RTE(); + Sh4Context *getContext() { return ctx; } + + static Sh4Interpreter *Instance; + +protected: + Sh4Context *ctx = nullptr; + +private: + void ExecuteOpcode(u16 op); + u16 ReadNexOp(); + + Sh4Cycles sh4cycles{CPU_RATIO}; + // SH4 underclock factor when using the interpreter so that it's somewhat usable +#ifdef STRICT_MODE + static constexpr int CPU_RATIO = 1; +#else + static constexpr int CPU_RATIO = 8; +#endif }; - -void ExecuteDelayslot(); -void ExecuteDelayslot_RTE(); - -#define SH4_TIMESLICE 448 // at 112 Bangai-O doesn't start. 224 is ok - -int UpdateSystem(); -int UpdateSystem_INTC(); diff --git a/core/hw/sh4/sh4_interrupts.cpp b/core/hw/sh4/sh4_interrupts.cpp index 6274f4272..49a3be455 100644 --- a/core/hw/sh4/sh4_interrupts.cpp +++ b/core/hw/sh4/sh4_interrupts.cpp @@ -141,10 +141,10 @@ void SIIDRebuild() //Decode SR.IMSK into a interrupt mask, update and return the interrupt state bool SRdecode() { - if (sr.BL) + if (Sh4cntx.sr.BL) decoded_srimask=~0xFFFFFFFF; else - decoded_srimask=~InterruptLevelBit[sr.IMASK]; + decoded_srimask=~InterruptLevelBit[Sh4cntx.sr.IMASK]; recalc_pending_itrs(); return Sh4cntx.interrupt_pend; @@ -185,14 +185,14 @@ static void Do_Interrupt(Sh4ExceptionCode intEvn) { CCN_INTEVT = intEvn; - ssr = sh4_sr_GetFull(); - spc = next_pc; - sgr = r[15]; - sr.BL = 1; - sr.MD = 1; - sr.RB = 1; + Sh4cntx.ssr = Sh4cntx.sr.getFull(); + Sh4cntx.spc = Sh4cntx.pc; + Sh4cntx.sgr = Sh4cntx.r[15]; + Sh4cntx.sr.BL = 1; + Sh4cntx.sr.MD = 1; + Sh4cntx.sr.RB = 1; UpdateSR(); - next_pc = vbr + 0x600; + Sh4cntx.pc = Sh4cntx.vbr + 0x600; debugger::subroutineCall(); } @@ -200,19 +200,19 @@ void Do_Exception(u32 epc, Sh4ExceptionCode expEvn) { assert((expEvn >= Sh4Ex_TlbMissRead && expEvn <= Sh4Ex_SlotIllegalInstr) || expEvn == Sh4Ex_FpuDisabled || expEvn == Sh4Ex_SlotFpuDisabled || expEvn == Sh4Ex_UserBreak); - if (sr.BL != 0) + if (Sh4cntx.sr.BL != 0) throw FlycastException("Fatal: SH4 exception when blocked"); CCN_EXPEVT = expEvn; - ssr = sh4_sr_GetFull(); - spc = epc; - sgr = r[15]; - sr.BL = 1; - sr.MD = 1; - sr.RB = 1; + Sh4cntx.ssr = Sh4cntx.sr.getFull(); + Sh4cntx.spc = epc; + Sh4cntx.sgr = Sh4cntx.r[15]; + Sh4cntx.sr.BL = 1; + Sh4cntx.sr.MD = 1; + Sh4cntx.sr.RB = 1; UpdateSR(); - next_pc = vbr + (expEvn == Sh4Ex_TlbMissRead || expEvn == Sh4Ex_TlbMissWrite ? 0x400 : 0x100); + Sh4cntx.pc = Sh4cntx.vbr + (expEvn == Sh4Ex_TlbMissRead || expEvn == Sh4Ex_TlbMissWrite ? 0x400 : 0x100); debugger::subroutineCall(); //printf("RaiseException: from pc %08x to %08x, event %x\n", epc, next_pc, expEvn); diff --git a/core/hw/sh4/sh4_mem.cpp b/core/hw/sh4/sh4_mem.cpp index f7ca1f1c5..196cdf58f 100644 --- a/core/hw/sh4/sh4_mem.cpp +++ b/core/hw/sh4/sh4_mem.cpp @@ -11,6 +11,7 @@ #include "hw/pvr/pvr_mem.h" #include "hw/mem/addrspace.h" #include "hw/sh4/modules/mmu.h" +#include "cfg/option.h" #ifdef STRICT_MODE #include "sh4_cache.h" diff --git a/core/hw/sh4/sh4_mmr.cpp b/core/hw/sh4/sh4_mmr.cpp index 5372003a0..314ff8f82 100644 --- a/core/hw/sh4/sh4_mmr.cpp +++ b/core/hw/sh4/sh4_mmr.cpp @@ -12,7 +12,6 @@ #include "serialize.h" #include "sh4_interrupts.h" #include "sh4_sched.h" -#include "sh4_interpreter.h" #include #include @@ -615,7 +614,7 @@ void sh4_mmr_reset(bool hard) ubc.reset(); MMU_reset(); - memset(p_sh4rcb->sq_buffer, 0, sizeof(p_sh4rcb->sq_buffer)); + memset(p_sh4rcb->cntx.sq_buffer, 0, sizeof(p_sh4rcb->cntx.sq_buffer)); } void sh4_mmr_term() @@ -646,10 +645,10 @@ void map_area7() void map_p4() { // Store Queues -- Write only 32bit - addrspace::mapBlock(p_sh4rcb->sq_buffer, 0xE0, 0xE0, 63); - addrspace::mapBlock(p_sh4rcb->sq_buffer, 0xE1, 0xE1, 63); - addrspace::mapBlock(p_sh4rcb->sq_buffer, 0xE2, 0xE2, 63); - addrspace::mapBlock(p_sh4rcb->sq_buffer, 0xE3, 0xE3, 63); + addrspace::mapBlock(p_sh4rcb->cntx.sq_buffer, 0xE0, 0xE0, 63); + addrspace::mapBlock(p_sh4rcb->cntx.sq_buffer, 0xE1, 0xE1, 63); + addrspace::mapBlock(p_sh4rcb->cntx.sq_buffer, 0xE2, 0xE2, 63); + addrspace::mapBlock(p_sh4rcb->cntx.sq_buffer, 0xE3, 0xE3, 63); // sh4 IC, OC and TLB arrays addrspace::handler p4arrays_handler = addrspaceRegisterHandlerTemplate(ReadMem_P4, WriteMem_P4); @@ -685,8 +684,6 @@ void serialize(Serializer& ser) interrupts_serialize(ser); - ser << (*p_sh4rcb).sq_buffer; - ser << (*p_sh4rcb).cntx; sh4_sched_serialize(ser); @@ -721,8 +718,6 @@ void deserialize(Deserializer& deser) CCN_QACR_write<0>(0, CCN_QACR0.reg_data); CCN_QACR_write<1>(0, CCN_QACR1.reg_data); - deser >> (*p_sh4rcb).sq_buffer; - deser >> (*p_sh4rcb).cntx; if (deser.version() >= Deserializer::V19 && deser.version() < Deserializer::V21) deser.skip(); // sh4InterpCycles diff --git a/core/hw/sh4/sh4_mmr.h b/core/hw/sh4/sh4_mmr.h index 04bd7859a..6b6e554c7 100644 --- a/core/hw/sh4/sh4_mmr.h +++ b/core/hw/sh4/sh4_mmr.h @@ -9,8 +9,6 @@ void map_area7(); void map_p4(); -#define sq_both (sh4rcb.sq_buffer) - void sh4_mmr_init(); void sh4_mmr_reset(bool hard); void sh4_mmr_term(); @@ -1142,12 +1140,12 @@ union SCIF_SCSMR2_type { struct { - u32 CKS : 2; + u32 CKS : 2; // Clock Select u32 : 1; - u32 STOP : 1; - u32 OE_paritymode : 1; - u32 PE : 1; - u32 CHR : 1; + u32 STOP : 1; // Stop Bit Length + u32 OE_paritymode : 1; // Parity Mode + u32 PE : 1; // Parity Enable + u32 CHR : 1; // Character Length u32 : 9; //16 }; @@ -1165,13 +1163,13 @@ union SCIF_SCSCR2_type struct { u32 : 1; - u32 CKE1 : 1; + u32 CKE1 : 1; // Clock Enable 1 u32 : 1; - u32 REIE : 1; - u32 RE : 1; - u32 TE : 1; - u32 RIE : 1; - u32 TIE : 1; + u32 REIE : 1; // Receive Error Interrupt Enable + u32 RE : 1; // Receive Enable + u32 TE : 1; // Transmit Enable + u32 RIE : 1; // Receive Interrupt Enable + u32 TIE : 1; // Transmit Interrupt Enable //8 u32 : 8; //16 @@ -1188,17 +1186,17 @@ union SCIF_SCFSR2_type { struct { - u32 DR : 1; - u32 RDF : 1; - u32 PER : 1; - u32 FER : 1; - u32 BRK : 1; - u32 TDFE : 1; - u32 TEND : 1; - u32 ER : 1; + u32 DR : 1; // Receive Data Ready + u32 RDF : 1; // Receive FIFO Data Full + u32 PER : 1; // Parity Error + u32 FER : 1; // Framing Error + u32 BRK : 1; // Break Detect + u32 TDFE : 1; // Transmit FIFO Data Empty + u32 TEND : 1; // Transmit End + u32 ER : 1; // Receive Error //8 - u32 FERn : 4; - u32 PERn : 4; + u32 FERn : 4; // Number of Framing Errors + u32 PERn : 4; // Number of Parity Errors //16 }; u16 full; @@ -1213,12 +1211,12 @@ union SCIF_SCFCR2_type { struct { - u32 LOOP : 1; - u32 RFRST : 1; - u32 TFRST : 1; - u32 MCE : 1; - u32 TTRG : 2; - u32 RTRG : 2; + u32 LOOP : 1; // Loopback Test + u32 RFRST : 1; // Receive FIFO Data Register Reset + u32 TFRST : 1; // Transmit FIFO Data Register Reset + u32 MCE : 1; // Modem Control Enable + u32 TTRG : 2; // Transmit FIFO Data Number Trigger + u32 RTRG : 2; // Receive FIFO Data Number Trigger //8 u32 : 8; //16 @@ -1227,16 +1225,16 @@ union SCIF_SCFCR2_type }; #define SCIF_SCFCR2 SH4IO_REG_T(SCIF, SCFCR2) -//Read OLNY +//Read ONLY //SCIF SCFDR2 0xFFE8001C 0x1FE8001C 16 0x0000 0x0000 Held Held Pclk union SCIF_SCFDR2_type { struct { - u32 R : 5; + u32 R : 5; // Number of received data bytes u32 : 3; //8 - u32 T : 5; + u32 T : 5; // Number of untransmitted data bytes u32 : 3; //16 }; @@ -1248,13 +1246,13 @@ union SCIF_SCSPTR2_type { struct { - u32 SPB2DT : 1; - u32 SPB2IO : 1; + u32 SPB2DT : 1; // Serial Port Break Data + u32 SPB2IO : 1; // Serial Port Break I/O u32 : 2; - u32 CTSDT : 1; - u32 CTSIO : 1; - u32 RTSDT : 1; - u32 RTSIO : 1; + u32 CTSDT : 1; // Serial Port CTS Port Data + u32 CTSIO : 1; // Serial Port CTS Port I/O + u32 RTSDT : 1; // Serial Port RTS Port Data + u32 RTSIO : 1; // Serial Port RTS Port I/O //8 u32 : 8; //16 @@ -1268,7 +1266,7 @@ union SCIF_SCLSR2_type { struct { - u32 ORER : 1; + u32 ORER : 1; // Overrun Error u32 :15; //16 }; diff --git a/core/hw/sh4/sh4_opcode_list.cpp b/core/hw/sh4/sh4_opcode_list.cpp index 67ac81d2d..654fd6ac6 100644 --- a/core/hw/sh4/sh4_opcode_list.cpp +++ b/core/hw/sh4/sh4_opcode_list.cpp @@ -461,25 +461,25 @@ std::string disassemble_op(const char* tx1, u32 pc, u16 opcode) } else if (strcmp2(tx1,"FREG_N>")) { - sprintf(buf,"FR%d=%f ", GetN(opcode), p_sh4rcb->cntx.xffr[16 + GetN(opcode)]); + sprintf(buf,"FR%d=%f ", GetN(opcode), p_sh4rcb->cntx.fr[GetN(opcode)]); regs += buf; sprintf(buf,"FR%d",GetN(opcode)); } else if (strcmp2(tx1,"FREG_M>")) { - sprintf(buf,"FR%d=%f ", GetM(opcode), p_sh4rcb->cntx.xffr[16 + GetM(opcode)]); + sprintf(buf,"FR%d=%f ", GetM(opcode), p_sh4rcb->cntx.fr[GetM(opcode)]); regs += buf; sprintf(buf,"FR%d",GetM(opcode)); } else if (strcmp2(tx1, "FREG_M_SD_F>")) { - sprintf(buf,"FR%d=%f ", GetM(opcode), p_sh4rcb->cntx.xffr[16 + GetM(opcode)]); + sprintf(buf,"FR%d=%f ", GetM(opcode), p_sh4rcb->cntx.fr[GetM(opcode)]); regs += buf; sprintf(buf,"FR%d", GetM(opcode)); } else if (strcmp2(tx1,"FREG_0>")) { - sprintf(buf,"FR0=%f ", p_sh4rcb->cntx.xffr[16]); + sprintf(buf,"FR0=%f ", p_sh4rcb->cntx.fr[0]); regs += buf; sprintf(buf,"FR0"); } diff --git a/core/hw/sh4/sh4_opcode_list.h b/core/hw/sh4/sh4_opcode_list.h index a87be546e..d0b905aae 100644 --- a/core/hw/sh4/sh4_opcode_list.h +++ b/core/hw/sh4/sh4_opcode_list.h @@ -1,9 +1,11 @@ #pragma once #include "types.h" -#include "sh4_interpreter.h" - +#include "sh4_if.h" #include +#define sh4op(str) void DYNACALL str (Sh4Context *ctx, u32 op) + +typedef void (DYNACALL OpCallFP) (Sh4Context *ctx, u32 op); extern OpCallFP* OpPtr[0x10000]; typedef void OpDissasmFP(char* out,const char* const FormatString,u32 pc,u16 opcode); @@ -18,6 +20,34 @@ enum sh4_eu CO, }; +enum OpcodeType +{ + //basic + Normal = 0, // Heh , nothing special :P + ReadsPC = 1, // PC must be set upon calling it + WritesPC = 2, // It will write PC (branch) + Delayslot = 4, // Has a delayslot opcode , valid only when WritesPC is set + + WritesSR = 8, // Writes to SR , and UpdateSR needs to be called + WritesFPSCR = 16, // Writes to FPSCR , and UpdateSR needs to be called + Invalid = 128, // Invalid + + UsesFPU = 2048, // Floating point op + FWritesFPSCR = UsesFPU | WritesFPSCR, + + // Heh, not basic :P + ReadWritePC = ReadsPC|WritesPC, // Read and writes pc :P + WritesSRRWPC = WritesSR|ReadsPC|WritesPC, + + // Branches (not delay slot): + Branch_dir = ReadWritePC, // Direct (eg , pc=r[xx]) -- this one is ReadWritePC b/c the delayslot may use pc ;) + Branch_rel = ReadWritePC, // Relative (rg pc+=10); + + // Delay slot + Branch_dir_d = Delayslot|Branch_dir, // Direct (eg , pc=r[xx]) + Branch_rel_d = Delayslot|Branch_rel, // Relative (rg pc+=10); +}; + std::string disassemble_op(const char* tx1, u32 pc, u16 opcode); typedef void ( RecOpCallFP) (u32 op); diff --git a/core/hw/sh4/sh4_sched.h b/core/hw/sh4/sh4_sched.h index 029f9ba8b..b40368972 100644 --- a/core/hw/sh4/sh4_sched.h +++ b/core/hw/sh4/sh4_sched.h @@ -1,8 +1,8 @@ -#ifndef SH4_SCHED_H -#define SH4_SCHED_H - +#pragma once #include "types.h" +#define SH4_TIMESLICE 448 // at 112 Bangai-O doesn't start. 224 is ok + /* tag, as passed on sh4_sched_register sch_cycles, the cycle duration that the callback requested (sh4_sched_request) @@ -53,5 +53,3 @@ void sh4_sched_serialize(Serializer& ser); void sh4_sched_deserialize(Deserializer& deser); void sh4_sched_serialize(Serializer& ser, int id); void sh4_sched_deserialize(Deserializer& deser, int id); - -#endif //SH4_SCHED_H diff --git a/core/hw/sh4/storeq.cpp b/core/hw/sh4/storeq.cpp index 5a8f9f0ba..2f4f71267 100644 --- a/core/hw/sh4/storeq.cpp +++ b/core/hw/sh4/storeq.cpp @@ -22,14 +22,13 @@ static u32 CCN_QACR_TR[2]; template -void DYNACALL do_sqw(u32 Dest, const SQBuffer *sqb) +static void DYNACALL sqWrite(u32 dest, Sh4Context *ctx) { - u32 Address; - + u32 address; //Translate the SQ addresses as needed if (mmu_on) { - mmu_TranslateSQW(Dest, &Address); + mmu_TranslateSQW(dest, &address); } else { @@ -38,52 +37,46 @@ void DYNACALL do_sqw(u32 Dest, const SQBuffer *sqb) u32 QACR = CCN_QACR_TR[0]; //QACR has already 0xE000_0000 - Address = QACR + (Dest & ~0x1f); + address = QACR + (dest & ~0x1f); } - if (((Address >> 26) & 7) != 4)//Area 4 + if (((address >> 26) & 7) != 4)//Area 4 { - const SQBuffer *sq = &sqb[(Dest >> 5) & 1]; - WriteMemBlock_nommu_sq(Address, sq); + const SQBuffer *sq = &ctx->sq_buffer[(dest >> 5) & 1]; + WriteMemBlock_nommu_sq(address, sq); } else { - TAWriteSQ(Address, sqb); + TAWriteSQ(address, ctx->sq_buffer); // TODO pass the correct SQBuffer instead of letting TAWriteSQ deal with it } } -void DYNACALL do_sqw_mmu(u32 dst) { - do_sqw(dst, sq_both); -} - -static void DYNACALL do_sqw_simplemmu(u32 dst, const SQBuffer *sqb) { - do_sqw(dst, sqb); -} - //yes, this micro optimization makes a difference -static void DYNACALL do_sqw_nommu_area_3(u32 dst, const SQBuffer *sqb) +static void DYNACALL sqWrite_nommu_area_3(u32 dest, Sh4Context *ctx) { - SQBuffer *pmem = (SQBuffer *)((u8 *)sqb + sizeof(Sh4RCB::sq_buffer) + sizeof(Sh4RCB::cntx) + 0x0C000000); - pmem += (dst & (RAM_SIZE_MAX - 1)) >> 5; - *pmem = sqb[(dst >> 5) & 1]; + SQBuffer *pmem = (SQBuffer *)((u8 *)ctx + sizeof(Sh4Context) + 0x0C000000); + pmem += (dest & (RAM_SIZE_MAX - 1)) >> 5; + *pmem = ctx->sq_buffer[(dest >> 5) & 1]; } -static void DYNACALL do_sqw_nommu_area_3_nonvmem(u32 dst, const SQBuffer *sqb) +static void DYNACALL sqWrite_nommu_area_3_nonvmem(u32 dest, Sh4Context *ctx) { u8* pmem = &mem_b[0]; - memcpy((SQBuffer *)&pmem[dst & (RAM_MASK - 0x1F)], &sqb[(dst >> 5) & 1], sizeof(SQBuffer)); + memcpy((SQBuffer *)&pmem[dest & (RAM_MASK - 0x1F)], &ctx->sq_buffer[(dest >> 5) & 1], sizeof(SQBuffer)); } -static void DYNACALL do_sqw_nommu_full(u32 dst, const SQBuffer *sqb) { - do_sqw(dst, sqb); +static void DYNACALL sqWriteTA(u32 dest, Sh4Context *ctx) +{ + TAWriteSQ(dest, ctx->sq_buffer); } void setSqwHandler() { + Sh4Context& ctx = p_sh4rcb->cntx; if (CCN_MMUCR.AT == 1) { - do_sqw_nommu = &do_sqw_simplemmu; + ctx.doSqWrite = &sqWrite; } else { @@ -95,17 +88,17 @@ void setSqwHandler() { case 3: if (addrspace::virtmemEnabled()) - do_sqw_nommu = &do_sqw_nommu_area_3; + ctx.doSqWrite = &sqWrite_nommu_area_3; else - do_sqw_nommu = &do_sqw_nommu_area_3_nonvmem; + ctx.doSqWrite = &sqWrite_nommu_area_3_nonvmem; break; case 4: - do_sqw_nommu = &TAWriteSQ; + ctx.doSqWrite = &sqWriteTA; break; default: - do_sqw_nommu = &do_sqw_nommu_full; + ctx.doSqWrite = &sqWrite; break; } } diff --git a/core/imgread/cdio.cpp b/core/imgread/cdio.cpp new file mode 100755 index 000000000..8b328c11e --- /dev/null +++ b/core/imgread/cdio.cpp @@ -0,0 +1,241 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . + */ +#include "build.h" +#ifdef USE_LIBCDIO +#include "types.h" +#include "imgread/common.h" +#include +#include +#include +#include + +namespace hostfs +{ + +const std::vector& getCdromDrives() +{ + static std::vector cdromDevices; + static bool devicesFetched; + + if (devicesFetched) + return cdromDevices; + devicesFetched = true; + // Set a custom log handler + cdio_log_set_handler([](cdio_log_level_t level, const char message[]) { + switch (level) + { + case CDIO_LOG_DEBUG: + DEBUG_LOG(GDROM, "%s", message); + break; + case CDIO_LOG_INFO: + INFO_LOG(GDROM, "%s", message); + break; + case CDIO_LOG_WARN: + WARN_LOG(GDROM, "%s", message); + break; + case CDIO_LOG_ERROR: + case CDIO_LOG_ASSERT: + ERROR_LOG(GDROM, "%s", message); + break; + } + }); + // Get the list of all hardware devices + char **list = cdio_get_devices(DRIVER_DEVICE); + if (list != nullptr) + { + for (char **dev = &list[0]; *dev != nullptr; dev++) + cdromDevices.push_back(*dev); + cdio_free_device_list(list); + } + + return cdromDevices; +} + +} + +struct CdioDrive; + +struct CdioTrack : public TrackFile +{ + CdioTrack(CdioDrive& disk, bool audio) + : disk(disk), audio(audio) {} + bool Read(u32 FAD, u8 *dst, SectorFormat *sector_type, u8 *subcode, SubcodeFormat *subcode_type); + + CdioDrive& disk; + bool audio; +}; + +struct CdioDrive : public Disc +{ + bool open(const char *path) + { + const std::vector& devices = hostfs::getCdromDrives(); + if (!devices.empty()) + { + // If the list isn't empty, check that an entry exists for the current path + std::string lpath(path); +#ifdef _WIN32 + if (lpath.substr(0, 4) != "\\\\.\\") + lpath = "\\\\.\\" + lpath; +#endif + bool found = false; + for (const std::string& dev : devices) + if (dev == lpath) { + found = true; + break; + } + if (!found) { + WARN_LOG(GDROM, "%s isn't a CD device", path); + return false; + } + } + pCdio = cdio_open(path, DRIVER_DEVICE); + if (pCdio == nullptr) { + WARN_LOG(GDROM, "Can't open CD device %s", path); + return false; + } + track_t firstTrk = cdio_get_first_track_num(pCdio); + track_t lastTrk = cdio_get_last_track_num(pCdio); + if (firstTrk == CDIO_INVALID_TRACK || lastTrk == CDIO_INVALID_TRACK) + { + WARN_LOG(GDROM, "Can't find first and/or last track"); + close(); + return false; + } + + Session session; + session.StartFAD = 150; + session.FirstTrack = firstTrk; + sessions.push_back(session); + type = CdDA; // TODO more CD types + + for (int i = firstTrk; i <= lastTrk; i++) + { + lba_t lba = cdio_get_track_lba(pCdio, i); + if (lba == CDIO_INVALID_LBA) + { + WARN_LOG(GDROM, "Can't find track %d", i); + close(); + return false; + } + track_format_t format = cdio_get_track_format(pCdio, i); + bool copy = true; + if (format == TRACK_FORMAT_AUDIO) { + track_flag_t copyFlag = cdio_get_track_copy_permit(pCdio, i); + copy = copyFlag == CDIO_TRACK_FLAG_TRUE; + } + else if (!tracks.empty() && !tracks.back().isDataTrack()) + { + // session 1 lead-out & session 2 lead-in and pre-gap + tracks.back().EndFAD -= 11400; + + type = CdRom_XA; + Session session; + session.StartFAD = lba; + session.FirstTrack = i; + sessions.push_back(session); + } + Track t; + t.ADR = 1; // FIXME correct? + t.CTRL = format == TRACK_FORMAT_AUDIO ? 0 : CDIO_CDROM_DATA_TRACK; + t.StartFAD = lba; + lsn_t last = cdio_get_track_last_lsn(pCdio, i); + if (last == CDIO_INVALID_LSN) + WARN_LOG(GDROM, "Can't get last lsn of track %d", i); + else + t.EndFAD = cdio_lsn_to_lba(last); + if (i == firstTrk) + sessions.front().StartFAD = t.StartFAD; + INFO_LOG(GDROM, "Track #%d: start %d end %d format %d copy %d", i, t.StartFAD, t.EndFAD, format, copy); + t.file = new CdioTrack(*this, format == TRACK_FORMAT_AUDIO); + tracks.push_back(t); + } + lba_t leadout = cdio_get_track_lba(pCdio, CDIO_CDROM_LEADOUT_TRACK); + if (leadout == CDIO_INVALID_LBA) + { + WARN_LOG(GDROM, "Can't find leadout track"); + close(); + return false; + } + LeadOut.StartFAD = leadout; + LeadOut.ADR = 1; + LeadOut.CTRL = CDIO_CDROM_DATA_TRACK; + + return true; + } + + void close() + { + if (pCdio != nullptr) { + cdio_destroy(pCdio); + pCdio = nullptr; + } + } + ~CdioDrive() { + close(); + } + + CdIo_t *pCdio = nullptr; +}; + +bool CdioTrack::Read(u32 FAD, u8 *dst, SectorFormat *sector_type, u8 *subcode, SubcodeFormat *subcode_type) +{ + lsn_t lsn = cdio_lba_to_lsn(FAD); + if (audio) + { + *sector_type = SECFMT_2352; + if (cdio_read_audio_sector(disk.pCdio, dst, lsn) != DRIVER_OP_SUCCESS) { + WARN_LOG(GDROM, "Read audio fad %d failed", FAD); + return false; + } + } + else + { + *sector_type = SECFMT_2048_MODE2_FORM1; + if (cdio_read_mode2_sector(disk.pCdio, dst, lsn, false) != DRIVER_OP_SUCCESS) + { + if (cdio_read_mode1_sector(disk.pCdio, dst, lsn, false) != DRIVER_OP_SUCCESS) { + WARN_LOG(GDROM, "Read data fad %d failed", FAD); + return false; + } + *sector_type = SECFMT_2048_MODE1; + } + } + + return true; +} + +Disc *cdio_parse(const char *file, std::vector *digest) +{ + INFO_LOG(GDROM, "Opening CDIO device %s", file); + CdioDrive *disk = new CdioDrive(); + + if (disk->open(file)) + { + if (digest != nullptr) + digest->clear(); + return disk; + } + else { + delete disk; + return nullptr; + } +} + +#endif // USE_LIBCDIO diff --git a/core/imgread/common.cpp b/core/imgread/common.cpp index b99ed778f..3a55a2b63 100644 --- a/core/imgread/common.cpp +++ b/core/imgread/common.cpp @@ -9,7 +9,7 @@ Disc* chd_parse(const char* file, std::vector *digest); Disc* gdi_parse(const char* file, std::vector *digest); Disc* cdi_parse(const char* file, std::vector *digest); Disc* cue_parse(const char* file, std::vector *digest); -Disc* ioctl_parse(const char* file, std::vector *digest); +Disc *cdio_parse(const char *file, std::vector *digest); static u32 NullDriveDiscType; Disc* disc; @@ -21,8 +21,8 @@ constexpr Disc* (*drivers[])(const char* path, std::vector *digest) gdi_parse, cdi_parse, cue_parse, -#if defined(_WIN32) && !defined(TARGET_UWP) - ioctl_parse, +#ifdef USE_LIBCDIO + cdio_parse, #endif }; @@ -96,9 +96,11 @@ Disc* OpenDisc(const std::string& path, std::vector *digest) throw FlycastException("Unknown disk format"); } +namespace gdr { + static bool loadDisk(const std::string& path) { - TermDrive(); + termDrive(); //try all drivers std::vector digest; @@ -122,7 +124,8 @@ static bool loadDisk(const std::string& path) static bool doDiscSwap(const std::string& path); -bool InitDrive(const std::string& path) + +bool initDrive(const std::string& path) { bool rc = doDiscSwap(path); if (rc && disc == nullptr) @@ -141,9 +144,10 @@ bool InitDrive(const std::string& path) return rc; } -void DiscOpenLid() +void openLid() { - TermDrive(); + settings.content.path.clear(); + termDrive(); NullDriveDiscType = Open; gd_setdisc(); } @@ -152,7 +156,7 @@ static bool doDiscSwap(const std::string& path) { if (path.empty()) { - TermDrive(); + termDrive(); NullDriveDiscType = NoDisk; return true; } @@ -164,13 +168,22 @@ static bool doDiscSwap(const std::string& path) return false; } -void TermDrive() +void termDrive() { sh4_sched_request(schedId, -1); delete disc; disc = nullptr; } +bool isOpen() { + return disc == nullptr && NullDriveDiscType == Open; +} + +bool isLoaded() { + return disc != nullptr; +} + +} // namespace gdr // //convert our nice toc struct to dc's native one :) @@ -192,10 +205,14 @@ static u32 createTrackInfoFirstLast(const Track& track, u32 tracknum) return createTrackInfo(track, tracknum << 16); } -void libGDR_ReadSector(u8 *buff, u32 startSector, u32 sectorCount, u32 sectorSize) +u32 libGDR_ReadSector(u8 *buff, u32 startSector, u32 sectorCount, u32 sectorSize, bool stopOnMiss) { if (disc != nullptr) - disc->ReadSectors(startSector, sectorCount, buff, sectorSize); + return disc->ReadSectors(startSector, sectorCount, buff, sectorSize, stopOnMiss); + if (stopOnMiss) + return 0; + memset(buff, 0, sectorCount * sectorSize); + return sectorCount; } void libGDR_GetToc(u32* to, DiskArea area) @@ -263,13 +280,13 @@ bool Disc::readSector(u32 FAD, u8 *dst, SectorFormat *sector_type, u8 *subcode, return false; } -void Disc::ReadSectors(u32 FAD, u32 count, u8* dst, u32 fmt, LoadProgress *progress) +u32 Disc::ReadSectors(u32 FAD, u32 count, u8* dst, u32 fmt, bool stopOnMiss, LoadProgress *progress) { - u8 temp[2448]; + u8 temp[2352]; SectorFormat secfmt; SubcodeFormat subfmt; - for (u32 i = 1; i <= count; i++) + for (u32 i = 0; i < count; i++) { if (progress != nullptr) { @@ -278,43 +295,40 @@ void Disc::ReadSectors(u32 FAD, u32 count, u8* dst, u32 fmt, LoadProgress *progr progress->label = "Loading..."; progress->progress = (float)i / count; } - if (readSector(FAD, temp, &secfmt, q_subchannel, &subfmt)) - { - //TODO: Proper sector conversions - if (secfmt==SECFMT_2352) - { - convertSector(temp,dst,2352,fmt,FAD); - } - else if (fmt == 2048 && secfmt==SECFMT_2336_MODE2) - memcpy(dst,temp+8,2048); - else if (fmt==2048 && (secfmt==SECFMT_2048_MODE1 || secfmt==SECFMT_2048_MODE2_FORM1 )) - { - memcpy(dst,temp,2048); - } - else if (fmt==2352 && (secfmt==SECFMT_2048_MODE1 || secfmt==SECFMT_2048_MODE2_FORM1 )) - { - INFO_LOG(GDROM, "GDR:fmt=2352;secfmt=2048"); - memcpy(dst,temp,2048); - } - else if (fmt==2048 && secfmt==SECFMT_2448_MODE2) - { - // Pier Solar and the Great Architects - convertSector(temp, dst, 2448, fmt, FAD); - } - else - { - WARN_LOG(GDROM, "ERROR: UNABLE TO CONVERT SECTOR. THIS IS FATAL. Format: %d Sector format: %d", fmt, secfmt); - //verify(false); - } - } - else + if (!readSector(FAD, temp, &secfmt, q_subchannel, &subfmt)) { WARN_LOG(GDROM, "Sector Read miss FAD: %d", FAD); - memset(dst, 0, fmt); + if (stopOnMiss) + return i; + memset(temp, 0, sizeof(temp)); + secfmt = SECFMT_2352; } - dst+=fmt; + + //TODO: Proper sector conversions + if (secfmt == SECFMT_2352) { + convertSector(temp, dst, 2352, fmt, FAD); + } + else if (fmt == 2048 && secfmt == SECFMT_2336_MODE2) { + memcpy(dst, temp + 8, 2048); + } + else if (fmt == 2048 && (secfmt == SECFMT_2048_MODE1 || secfmt == SECFMT_2048_MODE2_FORM1)) { + memcpy(dst, temp, 2048); + } + else if (fmt == 2352 && (secfmt == SECFMT_2048_MODE1 || secfmt == SECFMT_2048_MODE2_FORM1 )) { + INFO_LOG(GDROM, "GDR:fmt=2352;secfmt=2048"); + memcpy(dst, temp, 2048); + } + else if (fmt == 2048 && secfmt == SECFMT_2448_MODE2) { + // Pier Solar and the Great Architects + convertSector(temp, dst, 2448, fmt, FAD); + } + else { + WARN_LOG(GDROM, "ERROR: UNABLE TO CONVERT SECTOR. THIS IS FATAL. Format: %d Sector format: %d", fmt, secfmt); + } + dst += fmt; FAD++; } + return count; } void libGDR_ReadSubChannel(u8 * buff, u32 len) @@ -346,19 +360,22 @@ static int discSwapCallback(int tag, int sch_cycl, int jitter, void *arg) return 0; } -bool DiscSwap(const std::string& path) +namespace gdr +{ + +void insertDisk(const std::string& path) { if (!doDiscSwap(path)) throw FlycastException("This media cannot be loaded"); - EventManager::event(Event::DiskChange); + settings.content.path = path; // Drive is busy after the lid was closed sns_asc = 4; sns_ascq = 1; sns_key = 2; SecNumber.Status = GD_BUSY; sh4_sched_request(schedId, SH4_MAIN_CLOCK); // 1 s +} - return true; } void libGDR_init() @@ -368,7 +385,7 @@ void libGDR_init() } void libGDR_term() { - TermDrive(); + gdr::termDrive(); sh4_sched_unregister(schedId); schedId = -1; } diff --git a/core/imgread/common.h b/core/imgread/common.h index 525eec341..fc740a518 100644 --- a/core/imgread/common.h +++ b/core/imgread/common.h @@ -60,10 +60,16 @@ enum DiskArea DoubleDensity }; -bool InitDrive(const std::string& path); -void TermDrive(); -bool DiscSwap(const std::string& path); -void DiscOpenLid(); +namespace gdr { + +bool initDrive(const std::string& path); +void termDrive(); +void insertDisk(const std::string& path); +void openLid(); +bool isOpen(); +bool isLoaded(); + +} struct Session { @@ -112,7 +118,7 @@ struct Disc DiscType type; std::string catalog; - void ReadSectors(u32 FAD, u32 count, u8 *dst, u32 fmt, LoadProgress *progress = nullptr); + u32 ReadSectors(u32 FAD, u32 count, u8 *dst, u32 fmt, bool stopOnMiss = false, LoadProgress *progress = nullptr); virtual ~Disc() { @@ -255,7 +261,7 @@ struct RawTrackFile : TrackFile DiscType GuessDiscType(bool m1, bool m2, bool da); //IO -void libGDR_ReadSector(u8 * buff,u32 StartSector,u32 SectorCount,u32 secsz); +u32 libGDR_ReadSector(u8 * buff, u32 StartSector, u32 SectorCount, u32 secsz, bool stopOnMiss = false); void libGDR_ReadSubChannel(u8 * buff, u32 len); void libGDR_GetToc(u32 *toc, DiskArea area); u32 libGDR_GetDiscType(); diff --git a/core/imgread/ioctl.cpp b/core/imgread/ioctl.cpp deleted file mode 100644 index 6b879e0ef..000000000 --- a/core/imgread/ioctl.cpp +++ /dev/null @@ -1,360 +0,0 @@ -#include "build.h" -#if defined(_WIN32) && !defined(TARGET_UWP) -#include "types.h" -#include "common.h" - -#include -#include - -#include -#include - -#ifdef _MSC_VER -#define _NTSCSI_USER_MODE_ -#include -#undef _NTSCSI_USER_MODE_ -#else -#define CD_RAW_READ_SUBCODE_SIZE ( 96) - -#pragma pack(push, cdb, 1) -typedef union _CDB { - struct _READ_CD { - UCHAR OperationCode; // 0xBE - SCSIOP_READ_CD - UCHAR RelativeAddress : 1; - UCHAR Reserved0 : 1; - UCHAR ExpectedSectorType : 3; - UCHAR Lun : 3; - UCHAR StartingLBA[4]; - UCHAR TransferBlocks[3]; - UCHAR Reserved2 : 1; - UCHAR ErrorFlags : 2; - UCHAR IncludeEDC : 1; - UCHAR IncludeUserData : 1; - UCHAR HeaderCode : 2; - UCHAR IncludeSyncData : 1; - UCHAR SubChannelSelection : 3; - UCHAR Reserved3 : 5; - UCHAR Control; - } READ_CD; -} CDB, *PCDB; -#pragma pack(pop, cdb) - -#define READ_TOC_FORMAT_FULL_TOC 0x02 - -#define SCSIOP_READ 0x28 -#define SCSIOP_READ_CD 0xBE -#endif - -#define RAW_SECTOR_SIZE 2352 -#define CD_SECTOR_SIZE 2048 -#define SECTORS_AT_READ 20 -#define CD_BLOCKS_PER_SECOND 75 - -struct spti_s -{ - SCSI_PASS_THROUGH_DIRECT sptd; - DWORD alignmentDummy; - BYTE senseBuf[0x12]; -}; - -ULONG msf2fad(const UCHAR Addr[4]) -{ - ULONG Sectors = ( Addr[0] * (CD_BLOCKS_PER_SECOND*60) ) + ( Addr[1]*CD_BLOCKS_PER_SECOND) + Addr[2]; - return Sectors; -} - - -// Msf: Hours, Minutes, Seconds, Frames -ULONG AddressToSectors( UCHAR Addr[4] ); - - -bool spti_SendCommand(HANDLE hand,spti_s& s,SCSI_ADDRESS& ioctl_addr) -{ - s.sptd.Length = sizeof(SCSI_PASS_THROUGH_DIRECT); - s.sptd.PathId = ioctl_addr.PathId; - s.sptd.TargetId = ioctl_addr.TargetId; - s.sptd.Lun = ioctl_addr.Lun; - s.sptd.TimeOutValue = 30; - //s.sptd.CdbLength = 0x0A; - s.sptd.SenseInfoLength = 0x12; - s.sptd.SenseInfoOffset = offsetof(spti_s, senseBuf); -// s.sptd.DataIn = SCSI_IOCTL_DATA_IN; -// s.sptd.DataTransferLength = 0x800; -// s.sptd.DataBuffer = pdata; - - DWORD bytesReturnedIO = 0; - if(!DeviceIoControl(hand, IOCTL_SCSI_PASS_THROUGH_DIRECT, &s, sizeof(s), &s, sizeof(s), &bytesReturnedIO, NULL)) - return false; - - if(s.sptd.ScsiStatus) - return false; - return true; -} - -bool spti_Read10(HANDLE hand,void * pdata,u32 sector,SCSI_ADDRESS& ioctl_addr) -{ - spti_s s; - memset(&s,0,sizeof(spti_s)); - - s.sptd.Cdb[0] = SCSIOP_READ; - s.sptd.Cdb[1] = (ioctl_addr.Lun&7) << 5;// | DPO ; DPO = 8 - - s.sptd.Cdb[2] = (BYTE)(sector >> 0x18 & 0xFF); // MSB - s.sptd.Cdb[3] = (BYTE)(sector >> 0x10 & 0xFF); - s.sptd.Cdb[4] = (BYTE)(sector >> 0x08 & 0xFF); - s.sptd.Cdb[5] = (BYTE)(sector >> 0x00 & 0xFF); // LSB - - s.sptd.Cdb[7] = 0; - s.sptd.Cdb[8] = 1; - - s.sptd.CdbLength = 0x0A; - s.sptd.DataIn = SCSI_IOCTL_DATA_IN; - s.sptd.DataTransferLength = 0x800; - s.sptd.DataBuffer = pdata; - - return spti_SendCommand(hand,s,ioctl_addr); -} -bool spti_ReadCD(HANDLE hand,void * pdata,u32 sector,SCSI_ADDRESS& ioctl_addr) -{ - spti_s s; - memset(&s,0,sizeof(spti_s)); - CDB& r = *(PCDB)s.sptd.Cdb; - - r.READ_CD.OperationCode = SCSIOP_READ_CD; - - r.READ_CD.StartingLBA[0] = (BYTE)(sector >> 0x18 & 0xFF); - r.READ_CD.StartingLBA[1] = (BYTE)(sector >> 0x10 & 0xFF); - r.READ_CD.StartingLBA[2] = (BYTE)(sector >> 0x08 & 0xFF); - r.READ_CD.StartingLBA[3] = (BYTE)(sector >> 0x00 & 0xFF); - - // 1 sector - r.READ_CD.TransferBlocks[0] = 0; - r.READ_CD.TransferBlocks[1] = 0; - r.READ_CD.TransferBlocks[2] = 1; - - // 0xF8 - r.READ_CD.IncludeSyncData = 1; - r.READ_CD.HeaderCode = 3; - r.READ_CD.IncludeUserData = 1; - r.READ_CD.IncludeEDC = 1; - - r.READ_CD.SubChannelSelection = 1; - - s.sptd.CdbLength = 12; - s.sptd.DataIn = SCSI_IOCTL_DATA_IN; - s.sptd.DataTransferLength = 2448; - s.sptd.DataBuffer = pdata; - return spti_SendCommand(hand,s,ioctl_addr); -} - -struct PhysicalDrive; -struct PhysicalTrack:TrackFile -{ - PhysicalDrive* disc; - PhysicalTrack(PhysicalDrive* disc) { this->disc=disc; } - - bool Read(u32 FAD,u8* dst,SectorFormat* sector_type,u8* subcode,SubcodeFormat* subcode_type) override; -}; - -struct PhysicalDrive:Disc -{ - HANDLE drive; - SCSI_ADDRESS scsi_addr; - bool use_scsi; - - PhysicalDrive() - { - drive=INVALID_HANDLE_VALUE; - memset(&scsi_addr,0,sizeof(scsi_addr)); - use_scsi=false; - } - - bool Build(char* path) - { - drive = CreateFile( path, GENERIC_READ|GENERIC_WRITE,FILE_SHARE_READ|FILE_SHARE_WRITE,NULL, OPEN_EXISTING, 0, NULL); - - if ( INVALID_HANDLE_VALUE == drive ) - return false; //failed to open - - printf(" Opened device %s, reading TOC ...",path); - // Get track-table and parse it - CDROM_READ_TOC_EX tocrq={0}; - - tocrq.Format = READ_TOC_FORMAT_FULL_TOC; - tocrq.Msf=1; - tocrq.SessionTrack=1; - u8 buff[2048]; - CDROM_TOC_FULL_TOC_DATA *ftd=(CDROM_TOC_FULL_TOC_DATA*)buff; - - ULONG BytesRead; - memset(buff,0,sizeof(buff)); - int code = DeviceIoControl(drive,IOCTL_CDROM_READ_TOC_EX,&tocrq,sizeof(tocrq),ftd, 2048, &BytesRead, NULL); - -// CDROM_TOC toc; - int currs=-1; - if (0==code) - { - printf(" failed\n"); - //failed to read toc - CloseHandle(drive); - return false; - } - else - { - printf(" done !\n"); - - type=CdRom_XA; - - BytesRead-=sizeof(CDROM_TOC_FULL_TOC_DATA); - BytesRead/=sizeof(ftd->Descriptors[0]); - - for (u32 i=0;iDescriptors[i].Point==0xA2) - { - this->EndFAD=msf2fad(ftd->Descriptors[i].Msf); - continue; - } - if (ftd->Descriptors[i].Point>=1 && ftd->Descriptors[i].Point<=0x63 && - ftd->Descriptors[i].Adr==1) - { - u32 trackn=ftd->Descriptors[i].Point-1; - verify(trackn==tracks.size()); - Track t; - - t.ADR=ftd->Descriptors[i].Adr; - t.CTRL=ftd->Descriptors[i].Control; - t.StartFAD=msf2fad(ftd->Descriptors[i].Msf); - t.file = new PhysicalTrack(this); - - tracks.push_back(t); - - if (currs!=ftd->Descriptors[i].SessionNumber) - { - currs=ftd->Descriptors[i].SessionNumber; - verify(sessions.size()==(currs-1)); - Session s; - s.FirstTrack=trackn+1; - s.StartFAD=t.StartFAD; - - sessions.push_back(s); - } - } - } - LeadOut.StartFAD = EndFAD; - LeadOut.ADR = 1; - LeadOut.CTRL = 4; - } - - DWORD bytesReturnedIO = 0; - BOOL resultIO = DeviceIoControl(drive, IOCTL_SCSI_GET_ADDRESS, NULL, 0, &scsi_addr, sizeof(scsi_addr), &bytesReturnedIO, NULL); - //done ! - if (resultIO) - use_scsi=true; - else - use_scsi=false; - - return true; - } -}; - -bool PhysicalTrack::Read(u32 FAD,u8* dst,SectorFormat* sector_type,u8* subcode,SubcodeFormat* subcode_type) -{ - static u8 temp[2500]; - - u32 LBA=FAD-150; - - if (disc->use_scsi) - { - if (!spti_ReadCD(disc->drive, temp,LBA,disc->scsi_addr)) - { - if (spti_Read10(disc->drive, dst,LBA,disc->scsi_addr)) - { - //sector read success, just user data - *sector_type=SECFMT_2048_MODE2_FORM1; //m2f1 seems more common ? is there some way to detect it properly here? - return true; - } - } - else - { - //sector read success, with subcode - memcpy(dst, temp, 2352); - memcpy(subcode, temp + 2352, CD_RAW_READ_SUBCODE_SIZE); - - *sector_type=SECFMT_2352; - *subcode_type=SUBFMT_96; - return true; - } - } - - //hmm, spti failed/cannot be used - - - //try IOCTL_CDROM_RAW_READ - - - static __RAW_READ_INFO Info; - - Info.SectorCount=1; - Info.DiskOffset.QuadPart = LBA * CD_SECTOR_SIZE; //CD_SECTOR_SIZE, even though we read RAW sectors. Its how winapi works. - ULONG Dummy; - - //try all 3 track modes, starting from the one that succeeded last time (Info is static) to save time ! - for (int tr=0;tr<3;tr++) - { - if ( 0 == DeviceIoControl( disc->drive, IOCTL_CDROM_RAW_READ, &Info, sizeof(Info), dst, RAW_SECTOR_SIZE, &Dummy, NULL ) ) - { - Info.TrackMode=(TRACK_MODE_TYPE)((Info.TrackMode+1)%3); //try next mode - } - else - { - //sector read success - *sector_type=SECFMT_2352; - return true; - } - } - - //finally, try ReadFile - if (SetFilePointer(disc->drive,LBA*2048,0,FILE_BEGIN)!=INVALID_SET_FILE_POINTER) - { - DWORD BytesRead; - if (FALSE!=ReadFile(disc->drive,dst,2048,&BytesRead,0) && BytesRead==2048) - { - //sector read success, just user data - *sector_type=SECFMT_2048_MODE2_FORM1; //m2f1 seems more common ? is there some way to detect it properly here? - return true; - } - } - - printf("IOCTL: Totally failed to read sector @LBA %d\n", LBA); - return false; -} - - -Disc* ioctl_parse(const char* file, std::vector *digest) -{ - - if (strlen(file)==3 && GetDriveType(file)==DRIVE_CDROM) - { - printf("Opening device %s ...",file); - char fn[]={ '\\', '\\', '.', '\\', file[0],':', '\0' }; - PhysicalDrive* rv = new PhysicalDrive(); - - if (rv->Build(fn)) - { - if (digest != nullptr) - digest->clear(); - return rv; - } - else - { - delete rv; - return 0; - } - } - else - { - return 0; - } -} -#endif diff --git a/core/input/gamepad.h b/core/input/gamepad.h index 88bce6fe7..e4fdc049b 100644 --- a/core/input/gamepad.h +++ b/core/input/gamepad.h @@ -52,6 +52,7 @@ enum DreamcastKey EMU_BTN_SAVESTATE, EMU_BTN_BYPASS_KB, EMU_BTN_SCREENSHOT, + EMU_BTN_SRVMODE, // used internally by virtual gamepad // Real axes DC_AXIS_TRIGGERS = 0x1000000, diff --git a/core/input/gamepad_device.cpp b/core/input/gamepad_device.cpp index 7ca658414..5dcb38b56 100644 --- a/core/input/gamepad_device.cpp +++ b/core/input/gamepad_device.cpp @@ -447,24 +447,18 @@ bool GamepadDevice::find_mapping(int system /* = settings.platform.system */) return false; } -int GamepadDevice::GetGamepadCount() -{ - _gamepads_mutex.lock(); - int count = _gamepads.size(); - _gamepads_mutex.unlock(); - return count; +int GamepadDevice::GetGamepadCount() { + Lock _(_gamepads_mutex); + return _gamepads.size(); } std::shared_ptr GamepadDevice::GetGamepad(int index) { - _gamepads_mutex.lock(); - std::shared_ptr dev; + Lock _(_gamepads_mutex); if (index >= 0 && index < (int)_gamepads.size()) - dev = _gamepads[index]; + return _gamepads[index]; else - dev = NULL; - _gamepads_mutex.unlock(); - return dev; + return nullptr; } void GamepadDevice::save_mapping(int system /* = settings.platform.system */) @@ -557,21 +551,19 @@ void GamepadDevice::Register(const std::shared_ptr& gamepad) setbuf(record_input, NULL); } #endif - _gamepads_mutex.lock(); + Lock _(_gamepads_mutex); _gamepads.push_back(gamepad); - _gamepads_mutex.unlock(); MapleConfigMap::UpdateVibration = updateVibration; } void GamepadDevice::Unregister(const std::shared_ptr& gamepad) { - _gamepads_mutex.lock(); + Lock _(_gamepads_mutex); for (auto it = _gamepads.begin(); it != _gamepads.end(); it++) if (*it == gamepad) { _gamepads.erase(it); break; } - _gamepads_mutex.unlock(); } void GamepadDevice::SaveMaplePorts() @@ -584,6 +576,83 @@ void GamepadDevice::SaveMaplePorts() } } +s16 (&GamepadDevice::getTargetArray(DigAnalog axis))[4] +{ + switch (axis) + { + case DIGANA_LEFT: + case DIGANA_RIGHT: + return joyx; + case DIGANA_UP:; + case DIGANA_DOWN: + return joyy; + case DIGANA2_LEFT: + case DIGANA2_RIGHT: + return joyrx; + case DIGANA2_UP: + case DIGANA2_DOWN: + return joyry; + case DIGANA3_LEFT: + case DIGANA3_RIGHT: + return joy3x; + case DIGANA3_UP: + case DIGANA3_DOWN: + return joy3y; + default: + die("unknown axis"); + } +} + +void GamepadDevice::rampAnalog() +{ + Lock _(rampMutex); + if (lastAnalogUpdate == 0) + // also used as a flag that no analog ramping is needed on this device (yet) + return; + + const u64 now = getTimeMs(); + const int delta = std::round(static_cast(now - lastAnalogUpdate) * AnalogRamp); + lastAnalogUpdate = now; + for (unsigned port = 0; port < std::size(digitalToAnalogState); port++) + { + for (int axis = 0; axis < 12; axis += 2) // 3 sticks with 2 axes each + { + DigAnalog negDir = static_cast(1 << axis); + if ((rampAnalogState[port] & negDir) == 0) + // axis not active + continue; + DigAnalog posDir = static_cast(1 << (axis + 1)); + const int socd = digitalToAnalogState[port] & (negDir | posDir); + s16& axisValue = getTargetArray(negDir)[port]; + if (socd != 0 && socd != (negDir | posDir)) + { + // One axis is pressed => ramp up + if (socd == posDir) + axisValue = std::min(32767, axisValue + delta); + else + axisValue = std::max(-32768, axisValue - delta); + } + else + { + // No axis is pressed (or both) => ramp down + if (axisValue > 0) + axisValue = std::max(0, axisValue - delta); + else if (axisValue < 0) + axisValue = std::min(0, axisValue + delta); + else + rampAnalogState[port] &= ~negDir; + } + } + } +} + +void GamepadDevice::RampAnalog() +{ + Lock _(_gamepads_mutex); + for (auto& gamepad : _gamepads) + gamepad->rampAnalog(); +} + #ifdef TEST_AUTOMATION #include "cfg/option.h" static bool replay_inited; diff --git a/core/input/gamepad_device.h b/core/input/gamepad_device.h index aef633870..4cf560f68 100644 --- a/core/input/gamepad_device.h +++ b/core/input/gamepad_device.h @@ -20,6 +20,7 @@ #pragma once #include "types.h" #include "mapping.h" +#include "stdclass.h" #include #include @@ -92,12 +93,21 @@ class GamepadDevice } static void Register(const std::shared_ptr& gamepad); - static void Unregister(const std::shared_ptr& gamepad); - static int GetGamepadCount(); static std::shared_ptr GetGamepad(int index); static void SaveMaplePorts(); + static void RampAnalog(); + + template + static std::shared_ptr GetGamepad() + { + Lock _(_gamepads_mutex); + for (const auto& gamepad : _gamepads) + if (dynamic_cast(gamepad.get()) != nullptr) + return std::dynamic_pointer_cast(gamepad); + return {}; + } static void load_system_mappings(); bool find_mapping(int system = settings.platform.system); @@ -158,21 +168,20 @@ class GamepadDevice { if (port < 0) return; + Lock _(rampMutex); DigAnalog axis = key == DcNegDir ? NegDir : PosDir; if (pressed) digitalToAnalogState[port] |= axis; else digitalToAnalogState[port] &= ~axis; - const u32 socd = digitalToAnalogState[port] & (NegDir | PosDir); - if (socd == 0 || socd == (NegDir | PosDir)) - joystick = 0; - else if (socd == NegDir) - joystick = -32768; - else - joystick = 32767; - + rampAnalogState[port] |= NegDir; + if (lastAnalogUpdate == 0) + lastAnalogUpdate = getTimeMs(); } + s16 (&getTargetArray(DigAnalog axis))[4]; + void rampAnalog(); + std::string _api_name; int _maple_port; bool _detecting_button = false; @@ -184,9 +193,16 @@ class GamepadDevice std::map lastAxisValue[4]; bool perGameMapping = false; bool instanceMapping = false; - + + u64 lastAnalogUpdate = 0; + u32 rampAnalogState[4] {}; + static constexpr float AnalogRamp = 32767.f / 100.f; // 100 ms ramp time + std::mutex rampMutex; + static std::vector> _gamepads; static std::mutex _gamepads_mutex; + + using Lock = std::lock_guard; }; #ifdef TEST_AUTOMATION diff --git a/core/input/mouse.h b/core/input/mouse.h index d17f8dcf1..f5f238815 100644 --- a/core/input/mouse.h +++ b/core/input/mouse.h @@ -104,3 +104,15 @@ class SystemMouse : public Mouse void setButton(Button button, bool pressed); void setWheel(int delta); }; + +class TouchMouse : public SystemMouse +{ +public: + TouchMouse() : SystemMouse("Flycast", -1) + { + _name = "Touch Mouse"; + _unique_id = "touch_mouse"; + loadMapping(); + } +}; + diff --git a/core/input/virtual_gamepad.h b/core/input/virtual_gamepad.h new file mode 100644 index 000000000..69adc2828 --- /dev/null +++ b/core/input/virtual_gamepad.h @@ -0,0 +1,117 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#pragma once +#include "gamepad_device.h" +#include "ui/vgamepad.h" + +class VirtualGamepad : public GamepadDevice +{ +public: + VirtualGamepad(const char *api_name, int maple_port = 0) + : GamepadDevice(maple_port, api_name, false) + { + _name = "Virtual Gamepad"; + _unique_id = "virtual_gamepad_uid"; + input_mapper = std::make_shared(); + // hasAnalogStick = true; // TODO has an analog stick but input mapping isn't persisted + + leftTrigger = DC_AXIS_LT; + rightTrigger = DC_AXIS_RT; + } + + bool is_virtual_gamepad() override { + return true; + }; + + // normalized coordinates [-1, 1] + void joystickInput(float x, float y) + { + vgamepad::setAnalogStick(x, y); + int joyx = std::round(x * 32767.f); + int joyy = std::round(y * 32767.f); + if (joyx >= 0) + gamepad_axis_input(DC_AXIS_RIGHT, joyx); + else + gamepad_axis_input(DC_AXIS_LEFT, -joyx); + if (joyy >= 0) + gamepad_axis_input(DC_AXIS_DOWN, joyy); + else + gamepad_axis_input(DC_AXIS_UP, -joyy); + } + + void releaseAll() + { + for (int i = 0; i < 32; i++) + if (buttonState & (1 << i)) + gamepad_btn_input(1 << i, false); + buttonState = 0; + joystickInput(0, 0); + gamepad_axis_input(DC_AXIS_LT, 0); + gamepad_axis_input(DC_AXIS_RT, 0); + if (previousFastForward) + gamepad_btn_input(EMU_BTN_FFORWARD, false); + previousFastForward = false; + } + + virtual bool handleButtonInput(u32& state, u32 key, bool pressed) { + // can be overridden in derived classes to handle specific key combos + // (iOS up+down or left+right) + return false; + } + + void buttonInput(vgamepad::ControlId controlId, bool pressed) + { + u32 kcode = vgamepad::controlToDcKey(controlId); + if (kcode == 0) + return; + if (handleButtonInput(buttonState, kcode, pressed)) + return; + if (kcode == DC_AXIS_LT) { + gamepad_axis_input(DC_AXIS_LT, pressed ? 0x7fff : 0); + } + else if (kcode == DC_AXIS_RT) { + gamepad_axis_input(DC_AXIS_RT, pressed ? 0x7fff : 0); + } + else if (kcode == EMU_BTN_SRVMODE) { + if (pressed) + vgamepad::toggleServiceMode(); + } + else + { + if (pressed) + buttonState |= kcode; + else + buttonState &= ~kcode; + if ((kcode & (DC_DPAD_LEFT | DC_DPAD_RIGHT)) != 0 + && (kcode & (DC_DPAD_UP | DC_DPAD_DOWN)) != 0) + { + // diagonals + gamepad_btn_input(kcode & (DC_DPAD_LEFT | DC_DPAD_RIGHT), pressed); + gamepad_btn_input(kcode & (DC_DPAD_UP | DC_DPAD_DOWN), pressed); + } + else { + gamepad_btn_input(kcode, pressed); + } + } + } + +private: + u32 buttonState = 0; + bool previousFastForward = false; +}; diff --git a/core/linux/common.cpp b/core/linux/common.cpp index 0ca150af0..c1f9d8b77 100644 --- a/core/linux/common.cpp +++ b/core/linux/common.cpp @@ -30,8 +30,6 @@ extern "C" char __start__; #define siginfo_t switch_siginfo_t #endif // __SWITCH__ -#if !defined(TARGET_NO_EXCEPTIONS) - void context_from_segfault(host_context_t* hctx, void* segfault_ctx); void context_to_segfault(host_context_t* hctx, void* segfault_ctx); @@ -53,11 +51,11 @@ void fault_handler(int sn, siginfo_t * si, void *segfault_ctx) // texture protection in VRAM if (VramLockedWrite((u8*)si->si_addr)) return; +#if FEAT_SHREC == DYNAREC_JIT // FPCB jump table protection if (addrspace::bm_lockedWrite((u8*)si->si_addr)) return; -#if FEAT_SHREC == DYNAREC_JIT // fast mem access rewriting host_context_t ctx; context_from_segfault(&ctx, segfault_ctx); @@ -110,16 +108,6 @@ void os_UninstallFaultHandler() #endif } -#else // !defined(TARGET_NO_EXCEPTIONS) - -void os_InstallFaultHandler() -{ -} -void os_UninstallFaultHandler() -{ -} -#endif // !defined(TARGET_NO_EXCEPTIONS) - #if !defined(__unix__) && !defined(LIBRETRO) && !defined(__SWITCH__) [[noreturn]] void os_DebugBreak() { diff --git a/core/linux/context.cpp b/core/linux/context.cpp index 6cd6c6eb9..c61d504e7 100644 --- a/core/linux/context.cpp +++ b/core/linux/context.cpp @@ -8,7 +8,7 @@ #define __USE_GNU 1 #endif - #if !defined(TARGET_NO_EXCEPTIONS) && !defined(__OpenBSD__) + #if !defined(__OpenBSD__) #include #endif @@ -39,7 +39,6 @@ static void bicopy(Tctx& ctx, Tseg& seg) template static void context_segfault(host_context_t* hostctx, void* segfault_ctx) { -#if !defined(TARGET_NO_EXCEPTIONS) #if HOST_CPU == CPU_ARM #if defined(__FreeBSD__) bicopy(hostctx->pc, MCTX(.__gregs[_REG_PC])); @@ -133,8 +132,6 @@ static void context_segfault(host_context_t* hostctx, void* segfault_ctx) #else #error Unsupported HOST_CPU #endif - #endif - } void context_from_segfault(host_context_t* hostctx, void* segfault_ctx) { diff --git a/core/lua/lua.cpp b/core/lua/lua.cpp index 194eb1542..e5512a043 100644 --- a/core/lua/lua.cpp +++ b/core/lua/lua.cpp @@ -229,6 +229,12 @@ static void setMapleType(int bus, int type, lua_State *L) case MDT_Mouse: case MDT_LightGun: case MDT_TwinStick: + case MDT_MaracasController: + case MDT_FishingController: + case MDT_PopnMusicController: + case MDT_RacingController: + case MDT_DenshaDeGoController: + case MDT_SegaControllerXL: case MDT_None: config::MapleMainDevices[bus - 1] = (MapleDeviceType)type; maple_ReconnectDevices(); diff --git a/core/network/ggpo.cpp b/core/network/ggpo.cpp index 0672af39b..6d4bb5fce 100644 --- a/core/network/ggpo.cpp +++ b/core/network/ggpo.cpp @@ -330,7 +330,7 @@ static bool load_game_state(unsigned char *buffer, int len) */ static bool save_game_state(unsigned char **buffer, int *len, int *checksum, int frame) { - verify(!sh4_cpu.IsCpuRunning()); + verify(!emu.getSh4Executor()->IsCpuRunning()); lastSavedFrame = frame; // TODO this is way too much memory size_t allocSize = settings.platform.isNaomi() ? 20_MB : 10_MB; @@ -914,7 +914,7 @@ void endOfFrame() if (active()) { _endOfFrame = true; - sh4_cpu.Stop(); + emu.getSh4Executor()->Stop(); } } diff --git a/core/network/net_platform.h b/core/network/net_platform.h index ccf6e2222..f4eab1ed1 100644 --- a/core/network/net_platform.h +++ b/core/network/net_platform.h @@ -29,7 +29,7 @@ #define INADDR_NONE 0xffffffff #endif #ifndef INET_ADDRSTRLEN -#define INET_ADDRSTRLEN sizeof(struct sockaddr_in) +#define INET_ADDRSTRLEN 16 #endif #define SOL_TCP 6 // Shrug #else diff --git a/core/network/null_modem.h b/core/network/null_modem.h index 440ee589a..199f162b7 100644 --- a/core/network/null_modem.h +++ b/core/network/null_modem.h @@ -166,10 +166,22 @@ class NullModemPipe : public SerialPort::Pipe } if (rc == 2) { - if (data[0] != 'D') + if (data[0] != 'D') { ERROR_LOG(NETWORK, "Unexpected packet '%c'", data[0]); + } else + { rxBuffer.push_back(data[1]); + if (!dataReceived) + { + dataReceived = true; + char name[INET_ADDRSTRLEN]; + inet_ntop(AF_INET, &addr.sin_addr, name, sizeof(name)); + std::string s(name); + s += ":" + std::to_string(htons(addr.sin_port)); + os_notify("Network connected", 5000, s.c_str()); + } + } } else if (rc == 1) { @@ -249,6 +261,7 @@ class NullModemPipe : public SerialPort::Pipe std::deque rxBuffer; sockaddr_in peerAddress{}; u64 lastPoll = 0; + bool dataReceived = false; }; class BattleCableHandshake : public NetworkHandshake diff --git a/core/nullDC.cpp b/core/nullDC.cpp index 0c584a514..8e60388a3 100644 --- a/core/nullDC.cpp +++ b/core/nullDC.cpp @@ -270,7 +270,7 @@ void dc_loadstate(int index) try { Deserializer deser(data, total_size); - dc_loadstate(deser); + emu.loadstate(deser); NOTICE_LOG(SAVESTATE, "Loaded state ver %d from %s size %d", deser.version(), filename.c_str(), total_size); if (deser.size() != total_size) // Note: this isn't true for RA savestates @@ -281,7 +281,6 @@ void dc_loadstate(int index) } free(data); - EventManager::event(Event::LoadState); } time_t dc_getStateCreationDate(int index) diff --git a/core/oslib/oslib.cpp b/core/oslib/oslib.cpp index a3f64603f..4595ed42d 100644 --- a/core/oslib/oslib.cpp +++ b/core/oslib/oslib.cpp @@ -40,22 +40,54 @@ #include #endif #include "profiler/fc_profiler.h" +#include "input/gamepad_device.h" namespace hostfs { -std::string getVmuPath(const std::string& port) +std::string getVmuPath(const std::string& port, bool save) { - if (port == "A1" && config::PerGameVmu && !settings.content.path.empty()) - return get_game_save_prefix() + "_vmu_save_A1.bin"; + if (port == "A1" && config::PerGameVmu) + { + if (settings.platform.isConsole() && !settings.content.gameId.empty()) + { + constexpr std::string_view INVALID_CHARS { " /\\:*?|<>" }; + std::string vmuName = settings.content.gameId; + for (char &c: vmuName) + if (INVALID_CHARS.find(c) != INVALID_CHARS.npos) + c = '_'; + vmuName += "_vmu_save_A1.bin"; + std::string wpath = get_writable_data_path(vmuName); + if (save || file_exists(wpath)) + return wpath; + std::string rpath = get_readonly_data_path(vmuName); + if (hostfs::storage().exists(rpath)) + return rpath; + if (!settings.content.path.empty()) + { + // Legacy path using the rom file name + rpath = get_game_save_prefix() + "_vmu_save_A1.bin"; + if (file_exists(rpath)) + return rpath; + } + return wpath; + } + if (!settings.content.path.empty()) + return get_game_save_prefix() + "_vmu_save_A1.bin"; + } - char tempy[512]; - sprintf(tempy, "vmu_save_%s.bin", port.c_str()); + std::string vmuName = "vmu_save_" + port + ".bin"; + std::string wpath = get_writable_data_path(vmuName); + if (save || file_exists(wpath)) + return wpath; + std::string rpath = get_readonly_data_path(vmuName); + if (hostfs::storage().exists(rpath)) + return rpath; // VMU saves used to be stored in .reicast, not in .reicast/data - std::string apath = get_writable_config_path(tempy); - if (!file_exists(apath)) - apath = get_writable_data_path(tempy); - return apath; + rpath = get_readonly_config_path(vmuName); + if (file_exists(rpath)) + return rpath; + return wpath; } std::string getArcadeFlashPath() @@ -283,6 +315,13 @@ void saveScreenshot(const std::string& name, const std::vector& data) #endif +#ifndef USE_LIBCDIO +const std::vector& getCdromDrives() { + static std::vector empty; + return empty; +} +#endif + } // namespace hostfs void os_CreateWindow() @@ -340,12 +379,11 @@ void os_UpdateInputState() { FC_PROFILE_SCOPE; + GamepadDevice::RampAnalog(); #if defined(USE_SDL) input_sdl_handle(); -#else - #if defined(USE_EVDEV) - input_evdev_handle(); - #endif +#elif defined(USE_EVDEV) + input_evdev_handle(); #endif } diff --git a/core/oslib/oslib.h b/core/oslib/oslib.h index 916b33e98..e55fffea3 100644 --- a/core/oslib/oslib.h +++ b/core/oslib/oslib.h @@ -47,7 +47,7 @@ u32 static inline bitscanrev(u32 v) namespace hostfs { - std::string getVmuPath(const std::string& port); + std::string getVmuPath(const std::string& port, bool save); std::string getArcadeFlashPath(); @@ -63,6 +63,7 @@ namespace hostfs std::string getShaderCachePath(const std::string& filename); void saveScreenshot(const std::string& name, const std::vector& data); + const std::vector& getCdromDrives(); #ifdef __ANDROID__ void importHomeDirectory(); void exportHomeDirectory(); diff --git a/core/oslib/storage.cpp b/core/oslib/storage.cpp index 7fc2db3a1..dbb1546d9 100644 --- a/core/oslib/storage.cpp +++ b/core/oslib/storage.cpp @@ -42,7 +42,7 @@ CustomStorage& customStorage() FileInfo getFileInfo(const std::string& path) override { die("Not implemented"); } bool exists(const std::string& path) override { die("Not implemented"); } bool addStorage(bool isDirectory, bool writeAccess, const std::string& description, - void (*callback)(bool cancelled, std::string selectedPath)) override { + void (*callback)(bool cancelled, std::string selectedPath), const std::string& mimeType) override { die("Not implemented"); } }; @@ -194,24 +194,43 @@ class StdStorage : public Storage info.size = st.st_size; info.updateTime = st.st_mtime; #else // _WIN32 + std::string lpath(path); + if (path.length() == 2 && isalpha(path[0]) && path[1] == ':') + /* D: -> \\.\D:\ */ + lpath = "\\\\.\\" + path + "\\"; + else if (path.substr(0, 4) == "\\\\.\\" && path.length() == 6) + /* \\.\D: -> \\.\D:\ */ + lpath += "\\"; nowide::wstackstring wname; - if (wname.convert(path.c_str())) + if (wname.convert(lpath.c_str())) { - WIN32_FILE_ATTRIBUTE_DATA fileAttribs; - if (GetFileAttributesExW(wname.get(), GetFileExInfoStandard, &fileAttribs)) + if (lpath.substr(0, 4) == "\\\\.\\") { - info.isDirectory = (fileAttribs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; - info.size = fileAttribs.nFileSizeLow + ((u64)fileAttribs.nFileSizeHigh << 32); - u64 t = ((u64)fileAttribs.ftLastWriteTime.dwHighDateTime << 32) | fileAttribs.ftLastWriteTime.dwLowDateTime; - info.updateTime = t / 10000000 - 11644473600LL; // 100-nano to secs minus (unix epoch - windows epoch) + // Win32 device namespace + UINT type = GetDriveTypeW(wname.get()); + if (type != DRIVE_CDROM) + throw StorageException("Invalid device " + lpath.substr(4, 2)); + info.isDirectory = false; + info.isWritable = false; } else { - const int error = GetLastError(); - if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND) - INFO_LOG(COMMON, "Cannot get attributes of '%s' error 0x%x", path.c_str(), error); - _set_errno(error); - throw StorageException("Cannot get attributes of " + path); + WIN32_FILE_ATTRIBUTE_DATA fileAttribs; + if (GetFileAttributesExW(wname.get(), GetFileExInfoStandard, &fileAttribs)) + { + info.isDirectory = (fileAttribs.dwFileAttributes & FILE_ATTRIBUTE_DIRECTORY) != 0; + info.size = fileAttribs.nFileSizeLow + ((u64)fileAttribs.nFileSizeHigh << 32); + u64 t = ((u64)fileAttribs.ftLastWriteTime.dwHighDateTime << 32) | fileAttribs.ftLastWriteTime.dwLowDateTime; + info.updateTime = t / 10000000 - 11644473600LL; // 100-nano to secs minus (unix epoch - windows epoch) + } + else + { + const int error = GetLastError(); + if (error != ERROR_FILE_NOT_FOUND && error != ERROR_PATH_NOT_FOUND) + INFO_LOG(COMMON, "Cannot get attributes of '%s' error 0x%x", lpath.c_str(), error); + _set_errno(error); + throw StorageException("Cannot get attributes of " + lpath); + } } } else @@ -394,9 +413,9 @@ AllStorage& storage() } bool addStorage(bool isDirectory, bool writeAccess, const std::string& description, - void (*callback)(bool cancelled, std::string selectedPath)) + void (*callback)(bool cancelled, std::string selectedPath), const std::string& mimeType) { - return customStorage().addStorage(isDirectory, writeAccess, description, callback); + return customStorage().addStorage(isDirectory, writeAccess, description, callback, mimeType); } } diff --git a/core/oslib/storage.h b/core/oslib/storage.h index a53e2457f..47281dccc 100644 --- a/core/oslib/storage.h +++ b/core/oslib/storage.h @@ -65,7 +65,7 @@ class CustomStorage : public Storage { public: virtual bool addStorage(bool isDirectory, bool writeAccess, const std::string& description, - void (*callback)(bool cancelled, std::string selectedPath)) = 0; + void (*callback)(bool cancelled, std::string selectedPath), const std::string& mimeType) = 0; }; class AllStorage : public Storage @@ -84,7 +84,7 @@ class AllStorage : public Storage AllStorage& storage(); bool addStorage(bool isDirectory, bool writeAccess, const std::string& description, - void (*callback)(bool cancelled, std::string selectedPath)); + void (*callback)(bool cancelled, std::string selectedPath), const std::string& mimeType = {}); // iterate depth-first over the files contained in a folder hierarchy class DirectoryTree diff --git a/core/rec-ARM/rec_arm.cpp b/core/rec-ARM/rec_arm.cpp index ac5c48aa5..4552a3c5a 100644 --- a/core/rec-ARM/rec_arm.cpp +++ b/core/rec-ARM/rec_arm.cpp @@ -89,15 +89,17 @@ extern "C" char *stpcpy(char *dst, char const *src) } #endif -#undef do_sqw_nommu #define rcbOffset(x) (-sizeof(Sh4RCB) + offsetof(Sh4RCB, x)) +#define ctxOffset(x) (-sizeof(Sh4Context) + offsetof(Sh4Context, x)) struct DynaRBI : RuntimeBlockInfo { - DynaRBI(Sh4CodeBuffer& codeBuffer) : codeBuffer(codeBuffer) {} + DynaRBI(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) + : sh4ctx(sh4ctx), codeBuffer(codeBuffer) {} u32 Relink() override; Register T_reg; + Sh4Context& sh4ctx; Sh4CodeBuffer& codeBuffer; }; @@ -157,8 +159,10 @@ class Arm32Assembler : public MacroAssembler using BinaryOP = void (MacroAssembler::*)(Register, Register, const Operand&); public: - Arm32Assembler(Sh4CodeBuffer& codeBuffer) : MacroAssembler((u8 *)codeBuffer.get(), codeBuffer.getFreeSpace(), A32), codeBuffer(codeBuffer), reg(*this) {} - Arm32Assembler(Sh4CodeBuffer& codeBuffer, u8 *buffer, size_t size) : MacroAssembler(buffer, size, A32), codeBuffer(codeBuffer), reg(*this) {} + Arm32Assembler(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) + : MacroAssembler((u8 *)codeBuffer.get(), codeBuffer.getFreeSpace(), A32), sh4ctx(sh4ctx), codeBuffer(codeBuffer), reg(*this) {} + Arm32Assembler(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer, u8 *buffer, size_t size) + : MacroAssembler(buffer, size, A32), sh4ctx(sh4ctx), codeBuffer(codeBuffer), reg(*this) {} void compile(RuntimeBlockInfo* block, bool force_checks, bool optimise); void rewrite(Register raddr, Register rt, SRegister ft, DRegister fd, bool write, bool is_sq, mem_op_type optp); @@ -170,14 +174,14 @@ class Arm32Assembler : public MacroAssembler void loadSh4Reg(Register Rt, u32 Sh4_Reg) { - const int shRegOffs = (u8*)GetRegPtr(Sh4_Reg) - (u8*)&p_sh4rcb->cntx - sizeof(Sh4cntx); + const int shRegOffs = getRegOffset((Sh4RegType)Sh4_Reg) - sizeof(Sh4Context); Ldr(Rt, MemOperand(r8, shRegOffs)); } void storeSh4Reg(Register Rt, u32 Sh4_Reg) { - const int shRegOffs = (u8*)GetRegPtr(Sh4_Reg) - (u8*)&p_sh4rcb->cntx - sizeof(Sh4cntx); + const int shRegOffs = getRegOffset((Sh4RegType)Sh4_Reg) - sizeof(Sh4Context); Str(Rt, MemOperand(r8, shRegOffs)); } @@ -367,6 +371,7 @@ class Arm32Assembler : public MacroAssembler void genMmuLookup(RuntimeBlockInfo* block, const shil_opcode& op, u32 write, Register& raddr); void compileOp(RuntimeBlockInfo* block, shil_opcode* op, bool optimise); + Sh4Context& sh4ctx; Sh4CodeBuffer& codeBuffer; arm_reg_alloc reg; struct CC_PS @@ -393,13 +398,13 @@ void arm_reg_alloc::Writeback(u32 reg, int nreg) void arm_reg_alloc::Preload_FPU(u32 reg, int nreg) { - const s32 shRegOffs = (u8*)GetRegPtr(reg) - (u8*)&p_sh4rcb->cntx - sizeof(Sh4cntx); + const s32 shRegOffs = getRegOffset((Sh4RegType)reg) - sizeof(Sh4Context); ass.Vldr(SRegister(nreg), MemOperand(r8, shRegOffs)); } void arm_reg_alloc::Writeback_FPU(u32 reg, int nreg) { - const s32 shRegOffs = (u8*)GetRegPtr(reg) - (u8*)&p_sh4rcb->cntx - sizeof(Sh4cntx); + const s32 shRegOffs = getRegOffset((Sh4RegType)reg) - sizeof(Sh4Context); ass.Vstr(SRegister(nreg), MemOperand(r8, shRegOffs)); } @@ -416,7 +421,7 @@ class Arm32Dynarec : public Sh4Dynarec sh4Dynarec = this; } - void init(Sh4CodeBuffer& codeBuffer) override; + void init(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) override; void reset() override; RuntimeBlockInfo *allocateBlock() override; void handleException(host_context_t &context) override; @@ -435,7 +440,7 @@ class Arm32Dynarec : public Sh4Dynarec } void compile(RuntimeBlockInfo* block, bool smc_check, bool optimise) override { - ass = new Arm32Assembler(*codeBuffer); + ass = new Arm32Assembler(*sh4ctx, *codeBuffer); ass->compile(block, smc_check, optimise); delete ass; ass = nullptr; @@ -457,6 +462,7 @@ class Arm32Dynarec : public Sh4Dynarec private: void generate_mainloop(); + Sh4Context *sh4ctx = nullptr; Sh4CodeBuffer *codeBuffer = nullptr; bool restarting = false; Arm32Assembler *ass = nullptr; @@ -465,7 +471,7 @@ static Arm32Dynarec instance; u32 DynaRBI::Relink() { - Arm32Assembler ass(codeBuffer, (u8 *)code + relink_offset, host_code_size - relink_offset); + Arm32Assembler ass(sh4ctx, codeBuffer, (u8 *)code + relink_offset, host_code_size - relink_offset); u32 size = ass.relinkBlock(this); @@ -584,6 +590,7 @@ void Arm32Assembler::canonParam(const shil_opcode *op, const shil_param *par, Ca case CPT_u32: case CPT_ptr: case CPT_f32: + case CPT_sh4ctx: { CC_PS t = { tp, par }; CC_pars.push_back(t); @@ -606,7 +613,11 @@ void Arm32Assembler::canonCall(const shil_opcode *op, void *function) CC_PS& param = CC_pars[i]; if (param.type == CPT_ptr) { - Mov(rd, (u32)param.par->reg_ptr()); + Mov(rd, (u32)param.par->reg_ptr(sh4ctx)); + } + else if (param.type == CPT_sh4ctx) + { + Mov(rd, reinterpret_cast(&sh4ctx)); } else { @@ -649,7 +660,7 @@ void Arm32Assembler::canonCall(const shil_opcode *op, void *function) if (ccParam.type == CPT_ptr && prm.count() == 2 && reg.IsAllocf(prm) && (op->rd._reg == prm._reg || op->rd2._reg == prm._reg)) { // fsca rd param is a pointer to a 64-bit reg so reload the regs if allocated - const int shRegOffs = (u8*)GetRegPtr(prm._reg) - (u8*)&p_sh4rcb->cntx - sizeof(Sh4cntx); + const int shRegOffs = prm.reg_nofs(); Vldr(reg.mapFReg(prm, 0), MemOperand(r8, shRegOffs)); Vldr(reg.mapFReg(prm, 1), MemOperand(r8, shRegOffs + 4)); } @@ -846,7 +857,7 @@ bool Arm32Dynarec::rewrite(host_context_t& context, void *faultAddress) // ignore last 2 bits zeroed to avoid sigbus errors verify(fault_offs == 0 || (fault_offs & ~3) == (sh4_addr & 0x1FFFFFFC)); - ass = new Arm32Assembler(*codeBuffer, (u8 *)ptr, 12); + ass = new Arm32Assembler(*sh4ctx, *codeBuffer, (u8 *)ptr, 12); ass->rewrite(raddr, rt, ft, fd, !read, is_sq, optp); delete ass; ass = nullptr; @@ -1176,10 +1187,10 @@ void Arm32Assembler::genMmuLookup(RuntimeBlockInfo* block, const shil_opcode& op } } -static void interpreter_fallback(u16 op, OpCallFP *oph, u32 pc) +static void interpreter_fallback(Sh4Context *ctx, u16 op, OpCallFP *oph, u32 pc) { try { - oph(op); + oph(ctx, op); } catch (SH4ThrownException& ex) { if (pc & 1) { @@ -1192,10 +1203,10 @@ static void interpreter_fallback(u16 op, OpCallFP *oph, u32 pc) } } -static void do_sqw_mmu_no_ex(u32 addr, u32 pc) +static void do_sqw_mmu_no_ex(u32 addr, Sh4Context *ctx, u32 pc) { try { - do_sqw_mmu(addr); + ctx->doSqWrite(addr, ctx); } catch (SH4ThrownException& ex) { if (pc & 1) { @@ -1515,15 +1526,16 @@ void Arm32Assembler::compileOp(RuntimeBlockInfo* block, shil_opcode* op, bool op storeSh4Reg(r1, reg_nextpc); } - Mov(r0, op->rs3._imm); + Sub(r0, r8, sizeof(Sh4Context)); + Mov(r1, op->rs3._imm); if (!mmu_enabled()) { call((void *)OpPtr[op->rs3._imm]); } else { - Mov(r1, reinterpret_cast(*OpDesc[op->rs3._imm]->oph)); // op handler - Mov(r2, block->vaddr + op->guest_offs - (op->delay_slot ? 1 : 0)); // pc + Mov(r2, reinterpret_cast(*OpDesc[op->rs3._imm]->oph)); // op handler + Mov(r3, block->vaddr + op->guest_offs - (op->delay_slot ? 1 : 0)); // pc call((void *)interpreter_fallback); } break; @@ -1673,6 +1685,11 @@ void Arm32Assembler::compileOp(RuntimeBlockInfo* block, shil_opcode* op, bool op call((void *)UpdateSR); break; + case shop_sync_fpscr: + Sub(r0, r8, sizeof(Sh4Context)); + call((void *)Sh4Context::UpdateFPSCR); + break; + case shop_test: case shop_seteq: case shop_setge: @@ -1798,15 +1815,15 @@ void Arm32Assembler::compileOp(RuntimeBlockInfo* block, shil_opcode* op, bool op cc = al; } + Sub(r1, r8, sizeof(Sh4Context)); if (mmu_enabled()) { - Mov(r1, block->vaddr + op->guest_offs - (op->delay_slot ? 1 : 0)); // pc + Mov(r2, block->vaddr + op->guest_offs - (op->delay_slot ? 1 : 0)); // pc call((void *)do_sqw_mmu_no_ex, cc); } else { - Ldr(r2, MemOperand(r8, rcbOffset(do_sqw_nommu))); - Sub(r1, r8, -rcbOffset(sq_buffer)); + Ldr(r2, MemOperand(r8, ctxOffset(doSqWrite))); Blx(cc, r2); } } @@ -1990,14 +2007,14 @@ void Arm32Assembler::compileOp(RuntimeBlockInfo* block, shil_opcode* op, bool op QRegister _r1 = q0; QRegister _r2 = q0; - Sub(r0, r8, op->rs1.reg_aofs()); - if (op->rs2.reg_aofs() == op->rs1.reg_aofs()) + Sub(r0, r8, -op->rs1.reg_nofs()); + if (op->rs2.reg_nofs() == op->rs1.reg_nofs()) { Vldm(r0, NO_WRITE_BACK, DRegisterList(d0, 2)); } else { - Sub(r1, r8, op->rs2.reg_aofs()); + Sub(r1, r8, -op->rs2.reg_nofs()); Vldm(r0, NO_WRITE_BACK, DRegisterList(d0, 2)); Vldm(r1, NO_WRITE_BACK, DRegisterList(d2, 2)); _r2 = q1; @@ -2022,12 +2039,12 @@ void Arm32Assembler::compileOp(RuntimeBlockInfo* block, shil_opcode* op, bool op case shop_ftrv: { Register rdp = r1; - Sub(r2, r8, op->rs2.reg_aofs()); - Sub(r1, r8, op->rs1.reg_aofs()); - if (op->rs1.reg_aofs() != op->rd.reg_aofs()) + Sub(r2, r8, -op->rs2.reg_nofs()); + Sub(r1, r8, -op->rs1.reg_nofs()); + if (op->rs1.reg_nofs() != op->rd.reg_nofs()) { rdp = r0; - Sub(r0, r8, op->rd.reg_aofs()); + Sub(r0, r8, -op->rd.reg_nofs()); } #if 1 @@ -2083,8 +2100,8 @@ void Arm32Assembler::compileOp(RuntimeBlockInfo* block, shil_opcode* op, bool op break; case shop_frswap: - Sub(r0, r8, op->rs1.reg_aofs()); - Sub(r1, r8, op->rd.reg_aofs()); + Sub(r0, r8, -op->rs1.reg_nofs()); + Sub(r1, r8, -op->rd.reg_nofs()); //Assumes no FPU reg alloc here //frswap touches all FPU regs, so all spans should be clear here .. Vldm(r1, NO_WRITE_BACK, DRegisterList(d0, 8)); @@ -2180,7 +2197,7 @@ void Arm32Assembler::compile(RuntimeBlockInfo* block, bool force_checks, bool op } //scheduler - Ldr(r1, MemOperand(r8, rcbOffset(cntx.cycle_counter))); + Ldr(r1, MemOperand(r8, ctxOffset(cycle_counter))); Cmp(r1, 0); Label cyclesRemaining; B(pl, &cyclesRemaining); @@ -2198,7 +2215,7 @@ void Arm32Assembler::compile(RuntimeBlockInfo* block, bool force_checks, bool op { Sub(r1, r1, cycles); } - Str(r1, MemOperand(r8, rcbOffset(cntx.cycle_counter))); + Str(r1, MemOperand(r8, ctxOffset(cycle_counter))); //compile the block's opcodes shil_opcode* op; @@ -2250,10 +2267,10 @@ void Arm32Dynarec::reset() ::mainloop = nullptr; unwinder.clear(); - if (p_sh4rcb->cntx.CpuRunning) + if (sh4ctx->CpuRunning) { // Force the dynarec out of mainloop() to regenerate it - p_sh4rcb->cntx.CpuRunning = 0; + sh4ctx->CpuRunning = 0; restarting = true; } else @@ -2266,7 +2283,7 @@ void Arm32Dynarec::generate_mainloop() return; INFO_LOG(DYNAREC, "Generating main loop"); - Arm32Assembler ass(*codeBuffer); + Arm32Assembler ass(*sh4ctx, *codeBuffer); ass.genMainLoop(); } @@ -2366,7 +2383,7 @@ void Arm32Assembler::genMainLoop() Ldr(r8, MemOperand(sp)); // r8: context Mov(r9, (uintptr_t)mmuAddressLUT); // r9: mmu LUT } - Ldr(r4, MemOperand(r8, rcbOffset(cntx.pc))); // r4: pc + Ldr(r4, MemOperand(r8, ctxOffset(pc))); // r4: pc B(&no_updateLabel); // Go to mainloop ! // this code is here for fall-through behavior of do_iter Label do_iter; @@ -2374,9 +2391,9 @@ void Arm32Assembler::genMainLoop() // intc_sched: r0 is pc, r1 is cycle_counter intc_sched = GetCursorAddress(); Add(r1, r1, SH4_TIMESLICE); - Str(r1, MemOperand(r8, rcbOffset(cntx.cycle_counter))); - Str(r0, MemOperand(r8, rcbOffset(cntx.pc))); - Ldr(r0, MemOperand(r8, rcbOffset(cntx.CpuRunning))); + Str(r1, MemOperand(r8, ctxOffset(cycle_counter))); + Str(r0, MemOperand(r8, ctxOffset(pc))); + Ldr(r0, MemOperand(r8, ctxOffset(CpuRunning))); Cmp(r0, 0); B(eq, &cleanup); Mov(r4, lr); @@ -2384,17 +2401,17 @@ void Arm32Assembler::genMainLoop() Cmp(r0, 0); B(ne, &do_iter); Mov(lr, r4); - Ldr(r0, MemOperand(r8, rcbOffset(cntx.cycle_counter))); + Ldr(r0, MemOperand(r8, ctxOffset(cycle_counter))); Bx(lr); // do_iter: Bind(&do_iter); - Ldr(r4, MemOperand(r8, rcbOffset(cntx.pc))); + Ldr(r4, MemOperand(r8, ctxOffset(pc))); // no_update: no_update = GetCursorAddress(); Bind(&no_updateLabel); // next_pc _MUST_ be on r4 - Ldr(r0, MemOperand(r8, rcbOffset(cntx.CpuRunning))); + Ldr(r0, MemOperand(r8, ctxOffset(CpuRunning))); Cmp(r0, 0); B(eq, &cleanup); @@ -2513,7 +2530,7 @@ void Arm32Assembler::genMainLoop() And(r1, r0, 0x3F); Add(r1, r1, r8); jump((void *)&addrspace::write64, ne); - Strd(r2, r3, MemOperand(r1, rcbOffset(sq_buffer))); + Strd(r2, r3, MemOperand(r1, getRegOffset(reg_sq_buffer) - sizeof(Sh4Context))); } else { @@ -2524,7 +2541,7 @@ void Arm32Assembler::genMainLoop() if (reg != 0) Mov(ne, r0, Register(reg)); jump((void *)&addrspace::write32, ne); - Str(r1, MemOperand(r3, rcbOffset(sq_buffer))); + Str(r1, MemOperand(r3, getRegOffset(reg_sq_buffer) - sizeof(Sh4Context))); } Bx(lr); } @@ -2540,7 +2557,7 @@ void Arm32Assembler::genMainLoop() INFO_LOG(DYNAREC, "readm helpers: up to %p", GetCursorAddress()); } -void Arm32Dynarec::init(Sh4CodeBuffer& codeBuffer) +void Arm32Dynarec::init(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) { INFO_LOG(DYNAREC, "Initializing the ARM32 dynarec"); @@ -2562,6 +2579,7 @@ void Arm32Dynarec::init(Sh4CodeBuffer& codeBuffer) ccmap[shop_setab] = hi; ccnmap[shop_setab] = ls; + this->sh4ctx = &sh4ctx; this->codeBuffer = &codeBuffer; } @@ -2573,6 +2591,6 @@ void Arm32Dynarec::handleException(host_context_t &context) RuntimeBlockInfo* Arm32Dynarec::allocateBlock() { generate_mainloop(); // FIXME why is this needed? - return new DynaRBI(*codeBuffer); + return new DynaRBI(*sh4ctx, *codeBuffer); }; #endif diff --git a/core/rec-ARM64/rec_arm64.cpp b/core/rec-ARM64/rec_arm64.cpp index a5a2ea98a..c9fbe7f81 100644 --- a/core/rec-ARM64/rec_arm64.cpp +++ b/core/rec-ARM64/rec_arm64.cpp @@ -45,14 +45,14 @@ using namespace vixl::aarch64; #include "oslib/virtmem.h" #include "emulator.h" -#undef do_sqw_nommu - struct DynaRBI : RuntimeBlockInfo { - DynaRBI(Sh4CodeBuffer& codeBuffer) : codeBuffer(codeBuffer) {} + DynaRBI(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) + : sh4ctx(sh4ctx), codeBuffer(codeBuffer) {} u32 Relink() override; private: + Sh4Context& sh4ctx; Sh4CodeBuffer& codeBuffer; }; @@ -87,10 +87,10 @@ static void jitWriteProtect(Sh4CodeBuffer &codeBuffer, bool enable) #endif } -static void interpreter_fallback(u16 op, OpCallFP *oph, u32 pc) +static void interpreter_fallback(Sh4Context *ctx, u16 op, OpCallFP *oph, u32 pc) { try { - oph(op); + oph(ctx, op); } catch (SH4ThrownException& ex) { if (pc & 1) { @@ -103,10 +103,10 @@ static void interpreter_fallback(u16 op, OpCallFP *oph, u32 pc) } } -static void do_sqw_mmu_no_ex(u32 addr, u32 pc) +static void do_sqw_mmu_no_ex(u32 addr, Sh4Context *ctx, u32 pc) { try { - do_sqw_mmu(addr); + ctx->doSqWrite(addr, ctx); } catch (SH4ThrownException& ex) { if (pc & 1) { @@ -126,10 +126,11 @@ class Arm64Assembler : public MacroAssembler typedef void (MacroAssembler::*Arm64Fop_RRR)(const VRegister&, const VRegister&, const VRegister&); public: - Arm64Assembler(Sh4CodeBuffer& codeBuffer) : Arm64Assembler(codeBuffer, codeBuffer.get()) { - } + Arm64Assembler(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) + : Arm64Assembler(sh4ctx, codeBuffer, codeBuffer.get()) { } - Arm64Assembler(Sh4CodeBuffer& codeBuffer, void *buffer) : MacroAssembler((u8 *)buffer, codeBuffer.getFreeSpace()), regalloc(this), codeBuffer(codeBuffer) + Arm64Assembler(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer, void *buffer) + : MacroAssembler((u8 *)buffer, codeBuffer.getFreeSpace()), regalloc(this), sh4ctx(sh4ctx), codeBuffer(codeBuffer) { call_regs.push_back((const WRegister*)&w0); call_regs.push_back((const WRegister*)&w1); @@ -211,7 +212,7 @@ class Arm64Assembler : public MacroAssembler Add(*ret_reg, regalloc.MapRegister(op.rs1), op.rs3._imm); else { - Ldr(*ret_reg, sh4_context_mem_operand(op.rs1.reg_ptr())); + Ldr(*ret_reg, sh4_context_mem_operand(op.rs1._reg)); Add(*ret_reg, *ret_reg, op.rs3._imm); } } @@ -221,8 +222,8 @@ class Arm64Assembler : public MacroAssembler Add(*ret_reg, regalloc.MapRegister(op.rs1), regalloc.MapRegister(op.rs3)); else { - Ldr(*ret_reg, sh4_context_mem_operand(op.rs1.reg_ptr())); - Ldr(w8, sh4_context_mem_operand(op.rs3.reg_ptr())); + Ldr(*ret_reg, sh4_context_mem_operand(op.rs1._reg)); + Ldr(w8, sh4_context_mem_operand(op.rs3._reg)); Add(*ret_reg, *ret_reg, w8); } } @@ -241,7 +242,7 @@ class Arm64Assembler : public MacroAssembler } else { - Ldr(*ret_reg, sh4_context_mem_operand(op.rs1.reg_ptr())); + Ldr(*ret_reg, sh4_context_mem_operand(op.rs1._reg)); } } else @@ -264,7 +265,7 @@ class Arm64Assembler : public MacroAssembler regalloc.DoAlloc(block); // scheduler - Ldr(w1, sh4_context_mem_operand(&Sh4cntx.cycle_counter)); + Ldr(w1, sh4_context_mem_operand(&sh4ctx.cycle_counter)); Cmp(w1, 0); Label cycles_remaining; B(&cycles_remaining, pl); @@ -274,7 +275,7 @@ class Arm64Assembler : public MacroAssembler Bind(&cycles_remaining); Sub(w1, w1, block->guest_cycles); - Str(w1, sh4_context_mem_operand(&Sh4cntx.cycle_counter)); + Str(w1, sh4_context_mem_operand(&sh4ctx.cycle_counter)); for (size_t i = 0; i < block->oplist.size(); i++) { @@ -287,18 +288,19 @@ class Arm64Assembler : public MacroAssembler if (op.rs1._imm) // if NeedPC() { Mov(w10, op.rs2._imm); - Str(w10, sh4_context_mem_operand(&next_pc)); + Str(w10, sh4_context_mem_operand(&sh4ctx.pc)); } - Mov(w0, op.rs3._imm); + Mov(x0, x28); + Mov(w1, op.rs3._imm); if (!mmu_enabled()) { GenCallRuntime(OpDesc[op.rs3._imm]->oph); } else { - Mov(x1, reinterpret_cast(*OpDesc[op.rs3._imm]->oph)); // op handler - Mov(w2, block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc + Mov(x2, reinterpret_cast(*OpDesc[op.rs3._imm]->oph)); // op handler + Mov(w3, block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc GenCallRuntime(interpreter_fallback); } @@ -390,7 +392,8 @@ class Arm64Assembler : public MacroAssembler GenCallRuntime(UpdateSR); break; case shop_sync_fpscr: - GenCallRuntime(UpdateFPSCR); + Mov(x0, x28); + GenCallRuntime(Sh4Context::UpdateFPSCR); break; case shop_swaplb: @@ -779,7 +782,7 @@ class Arm64Assembler : public MacroAssembler Lsr(w1, regalloc.MapRegister(op.rs1), 26); else { - Ldr(w0, sh4_context_mem_operand(op.rs1.reg_ptr())); + Ldr(w0, sh4_context_mem_operand(op.rs1._reg)); Lsr(w1, w0, 26); } Cmp(w1, 0x38); @@ -788,17 +791,15 @@ class Arm64Assembler : public MacroAssembler Mov(w0, regalloc.MapRegister(op.rs1)); } + Mov(x1, x28); if (mmu_enabled()) { - Mov(w1, block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc - + Mov(w2, block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc GenCallRuntime(do_sqw_mmu_no_ex); } else { - Sub(x9, x28, offsetof(Sh4RCB, cntx) - offsetof(Sh4RCB, do_sqw_nommu)); - Ldr(x9, MemOperand(x9)); - Sub(x1, x28, offsetof(Sh4RCB, cntx) - offsetof(Sh4RCB, sq_buffer)); + Ldr(x9, sh4_context_mem_operand(&sh4ctx.doSqWrite)); Blr(x9); } Bind(¬_sqw); @@ -904,17 +905,17 @@ class Arm64Assembler : public MacroAssembler else { Ldr(x2, MemOperand(x1)); - Str(x2, sh4_context_mem_operand(op.rd.reg_ptr())); + Str(x2, sh4_context_mem_operand(op.rd._reg)); } break; /* fall back to the canonical implementations for better precision case shop_fipr: - Add(x9, x28, sh4_context_mem_operand(op.rs1.reg_ptr()).GetOffset()); + Add(x9, x28, op.rs1.reg_offset()); Ld1(v0.V4S(), MemOperand(x9)); if (op.rs1._reg != op.rs2._reg) { - Add(x9, x28, sh4_context_mem_operand(op.rs2.reg_ptr()).GetOffset()); + Add(x9, x28, op.rs2.reg_offset()); Ld1(v1.V4S(), MemOperand(x9)); Fmul(v0.V4S(), v0.V4S(), v1.V4S()); } @@ -925,9 +926,9 @@ class Arm64Assembler : public MacroAssembler break; case shop_ftrv: - Add(x9, x28, sh4_context_mem_operand(op.rs1.reg_ptr()).GetOffset()); + Add(x9, x28, op.rs1.reg_offset()); Ld1(v0.V4S(), MemOperand(x9)); - Add(x9, x28, sh4_context_mem_operand(op.rs2.reg_ptr()).GetOffset()); + Add(x9, x28, op.rs2.reg_offset()); Ld1(v1.V4S(), MemOperand(x9, 16, PostIndex)); Ld1(v2.V4S(), MemOperand(x9, 16, PostIndex)); Ld1(v3.V4S(), MemOperand(x9, 16, PostIndex)); @@ -936,14 +937,14 @@ class Arm64Assembler : public MacroAssembler Fmla(v5.V4S(), v2.V4S(), s0, 1); Fmla(v5.V4S(), v3.V4S(), s0, 2); Fmla(v5.V4S(), v4.V4S(), s0, 3); - Add(x9, x28, sh4_context_mem_operand(op.rd.reg_ptr()).GetOffset()); + Add(x9, x28, op.rd.reg_offset()); St1(v5.V4S(), MemOperand(x9)); break; */ case shop_frswap: - Add(x9, x28, sh4_context_mem_operand(op.rs1.reg_ptr()).GetOffset()); - Add(x10, x28, sh4_context_mem_operand(op.rd.reg_ptr()).GetOffset()); + Add(x9, x28, op.rs1.reg_offset()); + Add(x10, x28, op.rd.reg_offset()); Ld4(v0.V2D(), v1.V2D(), v2.V2D(), v3.V2D(), MemOperand(x9)); Ld4(v4.V2D(), v5.V2D(), v6.V2D(), v7.V2D(), MemOperand(x10)); St4(v4.V2D(), v5.V2D(), v6.V2D(), v7.V2D(), MemOperand(x9)); @@ -980,7 +981,7 @@ class Arm64Assembler : public MacroAssembler CC_pars.clear(); } - void canonParam(const shil_opcode& op, const shil_param& prm, CanonicalParamType tp) + void canonParam(const shil_opcode& op, const shil_param *prm, CanonicalParamType tp) { switch (tp) { @@ -988,24 +989,25 @@ class Arm64Assembler : public MacroAssembler case CPT_u32: case CPT_ptr: case CPT_f32: + case CPT_sh4ctx: { - CC_PS t = { tp, &prm }; + CC_PS t = { tp, prm }; CC_pars.push_back(t); } break; case CPT_u64rvL: case CPT_u32rv: - host_reg_to_shil_param(prm, w0); + host_reg_to_shil_param(*prm, w0); break; case CPT_u64rvH: Lsr(x10, x0, 32); - host_reg_to_shil_param(prm, w10); + host_reg_to_shil_param(*prm, w10); break; case CPT_f32rv: - host_reg_to_shil_param(prm, s0); + host_reg_to_shil_param(*prm, s0); break; } } @@ -1023,10 +1025,8 @@ class Arm64Assembler : public MacroAssembler switch (CC_pars[i].type) { // push the params - case CPT_u32: shil_param_to_host_reg(prm, *call_regs[regused++]); - break; case CPT_f32: @@ -1042,9 +1042,13 @@ class Arm64Assembler : public MacroAssembler case CPT_ptr: verify(prm.is_reg()); // push the ptr itself - Mov(*call_regs64[regused++], reinterpret_cast(prm.reg_ptr())); + Mov(*call_regs64[regused++], reinterpret_cast(prm.reg_ptr(sh4ctx))); + break; + case CPT_sh4ctx: + Mov(*call_regs64[regused++], reinterpret_cast(&sh4ctx)); break; + case CPT_u32rv: case CPT_u64rvL: case CPT_u64rvH: @@ -1060,15 +1064,21 @@ class Arm64Assembler : public MacroAssembler if (ccParam.type == CPT_ptr && prm.count() == 2 && regalloc.IsAllocf(prm) && (op->rd._reg == prm._reg || op->rd2._reg == prm._reg)) { // fsca rd param is a pointer to a 64-bit reg so reload the regs if allocated - Ldr(regalloc.MapVRegister(prm, 0), sh4_context_mem_operand(GetRegPtr(prm._reg))); - Ldr(regalloc.MapVRegister(prm, 1), sh4_context_mem_operand(GetRegPtr(prm._reg + 1))); + Ldr(regalloc.MapVRegister(prm, 0), sh4_context_mem_operand(prm._reg)); + Ldr(regalloc.MapVRegister(prm, 1), sh4_context_mem_operand((Sh4RegType)(prm._reg + 1))); } } } MemOperand sh4_context_mem_operand(void *p) { - u32 offset = (u8*)p - (u8*)&p_sh4rcb->cntx; + u32 offset = (u8*)p - (u8*)&sh4ctx; + verify((offset & 3) == 0 && offset <= 16380); // FIXME 64-bit regs need multiple of 8 up to 32760 + return MemOperand(x28, offset); + } + MemOperand sh4_context_mem_operand(Sh4RegType reg) + { + u32 offset = getRegOffset(reg); verify((offset & 3) == 0 && offset <= 16380); // FIXME 64-bit regs need multiple of 8 up to 32760 return MemOperand(x28, offset); } @@ -1164,7 +1174,7 @@ class Arm64Assembler : public MacroAssembler #endif { Mov(w29, block->BranchBlock); - Str(w29, sh4_context_mem_operand(&next_pc)); + Str(w29, sh4_context_mem_operand(&sh4ctx.pc)); GenBranch(arm64_no_update); } } @@ -1178,9 +1188,9 @@ class Arm64Assembler : public MacroAssembler // next_pc = branch_pc_value; if (block->has_jcond) - Ldr(w11, sh4_context_mem_operand(&Sh4cntx.jdyn)); + Ldr(w11, sh4_context_mem_operand(&sh4ctx.jdyn)); else - Ldr(w11, sh4_context_mem_operand(&sr.T)); + Ldr(w11, sh4_context_mem_operand(&sh4ctx.sr.T)); Cmp(w11, block->BlockType & 1); @@ -1208,7 +1218,7 @@ class Arm64Assembler : public MacroAssembler #endif { Mov(w29, block->BranchBlock); - Str(w29, sh4_context_mem_operand(&next_pc)); + Str(w29, sh4_context_mem_operand(&sh4ctx.pc)); GenBranch(arm64_no_update); } } @@ -1236,7 +1246,7 @@ class Arm64Assembler : public MacroAssembler #endif { Mov(w29, block->NextBlock); - Str(w29, sh4_context_mem_operand(&next_pc)); + Str(w29, sh4_context_mem_operand(&sh4ctx.pc)); GenBranch(arm64_no_update); } } @@ -1248,7 +1258,7 @@ class Arm64Assembler : public MacroAssembler case BET_DynamicRet: // next_pc = *jdyn; - Str(w29, sh4_context_mem_operand(&next_pc)); + Str(w29, sh4_context_mem_operand(&sh4ctx.pc)); if (!mmu_enabled()) { // TODO Call no_update instead (and check CpuRunning less frequently?) @@ -1277,11 +1287,11 @@ class Arm64Assembler : public MacroAssembler Mov(w29, block->NextBlock); // else next_pc = *jdyn (already in w29) - Str(w29, sh4_context_mem_operand(&next_pc)); + Str(w29, sh4_context_mem_operand(&sh4ctx.pc)); GenCallRuntime(UpdateINTC); - Ldr(w29, sh4_context_mem_operand(&next_pc)); + Ldr(w29, sh4_context_mem_operand(&sh4ctx.pc)); GenBranch(arm64_no_update); break; @@ -1451,21 +1461,21 @@ class Arm64Assembler : public MacroAssembler Bind(&intc_sched); // w0 is pc, w1 is cycle_counter - Str(w0, sh4_context_mem_operand(&Sh4cntx.pc)); + Str(w0, sh4_context_mem_operand(&sh4ctx.pc)); // Add timeslice to cycle counter Add(w1, w1, SH4_TIMESLICE); - Str(w1, sh4_context_mem_operand(&Sh4cntx.cycle_counter)); - Ldr(w0, sh4_context_mem_operand(&Sh4cntx.CpuRunning)); + Str(w1, sh4_context_mem_operand(&sh4ctx.cycle_counter)); + Ldr(w0, sh4_context_mem_operand(&sh4ctx.CpuRunning)); Cbz(w0, &end_mainloop); Mov(x29, lr); // Save link register in case we return GenCallRuntime(UpdateSystem_INTC); Cbnz(w0, &do_interrupts); Mov(lr, x29); - Ldr(w0, sh4_context_mem_operand(&Sh4cntx.cycle_counter)); + Ldr(w0, sh4_context_mem_operand(&sh4ctx.cycle_counter)); Ret(); Bind(&do_interrupts); - Ldr(w29, sh4_context_mem_operand(&Sh4cntx.pc)); + Ldr(w29, sh4_context_mem_operand(&sh4ctx.pc)); B(&no_update); Bind(&end_mainloop); @@ -1500,12 +1510,12 @@ class Arm64Assembler : public MacroAssembler // w0: vaddr, w1: addr checkBlockFpu = GetCursorAddress(); Label fpu_enabled; - Ldr(w10, sh4_context_mem_operand(&sr.status)); + Ldr(w10, sh4_context_mem_operand(&sh4ctx.sr.status)); Tbz(w10, 15, &fpu_enabled); // test SR.FD bit Mov(w1, Sh4Ex_FpuDisabled); // exception code GenCallRuntime(Do_Exception); - Ldr(w29, sh4_context_mem_operand(&next_pc)); + Ldr(w29, sh4_context_mem_operand(&sh4ctx.pc)); B(&no_update); Bind(&fpu_enabled); // fallthrough @@ -1514,7 +1524,7 @@ class Arm64Assembler : public MacroAssembler // MMU Block check (no fpu) // w0: vaddr, w1: addr checkBlockNoFpu = GetCursorAddress(); - Ldr(w2, sh4_context_mem_operand(&Sh4cntx.pc)); + Ldr(w2, sh4_context_mem_operand(&sh4ctx.pc)); Cmp(w2, w0); Mov(w0, w1); B(&blockCheckFailLabel, ne); @@ -1559,8 +1569,7 @@ class Arm64Assembler : public MacroAssembler Cmp(x7, 0x38); GenBranchRuntime(addrspace::write32, Condition::ne); And(x0, x0, 0x3f); - Sub(x7, x0, sizeof(Sh4RCB::sq_buffer), LeaveFlags); - Str(w1, MemOperand(x28, x7)); + Str(w1, MemOperand(x28, x0)); Ret(); Label writeStoreQueue64Label; @@ -1569,8 +1578,7 @@ class Arm64Assembler : public MacroAssembler Cmp(x7, 0x38); GenBranchRuntime(addrspace::write64, Condition::ne); And(x0, x0, 0x3f); - Sub(x7, x0, sizeof(Sh4RCB::sq_buffer), LeaveFlags); - Str(x1, MemOperand(x28, x7)); + Str(x1, MemOperand(x28, x0)); Ret(); FinalizeCode(); @@ -1785,9 +1793,9 @@ class Arm64Assembler : public MacroAssembler break; } if (op.size == 8) - Str(x1, sh4_context_mem_operand(op.rd.reg_ptr())); + Str(x1, sh4_context_mem_operand(op.rd._reg)); else - Str(w1, sh4_context_mem_operand(op.rd.reg_ptr())); + Str(w1, sh4_context_mem_operand(op.rd._reg)); } } else @@ -1801,14 +1809,14 @@ class Arm64Assembler : public MacroAssembler if (regalloc.IsAllocf(op.rd)) Fmov(regalloc.MapVRegister(op.rd, 0), w0); else - Str(w0, sh4_context_mem_operand(op.rd.reg_ptr())); + Str(w0, sh4_context_mem_operand(op.rd._reg)); Mov(w0, addr + 4); GenCallRuntime((void (*)())ptr); if (regalloc.IsAllocf(op.rd)) Fmov(regalloc.MapVRegister(op.rd, 1), w0); else - Str(w0, sh4_context_mem_operand((u8*)op.rd.reg_ptr() + 4)); + Str(w0, sh4_context_mem_operand((Sh4RegType)(op.rd._reg + 1))); } else { @@ -2104,14 +2112,14 @@ class Arm64Assembler : public MacroAssembler { if (param.is_r64f() && !regalloc.IsAllocf(param)) { - Ldr(reg, sh4_context_mem_operand(param.reg_ptr())); + Ldr(reg, sh4_context_mem_operand(param._reg)); } else if (param.is_r32f() || param.is_r64f()) { if (regalloc.IsAllocf(param)) Fmov(reg.W(), regalloc.MapVRegister(param, 0)); else - Ldr(reg.W(), sh4_context_mem_operand(param.reg_ptr())); + Ldr(reg.W(), sh4_context_mem_operand(param._reg)); if (param.is_r64f()) { Fmov(w15, regalloc.MapVRegister(param, 1)); @@ -2123,7 +2131,7 @@ class Arm64Assembler : public MacroAssembler if (regalloc.IsAllocg(param)) Mov(reg.W(), regalloc.MapRegister(param)); else - Ldr(reg.W(), sh4_context_mem_operand(param.reg_ptr())); + Ldr(reg.W(), sh4_context_mem_operand(param._reg)); } } else @@ -2145,7 +2153,7 @@ class Arm64Assembler : public MacroAssembler } else { - Str((const Register&)reg, sh4_context_mem_operand(param.reg_ptr())); + Str((const Register&)reg, sh4_context_mem_operand(param._reg)); } } else if (regalloc.IsAllocg(param)) @@ -2164,7 +2172,7 @@ class Arm64Assembler : public MacroAssembler } else { - Str(reg, sh4_context_mem_operand(param.reg_ptr())); + Str(reg, sh4_context_mem_operand(param._reg)); } } @@ -2181,6 +2189,7 @@ class Arm64Assembler : public MacroAssembler RuntimeBlockInfo* block = NULL; const int read_memory_rewrite_size = 5; // ubfx, add, ldr for fast access. calling a handler can use more than 3 depending on offset const int write_memory_rewrite_size = 5; // ubfx, add, str + Sh4Context& sh4ctx; Sh4CodeBuffer& codeBuffer; }; @@ -2191,9 +2200,10 @@ class Arm64Dynarec : public Sh4Dynarec sh4Dynarec = this; } - void init(Sh4CodeBuffer& codeBuffer) override + void init(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) override { INFO_LOG(DYNAREC, "Initializing the ARM64 dynarec"); + this->sh4ctx = &sh4ctx; this->codeBuffer = &codeBuffer; } @@ -2202,10 +2212,10 @@ class Arm64Dynarec : public Sh4Dynarec unwinder.clear(); ::mainloop = nullptr; - if (p_sh4rcb->cntx.CpuRunning) + if (sh4ctx->CpuRunning) { // Force the dynarec out of mainloop() to regenerate it - p_sh4rcb->cntx.CpuRunning = 0; + sh4ctx->CpuRunning = 0; restarting = true; } else @@ -2233,7 +2243,7 @@ class Arm64Dynarec : public Sh4Dynarec { verify(codeBuffer->getFreeSpace() >= 16 * 1024); - compiler = new Arm64Assembler(*codeBuffer); + compiler = new Arm64Assembler(*sh4ctx, *codeBuffer); compiler->compileBlock(block, smc_checks, optimise); @@ -2248,7 +2258,7 @@ class Arm64Dynarec : public Sh4Dynarec void canonParam(const shil_opcode *op, const shil_param *par, CanonicalParamType tp) override { - compiler->canonParam(*op, *par, tp); + compiler->canonParam(*op, par, tp); } void canonCall(const shil_opcode *op, void *function) override @@ -2264,7 +2274,7 @@ class Arm64Dynarec : public Sh4Dynarec if (::mainloop != nullptr) return; jitWriteProtect(*codeBuffer, false); - compiler = new Arm64Assembler(*codeBuffer); + compiler = new Arm64Assembler(*sh4ctx, *codeBuffer); compiler->GenMainloop(); @@ -2276,7 +2286,7 @@ class Arm64Dynarec : public Sh4Dynarec RuntimeBlockInfo* allocateBlock() override { generate_mainloop(); - return new DynaRBI(*codeBuffer); + return new DynaRBI(*sh4ctx, *codeBuffer); } void handleException(host_context_t &context) override @@ -2347,7 +2357,7 @@ class Arm64Dynarec : public Sh4Dynarec // Skip the preceding ops (add, ubfx) u32 *code_rewrite = code_ptr - 2; - Arm64Assembler *assembler = new Arm64Assembler(*codeBuffer, code_rewrite); + Arm64Assembler *assembler = new Arm64Assembler(*sh4ctx, *codeBuffer, code_rewrite); if (is_read) assembler->GenReadMemorySlow(size); else if (!is_read && size >= 4 && (context.x0 >> 26) == 0x38) @@ -2365,6 +2375,7 @@ class Arm64Dynarec : public Sh4Dynarec private: Arm64Assembler* compiler = nullptr; bool restarting = false; + Sh4Context *sh4ctx = nullptr; Sh4CodeBuffer *codeBuffer = nullptr; }; @@ -2375,7 +2386,7 @@ u32 DynaRBI::Relink() #ifndef NO_BLOCK_LINKING //printf("DynaRBI::Relink %08x\n", this->addr); jitWriteProtect(codeBuffer, false); - Arm64Assembler *compiler = new Arm64Assembler(codeBuffer, (u8 *)this->code + this->relink_offset); + Arm64Assembler *compiler = new Arm64Assembler(sh4ctx, codeBuffer, (u8 *)this->code + this->relink_offset); u32 code_size = compiler->RelinkBlock(this); compiler->Finalize(true); @@ -2390,18 +2401,18 @@ u32 DynaRBI::Relink() void Arm64RegAlloc::Preload(u32 reg, eReg nreg) { - assembler->Ldr(Register(nreg, 32), assembler->sh4_context_mem_operand(GetRegPtr(reg))); + assembler->Ldr(Register(nreg, 32), assembler->sh4_context_mem_operand((Sh4RegType)reg)); } void Arm64RegAlloc::Writeback(u32 reg, eReg nreg) { - assembler->Str(Register(nreg, 32), assembler->sh4_context_mem_operand(GetRegPtr(reg))); + assembler->Str(Register(nreg, 32), assembler->sh4_context_mem_operand((Sh4RegType)reg)); } void Arm64RegAlloc::Preload_FPU(u32 reg, eFReg nreg) { - assembler->Ldr(VRegister(nreg, 32), assembler->sh4_context_mem_operand(GetRegPtr(reg))); + assembler->Ldr(VRegister(nreg, 32), assembler->sh4_context_mem_operand((Sh4RegType)reg)); } void Arm64RegAlloc::Writeback_FPU(u32 reg, eFReg nreg) { - assembler->Str(VRegister(nreg, 32), assembler->sh4_context_mem_operand(GetRegPtr(reg))); + assembler->Str(VRegister(nreg, 32), assembler->sh4_context_mem_operand((Sh4RegType)reg)); } #endif // FEAT_SHREC == DYNAREC_JIT diff --git a/core/rec-x64/rec_x64.cpp b/core/rec-x64/rec_x64.cpp index ffbaa2573..08e5df24e 100644 --- a/core/rec-x64/rec_x64.cpp +++ b/core/rec-x64/rec_x64.cpp @@ -20,6 +20,7 @@ using namespace Xbyak::util; #include "xbyak_base.h" #include "oslib/unwind_info.h" #include "oslib/virtmem.h" +#include "cfg/option.h" static void (*mainloop)(); static void (*handleException)(); @@ -63,7 +64,7 @@ static void ngen_blockcheckfail(u32 pc) { rdv_BlockCheckFail(pc); } -static void handle_sh4_exception(SH4ThrownException& ex, u32 pc) +static void handle_sh4_exception(Sh4Context *ctx, SH4ThrownException& ex, u32 pc) { if (pc & 1) { @@ -72,25 +73,25 @@ static void handle_sh4_exception(SH4ThrownException& ex, u32 pc) pc--; } Do_Exception(pc, ex.expEvn); - p_sh4rcb->cntx.cycle_counter += 4; // probably more is needed + ctx->cycle_counter += 4; // probably more is needed handleException(); } -static void interpreter_fallback(u16 op, OpCallFP *oph, u32 pc) +static void interpreter_fallback(Sh4Context *ctx, u16 op, OpCallFP *oph, u32 pc) { try { - oph(op); + oph(ctx, op); } catch (SH4ThrownException& ex) { - handle_sh4_exception(ex, pc); + handle_sh4_exception(ctx, ex, pc); } } -static void do_sqw_mmu_no_ex(u32 addr, u32 pc) +static void do_sqw_mmu_no_ex(u32 addr, Sh4Context *ctx, u32 pc) { try { - do_sqw_mmu(addr); + ctx->doSqWrite(addr, ctx); } catch (SH4ThrownException& ex) { - handle_sh4_exception(ex, pc); + handle_sh4_exception(ctx, ex, pc); } } @@ -120,8 +121,8 @@ class BlockCompiler : public BaseXbyakRec using BaseCompiler = BaseXbyakRec; friend class BaseXbyakRec; - BlockCompiler(Sh4CodeBuffer& codeBuffer) : BaseCompiler(codeBuffer), regalloc(this) { } - BlockCompiler(Sh4CodeBuffer& codeBuffer, u8 *code_ptr) : BaseCompiler(codeBuffer, code_ptr), regalloc(this) { } + BlockCompiler(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) : BaseCompiler(sh4ctx, codeBuffer), regalloc(this) { } + BlockCompiler(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer, u8 *code_ptr) : BaseCompiler(sh4ctx, codeBuffer, code_ptr), regalloc(this) { } void compile(RuntimeBlockInfo* block, bool force_checks, bool optimise) { @@ -135,7 +136,7 @@ class BlockCompiler : public BaseXbyakRec if (mmu_enabled() && block->has_fpu_op) { Xbyak::Label fpu_enabled; - mov(rax, (uintptr_t)&sr.status); + mov(rax, (uintptr_t)&sh4ctx.sr.status); test(dword[rax], 0x8000); // test SR.FD bit jz(fpu_enabled); mov(call_regs[0], block->vaddr); // pc @@ -144,7 +145,7 @@ class BlockCompiler : public BaseXbyakRec jmp(exit_block, T_NEAR); L(fpu_enabled); } - mov(rax, (uintptr_t)&p_sh4rcb->cntx.cycle_counter); + mov(rax, (uintptr_t)&sh4ctx.cycle_counter); sub(dword[rax], block->guest_cycles); regalloc.DoAlloc(block); @@ -160,18 +161,19 @@ class BlockCompiler : public BaseXbyakRec case shop_ifb: if (mmu_enabled()) { - mov(call_regs64[1], reinterpret_cast(*OpDesc[op.rs3._imm]->oph)); // op handler - mov(call_regs[2], block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc + mov(call_regs64[2], reinterpret_cast(*OpDesc[op.rs3._imm]->oph)); // op handler + mov(call_regs[3], block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc } if (op.rs1._imm) { - mov(rax, (size_t)&next_pc); + mov(rax, (size_t)&sh4ctx.pc); mov(dword[rax], op.rs2._imm); } - mov(call_regs[0], op.rs3._imm); - + mov(call_regs[1], op.rs3._imm); + mov(call_regs64[0], (uintptr_t)&sh4ctx); + if (!mmu_enabled()) GenCall(OpDesc[op.rs3._imm]->oph); else @@ -185,9 +187,9 @@ class BlockCompiler : public BaseXbyakRec verify(op.rs1.is_r64f()); #if ALLOC_F64 == false - mov(rax, (uintptr_t)op.rs1.reg_ptr()); + mov(rax, (uintptr_t)op.rs1.reg_ptr(sh4ctx)); mov(rax, qword[rax]); - mov(rcx, (uintptr_t)op.rd.reg_ptr()); + mov(rcx, (uintptr_t)op.rd.reg_ptr(sh4ctx)); mov(qword[rcx], rax); #else Xbyak::Xmm rd0 = regalloc.MapXRegister(op.rd, 0); @@ -224,7 +226,7 @@ class BlockCompiler : public BaseXbyakRec add(call_regs[0], regalloc.MapRegister(op.rs3)); else { - mov(rax, (uintptr_t)op.rs3.reg_ptr()); + mov(rax, (uintptr_t)op.rs3.reg_ptr(sh4ctx)); add(call_regs[0], dword[rax]); } } @@ -236,7 +238,7 @@ class BlockCompiler : public BaseXbyakRec #if ALLOC_F64 == false if (size == MemSize::S64) { - mov(rcx, (uintptr_t)op.rd.reg_ptr()); + mov(rcx, (uintptr_t)op.rd.reg_ptr(sh4ctx)); mov(qword[rcx], rax); } else @@ -261,7 +263,7 @@ class BlockCompiler : public BaseXbyakRec add(call_regs[0], regalloc.MapRegister(op.rs3)); else { - mov(rax, (uintptr_t)op.rs3.reg_ptr()); + mov(rax, (uintptr_t)op.rs3.reg_ptr(sh4ctx)); add(call_regs[0], dword[rax]); } } @@ -270,7 +272,7 @@ class BlockCompiler : public BaseXbyakRec #if ALLOC_F64 == false if (op.size == 8) { - mov(rax, (uintptr_t)op.rs2.reg_ptr()); + mov(rax, (uintptr_t)op.rs2.reg_ptr(sh4ctx)); mov(call_regs64[1], qword[rax]); } else @@ -294,7 +296,8 @@ class BlockCompiler : public BaseXbyakRec GenCall(UpdateSR); break; case shop_sync_fpscr: - GenCall(UpdateFPSCR); + mov(call_regs64[0], (uintptr_t)&sh4ctx); + GenCall(Sh4Context::UpdateFPSCR); break; case shop_negc: @@ -358,7 +361,7 @@ class BlockCompiler : public BaseXbyakRec } else { - mov(rax, (uintptr_t)op.rs1.reg_ptr()); + mov(rax, (uintptr_t)op.rs1.reg_ptr(sh4ctx)); mov(eax, dword[rax]); rn = eax; } @@ -369,16 +372,15 @@ class BlockCompiler : public BaseXbyakRec mov(call_regs[0], rn); } + mov(call_regs64[1], (uintptr_t)&sh4ctx); if (mmu_enabled()) { - mov(call_regs[1], block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc - + mov(call_regs[2], block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc GenCall(do_sqw_mmu_no_ex); } else { - mov(call_regs64[1], (uintptr_t)sq_both); - mov(rax, (size_t)&do_sqw_nommu); + mov(rax, (size_t)&sh4ctx.doSqWrite); saveXmmRegisters(); call(qword[rax]); restoreXmmRegisters(); @@ -388,8 +390,8 @@ class BlockCompiler : public BaseXbyakRec break; case shop_frswap: - mov(rax, (uintptr_t)op.rs1.reg_ptr()); - mov(rcx, (uintptr_t)op.rd.reg_ptr()); + mov(rax, (uintptr_t)op.rs1.reg_ptr(sh4ctx)); + mov(rcx, (uintptr_t)op.rd.reg_ptr(sh4ctx)); if (cpu.has(Cpu::tAVX512F)) { vmovaps(zmm0, zword[rax]); @@ -470,7 +472,7 @@ class BlockCompiler : public BaseXbyakRec regalloc.Cleanup(); current_opid = -1; - mov(rax, (size_t)&next_pc); + mov(rax, (size_t)&sh4ctx.pc); switch (block->BlockType) { @@ -490,9 +492,9 @@ class BlockCompiler : public BaseXbyakRec mov(dword[rax], block->NextBlock); if (block->has_jcond) - mov(rdx, (size_t)&Sh4cntx.jdyn); + mov(rdx, (size_t)&sh4ctx.jdyn); else - mov(rdx, (size_t)&sr.T); + mov(rdx, (size_t)&sh4ctx.sr.T); cmp(dword[rdx], block->BlockType & 1); Xbyak::Label branch_not_taken; @@ -507,7 +509,7 @@ class BlockCompiler : public BaseXbyakRec case BET_DynamicCall: case BET_DynamicRet: //next_pc = *jdyn; - mov(rdx, (size_t)&Sh4cntx.jdyn); + mov(rdx, (size_t)&sh4ctx.jdyn); mov(edx, dword[rdx]); mov(dword[rax], edx); break; @@ -516,7 +518,7 @@ class BlockCompiler : public BaseXbyakRec case BET_StaticIntr: if (block->BlockType == BET_DynamicIntr) { //next_pc = *jdyn; - mov(rdx, (size_t)&Sh4cntx.jdyn); + mov(rdx, (size_t)&sh4ctx.jdyn); mov(edx, dword[rdx]); mov(dword[rax], edx); } @@ -549,35 +551,36 @@ class BlockCompiler : public BaseXbyakRec CC_pars.clear(); } - void canonParam(const shil_opcode& op, const shil_param& prm, CanonicalParamType tp) { + void canonParam(const shil_opcode& op, const shil_param *prm, CanonicalParamType tp) { switch (tp) { case CPT_u32: case CPT_ptr: case CPT_f32: + case CPT_sh4ctx: { - CC_PS t = { tp, &prm }; + CC_PS t = { tp, prm }; CC_pars.push_back(t); + break; } - break; // store from EAX case CPT_u64rvL: case CPT_u32rv: mov(rcx, rax); - host_reg_to_shil_param(prm, ecx); + host_reg_to_shil_param(*prm, ecx); break; case CPT_u64rvH: // assuming CPT_u64rvL has just been called shr(rcx, 32); - host_reg_to_shil_param(prm, ecx); + host_reg_to_shil_param(*prm, ecx); break; // store from xmm0 case CPT_f32rv: - host_reg_to_shil_param(prm, xmm0); + host_reg_to_shil_param(*prm, xmm0); break; } } @@ -605,7 +608,11 @@ class BlockCompiler : public BaseXbyakRec //push the ptr itself case CPT_ptr: verify(prm.is_reg()); - mov(call_regs64[regused++], (size_t)prm.reg_ptr()); + mov(call_regs64[regused++], (size_t)prm.reg_ptr(sh4ctx)); + break; + + case CPT_sh4ctx: + mov(call_regs64[regused++], (uintptr_t)&sh4ctx); break; default: @@ -620,9 +627,9 @@ class BlockCompiler : public BaseXbyakRec const shil_param& prm = *ccParam.prm; if (ccParam.type == CPT_ptr && prm.count() == 2 && regalloc.IsAllocf(prm) && (op.rd._reg == prm._reg || op.rd2._reg == prm._reg)) { // fsca rd param is a pointer to a 64-bit reg so reload the regs if allocated - mov(rax, (size_t)GetRegPtr(prm._reg)); + mov(rax, (size_t)GetRegPtr(sh4ctx, prm._reg)); movss(regalloc.MapXRegister(prm, 0), dword[rax]); - mov(rax, (size_t)GetRegPtr(prm._reg + 1)); + mov(rax, (size_t)GetRegPtr(sh4ctx, prm._reg + 1)); movss(regalloc.MapXRegister(prm, 1), dword[rax]); } } @@ -631,22 +638,22 @@ class BlockCompiler : public BaseXbyakRec void RegPreload(u32 reg, Xbyak::Operand::Code nreg) { - mov(rax, (size_t)GetRegPtr(reg)); + mov(rax, (size_t)GetRegPtr(sh4ctx, reg)); mov(Xbyak::Reg32(nreg), dword[rax]); } void RegWriteback(u32 reg, Xbyak::Operand::Code nreg) { - mov(rax, (size_t)GetRegPtr(reg)); + mov(rax, (size_t)GetRegPtr(sh4ctx, reg)); mov(dword[rax], Xbyak::Reg32(nreg)); } void RegPreload_FPU(u32 reg, s8 nreg) { - mov(rax, (size_t)GetRegPtr(reg)); + mov(rax, (size_t)GetRegPtr(sh4ctx, reg)); movss(Xbyak::Xmm(nreg), dword[rax]); } void RegWriteback_FPU(u32 reg, s8 nreg) { - mov(rax, (size_t)GetRegPtr(reg)); + mov(rax, (size_t)GetRegPtr(sh4ctx, reg)); movss(dword[rax], Xbyak::Xmm(nreg)); } @@ -682,7 +689,7 @@ class BlockCompiler : public BaseXbyakRec Xbyak::Label run_loop; L(run_loop); Xbyak::Label end_run_loop; - mov(rax, (size_t)&p_sh4rcb->cntx.CpuRunning); + mov(rax, (size_t)&sh4ctx.CpuRunning); mov(edx, dword[rax]); test(edx, edx); @@ -691,11 +698,11 @@ class BlockCompiler : public BaseXbyakRec //slice_loop: Xbyak::Label slice_loop; L(slice_loop); - mov(rax, (size_t)&p_sh4rcb->cntx.pc); + mov(rax, (size_t)&sh4ctx.pc); mov(call_regs[0], dword[rax]); call(bm_GetCodeByVAddr); call(rax); - mov(rax, (uintptr_t)&p_sh4rcb->cntx.cycle_counter); + mov(rax, (uintptr_t)&sh4ctx.cycle_counter); mov(ecx, dword[rax]); test(ecx, ecx); jg(slice_loop); @@ -860,7 +867,7 @@ class BlockCompiler : public BaseXbyakRec else { movsx(eax, byte[rax]); - mov(rcx, (uintptr_t)op.rd.reg_ptr()); + mov(rcx, (uintptr_t)op.rd.reg_ptr(sh4ctx)); mov(dword[rcx], eax); } break; @@ -871,7 +878,7 @@ class BlockCompiler : public BaseXbyakRec else { movsx(eax, word[rax]); - mov(rcx, (uintptr_t)op.rd.reg_ptr()); + mov(rcx, (uintptr_t)op.rd.reg_ptr(sh4ctx)); mov(dword[rcx], eax); } break; @@ -884,7 +891,7 @@ class BlockCompiler : public BaseXbyakRec else { mov(eax, dword[rax]); - mov(rcx, (uintptr_t)op.rd.reg_ptr()); + mov(rcx, (uintptr_t)op.rd.reg_ptr(sh4ctx)); mov(dword[rcx], eax); } break; @@ -892,7 +899,7 @@ class BlockCompiler : public BaseXbyakRec case 8: #if ALLOC_F64 == false mov(rcx, qword[rax]); - mov(rax, (uintptr_t)op.rd.reg_ptr()); + mov(rax, (uintptr_t)op.rd.reg_ptr(sh4ctx)); mov(qword[rax], rcx); #else movd(regalloc.MapXRegister(op.rd, 0), dword[rax]); @@ -914,7 +921,7 @@ class BlockCompiler : public BaseXbyakRec mov(call_regs[0], addr); GenCall((void (*)())ptr); #if ALLOC_F64 == false - mov(rcx, (size_t)op.rd.reg_ptr()); + mov(rcx, (size_t)op.rd.reg_ptr(sh4ctx)); mov(dword[rcx], eax); #else movd(regalloc.MapXRegister(op.rd, 0), eax); @@ -923,7 +930,7 @@ class BlockCompiler : public BaseXbyakRec mov(call_regs[0], addr + 4); GenCall((void (*)())ptr); #if ALLOC_F64 == false - mov(rcx, (size_t)op.rd.reg_ptr() + 4); + mov(rcx, (size_t)op.rd.reg_ptr(sh4ctx) + 4); mov(dword[rcx], eax); #else movd(regalloc.MapXRegister(op.rd, 1), eax); @@ -983,7 +990,7 @@ class BlockCompiler : public BaseXbyakRec mov(byte[rax], (u8)op.rs2._imm); else { - mov(rcx, (uintptr_t)op.rs2.reg_ptr()); + mov(rcx, (uintptr_t)op.rs2.reg_ptr(sh4ctx)); mov(cl, byte[rcx]); mov(byte[rax], cl); } @@ -996,7 +1003,7 @@ class BlockCompiler : public BaseXbyakRec mov(word[rax], (u16)op.rs2._imm); else { - mov(rcx, (uintptr_t)op.rs2.reg_ptr()); + mov(rcx, (uintptr_t)op.rs2.reg_ptr(sh4ctx)); mov(cx, word[rcx]); mov(word[rax], cx); } @@ -1011,7 +1018,7 @@ class BlockCompiler : public BaseXbyakRec mov(dword[rax], op.rs2._imm); else { - mov(rcx, (uintptr_t)op.rs2.reg_ptr()); + mov(rcx, (uintptr_t)op.rs2.reg_ptr(sh4ctx)); mov(ecx, dword[rcx]); mov(dword[rax], ecx); } @@ -1019,7 +1026,7 @@ class BlockCompiler : public BaseXbyakRec case 8: #if ALLOC_F64 == false - mov(rcx, (uintptr_t)op.rs2.reg_ptr()); + mov(rcx, (uintptr_t)op.rs2.reg_ptr(sh4ctx)); mov(rcx, qword[rcx]); mov(qword[rax], rcx); #else @@ -1056,7 +1063,7 @@ class BlockCompiler : public BaseXbyakRec // same at compile and run times. if (mmu_enabled()) { - mov(rax, (uintptr_t)&next_pc); + mov(rax, (uintptr_t)&sh4ctx.pc); cmp(dword[rax], block->vaddr); jne(reinterpret_cast(&ngen_blockcheckfail)); } @@ -1159,7 +1166,7 @@ class BlockCompiler : public BaseXbyakRec shr(r9d, 26); cmp(r9d, 0x38); jne(no_sqw); - mov(rax, (uintptr_t)p_sh4rcb->sq_buffer); + mov(rax, (uintptr_t)sh4ctx.sq_buffer); and_(call_regs[0], 0x3F); if (size == MemSize::S32) @@ -1316,7 +1323,7 @@ class X64Dynarec : public Sh4Dynarec size_t protSize = codeBuffer->getFreeSpace(); virtmem::jit_set_exec(protStart, protSize, false); - ccCompiler = new BlockCompiler(*codeBuffer); + ccCompiler = new BlockCompiler(*sh4ctx, *codeBuffer); try { ccCompiler->compile(block, smc_checks, optimise); } catch (const Xbyak::Error& e) { @@ -1327,8 +1334,9 @@ class X64Dynarec : public Sh4Dynarec virtmem::jit_set_exec(protStart, protSize, true); } - void init(Sh4CodeBuffer& codeBuffer) override + void init(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) override { + this->sh4ctx = &sh4ctx; this->codeBuffer = &codeBuffer; } @@ -1348,7 +1356,7 @@ class X64Dynarec : public Sh4Dynarec } void canonParam(const shil_opcode* op, const shil_param* par, CanonicalParamType tp) override { - ccCompiler->canonParam(*op, *par, tp); + ccCompiler->canonParam(*op, par, tp); } void canonCall(const shil_opcode* op, void* function) override { @@ -1368,7 +1376,7 @@ class X64Dynarec : public Sh4Dynarec virtmem::jit_set_exec(protStart, protSize, false); u8 *retAddr = *(u8 **)context.rsp - 5; - BlockCompiler compiler(*codeBuffer, retAddr); + BlockCompiler compiler(*sh4ctx, *codeBuffer, retAddr); bool rc = false; try { rc = compiler.rewriteMemAccess(context); @@ -1395,7 +1403,7 @@ class X64Dynarec : public Sh4Dynarec size_t protSize = codeBuffer->getFreeSpace(); virtmem::jit_set_exec(protStart, protSize, false); - BlockCompiler compiler(*codeBuffer); + BlockCompiler compiler(*sh4ctx, *codeBuffer); try { compiler.genMainloop(); } catch (const Xbyak::Error& e) { @@ -1405,6 +1413,7 @@ class X64Dynarec : public Sh4Dynarec } private: + Sh4Context *sh4ctx = nullptr; Sh4CodeBuffer *codeBuffer = nullptr; BlockCompiler *ccCompiler = nullptr; }; diff --git a/core/rec-x64/xbyak_base.h b/core/rec-x64/xbyak_base.h index 044795f74..cdbc01ad7 100644 --- a/core/rec-x64/xbyak_base.h +++ b/core/rec-x64/xbyak_base.h @@ -28,8 +28,9 @@ template class BaseXbyakRec : public Xbyak::CodeGenerator { protected: - BaseXbyakRec(Sh4CodeBuffer& codeBuffer) : BaseXbyakRec(codeBuffer, (u8 *)codeBuffer.get()) { } - BaseXbyakRec(Sh4CodeBuffer& codeBuffer, u8 *code_ptr) : Xbyak::CodeGenerator(codeBuffer.getFreeSpace(), code_ptr), codeBuffer(codeBuffer) { } + BaseXbyakRec(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) : BaseXbyakRec(sh4ctx, codeBuffer, (u8 *)codeBuffer.get()) { } + BaseXbyakRec(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer, u8 *code_ptr) + : Xbyak::CodeGenerator(codeBuffer.getFreeSpace(), code_ptr), sh4ctx(sh4ctx), codeBuffer(codeBuffer) { } using BinaryOp = void (BaseXbyakRec::*)(const Xbyak::Operand&, const Xbyak::Operand&); using BinaryFOp = void (BaseXbyakRec::*)(const Xbyak::Xmm&, const Xbyak::Operand&); @@ -582,7 +583,7 @@ class BaseXbyakRec : public Xbyak::CodeGenerator mov(rcx, (uintptr_t)&sin_table); mov(rcx, qword[rcx + rax * 8]); #if ALLOC_F64 == false - mov(rdx, (uintptr_t)op.rd.reg_ptr()); + mov(rdx, (uintptr_t)op.rd.reg_ptr(sh4ctx)); mov(qword[rdx], rcx); #else movd(mapXRegister(op.rd, 0), ecx); @@ -600,8 +601,8 @@ class BaseXbyakRec : public Xbyak::CodeGenerator verify(!isAllocAny(op.rd)); mov(ecx, dword[(size_t)&sin_table + eax * 8]); mov(edx, dword[(size_t)&sin_table[0].u[1] + eax * 8]); - mov(dword[op.rd.reg_ptr()], ecx); - mov(dword[op.rd.reg_ptr() + 1], edx); + mov(dword[op.rd.reg_ptr(sh4ctx)], ecx); + mov(dword[op.rd.reg_ptr(sh4ctx) + 1], edx); #endif } break; @@ -679,13 +680,13 @@ class BaseXbyakRec : public Xbyak::CodeGenerator if (ArchX64) { #ifndef XBYAK32 - mov(rax, (size_t)param.reg_ptr()); + mov(rax, (size_t)param.reg_ptr(sh4ctx)); mov(reg.cvt32(), dword[rax]); #endif } else { - mov(reg.cvt32(), dword[param.reg_ptr()]); + mov(reg.cvt32(), dword[param.reg_ptr(sh4ctx)]); } } } @@ -702,7 +703,7 @@ class BaseXbyakRec : public Xbyak::CodeGenerator if (ArchX64) { #ifndef XBYAK32 - mov(rax, (size_t)param.reg_ptr()); + mov(rax, (size_t)param.reg_ptr(sh4ctx)); if (!reg.isXMM()) mov(reg.cvt32(), dword[rax]); else @@ -712,9 +713,9 @@ class BaseXbyakRec : public Xbyak::CodeGenerator else { if (!reg.isXMM()) - mov(reg.cvt32(), dword[param.reg_ptr()]); + mov(reg.cvt32(), dword[param.reg_ptr(sh4ctx)]); else - movss((const Xbyak::Xmm &)reg, dword[param.reg_ptr()]); + movss((const Xbyak::Xmm &)reg, dword[param.reg_ptr(sh4ctx)]); } } } @@ -756,7 +757,7 @@ class BaseXbyakRec : public Xbyak::CodeGenerator if (ArchX64) { #ifndef XBYAK32 - mov(rax, (size_t)param.reg_ptr()); + mov(rax, (size_t)param.reg_ptr(sh4ctx)); if (!reg.isXMM()) mov(dword[rax], reg.cvt32()); else @@ -766,13 +767,14 @@ class BaseXbyakRec : public Xbyak::CodeGenerator else { if (!reg.isXMM()) - mov(dword[param.reg_ptr()], reg.cvt32()); + mov(dword[param.reg_ptr(sh4ctx)], reg.cvt32()); else - movss(dword[param.reg_ptr()], (const Xbyak::Xmm &)reg); + movss(dword[param.reg_ptr(sh4ctx)], (const Xbyak::Xmm &)reg); } } } + Sh4Context& sh4ctx; Sh4CodeBuffer& codeBuffer; private: diff --git a/core/rec-x86/rec_x86.cpp b/core/rec-x86/rec_x86.cpp index a8bc5fc09..6d422fc89 100644 --- a/core/rec-x86/rec_x86.cpp +++ b/core/rec-x86/rec_x86.cpp @@ -22,7 +22,6 @@ #include "rec_x86.h" #include "hw/sh4/sh4_core.h" -#include "hw/sh4/sh4_interpreter.h" #include "hw/sh4/sh4_interrupts.h" #include "hw/sh4/sh4_mem.h" #include "hw/mem/addrspace.h" @@ -70,11 +69,13 @@ void X86RegAlloc::Writeback_FPU(u32 reg, s8 nreg) struct DynaRBI : RuntimeBlockInfo { - DynaRBI(Sh4CodeBuffer *codeBuffer) : codeBuffer(codeBuffer) {} + DynaRBI(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) + : sh4ctx(sh4ctx), codeBuffer(codeBuffer) {} u32 Relink() override; private: - Sh4CodeBuffer *codeBuffer; + Sh4Context& sh4ctx; + Sh4CodeBuffer& codeBuffer; }; @@ -109,26 +110,26 @@ void X86Compiler::compile(RuntimeBlockInfo* block, bool force_checks, bool optim if (mmu_enabled() && block->has_fpu_op) { Xbyak::Label fpu_enabled; - mov(eax, dword[&sr.status]); + mov(eax, dword[&sh4ctx.sr.status]); test(eax, 0x8000); // test SR.FD bit jz(fpu_enabled); push(Sh4Ex_FpuDisabled); // exception code push(block->vaddr); // pc call((void (*)())Do_Exception); add(esp, 8); - mov(ecx, dword[&next_pc]); + mov(ecx, dword[&sh4ctx.pc]); jmp((const void *)no_update); L(fpu_enabled); } - mov(eax, dword[&Sh4cntx.cycle_counter]); + mov(eax, dword[&sh4ctx.cycle_counter]); test(eax, eax); Xbyak::Label no_up; jg(no_up); mov(ecx, block->vaddr); call((const void *)intc_sched); L(no_up); - sub(dword[&Sh4cntx.cycle_counter], block->guest_cycles); + sub(dword[&sh4ctx.cycle_counter], block->guest_cycles); regalloc.doAlloc(block); @@ -181,7 +182,7 @@ u32 X86Compiler::relinkBlock(RuntimeBlockInfo* block) mov(ecx, block->NextBlock); - cmp(dword[GetRegPtr(block->has_jcond ? reg_pc_dyn : reg_sr_T)], (u32)block->BlockType & 1); + cmp(dword[block->has_jcond ? &sh4ctx.jdyn : &sh4ctx.sr.T], (u32)block->BlockType & 1); Xbyak::Label branch_not_taken; jne(branch_not_taken, T_SHORT); @@ -194,7 +195,7 @@ u32 X86Compiler::relinkBlock(RuntimeBlockInfo* block) case BET_DynamicCall: case BET_DynamicRet: //next_pc = *jdyn; - mov(ecx, dword[GetRegPtr(reg_pc_dyn)]); + mov(ecx, dword[&sh4ctx.jdyn]); break; case BET_DynamicIntr: @@ -202,16 +203,16 @@ u32 X86Compiler::relinkBlock(RuntimeBlockInfo* block) if (block->BlockType == BET_DynamicIntr) { //next_pc = *jdyn; - mov(ecx, dword[GetRegPtr(reg_pc_dyn)]); - mov(dword[&next_pc], ecx); + mov(ecx, dword[&sh4ctx.jdyn]); + mov(dword[&sh4ctx.pc], ecx); } else { //next_pc = next_pc_value; - mov(dword[&next_pc], block->NextBlock); + mov(dword[&sh4ctx.pc], block->NextBlock); } call(UpdateINTC); - mov(ecx, dword[&next_pc]); + mov(ecx, dword[&sh4ctx.pc]); break; default: @@ -226,7 +227,7 @@ u32 X86Compiler::relinkBlock(RuntimeBlockInfo* block) { case BET_Cond_0: case BET_Cond_1: - cmp(dword[GetRegPtr(block->has_jcond ? reg_pc_dyn : reg_sr_T)], (u32)block->BlockType & 1); + cmp(dword[block->has_jcond ? &sh4ctx.jdyn : &sh4ctx.sr.T], (u32)block->BlockType & 1); if (mmu_enabled()) { @@ -268,7 +269,7 @@ u32 X86Compiler::relinkBlock(RuntimeBlockInfo* block) case BET_DynamicRet: case BET_DynamicCall: case BET_DynamicJump: - mov(ecx, dword[GetRegPtr(reg_pc_dyn)]); + mov(ecx, dword[&sh4ctx.jdyn]); jmp((const void *)no_update); break; @@ -298,16 +299,16 @@ u32 X86Compiler::relinkBlock(RuntimeBlockInfo* block) case BET_DynamicIntr: if (block->BlockType == BET_StaticIntr) { - mov(dword[&next_pc], block->NextBlock); + mov(dword[&sh4ctx.pc], block->NextBlock); } else { - mov(eax, dword[GetRegPtr(reg_pc_dyn)]); - mov(dword[&next_pc], eax); + mov(eax, dword[&sh4ctx.jdyn]); + mov(dword[&sh4ctx.pc], eax); } call(UpdateINTC); - mov(ecx, dword[&next_pc]); + mov(ecx, dword[&sh4ctx.pc]); jmp((const void *)no_update); break; @@ -324,7 +325,7 @@ u32 X86Compiler::relinkBlock(RuntimeBlockInfo* block) u32 DynaRBI::Relink() { - X86Compiler *compiler = new X86Compiler(*codeBuffer, (u8*)code + relink_offset); + X86Compiler *compiler = new X86Compiler(sh4ctx, codeBuffer, (u8*)code + relink_offset); u32 codeSize = compiler->relinkBlock(this); delete compiler; @@ -359,7 +360,13 @@ void X86Compiler::ngen_CC_param(const shil_opcode& op, const shil_param& param, //push the ptr itself case CPT_ptr: verify(param.is_reg()); - push((uintptr_t)param.reg_ptr()); + push((uintptr_t)param.reg_ptr(sh4ctx)); + CC_stackSize += 4; + unwinder.allocStackPtr(getCurr(), 4); + break; + + case CPT_sh4ctx: + push((uintptr_t)&sh4ctx); CC_stackSize += 4; unwinder.allocStackPtr(getCurr(), 4); break; @@ -377,8 +384,8 @@ void X86Compiler::ngen_CC_param(const shil_opcode& op, const shil_param& param, // store from ST(0) case CPT_f32rv: - fstp(dword[param.reg_ptr()]); - movss(regalloc.MapXRegister(param), dword[param.reg_ptr()]); + fstp(dword[param.reg_ptr(sh4ctx)]); + movss(regalloc.MapXRegister(param), dword[param.reg_ptr(sh4ctx)]); break; } } @@ -440,33 +447,33 @@ void X86Compiler::genMainloop() Xbyak::Label longjmpLabel; L(longjmpLabel); - mov(ecx, dword[&Sh4cntx.pc]); + mov(ecx, dword[&sh4ctx.pc]); //next_pc _MUST_ be on ecx Xbyak::Label cleanup; //no_update: Xbyak::Label no_updateLabel; L(no_updateLabel); - mov(edx, dword[&Sh4cntx.CpuRunning]); + mov(edx, dword[&sh4ctx.CpuRunning]); cmp(edx, 0); jz(cleanup); if (!mmu_enabled()) { mov(esi, ecx); // save sh4 pc in ESI, used below if FPCB is still empty for this address - mov(eax, (size_t)&p_sh4rcb->fpcb[0]); + mov(eax, (uintptr_t)&sh4ctx + sizeof(Sh4Context) - sizeof(Sh4RCB) + offsetof(Sh4RCB, fpcb)); // address of fpcb[0] and_(ecx, RAM_SIZE_MAX - 2); jmp(dword[eax + ecx * 2]); } else { - mov(dword[&next_pc], ecx); + mov(dword[&sh4ctx.pc], ecx); call((void *)bm_GetCodeByVAddr); jmp(eax); } //cleanup: L(cleanup); - mov(dword[&next_pc], ecx); + mov(dword[&sh4ctx.pc], ecx); #ifndef _WIN32 // 16-byte alignment add(esp, 12); @@ -482,7 +489,7 @@ void X86Compiler::genMainloop() Xbyak::Label do_iter; L(do_iter); add(esp, 4); // pop intc_sched() return address - mov(ecx, dword[&Sh4cntx.pc]); + mov(ecx, dword[sh4ctx.pc]); jmp(no_updateLabel); //ngen_LinkBlock_Shared_stub: @@ -504,8 +511,8 @@ void X86Compiler::genMainloop() unwinder.endProlog(0); Xbyak::Label intc_schedLabel; L(intc_schedLabel); - add(dword[&Sh4cntx.cycle_counter], SH4_TIMESLICE); - mov(dword[&Sh4cntx.pc], ecx); + add(dword[&sh4ctx.cycle_counter], SH4_TIMESLICE); + mov(dword[&sh4ctx.pc], ecx); call((void *)UpdateSystem_INTC); cmp(eax, 0); jnz(do_iter); @@ -526,7 +533,7 @@ void X86Compiler::genMainloop() //ngen_LinkBlock_Generic_stub: Xbyak::Label ngen_LinkBlock_Generic_label; L(ngen_LinkBlock_Generic_label); - mov(edx, dword[&Sh4cntx.jdyn]); + mov(edx, dword[&sh4ctx.jdyn]); jmp(ngen_LinkBlock_Shared_stub); genMemHandlers(); @@ -569,7 +576,7 @@ void X86Compiler::genMainloop() Xbyak::Label jumpblockLabel; cmp(eax, 0); jne(jumpblockLabel); - mov(ecx, dword[&next_pc]); + mov(ecx, dword[&sh4ctx.pc]); jmp(no_updateLabel); L(jumpblockLabel); } @@ -620,7 +627,7 @@ bool X86Compiler::genReadMemImmediate(const shil_opcode& op, RuntimeBlockInfo* b else { movsx(eax, byte[ptr]); - mov(dword[op.rd.reg_ptr()], eax); + mov(dword[op.rd.reg_ptr(sh4ctx)], eax); } break; @@ -630,7 +637,7 @@ bool X86Compiler::genReadMemImmediate(const shil_opcode& op, RuntimeBlockInfo* b else { movsx(eax, word[ptr]); - mov(dword[op.rd.reg_ptr()], eax); + mov(dword[op.rd.reg_ptr(sh4ctx)], eax); } break; @@ -642,7 +649,7 @@ bool X86Compiler::genReadMemImmediate(const shil_opcode& op, RuntimeBlockInfo* b else { mov(eax, dword[ptr]); - mov(dword[op.rd.reg_ptr()], eax); + mov(dword[op.rd.reg_ptr(sh4ctx)], eax); } break; @@ -655,7 +662,7 @@ bool X86Compiler::genReadMemImmediate(const shil_opcode& op, RuntimeBlockInfo* b else { movq(xmm0, qword[ptr]); - movq(qword[op.rd.reg_ptr()], xmm0); + movq(qword[op.rd.reg_ptr(sh4ctx)], xmm0); } break; @@ -674,11 +681,11 @@ bool X86Compiler::genReadMemImmediate(const shil_opcode& op, RuntimeBlockInfo* b // Need to call the handler twice mov(ecx, addr); genCall((void (DYNACALL *)())ptr); - mov(dword[op.rd.reg_ptr()], eax); + mov(dword[op.rd.reg_ptr(sh4ctx)], eax); mov(ecx, addr + 4); genCall((void (DYNACALL *)())ptr); - mov(dword[op.rd.reg_ptr() + 1], eax); + mov(dword[op.rd.reg_ptr(sh4ctx) + 1], eax); } else { @@ -742,7 +749,7 @@ bool X86Compiler::genWriteMemImmediate(const shil_opcode& op, RuntimeBlockInfo* mov(byte[ptr], (u8)op.rs2.imm_value()); else { - mov(al, byte[op.rs2.reg_ptr()]); + mov(al, byte[op.rs2.reg_ptr(sh4ctx)]); mov(byte[ptr], al); } break; @@ -754,7 +761,7 @@ bool X86Compiler::genWriteMemImmediate(const shil_opcode& op, RuntimeBlockInfo* mov(word[ptr], (u16)op.rs2.imm_value()); else { - mov(cx, word[op.rs2.reg_ptr()]); + mov(cx, word[op.rs2.reg_ptr(sh4ctx)]); mov(word[ptr], cx); } break; @@ -768,7 +775,7 @@ bool X86Compiler::genWriteMemImmediate(const shil_opcode& op, RuntimeBlockInfo* mov(dword[ptr], op.rs2.imm_value()); else { - mov(ecx, dword[op.rs2.reg_ptr()]); + mov(ecx, dword[op.rs2.reg_ptr(sh4ctx)]); mov(dword[ptr], ecx); } break; @@ -781,7 +788,7 @@ bool X86Compiler::genWriteMemImmediate(const shil_opcode& op, RuntimeBlockInfo* } else { - movq(xmm0, qword[op.rs2.reg_ptr()]); + movq(xmm0, qword[op.rs2.reg_ptr(sh4ctx)]); movq(qword[ptr], xmm0); } break; @@ -810,7 +817,7 @@ void X86Compiler::checkBlock(bool smc_checks, RuntimeBlockInfo* block) if (mmu_enabled()) { - mov(eax, dword[&next_pc]); + mov(eax, dword[&sh4ctx.pc]); cmp(eax, block->vaddr); jne(reinterpret_cast(ngen_blockcheckfail)); } @@ -843,8 +850,9 @@ class X86Dynarec : public Sh4Dynarec sh4Dynarec = this; } - void init(Sh4CodeBuffer& codeBuffer) override + void init(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) override { + this->sh4ctx = &sh4ctx; this->codeBuffer = &codeBuffer; } @@ -858,7 +866,7 @@ class X86Dynarec : public Sh4Dynarec if (::mainloop != nullptr) return; - compiler = new X86Compiler(*codeBuffer); + compiler = new X86Compiler(*sh4ctx, *codeBuffer); try { compiler->genMainloop(); @@ -877,10 +885,10 @@ class X86Dynarec : public Sh4Dynarec ::mainloop = nullptr; unwinder.clear(); - if (p_sh4rcb->cntx.CpuRunning) + if (sh4ctx->CpuRunning) { // Force the dynarec out of mainloop() to regenerate it - p_sh4rcb->cntx.CpuRunning = 0; + sh4ctx->CpuRunning = 0; restarting = true; } else @@ -890,7 +898,7 @@ class X86Dynarec : public Sh4Dynarec RuntimeBlockInfo* allocateBlock() override { generate_mainloop(); - return new DynaRBI(codeBuffer); + return new DynaRBI(*sh4ctx, *codeBuffer); } void mainloop(void* v_cntx) override @@ -912,7 +920,7 @@ class X86Dynarec : public Sh4Dynarec void compile(RuntimeBlockInfo* block, bool smc_checks, bool optimise) override { - compiler = new X86Compiler(*codeBuffer); + compiler = new X86Compiler(*sh4ctx, *codeBuffer); try { compiler->compile(block, smc_checks, optimise); @@ -929,7 +937,7 @@ class X86Dynarec : public Sh4Dynarec // init() not called yet return false; u8 *rewriteAddr = *(u8 **)context.esp - 5; - X86Compiler *compiler = new X86Compiler(*codeBuffer, rewriteAddr); + X86Compiler *compiler = new X86Compiler(*sh4ctx, *codeBuffer, rewriteAddr); bool rv = compiler->rewriteMemAccess(context); delete compiler; @@ -957,6 +965,7 @@ class X86Dynarec : public Sh4Dynarec } private: + Sh4Context *sh4ctx = nullptr; Sh4CodeBuffer *codeBuffer = nullptr; X86Compiler *compiler = nullptr; bool restarting = false; diff --git a/core/rec-x86/rec_x86.h b/core/rec-x86/rec_x86.h index 8eecf77fc..cfd41096b 100644 --- a/core/rec-x86/rec_x86.h +++ b/core/rec-x86/rec_x86.h @@ -31,8 +31,8 @@ class X86Compiler : public BaseXbyakRec public: using BaseCompiler = BaseXbyakRec; - X86Compiler(Sh4CodeBuffer& codeBuffer) : BaseCompiler(codeBuffer), regalloc(this) { } - X86Compiler(Sh4CodeBuffer& codeBuffer, u8 *code_ptr) : BaseCompiler(codeBuffer, code_ptr), regalloc(this) { } + X86Compiler(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer) : BaseCompiler(sh4ctx, codeBuffer), regalloc(this) { } + X86Compiler(Sh4Context& sh4ctx, Sh4CodeBuffer& codeBuffer, u8 *code_ptr) : BaseCompiler(sh4ctx, codeBuffer, code_ptr), regalloc(this) { } void compile(RuntimeBlockInfo* block, bool force_checks, bool optimise); @@ -50,22 +50,22 @@ class X86Compiler : public BaseXbyakRec void regPreload(u32 reg, Xbyak::Operand::Code nreg) { DEBUG_LOG(DYNAREC, "RegPreload reg %d -> %s", reg, Xbyak::Reg32(nreg).toString()); - mov(Xbyak::Reg32(nreg), dword[GetRegPtr(reg)]); + mov(Xbyak::Reg32(nreg), dword[GetRegPtr(sh4ctx, reg)]); } void regWriteback(u32 reg, Xbyak::Operand::Code nreg) { DEBUG_LOG(DYNAREC, "RegWriteback reg %d <- %s", reg, Xbyak::Reg32(nreg).toString()); - mov(dword[GetRegPtr(reg)], Xbyak::Reg32(nreg)); + mov(dword[GetRegPtr(sh4ctx, reg)], Xbyak::Reg32(nreg)); } void regPreload_FPU(u32 reg, s8 nreg) { DEBUG_LOG(DYNAREC, "RegPreload_FPU reg %d -> xmm%d", reg, nreg); - movss(Xbyak::Xmm(nreg), dword[GetRegPtr(reg)]); + movss(Xbyak::Xmm(nreg), dword[GetRegPtr(sh4ctx, reg)]); } void regWriteback_FPU(u32 reg, s8 nreg) { DEBUG_LOG(DYNAREC, "RegWriteback_FPU reg %d <- xmm%d", reg, nreg); - movss(dword[GetRegPtr(reg)], Xbyak::Xmm(nreg)); + movss(dword[GetRegPtr(sh4ctx, reg)], Xbyak::Xmm(nreg)); } void genMainloop(); diff --git a/core/rec-x86/x86_ops.cpp b/core/rec-x86/x86_ops.cpp index b6bfc2576..e539389fd 100644 --- a/core/rec-x86/x86_ops.cpp +++ b/core/rec-x86/x86_ops.cpp @@ -139,12 +139,12 @@ void X86Compiler::genMemHandlers() and_(ecx, 0x3F); if (size == MemSize::S32) - mov(dword[(size_t)p_sh4rcb->sq_buffer + ecx], edx); + mov(dword[(size_t)sh4ctx.sq_buffer + ecx], edx); else if (size >= MemSize::F32) { - movss(dword[(size_t)p_sh4rcb->sq_buffer + ecx], xmm0); + movss(dword[(size_t)sh4ctx.sq_buffer + ecx], xmm0); if (size == MemSize::F64) - movss(dword[((size_t)p_sh4rcb->sq_buffer + 4) + ecx], xmm1); + movss(dword[((size_t)sh4ctx.sq_buffer + 4) + ecx], xmm1); } ret(); L(no_sqw); @@ -283,7 +283,7 @@ void X86Compiler::genMmuLookup(RuntimeBlockInfo* block, const shil_opcode& op, u } [[noreturn]] -static void DYNACALL handle_sh4_exception(SH4ThrownException& ex, u32 pc) +static void DYNACALL handle_sh4_exception(Sh4Context *ctx, SH4ThrownException& ex, u32 pc) { if (pc & 1) { @@ -292,27 +292,27 @@ static void DYNACALL handle_sh4_exception(SH4ThrownException& ex, u32 pc) pc--; } Do_Exception(pc, ex.expEvn); - p_sh4rcb->cntx.cycle_counter += 4; // probably more is needed + ctx->cycle_counter += 4; // probably more is needed X86Compiler::handleException(); // not reached std::abort(); } -static void DYNACALL interpreter_fallback(u16 op, OpCallFP *oph, u32 pc) +static void DYNACALL interpreter_fallback(Sh4Context *ctx, u16 op, OpCallFP *oph, u32 pc) { try { - oph(op); + oph(ctx, op); } catch (SH4ThrownException& ex) { - handle_sh4_exception(ex, pc); + handle_sh4_exception(ctx, ex, pc); } } -static void DYNACALL do_sqw_mmu_no_ex(u32 addr, u32 pc) +static void DYNACALL do_sqw_mmu_no_ex(u32 addr, Sh4Context *ctx, u32 pc) { try { - do_sqw_mmu(addr); + ctx->doSqWrite(addr, ctx); } catch (SH4ThrownException& ex) { - handle_sh4_exception(ex, pc); + handle_sh4_exception(ctx, ex, pc); } } @@ -323,12 +323,13 @@ void X86Compiler::genOpcode(RuntimeBlockInfo* block, bool optimise, shil_opcode& case shop_ifb: if (mmu_enabled()) { - mov(edx, reinterpret_cast(*OpDesc[op.rs3._imm]->oph)); // op handler + push(reinterpret_cast(*OpDesc[op.rs3._imm]->oph)); // op handler push(block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc } if (op.rs1.is_imm() && op.rs1.imm_value()) - mov(dword[&next_pc], op.rs2.imm_value()); - mov(ecx, op.rs3.imm_value()); + mov(dword[&sh4ctx.pc], op.rs2.imm_value()); + mov(ecx, (uintptr_t)&sh4ctx); + mov(edx, op.rs3.imm_value()); if (!mmu_enabled()) genCall(OpDesc[op.rs3.imm_value()]->oph); else @@ -345,8 +346,8 @@ void X86Compiler::genOpcode(RuntimeBlockInfo* block, bool optimise, shil_opcode& movss(regalloc.MapXRegister(op.rd, 1), regalloc.MapXRegister(op.rs1, 1)); #else verify(!regalloc.IsAllocAny(op.rd)); - movq(xmm0, qword[op.rs1.reg_ptr()]); - movq(qword[op.rd.reg_ptr()], xmm0); + movq(xmm0, qword[op.rs1.reg_ptr(sh4ctx)]); + movq(qword[op.rd.reg_ptr(sh4ctx)], xmm0); #endif break; @@ -362,7 +363,7 @@ void X86Compiler::genOpcode(RuntimeBlockInfo* block, bool optimise, shil_opcode& else if (regalloc.IsAllocg(op.rs3)) add(ecx, regalloc.MapRegister(op.rs3)); else - add(ecx, dword[op.rs3.reg_ptr()]); + add(ecx, dword[op.rs3.reg_ptr(sh4ctx)]); } int memOpSize; @@ -409,8 +410,8 @@ void X86Compiler::genOpcode(RuntimeBlockInfo* block, bool optimise, shil_opcode& else { verify(!regalloc.IsAllocAny(op.rd)); - movss(dword[op.rd.reg_ptr()], xmm0); - movss(dword[op.rd.reg_ptr() + 1], xmm1); + movss(dword[op.rd.reg_ptr(sh4ctx)], xmm0); + movss(dword[op.rd.reg_ptr(sh4ctx) + 1], xmm1); } } } @@ -427,7 +428,7 @@ void X86Compiler::genOpcode(RuntimeBlockInfo* block, bool optimise, shil_opcode& else if (regalloc.IsAllocg(op.rs3)) add(ecx, regalloc.MapRegister(op.rs3)); else - add(ecx, dword[op.rs3.reg_ptr()]); + add(ecx, dword[op.rs3.reg_ptr(sh4ctx)]); } int memOpSize; @@ -463,8 +464,8 @@ void X86Compiler::genOpcode(RuntimeBlockInfo* block, bool optimise, shil_opcode& } else { - movd(xmm0, dword[op.rs2.reg_ptr()]); - movd(xmm1, dword[op.rs2.reg_ptr() + 1]); + movd(xmm0, dword[op.rs2.reg_ptr(sh4ctx)]); + movd(xmm1, dword[op.rs2.reg_ptr(sh4ctx) + 1]); } } const u8 *start = getCurr(); @@ -486,7 +487,8 @@ void X86Compiler::genOpcode(RuntimeBlockInfo* block, bool optimise, shil_opcode& genCallCdecl(UpdateSR); break; case shop_sync_fpscr: - genCallCdecl(UpdateFPSCR); + mov(ecx, (uintptr_t)&sh4ctx); + genCall(Sh4Context::UpdateFPSCR); break; case shop_pref: @@ -509,7 +511,7 @@ void X86Compiler::genOpcode(RuntimeBlockInfo* block, bool optimise, shil_opcode& } else { - mov(eax, dword[op.rs1.reg_ptr()]); + mov(eax, dword[op.rs1.reg_ptr(sh4ctx)]); rn = eax; } mov(ecx, rn); @@ -519,16 +521,16 @@ void X86Compiler::genOpcode(RuntimeBlockInfo* block, bool optimise, shil_opcode& mov(ecx, rn); } + mov(edx, (uintptr_t)&sh4ctx); if (mmu_enabled()) { - mov(edx, block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc + push(block->vaddr + op.guest_offs - (op.delay_slot ? 1 : 0)); // pc genCall(do_sqw_mmu_no_ex); } else { - mov(edx, (size_t)sh4rcb.sq_buffer); freezeXMM(); - call(dword[&do_sqw_nommu]); + call(dword[&sh4ctx.doSqWrite]); thawXMM(); } L(no_sqw); @@ -547,8 +549,8 @@ void X86Compiler::genOpcode(RuntimeBlockInfo* block, bool optimise, shil_opcode& break; case shop_frswap: - mov(eax, (uintptr_t)op.rs1.reg_ptr()); - mov(ecx, (uintptr_t)op.rd.reg_ptr()); + mov(eax, (uintptr_t)op.rs1.reg_ptr(sh4ctx)); + mov(ecx, (uintptr_t)op.rd.reg_ptr(sh4ctx)); for (int i = 0; i < 4; i++) { movaps(xmm0, xword[eax + (i * 16)]); diff --git a/core/reios/gdrom_hle.cpp b/core/reios/gdrom_hle.cpp index 1795ab471..17e2f5fc5 100644 --- a/core/reios/gdrom_hle.cpp +++ b/core/reios/gdrom_hle.cpp @@ -9,7 +9,6 @@ #include "hw/sh4/sh4_mem.h" #include "hw/sh4/sh4_sched.h" #include "hw/sh4/sh4_core.h" -#undef r #include "gdrom_hle.h" #include "hw/gdrom/gdromv3.h" @@ -23,8 +22,126 @@ #define SWAP32(a) ((((a) & 0xff) << 24) | (((a) & 0xff00) << 8) | (((a) >> 8) & 0xff00) | (((a) >> 24) & 0xff)) #define debugf(...) DEBUG_LOG(REIOS, __VA_ARGS__) +static void readSectors(u32 addr, u32 sector, u32 count, bool virtualAddr); -gdrom_hle_state_t gd_hle_state; +struct gdrom_hle_state_t +{ + gdrom_hle_state_t() : params{}, result{} {} + + u32 last_request_id = 0xFFFFFFFF; + u32 next_request_id = 2; + gd_return_value status = GDC_OK; + gd_command command = GDCC_NONE; + u32 params[4]; + u32 result[4]; + u32 cur_sector = 0; + u32 multi_read_sector = 0; + u32 multi_read_offset = 0; + u32 multi_read_count = 0; + u32 multi_read_total = 0; + u32 multi_callback = 0; + u32 multi_callback_arg = 0; + bool dma_trans_ended = false; + u64 xfer_end_time = 0; + + void Serialize(Serializer& ser) + { + ser << last_request_id; + ser << next_request_id; + ser << status; + ser << command; + ser << params; + ser << result; + ser << cur_sector; + ser << multi_read_sector; + ser << multi_read_offset; + ser << multi_read_count; + ser << multi_read_total; + ser << multi_callback; + ser << multi_callback_arg; + ser << dma_trans_ended; + ser << xfer_end_time; + + } + void Deserialize(Deserializer& deser) + { + deser >> last_request_id; + deser >> next_request_id; + deser >> status; + deser >> command; + deser >> params; + deser >> result; + deser >> cur_sector; + deser >> multi_read_sector; + deser >> multi_read_offset; + deser >> multi_read_count; + deser >> multi_read_total; + deser >> multi_callback; + deser >> multi_callback_arg; + deser >> dma_trans_ended; + deser >> xfer_end_time; + } +}; +static gdrom_hle_state_t gd_hle_state; + +static int schedId = -1; + +static int getGdromTicks() +{ + u32 len = gd_hle_state.multi_read_count * 2048; + if (len > 10240) + return 1000000; // Large transfers: GD-ROM transfer rate 1.8 MB/s + else + return len * 2; // Small transfers: Max G1 bus rate: 50 MHz x 16 bits +} + +static int schedCallback(int tag, int cycles, int jitter, void *arg) +{ + const u32 sect = std::min(gd_hle_state.multi_read_count, 5u); + readSectors(gd_hle_state.multi_read_offset, gd_hle_state.multi_read_sector, sect, false); + gd_hle_state.multi_read_count -= sect; + gd_hle_state.multi_read_sector += sect; + gd_hle_state.cur_sector = gd_hle_state.multi_read_sector; + gd_hle_state.multi_read_offset += sect * 2048; + + if (gd_hle_state.multi_read_count == 0) + { + gd_hle_state.result[3] = GDC_WAIT_INTERNAL; + SecNumber.Status = GD_STANDBY; + gd_hle_state.status = GDC_COMPLETE; + } + gd_hle_state.result[2] = (gd_hle_state.multi_read_total - gd_hle_state.multi_read_count) * 2048; + + return getGdromTicks(); +} + +void reios_init() { + if (schedId == -1) + schedId = sh4_sched_register(0, schedCallback); +} + +void reios_serialize(Serializer& ser) { + gd_hle_state.Serialize(ser); + sh4_sched_serialize(ser, schedId); +} + +void reios_deserialize(Deserializer& deser) +{ + gd_hle_state.Deserialize(deser); + if (deser.version() >= Deserializer::V54) + sh4_sched_deserialize(deser, schedId); +} + +void gdrom_hle_reset() { + gd_hle_state = {}; +} + +void reios_term() +{ + if (schedId != -1) + sh4_sched_unregister(schedId); + schedId = -1; +} static void GDROM_HLE_ReadSES() { @@ -65,19 +182,10 @@ static void GDROM_HLE_ReadTOC() WriteMem32(dest, toc[i]); } -template -static void read_sectors_to(u32 addr, u32 sector, u32 count) +static void readSectors(u32 addr, u32 sector, u32 count, bool virtualAddr) { gd_hle_state.cur_sector = sector + count - 1; - if (virtual_addr) - gd_hle_state.xfer_end_time = 0; - else if (count > 5 && !config::FastGDRomLoad) - // Large Transfers: GD-ROM rate (approx. 1.8 MB/s) - gd_hle_state.xfer_end_time = sh4_sched_now64() + (u64)count * 2048 * 1000000L / 10240; - else - // Small transfers: Max G1 bus rate: 50 MHz x 16 bits - gd_hle_state.xfer_end_time = sh4_sched_now64() + 5 * 2048 * 2; - if (!virtual_addr || !mmu_enabled()) + if (!virtualAddr || !mmu_enabled()) { u8 * pDst = GetMemPtr(addr, 0); @@ -95,7 +203,7 @@ static void read_sectors_to(u32 addr, u32 sector, u32 count) for (std::size_t i = 0; i < std::size(temp); i++) { - if (virtual_addr) + if (virtualAddr) WriteMem32(addr, temp[i]); else WriteMem32_nommu(addr, temp[i]); @@ -107,6 +215,12 @@ static void read_sectors_to(u32 addr, u32 sector, u32 count) } } +static void read_pio_sectors(u32 addr, u32 sector, u32 count) +{ + gd_hle_state.xfer_end_time = 0; + readSectors(addr, sector, count, true); +} + static void GDROM_HLE_ReadDMA() { u32 fad = gd_hle_state.params[0] & 0xffffff; @@ -116,7 +230,13 @@ static void GDROM_HLE_ReadDMA() debugf("GDROM: DMA READ Sector=%d, Num=%d, Buffer=%08x, zero=%x", fad, nsect, buffer, gd_hle_state.params[3]); - read_sectors_to(buffer, fad, nsect); + gd_hle_state.cur_sector = fad; + gd_hle_state.multi_read_sector = fad; + gd_hle_state.multi_read_offset = buffer; + gd_hle_state.multi_read_count = nsect; + gd_hle_state.multi_read_total = nsect; + sh4_sched_request(schedId, getGdromTicks()); + gd_hle_state.result[2] = 0; gd_hle_state.result[3] = 0; } @@ -130,7 +250,7 @@ static void GDROM_HLE_ReadPIO() debugf("GDROM: PIO READ Sector=%d, Num=%d, Buffer=%08x, SeekAhead=%x", fad, nsect, buffer, gd_hle_state.params[3]); - read_sectors_to(buffer, fad, nsect); + read_pio_sectors(buffer, fad, nsect); gd_hle_state.result[2] = nsect * 2048; gd_hle_state.result[3] = 0; } @@ -293,19 +413,9 @@ static void GD_HLE_Command(gd_command cc) case GDCC_DMAREAD: cdda.status = cdda_t::NoInfo; - if (gd_hle_state.xfer_end_time == 0) + if (gd_hle_state.multi_read_count == 0) GDROM_HLE_ReadDMA(); - if (gd_hle_state.xfer_end_time > 0) - { - if (gd_hle_state.xfer_end_time > sh4_sched_now64()) - return; - gd_hle_state.xfer_end_time = 0; - } - gd_hle_state.result[2] = gd_hle_state.params[1] * 2048; - gd_hle_state.result[3] = 0; - SecNumber.Status = GD_PAUSE; - break; - + return; case GDCC_PLAY2: { diff --git a/core/reios/gdrom_hle.h b/core/reios/gdrom_hle.h index 2f5d98785..acc9e22ca 100644 --- a/core/reios/gdrom_hle.h +++ b/core/reios/gdrom_hle.h @@ -1,5 +1,6 @@ #pragma once #include "serialize.h" +#include "cfg/option.h" #define SYSCALL_GDROM 0x00 @@ -69,9 +70,8 @@ enum misc_command { MISC_SETVECTOR }; -void gdrom_hle_init(); -void gdrom_hle_term(); void gdrom_hle_op(); +void gdrom_hle_reset(); typedef enum : int32_t { GDC_ERR = -1, @@ -84,66 +84,6 @@ typedef enum : int32_t { GDC_RET2 } gd_return_value; -struct gdrom_hle_state_t -{ - gdrom_hle_state_t() : params{}, result{} {} - - u32 last_request_id = 0xFFFFFFFF; - u32 next_request_id = 2; - gd_return_value status = GDC_OK; - gd_command command = GDCC_NONE; - u32 params[4]; - u32 result[4]; - u32 cur_sector = 0; - u32 multi_read_sector = 0; - u32 multi_read_offset = 0; - u32 multi_read_count = 0; - u32 multi_read_total = 0; - u32 multi_callback = 0; - u32 multi_callback_arg = 0; - bool dma_trans_ended = false; - u64 xfer_end_time = 0; - - void Serialize(Serializer& ser) - { - ser << last_request_id; - ser << next_request_id; - ser << status; - ser << command; - ser << params; - ser << result; - ser << cur_sector; - ser << multi_read_sector; - ser << multi_read_offset; - ser << multi_read_count; - ser << multi_read_total; - ser << multi_callback; - ser << multi_callback_arg; - ser << dma_trans_ended; - ser << xfer_end_time; - - } - void Deserialize(Deserializer& deser) - { - deser >> last_request_id; - deser >> next_request_id; - deser >> status; - deser >> command; - deser >> params; - deser >> result; - deser >> cur_sector; - deser >> multi_read_sector; - deser >> multi_read_offset; - deser >> multi_read_count; - deser >> multi_read_total; - deser >> multi_callback; - deser >> multi_callback_arg; - deser >> dma_trans_ended; - deser >> xfer_end_time; - } -}; -extern gdrom_hle_state_t gd_hle_state; - // status for GDROM_GET_DRV_STAT enum gd_drv_stat { GD_STAT_BUSY, diff --git a/core/reios/reios.cpp b/core/reios/reios.cpp index 42c2127b3..49f501f4c 100644 --- a/core/reios/reios.cpp +++ b/core/reios/reios.cpp @@ -15,8 +15,7 @@ #include "gdrom_hle.h" #include "descrambl.h" -#include "hw/sh4/sh4_core.h" -#undef r +#include "hw/sh4/sh4_if.h" #include "hw/sh4/sh4_mem.h" #include "hw/holly/sb.h" #include "hw/naomi/naomi_cart.h" @@ -27,6 +26,7 @@ #include "imgread/isofs.h" #include "hw/sh4/sh4_mmr.h" #include "oslib/resources.h" +#include "oslib/oslib.h" #include @@ -134,7 +134,7 @@ ip_meta_t ip_meta; void reios_disk_id() { - if (libGDR_GetDiscType() == Open || libGDR_GetDiscType() == NoDisk) + if (!gdr::isLoaded()) { memset(&ip_meta, 0, sizeof(ip_meta)); return; @@ -352,8 +352,17 @@ static void reios_sys_misc() break; case 1: // Exit to BIOS menu - WARN_LOG(REIOS, "SYS_MISC 1"); - throw FlycastException("Reboot to BIOS"); + { + WARN_LOG(REIOS, "SYS_MISC 1"); + if (gdr::isLoaded()) { + // just restart the game + p_sh4rcb->cntx.pc = 0xa0000000; + os_notify("Reboot to BIOS", 5000); + } + else { + throw FlycastException("Reboot to BIOS"); + } + } break; case 2: // check disk @@ -475,25 +484,26 @@ static void reios_setup_state(u32 boot_addr) */ //Setup registers to imitate a normal boot - p_sh4rcb->cntx.r[15] = 0x8d000000; - - gbr = 0x8c000000; - ssr = 0x40000001; - spc = 0x8c000776; - sgr = 0x8d000000; - dbr = 0x8c000010; - vbr = 0x8c000000; - pr = 0xac00043c; - fpul = 0x00000000; - next_pc = boot_addr; - - sr.status = 0x400000f0; - sr.T = 1; - - old_sr.status = 0x400000f0; - - fpscr.full = 0x00040001; - old_fpscr.full = 0x00040001; + Sh4Context& ctx = p_sh4rcb->cntx; + ctx.r[15] = 0x8d000000; + + ctx.gbr = 0x8c000000; + ctx.ssr = 0x40000001; + ctx.spc = 0x8c000776; + ctx.sgr = 0x8d000000; + ctx.dbr = 0x8c000010; + ctx.vbr = 0x8c000000; + ctx.pr = 0xac00043c; + ctx.fpul = 0x00000000; + ctx.pc = boot_addr; + + ctx.sr.status = 0x400000f0; + ctx.sr.T = 1; + + ctx.old_sr.status = 0x400000f0; + + ctx.fpscr.full = 0x00040001; + ctx.old_fpscr.full = 0x00040001; } static void reios_setup_naomi(u32 boot_addr) { @@ -501,7 +511,7 @@ static void reios_setup_naomi(u32 boot_addr) { SR 0x60000000 0x00000001 FPSRC 0x00040001 - - xffr 0x13e1fe40 float [32] + - xf,fr 0x13e1fe40 float [32] [0x0] 1.00000000 float [0x1] 0.000000000 float [0x2] 0.000000000 float @@ -581,40 +591,41 @@ static void reios_setup_naomi(u32 boot_addr) { */ //Setup registers to imitate a normal boot - p_sh4rcb->cntx.r[0] = 0x0c021000; - p_sh4rcb->cntx.r[1] = 0x0c01f820; - p_sh4rcb->cntx.r[2] = 0xa0710004; - p_sh4rcb->cntx.r[3] = 0x0c01f130; - p_sh4rcb->cntx.r[4] = 0x5bfccd08; - p_sh4rcb->cntx.r[5] = 0xa05f7000; - p_sh4rcb->cntx.r[6] = 0xa05f7008; - p_sh4rcb->cntx.r[7] = 0x00000007; - p_sh4rcb->cntx.r[8] = 0x00000000; - p_sh4rcb->cntx.r[9] = 0x00002000; - p_sh4rcb->cntx.r[10] = 0xffffffff; - p_sh4rcb->cntx.r[11] = 0x0c0e0000; - p_sh4rcb->cntx.r[12] = 0x00000000; - p_sh4rcb->cntx.r[13] = 0x00000000; - p_sh4rcb->cntx.r[14] = 0x00000000; - p_sh4rcb->cntx.r[15] = 0x0cc00000; - - gbr = 0x0c2abcc0; - ssr = 0x60000000; - spc = 0x0c041738; - sgr = 0x0cbfffb0; - dbr = 0x00000fff; - vbr = 0x0c000000; - pr = 0xac0195ee; - fpul = 0x000001e0; - next_pc = boot_addr; - - sr.status = 0x60000000; - sr.T = 1; - - old_sr.status = 0x60000000; - - fpscr.full = 0x00040001; - old_fpscr.full = 0x00040001; + Sh4Context& ctx = p_sh4rcb->cntx; + ctx.r[0] = 0x0c021000; + ctx.r[1] = 0x0c01f820; + ctx.r[2] = 0xa0710004; + ctx.r[3] = 0x0c01f130; + ctx.r[4] = 0x5bfccd08; + ctx.r[5] = 0xa05f7000; + ctx.r[6] = 0xa05f7008; + ctx.r[7] = 0x00000007; + ctx.r[8] = 0x00000000; + ctx.r[9] = 0x00002000; + ctx.r[10] = 0xffffffff; + ctx.r[11] = 0x0c0e0000; + ctx.r[12] = 0x00000000; + ctx.r[13] = 0x00000000; + ctx.r[14] = 0x00000000; + ctx.r[15] = 0x0cc00000; + + ctx.gbr = 0x0c2abcc0; + ctx.ssr = 0x60000000; + ctx.spc = 0x0c041738; + ctx.sgr = 0x0cbfffb0; + ctx.dbr = 0x00000fff; + ctx.vbr = 0x0c000000; + ctx.pr = 0xac0195ee; + ctx.fpul = 0x000001e0; + ctx.pc = boot_addr; + + ctx.sr.status = 0x60000000; + ctx.sr.T = 1; + + ctx.old_sr.status = 0x60000000; + + ctx.fpscr.full = 0x00040001; + ctx.old_fpscr.full = 0x00040001; } static void reios_boot() @@ -694,9 +705,10 @@ static const std::map hooks = { { SYSCALL_ADDR(dc_bios_entrypoint_gd2), gdrom_hle_op }, }; -void DYNACALL reios_trap(u32 op) { +void DYNACALL reios_trap(Sh4Context *ctx, u32 op) +{ verify(op == REIOS_OPCODE); - u32 pc = next_pc - 2; + u32 pc = ctx->pc - 2; u32 mapd = SYSCALL_ADDR(pc); @@ -711,17 +723,11 @@ void DYNACALL reios_trap(u32 op) { it->second(); // Return from syscall, except if pc was modified - if (pc == next_pc - 2) - next_pc = pr; -} - -bool reios_init() -{ - return true; + if (pc == ctx->pc - 2) + ctx->pc = ctx->pr; } -void reios_set_flash(MemChip* flash) -{ +void reios_set_flash(MemChip* flash) { flashrom = flash; } @@ -751,8 +757,5 @@ void reios_reset(u8* rom) std::unique_ptr fontData = resource::load("fonts/biosfont.bin", size); memcpy(pFont, fontData.get(), size); - gd_hle_state = {}; -} - -void reios_term() { + gdrom_hle_reset(); } diff --git a/core/reios/reios.h b/core/reios/reios.h index af1b616f5..c9bb54d18 100644 --- a/core/reios/reios.h +++ b/core/reios/reios.h @@ -3,13 +3,15 @@ #include "types.h" #include "hw/flashrom/flashrom.h" -bool reios_init(); +void reios_init(); void reios_set_flash(MemChip* flash); void reios_reset(u8* rom); - +void reios_serialize(Serializer& ser); +void reios_deserialize(Deserializer& deser); void reios_term(); -void DYNACALL reios_trap(u32 op); +struct Sh4Context; +void DYNACALL reios_trap(Sh4Context *ctx, u32 op); void reios_disk_id(); diff --git a/core/rend/CustomTexture.cpp b/core/rend/CustomTexture.cpp index 93078201f..1ff8444b2 100644 --- a/core/rend/CustomTexture.cpp +++ b/core/rend/CustomTexture.cpp @@ -16,8 +16,8 @@ You should have received a copy of the GNU General Public License along with reicast. If not, see . */ -#include "TexCache.h" #include "CustomTexture.h" +#include "TexCache.h" #include "oslib/directory.h" #include "oslib/storage.h" #include "cfg/option.h" diff --git a/core/rend/CustomTexture.h b/core/rend/CustomTexture.h index 29223ee8e..3994b9925 100644 --- a/core/rend/CustomTexture.h +++ b/core/rend/CustomTexture.h @@ -18,7 +18,7 @@ */ #pragma once -#include "TexCache.h" +#include "texconv.h" #include "stdclass.h" #include @@ -26,6 +26,8 @@ #include #include +class BaseTextureCacheData; + class CustomTexture { public: CustomTexture() : loader_thread(loader_thread_func, this, "CustomTexLoader") {} diff --git a/core/rend/TexCache.cpp b/core/rend/TexCache.cpp index dd4a75486..fe73022ed 100644 --- a/core/rend/TexCache.cpp +++ b/core/rend/TexCache.cpp @@ -1,10 +1,24 @@ +/* + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ #include "TexCache.h" -#include "CustomTexture.h" #include "deps/xbrz/xbrz.h" #include "hw/pvr/pvr_mem.h" #include "hw/mem/addrspace.h" -#include #include #include @@ -12,14 +26,6 @@ #include #endif -const u8 *vq_codebook; -u32 palette_index; -bool KillTex=false; -u32 palette16_ram[1024]; -u32 palette32_ram[1024]; -u32 pal_hash_256[4]; -u32 pal_hash_16[64]; -bool palette_updated; extern bool pal_needs_update; // Rough approximation of LoD bias from D adjust param, only used to increase LoD @@ -27,143 +33,6 @@ const std::array D_Adjust_LoD_Bias = { 0.f, -4.f, -2.f, -1.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f, 0.f }; -u32 detwiddle[2][11][1024]; -//input : address in the yyyyyxxxxx format -//output : address in the xyxyxyxy format -//U : x resolution , V : y resolution -//twiddle works on 64b words - - -static u32 twiddle_slow(u32 x,u32 y,u32 x_sz,u32 y_sz) -{ - u32 rv=0;//low 2 bits are directly passed -> needs some misc stuff to work.However - //Pvr internally maps the 64b banks "as if" they were twiddled :p - - u32 sh=0; - x_sz>>=1; - y_sz>>=1; - while(x_sz!=0 || y_sz!=0) - { - if (y_sz) - { - u32 temp=y&1; - rv|=temp<>=1; - y>>=1; - sh++; - } - if (x_sz) - { - u32 temp=x&1; - rv|=temp<>=1; - x>>=1; - sh++; - } - } - return rv; -} - -static void BuildTwiddleTables() -{ - for (u32 s = 0; s < 11; s++) - { - u32 x_sz = 1024; - u32 y_sz = 1 << s; - for (u32 i = 0; i < x_sz; i++) - { - detwiddle[0][s][i] = twiddle_slow(i, 0, x_sz, y_sz); - detwiddle[1][s][i] = twiddle_slow(0, i, y_sz, x_sz); - } - } -} - -static OnLoad btt(&BuildTwiddleTables); - -void palette_update() -{ - if (!pal_needs_update) - return; - pal_needs_update = false; - palette_updated = true; - - if (!isDirectX(config::RendererType)) - { - switch(PAL_RAM_CTRL&3) - { - case 0: - for (int i=0;i<1024;i++) - { - palette16_ram[i] = Unpacker1555::unpack(PALETTE_RAM[i]); - palette32_ram[i] = Unpacker1555_32::unpack(PALETTE_RAM[i]); - } - break; - - case 1: - for (int i=0;i<1024;i++) - { - palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); - palette32_ram[i] = Unpacker565_32::unpack(PALETTE_RAM[i]); - } - break; - - case 2: - for (int i=0;i<1024;i++) - { - palette16_ram[i] = Unpacker4444::unpack(PALETTE_RAM[i]); - palette32_ram[i] = Unpacker4444_32::unpack(PALETTE_RAM[i]); - } - break; - - case 3: - for (int i=0;i<1024;i++) - palette32_ram[i] = Unpacker8888::unpack(PALETTE_RAM[i]); - break; - } - } - else - { - switch(PAL_RAM_CTRL&3) - { - - case 0: - for (int i=0;i<1024;i++) - { - palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); - palette32_ram[i] = Unpacker1555_32::unpack(PALETTE_RAM[i]); - } - break; - - case 1: - for (int i=0;i<1024;i++) - { - palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); - palette32_ram[i] = Unpacker565_32::unpack(PALETTE_RAM[i]); - } - break; - - case 2: - for (int i=0;i<1024;i++) - { - palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); - palette32_ram[i] = Unpacker4444_32::unpack(PALETTE_RAM[i]); - } - break; - - case 3: - for (int i=0;i<1024;i++) - palette32_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); - break; - } - } - for (std::size_t i = 0; i < std::size(pal_hash_16); i++) - pal_hash_16[i] = XXH32(&PALETTE_RAM[i << 4], 16 * 4, 7); - for (std::size_t i = 0; i < std::size(pal_hash_256); i++) - pal_hash_256[i] = XXH32(&PALETTE_RAM[i << 8], 256 * 4, 7); -} - static std::vector VramLocks[VRAM_SIZE_MAX / PAGE_SIZE]; //List functions @@ -292,44 +161,6 @@ void UpscalexBRZ(int factor, u32* source, u32* dest, int width, int height, bool #endif } -struct PvrTexInfo -{ - const char* name; - int bpp; //4/8 for pal. 16 for yuv, rgb, argb - TextureType type; - // Conversion to 16 bpp - TexConvFP TW; - TexConvFP VQ; - // Conversion to 32 bpp - TexConvFP32 PL32; - TexConvFP32 TW32; - TexConvFP32 VQ32; - // Conversion to 8 bpp (palette) - TexConvFP8 TW8; -}; - -#define TEX_CONV_TABLE \ -const PvrTexInfo pvrTexInfo[8] = \ -{ /* name bpp Final format Twiddled VQ Planar(32b) Twiddled(32b) VQ (32b) Palette (8b) */ \ - {"1555", 16, TextureType::_5551, tex1555_TW, tex1555_VQ, tex1555_PL32, tex1555_TW32, tex1555_VQ32, nullptr }, \ - {"565", 16, TextureType::_565, tex565_TW, tex565_VQ, tex565_PL32, tex565_TW32, tex565_VQ32, nullptr }, \ - {"4444", 16, TextureType::_4444, tex4444_TW, tex4444_VQ, tex4444_PL32, tex4444_TW32, tex4444_VQ32, nullptr }, \ - {"yuv", 16, TextureType::_8888, nullptr, nullptr, texYUV422_PL, texYUV422_TW, texYUV422_VQ, nullptr }, \ - {"bumpmap", 16, TextureType::_4444, texBMP_TW, texBMP_VQ, tex4444_PL32, tex4444_TW32, tex4444_VQ32, nullptr }, \ - {"pal4", 4, TextureType::_5551, texPAL4_TW, texPAL4_VQ, nullptr, texPAL4_TW32, texPAL4_VQ32, texPAL4PT_TW }, \ - {"pal8", 8, TextureType::_5551, texPAL8_TW, texPAL8_VQ, nullptr, texPAL8_TW32, texPAL8_VQ32, texPAL8PT_TW }, \ - {"ns/1555", 0}, \ -} - -namespace opengl { - TEX_CONV_TABLE; -} -namespace directx { - TEX_CONV_TABLE; -} -#undef TEX_CONV_TABLE -static const PvrTexInfo *pvrTexInfo = opengl::pvrTexInfo; - extern const u32 VQMipPoint[11] = { VQ_CODEBOOK_SIZE + 0x00000, // 1 @@ -486,11 +317,6 @@ BaseTextureCacheData::BaseTextureCacheData(TSP tsp, TCW tcw) if (tcw.ScanOrder && tex->PL32 != nullptr) { //Texture is stored 'planar' in memory, no deswizzle is needed - if (tcw.VQ_Comp != 0) - { - WARN_LOG(RENDERER, "Warning: planar texture with VQ set (invalid)"); - this->tcw.VQ_Comp = 0; - } if (tcw.MipMapped != 0) { WARN_LOG(RENDERER, "Warning: planar texture with mipmaps (invalid)"); @@ -508,9 +334,20 @@ BaseTextureCacheData::BaseTextureCacheData(TSP tsp, TCW tcw) //Call the format specific conversion code texconv = nullptr; - texconv32 = tex->PL32; - //calculate the size, in bytes, for the locking - size = stride * height * tex->bpp / 8; + if (tcw.VQ_Comp != 0) + { + // VQ + texconv32 = tex->PLVQ32; + mmStartAddress += VQ_CODEBOOK_SIZE; + size = stride * height / 4; + } + else + { + // Normal + texconv32 = tex->PL32; + //calculate the size, in bytes, for the locking + size = stride * height * tex->bpp / 8; + } } else { diff --git a/core/rend/TexCache.h b/core/rend/TexCache.h index 188f44da9..1ce4ba250 100644 --- a/core/rend/TexCache.h +++ b/core/rend/TexCache.h @@ -1,7 +1,25 @@ +/* + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ #pragma once #include "oslib/oslib.h" #include "hw/pvr/Renderer_if.h" #include "cfg/option.h" +#include "texconv.h" +#include "CustomTexture.h" #include #include @@ -11,543 +29,6 @@ #include #include -extern const u8 *vq_codebook; -constexpr int VQ_CODEBOOK_SIZE = 256 * 8; -extern u32 palette_index; -extern u32 palette16_ram[1024]; -extern u32 palette32_ram[1024]; -extern bool fog_needs_update; -extern u32 pal_hash_256[4]; -extern u32 pal_hash_16[64]; -extern bool KillTex; -extern bool palette_updated; - -extern u32 detwiddle[2][11][1024]; - -void palette_update(); - -template -class PixelBuffer -{ - pixel_type* p_buffer_start = nullptr; - pixel_type* p_current_mipmap = nullptr; - pixel_type* p_current_line = nullptr; - pixel_type* p_current_pixel = nullptr; - - u32 pixels_per_line = 0; - -public: - ~PixelBuffer() - { - deinit(); - } - - void init(u32 width, u32 height, bool mipmapped) - { - deinit(); - size_t size = width * height * sizeof(pixel_type); - if (mipmapped) - { - do - { - width /= 2; - height /= 2; - size += width * height * sizeof(pixel_type); - } - while (width != 0 && height != 0); - } - p_buffer_start = p_current_line = p_current_pixel = p_current_mipmap = (pixel_type *)malloc(size); - this->pixels_per_line = 1; - } - - void init(u32 width, u32 height) - { - deinit(); - p_buffer_start = p_current_line = p_current_pixel = p_current_mipmap = (pixel_type *)malloc(width * height * sizeof(pixel_type)); - this->pixels_per_line = width; - } - - void deinit() - { - if (p_buffer_start != NULL) - { - free(p_buffer_start); - p_buffer_start = p_current_mipmap = p_current_line = p_current_pixel = NULL; - } - } - - void steal_data(PixelBuffer &buffer) - { - deinit(); - p_buffer_start = p_current_mipmap = p_current_line = p_current_pixel = buffer.p_buffer_start; - pixels_per_line = buffer.pixels_per_line; - buffer.p_buffer_start = buffer.p_current_mipmap = buffer.p_current_line = buffer.p_current_pixel = NULL; - } - - void set_mipmap(int level) - { - u32 offset = 0; - for (int i = 0; i < level; i++) - offset += (1 << (2 * i)); - p_current_mipmap = p_current_line = p_current_pixel = p_buffer_start + offset; - pixels_per_line = 1 << level; - } - - pixel_type *data(u32 x = 0, u32 y = 0) - { - return p_current_mipmap + pixels_per_line * y + x; - } - - void prel(u32 x, pixel_type value) - { - p_current_pixel[x] = value; - } - - void prel(u32 x, u32 y, pixel_type value) - { - p_current_pixel[y * pixels_per_line + x] = value; - } - - void rmovex(u32 value) - { - p_current_pixel += value; - } - - void rmovey(u32 value) - { - p_current_line += pixels_per_line * value; - p_current_pixel = p_current_line; - } - - void amove(u32 x_m, u32 y_m) - { - //p_current_pixel=p_buffer_start; - p_current_line = p_current_mipmap + pixels_per_line * y_m; - p_current_pixel = p_current_line + x_m; - } -}; - -// OpenGL -struct RGBAPacker { - static u32 pack(u8 r, u8 g, u8 b, u8 a) { - return r | (g << 8) | (b << 16) | (a << 24); - } -}; -// DirectX -struct BGRAPacker { - static u32 pack(u8 r, u8 g, u8 b, u8 a) { - return b | (g << 8) | (r << 16) | (a << 24); - } -}; - -template -inline static u32 YUV422(s32 Y, s32 Yu, s32 Yv) -{ - Yu -= 128; - Yv -= 128; - - s32 R = Y + Yv * 11 / 8; // Y + (Yv-128) * (11/8) ? - s32 G = Y - (Yu * 11 + Yv * 22) / 32; // Y - (Yu-128) * (11/8) * 0.25 - (Yv-128) * (11/8) * 0.5 ? - s32 B = Y + Yu * 110 / 64; // Y + (Yu-128) * (11/8) * 1.25 ? - - return Packer::pack(std::clamp(R, 0, 255), std::clamp(G, 0, 255), std::clamp(B, 0, 255), 0xFF); -} - -#define twop(x,y,bcx,bcy) (detwiddle[0][bcy][x]+detwiddle[1][bcx][y]) - -template -struct UnpackerNop { - using unpacked_type = Pixel; - static Pixel unpack(Pixel word) { - return word; - } -}; - -// ARGB1555 to RGBA5551 -struct Unpacker1555 { - using unpacked_type = u16; - static u16 unpack(u16 word) { - return ((word >> 15) & 1) | (((word >> 10) & 0x1F) << 11) | (((word >> 5) & 0x1F) << 6) | (((word >> 0) & 0x1F) << 1); - } -}; - -// ARGB4444 to RGBA4444 -struct Unpacker4444 { - using unpacked_type = u16; - static u16 unpack(u16 word) { - return (((word >> 0) & 0xF) << 4) | (((word >> 4) & 0xF) << 8) | (((word >> 8) & 0xF) << 12) | (((word >> 12) & 0xF) << 0); - } -}; - -template -struct Unpacker1555_32 { - using unpacked_type = u32; - static u32 unpack(u16 word) { - return Packer::pack( - (((word >> 10) & 0x1F) << 3) | ((word >> 12) & 7), - (((word >> 5) & 0x1F) << 3) | ((word >> 7) & 7), - (((word >> 0) & 0x1F) << 3) | ((word >> 2) & 7), - (word & 0x8000) ? 0xFF : 0); - } -}; - -template -struct Unpacker565_32 { - using unpacked_type = u32; - static u32 unpack(u16 word) { - return Packer::pack( - (((word >> 11) & 0x1F) << 3) | ((word >> 13) & 7), - (((word >> 5) & 0x3F) << 2) | ((word >> 9) & 3), - (((word >> 0) & 0x1F) << 3) | ((word >> 2) & 7), - 0xFF); - } -}; - -template -struct Unpacker4444_32 { - using unpacked_type = u32; - static u32 unpack(u16 word) { - return Packer::pack( - (((word >> 8) & 0xF) << 4) | ((word >> 8) & 0xF), - (((word >> 4) & 0xF) << 4) | ((word >> 4) & 0xF), - (((word >> 0) & 0xF) << 4) | ((word >> 0) & 0xF), - (((word >> 12) & 0xF) << 4) | ((word >> 12) & 0xF)); - } -}; - -// ARGB8888 to whatever -template -struct Unpacker8888 { - using unpacked_type = u32; - static u32 unpack(u32 word) { - return Packer::pack( - (word >> 16) & 0xFF, - (word >> 8) & 0xFF, - (word >> 0) & 0xFF, - (word >> 24) & 0xFF); - } -}; - -template -struct ConvertPlanar -{ - using unpacked_type = typename Unpacker::unpacked_type; - static constexpr u32 xpp = 4; - static constexpr u32 ypp = 1; - static void Convert(PixelBuffer *pb, const u8 *data) - { - const u16 *p_in = (const u16 *)data; - pb->prel(0, Unpacker::unpack(p_in[0])); - pb->prel(1, Unpacker::unpack(p_in[1])); - pb->prel(2, Unpacker::unpack(p_in[2])); - pb->prel(3, Unpacker::unpack(p_in[3])); - } -}; - -template -struct ConvertPlanarYUV -{ - using unpacked_type = u32; - static constexpr u32 xpp = 4; - static constexpr u32 ypp = 1; - static void Convert(PixelBuffer *pb, const u8 *data) - { - //convert 4x1 4444 to 4x1 8888 - const u32 *p_in = (const u32 *)data; - - - s32 Y0 = (p_in[0] >> 8) & 255; // - s32 Yu = (p_in[0] >> 0) & 255; //p_in[0] - s32 Y1 = (p_in[0] >> 24) & 255; //p_in[3] - s32 Yv = (p_in[0] >> 16) & 255; //p_in[2] - - //0,0 - pb->prel(0, YUV422(Y0, Yu, Yv)); - //1,0 - pb->prel(1, YUV422(Y1, Yu, Yv)); - - //next 4 bytes - p_in += 1; - - Y0 = (p_in[0] >> 8) & 255; // - Yu = (p_in[0] >> 0) & 255; //p_in[0] - Y1 = (p_in[0] >> 24) & 255; //p_in[3] - Yv = (p_in[0] >> 16) & 255; //p_in[2] - - //0,0 - pb->prel(2, YUV422(Y0, Yu, Yv)); - //1,0 - pb->prel(3, YUV422(Y1, Yu, Yv)); - } -}; - -template -struct ConvertTwiddle -{ - using unpacked_type = typename Unpacker::unpacked_type; - static constexpr u32 xpp = 2; - static constexpr u32 ypp = 2; - static void Convert(PixelBuffer *pb, const u8 *data) - { - const u16 *p_in = (const u16 *)data; - pb->prel(0, 0, Unpacker::unpack(p_in[0])); - pb->prel(0, 1, Unpacker::unpack(p_in[1])); - pb->prel(1, 0, Unpacker::unpack(p_in[2])); - pb->prel(1, 1, Unpacker::unpack(p_in[3])); - } -}; - -template -struct ConvertTwiddleYUV -{ - using unpacked_type = u32; - static constexpr u32 xpp = 2; - static constexpr u32 ypp = 2; - static void Convert(PixelBuffer *pb, const u8 *data) - { - //convert 4x1 4444 to 4x1 8888 - const u16* p_in = (const u16 *)data; - - s32 Y0 = (p_in[0] >> 8) & 255; // - s32 Yu = (p_in[0] >> 0) & 255; //p_in[0] - s32 Y1 = (p_in[2] >> 8) & 255; //p_in[3] - s32 Yv = (p_in[2] >> 0) & 255; //p_in[2] - - //0,0 - pb->prel(0, 0, YUV422(Y0, Yu, Yv)); - //1,0 - pb->prel(1, 0, YUV422(Y1, Yu, Yv)); - - //next 4 bytes - //p_in+=2; - - Y0 = (p_in[1] >> 8) & 255; // - Yu = (p_in[1] >> 0) & 255; //p_in[0] - Y1 = (p_in[3] >> 8) & 255; //p_in[3] - Yv = (p_in[3] >> 0) & 255; //p_in[2] - - //0,1 - pb->prel(0, 1, YUV422(Y0, Yu, Yv)); - //1,1 - pb->prel(1, 1, YUV422(Y1, Yu, Yv)); - } -}; - -template -struct UnpackerPalToRgb { - using unpacked_type = Pixel; - static Pixel unpack(u8 col) - { - u32 *pal = sizeof(Pixel) == 2 ? &palette16_ram[palette_index] : &palette32_ram[palette_index]; - return pal[col]; - } -}; - -template -struct ConvertTwiddlePal4 -{ - using unpacked_type = typename Unpacker::unpacked_type; - static constexpr u32 xpp = 4; - static constexpr u32 ypp = 4; - static void Convert(PixelBuffer *pb, const u8 *data) - { - const u8 *p_in = data; - - pb->prel(0, 0, Unpacker::unpack(p_in[0] & 0xF)); - pb->prel(0, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; - pb->prel(1, 0, Unpacker::unpack(p_in[0] & 0xF)); - pb->prel(1, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; - - pb->prel(0, 2, Unpacker::unpack(p_in[0] & 0xF)); - pb->prel(0, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; - pb->prel(1, 2, Unpacker::unpack(p_in[0] & 0xF)); - pb->prel(1, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; - - pb->prel(2, 0, Unpacker::unpack(p_in[0] & 0xF)); - pb->prel(2, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; - pb->prel(3, 0, Unpacker::unpack(p_in[0] & 0xF)); - pb->prel(3, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; - - pb->prel(2, 2, Unpacker::unpack(p_in[0] & 0xF)); - pb->prel(2, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; - pb->prel(3, 2, Unpacker::unpack(p_in[0] & 0xF)); - pb->prel(3, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; - } -}; - -template -struct ConvertTwiddlePal8 -{ - using unpacked_type = typename Unpacker::unpacked_type; - static constexpr u32 xpp = 2; - static constexpr u32 ypp = 4; - static void Convert(PixelBuffer *pb, const u8 *data) - { - const u8* p_in = (const u8 *)data; - - pb->prel(0, 0, Unpacker::unpack(p_in[0])); p_in++; - pb->prel(0, 1, Unpacker::unpack(p_in[0])); p_in++; - pb->prel(1, 0, Unpacker::unpack(p_in[0])); p_in++; - pb->prel(1, 1, Unpacker::unpack(p_in[0])); p_in++; - - pb->prel(0, 2, Unpacker::unpack(p_in[0])); p_in++; - pb->prel(0, 3, Unpacker::unpack(p_in[0])); p_in++; - pb->prel(1, 2, Unpacker::unpack(p_in[0])); p_in++; - pb->prel(1, 3, Unpacker::unpack(p_in[0])); p_in++; - } -}; - -//handler functions -template -void texture_PL(PixelBuffer* pb, const u8* p_in, u32 Width, u32 Height) -{ - pb->amove(0,0); - - Height/=PixelConvertor::ypp; - Width/=PixelConvertor::xpp; - - for (u32 y=0;yrmovex(PixelConvertor::xpp); - } - pb->rmovey(PixelConvertor::ypp); - } -} - -template -void texture_TW(PixelBuffer* pb, const u8* p_in, u32 Width, u32 Height) -{ - pb->amove(0, 0); - - const u32 divider = PixelConvertor::xpp * PixelConvertor::ypp; - - const u32 bcx = bitscanrev(Width); - const u32 bcy = bitscanrev(Height); - - for (u32 y = 0; y < Height; y += PixelConvertor::ypp) - { - for (u32 x = 0; x < Width; x += PixelConvertor::xpp) - { - const u8* p = &p_in[(twop(x, y, bcx, bcy) / divider) << 3]; - PixelConvertor::Convert(pb, p); - - pb->rmovex(PixelConvertor::xpp); - } - pb->rmovey(PixelConvertor::ypp); - } -} - -template -void texture_VQ(PixelBuffer* pb, const u8* p_in, u32 Width, u32 Height) -{ - pb->amove(0, 0); - - const u32 divider = PixelConvertor::xpp * PixelConvertor::ypp; - const u32 bcx = bitscanrev(Width); - const u32 bcy = bitscanrev(Height); - - for (u32 y = 0; y < Height; y += PixelConvertor::ypp) - { - for (u32 x = 0; x < Width; x += PixelConvertor::xpp) - { - u8 p = p_in[twop(x, y, bcx, bcy) / divider]; - PixelConvertor::Convert(pb, &vq_codebook[p * 8]); - - pb->rmovex(PixelConvertor::xpp); - } - pb->rmovey(PixelConvertor::ypp); - } -} - -typedef void (*TexConvFP)(PixelBuffer *pb, const u8 *p_in, u32 width, u32 height); -typedef void (*TexConvFP8)(PixelBuffer *pb, const u8 *p_in, u32 width, u32 height); -typedef void (*TexConvFP32)(PixelBuffer *pb, const u8 *p_in, u32 width, u32 height); - -//Twiddle -constexpr TexConvFP tex565_TW = texture_TW>>; -// Palette -constexpr TexConvFP texPAL4_TW = texture_TW>>; -constexpr TexConvFP texPAL8_TW = texture_TW>>; -constexpr TexConvFP32 texPAL4_TW32 = texture_TW>>; -constexpr TexConvFP32 texPAL8_TW32 = texture_TW>>; -constexpr TexConvFP8 texPAL4PT_TW = texture_TW>>; -constexpr TexConvFP8 texPAL8PT_TW = texture_TW>>; -//VQ -constexpr TexConvFP tex565_VQ = texture_VQ>>; -// According to the documentation, a texture cannot be compressed and use -// a palette at the same time. However the hardware displays them -// just fine. -constexpr TexConvFP texPAL4_VQ = texture_VQ>>; -constexpr TexConvFP texPAL8_VQ = texture_VQ>>; -constexpr TexConvFP32 texPAL4_VQ32 = texture_VQ>>; -constexpr TexConvFP32 texPAL8_VQ32 = texture_VQ>>; - -namespace opengl { -// OpenGL - -//Planar -constexpr TexConvFP32 texYUV422_PL = texture_PL>; -constexpr TexConvFP32 tex565_PL32 = texture_PL>>; -constexpr TexConvFP32 tex1555_PL32 = texture_PL>>; -constexpr TexConvFP32 tex4444_PL32 = texture_PL>>; - -//Twiddle -constexpr TexConvFP tex1555_TW = texture_TW>; -constexpr TexConvFP tex4444_TW = texture_TW>; -constexpr TexConvFP texBMP_TW = tex4444_TW; -constexpr TexConvFP32 texYUV422_TW = texture_TW>; - -constexpr TexConvFP32 tex565_TW32 = texture_TW>>; -constexpr TexConvFP32 tex1555_TW32 = texture_TW>>; -constexpr TexConvFP32 tex4444_TW32 = texture_TW>>; - -//VQ -constexpr TexConvFP tex1555_VQ = texture_VQ>; -constexpr TexConvFP tex4444_VQ = texture_VQ>; -constexpr TexConvFP texBMP_VQ = tex4444_VQ; -constexpr TexConvFP32 texYUV422_VQ = texture_VQ>; - -constexpr TexConvFP32 tex565_VQ32 = texture_VQ>>; -constexpr TexConvFP32 tex1555_VQ32 = texture_VQ>>; -constexpr TexConvFP32 tex4444_VQ32 = texture_VQ>>; -} - -namespace directx { -// DirectX - -//Planar -constexpr TexConvFP32 texYUV422_PL = texture_PL>; -constexpr TexConvFP32 tex565_PL32 = texture_PL>>; -constexpr TexConvFP32 tex1555_PL32 = texture_PL>>; -constexpr TexConvFP32 tex4444_PL32 = texture_PL>>; - -//Twiddle -constexpr TexConvFP tex1555_TW = texture_TW>>; -constexpr TexConvFP tex4444_TW = texture_TW>>; -constexpr TexConvFP texBMP_TW = tex4444_TW; -constexpr TexConvFP32 texYUV422_TW = texture_TW>; - -constexpr TexConvFP32 tex565_TW32 = texture_TW>>; -constexpr TexConvFP32 tex1555_TW32 = texture_TW>>; -constexpr TexConvFP32 tex4444_TW32 = texture_TW>>; - -//VQ -constexpr TexConvFP tex1555_VQ = texture_VQ>>; -constexpr TexConvFP tex4444_VQ = texture_VQ>>; -constexpr TexConvFP texBMP_VQ = tex4444_VQ; -constexpr TexConvFP32 texYUV422_VQ = texture_VQ>; - -constexpr TexConvFP32 tex565_VQ32 = texture_VQ>>; -constexpr TexConvFP32 tex1555_VQ32 = texture_VQ>>; -constexpr TexConvFP32 tex4444_VQ32 = texture_VQ>>; -} - class BaseTextureCacheData; struct vram_block @@ -563,9 +44,6 @@ bool VramLockedWrite(u8* address); void UpscalexBRZ(int factor, u32* source, u32* dest, int width, int height, bool has_alpha); -struct PvrTexInfo; -enum class TextureType { _565, _5551, _4444, _8888, _8 }; - class BaseTextureCacheData { protected: @@ -693,9 +171,6 @@ class BaseTextureCacheData static void SetDirectXColorOrder(bool enabled); }; -// TODO Split the texture cache in a separate header -#include "CustomTexture.h" - template class BaseTextureCache { @@ -788,7 +263,6 @@ class BaseTextureCache texture.Delete(); cache.clear(); - KillTex = false; INFO_LOG(RENDERER, "Texture cache cleared"); } diff --git a/core/rend/dx11/dx11_overlay.cpp b/core/rend/dx11/dx11_overlay.cpp index ff507c56e..2c0354741 100644 --- a/core/rend/dx11/dx11_overlay.cpp +++ b/core/rend/dx11/dx11_overlay.cpp @@ -142,39 +142,36 @@ void DX11Overlay::draw(u32 width, u32 height, bool vmu, bool crosshair) quad.draw(vmuTextureViews[i], samplers->getSampler(false)); } } - if (crosshair && crosshairsNeeded()) + if (crosshair) { - if (!xhairTexture) + for (u32 i = 0; i < config::CrosshairColor.size(); i++) { - const u32* texData = getCrosshairTextureData(); - D3D11_TEXTURE2D_DESC desc{}; - desc.Width = 16; - desc.Height = 16; - desc.ArraySize = 1; - desc.SampleDesc.Count = 1; - desc.Usage = D3D11_USAGE_DEFAULT; - desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; - desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; - desc.MipLevels = 1; - - if (SUCCEEDED(device->CreateTexture2D(&desc, nullptr, &xhairTexture.get()))) + if (!crosshairNeeded(i)) + continue; + if (!xhairTexture) { - D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{}; - viewDesc.Format = desc.Format; - viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; - viewDesc.Texture2D.MipLevels = desc.MipLevels; - device->CreateShaderResourceView(xhairTexture, &viewDesc, &xhairTextureView.get()); + const u32* texData = getCrosshairTextureData(); + D3D11_TEXTURE2D_DESC desc{}; + desc.Width = 16; + desc.Height = 16; + desc.ArraySize = 1; + desc.SampleDesc.Count = 1; + desc.Usage = D3D11_USAGE_DEFAULT; + desc.BindFlags = D3D11_BIND_SHADER_RESOURCE; + desc.Format = DXGI_FORMAT_R8G8B8A8_UNORM; + desc.MipLevels = 1; + + if (SUCCEEDED(device->CreateTexture2D(&desc, nullptr, &xhairTexture.get()))) + { + D3D11_SHADER_RESOURCE_VIEW_DESC viewDesc{}; + viewDesc.Format = desc.Format; + viewDesc.ViewDimension = D3D11_SRV_DIMENSION_TEXTURE2D; + viewDesc.Texture2D.MipLevels = desc.MipLevels; + device->CreateShaderResourceView(xhairTexture, &viewDesc, &xhairTextureView.get()); - deviceContext->UpdateSubresource(xhairTexture, 0, nullptr, texData, 16 * 4, 16 * 4 * 16); + deviceContext->UpdateSubresource(xhairTexture, 0, nullptr, texData, 16 * 4, 16 * 4 * 16); + } } - } - for (u32 i = 0; i < config::CrosshairColor.size(); i++) - { - if (config::CrosshairColor[i] == 0) - continue; - if (settings.platform.isConsole() - && config::MapleMainDevices[i] != MDT_LightGun) - continue; auto [x, y] = getCrosshairPosition(i); #ifdef LIBRETRO diff --git a/core/rend/dx11/dx11_renderer.cpp b/core/rend/dx11/dx11_renderer.cpp index cbc2d78a8..ebb15ede2 100644 --- a/core/rend/dx11/dx11_renderer.cpp +++ b/core/rend/dx11/dx11_renderer.cpp @@ -158,7 +158,7 @@ bool DX11Renderer::Init() quad->init(device, deviceContext, shaders); n2Helper.init(device, deviceContext); - fog_needs_update = true; + updateFogTable = true; if (!success) { @@ -318,8 +318,10 @@ BaseTextureCacheData *DX11Renderer::GetTexture(TSP tsp, TCW tcw) void DX11Renderer::Process(TA_context* ctx) { - if (KillTex) + if (resetTextureCache) { texCache.Clear(); + resetTextureCache = false; + } texCache.Cleanup(); ta_parse(ctx, true); @@ -513,7 +515,7 @@ bool DX11Renderer::Render() #ifndef LIBRETRO deviceContext->OMSetRenderTargets(1, &theDX11Context.getRenderTarget().get(), nullptr); displayFramebuffer(); - DrawOSD(false); + drawOSD(); renderVideoRouting(); theDX11Context.setFrameRendered(); #else @@ -523,6 +525,7 @@ bool DX11Renderer::Render() #endif frameRendered = true; frameRenderedOnce = true; + clearLastFrame = false; } return !is_rtt; @@ -936,7 +939,7 @@ void DX11Renderer::drawStrips() bool DX11Renderer::RenderLastFrame() { - if (!frameRenderedOnce) + if (!frameRenderedOnce || clearLastFrame) return false; displayFramebuffer(); return true; @@ -1028,7 +1031,7 @@ void DX11Renderer::RenderFramebuffer(const FramebufferInfo& info) deviceContext->OMSetRenderTargets(1, &theDX11Context.getRenderTarget().get(), nullptr); displayFramebuffer(); - DrawOSD(false); + drawOSD(); renderVideoRouting(); theDX11Context.setFrameRendered(); #else @@ -1038,6 +1041,7 @@ void DX11Renderer::RenderFramebuffer(const FramebufferInfo& info) #endif frameRendered = true; frameRenderedOnce = true; + clearLastFrame = false; } void DX11Renderer::setBaseScissor() @@ -1222,9 +1226,9 @@ void DX11Renderer::readRttRenderTarget(u32 texAddress) void DX11Renderer::updatePaletteTexture() { - if (palette_updated) + if (updatePalette) { - palette_updated = false; + updatePalette = false; deviceContext->UpdateSubresource(paletteTexture, 0, nullptr, palette32_ram, 32 * sizeof(u32), 32 * sizeof(u32) * 32); } deviceContext->PSSetShaderResources(1, 1, &paletteTextureView.get()); @@ -1235,9 +1239,9 @@ void DX11Renderer::updateFogTexture() { if (!config::Fog) return; - if (fog_needs_update) + if (updateFogTable) { - fog_needs_update = false; + updateFogTable = false; u8 temp_tex_buffer[256]; MakeFogTexture(temp_tex_buffer); @@ -1247,10 +1251,10 @@ void DX11Renderer::updateFogTexture() deviceContext->PSSetSamplers(2, 1, &samplers->getSampler(true).get()); } -void DX11Renderer::DrawOSD(bool clear_screen) +void DX11Renderer::drawOSD() { #ifndef LIBRETRO - theDX11Context.setOverlay(!clear_screen); + theDX11Context.setOverlay(true); gui_display_osd(); theDX11Context.setOverlay(false); #endif diff --git a/core/rend/dx11/dx11_renderer.h b/core/rend/dx11/dx11_renderer.h index 0880bd660..fb73fd7e8 100644 --- a/core/rend/dx11/dx11_renderer.h +++ b/core/rend/dx11/dx11_renderer.h @@ -42,7 +42,7 @@ struct DX11Renderer : public Renderer bool Present() override { - if (!frameRendered) + if (!frameRendered || clearLastFrame) return false; frameRendered = false; #ifndef LIBRETRO @@ -54,7 +54,6 @@ struct DX11Renderer : public Renderer } bool RenderLastFrame() override; - void DrawOSD(bool clear_screen) override; BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) override; bool GetLastFrame(std::vector& data, int& width, int& height) override; @@ -103,6 +102,7 @@ struct DX11Renderer : public Renderer void writeFramebufferToVRAM(); void renderVideoRouting(); void resetContextState(); + void drawOSD(); TileClipping setTileClip(u32 val, int clip_rect[4]); ComPtr device; diff --git a/core/rend/dx11/dx11context.cpp b/core/rend/dx11/dx11context.cpp index 0e0d5cf4d..808efac00 100644 --- a/core/rend/dx11/dx11context.cpp +++ b/core/rend/dx11/dx11context.cpp @@ -253,7 +253,10 @@ void DX11Context::EndImGuiFrame() { if (pDevice && pDeviceContext && renderTargetView) { - if (!overlayOnly) + if (overlayOnly) { + overlay.draw(settings.display.width, settings.display.height, config::FloatVMUs, true); + } + else { pDeviceContext->OMSetRenderTargets(1, &renderTargetView.get(), nullptr); const FLOAT black[4] { 0.f, 0.f, 0.f, 1.f }; @@ -261,11 +264,6 @@ void DX11Context::EndImGuiFrame() if (renderer != nullptr) renderer->RenderLastFrame(); } - if (overlayOnly) - { - if (crosshairsNeeded() || config::FloatVMUs) - overlay.draw(settings.display.width, settings.display.height, config::FloatVMUs, true); - } ImGui_ImplDX11_RenderDrawData(ImGui::GetDrawData()); } frameRendered = true; diff --git a/core/rend/dx11/oit/dx11_oitrenderer.cpp b/core/rend/dx11/oit/dx11_oitrenderer.cpp index 6a45b934a..faaa430a9 100644 --- a/core/rend/dx11/oit/dx11_oitrenderer.cpp +++ b/core/rend/dx11/oit/dx11_oitrenderer.cpp @@ -684,7 +684,7 @@ struct DX11OITRenderer : public DX11Renderer #ifndef LIBRETRO deviceContext->OMSetRenderTargets(1, &theDX11Context.getRenderTarget().get(), nullptr); displayFramebuffer(); - DrawOSD(false); + drawOSD(); renderVideoRouting(); theDX11Context.setFrameRendered(); #else @@ -694,6 +694,7 @@ struct DX11OITRenderer : public DX11Renderer #endif frameRendered = true; frameRenderedOnce = true; + clearLastFrame = false; } return !is_rtt; diff --git a/core/rend/dx9/d3d_overlay.cpp b/core/rend/dx9/d3d_overlay.cpp index b62c2ecb7..4dc527b7a 100644 --- a/core/rend/dx9/d3d_overlay.cpp +++ b/core/rend/dx9/d3d_overlay.cpp @@ -87,35 +87,32 @@ void D3DOverlay::draw(u32 width, u32 height, bool vmu, bool crosshair) drawQuad(rect, D3DCOLOR_ARGB(192, 255, 255, 255)); } } - if (crosshair && crosshairsNeeded()) + if (crosshair) { - if (!xhairTexture) + for (u32 i = 0; i < config::CrosshairColor.size(); i++) { - const u32* texData = getCrosshairTextureData(); - device->CreateTexture(16, 16, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &xhairTexture.get(), 0); - D3DLOCKED_RECT rect; - if (SUCCEEDED(xhairTexture->LockRect(0, &rect, nullptr, 0))) + if (!crosshairNeeded(i)) + continue; + + if (!xhairTexture) { - if (rect.Pitch == 16 * sizeof(u32)) - memcpy(rect.pBits, texData, 16 * 16 * sizeof(u32)); - else + const u32* texData = getCrosshairTextureData(); + device->CreateTexture(16, 16, 1, 0, D3DFMT_A8R8G8B8, D3DPOOL_MANAGED, &xhairTexture.get(), 0); + D3DLOCKED_RECT rect; + if (SUCCEEDED(xhairTexture->LockRect(0, &rect, nullptr, 0))) { - u8 *dst = (u8 *) rect.pBits; - for (int y = 0; y < 16; y++) - memcpy(dst + y * rect.Pitch, texData + y * 16, 16 * sizeof(u32)); + if (rect.Pitch == 16 * sizeof(u32)) + memcpy(rect.pBits, texData, 16 * 16 * sizeof(u32)); + else + { + u8 *dst = (u8 *) rect.pBits; + for (int y = 0; y < 16; y++) + memcpy(dst + y * rect.Pitch, texData + y * 16, 16 * sizeof(u32)); + } + xhairTexture->UnlockRect(0); } - xhairTexture->UnlockRect(0); } - } - device->SetTexture(0, xhairTexture); - for (u32 i = 0; i < config::CrosshairColor.size(); i++) - { - if (config::CrosshairColor[i] == 0) - continue; - if (settings.platform.isConsole() - && config::MapleMainDevices[i] != MDT_LightGun) - continue; - + device->SetTexture(0, xhairTexture); auto [x, y] = getCrosshairPosition(i); float halfWidth = config::CrosshairSize * settings.display.uiScale / 2.f; RECT rect { (long) (x - halfWidth), (long) (y - halfWidth), (long) (x + halfWidth), (long) (y + halfWidth) }; diff --git a/core/rend/dx9/d3d_renderer.cpp b/core/rend/dx9/d3d_renderer.cpp index 83f0ab3e4..f3554a56d 100644 --- a/core/rend/dx9/d3d_renderer.cpp +++ b/core/rend/dx9/d3d_renderer.cpp @@ -139,7 +139,7 @@ bool D3DRenderer::Init() success &= (bool)shaders.getVertexShader(true); success &= SUCCEEDED(device->CreateTexture(32, 32, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8R8G8B8, D3DPOOL_DEFAULT, &paletteTexture.get(), 0)); success &= SUCCEEDED(device->CreateTexture(128, 2, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8, D3DPOOL_DEFAULT, &fogTexture.get(), 0)); - fog_needs_update = true; + updateFogTable = true; if (!success) { @@ -199,8 +199,8 @@ void D3DRenderer::postReset() verify(rc); rc = SUCCEEDED(device->CreateTexture(128, 2, 1, D3DUSAGE_DYNAMIC, D3DFMT_A8, D3DPOOL_DEFAULT, &fogTexture.get(), 0)); verify(rc); - fog_needs_update = true; - palette_updated = true; + updateFogTable = true; + updatePalette = true; } void D3DRenderer::Term() @@ -299,9 +299,10 @@ void D3DRenderer::RenderFramebuffer(const FramebufferInfo& info) aspectRatio = getDCFramebufferAspectRatio(); displayFramebuffer(); - DrawOSD(false); + drawOSD(); frameRendered = true; frameRenderedOnce = true; + clearLastFrame = false; theDXContext.setFrameRendered(); } @@ -315,8 +316,10 @@ void D3DRenderer::Process(TA_context* ctx) if (settings.platform.isNaomi2()) throw FlycastException("DirectX 9 doesn't support Naomi 2 games. Select a different graphics API"); - if (KillTex) + if (resetTextureCache) { texCache.Clear(); + resetTextureCache = false; + } texCache.Cleanup(); ta_parse(ctx, false); @@ -1176,9 +1179,10 @@ bool D3DRenderer::Render() { aspectRatio = getOutputFramebufferAspectRatio(); displayFramebuffer(); - DrawOSD(false); + drawOSD(); frameRendered = true; frameRenderedOnce = true; + clearLastFrame = false; theDXContext.setFrameRendered(); } @@ -1279,7 +1283,7 @@ void D3DRenderer::displayFramebuffer() bool D3DRenderer::RenderLastFrame() { - if (!frameRenderedOnce || !theDXContext.isReady()) + if (clearLastFrame || !frameRenderedOnce || !theDXContext.isReady()) return false; backbuffer.reset(); bool rc = SUCCEEDED(device->GetRenderTarget(0, &backbuffer.get())); @@ -1292,9 +1296,9 @@ bool D3DRenderer::RenderLastFrame() void D3DRenderer::updatePaletteTexture() { - if (!palette_updated) + if (!updatePalette) return; - palette_updated = false; + updatePalette = false; D3DLOCKED_RECT rect; bool rc = SUCCEEDED(paletteTexture->LockRect(0, &rect, nullptr, 0)); @@ -1316,9 +1320,9 @@ void D3DRenderer::updatePaletteTexture() void D3DRenderer::updateFogTexture() { - if (!fog_needs_update || !config::Fog) + if (!updateFogTable || !config::Fog) return; - fog_needs_update = false; + updateFogTable = false; u8 temp_tex_buffer[256]; MakeFogTexture(temp_tex_buffer); @@ -1339,9 +1343,9 @@ void D3DRenderer::updateFogTexture() device->SetSamplerState(2, D3DSAMP_MAGFILTER, D3DTEXF_LINEAR); } -void D3DRenderer::DrawOSD(bool clear_screen) +void D3DRenderer::drawOSD() { - theDXContext.setOverlay(!clear_screen); + theDXContext.setOverlay(true); gui_display_osd(); theDXContext.setOverlay(false); } diff --git a/core/rend/dx9/d3d_renderer.h b/core/rend/dx9/d3d_renderer.h index d4e3d1e21..045c7a03e 100644 --- a/core/rend/dx9/d3d_renderer.h +++ b/core/rend/dx9/d3d_renderer.h @@ -106,13 +106,12 @@ struct D3DRenderer : public Renderer bool RenderLastFrame() override; bool Present() override { - if (!frameRendered) + if (!frameRendered || clearLastFrame) return false; imguiDriver->setFrameRendered(); frameRendered = false; return true; } - void DrawOSD(bool clear_screen) override; BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) override; void preReset(); void postReset(); @@ -141,6 +140,7 @@ struct D3DRenderer : public Renderer void prepareRttRenderTarget(u32 texAddress, int& vpWidth, int& vpHeight); void readRttRenderTarget(u32 texAddress); void writeFramebufferToVRAM(); + void drawOSD(); TileClipping setTileClip(u32 tileclip, int rect[4]); RenderStateCache devCache; diff --git a/core/rend/dx9/dxcontext.cpp b/core/rend/dx9/dxcontext.cpp index f57306725..2fb35aa07 100644 --- a/core/rend/dx9/dxcontext.cpp +++ b/core/rend/dx9/dxcontext.cpp @@ -196,10 +196,7 @@ void DXContext::EndImGuiFrame() if (SUCCEEDED(pDevice->BeginScene())) { if (overlayOnly) - { - if (crosshairsNeeded() || config::FloatVMUs) - overlay.draw(settings.display.width, settings.display.height, config::FloatVMUs, true); - } + overlay.draw(settings.display.width, settings.display.height, config::FloatVMUs, true); ImGui_ImplDX9_RenderDrawData(ImGui::GetDrawData()); pDevice->EndScene(); } diff --git a/core/rend/gl4/gldraw.cpp b/core/rend/gl4/gldraw.cpp index 54e1862bd..efc2ae7c6 100644 --- a/core/rend/gl4/gldraw.cpp +++ b/core/rend/gl4/gldraw.cpp @@ -20,7 +20,6 @@ #include "rend/gles/glcache.h" #include "rend/gles/naomi2.h" #include "rend/tileclip.h" -#include "rend/osd.h" #include diff --git a/core/rend/gl4/gles.cpp b/core/rend/gl4/gles.cpp index 7c7ffc589..ff0f64ec8 100644 --- a/core/rend/gl4/gles.cpp +++ b/core/rend/gl4/gles.cpp @@ -19,10 +19,10 @@ #include "gl4.h" #include "rend/gles/glcache.h" #include "rend/transform_matrix.h" -#include "rend/osd.h" #include "glsl.h" #include "gl4naomi2.h" #include "rend/gles/naomi2.h" +#include "rend/gles/quad.h" #ifdef LIBRETRO #include "rend/gles/postprocess.h" @@ -670,7 +670,7 @@ static void gl_create_resources() } GlVertexArray::unbind(); - initQuad(); + gl.quad = std::make_unique(); glCheck(); } @@ -713,7 +713,8 @@ struct OpenGL4Renderer : OpenGLRenderer if (!config::EmulateFramebuffer) { frameRendered = true; - DrawOSD(false); + clearLastFrame = false; + drawOSD(); renderVideoRouting(); } restoreCurrentFramebuffer(); @@ -729,14 +730,6 @@ struct OpenGL4Renderer : OpenGLRenderer } bool renderFrame(int width, int height); - - void DrawOSD(bool clearScreen) override - { - drawVmusAndCrosshairs(width, height); -#ifndef LIBRETRO - gui_display_osd(); -#endif - } }; //setup @@ -771,7 +764,7 @@ bool OpenGL4Renderer::Init() u32 dst[16]; UpscalexBRZ(2, src, dst, 2, 2, false); } - fog_needs_update = true; + updateFogTable = true; TextureCacheData::SetDirectXColorOrder(false); TextureCacheData::setUploadToGPUFlavor(); diff --git a/core/rend/gles/gldraw.cpp b/core/rend/gles/gldraw.cpp index 08cb3c6f9..d79d8e824 100644 --- a/core/rend/gles/gldraw.cpp +++ b/core/rend/gles/gldraw.cpp @@ -1,5 +1,6 @@ #include "glcache.h" #include "gles.h" +#include "quad.h" #include "rend/tileclip.h" #include "rend/osd.h" #include "naomi2.h" @@ -677,7 +678,7 @@ void OpenGLRenderer::RenderFramebuffer(const FramebufferInfo& info) else { glcache.Disable(GL_BLEND); - drawQuad(gl.dcfb.tex, false, false); + gl.quad->draw(gl.dcfb.tex, false, false); } #ifdef LIBRETRO postProcessor.render(glsm_get_current_framebuffer()); @@ -685,8 +686,9 @@ void OpenGLRenderer::RenderFramebuffer(const FramebufferInfo& info) renderLastFrame(); #endif - DrawOSD(false); + drawOSD(); frameRendered = true; + clearLastFrame = false; renderVideoRouting(); restoreCurrentFramebuffer(); } @@ -714,7 +716,7 @@ void writeFramebufferToVRAM() if (gl.fbscaling.framebuffer == nullptr) gl.fbscaling.framebuffer = std::make_unique(scaledW, scaledH); - if (gl.gl_major < 3) + if (gl.bogusBlitFramebuffer) { gl.fbscaling.framebuffer->bind(); glViewport(0, 0, scaledW, scaledH); @@ -725,7 +727,7 @@ void writeFramebufferToVRAM() glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); glcache.Disable(GL_BLEND); - drawQuad(gl.ofbo.framebuffer->getTexture(), false); + gl.quad->draw(gl.ofbo.framebuffer->getTexture(), false); } else { @@ -786,7 +788,7 @@ bool OpenGLRenderer::renderLastFrame() else dx = (int)roundf(settings.display.width * (1 - renderAR / screenAR) / 2.f); - if (gl.gl_major < 3 || config::Rotate90) + if (gl.bogusBlitFramebuffer || config::Rotate90) { glViewport(dx, dy, settings.display.width - dx * 2, settings.display.height - dy * 2); glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo); @@ -810,7 +812,7 @@ bool OpenGLRenderer::renderLastFrame() vertices = sverts; } glcache.Disable(GL_BLEND); - drawQuad(framebuffer->getTexture(), config::Rotate90, true, vertices); + gl.quad->draw(framebuffer->getTexture(), config::Rotate90, true, vertices); } else { @@ -869,7 +871,7 @@ bool OpenGLRenderer::GetLastFrame(std::vector& data, int& width, int& height }; vertices = &rvertices[0][0]; } - drawQuad(framebuffer->getTexture(), config::Rotate90, false, vertices); + gl.quad->draw(framebuffer->getTexture(), config::Rotate90, false, vertices); data.resize(width * height * 3); glPixelStorei(GL_PACK_ALIGNMENT, 1); @@ -997,7 +999,7 @@ static void drawVmuTexture(u8 vmuIndex, int width, int height) }; glcache.Enable(GL_BLEND); glcache.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - drawQuad(vmuTextureId[vmuIndex], false, false, vertices, color); + gl.quad->draw(vmuTextureId[vmuIndex], false, false, vertices, color); } static void updateLightGunTexture() @@ -1014,12 +1016,6 @@ static void updateLightGunTexture() static void drawGunCrosshair(u8 port, int width, int height) { - if (config::CrosshairColor[port] == 0) - return; - if (settings.platform.isConsole() - && config::MapleMainDevices[port] != MDT_LightGun) - return; - auto [x, y] = getCrosshairPosition(port); #ifdef LIBRETRO float halfWidth = lightgun_crosshair_size / 2.f / config::ScreenStretching * 100.f * config::RenderResolution / 480.f; @@ -1050,7 +1046,7 @@ static void drawGunCrosshair(u8 port, int width, int height) }; glcache.Enable(GL_BLEND); glcache.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - drawQuad(lightgunTextureId, false, false, vertices, color); + gl.quad->draw(lightgunTextureId, false, false, vertices, color); } void drawVmusAndCrosshairs(int width, int height) @@ -1072,10 +1068,9 @@ void drawVmusAndCrosshairs(int width, int height) drawVmuTexture(i, width, height); } - if (crosshairsNeeded()) { - for (int i = 0 ; i < 4 ; i++) + for (int i = 0 ; i < 4 ; i++) + if (crosshairNeeded(i)) drawGunCrosshair(i, width, height); - } glCheck(); } diff --git a/core/rend/gles/gles.cpp b/core/rend/gles/gles.cpp index c6807ca9d..d2d7b7ffd 100644 --- a/core/rend/gles/gles.cpp +++ b/core/rend/gles/gles.cpp @@ -1,5 +1,6 @@ #include "glcache.h" #include "gles.h" +#include "quad.h" #include "hw/pvr/ta.h" #ifndef LIBRETRO #include "ui/gui.h" @@ -7,8 +8,6 @@ #include "rend/gles/postprocess.h" #include "vmu_xhair.h" #endif -#include "rend/osd.h" -#include "rend/TexCache.h" #include "rend/transform_matrix.h" #include "wsi/gl_context.h" #include "emulator.h" @@ -395,42 +394,7 @@ void main() } )"; -const char* OSD_VertexShader = R"( -uniform highp vec4 scale; - -in highp vec4 in_pos; -in lowp vec4 in_base; -in mediump vec2 in_uv; - -out lowp vec4 vtx_base; -out mediump vec2 vtx_uv; - -void main() -{ - vtx_base = in_base; - vtx_uv = in_uv; - highp vec4 vpos = in_pos; - - vpos.w = 1.0; - vpos.z = vpos.w; - vpos.xy = vpos.xy * scale.xy - scale.zw; - gl_Position = vpos; -} -)"; - -const char* OSD_Shader = R"( -in lowp vec4 vtx_base; -in mediump vec2 vtx_uv; - -uniform sampler2D tex; -void main() -{ - gl_FragColor = vtx_base * texture(tex, vtx_uv); -} -)"; - void os_VideoRoutingTermGL(); -static void gl_free_osd_resources(); GLCache glcache; gl_ctx gl; @@ -499,7 +463,7 @@ void termGLCommon() #ifdef VIDEO_ROUTING os_VideoRoutingTermGL(); #endif - termQuad(); + gl.quad.reset(); // palette, fog glcache.DeleteTextures(1, &fogTextureId); @@ -509,7 +473,6 @@ void termGLCommon() // RTT gl.rtt.framebuffer.reset(); - gl_free_osd_resources(); gl.ofbo.framebuffer.reset(); glcache.DeleteTextures(1, &gl.dcfb.tex); gl.dcfb.tex = 0; @@ -534,6 +497,8 @@ static void gles_term() gl_delete_shaders(); } +bool testBlitFramebuffer(); + void findGLVersion() { gl.index_type = GL_UNSIGNED_INT; @@ -638,6 +603,17 @@ void findGLVersion() NOTICE_LOG(RENDERER, "Vendor '%s' Renderer '%s' Version '%s'", vendor, renderer, glGetString(GL_VERSION)); while (glGetError() != GL_NO_ERROR) ; + gl.bogusBlitFramebuffer = true; // not supported in GL/GLES 2 +#ifndef GLES2 + if (gl.gl_major >= 3) + { + gl.bogusBlitFramebuffer = !testBlitFramebuffer(); + if (gl.bogusBlitFramebuffer) + WARN_LOG(RENDERER, "glBlitFramebuffer is bogus. Using quad drawer instead"); + else + NOTICE_LOG(RENDERER, "glBlitFramebuffer test successful"); + } +#endif } struct ShaderUniforms_t ShaderUniforms; @@ -890,66 +866,6 @@ bool CompilePipelineShader(PipelineShader* s) return true; } -void OSDVertexArray::defineVtxAttribs() -{ - glEnableVertexAttribArray(VERTEX_POS_ARRAY); - glVertexAttribPointer(VERTEX_POS_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(OSDVertex), (void*)offsetof(OSDVertex, x)); - - glEnableVertexAttribArray(VERTEX_COL_BASE_ARRAY); - glVertexAttribPointer(VERTEX_COL_BASE_ARRAY, 4, GL_UNSIGNED_BYTE, GL_TRUE, sizeof(OSDVertex), (void*)offsetof(OSDVertex, r)); - - glEnableVertexAttribArray(VERTEX_UV_ARRAY); - glVertexAttribPointer(VERTEX_UV_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(OSDVertex), (void*)offsetof(OSDVertex, u)); - - glDisableVertexAttribArray(VERTEX_COL_OFFS_ARRAY); -} - -#ifdef __ANDROID__ -static void gl_load_osd_resources() -{ - OpenGlSource vertexSource; - vertexSource.addSource(VertexCompatShader) - .addSource(OSD_VertexShader); - OpenGlSource fragmentSource; - fragmentSource.addSource(PixelCompatShader) - .addSource(OSD_Shader); - - gl.OSD_SHADER.program = gl_CompileAndLink(vertexSource.generate().c_str(), fragmentSource.generate().c_str()); - gl.OSD_SHADER.scale = glGetUniformLocation(gl.OSD_SHADER.program, "scale"); - glUniform1i(glGetUniformLocation(gl.OSD_SHADER.program, "tex"), 0); //bind osd texture to slot 0 - - if (gl.OSD_SHADER.osd_tex == 0) - { - int width, height; - u8 *image_data = loadOSDButtons(width, height); - //Now generate the OpenGL texture object - gl.OSD_SHADER.osd_tex = glcache.GenTexture(); - glcache.BindTexture(GL_TEXTURE_2D, gl.OSD_SHADER.osd_tex); - glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, width, height, 0, GL_RGBA, GL_UNSIGNED_BYTE, (GLvoid *)image_data); - glcache.TexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); - - delete[] image_data; - } - gl.OSD_SHADER.geometry = std::make_unique(GL_ARRAY_BUFFER); -} -#endif - -static void gl_free_osd_resources() -{ - if (gl.OSD_SHADER.program != 0) - { - glcache.DeleteProgram(gl.OSD_SHADER.program); - gl.OSD_SHADER.program = 0; - } - - if (gl.OSD_SHADER.osd_tex != 0) { - glcache.DeleteTextures(1, &gl.OSD_SHADER.osd_tex); - gl.OSD_SHADER.osd_tex = 0; - } - gl.OSD_SHADER.geometry.reset(); - gl.OSD_SHADER.vao.term(); -} - static void create_modvol_shader() { if (gl.modvol_shader.program != 0) @@ -1000,7 +916,7 @@ static void gl_create_resources() gl.vbo.modvols = std::make_unique(GL_ARRAY_BUFFER); gl.vbo.idxs = std::make_unique(GL_ELEMENT_ARRAY_BUFFER); - initQuad(); + gl.quad = std::make_unique(); } GLuint gl_CompileShader(const char* shader,GLuint type); @@ -1065,7 +981,7 @@ bool OpenGLRenderer::Init() u32 dst[16]; UpscalexBRZ(2, src, dst, 2, 2, false); } - fog_needs_update = true; + updateFogTable = true; TextureCacheData::SetDirectXColorOrder(false); TextureCacheData::setUploadToGPUFlavor(); @@ -1119,68 +1035,12 @@ static void updatePaletteTexture(GLenum texture_slot) glActiveTexture(GL_TEXTURE0); } -void OpenGLRenderer::DrawOSD(bool clear_screen) +void OpenGLRenderer::drawOSD() { drawVmusAndCrosshairs(width, height); - #ifndef LIBRETRO gui_display_osd(); -#ifdef __ANDROID__ - if (gl.OSD_SHADER.osd_tex == 0) - gl_load_osd_resources(); - if (gl.OSD_SHADER.osd_tex != 0) - { - glcache.Disable(GL_SCISSOR_TEST); - glViewport(0, 0, settings.display.width, settings.display.height); - - if (clear_screen) - { - glcache.ClearColor(0.7f, 0.7f, 0.7f, 1.f); - glClear(GL_COLOR_BUFFER_BIT); - renderLastFrame(); - glViewport(0, 0, settings.display.width, settings.display.height); - } - - glcache.UseProgram(gl.OSD_SHADER.program); - - float scale_h = settings.display.height / 480.f; - float offs_x = (settings.display.width - scale_h * 640.f) / 2.f; - float scale[4]; - scale[0] = 2.f / (settings.display.width / scale_h); - scale[1]= -2.f / 480.f; - scale[2]= 1.f - 2.f * offs_x / settings.display.width; - scale[3]= -1.f; - glUniform4fv(gl.OSD_SHADER.scale, 1, scale); - - glActiveTexture(GL_TEXTURE0); - glcache.BindTexture(GL_TEXTURE_2D, gl.OSD_SHADER.osd_tex); - - glBindFramebuffer(GL_FRAMEBUFFER, gl.ofbo.origFbo); - - const std::vector& osdVertices = GetOSDVertices(); - gl.OSD_SHADER.geometry->update(osdVertices.data(), osdVertices.size() * sizeof(OSDVertex)); - gl.OSD_SHADER.vao.bind(gl.OSD_SHADER.geometry.get()); - - glcache.Enable(GL_BLEND); - glcache.Disable(GL_DEPTH_TEST); - glcache.BlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); - - glcache.DepthMask(false); - glcache.DepthFunc(GL_ALWAYS); - - glcache.Disable(GL_CULL_FACE); - int dfa = osdVertices.size() / 4; - - for (int i = 0; i < dfa; i++) - glDrawArrays(GL_TRIANGLE_STRIP, i * 4, 4); - - glCheck(); - if (clear_screen) - imguiDriver->setFrameRendered(); - } #endif -#endif - GlVertexArray::unbind(); } void OpenGLRenderer::Process(TA_context* ctx) @@ -1188,19 +1048,19 @@ void OpenGLRenderer::Process(TA_context* ctx) if (gl.gl_major < 3 && settings.platform.isNaomi2()) throw FlycastException("OpenGL ES 3.0+ required for Naomi 2"); - if (KillTex) + if (resetTextureCache) { TexCache.Clear(); + resetTextureCache = false; + } TexCache.Cleanup(); - if (fog_needs_update && config::Fog) - { - fog_needs_update = false; + if (updateFogTable && config::Fog) { + updateFogTable = false; updateFogTexture((u8 *)FOG_TABLE, getFogTextureSlot(), gl.single_channel_format); } - if (palette_updated) - { + if (updatePalette) { updatePaletteTexture(getPaletteTextureSlot()); - palette_updated = false; + updatePalette = false; } ta_parse(ctx, gl.prim_restart_fixed_supported || gl.prim_restart_supported); } @@ -1501,10 +1361,10 @@ bool OpenGLRenderer::Render() if (!config::EmulateFramebuffer) { frameRendered = true; - DrawOSD(false); + clearLastFrame = false; + drawOSD(); renderVideoRouting(); } - restoreCurrentFramebuffer(); return true; diff --git a/core/rend/gles/gles.h b/core/rend/gles/gles.h index ce1bd6423..13f3a1d71 100755 --- a/core/rend/gles/gles.h +++ b/core/rend/gles/gles.h @@ -223,11 +223,7 @@ class ModvolVertexArray final : public GlVertexArray void defineVtxAttribs() override; }; -class OSDVertexArray final : public GlVertexArray -{ -protected: - void defineVtxAttribs() override; -}; +class GlQuadDrawer; struct gl_ctx { @@ -254,15 +250,6 @@ struct gl_ctx std::unordered_map shaders; - struct - { - GLuint program; - GLint scale; - OSDVertexArray vao; - std::unique_ptr geometry; - GLuint osd_tex; - } OSD_SHADER; - struct { MainVertexArray mainVAO; @@ -308,6 +295,7 @@ struct gl_ctx std::unique_ptr framebuffer; } videorouting; + std::unique_ptr quad; const char *gl_version; const char *glsl_version_header; int gl_major; @@ -323,6 +311,7 @@ struct gl_ctx bool border_clamp_supported; bool prim_restart_supported; bool prim_restart_fixed_supported; + bool bogusBlitFramebuffer; size_t get_index_size() { return index_type == GL_UNSIGNED_INT ? sizeof(u32) : sizeof(u16); } }; @@ -513,6 +502,8 @@ struct OpenGLRenderer : Renderer bool RenderLastFrame() override { + if (clearLastFrame) + return false; saveCurrentFramebuffer(); bool ret = renderLastFrame(); restoreCurrentFramebuffer(); @@ -521,13 +512,11 @@ struct OpenGLRenderer : Renderer } bool GetLastFrame(std::vector& data, int& width, int& height) override; - void DrawOSD(bool clear_screen) override; - BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) override; bool Present() override { - if (!frameRendered) + if (!frameRendered || clearLastFrame) return false; #ifndef LIBRETRO imguiDriver->setFrameRendered(); @@ -558,6 +547,7 @@ struct OpenGLRenderer : Renderer bool renderLastFrame(); void renderVideoRouting(); + void drawOSD(); private: bool renderFrame(int width, int height); @@ -569,10 +559,6 @@ struct OpenGLRenderer : Renderer void initVideoRoutingFrameBuffer(); }; -void initQuad(); -void termQuad(); -void drawQuad(GLuint texId, bool rotate = false, bool swapY = false, const float *coords = nullptr, const float *color = nullptr); - extern const char* ShaderCompatSource; extern const char *VertexCompatShader; extern const char *PixelCompatShader; diff --git a/core/rend/gles/gltex.cpp b/core/rend/gles/gltex.cpp index e71e8992f..67efc8d07 100644 --- a/core/rend/gles/gltex.cpp +++ b/core/rend/gles/gltex.cpp @@ -1,7 +1,6 @@ #include "glcache.h" #include "gles.h" #include "hw/pvr/pvr_mem.h" -#include "rend/TexCache.h" #include @@ -459,3 +458,70 @@ GlFramebuffer::~GlFramebuffer() glcache.DeleteTextures(1, &texture); glDeleteRenderbuffers(1, &colorBuffer); } + +bool testBlitFramebuffer() +{ +#ifdef GLES2 + return false; +#else + GLint ofbo = 0; + glGetIntegerv(GL_FRAMEBUFFER_BINDING, (GLint *)&ofbo); + + GLuint texture = glcache.GenTexture(); + glcache.BindTexture(GL_TEXTURE_2D, texture); + + u32 data[32 * 32]; + // Lower half is red + for (int i = 0; i < 16 * 32; i++) + data[i] = 0xFF0000FF; + // Upper half is green + for (int i = 16 * 32; i < 32 * 32; i++) + data[i] = 0xFF00FF00; // green + glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 32, 32, 0, GL_RGBA, GL_UNSIGNED_BYTE, data); + GlFramebuffer src(32, 32, false, texture); + + GlFramebuffer dest(32, 64, false, true); + + src.bind(GL_READ_FRAMEBUFFER); + GLenum error = glCheckFramebufferStatus(GL_READ_FRAMEBUFFER); + if (error != GL_FRAMEBUFFER_COMPLETE) { + WARN_LOG(RENDERER, "testBlitFramebuffer: Source framebuffer error %x", error); + return false; + } + dest.bind(GL_DRAW_FRAMEBUFFER); + error = glCheckFramebufferStatus(GL_DRAW_FRAMEBUFFER); + if (error != GL_FRAMEBUFFER_COMPLETE) { + WARN_LOG(RENDERER, "testBlitFramebuffer: Destination framebuffer error %x", error); + return false; + } + + glcache.Disable(GL_SCISSOR_TEST); + glcache.ClearColor(0, 0, 0, 1); + glClear(GL_COLOR_BUFFER_BIT); + // Apple A8X chokes on negative coordinates + // Many mobile GPUs don't support dstY0 > dstY1 and the resulting image is flipped vertically + glBlitFramebuffer(0, -1, 32, 31, 0, 64, 32, 0, GL_COLOR_BUFFER_BIT, GL_NEAREST); + + u32 outdata[32 * 64]; + dest.bind(GL_READ_FRAMEBUFFER); + glPixelStorei(GL_PACK_ALIGNMENT, 1); + glReadPixels(0, 0, 32, 64, GL_RGBA, GL_UNSIGNED_BYTE, outdata); + glBindFramebuffer(GL_FRAMEBUFFER, ofbo); + error = glGetError(); + if (error != GL_NO_ERROR) { + WARN_LOG(RENDERER, "testBlitFramebuffer: OpenGL error %x", error); + return false; + } + // Now lower half should be green (except last line due to srcY0 == -1) + if (outdata[32 * 2] != 0xFF00FF00) { // green + WARN_LOG(RENDERER, "testBlitFramebuffer: Expected 0xFF00FF00 but was %08x", outdata[0]); + return false; + } + // And upper half should be red + if (outdata[31 * 64 - 1] != 0xFF0000FF) { // red + WARN_LOG(RENDERER, "testBlitFramebuffer: Expected 0xFF0000FF but was %08x", outdata[32 * 64 - 1]); + return false; + } + return true; +#endif +} diff --git a/core/rend/gles/postprocess.cpp b/core/rend/gles/postprocess.cpp index f0c2a2357..d6029d324 100644 --- a/core/rend/gles/postprocess.cpp +++ b/core/rend/gles/postprocess.cpp @@ -20,6 +20,7 @@ #include "postprocess.h" #ifdef LIBRETRO +#include "quad.h" #include PostProcessor postProcessor; @@ -284,7 +285,7 @@ void PostProcessor::render(GLuint output_fbo) if (!config::PowerVR2Filter) { // Just handle shifting and Y flipping - if (gl.gl_major < 3) + if (gl.bogusBlitFramebuffer) { glBindFramebuffer(GL_FRAMEBUFFER, output_fbo); glViewport(0, 0, framebuffer->getWidth(), framebuffer->getHeight()); @@ -303,7 +304,7 @@ void PostProcessor::render(GLuint output_fbo) vertices[1] = vertices[11] = -1.f - gl.ofbo.shiftY * 2.f / framebuffer->getHeight(); vertices[6] = vertices[16] = vertices[1] + 2; glcache.Disable(GL_BLEND); - drawQuad(framebuffer->getTexture(), false, false, vertices); + gl.quad->draw(framebuffer->getTexture(), false, false, vertices); } else { diff --git a/core/rend/gles/quad.cpp b/core/rend/gles/quad.cpp index 8c6047e97..d331b703c 100644 --- a/core/rend/gles/quad.cpp +++ b/core/rend/gles/quad.cpp @@ -16,7 +16,7 @@ You should have received a copy of the GNU General Public License along with Flycast. If not, see . */ -#include "gles.h" +#include "quad.h" static const char* VertexShader = R"( in highp vec3 in_pos; @@ -46,68 +46,34 @@ void main() } )"; -class QuadVertexArray final : public GlVertexArray +GlQuadDrawer::GlQuadDrawer() { -protected: - void defineVtxAttribs() override + OpenGlSource fragmentShader; + fragmentShader.addSource(PixelCompatShader) + .addSource(FragmentShader); + OpenGlSource vertexShader; + vertexShader.addConstant("ROTATE", 0) + .addSource(VertexCompatShader) + .addSource(VertexShader); + + const std::string fragmentGlsl = fragmentShader.generate(); + shader = gl_CompileAndLink(vertexShader.generate().c_str(), fragmentGlsl.c_str()); + GLint tex = glGetUniformLocation(shader, "tex"); + glUniform1i(tex, 0); // texture 0 + tintUniform = glGetUniformLocation(shader, "tint"); + + vertexShader.setConstant("ROTATE", 1); + rot90shader = gl_CompileAndLink(vertexShader.generate().c_str(), fragmentGlsl.c_str()); + tex = glGetUniformLocation(rot90shader, "tex"); + glUniform1i(tex, 0); // texture 0 + rot90TintUniform = glGetUniformLocation(rot90shader, "tint"); + + quadIndexBuffer = std::make_unique(GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW); + static const GLushort indices[] = { 0, 1, 2, 1, 3 }; + quadIndexBuffer->update(indices, sizeof(indices)); + + quadBuffer = std::make_unique(GL_ARRAY_BUFFER, GL_STATIC_DRAW); { - glEnableVertexAttribArray(VERTEX_POS_ARRAY); - glVertexAttribPointer(VERTEX_POS_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (void*)0); - glEnableVertexAttribArray(VERTEX_UV_ARRAY); - glVertexAttribPointer(VERTEX_UV_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (void*)(sizeof(float) * 3)); - glDisableVertexAttribArray(VERTEX_COL_BASE_ARRAY); - glDisableVertexAttribArray(VERTEX_COL_OFFS_ARRAY); - glDisableVertexAttribArray(VERTEX_COL_BASE1_ARRAY); - glDisableVertexAttribArray(VERTEX_COL_OFFS1_ARRAY); - glDisableVertexAttribArray(VERTEX_UV1_ARRAY); - } -}; - -static GLuint shader; -static GLint tintUniform; -static GLuint rot90shader; -static GLint rot90TintUniform; -static QuadVertexArray quadVertexArray; -static QuadVertexArray quadVertexArraySwapY; -static std::unique_ptr quadBuffer; -static std::unique_ptr quadBufferSwapY; -static std::unique_ptr quadIndexBuffer; -static std::unique_ptr quadBufferCustom; -static QuadVertexArray quadVertexArrayCustom; - -void initQuad() -{ - if (shader == 0) - { - OpenGlSource fragmentShader; - fragmentShader.addSource(PixelCompatShader) - .addSource(FragmentShader); - OpenGlSource vertexShader; - vertexShader.addConstant("ROTATE", 0) - .addSource(VertexCompatShader) - .addSource(VertexShader); - - const std::string fragmentGlsl = fragmentShader.generate(); - shader = gl_CompileAndLink(vertexShader.generate().c_str(), fragmentGlsl.c_str()); - GLint tex = glGetUniformLocation(shader, "tex"); - glUniform1i(tex, 0); // texture 0 - tintUniform = glGetUniformLocation(shader, "tint"); - - vertexShader.setConstant("ROTATE", 1); - rot90shader = gl_CompileAndLink(vertexShader.generate().c_str(), fragmentGlsl.c_str()); - tex = glGetUniformLocation(rot90shader, "tex"); - glUniform1i(tex, 0); // texture 0 - rot90TintUniform = glGetUniformLocation(rot90shader, "tint"); - } - if (quadIndexBuffer == nullptr) - { - quadIndexBuffer = std::make_unique(GL_ELEMENT_ARRAY_BUFFER, GL_STATIC_DRAW); - static const GLushort indices[] = { 0, 1, 2, 1, 3 }; - quadIndexBuffer->update(indices, sizeof(indices)); - } - if (quadBuffer == nullptr) - { - quadBuffer = std::make_unique(GL_ARRAY_BUFFER, GL_STATIC_DRAW); float vertices[4][5] = { { -1.f, 1.f, 1.f, 0.f, 1.f }, { -1.f, -1.f, 1.f, 0.f, 0.f }, @@ -116,9 +82,8 @@ void initQuad() }; quadBuffer->update(vertices, sizeof(vertices)); } - if (quadBufferSwapY == nullptr) + quadBufferSwapY = std::make_unique(GL_ARRAY_BUFFER, GL_STATIC_DRAW); { - quadBufferSwapY = std::make_unique(GL_ARRAY_BUFFER, GL_STATIC_DRAW); float vertices[4][5] = { { -1.f, 1.f, 1.f, 0.f, 0.f }, { -1.f, -1.f, 1.f, 0.f, 1.f }, @@ -127,12 +92,11 @@ void initQuad() }; quadBufferSwapY->update(vertices, sizeof(vertices)); } - if (quadBufferCustom == nullptr) - quadBufferCustom = std::make_unique(GL_ARRAY_BUFFER); + quadBufferCustom = std::make_unique(GL_ARRAY_BUFFER); glCheck(); } -void termQuad() +GlQuadDrawer::~GlQuadDrawer() { quadIndexBuffer.reset(); quadBuffer.reset(); @@ -149,8 +113,7 @@ void termQuad() rot90shader = 0; } -// coords is an optional array of 20 floats (4 vertices with x,y,z,u,v each) -void drawQuad(GLuint texId, bool rotate, bool swapY, const float *coords, const float *color) +void GlQuadDrawer::draw(GLuint texId, bool rotate, bool swapY, const float *coords, const float *color) { glcache.Disable(GL_SCISSOR_TEST); glcache.Disable(GL_DEPTH_TEST); diff --git a/core/rend/gles/quad.h b/core/rend/gles/quad.h new file mode 100644 index 000000000..d2c78cf3e --- /dev/null +++ b/core/rend/gles/quad.h @@ -0,0 +1,60 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . + */ +#pragma once +#include "gles.h" + +class GlQuadDrawer +{ +public: + GlQuadDrawer(); + ~GlQuadDrawer(); + + // coords is an optional array of 20 floats (4 vertices with x,y,z,u,v each) + void draw(GLuint texId, bool rotate = false, bool swapY = false, const float *coords = nullptr, const float *color = nullptr); + +private: + class VertexArray final : public GlVertexArray + { + protected: + void defineVtxAttribs() override + { + glEnableVertexAttribArray(VERTEX_POS_ARRAY); + glVertexAttribPointer(VERTEX_POS_ARRAY, 3, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (void*)0); + glEnableVertexAttribArray(VERTEX_UV_ARRAY); + glVertexAttribPointer(VERTEX_UV_ARRAY, 2, GL_FLOAT, GL_FALSE, sizeof(float) * 5, (void*)(sizeof(float) * 3)); + glDisableVertexAttribArray(VERTEX_COL_BASE_ARRAY); + glDisableVertexAttribArray(VERTEX_COL_OFFS_ARRAY); + glDisableVertexAttribArray(VERTEX_COL_BASE1_ARRAY); + glDisableVertexAttribArray(VERTEX_COL_OFFS1_ARRAY); + glDisableVertexAttribArray(VERTEX_UV1_ARRAY); + } + }; + + GLuint shader; + GLint tintUniform; + GLuint rot90shader; + GLint rot90TintUniform; + VertexArray quadVertexArray; + VertexArray quadVertexArraySwapY; + std::unique_ptr quadBuffer; + std::unique_ptr quadBufferSwapY; + std::unique_ptr quadIndexBuffer; + std::unique_ptr quadBufferCustom; + VertexArray quadVertexArrayCustom; +}; diff --git a/core/rend/osd.cpp b/core/rend/osd.cpp index b10c5493c..a88d8d7d6 100644 --- a/core/rend/osd.cpp +++ b/core/rend/osd.cpp @@ -15,166 +15,10 @@ along with reicast. If not, see . */ #include "osd.h" -#include "types.h" -#include "input/gamepad.h" -#include "input/gamepad_device.h" #include "stdclass.h" #ifdef LIBRETRO #include "vmu_xhair.h" #endif -#include "oslib/resources.h" - -#include - -#ifndef LIBRETRO - -#define OSD_TEX_W 512 -#define OSD_TEX_H 256 - -#ifdef __ANDROID__ -extern float vjoy_pos[15][8]; -#else - -static float vjoy_pos[15][8]= -{ - {24+0,24+64,64,64}, //LEFT - {24+64,24+0,64,64}, //UP - {24+128,24+64,64,64}, //RIGHT - {24+64,24+128,64,64}, //DOWN - - {440+0,280+64,64,64}, //X - {440+64,280+0,64,64}, //Y - {440+128,280+64,64,64}, //B - {440+64,280+128,64,64}, //A - - {320-32,360+32,64,64}, //Start - - {440,200,90,64}, //LT - {542,200,90,64}, //RT - - {-24,128+224,128,128}, //ANALOG_RING - {96,320,64,64}, //ANALOG_POINT - {320-32,24,64,64}, // FFORWARD - {1} // VJOY_VISIBLE -}; -#endif // !__ANDROID__ - -static const float vjoy_sz[2][15] = { - { 64,64,64,64, 64,64,64,64, 64, 90,90, 128, 64, 64 }, - { 64,64,64,64, 64,64,64,64, 64, 64,64, 128, 64, 64 }, -}; - -static std::vector osdVertices; - -void HideOSD() -{ - vjoy_pos[VJOY_VISIBLE][0] = 0; -} - -static void DrawButton(const float xy[8], u32 state) -{ - OSDVertex vtx; - - vtx.r = vtx.g = vtx.b = (0x7F - 0x40 * state / 255) * vjoy_pos[VJOY_VISIBLE][0]; - vtx.a = (100 - config::VirtualGamepadTransparency) * 0xff / 100 * vjoy_pos[VJOY_VISIBLE][4]; - vjoy_pos[VJOY_VISIBLE][4] += (vjoy_pos[VJOY_VISIBLE][0] - vjoy_pos[VJOY_VISIBLE][4]) / 2; - - vtx.x = xy[0]; vtx.y = xy[1]; - vtx.u = xy[4]; vtx.v = xy[5]; - osdVertices.push_back(vtx); - - vtx.x = xy[0] + xy[2]; vtx.y = xy[1]; - vtx.u = xy[6]; vtx.v = xy[5]; - osdVertices.push_back(vtx); - - vtx.x = xy[0]; vtx.y = xy[1] + xy[3]; - vtx.u = xy[4]; vtx.v = xy[7]; - osdVertices.push_back(vtx); - - vtx.x = xy[0] + xy[2]; vtx.y = xy[1] + xy[3]; - vtx.u = xy[6]; vtx.v = xy[7]; - osdVertices.push_back(vtx); -} - -static void DrawButton2(const float xy[8], bool state) -{ - DrawButton(xy, state ? 0 : 255); -} - -const std::vector& GetOSDVertices() -{ - osdVertices.reserve(std::size(vjoy_pos) * 4); - osdVertices.clear(); - DrawButton2(vjoy_pos[0], kcode[0] & DC_DPAD_LEFT); - DrawButton2(vjoy_pos[1], kcode[0] & DC_DPAD_UP); - DrawButton2(vjoy_pos[2], kcode[0] & DC_DPAD_RIGHT); - DrawButton2(vjoy_pos[3], kcode[0] & DC_DPAD_DOWN); - - DrawButton2(vjoy_pos[4], kcode[0] & (settings.platform.isConsole() ? DC_BTN_X : DC_BTN_C)); - DrawButton2(vjoy_pos[5], kcode[0] & (settings.platform.isConsole() ? DC_BTN_Y : DC_BTN_X)); - DrawButton2(vjoy_pos[6], kcode[0] & DC_BTN_B); - DrawButton2(vjoy_pos[7], kcode[0] & DC_BTN_A); - - DrawButton2(vjoy_pos[8], kcode[0] & DC_BTN_START); - - DrawButton(vjoy_pos[9], lt[0] >> 8); - - DrawButton(vjoy_pos[10], rt[0] >> 8); - - DrawButton2(vjoy_pos[11], true); - DrawButton2(vjoy_pos[12], false); - - DrawButton2(vjoy_pos[13], false); - - return osdVertices; -} - -static void setVjoyUV() -{ - float u = 0; - float v = 0; - - for (int i = 0; i < VJOY_VISIBLE; i++) - { - //umin, vmin, umax, vmax - vjoy_pos[i][4] = (u + 1) / OSD_TEX_W; - vjoy_pos[i][5] = 1.f - (v + 1) / OSD_TEX_H; - - vjoy_pos[i][6] = (u + vjoy_sz[0][i] - 1) / OSD_TEX_W; - vjoy_pos[i][7] = 1.f - (v + vjoy_sz[1][i] - 1) / OSD_TEX_H; - - u += vjoy_sz[0][i]; - if (u >= OSD_TEX_W) - { - u -= OSD_TEX_W; - v += vjoy_sz[1][i]; - } - } -} - -static OnLoad setVjoyUVOnLoad(&setVjoyUV); - -u8 *loadOSDButtons(int &width, int &height) -{ - int n; - stbi_set_flip_vertically_on_load(1); - - FILE *file = nowide::fopen(get_readonly_data_path("buttons.png").c_str(), "rb"); - u8 *image_data = nullptr; - if (file != nullptr) - { - image_data = stbi_load_from_file(file, &width, &height, &n, STBI_rgb_alpha); - std::fclose(file); - } - if (image_data == nullptr) - { - size_t size; - std::unique_ptr data = resource::load("picture/buttons.png", size); - image_data = stbi_load_from_memory(data.get(), size, &width, &height, &n, STBI_rgb_alpha); - } - return image_data; -} -#endif // LIBRETRO u32 vmu_lcd_data[8][48 * 32]; bool vmu_lcd_status[8]; diff --git a/core/rend/osd.h b/core/rend/osd.h index b3a819d6d..f88a13846 100644 --- a/core/rend/osd.h +++ b/core/rend/osd.h @@ -18,21 +18,6 @@ #include "types.h" #include "cfg/option.h" -#include - -#define VJOY_VISIBLE 14 - -struct OSDVertex -{ - float x, y; - float u, v; - u8 r, g, b, a; -}; - -const std::vector& GetOSDVertices(); - -u8 *loadOSDButtons(int &width, int &height); -void HideOSD(); // VMUs extern u32 vmu_lcd_data[8][48 * 32]; @@ -45,14 +30,23 @@ void push_vmu_screen(int bus_id, int bus_port, u8* buffer); const u32 *getCrosshairTextureData(); std::pair getCrosshairPosition(int playerNum); -static inline bool crosshairsNeeded() +static inline bool crosshairNeeded(int port) { - if (config::CrosshairColor[0] == 0 && config::CrosshairColor[1] == 0 - && config::CrosshairColor[2] == 0 && config::CrosshairColor[3] == 0) + if (port < 0 || port >= 4) return false; - if (settings.platform.isArcade() && !settings.input.lightgunGame) - // not a lightgun game + if (config::CrosshairColor[port] == 0) return false; + if (settings.platform.isArcade()) + { + // Arcade game: only for lightgun games and P1 or P2 (no known 4-player lightgun or touchscreen game for now) + if (!settings.input.lightgunGame || (port >= 2 && !settings.input.fourPlayerGames)) + return false; + } + else { + // Console game + if (config::MapleMainDevices[port] != MDT_LightGun) + return false; + } return true; } diff --git a/core/rend/texconv.cpp b/core/rend/texconv.cpp new file mode 100644 index 000000000..6b6cae678 --- /dev/null +++ b/core/rend/texconv.cpp @@ -0,0 +1,539 @@ +/* + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "texconv.h" +#include "cfg/option.h" +#include "hw/pvr/Renderer_if.h" +#include +#include + +const u8 *vq_codebook; +u32 palette_index; +u32 palette16_ram[1024]; +u32 palette32_ram[1024]; +u32 pal_hash_256[4]; +u32 pal_hash_16[64]; +extern bool pal_needs_update; + +u32 detwiddle[2][11][1024]; +//input : address in the yyyyyxxxxx format +//output : address in the xyxyxyxy format +//U : x resolution , V : y resolution +//twiddle works on 64b words + +static u32 twiddle_slow(u32 x, u32 y, u32 x_sz, u32 y_sz) +{ + u32 rv = 0; // low 2 bits are directly passed -> needs some misc stuff to work.However + // Pvr internally maps the 64b banks "as if" they were twiddled + + u32 sh = 0; + x_sz >>= 1; + y_sz >>= 1; + while (x_sz != 0 || y_sz != 0) + { + if (y_sz != 0) + { + u32 temp = y & 1; + rv |= temp << sh; + + y_sz >>= 1; + y >>= 1; + sh++; + } + if (x_sz != 0) + { + u32 temp = x & 1; + rv |= temp << sh; + + x_sz >>= 1; + x >>= 1; + sh++; + } + } + return rv; +} + +static OnLoad _([]() { + constexpr u32 x_sz = 1024; + for (u32 s = 0; s < 11; s++) + { + u32 y_sz = 1 << s; + for (u32 i = 0; i < x_sz; i++) { + detwiddle[0][s][i] = twiddle_slow(i, 0, x_sz, y_sz); + detwiddle[1][s][i] = twiddle_slow(0, i, y_sz, x_sz); + } + } +}); + +void palette_update() +{ + if (!pal_needs_update) + return; + pal_needs_update = false; + rend_updatePalette(); + + if (!isDirectX(config::RendererType)) + { + switch (PAL_RAM_CTRL & 3) + { + case 0: + for (int i = 0; i < 1024; i++) { + palette16_ram[i] = Unpacker1555::unpack(PALETTE_RAM[i]); + palette32_ram[i] = Unpacker1555_32::unpack(PALETTE_RAM[i]); + } + break; + + case 1: + for (int i = 0; i < 1024; i++) { + palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); + palette32_ram[i] = Unpacker565_32::unpack(PALETTE_RAM[i]); + } + break; + + case 2: + for (int i = 0; i < 1024; i++) { + palette16_ram[i] = Unpacker4444::unpack(PALETTE_RAM[i]); + palette32_ram[i] = Unpacker4444_32::unpack(PALETTE_RAM[i]); + } + break; + + case 3: + for (int i = 0; i < 1024; i++) + palette32_ram[i] = Unpacker8888::unpack(PALETTE_RAM[i]); + break; + } + } + else + { + switch (PAL_RAM_CTRL & 3) + { + case 0: + for (int i = 0; i < 1024; i++) { + palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); + palette32_ram[i] = Unpacker1555_32::unpack(PALETTE_RAM[i]); + } + break; + + case 1: + for (int i = 0; i < 1024; i++) { + palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); + palette32_ram[i] = Unpacker565_32::unpack(PALETTE_RAM[i]); + } + break; + + case 2: + for (int i = 0; i < 1024; i++) { + palette16_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); + palette32_ram[i] = Unpacker4444_32::unpack(PALETTE_RAM[i]); + } + break; + + case 3: + for (int i = 0; i < 1024; i++) + palette32_ram[i] = UnpackerNop::unpack(PALETTE_RAM[i]); + break; + } + } + for (std::size_t i = 0; i < std::size(pal_hash_16); i++) + pal_hash_16[i] = XXH32(&PALETTE_RAM[i << 4], 16 * 4, 7); + for (std::size_t i = 0; i < std::size(pal_hash_256); i++) + pal_hash_256[i] = XXH32(&PALETTE_RAM[i << 8], 256 * 4, 7); +} + +template +inline static u32 YUV422(s32 Y, s32 Yu, s32 Yv) +{ + Yu -= 128; + Yv -= 128; + + s32 R = Y + Yv * 11 / 8; // Y + (Yv-128) * (11/8) ? + s32 G = Y - (Yu * 11 + Yv * 22) / 32; // Y - (Yu-128) * (11/8) * 0.25 - (Yv-128) * (11/8) * 0.5 ? + s32 B = Y + Yu * 110 / 64; // Y + (Yu-128) * (11/8) * 1.25 ? + + return Packer::pack(std::clamp(R, 0, 255), std::clamp(G, 0, 255), std::clamp(B, 0, 255), 0xFF); +} + +static u32 twop(u32 x, u32 y, u32 bcx, u32 bcy) { + return detwiddle[0][bcy][x] + detwiddle[1][bcx][y]; +} + +template +struct ConvertPlanar +{ + using unpacked_type = typename Unpacker::unpacked_type; + static constexpr u32 xpp = 4; + static constexpr u32 ypp = 1; + static void Convert(PixelBuffer *pb, const u8 *data) + { + const u16 *p_in = (const u16 *)data; + pb->prel(0, Unpacker::unpack(p_in[0])); + pb->prel(1, Unpacker::unpack(p_in[1])); + pb->prel(2, Unpacker::unpack(p_in[2])); + pb->prel(3, Unpacker::unpack(p_in[3])); + } +}; + +template +struct ConvertPlanarYUV +{ + using unpacked_type = u32; + static constexpr u32 xpp = 4; + static constexpr u32 ypp = 1; + static void Convert(PixelBuffer *pb, const u8 *data) + { + //convert 4x1 4444 to 4x1 8888 + const u32 *p_in = (const u32 *)data; + + + s32 Y0 = (p_in[0] >> 8) & 255; // + s32 Yu = (p_in[0] >> 0) & 255; //p_in[0] + s32 Y1 = (p_in[0] >> 24) & 255; //p_in[3] + s32 Yv = (p_in[0] >> 16) & 255; //p_in[2] + + //0,0 + pb->prel(0, YUV422(Y0, Yu, Yv)); + //1,0 + pb->prel(1, YUV422(Y1, Yu, Yv)); + + //next 4 bytes + p_in += 1; + + Y0 = (p_in[0] >> 8) & 255; // + Yu = (p_in[0] >> 0) & 255; //p_in[0] + Y1 = (p_in[0] >> 24) & 255; //p_in[3] + Yv = (p_in[0] >> 16) & 255; //p_in[2] + + //0,0 + pb->prel(2, YUV422(Y0, Yu, Yv)); + //1,0 + pb->prel(3, YUV422(Y1, Yu, Yv)); + } +}; + +template +struct ConvertTwiddle +{ + using unpacked_type = typename Unpacker::unpacked_type; + static constexpr u32 xpp = 2; + static constexpr u32 ypp = 2; + static void Convert(PixelBuffer *pb, const u8 *data) + { + const u16 *p_in = (const u16 *)data; + pb->prel(0, 0, Unpacker::unpack(p_in[0])); + pb->prel(0, 1, Unpacker::unpack(p_in[1])); + pb->prel(1, 0, Unpacker::unpack(p_in[2])); + pb->prel(1, 1, Unpacker::unpack(p_in[3])); + } +}; + +template +struct ConvertTwiddleYUV +{ + using unpacked_type = u32; + static constexpr u32 xpp = 2; + static constexpr u32 ypp = 2; + static void Convert(PixelBuffer *pb, const u8 *data) + { + //convert 4x1 4444 to 4x1 8888 + const u16* p_in = (const u16 *)data; + + s32 Y0 = (p_in[0] >> 8) & 255; // + s32 Yu = (p_in[0] >> 0) & 255; //p_in[0] + s32 Y1 = (p_in[2] >> 8) & 255; //p_in[3] + s32 Yv = (p_in[2] >> 0) & 255; //p_in[2] + + //0,0 + pb->prel(0, 0, YUV422(Y0, Yu, Yv)); + //1,0 + pb->prel(1, 0, YUV422(Y1, Yu, Yv)); + + //next 4 bytes + //p_in+=2; + + Y0 = (p_in[1] >> 8) & 255; // + Yu = (p_in[1] >> 0) & 255; //p_in[0] + Y1 = (p_in[3] >> 8) & 255; //p_in[3] + Yv = (p_in[3] >> 0) & 255; //p_in[2] + + //0,1 + pb->prel(0, 1, YUV422(Y0, Yu, Yv)); + //1,1 + pb->prel(1, 1, YUV422(Y1, Yu, Yv)); + } +}; + +template +struct UnpackerPalToRgb { + using unpacked_type = Pixel; + static Pixel unpack(u8 col) + { + u32 *pal = sizeof(Pixel) == 2 ? &palette16_ram[palette_index] : &palette32_ram[palette_index]; + return pal[col]; + } +}; + +template +struct ConvertTwiddlePal4 +{ + using unpacked_type = typename Unpacker::unpacked_type; + static constexpr u32 xpp = 4; + static constexpr u32 ypp = 4; + static void Convert(PixelBuffer *pb, const u8 *data) + { + const u8 *p_in = data; + + pb->prel(0, 0, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(0, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + pb->prel(1, 0, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(1, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + + pb->prel(0, 2, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(0, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + pb->prel(1, 2, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(1, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + + pb->prel(2, 0, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(2, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + pb->prel(3, 0, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(3, 1, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + + pb->prel(2, 2, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(2, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + pb->prel(3, 2, Unpacker::unpack(p_in[0] & 0xF)); + pb->prel(3, 3, Unpacker::unpack((p_in[0] >> 4) & 0xF)); p_in++; + } +}; + +template +struct ConvertTwiddlePal8 +{ + using unpacked_type = typename Unpacker::unpacked_type; + static constexpr u32 xpp = 2; + static constexpr u32 ypp = 4; + static void Convert(PixelBuffer *pb, const u8 *data) + { + const u8* p_in = (const u8 *)data; + + pb->prel(0, 0, Unpacker::unpack(p_in[0])); p_in++; + pb->prel(0, 1, Unpacker::unpack(p_in[0])); p_in++; + pb->prel(1, 0, Unpacker::unpack(p_in[0])); p_in++; + pb->prel(1, 1, Unpacker::unpack(p_in[0])); p_in++; + + pb->prel(0, 2, Unpacker::unpack(p_in[0])); p_in++; + pb->prel(0, 3, Unpacker::unpack(p_in[0])); p_in++; + pb->prel(1, 2, Unpacker::unpack(p_in[0])); p_in++; + pb->prel(1, 3, Unpacker::unpack(p_in[0])); p_in++; + } +}; + +//handler functions +template +void texture_PL(PixelBuffer* pb, const u8* p_in, u32 width, u32 height) +{ + pb->amove(0,0); + + height /= PixelConvertor::ypp; + width /= PixelConvertor::xpp; + + for (u32 y = 0; y < height; y++) + { + for (u32 x = 0; x < width; x++) + { + const u8* p = p_in; + PixelConvertor::Convert(pb, p); + p_in += 8; + + pb->rmovex(PixelConvertor::xpp); + } + pb->rmovey(PixelConvertor::ypp); + } +} + +template +void texture_PLVQ(PixelBuffer* pb, const u8* p_in, u32 width, u32 height) +{ + pb->amove(0, 0); + + height /= PixelConvertor::ypp; + width /= PixelConvertor::xpp; + + for (u32 y = 0; y < height; y++) + { + for (u32 x = 0; x < width; x++) + { + u8 p = *p_in++; + PixelConvertor::Convert(pb, &vq_codebook[p * 8]); + pb->rmovex(PixelConvertor::xpp); + } + pb->rmovey(PixelConvertor::ypp); + } +} + +template +void texture_TW(PixelBuffer* pb, const u8* p_in, u32 width, u32 height) +{ + pb->amove(0, 0); + + const u32 divider = PixelConvertor::xpp * PixelConvertor::ypp; + + const u32 bcx = bitscanrev(width); + const u32 bcy = bitscanrev(height); + + for (u32 y = 0; y < height; y += PixelConvertor::ypp) + { + for (u32 x = 0; x < width; x += PixelConvertor::xpp) + { + const u8* p = &p_in[(twop(x, y, bcx, bcy) / divider) << 3]; + PixelConvertor::Convert(pb, p); + + pb->rmovex(PixelConvertor::xpp); + } + pb->rmovey(PixelConvertor::ypp); + } +} + +template +void texture_VQ(PixelBuffer* pb, const u8* p_in, u32 width, u32 height) +{ + pb->amove(0, 0); + + const u32 divider = PixelConvertor::xpp * PixelConvertor::ypp; + const u32 bcx = bitscanrev(width); + const u32 bcy = bitscanrev(height); + + for (u32 y = 0; y < height; y += PixelConvertor::ypp) + { + for (u32 x = 0; x < width; x += PixelConvertor::xpp) + { + u8 p = p_in[twop(x, y, bcx, bcy) / divider]; + PixelConvertor::Convert(pb, &vq_codebook[p * 8]); + + pb->rmovex(PixelConvertor::xpp); + } + pb->rmovey(PixelConvertor::ypp); + } +} + +//Twiddle +const TexConvFP tex565_TW = texture_TW>>; +// Palette +const TexConvFP texPAL4_TW = texture_TW>>; +const TexConvFP texPAL8_TW = texture_TW>>; +const TexConvFP32 texPAL4_TW32 = texture_TW>>; +const TexConvFP32 texPAL8_TW32 = texture_TW>>; +const TexConvFP8 texPAL4PT_TW = texture_TW>>; +const TexConvFP8 texPAL8PT_TW = texture_TW>>; +//VQ +const TexConvFP tex565_VQ = texture_VQ>>; +// According to the documentation, a texture cannot be compressed and use +// a palette at the same time. However the hardware displays them +// just fine. +const TexConvFP texPAL4_VQ = texture_VQ>>; +const TexConvFP texPAL8_VQ = texture_VQ>>; +const TexConvFP32 texPAL4_VQ32 = texture_VQ>>; +const TexConvFP32 texPAL8_VQ32 = texture_VQ>>; + +namespace opengl { +// OpenGL + +//Planar +const TexConvFP32 texYUV422_PL = texture_PL>; +const TexConvFP32 tex565_PL32 = texture_PL>>; +const TexConvFP32 tex1555_PL32 = texture_PL>>; +const TexConvFP32 tex4444_PL32 = texture_PL>>; + +const TexConvFP32 texYUV422_PLVQ = texture_PLVQ>; +const TexConvFP32 tex565_PLVQ32 = texture_PLVQ>>; +const TexConvFP32 tex1555_PLVQ32 = texture_PLVQ>>; +const TexConvFP32 tex4444_PLVQ32 = texture_PLVQ>>; + +//Twiddle +const TexConvFP tex1555_TW = texture_TW>; +const TexConvFP tex4444_TW = texture_TW>; +const TexConvFP texBMP_TW = tex4444_TW; +const TexConvFP32 texYUV422_TW = texture_TW>; + +const TexConvFP32 tex565_TW32 = texture_TW>>; +const TexConvFP32 tex1555_TW32 = texture_TW>>; +const TexConvFP32 tex4444_TW32 = texture_TW>>; + +//VQ +const TexConvFP tex1555_VQ = texture_VQ>; +const TexConvFP tex4444_VQ = texture_VQ>; +const TexConvFP texBMP_VQ = tex4444_VQ; +const TexConvFP32 texYUV422_VQ = texture_VQ>; + +const TexConvFP32 tex565_VQ32 = texture_VQ>>; +const TexConvFP32 tex1555_VQ32 = texture_VQ>>; +const TexConvFP32 tex4444_VQ32 = texture_VQ>>; +} + +namespace directx { +// DirectX + +//Planar +const TexConvFP32 texYUV422_PL = texture_PL>; +const TexConvFP32 tex565_PL32 = texture_PL>>; +const TexConvFP32 tex1555_PL32 = texture_PL>>; +const TexConvFP32 tex4444_PL32 = texture_PL>>; + +const TexConvFP32 texYUV422_PLVQ = texture_PLVQ>; +const TexConvFP32 tex565_PLVQ32 = texture_PLVQ>>; +const TexConvFP32 tex1555_PLVQ32 = texture_PLVQ>>; +const TexConvFP32 tex4444_PLVQ32 = texture_PLVQ>>; + +//Twiddle +const TexConvFP tex1555_TW = texture_TW>>; +const TexConvFP tex4444_TW = texture_TW>>; +const TexConvFP texBMP_TW = tex4444_TW; +const TexConvFP32 texYUV422_TW = texture_TW>; + +const TexConvFP32 tex565_TW32 = texture_TW>>; +const TexConvFP32 tex1555_TW32 = texture_TW>>; +const TexConvFP32 tex4444_TW32 = texture_TW>>; + +//VQ +const TexConvFP tex1555_VQ = texture_VQ>>; +const TexConvFP tex4444_VQ = texture_VQ>>; +const TexConvFP texBMP_VQ = tex4444_VQ; +const TexConvFP32 texYUV422_VQ = texture_VQ>; + +const TexConvFP32 tex565_VQ32 = texture_VQ>>; +const TexConvFP32 tex1555_VQ32 = texture_VQ>>; +const TexConvFP32 tex4444_VQ32 = texture_VQ>>; +} + +#define TEX_CONV_TABLE \ +const PvrTexInfo pvrTexInfo[8] = \ +{ /* name bpp Final format Twiddled VQ Planar(32b) Twiddled(32b) VQ (32b) PL VQ (32b) Palette (8b) */ \ + {"1555", 16, TextureType::_5551, tex1555_TW, tex1555_VQ, tex1555_PL32, tex1555_TW32, tex1555_VQ32, tex1555_PLVQ32, nullptr }, \ + {"565", 16, TextureType::_565, tex565_TW, tex565_VQ, tex565_PL32, tex565_TW32, tex565_VQ32, tex565_PLVQ32, nullptr }, \ + {"4444", 16, TextureType::_4444, tex4444_TW, tex4444_VQ, tex4444_PL32, tex4444_TW32, tex4444_VQ32, tex4444_PLVQ32, nullptr }, \ + {"yuv", 16, TextureType::_8888, nullptr, nullptr, texYUV422_PL, texYUV422_TW, texYUV422_VQ, texYUV422_PLVQ, nullptr }, \ + {"bumpmap", 16, TextureType::_4444, texBMP_TW, texBMP_VQ, tex4444_PL32, tex4444_TW32, tex4444_VQ32, tex4444_PLVQ32, nullptr }, \ + {"pal4", 4, TextureType::_5551, texPAL4_TW, texPAL4_VQ, nullptr, texPAL4_TW32, texPAL4_VQ32, nullptr, texPAL4PT_TW }, \ + {"pal8", 8, TextureType::_5551, texPAL8_TW, texPAL8_VQ, nullptr, texPAL8_TW32, texPAL8_VQ32, nullptr, texPAL8PT_TW }, \ + {"ns/1555", 0}, \ +} + +namespace opengl { + TEX_CONV_TABLE; +} +namespace directx { + TEX_CONV_TABLE; +} +#undef TEX_CONV_TABLE +const PvrTexInfo *pvrTexInfo = opengl::pvrTexInfo; diff --git a/core/rend/texconv.h b/core/rend/texconv.h new file mode 100644 index 000000000..cbaa2cf26 --- /dev/null +++ b/core/rend/texconv.h @@ -0,0 +1,257 @@ +/* + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#pragma once +#include "types.h" + +constexpr int VQ_CODEBOOK_SIZE = 256 * 8; +extern const u8 *vq_codebook; +extern u32 palette_index; +extern u32 palette16_ram[1024]; +extern u32 palette32_ram[1024]; +extern u32 pal_hash_256[4]; +extern u32 pal_hash_16[64]; + +void palette_update(); + +template +class PixelBuffer +{ + Pixel* p_buffer_start = nullptr; + Pixel* p_current_mipmap = nullptr; + Pixel* p_current_line = nullptr; + Pixel* p_current_pixel = nullptr; + + u32 pixels_per_line = 0; + +public: + ~PixelBuffer() { + deinit(); + } + + void init(u32 width, u32 height, bool mipmapped) + { + deinit(); + size_t size = width * height * sizeof(Pixel); + if (mipmapped) + { + do + { + width /= 2; + height /= 2; + size += width * height * sizeof(Pixel); + } + while (width != 0 && height != 0); + } + p_buffer_start = p_current_line = p_current_pixel = p_current_mipmap = (Pixel *)malloc(size); + this->pixels_per_line = 1; + } + + void init(u32 width, u32 height) + { + deinit(); + p_buffer_start = p_current_line = p_current_pixel = p_current_mipmap = (Pixel *)malloc(width * height * sizeof(Pixel)); + this->pixels_per_line = width; + } + + void deinit() + { + if (p_buffer_start != nullptr) + { + free(p_buffer_start); + p_buffer_start = p_current_mipmap = p_current_line = p_current_pixel = nullptr; + } + } + + void steal_data(PixelBuffer &buffer) + { + deinit(); + p_buffer_start = p_current_mipmap = p_current_line = p_current_pixel = buffer.p_buffer_start; + pixels_per_line = buffer.pixels_per_line; + buffer.p_buffer_start = buffer.p_current_mipmap = buffer.p_current_line = buffer.p_current_pixel = nullptr; + } + + void set_mipmap(int level) + { + u32 offset = 0; + for (int i = 0; i < level; i++) + offset += (1 << (2 * i)); + p_current_mipmap = p_current_line = p_current_pixel = p_buffer_start + offset; + pixels_per_line = 1 << level; + } + + Pixel *data(u32 x = 0, u32 y = 0) + { + return p_current_mipmap + pixels_per_line * y + x; + } + + void prel(u32 x, Pixel value) + { + p_current_pixel[x] = value; + } + + void prel(u32 x, u32 y, Pixel value) + { + p_current_pixel[y * pixels_per_line + x] = value; + } + + void rmovex(u32 value) + { + p_current_pixel += value; + } + + void rmovey(u32 value) + { + p_current_line += pixels_per_line * value; + p_current_pixel = p_current_line; + } + + void amove(u32 x_m, u32 y_m) + { + //p_current_pixel=p_buffer_start; + p_current_line = p_current_mipmap + pixels_per_line * y_m; + p_current_pixel = p_current_line + x_m; + } +}; + +// OpenGL +struct RGBAPacker { + static u32 pack(u8 r, u8 g, u8 b, u8 a) { + return r | (g << 8) | (b << 16) | (a << 24); + } +}; +// DirectX +struct BGRAPacker { + static u32 pack(u8 r, u8 g, u8 b, u8 a) { + return b | (g << 8) | (r << 16) | (a << 24); + } +}; + +template +struct UnpackerNop { + using unpacked_type = Pixel; + static Pixel unpack(Pixel word) { + return word; + } +}; + +// ARGB1555 to RGBA5551 +struct Unpacker1555 { + using unpacked_type = u16; + static u16 unpack(u16 word) { + return ((word >> 15) & 1) | (((word >> 10) & 0x1F) << 11) | (((word >> 5) & 0x1F) << 6) | (((word >> 0) & 0x1F) << 1); + } +}; + +// ARGB4444 to RGBA4444 +struct Unpacker4444 { + using unpacked_type = u16; + static u16 unpack(u16 word) { + return (((word >> 0) & 0xF) << 4) | (((word >> 4) & 0xF) << 8) | (((word >> 8) & 0xF) << 12) | (((word >> 12) & 0xF) << 0); + } +}; + +template +struct Unpacker1555_32 { + using unpacked_type = u32; + static u32 unpack(u16 word) { + return Packer::pack( + (((word >> 10) & 0x1F) << 3) | ((word >> 12) & 7), + (((word >> 5) & 0x1F) << 3) | ((word >> 7) & 7), + (((word >> 0) & 0x1F) << 3) | ((word >> 2) & 7), + (word & 0x8000) ? 0xFF : 0); + } +}; + +template +struct Unpacker565_32 { + using unpacked_type = u32; + static u32 unpack(u16 word) { + return Packer::pack( + (((word >> 11) & 0x1F) << 3) | ((word >> 13) & 7), + (((word >> 5) & 0x3F) << 2) | ((word >> 9) & 3), + (((word >> 0) & 0x1F) << 3) | ((word >> 2) & 7), + 0xFF); + } +}; + +template +struct Unpacker4444_32 { + using unpacked_type = u32; + static u32 unpack(u16 word) { + return Packer::pack( + (((word >> 8) & 0xF) << 4) | ((word >> 8) & 0xF), + (((word >> 4) & 0xF) << 4) | ((word >> 4) & 0xF), + (((word >> 0) & 0xF) << 4) | ((word >> 0) & 0xF), + (((word >> 12) & 0xF) << 4) | ((word >> 12) & 0xF)); + } +}; + +// ARGB8888 to whatever +template +struct Unpacker8888 { + using unpacked_type = u32; + static u32 unpack(u32 word) { + return Packer::pack( + (word >> 16) & 0xFF, + (word >> 8) & 0xFF, + (word >> 0) & 0xFF, + (word >> 24) & 0xFF); + } +}; + +enum class TextureType { _565, _5551, _4444, _8888, _8 }; + +typedef void (*TexConvFP)(PixelBuffer *pb, const u8 *p_in, u32 width, u32 height); +typedef void (*TexConvFP8)(PixelBuffer *pb, const u8 *p_in, u32 width, u32 height); +typedef void (*TexConvFP32)(PixelBuffer *pb, const u8 *p_in, u32 width, u32 height); + +struct PvrTexInfo +{ + const char* name; + int bpp; //4/8 for pal. 16 for yuv, rgb, argb + TextureType type; + // Conversion to 16 bpp + TexConvFP TW; + TexConvFP VQ; + // Conversion to 32 bpp + TexConvFP32 PL32; + TexConvFP32 TW32; + TexConvFP32 VQ32; + TexConvFP32 PLVQ32; + // Conversion to 8 bpp (palette) + TexConvFP8 TW8; +}; + +namespace opengl +{ + extern const TexConvFP32 tex1555_TW32; + extern const TexConvFP32 tex1555_VQ32; + extern const TexConvFP32 tex1555_PL32; + extern const TexConvFP32 tex565_TW32; + extern const TexConvFP32 tex565_VQ32; + extern const TexConvFP32 tex565_PL32; + extern const TexConvFP32 tex4444_TW32; + extern const TexConvFP32 tex4444_VQ32; + extern const TexConvFP32 tex4444_PL32; + + extern const PvrTexInfo pvrTexInfo[8]; +} +namespace directx +{ + extern const PvrTexInfo pvrTexInfo[8]; +} +extern const PvrTexInfo *pvrTexInfo; diff --git a/core/rend/transform_matrix.h b/core/rend/transform_matrix.h index e6335f01b..d79327272 100644 --- a/core/rend/transform_matrix.h +++ b/core/rend/transform_matrix.h @@ -19,7 +19,7 @@ along with Flycast. If not, see . */ #pragma once -#include "TexCache.h" +#include "hw/pvr/Renderer_if.h" #include "hw/pvr/ta_ctx.h" #include "cfg/option.h" diff --git a/core/rend/vulkan/drawer.cpp b/core/rend/vulkan/drawer.cpp index 2cd45751b..60b60363d 100644 --- a/core/rend/vulkan/drawer.cpp +++ b/core/rend/vulkan/drawer.cpp @@ -638,9 +638,14 @@ void ScreenDrawer::Init(SamplerManager *samplerManager, ShaderManager *shaderMan this->shaderManager = shaderManager; if (this->viewport != viewport) { - framebuffers.clear(); - colorAttachments.clear(); - depthAttachment.reset(); + if (!framebuffers.empty()) { + verify(commandPool != nullptr); + commandPool->addToFlight(new Deleter(std::move(framebuffers))); + } + if (!colorAttachments.empty()) + commandPool->addToFlight(new Deleter(std::move(colorAttachments))); + if (depthAttachment) + commandPool->addToFlight(new Deleter(depthAttachment.release())); transitionNeeded.clear(); clearNeeded.clear(); } diff --git a/core/rend/vulkan/oit/oit_drawer.h b/core/rend/vulkan/oit/oit_drawer.h index 7075351bf..7ea1a5613 100644 --- a/core/rend/vulkan/oit/oit_drawer.h +++ b/core/rend/vulkan/oit/oit_drawer.h @@ -126,7 +126,6 @@ class OITDrawer : public BaseDrawer SamplerManager *samplerManager = nullptr; OITBuffers *oitBuffers = nullptr; bool needAttachmentTransition = false; - bool needDepthTransition = false; OITDescriptorSets descriptorSets; vk::Buffer curMainBuffer; bool dithering = false; diff --git a/core/rend/vulkan/oit/oit_renderer.cpp b/core/rend/vulkan/oit/oit_renderer.cpp index e5e0bc7e0..090808b16 100644 --- a/core/rend/vulkan/oit/oit_renderer.cpp +++ b/core/rend/vulkan/oit/oit_renderer.cpp @@ -105,6 +105,8 @@ class OITVulkanRenderer final : public BaseVulkanRenderer bool Present() override { + if (clearLastFrame) + return false; if (config::EmulateFramebuffer || framebufferRendered) return presentFramebuffer(); else diff --git a/core/rend/vulkan/oit/oit_renderpass.h b/core/rend/vulkan/oit/oit_renderpass.h index 0208be5f1..9c3c9cf87 100644 --- a/core/rend/vulkan/oit/oit_renderpass.h +++ b/core/rend/vulkan/oit/oit_renderpass.h @@ -20,6 +20,7 @@ */ #pragma once #include "../vulkan_context.h" +#include "cfg/option.h" class RenderPasses { diff --git a/core/rend/vulkan/overlay.cpp b/core/rend/vulkan/overlay.cpp index 78bb7fb5e..483f1e8d9 100644 --- a/core/rend/vulkan/overlay.cpp +++ b/core/rend/vulkan/overlay.cpp @@ -212,19 +212,16 @@ void VulkanOverlay::Draw(vk::CommandBuffer commandBuffer, vk::Extent2D viewport, drawers[i]->Draw(commandBuffer, vmuTextures[i]->GetImageView(), vtx, true, color); } } - if (crosshair && crosshairsNeeded()) + if (crosshair) { pipeline->BindPipeline(commandBuffer); bool imageViewBound = false; for (size_t i = 0; i < config::CrosshairColor.size(); i++) { - if (config::CrosshairColor[i] == 0) - continue; - if (settings.platform.isConsole() && config::MapleMainDevices[i] != MDT_LightGun) + if (!crosshairNeeded(i)) continue; auto [x, y] = getCrosshairPosition(i); - #ifdef LIBRETRO float w = lightgun_crosshair_size * scaling / config::ScreenStretching * 100.f; float h = lightgun_crosshair_size * scaling; diff --git a/core/rend/vulkan/pipeline.cpp b/core/rend/vulkan/pipeline.cpp index 90ce604be..e2330571c 100644 --- a/core/rend/vulkan/pipeline.cpp +++ b/core/rend/vulkan/pipeline.cpp @@ -20,7 +20,6 @@ */ #include "pipeline.h" #include "hw/pvr/Renderer_if.h" -#include "rend/osd.h" void PipelineManager::CreateModVolPipeline(ModVolMode mode, int cullMode, bool naomi2) { @@ -436,79 +435,3 @@ void PipelineManager::CreatePipeline(u32 listType, bool sortTriangles, const Pol pipelines[hash(listType, sortTriangles, &pp, gpuPalette, dithering)] = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo).value; } - -void OSDPipeline::CreatePipeline() -{ - // Vertex input state - static const vk::VertexInputBindingDescription vertexInputBindingDescription(0, sizeof(OSDVertex)); - static const std::array vertexInputAttributeDescriptions = { - vk::VertexInputAttributeDescription(0, 0, vk::Format::eR32G32Sfloat, offsetof(OSDVertex, x)), // pos - vk::VertexInputAttributeDescription(1, 0, vk::Format::eR8G8B8A8Unorm, offsetof(OSDVertex, r)), // color - vk::VertexInputAttributeDescription(2, 0, vk::Format::eR32G32Sfloat, offsetof(OSDVertex, u)), // tex coord - }; - vk::PipelineVertexInputStateCreateInfo vertexInputStateCreateInfo( - vk::PipelineVertexInputStateCreateFlags(), - vertexInputBindingDescription, - vertexInputAttributeDescriptions); - - // Input assembly state - vk::PipelineInputAssemblyStateCreateInfo pipelineInputAssemblyStateCreateInfo(vk::PipelineInputAssemblyStateCreateFlags(), vk::PrimitiveTopology::eTriangleStrip); - - // Viewport and scissor states - vk::PipelineViewportStateCreateInfo pipelineViewportStateCreateInfo(vk::PipelineViewportStateCreateFlags(), 1, nullptr, 1, nullptr); - - // Rasterization and multisample states - vk::PipelineRasterizationStateCreateInfo pipelineRasterizationStateCreateInfo; - pipelineRasterizationStateCreateInfo.lineWidth = 1.0; - vk::PipelineMultisampleStateCreateInfo pipelineMultisampleStateCreateInfo; - - // Depth and stencil - vk::PipelineDepthStencilStateCreateInfo pipelineDepthStencilStateCreateInfo; - - // Color flags and blending - vk::PipelineColorBlendAttachmentState pipelineColorBlendAttachmentState( - true, // blendEnable - vk::BlendFactor::eSrcAlpha, // srcColorBlendFactor - vk::BlendFactor::eOneMinusSrcAlpha, // dstColorBlendFactor - vk::BlendOp::eAdd, // colorBlendOp - vk::BlendFactor::eSrcAlpha, // srcAlphaBlendFactor - vk::BlendFactor::eOneMinusSrcAlpha, // dstAlphaBlendFactor - vk::BlendOp::eAdd, // alphaBlendOp - vk::ColorComponentFlagBits::eR | vk::ColorComponentFlagBits::eG - | vk::ColorComponentFlagBits::eB | vk::ColorComponentFlagBits::eA - ); - vk::PipelineColorBlendStateCreateInfo pipelineColorBlendStateCreateInfo - ( - vk::PipelineColorBlendStateCreateFlags(), // flags - false, // logicOpEnable - vk::LogicOp::eNoOp, // logicOp - pipelineColorBlendAttachmentState, // attachments - { { 1.0f, 1.0f, 1.0f, 1.0f } } // blendConstants - ); - - std::array dynamicStates = { vk::DynamicState::eViewport, vk::DynamicState::eScissor }; - vk::PipelineDynamicStateCreateInfo pipelineDynamicStateCreateInfo(vk::PipelineDynamicStateCreateFlags(), dynamicStates); - - std::array stages = { - vk::PipelineShaderStageCreateInfo(vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eVertex, shaderManager->GetOSDVertexShader(), "main"), - vk::PipelineShaderStageCreateInfo(vk::PipelineShaderStageCreateFlags(), vk::ShaderStageFlagBits::eFragment, shaderManager->GetOSDFragmentShader(), "main"), - }; - vk::GraphicsPipelineCreateInfo graphicsPipelineCreateInfo - ( - vk::PipelineCreateFlags(), // flags - stages, // stages - &vertexInputStateCreateInfo, // pVertexInputState - &pipelineInputAssemblyStateCreateInfo, // pInputAssemblyState - nullptr, // pTessellationState - &pipelineViewportStateCreateInfo, // pViewportState - &pipelineRasterizationStateCreateInfo, // pRasterizationState - &pipelineMultisampleStateCreateInfo, // pMultisampleState - &pipelineDepthStencilStateCreateInfo, // pDepthStencilState - &pipelineColorBlendStateCreateInfo, // pColorBlendState - &pipelineDynamicStateCreateInfo, // pDynamicState - *pipelineLayout, // layout - renderPass // renderPass - ); - - pipeline = GetContext()->GetDevice().createGraphicsPipelineUnique(GetContext()->GetPipelineCache(), graphicsPipelineCreateInfo).value; -} diff --git a/core/rend/vulkan/pipeline.h b/core/rend/vulkan/pipeline.h index 5e5e32fb0..d3dfaa8a2 100644 --- a/core/rend/vulkan/pipeline.h +++ b/core/rend/vulkan/pipeline.h @@ -398,74 +398,3 @@ class RttPipelineManager : public PipelineManager vk::UniqueRenderPass rttRenderPass; bool renderToTextureBuffer = false; }; - -class OSDPipeline -{ -public: - void Init(ShaderManager *shaderManager, vk::ImageView imageView, vk::RenderPass renderPass) - { - this->shaderManager = shaderManager; - if (!pipelineLayout) - { - vk::DescriptorSetLayoutBinding binding(0, vk::DescriptorType::eCombinedImageSampler, 1, vk::ShaderStageFlagBits::eFragment); // texture - descSetLayout = GetContext()->GetDevice().createDescriptorSetLayoutUnique( - vk::DescriptorSetLayoutCreateInfo(vk::DescriptorSetLayoutCreateFlags(), binding)); - pipelineLayout = GetContext()->GetDevice().createPipelineLayoutUnique( - vk::PipelineLayoutCreateInfo(vk::PipelineLayoutCreateFlags(), descSetLayout.get())); - } - if (!sampler) - { - sampler = GetContext()->GetDevice().createSamplerUnique( - vk::SamplerCreateInfo(vk::SamplerCreateFlags(), vk::Filter::eLinear, vk::Filter::eLinear, - vk::SamplerMipmapMode::eLinear, vk::SamplerAddressMode::eClampToEdge, vk::SamplerAddressMode::eClampToEdge, - vk::SamplerAddressMode::eClampToEdge, 0.0f, false, 16.0f, false, - vk::CompareOp::eNever, 0.0f, vk::LodClampNone, vk::BorderColor::eFloatOpaqueBlack)); - } - if (this->renderPass != renderPass) - { - this->renderPass = renderPass; - pipeline.reset(); - } - if (!descriptorSet) - { - descriptorSet = std::move(GetContext()->GetDevice().allocateDescriptorSetsUnique( - vk::DescriptorSetAllocateInfo(GetContext()->GetDescriptorPool(), descSetLayout.get())).front()); - } - vk::DescriptorImageInfo imageInfo(*sampler, imageView, vk::ImageLayout::eShaderReadOnlyOptimal); - vk::WriteDescriptorSet writeDescriptorSet(*descriptorSet, 0, 0, vk::DescriptorType::eCombinedImageSampler, imageInfo); - GetContext()->GetDevice().updateDescriptorSets(writeDescriptorSet, nullptr); - } - - void Term() - { - descriptorSet.reset(); - pipeline.reset(); - sampler.reset(); - pipelineLayout.reset(); - descSetLayout.reset(); - } - - vk::Pipeline GetPipeline() - { - if (!pipeline) - CreatePipeline(); - return *pipeline; - } - - void BindDescriptorSets(vk::CommandBuffer cmdBuffer) const - { - cmdBuffer.bindDescriptorSets(vk::PipelineBindPoint::eGraphics, *pipelineLayout, 0, descriptorSet.get(), nullptr); - } - -private: - VulkanContext *GetContext() const { return VulkanContext::Instance(); } - void CreatePipeline(); - - vk::RenderPass renderPass; - vk::UniquePipeline pipeline; - vk::UniqueSampler sampler; - vk::UniqueDescriptorSet descriptorSet; - vk::UniquePipelineLayout pipelineLayout; - vk::UniqueDescriptorSetLayout descSetLayout; - ShaderManager *shaderManager = nullptr; -}; diff --git a/core/rend/vulkan/shaders.cpp b/core/rend/vulkan/shaders.cpp index 3420f0e25..4f21f74f2 100644 --- a/core/rend/vulkan/shaders.cpp +++ b/core/rend/vulkan/shaders.cpp @@ -394,33 +394,6 @@ void main() } )"; -static const char OSDVertexShaderSource[] = R"( -layout (location = 0) in vec4 inPos; -layout (location = 1) in vec4 inColor; -layout (location = 2) in vec2 inUV; -layout (location = 0) out lowp vec4 outColor; -layout (location = 1) out mediump vec2 outUV; - -void main() -{ - outColor = inColor; - outUV = inUV; - gl_Position = inPos; -} -)"; - -static const char OSDFragmentShaderSource[] = R"( -layout (binding = 0) uniform sampler2D tex; -layout (location = 0) in lowp vec4 inColor; -layout (location = 1) in mediump vec2 inUV; -layout (location = 0) out vec4 FragColor; - -void main() -{ - FragColor = inColor * texture(tex, inUV); -} -)"; - extern const char N2LightShaderSource[] = R"( layout (std140, set = 1, binding = 2) uniform N2VertexShaderUniforms @@ -825,13 +798,3 @@ vk::UniqueShaderModule ShaderManager::compileQuadFragmentShader(bool ignoreTexAl .addSource(QuadFragmentShaderSource); return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment,src.generate()); } - -vk::UniqueShaderModule ShaderManager::compileOSDVertexShader() -{ - return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eVertex, VulkanSource().addSource(OSDVertexShaderSource).generate()); -} - -vk::UniqueShaderModule ShaderManager::compileOSDFragmentShader() -{ - return ShaderCompiler::Compile(vk::ShaderStageFlagBits::eFragment, VulkanSource().addSource(OSDFragmentShaderSource).generate()); -} diff --git a/core/rend/vulkan/shaders.h b/core/rend/vulkan/shaders.h index 426081381..6ec5e019d 100644 --- a/core/rend/vulkan/shaders.h +++ b/core/rend/vulkan/shaders.h @@ -147,18 +147,6 @@ class ShaderManager return *quadFragmentShader; } } - vk::ShaderModule GetOSDVertexShader() - { - if (!osdVertexShader) - osdVertexShader = compileOSDVertexShader(); - return *osdVertexShader; - } - vk::ShaderModule GetOSDFragmentShader() - { - if (!osdFragmentShader) - osdFragmentShader = compileOSDFragmentShader(); - return *osdFragmentShader; - } void term() { @@ -171,8 +159,6 @@ class ShaderManager quadRotateVertexShader.reset(); quadFragmentShader.reset(); quadNoAlphaFragmentShader.reset(); - osdVertexShader.reset(); - osdFragmentShader.reset(); } private: @@ -192,8 +178,6 @@ class ShaderManager vk::UniqueShaderModule compileModVolFragmentShader(bool divPosZ); vk::UniqueShaderModule compileQuadVertexShader(bool rotate); vk::UniqueShaderModule compileQuadFragmentShader(bool ignoreTexAlpha); - vk::UniqueShaderModule compileOSDVertexShader(); - vk::UniqueShaderModule compileOSDFragmentShader(); std::map vertexShaders; std::map fragmentShaders; @@ -203,6 +187,4 @@ class ShaderManager vk::UniqueShaderModule quadRotateVertexShader; vk::UniqueShaderModule quadFragmentShader; vk::UniqueShaderModule quadNoAlphaFragmentShader; - vk::UniqueShaderModule osdVertexShader; - vk::UniqueShaderModule osdFragmentShader; }; diff --git a/core/rend/vulkan/vk_context_lr.cpp b/core/rend/vulkan/vk_context_lr.cpp index 2fd8e1b0f..c02c6b773 100644 --- a/core/rend/vulkan/vk_context_lr.cpp +++ b/core/rend/vulkan/vk_context_lr.cpp @@ -433,8 +433,8 @@ void VulkanContext::beginFrame(vk::Extent2D extent) vk::Extent2D caExtent = colorAttachments[currentImage]->getExtent(); if (extent != caExtent) { - colorAttachments[currentImage].reset(); - framebuffers[currentImage].reset(); + addToFlight(new Deleter(std::move(colorAttachments[currentImage]))); + addToFlight(new Deleter(std::move(framebuffers[currentImage]))); } } commandPool.BeginFrame(); diff --git a/core/rend/vulkan/vk_context_lr.h b/core/rend/vulkan/vk_context_lr.h index c97f489bc..2dc1f0afa 100644 --- a/core/rend/vulkan/vk_context_lr.h +++ b/core/rend/vulkan/vk_context_lr.h @@ -22,7 +22,7 @@ #include "vulkan.h" #include "vmallocator.h" #include "quad.h" -#include "rend/TexCache.h" +#include "rend/texconv.h" #include "libretro_vulkan.h" #include "wsi/context.h" #include "commandpool.h" diff --git a/core/rend/vulkan/vulkan_context.cpp b/core/rend/vulkan/vulkan_context.cpp index 304bd0c0b..557d6bb11 100644 --- a/core/rend/vulkan/vulkan_context.cpp +++ b/core/rend/vulkan/vulkan_context.cpp @@ -1059,7 +1059,6 @@ void VulkanContext::PresentFrame(vk::Image image, vk::ImageView imageView, const DrawOverlay(settings.display.uiScale, config::FloatVMUs, true); imguiDriver->renderDrawData(ImGui::GetDrawData(), false); - renderer->DrawOSD(false); EndFrame(overlayCmdBuffer); static_cast(renderer)->RenderVideoRouting(); diff --git a/core/rend/vulkan/vulkan_context.h b/core/rend/vulkan/vulkan_context.h index 8001905c2..4c6a45056 100644 --- a/core/rend/vulkan/vulkan_context.h +++ b/core/rend/vulkan/vulkan_context.h @@ -87,7 +87,7 @@ class CommandBufferDebugScope { #include "vmallocator.h" #include "quad.h" -#include "rend/TexCache.h" +#include "rend/texconv.h" #include "overlay.h" #include "wsi/context.h" #include diff --git a/core/rend/vulkan/vulkan_driver.h b/core/rend/vulkan/vulkan_driver.h index ea224139e..3c00d0aa9 100644 --- a/core/rend/vulkan/vulkan_driver.h +++ b/core/rend/vulkan/vulkan_driver.h @@ -56,7 +56,8 @@ class VulkanDriver final : public ImGuiDriver if (!rendering || newFrameStarted) { context->BeginRenderPass(); - context->PresentLastFrame(); + if (renderer->RenderLastFrame()) + context->PresentLastFrame(); } if (!justStarted) { @@ -88,6 +89,9 @@ class VulkanDriver final : public ImGuiDriver ImTextureID updateTexture(const std::string& name, const u8 *data, int width, int height, bool nearestSampling) override { + if (justStarted) + // give it some more time + return {}; VkTexture vkTex(std::make_unique()); vkTex.texture->tex_type = TextureType::_8888; vkTex.texture->SetCommandBuffer(getCommandBuffer()); diff --git a/core/rend/vulkan/vulkan_renderer.cpp b/core/rend/vulkan/vulkan_renderer.cpp index 27707cc43..c40fc989b 100644 --- a/core/rend/vulkan/vulkan_renderer.cpp +++ b/core/rend/vulkan/vulkan_renderer.cpp @@ -22,38 +22,12 @@ #include "vulkan_renderer.h" #include "drawer.h" #include "hw/pvr/ta.h" -#include "rend/osd.h" #include "rend/transform_matrix.h" bool BaseVulkanRenderer::BaseInit(vk::RenderPass renderPass, int subpass) { texCommandPool.Init(); fbCommandPool.Init(); - -#if defined(__ANDROID__) && !defined(LIBRETRO) - if (!vjoyTexture) - { - int w, h; - u8 *image_data = loadOSDButtons(w, h); - texCommandPool.BeginFrame(); - vjoyTexture = std::make_unique(); - vjoyTexture->tex_type = TextureType::_8888; - vk::CommandBuffer cmdBuffer = texCommandPool.Allocate(); - cmdBuffer.begin(vk::CommandBufferBeginInfo(vk::CommandBufferUsageFlagBits::eOneTimeSubmit)); - vjoyTexture->SetCommandBuffer(cmdBuffer); - vjoyTexture->UploadToGPU(w, h, image_data, false); - vjoyTexture->SetCommandBuffer(nullptr); - cmdBuffer.end(); - texCommandPool.EndFrame(); - delete [] image_data; - osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass()); - } - if (!osdBuffer) - { - osdBuffer = std::make_unique(sizeof(OSDVertex) * VJOY_VISIBLE * 4, - vk::BufferUsageFlagBits::eVertexBuffer); - } -#endif quadPipeline = std::make_unique(false, false); quadPipeline->Init(&shaderManager, renderPass, subpass); framebufferDrawer = std::make_unique(); @@ -71,9 +45,6 @@ void BaseVulkanRenderer::Term() #endif framebufferDrawer.reset(); quadPipeline.reset(); - osdBuffer.reset(); - osdPipeline.Term(); - vjoyTexture.reset(); textureCache.Clear(); fogTexture = nullptr; paletteTexture = nullptr; @@ -115,9 +86,15 @@ BaseTextureCacheData *BaseVulkanRenderer::GetTexture(TSP tsp, TCW tcw) void BaseVulkanRenderer::Process(TA_context* ctx) { - framebufferRendered = false; - if (KillTex) + if (!ctx->rend.isRTT) { + framebufferRendered = false; + if (!config::EmulateFramebuffer) + clearLastFrame = false; + } + if (resetTextureCache) { textureCache.Clear(); + resetTextureCache = false; + } texCommandPool.BeginFrame(); textureCache.SetCurrentIndex(texCommandPool.GetIndex()); @@ -138,58 +115,6 @@ void BaseVulkanRenderer::ReInitOSD() { texCommandPool.Init(); fbCommandPool.Init(); -#if defined(__ANDROID__) && !defined(LIBRETRO) - osdPipeline.Init(&shaderManager, vjoyTexture->GetImageView(), GetContext()->GetRenderPass()); -#endif -} - -void BaseVulkanRenderer::DrawOSD(bool clear_screen) -{ -#ifndef LIBRETRO - if (!vjoyTexture) - return; - try { - if (clear_screen) - { - GetContext()->NewFrame(); - GetContext()->BeginRenderPass(); - GetContext()->PresentLastFrame(); - } - const float dc2s_scale_h = settings.display.height / 480.0f; - const float sidebarWidth = (settings.display.width - dc2s_scale_h * 640.0f) / 2; - - std::vector osdVertices = GetOSDVertices(); - const float x1 = 2.0f / (settings.display.width / dc2s_scale_h); - const float y1 = 2.0f / 480; - const float x2 = 1 - 2 * sidebarWidth / settings.display.width; - const float y2 = 1; - for (OSDVertex& vtx : osdVertices) - { - vtx.x = vtx.x * x1 - x2; - vtx.y = vtx.y * y1 - y2; - } - - const vk::CommandBuffer cmdBuffer = GetContext()->GetCurrentCommandBuffer(); - - static const float scopeColor[4] = { 0.0f, 1.0f, 0.0f, 1.0f }; - CommandBufferDebugScope _(cmdBuffer, "DrawOSD", scopeColor); - - cmdBuffer.bindPipeline(vk::PipelineBindPoint::eGraphics, osdPipeline.GetPipeline()); - - osdPipeline.BindDescriptorSets(cmdBuffer); - const vk::Viewport viewport(0, 0, (float)settings.display.width, (float)settings.display.height, 0, 1.f); - cmdBuffer.setViewport(0, viewport); - const vk::Rect2D scissor({ 0, 0 }, { (u32)settings.display.width, (u32)settings.display.height }); - cmdBuffer.setScissor(0, scissor); - osdBuffer->upload((u32)(osdVertices.size() * sizeof(OSDVertex)), osdVertices.data()); - cmdBuffer.bindVertexBuffers(0, osdBuffer->buffer.get(), {0}); - for (u32 i = 0; i < (u32)osdVertices.size(); i += 4) - cmdBuffer.draw(4, 1, i, 0); - if (clear_screen) - GetContext()->EndFrame(); - } catch (const InvalidVulkanContext&) { - } -#endif } void BaseVulkanRenderer::RenderFramebuffer(const FramebufferInfo& info) @@ -235,6 +160,7 @@ void BaseVulkanRenderer::RenderFramebuffer(const FramebufferInfo& info) commandBuffer.end(); fbCommandPool.EndFrame(); framebufferRendered = true; + clearLastFrame = false; } void BaseVulkanRenderer::RenderVideoRouting() @@ -265,11 +191,11 @@ void BaseVulkanRenderer::CheckFogTexture() { fogTexture = std::make_unique(); fogTexture->tex_type = TextureType::_8; - fog_needs_update = true; + updateFogTable = true; } - if (!fog_needs_update || !config::Fog) + if (!updateFogTable || !config::Fog) return; - fog_needs_update = false; + updateFogTable = false; u8 texData[256]; MakeFogTexture(texData); @@ -280,15 +206,14 @@ void BaseVulkanRenderer::CheckFogTexture() void BaseVulkanRenderer::CheckPaletteTexture() { - if (!paletteTexture) - { + if (!paletteTexture) { paletteTexture = std::make_unique(); paletteTexture->tex_type = TextureType::_8888; - palette_updated = true; } - if (!palette_updated) + else if (!updatePalette) { return; - palette_updated = false; + } + updatePalette = false; paletteTexture->SetCommandBuffer(texCommandBuffer); paletteTexture->UploadToGPU(1024, 1, (u8 *)palette32_ram, false); @@ -380,6 +305,8 @@ class VulkanRenderer final : public BaseVulkanRenderer bool Present() override { + if (clearLastFrame) + return false; if (config::EmulateFramebuffer || framebufferRendered) return presentFramebuffer(); else diff --git a/core/rend/vulkan/vulkan_renderer.h b/core/rend/vulkan/vulkan_renderer.h index f22f1230d..f24ff3c00 100644 --- a/core/rend/vulkan/vulkan_renderer.h +++ b/core/rend/vulkan/vulkan_renderer.h @@ -38,10 +38,12 @@ class BaseVulkanRenderer : public Renderer BaseTextureCacheData *GetTexture(TSP tsp, TCW tcw) override; void Process(TA_context* ctx) override; void ReInitOSD(); - void DrawOSD(bool clear_screen) override; void RenderFramebuffer(const FramebufferInfo& info) override; void RenderVideoRouting(); + bool RenderLastFrame() override { + return !clearLastFrame; + } bool GetLastFrame(std::vector& data, int& width, int& height) override { return GetContext()->GetLastFrame(data, width, height); } @@ -67,9 +69,6 @@ class BaseVulkanRenderer : public Renderer CommandPool texCommandPool; std::vector> framebufferTextures; int framebufferTexIndex = 0; - OSDPipeline osdPipeline; - std::unique_ptr vjoyTexture; - std::unique_ptr osdBuffer; TextureCache textureCache; vk::Extent2D viewport; vk::CommandBuffer texCommandBuffer; diff --git a/core/sdl/dreamconn.cpp b/core/sdl/dreamconn.cpp new file mode 100644 index 000000000..c379042fb --- /dev/null +++ b/core/sdl/dreamconn.cpp @@ -0,0 +1,225 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . + */ +#include "dreamconn.h" + +#ifdef USE_DREAMCONN +#include "hw/maple/maple_devs.h" +#include "ui/gui.h" +#include +#include +#include +#include +#include + +void createDreamConnDevices(std::shared_ptr dreamconn, bool gameStart); + +static bool sendMsg(const MapleMsg& msg, asio::ip::tcp::iostream& stream) +{ + std::ostringstream s; + s.fill('0'); + s << std::hex << std::uppercase + << std::setw(2) << (u32)msg.command << " " + << std::setw(2) << (u32)msg.destAP << " " + << std::setw(2) << (u32)msg.originAP << " " + << std::setw(2) << (u32)msg.size; + const u32 sz = msg.getDataSize(); + for (u32 i = 0; i < sz; i++) + s << " " << std::setw(2) << (u32)msg.data[i]; + s << "\r\n"; + + asio::ip::tcp::socket& sock = static_cast(stream.socket()); + asio::error_code ec; + asio::write(sock, asio::buffer(s.str()), ec); + return !ec; +} + +static bool receiveMsg(MapleMsg& msg, std::istream& stream) +{ + std::string response; + if (!std::getline(stream, response)) + return false; + sscanf(response.c_str(), "%hhx %hhx %hhx %hhx", &msg.command, &msg.destAP, &msg.originAP, &msg.size); + if ((msg.getDataSize() - 1) * 3 + 13 >= response.length()) + return false; + for (unsigned i = 0; i < msg.getDataSize(); i++) + sscanf(&response[i * 3 + 12], "%hhx", &msg.data[i]); + return !stream.fail(); +} + +void DreamConn::connect() +{ + iostream = asio::ip::tcp::iostream("localhost", std::to_string(BASE_PORT + bus)); + if (!iostream) { + WARN_LOG(INPUT, "DreamConn[%d] connection failed: %s", bus, iostream.error().message().c_str()); + disconnect(); + return; + } + iostream.expires_from_now(std::chrono::seconds(1)); + // Now get the controller configuration + MapleMsg msg; + msg.command = MDCF_GetCondition; + msg.destAP = (bus << 6) | 0x20; + msg.originAP = bus << 6; + msg.setData(MFID_0_Input); + if (!sendMsg(msg, iostream)) + { + WARN_LOG(INPUT, "DreamConn[%d] communication failed", bus); + disconnect(); + return; + } + if (!receiveMsg(msg, iostream)) { + WARN_LOG(INPUT, "DreamConn[%d] read timeout", bus); + disconnect(); + return; + } + iostream.expires_from_now(std::chrono::duration::max()); // don't use a 64-bit based duration to avoid overflow + expansionDevs = msg.originAP & 0x1f; + NOTICE_LOG(INPUT, "Connected to DreamConn[%d]: VMU:%d, Rumble Pack:%d", bus, hasVmu(), hasRumble()); + config::MapleExpansionDevices[bus][0] = hasVmu() ? MDT_SegaVMU : MDT_None; + config::MapleExpansionDevices[bus][1] = hasRumble() ? MDT_PurupuruPack : MDT_None; +} + +void DreamConn::disconnect() +{ + if (iostream) { + iostream.close(); + NOTICE_LOG(INPUT, "Disconnected from DreamConn[%d]", bus); + } +} + +bool DreamConn::send(const MapleMsg& msg) +{ + if (!iostream) + return false; + if (!sendMsg(msg, iostream)) { + WARN_LOG(INPUT, "DreamConn[%d] send failed: %s", bus, iostream.error().message().c_str()); + return false; + } + return true; +} + +bool DreamConnGamepad::isDreamConn(int deviceIndex) +{ + char guid_str[33] {}; + SDL_JoystickGetGUIDString(SDL_JoystickGetDeviceGUID(deviceIndex), guid_str, sizeof(guid_str)); + INFO_LOG(INPUT, "GUID: %s VID:%c%c%c%c PID:%c%c%c%c", guid_str, + guid_str[10], guid_str[11], guid_str[8], guid_str[9], + guid_str[18], guid_str[19], guid_str[16], guid_str[17]); + // DreamConn VID:4457 PID:4443 + return memcmp("5744000043440000", guid_str + 8, 16) == 0; +} + +DreamConnGamepad::DreamConnGamepad(int maple_port, int joystick_idx, SDL_Joystick* sdl_joystick) + : SDLGamepad(maple_port, joystick_idx, sdl_joystick) +{ + _name = "DreamConn+ Controller"; + EventManager::listen(Event::Start, handleEvent, this); + EventManager::listen(Event::LoadState, handleEvent, this); +} + +DreamConnGamepad::~DreamConnGamepad() { + EventManager::unlisten(Event::Start, handleEvent, this); + EventManager::unlisten(Event::LoadState, handleEvent, this); +} + +void DreamConnGamepad::set_maple_port(int port) +{ + if (port < 0 || port >= 4) { + dreamconn.reset(); + } + else if (dreamconn == nullptr || dreamconn->getBus() != port) { + dreamconn.reset(); + dreamconn = std::make_shared(port); + } + SDLGamepad::set_maple_port(port); +} + +void DreamConnGamepad::handleEvent(Event event, void *arg) +{ + DreamConnGamepad *gamepad = static_cast(arg); + if (gamepad->dreamconn != nullptr) + createDreamConnDevices(gamepad->dreamconn, event == Event::Start); +} + +bool DreamConnGamepad::gamepad_btn_input(u32 code, bool pressed) +{ + if (!is_detecting_input() && input_mapper) + { + DreamcastKey key = input_mapper->get_button_id(0, code); + if (key == DC_BTN_START) { + startPressed = pressed; + checkKeyCombo(); + } + } + else { + startPressed = false; + } + return SDLGamepad::gamepad_btn_input(code, pressed); +} + +bool DreamConnGamepad::gamepad_axis_input(u32 code, int value) +{ + if (!is_detecting_input()) + { + if (code == leftTrigger) { + ltrigPressed = value > 0; + checkKeyCombo(); + } + else if (code == rightTrigger) { + rtrigPressed = value > 0; + checkKeyCombo(); + } + } + else { + ltrigPressed = false; + rtrigPressed = false; + } + return SDLGamepad::gamepad_axis_input(code, value); +} + +void DreamConnGamepad::checkKeyCombo() { + if (ltrigPressed && rtrigPressed && startPressed) + gui_open_settings(); +} + +#else + +void DreamConn::connect() { +} +void DreamConn::disconnect() { +} + +bool DreamConnGamepad::isDreamConn(int deviceIndex) { + return false; +} +DreamConnGamepad::DreamConnGamepad(int maple_port, int joystick_idx, SDL_Joystick* sdl_joystick) + : SDLGamepad(maple_port, joystick_idx, sdl_joystick) { +} +DreamConnGamepad::~DreamConnGamepad() { +} +void DreamConnGamepad::set_maple_port(int port) { + SDLGamepad::set_maple_port(port); +} +bool DreamConnGamepad::gamepad_btn_input(u32 code, bool pressed) { + return SDLGamepad::gamepad_btn_input(code, pressed); +} +bool DreamConnGamepad::gamepad_axis_input(u32 code, int value) { + return SDLGamepad::gamepad_axis_input(code, value); +} +#endif diff --git a/core/sdl/dreamconn.h b/core/sdl/dreamconn.h new file mode 100644 index 000000000..bf2e32680 --- /dev/null +++ b/core/sdl/dreamconn.h @@ -0,0 +1,101 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . + */ +#pragma once +#include "types.h" +#include "emulator.h" +#include "sdl_gamepad.h" +#if defined(_WIN32) && !defined(TARGET_UWP) +#define USE_DREAMCONN 1 +#include +#endif + +struct MapleMsg +{ + u8 command; + u8 destAP; + u8 originAP; + u8 size; + u8 data[1024]; + + u32 getDataSize() const { + return size * 4; + } + + template + void setData(const T& p) { + memcpy(data, &p, sizeof(T)); + this->size = (sizeof(T) + 3) / 4; + } +}; +static_assert(sizeof(MapleMsg) == 1028); + +class DreamConn +{ + const int bus; +#ifdef USE_DREAMCONN + asio::ip::tcp::iostream iostream; +#endif + u8 expansionDevs = 0; + static constexpr u16 BASE_PORT = 37393; + +public: + DreamConn(int bus) : bus(bus) { + connect(); + } + ~DreamConn() { + disconnect(); + } + + bool send(const MapleMsg& msg); + + int getBus() const { + return bus; + } + bool hasVmu() { + return expansionDevs & 1; + } + bool hasRumble() { + return expansionDevs & 2; + } + +private: + void connect(); + void disconnect(); +}; + +class DreamConnGamepad : public SDLGamepad +{ +public: + DreamConnGamepad(int maple_port, int joystick_idx, SDL_Joystick* sdl_joystick); + ~DreamConnGamepad(); + + void set_maple_port(int port) override; + bool gamepad_btn_input(u32 code, bool pressed) override; + bool gamepad_axis_input(u32 code, int value) override; + static bool isDreamConn(int deviceIndex); + +private: + static void handleEvent(Event event, void *arg); + void checkKeyCombo(); + + std::shared_ptr dreamconn; + bool ltrigPressed = false; + bool rtrigPressed = false; + bool startPressed = false; +}; diff --git a/core/sdl/sdl.cpp b/core/sdl/sdl.cpp index 9027dfa25..a93c19e3c 100644 --- a/core/sdl/sdl.cpp +++ b/core/sdl/sdl.cpp @@ -30,6 +30,7 @@ #include "nswitch.h" #include "switch_gamepad.h" #endif +#include "dreamconn.h" #include static SDL_Window* window = NULL; @@ -38,6 +39,7 @@ static u32 windowFlags; #define WINDOW_WIDTH 640 #define WINDOW_HEIGHT 480 +std::map> SDLGamepad::sdl_gamepads; static std::unordered_map> sdl_mice; static std::shared_ptr sdl_keyboard; static bool window_fullscreen; @@ -80,7 +82,11 @@ static void sdl_open_joystick(int index) #ifdef __SWITCH__ std::shared_ptr gamepad = std::make_shared(index < MAPLE_PORTS ? index : -1, index, pJoystick); #else - std::shared_ptr gamepad = std::make_shared(index < MAPLE_PORTS ? index : -1, index, pJoystick); + std::shared_ptr gamepad; + if (DreamConnGamepad::isDreamConn(index)) + gamepad = std::make_shared(index < MAPLE_PORTS ? index : -1, index, pJoystick); + else + gamepad = std::make_shared(index < MAPLE_PORTS ? index : -1, index, pJoystick); #endif SDLGamepad::AddSDLGamepad(gamepad); } catch (const FlycastException& e) { diff --git a/core/sdl/sdl_gamepad.h b/core/sdl/sdl_gamepad.h index d91224ff7..d9820e5e4 100644 --- a/core/sdl/sdl_gamepad.h +++ b/core/sdl/sdl_gamepad.h @@ -681,8 +681,6 @@ class SDLGamepad : public GamepadDevice int damperEffectId = -1; }; -std::map> SDLGamepad::sdl_gamepads; - class SDLMouse : public Mouse { public: diff --git a/core/serialize.cpp b/core/serialize.cpp index 029d5cdf9..74912657c 100644 --- a/core/serialize.cpp +++ b/core/serialize.cpp @@ -10,7 +10,7 @@ #include "hw/pvr/pvr.h" #include "hw/sh4/sh4_sched.h" #include "hw/sh4/sh4_mmr.h" -#include "reios/gdrom_hle.h" +#include "reios/reios.h" #include "hw/naomi/naomi.h" #include "hw/naomi/naomi_cart.h" #include "hw/bba/bba.h" @@ -50,7 +50,7 @@ void dc_serialize(Serializer& ser) ser << config::Region.get(); naomi_cart_serialize(ser); - gd_hle_state.Serialize(ser); + reios_serialize(ser); achievements::serialize(ser); DEBUG_LOG(SAVESTATE, "Saved %d bytes", (u32)ser.size()); @@ -93,7 +93,7 @@ void dc_deserialize(Deserializer& deser) verify(config::Region >= 0 && config::Region <= 3); naomi_cart_deserialize(deser); - gd_hle_state.Deserialize(deser); + reios_deserialize(deser); achievements::deserialize(deser); sh4_sched_ffts(); diff --git a/core/serialize.h b/core/serialize.h index cc397c68c..7a6cb2895 100644 --- a/core/serialize.h +++ b/core/serialize.h @@ -62,7 +62,10 @@ class SerializeBase V49, V50, V51, - Current = V51, + V52, + V53, + V54, + Current = V54, Next = Current + 1, }; diff --git a/core/types.h b/core/types.h index e518762a0..b9aba6131 100644 --- a/core/types.h +++ b/core/types.h @@ -160,11 +160,6 @@ struct settings_t float uiScale = 1.f; } display; - struct - { - bool disable_nvmem; - } dynarec; - struct { bool muteAudio; diff --git a/core/ui/boxart/pvrparser.h b/core/ui/boxart/pvrparser.h index 989f52e24..cf713ac62 100644 --- a/core/ui/boxart/pvrparser.h +++ b/core/ui/boxart/pvrparser.h @@ -18,7 +18,8 @@ */ #pragma once #include "types.h" -#include "rend/TexCache.h" +#include "rend/texconv.h" +#include "hw/pvr/ta_structs.h" extern const u32 VQMipPoint[11]; extern const u32 OtherMipPoint[11]; diff --git a/core/ui/game_scanner.cpp b/core/ui/game_scanner.cpp index 0dba94738..d56dd179e 100644 --- a/core/ui/game_scanner.cpp +++ b/core/ui/game_scanner.cpp @@ -152,9 +152,22 @@ void GameScanner::fetch_game_list() } std::string dcbios = hostfs::findFlash("dc_", "%bios.bin;%boot.bin"); { + const std::vector& cdromDrives = hostfs::getCdromDrives(); LockGuard _(mutex); + // CD-ROM devices + for (auto it = cdromDrives.rbegin(); it != cdromDrives.rend(); ++it) + { + std::string name; + if (it->substr(0, 4) == "\\\\.\\") + name = it->substr(4); + else + name = *it; + game_list.insert(game_list.begin(), { name, *it, name, "", true }); + } + // Dreamcast BIOS if (!dcbios.empty()) game_list.insert(game_list.begin(), { "Dreamcast BIOS" }); + // Arcade games game_list.insert(game_list.end(), arcade_game_list.begin(), arcade_game_list.end()); } if (running) diff --git a/core/ui/game_scanner.h b/core/ui/game_scanner.h index f6f5901d8..e92b17ddb 100644 --- a/core/ui/game_scanner.h +++ b/core/ui/game_scanner.h @@ -26,11 +26,13 @@ #include #include -struct GameMedia { +struct GameMedia +{ std::string name; // Display name std::string path; // Full path to rom. May be an encoded uri std::string fileName; // Last component of the path, decoded std::string gameName; // for arcade games only, description from the rom list + bool device = false; // Corresponds to a physical cdrom device }; class GameScanner diff --git a/core/ui/gui.cpp b/core/ui/gui.cpp index b1fc5c97a..9f68259f2 100644 --- a/core/ui/gui.cpp +++ b/core/ui/gui.cpp @@ -56,8 +56,8 @@ #include "sdl/sdl.h" #endif +#include "vgamepad.h" #ifdef __ANDROID__ -#include "gui_android.h" #if HOST_CPU == CPU_ARM64 && USE_VULKAN #include "rend/vulkan/adreno.h" #endif @@ -111,6 +111,7 @@ static void emuEventCallback(Event event, void *) { case Event::Resume: game_started = true; + vgamepad::startGame(); break; case Event::Start: GamepadDevice::load_system_mappings(); @@ -328,6 +329,7 @@ void gui_initFonts() largeFont = io.Fonts->AddFontFromMemoryTTF(data.release(), dataSize, largeFontSize, nullptr, ranges); NOTICE_LOG(RENDERER, "Screen DPI is %.0f, size %d x %d. Scaling by %.2f", settings.display.dpi, settings.display.width, settings.display.height, settings.display.uiScale); + vgamepad::applyUiScale(); } void gui_keyboard_input(u16 wc) @@ -516,7 +518,7 @@ void gui_open_settings() { if (achievements::canPause()) { - HideOSD(); + vgamepad::hide(); try { emu.stop(); gui_setState(GuiState::Commands); @@ -532,6 +534,9 @@ void gui_open_settings() } else if (gui_state == GuiState::VJoyEdit) { + vgamepad::pauseEditing(); + // iOS: force a touch up event to make up for the one eaten by the tap gesture recognizer + mouseButtons &= ~1; gui_setState(GuiState::VJoyEditCommands); } else if (gui_state == GuiState::Loading) @@ -709,14 +714,14 @@ static void gui_display_commands() ImGui::NextColumn(); // Insert/Eject Disk - const char *disk_label = libGDR_GetDiscType() == Open ? ICON_FA_COMPACT_DISC " Insert Disk" : ICON_FA_COMPACT_DISC " Eject Disk"; + const char *disk_label = gdr::isOpen() ? ICON_FA_COMPACT_DISC " Insert Disk" : ICON_FA_COMPACT_DISC " Eject Disk"; if (ImGui::Button(disk_label, ScaledVec2(buttonWidth, 50))) { - if (libGDR_GetDiscType() == Open) { + if (gdr::isOpen()) { gui_setState(GuiState::SelectDisk); } else { - DiscOpenLid(); + emu.openGdrom(); gui_setState(GuiState::Closed); } } @@ -811,6 +816,7 @@ const char *maple_device_types[] = "Pop'n Music controller", "Racing Controller", "Densha de Go! Controller", + "Full Controller", // "Dreameye", }; @@ -848,8 +854,10 @@ static const char *maple_device_name(MapleDeviceType type) return maple_device_types[10]; case MDT_DenshaDeGoController: return maple_device_types[11]; + case MDT_SegaControllerXL: + return maple_device_types[12]; case MDT_Dreameye: -// return maple_device_types[12]; +// return maple_device_types[13]; case MDT_None: default: return maple_device_types[0]; @@ -883,6 +891,8 @@ static MapleDeviceType maple_device_type_from_index(int idx) case 11: return MDT_DenshaDeGoController; case 12: + return MDT_SegaControllerXL; + case 13: return MDT_Dreameye; case 0: default: @@ -1391,13 +1401,21 @@ static void controller_mapping_popup(const std::shared_ptr& gamep } } +static void gamepadPngFileSelected(bool cancelled, std::string path) +{ + if (!cancelled) + gui_runOnUiThread([path]() { + vgamepad::loadImage(path); + }); +} + static void gamepadSettingsPopup(const std::shared_ptr& gamepad) { centerNextWindow(); ImGui::SetNextWindowSize(min(ImGui::GetIO().DisplaySize, ScaledVec2(450.f, 300.f))); ImguiStyleVar _(ImGuiStyleVar_WindowRounding, 0); - if (ImGui::BeginPopupModal("Gamepad Settings", NULL, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove)) + if (ImGui::BeginPopupModal("Gamepad Settings", NULL, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_DragScrolling)) { if (ImGui::Button("Done", ScaledVec2(100, 30))) { @@ -1440,10 +1458,38 @@ static void gamepadSettingsPopup(const std::shared_ptr& gamepad) ImGui::NewLine(); if (gamepad->is_virtual_gamepad()) { - header("Haptic"); - OptionSlider("Power", config::VirtualGamepadVibration, 0, 100, "Haptic feedback power", "%d%%"); + if (gamepad->is_rumble_enabled()) { + header("Haptic"); + OptionSlider("Power", config::VirtualGamepadVibration, 0, 100, "Haptic feedback power", "%d%%"); + } header("View"); OptionSlider("Transparency", config::VirtualGamepadTransparency, 0, 100, "Virtual gamepad buttons transparency", "%d%%"); + +#if defined(__ANDROID__) || defined(TARGET_IPHONE) + vgamepad::ImguiVGamepadTexture tex; + ImGui::Image(tex.getId(), ScaledVec2(300.f, 150.f), ImVec2(0, 1), ImVec2(1, 0)); +#endif + const char *gamepadPngTitle = "Select a PNG file"; + if (ImGui::Button("Choose Image...", ScaledVec2(150, 30))) +#ifdef __ANDROID__ + { + if (!hostfs::addStorage(false, false, gamepadPngTitle, gamepadPngFileSelected, "image/png")) + ImGui::OpenPopup(gamepadPngTitle); + } +#else + { + ImGui::OpenPopup(gamepadPngTitle); + } +#endif + ImGui::SameLine(); + if (ImGui::Button("Use Default", ScaledVec2(150, 30))) + vgamepad::loadImage(""); + + select_file_popup(gamepadPngTitle, [](bool cancelled, std::string selection) + { + gamepadPngFileSelected(cancelled, selection); + return true; + }, true, "png"); } else if (gamepad->is_rumble_enabled()) { @@ -1472,6 +1518,8 @@ static void gamepadSettingsPopup(const std::shared_ptr& gamepad) ShowHelpMarker("Value sent to the game at 100% thumbstick deflection. " "Values greater than 100% will saturate before full deflection of the thumbstick."); } + scrollWhenDraggingOnVoid(); + windowDragScroll(); ImGui::EndPopup(); } } @@ -1950,21 +1998,18 @@ static void gui_settings_controls(bool& maple_devices_changed) controller_mapping_popup(gamepad); -#ifdef __ANDROID__ +#if defined(__ANDROID__) || defined(TARGET_IPHONE) if (gamepad->is_virtual_gamepad()) { if (ImGui::Button("Edit Layout")) { - vjoy_start_editing(); + vgamepad::startEditing(); gui_setState(GuiState::VJoyEdit); } } #endif if (gamepad->is_rumble_enabled() || gamepad->has_analog_stick() -#ifdef __ANDROID__ - || gamepad->is_virtual_gamepad() -#endif - ) + || gamepad->is_virtual_gamepad()) { ImGui::SameLine(0, uiScaled(16)); if (ImGui::Button("Settings")) @@ -2022,6 +2067,7 @@ static void gui_settings_controls(bool& maple_devices_changed) int port_count = 0; switch (config::MapleMainDevices[bus]) { case MDT_SegaController: + case MDT_SegaControllerXL: port_count = 2; break; case MDT_LightGun: @@ -2758,6 +2804,7 @@ static void gui_settings_network() static void gui_settings_advanced() { +#if FEAT_SHREC != DYNAREC_NONE header("CPU Mode"); { ImGui::Columns(2, "cpu_modes", false); @@ -2773,6 +2820,7 @@ static void gui_settings_advanced() "%d MHz"); } ImGui::Spacing(); +#endif header("Other"); { OptionCheckbox("HLE BIOS", config::UseReios, "Force high-level BIOS emulation"); @@ -3201,6 +3249,13 @@ static void gui_display_content() if (iconButton(ICON_FA_GEAR, "Settings")) gui_setState(GuiState::Settings); } + else + { + ImGui::SameLine(ImGui::GetContentRegionMax().x + - ImGui::GetStyle().FramePadding.x * 2.0f - ImGui::CalcTextSize("Cancel").x); + if (ImGui::Button("Cancel")) + gui_setState(GuiState::Commands); + } ImGui::PopStyleVar(); scanner.fetch_game_list(); @@ -3228,7 +3283,7 @@ static void gui_display_content() if (gui_state == GuiState::SelectDisk) { std::string extension = get_file_extension(game.path); - if (extension != "gdi" && extension != "chd" + if (!game.device && extension != "gdi" && extension != "chd" && extension != "cdi" && extension != "cue") // Only dreamcast disks continue; @@ -3238,7 +3293,7 @@ static void gui_display_content() } std::string gameName = game.name; GameBoxart art; - if (config::BoxartDisplayMode) + if (config::BoxartDisplayMode && !game.device) { art = boxart.getBoxartAndLoad(game); gameName = art.name; @@ -3266,11 +3321,15 @@ static void gui_display_content() } if (pressed) { + if (!config::BoxartDisplayMode) + art = boxart.getBoxart(game); + settings.content.title = art.name; + if (settings.content.title.empty() || settings.content.title == game.fileName) + settings.content.title = get_file_basename(game.fileName); if (gui_state == GuiState::SelectDisk) { - settings.content.path = game.path; try { - DiscSwap(game.path); + emu.insertGdrom(game.path); gui_setState(GuiState::Closed); } catch (const FlycastException& e) { gui_error(e.what()); @@ -3278,11 +3337,6 @@ static void gui_display_content() } else { - if (!config::BoxartDisplayMode) - art = boxart.getBoxart(game); - settings.content.title = art.name; - if (settings.content.title.empty() || settings.content.title == game.fileName) - settings.content.title = get_file_basename(game.fileName); std::string gamePath(game.path); scanner.get_mutex().unlock(); gui_start_game(gamePath); @@ -3384,14 +3438,28 @@ static void gui_display_onboarding() select_file_popup(title, &systemdir_selected_callback); } +static void drawBoxartBackground() +{ + GameMedia game; + game.path = settings.content.path; + game.fileName = settings.content.fileName; + GameBoxart art = boxart.getBoxart(game); + ImguiFileTexture tex(art.boxartPath); + ImDrawList *dl = ImGui::GetBackgroundDrawList(); + tex.draw(dl, ImVec2(0, 0), ImVec2(settings.display.width, settings.display.height), 1.f); +} + static std::future networkStatus; static void gui_network_start() { + drawBoxartBackground(); centerNextWindow(); - ImGui::SetNextWindowSize(ScaledVec2(330, 180)); + ImGui::SetNextWindowSize(ScaledVec2(330, 0)); + ImGui::SetNextWindowBgAlpha(0.8f); + ImguiStyleVar _1(ImGuiStyleVar_WindowPadding, ScaledVec2(20, 20)); - ImGui::Begin("##network", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize); + ImGui::Begin("##network", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize); ImguiStyleVar _(ImGuiStyleVar_FramePadding, ScaledVec2(20, 10)); ImGui::AlignTextToFramePadding(); @@ -3419,7 +3487,6 @@ static void gui_network_start() float currentwidth = ImGui::GetContentRegionAvail().x; ImGui::SetCursorPosX((currentwidth - uiScaled(100.f)) / 2.f + ImGui::GetStyle().WindowPadding.x); - ImGui::SetCursorPosY(uiScaled(126.f)); if (ImGui::Button("Cancel", ScaledVec2(100.f, 0)) && NetworkHandshake::instance != nullptr) { NetworkHandshake::instance->stop(); @@ -3438,10 +3505,13 @@ static void gui_network_start() static void gui_display_loadscreen() { + drawBoxartBackground(); centerNextWindow(); - ImGui::SetNextWindowSize(ScaledVec2(330, 180)); + ImGui::SetNextWindowSize(ScaledVec2(330, 0)); + ImGui::SetNextWindowBgAlpha(0.8f); + ImguiStyleVar _(ImGuiStyleVar_WindowPadding, ScaledVec2(20, 20)); - if (ImGui::Begin("##loading", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_NoResize)) + if (ImGui::Begin("##loading", NULL, ImGuiWindowFlags_NoTitleBar | ImGuiWindowFlags_AlwaysAutoResize)) { ImguiStyleVar _(ImGuiStyleVar_FramePadding, ScaledVec2(20, 10)); ImGui::AlignTextToFramePadding(); @@ -3479,7 +3549,6 @@ static void gui_display_loadscreen() float currentwidth = ImGui::GetContentRegionAvail().x; ImGui::SetCursorPosX((currentwidth - uiScaled(100.f)) / 2.f + ImGui::GetStyle().WindowPadding.x); - ImGui::SetCursorPosY(uiScaled(126.f)); if (ImGui::Button("Cancel", ScaledVec2(100.f, 0))) gameLoader.cancel(); } @@ -3499,7 +3568,7 @@ void gui_display_ui() FC_PROFILE_SCOPE; const LockGuard lock(guiMutex); - if (gui_state == GuiState::Closed || gui_state == GuiState::VJoyEdit) + if (gui_state == GuiState::Closed) return; if (gui_state == GuiState::Main) { @@ -3536,11 +3605,10 @@ void gui_display_ui() gui_display_onboarding(); break; case GuiState::VJoyEdit: + vgamepad::draw(); break; case GuiState::VJoyEditCommands: -#ifdef __ANDROID__ - gui_display_vjoy_commands(); -#endif + vgamepad::displayCommands(); break; case GuiState::SelectDisk: gui_display_content(); @@ -3599,8 +3667,6 @@ static std::string getFPSNotification() void gui_draw_osd() { - if (gui_state == GuiState::VJoyEdit) - return; gui_newFrame(); ImGui::NewFrame(); @@ -3635,14 +3701,13 @@ void gui_draw_osd() } if (!settings.raHardcoreMode) lua::overlay(); + vgamepad::draw(); ImGui::Render(); uiThreadRunner.execTasks(); } void gui_display_osd() { - if (gui_state == GuiState::VJoyEdit) - return; gui_draw_osd(); gui_endFrame(gui_is_open()); } diff --git a/core/ui/gui.h b/core/ui/gui.h index 80be4f5e6..a8026910a 100644 --- a/core/ui/gui.h +++ b/core/ui/gui.h @@ -74,7 +74,7 @@ void gui_setState(GuiState newState); static inline bool gui_is_open() { - return gui_state != GuiState::Closed && gui_state != GuiState::VJoyEdit; + return gui_state != GuiState::Closed; } static inline bool gui_is_content_browser() { diff --git a/core/ui/gui_android.cpp b/core/ui/gui_android.cpp deleted file mode 100644 index e727b38bb..000000000 --- a/core/ui/gui_android.cpp +++ /dev/null @@ -1,60 +0,0 @@ -/* - Copyright 2019 flyinghead - - This file is part of reicast. - - reicast is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - reicast is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with reicast. If not, see . - */ -#ifdef __ANDROID__ - -#include "gui_android.h" -#include "gui.h" - -#include "types.h" -#include "stdclass.h" -#include "imgui.h" -#include "gui_util.h" - -void vjoy_reset_editing(); -void vjoy_stop_editing(bool canceled); - -void gui_display_vjoy_commands() -{ - centerNextWindow(); - - ImGui::Begin("Virtual Joystick", NULL, ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse - | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_AlwaysAutoResize | ImGuiWindowFlags_NoScrollbar); - - if (ImGui::Button("Save", ScaledVec2(150, 50))) - { - vjoy_stop_editing(false); - gui_setState(GuiState::Settings); - } - ImGui::SameLine(); - if (ImGui::Button("Reset", ScaledVec2(150, 50))) - { - vjoy_reset_editing(); - gui_setState(GuiState::VJoyEdit); - } - - ImGui::SameLine(); - if (ImGui::Button("Cancel", ScaledVec2(150, 50))) - { - vjoy_stop_editing(true); - gui_setState(GuiState::Settings); - } - ImGui::End(); -} - -#endif // __ANDROID__ diff --git a/core/ui/gui_android.h b/core/ui/gui_android.h deleted file mode 100644 index 68e5cc074..000000000 --- a/core/ui/gui_android.h +++ /dev/null @@ -1,22 +0,0 @@ -/* - Copyright 2019 flyinghead - - This file is part of reicast. - - reicast is free software: you can redistribute it and/or modify - it under the terms of the GNU General Public License as published by - the Free Software Foundation, either version 2 of the License, or - (at your option) any later version. - - reicast is distributed in the hope that it will be useful, - but WITHOUT ANY WARRANTY; without even the implied warranty of - MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the - GNU General Public License for more details. - - You should have received a copy of the GNU General Public License - along with reicast. If not, see . - */ -#pragma once - -void gui_display_vjoy_commands(); -void vjoy_start_editing(); diff --git a/core/ui/gui_util.cpp b/core/ui/gui_util.cpp index 83562dd96..ec60d6a67 100644 --- a/core/ui/gui_util.cpp +++ b/core/ui/gui_util.cpp @@ -751,6 +751,16 @@ void ImguiTexture::draw(ImDrawList *drawList, const ImVec2& pos, const ImVec2& s drawList->AddImage(id, pos, pos + size, uv0, uv1, col); } +void ImguiTexture::draw(ImDrawList *drawList, const ImVec2& pos, const ImVec2& size, + const ImVec2& uv0, const ImVec2& uv1, const ImVec4& color) +{ + ImTextureID id = getId(); + if (id == ImTextureID{}) + return; + u32 col = ImGui::ColorConvertFloat4ToU32(color); + drawList->AddImage(id, pos, pos + size, uv0, uv1, col); +} + bool ImguiTexture::button(const char* str_id, const ImVec2& image_size, const std::string& title, const ImVec4& bg_col, const ImVec4& tint_col) { diff --git a/core/ui/gui_util.h b/core/ui/gui_util.h index 7fec8ebae..02520f77f 100644 --- a/core/ui/gui_util.h +++ b/core/ui/gui_util.h @@ -201,7 +201,9 @@ class ImguiTexture public: void draw(const ImVec2& size, const ImVec4& tint_col = ImVec4(1, 1, 1, 1), const ImVec4& border_col = ImVec4(0, 0, 0, 0)); - void draw(ImDrawList *drawList, const ImVec2& pos, const ImVec2& size, float alpha = 1.f); + void draw(ImDrawList *drawList, const ImVec2& pos, const ImVec2& size, float alpha); + void draw(ImDrawList *drawList, const ImVec2& pos, const ImVec2& size, + const ImVec2& uv0 = ImVec2(0, 0), const ImVec2& uv1 = ImVec2(1, 1), const ImVec4& color = ImVec4(1, 1, 1, 1)); bool button(const char* str_id, const ImVec2& image_size, const std::string& title = {}, const ImVec4& bg_col = ImVec4(0, 0, 0, 0), const ImVec4& tint_col = ImVec4(1, 1, 1, 1)); diff --git a/core/ui/mainui.cpp b/core/ui/mainui.cpp index 209afaae9..4b1ad55b5 100644 --- a/core/ui/mainui.cpp +++ b/core/ui/mainui.cpp @@ -41,12 +41,9 @@ bool mainui_rend_frame() os_DoEvents(); os_UpdateInputState(); - if (gui_is_open() || gui_state == GuiState::VJoyEdit) + if (gui_is_open()) { gui_display_ui(); - // TODO refactor android vjoy out of renderer - if (gui_state == GuiState::VJoyEdit && renderer != nullptr) - renderer->DrawOSD(true); #ifndef TARGET_IPHONE std::this_thread::sleep_for(std::chrono::milliseconds(16)); #endif diff --git a/core/ui/vgamepad.cpp b/core/ui/vgamepad.cpp new file mode 100644 index 000000000..adf0f5ea4 --- /dev/null +++ b/core/ui/vgamepad.cpp @@ -0,0 +1,953 @@ +/* + Copyright 2019 flyinghead + + This file is part of reicast. + + reicast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + reicast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with reicast. If not, see . + */ +#if defined(__ANDROID__) || defined(TARGET_IPHONE) + +#include "vgamepad.h" +#include "gui.h" +#include "stdclass.h" +#include "imgui.h" +#include "rend/osd.h" +#include "imgui_driver.h" +#include "input/gamepad.h" +#include "input/gamepad_device.h" +#include "oslib/storage.h" +#include "oslib/resources.h" +#include "cfg/cfg.h" +#include "input/gamepad.h" +#include "input/mouse.h" +#include "hw/naomi/naomi_cart.h" +#include "hw/naomi/card_reader.h" +#include "hw/maple/maple_devs.h" +#include + +namespace vgamepad +{ +static void stopEditing(bool canceled); +static void loadLayout(); + +struct Control +{ + Control() = default; + Control(float x, float y, float w = 64.f, float h = 64.f) + : pos(x, y), size(w, h), uv0(0, 0), uv1(1, 1) {} + + ImVec2 pos; + ImVec2 size; + ImVec2 uv0; + ImVec2 uv1; + bool disabled = false; +}; +static Control Controls[_Count]; +static bool Visible = true; +static bool serviceMode; +static float AlphaTrans = 1.f; +static ImVec2 StickPos; // analog stick position [-1, 1] +constexpr char const *BTN_PATH = "picture/buttons.png"; +constexpr char const *BTN_PATH_ARCADE = "picture/buttons-arcade.png"; +constexpr char const *CFG_SECTION = "vgamepad"; + +void displayCommands() +{ + draw(); + centerNextWindow(); + + ImGui::Begin("##vgamepad", NULL, ImGuiWindowFlags_NoDecoration); + + if (ImGui::Button("Save", ScaledVec2(150, 50))) + { + stopEditing(false); + gui_setState(GuiState::Settings); + } + ImGui::SameLine(); + if (ImGui::Button("Reset", ScaledVec2(150, 50))) + { + resetEditing(); + startEditing(); + gui_setState(GuiState::VJoyEdit); + } + + ImGui::SameLine(); + if (ImGui::Button("Cancel", ScaledVec2(150, 50))) + { + stopEditing(true); + gui_setState(GuiState::Settings); + } + ImGui::End(); +} + +static const char *getButtonsResPath() { + return settings.platform.isConsole() ? BTN_PATH : BTN_PATH_ARCADE; +} + +static const char *getButtonsCfgName() { + return settings.platform.isConsole() ? "image" : "image_arcade"; +} + +static bool loadOSDButtons(const std::string& path) +{ + if (path.empty()) + return false; + FILE *file = hostfs::storage().openFile(path, "rb"); + if (file == nullptr) + return false; + + stbi_set_flip_vertically_on_load(1); + int width, height, n; + u8 *image_data = stbi_load_from_file(file, &width, &height, &n, STBI_rgb_alpha); + std::fclose(file); + if (image_data == nullptr) + return false; + try { + imguiDriver->updateTexture(getButtonsResPath(), image_data, width, height, false); + } catch (...) { + // vulkan can throw during resizing + } + free(image_data); + + return true; +} + +static ImTextureID loadOSDButtons() +{ + ImTextureID id{}; + // custom image + std::string path = cfgLoadStr(CFG_SECTION, getButtonsCfgName(), ""); + if (loadOSDButtons(path)) + return id; + if (settings.platform.isConsole()) + { + // legacy buttons.png in data folder + if (loadOSDButtons(get_readonly_data_path("buttons.png"))) + return id; + // also try the home folder (android) + if (loadOSDButtons(get_readonly_config_path("buttons.png"))) + return id; + } + // default in resource + size_t size; + std::unique_ptr data = resource::load(getButtonsResPath(), size); + stbi_set_flip_vertically_on_load(1); + int width, height, n; + u8 *image_data = stbi_load_from_memory(data.get(), (int)size, &width, &height, &n, STBI_rgb_alpha); + if (image_data != nullptr) + { + try { + id = imguiDriver->updateTexture(getButtonsResPath(), image_data, width, height, false); + } catch (...) { + // vulkan can throw during resizing + } + free(image_data); + } + return id; +} + +ImTextureID ImguiVGamepadTexture::getId() +{ + ImTextureID id = imguiDriver->getTexture(getButtonsResPath()); + if (id == ImTextureID()) + id = loadOSDButtons(); + + return id; +} + +constexpr float vjoy_tex[_Count][4] = { + // L + { 0, 0, 64, 64 }, + // U + { 64, 0, 64, 64 }, + // R + { 128, 0, 64, 64 }, + // D + { 192, 0, 64, 64 }, + // Y, btn3 + { 256, 0, 64, 64 }, + // X, btn2 + { 320, 0, 64, 64 }, + // B, btn1 + { 384, 0, 64, 64 }, + // A, btn0 + { 448, 0, 64, 64 }, + + // Start + { 0, 64, 64, 64 }, + // LT + { 64, 64, 90, 64 }, + // RT + { 154, 64, 90, 64 }, + // Analog + { 244, 64, 128, 128 }, + // Stick + { 372, 64, 64, 64 }, + // Fast forward + { 436, 64, 64, 64 }, + + // C, btn4 + { 0, 128, 64, 64 }, + // Z, btn5 + { 64, 128, 64, 64 }, + + // service mode + { 0, 192, 64, 64 }, + // insert card + { 64, 192, 64, 64 }, + + // Special controls + // service + { 128, 128, 64, 64 }, + // coin + { 384, 128, 64, 64 }, + // test + { 448, 128, 64, 64 }, +}; + +static ImVec2 coinUV0, coinUV1; +static ImVec2 serviceUV0, serviceUV1; +static ImVec2 testUV0, testUV1; + +constexpr float OSD_TEX_W = 512.f; +constexpr float OSD_TEX_H = 256.f; + +static void setUV() +{ + int i = 0; + + for (auto& control : Controls) + { + control.uv0.x = (vjoy_tex[i][0] + 1) / OSD_TEX_W; + control.uv0.y = 1.f - (vjoy_tex[i][1] + 1) / OSD_TEX_H; + control.uv1.x = (vjoy_tex[i][0] + vjoy_tex[i][2] - 1) / OSD_TEX_W; + control.uv1.y = 1.f - (vjoy_tex[i][1] + vjoy_tex[i][3] - 1) / OSD_TEX_H; + i++; + if (i >= _VisibleCount) + break; + } + serviceUV0.x = (vjoy_tex[i][0] + 1) / OSD_TEX_W; + serviceUV0.y = 1.f - (vjoy_tex[i][1] + 1) / OSD_TEX_H; + serviceUV1.x = (vjoy_tex[i][0] + vjoy_tex[i][2] - 1) / OSD_TEX_W; + serviceUV1.y = 1.f - (vjoy_tex[i][1] + vjoy_tex[i][3] - 1) / OSD_TEX_H; + i++; + coinUV0.x = (vjoy_tex[i][0] + 1) / OSD_TEX_W; + coinUV0.y = 1.f - (vjoy_tex[i][1] + 1) / OSD_TEX_H; + coinUV1.x = (vjoy_tex[i][0] + vjoy_tex[i][2] - 1) / OSD_TEX_W; + coinUV1.y = 1.f - (vjoy_tex[i][1] + vjoy_tex[i][3] - 1) / OSD_TEX_H; + i++; + testUV0.x = (vjoy_tex[i][0] + 1) / OSD_TEX_W; + testUV0.y = 1.f - (vjoy_tex[i][1] + 1) / OSD_TEX_H; + testUV1.x = (vjoy_tex[i][0] + vjoy_tex[i][2] - 1) / OSD_TEX_W; + testUV1.y = 1.f - (vjoy_tex[i][1] + vjoy_tex[i][3] - 1) / OSD_TEX_H; + i++; + +} +static OnLoad _(&setUV); + +void show() { + Visible = true; +} + +void hide() { + Visible = false; +} + +ControlId hitTest(float x, float y) +{ + for (const auto& control : Controls) + if (!control.disabled + && x >= control.pos.x && x < control.pos.x + control.size.x + && y >= control.pos.y && y < control.pos.y + control.size.y) + return static_cast(&control - &Controls[0]); + return None; +} + +static u32 buttonMap[_Count] { + DC_DPAD_LEFT, + DC_DPAD_UP, + DC_DPAD_RIGHT, + DC_DPAD_DOWN, + DC_BTN_X, + DC_BTN_Y, + DC_BTN_B, + DC_BTN_A, + DC_BTN_START, + DC_AXIS_LT, + DC_AXIS_RT, + 0, // not used: analog area + 0, // not used: analog stick + EMU_BTN_FFORWARD, + DC_BTN_Y, // button 4 + DC_BTN_Z, // button 5 + EMU_BTN_SRVMODE, + DC_BTN_INSERT_CARD, + DC_DPAD_LEFT | DC_DPAD_UP, + DC_DPAD_RIGHT | DC_DPAD_UP, + DC_DPAD_LEFT | DC_DPAD_DOWN, + DC_DPAD_RIGHT | DC_DPAD_DOWN, +}; + +void setButtonMap() +{ + const bool arcade = settings.platform.isArcade(); + if (serviceMode) + { + buttonMap[A] = DC_BTN_D; + buttonMap[B] = DC_DPAD2_UP; + buttonMap[X] = DC_DPAD2_DOWN; + } + else + { + buttonMap[A] = DC_BTN_A; + buttonMap[B] = DC_BTN_B; + buttonMap[X] = arcade ? DC_BTN_C : DC_BTN_X; + } + buttonMap[Y] = arcade ? DC_BTN_X : DC_BTN_Y; +} + +u32 controlToDcKey(ControlId control) +{ + if (control >= Left && control < _Count) + return buttonMap[control]; + else + return 0; +} + +void setAnalogStick(float x, float y) { + StickPos.x = x; + StickPos.y = y; +} + +float getControlWidth(ControlId control) { + return Controls[control].size.x; +} + +void toggleServiceMode() +{ + serviceMode = !serviceMode; + if (serviceMode) + { + Controls[A].disabled = false; + Controls[B].disabled = false; + Controls[X].disabled = false; + setButtonMap(); + } + else { + startGame(); + } +} + +static void drawButtonDim(ImDrawList *drawList, const Control& control, int state) +{ + if (control.disabled) + return; + float scale_h = settings.display.height / 480.f; + float offs_x = (settings.display.width - scale_h * 640.f) / 2.f; + ImVec2 pos = control.pos * scale_h; + ImVec2 size = control.size * scale_h; + pos.x += offs_x; + ControlId controlId = static_cast(&control - &Controls[0]); + if (controlId == AnalogStick) + pos += StickPos * size; + + float col = (0.5f - 0.25f * state / 255) * AlphaTrans; + float alpha = (100.f - config::VirtualGamepadTransparency) / 100.f * AlphaTrans; + ImVec4 color(col, col, col, alpha); + + const ImVec2* uv0 = &control.uv0; + const ImVec2* uv1 = &control.uv1; + if (serviceMode) + switch (controlId) + { + case A: + uv0 = &coinUV0; + uv1 = &coinUV1; + break; + case B: + uv0 = &serviceUV0; + uv1 = &serviceUV1; + break; + case X: + uv0 = &testUV0; + uv1 = &testUV1; + break; + default: + break; + } + + ImguiVGamepadTexture tex; + tex.draw(drawList, pos, size, *uv0, *uv1, color); +} + +static void drawButton(ImDrawList *drawList, const Control& control, bool state) { + drawButtonDim(drawList, control, state ? 0 : 255); +} + +void draw() +{ + if (Controls[Left].pos.x == 0.f) + { + loadLayout(); + if (Controls[Left].pos.x == 0.f) + // mark done + Controls[Left].pos.x = 1e-12f; + } + + ImDrawList *drawList = ImGui::GetBackgroundDrawList(); + drawButton(drawList, Controls[Left], kcode[0] & buttonMap[Left]); + drawButton(drawList, Controls[Up], kcode[0] & buttonMap[Up]); + drawButton(drawList, Controls[Right], kcode[0] & buttonMap[Right]); + drawButton(drawList, Controls[Down], kcode[0] & buttonMap[Down]); + + drawButton(drawList, Controls[X], kcode[0] & buttonMap[X]); + drawButton(drawList, Controls[Y], kcode[0] & buttonMap[Y]); + drawButton(drawList, Controls[B], kcode[0] & buttonMap[B]); + drawButton(drawList, Controls[A], kcode[0] & buttonMap[A]); + + drawButton(drawList, Controls[Start], kcode[0] & buttonMap[Start]); + + drawButtonDim(drawList, Controls[LeftTrigger], lt[0] >> 8); + + drawButtonDim(drawList, Controls[RightTrigger], rt[0] >> 8); + + drawButton(drawList, Controls[AnalogArea], true); + drawButton(drawList, Controls[AnalogStick], false); + + drawButton(drawList, Controls[FastForward], false); + + drawButton(drawList, Controls[Btn4], kcode[0] & buttonMap[Btn4]); + drawButton(drawList, Controls[Btn5], kcode[0] & buttonMap[Btn5]); + drawButton(drawList, Controls[ServiceMode], !serviceMode); + drawButton(drawList, Controls[InsertCard], kcode[0] & buttonMap[InsertCard]); + + AlphaTrans += ((float)Visible - AlphaTrans) / 2; +} + +static float getUIScale() { + // scale is 1.1 for a 320 dpi screen of height 750 + return 1.1f * 750.f / settings.display.height * settings.display.dpi / 320.f; +} + +struct LayoutElement +{ + const std::string name; + const float dx, dy; // default pos in dc coords, relative to sides (or middle if 0) + const float dw, dh; // default size in dc coords + + float x, y; // normalized coordinates [0, 1] + float w, h; // normalized coordinates [0, 1], scaled with uiScale + float scale; // user scale + + void load() + { + x = cfgLoadFloat(CFG_SECTION, name + "_x", x); + y = cfgLoadFloat(CFG_SECTION, name + "_y", y); + scale = cfgLoadFloat(CFG_SECTION, name + "_scale", scale); + } + void save() const + { + cfgSaveFloat(CFG_SECTION, name + "_x", x); + cfgSaveFloat(CFG_SECTION, name + "_y", y); + cfgSaveFloat(CFG_SECTION, name + "_scale", scale); + } + + bool hitTest(float nx, float ny) const { + return nx >= x && nx < x + w * scale + && ny >= y && ny < y + h * scale; + } + + void applyUiScale() + { + const float dcw = 480.f * (float)settings.display.width / settings.display.height; + const float uiscale = getUIScale(); + w = dw / dcw * uiscale; + h = dh / 480.f * uiscale; + } + + void reset() + { + applyUiScale(); + scale = 1.f; + const float dcw = 480.f * (float)settings.display.width / settings.display.height; + const float uiscale = getUIScale(); + if (dx == 0) + x = 0.5f - w / 2; + else if (dx > 0) + x = dx / dcw * uiscale; + else + x = 1.f - w + dx / dcw * uiscale; + if (dy == 0) + y = 0.5f - h / 2; + else if (dy > 0) + y = dy / 480.f * uiscale; + else + y = 1.f - h + dy / 480.f * uiscale; + } +}; +static LayoutElement Layout[] { + { "dpad", 32.f, -24.f, 192.f, 192.f }, + { "buttons", -24.f, -24.f, 192.f, 192.f }, + { "start", 0.f, -24.f, 64.f, 64.f }, + { "LT", -134.f,-240.f, 90.f, 64.f }, + { "RT", -32.f,-240.f, 90.f, 64.f }, + { "analog", 40.f,-320.f, 128.f, 128.f }, + { "fforward", -24.f, 24.f, 64.f, 64.f }, + + { "btn4", -24.f,-216.f, 64.f, 64.f }, + { "btn5", -152.f,-216.f, 64.f, 64.f }, + { "service", -24.f, 96.f, 64.f, 64.f }, + { "inscard", 40.f,-250.f, 64.f, 64.f }, +}; + +static void applyLayout() +{ + const float dcw = 480.f * (float)settings.display.width / settings.display.height; + const float dx = (dcw - 640.f) / 2; + const float uiscale = getUIScale(); + float x, y, scale; + + // DPad + x = Layout[Elem_DPad].x * dcw - dx; + y = Layout[Elem_DPad].y * 480.f; + scale = Layout[Elem_DPad].scale * uiscale; + Controls[Left].pos = { x + 0.f * scale, y + 64.f * scale }; + Controls[Up].pos = { x + 64.f * scale, y + 0.f * scale }; + Controls[Right].pos = { x + 128.f * scale, y + 64.f * scale }; + Controls[Down].pos = { x + 64.f * scale, y + 128.f * scale }; + for (int control = Left; control <= Down; control++) + Controls[control].size = { 64.f * scale, 64.f * scale }; + + Controls[LeftUp].pos = { x + 0.f * scale, y + 0.f * scale }; + Controls[LeftDown].pos = { x + 0.f * scale, y + 128.f * scale }; + Controls[RightUp].pos = { x + 128.f * scale, y + 0.f * scale }; + Controls[RightDown].pos = { x + 128.f * scale, y + 128.f * scale }; + for (int control = LeftUp; control <= RightDown; control++) + Controls[control].size = { 64.f * scale, 64.f * scale }; + + // Buttons + x = Layout[Elem_Buttons].x * dcw - dx; + y = Layout[Elem_Buttons].y * 480.f; + scale = Layout[Elem_Buttons].scale * uiscale; + Controls[X].pos = { x + 0.f * scale, y + 64.f * scale }; + Controls[Y].pos = { x + 64.f * scale, y + 0.f * scale }; + Controls[B].pos = { x + 128.f * scale, y + 64.f * scale }; + Controls[A].pos = { x + 64.f * scale, y + 128.f * scale }; + for (int control = X; control <= A; control++) + Controls[control].size = { 64.f * scale, 64.f * scale }; + + // Start + scale = Layout[Elem_Start].scale * uiscale; + Controls[Start].pos = { Layout[Elem_Start].x * dcw - dx, Layout[Elem_Start].y * 480.f }; + Controls[Start].size = { Layout[Elem_Start].dw * scale, Layout[Elem_Start].dh * scale }; + + // Left trigger + scale = Layout[Elem_LT].scale * uiscale; + Controls[LeftTrigger].pos = { Layout[Elem_LT].x * dcw - dx, Layout[Elem_LT].y * 480.f }; + Controls[LeftTrigger].size = { Layout[Elem_LT].dw * scale, Layout[Elem_LT].dh * scale }; + + // Right trigger + scale = Layout[Elem_RT].scale * uiscale; + Controls[RightTrigger].pos = { Layout[Elem_RT].x * dcw - dx, Layout[Elem_RT].y * 480.f }; + Controls[RightTrigger].size = { Layout[Elem_RT].dw * scale, Layout[Elem_RT].dh * scale }; + + // Analog + x = Layout[Elem_Analog].x * dcw - dx; + y = Layout[Elem_Analog].y * 480.f; + scale = Layout[Elem_Analog].scale * uiscale; + Controls[AnalogArea].pos = { x, y }; + Controls[AnalogArea].size = { Layout[Elem_Analog].dw * scale, Layout[Elem_Analog].dh * scale }; + Controls[AnalogStick].pos = { x + 32.f * scale, y + 32.f * scale }; + Controls[AnalogStick].size = { 64.f * scale, 64.f * scale }; + + // Fast forward + scale = Layout[Elem_FForward].scale * uiscale; + Controls[FastForward].pos = { Layout[Elem_FForward].x * dcw - dx, Layout[Elem_FForward].y * 480.f }; + Controls[FastForward].size = { Layout[Elem_FForward].dw * scale, Layout[Elem_FForward].dh * scale }; + + // ARCADE + // Button 4 + scale = Layout[Elem_Btn4].scale * uiscale; + Controls[Btn4].pos = { Layout[Elem_Btn4].x * dcw - dx, Layout[Elem_Btn4].y * 480.f }; + Controls[Btn4].size = { Layout[Elem_Btn4].dw * scale, Layout[Elem_Btn4].dh * scale }; + // Button 5 + scale = Layout[Elem_Btn5].scale * uiscale; + Controls[Btn5].pos = { Layout[Elem_Btn5].x * dcw - dx, Layout[Elem_Btn5].y * 480.f }; + Controls[Btn5].size = { Layout[Elem_Btn5].dw * scale, Layout[Elem_Btn5].dh * scale }; + + // Service Mode + scale = Layout[Elem_ServiceMode].scale * uiscale; + Controls[ServiceMode].pos = { Layout[Elem_ServiceMode].x * dcw - dx, Layout[Elem_ServiceMode].y * 480.f }; + Controls[ServiceMode].size = { Layout[Elem_ServiceMode].dw * scale, Layout[Elem_ServiceMode].dh * scale }; + + // Insert Card + scale = Layout[Elem_InsertCard].scale * uiscale; + Controls[InsertCard].pos = { Layout[Elem_InsertCard].x * dcw - dx, Layout[Elem_InsertCard].y * 480.f }; + Controls[InsertCard].size = { Layout[Elem_InsertCard].dw * scale, Layout[Elem_InsertCard].dh * scale }; +} + +void applyUiScale() { + for (auto& element : Layout) + element.applyUiScale(); +} + +static void loadLayout() +{ + for (auto& element : Layout) { + element.reset(); + element.load(); + } + applyLayout(); +} + +static void saveLayout() +{ + cfgSetAutoSave(false); + for (auto& element : Layout) + element.save(); + cfgSetAutoSave(false); +} + +static void resetLayout() +{ + for (auto& element : Layout) + element.reset(); + applyLayout(); +} + +Element layoutHitTest(float x, float y) +{ + for (const auto& element : Layout) + if (element.hitTest(x, y)) + return static_cast(&element - &Layout[0]); + return Elem_None; +} + +void translateElement(Element element, float dx, float dy) +{ + LayoutElement& e = Layout[element]; + e.x += dx; + e.y += dy; + applyLayout(); +} + +void scaleElement(Element element, float factor) +{ + LayoutElement& e = Layout[element]; + float dx = e.w * e.scale * (factor - 1.f) / 2.f; + float dy = e.h * e.scale * (factor - 1.f) / 2.f; + e.scale *= factor; + // keep centered + translateElement(element, -dx, -dy); +} + +void loadImage(const std::string& path) +{ + if (path.empty()) { + cfgSaveStr(CFG_SECTION, getButtonsCfgName(), ""); + loadOSDButtons(); + } + else if (loadOSDButtons(path)) { + cfgSaveStr(CFG_SECTION, getButtonsCfgName(), path); + } +} + +static void enableAllControls() +{ + for (auto& control : Controls) + control.disabled = false; +} + +static void disableControl(ControlId ctrlId) +{ +#ifdef TARGET_IPHONE + if (ctrlId == Up || ctrlId == Down) + // Needed to pause the emulator + return; +#endif + + Controls[ctrlId].disabled = true; + switch (ctrlId) + { + case Left: + Controls[LeftUp].disabled = true; + Controls[LeftDown].disabled = true; + break; + case Right: + Controls[RightUp].disabled = true; + Controls[RightDown].disabled = true; + break; + case Up: + Controls[LeftUp].disabled = true; + Controls[RightUp].disabled = true; + break; + case Down: + Controls[LeftDown].disabled = true; + Controls[RightDown].disabled = true; + break; + case AnalogArea: + case AnalogStick: + Controls[AnalogArea].disabled = true; + Controls[AnalogStick].disabled = true; + break; + default: + break; + } +} + +void startGame() +{ + enableAllControls(); + serviceMode = false; + setButtonMap(); + bool enableTouchMouse = false; + if (settings.platform.isConsole()) + { + disableControl(Btn4); + disableControl(Btn5); + disableControl(ServiceMode); + disableControl(InsertCard); + switch (config::MapleMainDevices[0]) + { + case MDT_LightGun: + enableTouchMouse = true; + disableControl(AnalogArea); + disableControl(LeftTrigger); + disableControl(RightTrigger); + disableControl(A); + disableControl(X); + disableControl(Y); + break; + case MDT_AsciiStick: + // TODO add CZ + disableControl(AnalogArea); + disableControl(LeftTrigger); + disableControl(RightTrigger); + break; + case MDT_PopnMusicController: + // TODO add C btn + disableControl(AnalogArea); + disableControl(LeftTrigger); + disableControl(RightTrigger); + break; + case MDT_RacingController: + disableControl(X); + disableControl(Y); + break; + default: + break; + } + } + else + { + // arcade game + if (!card_reader::readerAvailable()) + disableControl(InsertCard); + if (settings.platform.isAtomiswave()) { + disableControl(Btn5); + } + else if (settings.platform.isSystemSP()) + { + disableControl(Y); + disableControl(Btn4); + disableControl(Btn5); + } + if (NaomiGameInputs != nullptr) + { + bool fullAnalog = false; + bool rt = false; + bool lt = false; + for (const auto& axis : NaomiGameInputs->axes) + { + if (axis.name == nullptr) + break; + switch (axis.axis) + { + case 0: + case 1: + fullAnalog = true; + break; + case 4: + rt = true; + break; + case 5: + lt = true; + break; + } + } + if (!fullAnalog) + disableControl(AnalogArea); + if (!lt) + disableControl(LeftTrigger); + else + disableControl(Btn5); + if (!rt) + disableControl(RightTrigger); + else + disableControl(Btn4); + u32 usedButtons = 0; + for (const auto& button : NaomiGameInputs->buttons) + { + if (button.name == nullptr) + break; + usedButtons |= button.source; + } + if (settings.platform.isAtomiswave()) + { + // button order: A B X Y B4 + if ((usedButtons & AWAVE_BTN0_KEY) == 0 || settings.input.lightgunGame) + disableControl(A); + if ((usedButtons & AWAVE_BTN1_KEY) == 0) + disableControl(B); + if ((usedButtons & AWAVE_BTN2_KEY) == 0) + disableControl(X); + if ((usedButtons & AWAVE_BTN3_KEY) == 0) + disableControl(Y); + if ((usedButtons & AWAVE_BTN4_KEY) == 0) + disableControl(Btn4); + if ((usedButtons & AWAVE_UP_KEY) == 0) + disableControl(Up); + if ((usedButtons & AWAVE_DOWN_KEY) == 0) + disableControl(Down); + if ((usedButtons & AWAVE_LEFT_KEY) == 0) + disableControl(Left); + if ((usedButtons & AWAVE_RIGHT_KEY) == 0) + disableControl(Right); + if ((usedButtons & AWAVE_START_KEY) == 0) + disableControl(Start); + } + else if (settings.platform.isSystemSP()) + { + if ((usedButtons & DC_BTN_A) == 0) + disableControl(A); + if ((usedButtons & DC_BTN_B) == 0) + disableControl(B); + if ((usedButtons & DC_BTN_C) == 0) + disableControl(X); + if ((usedButtons & DC_DPAD_UP) == 0) + disableControl(Up); + if ((usedButtons & DC_DPAD_DOWN) == 0) + disableControl(Down); + if ((usedButtons & DC_DPAD_LEFT) == 0) + disableControl(Left); + if ((usedButtons & DC_DPAD_RIGHT) == 0) + disableControl(Right); + if ((usedButtons & DC_BTN_START) == 0) + disableControl(Start); + } + else + { + if ((usedButtons & NAOMI_BTN0_KEY) == 0 || settings.input.lightgunGame) + disableControl(A); + if ((usedButtons & (NAOMI_BTN1_KEY | NAOMI_RELOAD_KEY)) == 0) + disableControl(B); + else if (settings.input.lightgunGame + && (usedButtons & NAOMI_RELOAD_KEY) != 0 + && (usedButtons & NAOMI_BTN1_KEY) == 0) + // Remap button 1 to reload for lightgun games that need it + buttonMap[B] = DC_BTN_RELOAD; + if ((usedButtons & NAOMI_BTN2_KEY) == 0) + // C + disableControl(X); + if ((usedButtons & NAOMI_BTN3_KEY) == 0) + // X + disableControl(Y); + if ((usedButtons & NAOMI_BTN4_KEY) == 0) + // Y + disableControl(Btn4); + if ((usedButtons & NAOMI_BTN5_KEY) == 0) + // Z + disableControl(Btn5); + if ((usedButtons & NAOMI_UP_KEY) == 0) + disableControl(Up); + if ((usedButtons & NAOMI_DOWN_KEY) == 0) + disableControl(Down); + if ((usedButtons & NAOMI_LEFT_KEY) == 0) + disableControl(Left); + if ((usedButtons & NAOMI_RIGHT_KEY) == 0) + disableControl(Right); + if ((usedButtons & NAOMI_START_KEY) == 0) + disableControl(Start); + } + if (settings.input.lightgunGame) + enableTouchMouse = true; + } + else + { + if (settings.input.lightgunGame) + { + enableTouchMouse = true; + disableControl(A); + disableControl(X); + disableControl(Y); + disableControl(Btn4); + disableControl(Btn5); + disableControl(AnalogArea); + disableControl(LeftTrigger); + disableControl(RightTrigger); + disableControl(Up); + disableControl(Down); + disableControl(Left); + disableControl(Right); + } + else + { + // all analog games *should* have an input description + disableControl(AnalogArea); + disableControl(LeftTrigger); + disableControl(RightTrigger); + } + } + } + std::shared_ptr touchMouse = GamepadDevice::GetGamepad(); + if (touchMouse != nullptr) + { + if (enableTouchMouse) { + if (touchMouse->maple_port() == -1) + touchMouse->set_maple_port(0); + } + else { + if (touchMouse->maple_port() == 0) + touchMouse->set_maple_port(-1); + } + } +} + +void resetEditing() { + resetLayout(); +} + +void startEditing() +{ + enableAllControls(); + show(); + setEditMode(true); +} + +void pauseEditing() { + setEditMode(false); +} + +static void stopEditing(bool canceled) +{ + setEditMode(false); + if (canceled) + loadLayout(); + else + saveLayout(); +} + +} // namespace vgamepad + +#endif // __ANDROID__ || TARGET_IPHONE diff --git a/core/ui/vgamepad.h b/core/ui/vgamepad.h new file mode 100644 index 000000000..df5869802 --- /dev/null +++ b/core/ui/vgamepad.h @@ -0,0 +1,116 @@ +/* + Copyright 2019 flyinghead + + This file is part of reicast. + + reicast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + reicast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with reicast. If not, see . + */ +#pragma once +#include "types.h" +#include "gui_util.h" + +namespace vgamepad +{ +enum ControlId +{ + None = -1, + Left, + Up, + Right, + Down, + X, + Y, + B, + A, + Start, + LeftTrigger, + RightTrigger, + AnalogArea, + AnalogStick, + FastForward, + + Btn4, + Btn5, + ServiceMode, + InsertCard, + + LeftUp, + RightUp, + LeftDown, + RightDown, + + _Count, + _VisibleCount = LeftUp, +}; + +enum Element +{ + Elem_None = -1, + Elem_DPad, + Elem_Buttons, + Elem_Start, + Elem_LT, + Elem_RT, + Elem_Analog, + Elem_FForward, + Elem_Btn4, + Elem_Btn5, + Elem_ServiceMode, + Elem_InsertCard, +}; + +class ImguiVGamepadTexture : public ImguiTexture +{ +public: + ImTextureID getId() override; +}; + +#if defined(__ANDROID__) || defined(TARGET_IPHONE) + +void show(); +void hide(); +void draw(); +void startEditing(); +void pauseEditing(); +void setEditMode(bool editing); +void resetEditing(); +void displayCommands(); +void loadImage(const std::string& path); +void startGame(); + +ControlId hitTest(float x, float y); +u32 controlToDcKey(ControlId control); +void setAnalogStick(float x, float y); +float getControlWidth(ControlId); +void toggleServiceMode(); + +void applyUiScale(); +Element layoutHitTest(float x, float y); +void translateElement(Element element, float dx, float dy); +void scaleElement(Element element, float factor); + +#else + +void show() {} +void hide() {} +void draw() {} +void startEditing() {} +void pauseEditing() {} +void displayCommands() {} +void applyUiScale() {} +void loadImage(const std::string& path) {} +void startGame() {} + +#endif +} // namespace vgamepad diff --git a/core/util/shared_this.h b/core/util/shared_this.h new file mode 100644 index 000000000..400f17c85 --- /dev/null +++ b/core/util/shared_this.h @@ -0,0 +1,38 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#pragma once +#include + +template +class SharedThis : public std::enable_shared_from_this +{ +public: + using Ptr = std::shared_ptr; + + template + static Ptr create(Args&&... args) { + return Ptr(new T(std::forward(args)...)); + } + +protected: + using super = SharedThis; + + SharedThis() { + } +}; diff --git a/core/windows/fault_handler.cpp b/core/windows/fault_handler.cpp index ac7ce0903..babe160f8 100644 --- a/core/windows/fault_handler.cpp +++ b/core/windows/fault_handler.cpp @@ -128,9 +128,11 @@ static LONG WINAPI exceptionHandler(EXCEPTION_POINTERS *ep) // texture protection in VRAM if (VramLockedWrite(address)) return EXCEPTION_CONTINUE_EXECUTION; +#if FEAT_SHREC == DYNAREC_JIT // FPCB jump table protection if (addrspace::bm_lockedWrite(address)) return EXCEPTION_CONTINUE_EXECUTION; +#endif host_context_t context; readContext(ep, context); diff --git a/resources/picture/buttons-arcade.png b/resources/picture/buttons-arcade.png new file mode 100644 index 000000000..980212cff Binary files /dev/null and b/resources/picture/buttons-arcade.png differ diff --git a/resources/picture/buttons-arcade.svg b/resources/picture/buttons-arcade.svg new file mode 100644 index 000000000..db982d2ff --- /dev/null +++ b/resources/picture/buttons-arcade.svg @@ -0,0 +1,2279 @@ + + + +124356S$T111111LTRT diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/AndroidStorage.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/AndroidStorage.java index 0cf835384..5533e4482 100644 --- a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/AndroidStorage.java +++ b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/AndroidStorage.java @@ -293,14 +293,17 @@ public String mkdir(String parent, String name) throws FileNotFoundException return dir.getAbsolutePath(); } - public boolean addStorage(boolean isDirectory, boolean writeAccess, String description) + public boolean addStorage(boolean isDirectory, boolean writeAccess, String description, String mimeType) { if (isDirectory && Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP) return false; Intent intent = new Intent(isDirectory ? Intent.ACTION_OPEN_DOCUMENT_TREE : Intent.ACTION_OPEN_DOCUMENT); if (!isDirectory) { intent.addCategory(Intent.CATEGORY_OPENABLE); - intent.setType("application/*"); + if (!mimeType.isEmpty()) + intent.setType(mimeType); + else + intent.setType("application/*"); } intent = Intent.createChooser(intent, description); storageIntentPerms = Intent.FLAG_GRANT_READ_URI_PERMISSION | (writeAccess ? Intent.FLAG_GRANT_WRITE_URI_PERMISSION : 0); diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/BaseGLActivity.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/BaseGLActivity.java index c060f209d..873a4334b 100644 --- a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/BaseGLActivity.java +++ b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/BaseGLActivity.java @@ -49,7 +49,6 @@ public abstract class BaseGLActivity extends Activity implements ActivityCompat. private static final int AUDIO_PERM_REQUEST = 1002; protected SharedPreferences prefs; - protected float[][] vjoy_d_cached; // Used for VJoy editing private AudioBackend audioBackend; protected Handler handler = new Handler(); private boolean audioPermissionRequested = false; @@ -270,7 +269,7 @@ private boolean showMenu() { private boolean processJoystickInput(MotionEvent event, int axis) { float v = event.getAxisValue(axis); - return InputDeviceManager.getInstance().joystickAxisEvent(event.getDeviceId(), axis, (int)Math.round(v * 32767.f)); + return InputDeviceManager.getInstance().axisEvent(event.getDeviceId(), axis, (int)Math.round(v * 32767.f)); } @Override public boolean onGenericMotionEvent(MotionEvent event) { @@ -284,29 +283,29 @@ public boolean onGenericMotionEvent(MotionEvent event) { if (range.getAxis() == MotionEvent.AXIS_HAT_X) { float v = event.getAxisValue(MotionEvent.AXIS_HAT_X); if (v == -1.0) { - rc |= InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_LEFT, true); - InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_RIGHT, false); + rc |= InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_LEFT, true); + InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_RIGHT, false); } else if (v == 1.0) { - InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_LEFT, false); - rc |= InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_RIGHT, true); + InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_LEFT, false); + rc |= InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_RIGHT, true); } else { - InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_LEFT, false); - InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_RIGHT, false); + InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_LEFT, false); + InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_RIGHT, false); } } else if (range.getAxis() == MotionEvent.AXIS_HAT_Y) { float v = event.getAxisValue(MotionEvent.AXIS_HAT_Y); if (v == -1.0) { - rc |= InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_UP, true); - InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_DOWN, false); + rc |= InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_UP, true); + InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_DOWN, false); } else if (v == 1.0) { - InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_UP, false); - rc |= InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_DOWN, true); + InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_UP, false); + rc |= InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_DOWN, true); } else { - InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_UP, false); - InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_DOWN, false); + InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_UP, false); + InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), KeyEvent.KEYCODE_DPAD_DOWN, false); } } else @@ -330,7 +329,7 @@ else if ((event.getSource() & InputDevice.SOURCE_CLASS_POINTER) == InputDevice.S @Override public boolean onKeyUp(int keyCode, KeyEvent event) { - if (InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), keyCode, false)) + if (InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), keyCode, false)) return true; if (hasKeyboard && InputDeviceManager.getInstance().keyboardEvent(keyCode, false)) return true; @@ -341,16 +340,15 @@ public boolean onKeyUp(int keyCode, KeyEvent event) { public boolean onKeyDown(int keyCode, KeyEvent event) { if (event.getRepeatCount() == 0) { if (keyCode == KeyEvent.KEYCODE_BACK) { - if (!JNIdc.guiIsOpen()) { - showMenu(); - return true; - } - else if (JNIdc.guiIsContentBrowser()) { + if (JNIdc.guiIsContentBrowser()) { finish(); - return true; } + else { + showMenu(); + } + return true; } - if (InputDeviceManager.getInstance().joystickButtonEvent(event.getDeviceId(), keyCode, true)) + if (InputDeviceManager.getInstance().buttonEvent(event.getDeviceId(), keyCode, true)) return true; if (hasKeyboard) { @@ -417,7 +415,7 @@ else if (requestCode == STORAGE_PERM_REQUEST) { } //setup mic - if (Emulator.micPluggedIn()) + if (InputDeviceManager.isMicPluggedIn()) requestRecordAudioPermission(); } diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/Emulator.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/Emulator.java index 28d8ba14e..5a089d2bf 100644 --- a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/Emulator.java +++ b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/Emulator.java @@ -10,7 +10,8 @@ import androidx.appcompat.app.AppCompatDelegate; import com.flycast.emulator.config.Config; -import com.flycast.emulator.emu.JNIdc; +import com.flycast.emulator.emu.VGamepad; +import com.flycast.emulator.periph.InputDeviceManager; public class Emulator extends Application { private static Context context; @@ -18,32 +19,14 @@ public class Emulator extends Application { private WifiManager wifiManager = null; private WifiManager.MulticastLock multicastLock = null; - // see MapleDeviceType in hw/maple/maple_devs.h - public static final int MDT_Microphone = 2; - public static final int MDT_None = 8; - public static int vibrationPower = 80; - public static int[] maple_devices = { - MDT_None, - MDT_None, - MDT_None, - MDT_None - }; - public static int[][] maple_expansion_devices = { - { MDT_None, MDT_None }, - { MDT_None, MDT_None }, - { MDT_None, MDT_None }, - { MDT_None, MDT_None }, - }; - /** * Load the settings from native code * */ public void getConfigurationPrefs() { - Emulator.vibrationPower = JNIdc.getVirtualGamepadVibration(); - JNIdc.getControllers(maple_devices, maple_expansion_devices); + Emulator.vibrationPower = VGamepad.getVibrationPower(); } /** @@ -54,26 +37,17 @@ public void SaveAndroidSettings(String homeDirectory) { Log.i("flycast", "SaveAndroidSettings: saving preferences"); SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(this); - Emulator.vibrationPower = JNIdc.getVirtualGamepadVibration(); - JNIdc.getControllers(maple_devices, maple_expansion_devices); + Emulator.vibrationPower = VGamepad.getVibrationPower(); prefs.edit() .putString(Config.pref_home, homeDirectory).apply(); - if (micPluggedIn() && currentActivity instanceof BaseGLActivity) { + if (InputDeviceManager.isMicPluggedIn() && currentActivity instanceof BaseGLActivity) { + Log.i("flycast", "SaveAndroidSettings: MIC PLUGGED IN"); ((BaseGLActivity)currentActivity).requestRecordAudioPermission(); } } - public static boolean micPluggedIn() { - JNIdc.getControllers(maple_devices, maple_expansion_devices); - for (int[] maple_expansion_device : maple_expansion_devices) - if (maple_expansion_device[0] == MDT_Microphone - || maple_expansion_device[1] == MDT_Microphone) - return true; - return false; - } - @Override public void onCreate() { super.onCreate(); diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/NativeGLActivity.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/NativeGLActivity.java index 1cb336993..72bd5f74b 100644 --- a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/NativeGLActivity.java +++ b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/NativeGLActivity.java @@ -16,10 +16,8 @@ import androidx.annotation.Nullable; -import com.flycast.emulator.emu.JNIdc; import com.flycast.emulator.emu.NativeGLView; import com.flycast.emulator.periph.InputDeviceManager; -import com.flycast.emulator.periph.VJoy; public final class NativeGLActivity extends BaseGLActivity { @@ -61,32 +59,23 @@ public boolean isSurfaceReady() { return mView != null && mView.isSurfaceReady(); } - // Called from native code - private void VJoyStartEditing() { - vjoy_d_cached = VJoy.readCustomVjoyValues(getApplicationContext()); - JNIdc.show_osd(); - mView.setEditVjoyMode(true); - } - // Called from native code - private void VJoyResetEditing() { - VJoy.resetCustomVjoyValues(getApplicationContext()); - mView.readCustomVjoyValues(); - mView.resetEditMode(); - handler.post(new Runnable() { - @Override + @Override + public void onGameStateChange(boolean started) { + super.onGameStateChange(started); + runOnUiThread(new Runnable() { public void run() { - mView.requestLayout(); + if (started) + mView.showVGamepad(); } }); } + // Called from native code - private void VJoyStopEditing(final boolean canceled) { + public void setVGamepadEditMode(boolean editing) { handler.post(new Runnable() { @Override public void run() { - if (canceled) - mView.restoreCustomVjoyValues(vjoy_d_cached); - mView.setEditVjoyMode(false); + mView.setVGamepadEditMode(editing); } }); } diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/EditVirtualJoystickDelegate.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/EditVirtualJoystickDelegate.java new file mode 100644 index 000000000..1cad6c1a9 --- /dev/null +++ b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/EditVirtualJoystickDelegate.java @@ -0,0 +1,113 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +package com.flycast.emulator.emu; + +import android.view.MotionEvent; +import android.view.ScaleGestureDetector; +import android.view.View; + +import androidx.annotation.NonNull; + +public class EditVirtualJoystickDelegate implements TouchEventHandler +{ + private View view; + private ScaleGestureDetector scaleGestureDetector; + private int currentElement = -1; + private float lastX, lastY; + + public EditVirtualJoystickDelegate(View view) { + this.view = view; + scaleGestureDetector = new ScaleGestureDetector(view.getContext(), new EditVirtualJoystickDelegate.ScaleGestureListener()); + } + + @Override + public void stop() { + } + @Override + public void show() { + VGamepad.show(); + } + + @Override + public boolean onTouchEvent(MotionEvent event, int width, int height) + { + scaleGestureDetector.onTouchEvent(event); + if (scaleGestureDetector.isInProgress()) { + currentElement = -1; + return true; + } + + int actionMasked = event.getActionMasked(); + int actionIndex = event.getActionIndex(); + switch (actionMasked) + { + case MotionEvent.ACTION_UP: + case MotionEvent.ACTION_CANCEL: + currentElement = -1; + break; + + case MotionEvent.ACTION_DOWN: + lastX = event.getX(actionIndex) / view.getWidth(); + lastY = event.getY(actionIndex) / view.getHeight(); + currentElement = VGamepad.layoutHitTest(lastX, lastY); + return true; // must return true if we want the scale gesture detector to work + + case MotionEvent.ACTION_MOVE: + if (currentElement != -1 && event.getPointerCount() == 1) + { + float x = event.getX(actionIndex) / view.getWidth(); + float y = event.getY(actionIndex) / view.getHeight(); + VGamepad.translateElement(currentElement, x - lastX, y - lastY); + lastX = x; + lastY = y; + return true; + } + break; + + default: + break; + } + + return false; + } + + private class ScaleGestureListener implements ScaleGestureDetector.OnScaleGestureListener { + private int elemId = -1; + @Override + public boolean onScaleBegin(@NonNull ScaleGestureDetector detector) + { + elemId = VGamepad.layoutHitTest(detector.getFocusX() / view.getWidth(), detector.getFocusY() / view.getHeight()); + return elemId != -1; + } + + @Override + public boolean onScale(ScaleGestureDetector detector) + { + if (elemId == -1) + return false; + VGamepad.scaleElement(elemId, detector.getScaleFactor()); + return true; + } + + @Override + public void onScaleEnd(ScaleGestureDetector detector) { + elemId = -1; + } + } +} diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/JNIdc.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/JNIdc.java index 938fb5305..7a3a0eb5c 100644 --- a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/JNIdc.java +++ b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/JNIdc.java @@ -19,12 +19,7 @@ public final class JNIdc public static native void rendinitNative(Surface surface, int w, int h); - public static native void vjoy(int id, float x, float y, float w, float h); - - public static native void getControllers(int[] controllers, int[][] peripherals); - public static native void setupMic(SipEmulator sip); - public static native int getVirtualGamepadVibration(); public static native void screenCharacteristics(float screenDpi, float refreshRate); public static native void guiOpenSettings(); @@ -32,8 +27,4 @@ public final class JNIdc public static native boolean guiIsContentBrowser(); public static native void guiSetInsets(int left, int right, int top, int bottom); - public static void show_osd() { - JNIdc.vjoy(14, 1, 0, 0, 0); - } - public static native void hideOsd(); } diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/NativeGLView.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/NativeGLView.java index 0a928ac7c..739afdfd9 100644 --- a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/NativeGLView.java +++ b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/NativeGLView.java @@ -10,6 +10,7 @@ import android.util.Log; import android.view.Display; import android.view.DisplayCutout; +import android.view.InputDevice; import android.view.MotionEvent; import android.view.SurfaceHolder; import android.view.SurfaceView; @@ -24,11 +25,7 @@ public class NativeGLView extends SurfaceView implements SurfaceHolder.Callback { private boolean surfaceReady = false; private boolean paused = false; - VirtualJoystickDelegate vjoyDelegate; - - public void restoreCustomVjoyValues(float[][] vjoy_d_cached) { - vjoyDelegate.restoreCustomVjoyValues(vjoy_d_cached); - } + private TouchEventHandler vjoyDelegate = null; public NativeGLView(Context context) { this(context, null); @@ -63,20 +60,21 @@ public void onSystemUiVisibilityChange(int visibility) { @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); - vjoyDelegate = new VirtualJoystickDelegate(this); + if (InputDeviceManager.getInstance().hasTouchscreen()) + vjoyDelegate = new VirtualJoystickDelegate(this); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); - vjoyDelegate.stop(); + if (vjoyDelegate != null) + vjoyDelegate.stop(); } @Override protected void onLayout(boolean changed, int left, int top, int right, int bottom) { super.onLayout(changed, left, top, right, bottom); - vjoyDelegate.layout(getWidth(), getHeight()); DisplayMetrics dm = getContext().getResources().getDisplayMetrics(); Display d; @@ -101,10 +99,6 @@ protected void onLayout(boolean changed, int left, int top, int right, int botto } } - public void resetEditMode() { - vjoyDelegate.resetEditMode(); - } - @Override public boolean onTouchEvent(final MotionEvent event) { @@ -113,13 +107,13 @@ public boolean onTouchEvent(final MotionEvent event) InputDeviceManager.getInstance().mouseEvent(Math.round(event.getX()), Math.round(event.getY()), event.getButtonState()); return true; } - else + if (vjoyDelegate != null && (event.getSource() & InputDevice.SOURCE_TOUCHSCREEN) == InputDevice.SOURCE_TOUCHSCREEN) return vjoyDelegate.onTouchEvent(event, getWidth(), getHeight()); + return false; } @Override public void surfaceCreated(SurfaceHolder surfaceHolder) { - } @Override @@ -179,11 +173,18 @@ public void onWindowFocusChanged(boolean hasFocus) { } } - public void readCustomVjoyValues() { - vjoyDelegate.readCustomVjoyValues(); + public void setVGamepadEditMode(boolean editing) + { + if (!InputDeviceManager.getInstance().hasTouchscreen()) + return; + if (editing && !(vjoyDelegate instanceof EditVirtualJoystickDelegate)) + vjoyDelegate = new EditVirtualJoystickDelegate(this); + else if (!editing && !(vjoyDelegate instanceof VirtualJoystickDelegate)) + vjoyDelegate = new VirtualJoystickDelegate(this); } - public void setEditVjoyMode(boolean editVjoyMode) { - vjoyDelegate.setEditVjoyMode(editVjoyMode); + public void showVGamepad() { + if (vjoyDelegate != null) + vjoyDelegate.show(); } } diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/TouchEventHandler.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/TouchEventHandler.java new file mode 100644 index 000000000..40f351337 --- /dev/null +++ b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/TouchEventHandler.java @@ -0,0 +1,27 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +package com.flycast.emulator.emu; + +import android.view.MotionEvent; + +public interface TouchEventHandler { + boolean onTouchEvent(MotionEvent event, int width, int height); + void stop(); + void show(); +} diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/VGamepad.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/VGamepad.java new file mode 100644 index 000000000..4b85c8ce6 --- /dev/null +++ b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/VGamepad.java @@ -0,0 +1,35 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +package com.flycast.emulator.emu; + +public class VGamepad +{ + static { System.loadLibrary("flycast"); } + + public static native int getVibrationPower(); + + public static native void show(); + public static native void hide(); + public static native int hitTest(float x, float y); + public static native float getControlWidth(int controlId); + + public static native int layoutHitTest(float x, float y); + public static native void scaleElement(int elemId, float scale); + public static native void translateElement(int elemId, float x, float y); +} diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/VirtualJoystickDelegate.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/VirtualJoystickDelegate.java index 1b23cf0f0..3ecae4bee 100644 --- a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/VirtualJoystickDelegate.java +++ b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/emu/VirtualJoystickDelegate.java @@ -1,397 +1,273 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ package com.flycast.emulator.emu; import android.content.Context; -import android.content.res.Configuration; import android.os.Handler; -import android.view.InputDevice; import android.view.MotionEvent; -import android.view.ScaleGestureDetector; import android.view.View; import com.flycast.emulator.periph.InputDeviceManager; -import com.flycast.emulator.periph.VJoy; import com.flycast.emulator.periph.VibratorThread; -public class VirtualJoystickDelegate { - private VibratorThread vibratorThread; +import java.util.HashMap; +import java.util.Map; - private boolean editVjoyMode = false; - private int selectedVjoyElement = VJoy.ELEM_NONE; - private ScaleGestureDetector scaleGestureDetector; +public class VirtualJoystickDelegate implements TouchEventHandler +{ + private static final int CTLID_ANARING = 11; + private static final int CTLID_ANASTICK = 12; + private VibratorThread vibratorThread; private Handler handler = new Handler(); - private Runnable hideOsdRunnable = new Runnable() { + private Runnable hideVGamepadRunnable = new Runnable() { @Override public void run() { - JNIdc.hideOsd(); + VGamepad.hide(); } }; - - private float[][] vjoy_d_custom; - - private static final float[][] vjoy = VJoy.baseVJoy(); - private Context context; private View view; + private int joyPointerId = -1; + private float joyBiasX, joyBiasY; + private Map pidToControlId = new HashMap<>(); + private int mouseButtons = 0; + private int[] mousePos = { -32768, -32768 }; + private int mousePid = -1; public VirtualJoystickDelegate(View view) { this.view = view; this.context = view.getContext(); vibratorThread = VibratorThread.getInstance(); - - readCustomVjoyValues(); - scaleGestureDetector = new ScaleGestureDetector(context, new OscOnScaleGestureListener()); } + @Override public void stop() { vibratorThread.stopThread(); vibratorThread = null; } - public void readCustomVjoyValues() { - vjoy_d_custom = VJoy.readCustomVjoyValues(context); - } - - public void restoreCustomVjoyValues(float[][] vjoy_d_cached) { - vjoy_d_custom = vjoy_d_cached; - VJoy.writeCustomVjoyValues(vjoy_d_cached, context); - - resetEditMode(); - view.requestLayout(); - } - - private void reset_analog() + private boolean touchMouseEvent(MotionEvent event) { - - int j=11; - vjoy[j+1][0]=vjoy[j][0]+vjoy[j][2]/2-vjoy[j+1][2]/2; - vjoy[j+1][1]=vjoy[j][1]+vjoy[j][3]/2-vjoy[j+1][3]/2; - JNIdc.vjoy(j+1, vjoy[j+1][0], vjoy[j+1][1], vjoy[j+1][2], vjoy[j+1][3]); - } - - private int get_anal(int j, int axis) - { - return (int) (((vjoy[j+1][axis]+vjoy[j+1][axis+2]/2) - vjoy[j][axis] - vjoy[j][axis+2]/2)*254/vjoy[j][axis+2]); - } - - private float vbase(float p, float m, float scl) - { - return (int) ( m - (m -p)*scl); - } - - private float vbase(float p, float scl) - { - return (int) (p*scl ); - } - - private boolean isTablet() { - return (context.getResources().getConfiguration().screenLayout - & Configuration.SCREENLAYOUT_SIZE_MASK) - >= Configuration.SCREENLAYOUT_SIZE_LARGE; - } - - public void layout(int width, int height) - { - //dcpx/cm = dcpx/px * px/cm - float magic = isTablet() ? 0.8f : 0.7f; - float scl = 480.0f / height * context.getResources().getDisplayMetrics().density * magic; - float scl_dc = height / 480.0f; - float tx = (width - 640.0f * scl_dc) / 2 / scl_dc; - - float a_x = -tx + 24 * scl; - float a_y = -24 * scl; - - // Not sure how this can happen - if (vjoy_d_custom == null) - return; - - float[][] vjoy_d = VJoy.getVjoy_d(vjoy_d_custom); - - for (int i=0;i vjoy[j][0] && x <= (vjoy[j][0] + vjoy[j][2])) - { - if (y > vjoy[j][1] && y <= (vjoy[j][1] + vjoy[j][3])) - { - if (vjoy[j][4] >= VJoy.BTN_RTRIG) { - // Not for analog - if (vjoy[j][5] == 0) - if (!editVjoyMode) { - vibratorThread.click(); - } - vjoy[j][5] = 2; - } - - - if (vjoy[j][4] == VJoy.BTN_ANARING) { - if (editVjoyMode) { - selectedVjoyElement = VJoy.ELEM_ANALOG; - resetEditMode(); - } else { - vjoy[j + 1][0] = x - vjoy[j + 1][2] / 2; - vjoy[j + 1][1] = y - vjoy[j + 1][3] / 2; - - JNIdc.vjoy(j + 1, vjoy[j + 1][0], vjoy[j + 1][1], vjoy[j + 1][2], vjoy[j + 1][3]); - anal_id = event.getPointerId(i); - } - } else if (vjoy[j][4] != VJoy.BTN_ANAPOINT) { - if (vjoy[j][4] == VJoy.BTN_LTRIG) { - if (editVjoyMode) { - selectedVjoyElement = VJoy.ELEM_LTRIG; - resetEditMode(); - } else { - left_trigger = 255; - lt_id = event.getPointerId(i); - } - } else if (vjoy[j][4] == VJoy.BTN_RTRIG) { - if (editVjoyMode) { - selectedVjoyElement = VJoy.ELEM_RTRIG; - resetEditMode(); - } else { - right_trigger = 255; - rt_id = event.getPointerId(i); - } - } else { - if (editVjoyMode) { - selectedVjoyElement = getElementIdFromButtonId(j); - resetEditMode(); - } else if (vjoy[j][4] == VJoy.key_CONT_FFORWARD) - fastForward = true; - else - rv &= ~(int)vjoy[j][4]; - } - } - } - } - } - } else { - if (x < vjoy[11][0]) - x = vjoy[11][0]; - else if (x > (vjoy[11][0] + vjoy[11][2])) - x = vjoy[11][0] + vjoy[11][2]; - - if (y < vjoy[11][1]) - y = vjoy[11][1]; - else if (y > (vjoy[11][1] + vjoy[11][3])) - y = vjoy[11][1] + vjoy[11][3]; - - int j = 11; - vjoy[j + 1][0] = x - vjoy[j + 1][2] / 2; - vjoy[j + 1][1] = y - vjoy[j + 1][3] / 2; - - JNIdc.vjoy(j + 1, vjoy[j + 1][0], vjoy[j + 1][1], vjoy[j + 1][2], vjoy[j + 1][3]); - - } - } - - for (int j = 0; j < vjoy.length; j++) { - if (vjoy[j][5] == 2) - vjoy[j][5] = 1; - else if (vjoy[j][5] == 1) - vjoy[j][5] = 0; - } - } - - switch(aid) + int actionIndex = event.getActionIndex(); + switch (event.getActionMasked()) { case MotionEvent.ACTION_UP: case MotionEvent.ACTION_CANCEL: - selectedVjoyElement = -1; - reset_analog(); - anal_id = -1; - rv = 0xFFFFFFFF; - fastForward = false; - right_trigger = 0; - left_trigger = 0; - lt_id = -1; - rt_id = -1; - for (int j= 0 ;j < vjoy.length; j++) - vjoy[j][5] = 0; - mouse_btns = 0; + // Release all + pidToControlId.clear(); + joyPointerId = -1; + InputDeviceManager.getInstance().virtualReleaseAll(); break; - case MotionEvent.ACTION_POINTER_UP: - if (event.getPointerId(event.getActionIndex())==anal_id) - { - reset_analog(); - anal_id = -1; - } - else if (event.getPointerId(event.getActionIndex())==lt_id) - { - left_trigger = 0; - lt_id = -1; - } - else if (event.getPointerId(event.getActionIndex())==rt_id) + case MotionEvent.ACTION_DOWN: + // First release all + pidToControlId.clear(); + joyPointerId = -1; + InputDeviceManager.getInstance().virtualReleaseAll(); + // Release the mouse too + mousePid = -1; + mouseButtons = 0; + InputDeviceManager.getInstance().touchMouseEvent(mousePos[0], mousePos[1], mouseButtons); + // Then fall through + case MotionEvent.ACTION_POINTER_DOWN: + { + Point p = new Point(event.getX(actionIndex), event.getY(actionIndex)); + p = translateCoords(p, new Point(width, height)); + int control = VGamepad.hitTest(p.x, p.y); + if (control != -1) { - right_trigger = 0; - rt_id = -1; + int pid = event.getPointerId(actionIndex); + if (control == CTLID_ANARING || control == CTLID_ANASTICK) + { + if (joyPointerId == -1) + { + // Analog stick down + joyPointerId = pid; + joyBiasX = p.x; + joyBiasY = p.y; + InputDeviceManager.getInstance().virtualJoystick(0, 0); + return true; + } + } + else + { + // Button down + InputDeviceManager.getInstance().virtualButtonInput(control, true); + pidToControlId.put(pid, control); + vibratorThread.click(); + return true; + } } break; + } - case MotionEvent.ACTION_POINTER_DOWN: - case MotionEvent.ACTION_DOWN: - if (event.getPointerCount() != 1) - { - mouse_btns = 0; - } - else + case MotionEvent.ACTION_MOVE: + for (int i = 0; i < event.getPointerCount(); i++) { - mouse_pos[0] = Math.round(event.getX()); - mouse_pos[1] = Math.round(event.getY()); - mouse_btns = MotionEvent.BUTTON_PRIMARY; // Mouse left button down + int pid = event.getPointerId(i); + Point p = new Point(event.getX(i), event.getY(i)); + p = translateCoords(p, new Point(width, height)); + if (joyPointerId == pid) + { + // Analog stick + float dx = p.x - joyBiasX; + float dy = p.y - joyBiasY; + float sz = VGamepad.getControlWidth(CTLID_ANASTICK); + dx = Math.max(Math.min(1.f, dx / sz), -1.f); + dy = Math.max(Math.min(1.f, dy / sz), -1.f); + InputDeviceManager.getInstance().virtualJoystick(dx, dy); + continue; + } + // Buttons + int control = VGamepad.hitTest(p.x, p.y); + int oldControl = pidToControlId.containsKey(pid) ? pidToControlId.get(pid) : -1; + if (oldControl == control) + // same button still pressed, or none at all + continue; + if (oldControl != -1) { + // Previous button up + InputDeviceManager.getInstance().virtualButtonInput(oldControl, false); + pidToControlId.remove(pid); + } + if (control != -1 && control != CTLID_ANARING && control != CTLID_ANASTICK) + { + // New button down + InputDeviceManager.getInstance().virtualButtonInput(control, true); + pidToControlId.put(pid, control); + vibratorThread.click(); + } } break; - case MotionEvent.ACTION_MOVE: - if (event.getPointerCount() == 1) + case MotionEvent.ACTION_POINTER_UP: + { + int pid = event.getPointerId(actionIndex); + if (joyPointerId == pid) { - mouse_pos[0] = Math.round(event.getX()); - mouse_pos[1] = Math.round(event.getY()); + // Analog up + InputDeviceManager.getInstance().virtualJoystick(0, 0); + joyPointerId = -1; + return true; + } + if (pidToControlId.containsKey(pid)) + { + // Button up + int controlId = pidToControlId.get(pid); + InputDeviceManager.getInstance().virtualButtonInput(controlId, false); + return true; } break; - } - int joyx = get_anal(11, 0); - int joyy = get_anal(11, 1); - InputDeviceManager.getInstance().virtualGamepadEvent(rv, joyx, joyy, left_trigger, right_trigger, fastForward); - // Only register the mouse event if no virtual gamepad button is down - if ((!editVjoyMode && rv == 0xFFFFFFFF && left_trigger == 0 && right_trigger == 0 && joyx == 0 && joyy == 0 && !fastForward) - || JNIdc.guiIsOpen()) - InputDeviceManager.getInstance().mouseEvent(mouse_pos[0], mouse_pos[1], mouse_btns); - return(true); - } - - public void setEditVjoyMode(boolean editVjoyMode) { - this.editVjoyMode = editVjoyMode; - selectedVjoyElement = -1; - if (editVjoyMode) - this.handler.removeCallbacks(hideOsdRunnable); - resetEditMode(); - } - - private class OscOnScaleGestureListener extends - ScaleGestureDetector.SimpleOnScaleGestureListener { - - @Override - public boolean onScale(ScaleGestureDetector detector) { - if (editVjoyMode && selectedVjoyElement != -1) { - vjoy_d_custom[selectedVjoyElement][2] *= detector.getScaleFactor(); - view.requestLayout(); - - return true; } - - return false; } + return touchMouseEvent(event); + } - @Override - public void onScaleEnd(ScaleGestureDetector detector) { - selectedVjoyElement = -1; - } + @Override + public void show() + { + VGamepad.show(); + this.handler.removeCallbacks(hideVGamepadRunnable); + this.handler.postDelayed(hideVGamepadRunnable, 10000); } } diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/periph/InputDeviceManager.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/periph/InputDeviceManager.java index a8078f063..25850fc2a 100644 --- a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/periph/InputDeviceManager.java +++ b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/periph/InputDeviceManager.java @@ -11,8 +11,10 @@ import java.util.ArrayList; import java.util.HashMap; +import java.util.HashSet; import java.util.List; import java.util.Map; +import java.util.Set; import org.apache.commons.lang3.ArrayUtils; @@ -24,12 +26,15 @@ public final class InputDeviceManager implements InputManager.InputDeviceListene private InputManager inputManager; private int maple_port = 0; + private boolean hasTouchscreen = false; + private static class VibrationParams { float power; float inclination; long stopTime; } private Map vibParams = new HashMap<>(); + private Set knownDevices = new HashSet<>(); public InputDeviceManager() { @@ -39,12 +44,10 @@ public InputDeviceManager() public void startListening(Context applicationContext) { maple_port = 0; - if (applicationContext.getPackageManager().hasSystemFeature("android.hardware.touchscreen")) - joystickAdded(VIRTUAL_GAMEPAD_ID, "Virtual Gamepad", 0, "virtual_gamepad_uid", - new int[0], new int[0], getVibrator(VIRTUAL_GAMEPAD_ID) != null); - int[] ids = InputDevice.getDeviceIds(); - for (int id : ids) - onInputDeviceAdded(id); + hasTouchscreen = applicationContext.getPackageManager().hasSystemFeature("android.hardware.touchscreen"); + if (hasTouchscreen) + joystickAdded(VIRTUAL_GAMEPAD_ID, null, 0, null, + null, null, getVibrator(VIRTUAL_GAMEPAD_ID) != null); inputManager = (InputManager)applicationContext.getSystemService(Context.INPUT_SERVICE); inputManager.registerInputDeviceListener(this, null); } @@ -60,25 +63,6 @@ public void stopListening() @Override public void onInputDeviceAdded(int i) { - InputDevice device = InputDevice.getDevice(i); - if (device != null && (device.getSources() & InputDevice.SOURCE_CLASS_BUTTON) == InputDevice.SOURCE_CLASS_BUTTON) { - int port = 0; - if ((device.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) { - port = this.maple_port == 3 ? 3 : this.maple_port++; - } - List axes = device.getMotionRanges(); - List fullAxes = new ArrayList<>(); - List halfAxes = new ArrayList<>(); - for (InputDevice.MotionRange range : axes) { - if (range.getMin() == 0) - halfAxes.add(range.getAxis()); - else - fullAxes.add(range.getAxis()); - } - joystickAdded(i, device.getName(), port, device.getDescriptor(), - ArrayUtils.toPrimitive(fullAxes.toArray(new Integer[0])), ArrayUtils.toPrimitive(halfAxes.toArray(new Integer[0])), - getVibrator(i) != null); - } } @Override @@ -86,6 +70,7 @@ public void onInputDeviceRemoved(int i) { if (maple_port > 0) maple_port--; joystickRemoved(i); + knownDevices.remove(i); } @Override @@ -202,18 +187,67 @@ public void stopRumble() } } + public boolean hasTouchscreen() { + return hasTouchscreen; + } + + private boolean createDevice(int id) + { + if (id == 0) + return false; + if (knownDevices.contains(id)) + return true; + InputDevice device = InputDevice.getDevice(id); + if (device == null || (device.getSources() & InputDevice.SOURCE_CLASS_BUTTON) != InputDevice.SOURCE_CLASS_BUTTON) + return false; + int port = 0; + if ((device.getSources() & InputDevice.SOURCE_CLASS_JOYSTICK) == InputDevice.SOURCE_CLASS_JOYSTICK) { + port = this.maple_port == 3 ? 3 : this.maple_port++; + } + List axes = device.getMotionRanges(); + List fullAxes = new ArrayList<>(); + List halfAxes = new ArrayList<>(); + for (InputDevice.MotionRange range : axes) { + if (range.getMin() == 0) + halfAxes.add(range.getAxis()); + else + fullAxes.add(range.getAxis()); + } + joystickAdded(id, device.getName(), port, device.getDescriptor(), + ArrayUtils.toPrimitive(fullAxes.toArray(new Integer[0])), ArrayUtils.toPrimitive(halfAxes.toArray(new Integer[0])), + getVibrator(id) != null); + knownDevices.add(id); + return true; + } + public boolean buttonEvent(int id, int button, boolean pressed) + { + if (!createDevice(id)) + return false; + return joystickButtonEvent(id, button, pressed); + } + public boolean axisEvent(int id, int button, int value) + { + if (!createDevice(id)) + return false; + return joystickAxisEvent(id, button, value); + } + public static InputDeviceManager getInstance() { return INSTANCE; } public native void init(); - public native void virtualGamepadEvent(int kcode, int joyx, int joyy, int lt, int rt, boolean fastForward); - public native boolean joystickButtonEvent(int id, int button, boolean pressed); - public native boolean joystickAxisEvent(int id, int button, int value); + public native void virtualReleaseAll(); + public native void virtualJoystick(float x, float y); + public native void virtualButtonInput(int key, boolean pressed); + private native boolean joystickButtonEvent(int id, int button, boolean pressed); + private native boolean joystickAxisEvent(int id, int button, int value); public native void mouseEvent(int xpos, int ypos, int buttons); public native void mouseScrollEvent(int scrollValue); + public native void touchMouseEvent(int xpos, int ypos, int buttons); private native void joystickAdded(int id, String name, int maple_port, String uniqueId, int[] fullAxes, int[] halfAxes, boolean rumbleEnabled); private native void joystickRemoved(int id); public native boolean keyboardEvent(int key, boolean pressed); public native void keyboardText(int c); + public static native boolean isMicPluggedIn(); } diff --git a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/periph/VJoy.java b/shell/android-studio/flycast/src/main/java/com/flycast/emulator/periph/VJoy.java deleted file mode 100644 index c94950aed..000000000 --- a/shell/android-studio/flycast/src/main/java/com/flycast/emulator/periph/VJoy.java +++ /dev/null @@ -1,219 +0,0 @@ -package com.flycast.emulator.periph; - -import android.content.Context; -import android.content.SharedPreferences; -import android.preference.PreferenceManager; - -public class VJoy { - - public static final int key_CONT_C = 0x0001; - public static final int key_CONT_B = 0x0002; - public static final int key_CONT_A = 0x0004; - public static final int key_CONT_START = 0x0008; - public static final int key_CONT_DPAD_UP = 0x0010; - public static final int key_CONT_DPAD_DOWN = 0x0020; - public static final int key_CONT_DPAD_LEFT = 0x0040; - public static final int key_CONT_DPAD_RIGHT = 0x0080; - public static final int key_CONT_Y = 0x0200; - public static final int key_CONT_X = 0x0400; - public static final int key_CONT_FFORWARD = 0x3000002; - - public static final int BTN_LTRIG = -1; - public static final int BTN_RTRIG = -2; - public static final int BTN_ANARING = -3; - public static final int BTN_ANAPOINT = -4; - - public static final int ELEM_NONE = -1; - public static final int ELEM_DPAD = 0; - public static final int ELEM_BUTTONS = 1; - public static final int ELEM_START = 2; - public static final int ELEM_LTRIG = 3; - public static final int ELEM_RTRIG = 4; - public static final int ELEM_ANALOG = 5; - public static final int ELEM_FFORWARD = 6; - - public static int VJoyCount = 14; - - public static float[][] baseVJoy() { - return new float[][] { - new float[] { 24, 24+64, 64,64, key_CONT_DPAD_LEFT, 0}, - new float[] { 24+64, 24, 64,64, key_CONT_DPAD_UP, 0}, - new float[] { 24+128, 24+64, 64,64, key_CONT_DPAD_RIGHT, 0}, - new float[] { 24+64, 24+128, 64,64, key_CONT_DPAD_DOWN, 0}, - - new float[] { 440, 280+64, 64,64, key_CONT_X, 0}, - new float[] { 440+64, 280, 64,64, key_CONT_Y, 0}, - new float[] { 440+128, 280+64, 64,64, key_CONT_B, 0}, - new float[] { 440+64, 280+128,64,64, key_CONT_A, 0}, - - new float[] { 320-32, 360+32, 64,64, key_CONT_START, 0}, - - new float[] { 440, 200, 90,64, BTN_LTRIG, 0}, // LT - new float[] { 542, 200, 90,64, BTN_RTRIG, 0}, // RT - - new float[] { 0, 128+224,128,128,BTN_ANARING, 0}, // Analog ring - new float[] { 32, 128+256,64,64, BTN_ANAPOINT, 0}, // Analog point - - new float[] { 320-32, 12, 64,64, key_CONT_FFORWARD, 0}, // Fast-forward - - new float[] { 20, 288, 64,64, key_CONT_DPAD_LEFT|key_CONT_DPAD_UP, 0}, // DPad diagonals - new float[] { 20+128, 288, 64,64, key_CONT_DPAD_RIGHT|key_CONT_DPAD_UP, 0}, - new float[] { 20, 288+128,64,64, key_CONT_DPAD_LEFT|key_CONT_DPAD_DOWN, 0}, - new float[] { 20+128, 288+128,64,64, key_CONT_DPAD_RIGHT|key_CONT_DPAD_DOWN, 0}, - }; - } - - public static float[][] readCustomVjoyValues(Context context) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - - return new float[][] { - // x-shift, y-shift, sizing-factor - new float[] { prefs.getFloat("touch_x_shift_dpad", 0), - prefs.getFloat("touch_y_shift_dpad", 0), - prefs.getFloat("touch_scale_dpad", 1) - }, // DPAD - new float[] { prefs.getFloat("touch_x_shift_buttons", 0), - prefs.getFloat("touch_y_shift_buttons", 0), - prefs.getFloat("touch_scale_buttons", 1) - }, // X, Y, B, A Buttons - new float[] { prefs.getFloat("touch_x_shift_start", 0), - prefs.getFloat("touch_y_shift_start", 0), - prefs.getFloat("touch_scale_start", 1) - }, // Start - new float[] { prefs.getFloat("touch_x_shift_left_trigger", 0), - prefs.getFloat("touch_y_shift_left_trigger", 0), - prefs.getFloat("touch_scale_left_trigger", 1) - }, // Left Trigger - new float[] { prefs.getFloat("touch_x_shift_right_trigger", 0), - prefs.getFloat("touch_y_shift_right_trigger", 0), - prefs.getFloat("touch_scale_right_trigger", 1) - }, // Right Trigger - new float[] { prefs.getFloat("touch_x_shift_analog", 0), - prefs.getFloat("touch_y_shift_analog", 0), - prefs.getFloat("touch_scale_analog", 1) - }, // Analog Stick - new float[] { prefs.getFloat("touch_x_shift_fforward", 0), - prefs.getFloat("touch_y_shift_fforward", 0), - prefs.getFloat("touch_scale_fforward", 1) - } // Fast-forward - }; - } - - public static float[][] getVjoy_d(float[][] vjoy_d_custom) { - return new float[][] { - // LEFT, UP, RIGHT, DOWN - new float[] { 20+0*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+64*vjoy_d_custom[0][2]+vjoy_d_custom[0][1], - 64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_LEFT}, - new float[] { 20+64*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+0*vjoy_d_custom[0][2]+vjoy_d_custom[0][1], - 64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_UP}, - new float[] { 20+128*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+64*vjoy_d_custom[0][2]+vjoy_d_custom[0][1], - 64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_RIGHT}, - new float[] { 20+64*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+128*vjoy_d_custom[0][2]+vjoy_d_custom[0][1], - 64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_DOWN}, - - // X, Y, B, A - new float[] { 448+0*vjoy_d_custom[1][2]+vjoy_d_custom[1][0], 288+64*vjoy_d_custom[1][2]+vjoy_d_custom[1][1], - 64*vjoy_d_custom[1][2],64*vjoy_d_custom[1][2], key_CONT_X}, - new float[] { 448+64*vjoy_d_custom[1][2]+vjoy_d_custom[1][0], 288+0*vjoy_d_custom[1][2]+vjoy_d_custom[1][1], - 64*vjoy_d_custom[1][2],64*vjoy_d_custom[1][2], key_CONT_Y}, - new float[] { 448+128*vjoy_d_custom[1][2]+vjoy_d_custom[1][0], 288+64*vjoy_d_custom[1][2]+vjoy_d_custom[1][1], - 64*vjoy_d_custom[1][2],64*vjoy_d_custom[1][2], key_CONT_B}, - new float[] { 448+64*vjoy_d_custom[1][2]+vjoy_d_custom[1][0], 288+128*vjoy_d_custom[1][2]+vjoy_d_custom[1][1], - 64*vjoy_d_custom[1][2],64*vjoy_d_custom[1][2], key_CONT_A}, - - // START - new float[] { 320-32+vjoy_d_custom[2][0], 288+128+vjoy_d_custom[2][1], - 64*vjoy_d_custom[2][2],64*vjoy_d_custom[2][2], key_CONT_START}, - - // LT, RT - new float[] { 440+vjoy_d_custom[3][0], 200+vjoy_d_custom[3][1], - 90*vjoy_d_custom[3][2],64*vjoy_d_custom[3][2], -1}, - new float[] { 542+vjoy_d_custom[4][0], 200+vjoy_d_custom[4][1], - 90*vjoy_d_custom[4][2],64*vjoy_d_custom[4][2], -2}, - - // Analog ring and point - new float[] { 16+vjoy_d_custom[5][0], 24+32+vjoy_d_custom[5][1], - 128*vjoy_d_custom[5][2],128*vjoy_d_custom[5][2],-3}, - new float[] { 48+vjoy_d_custom[5][0], 24+64+vjoy_d_custom[5][1], - 64*vjoy_d_custom[5][2],64*vjoy_d_custom[5][2], -4}, - - // Fast-forward - new float[] { 320-32+vjoy_d_custom[6][0], 12+vjoy_d_custom[6][1], - 64*vjoy_d_custom[6][2],64*vjoy_d_custom[6][2], -5}, - - // DPad diagonals - new float[] { 20+0*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+0*vjoy_d_custom[0][2]+vjoy_d_custom[0][1], - 64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_LEFT|key_CONT_DPAD_UP}, - new float[] { 20+128*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+0*vjoy_d_custom[0][2]+vjoy_d_custom[0][1], - 64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_RIGHT|key_CONT_DPAD_UP}, - new float[] { 20+0*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+128*vjoy_d_custom[0][2]+vjoy_d_custom[0][1], - 64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_LEFT|key_CONT_DPAD_DOWN}, - new float[] { 20+128*vjoy_d_custom[0][2]+vjoy_d_custom[0][0], 288+128*vjoy_d_custom[0][2]+vjoy_d_custom[0][1], - 64*vjoy_d_custom[0][2],64*vjoy_d_custom[0][2], key_CONT_DPAD_RIGHT|key_CONT_DPAD_DOWN}, - }; - } - - public static void writeCustomVjoyValues(float[][] vjoy_d_custom, Context context) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - - prefs.edit().putFloat("touch_x_shift_dpad", vjoy_d_custom[0][0]).apply(); - prefs.edit().putFloat("touch_y_shift_dpad", vjoy_d_custom[0][1]).apply(); - prefs.edit().putFloat("touch_scale_dpad", vjoy_d_custom[0][2]).apply(); - - prefs.edit().putFloat("touch_x_shift_buttons", vjoy_d_custom[1][0]).apply(); - prefs.edit().putFloat("touch_y_shift_buttons", vjoy_d_custom[1][1]).apply(); - prefs.edit().putFloat("touch_scale_buttons", vjoy_d_custom[1][2]).apply(); - - prefs.edit().putFloat("touch_x_shift_start", vjoy_d_custom[2][0]).apply(); - prefs.edit().putFloat("touch_y_shift_start", vjoy_d_custom[2][1]).apply(); - prefs.edit().putFloat("touch_scale_start", vjoy_d_custom[2][2]).apply(); - - prefs.edit().putFloat("touch_x_shift_left_trigger", vjoy_d_custom[3][0]).apply(); - prefs.edit().putFloat("touch_y_shift_left_trigger", vjoy_d_custom[3][1]).apply(); - prefs.edit().putFloat("touch_scale_left_trigger", vjoy_d_custom[3][2]).apply(); - - prefs.edit().putFloat("touch_x_shift_right_trigger", vjoy_d_custom[4][0]).apply(); - prefs.edit().putFloat("touch_y_shift_right_trigger", vjoy_d_custom[4][1]).apply(); - prefs.edit().putFloat("touch_scale_right_trigger", vjoy_d_custom[4][2]).apply(); - - prefs.edit().putFloat("touch_x_shift_analog", vjoy_d_custom[5][0]).apply(); - prefs.edit().putFloat("touch_y_shift_analog", vjoy_d_custom[5][1]).apply(); - prefs.edit().putFloat("touch_scale_analog", vjoy_d_custom[5][2]).apply(); - - prefs.edit().putFloat("touch_x_shift_fforward", vjoy_d_custom[6][0]).apply(); - prefs.edit().putFloat("touch_y_shift_fforward", vjoy_d_custom[6][1]).apply(); - prefs.edit().putFloat("touch_scale_fforward", vjoy_d_custom[6][2]).apply(); - } - - public static void resetCustomVjoyValues(Context context) { - SharedPreferences prefs = PreferenceManager.getDefaultSharedPreferences(context); - - prefs.edit().remove("touch_x_shift_dpad").apply(); - prefs.edit().remove("touch_y_shift_dpad").apply(); - prefs.edit().remove("touch_scale_dpad").apply(); - - prefs.edit().remove("touch_x_shift_buttons").apply(); - prefs.edit().remove("touch_y_shift_buttons").apply(); - prefs.edit().remove("touch_scale_buttons").apply(); - - prefs.edit().remove("touch_x_shift_start").apply(); - prefs.edit().remove("touch_y_shift_start").apply(); - prefs.edit().remove("touch_scale_start").apply(); - - prefs.edit().remove("touch_x_shift_left_trigger").apply(); - prefs.edit().remove("touch_y_shift_left_trigger").apply(); - prefs.edit().remove("touch_scale_left_trigger").apply(); - - prefs.edit().remove("touch_x_shift_right_trigger").apply(); - prefs.edit().remove("touch_y_shift_right_trigger").apply(); - prefs.edit().remove("touch_scale_right_trigger").apply(); - - prefs.edit().remove("touch_x_shift_analog").apply(); - prefs.edit().remove("touch_y_shift_analog").apply(); - prefs.edit().remove("touch_scale_analog").apply(); - - prefs.edit().remove("touch_x_shift_fforward").apply(); - prefs.edit().remove("touch_y_shift_fforward").apply(); - prefs.edit().remove("touch_scale_fforward").apply(); - } -} diff --git a/shell/android-studio/flycast/src/main/jni/src/Android.cpp b/shell/android-studio/flycast/src/main/jni/src/Android.cpp index 1e17e0ee5..e4a250907 100644 --- a/shell/android-studio/flycast/src/main/jni/src/Android.cpp +++ b/shell/android-studio/flycast/src/main/jni/src/Android.cpp @@ -1,8 +1,4 @@ #include "types.h" -#include "hw/maple/maple_cfg.h" -#include "rend/osd.h" -#include "hw/maple/maple_devs.h" -#include "hw/maple/maple_if.h" #include "hw/naomi/naomi_cart.h" #include "audio/audiostream.h" #include "imgread/common.h" @@ -21,6 +17,7 @@ #endif #include "jni_util.h" #include "android_storage.h" +#include "http_client.h" #include #include @@ -39,26 +36,12 @@ namespace jni thread_local JVMAttacher jvm_attacher; } -#include "android_gamepad.h" -#include "android_keyboard.h" -#include "http_client.h" - -extern "C" JNIEXPORT jint JNICALL Java_com_flycast_emulator_emu_JNIdc_getVirtualGamepadVibration(JNIEnv *env, jobject obj) -{ - return (jint)config::VirtualGamepadVibration; -} - extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_screenCharacteristics(JNIEnv *env, jobject obj, jfloat screenDpi, jfloat refreshRate) { settings.display.dpi = screenDpi; settings.display.refreshRate = refreshRate; } -std::shared_ptr mouse; -std::shared_ptr keyboard; - -float vjoy_pos[15][8]; - static bool game_started; //stuff for saving prefs @@ -67,12 +50,10 @@ jmethodID saveAndroidSettingsMid; static ANativeWindow *g_window = 0; // Activity -static jobject g_activity; -static jmethodID VJoyStartEditingMID; -static jmethodID VJoyStopEditingMID; -static jmethodID VJoyResetEditingMID; -static jmethodID showScreenKeyboardMid; +jobject g_activity; +extern jmethodID showScreenKeyboardMid; static jmethodID onGameStateChangeMid; +extern jmethodID setVGamepadEditModeMid; static void emuEventCallback(Event event, void *) { @@ -359,43 +340,6 @@ extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_rendinitNa } } -extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_vjoy(JNIEnv * env, jobject obj,int id,float x, float y, float w, float h) -{ - if (id < std::size(vjoy_pos)) - { - vjoy_pos[id][0] = x; - vjoy_pos[id][1] = y; - vjoy_pos[id][2] = w; - vjoy_pos[id][3] = h; - } -} - -extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_hideOsd(JNIEnv * env, jobject obj) -{ - HideOSD(); -} - -extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_getControllers(JNIEnv *env, jobject obj, jintArray controllers, jobjectArray peripherals) -{ - // might be called before JNIdc.initEnvironment() - if (g_jvm == NULL) - env->GetJavaVM(&g_jvm); - - jni::IntArray jcontrollers(controllers, false); - std::vector devs; - for (u32 i = 0; i < config::MapleMainDevices.size(); i++) - devs.push_back((MapleDeviceType)config::MapleMainDevices[i]); - jcontrollers.setData(devs.data()); - - jni::ObjectArray jperipherals(peripherals, false); - int obj_len = jperipherals.size(); - for (int i = 0; i < obj_len; ++i) - { - std::vector devs { (MapleDeviceType)config::MapleExpansionDevices[i][0], (MapleDeviceType)config::MapleExpansionDevices[i][1] }; - jperipherals[i].setData(devs.data()); - } -} - extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_JNIdc_guiOpenSettings(JNIEnv *env, jobject obj) { gui_open_settings(); @@ -502,92 +446,6 @@ void SaveAndroidSettings() jni::env()->CallVoidMethod(g_emulator, saveAndroidSettingsMid, (jstring)homeDirectory); } -extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_init(JNIEnv *env, jobject obj) -{ - input_device_manager = env->NewGlobalRef(obj); - input_device_manager_rumble = env->GetMethodID(env->GetObjectClass(obj), "rumble", "(IFFI)Z"); - // FIXME Don't connect it by default or any screen touch will register as button A press - mouse = std::make_shared(-1); - GamepadDevice::Register(mouse); - keyboard = std::make_shared(); - GamepadDevice::Register(keyboard); - gui_setOnScreenKeyboardCallback([](bool show) { - if (g_activity != nullptr) - jni::env()->CallVoidMethod(g_activity, showScreenKeyboardMid, show); - }); -} - -extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, jint id, jstring name, - jint maple_port, jstring junique_id, jintArray fullAxes, jintArray halfAxes, jboolean hasRumble) -{ - std::string joyname = jni::String(name, false); - std::string unique_id = jni::String(junique_id, false); - std::vector full = jni::IntArray(fullAxes, false); - std::vector half = jni::IntArray(halfAxes, false); - - std::shared_ptr gamepad = std::make_shared(maple_port, id, joyname.c_str(), unique_id.c_str(), full, half); - AndroidGamepadDevice::AddAndroidGamepad(gamepad); - gamepad->setRumbleEnabled(hasRumble); -} -extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickRemoved(JNIEnv *env, jobject obj, jint id) -{ - std::shared_ptr device = AndroidGamepadDevice::GetAndroidGamepad(id); - if (device != NULL) - AndroidGamepadDevice::RemoveAndroidGamepad(device); -} - -extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_virtualGamepadEvent(JNIEnv *env, jobject obj, jint kcode, jint joyx, jint joyy, jint lt, jint rt, jboolean fastForward) -{ - std::shared_ptr device = AndroidGamepadDevice::GetAndroidGamepad(AndroidGamepadDevice::VIRTUAL_GAMEPAD_ID); - if (device != NULL) - device->virtual_gamepad_event(kcode, joyx, joyy, lt, rt, fastForward); -} - -extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickButtonEvent(JNIEnv *env, jobject obj, jint id, jint key, jboolean pressed) -{ - std::shared_ptr device = AndroidGamepadDevice::GetAndroidGamepad(id); - if (device != NULL) - return device->gamepad_btn_input(key, pressed); - else - return false; - -} - -extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_keyboardEvent(JNIEnv *env, jobject obj, jint key, jboolean pressed) -{ - keyboard->input(key, pressed); - return true; -} - -extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_keyboardText(JNIEnv *env, jobject obj, jint c) -{ - gui_keyboard_input((u16)c); -} - -static std::map, jint> previous_axis_values; - -extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickAxisEvent(JNIEnv *env, jobject obj, jint id, jint key, jint value) -{ - std::shared_ptr device = AndroidGamepadDevice::GetAndroidGamepad(id); - if (device != nullptr) - return device->gamepad_axis_input(key, value); - else - return false; -} - -extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_mouseEvent(JNIEnv *env, jobject obj, jint xpos, jint ypos, jint buttons) -{ - mouse->setAbsPos(xpos, ypos, settings.display.width, settings.display.height); - mouse->setButton(Mouse::LEFT_BUTTON, (buttons & 1) != 0); - mouse->setButton(Mouse::RIGHT_BUTTON, (buttons & 2) != 0); - mouse->setButton(Mouse::MIDDLE_BUTTON, (buttons & 4) != 0); -} - -extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_mouseScrollEvent(JNIEnv *env, jobject obj, jint scrollValue) -{ - mouse->setWheel(scrollValue); -} - extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_BaseGLActivity_register(JNIEnv *env, jobject obj, jobject activity) { if (g_activity != nullptr) { @@ -598,29 +456,12 @@ extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_BaseGLActivity_regis { g_activity = env->NewGlobalRef(activity); jclass actClass = env->GetObjectClass(activity); - VJoyStartEditingMID = env->GetMethodID(actClass, "VJoyStartEditing", "()V"); - VJoyStopEditingMID = env->GetMethodID(actClass, "VJoyStopEditing", "(Z)V"); - VJoyResetEditingMID = env->GetMethodID(actClass, "VJoyResetEditing", "()V"); showScreenKeyboardMid = env->GetMethodID(actClass, "showScreenKeyboard", "(Z)V"); onGameStateChangeMid = env->GetMethodID(actClass, "onGameStateChange", "(Z)V"); + setVGamepadEditModeMid = env->GetMethodID(actClass, "setVGamepadEditMode", "(Z)V"); } } -void vjoy_start_editing() -{ - jni::env()->CallVoidMethod(g_activity, VJoyStartEditingMID); -} - -void vjoy_reset_editing() -{ - jni::env()->CallVoidMethod(g_activity, VJoyResetEditingMID); -} - -void vjoy_stop_editing(bool canceled) -{ - jni::env()->CallVoidMethod(g_activity, VJoyStopEditingMID, canceled); -} - void enableNetworkBroadcast(bool enable) { JNIEnv *env = jni::env(); diff --git a/shell/android-studio/flycast/src/main/jni/src/android_gamepad.h b/shell/android-studio/flycast/src/main/jni/src/android_gamepad.h index c637e3c44..9b9fd4801 100644 --- a/shell/android-studio/flycast/src/main/jni/src/android_gamepad.h +++ b/shell/android-studio/flycast/src/main/jni/src/android_gamepad.h @@ -20,6 +20,7 @@ #include "input/gamepad_device.h" #include "input/mouse.h" +#include "input/virtual_gamepad.h" #include "jni_util.h" #include @@ -97,26 +98,18 @@ class AndroidGamepadDevice : public GamepadDevice public: AndroidGamepadDevice(int maple_port, int id, const char *name, const char *unique_id, const std::vector& fullAxes, const std::vector& halfAxes) - : GamepadDevice(maple_port, "Android", id != VIRTUAL_GAMEPAD_ID), android_id(id), + : GamepadDevice(maple_port, "Android"), android_id(id), fullAxes(fullAxes), halfAxes(halfAxes) { _name = name; _unique_id = unique_id; INFO_LOG(INPUT, "Android: Opened joystick %d on port %d: '%s' descriptor '%s'", id, maple_port, _name.c_str(), _unique_id.c_str()); - if (id == VIRTUAL_GAMEPAD_ID) - { - input_mapper = std::make_shared(); - // hasAnalogStick = true; // TODO has an analog stick but input mapping isn't persisted - } - else - { - loadMapping(); - save_mapping(); - hasAnalogStick = !fullAxes.empty(); - } + + loadMapping(); + save_mapping(); + hasAnalogStick = !fullAxes.empty(); } - ~AndroidGamepadDevice() override - { + ~AndroidGamepadDevice() override { INFO_LOG(INPUT, "Android: Joystick '%s' on port %d disconnected", _name.c_str(), maple_port()); } @@ -247,66 +240,6 @@ class AndroidGamepadDevice : public GamepadDevice GamepadDevice::Unregister(gamepad); }; - void virtual_gamepad_event(int kcode, int joyx, int joyy, int lt, int rt, bool fastForward) - { - // No virtual gamepad when the GUI is open: touch events only - if (gui_is_open()) - { - kcode = 0xffffffff; - joyx = joyy = rt = lt = 0; - } - if (settings.platform.isArcade()) - { - if (rt > 0) - { - if ((kcode & DC_BTN_A) == 0) - // RT + A -> D (coin) - kcode &= ~DC_BTN_D; - if ((kcode & DC_BTN_B) == 0) - // RT + B -> Service - kcode &= ~DC_DPAD2_UP; - if ((kcode & DC_BTN_X) == 0) - // RT + X -> Test - kcode &= ~DC_DPAD2_DOWN; - } - // arcade mapping: X -> btn2, Y -> btn3 - if ((kcode & DC_BTN_X) == 0) - { - kcode &= ~DC_BTN_C; - kcode |= DC_BTN_X; - } - if ((kcode & DC_BTN_Y) == 0) - { - kcode &= ~DC_BTN_X; - kcode |= DC_BTN_Y; - } - if (rt > 0) - // naomi btn4 - kcode &= ~DC_BTN_Y; - if (lt > 0) - // naomi btn5 - kcode &= ~DC_BTN_Z; - } - u32 changes = kcode ^ previous_kcode; - for (int i = 0; i < 32; i++) - if (changes & (1 << i)) - gamepad_btn_input(1 << i, (kcode & (1 << i)) == 0); - if (joyx >= 0) - gamepad_axis_input(DC_AXIS_RIGHT, joyx | (joyx << 8)); - else - gamepad_axis_input(DC_AXIS_LEFT, -joyx | (-joyx << 8)); - if (joyy >= 0) - gamepad_axis_input(DC_AXIS_DOWN, joyy | (joyy << 8)); - else - gamepad_axis_input(DC_AXIS_UP, -joyy | (-joyy << 8)); - gamepad_axis_input(DC_AXIS_LT, lt == 0 ? 0 : 0x7fff); - gamepad_axis_input(DC_AXIS_RT, rt == 0 ? 0 : 0x7fff); - previous_kcode = kcode; - if (fastForward != previousFastForward) - gamepad_btn_input(EMU_BTN_FFORWARD, fastForward); - previousFastForward = fastForward; - } - void rumble(float power, float inclination, u32 duration_ms) override { power *= rumblePower / 100.f; @@ -317,8 +250,6 @@ class AndroidGamepadDevice : public GamepadDevice this->rumbleEnabled = rumbleEnabled; } - bool is_virtual_gamepad() override { return android_id == VIRTUAL_GAMEPAD_ID; } - bool hasHalfAxis(int axis) const { return std::find(halfAxes.begin(), halfAxes.end(), axis) != halfAxes.end(); } bool hasFullAxis(int axis) const { return std::find(fullAxes.begin(), fullAxes.end(), axis) != fullAxes.end(); } @@ -336,13 +267,9 @@ class AndroidGamepadDevice : public GamepadDevice input_mapper = std::make_shared>(*this); } - static const int VIRTUAL_GAMEPAD_ID = 0x12345678; // must match the Java definition - private: int android_id; static std::map> android_gamepads; - u32 previous_kcode = 0xffffffff; - bool previousFastForward = false; std::vector fullAxes; std::vector halfAxes; }; @@ -481,3 +408,19 @@ class AndroidMouse : public SystemMouse } }; +class AndroidVirtualGamepad : public VirtualGamepad +{ +public: + AndroidVirtualGamepad(bool rumbleEnabled) : VirtualGamepad("Flycast") { + this->rumbleEnabled = rumbleEnabled; + } + + void rumble(float power, float inclination, u32 duration_ms) override + { + power *= rumblePower / 100.f; + jboolean has_vibrator = jni::env()->CallBooleanMethod(input_device_manager, input_device_manager_rumble, GAMEPAD_ID, power, inclination, duration_ms); + rumbleEnabled = has_vibrator; + } + + static constexpr int GAMEPAD_ID = 0x12345678; // must match the Java definition +}; diff --git a/shell/android-studio/flycast/src/main/jni/src/android_input.cpp b/shell/android-studio/flycast/src/main/jni/src/android_input.cpp new file mode 100644 index 000000000..9814a84f0 --- /dev/null +++ b/shell/android-studio/flycast/src/main/jni/src/android_input.cpp @@ -0,0 +1,235 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#include "android_gamepad.h" +#include "android_keyboard.h" +#include "ui/vgamepad.h" +#include "cfg/option.h" +#include "hw/maple/maple_if.h" + +std::shared_ptr mouse; +std::shared_ptr touchMouse; +std::shared_ptr keyboard; +std::shared_ptr virtualGamepad; + +extern jobject g_activity; +jmethodID showScreenKeyboardMid; +jmethodID setVGamepadEditModeMid; + +// +// VGamepad +// +extern "C" JNIEXPORT jint JNICALL Java_com_flycast_emulator_emu_VGamepad_getVibrationPower(JNIEnv *env, jobject obj) { + return (jint)config::VirtualGamepadVibration; +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_VGamepad_show(JNIEnv * env, jobject obj) { + vgamepad::show(); +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_VGamepad_hide(JNIEnv * env, jobject obj) { + vgamepad::hide(); +} + +extern "C" JNIEXPORT jint JNICALL Java_com_flycast_emulator_emu_VGamepad_hitTest(JNIEnv * env, jobject obj, + jfloat x, jfloat y) { + return vgamepad::hitTest(x, y); +} + +extern "C" JNIEXPORT jfloat JNICALL Java_com_flycast_emulator_emu_VGamepad_getControlWidth(JNIEnv * env, jobject obj, + jint controlId) { + return vgamepad::getControlWidth(static_cast(controlId)); +} + +extern "C" JNIEXPORT jint JNICALL Java_com_flycast_emulator_emu_VGamepad_layoutHitTest(JNIEnv * env, jobject obj, + jfloat x, jfloat y) { + return vgamepad::layoutHitTest(x, y); +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_VGamepad_scaleElement(JNIEnv * env, jobject obj, + jint elemId, jfloat scale) { + vgamepad::scaleElement(static_cast(elemId), scale); +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_emu_VGamepad_translateElement(JNIEnv * env, jobject obj, + jint elemId, jfloat x, jfloat y) { + vgamepad::translateElement(static_cast(elemId), x, y); +} + +namespace vgamepad +{ + +void setEditMode(bool editing) { + jni::env()->CallVoidMethod(g_activity, setVGamepadEditModeMid, editing); +} + +} + +// +// InputDeviceManager +// +extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_isMicPluggedIn(JNIEnv *env, jobject obj) +{ + for (const auto& devices : config::MapleExpansionDevices) + if (static_cast(devices[0]) == MDT_Microphone + || static_cast(devices[1]) == MDT_Microphone) + return true; + + return false; +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_init(JNIEnv *env, jobject obj) +{ + input_device_manager = env->NewGlobalRef(obj); + input_device_manager_rumble = env->GetMethodID(env->GetObjectClass(obj), "rumble", "(IFFI)Z"); + gui_setOnScreenKeyboardCallback([](bool show) { + if (g_activity != nullptr) + jni::env()->CallVoidMethod(g_activity, showScreenKeyboardMid, show); + }); +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickAdded(JNIEnv *env, jobject obj, + jint id, jstring name, jint maple_port, jstring junique_id, jintArray fullAxes, jintArray halfAxes, jboolean hasRumble) +{ + if (id == 0) + return; + if (id == AndroidVirtualGamepad::GAMEPAD_ID) + { + virtualGamepad = std::make_shared(hasRumble); + GamepadDevice::Register(virtualGamepad); + touchMouse = std::make_shared(); + GamepadDevice::Register(touchMouse); + } + else + { + std::string joyname = jni::String(name, false); + std::string unique_id = jni::String(junique_id, false); + std::vector full = jni::IntArray(fullAxes, false); + std::vector half = jni::IntArray(halfAxes, false); + + std::shared_ptr gamepad = std::make_shared(maple_port, id, joyname.c_str(), unique_id.c_str(), full, half); + AndroidGamepadDevice::AddAndroidGamepad(gamepad); + gamepad->setRumbleEnabled(hasRumble); + } +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickRemoved(JNIEnv *env, jobject obj, + jint id) +{ + if (id == AndroidVirtualGamepad::GAMEPAD_ID) + { + GamepadDevice::Unregister(virtualGamepad); + virtualGamepad.reset(); + GamepadDevice::Unregister(touchMouse); + touchMouse.reset(); + } + else { + std::shared_ptr device = AndroidGamepadDevice::GetAndroidGamepad(id); + if (device) + AndroidGamepadDevice::RemoveAndroidGamepad(device); + } +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_virtualReleaseAll(JNIEnv *env, jobject obj) { + if (virtualGamepad) + virtualGamepad->releaseAll(); +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_virtualJoystick(JNIEnv *env, jobject obj, + jfloat x, jfloat y) { + if (virtualGamepad) + virtualGamepad->joystickInput(x, y); +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_virtualButtonInput(JNIEnv *env, jobject obj, + jint controlId, jboolean pressed) { + if (virtualGamepad) + virtualGamepad->buttonInput(static_cast(controlId), pressed); +} + +extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickButtonEvent(JNIEnv *env, jobject obj, + jint id, jint key, jboolean pressed) +{ + std::shared_ptr device = AndroidGamepadDevice::GetAndroidGamepad(id); + if (device != NULL) + return device->gamepad_btn_input(key, pressed); + else + return false; +} + +extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_keyboardEvent(JNIEnv *env, jobject obj, + jint key, jboolean pressed) +{ + if (keyboard == nullptr) { + keyboard = std::make_shared(); + GamepadDevice::Register(keyboard); + } + keyboard->input(key, pressed); + return true; +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_keyboardText(JNIEnv *env, jobject obj, + jint c) { + gui_keyboard_input((u16)c); +} + +static std::map, jint> previous_axis_values; + +extern "C" JNIEXPORT jboolean JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_joystickAxisEvent(JNIEnv *env, jobject obj, + jint id, jint key, jint value) +{ + std::shared_ptr device = AndroidGamepadDevice::GetAndroidGamepad(id); + if (device != nullptr) + return device->gamepad_axis_input(key, value); + else + return false; +} + +static void createMouse() +{ + if (mouse == nullptr) { + mouse = std::make_shared(touchMouse == nullptr ? 0 : 1); + GamepadDevice::Register(mouse); + } +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_mouseEvent(JNIEnv *env, jobject obj, + jint xpos, jint ypos, jint buttons) +{ + createMouse(); + mouse->setAbsPos(xpos, ypos, settings.display.width, settings.display.height); + mouse->setButton(Mouse::LEFT_BUTTON, (buttons & 1) != 0); + mouse->setButton(Mouse::RIGHT_BUTTON, (buttons & 2) != 0); + mouse->setButton(Mouse::MIDDLE_BUTTON, (buttons & 4) != 0); +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_mouseScrollEvent(JNIEnv *env, jobject obj, + jint scrollValue) +{ + createMouse(); + mouse->setWheel(scrollValue); +} + +extern "C" JNIEXPORT void JNICALL Java_com_flycast_emulator_periph_InputDeviceManager_touchMouseEvent(JNIEnv *env, jobject obj, + jint xpos, jint ypos, jint buttons) +{ + touchMouse->setAbsPos(xpos, ypos, settings.display.width, settings.display.height); + touchMouse->setButton(Mouse::LEFT_BUTTON, (buttons & 1) != 0); + touchMouse->setButton(Mouse::RIGHT_BUTTON, (buttons & 2) != 0); + touchMouse->setButton(Mouse::MIDDLE_BUTTON, (buttons & 4) != 0); +} diff --git a/shell/android-studio/flycast/src/main/jni/src/android_storage.h b/shell/android-studio/flycast/src/main/jni/src/android_storage.h index 5476f391f..14a9ce3b2 100644 --- a/shell/android-studio/flycast/src/main/jni/src/android_storage.h +++ b/shell/android-studio/flycast/src/main/jni/src/android_storage.h @@ -38,7 +38,7 @@ class AndroidStorage : public CustomStorage jgetSubPath = env->GetMethodID(clazz, "getSubPath", "(Ljava/lang/String;Ljava/lang/String;)Ljava/lang/String;"); jgetFileInfo = env->GetMethodID(clazz, "getFileInfo", "(Ljava/lang/String;)Lcom/flycast/emulator/FileInfo;"); jexists = env->GetMethodID(clazz, "exists", "(Ljava/lang/String;)Z"); - jaddStorage = env->GetMethodID(clazz, "addStorage", "(ZZLjava/lang/String;)Z"); + jaddStorage = env->GetMethodID(clazz, "addStorage", "(ZZLjava/lang/String;Ljava/lang/String;)Z"); jsaveScreenshot = env->GetMethodID(clazz, "saveScreenshot", "(Ljava/lang/String;[B)V"); jimportHomeDirectory = env->GetMethodID(clazz, "importHomeDirectory", "()V"); jexportHomeDirectory = env->GetMethodID(clazz, "exportHomeDirectory", "()V"); @@ -136,12 +136,13 @@ class AndroidStorage : public CustomStorage } bool addStorage(bool isDirectory, bool writeAccess, const std::string& description, - void (*callback)(bool cancelled, std::string selectedPath)) override + void (*callback)(bool cancelled, std::string selectedPath), const std::string& mimeType) override { if (!config::UseSafFilePicker && !jni::env()->CallBooleanMethod(jstorage, jrequiresSafFilePicker)) return false; jni::String jdesc(description); - bool ret = jni::env()->CallBooleanMethod(jstorage, jaddStorage, isDirectory, writeAccess, (jstring)jdesc); + jni::String jmimeType(mimeType); + bool ret = jni::env()->CallBooleanMethod(jstorage, jaddStorage, isDirectory, writeAccess, (jstring)jdesc, (jstring)jmimeType); checkException(); if (ret) addStorageCallback = callback; diff --git a/shell/android-studio/flycast/src/main/jni/src/jni_util.h b/shell/android-studio/flycast/src/main/jni/src/jni_util.h index 30f7d2436..73e57034d 100644 --- a/shell/android-studio/flycast/src/main/jni/src/jni_util.h +++ b/shell/android-studio/flycast/src/main/jni/src/jni_util.h @@ -107,7 +107,7 @@ class Object bool isNull() const { return object == nullptr; } operator jobject() const { return object; } - Class getClass() const; + inline Class getClass() const; template T globalRef() { @@ -249,99 +249,143 @@ class ObjectArray : public Array } }; -class ByteArray : public Array +class ByteArray; +class IntArray; +class ShortArray; +class BooleanArray; + +namespace detail { -public: - using jtype = jbyteArray; +// Use a traits type and specializations to define types needed in the base CRTP template. +template struct JniArrayTraits; +template <> struct JniArrayTraits { + using jtype = jbyteArray; + using ctype = u8; + using vtype = ctype; + static constexpr char const * JNISignature = "[B"; +}; +template <> struct JniArrayTraits { + using jtype = jintArray; + using ctype = int; + using vtype = ctype; + static constexpr char const * JNISignature = "[I"; +}; +template <> struct JniArrayTraits { + using jtype = jshortArray; + using ctype = short; + using vtype = ctype; + static constexpr char const * JNISignature = "[S"; +}; +template <> struct JniArrayTraits { + using jtype = jbooleanArray; + using ctype = bool; + using vtype = u8; // avoid std::vector abomination + static constexpr char const * JNISignature = "[Z"; +}; +} - ByteArray(jobject array = nullptr, bool ownRef = true, bool globalRef = false) : Array(array, ownRef, globalRef) { } - ByteArray(ByteArray &&other) : Array(std::move(other)) {} - explicit ByteArray(size_t size) : ByteArray() { - object = env()->NewByteArray(size); - } +template +class PrimitiveArray : public Array +{ + using ctype = typename detail::JniArrayTraits::ctype; + using vtype = typename detail::JniArrayTraits::vtype; - ByteArray& operator=(const ByteArray& other) { - return (ByteArray&)Object::operator=(other); - } +public: + using jtype = typename detail::JniArrayTraits::jtype; - operator jbyteArray() const { return (jbyteArray)object; } + PrimitiveArray(jobject array = nullptr, bool ownRef = true, bool globalRef = false) : Array(array, ownRef, globalRef) { } + PrimitiveArray(PrimitiveArray &&other) : Array(std::move(other)) {} - void getData(u8 *dst, size_t first = 0, size_t len = 0) const { + operator jtype() const { return static_cast(object); } + + void getData(ctype *dst, size_t first = 0, size_t len = 0) const + { if (len == 0) len = size(); if (len != 0) - env()->GetByteArrayRegion((jbyteArray)object, first, len, (jbyte *)dst); + static_cast(this)->getJavaArrayRegion(object, first, len, dst); } - void setData(const u8 *src, size_t first = 0, size_t len = 0) { + void setData(const ctype *src, size_t first = 0, size_t len = 0) + { if (len == 0) len = size(); - env()->SetByteArrayRegion((jbyteArray)object, first, len, (const jbyte *)src); + static_cast(this)->setJavaArrayRegion(object, first, len, src); } - operator std::vector() const + operator std::vector() const { - std::vector v; + std::vector v; v.resize(size()); - getData(v.data()); + getData(static_cast(v.data())); return v; } static Class getClass() { - return Class(env()->FindClass("[B")); + return Class(env()->FindClass(detail::JniArrayTraits::JNISignature)); } }; -class IntArray : public Array +class ByteArray : public PrimitiveArray { + using super = PrimitiveArray; + friend super; + public: - using jtype = jintArray; + ByteArray(jobject array = nullptr, bool ownRef = true, bool globalRef = false) : super(array, ownRef, globalRef) { } + ByteArray(ByteArray &&other) : super(std::move(other)) {} + explicit ByteArray(size_t size) : ByteArray() { + object = env()->NewByteArray(size); + } - IntArray(jobject array = nullptr, bool ownRef = true, bool globalRef = false) : Array(array, ownRef, globalRef) { } - IntArray(IntArray &&other) : Array(std::move(other)) {} - explicit IntArray(size_t size) : IntArray() { - object = env()->NewIntArray(size); + ByteArray& operator=(const ByteArray& other) { + return (ByteArray&)Object::operator=(other); } - IntArray& operator=(const IntArray& other) { - return (IntArray&)Object::operator=(other); +protected: + void getJavaArrayRegion(jobject object, size_t first, size_t len, u8 *dst) const { + env()->GetByteArrayRegion((jbyteArray)object, first, len, (jbyte *)dst); } - operator jintArray() const { return (jintArray)object; } + void setJavaArrayRegion(jobject object, size_t first, size_t len, const u8 *dst) { + env()->SetByteArrayRegion((jbyteArray)object, first, len, (const jbyte *)dst); + } +}; - void getData(int *dst, size_t first = 0, size_t len = 0) const { - if (len == 0) - len = size(); - if (len != 0) - env()->GetIntArrayRegion((jintArray)object, first, len, (jint *)dst); +class IntArray : public PrimitiveArray +{ + using super = PrimitiveArray; + friend super; + +public: + IntArray(jobject array = nullptr, bool ownRef = true, bool globalRef = false) : super(array, ownRef, globalRef) { } + IntArray(IntArray &&other) : super(std::move(other)) {} + explicit IntArray(size_t size) : IntArray() { + object = env()->NewIntArray(size); } - void setData(const int *src, size_t first = 0, size_t len = 0) { - if (len == 0) - len = size(); - env()->SetIntArrayRegion((jintArray)object, first, len, (const jint *)src); + IntArray& operator=(const IntArray& other) { + return (IntArray&)Object::operator=(other); } - operator std::vector() const - { - std::vector v; - v.resize(size()); - getData(v.data()); - return v; +protected: + void getJavaArrayRegion(jobject object, size_t first, size_t len, int *dst) const { + env()->GetIntArrayRegion((jintArray)object, first, len, (jint *)dst); } - static Class getClass() { - return Class(env()->FindClass("[I")); + void setJavaArrayRegion(jobject object, size_t first, size_t len, const int *dst) { + env()->SetIntArrayRegion((jintArray)object, first, len, (const jint *)dst); } }; -class ShortArray : public Array +class ShortArray : public PrimitiveArray { -public: - using jtype = jshortArray; + using super = PrimitiveArray; + friend super; - ShortArray(jobject array = nullptr, bool ownRef = true, bool globalRef = false) : Array(array, ownRef, globalRef) { } - ShortArray(ShortArray &&other) : Array(std::move(other)) {} +public: + ShortArray(jobject array = nullptr, bool ownRef = true, bool globalRef = false) : super(array, ownRef, globalRef) { } + ShortArray(ShortArray &&other) : super(std::move(other)) {} explicit ShortArray(size_t size) : ShortArray() { object = env()->NewShortArray(size); } @@ -350,23 +394,39 @@ class ShortArray : public Array return (ShortArray&)Object::operator=(other); } - operator jshortArray() const { return (jshortArray)object; } +protected: + void getJavaArrayRegion(jobject object, size_t first, size_t len, short *dst) const { + env()->GetShortArrayRegion((jshortArray)object, first, len, (jshort *)dst); + } - void getData(short *dst, size_t first = 0, size_t len = 0) { - if (len == 0) - len = size(); - if (len != 0) - env()->GetShortArrayRegion((jshortArray)object, first, len, (jshort *)dst); + void setJavaArrayRegion(jobject object, size_t first, size_t len, const short *dst) { + env()->SetShortArrayRegion((jshortArray)object, first, len, (const jshort *)dst); } +}; - void setData(const short *src, size_t first = 0, size_t len = 0) { - if (len == 0) - len = size(); - env()->SetShortArrayRegion((jshortArray)object, first, len, (const jshort *)src); +class BooleanArray : public PrimitiveArray +{ + using super = PrimitiveArray; + friend super; + +public: + BooleanArray(jobject array = nullptr, bool ownRef = true, bool globalRef = false) : super(array, ownRef, globalRef) { } + BooleanArray(BooleanArray &&other) : super(std::move(other)) {} + explicit BooleanArray(size_t size) : BooleanArray() { + object = env()->NewBooleanArray(size); } - static Class getClass() { - return Class(env()->FindClass("[S")); + BooleanArray& operator=(const BooleanArray& other) { + return (BooleanArray&)Object::operator=(other); + } + +protected: + void getJavaArrayRegion(jobject object, size_t first, size_t len, bool *dst) const { + env()->GetBooleanArrayRegion((jbooleanArray)object, first, len, (jboolean *)dst); + } + + void setJavaArrayRegion(jobject object, size_t first, size_t len, const bool *dst) { + env()->SetBooleanArrayRegion((jbooleanArray)object, first, len, (const jboolean *)dst); } }; diff --git a/shell/apple/emulator-ios/emulator/EditPadViewController.h b/shell/apple/emulator-ios/emulator/EditPadViewController.h new file mode 100644 index 000000000..b347b8eba --- /dev/null +++ b/shell/apple/emulator-ios/emulator/EditPadViewController.h @@ -0,0 +1,31 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#pragma once +#import + +@interface EditPadViewController : UIViewController + +- (void) showController:(UIView *)parentView; +- (void) hideController; +- (BOOL) isControllerVisible; +- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer; +- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer; +- (IBAction)handleTap:(UITapGestureRecognizer *)recognizer; + +@end diff --git a/shell/apple/emulator-ios/emulator/EditPadViewController.mm b/shell/apple/emulator-ios/emulator/EditPadViewController.mm new file mode 100644 index 000000000..58c803a6b --- /dev/null +++ b/shell/apple/emulator-ios/emulator/EditPadViewController.mm @@ -0,0 +1,131 @@ +/* + Copyright 2024 flyinghead + + This file is part of Flycast. + + Flycast is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 2 of the License, or + (at your option) any later version. + + Flycast is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with Flycast. If not, see . +*/ +#import "EditPadViewController.h" +#include "types.h" +#include "ui/gui.h" +#include "ui/vgamepad.h" +#include "cfg/cfg.h" + +@interface EditPadViewController () { + vgamepad::Element currentControl; +} + +@end + +@implementation EditPadViewController + +- (void)viewDidLoad +{ + [super viewDidLoad]; + currentControl = vgamepad::Elem_None; +} + +- (void)showController:(UIView *)parentView +{ + if (!cfgLoadBool("help", "EditPadTip", false)) + { + UIAlertController* alert = [UIAlertController alertControllerWithTitle:@"Help Tip" + message:@"Double tap to exit." + preferredStyle:UIAlertControllerStyleAlert]; + [self presentViewController:alert animated:YES completion:nil]; + UIAlertAction* defaultAction = [UIAlertAction actionWithTitle:@"OK" style:UIAlertActionStyleDefault + handler:^(UIAlertAction * action) { + cfgSaveBool("help", "EditPadTip", true); + }]; + [alert addAction:defaultAction]; + } + [parentView addSubview:self.view]; +} + +- (void)hideController +{ + [self.view removeFromSuperview]; +} + +- (BOOL)isControllerVisible { + return self.view.window != nil; +} + +static void normalize(CGPoint& pos, const CGSize& size) { + pos.x /= size.width; + pos.y /= size.height; +} + +- (IBAction)handlePan:(UIPanGestureRecognizer *)recognizer +{ + CGPoint loc = [recognizer locationInView:self.view]; + normalize(loc, self.view.bounds.size); + switch (recognizer.state) + { + case UIGestureRecognizerStateBegan: + currentControl = vgamepad::layoutHitTest(loc.x, loc.y); + break; + case UIGestureRecognizerStateEnded: + currentControl = vgamepad::Elem_None; + break; + case UIGestureRecognizerStateChanged: + if (currentControl != vgamepad::Elem_None) + { + CGPoint translation = [recognizer translationInView:self.view]; + [recognizer setTranslation:CGPointMake(0, 0) inView:self.view]; + normalize(translation, self.view.bounds.size); + vgamepad::translateElement(currentControl, translation.x, translation.y); + } + break; + case UIGestureRecognizerStateCancelled: + currentControl = vgamepad::Elem_None; + break; + default: + break; + } +} + +- (IBAction)handlePinch:(UIPinchGestureRecognizer *)recognizer +{ + CGPoint loc = [recognizer locationInView:self.view]; + normalize(loc, self.view.bounds.size); + switch (recognizer.state) + { + case UIGestureRecognizerStateBegan: + currentControl = vgamepad::layoutHitTest(loc.x, loc.y); + break; + case UIGestureRecognizerStateChanged: + if (currentControl != vgamepad::Elem_None) + vgamepad::scaleElement(currentControl, recognizer.scale); + break; + case UIGestureRecognizerStateEnded: + currentControl = vgamepad::Elem_None; + break; + case UIGestureRecognizerStateCancelled: + currentControl = vgamepad::Elem_None; + break; + default: + break; + } + recognizer.scale = 1; +} + +- (IBAction)handleTap:(UITapGestureRecognizer *)recognizer +{ + if (recognizer.state == UIGestureRecognizerStateRecognized) + gui_open_settings(); +} + +@end + diff --git a/shell/apple/emulator-ios/emulator/EditPadViewController.xib b/shell/apple/emulator-ios/emulator/EditPadViewController.xib new file mode 100644 index 000000000..224cf0ac4 --- /dev/null +++ b/shell/apple/emulator-ios/emulator/EditPadViewController.xib @@ -0,0 +1,46 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/shell/apple/emulator-ios/emulator/EmulatorView.mm b/shell/apple/emulator-ios/emulator/EmulatorView.mm index d77e2eba0..42a34aff0 100644 --- a/shell/apple/emulator-ios/emulator/EmulatorView.mm +++ b/shell/apple/emulator-ios/emulator/EmulatorView.mm @@ -24,13 +24,13 @@ #include "ios_gamepad.h" @implementation EmulatorView { - std::shared_ptr mouse; + std::shared_ptr mouse; } - (void)didMoveToSuperview { [super didMoveToSuperview]; - mouse = std::make_shared(); + mouse = std::make_shared(); GamepadDevice::Register(mouse); } @@ -45,8 +45,7 @@ - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; { UITouch *touch = [touches anyObject]; [self touchLocation:touch]; - if (gui_is_open()) - mouse->setButton(Mouse::LEFT_BUTTON, true); + mouse->setButton(Mouse::LEFT_BUTTON, true); [super touchesBegan:touches withEvent:event]; } @@ -54,8 +53,7 @@ - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; { UITouch *touch = [touches anyObject]; [self touchLocation:touch]; - if (gui_is_open()) - mouse->setButton(Mouse::LEFT_BUTTON, false); + mouse->setButton(Mouse::LEFT_BUTTON, false); [super touchesEnded:touches withEvent:event]; } diff --git a/shell/apple/emulator-ios/emulator/FlycastViewController.mm b/shell/apple/emulator-ios/emulator/FlycastViewController.mm index fb437bead..c70ed41cf 100644 --- a/shell/apple/emulator-ios/emulator/FlycastViewController.mm +++ b/shell/apple/emulator-ios/emulator/FlycastViewController.mm @@ -29,6 +29,7 @@ #import #import "PadViewController.h" +#import "EditPadViewController.h" #import "EmulatorView.h" #include "types.h" @@ -43,7 +44,6 @@ #include "ios_mouse.h" #include "oslib/oslib.h" -//@import AltKit; #import "AltKit-Swift.h" static std::string iosJitStatus; @@ -92,6 +92,7 @@ static void updateAudioSession(Event event, void *) switch (config::MapleMainDevices[bus]) { case MDT_SegaController: + case MDT_SegaControllerXL: for (int port = 0; port < 2; port++) if (config::MapleExpansionDevices[bus][port] == MDT_Microphone) hasMicrophone = true; @@ -99,6 +100,7 @@ static void updateAudioSession(Event event, void *) case MDT_LightGun: case MDT_AsciiStick: case MDT_TwinStick: + case MDT_RacingController: if (config::MapleExpansionDevices[bus][0] == MDT_Microphone) hasMicrophone = true; break; @@ -147,6 +149,7 @@ @interface FlycastViewController () virtualGamepad; NSMutableDictionary *touchToButton; + NSTimer *hideTimer; } @end @@ -58,11 +60,13 @@ - (void)showController:(UIView *)parentView [alert addAction:defaultAction]; } [parentView addSubview:self.view]; + [self startHideTimer]; } - (void)hideController { - [self resetTouch]; + [self resetAnalog]; + [hideTimer invalidate]; [self.view removeFromSuperview]; } @@ -70,109 +74,146 @@ - (BOOL)isControllerVisible { return self.view.window != nil; } -- (void)resetTouch +-(void)startHideTimer +{ + [hideTimer invalidate]; + hideTimer = [NSTimer scheduledTimerWithTimeInterval:10 + target:self + selector:@selector(hideTimer) + userInfo:nil + repeats:NO]; + vgamepad::show(); +} + +-(void)hideTimer { + vgamepad::hide(); +} + +- (void)resetAnalog { joyTouch = nil; - self.joyXConstraint.constant = 0; - self.joyYConstraint.constant = 0; - virtualGamepad->gamepad_axis_input(IOS_AXIS_LX, 0); - virtualGamepad->gamepad_axis_input(IOS_AXIS_LY, 0); + virtualGamepad->joystickInput(0, 0); +} + +static CGPoint translateCoords(const CGPoint& pos, const CGSize& size) +{ + CGFloat hscale = 480.0 / size.height; + CGPoint p; + p.y = pos.y * hscale; + p.x = (pos.x - (size.width - 640.0 / hscale) / 2.0) * hscale; + return p; } - (void)touchesBegan:(NSSet *)touches withEvent:(UIEvent *)event; { - for (UITouch *touch in touches) { - if (joyTouch == nil) { - CGPoint loc = [touch locationInView:self.joystickBackground]; - if ([self.joystickBackground pointInside:loc withEvent:event]) { - joyTouch = touch; - joyBias = loc; - virtualGamepad->gamepad_axis_input(IOS_AXIS_LX, 0); - virtualGamepad->gamepad_axis_input(IOS_AXIS_LY, 0); - continue; - } - } + bool forwardEvent = true; + [self startHideTimer]; + for (UITouch *touch in touches) + { CGPoint point = [touch locationInView:self.view]; - UIView *touchedView = [self.view hitTest:point withEvent:nil]; + point = translateCoords(point, self.view.bounds.size); + vgamepad::ControlId control = vgamepad::hitTest(point.x, point.y); + if (joyTouch == nil && (control == vgamepad::AnalogArea || control == vgamepad::AnalogStick)) + { + [self resetAnalog]; + joyTouch = touch; + joyBias = point; + forwardEvent = false; + continue; + } NSValue *key = [NSValue valueWithPointer:(const void *)touch]; - if (touchedView.tag != 0 && touchToButton[key] == nil) { - touchToButton[key] = touchedView; + if (control != vgamepad::None && control != vgamepad::AnalogArea + && control != vgamepad::AnalogStick && touchToButton[key] == nil) + { + touchToButton[key] = [NSNumber numberWithInt:control]; // button down - virtualGamepad->gamepad_btn_input((u32)touchedView.tag, true); + virtualGamepad->buttonInput(control, true); + forwardEvent = false; } } - [super touchesBegan:touches withEvent:event]; + if (forwardEvent) + [super touchesBegan:touches withEvent:event]; } - (void)touchesEnded:(NSSet *)touches withEvent:(UIEvent *)event; { - for (UITouch *touch in touches) { + bool forwardEvent = true; + for (UITouch *touch in touches) + { if (touch == joyTouch) { - [self resetTouch]; + [self resetAnalog]; + forwardEvent = false; continue; } NSValue *key = [NSValue valueWithPointer:(const void *)touch]; - UIView *button = touchToButton[key]; - if (button != nil) { + NSNumber *control = touchToButton[key]; + if (control != nil) { [touchToButton removeObjectForKey:key]; // button up - virtualGamepad->gamepad_btn_input((u32)button.tag, false); + virtualGamepad->buttonInput(static_cast(control.intValue), false); + forwardEvent = false; } } - [super touchesEnded:touches withEvent:event]; + if (forwardEvent) + [super touchesEnded:touches withEvent:event]; } - (void)touchesMoved:(NSSet *)touches withEvent:(UIEvent *)event; { - for (UITouch *touch in touches) { - if (touch == joyTouch) { - CGPoint pos = [touch locationInView:[self joystickBackground]]; - pos.x -= joyBias.x; - pos.y -= joyBias.y; - pos.x = std::max(std::min(25.0, pos.x), -25.0); - pos.y = std::max(std::min(25.0, pos.y), -25.0); - self.joyXConstraint.constant = pos.x; - self.joyYConstraint.constant = pos.y; - virtualGamepad->gamepad_axis_input(IOS_AXIS_LX, (s8)std::round(pos.x * 32767.0 / 25.0)); - virtualGamepad->gamepad_axis_input(IOS_AXIS_LY, (s8)std::round(pos.y * 32767.0 / 25.0)); + bool forwardEvent = true; + [self startHideTimer]; + for (UITouch *touch in touches) + { + CGPoint point = [touch locationInView:self.view]; + point = translateCoords(point, self.view.bounds.size); + if (touch == joyTouch) + { + point.x -= joyBias.x; + point.y -= joyBias.y; + double sz = vgamepad::getControlWidth(vgamepad::AnalogStick); + point.x = std::max(std::min(1.0, point.x / sz), -1.0); + point.y = std::max(std::min(1.0, point.y / sz), -1.0); + virtualGamepad->joystickInput(point.x, point.y); + forwardEvent = false; continue; } - CGPoint point = [touch locationInView:self.view]; - UIView *touchedView = [self.view hitTest:point withEvent:nil]; + vgamepad::ControlId control = vgamepad::hitTest(point.x, point.y); NSValue *key = [NSValue valueWithPointer:(const void *)touch]; - UIView *button = touchToButton[key]; - if (button != nil && touchedView.tag != button.tag) { + NSNumber *prevControl = touchToButton[key]; + if (prevControl.intValue == control) + continue; + if (prevControl != nil && prevControl.intValue != vgamepad::None && prevControl.intValue != vgamepad::AnalogArea) { // button up - virtualGamepad->gamepad_btn_input((u32)button.tag, false); - touchToButton[key] = touchedView; - // button down - virtualGamepad->gamepad_btn_input((u32)touchedView.tag, true); - } - else if (button == nil && touchedView.tag != 0) - { - touchToButton[key] = touchedView; - // button down - virtualGamepad->gamepad_btn_input((u32)touchedView.tag, true); + virtualGamepad->buttonInput(static_cast(prevControl.intValue), false); } + // button down + virtualGamepad->buttonInput(control, true); + touchToButton[key] = [NSNumber numberWithInt:control]; + forwardEvent = false; } - [super touchesMoved:touches withEvent:event]; + if (forwardEvent) + [super touchesMoved:touches withEvent:event]; } - (void)touchesCancelled:(NSSet *)touches withEvent:(UIEvent *)event; { + bool forwardEvent = true; for (UITouch *touch in touches) { if (touch == joyTouch) { - [self resetTouch]; + [self resetAnalog]; + forwardEvent = false; continue; } NSValue *key = [NSValue valueWithPointer:(const void *)touch]; - UIView *button = touchToButton[key]; - if (button != nil) { + NSNumber *control = touchToButton[key]; + if (control != nil) { [touchToButton removeObjectForKey:key]; // button up - virtualGamepad->gamepad_btn_input((u32)button.tag, false); + virtualGamepad->buttonInput(static_cast(control.intValue), false); + forwardEvent = false; } } - [super touchesCancelled:touches withEvent:event]; + if (forwardEvent) + [super touchesCancelled:touches withEvent:event]; } @end diff --git a/shell/apple/emulator-ios/emulator/PadViewController.xib b/shell/apple/emulator-ios/emulator/PadViewController.xib index 94a774c7a..6ccc8b085 100644 --- a/shell/apple/emulator-ios/emulator/PadViewController.xib +++ b/shell/apple/emulator-ios/emulator/PadViewController.xib @@ -10,214 +10,15 @@ - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - diff --git a/shell/apple/emulator-ios/emulator/ios_gamepad.h b/shell/apple/emulator-ios/emulator/ios_gamepad.h index 3d768c713..420132e5b 100644 --- a/shell/apple/emulator-ios/emulator/ios_gamepad.h +++ b/shell/apple/emulator-ios/emulator/ios_gamepad.h @@ -23,6 +23,7 @@ #include #include "input/gamepad_device.h" #include "input/mouse.h" +#include "input/virtual_gamepad.h" #include "ui/gui.h" enum IOSButton { @@ -49,11 +50,6 @@ enum IOSButton { IOS_BTN_PADDLE3, IOS_BTN_PADDLE4, IOS_BTN_TOUCHPAD, - - IOS_BTN_UP_RIGHT, - IOS_BTN_UP_LEFT, - IOS_BTN_DOWN_LEFT, - IOS_BTN_DOWN_RIGHT, }; enum IOSAxis { @@ -485,119 +481,28 @@ class IOSGamepad : public GamepadDevice static std::map> controllers; }; -class IOSVirtualGamepad : public GamepadDevice +class IOSVirtualGamepad : public VirtualGamepad { public: - IOSVirtualGamepad() : GamepadDevice(0, "iOS", false) { - _name = "Virtual Gamepad"; - _unique_id = "ios-virtual-gamepad"; - input_mapper = getDefaultMapping(); - //hasAnalogStick = true; // TODO has an analog stick but input mapping isn't persisted - } - - bool is_virtual_gamepad() override { return true; } - - std::shared_ptr getDefaultMapping() override { - return std::make_shared>(); + IOSVirtualGamepad() : VirtualGamepad("iOS") { } - bool gamepad_btn_input(u32 code, bool pressed) override + bool handleButtonInput(u32& state, u32 key, bool pressed) override { - if (pressed) - buttonState |= 1 << code; - else - buttonState &= ~(1 << code); - switch (code) + if (!pressed + || (key != DC_DPAD_UP && key != DC_DPAD_DOWN && key != DC_DPAD_LEFT && key != DC_DPAD_RIGHT)) + return false; + if (((state | key) & (DC_DPAD_UP | DC_DPAD_DOWN)) == (DC_DPAD_UP | DC_DPAD_DOWN) + || ((state | key) & (DC_DPAD_LEFT | DC_DPAD_RIGHT)) == (DC_DPAD_LEFT | DC_DPAD_RIGHT)) { - case IOS_BTN_L2: - gamepad_axis_input(IOS_AXIS_L2, pressed ? 0x7fff : 0); - if (settings.platform.isArcade()) - GamepadDevice::gamepad_btn_input(IOS_BTN_L1, pressed); // Z, btn5 - return true; - case IOS_BTN_R2: - if (!pressed && maple_port() >= 0 && maple_port() <= 3) - kcode[maple_port()] |= DC_DPAD2_UP | DC_BTN_D | DC_DPAD2_DOWN; - gamepad_axis_input(IOS_AXIS_R2, pressed ? 0x7fff : 0); - if (settings.platform.isArcade()) - GamepadDevice::gamepad_btn_input(IOS_BTN_Y, pressed); // Y, btn4 - return true; - default: - if ((buttonState & ((1 << IOS_BTN_UP) | (1 << IOS_BTN_DOWN))) == ((1 << IOS_BTN_UP) | (1 << IOS_BTN_DOWN)) - || (buttonState & ((1 << IOS_BTN_LEFT) | (1 << IOS_BTN_RIGHT))) == ((1 << IOS_BTN_LEFT) | (1 << IOS_BTN_RIGHT))) - { - GamepadDevice::gamepad_btn_input(IOS_BTN_UP, false); - GamepadDevice::gamepad_btn_input(IOS_BTN_DOWN, false); - GamepadDevice::gamepad_btn_input(IOS_BTN_LEFT, false); - GamepadDevice::gamepad_btn_input(IOS_BTN_RIGHT, false); - buttonState = 0; - gui_open_settings(); - return true; - } - if (settings.platform.isArcade() && maple_port() >= 0 && maple_port() <= 3) - { - u32& keycode = kcode[maple_port()]; - if ((buttonState & (1 << IOS_BTN_R2)) != 0) - { - switch (code) { - case IOS_BTN_A: - // RT + A -> D (coin) - keycode = pressed ? keycode & ~DC_BTN_D : keycode | DC_BTN_D; - break; - case IOS_BTN_B: - // RT + B -> Service - keycode = pressed ? keycode & ~DC_DPAD2_UP : keycode | DC_DPAD2_UP; - break; - case IOS_BTN_X: - // RT + X -> Test - keycode = pressed ? keycode & ~DC_DPAD2_DOWN : keycode | DC_DPAD2_DOWN; - break; - default: - break; - } - } - // arcade mapping: X -> btn2, Y -> btn3 - if (code == IOS_BTN_X) - code = IOS_BTN_R1; // C, btn2 - if (code == IOS_BTN_Y) - code = IOS_BTN_X; // btn3 - } - switch (code) - { - case IOS_BTN_UP_RIGHT: - GamepadDevice::gamepad_btn_input(IOS_BTN_UP, pressed); - code = IOS_BTN_RIGHT; - break; - case IOS_BTN_DOWN_RIGHT: - GamepadDevice::gamepad_btn_input(IOS_BTN_DOWN, pressed); - code = IOS_BTN_RIGHT; - break; - case IOS_BTN_DOWN_LEFT: - GamepadDevice::gamepad_btn_input(IOS_BTN_DOWN, pressed); - code = IOS_BTN_LEFT; - break; - case IOS_BTN_UP_LEFT: - GamepadDevice::gamepad_btn_input(IOS_BTN_UP, pressed); - code = IOS_BTN_LEFT; - break; - default: - break; - } - - return GamepadDevice::gamepad_btn_input(code, pressed); + gamepad_btn_input(DC_DPAD_UP, false); + gamepad_btn_input(DC_DPAD_DOWN, false); + gamepad_btn_input(DC_DPAD_LEFT, false); + gamepad_btn_input(DC_DPAD_RIGHT, false); + state = 0; + gui_open_settings(); + return true; } + return false; } - -private: - u32 buttonState = 0; -}; - -class IOSTouchMouse : public SystemMouse -{ -public: - IOSTouchMouse() : SystemMouse("iOS") - { - _unique_id = "ios_mouse"; - _name = "Touchscreen (Mouse)"; - loadMapping(); - } }; diff --git a/shell/libretro/libretro.cpp b/shell/libretro/libretro.cpp index 695bffb8c..cc80eac56 100644 --- a/shell/libretro/libretro.cpp +++ b/shell/libretro/libretro.cpp @@ -51,21 +51,18 @@ #include "emulator.h" #include "hw/sh4/sh4_mem.h" #include "hw/sh4/sh4_sched.h" -#include "hw/sh4/dyna/blockmanager.h" #include "keyboard_map.h" #include "hw/maple/maple_cfg.h" #include "hw/maple/maple_if.h" -#include "hw/maple/maple_cfg.h" -#include "hw/pvr/spg.h" +#include "hw/pvr/pvr_regs.h" +#include "hw/pvr/Renderer_if.h" #include "hw/naomi/naomi_cart.h" #include "hw/naomi/card_reader.h" -#include "imgread/common.h" #include "LogManager.h" #include "cheats.h" #include "rend/osd.h" #include "cfg/option.h" #include "version.h" -#include "rend/transform_matrix.h" #include "oslib/oslib.h" constexpr char slash = path_default_slash_c(); @@ -78,6 +75,7 @@ constexpr char slash = path_default_slash_c(); #define RETRO_DEVICE_POPNMUSIC RETRO_DEVICE_SUBCLASS( RETRO_DEVICE_JOYPAD, 6 ) #define RETRO_DEVICE_RACING RETRO_DEVICE_SUBCLASS( RETRO_DEVICE_JOYPAD, 7 ) #define RETRO_DEVICE_DENSHA RETRO_DEVICE_SUBCLASS( RETRO_DEVICE_JOYPAD, 8 ) +#define RETRO_DEVICE_FULL_CONTROLLER RETRO_DEVICE_SUBCLASS( RETRO_DEVICE_JOYPAD, 9 ) #define RETRO_ENVIRONMENT_RETROARCH_START_BLOCK 0x800000 @@ -296,13 +294,13 @@ void retro_set_environment(retro_environment_t cb) { "Pop'n Music", RETRO_DEVICE_POPNMUSIC }, { "Race Controller", RETRO_DEVICE_RACING }, { "Densha de Go!", RETRO_DEVICE_DENSHA }, - { 0 }, + { "Full Controller", RETRO_DEVICE_FULL_CONTROLLER }, }; static const struct retro_controller_info ports[] = { - { ports_default, 13 }, - { ports_default, 13 }, - { ports_default, 13 }, - { ports_default, 13 }, + { ports_default, std::size(ports_default) }, + { ports_default, std::size(ports_default) }, + { ports_default, std::size(ports_default) }, + { ports_default, std::size(ports_default) }, { 0 }, }; environ_cb(RETRO_ENVIRONMENT_SET_CONTROLLER_INFO, (void*)ports); @@ -580,13 +578,15 @@ static bool set_variable_visibility(void) || config::MapleMainDevices[i] == MDT_LightGun || config::MapleMainDevices[i] == MDT_TwinStick || config::MapleMainDevices[i] == MDT_AsciiStick - || config::MapleMainDevices[i] == MDT_RacingController); + || config::MapleMainDevices[i] == MDT_RacingController + || config::MapleMainDevices[i] == MDT_SegaControllerXL); snprintf(key, sizeof(key), CORE_OPTION_NAME "_device_port%d_slot1", i + 1); environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display); - // Only the regular controller has 2 expansion slots - option_display.visible = platformIsDreamcast && config::MapleMainDevices[i] == MDT_SegaController; + // Only the regular controller (and the XL version) has 2 expansion slots + option_display.visible = platformIsDreamcast + && (config::MapleMainDevices[i] == MDT_SegaController || config::MapleMainDevices[i] == MDT_SegaControllerXL); snprintf(key, sizeof(key), CORE_OPTION_NAME "_device_port%d_slot2", i + 1); environ_cb(RETRO_ENVIRONMENT_SET_CORE_OPTIONS_DISPLAY, &option_display); @@ -953,12 +953,13 @@ static void update_variables(bool first_startup) || config::MapleMainDevices[i] == MDT_LightGun || config::MapleMainDevices[i] == MDT_TwinStick || config::MapleMainDevices[i] == MDT_AsciiStick - || config::MapleMainDevices[i] == MDT_RacingController) + || config::MapleMainDevices[i] == MDT_RacingController + || config::MapleMainDevices[i] == MDT_SegaControllerXL) { for (int slot = 0; slot < 2; slot++) { // Only regular controller has a 2nd slot - if (slot == 1 && config::MapleMainDevices[i] != MDT_SegaController) + if (slot == 1 && config::MapleMainDevices[i] != MDT_SegaController && config::MapleMainDevices[i] != MDT_SegaControllerXL) { config::MapleExpansionDevices[i][1] = MDT_None; continue; @@ -1328,7 +1329,7 @@ static uint32_t map_gamepad_button(unsigned device, unsigned id) { /* JOYPAD_B */ DC_BTN_A, /* JOYPAD_Y */ DC_BTN_X, - /* JOYPAD_SELECT */ 0, + /* JOYPAD_SELECT */ DC_BTN_D, /* JOYPAD_START */ DC_BTN_START, /* JOYPAD_UP */ DC_DPAD_UP, /* JOYPAD_DOWN */ DC_DPAD_DOWN, @@ -1336,6 +1337,8 @@ static uint32_t map_gamepad_button(unsigned device, unsigned id) /* JOYPAD_RIGHT */ DC_DPAD_RIGHT, /* JOYPAD_A */ DC_BTN_B, /* JOYPAD_X */ DC_BTN_Y, + /* JOYPAD_L */ DC_BTN_C, + /* JOYPAD_R */ DC_BTN_Z, }; static const uint32_t dc_lg_joymap[] = @@ -1812,6 +1815,28 @@ static void set_input_descriptors() desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }; break; + case MDT_SegaControllerXL: + // No DPad2 + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_LEFT, "D-Pad Left" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_UP, "D-Pad Up" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_DOWN, "D-Pad Down" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_RIGHT, "D-Pad Right" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_B, "A" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_A, "B" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_X, "Y" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_Y, "X" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L, "C" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R, "Z" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_SELECT,"D" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_L2, "L Trigger" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_R2, "R Trigger" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_JOYPAD, 0, RETRO_DEVICE_ID_JOYPAD_START, "Start" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_X, "Analog X" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_LEFT, RETRO_DEVICE_ID_ANALOG_Y, "Analog Y" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_X, "R. Analog X" }; + desc[descriptor_index++] = { i, RETRO_DEVICE_ANALOG, RETRO_DEVICE_INDEX_ANALOG_RIGHT, RETRO_DEVICE_ID_ANALOG_Y, "R. Analog Y" }; + break; + default: break; } @@ -2341,7 +2366,7 @@ bool retro_unserialize(const void * data, size_t size) try { Deserializer deser(data, size); - dc_loadstate(deser); + emu.loadstate(deser); retro_audio_flush_buffer(); if (!first_run) emu.start(); @@ -2451,6 +2476,9 @@ void retro_set_controller_port_device(unsigned in_port, unsigned device) case RETRO_DEVICE_DENSHA: config::MapleMainDevices[in_port] = MDT_DenshaDeGoController; break; + case RETRO_DEVICE_FULL_CONTROLLER: + config::MapleMainDevices[in_port] = MDT_SegaControllerXL; + break; default: config::MapleMainDevices[in_port] = MDT_None; break; @@ -2981,15 +3009,17 @@ static void UpdateInputState(u32 port) switch (config::MapleMainDevices[port]) { case MDT_SegaController: + case MDT_SegaControllerXL: { int16_t ret = getBitmask(port, RETRO_DEVICE_JOYPAD); // -- buttons - for (int id = RETRO_DEVICE_ID_JOYPAD_B; id <= RETRO_DEVICE_ID_JOYPAD_X; ++id) + for (int id = RETRO_DEVICE_ID_JOYPAD_B; id <= RETRO_DEVICE_ID_JOYPAD_R; ++id) setDeviceButtonStateFromBitmap(ret, port, RETRO_DEVICE_JOYPAD, id); - // -- analog stick - get_analog_stick( input_cb, port, RETRO_DEVICE_INDEX_ANALOG_LEFT, &(joyx[port]), &(joyy[port]) ); + // -- analog sticks + get_analog_stick(input_cb, port, RETRO_DEVICE_INDEX_ANALOG_LEFT, &joyx[port], &joyy[port]); + get_analog_stick(input_cb, port, RETRO_DEVICE_INDEX_ANALOG_RIGHT, &joyrx[port], &joyry[port]); // -- triggers if ( digital_triggers ) @@ -3493,13 +3523,14 @@ static bool retro_set_eject_state(bool ejected) disc_tray_open = ejected; if (ejected) { - DiscOpenLid(); + emu.openGdrom(); return true; } else { try { - return DiscSwap(disk_paths[disk_index]); + emu.insertGdrom(disk_paths[disk_index]); + return true; } catch (const FlycastException& e) { ERROR_LOG(GDROM, "%s", e.what()); return false; @@ -3520,19 +3551,19 @@ static unsigned retro_get_image_index() static bool retro_set_image_index(unsigned index) { disk_index = index; - if (disk_index >= disk_paths.size()) - { - // No disk in drive - settings.content.path.clear(); - return true; - } - settings.content.path = disk_paths[index]; + try { + if (disk_index >= disk_paths.size()) + { + // No disk in drive + emu.insertGdrom(""); + return true; + } - if (disc_tray_open) - return true; + if (disc_tray_open) + return true; - try { - return DiscSwap(settings.content.path); + emu.insertGdrom(disk_paths[index]); + return true; } catch (const FlycastException& e) { ERROR_LOG(GDROM, "%s", e.what()); return false; diff --git a/shell/libretro/oslib.cpp b/shell/libretro/oslib.cpp index f21ac97e0..2845b46fd 100644 --- a/shell/libretro/oslib.cpp +++ b/shell/libretro/oslib.cpp @@ -35,21 +35,35 @@ extern std::string arcadeFlashPath; namespace hostfs { -std::string getVmuPath(const std::string& port) +std::string getVmuPath(const std::string& port, bool save) { - char filename[PATH_MAX + 8]; - - if ((per_content_vmus == 1 && port == "A1") - || per_content_vmus == 2) - { - sprintf(filename, "%s.%s.bin", content_name, port.c_str()); - return std::string(vmu_dir_no_slash) + std::string(path_default_slash()) + filename; - } - else - { - sprintf(filename, "vmu_save_%s.bin", port.c_str()); - return std::string(game_dir_no_slash) + std::string(path_default_slash()) + filename; - } + if ((per_content_vmus == 1 && port == "A1") + || per_content_vmus == 2) + { + std::string vmuDir = vmu_dir_no_slash + std::string(path_default_slash()); + if (settings.platform.isConsole() && !settings.content.gameId.empty()) + { + constexpr std::string_view INVALID_CHARS { " /\\:*?|<>" }; + std::string vmuName = settings.content.gameId; + for (char &c: vmuName) + if (INVALID_CHARS.find(c) != INVALID_CHARS.npos) + c = '_'; + vmuName += "." + port + ".bin"; + std::string wpath = vmuDir + vmuName; + if (save || file_exists(wpath.c_str())) + return wpath; + // Legacy path with rom name + std::string rpath = vmuDir + std::string(content_name) + "." + port + ".bin"; + if (file_exists(rpath.c_str())) + return rpath; + else + return wpath; + } + return vmuDir + std::string(content_name) + "." + port + ".bin"; + } + else { + return std::string(game_dir_no_slash) + std::string(path_default_slash()) + "vmu_save_" + port + ".bin"; + } } std::string getArcadeFlashPath() diff --git a/shell/linux/make-appimage.sh b/shell/linux/make-appimage.sh index 67f594dc8..e8dddbfdd 100755 --- a/shell/linux/make-appimage.sh +++ b/shell/linux/make-appimage.sh @@ -72,6 +72,7 @@ SHLIBS=( libsqlite3.so.0 libcrypt.so.1 libbsd.so.0 + libcdio.so.18 ) if [ ! -f appimagetool-x86_64.AppImage ]; then diff --git a/shell/switch/libnx_vmem.cpp b/shell/switch/libnx_vmem.cpp index 395079f12..3c5320429 100644 --- a/shell/switch/libnx_vmem.cpp +++ b/shell/switch/libnx_vmem.cpp @@ -283,8 +283,6 @@ void release_jit_block(void *code_area1, void *code_area2, size_t size) } // namespace virtmem -#ifndef TARGET_NO_EXCEPTIONS - #include void fault_handler(int sn, siginfo_t * si, void *segfault_ctx); @@ -325,7 +323,6 @@ void __libnx_exception_handler(ThreadExceptionDump *ctx) context_switch_aarch64(ptr); } } -#endif // TARGET_NO_EXCEPTIONS #ifndef LIBRETRO [[noreturn]] void os_DebugBreak() diff --git a/tests/src/AicaArmTest.cpp b/tests/src/AicaArmTest.cpp index ad6ca7e66..994d03b6f 100644 --- a/tests/src/AicaArmTest.cpp +++ b/tests/src/AicaArmTest.cpp @@ -27,7 +27,7 @@ class AicaArmTest : public ::testing::Test { if (!addrspace::reserve()) die("addrspace::reserve failed"); emu.init(); - dc_reset(true); + emu.dc_reset(true); Arm7Enabled = true; } diff --git a/tests/src/CheatManagerTest.cpp b/tests/src/CheatManagerTest.cpp index 7300c57d6..813b63213 100644 --- a/tests/src/CheatManagerTest.cpp +++ b/tests/src/CheatManagerTest.cpp @@ -287,7 +287,7 @@ cheats = "2" mgr.reset("TESTSUB8"); mgr.loadCheatFile("test.cht"); mem_map_default(); - dc_reset(true); + emu.dc_reset(true); mgr.enableCheat(0, true); WriteMem8_nommu(0x8c010000, 0xFA); diff --git a/tests/src/MmuTest.cpp b/tests/src/MmuTest.cpp index 74933979e..3956fd4b0 100644 --- a/tests/src/MmuTest.cpp +++ b/tests/src/MmuTest.cpp @@ -30,7 +30,7 @@ class MmuTest : public ::testing::Test { if (!addrspace::reserve()) die("addrspace::reserve failed"); emu.init(); - dc_reset(true); + emu.dc_reset(true); CCN_MMUCR.AT = 1; MMU_reset(); } diff --git a/tests/src/Sh4InterpreterTest.cpp b/tests/src/Sh4InterpreterTest.cpp index adb573dcd..0cb4dc296 100644 --- a/tests/src/Sh4InterpreterTest.cpp +++ b/tests/src/Sh4InterpreterTest.cpp @@ -28,9 +28,10 @@ class Sh4InterpreterTest : public Sh4OpTest { die("addrspace::reserve failed"); emu.init(); mem_map_default(); - dc_reset(true); + emu.dc_reset(true); ctx = &p_sh4rcb->cntx; - Get_Sh4Interpreter(&sh4); + sh4 = Get_Sh4Interpreter(); + sh4->Init(); } void PrepareOp(u16 op, u16 op2 = 0, u16 op3 = 0) override { @@ -45,7 +46,7 @@ class Sh4InterpreterTest : public Sh4OpTest { { ctx->pc = START_PC; for (int i = 0; i < numOp; i++) - sh4.Step(); + sh4->Step(); } }; diff --git a/tests/src/div32_test.cpp b/tests/src/div32_test.cpp index 3805abec2..c6c68eebc 100644 --- a/tests/src/div32_test.cpp +++ b/tests/src/div32_test.cpp @@ -10,6 +10,7 @@ static void div1(u32& r1, u32 r2) { + sr_t& sr = Sh4cntx.sr; const u8 old_q = sr.Q; sr.Q = (u8)((0x80000000 & r1) != 0); @@ -53,6 +54,7 @@ static void div1(u32& r1, u32 r2) static void div32s_slow(u32& r1, u32 r2, u32& r3) { + sr_t& sr = Sh4cntx.sr; sr.Q = r3 >> 31; sr.M = r2 >> 31; sr.T = sr.Q ^ sr.M; @@ -68,6 +70,7 @@ static void div32s_slow(u32& r1, u32 r2, u32& r3) static void div32s_fast(u32& r1, u32 r2, u32& r3) { + sr_t& sr = Sh4cntx.sr; sr.T = (r3 ^ r2) & 0x80000000; u64 rv = shil_opcl_div32s::f1::impl(r1, r2, r3); r1 = (u32)rv; @@ -80,6 +83,7 @@ static void div32s_fast(u32& r1, u32 r2, u32& r3) static void div32u_fast(u32& r1, u32 r2, u32& r3) { + sr_t& sr = Sh4cntx.sr; u64 rv = shil_opcl_div32u::f1::impl(r1, r2, r3); r1 = (u32)rv; r3 = rv >> 32; @@ -90,6 +94,7 @@ static void div32u_fast(u32& r1, u32 r2, u32& r3) static void div32u_slow(u32& r1, u32 r2, u32& r3) { + sr_t& sr = Sh4cntx.sr; sr.Q = 0; sr.M = 0; sr.T = 0; @@ -111,7 +116,7 @@ class Div32Test : public ::testing::Test { if (!addrspace::reserve()) die("addrspace::reserve failed"); emu.init(); - dc_reset(true); + emu.dc_reset(true); } void div32s(u32 n1, u32 n2, u32 n3) diff --git a/tests/src/serialize_test.cpp b/tests/src/serialize_test.cpp index 75a769128..24f168fc2 100644 --- a/tests/src/serialize_test.cpp +++ b/tests/src/serialize_test.cpp @@ -13,7 +13,7 @@ class SerializeTest : public ::testing::Test { if (!addrspace::reserve()) die("addrspace::reserve failed"); emu.init(); - dc_reset(true); + emu.dc_reset(true); } }; @@ -32,8 +32,5 @@ TEST_F(SerializeTest, SizeTest) std::vector data(30000000); Serializer ser(data.data(), data.size()); dc_serialize(ser); - ASSERT_EQ(28191434u, ser.size()); + ASSERT_EQ(28050642u, ser.size()); } - - - diff --git a/tests/src/sh4_ops.h b/tests/src/sh4_ops.h index ba0e4f20b..b9d7b2084 100644 --- a/tests/src/sh4_ops.h +++ b/tests/src/sh4_ops.h @@ -23,14 +23,6 @@ #include "types.h" #include "hw/sh4/sh4_if.h" #include "hw/mem/addrspace.h" -#include "hw/sh4/sh4_core.h" -#undef r -#undef fr -#undef sr -#undef mac -#undef gbr -#undef fpscr -#undef fpul constexpr u32 REG_MAGIC = 0xbaadf00d; @@ -43,17 +35,18 @@ class Sh4OpTest : public ::testing::Test { virtual void PrepareOp(u16 op, u16 op2 = 0, u16 op3 = 0) = 0; virtual void RunOp(int numOp = 1) = 0; - void ClearRegs() { - for (int i = 0; i < 16; i++) - r(i) = REG_MAGIC; - for (int i = 0; i < 32; i++) - *(u32 *)&ctx->xffr[i] = REG_MAGIC; - sh4_sr_SetFull(0x700000F0); + void ClearRegs() + { + std::fill(std::begin(ctx->r), std::end(ctx->r), REG_MAGIC); + std::fill(std::begin(reinterpret_cast(ctx->xf)), std::end(reinterpret_cast(ctx->xf)), REG_MAGIC); + std::fill(std::begin(reinterpret_cast(ctx->fr)), std::end(reinterpret_cast(ctx->fr)), REG_MAGIC); + sr().setFull(0x700000F0); mac() = 0; gbr() = REG_MAGIC; checkedRegs.clear(); } - void AssertState() { + void AssertState() + { for (int i = 0; i < 16; i++) if (checkedRegs.count(&ctx->r[i]) == 0) ASSERT_CLEAN_REG(i); @@ -61,10 +54,15 @@ class Sh4OpTest : public ::testing::Test { { ASSERT_EQ(ctx->gbr, REG_MAGIC); } - for (int i = 0; i < 32; i++) - if (checkedRegs.count((u32 *)&ctx->xffr[i]) == 0) + for (int i = 0; i < 16; i++) + if (checkedRegs.count((u32 *)&ctx->xf[i]) == 0) + { + ASSERT_EQ(*(u32 *)&ctx->xf[i], REG_MAGIC); + } + for (int i = 0; i < 16; i++) + if (checkedRegs.count((u32 *)&ctx->fr[i]) == 0) { - ASSERT_EQ(*(u32 *)&ctx->xffr[i], REG_MAGIC); + ASSERT_EQ(*(u32 *)&ctx->fr[i], REG_MAGIC); } } static u16 Rm(int r) { return r << 4; } @@ -78,20 +76,20 @@ class Sh4OpTest : public ::testing::Test { u32& mach() { return ctx->mac.l; } u32& macl() { return ctx->mac.h; } sr_t& sr() { return ctx->sr; } - f32& fr(int regNum) { checkedRegs.insert((u32 *)&ctx->xffr[regNum + 16]); return ctx->xffr[regNum + 16]; } + f32& fr(int regNum) { checkedRegs.insert((u32 *)&ctx->fr[regNum]); return ctx->fr[regNum]; } double getDr(int regNum) { - checkedRegs.insert((u32 *)&ctx->xffr[regNum * 2 + 16]); - checkedRegs.insert((u32 *)&ctx->xffr[regNum * 2 + 1 + 16]); - return GetDR(regNum); + checkedRegs.insert((u32 *)&ctx->fr[regNum * 2]); + checkedRegs.insert((u32 *)&ctx->fr[regNum * 2 + 1]); + return ctx->getDR(regNum); } void setDr(int regNum, double d) { - checkedRegs.insert((u32 *)&ctx->xffr[regNum * 2 + 16]); - checkedRegs.insert((u32 *)&ctx->xffr[regNum * 2 + 1 + 16]); - SetDR(regNum, d); + checkedRegs.insert((u32 *)&ctx->fr[regNum * 2]); + checkedRegs.insert((u32 *)&ctx->fr[regNum * 2 + 1]); + ctx->setDR(regNum, d); } Sh4Context *ctx; - sh4_if sh4; + Sh4Executor *sh4; std::set checkedRegs; static constexpr u32 START_PC = 0xAC000000;