[
  {
    "path": ".github/FUNDING.yml",
    "content": "# These are supported funding model platforms\n\ngithub: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2]\npatreon: # Replace with a single Patreon username\nopen_collective: # Replace with a single Open Collective username\nko_fi: # Replace with a single Ko-fi username\ntidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel\ncommunity_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry\nliberapay: # Replace with a single Liberapay username\nissuehunt: # Replace with a single IssueHunt username\nlfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry\npolar: # Replace with a single Polar username\nbuy_me_a_coffee: allkern\ncustom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']\n"
  },
  {
    "path": ".github/workflows/linux.yml",
    "content": "name: Ubuntu CI\r\n\r\non:\r\n  push:\r\n    branches: [ \"master\" ]\r\n\r\njobs:\r\n  build:\r\n    runs-on: ubuntu-latest\r\n\r\n    steps:\r\n    - uses: actions/checkout@v6\r\n      with:\r\n        submodules: 'recursive'\r\n        fetch-depth: 0\r\n    - name: Install deps\r\n      run: |\r\n        sudo add-apt-repository universe\r\n        sudo apt-get install build-essential git make \\\r\n        pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \\\r\n        libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev \\\r\n        libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \\\r\n        libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \\\r\n        libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libfuse2t64\r\n    - name: Build and pack Iris\r\n      run: |\r\n        git fetch --all --tags\r\n        cmake -S . -B build\r\n        cmake --build build -j8\r\n        sudo cmake --install build\r\n        mkdir dist\r\n        mv ./build/iris ./dist\r\n        mkdir appimage\r\n        mv ./build/*.AppImage ./appimage\r\n    - uses: actions/upload-artifact@v7\r\n      with:\r\n        name: iris-latest-linux\r\n        path: ./dist\r\n    - uses: actions/upload-artifact@v7\r\n      with:\r\n        name: iris-latest-linux-appimage\r\n        path: ./appimage\r\n\r\n\r\n\r\n"
  },
  {
    "path": ".github/workflows/macos.yml",
    "content": "name: macOS CI\r\n\r\non:\r\n  push:\r\n    branches: [ \"master\" ]\r\n\r\njobs:\r\n  macos-universal-build:\r\n    runs-on: macos-latest\r\n\r\n    steps:\r\n    - uses: actions/checkout@v6\r\n      with:\r\n        submodules: 'recursive'\r\n        fetch-depth: 0\r\n    - name: Install Vulkan SDK\r\n      uses: jakoch/install-vulkan-sdk-action@v1\r\n      with:\r\n        optional_components: com.lunarg.vulkan.volk\r\n        install_runtime: true\r\n        cache: true\r\n        stripdown: true\r\n    - name: Build and pack Iris\r\n      run: |\r\n        git fetch --all --tags\r\n        cmake -S . -B build -G \"Unix Makefiles\"\r\n        cmake --build build -j8\r\n        sudo cmake --install build\r\n        mkdir -p ./build/dist\r\n        cp -R ./build/iris.app ./build/dist\r\n    - uses: actions/upload-artifact@v7\r\n      with:\r\n        name: iris-latest-macos-universal\r\n        path: ./build/dist\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n\r\n"
  },
  {
    "path": ".github/workflows/release.yml",
    "content": "name: Release CI\n\non:\n  push:\n    tags:\n    - '0.*'\n\njobs:\n  windows-build:\n    runs-on: windows-latest\n\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        submodules: 'recursive'\n        fetch-depth: 0\n    - name: Build and pack Iris\n      run: |\n        git fetch --all --tags\n        cmake -S . -B build -G \"MinGW Makefiles\" -DCMAKE_BUILD_TYPE=Release\n        cmake --build build -j8\n        New-Item -Path \"dist\" -ItemType Directory\n        Copy-Item -Path \".\\build\\iris.exe\" \"dist\"\n        Copy-Item \".\\build\\*.dll\" \"dist\"\n        Copy-Item \"C:\\mingw64\\bin\\*.dll\" \"dist\"\n    - name: Generate artifact name\n      id: generate-name\n      run: |\n        echo \"::set-output name=artifact::iris-${{ github.ref_name }}-windows\"\n    - name: Zip artifact\n      run: |\n        $artifactName = \"${{ steps.generate-name.outputs.artifact }}\"\n        Compress-Archive -Path \"dist\\*\" -DestinationPath \"$artifactName.zip\"\n    - name: Upload to release\n      uses: softprops/action-gh-release@v2.6.1\n      with:\n        files: |\n          ${{ steps.generate-name.outputs.artifact }}.zip\n\n  linux-build:\n    runs-on: ubuntu-latest\n\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        submodules: 'recursive'\n        fetch-depth: 0\n    - name: Install deps\n      run: |\n        sudo add-apt-repository universe\n        sudo apt-get install build-essential git make \\\n        pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \\\n        libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev \\\n        libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \\\n        libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \\\n        libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libfuse2t64\n    - name: Build and pack Iris\n      run: |\n        git fetch --all --tags\n        cmake -S . -B build -DCMAKE_BUILD_TYPE=Release\n        cmake --build build -j8\n        sudo cmake --install build\n        mkdir dist\n        mv ./build/iris ./dist\n        mkdir appimage\n        mv ./build/*.AppImage ./appimage\n    - name: Generate artifact name\n      id: generate-name\n      run: |\n        echo \"::set-output name=artifact::iris-${{ github.ref_name }}-linux\"\n    - name: Generate AppImage artifact name\n      id: generate-name-appimage\n      run: |\n        echo \"::set-output name=artifact::iris-${{ github.ref_name }}-linux-appimage\"\n    - name: Zip artifact\n      run: |\n        zip -rj ${{ steps.generate-name.outputs.artifact }}.zip dist\n    - name: Zip AppImage\n      run: |\n        zip -rj ${{ steps.generate-name-appimage.outputs.artifact }}.zip appimage\n    - name: Upload normal binary to release\n      uses: softprops/action-gh-release@vv2.6.1\n      with:\n        files: |\n          ${{ steps.generate-name.outputs.artifact }}.zip\n    - name: Upload AppImage to release\n      uses: softprops/action-gh-release@v2.6.1\n      with:\n        files: |\n          ${{ steps.generate-name-appimage.outputs.artifact }}.zip\n\n  macos-universal-build:\n    runs-on: macos-latest\n\n    steps:\n    - uses: actions/checkout@v6\n      with:\n        submodules: 'recursive'\n        fetch-depth: 0\n    - name: Install Vulkan SDK\n      uses: jakoch/install-vulkan-sdk-action@v1\n      with:\n        optional_components: com.lunarg.vulkan.volk\n        install_runtime: true\n        cache: true\n        stripdown: true\n    - name: Build and pack Iris\n      run: |\n        git fetch --all --tags\n        cmake -S . -B build -DCMAKE_BUILD_TYPE=Release\n        cmake --build build -j8\n        sudo cmake --install build\n    - name: Generate artifact name\n      id: generate-name\n      run: |\n        echo \"::set-output name=artifact::iris-${{ github.ref_name }}-macos-universal\"\n    - name: Zip artifact\n      run: |\n        cd build; zip -r ../${{ steps.generate-name.outputs.artifact }}.zip ./iris.app; cd ..\n    - name: Upload to release\n      uses: softprops/action-gh-release@v2.6.1\n      with:\n        files: |\n          ${{ steps.generate-name.outputs.artifact }}.zip\n"
  },
  {
    "path": ".github/workflows/windows.yml",
    "content": "name: Windows CI\r\n\r\non:\r\n  push:\r\n    branches: [ \"master\" ]\r\n\r\njobs:\r\n  build:\r\n    runs-on: windows-latest\r\n\r\n    steps:\r\n    - uses: actions/checkout@v6\r\n      with:\r\n        submodules: 'recursive'\r\n        fetch-depth: 0\r\n    - name: Build and pack Iris\r\n      run: |\r\n        git fetch --all --tags\r\n        cmake -S . -B build -G \"MinGW Makefiles\"\r\n        cmake --build build -j8\r\n        New-Item -Path \"dist\" -ItemType Directory\r\n        Copy-Item -Path \".\\build\\iris.exe\" \"dist\"\r\n        Copy-Item \".\\build\\*.dll\" \"dist\"\r\n        Copy-Item \"C:\\mingw64\\bin\\*.dll\" \"dist\"\r\n    - name: Upload artifact\r\n      uses: actions/upload-artifact@v7\r\n      with:\r\n        name: iris-latest-windows\r\n        path: dist/\r\n\r\n\r\n\r\n"
  },
  {
    "path": ".gitignore",
    "content": "SDL2-2.32.0/\n.vscode/\nbuild/\nbin/\ntools/\nelf/\nroms/\n.vs/\nac/\nscreens/\nmain2.cpp\ncompare\ncompare.cpp\nimgui.ini\n*.AppImage\n*.expected\n*.dump\n*.cue\n*.bin\n*.elf\n*.exe\n*.iso\n*.mec\n*.nvm\n*.mcd\n*.wav\n*.irx\n*.o\n.DS_Store\n"
  },
  {
    "path": ".gitmodules",
    "content": "[submodule \"imgui\"]\n\tpath = deps/imgui\n\turl = https://github.com/ocornut/imgui\n\tbranch = docking\n[submodule \"tomlplusplus\"]\n\tpath = deps/tomlplusplus\n\turl = https://github.com/marzer/tomlplusplus\n[submodule \"SDL\"]\n\tpath = deps/SDL\n\turl = https://github.com/libsdl-org/SDL\n\tbranch = release-3.2.24\n[submodule \"incbin\"]\n\tpath = deps/incbin\n\turl = https://github.com/graphitemaster/incbin\n[submodule \"implot\"]\n\tpath = deps/implot\n\turl = https://github.com/epezent/implot\n[submodule \"parallel-gs\"]\n\tpath = deps/parallel-gs\n\turl = https://github.com/Arntzen-software/parallel-gs\n[submodule \"libchdr\"]\n\tpath = deps/libchdr\n\turl = https://github.com/rtissera/libchdr\n[submodule \"stb\"]\n\tpath = deps/stb\n\turl = https://github.com/nothings/stb\n[submodule \"portable-file-dialogs\"]\n\tpath = deps/portable-file-dialogs\n\turl = https://github.com/samhocevar/portable-file-dialogs\n[submodule \"deps/libdeflate\"]\n\tpath = deps/libdeflate\n\turl = https://github.com/ebiggers/libdeflate\n[submodule \"deps/lz4\"]\n\tpath = deps/lz4\n\turl = https://github.com/lz4/lz4\n[submodule \"deps/SDL_GameControllerDB\"]\n\tpath = deps/SDL_GameControllerDB\n\turl = https://github.com/mdqinc/SDL_GameControllerDB\n[submodule \"deps/asmjit\"]\n\tpath = deps/asmjit\n\turl = https://github.com/asmjit/asmjit\n"
  },
  {
    "path": "AppImage.cmake",
    "content": "function(make_appimage)\n\tset(optional)\n\tset(args EXE NAME DIR_ICON ICON OUTPUT_NAME)\n\tset(list_args ASSETS)\n\tcmake_parse_arguments(\n\t\tPARSE_ARGV 0\n\t\tARGS\n\t\t\"${optional}\"\n\t\t\"${args}\"\n\t\t\"${list_args}\"\n\t)\n\n\tif(${ARGS_UNPARSED_ARGUMENTS})\n\t\tmessage(WARNING \"Unparsed arguments: ${ARGS_UNPARSED_ARGUMENTS}\")\n\tendif()\n\n\n    # download AppImageTool if needed (TODO: non-x86 build machine?)\n    SET(AIT_PATH \"${CMAKE_BINARY_DIR}/AppImageTool.AppImage\" CACHE INTERNAL \"\")\n    if (NOT EXISTS \"${AIT_PATH}\")\n        file(DOWNLOAD https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage \"${AIT_PATH}\")\n        execute_process(COMMAND chmod +x ${AIT_PATH})\n    endif()\n\n    # make the AppDir\n    set(APPDIR \"${CMAKE_BINARY_DIR}/AppDir\")\n    file(REMOVE_RECURSE \"${APPDIR}\")       # remove if leftover\n    file(MAKE_DIRECTORY \"${APPDIR}\")\n\n    # copy executable to appdir\n    file(COPY \"${ARGS_EXE}\" DESTINATION \"${APPDIR}\" FOLLOW_SYMLINK_CHAIN)\n    get_filename_component(EXE_NAME \"${ARGS_EXE}\" NAME)\n\n    # create the script that will launch the AppImage\nfile(WRITE \"${APPDIR}/AppRun\" \n\"#!/bin/sh\ncd \\\"$(dirname \\\"$0\\\")\\\";\n./${EXE_NAME} $@\"\n    )\n    execute_process(COMMAND chmod +x \"${APPDIR}/AppRun\")\n    \n    # copy assets to appdir\n    file(COPY ${ARGS_ASSETS} DESTINATION \"${APPDIR}\")\n\n    # copy icon thumbnail\n    file(COPY ${ARGS_DIR_ICON} DESTINATION \"${APPDIR}\")\n    get_filename_component(THUMB_NAME \"${ARGS_DIR_ICON}\" NAME)\n    file(RENAME \"${APPDIR}/${THUMB_NAME}\" \"${APPDIR}/.DirIcon\")\n\n    # copy icon highres\n    file(COPY ${ARGS_ICON} DESTINATION \"${APPDIR}\")\n    get_filename_component(ICON_NAME \"${ARGS_ICON}\" NAME)\n    get_filename_component(ICON_EXT \"${ARGS_ICON}\" EXT)\n    file(RENAME \"${APPDIR}/${ICON_NAME}\" \"${APPDIR}/${ARGS_NAME}${ICON_EXT}\")\n\n    # Create the .desktop file\n    file(WRITE \"${APPDIR}/${ARGS_NAME}.desktop\" \n    \"[Desktop Entry]\nType=Application\nName=${ARGS_NAME}\nIcon=${ARGS_NAME}\nCategories=X-None;\"    \n    )\n\n    # Invoke AppImageTool\n    execute_process(COMMAND ${AIT_PATH} ${APPDIR} ${ARGS_OUTPUT_NAME})\n    \n    file(REMOVE_RECURSE \"${APPDIR}\")\nendfunction()\n"
  },
  {
    "path": "CMakeLists.txt",
    "content": "cmake_minimum_required(VERSION 3.21)\nproject(iris LANGUAGES C CXX)\n\nset(WIN_ICON ${CMAKE_CURRENT_SOURCE_DIR}/res/iris.rc)\nset(CMAKE_OSX_ARCHITECTURES \"x86_64;arm64\")\nset(CMAKE_OSX_DEPLOYMENT_TARGET \"10.15\")\nset(OSX_ICON ${CMAKE_CURRENT_SOURCE_DIR}/res/iris.icns)\n\nset_source_files_properties(${OSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION \"Resources\")\n\n# Statically link SDL3 for Linux targets\nset(SDL_STATIC ON)\nset(ASMJIT_STATIC ON)\nset(CMAKE_POSITION_INDEPENDENT_CODE ON)\n\nfind_package(Git QUIET)\n\nif (GIT_FOUND)\n    execute_process(\n        COMMAND ${GIT_EXECUTABLE} describe --tags --always --dirty --match \"0.*\"\n        WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR}\n        OUTPUT_VARIABLE GIT_VERSION_STRING\n        OUTPUT_STRIP_TRAILING_WHITESPACE\n        ERROR_QUIET\n    )\n    if(NOT GIT_VERSION_STRING)\n        # Fallback if git describe fails (e.g., no tags in the history)\n        set(GIT_VERSION_STRING \"unknown-version\")\n    endif()\nelse()\n    set(GIT_VERSION_STRING \"git-not-found\")\nendif()\n\nmessage(STATUS \"Project Version: ${GIT_VERSION_STRING}\")\n\n# You can then use GIT_VERSION_STRING in your project, e.g., to generate a header file:\n# configure_file(\n#     ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in\n#     ${CMAKE_CURRENT_BINARY_DIR}/version.h\n#     @ONLY\n# )\n\ninclude(CheckIPOSupported)\ncheck_ipo_supported(RESULT LTO_SUPPORTED OUTPUT LTO_ERROR)\n\nset(BUILD_SHARED_LIBS OFF)\n\n# And this part tells CMake where to find and install the file itself\nadd_executable(iris MACOSX_BUNDLE ${OSX_ICON} ${WIN_ICON})\n\ntarget_compile_definitions(iris PUBLIC IMGUI_IMPL_VULKAN_NO_PROTOTYPES)\ntarget_compile_definitions(iris PUBLIC IMGUI_IMPL_VULKAN_USE_VOLK)\ntarget_compile_options(iris PUBLIC \"-Wno-deprecated-declarations\")\n# target_compile_definitions(iris PUBLIC _DEBUG)\n\nset(PARALLEL_GS_STANDALONE ON CACHE BOOL \"\" FORCE)\nadd_subdirectory(deps/asmjit EXCLUDE_FROM_ALL)\nadd_subdirectory(deps/tomlplusplus EXCLUDE_FROM_ALL)\nadd_subdirectory(deps/libdeflate EXCLUDE_FROM_ALL)\nadd_subdirectory(deps/parallel-gs EXCLUDE_FROM_ALL)\nadd_subdirectory(deps/libchdr EXCLUDE_FROM_ALL)\nadd_subdirectory(deps/SDL EXCLUDE_FROM_ALL)\n\nif (CMAKE_SYSTEM_PROCESSOR MATCHES \"AMD64\")\n    target_compile_options(iris PRIVATE -D_EE_USE_INTRINSICS -mssse3 -msse4.1)\nendif()\n\nif (X11_API)\n    target_compile_definitions(granite-volk PUBLIC VK_USE_PLATFORM_XLIB_KHR)\nendif()\nif (WAYLAND_API)\n    target_compile_definitions(granite-volk PUBLIC VK_USE_PLATFORM_WAYLAND_KHR)\nendif()\nif (WIN32)\n    target_compile_definitions(granite-volk PUBLIC VK_USE_PLATFORM_WIN32_KHR)\nendif()\n\nif (NOT CMAKE_SYSTEM_NAME MATCHES \"Windows\")\n    if (LTO_SUPPORTED)\n        message(STATUS \"IPO/LTO enabled\")\n        set_property(TARGET iris PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE)\n    else()\n        message(STATUS \"IPO/LTO not supported: ${LTO_ERROR}\")\n    endif()\nendif()\n\nset_property(TARGET iris PROPERTY CXX_STANDARD 20)\nadd_definitions(\"-D_IRIS_VERSION=${GIT_VERSION_STRING}\")\n\ntarget_sources(iris PRIVATE\n    main.cpp\n    frontend/audio.cpp\n    frontend/handlers.cpp\n    frontend/vulkan.cpp\n    frontend/imgui.cpp\n    frontend/emu.cpp\n    frontend/render.cpp\n    frontend/shaders.cpp\n    frontend/input.cpp\n    frontend/iris.cpp\n    frontend/elf.cpp\n    frontend/notifications.cpp\n    frontend/settings.cpp\n    frontend/ui/about.cpp\n    frontend/ui/bios_setting.cpp\n    frontend/ui/breakpoints.cpp\n    frontend/ui/control.cpp\n    frontend/ui/dma.cpp\n    frontend/ui/gs.cpp\n    frontend/ui/intc.cpp\n    frontend/ui/logs.cpp\n    frontend/ui/memory.cpp\n    frontend/ui/memory_card_tool.cpp\n    frontend/ui/memory_search.cpp\n    frontend/ui/menubar.cpp\n    frontend/ui/modules.cpp\n    frontend/ui/overlay.cpp\n    frontend/ui/pad.cpp\n    frontend/ui/settings.cpp\n    frontend/ui/spu2.cpp\n    frontend/ui/state.cpp\n    frontend/ui/statusbar.cpp\n    frontend/ui/symbols.cpp\n    frontend/ui/threads.cpp\n    frontend/ui/vu_disassembly.cpp\n    src/ps2.c\n    src/ps2_elf.c\n    src/ps2_iso9660.c\n    src/queue.c\n    src/rom.c\n    src/md5.c\n    src/list.c\n    src/scheduler.c\n    src/dev/ds.c\n    src/dev/guncon.c\n    src/dev/mcd.c\n    src/dev/mtap.c\n    src/dev/ps1_mcd.c\n    src/dev/ps1_mcd.c\n    src/ee/ee_cached.cpp\n    src/ee/bus.c\n    src/ee/dmac.c\n    src/ee/ee_dis.c\n    src/ee/gif.c\n    src/ee/intc.c\n    src/ee/timers.c\n    src/ee/vif.c\n    src/ee/vu_cached.cpp\n    src/ee/vu_dis.c\n    src/gs/gs.c\n    src/gs/renderer/null.cpp\n    src/gs/renderer/renderer.cpp\n    src/gs/renderer/hardware.cpp\n    src/iop/bus.c\n    src/iop/cdvd.c\n    src/iop/disc.c\n    src/iop/dma.c\n    src/iop/fw.c\n    src/iop/intc.c\n    src/iop/iop.c\n    src/iop/iop_dis.c\n    src/iop/iop_export.c\n    src/iop/rpc.c\n    src/iop/sio2.c\n    src/iop/spu2.c\n    src/iop/timers.c\n    src/iop/usb.c\n    src/iop/disc/bin.c\n    src/iop/disc/cue.c\n    src/iop/disc/chd.c\n    src/iop/disc/ciso.c\n    src/iop/disc/iso.c\n    src/iop/hle/ioman.cpp\n    src/iop/hle/loadcore.c\n    src/iop/hle/sysmem.c\n    src/ipu/chromtable.cpp\n    src/ipu/codedblockpattern.cpp\n    src/ipu/dct_coeff.cpp\n    src/ipu/dct_coeff_table0.cpp\n    src/ipu/dct_coeff_table1.cpp\n    src/ipu/ipu.cpp\n    src/ipu/ipu_fifo.cpp\n    src/ipu/lumtable.cpp\n    src/ipu/mac_addr_inc.cpp\n    src/ipu/mac_b_pic.cpp\n    src/ipu/mac_i_pic.cpp\n    src/ipu/mac_p_pic.cpp\n    src/ipu/motioncode.cpp\n    src/ipu/vlc_table.cpp\n    src/shared/bios.c\n    src/shared/dev9.c\n    src/shared/ram.c\n    src/shared/sbus.c\n    src/shared/sif.c\n    src/shared/speed.c\n    src/shared/speed/ata.c\n    src/shared/speed/eeprom.c\n    src/shared/speed/flash.c\n    src/s14x/nand.c\n    src/s14x/syscon.c\n    src/s14x/sram.c\n    src/s14x/link.c\n    src/s14x/ioboard.c\n    src/s14x/aiboard.c\n    deps/imgui/imgui.cpp\n    deps/imgui/imgui_demo.cpp\n    deps/imgui/imgui_draw.cpp\n    deps/imgui/imgui_tables.cpp\n    deps/imgui/imgui_widgets.cpp\n    deps/imgui/backends/imgui_impl_sdl3.cpp\n    deps/imgui/backends/imgui_impl_vulkan.cpp\n    deps/implot/implot_demo.cpp\n    deps/implot/implot_items.cpp\n    deps/implot/implot.cpp\n    deps/lz4/lib/lz4.c\n)\n\ntarget_include_directories(iris PRIVATE\n    deps/asmjit\n    deps/imgui\n    deps/imgui/backends\n    deps/implot\n    deps/SDL/include\n    deps/incbin\n    deps/parallel-gs\n    deps/libchdr/include\n    deps/lz4/lib\n    deps/stb\n    deps/portable-file-dialogs\n    frontend\n    src\n    res\n)\n\ntarget_link_libraries(iris PUBLIC\n    asmjit::asmjit\n    libdeflate::libdeflate_static\n    tomlplusplus::tomlplusplus\n    SDL3::SDL3-static\n    parallel-gs\n    chdr-static\n)\n\nif (CMAKE_SYSTEM_NAME MATCHES \"Darwin\")\n    find_package(Vulkan COMPONENTS MoltenVK)\n\n    message(STATUS \"VulkanSDK path: $ENV{VULKAN_SDK}\")\n\n    find_library(MOLTENVK_LIBRARY\n        NAMES libMoltenVK.dylib\n        PATHS ENV VULKAN_SDK\n        PATH_SUFFIXES /lib\n    )\n\n    target_link_libraries(iris PUBLIC\n        Vulkan::Vulkan\n        ${MOLTENVK_LIBRARY}\n    )\nendif()\n\nif (WIN32)\n    target_link_libraries(iris PRIVATE dwmapi)\n    target_sources(iris PRIVATE frontend/platform/windows.cpp)\nelse()\n    target_sources(iris PRIVATE frontend/platform/stub.cpp)\nendif()\n\nset_target_properties(iris PROPERTIES \n    # On macOS, make a proper .app bundle instead of a bare executable\n    MACOSX_BUNDLE TRUE\n    # Set the Info.plist file for Apple Mobile platforms. Without this file, your app\n    # will not launch. \n    MACOSX_BUNDLE_INFO_PLIST \"${CMAKE_CURRENT_SOURCE_DIR}/Info.plist\"\n    MACOSX_BUNDLE_ICON_FILE iris.icns\n\n    # in Xcode, create a Scheme in the schemes dropdown for the app.\n    XCODE_GENERATE_SCHEME TRUE\n    # Identification for Xcode\n    XCODE_ATTRIBUTE_BUNDLE_IDENTIFIER \"com.allkern.iris\"\n    XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER \"com.allkern.iris\"\n    XCODE_ATTRIBUTE_CURRENTYEAR \"${CURRENTYEAR}\"\n    RESOURCE \"${RESOURCE_FILES}\"\n)\n\n# on Visual Studio, set our app as the default project\nset_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT \"${EXECUTABLE_NAME}\")\n\n# On macOS Platforms, ensure that the bundle is valid for distribution by calling fixup_bundle.\n# note that fixup_bundle does not work on iOS, so you will want to use static libraries \n# or manually copy dylibs and set rpaths\nmessage(STATUS \"CMake System Name: ${CMAKE_SYSTEM_NAME}\")\n\nif (CMAKE_SYSTEM_NAME MATCHES \"Darwin\")\n    # tell Install about the target, otherwise fixup won't know about the transitive dependencies\n    install(TARGETS iris\n        BUNDLE DESTINATION ./install COMPONENT Runtime\n           RUNTIME DESTINATION ./install/bin COMPONENT Runtime\n    )\n    \n    set(BUNDLE_PATH \"${CMAKE_BINARY_DIR}/iris.app\") # where to look for dependencies when fixing up\n    file(GLOB VULKAN_DYLIBS $ENV{VULKAN_SDK}/lib/libvulkan.*.dylib)\n    set(VULKAN_DYLIBS ${VULKAN_DYLIBS} $ENV{VULKAN_SDK}/lib/libMoltenVK.dylib)\n    install(CODE \"\n        execute_process(COMMAND echo \\\"Preparing to bundle iris...\\\")\n        execute_process(COMMAND echo \\\"Bundle path: ${BUNDLE_PATH}\\\")\n        execute_process(COMMAND echo \\\"Vulkan dylibs: ${VULKAN_DYLIBS}\\\")\n        execute_process(COMMAND echo \\\"Adding local RPATH...\\\")\n        execute_process(\n            COMMAND install_name_tool -add_rpath\n            \\\"@executable_path/../Frameworks\\\"\n            \\\"${BUNDLE_PATH}/Contents/MacOS/iris\\\"\n        )\n        execute_process(COMMAND echo \\\"Creating ICD JSON directory...\\\")\n        make_directory(${BUNDLE_PATH}/Contents/Resources/vulkan/icd.d)\n        execute_process(COMMAND echo \\\"Creating Frameworks directory...\\\")\n        make_directory(${BUNDLE_PATH}/Contents/Frameworks)\n        execute_process(COMMAND echo \\\"Copying files...\\\")\n        file(COPY\n            ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK_icd.json\n            DESTINATION ${BUNDLE_PATH}/Contents/Resources/vulkan/icd.d/\n        )\n        file(COPY\n            ${VULKAN_DYLIBS}\n            DESTINATION ${BUNDLE_PATH}/Contents/Frameworks/\n        )\n        execute_process(COMMAND echo \\\"Signing app bundle...\\\")\n        execute_process(COMMAND\n            codesign --force --deep --sign - ${BUNDLE_PATH}\n            RESULT_VARIABLE codesign_result\n            OUTPUT_VARIABLE codesign_output\n        )\n        if (codesign_result)\n            execute_process(COMMAND echo \\\"${codesign_output}\\\")\n        endif()\n    \")\n    set(CPACK_GENERATOR \"DragNDrop\")\n    include(CPack)\nendif()\n\nif (CMAKE_SYSTEM_NAME MATCHES \"Linux\")\n    install(CODE\n        \"include(${CMAKE_CURRENT_SOURCE_DIR}/AppImage.cmake)\n        make_appimage(\n            EXE \\\"${CMAKE_CURRENT_SOURCE_DIR}/build/iris\\\"\n            NAME \\\"Iris\\\"\n            ICON \\\"${CMAKE_CURRENT_SOURCE_DIR}/res/iris.png\\\"\n            DIR_ICON \\\"${CMAKE_CURRENT_SOURCE_DIR}/res/iris.png\\\"\n            OUTPUT_NAME \\\"${CMAKE_CURRENT_SOURCE_DIR}/build/Iris-${GIT_VERSION_STRING}.AppImage\\\"\n        )\n        \"\n        COMPONENT Runtime\n    )\nendif()"
  },
  {
    "path": "Info.plist",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n<!DOCTYPE plist PUBLIC \"-//Apple//DTD PLIST 1.0//EN\" \"http://www.apple.com/DTDs/PropertyList-1.0.dtd\">\n<plist version=\"1.0\">\n\t<dict>\n\t\t<key>CFBundleDevelopmentRegion</key>\n\t\t\t<string>en</string>\n\t\t<key>CFBundleExecutable</key>\n\t\t\t<string>iris</string>\n\t\t<key>CFBundleIdentifier</key>\n\t\t\t<string>iris.app.0</string>\n\t\t<key>CFBundleInfoDictionaryVersion</key>\n\t\t\t<string>6.0</string>\n\t\t<key>CFBundleName</key>\n\t\t\t<string>Iris</string>\n\t\t<key>CFBundlePackageType</key>\n\t\t\t<string>APPL</string>\n\t\t<key>CFBundleShortVersionString</key>\n\t\t\t<string>0.10-alpha</string>\n\t\t<key>CFBundleSignature</key>\n\t\t\t<string>iris</string>\n\t\t<key>CFBundleVersion</key>\n\t\t\t<string>0.10-alpha</string>\n\t\t<key>LSApplicationCategoryType</key>\n\t\t\t<string>public.app-category.games</string>\n\t\t<key>LSMinimumSystemVersion</key>\n\t\t\t<string>10.15</string>\n\t\t<key>NSHighResolutionCapable</key>\n\t\t\t<true/>\n\t\t<key>NSPrincipalClass</key>\n\t\t\t<string>NSApplication</string>\n\t\t<key>CFBundleIconFile</key>\n\t\t\t<string>iris</string>\n\t</dict>\n</plist>\n"
  },
  {
    "path": "LICENSE",
    "content": "MIT License\n\nCopyright (c) 2025 Allkern/Lisandro Alarcon\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the \"Software\"), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all\ncopies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\nSOFTWARE.\n"
  },
  {
    "path": "MoltenVK_icd.json",
    "content": "{\n    \"file_format_version\" : \"1.0.0\",\n    \"ICD\": {\n        \"library_path\": \"../../../Frameworks/libMoltenVK.dylib\",\n        \"api_version\" : \"1.4.0\",\n        \"is_portability_driver\": true\n    }\n}\n"
  },
  {
    "path": "README.md",
    "content": "<div align=\"center\" text-align=\"center\" width=\"100%\">\n    <img width=\"55%\" src=\"https://github.com/user-attachments/assets/d59e2d95-5791-4497-9985-442ca5115ac6\">\n</div>\n\n# 🐣 Iris\nSony PlayStation 2 emulator for Windows, Linux and macOS \n\n## Screenshots\n<div align=\"center\" class=\"grid\" markdown>\n    <img width=\"47%\" alt=\"Metal Gear Solid 3 - Snake Eater (Japan)\" src=\"https://github.com/user-attachments/assets/9ffd0131-5fa0-4e2f-97ee-6d180f7dcdcc\" />\n    <img width=\"47%\" alt=\"Resident Evil 4 (USA)\" src=\"https://github.com/user-attachments/assets/d2f68c22-c6c9-4e00-9430-ecf609f9b269\" />\n    <img width=\"47%\" alt=\"God of War II (USA)\" src=\"https://github.com/user-attachments/assets/40747140-1b0c-4834-b3ab-e5439cc1272d\" />\n    <img width=\"47%\" alt=\"Kingdom Hearts II (USA)\" src=\"https://github.com/user-attachments/assets/53aa9486-958f-49c6-aa88-cd435260bb0a\" />\n    <img width=\"47%\" alt=\"Ace Combat Zero - The Belkan War (USA)\" src=\"https://github.com/user-attachments/assets/1adfaf21-305d-4d2c-a828-00cbbbfba920\" />\n    <img width=\"47%\" alt=\"Virtua Fighter 4 (USA)\" src=\"https://github.com/user-attachments/assets/1d1662ed-177d-48de-b21f-300a035da1b3\" />\n    <img width=\"47%\" alt=\"Devil May Cry 3 - Dante's Awakening (USA)\" src=\"https://github.com/user-attachments/assets/a7fc4654-cdb1-4a00-8187-a99ab5b4f7ea\" />\n    <img width=\"47%\" alt=\"Ico (USA)\" src=\"https://github.com/user-attachments/assets/419b76a4-5208-4e18-bebe-ab762c0e08ed\" />\n</div>\n\n## Usage\n> [!WARNING]  \n> This emulator is under development, most games WILL run at very low/unplayable framerates.\n\n### GUI\nNavigate over to `Iris > Open...` and choose a disc image or ELF executable, drag-and-drop is also supported\n\n### CLI\n```\nUsage: iris [OPTION]... <path-to-disc-image>\n\n  -b, --bios               Specify a PlayStation 2 BIOS dump file\n      --rom1               Specify a DVD player dump file\n      --rom2               Specify a ROM2 dump file\n  -d, --boot               Specify a direct kernel boot path\n  -i, --disc               Specify a path to a disc image file\n  -x, --executable         Specify a path to an ELF executable to be\n                             loaded on system startup\n      --slot1              Specify a path to a memory card file to\n                             be inserted on slot 1\n      --slot2              Specify a path to a memory card file to\n                             be inserted on slot 2\n  -h, --help               Display this help and exit\n  -v, --version            Output version information and exit\n```\n\n## Features\n- Support for ISO, BIN/CUE, CHD and CSO/ZSO disc image formats\n- Hardware-accelerated Vulkan GS renderer with support for up to 16x SSAA\n- Feature-packed debugger\n- Easy to use graphical interface\n- Game controller support with input remapping\n- Support for post-processing shaders\n\n## Building\n> [!WARNING]  \n> Building requires CMake and the Vulkan SDK on all supported platforms\n\n### Linux\nBuilding on Linux requires installing SDL3 dependencies and FUSE if you wish to generate AppImages.\n```\nsudo apt update\nsudo apt upgrade\nsudo add-apt-repository universe\nsudo apt-get install build-essential git make \\\n    pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \\\n    libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev \\\n    libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \\\n    libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \\\n    libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libfuse2t64\n```\nThen clone the repository and run CMake:\n```\ngit clone https://github.com/allkern/iris --recursive\ncd iris\ncmake -S . -B build\ncmake --build build -j8\n```\nOptionally run `cmake --install build` to generate an AppImage.\n\n### Windows\nWe currently only support GCC as a compiler on Windows, this is because MSVC doesn't have an inline assembler, which we need to embed resources into the executable. This might eventually be fixed though!\n```\ngit clone https://github.com/allkern/iris --recursive\ncd iris\ncmake -S . -B build -G \"MinGW Makefiles\"\ncmake --build build -j8\n```\n\n### macOS\n\n```\ngit clone https://github.com/allkern/iris --recursive\ncd iris\ncmake -S . -B build\ncmake --build build -j8\n```\nOptionally run `sudo cmake --install build` to generate a macOS App Bundle\n\n## Progress/Insights\nIris can boot/run a fairly large number of commercial games, playability may be all over the place though, some games run fairly smoothly, while others can't break the 1 digit FPS mark, this is due to the lack of EE/VU JITs which will be addressed soon.\n\nThe PlayStation 2 can have up to three processors running simultaneously at ~300 MHz, plus the IOP running at 33 MHz, not counting all the different peripherals/chips doing their own work, such as the GS rendering massive amounts of graphics, the IPU decoding MPEG-2 video, the SPU2 rendering up to 48 ADPCM audio channels at 48 KHz, and more. You can see how emulating this system can become a pretty complex task once you factor in all the processing that's done per-frame.\n\nIn order to alleviate the struggles of emulating the PS2, we have a number of optimization techniques at our disposal, some of which have already been implemented:\n- Scheduling (done)\n- Software fastmem (done)\n- EE interpreter caching (done)\n- Hardware-accelerated GS rendering (done)\n- EE JIT/Dynarec (coming up)\n- VU JIT/Dynarec (soon)\n- Hardware fastmem (eventually)\n- etc.\n\nIntegrating Parallel-GS was a big milestone for Iris, now we need to work on JITing the EE. Once that's done, I'd expect the emulator to start running a lot more games at playable speeds.\n\n## Preservation\nIris aims to emulate not only the retail PlayStation 2, but also other systems based on the PS2, such as the PSX DESR (Japanese PS2/DVR hybrid) and all or most of the PS2-based arcade systems, work towards this goal is ongoing, in fact, Iris was the first PS2 emulator to boot [the PSX DESR BIOS/bootrom](https://www.youtube.com/watch?v=YtsoRjofYKA).\n\nIn order to emulate these systems, a pile of extra hardware needs to be implemented, such as the onboard flash memory on the PSX DESR, and the NAND storage on all of the Namco boards. This is not trivial and the lack of documentation makes it a pretty daunting task, but we're working on it.\n\nIt's worth to mention that PSX DESR support has been merged to the main branch, which means you (our beloved user) should be able to dump the BIOS out of your PSX DESR machine and run it on Iris. It won't boot past some error screens after running the boot animation but I'd say it's still pretty cool.\n\nIn addition to emulating other systems based on the PS2, Iris also aims to support **all** the features/capabilities of the retail system, this includes the often overlooked DVD player, PSBBN, Linux, and eventually all of the available external USB/SIO peripherals and input/output devices.\n\n# Special thanks and acknowledgements\nI would like to thank the emudev Discord server, Ziemas, Nelson (ncarrillo), cakehonolulu, PSI-rockin, noumi and the PCSX2 team for their kind support.\n\nThis project makes use of the following third-party libraries:\n- [ImGui](https://github.com/ocornut/imgui)\n- [ImPlot](https://github.com/epezent/implot)\n- [SDL3](https://github.com/libsdl-org/SDL)\n- [SDL_GameControllerDB](https://github.com/mdqinc/SDL_GameControllerDB)\n- [incbin](https://github.com/graphitemaster/incbin)\n- [Parallel-GS](https://github.com/Arntzen-software/parallel-gs)\n- [libchdr](https://github.com/rtissera/libchdr)\n- [libdeflate](https://github.com/ebiggers/libdeflate)\n- [lz4](https://github.com/lz4/lz4)\n- [toml++](https://marzer.github.io/tomlplusplus/)\n- [Portable File Dialogs](https://github.com/samhocevar/portable-file-dialogs)\n- [stb_image](https://github.com/nothings/stb)\n\nCredit goes out to the developers of these libraries, Iris wouldn't have been possible without your outstanding work.\n\n### Components\nThis console is significantly more complex compared to the PS1, here's a rough list of components:\n```\n🟡 EE (R5900) CPU\n- 🟡 FPU\n- 🟡 MMI (SIMD)\n- 🟡 TLB\n- 🟡 DMAC\n- 🟢 INTC\n- 🟡 Timers\n- 🟢 GIF\n- 🟡 GS\n- 🟡 VU0\n  = 🟡 Macro mode\n  = 🟡 Micro mode\n  = 🟡 VIF0\n- 🟡 VU1 (always micro mode)\n  = 🟡 VIF1\n- 🟡 IPU\n🟢 IOP (R3000) CPU\n- 🟡 DMAC\n- 🟢 INTC\n- 🟡 Timers\n- 🟢 CDVD\n- 🟢 SIO2 (controllers and Memory Cards)\n- 🟢 SPU2\n- 🟡 DEV9\n- 🟡 USB/FireWire?\n- 🔴 Ethernet\n- 🔴 PS1 backcompat (PS1 hardware)\n🟢 SIF\n```\n"
  },
  {
    "path": "compat.txt",
    "content": "\".hack - Infection (USA)\" - Boots to menus, runs very slow\n\"007 - Nightfire (USA)\" - Hangs trying to play an FMV at the very beginning\n\"Arcana Heart (USA)\" - REGRESSION! Seems to trash IOP memory with what looks like ADPCM data\n\"Babe (Australia)\" - Boots, won't boot with FMV skipping enabled though\n\"Batman - Vengeance (USA)\" - Requires MFIFO\n\"Big Mutha Truckers (USA)\" - Prints \"CD Error 0xfd, ditching this request\", reads sectors 1eb02,1eb12,1eb22 repeatedly\n\"Buffy the Vampire Slayer - Chaos Bleeds (USA)\" - Gets stuck waiting for GIF_STAT.8-9 to be non-zero\n\"Bully (USA)\" - Boots in-game, runs incredibly slow, textures broken, image looks yellow tinted (similar to GoW)\n\"Dead or Alive 2 (Japan)\" - Nothing, prints \"Read Time Out 15000(msec)\" to the terminal, possibly CDVD-related?\n\"Final Fantasy XII (USA)\" - Enables MFIFO through an 8-bit write to CTRL, uses transfer \"mode 3\" (actually just chain mode)\n\"Gallop Racer 2006 (USA)\" - Requires proper EE timings (at most 2x underclock)\n\"Klonoa 2 - Lunatea's Veil (USA)\" - Doesn't work on Release builds (!), requires 16-bit DMAC writes\n\"Namco Museum 50th Anniversary (USA)\" - Requires MFIFO\n\"R-Type Final (Japan)\" - Requires MFIFO\n\"Sega Ages 2500 Series Vol. 23 - Sega Memorial Selection (Japan)\" - GIF DMA read from NULL\n\"Sega Genesis Collection (USA)\" - Works, \"forgets\" to change the videomode causing wrong graphics at startup (needs 24-bit 640x448 interlaced?)\n\"Simpsons, The - Hit & Run (USA)\" - Needs MFIFO\n\"Tekken 4 (USA)\" - Needs MFIFO\n\"Tekken Tag Tournament (USA) (v1.00)\" - Needs MFIFO\n\"Thunder Force VI (Japan)\" - Crashes on an invalid GIF DMA address when starting up (compare against Dobie)\n\"Virtua Fighter 4 - Evolution (USA)\" - Works, uses filling mode\n\"Virtua Fighter - Cyber Generation - Judgment Six no Yabou (Japan)\" - PMTHL unimplemented\n\"We Love Katamari (USA)\" - Uses filling mode, gets stuck trying to play an FMV after the Namco logo?"
  },
  {
    "path": "frontend/arcade.hpp",
    "content": "#include <toml++/toml.hpp>\n\n#include \"ps2.h\"\n\nconst toml::table g_arcade_definitions = toml::table {\n    { \"pacmanap\", toml::table {\n        { \"system\", PS2_SYSTEM_NAMCO_S147 },\n        { \"name\", \"Pac-Man's Arcade Party\" },\n        { \"nand\", \"kp007a_k9k8g08u0b_pmaam12-na-c.ic26\" },\n        { \"bios\", \"common_system147b_bootrom.ic1\" },\n        { \"boot\", \"atfile0:PMAAC.elf\" }\n    }},\n    { \"pacmanbr\", toml::table {\n        { \"system\", PS2_SYSTEM_NAMCO_S147 },\n        { \"name\", \"Pac-Man: Battle Royale\" },\n        { \"nand\", \"pbr102-2-na-mpro-a13_kp006b.ic26\" },\n        { \"bios\", \"common_system147b_bootrom.ic1\" },\n        { \"boot\", \"atfile0:pacmanBR.elf\" }\n    }},\n    { \"akaiser\", toml::table {\n        { \"system\", PS2_SYSTEM_NAMCO_S147 },\n        { \"name\", \"Animal Kaiser: The King of Animals\" },\n        { \"nand\", \"kp005a_ana1004-na-b.ic26\" },\n        { \"bios\", \"common_system147b_bootrom.ic1\" },\n        { \"boot\", \"atfile0:main.elf\" },\n        { \"ioboard_mode\", 1 }\n    }},\n    { \"akaievo\", toml::table {\n        { \"system\", PS2_SYSTEM_NAMCO_S147 },\n        { \"name\", \"Animal Kaiser Evolution\" },\n        { \"nand\", \"kp012b_k9k8g08u0b.ic31\" },\n        { \"bios\", \"common_system147b_bootrom.ic1\" },\n        { \"boot\", \"atfile0:main.elf\" },\n        { \"ioboard_mode\", 1 }\n    }},\n    { \"umilucky\", toml::table {\n        { \"system\", PS2_SYSTEM_NAMCO_S148 },\n        { \"name\", \"Umimonogatari Lucky Marine Theater\" },\n        { \"nand\", \"uls100-1-na-mpro-b01_kp008a.ic31\" },\n        { \"bios\", \"common_system148_bootrom.ic1\" },\n        { \"boot\", \"atfile0:prog.elf\" }\n    }}\n};"
  },
  {
    "path": "frontend/audio.cpp",
    "content": "#include <chrono>\n#include <thread>\n\n#include \"iris.hpp\"\n\nnamespace iris::audio {\n\nvoid update(void* userdata, SDL_AudioStream* stream, int additional_amount, int total_amount) {\n    iris::instance* iris = (iris::instance*)userdata;\n\n    if (iris->pause)\n        std::this_thread::sleep_for(std::chrono::milliseconds(1));\n\n    if (iris->pause || !additional_amount)\n        return;\n\n    iris->audio_buf.resize(additional_amount);\n\n    for (int i = 0; i < additional_amount; i++) {\n        iris->audio_buf[i] = ps2_spu2_get_sample(iris->ps2->spu2, !iris->mute_adma);\n        iris->audio_buf[i].s16[0] *= iris->mute ? 0.0f : iris->volume;\n        iris->audio_buf[i].s16[1] *= iris->mute ? 0.0f : iris->volume;\n    }\n\n    SDL_PutAudioStreamData(stream, (void*)iris->audio_buf.data(), additional_amount * sizeof(spu2_sample));\n}\n\nbool init(iris::instance* iris) {\n    SDL_AudioSpec spec;\n\n    spec.channels = 2;\n    spec.format = SDL_AUDIO_S16;\n    spec.freq = 48000;\n\n    iris->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, iris::audio::update, iris);\n\n    if (!iris->stream) {\n        fprintf(stderr, \"audio: Failed to open audio device\\n\");\n\n        return false;\n    }\n\n    /* SDL_OpenAudioDeviceStream starts the device paused. You have to tell it to start! */\n    SDL_ResumeAudioStreamDevice(iris->stream);\n\n    return true;\n}\n\nvoid close(iris::instance* iris) {\n    if (!iris->stream) {\n        return;\n    }\n\n    SDL_PauseAudioStreamDevice(iris->stream);\n    SDL_DestroyAudioStream(iris->stream);\n\n    iris->stream = nullptr;\n}\n\nbool mute(iris::instance* iris) {\n    iris->prev_mute = iris->mute;\n\n    iris->mute = true;\n\n    return iris->prev_mute;\n}\n\nvoid unmute(iris::instance* iris) {\n    iris->mute = iris->prev_mute;\n}\n\n}"
  },
  {
    "path": "frontend/config.hpp",
    "content": "#pragma once\n\n#ifndef _IRIS_VERSION\n#define _IRIS_VERSION latest\n#endif\n\n#ifndef _IRIS_COMMIT\n#define _IRIS_COMMIT latest\n#endif\n\n#ifndef _IRIS_OSVERSION\n#define _IRIS_OSVERSION unknown\n#endif\n\n#define STR1(m) #m\n#define STR(m) STR1(m)\n\n#define IRIS_TITLE \"Iris (\" STR(_IRIS_VERSION) \")\"\n#define IRIS_VULKAN_API_VERSION VK_API_VERSION_1_2"
  },
  {
    "path": "frontend/elf.cpp",
    "content": "#include <cstdlib>\n#include <cstdint>\n#include <cstdio>\n\n#include \"iris.hpp\"\n\n#ifdef __linux__\n#include <elf.h>\n#else\n#include \"elf.h\"\n#endif\n\nnamespace iris::elf {\n\nvoid load_symbols_from_memory(iris::instance* iris, char* buf) {\n    if (!buf)\n        return;\n\n    // Clear previous symbols\n    iris->symbols.clear();\n    iris->strtab.clear();\n\n    Elf32_Ehdr* ehdr = (Elf32_Ehdr*)buf;\n\n    // Parse ELF header\n    if (strncmp((char*)ehdr->e_ident, \"\\x7f\" \"ELF\", 4) != 0) {\n        printf(\"elf: Invalid ELF magic number\\n\");\n\n        return;\n    }\n\n    // Read symbol table header\n    Elf32_Shdr* symtab = nullptr;\n\n    for (int i = 0; i < ehdr->e_shnum; i++) {\n        Elf32_Shdr* shdr = (Elf32_Shdr*)(buf + ehdr->e_shoff + (i * ehdr->e_shentsize));\n\n        if ((shdr->sh_type == SHT_STRTAB) && (i != ehdr->e_shstrndx)) {\n            printf(\"elf: Loading string table size=%x offset=%x\\n\", shdr->sh_size, shdr->sh_offset);\n\n            // Get string table\n            iris->strtab.resize(shdr->sh_size);\n\n            memcpy(iris->strtab.data(), buf + shdr->sh_offset, shdr->sh_size);\n        }\n\n        if (shdr->sh_type == SHT_SYMTAB) {\n            symtab = shdr;\n\n            printf(\"elf: Found symbol table size=%x offset=%x\\n\", symtab->sh_size, symtab->sh_offset);\n        }\n    }\n\n    // No symbol table present\n    if (!symtab) {\n        printf(\"elf: No symbol table found\\n\");\n\n        return;\n    }\n\n    if (!symtab->sh_entsize) {\n        printf(\"elf: Invalid symbol table entry size\\n\");\n\n        return;\n    }\n\n    if (!symtab->sh_size) {\n        printf(\"elf: Symbol table is empty\\n\");\n\n        return;\n    }\n\n    size_t symbol_count = symtab->sh_size / symtab->sh_entsize;\n\n    printf(\"elf: Found symbol table with %d symbols\\n\", symbol_count);\n\n    // Read symbol table\n    Elf32_Sym* sym;\n\n    for (int i = 0; i < symbol_count; i++) {\n        sym = (Elf32_Sym*)(buf + symtab->sh_offset + (i * symtab->sh_entsize));\n\n        if (ELF32_ST_TYPE(sym->st_info) != STT_FUNC)\n            continue;\n\n        elf_symbol symbol;\n\n        symbol.name = (char*)(iris->strtab.data() + sym->st_name);\n        symbol.addr = sym->st_value;\n        symbol.size = sym->st_size;\n\n        // printf(\"symbol: %s at 0x%08x\\n\", symbol.name, symbol.addr);\n\n        iris->symbols.push_back(symbol);\n    }\n}\n\nbool load_symbols_from_disc(iris::instance* iris) {\n    if (!iris->ps2 || !iris->ps2->cdvd || !iris->ps2->cdvd->disc) {\n        printf(\"elf: No disc loaded\\n\");\n\n        return false;\n    }\n\n    char* elf = disc_read_boot_elf(iris->ps2->cdvd->disc, 0);\n\n    load_symbols_from_memory(iris, elf);\n\n    free(elf);\n\n    return true;\n}\n\nbool load_symbols_from_file(iris::instance* iris, std::string path) {\n    if (path.empty()) {\n        printf(\"elf: No file path provided\\n\");\n\n        return false;\n    }\n\n    FILE* file = fopen(path.c_str(), \"rb\");\n\n    if (!file) {\n        printf(\"elf: Failed to open file %s\\n\", path.c_str());\n\n        return false;\n    }\n\n    fseek(file, 0, SEEK_END);\n    size_t size = ftell(file);\n    fseek(file, 0, SEEK_SET);\n\n    char* buf = new char[size];\n\n    fread(buf, 1, size, file);\n    fclose(file);\n\n    load_symbols_from_memory(iris, buf);\n\n    delete[] buf;\n\n    return true;\n}\n\n}"
  },
  {
    "path": "frontend/emu.cpp",
    "content": "#include \"iris.hpp\"\n#include \"arcade.hpp\"\n\n#include <filesystem>\n#include <optional>\n\nnamespace iris::emu {\n\nbool init(iris::instance* iris) {\n    // Initialize our emulator state\n    iris->ps2 = ps2_create();\n\n    ps2_init(iris->ps2);\n    ps2_init_tty_handler(iris->ps2, PS2_TTY_EE, iris::handle_ee_tty_event, iris);\n    ps2_init_tty_handler(iris->ps2, PS2_TTY_IOP, iris::handle_iop_tty_event, iris);\n    ps2_init_tty_handler(iris->ps2, PS2_TTY_SYSMEM, iris::handle_sysmem_tty_event, iris);\n\n    iris->ds[0] = ds_attach(iris->ps2->sio2, 0);\n\n    return true;\n}\n\nvoid destroy(iris::instance* iris) {\n    if (iris->ps2) ps2_destroy(iris->ps2);\n}\n\nconst char* get_extension(const char* path) {\n    const char* dot = strrchr(path, '.');\n\n    if (!dot || dot == path)\n        return nullptr;\n\n    return dot + 1;\n}\n\ntemplate <typename T> std::optional<T> query_arcade_value(std::string arcade_name, std::string key) {\n    auto it = g_arcade_definitions.find(arcade_name);\n\n    if (it == g_arcade_definitions.end())\n        return {};\n\n    auto arcade_table = it->second.as_table();\n\n    auto key_it = arcade_table->find(key);\n\n    if (key_it == arcade_table->end())\n        return {};\n\n    if constexpr (std::is_same_v<T, std::string>) {\n        return key_it->second.as_string()->get();\n    } else if constexpr (std::is_integral_v<T>) {\n        return key_it->second.as_integer()->get();\n    } else if constexpr (std::is_same_v<T, bool>) {\n        return key_it->second.as_boolean()->get();\n    } else if constexpr (std::is_floating_point_v<T>) {\n        return key_it->second.as_floating_point()->get();\n    } else if constexpr (std::is_array_v<T>) {\n        return key_it->second.as_array();\n    } else {\n        return {};\n    }\n\n    return {};\n}\n\nbool load_arcade(iris::instance* iris, std::string path) {\n    std::filesystem::path base_path(path);\n\n    std::string id = base_path.stem().string();\n    std::string name = query_arcade_value<std::string>(id, \"name\").value_or(\"\");\n\n    if (!name.size()) {\n        return false;\n    }\n\n    printf(\"emu: Loading arcade game \\\"%s\\\"...\\n\", id.c_str(), name.c_str());\n\n    int system = query_arcade_value<int>(id, \"system\").value_or(PS2_SYSTEM_AUTO);\n\n    switch (system) {\n        case PS2_SYSTEM_NAMCO_S147:\n        case PS2_SYSTEM_NAMCO_S148: {\n            std::string bios = query_arcade_value<std::string>(id, \"bios\").value_or(\"\");\n            std::string nand = query_arcade_value<std::string>(id, \"nand\").value_or(\"\");\n\n            int ioboard_mode = query_arcade_value<int>(id, \"ioboard_mode\").value_or(0);\n\n            std::filesystem::path bios_path = base_path / bios;\n            std::filesystem::path nand_path = base_path / nand;\n            std::filesystem::path sram_path = base_path / \"sram.bin\";\n\n            if (!std::filesystem::exists(bios_path)) {\n                printf(\"emu: Couldn't find bootrom file \\\"%s\\\"\\n\", bios_path.string().c_str());\n\n                push_info(iris, \"Couldn't start arcade game (Missing bootrom)\");\n\n                return false;\n            }\n\n            if (!std::filesystem::exists(nand_path)) {\n                printf(\"emu: Couldn't find NAND file \\\"%s\\\"\\n\", nand_path.string().c_str());\n\n                push_info(iris, \"Couldn't start arcade game (Missing NAND)\");\n\n                return false;\n            }\n\n            ps2_set_system(iris->ps2, system);\n            ps2_load_bios(iris->ps2, bios_path.string().c_str());\n            s14x_nand_load(iris->ps2->s14x_nand, nand_path.string().c_str());\n            s14x_sram_load(iris->ps2->s14x_sram, sram_path.string().c_str());\n\n            if (iris->ps2->s14x_ioboard) {\n                iris->ps2->s14x_ioboard->mode = ioboard_mode;\n            }\n\n            ps2_reset(iris->ps2);\n\n            iris->loaded = name + \" (\" + id + \")\";\n\n            if (iris->autostart) {\n                iris->pause = false;\n            }\n\n            return true;\n        } break;\n\n        default: {\n            const char* names[] = {\n                \"Auto\",\n                \"Retail (Fat)\",\n                \"Retail (Slim)\",\n                \"PSX DESR\",\n                \"TEST unit (DTL-H)\",\n                \"TOOL unit (DTL-T)\",\n                \"Konami Python\",\n                \"Konami Python 2\",\n                \"Namco System 147\",\n                \"Namco System 148\",\n                \"Namco System 246\",\n                \"Namco System 256\"\n            };\n\n            printf(\"emu: %s isn't supported yet\\n\", names[system]);\n        } break;\n    }\n\n    return false;\n}\n\nint attach_memory_card(iris::instance* iris, int slot, const char* path) {\n    detach_memory_card(iris, slot);\n\n    FILE* file = fopen(path, \"rb\");\n\n    if (!file) {\n        return 0;\n    }\n\n    fseek(file, 0, SEEK_END);\n    int size = ftell(file);\n    fclose(file);\n\n    if (size < 0x800000) {\n        struct ps1_mcd_state* mcd = ps1_mcd_attach(iris->ps2->sio2, slot+2, path);\n\n        std::string ext = get_extension(path);\n\n        if (ext == \"psm\" || ext == \"pocket\") {\n            ps1_mcd_set_type(mcd, 1);\n\n            iris->mcd_slot_type[slot] = 3;\n        } else {\n            ps1_mcd_set_type(mcd, 0);\n\n            iris->mcd_slot_type[slot] = 2;\n        }\n\n        return 1;\n    }\n\n    mcd_attach(iris->ps2->sio2, slot+2, path);\n\n    iris->mcd_slot_type[slot] = 1;\n\n    return 1;\n}\n\nvoid detach_memory_card(iris::instance* iris, int slot) {\n    iris->mcd_slot_type[slot] = 0;\n\n    ps2_sio2_detach_device(iris->ps2->sio2, slot+2);\n}\n\nconst char* g_system_names[] = {\n    \"Auto\",\n    \"PlayStation 2 (Fat)\",\n    \"PlayStation 2 (Slim)\",\n    \"PSX DESR\",\n    \"TEST Unit\",\n    \"TOOL Unit\",\n    \"Konami Python\",\n    \"Konami Python 2\",\n    \"Namco System 147\",\n    \"Namco System 148\",\n    \"Namco System 246\",\n    \"Namco System 256\"\n};\n\nconst char* get_system_name(iris::instance* iris, int system) {\n    return g_system_names[system];\n}\n\nconst char* get_current_system_name(iris::instance* iris) {\n    switch (iris->system) {\n        case PS2_SYSTEM_AUTO: return get_system_name(iris, iris->ps2->detected_system);\n        case PS2_SYSTEM_RETAIL:\n        case PS2_SYSTEM_RETAIL_DECKARD:\n        case PS2_SYSTEM_DESR:\n        case PS2_SYSTEM_TEST:\n        case PS2_SYSTEM_TOOL:\n        case PS2_SYSTEM_KONAMI_PYTHON:\n        case PS2_SYSTEM_KONAMI_PYTHON2:\n        case PS2_SYSTEM_NAMCO_S147:\n        case PS2_SYSTEM_NAMCO_S148:\n        case PS2_SYSTEM_NAMCO_S246:\n        case PS2_SYSTEM_NAMCO_S256:\n            return g_system_names[iris->system];\n        default: return \"Unknown\";\n    }\n}\n\nint get_system_count(iris::instance* iris) {\n    return sizeof(g_system_names) / sizeof(const char*);\n}\n\n}"
  },
  {
    "path": "frontend/handlers.cpp",
    "content": "#include \"iris.hpp\"\n\nnamespace iris {\n\nvoid handle_ee_tty_event(void* udata, char c) {\n    iris::instance* iris = (iris::instance*)udata;\n\n    if (c == '\\r')\n        return;\n\n    if (c == '\\n') {\n        iris->ee_log.push_back(\"\");\n    } else {\n        iris->ee_log.back().push_back(c);\n    }\n}\n\nvoid handle_iop_tty_event(void* udata, char c) {\n    iris::instance* iris = (iris::instance*)udata;\n\n    if (c == '\\r')\n        return;\n\n    if (c == '\\n') {\n        iris->iop_log.push_back(\"\");\n    } else {\n        iris->iop_log.back().push_back(c);\n    }\n}\n\nvoid handle_sysmem_tty_event(void* udata, char c) {\n    iris::instance* iris = (iris::instance*)udata;\n\n    if (c == '\\r')\n        return;\n\n    if (c == '\\n') {\n        iris->sysmem_log.push_back(\"\");\n    } else {\n        iris->sysmem_log.back().push_back(c);\n    }\n}\n\n}"
  },
  {
    "path": "frontend/imgui.cpp",
    "content": "#include \"iris.hpp\"\n\n#include \"imgui.h\"\n#include \"imgui_impl_sdl3.h\"\n#include \"imgui_impl_vulkan.h\"\n#include \"implot.h\"\n\n#include <SDL3/SDL.h>\n#include <SDL3/SDL_vulkan.h>\n\n#include <array>\n\n// External includes\n#include \"res/IconsMaterialSymbols.h\"\n\n// INCBIN stuff\n#define INCBIN_PREFIX g_\n#define INCBIN_STYLE INCBIN_STYLE_SNAKE\n\n#include \"incbin.h\"\n\nINCBIN(roboto, \"../res/Roboto-Regular.ttf\");\nINCBIN(roboto_black, \"../res/Roboto-Black.ttf\");\nINCBIN(symbols, \"../res/MaterialSymbolsRounded.ttf\");\nINCBIN(firacode, \"../res/FiraCode-Regular.ttf\");\nINCBIN(ps1_memory_card_icon, \"../res/ps1_mcd.png\");\nINCBIN(ps2_memory_card_icon, \"../res/ps2_mcd.png\");\nINCBIN(dualshock2_icon, \"../res/ds2.png\");\nINCBIN(pocketstation_icon, \"../res/pocketstation.png\");\nINCBIN(iris_icon, \"../res/iris.png\");\nINCBIN(vertex_shader, \"../shaders/vertex.spv\");\nINCBIN(fragment_shader, \"../shaders/fragment.spv\");\n\n#include \"stb_image.h\"\n\n#define VOLK_IMPLEMENTATION\n#include <volk.h>\n\nnamespace iris::imgui {\n\nstatic constexpr uint32_t DESCRIPTOR_SET_RING_SIZE = 8;\n\nstatic const ImWchar g_icon_range[] = { ICON_MIN_MS, ICON_MAX_16_MS, 0 };\n\nstatic bool setup_vulkan_window(iris::instance* iris, ImGui_ImplVulkanH_Window* wd, int width, int height, bool vsync) {\n    wd->Surface = iris->surface;\n\n    VkAttachmentDescription attachment = {};\n    attachment.format = wd->SurfaceFormat.format;\n    attachment.samples = VK_SAMPLE_COUNT_1_BIT;\n    attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD;\n    attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n    attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n    attachment.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;\n    attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR;\n\n    wd->AttachmentDesc = attachment;\n\n    // Check for WSI support\n    VkBool32 res;\n\n    vkGetPhysicalDeviceSurfaceSupportKHR(iris->physical_device, iris->queue_family, wd->Surface, &res);\n\n    if (!res) {\n        fprintf(stderr, \"imgui: No WSI support on physical device\\n\");\n        \n        return false;\n    }\n\n    // Select Surface Format\n    const VkFormat requestSurfaceImageFormat[] = {\n        VK_FORMAT_B8G8R8A8_UNORM,\n        VK_FORMAT_R8G8B8A8_UNORM,\n        VK_FORMAT_B8G8R8_UNORM,\n        VK_FORMAT_R8G8B8_UNORM\n    };\n\n    const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR;\n\n    wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat(\n        iris->physical_device,\n        wd->Surface,\n        requestSurfaceImageFormat,\n        (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat),\n        requestSurfaceColorSpace\n    );\n\n    // Select Present Mode\n    std::vector <VkPresentModeKHR> present_modes;\n\n    if (vsync) {\n        present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);\n    } else {\n        present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);\n        present_modes.push_back(VK_PRESENT_MODE_IMMEDIATE_KHR);\n        present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);\n    }\n \n    wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode(\n        iris->physical_device,\n        wd->Surface,\n        present_modes.data(),\n        present_modes.size()\n    );\n\n    // Create SwapChain, RenderPass, Framebuffer, etc.\n    IM_ASSERT(iris->min_image_count >= 2);\n\n    ImGui_ImplVulkanH_CreateOrResizeWindow(\n        iris->instance,\n        iris->physical_device,\n        iris->device,\n        wd,\n        iris->queue_family,\n        VK_NULL_HANDLE,\n        width, height,\n        iris->min_image_count,\n        0\n    );\n\n    return true;\n}\n\nvoid set_vsync(iris::instance* iris, bool vsync) {\n    std::vector <VkPresentModeKHR> present_modes;\n\n    if (vsync) {\n        present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);\n    } else {\n        present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR);\n        present_modes.push_back(VK_PRESENT_MODE_IMMEDIATE_KHR);\n        present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR);\n    }\n \n    iris->main_window_data.PresentMode = ImGui_ImplVulkanH_SelectPresentMode(\n        iris->physical_device,\n        iris->main_window_data.Surface,\n        present_modes.data(),\n        present_modes.size()\n    );\n\n    render::refresh(iris);\n}\n\nbool setup_fonts(iris::instance* iris, ImGuiIO& io) {\n    io.Fonts->AddFontDefault();\n\n    ImFontConfig config;\n    config.MergeMode = true;\n    config.GlyphMinAdvanceX = 13.0f;\n    config.GlyphOffset = ImVec2(0.0f, 4.0f);\n    config.FontDataOwnedByAtlas = false;\n\n    ImFontConfig config_no_own;\n    config_no_own.FontDataOwnedByAtlas = false;\n\n    iris->font_small_code = io.Fonts->AddFontFromMemoryTTF((void*)g_firacode_data, g_firacode_size, 12.0F, &config_no_own);\n    iris->font_code       = io.Fonts->AddFontFromMemoryTTF((void*)g_firacode_data, g_firacode_size, 16.0F, &config_no_own);\n    iris->font_small      = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_data, g_roboto_size, 12.0F, &config_no_own);\n    iris->font_heading    = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_data, g_roboto_size, 20.0F, &config_no_own);\n    iris->font_body       = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_data, g_roboto_size, 16.0F, &config_no_own);\n    iris->font_icons      = io.Fonts->AddFontFromMemoryTTF((void*)g_symbols_data, g_symbols_size, 20.0F, &config, g_icon_range);\n    iris->font_icons_big  = io.Fonts->AddFontFromMemoryTTF((void*)g_symbols_data, g_symbols_size, 50.0F, &config_no_own, g_icon_range);\n    iris->font_black      = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_black_data, g_roboto_black_size, 30.0F, &config_no_own);\n\n    if (!iris->font_small_code ||\n        !iris->font_code ||\n        !iris->font_small ||\n        !iris->font_heading ||\n        !iris->font_body ||\n        !iris->font_icons ||\n        !iris->font_icons_big ||\n        !iris->font_black) {\n        return false;\n    }\n\n    io.FontDefault = iris->font_icons;\n\n    return true;\n}\n\nvoid set_theme(iris::instance* iris, int theme, bool set_bg_color) {\n    // Init 'Granite' theme\n    ImGuiStyle& style = ImGui::GetStyle();\n    style.WindowPadding           = ImVec2(8.0, 8.0);\n    style.FramePadding            = ImVec2(5.0, 5.0);\n    style.ItemSpacing             = ImVec2(8.0, 6.0);\n    style.WindowBorderSize        = 0;\n    style.ChildBorderSize         = 0;\n    style.FrameBorderSize         = 1;\n    style.PopupBorderSize         = 0;\n    style.TabBorderSize           = 0;\n    style.TabBarBorderSize        = 0;\n    style.WindowRounding          = 6;\n    style.ChildRounding           = 4;\n    style.FrameRounding           = 4;\n    style.PopupRounding           = 4;\n    style.ScrollbarRounding       = 9;\n    style.GrabRounding            = 2;\n    style.TabRounding             = 4;\n    style.WindowTitleAlign        = ImVec2(0.5, 0.5);\n    style.DockingSeparatorSize    = 0;\n    style.SeparatorTextBorderSize = 1;\n    style.SeparatorTextPadding    = ImVec2(20, 0);\n\n    // Use ImGui's default dark style as a base for our own style\n    ImGui::StyleColorsDark();\n\n    switch (theme) {\n        case IRIS_THEME_GRANITE: {\n            ImVec4* colors = style.Colors;\n\n            colors[ImGuiCol_Text]                   = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n            colors[ImGuiCol_TextDisabled]           = ImVec4(0.35f, 0.35f, 0.35f, 1.00f);\n            colors[ImGuiCol_WindowBg]               = ImVec4(0.02f, 0.02f, 0.02f, 1.00f);\n            colors[ImGuiCol_ChildBg]                = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n            colors[ImGuiCol_PopupBg]                = ImVec4(0.07f, 0.09f, 0.10f, 1.00f);\n            colors[ImGuiCol_Border]                 = ImVec4(0.10f, 0.12f, 0.13f, 1.00f);\n            colors[ImGuiCol_BorderShadow]           = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n            colors[ImGuiCol_FrameBg]                = ImVec4(0.10f, 0.12f, 0.13f, 0.50f);\n            colors[ImGuiCol_FrameBgHovered]         = ImVec4(0.20f, 0.24f, 0.26f, 0.50f);\n            colors[ImGuiCol_FrameBgActive]          = ImVec4(0.29f, 0.35f, 0.39f, 0.50f);\n            colors[ImGuiCol_TitleBg]                = ImVec4(0.04f, 0.04f, 0.04f, 1.00f);\n            colors[ImGuiCol_TitleBgActive]          = ImVec4(0.16f, 0.20f, 0.22f, 1.00f);\n            colors[ImGuiCol_TitleBgCollapsed]       = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);\n            colors[ImGuiCol_MenuBarBg]              = ImVec4(0.14f, 0.14f, 0.14f, 1.00f);\n            colors[ImGuiCol_ScrollbarBg]            = ImVec4(0.02f, 0.02f, 0.02f, 0.53f);\n            colors[ImGuiCol_ScrollbarGrab]          = ImVec4(0.31f, 0.31f, 0.31f, 1.00f);\n            colors[ImGuiCol_ScrollbarGrabHovered]   = ImVec4(0.41f, 0.41f, 0.41f, 1.00f);\n            colors[ImGuiCol_ScrollbarGrabActive]    = ImVec4(0.51f, 0.51f, 0.51f, 1.00f);\n            colors[ImGuiCol_CheckMark]              = ImVec4(0.88f, 0.88f, 0.88f, 1.00f);\n            colors[ImGuiCol_SliderGrab]             = ImVec4(0.39f, 0.47f, 0.52f, 0.50f);\n            colors[ImGuiCol_SliderGrabActive]       = ImVec4(0.49f, 0.59f, 0.65f, 0.50f);\n            colors[ImGuiCol_Button]                 = ImVec4(0.13f, 0.16f, 0.17f, 0.25f);\n            colors[ImGuiCol_ButtonHovered]          = ImVec4(0.20f, 0.24f, 0.26f, 0.50f);\n            colors[ImGuiCol_ButtonActive]           = ImVec4(0.29f, 0.35f, 0.39f, 0.50f);\n            colors[ImGuiCol_Header]                 = ImVec4(0.13f, 0.16f, 0.17f, 0.50f);\n            colors[ImGuiCol_HeaderHovered]          = ImVec4(0.20f, 0.24f, 0.26f, 0.50f);\n            colors[ImGuiCol_HeaderActive]           = ImVec4(0.29f, 0.35f, 0.39f, 0.50f);\n            colors[ImGuiCol_Separator]              = ImVec4(0.23f, 0.28f, 0.30f, 1.00f);\n            colors[ImGuiCol_SeparatorHovered]       = ImVec4(0.33f, 0.39f, 0.43f, 1.00f);\n            colors[ImGuiCol_SeparatorActive]        = ImVec4(0.38f, 0.46f, 0.51f, 1.00f);\n            colors[ImGuiCol_ResizeGrip]             = ImVec4(0.15f, 0.20f, 0.22f, 1.00f);\n            colors[ImGuiCol_ResizeGripHovered]      = ImVec4(0.00f, 0.30f, 0.25f, 1.00f);\n            colors[ImGuiCol_ResizeGripActive]       = ImVec4(0.00f, 0.39f, 0.32f, 1.00f);\n            colors[ImGuiCol_InputTextCursor]        = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n            colors[ImGuiCol_TabHovered]             = ImVec4(0.23f, 0.28f, 0.30f, 0.59f);\n            colors[ImGuiCol_Tab]                    = ImVec4(0.20f, 0.24f, 0.26f, 0.59f);\n            colors[ImGuiCol_TabSelected]            = ImVec4(0.26f, 0.31f, 0.35f, 0.59f);\n            colors[ImGuiCol_TabSelectedOverline]    = ImVec4(0.00f, 0.39f, 0.32f, 1.00f);\n            colors[ImGuiCol_TabDimmed]              = ImVec4(0.07f, 0.10f, 0.15f, 0.97f);\n            colors[ImGuiCol_TabDimmedSelected]      = ImVec4(0.10f, 0.12f, 0.13f, 1.00f);\n            colors[ImGuiCol_TabDimmedSelectedOverline]  = ImVec4(0.50f, 0.50f, 0.50f, 0.00f);\n            colors[ImGuiCol_DockingPreview]         = ImVec4(0.15f, 0.20f, 0.22f, 1.00f);\n            colors[ImGuiCol_DockingEmptyBg]         = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);\n            colors[ImGuiCol_PlotLines]              = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);\n            colors[ImGuiCol_PlotLinesHovered]       = ImVec4(1.00f, 0.43f, 0.35f, 1.00f);\n            colors[ImGuiCol_PlotHistogram]          = ImVec4(0.90f, 0.70f, 0.00f, 1.00f);\n            colors[ImGuiCol_PlotHistogramHovered]   = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);\n            colors[ImGuiCol_TableHeaderBg]          = ImVec4(0.19f, 0.19f, 0.20f, 1.00f);\n            colors[ImGuiCol_TableBorderStrong]      = ImVec4(0.31f, 0.31f, 0.35f, 1.00f);\n            colors[ImGuiCol_TableBorderLight]       = ImVec4(0.23f, 0.23f, 0.25f, 1.00f);\n            colors[ImGuiCol_TableRowBg]             = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n            colors[ImGuiCol_TableRowBgAlt]          = ImVec4(1.00f, 1.00f, 1.00f, 0.06f);\n            colors[ImGuiCol_TextLink]               = ImVec4(0.26f, 0.59f, 0.98f, 1.00f);\n            colors[ImGuiCol_TextSelectedBg]         = ImVec4(0.15f, 0.20f, 0.22f, 1.00f);\n            colors[ImGuiCol_DragDropTarget]         = ImVec4(0.29f, 0.38f, 0.42f, 1.00f);\n            colors[ImGuiCol_NavCursor]              = ImVec4(0.15f, 0.20f, 0.22f, 1.00f);\n            colors[ImGuiCol_NavWindowingHighlight]  = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);\n            colors[ImGuiCol_NavWindowingDimBg]      = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);\n            colors[ImGuiCol_ModalWindowDimBg]       = ImVec4(0.00f, 0.00f, 0.00f, 0.35f);\n\n            if (!set_bg_color) break;\n\n            iris->clear_value.color.float32[0] = 0.11f;\n            iris->clear_value.color.float32[1] = 0.11f;\n            iris->clear_value.color.float32[2] = 0.11f;\n            iris->clear_value.color.float32[3] = 1.00f;\n        } break;\n\n        case IRIS_THEME_IMGUI_DARK: {\n            ImGui::StyleColorsDark();\n\n            if (!set_bg_color) break;\n\n            iris->clear_value.color.float32[0] = 0.11f;\n            iris->clear_value.color.float32[1] = 0.11f;\n            iris->clear_value.color.float32[2] = 0.11f;\n            iris->clear_value.color.float32[3] = 1.00f;\n        } break;\n\n        case IRIS_THEME_IMGUI_LIGHT: {\n            ImGui::StyleColorsLight();\n\n            if (!set_bg_color) break;\n\n            iris->clear_value.color.float32[0] = 0.89f;\n            iris->clear_value.color.float32[1] = 0.89f;\n            iris->clear_value.color.float32[2] = 0.89f;\n            iris->clear_value.color.float32[3] = 1.00f;\n        } break;\n\n        case IRIS_THEME_IMGUI_CLASSIC: {\n            ImGui::StyleColorsClassic();\n\n            if (!set_bg_color) break;\n\n            iris->clear_value.color.float32[0] = 0.11f;\n            iris->clear_value.color.float32[1] = 0.11f;\n            iris->clear_value.color.float32[2] = 0.11f;\n            iris->clear_value.color.float32[3] = 1.00f;\n        } break;\n\n        case IRIS_THEME_CHERRY: {\n            // cherry colors, 3 intensities\n            #define HI(v)   ImVec4(0.502f, 0.075f, 0.256f, v)\n            #define MED(v)  ImVec4(0.455f, 0.198f, 0.301f, v)\n            #define LOW(v)  ImVec4(0.232f, 0.201f, 0.271f, v)\n            // backgrounds\n            #define BG(v)   ImVec4(0.200f, 0.220f, 0.270f, v)\n            // text\n            #define TEXT(v) ImVec4(0.860f, 0.930f, 0.890f, v)\n\n            auto &style = ImGui::GetStyle();\n            style.Colors[ImGuiCol_Text]                  = TEXT(0.78f);\n            style.Colors[ImGuiCol_TextDisabled]          = TEXT(0.28f);\n            style.Colors[ImGuiCol_WindowBg]              = ImVec4(0.13f, 0.14f, 0.17f, 1.00f);\n            style.Colors[ImGuiCol_ChildBg]               = BG( 0.58f);\n            style.Colors[ImGuiCol_PopupBg]               = BG( 0.9f);\n            style.Colors[ImGuiCol_Border]                = ImVec4(0.31f, 0.31f, 1.00f, 0.00f);\n            style.Colors[ImGuiCol_BorderShadow]          = ImVec4(0.00f, 0.00f, 0.00f, 0.00f);\n            style.Colors[ImGuiCol_FrameBg]               = BG( 1.00f);\n            style.Colors[ImGuiCol_FrameBgHovered]        = MED( 0.78f);\n            style.Colors[ImGuiCol_FrameBgActive]         = MED( 1.00f);\n            style.Colors[ImGuiCol_TitleBg]               = LOW( 1.00f);\n            style.Colors[ImGuiCol_TitleBgActive]         = HI( 1.00f);\n            style.Colors[ImGuiCol_TitleBgCollapsed]      = BG( 0.75f);\n            style.Colors[ImGuiCol_MenuBarBg]             = BG( 0.47f);\n            style.Colors[ImGuiCol_ScrollbarBg]           = BG( 1.00f);\n            style.Colors[ImGuiCol_ScrollbarGrab]         = ImVec4(0.09f, 0.15f, 0.16f, 1.00f);\n            style.Colors[ImGuiCol_ScrollbarGrabHovered]  = MED( 0.78f);\n            style.Colors[ImGuiCol_ScrollbarGrabActive]   = MED( 1.00f);\n            style.Colors[ImGuiCol_CheckMark]             = ImVec4(0.71f, 0.22f, 0.27f, 1.00f);\n            style.Colors[ImGuiCol_SliderGrab]            = ImVec4(0.47f, 0.77f, 0.83f, 0.14f);\n            style.Colors[ImGuiCol_SliderGrabActive]      = ImVec4(0.71f, 0.22f, 0.27f, 1.00f);\n            style.Colors[ImGuiCol_Button]                = ImVec4(0.47f, 0.77f, 0.83f, 0.14f);\n            style.Colors[ImGuiCol_ButtonHovered]         = MED( 0.86f);\n            style.Colors[ImGuiCol_ButtonActive]          = MED( 1.00f);\n            style.Colors[ImGuiCol_Header]                = MED( 0.76f);\n            style.Colors[ImGuiCol_HeaderHovered]         = MED( 0.86f);\n            style.Colors[ImGuiCol_HeaderActive]          = HI( 1.00f);\n            style.Colors[ImGuiCol_ResizeGrip]            = ImVec4(0.47f, 0.77f, 0.83f, 0.04f);\n            style.Colors[ImGuiCol_ResizeGripHovered]     = MED( 0.78f);\n            style.Colors[ImGuiCol_ResizeGripActive]      = MED( 1.00f);\n            style.Colors[ImGuiCol_PlotLines]             = TEXT(0.63f);\n            style.Colors[ImGuiCol_PlotLinesHovered]      = MED( 1.00f);\n            style.Colors[ImGuiCol_PlotHistogram]         = TEXT(0.63f);\n            style.Colors[ImGuiCol_PlotHistogramHovered]  = MED( 1.00f);\n            style.Colors[ImGuiCol_TextSelectedBg]        = MED( 0.43f);\n            style.Colors[ImGuiCol_ModalWindowDimBg]      = BG( 0.73f);\n\n            #undef HI\n            #undef MED\n            #undef LOW\n            #undef BG\n            #undef TEXT\n\n            if (!set_bg_color) break;\n\n            iris->clear_value.color.float32[0] = 0.20f * 0.5f;\n            iris->clear_value.color.float32[1] = 0.22f * 0.5f;\n            iris->clear_value.color.float32[2] = 0.27f * 0.5f;\n            iris->clear_value.color.float32[3] = 1.00f;\n        } break;\n\n        case IRIS_THEME_SOURCE: {\n            ImVec4* colors = ImGui::GetStyle().Colors;\n\n            colors[ImGuiCol_Text]                  = ImVec4(1.00f, 1.00f, 1.00f, 1.00f);\n            colors[ImGuiCol_TextDisabled]          = ImVec4(0.50f, 0.50f, 0.50f, 1.00f);\n            colors[ImGuiCol_WindowBg]              = ImVec4(0.29f, 0.34f, 0.26f, 1.00f);\n            colors[ImGuiCol_ChildBg]               = ImVec4(0.29f, 0.34f, 0.26f, 1.00f);\n            colors[ImGuiCol_PopupBg]               = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);\n            colors[ImGuiCol_Border]                = ImVec4(0.54f, 0.57f, 0.51f, 0.50f);\n            colors[ImGuiCol_BorderShadow]          = ImVec4(0.14f, 0.16f, 0.11f, 0.52f);\n            colors[ImGuiCol_FrameBg]               = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);\n            colors[ImGuiCol_FrameBgHovered]        = ImVec4(0.27f, 0.30f, 0.23f, 1.00f);\n            colors[ImGuiCol_FrameBgActive]         = ImVec4(0.30f, 0.34f, 0.26f, 1.00f);\n            colors[ImGuiCol_TitleBg]               = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);\n            colors[ImGuiCol_TitleBgActive]         = ImVec4(0.29f, 0.34f, 0.26f, 1.00f);\n            colors[ImGuiCol_TitleBgCollapsed]      = ImVec4(0.00f, 0.00f, 0.00f, 0.51f);\n            colors[ImGuiCol_MenuBarBg]             = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);\n            colors[ImGuiCol_ScrollbarBg]           = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);\n            colors[ImGuiCol_ScrollbarGrab]         = ImVec4(0.28f, 0.32f, 0.24f, 1.00f);\n            colors[ImGuiCol_ScrollbarGrabHovered]  = ImVec4(0.25f, 0.30f, 0.22f, 1.00f);\n            colors[ImGuiCol_ScrollbarGrabActive]   = ImVec4(0.23f, 0.27f, 0.21f, 1.00f);\n            colors[ImGuiCol_CheckMark]             = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);\n            colors[ImGuiCol_SliderGrab]            = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);\n            colors[ImGuiCol_SliderGrabActive]      = ImVec4(0.54f, 0.57f, 0.51f, 0.50f);\n            colors[ImGuiCol_Button]                = ImVec4(0.29f, 0.34f, 0.26f, 0.40f);\n            colors[ImGuiCol_ButtonHovered]         = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);\n            colors[ImGuiCol_ButtonActive]          = ImVec4(0.54f, 0.57f, 0.51f, 0.50f);\n            colors[ImGuiCol_Header]                = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);\n            colors[ImGuiCol_HeaderHovered]         = ImVec4(0.35f, 0.42f, 0.31f, 0.60f);\n            colors[ImGuiCol_HeaderActive]          = ImVec4(0.54f, 0.57f, 0.51f, 0.50f);\n            colors[ImGuiCol_Separator]             = ImVec4(0.14f, 0.16f, 0.11f, 1.00f);\n            colors[ImGuiCol_SeparatorHovered]      = ImVec4(0.54f, 0.57f, 0.51f, 1.00f);\n            colors[ImGuiCol_SeparatorActive]       = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);\n            colors[ImGuiCol_ResizeGrip]            = ImVec4(0.19f, 0.23f, 0.18f, 0.00f);\n            colors[ImGuiCol_ResizeGripHovered]     = ImVec4(0.54f, 0.57f, 0.51f, 1.00f);\n            colors[ImGuiCol_ResizeGripActive]      = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);\n            colors[ImGuiCol_Tab]                   = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);\n            colors[ImGuiCol_TabHovered]            = ImVec4(0.54f, 0.57f, 0.51f, 0.78f);\n            colors[ImGuiCol_TabActive]             = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);\n            colors[ImGuiCol_TabUnfocused]          = ImVec4(0.24f, 0.27f, 0.20f, 1.00f);\n            colors[ImGuiCol_TabUnfocusedActive]    = ImVec4(0.35f, 0.42f, 0.31f, 1.00f);\n            colors[ImGuiCol_DockingPreview]        = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);\n            colors[ImGuiCol_DockingEmptyBg]        = ImVec4(0.20f, 0.20f, 0.20f, 1.00f);\n            colors[ImGuiCol_PlotLines]             = ImVec4(0.61f, 0.61f, 0.61f, 1.00f);\n            colors[ImGuiCol_PlotLinesHovered]      = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);\n            colors[ImGuiCol_PlotHistogram]         = ImVec4(1.00f, 0.78f, 0.28f, 1.00f);\n            colors[ImGuiCol_PlotHistogramHovered]  = ImVec4(1.00f, 0.60f, 0.00f, 1.00f);\n            colors[ImGuiCol_TextSelectedBg]        = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);\n            colors[ImGuiCol_DragDropTarget]        = ImVec4(0.73f, 0.67f, 0.24f, 1.00f);\n            colors[ImGuiCol_NavHighlight]          = ImVec4(0.59f, 0.54f, 0.18f, 1.00f);\n            colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f);\n            colors[ImGuiCol_NavWindowingDimBg]     = ImVec4(0.80f, 0.80f, 0.80f, 0.20f);\n            colors[ImGuiCol_ModalWindowDimBg]      = ImVec4(0.80f, 0.80f, 0.80f, 0.35f);\n\n            if (!set_bg_color) break;\n\n            iris->clear_value.color.float32[0] = 0.13f;\n            iris->clear_value.color.float32[1] = 0.15f;\n            iris->clear_value.color.float32[2] = 0.11f;\n            iris->clear_value.color.float32[3] = 1.00f;\n        } break;\n    }\n\n    ImPlotStyle& pstyle = ImPlot::GetStyle();\n\n    pstyle.MinorGridSize = ImVec2(0.0f, 0.0f);\n    pstyle.MajorGridSize = ImVec2(0.0f, 0.0f);\n    pstyle.MinorTickLen = ImVec2(0.0f, 0.0f);\n    pstyle.MajorTickLen = ImVec2(0.0f, 0.0f);\n    pstyle.PlotDefaultSize = ImVec2(250.0f, 150.0f);\n    pstyle.PlotPadding = ImVec2(0.0f, 0.0f);\n    pstyle.LegendPadding = ImVec2(0.0f, 0.0f);\n    pstyle.LegendInnerPadding = ImVec2(0.0f, 0.0f);\n    pstyle.LineWeight = 2.0f;\n\n    pstyle.Colors[ImPlotCol_Line]       = ImVec4(0.0f, 1.0f, 0.2f, 1.0f);\n    pstyle.Colors[ImPlotCol_FrameBg]    = ImVec4(0.0f, 0.0f, 0.0f, 0.0f);\n    pstyle.Colors[ImPlotCol_PlotBg]     = ImVec4(0.0f, 0.0f, 0.0f, 0.0f);\n}\n\nvoid set_codeview_scheme(iris::instance* iris, int scheme) {\n    switch (scheme) {\n        default: case IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_DARK: {\n            iris->codeview_color_text = IM_COL32(131, 148, 150, 255);\n            iris->codeview_color_comment = IM_COL32(88, 110, 117, 255);\n            iris->codeview_color_mnemonic = IM_COL32(211, 167, 30, 255);\n            iris->codeview_color_number = IM_COL32(138, 143, 226, 255);\n            iris->codeview_color_register = IM_COL32(68, 169, 240, 255);\n            iris->codeview_color_other = IM_COL32(89, 89, 89, 255);\n            iris->codeview_color_background = IM_COL32(0, 43, 54, 255);\n            iris->codeview_color_highlight = IM_COL32(7, 54, 66, 255);\n        } break;\n\n        case IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_LIGHT: {\n            iris->codeview_color_text = IM_COL32(101, 123, 131, 255);\n            iris->codeview_color_comment = IM_COL32(147, 161, 161, 255);\n            iris->codeview_color_mnemonic = IM_COL32(147, 101, 21, 255);\n            iris->codeview_color_number = IM_COL32(101, 123, 179, 255);\n            iris->codeview_color_register = IM_COL32(38, 139, 210, 255);\n            iris->codeview_color_other = IM_COL32(88, 110, 117, 255);\n            iris->codeview_color_background = IM_COL32(253, 246, 227, 255);\n            iris->codeview_color_highlight = IM_COL32(238, 232, 213, 255);\n        } break;\n\n        case IRIS_CODEVIEW_COLOR_SCHEME_ONE_DARK_PRO: {\n            iris->codeview_color_text = IM_COL32(171, 178, 191, 255);\n            iris->codeview_color_comment = IM_COL32(92, 99, 112, 255);\n            iris->codeview_color_mnemonic = IM_COL32(198, 120, 221, 255);\n            iris->codeview_color_number = IM_COL32(209, 154, 102, 255);\n            iris->codeview_color_register = IM_COL32(97, 175, 239, 255);\n            iris->codeview_color_other = IM_COL32(171, 178, 191, 255);\n            iris->codeview_color_background = IM_COL32(40, 44, 52, 255);\n            iris->codeview_color_highlight = IM_COL32(60, 64, 72, 255);\n        } break;\n\n        case IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_LATTE: {\n            iris->codeview_color_text = IM_COL32(76, 79, 105, 255);\n            iris->codeview_color_comment = IM_COL32(124, 127, 147, 255);\n            iris->codeview_color_mnemonic = IM_COL32(136, 57, 239, 255);\n            iris->codeview_color_number = IM_COL32(254, 100, 11, 255);\n            iris->codeview_color_register = IM_COL32(4, 165, 229, 255);\n            iris->codeview_color_other = IM_COL32(114, 135, 253, 255);\n            iris->codeview_color_background = IM_COL32(239, 241, 245, 255);\n            iris->codeview_color_highlight = IM_COL32(204, 208, 218, 255);\n        } break;\n\n        case IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_FRAPPE: {\n            iris->codeview_color_text = IM_COL32(198, 208, 245, 255);\n            iris->codeview_color_comment = IM_COL32(148, 156, 187, 255);\n            iris->codeview_color_mnemonic = IM_COL32(202, 158, 230, 255);\n            iris->codeview_color_number = IM_COL32(239, 159, 118, 255);\n            iris->codeview_color_register = IM_COL32(153, 209, 219, 255);\n            iris->codeview_color_other = IM_COL32(186, 187, 241, 255);\n            iris->codeview_color_background = IM_COL32(48, 52, 70, 255);\n            iris->codeview_color_highlight = IM_COL32(81, 87, 109, 255);\n        } break;\n\n        case IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MACCHIATO: {\n            iris->codeview_color_text = IM_COL32(174, 178, 208, 255);\n            iris->codeview_color_comment = IM_COL32(134, 138, 162, 255);\n            iris->codeview_color_mnemonic = IM_COL32(190, 132, 255, 255);\n            iris->codeview_color_number = IM_COL32(245, 142, 110, 255);\n            iris->codeview_color_register = IM_COL32(125, 182, 191, 255);\n            iris->codeview_color_other = IM_COL32(166, 167, 222, 255);\n            iris->codeview_color_background = IM_COL32(58, 60, 79, 255);\n            iris->codeview_color_highlight = IM_COL32(97, 100, 120, 255);\n        } break;\n\n        case IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MOCHA: {\n            iris->codeview_color_text = IM_COL32(205, 214, 244, 255);\n            iris->codeview_color_comment = IM_COL32(145, 151, 181, 255);\n            iris->codeview_color_mnemonic = IM_COL32(220, 162, 255, 255);\n            iris->codeview_color_number = IM_COL32(248, 159, 128, 255);\n            iris->codeview_color_register = IM_COL32(159, 226, 235, 255);\n            iris->codeview_color_other = IM_COL32(189, 191, 248, 255);\n            iris->codeview_color_background = IM_COL32(46, 49, 64, 255);\n            iris->codeview_color_highlight = IM_COL32(76, 80, 100, 255);\n        } break;\n    }\n}\n\nVkShaderModule create_shader(iris::instance* iris, uint32_t* code, size_t size) {\n    VkShaderModuleCreateInfo info = {};\n    info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;\n    info.pCode = code;\n    info.codeSize = size;\n\n    VkShaderModule shader;\n\n    if (vkCreateShaderModule(iris->device, &info, nullptr, &shader) != VK_SUCCESS) {\n        return VK_NULL_HANDLE;\n    }\n\n    return shader;\n}\n\nVkPipeline create_pipeline(iris::instance* iris, VkShaderModule vert_shader, VkShaderModule frag_shader) {\n    // Create pipeline layout\n    VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;\n\n    VkPipelineLayoutCreateInfo pipeline_layout_info = {};\n    pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;\n    pipeline_layout_info.setLayoutCount = 1;\n    pipeline_layout_info.pSetLayouts = &iris->descriptor_set_layout;\n    pipeline_layout_info.pushConstantRangeCount = 0;\n    pipeline_layout_info.pPushConstantRanges = VK_NULL_HANDLE;\n\n    if (vkCreatePipelineLayout(iris->device, &pipeline_layout_info, nullptr, &pipeline_layout) != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to create pipeline layout\\n\");\n\n        return VK_NULL_HANDLE;\n    }\n\n    iris->pipeline_layout = pipeline_layout;\n\n    VkRenderPass render_pass = iris->main_window_data.RenderPass;\n\n    // Create graphics pipeline\n    VkPipelineShaderStageCreateInfo shader_stages[2] = {};\n    shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n    shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;\n    shader_stages[0].module = vert_shader;\n    shader_stages[0].pName = \"main\";\n    shader_stages[0].pNext = VK_NULL_HANDLE;\n    shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n    shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;\n    shader_stages[1].module = frag_shader;\n    shader_stages[1].pName = \"main\";\n    shader_stages[1].pNext = VK_NULL_HANDLE;\n\n    static const VkDynamicState dynamic_states[] = {\n        VK_DYNAMIC_STATE_VIEWPORT,\n        VK_DYNAMIC_STATE_SCISSOR\n    };\n\n    VkPipelineDynamicStateCreateInfo dynamic_state_info = {};\n    dynamic_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;\n    dynamic_state_info.dynamicStateCount = 2;\n    dynamic_state_info.pDynamicStates = dynamic_states;\n\n    const auto binding_description = vertex::get_binding_description();\n    const auto attribute_descriptions = vertex::get_attribute_descriptions();\n\n    VkPipelineVertexInputStateCreateInfo vertex_input_info = {};\n    vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;\n    vertex_input_info.vertexBindingDescriptionCount = 1;\n    vertex_input_info.pVertexBindingDescriptions = &binding_description;\n    vertex_input_info.vertexAttributeDescriptionCount = 2;\n    vertex_input_info.pVertexAttributeDescriptions = attribute_descriptions.data();\n\n    VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {};\n    input_assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;\n    input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n    input_assembly_info.primitiveRestartEnable = VK_FALSE;\n\n    VkViewport viewport = {};\n    viewport.x = 0.0f;\n    viewport.y = 0.0f;\n    viewport.width = (float)iris->main_window_data.Width;\n    viewport.height = (float)iris->main_window_data.Height;\n    viewport.minDepth = 0.0f;\n    viewport.maxDepth = 1.0f;\n\n    VkExtent2D extent = {};\n    extent.width = iris->main_window_data.Width;\n    extent.height = iris->main_window_data.Height;\n\n    VkRect2D scissor = {};\n    scissor.offset = {0, 0};\n    scissor.extent = extent;\n\n    VkPipelineViewportStateCreateInfo viewport_state_info = {};\n    viewport_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;\n    viewport_state_info.viewportCount = 1;\n    viewport_state_info.pViewports = &viewport;\n    viewport_state_info.scissorCount = 1;\n    viewport_state_info.pScissors = &scissor;\n\n    VkPipelineRasterizationStateCreateInfo rasterizer_info = {};\n    rasterizer_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;\n    rasterizer_info.depthClampEnable = VK_FALSE;\n    rasterizer_info.rasterizerDiscardEnable = VK_FALSE;\n    rasterizer_info.polygonMode = VK_POLYGON_MODE_FILL;\n    rasterizer_info.lineWidth = 1.0f;\n    rasterizer_info.cullMode = VK_CULL_MODE_NONE;\n    rasterizer_info.frontFace = VK_FRONT_FACE_CLOCKWISE;\n    rasterizer_info.depthBiasEnable = VK_FALSE;\n\n    VkPipelineColorBlendAttachmentState blend_attachment_state = {};\n    blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;\n    blend_attachment_state.blendEnable = VK_FALSE;\n\n    VkPipelineColorBlendStateCreateInfo blend_state_info{};\n    blend_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;\n    blend_state_info.logicOpEnable = VK_FALSE;\n    blend_state_info.attachmentCount = 1;\n    blend_state_info.pAttachments = &blend_attachment_state;\n\n    VkPipelineMultisampleStateCreateInfo multisampling_state_info = {};\n    multisampling_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;\n    multisampling_state_info.sampleShadingEnable = VK_FALSE;\n    multisampling_state_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;\n\n    VkGraphicsPipelineCreateInfo pipeline_info = {};\n    pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;\n    pipeline_info.stageCount = 2;\n    pipeline_info.pStages = shader_stages;\n    pipeline_info.pVertexInputState = &vertex_input_info;\n    pipeline_info.pInputAssemblyState = &input_assembly_info;\n    pipeline_info.pViewportState = &viewport_state_info;\n    pipeline_info.pRasterizationState = &rasterizer_info;\n    pipeline_info.pMultisampleState = &multisampling_state_info;\n    pipeline_info.pDepthStencilState = nullptr; // Optional\n    pipeline_info.pColorBlendState = &blend_state_info;\n    pipeline_info.pDynamicState = &dynamic_state_info;\n    pipeline_info.layout = pipeline_layout;\n    pipeline_info.renderPass = render_pass;\n    pipeline_info.subpass = 0;\n    pipeline_info.pTessellationState = VK_NULL_HANDLE;\n    pipeline_info.basePipelineHandle = VK_NULL_HANDLE; // Optional\n    pipeline_info.basePipelineIndex = -1; // Optional\n\n    VkPipeline pipeline = VK_NULL_HANDLE;\n\n    if (vkCreateGraphicsPipelines(iris->device, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &pipeline) != VK_SUCCESS) {\n        return VK_NULL_HANDLE;\n    }\n\n    vkDestroyShaderModule(iris->device, frag_shader, nullptr);\n    vkDestroyShaderModule(iris->device, vert_shader, nullptr);\n\n    return pipeline;\n}\n\nbool init(iris::instance* iris) {\n    VkDescriptorSetLayoutBinding sampler_layout_binding = {};\n    sampler_layout_binding.binding = 0;\n    sampler_layout_binding.descriptorCount = 1;\n    sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n    sampler_layout_binding.pImmutableSamplers = nullptr;\n    sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;\n\n    // VkDescriptorBindingFlags flags = {};\n    // flags = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT;\n\n    // VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags = {};\n    // binding_flags.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO;\n    // binding_flags.pNext = nullptr;\n    // binding_flags.pBindingFlags = &flags;\n    // binding_flags.bindingCount = 1;\n\n    VkDescriptorSetLayoutCreateInfo layout_info = {};\n    layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;\n    layout_info.bindingCount = 1;\n    layout_info.pBindings = &sampler_layout_binding;\n    // layout_info.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT;\n    // layout_info.pNext = &binding_flags;\n\n    if (vkCreateDescriptorSetLayout(iris->device, &layout_info, nullptr, &iris->descriptor_set_layout) != VK_SUCCESS) {\n        fprintf(stderr, \"imgui: Failed to create descriptor set layout\\n\");\n\n        return false;\n    }\n\n    std::vector <VkDescriptorSetLayout> layouts(DESCRIPTOR_SET_RING_SIZE, iris->descriptor_set_layout);\n\n    iris->descriptor_sets.resize(DESCRIPTOR_SET_RING_SIZE, VK_NULL_HANDLE);\n\n    VkDescriptorSetAllocateInfo alloc_info = {};\n    alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;\n    alloc_info.descriptorPool = iris->descriptor_pool;\n    alloc_info.descriptorSetCount = DESCRIPTOR_SET_RING_SIZE;\n    alloc_info.pSetLayouts = layouts.data();\n\n    if (vkAllocateDescriptorSets(iris->device, &alloc_info, iris->descriptor_sets.data()) != VK_SUCCESS) {\n        fprintf(stderr, \"imgui: Failed to allocate descriptor sets\\n\");\n\n        return false;\n    }\n\n    iris->descriptor_set = iris->descriptor_sets[0];\n\n    if (!SDL_Vulkan_CreateSurface(iris->window, iris->instance, VK_NULL_HANDLE, &iris->surface)) {\n        printf(\"imgui: Failed to create Vulkan surface\\n\");\n\n        return false;\n    }\n\n    if (!setup_vulkan_window(iris, &iris->main_window_data, iris->window_width, iris->window_height, iris->vsync)) {\n        printf(\"imgui: Failed to setup Vulkan window\\n\");\n\n        return false;\n    }\n\n    iris->ini_path = iris->pref_path + \"imgui.ini\";\n\n    IMGUI_CHECKVERSION();\n    ImGui::CreateContext();\n    ImPlot::CreateContext();\n    ImGuiIO& io = ImGui::GetIO(); (void)io;\n    io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard;     // Enable Keyboard Controls\n    io.ConfigFlags |= ImGuiConfigFlags_DockingEnable;         // Enable Docking\n\n    if (iris->imgui_enable_viewports) {\n        io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable;\n        io.ConfigViewportsNoDecoration = false;\n        io.ConfigViewportsNoAutoMerge = true;\n    }\n\n    io.IniFilename = iris->ini_path.c_str();\n\n    // Setup scaling\n    ImGuiStyle& style = ImGui::GetStyle();\n    style.ScaleAllSizes(iris->main_scale);\n    style.FontScaleDpi = iris->main_scale;\n    style.FontScaleMain = iris->ui_scale;\n\n    io.ConfigDpiScaleFonts = true;\n    io.ConfigDpiScaleViewports = true;\n\n    // Setup Platform/Renderer backends\n    if (!ImGui_ImplSDL3_InitForVulkan(iris->window)) {\n        fprintf(stderr, \"imgui: Failed to initialize SDL3/Vulkan backend\\n\");\n\n        return false;\n    }\n\n    ImGui_ImplVulkan_InitInfo init_info = {};\n    init_info.ApiVersion = IRIS_VULKAN_API_VERSION;\n    init_info.Instance = iris->instance;\n    init_info.PhysicalDevice = iris->physical_device;\n    init_info.Device = iris->device;\n    init_info.QueueFamily = iris->queue_family;\n    init_info.Queue = iris->queue;\n    init_info.PipelineCache = VK_NULL_HANDLE;\n    init_info.DescriptorPool = iris->descriptor_pool;\n    init_info.MinImageCount = iris->min_image_count;\n    init_info.ImageCount = iris->main_window_data.ImageCount;\n    init_info.Allocator = VK_NULL_HANDLE;\n    init_info.PipelineInfoMain.RenderPass = iris->main_window_data.RenderPass;\n    init_info.PipelineInfoMain.Subpass = 0;\n    init_info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT;\n    init_info.CheckVkResultFn = VK_NULL_HANDLE;\n\n    if (!ImGui_ImplVulkan_Init(&init_info)) {\n        fprintf(stderr, \"imgui: Failed to initialize Vulkan backend\\n\");\n\n        return false;\n    }\n\n    if (!setup_fonts(iris, io)) {\n        fprintf(stderr, \"imgui: Failed to setup fonts\\n\");\n\n        return false;\n    }\n\n    set_theme(iris, iris->theme, false);\n    set_codeview_scheme(iris, iris->codeview_color_scheme);\n\n    // Initialize our pipeline\n    VkShaderModule vert_shader = create_shader(iris, (uint32_t*)g_vertex_shader_data, g_vertex_shader_size);\n    VkShaderModule frag_shader = create_shader(iris, (uint32_t*)g_fragment_shader_data, g_fragment_shader_size);\n\n    if (!vert_shader || !frag_shader) {\n        fprintf(stderr, \"vulkan: Failed to create shader modules\\n\");\n\n        return false;\n    }\n\n    iris->pipeline = create_pipeline(iris, vert_shader, frag_shader);\n\n    if (!iris->pipeline) {\n        fprintf(stderr, \"imgui: Failed to create graphics pipeline\\n\");\n\n        return false;\n    }\n\n    auto load_texture = [iris](const stbi_uc* data, size_t size) -> texture {\n        int x, y, c;\n\n        stbi_uc* buf = stbi_load_from_memory(data, size, &x, &y, &c, 4);\n\n        auto tex = vulkan::upload_texture(iris, buf, x, y, c);\n\n        stbi_image_free(buf);\n\n        return tex;\n    };\n\n    iris->ps1_memory_card_icon = load_texture(g_ps1_memory_card_icon_data, g_ps1_memory_card_icon_size);\n    iris->ps2_memory_card_icon = load_texture(g_ps2_memory_card_icon_data, g_ps2_memory_card_icon_size);\n    iris->pocketstation_icon = load_texture(g_pocketstation_icon_data, g_pocketstation_icon_size);\n    iris->dualshock2_icon = load_texture(g_dualshock2_icon_data, g_dualshock2_icon_size);\n    iris->iris_icon = load_texture(g_iris_icon_data, g_iris_icon_size);\n\n    return true;\n}\n\nvoid cleanup(iris::instance* iris) {\n    vulkan::wait_idle(iris);\n\n    vulkan::free_texture(iris, iris->ps1_memory_card_icon);\n    vulkan::free_texture(iris, iris->ps2_memory_card_icon);\n    vulkan::free_texture(iris, iris->pocketstation_icon);\n    vulkan::free_texture(iris, iris->dualshock2_icon);\n    vulkan::free_texture(iris, iris->iris_icon);\n\n    ImGui_ImplVulkan_Shutdown();\n    ImGui_ImplSDL3_Shutdown();\n    ImPlot::DestroyContext();\n    ImGui::DestroyContext();\n\n    ImGui_ImplVulkanH_DestroyWindow(iris->instance, iris->device, &iris->main_window_data, VK_NULL_HANDLE);\n\n    iris->instance = NULL;\n}\n\nbool render_frame(iris::instance* iris, ImDrawData* draw_data) {\n    if (iris->swapchain_rebuild)\n        return true;\n\n    ImGui_ImplVulkanH_Window* wd = &iris->main_window_data;\n    VkSemaphore acquire_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore;\n\n    uint32_t image_index;\n\n    VkResult err;\n\n    err = vkAcquireNextImageKHR(\n        iris->device,\n        wd->Swapchain,\n        UINT64_MAX,\n        acquire_semaphore,\n        VK_NULL_HANDLE,\n        &image_index\n    );\n\n    VkSemaphore submit_semaphore = wd->FrameSemaphores[image_index].RenderCompleteSemaphore;\n\n    if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) {\n        iris->swapchain_rebuild = true;\n\n        return true;\n    } else if (err != VK_SUCCESS) {\n        fprintf(stderr, \"imgui: Failed to acquire next image\\n\");\n\n        return false;\n    }\n\n    wd->FrameIndex = image_index;\n\n    ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex];\n\n    if (vkWaitForFences(iris->device, 1, &fd->Fence, VK_TRUE, UINT64_MAX) != VK_SUCCESS) {\n        fprintf(stderr, \"imgui: Failed to wait for fence\\n\");\n\n        return false;\n    }\n\n    if (vkResetFences(iris->device, 1, &fd->Fence) != VK_SUCCESS) {\n        fprintf(stderr, \"imgui: Failed to reset fence\\n\");\n\n        return false;\n    }\n\n    if (vkResetCommandPool(iris->device, fd->CommandPool, 0) != VK_SUCCESS) {\n        fprintf(stderr, \"imgui: Failed to reset command pool\\n\");\n\n        return false;\n    }\n\n    VkCommandBufferBeginInfo begin_info = {};\n    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;\n    begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;\n\n    if (vkBeginCommandBuffer(fd->CommandBuffer, &begin_info) != VK_SUCCESS) {\n        fprintf(stderr, \"imgui: Failed to begin command buffer\\n\");\n        \n        return false;\n    }\n\n    if (iris->instance) {\n        render::render_frame(iris, fd->CommandBuffer, fd->Framebuffer);\n    }\n\n    {\n        VkRenderPassBeginInfo render_pass_info = {};\n        render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;\n        render_pass_info.renderPass = wd->RenderPass;\n        render_pass_info.framebuffer = fd->Framebuffer;\n        render_pass_info.renderArea.extent.width = wd->Width;\n        render_pass_info.renderArea.extent.height = wd->Height;\n        render_pass_info.clearValueCount = 1;\n        render_pass_info.pClearValues = &iris->clear_value;\n\n        vkCmdBeginRenderPass(fd->CommandBuffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE);\n    }\n\n    // Record dear imgui primitives into command buffer\n    ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer);\n\n    // Submit command buffer\n    vkCmdEndRenderPass(fd->CommandBuffer);\n\n    {\n        VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n        VkSubmitInfo submit_info = {};\n        submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n        submit_info.waitSemaphoreCount = 1;\n        submit_info.pWaitSemaphores = &acquire_semaphore;\n        submit_info.pWaitDstStageMask = &wait_stage;\n        submit_info.commandBufferCount = 1;\n        submit_info.pCommandBuffers = &fd->CommandBuffer;\n        submit_info.signalSemaphoreCount = 1;\n        submit_info.pSignalSemaphores = &submit_semaphore;\n\n        if (vkEndCommandBuffer(fd->CommandBuffer) != VK_SUCCESS) {\n            fprintf(stderr, \"imgui: Failed to end command buffer\\n\");\n        \n            return false;\n        }\n\n        if (vkQueueSubmit(iris->queue, 1, &submit_info, fd->Fence) != VK_SUCCESS) {\n            fprintf(stderr, \"imgui: Failed to submit queue\\n\");\n        \n            return false;\n        }\n    }\n\n    if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {\n        ImGui::UpdatePlatformWindows();\n        ImGui::RenderPlatformWindowsDefault();\n    }\n\n    VkPresentInfoKHR present_info = {};\n    present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR;\n    present_info.waitSemaphoreCount = 1;\n    present_info.pWaitSemaphores = &submit_semaphore;\n    present_info.swapchainCount = 1;\n    present_info.pSwapchains = &wd->Swapchain;\n    present_info.pImageIndices = &image_index;\n\n    err = vkQueuePresentKHR(iris->queue, &present_info);\n\n    if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) {\n        iris->swapchain_rebuild = true;\n\n        return true;\n    } else if (err != VK_SUCCESS) {\n        fprintf(stderr, \"imgui: Failed to acquire next image\\n\");\n\n        return false;\n    }\n\n    wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount;\n\n    return true;\n}\n\nbool BeginEx(const char* name, bool* p_open, ImGuiWindowFlags flags) {\n    ImGui::SetNextWindowSize(ImVec2(600.0, 600.0), ImGuiCond_FirstUseEver);\n    ImGui::SetNextWindowPos(ImVec2(50.0, 50.0), ImGuiCond_FirstUseEver);\n\n    if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {\n        flags |= ImGuiWindowFlags_NoTitleBar;\n    }\n\n    return ImGui::Begin(name, p_open, flags);\n}\n\n}\n"
  },
  {
    "path": "frontend/input.cpp",
    "content": "#include <filesystem>\n#include <string>\n\n#include \"iris.hpp\"\n\n#define STB_IMAGE_WRITE_IMPLEMENTATION\n#include \"stb_image_write.h\"\n\n#define INCBIN_PREFIX g_\n#define INCBIN_STYLE INCBIN_STYLE_SNAKE\n\n#include \"incbin.h\"\n\nINCBIN(gamecontrollerdb, \"../deps/SDL_GameControllerDB/gamecontrollerdb.txt\");\n\nnamespace iris {\n\nvoid keyboard_device::handle_event(iris::instance* iris, SDL_Event* event) {\n    auto ievent = input::sdl_event_to_input_event(event);\n    auto action = input::get_input_action(iris, m_slot, ievent.u64);\n\n    if (!action)\n        return;\n\n    input::execute_action(iris, *action, m_slot, event->type == SDL_EVENT_KEY_DOWN ? 1.0f : 0.0f);\n}\n\nvoid gamepad_device::handle_event(iris::instance* iris, SDL_Event* event) {\n    auto ievent = input::sdl_event_to_input_event(event);\n    auto action = input::get_input_action(iris, m_slot, ievent.u64);\n\n    if (!action)\n        return;\n\n    if (event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) {\n        input::execute_action(iris, *action, m_slot, 1.0f);\n    } else if (event->type == SDL_EVENT_GAMEPAD_BUTTON_UP) {\n        input::execute_action(iris, *action, m_slot, 0.0f);\n    } else if (event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {\n        // Convert from -32768->32767 to -1.0->1.0 and take absolute value\n        float value = fabs(event->gaxis.value / 32767.0f);\n\n        input::execute_action(iris, *action, m_slot, value);\n    }\n}\n\n}\n\nnamespace iris::input {\n\nvoid load_db_default(iris::instance* iris) {\n    SDL_IOStream* ios = SDL_IOFromConstMem(g_gamecontrollerdb_data, g_gamecontrollerdb_size);\n\n    SDL_AddGamepadMappingsFromIO(ios, true);\n}\n\nbool load_db_from_file(iris::instance* iris, const char* path) {\n    if (SDL_AddGamepadMappingsFromFile(path) == -1)\n        return false;\n\n    return true;\n}\n\n#define IEVENT(event, id, mod) \\\n    (((uint64_t)event << 32) | (((id & 0xf0000fff) | ((mod & 0xffff) << 12)) & 0xffffffff))\n\nvoid init_default_mapping(iris::instance* iris, int id) {\n    mapping& map = iris->input_maps[id];\n\n    if (id == 0) {\n        map.name = \"Keyboard (default)\";\n\n        map.map.clear();\n\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_X     , SDL_KMOD_NONE), IRIS_DS_BT_CROSS);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_A     , SDL_KMOD_NONE), IRIS_DS_BT_SQUARE);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_W     , SDL_KMOD_NONE), IRIS_DS_BT_TRIANGLE);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_D     , SDL_KMOD_NONE), IRIS_DS_BT_CIRCLE);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_RETURN, SDL_KMOD_NONE), IRIS_DS_BT_START);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_S     , SDL_KMOD_NONE), IRIS_DS_BT_SELECT);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_UP    , SDL_KMOD_NONE), IRIS_DS_BT_UP);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_DOWN  , SDL_KMOD_NONE), IRIS_DS_BT_DOWN);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_LEFT  , SDL_KMOD_NONE), IRIS_DS_BT_LEFT);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_RIGHT , SDL_KMOD_NONE), IRIS_DS_BT_RIGHT);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_Q     , SDL_KMOD_NONE), IRIS_DS_BT_L1);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_E     , SDL_KMOD_NONE), IRIS_DS_BT_R1);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_1     , SDL_KMOD_NONE), IRIS_DS_BT_L2);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_3     , SDL_KMOD_NONE), IRIS_DS_BT_R2);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_Z     , SDL_KMOD_NONE), IRIS_DS_BT_L3);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_C     , SDL_KMOD_NONE), IRIS_DS_BT_R3);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_I     , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_J     , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_K     , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_L     , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_T     , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_F     , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_G     , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_H     , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_0     , SDL_KMOD_NONE), IRIS_S14X_SW_SERVICE);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_9     , SDL_KMOD_NONE), IRIS_S14X_SW_TEST);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_8     , SDL_KMOD_NONE), IRIS_S14X_SW_ENTER);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_7     , SDL_KMOD_NONE), IRIS_S14X_SW_UP);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_6     , SDL_KMOD_NONE), IRIS_S14X_SW_DOWN);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_1     , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P1_START);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_2     , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P2_START);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_3     , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P3_START);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_4     , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P4_START);\n    } else {\n        map.name = \"Gamepad (default)\";\n\n        map.map.clear();\n\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_SOUTH         , SDL_KMOD_NONE), IRIS_DS_BT_CROSS);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_WEST          , SDL_KMOD_NONE), IRIS_DS_BT_SQUARE);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_NORTH         , SDL_KMOD_NONE), IRIS_DS_BT_TRIANGLE);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_EAST          , SDL_KMOD_NONE), IRIS_DS_BT_CIRCLE);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_START         , SDL_KMOD_NONE), IRIS_DS_BT_START);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_BACK          , SDL_KMOD_NONE), IRIS_DS_BT_SELECT);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_DPAD_UP       , SDL_KMOD_NONE), IRIS_DS_BT_UP);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_DPAD_DOWN     , SDL_KMOD_NONE), IRIS_DS_BT_DOWN);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_DPAD_LEFT     , SDL_KMOD_NONE), IRIS_DS_BT_LEFT);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_DPAD_RIGHT    , SDL_KMOD_NONE), IRIS_DS_BT_RIGHT);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_LEFT_SHOULDER , SDL_KMOD_NONE), IRIS_DS_BT_L1);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, SDL_KMOD_NONE), IRIS_DS_BT_R1);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_LEFT_STICK    , SDL_KMOD_NONE), IRIS_DS_BT_L3);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_RIGHT_STICK   , SDL_KMOD_NONE), IRIS_DS_BT_R3);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFT_TRIGGER    , SDL_KMOD_NONE), IRIS_DS_BT_L2);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER   , SDL_KMOD_NONE), IRIS_DS_BT_R2);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFTY           , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_LEFTY           , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFTX           , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_LEFTX           , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHTY          , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_RIGHTY          , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHTX          , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_RIGHTX          , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_NEG);\n    }\n}\n\nbool init(iris::instance* iris) {\n    if (!iris->gcdb_path.size()) {\n        fprintf(stdout, \"input: Adding default database\\n\");\n\n        load_db_default(iris);\n    } else {\n        fprintf(stdout, \"input: Adding database from file \\'%s\\'\\n\", iris->gcdb_path.c_str());\n\n        load_db_from_file(iris, iris->gcdb_path.c_str());\n    }\n\n    iris->input_devices[0] = new iris::keyboard_device();\n\n    if (iris->input_maps.size() == 0) {\n        mapping map;\n\n        map.name = \"Keyboard (default)\";\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_X     , SDL_KMOD_NONE), IRIS_DS_BT_CROSS);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_A     , SDL_KMOD_NONE), IRIS_DS_BT_SQUARE);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_W     , SDL_KMOD_NONE), IRIS_DS_BT_TRIANGLE);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_D     , SDL_KMOD_NONE), IRIS_DS_BT_CIRCLE);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_RETURN, SDL_KMOD_NONE), IRIS_DS_BT_START);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_S     , SDL_KMOD_NONE), IRIS_DS_BT_SELECT);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_UP    , SDL_KMOD_NONE), IRIS_DS_BT_UP);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_DOWN  , SDL_KMOD_NONE), IRIS_DS_BT_DOWN);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_LEFT  , SDL_KMOD_NONE), IRIS_DS_BT_LEFT);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_RIGHT , SDL_KMOD_NONE), IRIS_DS_BT_RIGHT);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_Q     , SDL_KMOD_NONE), IRIS_DS_BT_L1);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_E     , SDL_KMOD_NONE), IRIS_DS_BT_R1);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_1     , SDL_KMOD_NONE), IRIS_DS_BT_L2);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_3     , SDL_KMOD_NONE), IRIS_DS_BT_R2);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_Z     , SDL_KMOD_NONE), IRIS_DS_BT_L3);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_C     , SDL_KMOD_NONE), IRIS_DS_BT_R3);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_I     , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_J     , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_K     , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_L     , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_T     , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_F     , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_G     , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_H     , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_0     , SDL_KMOD_NONE), IRIS_S14X_SW_SERVICE);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_9     , SDL_KMOD_NONE), IRIS_S14X_SW_TEST);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_8     , SDL_KMOD_NONE), IRIS_S14X_SW_ENTER);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_7     , SDL_KMOD_NONE), IRIS_S14X_SW_UP);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_6     , SDL_KMOD_NONE), IRIS_S14X_SW_DOWN);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_1     , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P1_START);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_2     , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P2_START);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_3     , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P3_START);\n        map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_4     , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P4_START);\n\n        iris->input_maps.push_back(map);\n\n        map.map.clear();\n        map = {};\n\n        map.name = \"Gamepad (default)\";\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_SOUTH         , SDL_KMOD_NONE), IRIS_DS_BT_CROSS);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_WEST          , SDL_KMOD_NONE), IRIS_DS_BT_SQUARE);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_NORTH         , SDL_KMOD_NONE), IRIS_DS_BT_TRIANGLE);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_EAST          , SDL_KMOD_NONE), IRIS_DS_BT_CIRCLE);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_START         , SDL_KMOD_NONE), IRIS_DS_BT_START);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_BACK          , SDL_KMOD_NONE), IRIS_DS_BT_SELECT);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_DPAD_UP       , SDL_KMOD_NONE), IRIS_DS_BT_UP);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_DPAD_DOWN     , SDL_KMOD_NONE), IRIS_DS_BT_DOWN);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_DPAD_LEFT     , SDL_KMOD_NONE), IRIS_DS_BT_LEFT);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_DPAD_RIGHT    , SDL_KMOD_NONE), IRIS_DS_BT_RIGHT);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_LEFT_SHOULDER , SDL_KMOD_NONE), IRIS_DS_BT_L1);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, SDL_KMOD_NONE), IRIS_DS_BT_R1);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_LEFT_STICK    , SDL_KMOD_NONE), IRIS_DS_BT_L3);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON  , SDL_GAMEPAD_BUTTON_RIGHT_STICK   , SDL_KMOD_NONE), IRIS_DS_BT_R3);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFT_TRIGGER    , SDL_KMOD_NONE), IRIS_DS_BT_L2);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER   , SDL_KMOD_NONE), IRIS_DS_BT_R2);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFTY           , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_LEFTY           , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFTX           , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_LEFTX           , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHTY          , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_RIGHTY          , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_NEG);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHTX          , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_POS);\n        map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_RIGHTX          , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_NEG);\n\n        iris->input_maps.push_back(map);\n    }\n\n#undef IEVENT\n\n    // Ensure default mappings are in the correct order\n    if (iris->input_maps[0].name == \"Gamepad (default)\") {\n        auto map = iris->input_maps[0];\n\n        iris->input_maps[0] = iris->input_maps[1];\n        iris->input_maps[1] = map;\n    }\n\n    // Use keyboard mapping for slot 0 and none for slot 1 by default\n    if (iris->input_map[0] <= 1) {\n        iris->input_map[0] = 0;\n    }\n\n    if (iris->input_map[1] <= 1) {\n        iris->input_map[1] = -1;\n    }\n\n    return true;\n}\n\ninput_action* get_input_action(iris::instance* iris, int slot, uint64_t input) {\n    if (iris->input_map[slot] == -1)\n        return nullptr;\n\n    return iris->input_maps[iris->input_map[slot]].map.get_value(input);\n}\n\nstatic inline void change_button(iris::instance* iris, int slot, float value, uint32_t button) {\n    if (!iris->ds[slot]) return;\n\n    if (value > 0.5f) {\n        ds_button_press(iris->ds[slot], button);\n    } else {\n        ds_button_release(iris->ds[slot], button);\n    }\n}\n\nstatic inline void change_s14x_switch(iris::instance* iris, float value, uint32_t mask) {\n    if (!iris->ps2->s14x_ioboard)\n        return;\n\n    if (value > 0.5) {\n        s14x_ioboard_press_switch(iris->ps2->s14x_ioboard, mask);\n    } else {\n        s14x_ioboard_release_switch(iris->ps2->s14x_ioboard, mask);\n    }\n}\n\nvoid execute_action(iris::instance* iris, input_action action, int slot, float value) {\n    if (!iris->ds[slot])\n        return;\n\n    switch (action) {\n        case IRIS_DS_BT_SELECT: change_button(iris, slot, value, DS_BT_SELECT); break;\n        case IRIS_DS_BT_L3: change_button(iris, slot, value, DS_BT_L3); break;\n        case IRIS_DS_BT_R3: change_button(iris, slot, value, DS_BT_R3); break;\n        case IRIS_DS_BT_START: change_button(iris, slot, value, DS_BT_START); break;\n        case IRIS_DS_BT_UP: change_button(iris, slot, value, DS_BT_UP); break;\n        case IRIS_DS_BT_RIGHT: change_button(iris, slot, value, DS_BT_RIGHT); break;\n        case IRIS_DS_BT_DOWN: change_button(iris, slot, value, DS_BT_DOWN); break;\n        case IRIS_DS_BT_LEFT: change_button(iris, slot, value, DS_BT_LEFT); break;\n        case IRIS_DS_BT_L2: change_button(iris, slot, value, DS_BT_L2); break;\n        case IRIS_DS_BT_R2: change_button(iris, slot, value, DS_BT_R2); break;\n        case IRIS_DS_BT_L1: change_button(iris, slot, value, DS_BT_L1); break;\n        case IRIS_DS_BT_R1: change_button(iris, slot, value, DS_BT_R1); break;\n        case IRIS_DS_BT_TRIANGLE: change_button(iris, slot, value, DS_BT_TRIANGLE); break;\n        case IRIS_DS_BT_CIRCLE: change_button(iris, slot, value, DS_BT_CIRCLE); break;\n        case IRIS_DS_BT_CROSS: change_button(iris, slot, value, DS_BT_CROSS); break;\n        case IRIS_DS_BT_SQUARE: change_button(iris, slot, value, DS_BT_SQUARE); break;\n        case IRIS_DS_BT_ANALOG: change_button(iris, slot, value, DS_BT_ANALOG); break;\n        case IRIS_DS_AX_RIGHTV_POS: ds_analog_change(iris->ds[slot], DS_AX_RIGHT_V, 0x7f + (value * 0x80)); break;\n        case IRIS_DS_AX_RIGHTV_NEG: ds_analog_change(iris->ds[slot], DS_AX_RIGHT_V, 0x7f - (value * 0x7f)); break;\n        case IRIS_DS_AX_RIGHTH_POS: ds_analog_change(iris->ds[slot], DS_AX_RIGHT_H, 0x7f + (value * 0x80)); break;\n        case IRIS_DS_AX_RIGHTH_NEG: ds_analog_change(iris->ds[slot], DS_AX_RIGHT_H, 0x7f - (value * 0x7f)); break;\n        case IRIS_DS_AX_LEFTV_POS: ds_analog_change(iris->ds[slot], DS_AX_LEFT_V, 0x7f + (value * 0x80)); break;\n        case IRIS_DS_AX_LEFTV_NEG: ds_analog_change(iris->ds[slot], DS_AX_LEFT_V, 0x7f - (value * 0x7f)); break;\n        case IRIS_DS_AX_LEFTH_POS: ds_analog_change(iris->ds[slot], DS_AX_LEFT_H, 0x7f + (value * 0x80)); break;\n        case IRIS_DS_AX_LEFTH_NEG: ds_analog_change(iris->ds[slot], DS_AX_LEFT_H, 0x7f - (value * 0x7f)); break;\n        case IRIS_S14X_SW_SERVICE: change_s14x_switch(iris, value, S14X_IOBOARD_SW_SERVICE); break;\n        case IRIS_S14X_SW_TEST: change_s14x_switch(iris, value, S14X_IOBOARD_SW_TEST); break;\n        case IRIS_S14X_SW_ENTER: change_s14x_switch(iris, value, S14X_IOBOARD_SW_ENTER); break;\n        case IRIS_S14X_SW_UP: change_s14x_switch(iris, value, S14X_IOBOARD_SW_UP); break;\n        case IRIS_S14X_SW_DOWN: change_s14x_switch(iris, value, S14X_IOBOARD_SW_DOWN); break;\n        case IRIS_S14X_SW_P1_START: change_s14x_switch(iris, value, S14X_IOBOARD_SW_P1_START); break;\n        case IRIS_S14X_SW_P2_START: change_s14x_switch(iris, value, S14X_IOBOARD_SW_P2_START); break;\n        case IRIS_S14X_SW_P3_START: change_s14x_switch(iris, value, S14X_IOBOARD_SW_P3_START); break;\n        case IRIS_S14X_SW_P4_START: change_s14x_switch(iris, value, S14X_IOBOARD_SW_P4_START); break;\n    }\n}\n\ninput_event sdl_event_to_input_event(SDL_Event* event) {\n    input_event ievent = {};\n\n    switch (event->type) {\n        case SDL_EVENT_KEY_DOWN:\n        case SDL_EVENT_KEY_UP: {\n            ievent.type = IRIS_EVENT_KEYBOARD;\n            ievent.id = event->key.key;\n\n            // Devious hack, we have enough spare bits in the \n            // SDL_Keycode so we can actually do this\n            const uint16_t mask =\n                SDL_KMOD_LSHIFT | SDL_KMOD_RSHIFT |\n                SDL_KMOD_LCTRL  | SDL_KMOD_RCTRL  |\n                SDL_KMOD_LALT   | SDL_KMOD_RALT;\n\n            ievent.id |= (event->key.mod & mask) << 12;\n        } break;\n\n        case SDL_EVENT_GAMEPAD_BUTTON_DOWN:\n        case SDL_EVENT_GAMEPAD_BUTTON_UP: {\n            ievent.type = IRIS_EVENT_GAMEPAD_BUTTON;\n            ievent.id = event->gbutton.button;\n        } break;\n\n        case SDL_EVENT_GAMEPAD_AXIS_MOTION: {\n            if (event->gaxis.value > 0) {\n                ievent.type = IRIS_EVENT_GAMEPAD_AXIS_POS;\n            } else {\n                ievent.type = IRIS_EVENT_GAMEPAD_AXIS_NEG;\n            }\n\n            ievent.id = event->gaxis.axis;\n        } break;\n    }\n\n    return ievent;\n}\n\nstd::string get_default_screenshot_filename(iris::instance* iris) {\n    SDL_Time t;\n    SDL_DateTime dt;\n\n    SDL_GetCurrentTime(&t);\n    SDL_TimeToDateTime(t, &dt, true);\n\n    char buf[512];\n\n    sprintf(buf, \"Screenshot-%04d-%02d-%02d_%02d-%02d-%02d-%d\",\n            dt.year, dt.month, dt.day,\n            dt.hour, dt.minute, dt.second,\n            iris->screenshot_counter + 1\n    );\n\n    std::string str(buf);\n\n    switch (iris->screenshot_format) {\n        case IRIS_SCREENSHOT_FORMAT_PNG: str += \".png\"; break;\n        case IRIS_SCREENSHOT_FORMAT_BMP: str += \".bmp\"; break;\n        case IRIS_SCREENSHOT_FORMAT_JPG: str += \".jpg\"; break;\n        case IRIS_SCREENSHOT_FORMAT_TGA: str += \".tga\"; break;\n    }\n\n    return str;\n}\n\nint get_screenshot_jpg_quality(iris::instance* iris) {\n    switch (iris->screenshot_jpg_quality_mode) {\n        case IRIS_SCREENSHOT_JPG_QUALITY_MINIMUM: return 1;\n        case IRIS_SCREENSHOT_JPG_QUALITY_LOW:     return 25;\n        case IRIS_SCREENSHOT_JPG_QUALITY_MEDIUM:  return 50;\n        case IRIS_SCREENSHOT_JPG_QUALITY_HIGH:    return 90;\n        case IRIS_SCREENSHOT_JPG_QUALITY_MAXIMUM: return 100;\n        case IRIS_SCREENSHOT_JPG_QUALITY_CUSTOM: return iris->screenshot_jpg_quality;\n    }\n\n    return 90;\n}\n\nbool save_screenshot(iris::instance* iris, std::string path) {\n    std::filesystem::path fn(path);\n\n    std::string directory = iris->snap_path;\n    \n    if (iris->snap_path.empty()) {\n        directory = \"snap\";\n    }\n\n    std::filesystem::path p(directory);\n    std::string absolute_path;\n    std::string filename;\n\n    if (path.size()) {\n        filename = path;\n    } else {\n        filename = get_default_screenshot_filename(iris);\n    }\n\n    if (p.is_absolute()) {\n        absolute_path = p.string();\n    } else {\n        absolute_path = iris->pref_path + p.string();\n    }\n\n    absolute_path += \"/\" + filename;\n\n    if (fn.is_absolute()) {\n        absolute_path = fn.string();\n    }\n\n    void* ptr = nullptr;\n    int width = 0, height = 0, offset = 0;\n\n    if (iris->screenshot_mode == IRIS_SCREENSHOT_MODE_INTERNAL) {\n        renderer_image* image = iris->screenshot_shader_processing ? &iris->output_image : &iris->image;\n\n        ptr = vulkan::read_image(iris,\n            image->image,\n            image->format,\n            image->width,\n            image->height\n        );\n\n        width = image->width;\n        height = image->height;\n    } else {\n        ptr = vulkan::read_image(iris,\n            iris->main_window_data.Frames[0].Backbuffer,\n            iris->main_window_data.SurfaceFormat.format,\n            iris->main_window_data.Width,\n            iris->main_window_data.Height\n        );\n\n        width = iris->main_window_data.Width;\n        height = iris->main_window_data.Height;\n        \n        if (!iris->fullscreen) {\n            offset = iris->menubar_height;\n            height -= iris->menubar_height;\n        }\n    }\n\n    if (!ptr) {\n        push_info(iris, \"Couldn't save screenshot\");\n\n        return false;\n    }\n\n    uint32_t* buf = (uint32_t*)malloc((width * 4) * height);\n\n    memcpy(buf, ((uint32_t*)ptr) + offset * width, (width * 4) * height);\n\n    for (int y = 0; y < height; y++) {\n        for (int x = 0; x < width; x++) {\n            buf[x + (y * width)] |= 0xff000000;\n        }\n    }\n\n    int r = 0;\n\n    switch (iris->screenshot_format) {\n        case IRIS_SCREENSHOT_FORMAT_PNG:\n            r = stbi_write_png(absolute_path.c_str(), width, height, 4, buf, width * 4);\n            break;\n        case IRIS_SCREENSHOT_FORMAT_BMP:\n            r = stbi_write_bmp(absolute_path.c_str(), width, height, 4, buf);\n            break;\n        case IRIS_SCREENSHOT_FORMAT_JPG:\n            r = stbi_write_jpg(absolute_path.c_str(), width, height, 4, buf, get_screenshot_jpg_quality(iris));\n            break;\n        case IRIS_SCREENSHOT_FORMAT_TGA:\n            r = stbi_write_tga(absolute_path.c_str(), width, height, 4, buf);\n            break;\n    }\n\n    printf(\"Saving screenshot to '%s' (%dx%d, %d bpp): %s\\n\",\n           absolute_path.c_str(), width, height, 32, r ? \"Success\" : \"Failure\"\n    );\n\n    free(ptr);\n    free(buf);\n\n    if (!r) {\n        push_info(iris, \"Couldn't save screenshot\");\n\n        return false;\n    }\n\n    iris->screenshot_counter++;\n\n    push_info(iris, \"Screenshot saved as '\" + filename + \"'\");\n\n    return true;\n}\n\nvoid handle_keydown_event(iris::instance* iris, SDL_Event* event) {\n    SDL_Keycode key = event->key.key;\n\n    switch (key) {\n        case SDLK_SPACE: {\n            iris->pause = !iris->pause;\n\n            // vulkan::wait_idle(iris);\n        } break;\n        case SDLK_F9: {\n            vulkan::wait_idle(iris);\n\n            bool saved = save_screenshot(iris);\n        } break;\n        case SDLK_F11: {\n            iris->fullscreen = !iris->fullscreen;\n\n            SDL_SetWindowFullscreen(iris->window, iris->fullscreen ? true : false);\n        } break;\n        case SDLK_F1: {\n            printf(\"ps2: Sending poweroff signal\\n\");\n            ps2_cdvd_power_off(iris->ps2->cdvd);\n        } break;\n    }\n\n    iris->last_input_event_read = false;\n    iris->last_input_event_value = 1.0f;\n    iris->last_input_event = sdl_event_to_input_event(event);\n\n    if (iris->input_devices[0]) iris->input_devices[0]->handle_event(iris, event);\n    if (iris->input_devices[1]) iris->input_devices[1]->handle_event(iris, event);\n}\n\nvoid handle_keyup_event(iris::instance* iris, SDL_Event* event) {\n    // Add special keyup handling here if needed\n\n    if (iris->input_devices[0]) iris->input_devices[0]->handle_event(iris, event);\n    if (iris->input_devices[1]) iris->input_devices[1]->handle_event(iris, event);\n}\n\n}"
  },
  {
    "path": "frontend/iris.cpp",
    "content": "// Standard includes\n#include <filesystem>\n#include <algorithm>\n#include <cstdlib>\n#include <cstdio>\n#include <thread>\n#include <cmath>\n\n// Iris includes\n#include \"iris.hpp\"\n#include \"config.hpp\"\n#include \"ee/ee_def.hpp\"\n#include \"ee/vu_def.hpp\"\n\n// SDL3 includes\n#include <SDL3/SDL.h>\n\n// External includes\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nvoid add_recent(iris::instance* iris, std::string file, int type) {\n    auto it = std::find_if(iris->recents.begin(), iris->recents.end(), [file, type](const recent& a) {\n        return a.type == type && a.path == file;\n    });\n\n    if (it != iris->recents.end()) {\n        iris->recents.erase(it);\n        iris->recents.push_front({file, type});\n\n        return;\n    }\n\n    iris->recents.push_front({file, type});\n\n    if (iris->recents.size() == 11)\n        iris->recents.pop_back();\n}\n\nint open_file(iris::instance* iris, std::string file) {\n    std::filesystem::path path(file);\n    std::string ext = path.extension().string();\n\n    for (char& c : ext)\n        c = tolower(c);\n\n    // Load disc image\n    if (ext == \".iso\" || ext == \".bin\" || ext == \".cue\" ||\n        ext == \".chd\" || ext == \".cso\" || ext == \".zso\") {\n        if (ps2_cdvd_open(iris->ps2->cdvd, file.c_str(), 0))\n            return 1;\n\n        char* boot_file = disc_get_boot_path(iris->ps2->cdvd->disc);\n\n        if (!boot_file)\n            return 2;\n\n        elf::load_symbols_from_disc(iris);\n\n        renderer_reset(iris->renderer);\n\n        ps2_set_system(iris->ps2, iris->system);\n        ps2_load_bios(iris->ps2, iris->bios_path.c_str());\n        ps2_boot_file(iris->ps2, boot_file);\n\n        iris->loaded = file;\n\n        if (iris->autostart) {\n            iris->pause = false;\n        }\n\n        return 0;\n    }\n\n    elf::load_symbols_from_file(iris, file);\n\n    // Note: We need the trailing whitespaces here because of IOMAN HLE\n    // Load executable\n    file = \"host:  \" + file;\n\n    renderer_reset(iris->renderer);\n\n    ps2_set_system(iris->ps2, iris->system);\n    ps2_load_bios(iris->ps2, iris->bios_path.c_str());\n    ps2_boot_file(iris->ps2, file.c_str());\n\n    iris->loaded = file;\n\n    if (iris->autostart) {\n        iris->pause = false;\n    }\n\n    return 0;\n}\n\nvoid update_title(iris::instance* iris) {\n    char buf[512];\n\n    std::string base = \"\";\n\n    if (iris->loaded.size()) {\n        base = std::filesystem::path(iris->loaded).filename().string();\n    }\n\n    sprintf(buf, base.size() ? IRIS_TITLE \" | %s\" : IRIS_TITLE,\n        base.c_str()\n    );\n\n    SDL_SetWindowTitle(iris->window, buf);\n}\n\nvoid update_time(iris::instance* iris) {\n    int t = SDL_GetTicks() - iris->ticks;\n\n    if (t < 500)\n        return;\n\n    if (iris->fps == 0.0f) {\n        iris->fps = (float)iris->frames;\n    } else {\n        iris->fps += (float)iris->frames;\n        iris->fps /= 2.0f;\n    }\n\n    iris->ticks = SDL_GetTicks();\n    iris->frames = 0;\n}\n\nvoid sleep_limiter(iris::instance* iris) {\n    uint32_t ticks = (1.0f / iris->fps_cap) * 1000.0f;\n\n    std::this_thread::sleep_for(std::chrono::milliseconds(ticks / 2));\n\n    // uint32_t now = SDL_GetTicks();\n\n    // while ((SDL_GetTicks() - now) < ticks) {\n    //     std::this_thread::sleep_for(std::chrono::milliseconds(ticks / 4));\n    // }\n}\n\nstatic inline void do_cycle(iris::instance* iris) {\n    ps2_cycle(iris->ps2);\n\n    if (iris->step_out) {\n        // jr $ra\n        if (iris->ps2->ee->opcode == 0x03e00008) {\n            iris->step_out = false;\n            iris->pause = true;\n\n            // Consume the delay slot\n            ps2_cycle(iris->ps2);\n        }\n    }\n\n    if (iris->step_over) {\n        if (iris->ps2->ee->pc == iris->step_over_addr) {\n            iris->step_over = false;\n            iris->pause = true;\n        }\n    }\n\n    for (const iris::breakpoint& b : iris->breakpoints) {\n        if (b.cpu == iris::BKPT_CPU_EE) {\n            if (iris->ps2->ee->pc == b.addr) {\n                iris->pause = true;\n            }\n        } else {\n            if (iris->ps2->iop->pc == b.addr) {\n                iris->pause = true;\n            }\n        }\n    }\n}\n\nvoid update_window(iris::instance* iris) {\n    using namespace ImGui;\n\n    // Limit FPS to 60 only when paused\n    if (iris->limit_fps && iris->pause)\n        sleep_limiter(iris);\n\n    update_title(iris);\n    update_time(iris);\n\n    ImGuiIO& io = ImGui::GetIO();\n\n    // Start the Dear ImGui frame\n    if (SDL_GetWindowFlags(iris->window) & SDL_WINDOW_MINIMIZED) {\n        SDL_Delay(1);\n\n        return;\n    }\n\n    // Resize swapchain?\n    int width, height;\n\n    SDL_GetWindowSize(iris->window, &width, &height);\n\n    if (width > 0 && height > 0 && (iris->swapchain_rebuild || iris->main_window_data.Width != width || iris->main_window_data.Height != height)) {\n        ImGui_ImplVulkan_SetMinImageCount(iris->min_image_count);\n    \n        ImGui_ImplVulkanH_CreateOrResizeWindow(\n            iris->instance,\n            iris->physical_device,\n            iris->device,\n            &iris->main_window_data,\n            iris->queue_family,\n            nullptr,\n            width, height,\n            iris->min_image_count,\n            0\n        );\n\n        iris->main_window_data.FrameIndex = 0;\n        iris->swapchain_rebuild = false;\n    }\n\n    // Start the Dear ImGui frame\n    ImGui_ImplVulkan_NewFrame();\n    ImGui_ImplSDL3_NewFrame();\n    ImGui::NewFrame();\n\n    if (!iris->fullscreen) {\n        show_main_menubar(iris);\n    }\n\n    DockSpaceOverViewport(0, GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode);\n\n    // Drop file fade animation\n    if (iris->drop_file_active) {\n        iris->drop_file_alpha += iris->drop_file_alpha_delta;\n\n        if (iris->drop_file_alpha_delta > 0.0f) {\n            if (iris->drop_file_alpha >= 1.0f) {\n                iris->drop_file_alpha = 1.0f;\n                iris->drop_file_alpha_delta = 0.0f;\n            }\n        } else {\n            if (iris->drop_file_alpha <= 0.0f) {\n                iris->drop_file_alpha = 0.0f;\n                iris->drop_file_alpha_delta = 0.0f;\n                iris->drop_file_active = false;\n            }\n        }\n\n        GetForegroundDrawList()->AddRectFilled(\n            ImVec2(0, 0),\n            ImVec2(width, height),\n            ImColor(0.0f, 0.0f, 0.0f, iris->drop_file_alpha * 0.35f)\n        );\n\n        ImVec2 text_size = CalcTextSize(\"Drop file here to launch\");\n\n        PushFont(iris->font_icons_big);\n\n        ImVec2 icon_size = CalcTextSize(ICON_MS_DOWNLOAD);\n\n        ImVec2 total_size = ImVec2(\n            std::max(icon_size.x, text_size.x),\n            icon_size.y + text_size.y\n        );\n\n        GetForegroundDrawList()->AddText(\n            ImVec2(width / 2 - icon_size.x / 2, height / 2 - icon_size.y),\n            ImColor(1.0f, 1.0f, 1.0f, iris->drop_file_alpha),\n            ICON_MS_DOWNLOAD\n        );\n\n        PopFont();\n\n        GetForegroundDrawList()->AddText(\n            ImVec2(width / 2 - text_size.x / 2, height / 2),\n            ImColor(1.0f, 1.0f, 1.0f, iris->drop_file_alpha),\n            \"Drop file here to launch\"\n        );\n    }\n\n    if (iris->show_ee_control) show_ee_control(iris);\n    if (iris->show_ee_state) show_ee_state(iris);\n    if (iris->show_ee_logs) show_ee_logs(iris);\n    if (iris->show_ee_interrupts) show_ee_interrupts(iris);\n    if (iris->show_ee_dmac) show_ee_dmac(iris);\n    if (iris->show_iop_control) show_iop_control(iris);\n    if (iris->show_iop_state) show_iop_state(iris);\n    if (iris->show_iop_logs) show_iop_logs(iris);\n    if (iris->show_iop_interrupts) show_iop_interrupts(iris);\n    if (iris->show_iop_modules) show_iop_modules(iris);\n    if (iris->show_iop_dma) show_iop_dma(iris);\n    if (iris->show_gs_debugger) show_gs_debugger(iris);\n    if (iris->show_spu2_debugger) show_spu2_debugger(iris);\n    if (iris->show_memory_viewer) show_memory_viewer(iris);\n    if (iris->show_vu_disassembler) show_vu_disassembler(iris);\n    if (iris->show_status_bar && !iris->fullscreen) show_status_bar(iris);\n    if (iris->show_breakpoints) show_breakpoints(iris);\n    if (iris->show_about_window) show_about_window(iris);\n    if (iris->show_settings) show_settings(iris);\n    if (iris->show_pad_debugger) show_pad_debugger(iris);\n    if (iris->show_symbols) show_symbols(iris);\n    if (iris->show_threads) show_threads(iris);\n    if (iris->show_sysmem_logs) show_sysmem_logs(iris);\n    if (iris->show_memory_card_tool) show_memory_card_tool(iris);\n    if (iris->show_memory_search) show_memory_search(iris);\n    // if (iris->show_gamelist) show_gamelist(iris);\n    if (iris->show_imgui_demo) ShowDemoWindow(&iris->show_imgui_demo);\n    if (iris->show_bios_setting_window) show_bios_setting_window(iris);\n    if (iris->show_overlay) show_overlay(iris);\n\n    // Display little pause icon in the top right corner\n    if (iris->pause) {\n        ImVec2 ts = CalcTextSize(ICON_MS_PAUSE);\n        ImVec2 offset = ImVec2(10.0f, 10.0f);\n        ImVec2 padding = ImVec2(0.0f, 0.0f);\n\n        ts.x -= 1.0f;\n\n        int menubar_offset = 0;\n\n        if (!iris->fullscreen) {\n            menubar_offset += iris->menubar_height;\n        }\n\n        // GetBackgroundDrawList()->AddRectFilled(\n        //     ImVec2(width - ts.x - offset.x - padding.x, menubar_offset + offset.y - padding.y),\n        //     ImVec2(width - offset.x + padding.x, menubar_offset + ts.y + offset.y + padding.y),\n        //     GetColorU32(GetStyleColorVec4(ImGuiCol_WindowBg)), 8.0f\n        // );\n\n        GetBackgroundDrawList(GetMainViewport())->AddText(\n            ImVec2(width - ts.x - offset.x, menubar_offset + offset.y),\n            GetColorU32(GetStyleColorVec4(ImGuiCol_Text)),\n            ICON_MS_PAUSE\n        );\n    }\n\n    handle_animations(iris);\n\n    // Rendering\n    ImGui::Render();\n\n    ImDrawData* draw_data = ImGui::GetDrawData();\n\n    const bool main_is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f);\n\n    iris->main_window_data.ClearValue.color.float32[0] = 0.0f;\n    iris->main_window_data.ClearValue.color.float32[1] = 0.0f;\n    iris->main_window_data.ClearValue.color.float32[2] = 0.0f;\n    iris->main_window_data.ClearValue.color.float32[3] = 1.0f;\n\n    if (!main_is_minimized) {\n        if (!imgui::render_frame(iris, draw_data)) {\n            printf(\"iris: Failed to render ImGui frame\\n\");\n        }\n    }\n\n    iris->frames++;\n}\n\niris::instance* create() {\n    return new iris::instance();\n}\n\nbool init(iris::instance* iris, int argc, const char* argv[]) {\n    if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS | SDL_INIT_GAMEPAD)) {\n        fprintf(stderr, \"iris: Failed to init SDL \\'%s\\'\\n\", SDL_GetError());\n\n        return false;\n    }\n\n    // Create and check window\n    iris->main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay());\n\n    // Init preferences path\n    if (std::filesystem::exists(\"portable\") || std::filesystem::exists(\"portable.txt\")) {\n        iris->pref_path = \"./\";\n    } else {\n        char* pref = SDL_GetPrefPath(\"Allkern\", \"Iris\");\n\n        iris->pref_path = std::string(pref);\n\n        SDL_free(pref);\n    }\n\n    if (!iris::emu::init(iris)) {\n        fprintf(stderr, \"iris: Failed to initialize emulator state\\n\");\n\n        return false;\n    }\n\n    if (!iris::settings::init(iris, argc, argv)) {\n        fprintf(stderr, \"iris: Failed to initialize settings\\n\");\n\n        return false;\n    }\n\n    iris->window = SDL_CreateWindow(\n        IRIS_TITLE,\n        iris->window_width, iris->window_height,\n        SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE |\n        SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_HIDDEN\n    );\n\n    if (!iris->window) {\n        printf(\"iris: Failed to create SDL window \\'%s\\'\\n\", SDL_GetError());\n\n        return false;\n    }\n\n    if (!iris::vulkan::init(iris, iris->vulkan_enable_validation_layers)) {\n        fprintf(stderr, \"iris: Failed to initialize Vulkan\\n\");\n\n        return false;\n    }\n\n    if (!iris::imgui::init(iris)) {\n        fprintf(stderr, \"iris: Failed to initialize ImGui\\n\");\n\n        return false;\n    }\n\n    if (!iris::platform::init(iris)) {\n        fprintf(stderr, \"iris: Failed to initialize platform\\n\");\n\n        return false;\n    }\n\n    if (!iris::audio::init(iris)) {\n        fprintf(stderr, \"iris: Failed to initialize audio\\n\");\n\n        return false;\n    }\n\n    if (!iris::render::init(iris)) {\n        fprintf(stderr, \"iris: Failed to initialize render state\\n\");\n\n        return false;\n    }\n\n    if (!iris::input::init(iris)) {\n        fprintf(stderr, \"iris: Failed to initialize input\\n\");\n\n        return false;\n    }\n\n    for (const std::string& s : iris->shader_passes_pending)\n        shaders::push(iris, s);\n\n    iris->shader_passes_pending.clear();\n\n    // Sadly we need to start a frame here to measure menubar height\n    ImGui_ImplVulkan_NewFrame();\n    ImGui_ImplSDL3_NewFrame();\n    ImGui::NewFrame();\n\n    iris->menubar_height = ImGui::GetFrameHeight();\n\n    ImGui::EndFrame();\n\n    if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {\n        ImGui::UpdatePlatformWindows();\n        ImGui::RenderPlatformWindowsDefault();\n    }\n\n    SDL_SetWindowSize(iris->window, iris->window_width, iris->window_height + get_menubar_height(iris));\n    SDL_ShowWindow(iris->window);\n\n    return true;\n}\n\nSDL_AppResult update(iris::instance* iris) {\n    if (iris->double_click_counter) {\n        iris->double_click_counter--;\n    }\n\n    if (iris->pause) {\n        iris->step_out = false;\n        iris->step_over = false;\n\n        if (iris->step) {\n            ps2_step_ee(iris->ps2);\n\n            iris->step = false;\n        }\n\n        iris::update_window(iris);\n\n        return SDL_APP_CONTINUE;\n    }\n\n    // Execute until VBlank\n    while (!ps2_gs_is_vblank(iris->ps2->gs)) {\n        do_cycle(iris);\n\n        if (iris->pause) {\n            iris::update_window(iris);\n\n            return SDL_APP_CONTINUE;\n        }\n    }\n\n    // Draw frame\n    iris::update_window(iris);\n    \n    // Execute until vblank is over\n    while (ps2_gs_is_vblank(iris->ps2->gs)) {\n        do_cycle(iris);\n\n        if (iris->pause) {\n            iris::update_window(iris);\n\n            return SDL_APP_CONTINUE;\n        }\n    }\n\n    // float p = ((float)iris->ps2->ee->eenull_counter / (float)(4920115)) * 100.0f;\n\n    // printf(\"ee: Time spent idling: %ld cycles (%.2f%%) INTC reads: %d CSR reads: %d (%.1f fps)\\n\", iris->ps2->ee->eenull_counter, p, iris->ps2->ee->intc_reads, iris->ps2->ee->csr_reads, 1.0f / ImGui::GetIO().DeltaTime);\n\n    iris->ps2->ee->eenull_counter = 0;\n    iris->ps2->ee->intc_reads = 0;\n    iris->ps2->ee->csr_reads = 0;\n\n    return SDL_APP_CONTINUE;\n}\n\nSDL_AppResult handle_events(iris::instance* iris, SDL_Event* event) {\n    ImGui_ImplSDL3_ProcessEvent(event);\n\n    switch (event->type) {\n        case SDL_EVENT_QUIT: {\n            return SDL_APP_SUCCESS;\n        } break;\n\n        case SDL_EVENT_MOUSE_BUTTON_DOWN: {\n            if (ImGui::GetIO().WantCaptureMouse) {\n                break;\n            }\n\n            if (event->button.button == SDL_BUTTON_LEFT && event->button.windowID == SDL_GetWindowID(iris->window)) {\n                if (iris->double_click_counter) {\n                    if ((SDL_GetTicks() - iris->double_click_counter) > iris->double_click_interval) {\n                        iris->double_click_counter = SDL_GetTicks();\n                    } else {\n                        iris->fullscreen = !iris->fullscreen;\n\n                        SDL_SetWindowFullscreen(iris->window, iris->fullscreen);\n                    }\n                } else {\n                    iris->double_click_counter = SDL_GetTicks();\n                }\n            }\n        } break;\n\n        case SDL_EVENT_GAMEPAD_ADDED: {\n            SDL_Gamepad* gamepad = SDL_OpenGamepad(event->gdevice.which);\n\n            if (!gamepad) {\n                SDL_Log(\"Failed to open gamepad ID %u: %s\", (unsigned int) event->gdevice.which, SDL_GetError());\n            }\n\n            if (iris->ds[0] && ((iris->input_devices[0] == nullptr) || (iris->input_devices[0]->get_type() == 0))) {\n                if (iris->input_devices[0]) delete iris->input_devices[0];\n\n                iris->input_devices[0] = new iris::gamepad_device(event->gdevice.which);\n                iris->input_devices[0]->set_slot(0);\n\n                if (iris->input_map[0] <= 1) {\n                    iris->input_map[0] = 1;\n                }\n\n                push_info(iris, \"\\'\" + std::string(SDL_GetGamepadName(gamepad)) + \"\\' connected to slot 1\");\n            } else if (iris->ds[1] && ((iris->input_devices[1] == nullptr) || (iris->input_devices[1]->get_type() == 0))) {\n                if (iris->input_devices[1]) delete iris->input_devices[1];\n\n                iris->input_devices[1] = new iris::gamepad_device(event->gdevice.which);\n                iris->input_devices[1]->set_slot(1);\n\n                if (iris->input_map[1] <= 1) {\n                    iris->input_map[1] = 1;\n                }\n\n                push_info(iris, \"\\'\" + std::string(SDL_GetGamepadName(gamepad)) + \"\\' connected to slot 2\");\n            } else {\n                push_info(iris, \"\\'\" + std::string(SDL_GetGamepadName(gamepad)) + \"\\' connected\");\n            }\n\n            iris->gamepads[event->gdevice.which] = gamepad;\n        } break;\n\n        case SDL_EVENT_GAMEPAD_REMOVED: {\n            SDL_Gamepad* gamepad = iris->gamepads[event->gdevice.which];\n\n            for (int i = 0; i < 2; i++) {\n                if (iris->input_devices[i] && iris->input_devices[i]->get_type() == 1) {\n                    iris::gamepad_device* gp = static_cast<iris::gamepad_device*>(iris->input_devices[i]);\n\n                    if (gp->get_id() == event->gdevice.which) {\n                        delete iris->input_devices[i];\n                        iris->input_devices[i] = new iris::keyboard_device();\n\n                        if (iris->input_map[i] <= 1) {\n                            iris->input_map[i] = 0;\n                        }\n\n                        push_info(iris, \"\\'\" + std::string(SDL_GetGamepadName(gamepad)) + \"\\' in slot \" + std::to_string(i + 1) + \" disconnected\");\n                    }\n                }\n            }\n\n            if (gamepad) {\n                SDL_CloseGamepad(gamepad);\n\n                iris->gamepads.erase(event->gdevice.which);\n            }\n        } break;\n\n        case SDL_EVENT_WINDOW_CLOSE_REQUESTED: {\n            if (event->window.windowID == SDL_GetWindowID(iris->window)) {\n                return SDL_APP_SUCCESS;\n            }\n        } break;\n\n        case SDL_EVENT_GAMEPAD_BUTTON_DOWN:\n        case SDL_EVENT_GAMEPAD_BUTTON_UP:\n        case SDL_EVENT_GAMEPAD_AXIS_MOTION:\n        case SDL_EVENT_KEY_UP: {\n            iris->last_input_event_read = false;\n            iris->last_input_event = input::sdl_event_to_input_event(event);\n\n            if (event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION) {\n                iris->last_input_event_value = fabs(event->gaxis.value / 32767.0f);\n            } else {\n                iris->last_input_event_value = 1.0f;\n            }\n\n            if (iris->input_devices[0]) iris->input_devices[0]->handle_event(iris, event);\n            if (iris->input_devices[1]) iris->input_devices[1]->handle_event(iris, event);\n        } break;\n\n        case SDL_EVENT_KEY_DOWN: {\n            input::handle_keydown_event(iris, event);\n        } break;\n\n        case SDL_EVENT_DROP_BEGIN: {\n            iris->drop_file_active = true;\n            iris->drop_file_alpha = 0.0f;\n            iris->drop_file_alpha_delta = 1.0f / 10.0f;\n            iris->drop_file_alpha_target = 1.0f;\n        } break;\n        \n        case SDL_EVENT_DROP_COMPLETE: {\n            iris->drop_file_active = true;\n            iris->drop_file_alpha = iris->drop_file_alpha_target;\n            iris->drop_file_alpha_delta = -(1.0f / 10.0f);\n            iris->drop_file_alpha_target = 0.0f;\n        } break;\n\n        case SDL_EVENT_DROP_FILE: {\n            if (!event->drop.data)\n                break;\n\n            std::string path(event->drop.data);\n\n            std::filesystem::path p(path);\n\n            if (std::filesystem::is_regular_file(p)) {\n                if (open_file(iris, path)) {\n                    push_info(iris, \"Failed to open file: \" + path);\n                } else {\n                    add_recent(iris, path, RECENT_TYPE_PS2);\n                }\n            } else {\n                if (emu::load_arcade(iris, path)) {\n                    add_recent(iris, path, RECENT_TYPE_ARCADE);\n                } else {\n                    push_info(iris, \"Failed to boot arcade: \" + path);\n                }\n            }\n\n            // Maybe not needed anymore?\n            // SDL_free(event->drop.data);\n        } break;\n    }\n\n    return SDL_APP_CONTINUE;\n}\n\nint get_menubar_height(iris::instance* iris) {\n    if (iris->show_status_bar) {\n        return iris->menubar_height * 2;\n    }\n\n    return iris->menubar_height;\n}\n\nvoid destroy(iris::instance* iris) {\n    for (int i = 0; i < 2; i++) {\n        if (iris->input_devices[i]) {\n            delete iris->input_devices[i];\n            iris->input_devices[i] = nullptr;\n        }\n    }\n\n    if (iris->imgui_enable_viewports) {\n        iris->show_ee_control = false;\n        iris->show_ee_state = false;\n        iris->show_ee_logs = false;\n        iris->show_ee_interrupts = false;\n        iris->show_ee_dmac = false;\n        iris->show_iop_control = false;\n        iris->show_iop_state = false;\n        iris->show_iop_logs = false;\n        iris->show_iop_interrupts = false;\n        iris->show_iop_modules = false;\n        iris->show_iop_dma = false;\n        iris->show_gs_debugger = false;\n        iris->show_spu2_debugger = false;\n        iris->show_memory_viewer = false;\n        iris->show_memory_search = false;\n        iris->show_vu_disassembler = false;\n        iris->show_breakpoints = false;\n        iris->show_threads = false;\n        iris->show_sysmem_logs = false;\n        iris->show_imgui_demo = false;\n        iris->show_overlay = false;\n    }\n\n    if (iris->window) SDL_HideWindow(iris->window);\n\n    iris::imgui::cleanup(iris);\n    iris::audio::close(iris);\n    iris::settings::close(iris);\n    iris::render::destroy(iris);\n    iris::vulkan::cleanup(iris);\n    iris::platform::destroy(iris);\n    iris::emu::destroy(iris);\n\n    if (iris->window) SDL_DestroyWindow(iris->window);\n\n    SDL_Quit();\n\n    delete iris;\n}\n\n}"
  },
  {
    "path": "frontend/iris.hpp",
    "content": "#pragma once\n\n#include <unordered_map>\n#include <cstdint>\n#include <string>\n#include <vector>\n#include <chrono>\n#include <array>\n#include <deque>\n\n#include \"gs/renderer/renderer.hpp\"\n#include \"gs/renderer/config.hpp\"\n\n#include <SDL3/SDL.h>\n#include <volk.h>\n\n#include \"imgui.h\"\n#include \"imgui_impl_sdl3.h\"\n#include \"imgui_impl_vulkan.h\"\n\n#include \"ps2.h\"\n#include \"config.hpp\"\n\nnamespace iris {\n\n#define RENDER_ASPECT_NATIVE 0\n#define RENDER_ASPECT_STRETCH 1\n#define RENDER_ASPECT_STRETCH_KEEP 2\n#define RENDER_ASPECT_4_3 3\n#define RENDER_ASPECT_16_9 4\n#define RENDER_ASPECT_5_4 5\n#define RENDER_ASPECT_AUTO 6\n\n#define IRIS_THEME_GRANITE 0\n#define IRIS_THEME_IMGUI_DARK 1\n#define IRIS_THEME_IMGUI_LIGHT 2\n#define IRIS_THEME_IMGUI_CLASSIC 3\n#define IRIS_THEME_CHERRY 4\n#define IRIS_THEME_SOURCE 5\n\n#define IRIS_SCREENSHOT_FORMAT_PNG 0\n#define IRIS_SCREENSHOT_FORMAT_BMP 1\n#define IRIS_SCREENSHOT_FORMAT_JPG 2\n#define IRIS_SCREENSHOT_FORMAT_TGA 3\n\n#define IRIS_SCREENSHOT_MODE_INTERNAL 0\n#define IRIS_SCREENSHOT_MODE_DISPLAY 1\n\n#define IRIS_SCREENSHOT_JPG_QUALITY_MINIMUM 0\n#define IRIS_SCREENSHOT_JPG_QUALITY_LOW 1\n#define IRIS_SCREENSHOT_JPG_QUALITY_MEDIUM 2\n#define IRIS_SCREENSHOT_JPG_QUALITY_HIGH 3\n#define IRIS_SCREENSHOT_JPG_QUALITY_MAXIMUM 4\n#define IRIS_SCREENSHOT_JPG_QUALITY_CUSTOM 5 \n\n#define IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_DARK 0\n#define IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_LIGHT 1\n#define IRIS_CODEVIEW_COLOR_SCHEME_ONE_DARK_PRO 2\n#define IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_LATTE 3\n#define IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_FRAPPE 4\n#define IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MACCHIATO 5\n#define IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MOCHA 6\n\n#define IRIS_TITLEBAR_DEFAULT 0\n#define IRIS_TITLEBAR_SEAMLESS 1\n\nclass instance;\n\n// class widget {\n// public:\n//     virtual bool init(iris::instance* iris) = 0;\n//     virtual void render(iris::instance* iris) = 0;\n//     virtual ~widget() = default;\n// };\n\nenum : int {\n    BKPT_CPU_EE,\n    BKPT_CPU_IOP\n};\n\nstruct breakpoint {\n    uint32_t addr;\n    const char* symbol = nullptr;\n    int cpu;\n    bool cond_r, cond_w, cond_x;\n    int size;\n    bool enabled;\n};\n\nstruct move_animation {\n    int frames;\n    int frames_remaining;\n    float source_x, source_y;\n    float target_x, target_y;\n    float x, y;\n};\n\nstruct fade_animation {\n    int frames;\n    int frames_remaining;\n    int source_alpha, target_alpha;\n    int alpha;\n};\n\nstruct notification {\n    int type;\n    int state;\n    int frames;\n    int frames_remaining;\n    float width, height;\n    float text_width, text_height;\n    bool end;\n    move_animation move;\n    fade_animation fade;\n    std::string text;\n};\n\nstruct elf_symbol {\n    char* name;\n    uint32_t addr;\n    uint32_t size;\n};\n\nenum {\n    RECENT_TYPE_PS2,\n    RECENT_TYPE_ARCADE\n};\n\nenum {\n    INPUT_CONTROLLER_DUALSHOCK2\n\n    // Large To-do list here, we're missing the Namco GunCon\n    // controllers, JogCon, NegCon, Buzz! Buzzer, the Train\n    // controllers, Taiko Drum Master controller, the Dance Dance\n    // Revolution mat, Guitar Hero controllers, etc.\n};\n\nstruct input_device {\n    int m_slot;\n\n    void set_slot(int slot) {\n        this->m_slot = slot;\n    }\n\n    int get_slot() {\n        return this->m_slot;\n    }\n\n    virtual int get_type() = 0;\n    virtual void handle_event(iris::instance* iris, SDL_Event* event) = 0;\n};\n\nclass keyboard_device : public input_device {\npublic:\n    int get_type() override {\n        return 0;\n    }\n\n    void handle_event(iris::instance* iris, SDL_Event* event) override;\n};\n\nclass gamepad_device : public input_device {\n    SDL_JoystickID id;\n\npublic:\n    gamepad_device(SDL_JoystickID id) : id(id) {}\n\n    int get_type() override {\n        return 1;\n    }\n\n    SDL_JoystickID get_id() {\n        return id;\n    }\n\n    void handle_event(iris::instance* iris, SDL_Event* event) override;\n};\n\nunion input_event {\n    struct {\n        uint32_t id;\n        uint32_t type;\n    };\n\n    uint64_t u64;\n};\n\nenum event {\n    IRIS_EVENT_KEYBOARD,\n    IRIS_EVENT_GAMEPAD_BUTTON,\n    IRIS_EVENT_GAMEPAD_AXIS_POS,\n    IRIS_EVENT_GAMEPAD_AXIS_NEG\n};\n\nenum input_action : uint32_t {\n    IRIS_DS_BT_CROSS,\n    IRIS_DS_BT_CIRCLE,\n    IRIS_DS_BT_SQUARE,\n    IRIS_DS_BT_TRIANGLE,\n    IRIS_DS_BT_START,\n    IRIS_DS_BT_SELECT,\n    IRIS_DS_BT_ANALOG,\n    IRIS_DS_BT_UP,\n    IRIS_DS_BT_DOWN,\n    IRIS_DS_BT_LEFT,\n    IRIS_DS_BT_RIGHT,\n    IRIS_DS_BT_L1,\n    IRIS_DS_BT_R1,\n    IRIS_DS_BT_L2,\n    IRIS_DS_BT_R2,\n    IRIS_DS_BT_L3,\n    IRIS_DS_BT_R3,\n    IRIS_DS_AX_RIGHTV_POS,\n    IRIS_DS_AX_RIGHTV_NEG,\n    IRIS_DS_AX_RIGHTH_POS,\n    IRIS_DS_AX_RIGHTH_NEG,\n    IRIS_DS_AX_LEFTV_POS,\n    IRIS_DS_AX_LEFTV_NEG,\n    IRIS_DS_AX_LEFTH_POS,\n    IRIS_DS_AX_LEFTH_NEG,\n\n    IRIS_S14X_SW_DOWN,\n    IRIS_S14X_SW_UP,\n    IRIS_S14X_SW_ENTER,\n    IRIS_S14X_SW_TEST,\n    IRIS_S14X_SW_SERVICE,\n    IRIS_S14X_SW_P1_START,\n    IRIS_S14X_SW_P2_START,\n    IRIS_S14X_SW_P3_START,\n    IRIS_S14X_SW_P4_START,\n    IRIS_INPUT_ACTION_MAX\n};\n\nstruct vertex {\n    struct {\n        float x, y;\n    } pos, uv;\n\n    static constexpr const VkVertexInputBindingDescription get_binding_description() {\n        VkVertexInputBindingDescription binding_description = {};\n\n        binding_description.binding = 0;\n        binding_description.stride = sizeof(vertex);\n        binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX;\n\n        return binding_description;\n    }\n\n    static constexpr const std::array<VkVertexInputAttributeDescription, 2> get_attribute_descriptions() {\n        std::array<VkVertexInputAttributeDescription, 2> attribute_descriptions = {};\n\n        attribute_descriptions[0].binding = 0;\n        attribute_descriptions[0].location = 0;\n        attribute_descriptions[0].format = VK_FORMAT_R32G32_SFLOAT;\n        attribute_descriptions[0].offset = offsetof(vertex, pos);\n\n        attribute_descriptions[1].binding = 0;\n        attribute_descriptions[1].location = 1;\n        attribute_descriptions[1].format = VK_FORMAT_R32G32_SFLOAT;\n        attribute_descriptions[1].offset = offsetof(vertex, uv);\n\n        return attribute_descriptions;\n    }\n};\n\nstruct texture {\n    int width = 0, height = 0, stride = 0;\n    VkDeviceSize image_size = 0;\n    VkImage image = VK_NULL_HANDLE;\n    VkImageView image_view = VK_NULL_HANDLE;\n    VkSampler sampler = VK_NULL_HANDLE;\n    VkDeviceMemory image_memory = VK_NULL_HANDLE;\n    VkDescriptorSet descriptor_set = VK_NULL_HANDLE;\n};\n\nnamespace shaders {\n    class pass;\n}\n\nstruct vulkan_gpu {\n    VkPhysicalDeviceType type = VK_PHYSICAL_DEVICE_TYPE_OTHER;\n    VkPhysicalDevice device = VK_NULL_HANDLE;\n    std::string name = \"\";\n    uint32_t api_version = 0;\n};\n\ntemplate <typename Key, typename Value> class bidirectional_map {\n    std::unordered_map <Key, Value> m_forward_map;\n    std::unordered_map <Value, Key> m_reverse_map;\n\npublic:\n    void insert(const Key& key, const Value& value) {\n        m_forward_map[key] = value;\n        m_reverse_map[value] = key;\n    }\n\n    std::unordered_map <Key, Value>& forward_map() {\n        return m_forward_map;\n    }\n\n    std::unordered_map <Value, Key>& reverse_map() {\n        return m_reverse_map;\n    }\n\n    bool erase_by_key(const Key& key) {\n        auto it = m_forward_map.find(key);\n        if (it != m_forward_map.end()) {\n            Value value = it->second;\n            m_forward_map.erase(it);\n            m_reverse_map.erase(value);\n            return true;\n        }\n        return false;\n    }\n\n    bool erase_by_value(const Value& value) {\n        auto it = m_reverse_map.find(value);\n        if (it != m_reverse_map.end()) {\n            Key key = it->second;\n            m_reverse_map.erase(it);\n            m_forward_map.erase(key);\n            return true;\n        }\n        return false;\n    }\n\n    void clear() {\n        m_forward_map.clear();\n        m_reverse_map.clear();\n    }\n\n    Value* get_value(const Key& key) {\n        auto it = m_forward_map.find(key);\n        if (it != m_forward_map.end()) {\n            return &it->second;\n        }\n        return nullptr;\n    }\n\n    Key* get_key(const Value& value) {\n        auto it = m_reverse_map.find(value);\n        if (it != m_reverse_map.end()) {\n            return &it->second;\n        }\n        return nullptr;\n    }\n};\n\nstruct mapping {\n    std::string name;\n    bidirectional_map <uint64_t, input_action> map;\n};\n\nstruct recent {\n    std::string path;\n    int type;\n};\n\nstruct instance {\n    SDL_Window* window = nullptr;\n    SDL_AudioStream* stream = nullptr;\n\n    // Vulkan state\n    std::vector <VkExtensionProperties> instance_extensions;\n    std::vector <VkLayerProperties> instance_layers;\n    std::vector <VkExtensionProperties> device_extensions;\n    std::vector <VkLayerProperties> device_layers;\n    std::vector <const char*> enabled_instance_extensions;\n    std::vector <const char*> enabled_instance_layers;\n    std::vector <const char*> enabled_device_extensions;\n    std::vector <const char*> enabled_device_layers;\n    std::vector <vulkan_gpu> vulkan_gpus;\n    VkApplicationInfo app_info = {};\n    VkInstanceCreateInfo instance_create_info = {};\n    VkDeviceCreateInfo device_create_info = {};\n    VkInstance instance = VK_NULL_HANDLE;\n    VkPhysicalDevice physical_device = VK_NULL_HANDLE;\n    VkPhysicalDeviceFeatures2 device_features = {};\n    VkDeviceQueueCreateInfo queue_create_info = {};\n    uint32_t queue_family = (uint32_t)-1;\n    VkQueue queue = VK_NULL_HANDLE;\n    VkDevice device = VK_NULL_HANDLE;\n    VkDescriptorPool descriptor_pool = VK_NULL_HANDLE;\n    ImGui_ImplVulkanH_Window main_window_data = {};\n    uint32_t min_image_count = 2;\n    bool swapchain_rebuild = false;\n    VkSurfaceKHR surface = VK_NULL_HANDLE;\n    float main_scale = 1;\n    VkPhysicalDeviceVulkan11Features vulkan_11_features = {};\n    VkPhysicalDeviceVulkan12Features vulkan_12_features = {};\n    VkPhysicalDeviceSubgroupSizeControlFeatures subgroup_size_control_features = {};\n    VkSampler sampler[3] = { VK_NULL_HANDLE };\n    bool cubic_supported = false;\n    VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE;\n    VkDescriptorSet descriptor_set = VK_NULL_HANDLE;\n    std::vector <VkDescriptorSet> descriptor_sets = {};\n    VkPipelineLayout pipeline_layout = VK_NULL_HANDLE;\n    VkRenderPass render_pass = VK_NULL_HANDLE;\n    VkPipeline pipeline = VK_NULL_HANDLE;\n    VkClearValue clear_value = { 0.11, 0.11, 0.11, 1.0 };\n    VkBuffer vertex_buffer = VK_NULL_HANDLE;\n    VkDeviceMemory vertex_buffer_memory = VK_NULL_HANDLE;\n    VkBuffer vertex_staging_buffer = VK_NULL_HANDLE;\n    VkDeviceMemory vertex_staging_buffer_memory = VK_NULL_HANDLE;\n    VkDeviceSize vertex_buffer_size = 0;\n    VkBuffer index_buffer = VK_NULL_HANDLE;\n    VkDeviceMemory index_buffer_memory = VK_NULL_HANDLE;\n    std::array <vertex, 4> vertices = {};\n    std::array <uint16_t, 6> indices = {};\n    renderer_image image = {};\n    renderer_image output_image = {};\n\n    // Multipass shader stuff\n    std::vector <std::string> shader_passes_pending;\n    std::vector <shaders::pass*> shader_passes = {};\n    VkDescriptorSetLayout shader_descriptor_set_layout = VK_NULL_HANDLE;\n    VkDescriptorSet shader_descriptor_set = VK_NULL_HANDLE;\n    std::vector <VkDescriptorSet> shader_descriptor_sets = {};\n    VkShaderModule default_vert_shader = VK_NULL_HANDLE;\n\n    struct {\n        VkImage image = VK_NULL_HANDLE;\n        VkDeviceMemory memory = VK_NULL_HANDLE;\n        VkImageView view = VK_NULL_HANDLE;\n    } shader_framebuffers[2];\n\n    std::vector <std::array <VkFramebuffer, 2>> shader_pass_framebuffers = {};\n\n    struct ps2_state* ps2 = nullptr;\n\n    unsigned int window_width = 960;\n    unsigned int window_height = 720;\n    unsigned int render_width = 640;\n    unsigned int render_height = 480;\n\n    unsigned int renderer_backend = RENDERER_BACKEND_HARDWARE;\n    renderer_state* renderer = nullptr;\n\n    texture ps2_memory_card_icon = {};\n    texture ps1_memory_card_icon = {};\n    texture pocketstation_icon = {};\n    texture dualshock2_icon = {};\n    texture iris_icon = {};\n\n    ImFont* font_small_code = nullptr;\n    ImFont* font_code = nullptr;\n    ImFont* font_small = nullptr;\n    ImFont* font_heading = nullptr;\n    ImFont* font_body = nullptr;\n    ImFont* font_icons = nullptr;\n    ImFont* font_icons_big = nullptr;\n    ImFont* font_black = nullptr;\n\n    std::string elf_path = \"\";\n    std::string boot_path = \"\";\n    std::string bios_path = \"\";\n    std::string rom1_path = \"\";\n    std::string rom2_path = \"\";\n    std::string nvram_path = \"\";\n    std::string disc_path = \"\";\n    std::string pref_path = \"\";\n    std::string mcd0_path = \"\";\n    std::string mcd1_path = \"\";\n    std::string snap_path = \"\";\n    std::string flash_path = \"\";\n    std::string ini_path = \"\";\n    std::string gcdb_path = \"\";\n\n    uint8_t mac_address[6] = { 0 };\n\n    bool core0_mute[24] = { false };\n    bool core1_mute[24] = { false };\n    int core0_solo = -1;\n    int core1_solo = -1;\n\n    bool open = false;\n    bool pause = true;\n    bool step = false;\n    bool step_over = false;\n    bool step_out = false;\n    uint32_t step_over_addr = 0;\n\n    bool show_ee_control = false;\n    bool show_ee_state = false;\n    bool show_ee_logs = false;\n    bool show_ee_interrupts = false;\n    bool show_ee_dmac = false;\n    bool show_iop_control = false;\n    bool show_iop_state = false;\n    bool show_iop_logs = false;\n    bool show_iop_interrupts = false;\n    bool show_iop_modules = false;\n    bool show_iop_dma = false;\n    bool show_sysmem_logs = false;\n    bool show_gs_debugger = false;\n    bool show_spu2_debugger = false;\n    bool show_memory_viewer = false;\n    bool show_status_bar = true;\n    bool show_breakpoints = false;\n    bool show_settings = false;\n    bool show_pad_debugger = false;\n    bool show_symbols = false;\n    bool show_threads = false;\n    bool show_memory_card_tool = false;\n    bool show_imgui_demo = false;\n    bool show_vu_disassembler = false;\n    bool show_overlay = false;\n    bool show_memory_search = false;\n\n    // Special windows\n    bool show_bios_setting_window = false;\n    bool show_about_window = false;\n\n    bool fullscreen = false;\n    int aspect_mode = RENDER_ASPECT_AUTO;\n    int filter = 1;\n    bool integer_scaling = false;\n    float scale = 1.5f;\n    int window_mode = 0;\n    bool ee_control_follow_pc = true;\n    bool iop_control_follow_pc = true;\n    uint32_t ee_control_address = 0;\n    uint32_t iop_control_address = 0;\n    bool skip_fmv = false;\n    int system = PS2_SYSTEM_AUTO;\n    int theme = IRIS_THEME_GRANITE;\n    bool enable_shaders = false;\n    int vulkan_physical_device = -1;\n    int vulkan_selected_device_index = 0;\n    bool vulkan_enable_validation_layers = false;\n    bool imgui_enable_viewports = false;\n    int codeview_color_scheme = 0;\n    ImColor codeview_color_text = IM_COL32(131, 148, 150, 255);\n    ImColor codeview_color_comment = IM_COL32(88, 110, 117, 255);\n    ImColor codeview_color_mnemonic = IM_COL32(211, 167, 30, 255);\n    ImColor codeview_color_number = IM_COL32(138, 143, 226, 255);\n    ImColor codeview_color_register = IM_COL32(68, 169, 240, 255);\n    ImColor codeview_color_other = IM_COL32(89, 89, 89, 255);\n    ImColor codeview_color_background = IM_COL32(30, 30, 30, 255);\n    ImColor codeview_color_highlight = IM_COL32(75, 75, 75, 255);\n    float codeview_font_scale = 1.0f;\n    bool codeview_use_theme_background = true;\n    bool autostart = true;\n    int angle = 0;\n    bool flip_x = false;\n    bool flip_y = false;\n    uint64_t double_click_interval = 500;\n    uint64_t double_click_counter = 0;\n\n    std::deque <recent> recents;\n\n    bool dump_to_file = true;\n    std::string settings_path = \"\";\n    std::string mappings_path = \"\";\n\n    int frames = 0;\n    float fps = 0.0f;\n    unsigned int ticks = 0;\n    int menubar_height = 0;\n    bool mute = false;\n    bool prev_mute = false;\n    float volume = 1.0f;\n    int timescale = 8;\n    bool mute_adma = true;\n    bool vsync = true;\n    float ui_scale = 1.0f;\n    int screenshot_format = IRIS_SCREENSHOT_FORMAT_PNG;\n    int screenshot_jpg_quality_mode = IRIS_SCREENSHOT_JPG_QUALITY_MAXIMUM;\n    int screenshot_jpg_quality = 50;\n    int screenshot_mode = IRIS_SCREENSHOT_MODE_INTERNAL;\n    int docking_mode = 0;\n    bool screenshot_shader_processing = false;\n    input_device* input_devices[2] = { nullptr };\n    std::unordered_map <SDL_JoystickID, SDL_Gamepad*> gamepads;\n    std::vector <mapping> input_maps = {};\n    int input_map[2] = { -1, -1 };\n    input_event last_input_event = {};\n    bool last_input_event_read = true;\n    float last_input_event_value = 0.0f;\n\n    bool limit_fps = true;\n    float fps_cap = 60.0f;\n\n    std::string loaded = \"\";\n\n    std::vector <std::string> ee_log = { \"\" };\n    std::vector <std::string> iop_log = { \"\" };\n    std::vector <std::string> sysmem_log = { \"\" };\n\n    std::vector <iris::breakpoint> breakpoints = {};\n    std::deque <iris::notification> notifications = {};\n\n    struct ds_state* ds[2] = { nullptr };\n    struct mcd_state* mcd[2] = { nullptr };\n    int mcd_slot_type[2] = { 0 };\n\n    // input_device* device[2];\n\n    float drop_file_alpha = 0.0f;\n    float drop_file_alpha_delta = 0.0f;\n    float drop_file_alpha_target = 0.0f;\n    bool drop_file_active = false;\n\n    // Debug\n    std::vector <elf_symbol> symbols;\n    std::vector <uint8_t> strtab;\n\n    std::vector <spu2_sample> audio_buf;\n\n    float avg_fps;\n    float avg_frames;\n    int screenshot_counter = 0;\n\n    // Renderer configs\n    hardware_config hardware_backend_config;\n\n    // Windows-specific settings\n#ifdef _WIN32\n    int windows_titlebar_style = IRIS_TITLEBAR_DEFAULT;\n    bool windows_enable_borders = true;\n    bool windows_dark_mode = true;\n#endif\n};\n\nstruct push_constants {\n    float resolution[2];\n    int frame;\n};\n\nnamespace audio {\n    bool init(iris::instance* iris);\n    void close(iris::instance* iris);\n    void update(void* udata, SDL_AudioStream* stream, int additional_amount, int total_amount);\n    bool mute(iris::instance* iris);\n    void unmute(iris::instance* iris);\n}\n\nnamespace settings {\n    bool init(iris::instance* iris, int argc, const char* argv[]);\n    bool check_for_quick_exit(int argc, const char* argv[]);\n    void close(iris::instance* iris);\n}\n\nnamespace shaders {\n    class pass {\n        VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE;\n        VkPipeline m_pipeline = VK_NULL_HANDLE;\n        VkRenderPass m_render_pass = VK_NULL_HANDLE;\n        VkImageView m_input = VK_NULL_HANDLE;\n        VkShaderModule m_vert_shader = VK_NULL_HANDLE;\n        VkShaderModule m_frag_shader = VK_NULL_HANDLE;\n        iris::instance* m_iris = nullptr;\n        std::string m_id;\n\n    public:\n        void destroy();\n        bool init(iris::instance* iris, const void* data, size_t size, std::string id);\n\n        void swap(pass& rhs) {\n            VkPipelineLayout pipeline_layout = m_pipeline_layout;\n            VkPipeline pipeline = m_pipeline;\n            VkRenderPass render_pass = m_render_pass;\n            VkImageView input = m_input;\n            VkShaderModule vert_shader = m_vert_shader;\n            VkShaderModule frag_shader = m_frag_shader;\n            iris::instance* iris = m_iris;\n            std::string id = m_id;\n\n            m_pipeline_layout = rhs.m_pipeline_layout;\n            m_pipeline = rhs.m_pipeline;\n            m_render_pass = rhs.m_render_pass;\n            m_input = rhs.m_input;\n            m_vert_shader = rhs.m_vert_shader;\n            m_frag_shader = rhs.m_frag_shader;\n            m_iris = rhs.m_iris;\n            m_id = rhs.m_id;\n\n            rhs.m_pipeline_layout = pipeline_layout;\n            rhs.m_pipeline = pipeline;\n            rhs.m_render_pass = render_pass;\n            rhs.m_input = input;\n            rhs.m_vert_shader = vert_shader;\n            rhs.m_frag_shader = frag_shader;\n            rhs.m_iris = iris;\n            rhs.m_id = id;\n        }\n\n        pass(iris::instance* iris, const void* data, size_t size, std::string id);\n        pass(pass&& other);\n        pass() = default;\n        ~pass();\n\n        pass& operator=(pass&& other);\n\n        VkPipelineLayout& get_pipeline_layout();\n        VkPipeline& get_pipeline();\n        VkRenderPass& get_render_pass();\n        VkImageView& get_input();\n        VkShaderModule& get_vert_shader();\n        VkShaderModule& get_frag_shader();\n        std::string get_id() const;\n\n        bool bypass = false;\n        bool ready();\n        bool rebuild();\n    };\n\n    void push(iris::instance* iris, void* data, size_t size, std::string id);\n    void push(iris::instance* iris, std::string id);\n    void pop(iris::instance* iris);\n    void insert(iris::instance* iris, int i, void* data, size_t size, std::string id);\n    void insert(iris::instance* iris, std::string id);\n    void erase(iris::instance* iris, int i);\n    pass* at(iris::instance* iris, int i);\n    void swap(iris::instance* iris, int i1, int i2);\n    pass* front(iris::instance* iris);\n    pass* back(iris::instance* iris);\n    size_t count(iris::instance* iris);\n    void clear(iris::instance* iris);\n    std::vector <shaders::pass*>& vector(iris::instance* iris);\n}\n\nnamespace imgui {\n    bool init(iris::instance* iris);\n    void set_theme(iris::instance* iris, int theme, bool set_bg_color = true);\n    void set_codeview_scheme(iris::instance* iris, int scheme);\n    bool render_frame(iris::instance* iris, ImDrawData* draw_data);\n    void cleanup(iris::instance* iris);\n    void set_vsync(iris::instance* iris, bool vsync);\n\n    // Wrapper for ImGui::Begin that sets a default size\n    bool BeginEx(const char* name, bool* p_open, ImGuiWindowFlags flags = 0);\n}\n\nnamespace vulkan {\n    bool init(iris::instance* iris, bool enable_validation = false);\n    void cleanup(iris::instance* iris);\n    texture upload_texture(iris::instance* iris, void* pixels, int width, int height, int stride);\n    void free_texture(iris::instance* iris, texture& tex);\n    void* read_image(iris::instance* iris, VkImage image, VkFormat format, int width, int height);\n    void wait_idle(iris::instance* iris);\n}\n\nnamespace platform {\n    bool init(iris::instance* iris);\n    bool apply_settings(iris::instance* iris);\n    void destroy(iris::instance* iris);\n}\n\nnamespace elf {\n    bool load_symbols_from_disc(iris::instance* iris);\n    bool load_symbols_from_file(iris::instance* iris, std::string path);\n}\n\nnamespace emu {\n    bool init(iris::instance* iris);\n    void destroy(iris::instance* iris);\n    bool load_arcade(iris::instance* iris, std::string path);\n    int attach_memory_card(iris::instance* iris, int slot, const char* path);\n    void detach_memory_card(iris::instance* iris, int slot);\n    const char* get_system_name(iris::instance* iris, int system);\n    const char* get_current_system_name(iris::instance* iris);\n    int get_system_count(iris::instance* iris);\n}\n\nnamespace render {\n    bool init(iris::instance* iris);\n    void destroy(iris::instance* iris);\n    bool render_frame(iris::instance* iris, VkCommandBuffer command_buffer, VkFramebuffer framebuffer);\n    bool save_screenshot(iris::instance* iris, std::string path);\n    void switch_backend(iris::instance* iris, int backend);\n    void refresh(iris::instance* iris);\n}\n\nnamespace input {\n    bool init(iris::instance* iris);\n    void init_default_mapping(iris::instance* iris, int id);\n    void load_db_default(iris::instance* iris);\n    bool load_db_from_file(iris::instance* iris, const char* path);\n    input_action* get_input_action(iris::instance* iris, int slot, uint64_t input);\n    input_event sdl_event_to_input_event(SDL_Event* event);\n    std::string get_default_screenshot_filename(iris::instance* iris);\n    void execute_action(iris::instance* iris, input_action action, int slot, float value);\n    bool save_screenshot(iris::instance* iris, std::string path = \"\");\n    void handle_keydown_event(iris::instance* iris, SDL_Event* event);\n    void handle_keyup_event(iris::instance* iris, SDL_Event* event);\n}\n\niris::instance* create();\nbool init(iris::instance* iris, int argc, const char* argv[]);\nvoid destroy(iris::instance* iris);\nSDL_AppResult handle_events(iris::instance* iris, SDL_Event* event);\nSDL_AppResult update(iris::instance* iris);\nvoid update_window(iris::instance* iris);\nint get_menubar_height(iris::instance* iris);\n\nvoid show_main_menubar(iris::instance* iris);\nvoid show_ee_control(iris::instance* iris);\nvoid show_ee_state(iris::instance* iris);\nvoid show_ee_logs(iris::instance* iris);\nvoid show_ee_interrupts(iris::instance* iris);\nvoid show_ee_dmac(iris::instance* iris);\nvoid show_iop_control(iris::instance* iris);\nvoid show_iop_state(iris::instance* iris);\nvoid show_iop_logs(iris::instance* iris);\nvoid show_iop_interrupts(iris::instance* iris);\nvoid show_iop_modules(iris::instance* iris);\nvoid show_iop_dma(iris::instance* iris);\nvoid show_sysmem_logs(iris::instance* iris);\nvoid show_gs_debugger(iris::instance* iris);\nvoid show_spu2_debugger(iris::instance* iris);\nvoid show_memory_viewer(iris::instance* iris);\nvoid show_vu_disassembler(iris::instance* iris);\nvoid show_status_bar(iris::instance* iris);\nvoid show_breakpoints(iris::instance* iris);\nvoid show_about_window(iris::instance* iris);\nvoid show_settings(iris::instance* iris);\nvoid show_pad_debugger(iris::instance* iris);\nvoid show_symbols(iris::instance* iris);\nvoid show_threads(iris::instance* iris);\nvoid show_overlay(iris::instance* iris);\nvoid show_memory_card_tool(iris::instance* iris);\nvoid show_bios_setting_window(iris::instance* iris);\nvoid show_memory_search(iris::instance* iris);\n// void show_gamelist(iris::instance* iris);\n\nvoid handle_keydown_event(iris::instance* iris, SDL_Event* event);\nvoid handle_keyup_event(iris::instance* iris, SDL_Event* event);\nvoid handle_scissor_event(void* udata);\nvoid handle_drag_and_drop_event(void* udata, const char* path);\nvoid handle_ee_tty_event(void* udata, char c);\nvoid handle_iop_tty_event(void* udata, char c);\nvoid handle_sysmem_tty_event(void* udata, char c);\n\nvoid handle_animations(iris::instance* iris);\n\nvoid push_info(iris::instance* iris, std::string text);\n\nvoid add_recent(iris::instance* iris, std::string file, int type);\nint open_file(iris::instance* iris, std::string file);\n\n}"
  },
  {
    "path": "frontend/notifications.cpp",
    "content": "#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nenum {\n    STATE_IDLE,\n    STATE_MOVING = 1,\n    STATE_FADING = 2\n};\n\nenum {\n    TYPE_INFO = 0,\n};\n\nstatic inline int seconds_to_frames(float s) {\n    return s * 60.0f;\n}\n\nfloat ease_in_out(float t) {\n    if (t <= 0.5f) return 2.0f * t * t;\n\n    t -= 0.5f;\n\n    return 2.0f * t * (1.0f - t) + 0.5f;\n}\n\nvoid handle_move(iris::notification* notif) {\n    float f = (float)notif->move.frames_remaining / (float)notif->move.frames;\n\n    f = ease_in_out(f);\n\n    float fs = f;\n    float ft = 1.0 - f;\n\n    notif->move.x = notif->move.source_x * fs + notif->move.target_x * ft;\n    notif->move.y = notif->move.source_y * fs + notif->move.target_y * ft;\n\n    if (!notif->move.frames_remaining) {\n        notif->state &= ~STATE_MOVING;\n    } else {\n        notif->move.frames_remaining--;\n    }\n}\n\nvoid handle_fade(iris::notification* notif) {\n    float f = (float)notif->fade.frames_remaining / (float)notif->fade.frames;\n\n    f = ease_in_out(f);\n\n    float fs = f;\n    float ft = 1.0 - f;\n\n    notif->fade.alpha = notif->fade.source_alpha * fs + notif->fade.target_alpha * ft;\n\n    if (!notif->fade.frames_remaining) {\n        notif->state &= ~STATE_FADING;\n    } else {\n        notif->fade.frames_remaining--;\n    }\n}\n\nvoid render_notification(iris::instance* iris, iris::notification* notif) {\n    using namespace ImGui;\n\n    ImGuiStyle& style = ImGui::GetStyle();\n\n    ImVec4 bg = style.Colors[ImGuiCol_MenuBarBg];\n    ImVec4 tc = style.Colors[ImGuiCol_Text];\n    ImVec4 td = style.Colors[ImGuiCol_TextDisabled];\n\n    uint32_t alpha = (notif->fade.alpha) << 24;\n\n    uint32_t bg_col =\n        ((uint32_t)(bg.x * 255.0) << 0 ) |\n        ((uint32_t)(bg.y * 255.0) << 8 ) |\n        ((uint32_t)(bg.z * 255.0) << 16) |\n        alpha;\n\n    uint32_t text_col =\n        ((uint32_t)(tc.x * 255.0) << 0 ) |\n        ((uint32_t)(tc.y * 255.0) << 8 ) |\n        ((uint32_t)(tc.z * 255.0) << 16) |\n        alpha;\n\n    uint32_t icon_col = 0x799fa7 | alpha;\n\n    uint32_t bar_col =\n        ((uint32_t)(td.x * 255.0) << 0 ) |\n        ((uint32_t)(td.y * 255.0) << 8 ) |\n        ((uint32_t)(td.z * 255.0) << 16) |\n        alpha;\n\n    float x = notif->move.x;\n    float y = notif->move.y;\n    float width = notif->width;\n    float height = notif->height;\n\n    GetForegroundDrawList()->AddRectFilled(\n        ImVec2(x, y),\n        ImVec2(x + width, y + height),\n        bg_col, 10.0, ImDrawFlags_RoundCornersAll\n    );\n\n    GetForegroundDrawList()->AddText(\n        ImVec2(x + 10, y + (height / 2) - (notif->text_height / 2)), icon_col, ICON_MS_INFO\n    );\n\n    GetForegroundDrawList()->AddText(\n        ImVec2(x + 35, y + (height / 2) - (notif->text_height / 2)), text_col, notif->text.c_str()\n    );\n\n    int progress_width = width * (1.0 - ((float)notif->frames_remaining / (float)notif->frames));\n\n    GetForegroundDrawList()->AddRectFilled(\n        ImVec2(x, y + height - 2),\n        ImVec2(x + progress_width, y + height),\n        bar_col, 10.0, ImDrawFlags_RoundCornersBottom\n    );\n}\n\nvoid remove_notification(iris::instance* iris, int i) {\n    iris::notification& n = iris->notifications.at(i);\n\n    for (unsigned int j = i + 1; j < iris->notifications.size(); j++) {\n        iris::notification& n1 = iris->notifications.at(j);\n\n        int target_x = n1.state & STATE_MOVING ? n1.move.target_x : n1.move.x;\n        int target_y = n1.state & STATE_MOVING ? n1.move.target_y : n1.move.y;\n\n        n1.move.source_x = n1.move.x;\n        n1.move.source_y = n1.move.y;\n        n1.move.target_x = target_x;\n        n1.move.target_y = target_y + n.height + 10;\n        n1.move.frames = seconds_to_frames(0.25);\n        n1.move.frames_remaining = n.move.frames;\n        n1.state |= STATE_MOVING;\n    }\n\n    iris->notifications.erase(std::begin(iris->notifications) + i);\n}\n\nvoid handle_animations(iris::instance* iris) {\n    for (unsigned int i = 0; i < iris->notifications.size(); i++) {\n        iris::notification& n = iris->notifications.at(i);\n\n        render_notification(iris, &n);\n\n        if (n.frames_remaining) {\n            n.frames_remaining--;\n\n            if (!n.frames_remaining) {\n                n.fade.source_alpha = n.fade.alpha;\n                n.fade.target_alpha = 0;\n                n.fade.frames = seconds_to_frames(0.25);\n                n.fade.frames_remaining = n.fade.frames;\n                n.state |= STATE_FADING;\n                n.end = true;\n            }\n        }\n\n        if (n.state == STATE_IDLE) {\n            if (n.end) {\n                remove_notification(iris, i);\n            }\n\n            continue;\n        }\n\n        if (n.state & STATE_MOVING) handle_move(&n);\n        if (n.state & STATE_FADING) handle_fade(&n);\n    }\n}\n\nvoid push_notification(iris::instance* iris, iris::notification notif) {\n    for (iris::notification& n : iris->notifications) {\n        int target_x = n.state & STATE_MOVING ? n.move.target_x : n.move.x;\n        int target_y = n.state & STATE_MOVING ? n.move.target_y : n.move.y;\n\n        n.move.source_x = n.move.x;\n        n.move.source_y = n.move.y;\n        n.move.target_x = target_x;\n        n.move.target_y = target_y - notif.height - 10;\n        n.move.frames = seconds_to_frames(0.25);\n        n.move.frames_remaining = n.move.frames;\n        n.state |= STATE_MOVING;\n    }\n\n    iris->notifications.push_front(notif);\n}\n\nvoid push_info(iris::instance* iris, std::string text) {\n    using namespace ImGui;\n\n    iris::notification notif;\n\n    int window_width, window_height;\n    int statusbar_offset = iris->show_status_bar ? iris->menubar_height : 0;\n\n    SDL_GetWindowSizeInPixels(iris->window, &window_width, &window_height);\n\n    ImVec2 ts = CalcTextSize(text.c_str());\n\n    notif.text = text;\n    notif.text_width = ts.x;\n    notif.text_height = ts.y;\n    notif.width = notif.text_width + 50;\n    notif.height = notif.text_height + 25;\n    notif.frames = seconds_to_frames(5.0);\n    notif.frames_remaining = notif.frames;\n    notif.state = STATE_MOVING | STATE_FADING;\n    notif.move.source_x = window_width + 5;\n    notif.move.source_y = window_height - notif.height - 10 - statusbar_offset;\n    notif.move.target_x = window_width - notif.width - 10;\n    notif.move.target_y = notif.move.source_y;\n    notif.move.frames = seconds_to_frames(0.25);\n    notif.move.frames_remaining = notif.move.frames;\n    notif.fade.source_alpha = 0;\n    notif.fade.target_alpha = 255;\n    notif.fade.frames = seconds_to_frames(0.25);\n    notif.fade.frames_remaining = notif.fade.frames;\n    notif.type = TYPE_INFO;\n    notif.end = false;\n\n    push_notification(iris, notif);\n}\n\n}"
  },
  {
    "path": "frontend/platform/stub.cpp",
    "content": "#include \"iris.hpp\"\n\n// Note: This is a stub implementation for platforms that\n//       do not need special initialization\n\nnamespace iris::platform {\n\nbool init(iris::instance* iris) {\n    return true;\n}\n\nbool apply_settings(iris::instance* iris) {\n    return true;\n}\n\nvoid destroy(iris::instance* iris) {}\n\n}"
  },
  {
    "path": "frontend/platform/windows.cpp",
    "content": "#include \"iris.hpp\"\n#include \"imgui.h\"\n\n#include <dwmapi.h>\n\nnamespace iris::platform {\n\nbool init(iris::instance* iris) {\n    apply_settings(iris);\n\n    return true;\n}\n\nbool apply_settings(iris::instance* iris) {\n    SDL_PropertiesID props = SDL_GetWindowProperties(iris->window);\n\n    HWND hwnd = (HWND)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL);\n\n    COLORREF border_color = iris->windows_enable_borders ? DWMWA_COLOR_DEFAULT : DWMWA_COLOR_NONE;\n\n    if (!SUCCEEDED(DwmSetWindowAttribute(\n        hwnd,\n        DWMWINDOWATTRIBUTE::DWMWA_BORDER_COLOR,\n        &border_color,\n        sizeof(border_color)\n    ))) {\n        return false;\n    }\n\n    if (iris->windows_titlebar_style == IRIS_TITLEBAR_DEFAULT) {\n        BOOL dark_mode = iris->windows_dark_mode;\n\n        if (!SUCCEEDED(DwmSetWindowAttribute(\n            hwnd,\n            DWMWINDOWATTRIBUTE::DWMWA_USE_IMMERSIVE_DARK_MODE,\n            &dark_mode,\n            sizeof(BOOL)\n        ))) {\n            printf(\"iris: Failed to set immersive dark mode\\n\");\n\n            return false;\n        }\n    }\n\n    bool result = false;\n\n    switch (iris->windows_titlebar_style) {\n        case IRIS_TITLEBAR_DEFAULT: {\n            COLORREF color = DWMWA_COLOR_DEFAULT;\n\n            result = SUCCEEDED(DwmSetWindowAttribute(\n                hwnd,\n                DWMWINDOWATTRIBUTE::DWMWA_CAPTION_COLOR,\n                &color,\n                sizeof(color)\n            ));\n        } break;\n\n        case IRIS_TITLEBAR_SEAMLESS: {\n            COLORREF color = 0;\n\n            ImVec4 menubar_color = ImGui::GetStyleColorVec4(ImGuiCol_MenuBarBg);\n\n            color = (uint32_t)(menubar_color.x * 255) |\n                    ((uint32_t)(menubar_color.y * 255) << 8) |\n                    ((uint32_t)(menubar_color.z * 255) << 16);\n\n            result = SUCCEEDED(DwmSetWindowAttribute(\n                hwnd,\n                DWMWINDOWATTRIBUTE::DWMWA_CAPTION_COLOR,\n                &color,\n                sizeof(color)\n            ));\n        } break;\n    }\n\n    // ShowWindow(hwnd, SW_MINIMIZE);\n    // ShowWindow(hwnd, SW_RESTORE);\n\n    return result;\n}\n\nvoid destroy(iris::instance* iris) {}\n\n}"
  },
  {
    "path": "frontend/render.cpp",
    "content": "#include <cmath>\n\n#include \"iris.hpp\"\n\n#define RENDER_MAX_SHADER_PASSES 16\n\n// INCBIN stuff\n#define INCBIN_PREFIX g_\n#define INCBIN_STYLE INCBIN_STYLE_SNAKE\n\n#include \"incbin.h\"\n\nINCBIN(default_vertex_shader, \"../shaders/shader.spv\");\n\nnamespace iris::render {\n\nstatic int frame = 0;\nstatic constexpr uint32_t DESCRIPTOR_SET_RING_SIZE = 8;\n\nbool create_image(iris::instance* iris, uint32_t width, uint32_t height, VkFormat format, VkImageUsageFlags usage, VkImage& image, VkImageView& view, VkDeviceMemory& memory) {\n    VkImageCreateInfo image_info = {};\n    image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;\n    image_info.imageType = VK_IMAGE_TYPE_2D;\n    image_info.extent.width = width;\n    image_info.extent.height = height;\n    image_info.extent.depth = 1;\n    image_info.mipLevels = 1;\n    image_info.arrayLayers = 1;\n    image_info.format = format;\n    image_info.tiling = VK_IMAGE_TILING_OPTIMAL;\n    image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    image_info.usage = usage;\n    image_info.samples = VK_SAMPLE_COUNT_1_BIT;\n    image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\n    if (vkCreateImage(iris->device, &image_info, nullptr, &image) != VK_SUCCESS) {\n        return false;\n    }\n\n    VkMemoryRequirements memory_requirements;\n    vkGetImageMemoryRequirements(iris->device, image, &memory_requirements);\n\n    VkMemoryAllocateInfo alloc_info = {};\n    alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n    alloc_info.allocationSize = memory_requirements.size;\n\n    VkPhysicalDeviceMemoryProperties memory_properties;\n    vkGetPhysicalDeviceMemoryProperties(iris->physical_device, &memory_properties);\n\n    for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) {\n        if ((memory_requirements.memoryTypeBits & (1 << i)) &&\n            (memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {\n            alloc_info.memoryTypeIndex = i;\n            break;\n        }\n    }\n\n    if (vkAllocateMemory(iris->device, &alloc_info, nullptr, &memory) != VK_SUCCESS) {\n        fprintf(stderr, \"render: Failed to allocate image memory\\n\");\n\n        vkDestroyImage(iris->device, image, nullptr);\n\n        return false;\n    }\n\n    vkBindImageMemory(iris->device, image, memory, 0);\n\n    VkImageViewCreateInfo image_view_info = {};\n    image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n    image_view_info.image = image;\n    image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D;\n    image_view_info.format = format;\n    image_view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY;\n    image_view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY;\n    image_view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY;\n    image_view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY;\n    image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    image_view_info.subresourceRange.baseMipLevel = 0;\n    image_view_info.subresourceRange.levelCount = 1;\n    image_view_info.subresourceRange.baseArrayLayer = 0;\n    image_view_info.subresourceRange.layerCount = 1;\n\n    if (vkCreateImageView(iris->device, &image_view_info, nullptr, &view) != VK_SUCCESS) {\n        return false;\n    }\n\n    return true;\n}\n\nbool rebuild_framebuffers(iris::instance* iris) {\n    if (!shaders::count(iris))\n        return true;\n\n    vulkan::wait_idle(iris);\n\n    for (auto& pass_framebuffers : iris->shader_pass_framebuffers) {\n        for (VkFramebuffer& framebuffer : pass_framebuffers) {\n            if (framebuffer) {\n                vkDestroyFramebuffer(iris->device, framebuffer, nullptr);\n                framebuffer = VK_NULL_HANDLE;\n            }\n        }\n    }\n\n    iris->shader_pass_framebuffers.clear();\n\n    for (auto& fb : iris->shader_framebuffers) {\n        if (fb.view) vkDestroyImageView(iris->device, fb.view, nullptr);\n        if (fb.image) vkDestroyImage(iris->device, fb.image, nullptr);\n        if (fb.memory) vkFreeMemory(iris->device, fb.memory, nullptr);\n\n        bool res = create_image(iris,\n            iris->image.width,\n            iris->image.height,\n            iris->image.format,\n            VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT,\n            fb.image,\n            fb.view,\n            fb.memory\n        );\n\n        if (!res) {\n            fprintf(stderr, \"render: Failed to create shader pass framebuffer image\\n\");\n\n            return false;\n        }\n    }\n\n    const size_t pass_count = shaders::count(iris);\n\n    iris->shader_pass_framebuffers.resize(pass_count);\n\n    for (size_t pass_index = 0; pass_index < pass_count; pass_index++) {\n        auto* pass = shaders::at(iris, (int)pass_index);\n\n        if (!pass || !pass->ready()) {\n            iris->shader_pass_framebuffers[pass_index] = { VK_NULL_HANDLE, VK_NULL_HANDLE };\n\n            continue;\n        }\n\n        for (int framebuffer_index = 0; framebuffer_index < 2; framebuffer_index++) {\n            VkFramebufferCreateInfo framebuffer_info = {};\n            framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO;\n            framebuffer_info.renderPass = pass->get_render_pass();\n            framebuffer_info.attachmentCount = 1;\n            framebuffer_info.pAttachments = &iris->shader_framebuffers[framebuffer_index].view;\n            framebuffer_info.width = iris->image.width;\n            framebuffer_info.height = iris->image.height;\n            framebuffer_info.layers = 1;\n\n            if (vkCreateFramebuffer(iris->device, &framebuffer_info, nullptr, &iris->shader_pass_framebuffers[pass_index][framebuffer_index]) != VK_SUCCESS) {\n                fprintf(stderr, \"render: Failed to create shader pass framebuffer\\n\");\n\n                return false;\n            }\n        }\n    }\n\n    return true;\n}\n\nbool init(iris::instance* iris) {\n    // Initialize our renderer\n    iris->renderer = renderer_create();\n\n    renderer_create_info info = {};\n\n    info.backend = iris->renderer_backend;\n    info.gif = iris->ps2->gif;\n    info.gs = iris->ps2->gs;\n    info.instance = iris->instance;\n    info.device = iris->device;\n    info.physical_device = iris->physical_device;\n    info.instance_create_info = iris->instance_create_info;\n    info.device_create_info = iris->device_create_info;\n\n    switch (info.backend) {\n        case RENDERER_BACKEND_HARDWARE: {\n            info.config = &iris->hardware_backend_config;\n        } break;\n    }\n\n    if (!renderer_init(iris->renderer, info)) {\n        fprintf(stderr, \"render: Failed to initialize renderer backend\\n\");\n\n        return false;\n    }\n\n    VkSamplerCreateInfo nearest_sampler_info = {};\n    nearest_sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;\n    nearest_sampler_info.magFilter = VK_FILTER_NEAREST;\n    nearest_sampler_info.minFilter = VK_FILTER_NEAREST;\n    nearest_sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n    nearest_sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n    nearest_sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n    nearest_sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n    nearest_sampler_info.minLod = -1000;\n    nearest_sampler_info.maxLod = 1000;\n    nearest_sampler_info.maxAnisotropy = 1.0f;\n\n    if (vkCreateSampler(iris->device, &nearest_sampler_info, VK_NULL_HANDLE, &iris->sampler[0]) != VK_SUCCESS) {\n        fprintf(stderr, \"render: Failed to create nearest texture sampler\\n\");\n\n        return false;\n    }\n\n    VkSamplerCreateInfo bilinear_sampler_info = {};\n    bilinear_sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;\n    bilinear_sampler_info.magFilter = VK_FILTER_LINEAR;\n    bilinear_sampler_info.minFilter = VK_FILTER_LINEAR;\n    bilinear_sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n    bilinear_sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n    bilinear_sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n    bilinear_sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n    bilinear_sampler_info.minLod = -1000;\n    bilinear_sampler_info.maxLod = 1000;\n    bilinear_sampler_info.maxAnisotropy = 1.0f;\n\n    if (vkCreateSampler(iris->device, &bilinear_sampler_info, VK_NULL_HANDLE, &iris->sampler[1]) != VK_SUCCESS) {\n        fprintf(stderr, \"render: Failed to create bilinear texture sampler\\n\");\n\n        return false;\n    }\n\n    if (iris->cubic_supported) {\n        VkSamplerCreateInfo cubic_sampler_info = {};\n        cubic_sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;\n        cubic_sampler_info.magFilter = VK_FILTER_LINEAR;\n        cubic_sampler_info.minFilter = VK_FILTER_LINEAR;\n        cubic_sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n        cubic_sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n        cubic_sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n        cubic_sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE;\n        cubic_sampler_info.minLod = -1000;\n        cubic_sampler_info.maxLod = 1000;\n        cubic_sampler_info.maxAnisotropy = 1.0f;\n\n        if (vkCreateSampler(iris->device, &cubic_sampler_info, VK_NULL_HANDLE, &iris->sampler[2]) != VK_SUCCESS) {\n            fprintf(stderr, \"render: Failed to create cubic texture sampler\\n\");\n\n            return false;\n        }\n    }\n\n    VkShaderModuleCreateInfo vert_shader_create_info = {};\n    vert_shader_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;\n    vert_shader_create_info.pCode = (const uint32_t*)g_default_vertex_shader_data;\n    vert_shader_create_info.codeSize = g_default_vertex_shader_size;\n\n    if (vkCreateShaderModule(iris->device, &vert_shader_create_info, nullptr, &iris->default_vert_shader) != VK_SUCCESS) {\n        fprintf(stderr, \"render: Failed to create default vertex shader module\\n\");\n\n        return false;\n    }\n\n    // Create descriptor set\n    VkDescriptorSetLayoutBinding sampler_layout_binding = {};\n    sampler_layout_binding.binding = 0;\n    sampler_layout_binding.descriptorCount = 1;\n    sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n    sampler_layout_binding.pImmutableSamplers = nullptr;\n    sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;\n\n    VkDescriptorSetLayoutCreateInfo layout_info = {};\n    layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO;\n    layout_info.bindingCount = 1;\n    layout_info.pBindings = &sampler_layout_binding;\n\n    if (vkCreateDescriptorSetLayout(iris->device, &layout_info, nullptr, &iris->shader_descriptor_set_layout) != VK_SUCCESS) {\n        fprintf(stderr, \"render: Failed to create descriptor set layout\\n\");\n\n        return false;\n    }\n\n    const uint32_t shader_descriptor_set_count = DESCRIPTOR_SET_RING_SIZE * RENDER_MAX_SHADER_PASSES;\n\n    std::vector <VkDescriptorSetLayout> shader_layouts(shader_descriptor_set_count, iris->shader_descriptor_set_layout);\n\n    iris->shader_descriptor_sets.resize(shader_descriptor_set_count, VK_NULL_HANDLE);\n\n    VkDescriptorSetAllocateInfo alloc_info = {};\n    alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;\n    alloc_info.descriptorPool = iris->descriptor_pool;\n    alloc_info.descriptorSetCount = shader_descriptor_set_count;\n    alloc_info.pSetLayouts = shader_layouts.data();\n\n    if (vkAllocateDescriptorSets(iris->device, &alloc_info, iris->shader_descriptor_sets.data()) != VK_SUCCESS) {\n        fprintf(stderr, \"render: Failed to allocate descriptor sets\\n\");\n\n        return false;\n    }\n\n    iris->shader_descriptor_set = iris->shader_descriptor_sets[0];\n\n    return true;\n}\n\nstatic inline VkDescriptorSet get_frame_descriptor_set(iris::instance* iris) {\n    if (!iris->descriptor_sets.size()) {\n        return iris->descriptor_set;\n    }\n\n    const uint32_t frame_index = iris->main_window_data.FrameIndex;\n\n    return iris->descriptor_sets[frame_index % iris->descriptor_sets.size()];\n}\n\nstatic inline VkDescriptorSet get_frame_shader_descriptor_set(iris::instance* iris, uint32_t pass_index) {\n    if (!iris->shader_descriptor_sets.size()) {\n        return iris->shader_descriptor_set;\n    }\n\n    const uint32_t frame_index = iris->main_window_data.FrameIndex % DESCRIPTOR_SET_RING_SIZE;\n    const uint32_t slot = (frame_index * RENDER_MAX_SHADER_PASSES) + (pass_index % RENDER_MAX_SHADER_PASSES);\n\n    return iris->shader_descriptor_sets[slot % iris->shader_descriptor_sets.size()];\n}\n\nstatic inline void update_vertex_buffer(iris::instance* iris, VkCommandBuffer command_buffer) {\n    SDL_Rect size, rect, display;\n\n    const int normalized_angle = ((iris->angle % 360) + 360) % 360;\n    const bool swap_axes = normalized_angle == 90 || normalized_angle == 270;\n\n    float aspect_ratio = 4.0 / 3.0;\n\n    if (swap_axes) {\n        aspect_ratio = 3.0 / 4.0;\n    }\n\n    float aspect_ratio_inv = 1.0f / aspect_ratio;\n\n    SDL_GetWindowSize(iris->window, &size.w, &size.h);\n\n    display.w = size.w;\n    display.h = size.h;\n    display.x = 0;\n    display.y = 0;\n    \n    if (!iris->fullscreen) {\n        display.h -= iris->menubar_height;\n        display.y += iris->menubar_height;\n\n        if (iris->show_status_bar) {\n            display.h -= iris->menubar_height;\n        }\n    }\n\n    rect.w = iris->image.width;\n    rect.h = iris->image.height;\n\n    float scale = iris->integer_scaling ? floorf(iris->scale) : iris->scale;\n\n    switch (iris->aspect_mode) {\n        case RENDER_ASPECT_NATIVE: {\n            rect.w *= scale;\n            rect.h *= scale;\n        } break;\n\n        case RENDER_ASPECT_4_3: {\n            rect.w *= scale;\n            rect.h = (float)rect.w * (3.0f / 4.0f);\n        } break;\n\n        case RENDER_ASPECT_16_9: {\n            rect.w *= scale;\n            rect.h = (float)rect.w * (9.0f / 16.0f);\n        } break;\n\n        case RENDER_ASPECT_5_4: {\n            rect.w *= scale;\n            rect.h = (float)rect.w * (4.0f / 5.0f);\n        } break;\n\n        case RENDER_ASPECT_STRETCH: {\n            rect.w = display.w;\n            rect.h = display.h;\n        } break;\n\n        case RENDER_ASPECT_AUTO:\n        case RENDER_ASPECT_STRETCH_KEEP: {\n            if (swap_axes) {\n                std::swap(rect.w, rect.h);\n            }\n\n            rect.h = display.h;\n            rect.w = (float)rect.h * aspect_ratio;\n\n            // Scale vertically if the rect ends up being bigger\n            // than our display area\n            if (rect.w > display.w) {\n                rect.w = display.w;\n                rect.h = (float)rect.w * aspect_ratio_inv;\n            }\n        } break;\n    }\n\n    if (iris->aspect_mode != RENDER_ASPECT_AUTO && iris->aspect_mode != RENDER_ASPECT_STRETCH_KEEP) {\n        if (swap_axes) {\n            std::swap(rect.w, rect.h);\n        }\n    }\n\n    iris->render_width = rect.w;\n    iris->render_height = rect.h;\n\n    rect.x = display.x + ((display.w / 2) - (rect.w / 2));\n    rect.y = display.y + ((display.h / 2) - (rect.h / 2));\n\n    float x0 = (rect.x / ((float)size.w / 2.0f)) - 1.0f;\n    float y0 = (rect.y / ((float)size.h / 2.0f)) - 1.0f;\n    float x1 = ((rect.x + rect.w) / ((float)size.w / 2.0f)) - 1.0f;\n    float y1 = ((rect.y + rect.h) / ((float)size.h / 2.0f)) - 1.0f;\n\n    float uvs[4][2] = {\n        {0.0f, 1.0f},\n        {1.0f, 1.0f},\n        {1.0f, 0.0f},\n        {0.0f, 0.0f}\n    };\n\n    for (int i = 0; i < 4; i++) {\n        float u = uvs[i][0];\n        float v = uvs[i][1];\n\n        if (iris->flip_x) {\n            u = 1.0f - u;\n        }\n\n        if (iris->flip_y) {\n            v = 1.0f - v;\n        }\n\n        switch (normalized_angle) {\n            case 90: {\n                // Rotate clockwise in UV space.\n                const float prev_u = u;\n                u = v;\n                v = 1.0f - prev_u;\n            } break;\n\n            case 180: {\n                u = 1.0f - u;\n                v = 1.0f - v;\n            } break;\n\n            case 270: {\n                // Rotate counter-clockwise in UV space.\n                const float prev_u = u;\n                u = 1.0f - v;\n                v = prev_u;\n            } break;\n        }\n\n        uvs[i][0] = u;\n        uvs[i][1] = v;\n    }\n\n    iris->vertices[0] = vertex{ { x0, y0 }, {uvs[0][0], uvs[0][1]} };\n    iris->vertices[1] = vertex{ { x1, y0 }, {uvs[1][0], uvs[1][1]} };\n    iris->vertices[2] = vertex{ { x1, y1 }, {uvs[2][0], uvs[2][1]} };\n    iris->vertices[3] = vertex{ { x0, y1 }, {uvs[3][0], uvs[3][1]} };\n\n    void* ptr;\n\n    vkMapMemory(iris->device, iris->vertex_staging_buffer_memory, 0, iris->vertex_buffer_size, 0, &ptr);\n    memcpy(ptr, iris->vertices.data(), (size_t)iris->vertex_buffer_size);\n    vkUnmapMemory(iris->device, iris->vertex_staging_buffer_memory);\n\n    static VkBufferCopy region = {};\n    region.srcOffset = 0;\n    region.dstOffset = 0;\n    region.size = iris->vertex_buffer_size;\n\n    vkCmdCopyBuffer(\n        command_buffer,\n        iris->vertex_staging_buffer,\n        iris->vertex_buffer,\n        1,\n        &region\n    );\n}\n\nstatic inline void update_descriptor_set(iris::instance* iris, VkDescriptorSet set, VkImageView view, VkSampler sampler) {\n    VkDescriptorImageInfo image_info = {};\n    image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n    image_info.imageView = view;\n    image_info.sampler = sampler;\n\n    VkWriteDescriptorSet descriptor_write = {};\n    descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n    descriptor_write.dstSet = set;\n    descriptor_write.dstBinding = 0;\n    descriptor_write.dstArrayElement = 0;\n    descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n    descriptor_write.descriptorCount = 1;\n    descriptor_write.pImageInfo = &image_info;\n\n    vkUpdateDescriptorSets(iris->device, 1, &descriptor_write, 0, nullptr);\n}\n\nvoid render_shader_passes(iris::instance* iris, VkCommandBuffer command_buffer, VkImageView& output_view, VkImage& output_image) {\n    if (!shaders::count(iris))\n        return;\n\n    if (iris->shader_pass_framebuffers.size() != shaders::count(iris)) {\n        if (!rebuild_framebuffers(iris)) {\n            fprintf(stderr, \"render: Failed to rebuild shader framebuffers\\n\");\n\n            return;\n        }\n    }\n\n    int i = 0;\n\n    for (size_t pass_index = 0; pass_index < shaders::vector(iris).size(); pass_index++) {\n        auto& pass = shaders::vector(iris)[pass_index];\n\n        if (pass->bypass || !pass->ready())\n            continue;\n\n        const int fb = i & 1;\n        const VkImageView input_view = i == 0 ? iris->image.view : iris->shader_framebuffers[fb ^ 1].view;\n        VkFramebuffer framebuffer = iris->shader_pass_framebuffers[pass_index][fb];\n\n        if (framebuffer == VK_NULL_HANDLE) {\n            if (!rebuild_framebuffers(iris)) {\n                fprintf(stderr, \"render: Failed to rebuild shader framebuffers\\n\");\n\n                return;\n            }\n\n            framebuffer = iris->shader_pass_framebuffers[pass_index][fb];\n\n            if (framebuffer == VK_NULL_HANDLE) {\n                fprintf(stderr, \"render: Shader framebuffer is null after rebuild\\n\");\n\n                return;\n            }\n        }\n\n        output_view = iris->shader_framebuffers[fb].view;\n        output_image = iris->shader_framebuffers[fb].image;\n\n        VkRenderPassBeginInfo info = {};\n        info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;\n        info.renderPass = pass->get_render_pass();\n        info.framebuffer = framebuffer;\n        info.renderArea.extent.width = iris->image.width;\n        info.renderArea.extent.height = iris->image.height;\n        info.clearValueCount = 1;\n        info.pClearValues = &iris->clear_value;\n\n        VkDescriptorImageInfo image_info = {};\n        image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n        image_info.imageView = input_view;\n        image_info.sampler = iris->sampler[iris->filter];\n\n        VkWriteDescriptorSet descriptor_write = {};\n        descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n        const VkDescriptorSet shader_descriptor_set = get_frame_shader_descriptor_set(iris, i);\n        descriptor_write.dstSet = shader_descriptor_set;\n        descriptor_write.dstBinding = 0;\n        descriptor_write.dstArrayElement = 0;\n        descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n        descriptor_write.descriptorCount = 1;\n        descriptor_write.pImageInfo = &image_info;\n\n        vkUpdateDescriptorSets(iris->device, 1, &descriptor_write, 0, nullptr);\n\n        vkCmdBeginRenderPass(command_buffer, &info, VK_SUBPASS_CONTENTS_INLINE);\n\n        vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pass->get_pipeline());\n\n        VkDeviceSize offsets[] = { 0 };\n        vkCmdBindIndexBuffer(command_buffer, iris->index_buffer, 0, VK_INDEX_TYPE_UINT16);\n        vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pass->get_pipeline_layout(), 0, 1, &shader_descriptor_set, 0, nullptr);\n\n        VkViewport viewport = {};\n        viewport.x = 0.0f;\n        viewport.y = 0.0f;\n        viewport.width = iris->image.width;\n        viewport.height = iris->image.height;\n        viewport.minDepth = 0.0f;\n        viewport.maxDepth = 1.0f;\n        vkCmdSetViewport(command_buffer, 0, 1, &viewport);\n\n        VkRect2D scissor = {};\n        scissor.offset = {0, 0};\n        scissor.extent = { (uint32_t)iris->image.width, (uint32_t)iris->image.height };\n        vkCmdSetScissor(command_buffer, 0, 1, &scissor);\n\n        push_constants constants = {\n            .resolution = { (float)iris->image.width, (float)iris->image.height },\n            .frame = frame\n        };\n\n        vkCmdPushConstants(command_buffer, pass->get_pipeline_layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(push_constants), &constants);\n\n        vkCmdDrawIndexed(command_buffer, 6, 1, 0, 0, 0);\n\n        vkCmdEndRenderPass(command_buffer);\n\n        i++;\n    }\n}\n\nbool render_frame(iris::instance* iris, VkCommandBuffer command_buffer, VkFramebuffer framebuffer) {\n    renderer_image image;\n\n    if (iris->pause) {\n        image = iris->image;\n    } else {\n        image = renderer_get_frame(iris->renderer);\n    }\n\n    bool need_rebuild = image.width != iris->image.width ||\n                        image.height != iris->image.height ||\n                        image.format != iris->image.format;\n\n    iris->image = image;\n\n    if (need_rebuild && image.view != VK_NULL_HANDLE) {\n        vulkan::wait_idle(iris);\n\n        for (auto& pass : shaders::vector(iris)) {\n            if (!pass->rebuild()) {\n                fprintf(stderr, \"render: Failed to rebuild shader pass\\n\");\n\n                return false;\n            }\n        }\n\n        if (!rebuild_framebuffers(iris)) {\n            fprintf(stderr, \"render: Failed to rebuild shader pass framebuffers\\n\");\n\n            return false;\n        }\n    }\n\n    // Process shader passes here\n    iris->output_image = iris->image;\n\n    if (iris->enable_shaders && iris->output_image.view != VK_NULL_HANDLE) {\n        render_shader_passes(iris, command_buffer, iris->output_image.view, iris->output_image.image);\n    }\n\n    VkRenderPassBeginInfo info = {};\n    info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO;\n    info.renderPass = iris->main_window_data.RenderPass;\n    info.framebuffer = framebuffer;\n    info.renderArea.extent.width = iris->main_window_data.Width;\n    info.renderArea.extent.height = iris->main_window_data.Height;\n    info.clearValueCount = 1;\n    info.pClearValues = &iris->clear_value;\n\n    if (iris->output_image.view != VK_NULL_HANDLE) {\n        const VkDescriptorSet descriptor_set = get_frame_descriptor_set(iris);\n\n        update_vertex_buffer(iris, command_buffer);\n        update_descriptor_set(iris, descriptor_set, iris->output_image.view, iris->sampler[iris->filter]);\n    }\n\n    vkCmdBeginRenderPass(command_buffer, &info, VK_SUBPASS_CONTENTS_INLINE);\n\n    VkClearAttachment clear_attachment = {};\n    clear_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n    clear_attachment.colorAttachment = 0;\n    clear_attachment.clearValue = iris->clear_value;\n\n    VkClearRect clear_rect = {};\n    clear_rect.rect.offset = { 0, 0 };\n    clear_rect.rect.extent = { (uint32_t)iris->main_window_data.Width, (uint32_t)iris->main_window_data.Height };\n    clear_rect.baseArrayLayer = 0;\n    clear_rect.layerCount = 1;\n\n    vkCmdClearAttachments(command_buffer, 1, &clear_attachment, 1, &clear_rect);\n\n    vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, iris->pipeline);\n\n    if (iris->output_image.view != VK_NULL_HANDLE) {\n        VkDeviceSize offsets[] = { 0 };\n        const VkDescriptorSet descriptor_set = get_frame_descriptor_set(iris);\n\n        vkCmdBindVertexBuffers(command_buffer, 0, 1, &iris->vertex_buffer, offsets);\n        vkCmdBindIndexBuffer(command_buffer, iris->index_buffer, 0, VK_INDEX_TYPE_UINT16);\n        vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, iris->pipeline_layout, 0, 1, &descriptor_set, 0, nullptr);\n    }\n\n    VkViewport viewport = {};\n    viewport.x = 0.0f;\n    viewport.y = 0.0f;\n    viewport.width = iris->main_window_data.Width;\n    viewport.height = iris->main_window_data.Height;\n    viewport.minDepth = 0.0f;\n    viewport.maxDepth = 1.0f;\n    vkCmdSetViewport(command_buffer, 0, 1, &viewport);\n\n    VkRect2D scissor = {};\n    scissor.offset = {0, 0};\n    scissor.extent = { (uint32_t)iris->main_window_data.Width, (uint32_t)iris->main_window_data.Height };\n    vkCmdSetScissor(command_buffer, 0, 1, &scissor);\n\n    if (iris->output_image.view != VK_NULL_HANDLE) {\n        vkCmdDrawIndexed(command_buffer, 6, 1, 0, 0, 0);\n    }\n\n    vkCmdEndRenderPass(command_buffer);\n\n    if (!iris->pause)\n        frame++;\n\n    return true;\n}\n\nvoid switch_backend(iris::instance* iris, int backend) {\n    if (iris->renderer_backend == backend)\n        return;\n\n    renderer_destroy(iris->renderer);\n\n    iris->renderer = renderer_create();\n\n    renderer_create_info info = {};\n\n    info.backend = backend;\n    info.gif = iris->ps2->gif;\n    info.gs = iris->ps2->gs;\n    info.instance = iris->instance;\n    info.device = iris->device;\n    info.physical_device = iris->physical_device;\n    info.instance_create_info = iris->instance_create_info;\n    info.device_create_info = iris->device_create_info;\n\n    switch (info.backend) {\n        case RENDERER_BACKEND_HARDWARE: {\n            info.config = &iris->hardware_backend_config;\n        } break;\n    }\n\n    if (!renderer_init(iris->renderer, info)) {\n        fprintf(stderr, \"render: Failed to initialize renderer backend\\n\");\n    } else {\n        iris->renderer_backend = backend;\n    }\n}\n\nvoid refresh(iris::instance* iris) {\n    switch (iris->renderer_backend) {\n        case RENDERER_BACKEND_HARDWARE: {\n            renderer_set_config(iris->renderer, &iris->hardware_backend_config);\n        } break;\n    }\n\n    iris->image = renderer_get_frame(iris->renderer);\n\n    if (iris->image.view == VK_NULL_HANDLE)\n        return;\n\n    if (shaders::count(iris) == 0)\n        return;\n\n    vulkan::wait_idle(iris);\n\n    for (auto& pass : shaders::vector(iris)) {\n        pass->rebuild();\n    }\n\n    rebuild_framebuffers(iris);\n}\n\nvoid destroy(iris::instance* iris) {\n    if (!iris->window)\n        return;\n\n    vulkan::wait_idle(iris);\n\n    for (auto& pass_framebuffers : iris->shader_pass_framebuffers) {\n        for (VkFramebuffer& framebuffer : pass_framebuffers) {\n            if (framebuffer) {\n                vkDestroyFramebuffer(iris->device, framebuffer, nullptr);\n            }\n        }\n    }\n\n    iris->shader_pass_framebuffers.clear();\n\n    for (auto& fb : iris->shader_framebuffers) {\n        if (fb.view) vkDestroyImageView(iris->device, fb.view, nullptr);\n        if (fb.image) vkDestroyImage(iris->device, fb.image, nullptr);\n        if (fb.memory) vkFreeMemory(iris->device, fb.memory, nullptr);\n    }\n\n    if (iris->shader_descriptor_set_layout) {\n        vkDestroyDescriptorSetLayout(iris->device, iris->shader_descriptor_set_layout, nullptr);\n    }\n\n    if (iris->default_vert_shader) {\n        vkDestroyShaderModule(iris->device, iris->default_vert_shader, nullptr);\n    }\n\n    shaders::clear(iris);\n\n    if (iris->renderer) renderer_destroy(iris->renderer);\n}\n\n}"
  },
  {
    "path": "frontend/res/IconsMaterialSymbols.h",
    "content": "// Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py\n// for C and C++\n// from codepoints https://github.com/google/material-design-icons/raw/master/variablefont/MaterialSymbolsOutlined%5BFILL%2CGRAD%2Copsz%2Cwght%5D.codepoints\n// for use with font https://github.com/google/material-design-icons/raw/master/variablefont/MaterialSymbolsOutlined%5BFILL,GRAD,opsz,wght%5D.ttf, https://github.com/google/material-design-icons/raw/master/variablefont/MaterialSymbolsRounded%5BFILL,GRAD,opsz,wght%5D.ttf, https://github.com/google/material-design-icons/raw/master/variablefont/MaterialSymbolsSharp%5BFILL,GRAD,opsz,wght%5D.ttf\n\n#pragma once\n\n#define FONT_ICON_FILE_NAME_MSO \"MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].ttf\"\n#define FONT_ICON_FILE_NAME_MSR \"MaterialSymbolsRounded[FILL,GRAD,opsz,wght].ttf\"\n#define FONT_ICON_FILE_NAME_MSS \"MaterialSymbolsSharp[FILL,GRAD,opsz,wght].ttf\"\n\n#define ICON_MIN_MS 0xe003\n#define ICON_MAX_16_MS 0xf8ff\n#define ICON_MAX_MS 0xf8ff\n\n#define ICON_MS_10K \"\\xee\\xa5\\x91\"\t// U+e951\n#define ICON_MS_10MP \"\\xee\\xa5\\x92\"\t// U+e952\n#define ICON_MS_11MP \"\\xee\\xa5\\x93\"\t// U+e953\n#define ICON_MS_123 \"\\xee\\xae\\x8d\"\t// U+eb8d\n#define ICON_MS_12MP \"\\xee\\xa5\\x94\"\t// U+e954\n#define ICON_MS_13MP \"\\xee\\xa5\\x95\"\t// U+e955\n#define ICON_MS_14MP \"\\xee\\xa5\\x96\"\t// U+e956\n#define ICON_MS_15MP \"\\xee\\xa5\\x97\"\t// U+e957\n#define ICON_MS_16MP \"\\xee\\xa5\\x98\"\t// U+e958\n#define ICON_MS_17MP \"\\xee\\xa5\\x99\"\t// U+e959\n#define ICON_MS_18_UP_RATING \"\\xef\\xa3\\xbd\"\t// U+f8fd\n#define ICON_MS_18MP \"\\xee\\xa5\\x9a\"\t// U+e95a\n#define ICON_MS_19MP \"\\xee\\xa5\\x9b\"\t// U+e95b\n#define ICON_MS_1K \"\\xee\\xa5\\x9c\"\t// U+e95c\n#define ICON_MS_1K_PLUS \"\\xee\\xa5\\x9d\"\t// U+e95d\n#define ICON_MS_1X_MOBILEDATA \"\\xee\\xbf\\x8d\"\t// U+efcd\n#define ICON_MS_1X_MOBILEDATA_BADGE \"\\xef\\x9f\\xb1\"\t// U+f7f1\n#define ICON_MS_20MP \"\\xee\\xa5\\x9e\"\t// U+e95e\n#define ICON_MS_21MP \"\\xee\\xa5\\x9f\"\t// U+e95f\n#define ICON_MS_22MP \"\\xee\\xa5\\xa0\"\t// U+e960\n#define ICON_MS_23MP \"\\xee\\xa5\\xa1\"\t// U+e961\n#define ICON_MS_24MP \"\\xee\\xa5\\xa2\"\t// U+e962\n#define ICON_MS_2D \"\\xee\\xbc\\xb7\"\t// U+ef37\n#define ICON_MS_2K \"\\xee\\xa5\\xa3\"\t// U+e963\n#define ICON_MS_2K_PLUS \"\\xee\\xa5\\xa4\"\t// U+e964\n#define ICON_MS_2MP \"\\xee\\xa5\\xa5\"\t// U+e965\n#define ICON_MS_30FPS \"\\xee\\xbf\\x8e\"\t// U+efce\n#define ICON_MS_30FPS_SELECT \"\\xee\\xbf\\x8f\"\t// U+efcf\n#define ICON_MS_360 \"\\xee\\x95\\xb7\"\t// U+e577\n#define ICON_MS_3D_ROTATION \"\\xee\\xa1\\x8d\"\t// U+e84d\n#define ICON_MS_3G_MOBILEDATA \"\\xee\\xbf\\x90\"\t// U+efd0\n#define ICON_MS_3G_MOBILEDATA_BADGE \"\\xef\\x9f\\xb0\"\t// U+f7f0\n#define ICON_MS_3K \"\\xee\\xa5\\xa6\"\t// U+e966\n#define ICON_MS_3K_PLUS \"\\xee\\xa5\\xa7\"\t// U+e967\n#define ICON_MS_3MP \"\\xee\\xa5\\xa8\"\t// U+e968\n#define ICON_MS_3P \"\\xee\\xbf\\x91\"\t// U+efd1\n#define ICON_MS_4G_MOBILEDATA \"\\xee\\xbf\\x92\"\t// U+efd2\n#define ICON_MS_4G_MOBILEDATA_BADGE \"\\xef\\x9f\\xaf\"\t// U+f7ef\n#define ICON_MS_4G_PLUS_MOBILEDATA \"\\xee\\xbf\\x93\"\t// U+efd3\n#define ICON_MS_4K \"\\xee\\x81\\xb2\"\t// U+e072\n#define ICON_MS_4K_PLUS \"\\xee\\xa5\\xa9\"\t// U+e969\n#define ICON_MS_4MP \"\\xee\\xa5\\xaa\"\t// U+e96a\n#define ICON_MS_50MP \"\\xef\\x9b\\xb3\"\t// U+f6f3\n#define ICON_MS_5G \"\\xee\\xbc\\xb8\"\t// U+ef38\n#define ICON_MS_5G_MOBILEDATA_BADGE \"\\xef\\x9f\\xae\"\t// U+f7ee\n#define ICON_MS_5K \"\\xee\\xa5\\xab\"\t// U+e96b\n#define ICON_MS_5K_PLUS \"\\xee\\xa5\\xac\"\t// U+e96c\n#define ICON_MS_5MP \"\\xee\\xa5\\xad\"\t// U+e96d\n#define ICON_MS_60FPS \"\\xee\\xbf\\x94\"\t// U+efd4\n#define ICON_MS_60FPS_SELECT \"\\xee\\xbf\\x95\"\t// U+efd5\n#define ICON_MS_6_FT_APART \"\\xef\\x88\\x9e\"\t// U+f21e\n#define ICON_MS_6K \"\\xee\\xa5\\xae\"\t// U+e96e\n#define ICON_MS_6K_PLUS \"\\xee\\xa5\\xaf\"\t// U+e96f\n#define ICON_MS_6MP \"\\xee\\xa5\\xb0\"\t// U+e970\n#define ICON_MS_7K \"\\xee\\xa5\\xb1\"\t// U+e971\n#define ICON_MS_7K_PLUS \"\\xee\\xa5\\xb2\"\t// U+e972\n#define ICON_MS_7MP \"\\xee\\xa5\\xb3\"\t// U+e973\n#define ICON_MS_8K \"\\xee\\xa5\\xb4\"\t// U+e974\n#define ICON_MS_8K_PLUS \"\\xee\\xa5\\xb5\"\t// U+e975\n#define ICON_MS_8MP \"\\xee\\xa5\\xb6\"\t// U+e976\n#define ICON_MS_9K \"\\xee\\xa5\\xb7\"\t// U+e977\n#define ICON_MS_9K_PLUS \"\\xee\\xa5\\xb8\"\t// U+e978\n#define ICON_MS_9MP \"\\xee\\xa5\\xb9\"\t// U+e979\n#define ICON_MS_ABC \"\\xee\\xae\\x94\"\t// U+eb94\n#define ICON_MS_AC_UNIT \"\\xee\\xac\\xbb\"\t// U+eb3b\n#define ICON_MS_ACCESS_ALARM \"\\xee\\xa1\\x95\"\t// U+e855\n#define ICON_MS_ACCESS_ALARMS \"\\xee\\xa1\\x95\"\t// U+e855\n#define ICON_MS_ACCESS_TIME \"\\xee\\xbf\\x96\"\t// U+efd6\n#define ICON_MS_ACCESS_TIME_FILLED \"\\xee\\xbf\\x96\"\t// U+efd6\n#define ICON_MS_ACCESSIBILITY \"\\xee\\xa1\\x8e\"\t// U+e84e\n#define ICON_MS_ACCESSIBILITY_NEW \"\\xee\\xa4\\xac\"\t// U+e92c\n#define ICON_MS_ACCESSIBLE \"\\xee\\xa4\\x94\"\t// U+e914\n#define ICON_MS_ACCESSIBLE_FORWARD \"\\xee\\xa4\\xb4\"\t// U+e934\n#define ICON_MS_ACCOUNT_BALANCE \"\\xee\\xa1\\x8f\"\t// U+e84f\n#define ICON_MS_ACCOUNT_BALANCE_WALLET \"\\xee\\xa1\\x90\"\t// U+e850\n#define ICON_MS_ACCOUNT_BOX \"\\xee\\xa1\\x91\"\t// U+e851\n#define ICON_MS_ACCOUNT_CHILD \"\\xee\\xa1\\x92\"\t// U+e852\n#define ICON_MS_ACCOUNT_CHILD_INVERT \"\\xee\\x99\\x99\"\t// U+e659\n#define ICON_MS_ACCOUNT_CIRCLE \"\\xef\\x88\\x8b\"\t// U+f20b\n#define ICON_MS_ACCOUNT_CIRCLE_FILLED \"\\xef\\x88\\x8b\"\t// U+f20b\n#define ICON_MS_ACCOUNT_CIRCLE_OFF \"\\xef\\x9e\\xb3\"\t// U+f7b3\n#define ICON_MS_ACCOUNT_TREE \"\\xee\\xa5\\xba\"\t// U+e97a\n#define ICON_MS_ACTION_KEY \"\\xef\\x94\\x82\"\t// U+f502\n#define ICON_MS_ACTIVITY_ZONE \"\\xee\\x87\\xa6\"\t// U+e1e6\n#define ICON_MS_ACUTE \"\\xee\\x93\\x8b\"\t// U+e4cb\n#define ICON_MS_AD \"\\xee\\x99\\x9a\"\t// U+e65a\n#define ICON_MS_AD_GROUP \"\\xee\\x99\\x9b\"\t// U+e65b\n#define ICON_MS_AD_GROUP_OFF \"\\xee\\xab\\xa5\"\t// U+eae5\n#define ICON_MS_AD_OFF \"\\xef\\x9e\\xb2\"\t// U+f7b2\n#define ICON_MS_AD_UNITS \"\\xee\\xbc\\xb9\"\t// U+ef39\n#define ICON_MS_ADAPTIVE_AUDIO_MIC \"\\xef\\x93\\x8c\"\t// U+f4cc\n#define ICON_MS_ADAPTIVE_AUDIO_MIC_OFF \"\\xef\\x93\\x8b\"\t// U+f4cb\n#define ICON_MS_ADB \"\\xee\\x98\\x8e\"\t// U+e60e\n#define ICON_MS_ADD \"\\xee\\x85\\x85\"\t// U+e145\n#define ICON_MS_ADD_A_PHOTO \"\\xee\\x90\\xb9\"\t// U+e439\n#define ICON_MS_ADD_AD \"\\xee\\x9c\\xaa\"\t// U+e72a\n#define ICON_MS_ADD_ALARM \"\\xee\\xa1\\x96\"\t// U+e856\n#define ICON_MS_ADD_ALERT \"\\xee\\x80\\x83\"\t// U+e003\n#define ICON_MS_ADD_BOX \"\\xee\\x85\\x86\"\t// U+e146\n#define ICON_MS_ADD_BUSINESS \"\\xee\\x9c\\xa9\"\t// U+e729\n#define ICON_MS_ADD_CALL \"\\xef\\x82\\xb7\"\t// U+f0b7\n#define ICON_MS_ADD_CARD \"\\xee\\xae\\x86\"\t// U+eb86\n#define ICON_MS_ADD_CHART \"\\xee\\xbc\\xbc\"\t// U+ef3c\n#define ICON_MS_ADD_CIRCLE \"\\xee\\x8e\\xba\"\t// U+e3ba\n#define ICON_MS_ADD_CIRCLE_OUTLINE \"\\xee\\x8e\\xba\"\t// U+e3ba\n#define ICON_MS_ADD_COMMENT \"\\xee\\x89\\xa6\"\t// U+e266\n#define ICON_MS_ADD_DIAMOND \"\\xef\\x92\\x9c\"\t// U+f49c\n#define ICON_MS_ADD_HOME \"\\xef\\xa3\\xab\"\t// U+f8eb\n#define ICON_MS_ADD_HOME_WORK \"\\xef\\xa3\\xad\"\t// U+f8ed\n#define ICON_MS_ADD_IC_CALL \"\\xef\\x82\\xb7\"\t// U+f0b7\n#define ICON_MS_ADD_LINK \"\\xee\\x85\\xb8\"\t// U+e178\n#define ICON_MS_ADD_LOCATION \"\\xee\\x95\\xa7\"\t// U+e567\n#define ICON_MS_ADD_LOCATION_ALT \"\\xee\\xbc\\xba\"\t// U+ef3a\n#define ICON_MS_ADD_MODERATOR \"\\xee\\xa5\\xbd\"\t// U+e97d\n#define ICON_MS_ADD_NOTES \"\\xee\\x82\\x91\"\t// U+e091\n#define ICON_MS_ADD_PHOTO_ALTERNATE \"\\xee\\x90\\xbe\"\t// U+e43e\n#define ICON_MS_ADD_REACTION \"\\xee\\x87\\x93\"\t// U+e1d3\n#define ICON_MS_ADD_ROAD \"\\xee\\xbc\\xbb\"\t// U+ef3b\n#define ICON_MS_ADD_SHOPPING_CART \"\\xee\\xa1\\x94\"\t// U+e854\n#define ICON_MS_ADD_TASK \"\\xef\\x88\\xba\"\t// U+f23a\n#define ICON_MS_ADD_TO_DRIVE \"\\xee\\x99\\x9c\"\t// U+e65c\n#define ICON_MS_ADD_TO_HOME_SCREEN \"\\xee\\x87\\xbe\"\t// U+e1fe\n#define ICON_MS_ADD_TO_PHOTOS \"\\xee\\x8e\\x9d\"\t// U+e39d\n#define ICON_MS_ADD_TO_QUEUE \"\\xee\\x81\\x9c\"\t// U+e05c\n#define ICON_MS_ADD_TRIANGLE \"\\xef\\x92\\x8e\"\t// U+f48e\n#define ICON_MS_ADDCHART \"\\xee\\xbc\\xbc\"\t// U+ef3c\n#define ICON_MS_ADF_SCANNER \"\\xee\\xab\\x9a\"\t// U+eada\n#define ICON_MS_ADJUST \"\\xee\\x8e\\x9e\"\t// U+e39e\n#define ICON_MS_ADMIN_MEDS \"\\xee\\x92\\x8d\"\t// U+e48d\n#define ICON_MS_ADMIN_PANEL_SETTINGS \"\\xee\\xbc\\xbd\"\t// U+ef3d\n#define ICON_MS_ADS_CLICK \"\\xee\\x9d\\xa2\"\t// U+e762\n#define ICON_MS_AGENDER \"\\xef\\xa2\\x88\"\t// U+f888\n#define ICON_MS_AGRICULTURE \"\\xee\\xa9\\xb9\"\t// U+ea79\n#define ICON_MS_AIR \"\\xee\\xbf\\x98\"\t// U+efd8\n#define ICON_MS_AIR_FRESHENER \"\\xee\\x8b\\x8a\"\t// U+e2ca\n#define ICON_MS_AIR_PURIFIER \"\\xee\\xa5\\xbe\"\t// U+e97e\n#define ICON_MS_AIR_PURIFIER_GEN \"\\xee\\xa0\\xa9\"\t// U+e829\n#define ICON_MS_AIRLINE_SEAT_FLAT \"\\xee\\x98\\xb0\"\t// U+e630\n#define ICON_MS_AIRLINE_SEAT_FLAT_ANGLED \"\\xee\\x98\\xb1\"\t// U+e631\n#define ICON_MS_AIRLINE_SEAT_INDIVIDUAL_SUITE \"\\xee\\x98\\xb2\"\t// U+e632\n#define ICON_MS_AIRLINE_SEAT_LEGROOM_EXTRA \"\\xee\\x98\\xb3\"\t// U+e633\n#define ICON_MS_AIRLINE_SEAT_LEGROOM_NORMAL \"\\xee\\x98\\xb4\"\t// U+e634\n#define ICON_MS_AIRLINE_SEAT_LEGROOM_REDUCED \"\\xee\\x98\\xb5\"\t// U+e635\n#define ICON_MS_AIRLINE_SEAT_RECLINE_EXTRA \"\\xee\\x98\\xb6\"\t// U+e636\n#define ICON_MS_AIRLINE_SEAT_RECLINE_NORMAL \"\\xee\\x98\\xb7\"\t// U+e637\n#define ICON_MS_AIRLINE_STOPS \"\\xee\\x9f\\x90\"\t// U+e7d0\n#define ICON_MS_AIRLINES \"\\xee\\x9f\\x8a\"\t// U+e7ca\n#define ICON_MS_AIRPLANE_TICKET \"\\xee\\xbf\\x99\"\t// U+efd9\n#define ICON_MS_AIRPLANEMODE_ACTIVE \"\\xee\\x94\\xbd\"\t// U+e53d\n#define ICON_MS_AIRPLANEMODE_INACTIVE \"\\xee\\x86\\x94\"\t// U+e194\n#define ICON_MS_AIRPLAY \"\\xee\\x81\\x95\"\t// U+e055\n#define ICON_MS_AIRPORT_SHUTTLE \"\\xee\\xac\\xbc\"\t// U+eb3c\n#define ICON_MS_AIRWARE \"\\xef\\x85\\x94\"\t// U+f154\n#define ICON_MS_AIRWAVE \"\\xef\\x85\\x94\"\t// U+f154\n#define ICON_MS_ALARM \"\\xee\\xa1\\x95\"\t// U+e855\n#define ICON_MS_ALARM_ADD \"\\xee\\xa1\\x96\"\t// U+e856\n#define ICON_MS_ALARM_OFF \"\\xee\\xa1\\x97\"\t// U+e857\n#define ICON_MS_ALARM_ON \"\\xee\\xa1\\x98\"\t// U+e858\n#define ICON_MS_ALARM_SMART_WAKE \"\\xef\\x9a\\xb0\"\t// U+f6b0\n#define ICON_MS_ALBUM \"\\xee\\x80\\x99\"\t// U+e019\n#define ICON_MS_ALIGN_CENTER \"\\xee\\x8d\\x96\"\t// U+e356\n#define ICON_MS_ALIGN_END \"\\xef\\x9e\\x97\"\t// U+f797\n#define ICON_MS_ALIGN_FLEX_CENTER \"\\xef\\x9e\\x96\"\t// U+f796\n#define ICON_MS_ALIGN_FLEX_END \"\\xef\\x9e\\x95\"\t// U+f795\n#define ICON_MS_ALIGN_FLEX_START \"\\xef\\x9e\\x94\"\t// U+f794\n#define ICON_MS_ALIGN_HORIZONTAL_CENTER \"\\xee\\x80\\x8f\"\t// U+e00f\n#define ICON_MS_ALIGN_HORIZONTAL_LEFT \"\\xee\\x80\\x8d\"\t// U+e00d\n#define ICON_MS_ALIGN_HORIZONTAL_RIGHT \"\\xee\\x80\\x90\"\t// U+e010\n#define ICON_MS_ALIGN_ITEMS_STRETCH \"\\xef\\x9e\\x93\"\t// U+f793\n#define ICON_MS_ALIGN_JUSTIFY_CENTER \"\\xef\\x9e\\x92\"\t// U+f792\n#define ICON_MS_ALIGN_JUSTIFY_FLEX_END \"\\xef\\x9e\\x91\"\t// U+f791\n#define ICON_MS_ALIGN_JUSTIFY_FLEX_START \"\\xef\\x9e\\x90\"\t// U+f790\n#define ICON_MS_ALIGN_JUSTIFY_SPACE_AROUND \"\\xef\\x9e\\x8f\"\t// U+f78f\n#define ICON_MS_ALIGN_JUSTIFY_SPACE_BETWEEN \"\\xef\\x9e\\x8e\"\t// U+f78e\n#define ICON_MS_ALIGN_JUSTIFY_SPACE_EVEN \"\\xef\\x9e\\x8d\"\t// U+f78d\n#define ICON_MS_ALIGN_JUSTIFY_STRETCH \"\\xef\\x9e\\x8c\"\t// U+f78c\n#define ICON_MS_ALIGN_SELF_STRETCH \"\\xef\\x9e\\x8b\"\t// U+f78b\n#define ICON_MS_ALIGN_SPACE_AROUND \"\\xef\\x9e\\x8a\"\t// U+f78a\n#define ICON_MS_ALIGN_SPACE_BETWEEN \"\\xef\\x9e\\x89\"\t// U+f789\n#define ICON_MS_ALIGN_SPACE_EVEN \"\\xef\\x9e\\x88\"\t// U+f788\n#define ICON_MS_ALIGN_START \"\\xef\\x9e\\x87\"\t// U+f787\n#define ICON_MS_ALIGN_STRETCH \"\\xef\\x9e\\x86\"\t// U+f786\n#define ICON_MS_ALIGN_VERTICAL_BOTTOM \"\\xee\\x80\\x95\"\t// U+e015\n#define ICON_MS_ALIGN_VERTICAL_CENTER \"\\xee\\x80\\x91\"\t// U+e011\n#define ICON_MS_ALIGN_VERTICAL_TOP \"\\xee\\x80\\x8c\"\t// U+e00c\n#define ICON_MS_ALL_INBOX \"\\xee\\xa5\\xbf\"\t// U+e97f\n#define ICON_MS_ALL_INCLUSIVE \"\\xee\\xac\\xbd\"\t// U+eb3d\n#define ICON_MS_ALL_MATCH \"\\xee\\x82\\x93\"\t// U+e093\n#define ICON_MS_ALL_OUT \"\\xee\\xa4\\x8b\"\t// U+e90b\n#define ICON_MS_ALLERGIES \"\\xee\\x82\\x94\"\t// U+e094\n#define ICON_MS_ALLERGY \"\\xee\\x99\\x8e\"\t// U+e64e\n#define ICON_MS_ALT_ROUTE \"\\xef\\x86\\x84\"\t// U+f184\n#define ICON_MS_ALTERNATE_EMAIL \"\\xee\\x83\\xa6\"\t// U+e0e6\n#define ICON_MS_ALTITUDE \"\\xef\\xa1\\xb3\"\t// U+f873\n#define ICON_MS_AMBIENT_SCREEN \"\\xef\\x9b\\x84\"\t// U+f6c4\n#define ICON_MS_AMBULANCE \"\\xef\\xa0\\x83\"\t// U+f803\n#define ICON_MS_AMEND \"\\xef\\xa0\\x82\"\t// U+f802\n#define ICON_MS_AMP_STORIES \"\\xee\\xa8\\x93\"\t// U+ea13\n#define ICON_MS_ANALYTICS \"\\xee\\xbc\\xbe\"\t// U+ef3e\n#define ICON_MS_ANCHOR \"\\xef\\x87\\x8d\"\t// U+f1cd\n#define ICON_MS_ANDROID \"\\xee\\xa1\\x99\"\t// U+e859\n#define ICON_MS_ANIMATED_IMAGES \"\\xef\\x92\\x9a\"\t// U+f49a\n#define ICON_MS_ANIMATION \"\\xee\\x9c\\x9c\"\t// U+e71c\n#define ICON_MS_ANNOUNCEMENT \"\\xee\\xa1\\xbf\"\t// U+e87f\n#define ICON_MS_AOD \"\\xee\\xbf\\x9a\"\t// U+efda\n#define ICON_MS_AOD_TABLET \"\\xef\\xa2\\x9f\"\t// U+f89f\n#define ICON_MS_AOD_WATCH \"\\xef\\x9a\\xac\"\t// U+f6ac\n#define ICON_MS_APARTMENT \"\\xee\\xa9\\x80\"\t// U+ea40\n#define ICON_MS_API \"\\xef\\x86\\xb7\"\t// U+f1b7\n#define ICON_MS_APK_DOCUMENT \"\\xef\\xa2\\x8e\"\t// U+f88e\n#define ICON_MS_APK_INSTALL \"\\xef\\xa2\\x8f\"\t// U+f88f\n#define ICON_MS_APP_BADGING \"\\xef\\x9c\\xaf\"\t// U+f72f\n#define ICON_MS_APP_BLOCKING \"\\xee\\xbc\\xbf\"\t// U+ef3f\n#define ICON_MS_APP_PROMO \"\\xee\\xa6\\x81\"\t// U+e981\n#define ICON_MS_APP_REGISTRATION \"\\xee\\xbd\\x80\"\t// U+ef40\n#define ICON_MS_APP_SETTINGS_ALT \"\\xee\\xbd\\x81\"\t// U+ef41\n#define ICON_MS_APP_SHORTCUT \"\\xee\\xab\\xa4\"\t// U+eae4\n#define ICON_MS_APPAREL \"\\xee\\xbd\\xbb\"\t// U+ef7b\n#define ICON_MS_APPROVAL \"\\xee\\xa6\\x82\"\t// U+e982\n#define ICON_MS_APPROVAL_DELEGATION \"\\xef\\xa1\\x8a\"\t// U+f84a\n#define ICON_MS_APPS \"\\xee\\x97\\x83\"\t// U+e5c3\n#define ICON_MS_APPS_OUTAGE \"\\xee\\x9f\\x8c\"\t// U+e7cc\n#define ICON_MS_AQ \"\\xef\\x95\\x9a\"\t// U+f55a\n#define ICON_MS_AQ_INDOOR \"\\xef\\x95\\x9b\"\t// U+f55b\n#define ICON_MS_AR_ON_YOU \"\\xee\\xbd\\xbc\"\t// U+ef7c\n#define ICON_MS_AR_STICKERS \"\\xee\\xa6\\x83\"\t// U+e983\n#define ICON_MS_ARCHITECTURE \"\\xee\\xa8\\xbb\"\t// U+ea3b\n#define ICON_MS_ARCHIVE \"\\xee\\x85\\x89\"\t// U+e149\n#define ICON_MS_AREA_CHART \"\\xee\\x9d\\xb0\"\t// U+e770\n#define ICON_MS_ARMING_COUNTDOWN \"\\xee\\x9e\\x8a\"\t// U+e78a\n#define ICON_MS_ARROW_AND_EDGE \"\\xef\\x97\\x97\"\t// U+f5d7\n#define ICON_MS_ARROW_BACK \"\\xee\\x97\\x84\"\t// U+e5c4\n#define ICON_MS_ARROW_BACK_IOS \"\\xee\\x97\\xa0\"\t// U+e5e0\n#define ICON_MS_ARROW_BACK_IOS_NEW \"\\xee\\x8b\\xaa\"\t// U+e2ea\n#define ICON_MS_ARROW_CIRCLE_DOWN \"\\xef\\x86\\x81\"\t// U+f181\n#define ICON_MS_ARROW_CIRCLE_LEFT \"\\xee\\xaa\\xa7\"\t// U+eaa7\n#define ICON_MS_ARROW_CIRCLE_RIGHT \"\\xee\\xaa\\xaa\"\t// U+eaaa\n#define ICON_MS_ARROW_CIRCLE_UP \"\\xef\\x86\\x82\"\t// U+f182\n#define ICON_MS_ARROW_COOL_DOWN \"\\xef\\x92\\xb6\"\t// U+f4b6\n#define ICON_MS_ARROW_DOWNWARD \"\\xee\\x97\\x9b\"\t// U+e5db\n#define ICON_MS_ARROW_DOWNWARD_ALT \"\\xee\\xa6\\x84\"\t// U+e984\n#define ICON_MS_ARROW_DROP_DOWN \"\\xee\\x97\\x85\"\t// U+e5c5\n#define ICON_MS_ARROW_DROP_DOWN_CIRCLE \"\\xee\\x97\\x86\"\t// U+e5c6\n#define ICON_MS_ARROW_DROP_UP \"\\xee\\x97\\x87\"\t// U+e5c7\n#define ICON_MS_ARROW_FORWARD \"\\xee\\x97\\x88\"\t// U+e5c8\n#define ICON_MS_ARROW_FORWARD_IOS \"\\xee\\x97\\xa1\"\t// U+e5e1\n#define ICON_MS_ARROW_INSERT \"\\xef\\xa0\\xb7\"\t// U+f837\n#define ICON_MS_ARROW_LEFT \"\\xee\\x97\\x9e\"\t// U+e5de\n#define ICON_MS_ARROW_LEFT_ALT \"\\xee\\xbd\\xbd\"\t// U+ef7d\n#define ICON_MS_ARROW_OR_EDGE \"\\xef\\x97\\x96\"\t// U+f5d6\n#define ICON_MS_ARROW_OUTWARD \"\\xef\\xa3\\x8e\"\t// U+f8ce\n#define ICON_MS_ARROW_RANGE \"\\xef\\x9a\\x9b\"\t// U+f69b\n#define ICON_MS_ARROW_RIGHT \"\\xee\\x97\\x9f\"\t// U+e5df\n#define ICON_MS_ARROW_RIGHT_ALT \"\\xee\\xa5\\x81\"\t// U+e941\n#define ICON_MS_ARROW_SELECTOR_TOOL \"\\xef\\xa0\\xaf\"\t// U+f82f\n#define ICON_MS_ARROW_SPLIT \"\\xee\\xa8\\x84\"\t// U+ea04\n#define ICON_MS_ARROW_TOP_LEFT \"\\xef\\x9c\\xae\"\t// U+f72e\n#define ICON_MS_ARROW_TOP_RIGHT \"\\xef\\x9c\\xad\"\t// U+f72d\n#define ICON_MS_ARROW_UPWARD \"\\xee\\x97\\x98\"\t// U+e5d8\n#define ICON_MS_ARROW_UPWARD_ALT \"\\xee\\xa6\\x86\"\t// U+e986\n#define ICON_MS_ARROW_WARM_UP \"\\xef\\x92\\xb5\"\t// U+f4b5\n#define ICON_MS_ARROWS_MORE_DOWN \"\\xef\\xa2\\xab\"\t// U+f8ab\n#define ICON_MS_ARROWS_MORE_UP \"\\xef\\xa2\\xac\"\t// U+f8ac\n#define ICON_MS_ARROWS_OUTWARD \"\\xef\\x9c\\xac\"\t// U+f72c\n#define ICON_MS_ART_TRACK \"\\xee\\x81\\xa0\"\t// U+e060\n#define ICON_MS_ARTICLE \"\\xee\\xbd\\x82\"\t// U+ef42\n#define ICON_MS_ARTICLE_SHORTCUT \"\\xef\\x96\\x87\"\t// U+f587\n#define ICON_MS_ARTIST \"\\xee\\x80\\x9a\"\t// U+e01a\n#define ICON_MS_ASPECT_RATIO \"\\xee\\xa1\\x9b\"\t// U+e85b\n#define ICON_MS_ASSESSMENT \"\\xef\\x83\\x8c\"\t// U+f0cc\n#define ICON_MS_ASSIGNMENT \"\\xee\\xa1\\x9d\"\t// U+e85d\n#define ICON_MS_ASSIGNMENT_ADD \"\\xef\\xa1\\x88\"\t// U+f848\n#define ICON_MS_ASSIGNMENT_IND \"\\xee\\xa1\\x9e\"\t// U+e85e\n#define ICON_MS_ASSIGNMENT_LATE \"\\xee\\xa1\\x9f\"\t// U+e85f\n#define ICON_MS_ASSIGNMENT_RETURN \"\\xee\\xa1\\xa0\"\t// U+e860\n#define ICON_MS_ASSIGNMENT_RETURNED \"\\xee\\xa1\\xa1\"\t// U+e861\n#define ICON_MS_ASSIGNMENT_TURNED_IN \"\\xee\\xa1\\xa2\"\t// U+e862\n#define ICON_MS_ASSIST_WALKER \"\\xef\\xa3\\x95\"\t// U+f8d5\n#define ICON_MS_ASSISTANT \"\\xee\\x8e\\x9f\"\t// U+e39f\n#define ICON_MS_ASSISTANT_DEVICE \"\\xee\\xa6\\x87\"\t// U+e987\n#define ICON_MS_ASSISTANT_DIRECTION \"\\xee\\xa6\\x88\"\t// U+e988\n#define ICON_MS_ASSISTANT_NAVIGATION \"\\xee\\xa6\\x89\"\t// U+e989\n#define ICON_MS_ASSISTANT_ON_HUB \"\\xef\\x9b\\x81\"\t// U+f6c1\n#define ICON_MS_ASSISTANT_PHOTO \"\\xef\\x83\\x86\"\t// U+f0c6\n#define ICON_MS_ASSURED_WORKLOAD \"\\xee\\xad\\xaf\"\t// U+eb6f\n#define ICON_MS_ASTERISK \"\\xef\\x94\\xa5\"\t// U+f525\n#define ICON_MS_ASTROPHOTOGRAPHY_AUTO \"\\xef\\x87\\x99\"\t// U+f1d9\n#define ICON_MS_ASTROPHOTOGRAPHY_OFF \"\\xef\\x87\\x9a\"\t// U+f1da\n#define ICON_MS_ATM \"\\xee\\x95\\xb3\"\t// U+e573\n#define ICON_MS_ATR \"\\xee\\xaf\\x87\"\t// U+ebc7\n#define ICON_MS_ATTACH_EMAIL \"\\xee\\xa9\\x9e\"\t// U+ea5e\n#define ICON_MS_ATTACH_FILE \"\\xee\\x88\\xa6\"\t// U+e226\n#define ICON_MS_ATTACH_FILE_ADD \"\\xef\\xa1\\x81\"\t// U+f841\n#define ICON_MS_ATTACH_FILE_OFF \"\\xef\\x93\\x99\"\t// U+f4d9\n#define ICON_MS_ATTACH_MONEY \"\\xee\\x88\\xa7\"\t// U+e227\n#define ICON_MS_ATTACHMENT \"\\xee\\x8a\\xbc\"\t// U+e2bc\n#define ICON_MS_ATTRACTIONS \"\\xee\\xa9\\x92\"\t// U+ea52\n#define ICON_MS_ATTRIBUTION \"\\xee\\xbf\\x9b\"\t// U+efdb\n#define ICON_MS_AUDIO_DESCRIPTION \"\\xef\\x96\\x8c\"\t// U+f58c\n#define ICON_MS_AUDIO_FILE \"\\xee\\xae\\x82\"\t// U+eb82\n#define ICON_MS_AUDIO_VIDEO_RECEIVER \"\\xef\\x97\\x93\"\t// U+f5d3\n#define ICON_MS_AUDIOTRACK \"\\xee\\x90\\x85\"\t// U+e405\n#define ICON_MS_AUTO_ACTIVITY_ZONE \"\\xef\\xa2\\xad\"\t// U+f8ad\n#define ICON_MS_AUTO_AWESOME \"\\xee\\x99\\x9f\"\t// U+e65f\n#define ICON_MS_AUTO_AWESOME_MOSAIC \"\\xee\\x99\\xa0\"\t// U+e660\n#define ICON_MS_AUTO_AWESOME_MOTION \"\\xee\\x99\\xa1\"\t// U+e661\n#define ICON_MS_AUTO_DELETE \"\\xee\\xa9\\x8c\"\t// U+ea4c\n#define ICON_MS_AUTO_DETECT_VOICE \"\\xef\\xa0\\xbe\"\t// U+f83e\n#define ICON_MS_AUTO_DRAW_SOLID \"\\xee\\xa6\\x8a\"\t// U+e98a\n#define ICON_MS_AUTO_FIX \"\\xee\\x99\\xa3\"\t// U+e663\n#define ICON_MS_AUTO_FIX_HIGH \"\\xee\\x99\\xa3\"\t// U+e663\n#define ICON_MS_AUTO_FIX_NORMAL \"\\xee\\x99\\xa4\"\t// U+e664\n#define ICON_MS_AUTO_FIX_OFF \"\\xee\\x99\\xa5\"\t// U+e665\n#define ICON_MS_AUTO_GRAPH \"\\xee\\x93\\xbb\"\t// U+e4fb\n#define ICON_MS_AUTO_LABEL \"\\xef\\x9a\\xbe\"\t// U+f6be\n#define ICON_MS_AUTO_MEETING_ROOM \"\\xef\\x9a\\xbf\"\t// U+f6bf\n#define ICON_MS_AUTO_MODE \"\\xee\\xb0\\xa0\"\t// U+ec20\n#define ICON_MS_AUTO_READ_PAUSE \"\\xef\\x88\\x99\"\t// U+f219\n#define ICON_MS_AUTO_READ_PLAY \"\\xef\\x88\\x96\"\t// U+f216\n#define ICON_MS_AUTO_SCHEDULE \"\\xee\\x88\\x94\"\t// U+e214\n#define ICON_MS_AUTO_STORIES \"\\xee\\x99\\xa6\"\t// U+e666\n#define ICON_MS_AUTO_TIMER \"\\xee\\xbd\\xbf\"\t// U+ef7f\n#define ICON_MS_AUTO_TOWING \"\\xee\\x9c\\x9e\"\t// U+e71e\n#define ICON_MS_AUTO_TRANSMISSION \"\\xef\\x94\\xbf\"\t// U+f53f\n#define ICON_MS_AUTO_VIDEOCAM \"\\xef\\x9b\\x80\"\t// U+f6c0\n#define ICON_MS_AUTOFPS_SELECT \"\\xee\\xbf\\x9c\"\t// U+efdc\n#define ICON_MS_AUTOPAUSE \"\\xef\\x9a\\xb6\"\t// U+f6b6\n#define ICON_MS_AUTOPAY \"\\xef\\xa1\\x8b\"\t// U+f84b\n#define ICON_MS_AUTOPLAY \"\\xef\\x9a\\xb5\"\t// U+f6b5\n#define ICON_MS_AUTORENEW \"\\xee\\xa1\\xa3\"\t// U+e863\n#define ICON_MS_AUTOSTOP \"\\xef\\x9a\\x82\"\t// U+f682\n#define ICON_MS_AV1 \"\\xef\\x92\\xb0\"\t// U+f4b0\n#define ICON_MS_AV_TIMER \"\\xee\\x80\\x9b\"\t// U+e01b\n#define ICON_MS_AVC \"\\xef\\x92\\xaf\"\t// U+f4af\n#define ICON_MS_AVG_PACE \"\\xef\\x9a\\xbb\"\t// U+f6bb\n#define ICON_MS_AVG_TIME \"\\xef\\xa0\\x93\"\t// U+f813\n#define ICON_MS_AWARD_STAR \"\\xef\\x98\\x92\"\t// U+f612\n#define ICON_MS_AZM \"\\xef\\x9b\\xac\"\t// U+f6ec\n#define ICON_MS_BABY_CHANGING_STATION \"\\xef\\x86\\x9b\"\t// U+f19b\n#define ICON_MS_BACK_HAND \"\\xee\\x9d\\xa4\"\t// U+e764\n#define ICON_MS_BACK_TO_TAB \"\\xef\\x9c\\xab\"\t// U+f72b\n#define ICON_MS_BACKGROUND_DOT_LARGE \"\\xef\\x9e\\x9e\"\t// U+f79e\n#define ICON_MS_BACKGROUND_DOT_SMALL \"\\xef\\x94\\x94\"\t// U+f514\n#define ICON_MS_BACKGROUND_GRID_SMALL \"\\xef\\x9e\\x9d\"\t// U+f79d\n#define ICON_MS_BACKGROUND_REPLACE \"\\xef\\x88\\x8a\"\t// U+f20a\n#define ICON_MS_BACKLIGHT_HIGH \"\\xef\\x9f\\xad\"\t// U+f7ed\n#define ICON_MS_BACKLIGHT_HIGH_OFF \"\\xef\\x93\\xaf\"\t// U+f4ef\n#define ICON_MS_BACKLIGHT_LOW \"\\xef\\x9f\\xac\"\t// U+f7ec\n#define ICON_MS_BACKPACK \"\\xef\\x86\\x9c\"\t// U+f19c\n#define ICON_MS_BACKSPACE \"\\xee\\x85\\x8a\"\t// U+e14a\n#define ICON_MS_BACKUP \"\\xee\\xa1\\xa4\"\t// U+e864\n#define ICON_MS_BACKUP_TABLE \"\\xee\\xbd\\x83\"\t// U+ef43\n#define ICON_MS_BADGE \"\\xee\\xa9\\xa7\"\t// U+ea67\n#define ICON_MS_BADGE_CRITICAL_BATTERY \"\\xef\\x85\\x96\"\t// U+f156\n#define ICON_MS_BAKERY_DINING \"\\xee\\xa9\\x93\"\t// U+ea53\n#define ICON_MS_BALANCE \"\\xee\\xab\\xb6\"\t// U+eaf6\n#define ICON_MS_BALCONY \"\\xee\\x96\\x8f\"\t// U+e58f\n#define ICON_MS_BALLOT \"\\xee\\x85\\xb2\"\t// U+e172\n#define ICON_MS_BAR_CHART \"\\xee\\x89\\xab\"\t// U+e26b\n#define ICON_MS_BAR_CHART_4_BARS \"\\xef\\x9a\\x81\"\t// U+f681\n#define ICON_MS_BARCODE \"\\xee\\x9c\\x8b\"\t// U+e70b\n#define ICON_MS_BARCODE_READER \"\\xef\\xa1\\x9c\"\t// U+f85c\n#define ICON_MS_BARCODE_SCANNER \"\\xee\\x9c\\x8c\"\t// U+e70c\n#define ICON_MS_BAREFOOT \"\\xef\\xa1\\xb1\"\t// U+f871\n#define ICON_MS_BATCH_PREDICTION \"\\xef\\x83\\xb5\"\t// U+f0f5\n#define ICON_MS_BATH_OUTDOOR \"\\xef\\x9b\\xbb\"\t// U+f6fb\n#define ICON_MS_BATH_PRIVATE \"\\xef\\x9b\\xba\"\t// U+f6fa\n#define ICON_MS_BATH_PUBLIC_LARGE \"\\xef\\x9b\\xb9\"\t// U+f6f9\n#define ICON_MS_BATHROOM \"\\xee\\xbf\\x9d\"\t// U+efdd\n#define ICON_MS_BATHTUB \"\\xee\\xa9\\x81\"\t// U+ea41\n#define ICON_MS_BATTERY_0_BAR \"\\xee\\xaf\\x9c\"\t// U+ebdc\n#define ICON_MS_BATTERY_1_BAR \"\\xef\\x82\\x9c\"\t// U+f09c\n#define ICON_MS_BATTERY_20 \"\\xef\\x82\\x9c\"\t// U+f09c\n#define ICON_MS_BATTERY_2_BAR \"\\xef\\x82\\x9d\"\t// U+f09d\n#define ICON_MS_BATTERY_30 \"\\xef\\x82\\x9d\"\t// U+f09d\n#define ICON_MS_BATTERY_3_BAR \"\\xef\\x82\\x9e\"\t// U+f09e\n#define ICON_MS_BATTERY_4_BAR \"\\xef\\x82\\x9f\"\t// U+f09f\n#define ICON_MS_BATTERY_50 \"\\xef\\x82\\x9e\"\t// U+f09e\n#define ICON_MS_BATTERY_5_BAR \"\\xef\\x82\\xa0\"\t// U+f0a0\n#define ICON_MS_BATTERY_60 \"\\xef\\x82\\x9f\"\t// U+f09f\n#define ICON_MS_BATTERY_6_BAR \"\\xef\\x82\\xa1\"\t// U+f0a1\n#define ICON_MS_BATTERY_80 \"\\xef\\x82\\xa0\"\t// U+f0a0\n#define ICON_MS_BATTERY_90 \"\\xef\\x82\\xa1\"\t// U+f0a1\n#define ICON_MS_BATTERY_ALERT \"\\xee\\x86\\x9c\"\t// U+e19c\n#define ICON_MS_BATTERY_CHANGE \"\\xef\\x9f\\xab\"\t// U+f7eb\n#define ICON_MS_BATTERY_CHARGING_20 \"\\xef\\x82\\xa2\"\t// U+f0a2\n#define ICON_MS_BATTERY_CHARGING_30 \"\\xef\\x82\\xa3\"\t// U+f0a3\n#define ICON_MS_BATTERY_CHARGING_50 \"\\xef\\x82\\xa4\"\t// U+f0a4\n#define ICON_MS_BATTERY_CHARGING_60 \"\\xef\\x82\\xa5\"\t// U+f0a5\n#define ICON_MS_BATTERY_CHARGING_80 \"\\xef\\x82\\xa6\"\t// U+f0a6\n#define ICON_MS_BATTERY_CHARGING_90 \"\\xef\\x82\\xa7\"\t// U+f0a7\n#define ICON_MS_BATTERY_CHARGING_FULL \"\\xee\\x86\\xa3\"\t// U+e1a3\n#define ICON_MS_BATTERY_ERROR \"\\xef\\x9f\\xaa\"\t// U+f7ea\n#define ICON_MS_BATTERY_FULL \"\\xee\\x86\\xa5\"\t// U+e1a5\n#define ICON_MS_BATTERY_FULL_ALT \"\\xef\\x84\\xbb\"\t// U+f13b\n#define ICON_MS_BATTERY_HORIZ_000 \"\\xef\\xa2\\xae\"\t// U+f8ae\n#define ICON_MS_BATTERY_HORIZ_050 \"\\xef\\xa2\\xaf\"\t// U+f8af\n#define ICON_MS_BATTERY_HORIZ_075 \"\\xef\\xa2\\xb0\"\t// U+f8b0\n#define ICON_MS_BATTERY_LOW \"\\xef\\x85\\x95\"\t// U+f155\n#define ICON_MS_BATTERY_PLUS \"\\xef\\x9f\\xa9\"\t// U+f7e9\n#define ICON_MS_BATTERY_PROFILE \"\\xee\\x88\\x86\"\t// U+e206\n#define ICON_MS_BATTERY_SAVER \"\\xee\\xbf\\x9e\"\t// U+efde\n#define ICON_MS_BATTERY_SHARE \"\\xef\\x99\\xbe\"\t// U+f67e\n#define ICON_MS_BATTERY_STATUS_GOOD \"\\xef\\x99\\xbd\"\t// U+f67d\n#define ICON_MS_BATTERY_STD \"\\xee\\x86\\xa5\"\t// U+e1a5\n#define ICON_MS_BATTERY_UNKNOWN \"\\xee\\x86\\xa6\"\t// U+e1a6\n#define ICON_MS_BATTERY_VERT_005 \"\\xef\\xa2\\xb1\"\t// U+f8b1\n#define ICON_MS_BATTERY_VERT_020 \"\\xef\\xa2\\xb2\"\t// U+f8b2\n#define ICON_MS_BATTERY_VERT_050 \"\\xef\\xa2\\xb3\"\t// U+f8b3\n#define ICON_MS_BATTERY_VERY_LOW \"\\xef\\x85\\x96\"\t// U+f156\n#define ICON_MS_BEACH_ACCESS \"\\xee\\xac\\xbe\"\t// U+eb3e\n#define ICON_MS_BED \"\\xee\\xbf\\x9f\"\t// U+efdf\n#define ICON_MS_BEDROOM_BABY \"\\xee\\xbf\\xa0\"\t// U+efe0\n#define ICON_MS_BEDROOM_CHILD \"\\xee\\xbf\\xa1\"\t// U+efe1\n#define ICON_MS_BEDROOM_PARENT \"\\xee\\xbf\\xa2\"\t// U+efe2\n#define ICON_MS_BEDTIME \"\\xee\\xbd\\x84\"\t// U+ef44\n#define ICON_MS_BEDTIME_OFF \"\\xee\\xad\\xb6\"\t// U+eb76\n#define ICON_MS_BEENHERE \"\\xee\\x94\\xad\"\t// U+e52d\n#define ICON_MS_BENTO \"\\xef\\x87\\xb4\"\t// U+f1f4\n#define ICON_MS_BIA \"\\xef\\x9b\\xab\"\t// U+f6eb\n#define ICON_MS_BID_LANDSCAPE \"\\xee\\x99\\xb8\"\t// U+e678\n#define ICON_MS_BID_LANDSCAPE_DISABLED \"\\xee\\xbe\\x81\"\t// U+ef81\n#define ICON_MS_BIGTOP_UPDATES \"\\xee\\x99\\xa9\"\t// U+e669\n#define ICON_MS_BIKE_SCOOTER \"\\xee\\xbd\\x85\"\t// U+ef45\n#define ICON_MS_BIOTECH \"\\xee\\xa8\\xba\"\t// U+ea3a\n#define ICON_MS_BLANKET \"\\xee\\xa0\\xa8\"\t// U+e828\n#define ICON_MS_BLENDER \"\\xee\\xbf\\xa3\"\t// U+efe3\n#define ICON_MS_BLIND \"\\xef\\xa3\\x96\"\t// U+f8d6\n#define ICON_MS_BLINDS \"\\xee\\x8a\\x86\"\t// U+e286\n#define ICON_MS_BLINDS_CLOSED \"\\xee\\xb0\\x9f\"\t// U+ec1f\n#define ICON_MS_BLOCK \"\\xef\\x82\\x8c\"\t// U+f08c\n#define ICON_MS_BLOOD_PRESSURE \"\\xee\\x82\\x97\"\t// U+e097\n#define ICON_MS_BLOODTYPE \"\\xee\\xbf\\xa4\"\t// U+efe4\n#define ICON_MS_BLUETOOTH \"\\xee\\x86\\xa7\"\t// U+e1a7\n#define ICON_MS_BLUETOOTH_AUDIO \"\\xee\\x98\\x8f\"\t// U+e60f\n#define ICON_MS_BLUETOOTH_CONNECTED \"\\xee\\x86\\xa8\"\t// U+e1a8\n#define ICON_MS_BLUETOOTH_DISABLED \"\\xee\\x86\\xa9\"\t// U+e1a9\n#define ICON_MS_BLUETOOTH_DRIVE \"\\xee\\xbf\\xa5\"\t// U+efe5\n#define ICON_MS_BLUETOOTH_SEARCHING \"\\xee\\x98\\x8f\"\t// U+e60f\n#define ICON_MS_BLUR_CIRCULAR \"\\xee\\x8e\\xa2\"\t// U+e3a2\n#define ICON_MS_BLUR_LINEAR \"\\xee\\x8e\\xa3\"\t// U+e3a3\n#define ICON_MS_BLUR_MEDIUM \"\\xee\\xa1\\x8c\"\t// U+e84c\n#define ICON_MS_BLUR_OFF \"\\xee\\x8e\\xa4\"\t// U+e3a4\n#define ICON_MS_BLUR_ON \"\\xee\\x8e\\xa5\"\t// U+e3a5\n#define ICON_MS_BLUR_SHORT \"\\xee\\xa3\\x8f\"\t// U+e8cf\n#define ICON_MS_BODY_FAT \"\\xee\\x82\\x98\"\t// U+e098\n#define ICON_MS_BODY_SYSTEM \"\\xee\\x82\\x99\"\t// U+e099\n#define ICON_MS_BOLT \"\\xee\\xa8\\x8b\"\t// U+ea0b\n#define ICON_MS_BOMB \"\\xef\\x95\\xa8\"\t// U+f568\n#define ICON_MS_BOOK \"\\xee\\xa1\\xae\"\t// U+e86e\n#define ICON_MS_BOOK_2 \"\\xef\\x94\\xbe\"\t// U+f53e\n#define ICON_MS_BOOK_3 \"\\xef\\x94\\xbd\"\t// U+f53d\n#define ICON_MS_BOOK_4 \"\\xef\\x94\\xbc\"\t// U+f53c\n#define ICON_MS_BOOK_5 \"\\xef\\x94\\xbb\"\t// U+f53b\n#define ICON_MS_BOOK_ONLINE \"\\xef\\x88\\x97\"\t// U+f217\n#define ICON_MS_BOOKMARK \"\\xee\\xa3\\xa7\"\t// U+e8e7\n#define ICON_MS_BOOKMARK_ADD \"\\xee\\x96\\x98\"\t// U+e598\n#define ICON_MS_BOOKMARK_ADDED \"\\xee\\x96\\x99\"\t// U+e599\n#define ICON_MS_BOOKMARK_BORDER \"\\xee\\xa3\\xa7\"\t// U+e8e7\n#define ICON_MS_BOOKMARK_MANAGER \"\\xef\\x9e\\xb1\"\t// U+f7b1\n#define ICON_MS_BOOKMARK_REMOVE \"\\xee\\x96\\x9a\"\t// U+e59a\n#define ICON_MS_BOOKMARKS \"\\xee\\xa6\\x8b\"\t// U+e98b\n#define ICON_MS_BORDER_ALL \"\\xee\\x88\\xa8\"\t// U+e228\n#define ICON_MS_BORDER_BOTTOM \"\\xee\\x88\\xa9\"\t// U+e229\n#define ICON_MS_BORDER_CLEAR \"\\xee\\x88\\xaa\"\t// U+e22a\n#define ICON_MS_BORDER_COLOR \"\\xee\\x88\\xab\"\t// U+e22b\n#define ICON_MS_BORDER_HORIZONTAL \"\\xee\\x88\\xac\"\t// U+e22c\n#define ICON_MS_BORDER_INNER \"\\xee\\x88\\xad\"\t// U+e22d\n#define ICON_MS_BORDER_LEFT \"\\xee\\x88\\xae\"\t// U+e22e\n#define ICON_MS_BORDER_OUTER \"\\xee\\x88\\xaf\"\t// U+e22f\n#define ICON_MS_BORDER_RIGHT \"\\xee\\x88\\xb0\"\t// U+e230\n#define ICON_MS_BORDER_STYLE \"\\xee\\x88\\xb1\"\t// U+e231\n#define ICON_MS_BORDER_TOP \"\\xee\\x88\\xb2\"\t// U+e232\n#define ICON_MS_BORDER_VERTICAL \"\\xee\\x88\\xb3\"\t// U+e233\n#define ICON_MS_BOTTOM_APP_BAR \"\\xee\\x9c\\xb0\"\t// U+e730\n#define ICON_MS_BOTTOM_DRAWER \"\\xee\\x9c\\xad\"\t// U+e72d\n#define ICON_MS_BOTTOM_NAVIGATION \"\\xee\\xa6\\x8c\"\t// U+e98c\n#define ICON_MS_BOTTOM_PANEL_CLOSE \"\\xef\\x9c\\xaa\"\t// U+f72a\n#define ICON_MS_BOTTOM_PANEL_OPEN \"\\xef\\x9c\\xa9\"\t// U+f729\n#define ICON_MS_BOTTOM_RIGHT_CLICK \"\\xef\\x9a\\x84\"\t// U+f684\n#define ICON_MS_BOTTOM_SHEETS \"\\xee\\xa6\\x8d\"\t// U+e98d\n#define ICON_MS_BOX \"\\xef\\x96\\xa4\"\t// U+f5a4\n#define ICON_MS_BOX_ADD \"\\xef\\x96\\xa5\"\t// U+f5a5\n#define ICON_MS_BOX_EDIT \"\\xef\\x96\\xa6\"\t// U+f5a6\n#define ICON_MS_BOY \"\\xee\\xad\\xa7\"\t// U+eb67\n#define ICON_MS_BRAND_AWARENESS \"\\xee\\xa6\\x8e\"\t// U+e98e\n#define ICON_MS_BRAND_FAMILY \"\\xef\\x93\\xb1\"\t// U+f4f1\n#define ICON_MS_BRANDING_WATERMARK \"\\xee\\x81\\xab\"\t// U+e06b\n#define ICON_MS_BREAKFAST_DINING \"\\xee\\xa9\\x94\"\t// U+ea54\n#define ICON_MS_BREAKING_NEWS \"\\xee\\xa8\\x88\"\t// U+ea08\n#define ICON_MS_BREAKING_NEWS_ALT_1 \"\\xef\\x82\\xba\"\t// U+f0ba\n#define ICON_MS_BREASTFEEDING \"\\xef\\xa1\\x96\"\t// U+f856\n#define ICON_MS_BRIGHTNESS_1 \"\\xee\\x8f\\xba\"\t// U+e3fa\n#define ICON_MS_BRIGHTNESS_2 \"\\xef\\x80\\xb6\"\t// U+f036\n#define ICON_MS_BRIGHTNESS_3 \"\\xee\\x8e\\xa8\"\t// U+e3a8\n#define ICON_MS_BRIGHTNESS_4 \"\\xee\\x8e\\xa9\"\t// U+e3a9\n#define ICON_MS_BRIGHTNESS_5 \"\\xee\\x8e\\xaa\"\t// U+e3aa\n#define ICON_MS_BRIGHTNESS_6 \"\\xee\\x8e\\xab\"\t// U+e3ab\n#define ICON_MS_BRIGHTNESS_7 \"\\xee\\x8e\\xac\"\t// U+e3ac\n#define ICON_MS_BRIGHTNESS_ALERT \"\\xef\\x97\\x8f\"\t// U+f5cf\n#define ICON_MS_BRIGHTNESS_AUTO \"\\xee\\x86\\xab\"\t// U+e1ab\n#define ICON_MS_BRIGHTNESS_EMPTY \"\\xef\\x9f\\xa8\"\t// U+f7e8\n#define ICON_MS_BRIGHTNESS_HIGH \"\\xee\\x86\\xac\"\t// U+e1ac\n#define ICON_MS_BRIGHTNESS_LOW \"\\xee\\x86\\xad\"\t// U+e1ad\n#define ICON_MS_BRIGHTNESS_MEDIUM \"\\xee\\x86\\xae\"\t// U+e1ae\n#define ICON_MS_BRING_YOUR_OWN_IP \"\\xee\\x80\\x96\"\t// U+e016\n#define ICON_MS_BROADCAST_ON_HOME \"\\xef\\xa3\\xb8\"\t// U+f8f8\n#define ICON_MS_BROADCAST_ON_PERSONAL \"\\xef\\xa3\\xb9\"\t// U+f8f9\n#define ICON_MS_BROKEN_IMAGE \"\\xee\\x8e\\xad\"\t// U+e3ad\n#define ICON_MS_BROWSE \"\\xee\\xac\\x93\"\t// U+eb13\n#define ICON_MS_BROWSE_ACTIVITY \"\\xef\\xa2\\xa5\"\t// U+f8a5\n#define ICON_MS_BROWSE_GALLERY \"\\xee\\xaf\\x91\"\t// U+ebd1\n#define ICON_MS_BROWSER_NOT_SUPPORTED \"\\xee\\xbd\\x87\"\t// U+ef47\n#define ICON_MS_BROWSER_UPDATED \"\\xee\\x9f\\x8f\"\t// U+e7cf\n#define ICON_MS_BRUNCH_DINING \"\\xee\\xa9\\xb3\"\t// U+ea73\n#define ICON_MS_BRUSH \"\\xee\\x8e\\xae\"\t// U+e3ae\n#define ICON_MS_BUBBLE \"\\xee\\xbe\\x83\"\t// U+ef83\n#define ICON_MS_BUBBLE_CHART \"\\xee\\x9b\\x9d\"\t// U+e6dd\n#define ICON_MS_BUBBLES \"\\xef\\x99\\x8e\"\t// U+f64e\n#define ICON_MS_BUG_REPORT \"\\xee\\xa1\\xa8\"\t// U+e868\n#define ICON_MS_BUILD \"\\xef\\xa3\\x8d\"\t// U+f8cd\n#define ICON_MS_BUILD_CIRCLE \"\\xee\\xbd\\x88\"\t// U+ef48\n#define ICON_MS_BUNGALOW \"\\xee\\x96\\x91\"\t// U+e591\n#define ICON_MS_BURST_MODE \"\\xee\\x90\\xbc\"\t// U+e43c\n#define ICON_MS_BUS_ALERT \"\\xee\\xa6\\x8f\"\t// U+e98f\n#define ICON_MS_BUSINESS \"\\xee\\x9f\\xae\"\t// U+e7ee\n#define ICON_MS_BUSINESS_CENTER \"\\xee\\xac\\xbf\"\t// U+eb3f\n#define ICON_MS_BUSINESS_CHIP \"\\xef\\xa1\\x8c\"\t// U+f84c\n#define ICON_MS_BUSINESS_MESSAGES \"\\xee\\xbe\\x84\"\t// U+ef84\n#define ICON_MS_BUTTONS_ALT \"\\xee\\x9c\\xaf\"\t// U+e72f\n#define ICON_MS_CABIN \"\\xee\\x96\\x89\"\t// U+e589\n#define ICON_MS_CABLE \"\\xee\\xbf\\xa6\"\t// U+efe6\n#define ICON_MS_CACHED \"\\xee\\xa1\\xaa\"\t// U+e86a\n#define ICON_MS_CADENCE \"\\xef\\x92\\xb4\"\t// U+f4b4\n#define ICON_MS_CAKE \"\\xee\\x9f\\xa9\"\t// U+e7e9\n#define ICON_MS_CAKE_ADD \"\\xef\\xa1\\x9b\"\t// U+f85b\n#define ICON_MS_CALCULATE \"\\xee\\xa9\\x9f\"\t// U+ea5f\n#define ICON_MS_CALENDAR_ADD_ON \"\\xee\\xbe\\x85\"\t// U+ef85\n#define ICON_MS_CALENDAR_APPS_SCRIPT \"\\xef\\x82\\xbb\"\t// U+f0bb\n#define ICON_MS_CALENDAR_CLOCK \"\\xef\\x95\\x80\"\t// U+f540\n#define ICON_MS_CALENDAR_MONTH \"\\xee\\xaf\\x8c\"\t// U+ebcc\n#define ICON_MS_CALENDAR_TODAY \"\\xee\\xa4\\xb5\"\t// U+e935\n#define ICON_MS_CALENDAR_VIEW_DAY \"\\xee\\xa4\\xb6\"\t// U+e936\n#define ICON_MS_CALENDAR_VIEW_MONTH \"\\xee\\xbf\\xa7\"\t// U+efe7\n#define ICON_MS_CALENDAR_VIEW_WEEK \"\\xee\\xbf\\xa8\"\t// U+efe8\n#define ICON_MS_CALL \"\\xef\\x83\\x94\"\t// U+f0d4\n#define ICON_MS_CALL_END \"\\xef\\x82\\xbc\"\t// U+f0bc\n#define ICON_MS_CALL_END_ALT \"\\xef\\x82\\xbc\"\t// U+f0bc\n#define ICON_MS_CALL_LOG \"\\xee\\x82\\x8e\"\t// U+e08e\n#define ICON_MS_CALL_MADE \"\\xee\\x82\\xb2\"\t// U+e0b2\n#define ICON_MS_CALL_MERGE \"\\xee\\x82\\xb3\"\t// U+e0b3\n#define ICON_MS_CALL_MISSED \"\\xee\\x82\\xb4\"\t// U+e0b4\n#define ICON_MS_CALL_MISSED_OUTGOING \"\\xee\\x83\\xa4\"\t// U+e0e4\n#define ICON_MS_CALL_QUALITY \"\\xef\\x99\\x92\"\t// U+f652\n#define ICON_MS_CALL_RECEIVED \"\\xee\\x82\\xb5\"\t// U+e0b5\n#define ICON_MS_CALL_SPLIT \"\\xee\\x82\\xb6\"\t// U+e0b6\n#define ICON_MS_CALL_TO_ACTION \"\\xee\\x81\\xac\"\t// U+e06c\n#define ICON_MS_CAMERA \"\\xee\\x8e\\xaf\"\t// U+e3af\n#define ICON_MS_CAMERA_ALT \"\\xee\\x90\\x92\"\t// U+e412\n#define ICON_MS_CAMERA_ENHANCE \"\\xee\\xa3\\xbc\"\t// U+e8fc\n#define ICON_MS_CAMERA_FRONT \"\\xee\\x8e\\xb1\"\t// U+e3b1\n#define ICON_MS_CAMERA_INDOOR \"\\xee\\xbf\\xa9\"\t// U+efe9\n#define ICON_MS_CAMERA_OUTDOOR \"\\xee\\xbf\\xaa\"\t// U+efea\n#define ICON_MS_CAMERA_REAR \"\\xee\\x8e\\xb2\"\t// U+e3b2\n#define ICON_MS_CAMERA_ROLL \"\\xee\\x8e\\xb3\"\t// U+e3b3\n#define ICON_MS_CAMERA_VIDEO \"\\xef\\x9e\\xa6\"\t// U+f7a6\n#define ICON_MS_CAMERASWITCH \"\\xee\\xbf\\xab\"\t// U+efeb\n#define ICON_MS_CAMPAIGN \"\\xee\\xbd\\x89\"\t// U+ef49\n#define ICON_MS_CAMPING \"\\xef\\xa2\\xa2\"\t// U+f8a2\n#define ICON_MS_CANCEL \"\\xee\\xa2\\x88\"\t// U+e888\n#define ICON_MS_CANCEL_PRESENTATION \"\\xee\\x83\\xa9\"\t// U+e0e9\n#define ICON_MS_CANCEL_SCHEDULE_SEND \"\\xee\\xa8\\xb9\"\t// U+ea39\n#define ICON_MS_CANDLE \"\\xef\\x96\\x88\"\t// U+f588\n#define ICON_MS_CANDLESTICK_CHART \"\\xee\\xab\\x94\"\t// U+ead4\n#define ICON_MS_CAPTIVE_PORTAL \"\\xef\\x9c\\xa8\"\t// U+f728\n#define ICON_MS_CAPTURE \"\\xef\\x9c\\xa7\"\t// U+f727\n#define ICON_MS_CAR_CRASH \"\\xee\\xaf\\xb2\"\t// U+ebf2\n#define ICON_MS_CAR_RENTAL \"\\xee\\xa9\\x95\"\t// U+ea55\n#define ICON_MS_CAR_REPAIR \"\\xee\\xa9\\x96\"\t// U+ea56\n#define ICON_MS_CAR_TAG \"\\xef\\x93\\xa3\"\t// U+f4e3\n#define ICON_MS_CARD_GIFTCARD \"\\xee\\xa3\\xb6\"\t// U+e8f6\n#define ICON_MS_CARD_MEMBERSHIP \"\\xee\\xa3\\xb7\"\t// U+e8f7\n#define ICON_MS_CARD_TRAVEL \"\\xee\\xa3\\xb8\"\t// U+e8f8\n#define ICON_MS_CARDIO_LOAD \"\\xef\\x92\\xb9\"\t// U+f4b9\n#define ICON_MS_CARDIOLOGY \"\\xee\\x82\\x9c\"\t// U+e09c\n#define ICON_MS_CARDS \"\\xee\\xa6\\x91\"\t// U+e991\n#define ICON_MS_CARPENTER \"\\xef\\x87\\xb8\"\t// U+f1f8\n#define ICON_MS_CARRY_ON_BAG \"\\xee\\xac\\x88\"\t// U+eb08\n#define ICON_MS_CARRY_ON_BAG_CHECKED \"\\xee\\xac\\x8b\"\t// U+eb0b\n#define ICON_MS_CARRY_ON_BAG_INACTIVE \"\\xee\\xac\\x8a\"\t// U+eb0a\n#define ICON_MS_CARRY_ON_BAG_QUESTION \"\\xee\\xac\\x89\"\t// U+eb09\n#define ICON_MS_CASES \"\\xee\\xa6\\x92\"\t// U+e992\n#define ICON_MS_CASINO \"\\xee\\xad\\x80\"\t// U+eb40\n#define ICON_MS_CAST \"\\xee\\x8c\\x87\"\t// U+e307\n#define ICON_MS_CAST_CONNECTED \"\\xee\\x8c\\x88\"\t// U+e308\n#define ICON_MS_CAST_FOR_EDUCATION \"\\xee\\xbf\\xac\"\t// U+efec\n#define ICON_MS_CAST_PAUSE \"\\xef\\x97\\xb0\"\t// U+f5f0\n#define ICON_MS_CAST_WARNING \"\\xef\\x97\\xaf\"\t// U+f5ef\n#define ICON_MS_CASTLE \"\\xee\\xaa\\xb1\"\t// U+eab1\n#define ICON_MS_CATEGORY \"\\xee\\x95\\xb4\"\t// U+e574\n#define ICON_MS_CELEBRATION \"\\xee\\xa9\\xa5\"\t// U+ea65\n#define ICON_MS_CELL_MERGE \"\\xef\\xa0\\xae\"\t// U+f82e\n#define ICON_MS_CELL_TOWER \"\\xee\\xae\\xba\"\t// U+ebba\n#define ICON_MS_CELL_WIFI \"\\xee\\x83\\xac\"\t// U+e0ec\n#define ICON_MS_CENTER_FOCUS_STRONG \"\\xee\\x8e\\xb4\"\t// U+e3b4\n#define ICON_MS_CENTER_FOCUS_WEAK \"\\xee\\x8e\\xb5\"\t// U+e3b5\n#define ICON_MS_CHAIR \"\\xee\\xbf\\xad\"\t// U+efed\n#define ICON_MS_CHAIR_ALT \"\\xee\\xbf\\xae\"\t// U+efee\n#define ICON_MS_CHALET \"\\xee\\x96\\x85\"\t// U+e585\n#define ICON_MS_CHANGE_CIRCLE \"\\xee\\x8b\\xa7\"\t// U+e2e7\n#define ICON_MS_CHANGE_HISTORY \"\\xee\\xa1\\xab\"\t// U+e86b\n#define ICON_MS_CHARGER \"\\xee\\x8a\\xae\"\t// U+e2ae\n#define ICON_MS_CHARGING_STATION \"\\xef\\x86\\x9d\"\t// U+f19d\n#define ICON_MS_CHART_DATA \"\\xee\\x91\\xb3\"\t// U+e473\n#define ICON_MS_CHAT \"\\xee\\x83\\x89\"\t// U+e0c9\n#define ICON_MS_CHAT_ADD_ON \"\\xef\\x83\\xb3\"\t// U+f0f3\n#define ICON_MS_CHAT_APPS_SCRIPT \"\\xef\\x82\\xbd\"\t// U+f0bd\n#define ICON_MS_CHAT_BUBBLE \"\\xee\\x83\\x8b\"\t// U+e0cb\n#define ICON_MS_CHAT_BUBBLE_OUTLINE \"\\xee\\x83\\x8b\"\t// U+e0cb\n#define ICON_MS_CHAT_ERROR \"\\xef\\x9e\\xac\"\t// U+f7ac\n#define ICON_MS_CHAT_INFO \"\\xef\\x94\\xab\"\t// U+f52b\n#define ICON_MS_CHAT_PASTE_GO \"\\xef\\x9a\\xbd\"\t// U+f6bd\n#define ICON_MS_CHECK \"\\xee\\x97\\x8a\"\t// U+e5ca\n#define ICON_MS_CHECK_BOX \"\\xee\\xa0\\xb4\"\t// U+e834\n#define ICON_MS_CHECK_BOX_OUTLINE_BLANK \"\\xee\\xa0\\xb5\"\t// U+e835\n#define ICON_MS_CHECK_CIRCLE \"\\xef\\x82\\xbe\"\t// U+f0be\n#define ICON_MS_CHECK_CIRCLE_FILLED \"\\xef\\x82\\xbe\"\t// U+f0be\n#define ICON_MS_CHECK_CIRCLE_OUTLINE \"\\xef\\x82\\xbe\"\t// U+f0be\n#define ICON_MS_CHECK_IN_OUT \"\\xef\\x9b\\xb6\"\t// U+f6f6\n#define ICON_MS_CHECK_INDETERMINATE_SMALL \"\\xef\\xa2\\x8a\"\t// U+f88a\n#define ICON_MS_CHECK_SMALL \"\\xef\\xa2\\x8b\"\t// U+f88b\n#define ICON_MS_CHECKBOOK \"\\xee\\x9c\\x8d\"\t// U+e70d\n#define ICON_MS_CHECKED_BAG \"\\xee\\xac\\x8c\"\t// U+eb0c\n#define ICON_MS_CHECKED_BAG_QUESTION \"\\xee\\xac\\x8d\"\t// U+eb0d\n#define ICON_MS_CHECKLIST \"\\xee\\x9a\\xb1\"\t// U+e6b1\n#define ICON_MS_CHECKLIST_RTL \"\\xee\\x9a\\xb3\"\t// U+e6b3\n#define ICON_MS_CHECKROOM \"\\xef\\x86\\x9e\"\t// U+f19e\n#define ICON_MS_CHEER \"\\xef\\x9a\\xa8\"\t// U+f6a8\n#define ICON_MS_CHESS \"\\xef\\x97\\xa7\"\t// U+f5e7\n#define ICON_MS_CHEVRON_LEFT \"\\xee\\x97\\x8b\"\t// U+e5cb\n#define ICON_MS_CHEVRON_RIGHT \"\\xee\\x97\\x8c\"\t// U+e5cc\n#define ICON_MS_CHILD_CARE \"\\xee\\xad\\x81\"\t// U+eb41\n#define ICON_MS_CHILD_FRIENDLY \"\\xee\\xad\\x82\"\t// U+eb42\n#define ICON_MS_CHIP_EXTRACTION \"\\xef\\xa0\\xa1\"\t// U+f821\n#define ICON_MS_CHIPS \"\\xee\\xa6\\x93\"\t// U+e993\n#define ICON_MS_CHROME_READER_MODE \"\\xee\\xa1\\xad\"\t// U+e86d\n#define ICON_MS_CHROMECAST_2 \"\\xef\\x85\\xbb\"\t// U+f17b\n#define ICON_MS_CHROMECAST_DEVICE \"\\xee\\xa0\\xbc\"\t// U+e83c\n#define ICON_MS_CHRONIC \"\\xee\\xae\\xb2\"\t// U+ebb2\n#define ICON_MS_CHURCH \"\\xee\\xaa\\xae\"\t// U+eaae\n#define ICON_MS_CINEMATIC_BLUR \"\\xef\\xa1\\x93\"\t// U+f853\n#define ICON_MS_CIRCLE \"\\xee\\xbd\\x8a\"\t// U+ef4a\n#define ICON_MS_CIRCLE_NOTIFICATIONS \"\\xee\\xa6\\x94\"\t// U+e994\n#define ICON_MS_CIRCLES \"\\xee\\x9f\\xaa\"\t// U+e7ea\n#define ICON_MS_CIRCLES_EXT \"\\xee\\x9f\\xac\"\t// U+e7ec\n#define ICON_MS_CLARIFY \"\\xef\\x82\\xbf\"\t// U+f0bf\n#define ICON_MS_CLASS \"\\xee\\xa1\\xae\"\t// U+e86e\n#define ICON_MS_CLEAN_HANDS \"\\xef\\x88\\x9f\"\t// U+f21f\n#define ICON_MS_CLEANING \"\\xee\\xa6\\x95\"\t// U+e995\n#define ICON_MS_CLEANING_BUCKET \"\\xef\\xa2\\xb4\"\t// U+f8b4\n#define ICON_MS_CLEANING_SERVICES \"\\xef\\x83\\xbf\"\t// U+f0ff\n#define ICON_MS_CLEAR \"\\xee\\x97\\x8d\"\t// U+e5cd\n#define ICON_MS_CLEAR_ALL \"\\xee\\x82\\xb8\"\t// U+e0b8\n#define ICON_MS_CLEAR_DAY \"\\xef\\x85\\x97\"\t// U+f157\n#define ICON_MS_CLEAR_NIGHT \"\\xef\\x85\\x99\"\t// U+f159\n#define ICON_MS_CLIMATE_MINI_SPLIT \"\\xef\\xa2\\xb5\"\t// U+f8b5\n#define ICON_MS_CLINICAL_NOTES \"\\xee\\x82\\x9e\"\t// U+e09e\n#define ICON_MS_CLOCK_LOADER_10 \"\\xef\\x9c\\xa6\"\t// U+f726\n#define ICON_MS_CLOCK_LOADER_20 \"\\xef\\x9c\\xa5\"\t// U+f725\n#define ICON_MS_CLOCK_LOADER_40 \"\\xef\\x9c\\xa4\"\t// U+f724\n#define ICON_MS_CLOCK_LOADER_60 \"\\xef\\x9c\\xa3\"\t// U+f723\n#define ICON_MS_CLOCK_LOADER_80 \"\\xef\\x9c\\xa2\"\t// U+f722\n#define ICON_MS_CLOCK_LOADER_90 \"\\xef\\x9c\\xa1\"\t// U+f721\n#define ICON_MS_CLOSE \"\\xee\\x97\\x8d\"\t// U+e5cd\n#define ICON_MS_CLOSE_FULLSCREEN \"\\xef\\x87\\x8f\"\t// U+f1cf\n#define ICON_MS_CLOSE_SMALL \"\\xef\\x94\\x88\"\t// U+f508\n#define ICON_MS_CLOSED_CAPTION \"\\xee\\xa6\\x96\"\t// U+e996\n#define ICON_MS_CLOSED_CAPTION_ADD \"\\xef\\x92\\xae\"\t// U+f4ae\n#define ICON_MS_CLOSED_CAPTION_DISABLED \"\\xef\\x87\\x9c\"\t// U+f1dc\n#define ICON_MS_CLOSED_CAPTION_OFF \"\\xee\\xa6\\x96\"\t// U+e996\n#define ICON_MS_CLOUD \"\\xef\\x85\\x9c\"\t// U+f15c\n#define ICON_MS_CLOUD_CIRCLE \"\\xee\\x8a\\xbe\"\t// U+e2be\n#define ICON_MS_CLOUD_DONE \"\\xee\\x8a\\xbf\"\t// U+e2bf\n#define ICON_MS_CLOUD_DOWNLOAD \"\\xee\\x8b\\x80\"\t// U+e2c0\n#define ICON_MS_CLOUD_OFF \"\\xee\\x8b\\x81\"\t// U+e2c1\n#define ICON_MS_CLOUD_QUEUE \"\\xef\\x85\\x9c\"\t// U+f15c\n#define ICON_MS_CLOUD_SYNC \"\\xee\\xad\\x9a\"\t// U+eb5a\n#define ICON_MS_CLOUD_UPLOAD \"\\xee\\x8b\\x83\"\t// U+e2c3\n#define ICON_MS_CLOUDY \"\\xef\\x85\\x9c\"\t// U+f15c\n#define ICON_MS_CLOUDY_FILLED \"\\xef\\x85\\x9c\"\t// U+f15c\n#define ICON_MS_CLOUDY_SNOWING \"\\xee\\xa0\\x90\"\t// U+e810\n#define ICON_MS_CO2 \"\\xee\\x9e\\xb0\"\t// U+e7b0\n#define ICON_MS_CO_PRESENT \"\\xee\\xab\\xb0\"\t// U+eaf0\n#define ICON_MS_CODE \"\\xee\\xa1\\xaf\"\t// U+e86f\n#define ICON_MS_CODE_BLOCKS \"\\xef\\xa1\\x8d\"\t// U+f84d\n#define ICON_MS_CODE_OFF \"\\xee\\x93\\xb3\"\t// U+e4f3\n#define ICON_MS_COFFEE \"\\xee\\xbf\\xaf\"\t// U+efef\n#define ICON_MS_COFFEE_MAKER \"\\xee\\xbf\\xb0\"\t// U+eff0\n#define ICON_MS_COGNITION \"\\xee\\x82\\x9f\"\t// U+e09f\n#define ICON_MS_COLLAPSE_ALL \"\\xee\\xa5\\x84\"\t// U+e944\n#define ICON_MS_COLLAPSE_CONTENT \"\\xef\\x94\\x87\"\t// U+f507\n#define ICON_MS_COLLECTIONS \"\\xee\\x8f\\x93\"\t// U+e3d3\n#define ICON_MS_COLLECTIONS_BOOKMARK \"\\xee\\x90\\xb1\"\t// U+e431\n#define ICON_MS_COLOR_LENS \"\\xee\\x90\\x8a\"\t// U+e40a\n#define ICON_MS_COLORIZE \"\\xee\\x8e\\xb8\"\t// U+e3b8\n#define ICON_MS_COLORS \"\\xee\\xa6\\x97\"\t// U+e997\n#define ICON_MS_COMEDY_MASK \"\\xef\\x93\\x96\"\t// U+f4d6\n#define ICON_MS_COMIC_BUBBLE \"\\xef\\x97\\x9d\"\t// U+f5dd\n#define ICON_MS_COMMENT \"\\xee\\x89\\x8c\"\t// U+e24c\n#define ICON_MS_COMMENT_BANK \"\\xee\\xa9\\x8e\"\t// U+ea4e\n#define ICON_MS_COMMENTS_DISABLED \"\\xee\\x9e\\xa2\"\t// U+e7a2\n#define ICON_MS_COMMIT \"\\xee\\xab\\xb5\"\t// U+eaf5\n#define ICON_MS_COMMUNICATION \"\\xee\\x89\\xbc\"\t// U+e27c\n#define ICON_MS_COMMUNITIES \"\\xee\\xac\\x96\"\t// U+eb16\n#define ICON_MS_COMMUNITIES_FILLED \"\\xee\\xac\\x96\"\t// U+eb16\n#define ICON_MS_COMMUTE \"\\xee\\xa5\\x80\"\t// U+e940\n#define ICON_MS_COMPARE \"\\xee\\x8e\\xb9\"\t// U+e3b9\n#define ICON_MS_COMPARE_ARROWS \"\\xee\\xa4\\x95\"\t// U+e915\n#define ICON_MS_COMPASS_CALIBRATION \"\\xee\\x95\\xbc\"\t// U+e57c\n#define ICON_MS_COMPONENT_EXCHANGE \"\\xef\\x87\\xa7\"\t// U+f1e7\n#define ICON_MS_COMPOST \"\\xee\\x9d\\xa1\"\t// U+e761\n#define ICON_MS_COMPRESS \"\\xee\\xa5\\x8d\"\t// U+e94d\n#define ICON_MS_COMPUTER \"\\xee\\x8c\\x9e\"\t// U+e31e\n#define ICON_MS_CONCIERGE \"\\xef\\x95\\xa1\"\t// U+f561\n#define ICON_MS_CONDITIONS \"\\xee\\x82\\xa0\"\t// U+e0a0\n#define ICON_MS_CONFIRMATION_NUMBER \"\\xee\\x98\\xb8\"\t// U+e638\n#define ICON_MS_CONGENITAL \"\\xee\\x82\\xa1\"\t// U+e0a1\n#define ICON_MS_CONNECT_WITHOUT_CONTACT \"\\xef\\x88\\xa3\"\t// U+f223\n#define ICON_MS_CONNECTED_TV \"\\xee\\xa6\\x98\"\t// U+e998\n#define ICON_MS_CONNECTING_AIRPORTS \"\\xee\\x9f\\x89\"\t// U+e7c9\n#define ICON_MS_CONSTRUCTION \"\\xee\\xa8\\xbc\"\t// U+ea3c\n#define ICON_MS_CONTACT_EMERGENCY \"\\xef\\xa3\\x91\"\t// U+f8d1\n#define ICON_MS_CONTACT_MAIL \"\\xee\\x83\\x90\"\t// U+e0d0\n#define ICON_MS_CONTACT_PAGE \"\\xef\\x88\\xae\"\t// U+f22e\n#define ICON_MS_CONTACT_PHONE \"\\xef\\x83\\x80\"\t// U+f0c0\n#define ICON_MS_CONTACT_PHONE_FILLED \"\\xef\\x83\\x80\"\t// U+f0c0\n#define ICON_MS_CONTACT_SUPPORT \"\\xee\\xa5\\x8c\"\t// U+e94c\n#define ICON_MS_CONTACTLESS \"\\xee\\xa9\\xb1\"\t// U+ea71\n#define ICON_MS_CONTACTLESS_OFF \"\\xef\\xa1\\x98\"\t// U+f858\n#define ICON_MS_CONTACTS \"\\xee\\x82\\xba\"\t// U+e0ba\n#define ICON_MS_CONTACTS_PRODUCT \"\\xee\\xa6\\x99\"\t// U+e999\n#define ICON_MS_CONTENT_COPY \"\\xee\\x85\\x8d\"\t// U+e14d\n#define ICON_MS_CONTENT_CUT \"\\xee\\x85\\x8e\"\t// U+e14e\n#define ICON_MS_CONTENT_PASTE \"\\xee\\x85\\x8f\"\t// U+e14f\n#define ICON_MS_CONTENT_PASTE_GO \"\\xee\\xaa\\x8e\"\t// U+ea8e\n#define ICON_MS_CONTENT_PASTE_OFF \"\\xee\\x93\\xb8\"\t// U+e4f8\n#define ICON_MS_CONTENT_PASTE_SEARCH \"\\xee\\xaa\\x9b\"\t// U+ea9b\n#define ICON_MS_CONTRACT \"\\xef\\x96\\xa0\"\t// U+f5a0\n#define ICON_MS_CONTRACT_DELETE \"\\xef\\x96\\xa2\"\t// U+f5a2\n#define ICON_MS_CONTRACT_EDIT \"\\xef\\x96\\xa1\"\t// U+f5a1\n#define ICON_MS_CONTRAST \"\\xee\\xac\\xb7\"\t// U+eb37\n#define ICON_MS_CONTRAST_CIRCLE \"\\xef\\x92\\x9f\"\t// U+f49f\n#define ICON_MS_CONTRAST_RTL_OFF \"\\xee\\xb1\\xb2\"\t// U+ec72\n#define ICON_MS_CONTRAST_SQUARE \"\\xef\\x92\\xa0\"\t// U+f4a0\n#define ICON_MS_CONTROL_CAMERA \"\\xee\\x81\\xb4\"\t// U+e074\n#define ICON_MS_CONTROL_POINT \"\\xee\\x8e\\xba\"\t// U+e3ba\n#define ICON_MS_CONTROL_POINT_DUPLICATE \"\\xee\\x8e\\xbb\"\t// U+e3bb\n#define ICON_MS_CONTROLLER_GEN \"\\xee\\xa0\\xbd\"\t// U+e83d\n#define ICON_MS_CONVERSION_PATH \"\\xef\\x83\\x81\"\t// U+f0c1\n#define ICON_MS_CONVERSION_PATH_OFF \"\\xef\\x9e\\xb4\"\t// U+f7b4\n#define ICON_MS_CONVEYOR_BELT \"\\xef\\xa1\\xa7\"\t// U+f867\n#define ICON_MS_COOKIE \"\\xee\\xaa\\xac\"\t// U+eaac\n#define ICON_MS_COOKIE_OFF \"\\xef\\x9e\\x9a\"\t// U+f79a\n#define ICON_MS_COOKING \"\\xee\\x8a\\xb6\"\t// U+e2b6\n#define ICON_MS_COOL_TO_DRY \"\\xee\\x89\\xb6\"\t// U+e276\n#define ICON_MS_COPY_ALL \"\\xee\\x8b\\xac\"\t// U+e2ec\n#define ICON_MS_COPYRIGHT \"\\xee\\xa4\\x8c\"\t// U+e90c\n#define ICON_MS_CORONAVIRUS \"\\xef\\x88\\xa1\"\t// U+f221\n#define ICON_MS_CORPORATE_FARE \"\\xef\\x87\\x90\"\t// U+f1d0\n#define ICON_MS_COTTAGE \"\\xee\\x96\\x87\"\t// U+e587\n#define ICON_MS_COUNTER_0 \"\\xef\\x9e\\x85\"\t// U+f785\n#define ICON_MS_COUNTER_1 \"\\xef\\x9e\\x84\"\t// U+f784\n#define ICON_MS_COUNTER_2 \"\\xef\\x9e\\x83\"\t// U+f783\n#define ICON_MS_COUNTER_3 \"\\xef\\x9e\\x82\"\t// U+f782\n#define ICON_MS_COUNTER_4 \"\\xef\\x9e\\x81\"\t// U+f781\n#define ICON_MS_COUNTER_5 \"\\xef\\x9e\\x80\"\t// U+f780\n#define ICON_MS_COUNTER_6 \"\\xef\\x9d\\xbf\"\t// U+f77f\n#define ICON_MS_COUNTER_7 \"\\xef\\x9d\\xbe\"\t// U+f77e\n#define ICON_MS_COUNTER_8 \"\\xef\\x9d\\xbd\"\t// U+f77d\n#define ICON_MS_COUNTER_9 \"\\xef\\x9d\\xbc\"\t// U+f77c\n#define ICON_MS_COUNTERTOPS \"\\xef\\x87\\xb7\"\t// U+f1f7\n#define ICON_MS_CREATE \"\\xef\\x82\\x97\"\t// U+f097\n#define ICON_MS_CREATE_NEW_FOLDER \"\\xee\\x8b\\x8c\"\t// U+e2cc\n#define ICON_MS_CREDIT_CARD \"\\xee\\xa2\\xa1\"\t// U+e8a1\n#define ICON_MS_CREDIT_CARD_GEAR \"\\xef\\x94\\xad\"\t// U+f52d\n#define ICON_MS_CREDIT_CARD_HEART \"\\xef\\x94\\xac\"\t// U+f52c\n#define ICON_MS_CREDIT_CARD_OFF \"\\xee\\x93\\xb4\"\t// U+e4f4\n#define ICON_MS_CREDIT_SCORE \"\\xee\\xbf\\xb1\"\t// U+eff1\n#define ICON_MS_CRIB \"\\xee\\x96\\x88\"\t// U+e588\n#define ICON_MS_CRISIS_ALERT \"\\xee\\xaf\\xa9\"\t// U+ebe9\n#define ICON_MS_CROP \"\\xee\\x8e\\xbe\"\t// U+e3be\n#define ICON_MS_CROP_16_9 \"\\xee\\x8e\\xbc\"\t// U+e3bc\n#define ICON_MS_CROP_3_2 \"\\xee\\x8e\\xbd\"\t// U+e3bd\n#define ICON_MS_CROP_5_4 \"\\xee\\x8e\\xbf\"\t// U+e3bf\n#define ICON_MS_CROP_7_5 \"\\xee\\x8f\\x80\"\t// U+e3c0\n#define ICON_MS_CROP_9_16 \"\\xef\\x95\\x89\"\t// U+f549\n#define ICON_MS_CROP_DIN \"\\xee\\x8f\\x86\"\t// U+e3c6\n#define ICON_MS_CROP_FREE \"\\xee\\x8f\\x82\"\t// U+e3c2\n#define ICON_MS_CROP_LANDSCAPE \"\\xee\\x8f\\x83\"\t// U+e3c3\n#define ICON_MS_CROP_ORIGINAL \"\\xee\\x8f\\xb4\"\t// U+e3f4\n#define ICON_MS_CROP_PORTRAIT \"\\xee\\x8f\\x85\"\t// U+e3c5\n#define ICON_MS_CROP_ROTATE \"\\xee\\x90\\xb7\"\t// U+e437\n#define ICON_MS_CROP_SQUARE \"\\xee\\x8f\\x86\"\t// U+e3c6\n#define ICON_MS_CROSSWORD \"\\xef\\x97\\xa5\"\t// U+f5e5\n#define ICON_MS_CROWDSOURCE \"\\xee\\xac\\x98\"\t// U+eb18\n#define ICON_MS_CRUELTY_FREE \"\\xee\\x9e\\x99\"\t// U+e799\n#define ICON_MS_CSS \"\\xee\\xae\\x93\"\t// U+eb93\n#define ICON_MS_CSV \"\\xee\\x9b\\x8f\"\t// U+e6cf\n#define ICON_MS_CURRENCY_BITCOIN \"\\xee\\xaf\\x85\"\t// U+ebc5\n#define ICON_MS_CURRENCY_EXCHANGE \"\\xee\\xad\\xb0\"\t// U+eb70\n#define ICON_MS_CURRENCY_FRANC \"\\xee\\xab\\xba\"\t// U+eafa\n#define ICON_MS_CURRENCY_LIRA \"\\xee\\xab\\xaf\"\t// U+eaef\n#define ICON_MS_CURRENCY_POUND \"\\xee\\xab\\xb1\"\t// U+eaf1\n#define ICON_MS_CURRENCY_RUBLE \"\\xee\\xab\\xac\"\t// U+eaec\n#define ICON_MS_CURRENCY_RUPEE \"\\xee\\xab\\xb7\"\t// U+eaf7\n#define ICON_MS_CURRENCY_YEN \"\\xee\\xab\\xbb\"\t// U+eafb\n#define ICON_MS_CURRENCY_YUAN \"\\xee\\xab\\xb9\"\t// U+eaf9\n#define ICON_MS_CURTAINS \"\\xee\\xb0\\x9e\"\t// U+ec1e\n#define ICON_MS_CURTAINS_CLOSED \"\\xee\\xb0\\x9d\"\t// U+ec1d\n#define ICON_MS_CUSTOM_TYPOGRAPHY \"\\xee\\x9c\\xb2\"\t// U+e732\n#define ICON_MS_CUT \"\\xef\\x82\\x8b\"\t// U+f08b\n#define ICON_MS_CYCLE \"\\xef\\xa1\\x94\"\t// U+f854\n#define ICON_MS_CYCLONE \"\\xee\\xaf\\x95\"\t// U+ebd5\n#define ICON_MS_DANGEROUS \"\\xee\\xa6\\x9a\"\t// U+e99a\n#define ICON_MS_DARK_MODE \"\\xee\\x94\\x9c\"\t// U+e51c\n#define ICON_MS_DASHBOARD \"\\xee\\xa1\\xb1\"\t// U+e871\n#define ICON_MS_DASHBOARD_CUSTOMIZE \"\\xee\\xa6\\x9b\"\t// U+e99b\n#define ICON_MS_DATA_ALERT \"\\xef\\x9f\\xb6\"\t// U+f7f6\n#define ICON_MS_DATA_ARRAY \"\\xee\\xab\\x91\"\t// U+ead1\n#define ICON_MS_DATA_CHECK \"\\xef\\x9f\\xb2\"\t// U+f7f2\n#define ICON_MS_DATA_EXPLORATION \"\\xee\\x9d\\xaf\"\t// U+e76f\n#define ICON_MS_DATA_INFO_ALERT \"\\xef\\x9f\\xb5\"\t// U+f7f5\n#define ICON_MS_DATA_LOSS_PREVENTION \"\\xee\\x8b\\x9c\"\t// U+e2dc\n#define ICON_MS_DATA_OBJECT \"\\xee\\xab\\x93\"\t// U+ead3\n#define ICON_MS_DATA_SAVER_OFF \"\\xee\\xbf\\xb2\"\t// U+eff2\n#define ICON_MS_DATA_SAVER_ON \"\\xee\\xbf\\xb3\"\t// U+eff3\n#define ICON_MS_DATA_TABLE \"\\xee\\xa6\\x9c\"\t// U+e99c\n#define ICON_MS_DATA_THRESHOLDING \"\\xee\\xae\\x9f\"\t// U+eb9f\n#define ICON_MS_DATA_USAGE \"\\xee\\xbf\\xb2\"\t// U+eff2\n#define ICON_MS_DATABASE \"\\xef\\x88\\x8e\"\t// U+f20e\n#define ICON_MS_DATASET \"\\xef\\xa3\\xae\"\t// U+f8ee\n#define ICON_MS_DATASET_LINKED \"\\xef\\xa3\\xaf\"\t// U+f8ef\n#define ICON_MS_DATE_RANGE \"\\xee\\xa4\\x96\"\t// U+e916\n#define ICON_MS_DEBLUR \"\\xee\\xad\\xb7\"\t// U+eb77\n#define ICON_MS_DECEASED \"\\xee\\x82\\xa5\"\t// U+e0a5\n#define ICON_MS_DECIMAL_DECREASE \"\\xef\\xa0\\xad\"\t// U+f82d\n#define ICON_MS_DECIMAL_INCREASE \"\\xef\\xa0\\xac\"\t// U+f82c\n#define ICON_MS_DECK \"\\xee\\xa9\\x82\"\t// U+ea42\n#define ICON_MS_DEHAZE \"\\xee\\x8f\\x87\"\t// U+e3c7\n#define ICON_MS_DELETE \"\\xee\\xa4\\xae\"\t// U+e92e\n#define ICON_MS_DELETE_FOREVER \"\\xee\\xa4\\xab\"\t// U+e92b\n#define ICON_MS_DELETE_HISTORY \"\\xef\\x94\\x98\"\t// U+f518\n#define ICON_MS_DELETE_OUTLINE \"\\xee\\xa4\\xae\"\t// U+e92e\n#define ICON_MS_DELETE_SWEEP \"\\xee\\x85\\xac\"\t// U+e16c\n#define ICON_MS_DEMOGRAPHY \"\\xee\\x92\\x89\"\t// U+e489\n#define ICON_MS_DENSITY_LARGE \"\\xee\\xae\\xa9\"\t// U+eba9\n#define ICON_MS_DENSITY_MEDIUM \"\\xee\\xae\\x9e\"\t// U+eb9e\n#define ICON_MS_DENSITY_SMALL \"\\xee\\xae\\xa8\"\t// U+eba8\n#define ICON_MS_DENTISTRY \"\\xee\\x82\\xa6\"\t// U+e0a6\n#define ICON_MS_DEPARTURE_BOARD \"\\xee\\x95\\xb6\"\t// U+e576\n#define ICON_MS_DEPLOYED_CODE \"\\xef\\x9c\\xa0\"\t// U+f720\n#define ICON_MS_DEPLOYED_CODE_ACCOUNT \"\\xef\\x94\\x9b\"\t// U+f51b\n#define ICON_MS_DEPLOYED_CODE_ALERT \"\\xef\\x97\\xb2\"\t// U+f5f2\n#define ICON_MS_DEPLOYED_CODE_HISTORY \"\\xef\\x97\\xb3\"\t// U+f5f3\n#define ICON_MS_DEPLOYED_CODE_UPDATE \"\\xef\\x97\\xb4\"\t// U+f5f4\n#define ICON_MS_DERMATOLOGY \"\\xee\\x82\\xa7\"\t// U+e0a7\n#define ICON_MS_DESCRIPTION \"\\xee\\xa1\\xb3\"\t// U+e873\n#define ICON_MS_DESELECT \"\\xee\\xae\\xb6\"\t// U+ebb6\n#define ICON_MS_DESIGN_SERVICES \"\\xef\\x84\\x8a\"\t// U+f10a\n#define ICON_MS_DESK \"\\xef\\xa3\\xb4\"\t// U+f8f4\n#define ICON_MS_DESKPHONE \"\\xef\\x9f\\xba\"\t// U+f7fa\n#define ICON_MS_DESKTOP_ACCESS_DISABLED \"\\xee\\xa6\\x9d\"\t// U+e99d\n#define ICON_MS_DESKTOP_MAC \"\\xee\\x8c\\x8b\"\t// U+e30b\n#define ICON_MS_DESKTOP_WINDOWS \"\\xee\\x8c\\x8c\"\t// U+e30c\n#define ICON_MS_DESTRUCTION \"\\xef\\x96\\x85\"\t// U+f585\n#define ICON_MS_DETAILS \"\\xee\\x8f\\x88\"\t// U+e3c8\n#define ICON_MS_DETECTION_AND_ZONE \"\\xee\\x8a\\x9f\"\t// U+e29f\n#define ICON_MS_DETECTOR \"\\xee\\x8a\\x82\"\t// U+e282\n#define ICON_MS_DETECTOR_ALARM \"\\xee\\x87\\xb7\"\t// U+e1f7\n#define ICON_MS_DETECTOR_BATTERY \"\\xee\\x88\\x84\"\t// U+e204\n#define ICON_MS_DETECTOR_CO \"\\xee\\x8a\\xaf\"\t// U+e2af\n#define ICON_MS_DETECTOR_OFFLINE \"\\xee\\x88\\xa3\"\t// U+e223\n#define ICON_MS_DETECTOR_SMOKE \"\\xee\\x8a\\x85\"\t// U+e285\n#define ICON_MS_DETECTOR_STATUS \"\\xee\\x87\\xa8\"\t// U+e1e8\n#define ICON_MS_DEVELOPER_BOARD \"\\xee\\x8c\\x8d\"\t// U+e30d\n#define ICON_MS_DEVELOPER_BOARD_OFF \"\\xee\\x93\\xbf\"\t// U+e4ff\n#define ICON_MS_DEVELOPER_GUIDE \"\\xee\\xa6\\x9e\"\t// U+e99e\n#define ICON_MS_DEVELOPER_MODE \"\\xee\\x86\\xb0\"\t// U+e1b0\n#define ICON_MS_DEVELOPER_MODE_TV \"\\xee\\xa1\\xb4\"\t// U+e874\n#define ICON_MS_DEVICE_HUB \"\\xee\\x8c\\xb5\"\t// U+e335\n#define ICON_MS_DEVICE_RESET \"\\xee\\xa2\\xb3\"\t// U+e8b3\n#define ICON_MS_DEVICE_THERMOSTAT \"\\xee\\x87\\xbf\"\t// U+e1ff\n#define ICON_MS_DEVICE_UNKNOWN \"\\xee\\x8c\\xb9\"\t// U+e339\n#define ICON_MS_DEVICES \"\\xee\\x8c\\xa6\"\t// U+e326\n#define ICON_MS_DEVICES_FOLD \"\\xee\\xaf\\x9e\"\t// U+ebde\n#define ICON_MS_DEVICES_OFF \"\\xef\\x9e\\xa5\"\t// U+f7a5\n#define ICON_MS_DEVICES_OTHER \"\\xee\\x8c\\xb7\"\t// U+e337\n#define ICON_MS_DEVICES_WEARABLES \"\\xef\\x9a\\xab\"\t// U+f6ab\n#define ICON_MS_DEW_POINT \"\\xef\\xa1\\xb9\"\t// U+f879\n#define ICON_MS_DIAGNOSIS \"\\xee\\x82\\xa8\"\t// U+e0a8\n#define ICON_MS_DIALER_SIP \"\\xee\\x82\\xbb\"\t// U+e0bb\n#define ICON_MS_DIALOGS \"\\xee\\xa6\\x9f\"\t// U+e99f\n#define ICON_MS_DIALPAD \"\\xee\\x82\\xbc\"\t// U+e0bc\n#define ICON_MS_DIAMOND \"\\xee\\xab\\x95\"\t// U+ead5\n#define ICON_MS_DICTIONARY \"\\xef\\x94\\xb9\"\t// U+f539\n#define ICON_MS_DIFFERENCE \"\\xee\\xad\\xbd\"\t// U+eb7d\n#define ICON_MS_DIGITAL_OUT_OF_HOME \"\\xef\\x87\\x9e\"\t// U+f1de\n#define ICON_MS_DIGITAL_WELLBEING \"\\xee\\xbe\\x86\"\t// U+ef86\n#define ICON_MS_DINING \"\\xee\\xbf\\xb4\"\t// U+eff4\n#define ICON_MS_DINNER_DINING \"\\xee\\xa9\\x97\"\t// U+ea57\n#define ICON_MS_DIRECTIONS \"\\xee\\x94\\xae\"\t// U+e52e\n#define ICON_MS_DIRECTIONS_ALT \"\\xef\\xa2\\x80\"\t// U+f880\n#define ICON_MS_DIRECTIONS_ALT_OFF \"\\xef\\xa2\\x81\"\t// U+f881\n#define ICON_MS_DIRECTIONS_BIKE \"\\xee\\x94\\xaf\"\t// U+e52f\n#define ICON_MS_DIRECTIONS_BOAT \"\\xee\\xbf\\xb5\"\t// U+eff5\n#define ICON_MS_DIRECTIONS_BOAT_FILLED \"\\xee\\xbf\\xb5\"\t// U+eff5\n#define ICON_MS_DIRECTIONS_BUS \"\\xee\\xbf\\xb6\"\t// U+eff6\n#define ICON_MS_DIRECTIONS_BUS_FILLED \"\\xee\\xbf\\xb6\"\t// U+eff6\n#define ICON_MS_DIRECTIONS_CAR \"\\xee\\xbf\\xb7\"\t// U+eff7\n#define ICON_MS_DIRECTIONS_CAR_FILLED \"\\xee\\xbf\\xb7\"\t// U+eff7\n#define ICON_MS_DIRECTIONS_OFF \"\\xef\\x84\\x8f\"\t// U+f10f\n#define ICON_MS_DIRECTIONS_RAILWAY \"\\xee\\xbf\\xb8\"\t// U+eff8\n#define ICON_MS_DIRECTIONS_RAILWAY_FILLED \"\\xee\\xbf\\xb8\"\t// U+eff8\n#define ICON_MS_DIRECTIONS_RUN \"\\xee\\x95\\xa6\"\t// U+e566\n#define ICON_MS_DIRECTIONS_SUBWAY \"\\xee\\xbf\\xba\"\t// U+effa\n#define ICON_MS_DIRECTIONS_SUBWAY_FILLED \"\\xee\\xbf\\xba\"\t// U+effa\n#define ICON_MS_DIRECTIONS_TRANSIT \"\\xee\\xbf\\xba\"\t// U+effa\n#define ICON_MS_DIRECTIONS_TRANSIT_FILLED \"\\xee\\xbf\\xba\"\t// U+effa\n#define ICON_MS_DIRECTIONS_WALK \"\\xee\\x94\\xb6\"\t// U+e536\n#define ICON_MS_DIRECTORY_SYNC \"\\xee\\x8e\\x94\"\t// U+e394\n#define ICON_MS_DIRTY_LENS \"\\xee\\xbd\\x8b\"\t// U+ef4b\n#define ICON_MS_DISABLED_BY_DEFAULT \"\\xef\\x88\\xb0\"\t// U+f230\n#define ICON_MS_DISABLED_VISIBLE \"\\xee\\x9d\\xae\"\t// U+e76e\n#define ICON_MS_DISC_FULL \"\\xee\\x98\\x90\"\t// U+e610\n#define ICON_MS_DISCOVER_TUNE \"\\xee\\x80\\x98\"\t// U+e018\n#define ICON_MS_DISHWASHER \"\\xee\\xa6\\xa0\"\t// U+e9a0\n#define ICON_MS_DISHWASHER_GEN \"\\xee\\xa0\\xb2\"\t// U+e832\n#define ICON_MS_DISPLAY_EXTERNAL_INPUT \"\\xef\\x9f\\xa7\"\t// U+f7e7\n#define ICON_MS_DISPLAY_SETTINGS \"\\xee\\xae\\x97\"\t// U+eb97\n#define ICON_MS_DISTANCE \"\\xef\\x9b\\xaa\"\t// U+f6ea\n#define ICON_MS_DIVERSITY_1 \"\\xef\\xa3\\x97\"\t// U+f8d7\n#define ICON_MS_DIVERSITY_2 \"\\xef\\xa3\\x98\"\t// U+f8d8\n#define ICON_MS_DIVERSITY_3 \"\\xef\\xa3\\x99\"\t// U+f8d9\n#define ICON_MS_DIVERSITY_4 \"\\xef\\xa1\\x97\"\t// U+f857\n#define ICON_MS_DNS \"\\xee\\xa1\\xb5\"\t// U+e875\n#define ICON_MS_DO_DISTURB \"\\xef\\x82\\x8c\"\t// U+f08c\n#define ICON_MS_DO_DISTURB_ALT \"\\xef\\x82\\x8d\"\t// U+f08d\n#define ICON_MS_DO_DISTURB_OFF \"\\xef\\x82\\x8e\"\t// U+f08e\n#define ICON_MS_DO_DISTURB_ON \"\\xef\\x82\\x8f\"\t// U+f08f\n#define ICON_MS_DO_NOT_DISTURB \"\\xef\\x82\\x8d\"\t// U+f08d\n#define ICON_MS_DO_NOT_DISTURB_ALT \"\\xef\\x82\\x8c\"\t// U+f08c\n#define ICON_MS_DO_NOT_DISTURB_OFF \"\\xef\\x82\\x8e\"\t// U+f08e\n#define ICON_MS_DO_NOT_DISTURB_ON \"\\xef\\x82\\x8f\"\t// U+f08f\n#define ICON_MS_DO_NOT_DISTURB_ON_TOTAL_SILENCE \"\\xee\\xbf\\xbb\"\t// U+effb\n#define ICON_MS_DO_NOT_STEP \"\\xef\\x86\\x9f\"\t// U+f19f\n#define ICON_MS_DO_NOT_TOUCH \"\\xef\\x86\\xb0\"\t// U+f1b0\n#define ICON_MS_DOCK \"\\xee\\x8c\\x8e\"\t// U+e30e\n#define ICON_MS_DOCK_TO_BOTTOM \"\\xef\\x9f\\xa6\"\t// U+f7e6\n#define ICON_MS_DOCK_TO_LEFT \"\\xef\\x9f\\xa5\"\t// U+f7e5\n#define ICON_MS_DOCK_TO_RIGHT \"\\xef\\x9f\\xa4\"\t// U+f7e4\n#define ICON_MS_DOCS_ADD_ON \"\\xef\\x83\\x82\"\t// U+f0c2\n#define ICON_MS_DOCS_APPS_SCRIPT \"\\xef\\x83\\x83\"\t// U+f0c3\n#define ICON_MS_DOCUMENT_SCANNER \"\\xee\\x97\\xba\"\t// U+e5fa\n#define ICON_MS_DOMAIN \"\\xee\\x9f\\xae\"\t// U+e7ee\n#define ICON_MS_DOMAIN_ADD \"\\xee\\xad\\xa2\"\t// U+eb62\n#define ICON_MS_DOMAIN_DISABLED \"\\xee\\x83\\xaf\"\t// U+e0ef\n#define ICON_MS_DOMAIN_VERIFICATION \"\\xee\\xbd\\x8c\"\t// U+ef4c\n#define ICON_MS_DOMAIN_VERIFICATION_OFF \"\\xef\\x9e\\xb0\"\t// U+f7b0\n#define ICON_MS_DOMINO_MASK \"\\xef\\x97\\xa4\"\t// U+f5e4\n#define ICON_MS_DONE \"\\xee\\xa1\\xb6\"\t// U+e876\n#define ICON_MS_DONE_ALL \"\\xee\\xa1\\xb7\"\t// U+e877\n#define ICON_MS_DONE_OUTLINE \"\\xee\\xa4\\xaf\"\t// U+e92f\n#define ICON_MS_DONUT_LARGE \"\\xee\\xa4\\x97\"\t// U+e917\n#define ICON_MS_DONUT_SMALL \"\\xee\\xa4\\x98\"\t// U+e918\n#define ICON_MS_DOOR_BACK \"\\xee\\xbf\\xbc\"\t// U+effc\n#define ICON_MS_DOOR_FRONT \"\\xee\\xbf\\xbd\"\t// U+effd\n#define ICON_MS_DOOR_OPEN \"\\xee\\x9d\\xbc\"\t// U+e77c\n#define ICON_MS_DOOR_SENSOR \"\\xee\\x8a\\x8a\"\t// U+e28a\n#define ICON_MS_DOOR_SLIDING \"\\xee\\xbf\\xbe\"\t// U+effe\n#define ICON_MS_DOORBELL \"\\xee\\xbf\\xbf\"\t// U+efff\n#define ICON_MS_DOORBELL_3P \"\\xee\\x87\\xa7\"\t// U+e1e7\n#define ICON_MS_DOORBELL_CHIME \"\\xee\\x87\\xb3\"\t// U+e1f3\n#define ICON_MS_DOUBLE_ARROW \"\\xee\\xa9\\x90\"\t// U+ea50\n#define ICON_MS_DOWNHILL_SKIING \"\\xee\\x94\\x89\"\t// U+e509\n#define ICON_MS_DOWNLOAD \"\\xef\\x82\\x90\"\t// U+f090\n#define ICON_MS_DOWNLOAD_2 \"\\xef\\x94\\xa3\"\t// U+f523\n#define ICON_MS_DOWNLOAD_DONE \"\\xef\\x82\\x91\"\t// U+f091\n#define ICON_MS_DOWNLOAD_FOR_OFFLINE \"\\xef\\x80\\x80\"\t// U+f000\n#define ICON_MS_DOWNLOADING \"\\xef\\x80\\x81\"\t// U+f001\n#define ICON_MS_DRAFT \"\\xee\\x99\\xad\"\t// U+e66d\n#define ICON_MS_DRAFT_ORDERS \"\\xee\\x9e\\xb3\"\t// U+e7b3\n#define ICON_MS_DRAFTS \"\\xee\\x85\\x91\"\t// U+e151\n#define ICON_MS_DRAG_CLICK \"\\xef\\x9c\\x9f\"\t// U+f71f\n#define ICON_MS_DRAG_HANDLE \"\\xee\\x89\\x9d\"\t// U+e25d\n#define ICON_MS_DRAG_INDICATOR \"\\xee\\xa5\\x85\"\t// U+e945\n#define ICON_MS_DRAG_PAN \"\\xef\\x9c\\x9e\"\t// U+f71e\n#define ICON_MS_DRAW \"\\xee\\x9d\\x86\"\t// U+e746\n#define ICON_MS_DRAW_ABSTRACT \"\\xef\\x9f\\xb8\"\t// U+f7f8\n#define ICON_MS_DRAW_COLLAGE \"\\xef\\x9f\\xb7\"\t// U+f7f7\n#define ICON_MS_DRAWING_RECOGNITION \"\\xee\\xac\\x80\"\t// U+eb00\n#define ICON_MS_DRESSER \"\\xee\\x88\\x90\"\t// U+e210\n#define ICON_MS_DRIVE_ETA \"\\xee\\xbf\\xb7\"\t// U+eff7\n#define ICON_MS_DRIVE_FILE_MOVE \"\\xee\\xa6\\xa1\"\t// U+e9a1\n#define ICON_MS_DRIVE_FILE_MOVE_OUTLINE \"\\xee\\xa6\\xa1\"\t// U+e9a1\n#define ICON_MS_DRIVE_FILE_MOVE_RTL \"\\xee\\xa6\\xa1\"\t// U+e9a1\n#define ICON_MS_DRIVE_FILE_RENAME_OUTLINE \"\\xee\\xa6\\xa2\"\t// U+e9a2\n#define ICON_MS_DRIVE_FOLDER_UPLOAD \"\\xee\\xa6\\xa3\"\t// U+e9a3\n#define ICON_MS_DRIVE_FUSIONTABLE \"\\xee\\x99\\xb8\"\t// U+e678\n#define ICON_MS_DROPDOWN \"\\xee\\xa6\\xa4\"\t// U+e9a4\n#define ICON_MS_DRY \"\\xef\\x86\\xb3\"\t// U+f1b3\n#define ICON_MS_DRY_CLEANING \"\\xee\\xa9\\x98\"\t// U+ea58\n#define ICON_MS_DUAL_SCREEN \"\\xef\\x9b\\x8f\"\t// U+f6cf\n#define ICON_MS_DUO \"\\xee\\xa6\\xa5\"\t// U+e9a5\n#define ICON_MS_DVR \"\\xee\\x86\\xb2\"\t// U+e1b2\n#define ICON_MS_DYNAMIC_FEED \"\\xee\\xa8\\x94\"\t// U+ea14\n#define ICON_MS_DYNAMIC_FORM \"\\xef\\x86\\xbf\"\t// U+f1bf\n#define ICON_MS_E911_AVATAR \"\\xef\\x84\\x9a\"\t// U+f11a\n#define ICON_MS_E911_EMERGENCY \"\\xef\\x84\\x99\"\t// U+f119\n#define ICON_MS_E_MOBILEDATA \"\\xef\\x80\\x82\"\t// U+f002\n#define ICON_MS_E_MOBILEDATA_BADGE \"\\xef\\x9f\\xa3\"\t// U+f7e3\n#define ICON_MS_EARBUDS \"\\xef\\x80\\x83\"\t// U+f003\n#define ICON_MS_EARBUDS_BATTERY \"\\xef\\x80\\x84\"\t// U+f004\n#define ICON_MS_EARLY_ON \"\\xee\\x8a\\xba\"\t// U+e2ba\n#define ICON_MS_EARTHQUAKE \"\\xef\\x99\\x8f\"\t// U+f64f\n#define ICON_MS_EAST \"\\xef\\x87\\x9f\"\t// U+f1df\n#define ICON_MS_ECG \"\\xef\\xa0\\x8f\"\t// U+f80f\n#define ICON_MS_ECG_HEART \"\\xef\\x9b\\xa9\"\t// U+f6e9\n#define ICON_MS_ECO \"\\xee\\xa8\\xb5\"\t// U+ea35\n#define ICON_MS_EDA \"\\xef\\x9b\\xa8\"\t// U+f6e8\n#define ICON_MS_EDGESENSOR_HIGH \"\\xef\\x80\\x85\"\t// U+f005\n#define ICON_MS_EDGESENSOR_LOW \"\\xef\\x80\\x86\"\t// U+f006\n#define ICON_MS_EDIT \"\\xef\\x82\\x97\"\t// U+f097\n#define ICON_MS_EDIT_ATTRIBUTES \"\\xee\\x95\\xb8\"\t// U+e578\n#define ICON_MS_EDIT_CALENDAR \"\\xee\\x9d\\x82\"\t// U+e742\n#define ICON_MS_EDIT_DOCUMENT \"\\xef\\xa2\\x8c\"\t// U+f88c\n#define ICON_MS_EDIT_LOCATION \"\\xee\\x95\\xa8\"\t// U+e568\n#define ICON_MS_EDIT_LOCATION_ALT \"\\xee\\x87\\x85\"\t// U+e1c5\n#define ICON_MS_EDIT_NOTE \"\\xee\\x9d\\x85\"\t// U+e745\n#define ICON_MS_EDIT_NOTIFICATIONS \"\\xee\\x94\\xa5\"\t// U+e525\n#define ICON_MS_EDIT_OFF \"\\xee\\xa5\\x90\"\t// U+e950\n#define ICON_MS_EDIT_ROAD \"\\xee\\xbd\\x8d\"\t// U+ef4d\n#define ICON_MS_EDIT_SQUARE \"\\xef\\xa2\\x8d\"\t// U+f88d\n#define ICON_MS_EDITOR_CHOICE \"\\xef\\x94\\xa8\"\t// U+f528\n#define ICON_MS_EGG \"\\xee\\xab\\x8c\"\t// U+eacc\n#define ICON_MS_EGG_ALT \"\\xee\\xab\\x88\"\t// U+eac8\n#define ICON_MS_EJECT \"\\xee\\xa3\\xbb\"\t// U+e8fb\n#define ICON_MS_ELDERLY \"\\xef\\x88\\x9a\"\t// U+f21a\n#define ICON_MS_ELDERLY_WOMAN \"\\xee\\xad\\xa9\"\t// U+eb69\n#define ICON_MS_ELECTRIC_BIKE \"\\xee\\xac\\x9b\"\t// U+eb1b\n#define ICON_MS_ELECTRIC_BOLT \"\\xee\\xb0\\x9c\"\t// U+ec1c\n#define ICON_MS_ELECTRIC_CAR \"\\xee\\xac\\x9c\"\t// U+eb1c\n#define ICON_MS_ELECTRIC_METER \"\\xee\\xb0\\x9b\"\t// U+ec1b\n#define ICON_MS_ELECTRIC_MOPED \"\\xee\\xac\\x9d\"\t// U+eb1d\n#define ICON_MS_ELECTRIC_RICKSHAW \"\\xee\\xac\\x9e\"\t// U+eb1e\n#define ICON_MS_ELECTRIC_SCOOTER \"\\xee\\xac\\x9f\"\t// U+eb1f\n#define ICON_MS_ELECTRICAL_SERVICES \"\\xef\\x84\\x82\"\t// U+f102\n#define ICON_MS_ELEVATION \"\\xef\\x9b\\xa7\"\t// U+f6e7\n#define ICON_MS_ELEVATOR \"\\xef\\x86\\xa0\"\t// U+f1a0\n#define ICON_MS_EMAIL \"\\xee\\x85\\x99\"\t// U+e159\n#define ICON_MS_EMERGENCY \"\\xee\\x87\\xab\"\t// U+e1eb\n#define ICON_MS_EMERGENCY_HEAT \"\\xef\\x85\\x9d\"\t// U+f15d\n#define ICON_MS_EMERGENCY_HEAT_2 \"\\xef\\x93\\xa5\"\t// U+f4e5\n#define ICON_MS_EMERGENCY_HOME \"\\xee\\xa0\\xaa\"\t// U+e82a\n#define ICON_MS_EMERGENCY_RECORDING \"\\xee\\xaf\\xb4\"\t// U+ebf4\n#define ICON_MS_EMERGENCY_SHARE \"\\xee\\xaf\\xb6\"\t// U+ebf6\n#define ICON_MS_EMERGENCY_SHARE_OFF \"\\xef\\x96\\x9e\"\t// U+f59e\n#define ICON_MS_EMOJI_EMOTIONS \"\\xee\\xa8\\xa2\"\t// U+ea22\n#define ICON_MS_EMOJI_EVENTS \"\\xee\\xa8\\xa3\"\t// U+ea23\n#define ICON_MS_EMOJI_FLAGS \"\\xef\\x83\\x86\"\t// U+f0c6\n#define ICON_MS_EMOJI_FOOD_BEVERAGE \"\\xee\\xa8\\x9b\"\t// U+ea1b\n#define ICON_MS_EMOJI_LANGUAGE \"\\xef\\x93\\x8d\"\t// U+f4cd\n#define ICON_MS_EMOJI_NATURE \"\\xee\\xa8\\x9c\"\t// U+ea1c\n#define ICON_MS_EMOJI_OBJECTS \"\\xee\\xa8\\xa4\"\t// U+ea24\n#define ICON_MS_EMOJI_PEOPLE \"\\xee\\xa8\\x9d\"\t// U+ea1d\n#define ICON_MS_EMOJI_SYMBOLS \"\\xee\\xa8\\x9e\"\t// U+ea1e\n#define ICON_MS_EMOJI_TRANSPORTATION \"\\xee\\xa8\\x9f\"\t// U+ea1f\n#define ICON_MS_EMOTICON \"\\xee\\x97\\xb3\"\t// U+e5f3\n#define ICON_MS_EMPTY_DASHBOARD \"\\xef\\xa1\\x84\"\t// U+f844\n#define ICON_MS_ENABLE \"\\xef\\x86\\x88\"\t// U+f188\n#define ICON_MS_ENCRYPTED \"\\xee\\x96\\x93\"\t// U+e593\n#define ICON_MS_ENDOCRINOLOGY \"\\xee\\x82\\xa9\"\t// U+e0a9\n#define ICON_MS_ENERGY \"\\xee\\xa6\\xa6\"\t// U+e9a6\n#define ICON_MS_ENERGY_PROGRAM_SAVING \"\\xef\\x85\\x9f\"\t// U+f15f\n#define ICON_MS_ENERGY_PROGRAM_TIME_USED \"\\xef\\x85\\xa1\"\t// U+f161\n#define ICON_MS_ENERGY_SAVINGS_LEAF \"\\xee\\xb0\\x9a\"\t// U+ec1a\n#define ICON_MS_ENGINEERING \"\\xee\\xa8\\xbd\"\t// U+ea3d\n#define ICON_MS_ENHANCED_ENCRYPTION \"\\xee\\x98\\xbf\"\t// U+e63f\n#define ICON_MS_ENT \"\\xee\\x82\\xaa\"\t// U+e0aa\n#define ICON_MS_ENTERPRISE \"\\xee\\x9c\\x8e\"\t// U+e70e\n#define ICON_MS_ENTERPRISE_OFF \"\\xee\\xad\\x8d\"\t// U+eb4d\n#define ICON_MS_EQUAL \"\\xef\\x9d\\xbb\"\t// U+f77b\n#define ICON_MS_EQUALIZER \"\\xee\\x80\\x9d\"\t// U+e01d\n#define ICON_MS_ERROR \"\\xef\\xa2\\xb6\"\t// U+f8b6\n#define ICON_MS_ERROR_CIRCLE_ROUNDED \"\\xef\\xa2\\xb6\"\t// U+f8b6\n#define ICON_MS_ERROR_MED \"\\xee\\x92\\x9b\"\t// U+e49b\n#define ICON_MS_ERROR_OUTLINE \"\\xef\\xa2\\xb6\"\t// U+f8b6\n#define ICON_MS_ESCALATOR \"\\xef\\x86\\xa1\"\t// U+f1a1\n#define ICON_MS_ESCALATOR_WARNING \"\\xef\\x86\\xac\"\t// U+f1ac\n#define ICON_MS_EURO \"\\xee\\xa8\\x95\"\t// U+ea15\n#define ICON_MS_EURO_SYMBOL \"\\xee\\xa4\\xa6\"\t// U+e926\n#define ICON_MS_EV_CHARGER \"\\xee\\x95\\xad\"\t// U+e56d\n#define ICON_MS_EV_MOBILEDATA_BADGE \"\\xef\\x9f\\xa2\"\t// U+f7e2\n#define ICON_MS_EV_SHADOW \"\\xee\\xbe\\x8f\"\t// U+ef8f\n#define ICON_MS_EV_SHADOW_ADD \"\\xef\\x96\\x80\"\t// U+f580\n#define ICON_MS_EV_SHADOW_MINUS \"\\xef\\x95\\xbf\"\t// U+f57f\n#define ICON_MS_EV_STATION \"\\xee\\x95\\xad\"\t// U+e56d\n#define ICON_MS_EVENT \"\\xee\\xa1\\xb8\"\t// U+e878\n#define ICON_MS_EVENT_AVAILABLE \"\\xee\\x98\\x94\"\t// U+e614\n#define ICON_MS_EVENT_BUSY \"\\xee\\x98\\x95\"\t// U+e615\n#define ICON_MS_EVENT_LIST \"\\xef\\x9a\\x83\"\t// U+f683\n#define ICON_MS_EVENT_NOTE \"\\xee\\x98\\x96\"\t// U+e616\n#define ICON_MS_EVENT_REPEAT \"\\xee\\xad\\xbb\"\t// U+eb7b\n#define ICON_MS_EVENT_SEAT \"\\xee\\xa4\\x83\"\t// U+e903\n#define ICON_MS_EVENT_UPCOMING \"\\xef\\x88\\xb8\"\t// U+f238\n#define ICON_MS_EXCLAMATION \"\\xef\\x88\\xaf\"\t// U+f22f\n#define ICON_MS_EXERCISE \"\\xef\\x9b\\xa6\"\t// U+f6e6\n#define ICON_MS_EXIT_TO_APP \"\\xee\\xa1\\xb9\"\t// U+e879\n#define ICON_MS_EXPAND \"\\xee\\xa5\\x8f\"\t// U+e94f\n#define ICON_MS_EXPAND_ALL \"\\xee\\xa5\\x86\"\t// U+e946\n#define ICON_MS_EXPAND_CIRCLE_DOWN \"\\xee\\x9f\\x8d\"\t// U+e7cd\n#define ICON_MS_EXPAND_CIRCLE_RIGHT \"\\xef\\x96\\x91\"\t// U+f591\n#define ICON_MS_EXPAND_CIRCLE_UP \"\\xef\\x97\\x92\"\t// U+f5d2\n#define ICON_MS_EXPAND_CONTENT \"\\xef\\xa0\\xb0\"\t// U+f830\n#define ICON_MS_EXPAND_LESS \"\\xee\\x97\\x8e\"\t// U+e5ce\n#define ICON_MS_EXPAND_MORE \"\\xee\\x97\\x8f\"\t// U+e5cf\n#define ICON_MS_EXPERIMENT \"\\xee\\x9a\\x86\"\t// U+e686\n#define ICON_MS_EXPLICIT \"\\xee\\x80\\x9e\"\t// U+e01e\n#define ICON_MS_EXPLORE \"\\xee\\xa1\\xba\"\t// U+e87a\n#define ICON_MS_EXPLORE_NEARBY \"\\xee\\x94\\xb8\"\t// U+e538\n#define ICON_MS_EXPLORE_OFF \"\\xee\\xa6\\xa8\"\t// U+e9a8\n#define ICON_MS_EXPLOSION \"\\xef\\x9a\\x85\"\t// U+f685\n#define ICON_MS_EXPORT_NOTES \"\\xee\\x82\\xac\"\t// U+e0ac\n#define ICON_MS_EXPOSURE \"\\xee\\x8f\\xb6\"\t// U+e3f6\n#define ICON_MS_EXPOSURE_NEG_1 \"\\xee\\x8f\\x8b\"\t// U+e3cb\n#define ICON_MS_EXPOSURE_NEG_2 \"\\xee\\x8f\\x8c\"\t// U+e3cc\n#define ICON_MS_EXPOSURE_PLUS_1 \"\\xee\\xa0\\x80\"\t// U+e800\n#define ICON_MS_EXPOSURE_PLUS_2 \"\\xee\\x8f\\x8e\"\t// U+e3ce\n#define ICON_MS_EXPOSURE_ZERO \"\\xee\\x8f\\x8f\"\t// U+e3cf\n#define ICON_MS_EXTENSION \"\\xee\\xa1\\xbb\"\t// U+e87b\n#define ICON_MS_EXTENSION_OFF \"\\xee\\x93\\xb5\"\t// U+e4f5\n#define ICON_MS_EYE_TRACKING \"\\xef\\x93\\x89\"\t// U+f4c9\n#define ICON_MS_EYEGLASSES \"\\xef\\x9b\\xae\"\t// U+f6ee\n#define ICON_MS_FACE \"\\xef\\x80\\x88\"\t// U+f008\n#define ICON_MS_FACE_2 \"\\xef\\xa3\\x9a\"\t// U+f8da\n#define ICON_MS_FACE_3 \"\\xef\\xa3\\x9b\"\t// U+f8db\n#define ICON_MS_FACE_4 \"\\xef\\xa3\\x9c\"\t// U+f8dc\n#define ICON_MS_FACE_5 \"\\xef\\xa3\\x9d\"\t// U+f8dd\n#define ICON_MS_FACE_6 \"\\xef\\xa3\\x9e\"\t// U+f8de\n#define ICON_MS_FACE_RETOUCHING_NATURAL \"\\xee\\xbd\\x8e\"\t// U+ef4e\n#define ICON_MS_FACE_RETOUCHING_OFF \"\\xef\\x80\\x87\"\t// U+f007\n#define ICON_MS_FACE_UNLOCK \"\\xef\\x80\\x88\"\t// U+f008\n#define ICON_MS_FACT_CHECK \"\\xef\\x83\\x85\"\t// U+f0c5\n#define ICON_MS_FACTORY \"\\xee\\xae\\xbc\"\t// U+ebbc\n#define ICON_MS_FALLING \"\\xef\\x98\\x8d\"\t// U+f60d\n#define ICON_MS_FAMILIAR_FACE_AND_ZONE \"\\xee\\x88\\x9c\"\t// U+e21c\n#define ICON_MS_FAMILY_HISTORY \"\\xee\\x82\\xad\"\t// U+e0ad\n#define ICON_MS_FAMILY_HOME \"\\xee\\xac\\xa6\"\t// U+eb26\n#define ICON_MS_FAMILY_LINK \"\\xee\\xac\\x99\"\t// U+eb19\n#define ICON_MS_FAMILY_RESTROOM \"\\xef\\x86\\xa2\"\t// U+f1a2\n#define ICON_MS_FAMILY_STAR \"\\xef\\x94\\xa7\"\t// U+f527\n#define ICON_MS_FARSIGHT_DIGITAL \"\\xef\\x95\\x99\"\t// U+f559\n#define ICON_MS_FAST_FORWARD \"\\xee\\x80\\x9f\"\t// U+e01f\n#define ICON_MS_FAST_REWIND \"\\xee\\x80\\xa0\"\t// U+e020\n#define ICON_MS_FASTFOOD \"\\xee\\x95\\xba\"\t// U+e57a\n#define ICON_MS_FAUCET \"\\xee\\x89\\xb8\"\t// U+e278\n#define ICON_MS_FAVORITE \"\\xee\\xa1\\xbe\"\t// U+e87e\n#define ICON_MS_FAVORITE_BORDER \"\\xee\\xa1\\xbe\"\t// U+e87e\n#define ICON_MS_FAX \"\\xee\\xab\\x98\"\t// U+ead8\n#define ICON_MS_FEATURE_SEARCH \"\\xee\\xa6\\xa9\"\t// U+e9a9\n#define ICON_MS_FEATURED_PLAY_LIST \"\\xee\\x81\\xad\"\t// U+e06d\n#define ICON_MS_FEATURED_SEASONAL_AND_GIFTS \"\\xee\\xbe\\x91\"\t// U+ef91\n#define ICON_MS_FEATURED_VIDEO \"\\xee\\x81\\xae\"\t// U+e06e\n#define ICON_MS_FEED \"\\xef\\x80\\x89\"\t// U+f009\n#define ICON_MS_FEEDBACK \"\\xee\\xa1\\xbf\"\t// U+e87f\n#define ICON_MS_FEMALE \"\\xee\\x96\\x90\"\t// U+e590\n#define ICON_MS_FEMUR \"\\xef\\xa2\\x91\"\t// U+f891\n#define ICON_MS_FEMUR_ALT \"\\xef\\xa2\\x92\"\t// U+f892\n#define ICON_MS_FENCE \"\\xef\\x87\\xb6\"\t// U+f1f6\n#define ICON_MS_FERTILE \"\\xef\\x9b\\xa5\"\t// U+f6e5\n#define ICON_MS_FESTIVAL \"\\xee\\xa9\\xa8\"\t// U+ea68\n#define ICON_MS_FIBER_DVR \"\\xee\\x81\\x9d\"\t// U+e05d\n#define ICON_MS_FIBER_MANUAL_RECORD \"\\xee\\x81\\xa1\"\t// U+e061\n#define ICON_MS_FIBER_NEW \"\\xee\\x81\\x9e\"\t// U+e05e\n#define ICON_MS_FIBER_PIN \"\\xee\\x81\\xaa\"\t// U+e06a\n#define ICON_MS_FIBER_SMART_RECORD \"\\xee\\x81\\xa2\"\t// U+e062\n#define ICON_MS_FILE_COPY \"\\xee\\x85\\xb3\"\t// U+e173\n#define ICON_MS_FILE_COPY_OFF \"\\xef\\x93\\x98\"\t// U+f4d8\n#define ICON_MS_FILE_DOWNLOAD \"\\xef\\x82\\x90\"\t// U+f090\n#define ICON_MS_FILE_DOWNLOAD_DONE \"\\xef\\x82\\x91\"\t// U+f091\n#define ICON_MS_FILE_DOWNLOAD_OFF \"\\xee\\x93\\xbe\"\t// U+e4fe\n#define ICON_MS_FILE_MAP \"\\xee\\x8b\\x85\"\t// U+e2c5\n#define ICON_MS_FILE_OPEN \"\\xee\\xab\\xb3\"\t// U+eaf3\n#define ICON_MS_FILE_PRESENT \"\\xee\\xa8\\x8e\"\t// U+ea0e\n#define ICON_MS_FILE_SAVE \"\\xef\\x85\\xbf\"\t// U+f17f\n#define ICON_MS_FILE_SAVE_OFF \"\\xee\\x94\\x85\"\t// U+e505\n#define ICON_MS_FILE_UPLOAD \"\\xef\\x82\\x9b\"\t// U+f09b\n#define ICON_MS_FILE_UPLOAD_OFF \"\\xef\\xa2\\x86\"\t// U+f886\n#define ICON_MS_FILTER \"\\xee\\x8f\\x93\"\t// U+e3d3\n#define ICON_MS_FILTER_1 \"\\xee\\x8f\\x90\"\t// U+e3d0\n#define ICON_MS_FILTER_2 \"\\xee\\x8f\\x91\"\t// U+e3d1\n#define ICON_MS_FILTER_3 \"\\xee\\x8f\\x92\"\t// U+e3d2\n#define ICON_MS_FILTER_4 \"\\xee\\x8f\\x94\"\t// U+e3d4\n#define ICON_MS_FILTER_5 \"\\xee\\x8f\\x95\"\t// U+e3d5\n#define ICON_MS_FILTER_6 \"\\xee\\x8f\\x96\"\t// U+e3d6\n#define ICON_MS_FILTER_7 \"\\xee\\x8f\\x97\"\t// U+e3d7\n#define ICON_MS_FILTER_8 \"\\xee\\x8f\\x98\"\t// U+e3d8\n#define ICON_MS_FILTER_9 \"\\xee\\x8f\\x99\"\t// U+e3d9\n#define ICON_MS_FILTER_9_PLUS \"\\xee\\x8f\\x9a\"\t// U+e3da\n#define ICON_MS_FILTER_ALT \"\\xee\\xbd\\x8f\"\t// U+ef4f\n#define ICON_MS_FILTER_ALT_OFF \"\\xee\\xac\\xb2\"\t// U+eb32\n#define ICON_MS_FILTER_B_AND_W \"\\xee\\x8f\\x9b\"\t// U+e3db\n#define ICON_MS_FILTER_CENTER_FOCUS \"\\xee\\x8f\\x9c\"\t// U+e3dc\n#define ICON_MS_FILTER_DRAMA \"\\xee\\x8f\\x9d\"\t// U+e3dd\n#define ICON_MS_FILTER_FRAMES \"\\xee\\x8f\\x9e\"\t// U+e3de\n#define ICON_MS_FILTER_HDR \"\\xee\\x8f\\x9f\"\t// U+e3df\n#define ICON_MS_FILTER_LIST \"\\xee\\x85\\x92\"\t// U+e152\n#define ICON_MS_FILTER_LIST_ALT \"\\xee\\xa5\\x8e\"\t// U+e94e\n#define ICON_MS_FILTER_LIST_OFF \"\\xee\\xad\\x97\"\t// U+eb57\n#define ICON_MS_FILTER_NONE \"\\xee\\x8f\\xa0\"\t// U+e3e0\n#define ICON_MS_FILTER_RETROLUX \"\\xee\\x8f\\xa1\"\t// U+e3e1\n#define ICON_MS_FILTER_TILT_SHIFT \"\\xee\\x8f\\xa2\"\t// U+e3e2\n#define ICON_MS_FILTER_VINTAGE \"\\xee\\x8f\\xa3\"\t// U+e3e3\n#define ICON_MS_FINANCE \"\\xee\\x9a\\xbf\"\t// U+e6bf\n#define ICON_MS_FINANCE_CHIP \"\\xef\\xa1\\x8e\"\t// U+f84e\n#define ICON_MS_FINANCE_MODE \"\\xee\\xbe\\x92\"\t// U+ef92\n#define ICON_MS_FIND_IN_PAGE \"\\xee\\xa2\\x80\"\t// U+e880\n#define ICON_MS_FIND_REPLACE \"\\xee\\xa2\\x81\"\t// U+e881\n#define ICON_MS_FINGERPRINT \"\\xee\\xa4\\x8d\"\t// U+e90d\n#define ICON_MS_FINGERPRINT_OFF \"\\xef\\x92\\x9d\"\t// U+f49d\n#define ICON_MS_FIRE_EXTINGUISHER \"\\xef\\x87\\x98\"\t// U+f1d8\n#define ICON_MS_FIRE_HYDRANT \"\\xef\\x86\\xa3\"\t// U+f1a3\n#define ICON_MS_FIRE_TRUCK \"\\xef\\xa3\\xb2\"\t// U+f8f2\n#define ICON_MS_FIREPLACE \"\\xee\\xa9\\x83\"\t// U+ea43\n#define ICON_MS_FIRST_PAGE \"\\xee\\x97\\x9c\"\t// U+e5dc\n#define ICON_MS_FIT_PAGE \"\\xef\\x9d\\xba\"\t// U+f77a\n#define ICON_MS_FIT_SCREEN \"\\xee\\xa8\\x90\"\t// U+ea10\n#define ICON_MS_FIT_WIDTH \"\\xef\\x9d\\xb9\"\t// U+f779\n#define ICON_MS_FITNESS_CENTER \"\\xee\\xad\\x83\"\t// U+eb43\n#define ICON_MS_FLAG \"\\xef\\x83\\x86\"\t// U+f0c6\n#define ICON_MS_FLAG_CIRCLE \"\\xee\\xab\\xb8\"\t// U+eaf8\n#define ICON_MS_FLAG_FILLED \"\\xef\\x83\\x86\"\t// U+f0c6\n#define ICON_MS_FLAKY \"\\xee\\xbd\\x90\"\t// U+ef50\n#define ICON_MS_FLARE \"\\xee\\x8f\\xa4\"\t// U+e3e4\n#define ICON_MS_FLASH_AUTO \"\\xee\\x8f\\xa5\"\t// U+e3e5\n#define ICON_MS_FLASH_OFF \"\\xee\\x8f\\xa6\"\t// U+e3e6\n#define ICON_MS_FLASH_ON \"\\xee\\x8f\\xa7\"\t// U+e3e7\n#define ICON_MS_FLASHLIGHT_OFF \"\\xef\\x80\\x8a\"\t// U+f00a\n#define ICON_MS_FLASHLIGHT_ON \"\\xef\\x80\\x8b\"\t// U+f00b\n#define ICON_MS_FLATWARE \"\\xef\\x80\\x8c\"\t// U+f00c\n#define ICON_MS_FLEX_DIRECTION \"\\xef\\x9d\\xb8\"\t// U+f778\n#define ICON_MS_FLEX_NO_WRAP \"\\xef\\x9d\\xb7\"\t// U+f777\n#define ICON_MS_FLEX_WRAP \"\\xef\\x9d\\xb6\"\t// U+f776\n#define ICON_MS_FLIGHT \"\\xee\\x94\\xb9\"\t// U+e539\n#define ICON_MS_FLIGHT_CLASS \"\\xee\\x9f\\x8b\"\t// U+e7cb\n#define ICON_MS_FLIGHT_LAND \"\\xee\\xa4\\x84\"\t// U+e904\n#define ICON_MS_FLIGHT_TAKEOFF \"\\xee\\xa4\\x85\"\t// U+e905\n#define ICON_MS_FLIGHTS_AND_HOTELS \"\\xee\\xa6\\xab\"\t// U+e9ab\n#define ICON_MS_FLIGHTSMODE \"\\xee\\xbe\\x93\"\t// U+ef93\n#define ICON_MS_FLIP \"\\xee\\x8f\\xa8\"\t// U+e3e8\n#define ICON_MS_FLIP_CAMERA_ANDROID \"\\xee\\xa8\\xb7\"\t// U+ea37\n#define ICON_MS_FLIP_CAMERA_IOS \"\\xee\\xa8\\xb8\"\t// U+ea38\n#define ICON_MS_FLIP_TO_BACK \"\\xee\\xa2\\x82\"\t// U+e882\n#define ICON_MS_FLIP_TO_FRONT \"\\xee\\xa2\\x83\"\t// U+e883\n#define ICON_MS_FLOOD \"\\xee\\xaf\\xa6\"\t// U+ebe6\n#define ICON_MS_FLOOR \"\\xef\\x9b\\xa4\"\t// U+f6e4\n#define ICON_MS_FLOOR_LAMP \"\\xee\\x88\\x9e\"\t// U+e21e\n#define ICON_MS_FLOURESCENT \"\\xef\\x81\\xbd\"\t// U+f07d\n#define ICON_MS_FLOWSHEET \"\\xee\\x82\\xae\"\t// U+e0ae\n#define ICON_MS_FLUID \"\\xee\\x92\\x83\"\t// U+e483\n#define ICON_MS_FLUID_BALANCE \"\\xef\\xa0\\x8d\"\t// U+f80d\n#define ICON_MS_FLUID_MED \"\\xef\\xa0\\x8c\"\t// U+f80c\n#define ICON_MS_FLUORESCENT \"\\xef\\x81\\xbd\"\t// U+f07d\n#define ICON_MS_FLUTTER \"\\xef\\x87\\x9d\"\t// U+f1dd\n#define ICON_MS_FLUTTER_DASH \"\\xee\\x80\\x8b\"\t// U+e00b\n#define ICON_MS_FMD_BAD \"\\xef\\x80\\x8e\"\t// U+f00e\n#define ICON_MS_FMD_GOOD \"\\xef\\x87\\x9b\"\t// U+f1db\n#define ICON_MS_FOGGY \"\\xee\\xa0\\x98\"\t// U+e818\n#define ICON_MS_FOLDED_HANDS \"\\xef\\x97\\xad\"\t// U+f5ed\n#define ICON_MS_FOLDER \"\\xee\\x8b\\x87\"\t// U+e2c7\n#define ICON_MS_FOLDER_COPY \"\\xee\\xae\\xbd\"\t// U+ebbd\n#define ICON_MS_FOLDER_DATA \"\\xef\\x96\\x86\"\t// U+f586\n#define ICON_MS_FOLDER_DELETE \"\\xee\\xac\\xb4\"\t// U+eb34\n#define ICON_MS_FOLDER_LIMITED \"\\xef\\x93\\xa4\"\t// U+f4e4\n#define ICON_MS_FOLDER_MANAGED \"\\xef\\x9d\\xb5\"\t// U+f775\n#define ICON_MS_FOLDER_OFF \"\\xee\\xae\\x83\"\t// U+eb83\n#define ICON_MS_FOLDER_OPEN \"\\xee\\x8b\\x88\"\t// U+e2c8\n#define ICON_MS_FOLDER_SHARED \"\\xee\\x8b\\x89\"\t// U+e2c9\n#define ICON_MS_FOLDER_SPECIAL \"\\xee\\x98\\x97\"\t// U+e617\n#define ICON_MS_FOLDER_SUPERVISED \"\\xef\\x9d\\xb4\"\t// U+f774\n#define ICON_MS_FOLDER_ZIP \"\\xee\\xac\\xac\"\t// U+eb2c\n#define ICON_MS_FOLLOW_THE_SIGNS \"\\xef\\x88\\xa2\"\t// U+f222\n#define ICON_MS_FONT_DOWNLOAD \"\\xee\\x85\\xa7\"\t// U+e167\n#define ICON_MS_FONT_DOWNLOAD_OFF \"\\xee\\x93\\xb9\"\t// U+e4f9\n#define ICON_MS_FOOD_BANK \"\\xef\\x87\\xb2\"\t// U+f1f2\n#define ICON_MS_FOOT_BONES \"\\xef\\xa2\\x93\"\t// U+f893\n#define ICON_MS_FOOTPRINT \"\\xef\\xa1\\xbd\"\t// U+f87d\n#define ICON_MS_FOR_YOU \"\\xee\\xa6\\xac\"\t// U+e9ac\n#define ICON_MS_FOREST \"\\xee\\xaa\\x99\"\t// U+ea99\n#define ICON_MS_FORK_LEFT \"\\xee\\xae\\xa0\"\t// U+eba0\n#define ICON_MS_FORK_RIGHT \"\\xee\\xae\\xac\"\t// U+ebac\n#define ICON_MS_FORKLIFT \"\\xef\\xa1\\xa8\"\t// U+f868\n#define ICON_MS_FORMAT_ALIGN_CENTER \"\\xee\\x88\\xb4\"\t// U+e234\n#define ICON_MS_FORMAT_ALIGN_JUSTIFY \"\\xee\\x88\\xb5\"\t// U+e235\n#define ICON_MS_FORMAT_ALIGN_LEFT \"\\xee\\x88\\xb6\"\t// U+e236\n#define ICON_MS_FORMAT_ALIGN_RIGHT \"\\xee\\x88\\xb7\"\t// U+e237\n#define ICON_MS_FORMAT_BOLD \"\\xee\\x88\\xb8\"\t// U+e238\n#define ICON_MS_FORMAT_CLEAR \"\\xee\\x88\\xb9\"\t// U+e239\n#define ICON_MS_FORMAT_COLOR_FILL \"\\xee\\x88\\xba\"\t// U+e23a\n#define ICON_MS_FORMAT_COLOR_RESET \"\\xee\\x88\\xbb\"\t// U+e23b\n#define ICON_MS_FORMAT_COLOR_TEXT \"\\xee\\x88\\xbc\"\t// U+e23c\n#define ICON_MS_FORMAT_H1 \"\\xef\\xa1\\x9d\"\t// U+f85d\n#define ICON_MS_FORMAT_H2 \"\\xef\\xa1\\x9e\"\t// U+f85e\n#define ICON_MS_FORMAT_H3 \"\\xef\\xa1\\x9f\"\t// U+f85f\n#define ICON_MS_FORMAT_H4 \"\\xef\\xa1\\xa0\"\t// U+f860\n#define ICON_MS_FORMAT_H5 \"\\xef\\xa1\\xa1\"\t// U+f861\n#define ICON_MS_FORMAT_H6 \"\\xef\\xa1\\xa2\"\t// U+f862\n#define ICON_MS_FORMAT_IMAGE_LEFT \"\\xef\\xa1\\xa3\"\t// U+f863\n#define ICON_MS_FORMAT_IMAGE_RIGHT \"\\xef\\xa1\\xa4\"\t// U+f864\n#define ICON_MS_FORMAT_INDENT_DECREASE \"\\xee\\x88\\xbd\"\t// U+e23d\n#define ICON_MS_FORMAT_INDENT_INCREASE \"\\xee\\x88\\xbe\"\t// U+e23e\n#define ICON_MS_FORMAT_INK_HIGHLIGHTER \"\\xef\\xa0\\xab\"\t// U+f82b\n#define ICON_MS_FORMAT_ITALIC \"\\xee\\x88\\xbf\"\t// U+e23f\n#define ICON_MS_FORMAT_LETTER_SPACING \"\\xef\\x9d\\xb3\"\t// U+f773\n#define ICON_MS_FORMAT_LETTER_SPACING_2 \"\\xef\\x98\\x98\"\t// U+f618\n#define ICON_MS_FORMAT_LETTER_SPACING_STANDARD \"\\xef\\x98\\x97\"\t// U+f617\n#define ICON_MS_FORMAT_LETTER_SPACING_WIDE \"\\xef\\x98\\x96\"\t// U+f616\n#define ICON_MS_FORMAT_LETTER_SPACING_WIDER \"\\xef\\x98\\x95\"\t// U+f615\n#define ICON_MS_FORMAT_LINE_SPACING \"\\xee\\x89\\x80\"\t// U+e240\n#define ICON_MS_FORMAT_LIST_BULLETED \"\\xee\\x89\\x81\"\t// U+e241\n#define ICON_MS_FORMAT_LIST_BULLETED_ADD \"\\xef\\xa1\\x89\"\t// U+f849\n#define ICON_MS_FORMAT_LIST_NUMBERED \"\\xee\\x89\\x82\"\t// U+e242\n#define ICON_MS_FORMAT_LIST_NUMBERED_RTL \"\\xee\\x89\\xa7\"\t// U+e267\n#define ICON_MS_FORMAT_OVERLINE \"\\xee\\xad\\xa5\"\t// U+eb65\n#define ICON_MS_FORMAT_PAINT \"\\xee\\x89\\x83\"\t// U+e243\n#define ICON_MS_FORMAT_PARAGRAPH \"\\xef\\xa1\\xa5\"\t// U+f865\n#define ICON_MS_FORMAT_QUOTE \"\\xee\\x89\\x84\"\t// U+e244\n#define ICON_MS_FORMAT_SHAPES \"\\xee\\x89\\x9e\"\t// U+e25e\n#define ICON_MS_FORMAT_SIZE \"\\xee\\x89\\x85\"\t// U+e245\n#define ICON_MS_FORMAT_STRIKETHROUGH \"\\xee\\x89\\x86\"\t// U+e246\n#define ICON_MS_FORMAT_TEXT_CLIP \"\\xef\\xa0\\xaa\"\t// U+f82a\n#define ICON_MS_FORMAT_TEXT_OVERFLOW \"\\xef\\xa0\\xa9\"\t// U+f829\n#define ICON_MS_FORMAT_TEXT_WRAP \"\\xef\\xa0\\xa8\"\t// U+f828\n#define ICON_MS_FORMAT_TEXTDIRECTION_L_TO_R \"\\xee\\x89\\x87\"\t// U+e247\n#define ICON_MS_FORMAT_TEXTDIRECTION_R_TO_L \"\\xee\\x89\\x88\"\t// U+e248\n#define ICON_MS_FORMAT_TEXTDIRECTION_VERTICAL \"\\xef\\x92\\xb8\"\t// U+f4b8\n#define ICON_MS_FORMAT_UNDERLINED \"\\xee\\x89\\x89\"\t// U+e249\n#define ICON_MS_FORMAT_UNDERLINED_SQUIGGLE \"\\xef\\xa2\\x85\"\t// U+f885\n#define ICON_MS_FORMS_ADD_ON \"\\xef\\x83\\x87\"\t// U+f0c7\n#define ICON_MS_FORMS_APPS_SCRIPT \"\\xef\\x83\\x88\"\t// U+f0c8\n#define ICON_MS_FORT \"\\xee\\xaa\\xad\"\t// U+eaad\n#define ICON_MS_FORUM \"\\xee\\xa2\\xaf\"\t// U+e8af\n#define ICON_MS_FORWARD \"\\xef\\x95\\xba\"\t// U+f57a\n#define ICON_MS_FORWARD_10 \"\\xee\\x81\\x96\"\t// U+e056\n#define ICON_MS_FORWARD_30 \"\\xee\\x81\\x97\"\t// U+e057\n#define ICON_MS_FORWARD_5 \"\\xee\\x81\\x98\"\t// U+e058\n#define ICON_MS_FORWARD_CIRCLE \"\\xef\\x9b\\xb5\"\t// U+f6f5\n#define ICON_MS_FORWARD_MEDIA \"\\xef\\x9b\\xb4\"\t// U+f6f4\n#define ICON_MS_FORWARD_TO_INBOX \"\\xef\\x86\\x87\"\t// U+f187\n#define ICON_MS_FOUNDATION \"\\xef\\x88\\x80\"\t// U+f200\n#define ICON_MS_FRAME_INSPECT \"\\xef\\x9d\\xb2\"\t// U+f772\n#define ICON_MS_FRAME_PERSON \"\\xef\\xa2\\xa6\"\t// U+f8a6\n#define ICON_MS_FRAME_PERSON_MIC \"\\xef\\x93\\x95\"\t// U+f4d5\n#define ICON_MS_FRAME_PERSON_OFF \"\\xef\\x9f\\x91\"\t// U+f7d1\n#define ICON_MS_FRAME_RELOAD \"\\xef\\x9d\\xb1\"\t// U+f771\n#define ICON_MS_FRAME_SOURCE \"\\xef\\x9d\\xb0\"\t// U+f770\n#define ICON_MS_FREE_BREAKFAST \"\\xee\\xad\\x84\"\t// U+eb44\n#define ICON_MS_FREE_CANCELLATION \"\\xee\\x9d\\x88\"\t// U+e748\n#define ICON_MS_FRONT_HAND \"\\xee\\x9d\\xa9\"\t// U+e769\n#define ICON_MS_FRONT_LOADER \"\\xef\\xa1\\xa9\"\t// U+f869\n#define ICON_MS_FULL_COVERAGE \"\\xee\\xac\\x92\"\t// U+eb12\n#define ICON_MS_FULL_HD \"\\xef\\x96\\x8b\"\t// U+f58b\n#define ICON_MS_FULL_STACKED_BAR_CHART \"\\xef\\x88\\x92\"\t// U+f212\n#define ICON_MS_FULLSCREEN \"\\xee\\x97\\x90\"\t// U+e5d0\n#define ICON_MS_FULLSCREEN_EXIT \"\\xee\\x97\\x91\"\t// U+e5d1\n#define ICON_MS_FUNCTION \"\\xef\\xa1\\xa6\"\t// U+f866\n#define ICON_MS_FUNCTIONS \"\\xee\\x89\\x8a\"\t// U+e24a\n#define ICON_MS_G_MOBILEDATA \"\\xef\\x80\\x90\"\t// U+f010\n#define ICON_MS_G_MOBILEDATA_BADGE \"\\xef\\x9f\\xa1\"\t// U+f7e1\n#define ICON_MS_G_TRANSLATE \"\\xee\\xa4\\xa7\"\t// U+e927\n#define ICON_MS_GALLERY_THUMBNAIL \"\\xef\\xa1\\xaf\"\t// U+f86f\n#define ICON_MS_GAMEPAD \"\\xee\\x8c\\x8f\"\t// U+e30f\n#define ICON_MS_GAMES \"\\xee\\x8c\\x8f\"\t// U+e30f\n#define ICON_MS_GARAGE \"\\xef\\x80\\x91\"\t// U+f011\n#define ICON_MS_GARAGE_DOOR \"\\xee\\x9c\\x94\"\t// U+e714\n#define ICON_MS_GARAGE_HOME \"\\xee\\xa0\\xad\"\t// U+e82d\n#define ICON_MS_GARDEN_CART \"\\xef\\xa2\\xa9\"\t// U+f8a9\n#define ICON_MS_GAS_METER \"\\xee\\xb0\\x99\"\t// U+ec19\n#define ICON_MS_GASTROENTEROLOGY \"\\xee\\x83\\xb1\"\t// U+e0f1\n#define ICON_MS_GATE \"\\xee\\x89\\xb7\"\t// U+e277\n#define ICON_MS_GAVEL \"\\xee\\xa4\\x8e\"\t// U+e90e\n#define ICON_MS_GENERAL_DEVICE \"\\xee\\x9b\\x9e\"\t// U+e6de\n#define ICON_MS_GENERATING_TOKENS \"\\xee\\x9d\\x89\"\t// U+e749\n#define ICON_MS_GENETICS \"\\xee\\x83\\xb3\"\t// U+e0f3\n#define ICON_MS_GENRES \"\\xee\\x9b\\xae\"\t// U+e6ee\n#define ICON_MS_GESTURE \"\\xee\\x85\\x95\"\t// U+e155\n#define ICON_MS_GESTURE_SELECT \"\\xef\\x99\\x97\"\t// U+f657\n#define ICON_MS_GET_APP \"\\xef\\x82\\x90\"\t// U+f090\n#define ICON_MS_GIF \"\\xee\\xa4\\x88\"\t// U+e908\n#define ICON_MS_GIF_BOX \"\\xee\\x9e\\xa3\"\t// U+e7a3\n#define ICON_MS_GIRL \"\\xee\\xad\\xa8\"\t// U+eb68\n#define ICON_MS_GITE \"\\xee\\x96\\x8b\"\t// U+e58b\n#define ICON_MS_GLASS_CUP \"\\xef\\x9b\\xa3\"\t// U+f6e3\n#define ICON_MS_GLOBE \"\\xee\\x99\\x8c\"\t// U+e64c\n#define ICON_MS_GLOBE_ASIA \"\\xef\\x9e\\x99\"\t// U+f799\n#define ICON_MS_GLOBE_UK \"\\xef\\x9e\\x98\"\t// U+f798\n#define ICON_MS_GLUCOSE \"\\xee\\x92\\xa0\"\t// U+e4a0\n#define ICON_MS_GLYPHS \"\\xef\\xa2\\xa3\"\t// U+f8a3\n#define ICON_MS_GO_TO_LINE \"\\xef\\x9c\\x9d\"\t// U+f71d\n#define ICON_MS_GOLF_COURSE \"\\xee\\xad\\x85\"\t// U+eb45\n#define ICON_MS_GOOGLE_HOME_DEVICES \"\\xee\\x9c\\x95\"\t// U+e715\n#define ICON_MS_GOOGLE_PLUS_RESHARE \"\\xef\\x95\\xba\"\t// U+f57a\n#define ICON_MS_GOOGLE_TV_REMOTE \"\\xef\\x97\\x9b\"\t// U+f5db\n#define ICON_MS_GOOGLE_WIFI \"\\xef\\x95\\xb9\"\t// U+f579\n#define ICON_MS_GPP_BAD \"\\xef\\x80\\x92\"\t// U+f012\n#define ICON_MS_GPP_GOOD \"\\xef\\x80\\x93\"\t// U+f013\n#define ICON_MS_GPP_MAYBE \"\\xef\\x80\\x94\"\t// U+f014\n#define ICON_MS_GPS_FIXED \"\\xee\\x95\\x9c\"\t// U+e55c\n#define ICON_MS_GPS_NOT_FIXED \"\\xee\\x86\\xb7\"\t// U+e1b7\n#define ICON_MS_GPS_OFF \"\\xee\\x86\\xb6\"\t// U+e1b6\n#define ICON_MS_GRADE \"\\xee\\xa2\\x85\"\t// U+e885\n#define ICON_MS_GRADIENT \"\\xee\\x8f\\xa9\"\t// U+e3e9\n#define ICON_MS_GRADING \"\\xee\\xa9\\x8f\"\t// U+ea4f\n#define ICON_MS_GRAIN \"\\xee\\x8f\\xaa\"\t// U+e3ea\n#define ICON_MS_GRAPHIC_EQ \"\\xee\\x86\\xb8\"\t// U+e1b8\n#define ICON_MS_GRASS \"\\xef\\x88\\x85\"\t// U+f205\n#define ICON_MS_GRID_3X3 \"\\xef\\x80\\x95\"\t// U+f015\n#define ICON_MS_GRID_3X3_OFF \"\\xef\\x99\\xbc\"\t// U+f67c\n#define ICON_MS_GRID_4X4 \"\\xef\\x80\\x96\"\t// U+f016\n#define ICON_MS_GRID_GOLDENRATIO \"\\xef\\x80\\x97\"\t// U+f017\n#define ICON_MS_GRID_GUIDES \"\\xef\\x9d\\xaf\"\t// U+f76f\n#define ICON_MS_GRID_OFF \"\\xee\\x8f\\xab\"\t// U+e3eb\n#define ICON_MS_GRID_ON \"\\xee\\x8f\\xac\"\t// U+e3ec\n#define ICON_MS_GRID_VIEW \"\\xee\\xa6\\xb0\"\t// U+e9b0\n#define ICON_MS_GROCERY \"\\xee\\xbe\\x97\"\t// U+ef97\n#define ICON_MS_GROUP \"\\xee\\xa8\\xa1\"\t// U+ea21\n#define ICON_MS_GROUP_ADD \"\\xee\\x9f\\xb0\"\t// U+e7f0\n#define ICON_MS_GROUP_OFF \"\\xee\\x9d\\x87\"\t// U+e747\n#define ICON_MS_GROUP_REMOVE \"\\xee\\x9e\\xad\"\t// U+e7ad\n#define ICON_MS_GROUP_WORK \"\\xee\\xa2\\x86\"\t// U+e886\n#define ICON_MS_GROUPED_BAR_CHART \"\\xef\\x88\\x91\"\t// U+f211\n#define ICON_MS_GROUPS \"\\xef\\x88\\xb3\"\t// U+f233\n#define ICON_MS_GROUPS_2 \"\\xef\\xa3\\x9f\"\t// U+f8df\n#define ICON_MS_GROUPS_3 \"\\xef\\xa3\\xa0\"\t// U+f8e0\n#define ICON_MS_GUARDIAN \"\\xef\\x93\\x81\"\t// U+f4c1\n#define ICON_MS_GYNECOLOGY \"\\xee\\x83\\xb4\"\t// U+e0f4\n#define ICON_MS_H_MOBILEDATA \"\\xef\\x80\\x98\"\t// U+f018\n#define ICON_MS_H_MOBILEDATA_BADGE \"\\xef\\x9f\\xa0\"\t// U+f7e0\n#define ICON_MS_H_PLUS_MOBILEDATA \"\\xef\\x80\\x99\"\t// U+f019\n#define ICON_MS_H_PLUS_MOBILEDATA_BADGE \"\\xef\\x9f\\x9f\"\t// U+f7df\n#define ICON_MS_HAIL \"\\xee\\xa6\\xb1\"\t// U+e9b1\n#define ICON_MS_HALLWAY \"\\xee\\x9b\\xb8\"\t// U+e6f8\n#define ICON_MS_HAND_BONES \"\\xef\\xa2\\x94\"\t// U+f894\n#define ICON_MS_HAND_GESTURE \"\\xee\\xbe\\x9c\"\t// U+ef9c\n#define ICON_MS_HANDHELD_CONTROLLER \"\\xef\\x93\\x86\"\t// U+f4c6\n#define ICON_MS_HANDSHAKE \"\\xee\\xaf\\x8b\"\t// U+ebcb\n#define ICON_MS_HANDWRITING_RECOGNITION \"\\xee\\xac\\x82\"\t// U+eb02\n#define ICON_MS_HANDYMAN \"\\xef\\x84\\x8b\"\t// U+f10b\n#define ICON_MS_HANGOUT_VIDEO \"\\xee\\x83\\x81\"\t// U+e0c1\n#define ICON_MS_HANGOUT_VIDEO_OFF \"\\xee\\x83\\x82\"\t// U+e0c2\n#define ICON_MS_HARD_DRIVE \"\\xef\\xa0\\x8e\"\t// U+f80e\n#define ICON_MS_HARD_DRIVE_2 \"\\xef\\x9e\\xa4\"\t// U+f7a4\n#define ICON_MS_HARDWARE \"\\xee\\xa9\\x99\"\t// U+ea59\n#define ICON_MS_HD \"\\xee\\x81\\x92\"\t// U+e052\n#define ICON_MS_HDR_AUTO \"\\xef\\x80\\x9a\"\t// U+f01a\n#define ICON_MS_HDR_AUTO_SELECT \"\\xef\\x80\\x9b\"\t// U+f01b\n#define ICON_MS_HDR_ENHANCED_SELECT \"\\xee\\xbd\\x91\"\t// U+ef51\n#define ICON_MS_HDR_OFF \"\\xee\\x8f\\xad\"\t// U+e3ed\n#define ICON_MS_HDR_OFF_SELECT \"\\xef\\x80\\x9c\"\t// U+f01c\n#define ICON_MS_HDR_ON \"\\xee\\x8f\\xae\"\t// U+e3ee\n#define ICON_MS_HDR_ON_SELECT \"\\xef\\x80\\x9d\"\t// U+f01d\n#define ICON_MS_HDR_PLUS \"\\xef\\x80\\x9e\"\t// U+f01e\n#define ICON_MS_HDR_PLUS_OFF \"\\xee\\x8f\\xaf\"\t// U+e3ef\n#define ICON_MS_HDR_STRONG \"\\xee\\x8f\\xb1\"\t// U+e3f1\n#define ICON_MS_HDR_WEAK \"\\xee\\x8f\\xb2\"\t// U+e3f2\n#define ICON_MS_HEAD_MOUNTED_DEVICE \"\\xef\\x93\\x85\"\t// U+f4c5\n#define ICON_MS_HEADPHONES \"\\xef\\x80\\x9f\"\t// U+f01f\n#define ICON_MS_HEADPHONES_BATTERY \"\\xef\\x80\\xa0\"\t// U+f020\n#define ICON_MS_HEADSET \"\\xef\\x80\\x9f\"\t// U+f01f\n#define ICON_MS_HEADSET_MIC \"\\xee\\x8c\\x91\"\t// U+e311\n#define ICON_MS_HEADSET_OFF \"\\xee\\x8c\\xba\"\t// U+e33a\n#define ICON_MS_HEALING \"\\xee\\x8f\\xb3\"\t// U+e3f3\n#define ICON_MS_HEALTH_AND_BEAUTY \"\\xee\\xbe\\x9d\"\t// U+ef9d\n#define ICON_MS_HEALTH_AND_SAFETY \"\\xee\\x87\\x95\"\t// U+e1d5\n#define ICON_MS_HEALTH_METRICS \"\\xef\\x9b\\xa2\"\t// U+f6e2\n#define ICON_MS_HEAP_SNAPSHOT_LARGE \"\\xef\\x9d\\xae\"\t// U+f76e\n#define ICON_MS_HEAP_SNAPSHOT_MULTIPLE \"\\xef\\x9d\\xad\"\t// U+f76d\n#define ICON_MS_HEAP_SNAPSHOT_THUMBNAIL \"\\xef\\x9d\\xac\"\t// U+f76c\n#define ICON_MS_HEARING \"\\xee\\x80\\xa3\"\t// U+e023\n#define ICON_MS_HEARING_DISABLED \"\\xef\\x84\\x84\"\t// U+f104\n#define ICON_MS_HEART_BROKEN \"\\xee\\xab\\x82\"\t// U+eac2\n#define ICON_MS_HEART_CHECK \"\\xef\\x98\\x8a\"\t// U+f60a\n#define ICON_MS_HEART_MINUS \"\\xef\\xa2\\x83\"\t// U+f883\n#define ICON_MS_HEART_PLUS \"\\xef\\xa2\\x84\"\t// U+f884\n#define ICON_MS_HEAT \"\\xef\\x94\\xb7\"\t// U+f537\n#define ICON_MS_HEAT_PUMP \"\\xee\\xb0\\x98\"\t// U+ec18\n#define ICON_MS_HEAT_PUMP_BALANCE \"\\xee\\x89\\xbe\"\t// U+e27e\n#define ICON_MS_HEIGHT \"\\xee\\xa8\\x96\"\t// U+ea16\n#define ICON_MS_HELICOPTER \"\\xef\\x98\\x8c\"\t// U+f60c\n#define ICON_MS_HELP \"\\xee\\xa3\\xbd\"\t// U+e8fd\n#define ICON_MS_HELP_CENTER \"\\xef\\x87\\x80\"\t// U+f1c0\n#define ICON_MS_HELP_CLINIC \"\\xef\\xa0\\x90\"\t// U+f810\n#define ICON_MS_HELP_OUTLINE \"\\xee\\xa3\\xbd\"\t// U+e8fd\n#define ICON_MS_HEMATOLOGY \"\\xee\\x83\\xb6\"\t// U+e0f6\n#define ICON_MS_HEVC \"\\xef\\x80\\xa1\"\t// U+f021\n#define ICON_MS_HEXAGON \"\\xee\\xac\\xb9\"\t// U+eb39\n#define ICON_MS_HIDE \"\\xee\\xbe\\x9e\"\t// U+ef9e\n#define ICON_MS_HIDE_IMAGE \"\\xef\\x80\\xa2\"\t// U+f022\n#define ICON_MS_HIDE_SOURCE \"\\xef\\x80\\xa3\"\t// U+f023\n#define ICON_MS_HIGH_DENSITY \"\\xef\\x9e\\x9c\"\t// U+f79c\n#define ICON_MS_HIGH_QUALITY \"\\xee\\x80\\xa4\"\t// U+e024\n#define ICON_MS_HIGH_RES \"\\xef\\x95\\x8b\"\t// U+f54b\n#define ICON_MS_HIGHLIGHT \"\\xee\\x89\\x9f\"\t// U+e25f\n#define ICON_MS_HIGHLIGHT_KEYBOARD_FOCUS \"\\xef\\x94\\x90\"\t// U+f510\n#define ICON_MS_HIGHLIGHT_MOUSE_CURSOR \"\\xef\\x94\\x91\"\t// U+f511\n#define ICON_MS_HIGHLIGHT_OFF \"\\xee\\xa2\\x88\"\t// U+e888\n#define ICON_MS_HIGHLIGHT_TEXT_CURSOR \"\\xef\\x94\\x92\"\t// U+f512\n#define ICON_MS_HIGHLIGHTER_SIZE_1 \"\\xef\\x9d\\xab\"\t// U+f76b\n#define ICON_MS_HIGHLIGHTER_SIZE_2 \"\\xef\\x9d\\xaa\"\t// U+f76a\n#define ICON_MS_HIGHLIGHTER_SIZE_3 \"\\xef\\x9d\\xa9\"\t// U+f769\n#define ICON_MS_HIGHLIGHTER_SIZE_4 \"\\xef\\x9d\\xa8\"\t// U+f768\n#define ICON_MS_HIGHLIGHTER_SIZE_5 \"\\xef\\x9d\\xa7\"\t// U+f767\n#define ICON_MS_HIKING \"\\xee\\x94\\x8a\"\t// U+e50a\n#define ICON_MS_HISTORY \"\\xee\\xa2\\xb3\"\t// U+e8b3\n#define ICON_MS_HISTORY_EDU \"\\xee\\xa8\\xbe\"\t// U+ea3e\n#define ICON_MS_HISTORY_OFF \"\\xef\\x93\\x9a\"\t// U+f4da\n#define ICON_MS_HISTORY_TOGGLE_OFF \"\\xef\\x85\\xbd\"\t// U+f17d\n#define ICON_MS_HIVE \"\\xee\\xaa\\xa6\"\t// U+eaa6\n#define ICON_MS_HLS \"\\xee\\xae\\x8a\"\t// U+eb8a\n#define ICON_MS_HLS_OFF \"\\xee\\xae\\x8c\"\t// U+eb8c\n#define ICON_MS_HOLIDAY_VILLAGE \"\\xee\\x96\\x8a\"\t// U+e58a\n#define ICON_MS_HOME \"\\xee\\xa6\\xb2\"\t// U+e9b2\n#define ICON_MS_HOME_AND_GARDEN \"\\xee\\xbe\\x9f\"\t// U+ef9f\n#define ICON_MS_HOME_APP_LOGO \"\\xee\\x8a\\x95\"\t// U+e295\n#define ICON_MS_HOME_FILLED \"\\xee\\xa6\\xb2\"\t// U+e9b2\n#define ICON_MS_HOME_HEALTH \"\\xee\\x92\\xb9\"\t// U+e4b9\n#define ICON_MS_HOME_IMPROVEMENT_AND_TOOLS \"\\xee\\xbe\\xa0\"\t// U+efa0\n#define ICON_MS_HOME_IOT_DEVICE \"\\xee\\x8a\\x83\"\t// U+e283\n#define ICON_MS_HOME_MAX \"\\xef\\x80\\xa4\"\t// U+f024\n#define ICON_MS_HOME_MAX_DOTS \"\\xee\\xa1\\x89\"\t// U+e849\n#define ICON_MS_HOME_MINI \"\\xef\\x80\\xa5\"\t// U+f025\n#define ICON_MS_HOME_PIN \"\\xef\\x85\\x8d\"\t// U+f14d\n#define ICON_MS_HOME_REPAIR_SERVICE \"\\xef\\x84\\x80\"\t// U+f100\n#define ICON_MS_HOME_SPEAKER \"\\xef\\x84\\x9c\"\t// U+f11c\n#define ICON_MS_HOME_STORAGE \"\\xef\\xa1\\xac\"\t// U+f86c\n#define ICON_MS_HOME_WORK \"\\xef\\x80\\xb0\"\t// U+f030\n#define ICON_MS_HORIZONTAL_DISTRIBUTE \"\\xee\\x80\\x94\"\t// U+e014\n#define ICON_MS_HORIZONTAL_RULE \"\\xef\\x84\\x88\"\t// U+f108\n#define ICON_MS_HORIZONTAL_SPLIT \"\\xee\\xa5\\x87\"\t// U+e947\n#define ICON_MS_HOT_TUB \"\\xee\\xad\\x86\"\t// U+eb46\n#define ICON_MS_HOTEL \"\\xee\\x95\\x89\"\t// U+e549\n#define ICON_MS_HOTEL_CLASS \"\\xee\\x9d\\x83\"\t// U+e743\n#define ICON_MS_HOURGLASS \"\\xee\\xaf\\xbf\"\t// U+ebff\n#define ICON_MS_HOURGLASS_BOTTOM \"\\xee\\xa9\\x9c\"\t// U+ea5c\n#define ICON_MS_HOURGLASS_DISABLED \"\\xee\\xbd\\x93\"\t// U+ef53\n#define ICON_MS_HOURGLASS_EMPTY \"\\xee\\xa2\\x8b\"\t// U+e88b\n#define ICON_MS_HOURGLASS_FULL \"\\xee\\xa2\\x8c\"\t// U+e88c\n#define ICON_MS_HOURGLASS_TOP \"\\xee\\xa9\\x9b\"\t// U+ea5b\n#define ICON_MS_HOUSE \"\\xee\\xa9\\x84\"\t// U+ea44\n#define ICON_MS_HOUSE_SIDING \"\\xef\\x88\\x82\"\t// U+f202\n#define ICON_MS_HOUSE_WITH_SHIELD \"\\xee\\x9e\\x86\"\t// U+e786\n#define ICON_MS_HOUSEBOAT \"\\xee\\x96\\x84\"\t// U+e584\n#define ICON_MS_HOUSEHOLD_SUPPLIES \"\\xee\\xbe\\xa1\"\t// U+efa1\n#define ICON_MS_HOW_TO_REG \"\\xee\\x85\\xb4\"\t// U+e174\n#define ICON_MS_HOW_TO_VOTE \"\\xee\\x85\\xb5\"\t// U+e175\n#define ICON_MS_HR_RESTING \"\\xef\\x9a\\xba\"\t// U+f6ba\n#define ICON_MS_HTML \"\\xee\\xad\\xbe\"\t// U+eb7e\n#define ICON_MS_HTTP \"\\xee\\xa4\\x82\"\t// U+e902\n#define ICON_MS_HTTPS \"\\xee\\xa2\\x99\"\t// U+e899\n#define ICON_MS_HUB \"\\xee\\xa7\\xb4\"\t// U+e9f4\n#define ICON_MS_HUMERUS \"\\xef\\xa2\\x95\"\t// U+f895\n#define ICON_MS_HUMERUS_ALT \"\\xef\\xa2\\x96\"\t// U+f896\n#define ICON_MS_HUMIDITY_HIGH \"\\xef\\x85\\xa3\"\t// U+f163\n#define ICON_MS_HUMIDITY_INDOOR \"\\xef\\x95\\x98\"\t// U+f558\n#define ICON_MS_HUMIDITY_LOW \"\\xef\\x85\\xa4\"\t// U+f164\n#define ICON_MS_HUMIDITY_MID \"\\xef\\x85\\xa5\"\t// U+f165\n#define ICON_MS_HUMIDITY_PERCENTAGE \"\\xef\\xa1\\xbe\"\t// U+f87e\n#define ICON_MS_HVAC \"\\xef\\x84\\x8e\"\t// U+f10e\n#define ICON_MS_ICE_SKATING \"\\xee\\x94\\x8b\"\t// U+e50b\n#define ICON_MS_ICECREAM \"\\xee\\xa9\\xa9\"\t// U+ea69\n#define ICON_MS_ID_CARD \"\\xef\\x93\\x8a\"\t// U+f4ca\n#define ICON_MS_IFL \"\\xee\\x80\\xa5\"\t// U+e025\n#define ICON_MS_IFRAME \"\\xef\\x9c\\x9b\"\t// U+f71b\n#define ICON_MS_IFRAME_OFF \"\\xef\\x9c\\x9c\"\t// U+f71c\n#define ICON_MS_IMAGE \"\\xee\\x8f\\xb4\"\t// U+e3f4\n#define ICON_MS_IMAGE_ASPECT_RATIO \"\\xee\\x8f\\xb5\"\t// U+e3f5\n#define ICON_MS_IMAGE_NOT_SUPPORTED \"\\xef\\x84\\x96\"\t// U+f116\n#define ICON_MS_IMAGE_SEARCH \"\\xee\\x90\\xbf\"\t// U+e43f\n#define ICON_MS_IMAGESEARCH_ROLLER \"\\xee\\xa6\\xb4\"\t// U+e9b4\n#define ICON_MS_IMAGESMODE \"\\xee\\xbe\\xa2\"\t// U+efa2\n#define ICON_MS_IMMUNOLOGY \"\\xee\\x83\\xbb\"\t// U+e0fb\n#define ICON_MS_IMPORT_CONTACTS \"\\xee\\x83\\xa0\"\t// U+e0e0\n#define ICON_MS_IMPORT_EXPORT \"\\xee\\xa3\\x95\"\t// U+e8d5\n#define ICON_MS_IMPORTANT_DEVICES \"\\xee\\xa4\\x92\"\t// U+e912\n#define ICON_MS_IN_HOME_MODE \"\\xee\\xa0\\xb3\"\t// U+e833\n#define ICON_MS_INACTIVE_ORDER \"\\xee\\x83\\xbc\"\t// U+e0fc\n#define ICON_MS_INBOX \"\\xee\\x85\\x96\"\t// U+e156\n#define ICON_MS_INBOX_CUSTOMIZE \"\\xef\\xa1\\x99\"\t// U+f859\n#define ICON_MS_INCOMPLETE_CIRCLE \"\\xee\\x9e\\x9b\"\t// U+e79b\n#define ICON_MS_INDETERMINATE_CHECK_BOX \"\\xee\\xa4\\x89\"\t// U+e909\n#define ICON_MS_INDETERMINATE_QUESTION_BOX \"\\xef\\x95\\xad\"\t// U+f56d\n#define ICON_MS_INFO \"\\xee\\xa2\\x8e\"\t// U+e88e\n#define ICON_MS_INFO_I \"\\xef\\x96\\x9b\"\t// U+f59b\n#define ICON_MS_INFRARED \"\\xef\\xa1\\xbc\"\t// U+f87c\n#define ICON_MS_INK_ERASER \"\\xee\\x9b\\x90\"\t// U+e6d0\n#define ICON_MS_INK_ERASER_OFF \"\\xee\\x9f\\xa3\"\t// U+e7e3\n#define ICON_MS_INK_HIGHLIGHTER \"\\xee\\x9b\\x91\"\t// U+e6d1\n#define ICON_MS_INK_HIGHLIGHTER_MOVE \"\\xef\\x94\\xa4\"\t// U+f524\n#define ICON_MS_INK_MARKER \"\\xee\\x9b\\x92\"\t// U+e6d2\n#define ICON_MS_INK_PEN \"\\xee\\x9b\\x93\"\t// U+e6d3\n#define ICON_MS_INPATIENT \"\\xee\\x83\\xbe\"\t// U+e0fe\n#define ICON_MS_INPUT \"\\xee\\xa2\\x90\"\t// U+e890\n#define ICON_MS_INPUT_CIRCLE \"\\xef\\x9c\\x9a\"\t// U+f71a\n#define ICON_MS_INSERT_CHART \"\\xef\\x83\\x8c\"\t// U+f0cc\n#define ICON_MS_INSERT_CHART_FILLED \"\\xef\\x83\\x8c\"\t// U+f0cc\n#define ICON_MS_INSERT_CHART_OUTLINED \"\\xef\\x83\\x8c\"\t// U+f0cc\n#define ICON_MS_INSERT_COMMENT \"\\xee\\x89\\x8c\"\t// U+e24c\n#define ICON_MS_INSERT_DRIVE_FILE \"\\xee\\x99\\xad\"\t// U+e66d\n#define ICON_MS_INSERT_EMOTICON \"\\xee\\xa8\\xa2\"\t// U+ea22\n#define ICON_MS_INSERT_INVITATION \"\\xee\\xa1\\xb8\"\t// U+e878\n#define ICON_MS_INSERT_LINK \"\\xee\\x89\\x90\"\t// U+e250\n#define ICON_MS_INSERT_PAGE_BREAK \"\\xee\\xab\\x8a\"\t// U+eaca\n#define ICON_MS_INSERT_PHOTO \"\\xee\\x8f\\xb4\"\t// U+e3f4\n#define ICON_MS_INSERT_TEXT \"\\xef\\xa0\\xa7\"\t// U+f827\n#define ICON_MS_INSIGHTS \"\\xef\\x82\\x92\"\t// U+f092\n#define ICON_MS_INSTALL_DESKTOP \"\\xee\\xad\\xb1\"\t// U+eb71\n#define ICON_MS_INSTALL_MOBILE \"\\xee\\xad\\xb2\"\t// U+eb72\n#define ICON_MS_INSTANT_MIX \"\\xee\\x80\\xa6\"\t// U+e026\n#define ICON_MS_INTEGRATION_INSTRUCTIONS \"\\xee\\xbd\\x94\"\t// U+ef54\n#define ICON_MS_INTERACTIVE_SPACE \"\\xef\\x9f\\xbf\"\t// U+f7ff\n#define ICON_MS_INTERESTS \"\\xee\\x9f\\x88\"\t// U+e7c8\n#define ICON_MS_INTERPRETER_MODE \"\\xee\\xa0\\xbb\"\t// U+e83b\n#define ICON_MS_INVENTORY \"\\xee\\x85\\xb9\"\t// U+e179\n#define ICON_MS_INVENTORY_2 \"\\xee\\x86\\xa1\"\t// U+e1a1\n#define ICON_MS_INVERT_COLORS \"\\xee\\xa2\\x91\"\t// U+e891\n#define ICON_MS_INVERT_COLORS_OFF \"\\xee\\x83\\x84\"\t// U+e0c4\n#define ICON_MS_IOS \"\\xee\\x80\\xa7\"\t// U+e027\n#define ICON_MS_IOS_SHARE \"\\xee\\x9a\\xb8\"\t// U+e6b8\n#define ICON_MS_IRON \"\\xee\\x96\\x83\"\t// U+e583\n#define ICON_MS_ISO \"\\xee\\x8f\\xb6\"\t// U+e3f6\n#define ICON_MS_JAMBOARD_KIOSK \"\\xee\\xa6\\xb5\"\t// U+e9b5\n#define ICON_MS_JAVASCRIPT \"\\xee\\xad\\xbc\"\t// U+eb7c\n#define ICON_MS_JOIN \"\\xef\\xa1\\x8f\"\t// U+f84f\n#define ICON_MS_JOIN_FULL \"\\xef\\xa1\\x8f\"\t// U+f84f\n#define ICON_MS_JOIN_INNER \"\\xee\\xab\\xb4\"\t// U+eaf4\n#define ICON_MS_JOIN_LEFT \"\\xee\\xab\\xb2\"\t// U+eaf2\n#define ICON_MS_JOIN_RIGHT \"\\xee\\xab\\xaa\"\t// U+eaea\n#define ICON_MS_JOYSTICK \"\\xef\\x97\\xae\"\t// U+f5ee\n#define ICON_MS_JUMP_TO_ELEMENT \"\\xef\\x9c\\x99\"\t// U+f719\n#define ICON_MS_KAYAKING \"\\xee\\x94\\x8c\"\t// U+e50c\n#define ICON_MS_KEBAB_DINING \"\\xee\\xa1\\x82\"\t// U+e842\n#define ICON_MS_KEEP \"\\xef\\x80\\xa6\"\t// U+f026\n#define ICON_MS_KEEP_OFF \"\\xee\\x9b\\xb9\"\t// U+e6f9\n#define ICON_MS_KEEP_PIN \"\\xef\\x80\\xa6\"\t// U+f026\n#define ICON_MS_KEEP_PUBLIC \"\\xef\\x95\\xaf\"\t// U+f56f\n#define ICON_MS_KETTLE \"\\xee\\x8a\\xb9\"\t// U+e2b9\n#define ICON_MS_KEY \"\\xee\\x9c\\xbc\"\t// U+e73c\n#define ICON_MS_KEY_OFF \"\\xee\\xae\\x84\"\t// U+eb84\n#define ICON_MS_KEY_VERTICAL \"\\xef\\x94\\x9a\"\t// U+f51a\n#define ICON_MS_KEY_VISUALIZER \"\\xef\\x86\\x99\"\t// U+f199\n#define ICON_MS_KEYBOARD \"\\xee\\x8c\\x92\"\t// U+e312\n#define ICON_MS_KEYBOARD_ALT \"\\xef\\x80\\xa8\"\t// U+f028\n#define ICON_MS_KEYBOARD_ARROW_DOWN \"\\xee\\x8c\\x93\"\t// U+e313\n#define ICON_MS_KEYBOARD_ARROW_LEFT \"\\xee\\x8c\\x94\"\t// U+e314\n#define ICON_MS_KEYBOARD_ARROW_RIGHT \"\\xee\\x8c\\x95\"\t// U+e315\n#define ICON_MS_KEYBOARD_ARROW_UP \"\\xee\\x8c\\x96\"\t// U+e316\n#define ICON_MS_KEYBOARD_BACKSPACE \"\\xee\\x8c\\x97\"\t// U+e317\n#define ICON_MS_KEYBOARD_CAPSLOCK \"\\xee\\x8c\\x98\"\t// U+e318\n#define ICON_MS_KEYBOARD_CAPSLOCK_BADGE \"\\xef\\x9f\\x9e\"\t// U+f7de\n#define ICON_MS_KEYBOARD_COMMAND_KEY \"\\xee\\xab\\xa7\"\t// U+eae7\n#define ICON_MS_KEYBOARD_CONTROL_KEY \"\\xee\\xab\\xa6\"\t// U+eae6\n#define ICON_MS_KEYBOARD_DOUBLE_ARROW_DOWN \"\\xee\\xab\\x90\"\t// U+ead0\n#define ICON_MS_KEYBOARD_DOUBLE_ARROW_LEFT \"\\xee\\xab\\x83\"\t// U+eac3\n#define ICON_MS_KEYBOARD_DOUBLE_ARROW_RIGHT \"\\xee\\xab\\x89\"\t// U+eac9\n#define ICON_MS_KEYBOARD_DOUBLE_ARROW_UP \"\\xee\\xab\\x8f\"\t// U+eacf\n#define ICON_MS_KEYBOARD_EXTERNAL_INPUT \"\\xef\\x9f\\x9d\"\t// U+f7dd\n#define ICON_MS_KEYBOARD_FULL \"\\xef\\x9f\\x9c\"\t// U+f7dc\n#define ICON_MS_KEYBOARD_HIDE \"\\xee\\x8c\\x9a\"\t// U+e31a\n#define ICON_MS_KEYBOARD_KEYS \"\\xef\\x99\\xbb\"\t// U+f67b\n#define ICON_MS_KEYBOARD_LOCK \"\\xef\\x92\\x92\"\t// U+f492\n#define ICON_MS_KEYBOARD_LOCK_OFF \"\\xef\\x92\\x91\"\t// U+f491\n#define ICON_MS_KEYBOARD_OFF \"\\xef\\x99\\xba\"\t// U+f67a\n#define ICON_MS_KEYBOARD_ONSCREEN \"\\xef\\x9f\\x9b\"\t// U+f7db\n#define ICON_MS_KEYBOARD_OPTION_KEY \"\\xee\\xab\\xa8\"\t// U+eae8\n#define ICON_MS_KEYBOARD_PREVIOUS_LANGUAGE \"\\xef\\x9f\\x9a\"\t// U+f7da\n#define ICON_MS_KEYBOARD_RETURN \"\\xee\\x8c\\x9b\"\t// U+e31b\n#define ICON_MS_KEYBOARD_TAB \"\\xee\\x8c\\x9c\"\t// U+e31c\n#define ICON_MS_KEYBOARD_TAB_RTL \"\\xee\\xb1\\xb3\"\t// U+ec73\n#define ICON_MS_KEYBOARD_VOICE \"\\xee\\x8c\\x9d\"\t// U+e31d\n#define ICON_MS_KID_STAR \"\\xef\\x94\\xa6\"\t// U+f526\n#define ICON_MS_KING_BED \"\\xee\\xa9\\x85\"\t// U+ea45\n#define ICON_MS_KITCHEN \"\\xee\\xad\\x87\"\t// U+eb47\n#define ICON_MS_KITESURFING \"\\xee\\x94\\x8d\"\t// U+e50d\n#define ICON_MS_LAB_PANEL \"\\xee\\x84\\x83\"\t// U+e103\n#define ICON_MS_LAB_PROFILE \"\\xee\\x84\\x84\"\t// U+e104\n#define ICON_MS_LAB_RESEARCH \"\\xef\\xa0\\x8b\"\t// U+f80b\n#define ICON_MS_LABEL \"\\xee\\xa2\\x93\"\t// U+e893\n#define ICON_MS_LABEL_IMPORTANT \"\\xee\\xa5\\x88\"\t// U+e948\n#define ICON_MS_LABEL_IMPORTANT_OUTLINE \"\\xee\\xa5\\x88\"\t// U+e948\n#define ICON_MS_LABEL_OFF \"\\xee\\xa6\\xb6\"\t// U+e9b6\n#define ICON_MS_LABEL_OUTLINE \"\\xee\\xa2\\x93\"\t// U+e893\n#define ICON_MS_LABS \"\\xee\\x84\\x85\"\t// U+e105\n#define ICON_MS_LAN \"\\xee\\xac\\xaf\"\t// U+eb2f\n#define ICON_MS_LANDSCAPE \"\\xee\\x95\\xa4\"\t// U+e564\n#define ICON_MS_LANDSCAPE_2 \"\\xef\\x93\\x84\"\t// U+f4c4\n#define ICON_MS_LANDSCAPE_2_OFF \"\\xef\\x93\\x83\"\t// U+f4c3\n#define ICON_MS_LANDSLIDE \"\\xee\\xaf\\x97\"\t// U+ebd7\n#define ICON_MS_LANGUAGE \"\\xee\\xa2\\x94\"\t// U+e894\n#define ICON_MS_LANGUAGE_CHINESE_ARRAY \"\\xef\\x9d\\xa6\"\t// U+f766\n#define ICON_MS_LANGUAGE_CHINESE_CANGJIE \"\\xef\\x9d\\xa5\"\t// U+f765\n#define ICON_MS_LANGUAGE_CHINESE_DAYI \"\\xef\\x9d\\xa4\"\t// U+f764\n#define ICON_MS_LANGUAGE_CHINESE_PINYIN \"\\xef\\x9d\\xa3\"\t// U+f763\n#define ICON_MS_LANGUAGE_CHINESE_QUICK \"\\xef\\x9d\\xa2\"\t// U+f762\n#define ICON_MS_LANGUAGE_CHINESE_WUBI \"\\xef\\x9d\\xa1\"\t// U+f761\n#define ICON_MS_LANGUAGE_FRENCH \"\\xef\\x9d\\xa0\"\t// U+f760\n#define ICON_MS_LANGUAGE_GB_ENGLISH \"\\xef\\x9d\\x9f\"\t// U+f75f\n#define ICON_MS_LANGUAGE_INTERNATIONAL \"\\xef\\x9d\\x9e\"\t// U+f75e\n#define ICON_MS_LANGUAGE_JAPANESE_KANA \"\\xef\\x94\\x93\"\t// U+f513\n#define ICON_MS_LANGUAGE_KOREAN_LATIN \"\\xef\\x9d\\x9d\"\t// U+f75d\n#define ICON_MS_LANGUAGE_PINYIN \"\\xef\\x9d\\x9c\"\t// U+f75c\n#define ICON_MS_LANGUAGE_SPANISH \"\\xef\\x97\\xa9\"\t// U+f5e9\n#define ICON_MS_LANGUAGE_US \"\\xef\\x9d\\x99\"\t// U+f759\n#define ICON_MS_LANGUAGE_US_COLEMAK \"\\xef\\x9d\\x9b\"\t// U+f75b\n#define ICON_MS_LANGUAGE_US_DVORAK \"\\xef\\x9d\\x9a\"\t// U+f75a\n#define ICON_MS_LAPS \"\\xef\\x9a\\xb9\"\t// U+f6b9\n#define ICON_MS_LAPTOP \"\\xee\\x8c\\x9e\"\t// U+e31e\n#define ICON_MS_LAPTOP_CHROMEBOOK \"\\xee\\x8c\\x9f\"\t// U+e31f\n#define ICON_MS_LAPTOP_MAC \"\\xee\\x8c\\xa0\"\t// U+e320\n#define ICON_MS_LAPTOP_WINDOWS \"\\xee\\x8c\\xa1\"\t// U+e321\n#define ICON_MS_LASSO_SELECT \"\\xee\\xac\\x83\"\t// U+eb03\n#define ICON_MS_LAST_PAGE \"\\xee\\x97\\x9d\"\t// U+e5dd\n#define ICON_MS_LAUNCH \"\\xee\\xa2\\x9e\"\t// U+e89e\n#define ICON_MS_LAUNDRY \"\\xee\\x8a\\xa8\"\t// U+e2a8\n#define ICON_MS_LAYERS \"\\xee\\x94\\xbb\"\t// U+e53b\n#define ICON_MS_LAYERS_CLEAR \"\\xee\\x94\\xbc\"\t// U+e53c\n#define ICON_MS_LDA \"\\xee\\x84\\x86\"\t// U+e106\n#define ICON_MS_LEADERBOARD \"\\xef\\x88\\x8c\"\t// U+f20c\n#define ICON_MS_LEAK_ADD \"\\xee\\x8f\\xb8\"\t// U+e3f8\n#define ICON_MS_LEAK_REMOVE \"\\xee\\x8f\\xb9\"\t// U+e3f9\n#define ICON_MS_LEFT_CLICK \"\\xef\\x9c\\x98\"\t// U+f718\n#define ICON_MS_LEFT_PANEL_CLOSE \"\\xef\\x9c\\x97\"\t// U+f717\n#define ICON_MS_LEFT_PANEL_OPEN \"\\xef\\x9c\\x96\"\t// U+f716\n#define ICON_MS_LEGEND_TOGGLE \"\\xef\\x84\\x9b\"\t// U+f11b\n#define ICON_MS_LENS \"\\xee\\x8f\\xba\"\t// U+e3fa\n#define ICON_MS_LENS_BLUR \"\\xef\\x80\\xa9\"\t// U+f029\n#define ICON_MS_LETTER_SWITCH \"\\xef\\x9d\\x98\"\t// U+f758\n#define ICON_MS_LIBRARY_ADD \"\\xee\\x80\\xbc\"\t// U+e03c\n#define ICON_MS_LIBRARY_ADD_CHECK \"\\xee\\xa6\\xb7\"\t// U+e9b7\n#define ICON_MS_LIBRARY_BOOKS \"\\xee\\x80\\xaf\"\t// U+e02f\n#define ICON_MS_LIBRARY_MUSIC \"\\xee\\x80\\xb0\"\t// U+e030\n#define ICON_MS_LICENSE \"\\xee\\xac\\x84\"\t// U+eb04\n#define ICON_MS_LIFT_TO_TALK \"\\xee\\xbe\\xa3\"\t// U+efa3\n#define ICON_MS_LIGHT \"\\xef\\x80\\xaa\"\t// U+f02a\n#define ICON_MS_LIGHT_GROUP \"\\xee\\x8a\\x8b\"\t// U+e28b\n#define ICON_MS_LIGHT_MODE \"\\xee\\x94\\x98\"\t// U+e518\n#define ICON_MS_LIGHT_OFF \"\\xee\\xa6\\xb8\"\t// U+e9b8\n#define ICON_MS_LIGHTBULB \"\\xee\\xa4\\x8f\"\t// U+e90f\n#define ICON_MS_LIGHTBULB_CIRCLE \"\\xee\\xaf\\xbe\"\t// U+ebfe\n#define ICON_MS_LIGHTBULB_OUTLINE \"\\xee\\xa4\\x8f\"\t// U+e90f\n#define ICON_MS_LIGHTNING_STAND \"\\xee\\xbe\\xa4\"\t// U+efa4\n#define ICON_MS_LINE_AXIS \"\\xee\\xaa\\x9a\"\t// U+ea9a\n#define ICON_MS_LINE_CURVE \"\\xef\\x9d\\x97\"\t// U+f757\n#define ICON_MS_LINE_END \"\\xef\\xa0\\xa6\"\t// U+f826\n#define ICON_MS_LINE_END_ARROW \"\\xef\\xa0\\x9d\"\t// U+f81d\n#define ICON_MS_LINE_END_ARROW_NOTCH \"\\xef\\xa0\\x9c\"\t// U+f81c\n#define ICON_MS_LINE_END_CIRCLE \"\\xef\\xa0\\x9b\"\t// U+f81b\n#define ICON_MS_LINE_END_DIAMOND \"\\xef\\xa0\\x9a\"\t// U+f81a\n#define ICON_MS_LINE_END_SQUARE \"\\xef\\xa0\\x99\"\t// U+f819\n#define ICON_MS_LINE_START \"\\xef\\xa0\\xa5\"\t// U+f825\n#define ICON_MS_LINE_START_ARROW \"\\xef\\xa0\\x98\"\t// U+f818\n#define ICON_MS_LINE_START_ARROW_NOTCH \"\\xef\\xa0\\x97\"\t// U+f817\n#define ICON_MS_LINE_START_CIRCLE \"\\xef\\xa0\\x96\"\t// U+f816\n#define ICON_MS_LINE_START_DIAMOND \"\\xef\\xa0\\x95\"\t// U+f815\n#define ICON_MS_LINE_START_SQUARE \"\\xef\\xa0\\x94\"\t// U+f814\n#define ICON_MS_LINE_STYLE \"\\xee\\xa4\\x99\"\t// U+e919\n#define ICON_MS_LINE_WEIGHT \"\\xee\\xa4\\x9a\"\t// U+e91a\n#define ICON_MS_LINEAR_SCALE \"\\xee\\x89\\xa0\"\t// U+e260\n#define ICON_MS_LINK \"\\xee\\x89\\x90\"\t// U+e250\n#define ICON_MS_LINK_OFF \"\\xee\\x85\\xaf\"\t// U+e16f\n#define ICON_MS_LINKED_CAMERA \"\\xee\\x90\\xb8\"\t// U+e438\n#define ICON_MS_LINKED_SERVICES \"\\xef\\x94\\xb5\"\t// U+f535\n#define ICON_MS_LIQUOR \"\\xee\\xa9\\xa0\"\t// U+ea60\n#define ICON_MS_LIST \"\\xee\\xa2\\x96\"\t// U+e896\n#define ICON_MS_LIST_ALT \"\\xee\\x83\\xae\"\t// U+e0ee\n#define ICON_MS_LIST_ALT_ADD \"\\xef\\x9d\\x96\"\t// U+f756\n#define ICON_MS_LISTS \"\\xee\\xa6\\xb9\"\t// U+e9b9\n#define ICON_MS_LIVE_HELP \"\\xee\\x83\\x86\"\t// U+e0c6\n#define ICON_MS_LIVE_TV \"\\xee\\x98\\xba\"\t// U+e63a\n#define ICON_MS_LIVING \"\\xef\\x80\\xab\"\t// U+f02b\n#define ICON_MS_LOCAL_ACTIVITY \"\\xee\\x95\\x93\"\t// U+e553\n#define ICON_MS_LOCAL_AIRPORT \"\\xee\\x94\\xbd\"\t// U+e53d\n#define ICON_MS_LOCAL_ATM \"\\xee\\x94\\xbe\"\t// U+e53e\n#define ICON_MS_LOCAL_BAR \"\\xee\\x95\\x80\"\t// U+e540\n#define ICON_MS_LOCAL_CAFE \"\\xee\\xad\\x84\"\t// U+eb44\n#define ICON_MS_LOCAL_CAR_WASH \"\\xee\\x95\\x82\"\t// U+e542\n#define ICON_MS_LOCAL_CONVENIENCE_STORE \"\\xee\\x95\\x83\"\t// U+e543\n#define ICON_MS_LOCAL_DINING \"\\xee\\x95\\xa1\"\t// U+e561\n#define ICON_MS_LOCAL_DRINK \"\\xee\\x95\\x84\"\t// U+e544\n#define ICON_MS_LOCAL_FIRE_DEPARTMENT \"\\xee\\xbd\\x95\"\t// U+ef55\n#define ICON_MS_LOCAL_FLORIST \"\\xee\\x95\\x85\"\t// U+e545\n#define ICON_MS_LOCAL_GAS_STATION \"\\xee\\x95\\x86\"\t// U+e546\n#define ICON_MS_LOCAL_GROCERY_STORE \"\\xee\\xa3\\x8c\"\t// U+e8cc\n#define ICON_MS_LOCAL_HOSPITAL \"\\xee\\x95\\x88\"\t// U+e548\n#define ICON_MS_LOCAL_HOTEL \"\\xee\\x95\\x89\"\t// U+e549\n#define ICON_MS_LOCAL_LAUNDRY_SERVICE \"\\xee\\x95\\x8a\"\t// U+e54a\n#define ICON_MS_LOCAL_LIBRARY \"\\xee\\x95\\x8b\"\t// U+e54b\n#define ICON_MS_LOCAL_MALL \"\\xee\\x95\\x8c\"\t// U+e54c\n#define ICON_MS_LOCAL_MOVIES \"\\xee\\xa3\\x9a\"\t// U+e8da\n#define ICON_MS_LOCAL_OFFER \"\\xef\\x81\\x9b\"\t// U+f05b\n#define ICON_MS_LOCAL_PARKING \"\\xee\\x95\\x8f\"\t// U+e54f\n#define ICON_MS_LOCAL_PHARMACY \"\\xee\\x95\\x90\"\t// U+e550\n#define ICON_MS_LOCAL_PHONE \"\\xef\\x83\\x94\"\t// U+f0d4\n#define ICON_MS_LOCAL_PIZZA \"\\xee\\x95\\x92\"\t// U+e552\n#define ICON_MS_LOCAL_PLAY \"\\xee\\x95\\x93\"\t// U+e553\n#define ICON_MS_LOCAL_POLICE \"\\xee\\xbd\\x96\"\t// U+ef56\n#define ICON_MS_LOCAL_POST_OFFICE \"\\xee\\x95\\x94\"\t// U+e554\n#define ICON_MS_LOCAL_PRINTSHOP \"\\xee\\xa2\\xad\"\t// U+e8ad\n#define ICON_MS_LOCAL_SEE \"\\xee\\x95\\x97\"\t// U+e557\n#define ICON_MS_LOCAL_SHIPPING \"\\xee\\x95\\x98\"\t// U+e558\n#define ICON_MS_LOCAL_TAXI \"\\xee\\x95\\x99\"\t// U+e559\n#define ICON_MS_LOCATION_AUTOMATION \"\\xef\\x85\\x8f\"\t// U+f14f\n#define ICON_MS_LOCATION_AWAY \"\\xef\\x85\\x90\"\t// U+f150\n#define ICON_MS_LOCATION_CHIP \"\\xef\\xa1\\x90\"\t// U+f850\n#define ICON_MS_LOCATION_CITY \"\\xee\\x9f\\xb1\"\t// U+e7f1\n#define ICON_MS_LOCATION_DISABLED \"\\xee\\x86\\xb6\"\t// U+e1b6\n#define ICON_MS_LOCATION_HOME \"\\xef\\x85\\x92\"\t// U+f152\n#define ICON_MS_LOCATION_OFF \"\\xee\\x83\\x87\"\t// U+e0c7\n#define ICON_MS_LOCATION_ON \"\\xef\\x87\\x9b\"\t// U+f1db\n#define ICON_MS_LOCATION_PIN \"\\xef\\x87\\x9b\"\t// U+f1db\n#define ICON_MS_LOCATION_SEARCHING \"\\xee\\x86\\xb7\"\t// U+e1b7\n#define ICON_MS_LOCATOR_TAG \"\\xef\\xa3\\x81\"\t// U+f8c1\n#define ICON_MS_LOCK \"\\xee\\xa2\\x99\"\t// U+e899\n#define ICON_MS_LOCK_CLOCK \"\\xee\\xbd\\x97\"\t// U+ef57\n#define ICON_MS_LOCK_OPEN \"\\xee\\xa2\\x98\"\t// U+e898\n#define ICON_MS_LOCK_OPEN_RIGHT \"\\xef\\x99\\x96\"\t// U+f656\n#define ICON_MS_LOCK_OUTLINE \"\\xee\\xa2\\x99\"\t// U+e899\n#define ICON_MS_LOCK_PERSON \"\\xef\\xa3\\xb3\"\t// U+f8f3\n#define ICON_MS_LOCK_RESET \"\\xee\\xab\\x9e\"\t// U+eade\n#define ICON_MS_LOGIN \"\\xee\\xa9\\xb7\"\t// U+ea77\n#define ICON_MS_LOGO_DEV \"\\xee\\xab\\x96\"\t// U+ead6\n#define ICON_MS_LOGOUT \"\\xee\\xa6\\xba\"\t// U+e9ba\n#define ICON_MS_LOOKS \"\\xee\\x8f\\xbc\"\t// U+e3fc\n#define ICON_MS_LOOKS_3 \"\\xee\\x8f\\xbb\"\t// U+e3fb\n#define ICON_MS_LOOKS_4 \"\\xee\\x8f\\xbd\"\t// U+e3fd\n#define ICON_MS_LOOKS_5 \"\\xee\\x8f\\xbe\"\t// U+e3fe\n#define ICON_MS_LOOKS_6 \"\\xee\\x8f\\xbf\"\t// U+e3ff\n#define ICON_MS_LOOKS_ONE \"\\xee\\x90\\x80\"\t// U+e400\n#define ICON_MS_LOOKS_TWO \"\\xee\\x90\\x81\"\t// U+e401\n#define ICON_MS_LOOP \"\\xee\\xa1\\xa3\"\t// U+e863\n#define ICON_MS_LOUPE \"\\xee\\x90\\x82\"\t// U+e402\n#define ICON_MS_LOW_DENSITY \"\\xef\\x9e\\x9b\"\t// U+f79b\n#define ICON_MS_LOW_PRIORITY \"\\xee\\x85\\xad\"\t// U+e16d\n#define ICON_MS_LOWERCASE \"\\xef\\x92\\x8a\"\t// U+f48a\n#define ICON_MS_LOYALTY \"\\xee\\xa2\\x9a\"\t// U+e89a\n#define ICON_MS_LTE_MOBILEDATA \"\\xef\\x80\\xac\"\t// U+f02c\n#define ICON_MS_LTE_MOBILEDATA_BADGE \"\\xef\\x9f\\x99\"\t// U+f7d9\n#define ICON_MS_LTE_PLUS_MOBILEDATA \"\\xef\\x80\\xad\"\t// U+f02d\n#define ICON_MS_LTE_PLUS_MOBILEDATA_BADGE \"\\xef\\x9f\\x98\"\t// U+f7d8\n#define ICON_MS_LUGGAGE \"\\xef\\x88\\xb5\"\t// U+f235\n#define ICON_MS_LUNCH_DINING \"\\xee\\xa9\\xa1\"\t// U+ea61\n#define ICON_MS_LYRICS \"\\xee\\xb0\\x8b\"\t// U+ec0b\n#define ICON_MS_MACRO_AUTO \"\\xef\\x9b\\xb2\"\t// U+f6f2\n#define ICON_MS_MACRO_OFF \"\\xef\\xa3\\x92\"\t// U+f8d2\n#define ICON_MS_MAGIC_BUTTON \"\\xef\\x84\\xb6\"\t// U+f136\n#define ICON_MS_MAGIC_EXCHANGE \"\\xef\\x9f\\xb4\"\t// U+f7f4\n#define ICON_MS_MAGIC_TETHER \"\\xef\\x9f\\x97\"\t// U+f7d7\n#define ICON_MS_MAGNIFICATION_LARGE \"\\xef\\xa0\\xbd\"\t// U+f83d\n#define ICON_MS_MAGNIFICATION_SMALL \"\\xef\\xa0\\xbc\"\t// U+f83c\n#define ICON_MS_MAGNIFY_DOCKED \"\\xef\\x9f\\x96\"\t// U+f7d6\n#define ICON_MS_MAGNIFY_FULLSCREEN \"\\xef\\x9f\\x95\"\t// U+f7d5\n#define ICON_MS_MAIL \"\\xee\\x85\\x99\"\t// U+e159\n#define ICON_MS_MAIL_LOCK \"\\xee\\xb0\\x8a\"\t// U+ec0a\n#define ICON_MS_MAIL_OFF \"\\xef\\x92\\x8b\"\t// U+f48b\n#define ICON_MS_MAIL_OUTLINE \"\\xee\\x85\\x99\"\t// U+e159\n#define ICON_MS_MALE \"\\xee\\x96\\x8e\"\t// U+e58e\n#define ICON_MS_MAN \"\\xee\\x93\\xab\"\t// U+e4eb\n#define ICON_MS_MAN_2 \"\\xef\\xa3\\xa1\"\t// U+f8e1\n#define ICON_MS_MAN_3 \"\\xef\\xa3\\xa2\"\t// U+f8e2\n#define ICON_MS_MAN_4 \"\\xef\\xa3\\xa3\"\t// U+f8e3\n#define ICON_MS_MANAGE_ACCOUNTS \"\\xef\\x80\\xae\"\t// U+f02e\n#define ICON_MS_MANAGE_HISTORY \"\\xee\\xaf\\xa7\"\t// U+ebe7\n#define ICON_MS_MANAGE_SEARCH \"\\xef\\x80\\xaf\"\t// U+f02f\n#define ICON_MS_MANGA \"\\xef\\x97\\xa3\"\t// U+f5e3\n#define ICON_MS_MANUFACTURING \"\\xee\\x9c\\xa6\"\t// U+e726\n#define ICON_MS_MAP \"\\xee\\x95\\x9b\"\t// U+e55b\n#define ICON_MS_MAPS_HOME_WORK \"\\xef\\x80\\xb0\"\t// U+f030\n#define ICON_MS_MAPS_UGC \"\\xee\\xbd\\x98\"\t// U+ef58\n#define ICON_MS_MARGIN \"\\xee\\xa6\\xbb\"\t// U+e9bb\n#define ICON_MS_MARK_AS_UNREAD \"\\xee\\xa6\\xbc\"\t// U+e9bc\n#define ICON_MS_MARK_CHAT_READ \"\\xef\\x86\\x8b\"\t// U+f18b\n#define ICON_MS_MARK_CHAT_UNREAD \"\\xef\\x86\\x89\"\t// U+f189\n#define ICON_MS_MARK_EMAIL_READ \"\\xef\\x86\\x8c\"\t// U+f18c\n#define ICON_MS_MARK_EMAIL_UNREAD \"\\xef\\x86\\x8a\"\t// U+f18a\n#define ICON_MS_MARK_UNREAD_CHAT_ALT \"\\xee\\xae\\x9d\"\t// U+eb9d\n#define ICON_MS_MARKDOWN \"\\xef\\x95\\x92\"\t// U+f552\n#define ICON_MS_MARKDOWN_COPY \"\\xef\\x95\\x93\"\t// U+f553\n#define ICON_MS_MARKDOWN_PASTE \"\\xef\\x95\\x94\"\t// U+f554\n#define ICON_MS_MARKUNREAD \"\\xee\\x85\\x99\"\t// U+e159\n#define ICON_MS_MARKUNREAD_MAILBOX \"\\xee\\xa2\\x9b\"\t// U+e89b\n#define ICON_MS_MASKED_TRANSITIONS \"\\xee\\x9c\\xae\"\t// U+e72e\n#define ICON_MS_MASKS \"\\xef\\x88\\x98\"\t// U+f218\n#define ICON_MS_MATCH_CASE \"\\xef\\x9b\\xb1\"\t// U+f6f1\n#define ICON_MS_MATCH_WORD \"\\xef\\x9b\\xb0\"\t// U+f6f0\n#define ICON_MS_MATTER \"\\xee\\xa4\\x87\"\t// U+e907\n#define ICON_MS_MAXIMIZE \"\\xee\\xa4\\xb0\"\t// U+e930\n#define ICON_MS_MEASURING_TAPE \"\\xef\\x9a\\xaf\"\t// U+f6af\n#define ICON_MS_MEDIA_BLUETOOTH_OFF \"\\xef\\x80\\xb1\"\t// U+f031\n#define ICON_MS_MEDIA_BLUETOOTH_ON \"\\xef\\x80\\xb2\"\t// U+f032\n#define ICON_MS_MEDIA_LINK \"\\xef\\xa0\\xbf\"\t// U+f83f\n#define ICON_MS_MEDIA_OUTPUT \"\\xef\\x93\\xb2\"\t// U+f4f2\n#define ICON_MS_MEDIA_OUTPUT_OFF \"\\xef\\x93\\xb3\"\t// U+f4f3\n#define ICON_MS_MEDIATION \"\\xee\\xbe\\xa7\"\t// U+efa7\n#define ICON_MS_MEDICAL_INFORMATION \"\\xee\\xaf\\xad\"\t// U+ebed\n#define ICON_MS_MEDICAL_MASK \"\\xef\\xa0\\x8a\"\t// U+f80a\n#define ICON_MS_MEDICAL_SERVICES \"\\xef\\x84\\x89\"\t// U+f109\n#define ICON_MS_MEDICATION \"\\xef\\x80\\xb3\"\t// U+f033\n#define ICON_MS_MEDICATION_LIQUID \"\\xee\\xaa\\x87\"\t// U+ea87\n#define ICON_MS_MEETING_ROOM \"\\xee\\xad\\x8f\"\t// U+eb4f\n#define ICON_MS_MEMORY \"\\xee\\x8c\\xa2\"\t// U+e322\n#define ICON_MS_MEMORY_ALT \"\\xef\\x9e\\xa3\"\t// U+f7a3\n#define ICON_MS_MENSTRUAL_HEALTH \"\\xef\\x9b\\xa1\"\t// U+f6e1\n#define ICON_MS_MENU \"\\xee\\x97\\x92\"\t// U+e5d2\n#define ICON_MS_MENU_BOOK \"\\xee\\xa8\\x99\"\t// U+ea19\n#define ICON_MS_MENU_OPEN \"\\xee\\xa6\\xbd\"\t// U+e9bd\n#define ICON_MS_MERGE \"\\xee\\xae\\x98\"\t// U+eb98\n#define ICON_MS_MERGE_TYPE \"\\xee\\x89\\x92\"\t// U+e252\n#define ICON_MS_MESSAGE \"\\xee\\x83\\x89\"\t// U+e0c9\n#define ICON_MS_METABOLISM \"\\xee\\x84\\x8b\"\t// U+e10b\n#define ICON_MS_MFG_NEST_YALE_LOCK \"\\xef\\x84\\x9d\"\t// U+f11d\n#define ICON_MS_MIC \"\\xee\\x8c\\x9d\"\t// U+e31d\n#define ICON_MS_MIC_DOUBLE \"\\xef\\x97\\x91\"\t// U+f5d1\n#define ICON_MS_MIC_EXTERNAL_OFF \"\\xee\\xbd\\x99\"\t// U+ef59\n#define ICON_MS_MIC_EXTERNAL_ON \"\\xee\\xbd\\x9a\"\t// U+ef5a\n#define ICON_MS_MIC_NONE \"\\xee\\x8c\\x9d\"\t// U+e31d\n#define ICON_MS_MIC_OFF \"\\xee\\x80\\xab\"\t// U+e02b\n#define ICON_MS_MICROBIOLOGY \"\\xee\\x84\\x8c\"\t// U+e10c\n#define ICON_MS_MICROWAVE \"\\xef\\x88\\x84\"\t// U+f204\n#define ICON_MS_MICROWAVE_GEN \"\\xee\\xa1\\x87\"\t// U+e847\n#define ICON_MS_MILITARY_TECH \"\\xee\\xa8\\xbf\"\t// U+ea3f\n#define ICON_MS_MIMO \"\\xee\\xa6\\xbe\"\t// U+e9be\n#define ICON_MS_MIMO_DISCONNECT \"\\xee\\xa6\\xbf\"\t// U+e9bf\n#define ICON_MS_MINDFULNESS \"\\xef\\x9b\\xa0\"\t// U+f6e0\n#define ICON_MS_MINIMIZE \"\\xee\\xa4\\xb1\"\t// U+e931\n#define ICON_MS_MINOR_CRASH \"\\xee\\xaf\\xb1\"\t// U+ebf1\n#define ICON_MS_MINTMARK \"\\xee\\xbe\\xa9\"\t// U+efa9\n#define ICON_MS_MISSED_VIDEO_CALL \"\\xef\\x83\\x8e\"\t// U+f0ce\n#define ICON_MS_MISSED_VIDEO_CALL_FILLED \"\\xef\\x83\\x8e\"\t// U+f0ce\n#define ICON_MS_MISSING_CONTROLLER \"\\xee\\x9c\\x81\"\t// U+e701\n#define ICON_MS_MIST \"\\xee\\x86\\x88\"\t// U+e188\n#define ICON_MS_MITRE \"\\xef\\x95\\x87\"\t// U+f547\n#define ICON_MS_MIXTURE_MED \"\\xee\\x93\\x88\"\t// U+e4c8\n#define ICON_MS_MMS \"\\xee\\x98\\x98\"\t// U+e618\n#define ICON_MS_MOBILE_FRIENDLY \"\\xee\\x88\\x80\"\t// U+e200\n#define ICON_MS_MOBILE_OFF \"\\xee\\x88\\x81\"\t// U+e201\n#define ICON_MS_MOBILE_SCREEN_SHARE \"\\xee\\x83\\xa7\"\t// U+e0e7\n#define ICON_MS_MOBILEDATA_OFF \"\\xef\\x80\\xb4\"\t// U+f034\n#define ICON_MS_MODE \"\\xef\\x82\\x97\"\t// U+f097\n#define ICON_MS_MODE_COMMENT \"\\xee\\x89\\x93\"\t// U+e253\n#define ICON_MS_MODE_COOL \"\\xef\\x85\\xa6\"\t// U+f166\n#define ICON_MS_MODE_COOL_OFF \"\\xef\\x85\\xa7\"\t// U+f167\n#define ICON_MS_MODE_DUAL \"\\xef\\x95\\x97\"\t// U+f557\n#define ICON_MS_MODE_EDIT \"\\xef\\x82\\x97\"\t// U+f097\n#define ICON_MS_MODE_EDIT_OUTLINE \"\\xef\\x82\\x97\"\t// U+f097\n#define ICON_MS_MODE_FAN \"\\xef\\x85\\xa8\"\t// U+f168\n#define ICON_MS_MODE_FAN_OFF \"\\xee\\xb0\\x97\"\t// U+ec17\n#define ICON_MS_MODE_HEAT \"\\xef\\x85\\xaa\"\t// U+f16a\n#define ICON_MS_MODE_HEAT_COOL \"\\xef\\x85\\xab\"\t// U+f16b\n#define ICON_MS_MODE_HEAT_OFF \"\\xef\\x85\\xad\"\t// U+f16d\n#define ICON_MS_MODE_NIGHT \"\\xef\\x80\\xb6\"\t// U+f036\n#define ICON_MS_MODE_OF_TRAVEL \"\\xee\\x9f\\x8e\"\t// U+e7ce\n#define ICON_MS_MODE_OFF_ON \"\\xef\\x85\\xaf\"\t// U+f16f\n#define ICON_MS_MODE_STANDBY \"\\xef\\x80\\xb7\"\t// U+f037\n#define ICON_MS_MODEL_TRAINING \"\\xef\\x83\\x8f\"\t// U+f0cf\n#define ICON_MS_MONETIZATION_ON \"\\xee\\x89\\xa3\"\t// U+e263\n#define ICON_MS_MONEY \"\\xee\\x95\\xbd\"\t// U+e57d\n#define ICON_MS_MONEY_OFF \"\\xef\\x80\\xb8\"\t// U+f038\n#define ICON_MS_MONEY_OFF_CSRED \"\\xef\\x80\\xb8\"\t// U+f038\n#define ICON_MS_MONITOR \"\\xee\\xbd\\x9b\"\t// U+ef5b\n#define ICON_MS_MONITOR_HEART \"\\xee\\xaa\\xa2\"\t// U+eaa2\n#define ICON_MS_MONITOR_WEIGHT \"\\xef\\x80\\xb9\"\t// U+f039\n#define ICON_MS_MONITOR_WEIGHT_GAIN \"\\xef\\x9b\\x9f\"\t// U+f6df\n#define ICON_MS_MONITOR_WEIGHT_LOSS \"\\xef\\x9b\\x9e\"\t// U+f6de\n#define ICON_MS_MONITORING \"\\xef\\x86\\x90\"\t// U+f190\n#define ICON_MS_MONOCHROME_PHOTOS \"\\xee\\x90\\x83\"\t// U+e403\n#define ICON_MS_MOOD \"\\xee\\xa8\\xa2\"\t// U+ea22\n#define ICON_MS_MOOD_BAD \"\\xee\\x9f\\xb3\"\t// U+e7f3\n#define ICON_MS_MOP \"\\xee\\x8a\\x8d\"\t// U+e28d\n#define ICON_MS_MORE \"\\xee\\x98\\x99\"\t// U+e619\n#define ICON_MS_MORE_DOWN \"\\xef\\x86\\x96\"\t// U+f196\n#define ICON_MS_MORE_HORIZ \"\\xee\\x97\\x93\"\t// U+e5d3\n#define ICON_MS_MORE_TIME \"\\xee\\xa9\\x9d\"\t// U+ea5d\n#define ICON_MS_MORE_UP \"\\xef\\x86\\x97\"\t// U+f197\n#define ICON_MS_MORE_VERT \"\\xee\\x97\\x94\"\t// U+e5d4\n#define ICON_MS_MOSQUE \"\\xee\\xaa\\xb2\"\t// U+eab2\n#define ICON_MS_MOTION_BLUR \"\\xef\\x83\\x90\"\t// U+f0d0\n#define ICON_MS_MOTION_MODE \"\\xef\\xa1\\x82\"\t// U+f842\n#define ICON_MS_MOTION_PHOTOS_AUTO \"\\xef\\x80\\xba\"\t// U+f03a\n#define ICON_MS_MOTION_PHOTOS_OFF \"\\xee\\xa7\\x80\"\t// U+e9c0\n#define ICON_MS_MOTION_PHOTOS_ON \"\\xee\\xa7\\x81\"\t// U+e9c1\n#define ICON_MS_MOTION_PHOTOS_PAUSE \"\\xef\\x88\\xa7\"\t// U+f227\n#define ICON_MS_MOTION_PHOTOS_PAUSED \"\\xef\\x88\\xa7\"\t// U+f227\n#define ICON_MS_MOTION_SENSOR_ACTIVE \"\\xee\\x9e\\x92\"\t// U+e792\n#define ICON_MS_MOTION_SENSOR_ALERT \"\\xee\\x9e\\x84\"\t// U+e784\n#define ICON_MS_MOTION_SENSOR_IDLE \"\\xee\\x9e\\x83\"\t// U+e783\n#define ICON_MS_MOTION_SENSOR_URGENT \"\\xee\\x9e\\x8e\"\t// U+e78e\n#define ICON_MS_MOTORCYCLE \"\\xee\\xa4\\x9b\"\t// U+e91b\n#define ICON_MS_MOUNTAIN_FLAG \"\\xef\\x97\\xa2\"\t// U+f5e2\n#define ICON_MS_MOUSE \"\\xee\\x8c\\xa3\"\t// U+e323\n#define ICON_MS_MOUSE_LOCK \"\\xef\\x92\\x90\"\t// U+f490\n#define ICON_MS_MOUSE_LOCK_OFF \"\\xef\\x92\\x8f\"\t// U+f48f\n#define ICON_MS_MOVE \"\\xee\\x9d\\x80\"\t// U+e740\n#define ICON_MS_MOVE_DOWN \"\\xee\\xad\\xa1\"\t// U+eb61\n#define ICON_MS_MOVE_GROUP \"\\xef\\x9c\\x95\"\t// U+f715\n#define ICON_MS_MOVE_ITEM \"\\xef\\x87\\xbf\"\t// U+f1ff\n#define ICON_MS_MOVE_LOCATION \"\\xee\\x9d\\x81\"\t// U+e741\n#define ICON_MS_MOVE_SELECTION_DOWN \"\\xef\\x9c\\x94\"\t// U+f714\n#define ICON_MS_MOVE_SELECTION_LEFT \"\\xef\\x9c\\x93\"\t// U+f713\n#define ICON_MS_MOVE_SELECTION_RIGHT \"\\xef\\x9c\\x92\"\t// U+f712\n#define ICON_MS_MOVE_SELECTION_UP \"\\xef\\x9c\\x91\"\t// U+f711\n#define ICON_MS_MOVE_TO_INBOX \"\\xee\\x85\\xa8\"\t// U+e168\n#define ICON_MS_MOVE_UP \"\\xee\\xad\\xa4\"\t// U+eb64\n#define ICON_MS_MOVED_LOCATION \"\\xee\\x96\\x94\"\t// U+e594\n#define ICON_MS_MOVIE \"\\xee\\x90\\x84\"\t// U+e404\n#define ICON_MS_MOVIE_CREATION \"\\xee\\x90\\x84\"\t// U+e404\n#define ICON_MS_MOVIE_EDIT \"\\xef\\xa1\\x80\"\t// U+f840\n#define ICON_MS_MOVIE_FILTER \"\\xee\\x90\\xba\"\t// U+e43a\n#define ICON_MS_MOVIE_INFO \"\\xee\\x80\\xad\"\t// U+e02d\n#define ICON_MS_MOVIE_OFF \"\\xef\\x92\\x99\"\t// U+f499\n#define ICON_MS_MOVING \"\\xee\\x94\\x81\"\t// U+e501\n#define ICON_MS_MOVING_BEDS \"\\xee\\x9c\\xbd\"\t// U+e73d\n#define ICON_MS_MOVING_MINISTRY \"\\xee\\x9c\\xbe\"\t// U+e73e\n#define ICON_MS_MP \"\\xee\\xa7\\x83\"\t// U+e9c3\n#define ICON_MS_MULTICOOKER \"\\xee\\x8a\\x93\"\t// U+e293\n#define ICON_MS_MULTILINE_CHART \"\\xee\\x9b\\x9f\"\t// U+e6df\n#define ICON_MS_MULTIPLE_STOP \"\\xef\\x86\\xb9\"\t// U+f1b9\n#define ICON_MS_MUSEUM \"\\xee\\xa8\\xb6\"\t// U+ea36\n#define ICON_MS_MUSIC_CAST \"\\xee\\xac\\x9a\"\t// U+eb1a\n#define ICON_MS_MUSIC_NOTE \"\\xee\\x90\\x85\"\t// U+e405\n#define ICON_MS_MUSIC_OFF \"\\xee\\x91\\x80\"\t// U+e440\n#define ICON_MS_MUSIC_VIDEO \"\\xee\\x81\\xa3\"\t// U+e063\n#define ICON_MS_MY_LOCATION \"\\xee\\x95\\x9c\"\t// U+e55c\n#define ICON_MS_MYSTERY \"\\xef\\x97\\xa1\"\t// U+f5e1\n#define ICON_MS_NAT \"\\xee\\xbd\\x9c\"\t// U+ef5c\n#define ICON_MS_NATURE \"\\xee\\x90\\x86\"\t// U+e406\n#define ICON_MS_NATURE_PEOPLE \"\\xee\\x90\\x87\"\t// U+e407\n#define ICON_MS_NAVIGATE_BEFORE \"\\xee\\x90\\x88\"\t// U+e408\n#define ICON_MS_NAVIGATE_NEXT \"\\xee\\x90\\x89\"\t// U+e409\n#define ICON_MS_NAVIGATION \"\\xee\\x95\\x9d\"\t// U+e55d\n#define ICON_MS_NEAR_ME \"\\xee\\x95\\xa9\"\t// U+e569\n#define ICON_MS_NEAR_ME_DISABLED \"\\xef\\x87\\xaf\"\t// U+f1ef\n#define ICON_MS_NEARBY \"\\xee\\x9a\\xb7\"\t// U+e6b7\n#define ICON_MS_NEARBY_ERROR \"\\xef\\x80\\xbb\"\t// U+f03b\n#define ICON_MS_NEARBY_OFF \"\\xef\\x80\\xbc\"\t// U+f03c\n#define ICON_MS_NEPHROLOGY \"\\xee\\x84\\x8d\"\t// U+e10d\n#define ICON_MS_NEST_AUDIO \"\\xee\\xae\\xbf\"\t// U+ebbf\n#define ICON_MS_NEST_CAM_FLOODLIGHT \"\\xef\\xa2\\xb7\"\t// U+f8b7\n#define ICON_MS_NEST_CAM_INDOOR \"\\xef\\x84\\x9e\"\t// U+f11e\n#define ICON_MS_NEST_CAM_IQ \"\\xef\\x84\\x9f\"\t// U+f11f\n#define ICON_MS_NEST_CAM_IQ_OUTDOOR \"\\xef\\x84\\xa0\"\t// U+f120\n#define ICON_MS_NEST_CAM_MAGNET_MOUNT \"\\xef\\xa2\\xb8\"\t// U+f8b8\n#define ICON_MS_NEST_CAM_OUTDOOR \"\\xef\\x84\\xa1\"\t// U+f121\n#define ICON_MS_NEST_CAM_STAND \"\\xef\\xa2\\xb9\"\t// U+f8b9\n#define ICON_MS_NEST_CAM_WALL_MOUNT \"\\xef\\xa2\\xba\"\t// U+f8ba\n#define ICON_MS_NEST_CAM_WIRED_STAND \"\\xee\\xb0\\x96\"\t// U+ec16\n#define ICON_MS_NEST_CLOCK_FARSIGHT_ANALOG \"\\xef\\xa2\\xbb\"\t// U+f8bb\n#define ICON_MS_NEST_CLOCK_FARSIGHT_DIGITAL \"\\xef\\xa2\\xbc\"\t// U+f8bc\n#define ICON_MS_NEST_CONNECT \"\\xef\\x84\\xa2\"\t// U+f122\n#define ICON_MS_NEST_DETECT \"\\xef\\x84\\xa3\"\t// U+f123\n#define ICON_MS_NEST_DISPLAY \"\\xef\\x84\\xa4\"\t// U+f124\n#define ICON_MS_NEST_DISPLAY_MAX \"\\xef\\x84\\xa5\"\t// U+f125\n#define ICON_MS_NEST_DOORBELL_VISITOR \"\\xef\\xa2\\xbd\"\t// U+f8bd\n#define ICON_MS_NEST_ECO_LEAF \"\\xef\\xa2\\xbe\"\t// U+f8be\n#define ICON_MS_NEST_FARSIGHT_WEATHER \"\\xef\\xa2\\xbf\"\t// U+f8bf\n#define ICON_MS_NEST_FOUND_SAVINGS \"\\xef\\xa3\\x80\"\t// U+f8c0\n#define ICON_MS_NEST_GALE_WIFI \"\\xef\\x95\\xb9\"\t// U+f579\n#define ICON_MS_NEST_HEAT_LINK_E \"\\xef\\x84\\xa6\"\t// U+f126\n#define ICON_MS_NEST_HEAT_LINK_GEN_3 \"\\xef\\x84\\xa7\"\t// U+f127\n#define ICON_MS_NEST_HELLO_DOORBELL \"\\xee\\xa0\\xac\"\t// U+e82c\n#define ICON_MS_NEST_LOCATOR_TAG \"\\xef\\xa3\\x81\"\t// U+f8c1\n#define ICON_MS_NEST_MINI \"\\xee\\x9e\\x89\"\t// U+e789\n#define ICON_MS_NEST_MULTI_ROOM \"\\xef\\xa3\\x82\"\t// U+f8c2\n#define ICON_MS_NEST_PROTECT \"\\xee\\x9a\\x8e\"\t// U+e68e\n#define ICON_MS_NEST_REMOTE \"\\xef\\x97\\x9b\"\t// U+f5db\n#define ICON_MS_NEST_REMOTE_COMFORT_SENSOR \"\\xef\\x84\\xaa\"\t// U+f12a\n#define ICON_MS_NEST_SECURE_ALARM \"\\xef\\x84\\xab\"\t// U+f12b\n#define ICON_MS_NEST_SUNBLOCK \"\\xef\\xa3\\x83\"\t// U+f8c3\n#define ICON_MS_NEST_TAG \"\\xef\\xa3\\x81\"\t// U+f8c1\n#define ICON_MS_NEST_THERMOSTAT \"\\xee\\x9a\\x8f\"\t// U+e68f\n#define ICON_MS_NEST_THERMOSTAT_E_EU \"\\xef\\x84\\xad\"\t// U+f12d\n#define ICON_MS_NEST_THERMOSTAT_GEN_3 \"\\xef\\x84\\xae\"\t// U+f12e\n#define ICON_MS_NEST_THERMOSTAT_SENSOR \"\\xef\\x84\\xaf\"\t// U+f12f\n#define ICON_MS_NEST_THERMOSTAT_SENSOR_EU \"\\xef\\x84\\xb0\"\t// U+f130\n#define ICON_MS_NEST_THERMOSTAT_ZIRCONIUM_EU \"\\xef\\x84\\xb1\"\t// U+f131\n#define ICON_MS_NEST_TRUE_RADIANT \"\\xef\\xa3\\x84\"\t// U+f8c4\n#define ICON_MS_NEST_WAKE_ON_APPROACH \"\\xef\\xa3\\x85\"\t// U+f8c5\n#define ICON_MS_NEST_WAKE_ON_PRESS \"\\xef\\xa3\\x86\"\t// U+f8c6\n#define ICON_MS_NEST_WIFI_GALE \"\\xef\\x84\\xb2\"\t// U+f132\n#define ICON_MS_NEST_WIFI_MISTRAL \"\\xef\\x84\\xb3\"\t// U+f133\n#define ICON_MS_NEST_WIFI_POINT \"\\xef\\x84\\xb4\"\t// U+f134\n#define ICON_MS_NEST_WIFI_POINT_VENTO \"\\xef\\x84\\xb4\"\t// U+f134\n#define ICON_MS_NEST_WIFI_PRO \"\\xef\\x95\\xab\"\t// U+f56b\n#define ICON_MS_NEST_WIFI_PRO_2 \"\\xef\\x95\\xaa\"\t// U+f56a\n#define ICON_MS_NEST_WIFI_ROUTER \"\\xef\\x84\\xb3\"\t// U+f133\n#define ICON_MS_NETWORK_CELL \"\\xee\\x86\\xb9\"\t// U+e1b9\n#define ICON_MS_NETWORK_CHECK \"\\xee\\x99\\x80\"\t// U+e640\n#define ICON_MS_NETWORK_INTELLIGENCE_HISTORY \"\\xef\\x97\\xb6\"\t// U+f5f6\n#define ICON_MS_NETWORK_INTELLIGENCE_UPDATE \"\\xef\\x97\\xb5\"\t// U+f5f5\n#define ICON_MS_NETWORK_LOCKED \"\\xee\\x98\\x9a\"\t// U+e61a\n#define ICON_MS_NETWORK_MANAGE \"\\xef\\x9e\\xab\"\t// U+f7ab\n#define ICON_MS_NETWORK_NODE \"\\xef\\x95\\xae\"\t// U+f56e\n#define ICON_MS_NETWORK_PING \"\\xee\\xaf\\x8a\"\t// U+ebca\n#define ICON_MS_NETWORK_WIFI \"\\xee\\x86\\xba\"\t// U+e1ba\n#define ICON_MS_NETWORK_WIFI_1_BAR \"\\xee\\xaf\\xa4\"\t// U+ebe4\n#define ICON_MS_NETWORK_WIFI_1_BAR_LOCKED \"\\xef\\x96\\x8f\"\t// U+f58f\n#define ICON_MS_NETWORK_WIFI_2_BAR \"\\xee\\xaf\\x96\"\t// U+ebd6\n#define ICON_MS_NETWORK_WIFI_2_BAR_LOCKED \"\\xef\\x96\\x8e\"\t// U+f58e\n#define ICON_MS_NETWORK_WIFI_3_BAR \"\\xee\\xaf\\xa1\"\t// U+ebe1\n#define ICON_MS_NETWORK_WIFI_3_BAR_LOCKED \"\\xef\\x96\\x8d\"\t// U+f58d\n#define ICON_MS_NETWORK_WIFI_LOCKED \"\\xef\\x94\\xb2\"\t// U+f532\n#define ICON_MS_NEUROLOGY \"\\xee\\x84\\x8e\"\t// U+e10e\n#define ICON_MS_NEW_LABEL \"\\xee\\x98\\x89\"\t// U+e609\n#define ICON_MS_NEW_RELEASES \"\\xee\\xbd\\xb6\"\t// U+ef76\n#define ICON_MS_NEW_WINDOW \"\\xef\\x9c\\x90\"\t// U+f710\n#define ICON_MS_NEWS \"\\xee\\x80\\xb2\"\t// U+e032\n#define ICON_MS_NEWSMODE \"\\xee\\xbe\\xad\"\t// U+efad\n#define ICON_MS_NEWSPAPER \"\\xee\\xae\\x81\"\t// U+eb81\n#define ICON_MS_NEWSSTAND \"\\xee\\xa7\\x84\"\t// U+e9c4\n#define ICON_MS_NEXT_PLAN \"\\xee\\xbd\\x9d\"\t// U+ef5d\n#define ICON_MS_NEXT_WEEK \"\\xee\\x85\\xaa\"\t// U+e16a\n#define ICON_MS_NFC \"\\xee\\x86\\xbb\"\t// U+e1bb\n#define ICON_MS_NIGHT_SHELTER \"\\xef\\x87\\xb1\"\t// U+f1f1\n#define ICON_MS_NIGHT_SIGHT_AUTO \"\\xef\\x87\\x97\"\t// U+f1d7\n#define ICON_MS_NIGHT_SIGHT_AUTO_OFF \"\\xef\\x87\\xb9\"\t// U+f1f9\n#define ICON_MS_NIGHT_SIGHT_MAX \"\\xef\\x9b\\x83\"\t// U+f6c3\n#define ICON_MS_NIGHTLIFE \"\\xee\\xa9\\xa2\"\t// U+ea62\n#define ICON_MS_NIGHTLIGHT \"\\xef\\x80\\xbd\"\t// U+f03d\n#define ICON_MS_NIGHTLIGHT_ROUND \"\\xef\\x80\\xbd\"\t// U+f03d\n#define ICON_MS_NIGHTS_STAY \"\\xee\\xa9\\x86\"\t// U+ea46\n#define ICON_MS_NO_ACCOUNTS \"\\xef\\x80\\xbe\"\t// U+f03e\n#define ICON_MS_NO_ADULT_CONTENT \"\\xef\\xa3\\xbe\"\t// U+f8fe\n#define ICON_MS_NO_BACKPACK \"\\xef\\x88\\xb7\"\t// U+f237\n#define ICON_MS_NO_CRASH \"\\xee\\xaf\\xb0\"\t// U+ebf0\n#define ICON_MS_NO_DRINKS \"\\xef\\x86\\xa5\"\t// U+f1a5\n#define ICON_MS_NO_ENCRYPTION \"\\xef\\x80\\xbf\"\t// U+f03f\n#define ICON_MS_NO_ENCRYPTION_GMAILERRORRED \"\\xef\\x80\\xbf\"\t// U+f03f\n#define ICON_MS_NO_FLASH \"\\xef\\x86\\xa6\"\t// U+f1a6\n#define ICON_MS_NO_FOOD \"\\xef\\x86\\xa7\"\t// U+f1a7\n#define ICON_MS_NO_LUGGAGE \"\\xef\\x88\\xbb\"\t// U+f23b\n#define ICON_MS_NO_MEALS \"\\xef\\x87\\x96\"\t// U+f1d6\n#define ICON_MS_NO_MEETING_ROOM \"\\xee\\xad\\x8e\"\t// U+eb4e\n#define ICON_MS_NO_PHOTOGRAPHY \"\\xef\\x86\\xa8\"\t// U+f1a8\n#define ICON_MS_NO_SIM \"\\xee\\x87\\x8e\"\t// U+e1ce\n#define ICON_MS_NO_SOUND \"\\xee\\x9c\\x90\"\t// U+e710\n#define ICON_MS_NO_STROLLER \"\\xef\\x86\\xaf\"\t// U+f1af\n#define ICON_MS_NO_TRANSFER \"\\xef\\x87\\x95\"\t// U+f1d5\n#define ICON_MS_NOISE_AWARE \"\\xee\\xaf\\xac\"\t// U+ebec\n#define ICON_MS_NOISE_CONTROL_OFF \"\\xee\\xaf\\xb3\"\t// U+ebf3\n#define ICON_MS_NOISE_CONTROL_ON \"\\xef\\xa2\\xa8\"\t// U+f8a8\n#define ICON_MS_NORDIC_WALKING \"\\xee\\x94\\x8e\"\t// U+e50e\n#define ICON_MS_NORTH \"\\xef\\x87\\xa0\"\t// U+f1e0\n#define ICON_MS_NORTH_EAST \"\\xef\\x87\\xa1\"\t// U+f1e1\n#define ICON_MS_NORTH_WEST \"\\xef\\x87\\xa2\"\t// U+f1e2\n#define ICON_MS_NOT_ACCESSIBLE \"\\xef\\x83\\xbe\"\t// U+f0fe\n#define ICON_MS_NOT_ACCESSIBLE_FORWARD \"\\xef\\x95\\x8a\"\t// U+f54a\n#define ICON_MS_NOT_INTERESTED \"\\xef\\x82\\x8c\"\t// U+f08c\n#define ICON_MS_NOT_LISTED_LOCATION \"\\xee\\x95\\xb5\"\t// U+e575\n#define ICON_MS_NOT_STARTED \"\\xef\\x83\\x91\"\t// U+f0d1\n#define ICON_MS_NOTE \"\\xee\\x99\\xad\"\t// U+e66d\n#define ICON_MS_NOTE_ADD \"\\xee\\xa2\\x9c\"\t// U+e89c\n#define ICON_MS_NOTE_ALT \"\\xef\\x81\\x80\"\t// U+f040\n#define ICON_MS_NOTE_STACK \"\\xef\\x95\\xa2\"\t// U+f562\n#define ICON_MS_NOTE_STACK_ADD \"\\xef\\x95\\xa3\"\t// U+f563\n#define ICON_MS_NOTES \"\\xee\\x89\\xac\"\t// U+e26c\n#define ICON_MS_NOTIFICATION_ADD \"\\xee\\x8e\\x99\"\t// U+e399\n#define ICON_MS_NOTIFICATION_IMPORTANT \"\\xee\\x80\\x84\"\t// U+e004\n#define ICON_MS_NOTIFICATION_MULTIPLE \"\\xee\\x9b\\x82\"\t// U+e6c2\n#define ICON_MS_NOTIFICATIONS \"\\xee\\x9f\\xb5\"\t// U+e7f5\n#define ICON_MS_NOTIFICATIONS_ACTIVE \"\\xee\\x9f\\xb7\"\t// U+e7f7\n#define ICON_MS_NOTIFICATIONS_NONE \"\\xee\\x9f\\xb5\"\t// U+e7f5\n#define ICON_MS_NOTIFICATIONS_OFF \"\\xee\\x9f\\xb6\"\t// U+e7f6\n#define ICON_MS_NOTIFICATIONS_PAUSED \"\\xee\\x9f\\xb8\"\t// U+e7f8\n#define ICON_MS_NOTIFICATIONS_UNREAD \"\\xef\\x93\\xbe\"\t// U+f4fe\n#define ICON_MS_NUMBERS \"\\xee\\xab\\x87\"\t// U+eac7\n#define ICON_MS_NUTRITION \"\\xee\\x84\\x90\"\t// U+e110\n#define ICON_MS_ODS \"\\xee\\x9b\\xa8\"\t// U+e6e8\n#define ICON_MS_ODT \"\\xee\\x9b\\xa9\"\t// U+e6e9\n#define ICON_MS_OFFLINE_BOLT \"\\xee\\xa4\\xb2\"\t// U+e932\n#define ICON_MS_OFFLINE_PIN \"\\xee\\xa4\\x8a\"\t// U+e90a\n#define ICON_MS_OFFLINE_PIN_OFF \"\\xef\\x93\\x90\"\t// U+f4d0\n#define ICON_MS_OFFLINE_SHARE \"\\xee\\xa7\\x85\"\t// U+e9c5\n#define ICON_MS_OIL_BARREL \"\\xee\\xb0\\x95\"\t// U+ec15\n#define ICON_MS_ON_DEVICE_TRAINING \"\\xee\\xaf\\xbd\"\t// U+ebfd\n#define ICON_MS_ON_HUB_DEVICE \"\\xee\\x9b\\x83\"\t// U+e6c3\n#define ICON_MS_ONCOLOGY \"\\xee\\x84\\x94\"\t// U+e114\n#define ICON_MS_ONDEMAND_VIDEO \"\\xee\\x98\\xba\"\t// U+e63a\n#define ICON_MS_ONLINE_PREDICTION \"\\xef\\x83\\xab\"\t// U+f0eb\n#define ICON_MS_ONSEN \"\\xef\\x9b\\xb8\"\t// U+f6f8\n#define ICON_MS_OPACITY \"\\xee\\xa4\\x9c\"\t// U+e91c\n#define ICON_MS_OPEN_IN_BROWSER \"\\xee\\xa2\\x9d\"\t// U+e89d\n#define ICON_MS_OPEN_IN_FULL \"\\xef\\x87\\x8e\"\t// U+f1ce\n#define ICON_MS_OPEN_IN_NEW \"\\xee\\xa2\\x9e\"\t// U+e89e\n#define ICON_MS_OPEN_IN_NEW_DOWN \"\\xef\\x9c\\x8f\"\t// U+f70f\n#define ICON_MS_OPEN_IN_NEW_OFF \"\\xee\\x93\\xb6\"\t// U+e4f6\n#define ICON_MS_OPEN_IN_PHONE \"\\xee\\x9c\\x82\"\t// U+e702\n#define ICON_MS_OPEN_JAM \"\\xee\\xbe\\xae\"\t// U+efae\n#define ICON_MS_OPEN_RUN \"\\xef\\x92\\xb7\"\t// U+f4b7\n#define ICON_MS_OPEN_WITH \"\\xee\\xa2\\x9f\"\t// U+e89f\n#define ICON_MS_OPHTHALMOLOGY \"\\xee\\x84\\x95\"\t// U+e115\n#define ICON_MS_ORAL_DISEASE \"\\xee\\x84\\x96\"\t// U+e116\n#define ICON_MS_ORDER_APPROVE \"\\xef\\xa0\\x92\"\t// U+f812\n#define ICON_MS_ORDER_PLAY \"\\xef\\xa0\\x91\"\t// U+f811\n#define ICON_MS_ORDERS \"\\xee\\xac\\x94\"\t// U+eb14\n#define ICON_MS_ORTHOPEDICS \"\\xef\\xa2\\x97\"\t// U+f897\n#define ICON_MS_OTHER_ADMISSION \"\\xee\\x91\\xbb\"\t// U+e47b\n#define ICON_MS_OTHER_HOUSES \"\\xee\\x96\\x8c\"\t// U+e58c\n#define ICON_MS_OUTBOUND \"\\xee\\x87\\x8a\"\t// U+e1ca\n#define ICON_MS_OUTBOX \"\\xee\\xbd\\x9f\"\t// U+ef5f\n#define ICON_MS_OUTBOX_ALT \"\\xee\\xac\\x97\"\t// U+eb17\n#define ICON_MS_OUTDOOR_GARDEN \"\\xee\\x88\\x85\"\t// U+e205\n#define ICON_MS_OUTDOOR_GRILL \"\\xee\\xa9\\x87\"\t// U+ea47\n#define ICON_MS_OUTGOING_MAIL \"\\xef\\x83\\x92\"\t// U+f0d2\n#define ICON_MS_OUTLET \"\\xef\\x87\\x94\"\t// U+f1d4\n#define ICON_MS_OUTLINED_FLAG \"\\xef\\x83\\x86\"\t// U+f0c6\n#define ICON_MS_OUTPATIENT \"\\xee\\x84\\x98\"\t// U+e118\n#define ICON_MS_OUTPATIENT_MED \"\\xee\\x84\\x99\"\t// U+e119\n#define ICON_MS_OUTPUT \"\\xee\\xae\\xbe\"\t// U+ebbe\n#define ICON_MS_OUTPUT_CIRCLE \"\\xef\\x9c\\x8e\"\t// U+f70e\n#define ICON_MS_OVEN \"\\xee\\xa7\\x87\"\t// U+e9c7\n#define ICON_MS_OVEN_GEN \"\\xee\\xa1\\x83\"\t// U+e843\n#define ICON_MS_OVERVIEW \"\\xee\\x92\\xa7\"\t// U+e4a7\n#define ICON_MS_OVERVIEW_KEY \"\\xef\\x9f\\x94\"\t// U+f7d4\n#define ICON_MS_OXYGEN_SATURATION \"\\xee\\x93\\x9e\"\t// U+e4de\n#define ICON_MS_P2P \"\\xef\\x94\\xaa\"\t// U+f52a\n#define ICON_MS_PACE \"\\xef\\x9a\\xb8\"\t// U+f6b8\n#define ICON_MS_PACEMAKER \"\\xee\\x99\\x96\"\t// U+e656\n#define ICON_MS_PACKAGE \"\\xee\\x92\\x8f\"\t// U+e48f\n#define ICON_MS_PACKAGE_2 \"\\xef\\x95\\xa9\"\t// U+f569\n#define ICON_MS_PADDING \"\\xee\\xa7\\x88\"\t// U+e9c8\n#define ICON_MS_PAGE_CONTROL \"\\xee\\x9c\\xb1\"\t// U+e731\n#define ICON_MS_PAGE_INFO \"\\xef\\x98\\x94\"\t// U+f614\n#define ICON_MS_PAGELESS \"\\xef\\x94\\x89\"\t// U+f509\n#define ICON_MS_PAGES \"\\xee\\x9f\\xb9\"\t// U+e7f9\n#define ICON_MS_PAGEVIEW \"\\xee\\xa2\\xa0\"\t// U+e8a0\n#define ICON_MS_PAID \"\\xef\\x81\\x81\"\t// U+f041\n#define ICON_MS_PALETTE \"\\xee\\x90\\x8a\"\t// U+e40a\n#define ICON_MS_PALLET \"\\xef\\xa1\\xaa\"\t// U+f86a\n#define ICON_MS_PAN_TOOL \"\\xee\\xa4\\xa5\"\t// U+e925\n#define ICON_MS_PAN_TOOL_ALT \"\\xee\\xae\\xb9\"\t// U+ebb9\n#define ICON_MS_PAN_ZOOM \"\\xef\\x99\\x95\"\t// U+f655\n#define ICON_MS_PANORAMA \"\\xee\\x90\\x8b\"\t// U+e40b\n#define ICON_MS_PANORAMA_FISH_EYE \"\\xee\\x90\\x8c\"\t// U+e40c\n#define ICON_MS_PANORAMA_HORIZONTAL \"\\xee\\x90\\x8d\"\t// U+e40d\n#define ICON_MS_PANORAMA_PHOTOSPHERE \"\\xee\\xa7\\x89\"\t// U+e9c9\n#define ICON_MS_PANORAMA_VERTICAL \"\\xee\\x90\\x8e\"\t// U+e40e\n#define ICON_MS_PANORAMA_WIDE_ANGLE \"\\xee\\x90\\x8f\"\t// U+e40f\n#define ICON_MS_PARAGLIDING \"\\xee\\x94\\x8f\"\t// U+e50f\n#define ICON_MS_PARK \"\\xee\\xa9\\xa3\"\t// U+ea63\n#define ICON_MS_PARTLY_CLOUDY_DAY \"\\xef\\x85\\xb2\"\t// U+f172\n#define ICON_MS_PARTLY_CLOUDY_NIGHT \"\\xef\\x85\\xb4\"\t// U+f174\n#define ICON_MS_PARTNER_EXCHANGE \"\\xef\\x9f\\xb9\"\t// U+f7f9\n#define ICON_MS_PARTNER_REPORTS \"\\xee\\xbe\\xaf\"\t// U+efaf\n#define ICON_MS_PARTY_MODE \"\\xee\\x9f\\xba\"\t// U+e7fa\n#define ICON_MS_PASSKEY \"\\xef\\xa1\\xbf\"\t// U+f87f\n#define ICON_MS_PASSWORD \"\\xef\\x81\\x82\"\t// U+f042\n#define ICON_MS_PASSWORD_2 \"\\xef\\x92\\xa9\"\t// U+f4a9\n#define ICON_MS_PASSWORD_2_OFF \"\\xef\\x92\\xa8\"\t// U+f4a8\n#define ICON_MS_PATIENT_LIST \"\\xee\\x99\\x93\"\t// U+e653\n#define ICON_MS_PATTERN \"\\xef\\x81\\x83\"\t// U+f043\n#define ICON_MS_PAUSE \"\\xee\\x80\\xb4\"\t// U+e034\n#define ICON_MS_PAUSE_CIRCLE \"\\xee\\x86\\xa2\"\t// U+e1a2\n#define ICON_MS_PAUSE_CIRCLE_FILLED \"\\xee\\x86\\xa2\"\t// U+e1a2\n#define ICON_MS_PAUSE_CIRCLE_OUTLINE \"\\xee\\x86\\xa2\"\t// U+e1a2\n#define ICON_MS_PAUSE_PRESENTATION \"\\xee\\x83\\xaa\"\t// U+e0ea\n#define ICON_MS_PAYMENT \"\\xee\\xa2\\xa1\"\t// U+e8a1\n#define ICON_MS_PAYMENTS \"\\xee\\xbd\\xa3\"\t// U+ef63\n#define ICON_MS_PEDAL_BIKE \"\\xee\\xac\\xa9\"\t// U+eb29\n#define ICON_MS_PEDIATRICS \"\\xee\\x84\\x9d\"\t// U+e11d\n#define ICON_MS_PEN_SIZE_1 \"\\xef\\x9d\\x95\"\t// U+f755\n#define ICON_MS_PEN_SIZE_2 \"\\xef\\x9d\\x94\"\t// U+f754\n#define ICON_MS_PEN_SIZE_3 \"\\xef\\x9d\\x93\"\t// U+f753\n#define ICON_MS_PEN_SIZE_4 \"\\xef\\x9d\\x92\"\t// U+f752\n#define ICON_MS_PEN_SIZE_5 \"\\xef\\x9d\\x91\"\t// U+f751\n#define ICON_MS_PENDING \"\\xee\\xbd\\xa4\"\t// U+ef64\n#define ICON_MS_PENDING_ACTIONS \"\\xef\\x86\\xbb\"\t// U+f1bb\n#define ICON_MS_PENTAGON \"\\xee\\xad\\x90\"\t// U+eb50\n#define ICON_MS_PEOPLE \"\\xee\\xa8\\xa1\"\t// U+ea21\n#define ICON_MS_PEOPLE_ALT \"\\xee\\xa8\\xa1\"\t// U+ea21\n#define ICON_MS_PEOPLE_OUTLINE \"\\xee\\xa8\\xa1\"\t// U+ea21\n#define ICON_MS_PERCENT \"\\xee\\xad\\x98\"\t// U+eb58\n#define ICON_MS_PERFORMANCE_MAX \"\\xee\\x94\\x9a\"\t// U+e51a\n#define ICON_MS_PERGOLA \"\\xee\\x88\\x83\"\t// U+e203\n#define ICON_MS_PERM_CAMERA_MIC \"\\xee\\xa2\\xa2\"\t// U+e8a2\n#define ICON_MS_PERM_CONTACT_CALENDAR \"\\xee\\xa2\\xa3\"\t// U+e8a3\n#define ICON_MS_PERM_DATA_SETTING \"\\xee\\xa2\\xa4\"\t// U+e8a4\n#define ICON_MS_PERM_DEVICE_INFORMATION \"\\xee\\xa2\\xa5\"\t// U+e8a5\n#define ICON_MS_PERM_IDENTITY \"\\xef\\x83\\x93\"\t// U+f0d3\n#define ICON_MS_PERM_MEDIA \"\\xee\\xa2\\xa7\"\t// U+e8a7\n#define ICON_MS_PERM_PHONE_MSG \"\\xee\\xa2\\xa8\"\t// U+e8a8\n#define ICON_MS_PERM_SCAN_WIFI \"\\xee\\xa2\\xa9\"\t// U+e8a9\n#define ICON_MS_PERSON \"\\xef\\x83\\x93\"\t// U+f0d3\n#define ICON_MS_PERSON_2 \"\\xef\\xa3\\xa4\"\t// U+f8e4\n#define ICON_MS_PERSON_3 \"\\xef\\xa3\\xa5\"\t// U+f8e5\n#define ICON_MS_PERSON_4 \"\\xef\\xa3\\xa6\"\t// U+f8e6\n#define ICON_MS_PERSON_ADD \"\\xee\\xa9\\x8d\"\t// U+ea4d\n#define ICON_MS_PERSON_ADD_ALT \"\\xee\\xa9\\x8d\"\t// U+ea4d\n#define ICON_MS_PERSON_ADD_DISABLED \"\\xee\\xa7\\x8b\"\t// U+e9cb\n#define ICON_MS_PERSON_ALERT \"\\xef\\x95\\xa7\"\t// U+f567\n#define ICON_MS_PERSON_APRON \"\\xef\\x96\\xa3\"\t// U+f5a3\n#define ICON_MS_PERSON_BOOK \"\\xef\\x97\\xa8\"\t// U+f5e8\n#define ICON_MS_PERSON_CANCEL \"\\xef\\x95\\xa6\"\t// U+f566\n#define ICON_MS_PERSON_CELEBRATE \"\\xef\\x9f\\xbe\"\t// U+f7fe\n#define ICON_MS_PERSON_CHECK \"\\xef\\x95\\xa5\"\t// U+f565\n#define ICON_MS_PERSON_EDIT \"\\xef\\x93\\xba\"\t// U+f4fa\n#define ICON_MS_PERSON_FILLED \"\\xef\\x83\\x93\"\t// U+f0d3\n#define ICON_MS_PERSON_OFF \"\\xee\\x94\\x90\"\t// U+e510\n#define ICON_MS_PERSON_OUTLINE \"\\xef\\x83\\x93\"\t// U+f0d3\n#define ICON_MS_PERSON_PIN \"\\xee\\x95\\x9a\"\t// U+e55a\n#define ICON_MS_PERSON_PIN_CIRCLE \"\\xee\\x95\\xaa\"\t// U+e56a\n#define ICON_MS_PERSON_PLAY \"\\xef\\x9f\\xbd\"\t// U+f7fd\n#define ICON_MS_PERSON_RAISED_HAND \"\\xef\\x96\\x9a\"\t// U+f59a\n#define ICON_MS_PERSON_REMOVE \"\\xee\\xbd\\xa6\"\t// U+ef66\n#define ICON_MS_PERSON_SEARCH \"\\xef\\x84\\x86\"\t// U+f106\n#define ICON_MS_PERSONAL_BAG \"\\xee\\xac\\x8e\"\t// U+eb0e\n#define ICON_MS_PERSONAL_BAG_OFF \"\\xee\\xac\\x8f\"\t// U+eb0f\n#define ICON_MS_PERSONAL_BAG_QUESTION \"\\xee\\xac\\x90\"\t// U+eb10\n#define ICON_MS_PERSONAL_INJURY \"\\xee\\x9b\\x9a\"\t// U+e6da\n#define ICON_MS_PERSONAL_PLACES \"\\xee\\x9c\\x83\"\t// U+e703\n#define ICON_MS_PERSONAL_VIDEO \"\\xee\\x98\\xbb\"\t// U+e63b\n#define ICON_MS_PEST_CONTROL \"\\xef\\x83\\xba\"\t// U+f0fa\n#define ICON_MS_PEST_CONTROL_RODENT \"\\xef\\x83\\xbd\"\t// U+f0fd\n#define ICON_MS_PET_SUPPLIES \"\\xee\\xbe\\xb1\"\t// U+efb1\n#define ICON_MS_PETS \"\\xee\\xa4\\x9d\"\t// U+e91d\n#define ICON_MS_PHISHING \"\\xee\\xab\\x97\"\t// U+ead7\n#define ICON_MS_PHONE \"\\xef\\x83\\x94\"\t// U+f0d4\n#define ICON_MS_PHONE_ALT \"\\xef\\x83\\x94\"\t// U+f0d4\n#define ICON_MS_PHONE_ANDROID \"\\xee\\x8c\\xa4\"\t// U+e324\n#define ICON_MS_PHONE_BLUETOOTH_SPEAKER \"\\xee\\x98\\x9b\"\t// U+e61b\n#define ICON_MS_PHONE_CALLBACK \"\\xee\\x99\\x89\"\t// U+e649\n#define ICON_MS_PHONE_DISABLED \"\\xee\\xa7\\x8c\"\t// U+e9cc\n#define ICON_MS_PHONE_ENABLED \"\\xee\\xa7\\x8d\"\t// U+e9cd\n#define ICON_MS_PHONE_FORWARDED \"\\xee\\x98\\x9c\"\t// U+e61c\n#define ICON_MS_PHONE_IN_TALK \"\\xee\\x98\\x9d\"\t// U+e61d\n#define ICON_MS_PHONE_IPHONE \"\\xee\\x8c\\xa5\"\t// U+e325\n#define ICON_MS_PHONE_LOCKED \"\\xee\\x98\\x9e\"\t// U+e61e\n#define ICON_MS_PHONE_MISSED \"\\xee\\x98\\x9f\"\t// U+e61f\n#define ICON_MS_PHONE_PAUSED \"\\xee\\x98\\xa0\"\t// U+e620\n#define ICON_MS_PHONELINK \"\\xee\\x8c\\xa6\"\t// U+e326\n#define ICON_MS_PHONELINK_ERASE \"\\xee\\x83\\x9b\"\t// U+e0db\n#define ICON_MS_PHONELINK_LOCK \"\\xee\\x83\\x9c\"\t// U+e0dc\n#define ICON_MS_PHONELINK_OFF \"\\xee\\x8c\\xa7\"\t// U+e327\n#define ICON_MS_PHONELINK_RING \"\\xee\\x83\\x9d\"\t// U+e0dd\n#define ICON_MS_PHONELINK_RING_OFF \"\\xef\\x9e\\xaa\"\t// U+f7aa\n#define ICON_MS_PHONELINK_SETUP \"\\xee\\xbd\\x81\"\t// U+ef41\n#define ICON_MS_PHOTO \"\\xee\\x90\\xb2\"\t// U+e432\n#define ICON_MS_PHOTO_ALBUM \"\\xee\\x90\\x91\"\t// U+e411\n#define ICON_MS_PHOTO_AUTO_MERGE \"\\xef\\x94\\xb0\"\t// U+f530\n#define ICON_MS_PHOTO_CAMERA \"\\xee\\x90\\x92\"\t// U+e412\n#define ICON_MS_PHOTO_CAMERA_BACK \"\\xee\\xbd\\xa8\"\t// U+ef68\n#define ICON_MS_PHOTO_CAMERA_FRONT \"\\xee\\xbd\\xa9\"\t// U+ef69\n#define ICON_MS_PHOTO_FILTER \"\\xee\\x90\\xbb\"\t// U+e43b\n#define ICON_MS_PHOTO_FRAME \"\\xef\\x83\\x99\"\t// U+f0d9\n#define ICON_MS_PHOTO_LIBRARY \"\\xee\\x90\\x93\"\t// U+e413\n#define ICON_MS_PHOTO_PRINTS \"\\xee\\xbe\\xb2\"\t// U+efb2\n#define ICON_MS_PHOTO_SIZE_SELECT_ACTUAL \"\\xee\\x90\\xb2\"\t// U+e432\n#define ICON_MS_PHOTO_SIZE_SELECT_LARGE \"\\xee\\x90\\xb3\"\t// U+e433\n#define ICON_MS_PHOTO_SIZE_SELECT_SMALL \"\\xee\\x90\\xb4\"\t// U+e434\n#define ICON_MS_PHP \"\\xee\\xae\\x8f\"\t// U+eb8f\n#define ICON_MS_PHYSICAL_THERAPY \"\\xee\\x84\\x9e\"\t// U+e11e\n#define ICON_MS_PIANO \"\\xee\\x94\\xa1\"\t// U+e521\n#define ICON_MS_PIANO_OFF \"\\xee\\x94\\xa0\"\t// U+e520\n#define ICON_MS_PICTURE_AS_PDF \"\\xee\\x90\\x95\"\t// U+e415\n#define ICON_MS_PICTURE_IN_PICTURE \"\\xee\\xa2\\xaa\"\t// U+e8aa\n#define ICON_MS_PICTURE_IN_PICTURE_ALT \"\\xee\\xa4\\x91\"\t// U+e911\n#define ICON_MS_PICTURE_IN_PICTURE_CENTER \"\\xef\\x95\\x90\"\t// U+f550\n#define ICON_MS_PICTURE_IN_PICTURE_LARGE \"\\xef\\x95\\x8f\"\t// U+f54f\n#define ICON_MS_PICTURE_IN_PICTURE_MEDIUM \"\\xef\\x95\\x8e\"\t// U+f54e\n#define ICON_MS_PICTURE_IN_PICTURE_MOBILE \"\\xef\\x94\\x97\"\t// U+f517\n#define ICON_MS_PICTURE_IN_PICTURE_OFF \"\\xef\\x94\\xaf\"\t// U+f52f\n#define ICON_MS_PICTURE_IN_PICTURE_SMALL \"\\xef\\x95\\x8d\"\t// U+f54d\n#define ICON_MS_PIE_CHART \"\\xef\\x83\\x9a\"\t// U+f0da\n#define ICON_MS_PIE_CHART_FILLED \"\\xef\\x83\\x9a\"\t// U+f0da\n#define ICON_MS_PIE_CHART_OUTLINE \"\\xef\\x83\\x9a\"\t// U+f0da\n#define ICON_MS_PIE_CHART_OUTLINED \"\\xef\\x83\\x9a\"\t// U+f0da\n#define ICON_MS_PILL \"\\xee\\x84\\x9f\"\t// U+e11f\n#define ICON_MS_PILL_OFF \"\\xef\\xa0\\x89\"\t// U+f809\n#define ICON_MS_PIN \"\\xef\\x81\\x85\"\t// U+f045\n#define ICON_MS_PIN_DROP \"\\xee\\x95\\x9e\"\t// U+e55e\n#define ICON_MS_PIN_END \"\\xee\\x9d\\xa7\"\t// U+e767\n#define ICON_MS_PIN_INVOKE \"\\xee\\x9d\\xa3\"\t// U+e763\n#define ICON_MS_PINCH \"\\xee\\xac\\xb8\"\t// U+eb38\n#define ICON_MS_PINCH_ZOOM_IN \"\\xef\\x87\\xba\"\t// U+f1fa\n#define ICON_MS_PINCH_ZOOM_OUT \"\\xef\\x87\\xbb\"\t// U+f1fb\n#define ICON_MS_PIP \"\\xef\\x99\\x8d\"\t// U+f64d\n#define ICON_MS_PIP_EXIT \"\\xef\\x9c\\x8d\"\t// U+f70d\n#define ICON_MS_PIVOT_TABLE_CHART \"\\xee\\xa7\\x8e\"\t// U+e9ce\n#define ICON_MS_PLACE \"\\xef\\x87\\x9b\"\t// U+f1db\n#define ICON_MS_PLACE_ITEM \"\\xef\\x87\\xb0\"\t// U+f1f0\n#define ICON_MS_PLAGIARISM \"\\xee\\xa9\\x9a\"\t// U+ea5a\n#define ICON_MS_PLANNER_BANNER_AD_PT \"\\xee\\x9a\\x92\"\t// U+e692\n#define ICON_MS_PLANNER_REVIEW \"\\xee\\x9a\\x94\"\t// U+e694\n#define ICON_MS_PLAY_ARROW \"\\xee\\x80\\xb7\"\t// U+e037\n#define ICON_MS_PLAY_CIRCLE \"\\xee\\x87\\x84\"\t// U+e1c4\n#define ICON_MS_PLAY_DISABLED \"\\xee\\xbd\\xaa\"\t// U+ef6a\n#define ICON_MS_PLAY_FOR_WORK \"\\xee\\xa4\\x86\"\t// U+e906\n#define ICON_MS_PLAY_LESSON \"\\xef\\x81\\x87\"\t// U+f047\n#define ICON_MS_PLAY_MUSIC \"\\xee\\x9b\\xae\"\t// U+e6ee\n#define ICON_MS_PLAY_PAUSE \"\\xef\\x84\\xb7\"\t// U+f137\n#define ICON_MS_PLAY_SHAPES \"\\xef\\x9f\\xbc\"\t// U+f7fc\n#define ICON_MS_PLAYING_CARDS \"\\xef\\x97\\x9c\"\t// U+f5dc\n#define ICON_MS_PLAYLIST_ADD \"\\xee\\x80\\xbb\"\t// U+e03b\n#define ICON_MS_PLAYLIST_ADD_CHECK \"\\xee\\x81\\xa5\"\t// U+e065\n#define ICON_MS_PLAYLIST_ADD_CHECK_CIRCLE \"\\xee\\x9f\\xa6\"\t// U+e7e6\n#define ICON_MS_PLAYLIST_ADD_CIRCLE \"\\xee\\x9f\\xa5\"\t// U+e7e5\n#define ICON_MS_PLAYLIST_PLAY \"\\xee\\x81\\x9f\"\t// U+e05f\n#define ICON_MS_PLAYLIST_REMOVE \"\\xee\\xae\\x80\"\t// U+eb80\n#define ICON_MS_PLUMBING \"\\xef\\x84\\x87\"\t// U+f107\n#define ICON_MS_PLUS_ONE \"\\xee\\xa0\\x80\"\t// U+e800\n#define ICON_MS_PODCASTS \"\\xef\\x81\\x88\"\t// U+f048\n#define ICON_MS_PODIATRY \"\\xee\\x84\\xa0\"\t// U+e120\n#define ICON_MS_PODIUM \"\\xef\\x9f\\xbb\"\t// U+f7fb\n#define ICON_MS_POINT_OF_SALE \"\\xef\\x85\\xbe\"\t// U+f17e\n#define ICON_MS_POINT_SCAN \"\\xef\\x9c\\x8c\"\t// U+f70c\n#define ICON_MS_POKER_CHIP \"\\xef\\x92\\x9b\"\t// U+f49b\n#define ICON_MS_POLICY \"\\xee\\xa8\\x97\"\t// U+ea17\n#define ICON_MS_POLL \"\\xef\\x83\\x8c\"\t// U+f0cc\n#define ICON_MS_POLYLINE \"\\xee\\xae\\xbb\"\t// U+ebbb\n#define ICON_MS_POLYMER \"\\xee\\xa2\\xab\"\t// U+e8ab\n#define ICON_MS_POOL \"\\xee\\xad\\x88\"\t// U+eb48\n#define ICON_MS_PORTABLE_WIFI_OFF \"\\xef\\x82\\x87\"\t// U+f087\n#define ICON_MS_PORTRAIT \"\\xee\\xa1\\x91\"\t// U+e851\n#define ICON_MS_POSITION_BOTTOM_LEFT \"\\xef\\x9c\\x8b\"\t// U+f70b\n#define ICON_MS_POSITION_BOTTOM_RIGHT \"\\xef\\x9c\\x8a\"\t// U+f70a\n#define ICON_MS_POSITION_TOP_RIGHT \"\\xef\\x9c\\x89\"\t// U+f709\n#define ICON_MS_POST \"\\xee\\x9c\\x85\"\t// U+e705\n#define ICON_MS_POST_ADD \"\\xee\\xa8\\xa0\"\t// U+ea20\n#define ICON_MS_POTTED_PLANT \"\\xef\\xa2\\xaa\"\t// U+f8aa\n#define ICON_MS_POWER \"\\xee\\x98\\xbc\"\t// U+e63c\n#define ICON_MS_POWER_INPUT \"\\xee\\x8c\\xb6\"\t// U+e336\n#define ICON_MS_POWER_OFF \"\\xee\\x99\\x86\"\t// U+e646\n#define ICON_MS_POWER_ROUNDED \"\\xef\\xa3\\x87\"\t// U+f8c7\n#define ICON_MS_POWER_SETTINGS_NEW \"\\xef\\xa3\\x87\"\t// U+f8c7\n#define ICON_MS_PRAYER_TIMES \"\\xef\\xa0\\xb8\"\t// U+f838\n#define ICON_MS_PRECISION_MANUFACTURING \"\\xef\\x81\\x89\"\t// U+f049\n#define ICON_MS_PREGNANCY \"\\xef\\x97\\xb1\"\t// U+f5f1\n#define ICON_MS_PREGNANT_WOMAN \"\\xef\\x97\\xb1\"\t// U+f5f1\n#define ICON_MS_PRELIMINARY \"\\xee\\x9f\\x98\"\t// U+e7d8\n#define ICON_MS_PRESCRIPTIONS \"\\xee\\x84\\xa1\"\t// U+e121\n#define ICON_MS_PRESENT_TO_ALL \"\\xee\\x83\\x9f\"\t// U+e0df\n#define ICON_MS_PREVIEW \"\\xef\\x87\\x85\"\t// U+f1c5\n#define ICON_MS_PREVIEW_OFF \"\\xef\\x9e\\xaf\"\t// U+f7af\n#define ICON_MS_PRICE_CHANGE \"\\xef\\x81\\x8a\"\t// U+f04a\n#define ICON_MS_PRICE_CHECK \"\\xef\\x81\\x8b\"\t// U+f04b\n#define ICON_MS_PRINT \"\\xee\\xa2\\xad\"\t// U+e8ad\n#define ICON_MS_PRINT_ADD \"\\xef\\x9e\\xa2\"\t// U+f7a2\n#define ICON_MS_PRINT_CONNECT \"\\xef\\x9e\\xa1\"\t// U+f7a1\n#define ICON_MS_PRINT_DISABLED \"\\xee\\xa7\\x8f\"\t// U+e9cf\n#define ICON_MS_PRINT_ERROR \"\\xef\\x9e\\xa0\"\t// U+f7a0\n#define ICON_MS_PRINT_LOCK \"\\xef\\x99\\x91\"\t// U+f651\n#define ICON_MS_PRIORITY \"\\xee\\x86\\x9f\"\t// U+e19f\n#define ICON_MS_PRIORITY_HIGH \"\\xee\\x99\\x85\"\t// U+e645\n#define ICON_MS_PRIVACY \"\\xef\\x85\\x88\"\t// U+f148\n#define ICON_MS_PRIVACY_TIP \"\\xef\\x83\\x9c\"\t// U+f0dc\n#define ICON_MS_PRIVATE_CONNECTIVITY \"\\xee\\x9d\\x84\"\t// U+e744\n#define ICON_MS_PROBLEM \"\\xee\\x84\\xa2\"\t// U+e122\n#define ICON_MS_PROCEDURE \"\\xee\\x99\\x91\"\t// U+e651\n#define ICON_MS_PROCESS_CHART \"\\xef\\xa1\\x95\"\t// U+f855\n#define ICON_MS_PRODUCTION_QUANTITY_LIMITS \"\\xee\\x87\\x91\"\t// U+e1d1\n#define ICON_MS_PRODUCTIVITY \"\\xee\\x8a\\x96\"\t// U+e296\n#define ICON_MS_PROGRESS_ACTIVITY \"\\xee\\xa7\\x90\"\t// U+e9d0\n#define ICON_MS_PROMPT_SUGGESTION \"\\xef\\x93\\xb6\"\t// U+f4f6\n#define ICON_MS_PROPANE \"\\xee\\xb0\\x94\"\t// U+ec14\n#define ICON_MS_PROPANE_TANK \"\\xee\\xb0\\x93\"\t// U+ec13\n#define ICON_MS_PSYCHIATRY \"\\xee\\x84\\xa3\"\t// U+e123\n#define ICON_MS_PSYCHOLOGY \"\\xee\\xa9\\x8a\"\t// U+ea4a\n#define ICON_MS_PSYCHOLOGY_ALT \"\\xef\\xa3\\xaa\"\t// U+f8ea\n#define ICON_MS_PUBLIC \"\\xee\\xa0\\x8b\"\t// U+e80b\n#define ICON_MS_PUBLIC_OFF \"\\xef\\x87\\x8a\"\t// U+f1ca\n#define ICON_MS_PUBLISH \"\\xee\\x89\\x95\"\t// U+e255\n#define ICON_MS_PUBLISHED_WITH_CHANGES \"\\xef\\x88\\xb2\"\t// U+f232\n#define ICON_MS_PULMONOLOGY \"\\xee\\x84\\xa4\"\t// U+e124\n#define ICON_MS_PULSE_ALERT \"\\xef\\x94\\x81\"\t// U+f501\n#define ICON_MS_PUNCH_CLOCK \"\\xee\\xaa\\xa8\"\t// U+eaa8\n#define ICON_MS_PUSH_PIN \"\\xef\\x84\\x8d\"\t// U+f10d\n#define ICON_MS_QR_CODE \"\\xee\\xbd\\xab\"\t// U+ef6b\n#define ICON_MS_QR_CODE_2 \"\\xee\\x80\\x8a\"\t// U+e00a\n#define ICON_MS_QR_CODE_2_ADD \"\\xef\\x99\\x98\"\t// U+f658\n#define ICON_MS_QR_CODE_SCANNER \"\\xef\\x88\\x86\"\t// U+f206\n#define ICON_MS_QUERY_BUILDER \"\\xee\\xbf\\x96\"\t// U+efd6\n#define ICON_MS_QUERY_STATS \"\\xee\\x93\\xbc\"\t// U+e4fc\n#define ICON_MS_QUESTION_ANSWER \"\\xee\\xa2\\xaf\"\t// U+e8af\n#define ICON_MS_QUESTION_EXCHANGE \"\\xef\\x9f\\xb3\"\t// U+f7f3\n#define ICON_MS_QUESTION_MARK \"\\xee\\xae\\x8b\"\t// U+eb8b\n#define ICON_MS_QUEUE \"\\xee\\x80\\xbc\"\t// U+e03c\n#define ICON_MS_QUEUE_MUSIC \"\\xee\\x80\\xbd\"\t// U+e03d\n#define ICON_MS_QUEUE_PLAY_NEXT \"\\xee\\x81\\xa6\"\t// U+e066\n#define ICON_MS_QUICK_PHRASES \"\\xee\\x9f\\x91\"\t// U+e7d1\n#define ICON_MS_QUICK_REFERENCE \"\\xee\\x91\\xae\"\t// U+e46e\n#define ICON_MS_QUICK_REFERENCE_ALL \"\\xef\\xa0\\x81\"\t// U+f801\n#define ICON_MS_QUICK_REORDER \"\\xee\\xac\\x95\"\t// U+eb15\n#define ICON_MS_QUICKREPLY \"\\xee\\xbd\\xac\"\t// U+ef6c\n#define ICON_MS_QUIET_TIME \"\\xee\\x87\\xb9\"\t// U+e1f9\n#define ICON_MS_QUIET_TIME_ACTIVE \"\\xee\\x8a\\x91\"\t// U+e291\n#define ICON_MS_QUIZ \"\\xef\\x81\\x8c\"\t// U+f04c\n#define ICON_MS_R_MOBILEDATA \"\\xef\\x81\\x8d\"\t// U+f04d\n#define ICON_MS_RADAR \"\\xef\\x81\\x8e\"\t// U+f04e\n#define ICON_MS_RADIO \"\\xee\\x80\\xbe\"\t// U+e03e\n#define ICON_MS_RADIO_BUTTON_CHECKED \"\\xee\\xa0\\xb7\"\t// U+e837\n#define ICON_MS_RADIO_BUTTON_PARTIAL \"\\xef\\x95\\xa0\"\t// U+f560\n#define ICON_MS_RADIO_BUTTON_UNCHECKED \"\\xee\\xa0\\xb6\"\t// U+e836\n#define ICON_MS_RADIOLOGY \"\\xee\\x84\\xa5\"\t// U+e125\n#define ICON_MS_RAILWAY_ALERT \"\\xee\\xa7\\x91\"\t// U+e9d1\n#define ICON_MS_RAINY \"\\xef\\x85\\xb6\"\t// U+f176\n#define ICON_MS_RAINY_HEAVY \"\\xef\\x98\\x9f\"\t// U+f61f\n#define ICON_MS_RAINY_LIGHT \"\\xef\\x98\\x9e\"\t// U+f61e\n#define ICON_MS_RAINY_SNOW \"\\xef\\x98\\x9d\"\t// U+f61d\n#define ICON_MS_RAMEN_DINING \"\\xee\\xa9\\xa4\"\t// U+ea64\n#define ICON_MS_RAMP_LEFT \"\\xee\\xae\\x9c\"\t// U+eb9c\n#define ICON_MS_RAMP_RIGHT \"\\xee\\xae\\x96\"\t// U+eb96\n#define ICON_MS_RANGE_HOOD \"\\xee\\x87\\xaa\"\t// U+e1ea\n#define ICON_MS_RATE_REVIEW \"\\xee\\x95\\xa0\"\t// U+e560\n#define ICON_MS_RAVEN \"\\xef\\x95\\x95\"\t// U+f555\n#define ICON_MS_RAW_OFF \"\\xef\\x81\\x8f\"\t// U+f04f\n#define ICON_MS_RAW_ON \"\\xef\\x81\\x90\"\t// U+f050\n#define ICON_MS_READ_MORE \"\\xee\\xbd\\xad\"\t// U+ef6d\n#define ICON_MS_READINESS_SCORE \"\\xef\\x9b\\x9d\"\t// U+f6dd\n#define ICON_MS_REAL_ESTATE_AGENT \"\\xee\\x9c\\xba\"\t// U+e73a\n#define ICON_MS_REAR_CAMERA \"\\xef\\x9b\\x82\"\t// U+f6c2\n#define ICON_MS_REBASE \"\\xef\\xa1\\x85\"\t// U+f845\n#define ICON_MS_REBASE_EDIT \"\\xef\\xa1\\x86\"\t// U+f846\n#define ICON_MS_RECEIPT \"\\xee\\xa2\\xb0\"\t// U+e8b0\n#define ICON_MS_RECEIPT_LONG \"\\xee\\xbd\\xae\"\t// U+ef6e\n#define ICON_MS_RECENT_ACTORS \"\\xee\\x80\\xbf\"\t// U+e03f\n#define ICON_MS_RECENT_PATIENT \"\\xef\\xa0\\x88\"\t// U+f808\n#define ICON_MS_RECENTER \"\\xef\\x93\\x80\"\t// U+f4c0\n#define ICON_MS_RECOMMEND \"\\xee\\xa7\\x92\"\t// U+e9d2\n#define ICON_MS_RECORD_VOICE_OVER \"\\xee\\xa4\\x9f\"\t// U+e91f\n#define ICON_MS_RECTANGLE \"\\xee\\xad\\x94\"\t// U+eb54\n#define ICON_MS_RECYCLING \"\\xee\\x9d\\xa0\"\t// U+e760\n#define ICON_MS_REDEEM \"\\xee\\xa3\\xb6\"\t// U+e8f6\n#define ICON_MS_REDO \"\\xee\\x85\\x9a\"\t// U+e15a\n#define ICON_MS_REDUCE_CAPACITY \"\\xef\\x88\\x9c\"\t// U+f21c\n#define ICON_MS_REFRESH \"\\xee\\x97\\x95\"\t// U+e5d5\n#define ICON_MS_REGULAR_EXPRESSION \"\\xef\\x9d\\x90\"\t// U+f750\n#define ICON_MS_RELAX \"\\xef\\x9b\\x9c\"\t// U+f6dc\n#define ICON_MS_RELEASE_ALERT \"\\xef\\x99\\x94\"\t// U+f654\n#define ICON_MS_REMEMBER_ME \"\\xef\\x81\\x91\"\t// U+f051\n#define ICON_MS_REMINDER \"\\xee\\x9b\\x86\"\t// U+e6c6\n#define ICON_MS_REMINDERS_ALT \"\\xee\\x9b\\x86\"\t// U+e6c6\n#define ICON_MS_REMOTE_GEN \"\\xee\\xa0\\xbe\"\t// U+e83e\n#define ICON_MS_REMOVE \"\\xee\\x85\\x9b\"\t// U+e15b\n#define ICON_MS_REMOVE_CIRCLE \"\\xef\\x82\\x8f\"\t// U+f08f\n#define ICON_MS_REMOVE_CIRCLE_OUTLINE \"\\xef\\x82\\x8f\"\t// U+f08f\n#define ICON_MS_REMOVE_DONE \"\\xee\\xa7\\x93\"\t// U+e9d3\n#define ICON_MS_REMOVE_FROM_QUEUE \"\\xee\\x81\\xa7\"\t// U+e067\n#define ICON_MS_REMOVE_MODERATOR \"\\xee\\xa7\\x94\"\t// U+e9d4\n#define ICON_MS_REMOVE_RED_EYE \"\\xee\\xa3\\xb4\"\t// U+e8f4\n#define ICON_MS_REMOVE_ROAD \"\\xee\\xaf\\xbc\"\t// U+ebfc\n#define ICON_MS_REMOVE_SELECTION \"\\xee\\xa7\\x95\"\t// U+e9d5\n#define ICON_MS_REMOVE_SHOPPING_CART \"\\xee\\xa4\\xa8\"\t// U+e928\n#define ICON_MS_REOPEN_WINDOW \"\\xef\\x9c\\x88\"\t// U+f708\n#define ICON_MS_REORDER \"\\xee\\xa3\\xbe\"\t// U+e8fe\n#define ICON_MS_REPARTITION \"\\xef\\xa3\\xa8\"\t// U+f8e8\n#define ICON_MS_REPEAT \"\\xee\\x81\\x80\"\t// U+e040\n#define ICON_MS_REPEAT_ON \"\\xee\\xa7\\x96\"\t// U+e9d6\n#define ICON_MS_REPEAT_ONE \"\\xee\\x81\\x81\"\t// U+e041\n#define ICON_MS_REPEAT_ONE_ON \"\\xee\\xa7\\x97\"\t// U+e9d7\n#define ICON_MS_REPLAY \"\\xee\\x81\\x82\"\t// U+e042\n#define ICON_MS_REPLAY_10 \"\\xee\\x81\\x99\"\t// U+e059\n#define ICON_MS_REPLAY_30 \"\\xee\\x81\\x9a\"\t// U+e05a\n#define ICON_MS_REPLAY_5 \"\\xee\\x81\\x9b\"\t// U+e05b\n#define ICON_MS_REPLAY_CIRCLE_FILLED \"\\xee\\xa7\\x98\"\t// U+e9d8\n#define ICON_MS_REPLY \"\\xee\\x85\\x9e\"\t// U+e15e\n#define ICON_MS_REPLY_ALL \"\\xee\\x85\\x9f\"\t// U+e15f\n#define ICON_MS_REPORT \"\\xef\\x81\\x92\"\t// U+f052\n#define ICON_MS_REPORT_GMAILERRORRED \"\\xef\\x81\\x92\"\t// U+f052\n#define ICON_MS_REPORT_OFF \"\\xee\\x85\\xb0\"\t// U+e170\n#define ICON_MS_REPORT_PROBLEM \"\\xef\\x82\\x83\"\t// U+f083\n#define ICON_MS_REQUEST_PAGE \"\\xef\\x88\\xac\"\t// U+f22c\n#define ICON_MS_REQUEST_QUOTE \"\\xef\\x86\\xb6\"\t// U+f1b6\n#define ICON_MS_RESET_IMAGE \"\\xef\\xa0\\xa4\"\t// U+f824\n#define ICON_MS_RESET_TV \"\\xee\\xa7\\x99\"\t// U+e9d9\n#define ICON_MS_RESET_WRENCH \"\\xef\\x95\\xac\"\t// U+f56c\n#define ICON_MS_RESIZE \"\\xef\\x9c\\x87\"\t// U+f707\n#define ICON_MS_RESPIRATORY_RATE \"\\xee\\x84\\xa7\"\t// U+e127\n#define ICON_MS_RESPONSIVE_LAYOUT \"\\xee\\xa7\\x9a\"\t// U+e9da\n#define ICON_MS_RESTART_ALT \"\\xef\\x81\\x93\"\t// U+f053\n#define ICON_MS_RESTAURANT \"\\xee\\x95\\xac\"\t// U+e56c\n#define ICON_MS_RESTAURANT_MENU \"\\xee\\x95\\xa1\"\t// U+e561\n#define ICON_MS_RESTORE \"\\xee\\xa2\\xb3\"\t// U+e8b3\n#define ICON_MS_RESTORE_FROM_TRASH \"\\xee\\xa4\\xb8\"\t// U+e938\n#define ICON_MS_RESTORE_PAGE \"\\xee\\xa4\\xa9\"\t// U+e929\n#define ICON_MS_RESUME \"\\xef\\x9f\\x90\"\t// U+f7d0\n#define ICON_MS_REVIEWS \"\\xef\\x81\\xbc\"\t// U+f07c\n#define ICON_MS_REWARDED_ADS \"\\xee\\xbe\\xb6\"\t// U+efb6\n#define ICON_MS_RHEUMATOLOGY \"\\xee\\x84\\xa8\"\t// U+e128\n#define ICON_MS_RIB_CAGE \"\\xef\\xa2\\x98\"\t// U+f898\n#define ICON_MS_RICE_BOWL \"\\xef\\x87\\xb5\"\t// U+f1f5\n#define ICON_MS_RIGHT_CLICK \"\\xef\\x9c\\x86\"\t// U+f706\n#define ICON_MS_RIGHT_PANEL_CLOSE \"\\xef\\x9c\\x85\"\t// U+f705\n#define ICON_MS_RIGHT_PANEL_OPEN \"\\xef\\x9c\\x84\"\t// U+f704\n#define ICON_MS_RING_VOLUME \"\\xef\\x83\\x9d\"\t// U+f0dd\n#define ICON_MS_RING_VOLUME_FILLED \"\\xef\\x83\\x9d\"\t// U+f0dd\n#define ICON_MS_RIPPLES \"\\xee\\xa7\\x9b\"\t// U+e9db\n#define ICON_MS_ROBOT \"\\xef\\xa2\\x82\"\t// U+f882\n#define ICON_MS_ROBOT_2 \"\\xef\\x97\\x90\"\t// U+f5d0\n#define ICON_MS_ROCKET \"\\xee\\xae\\xa5\"\t// U+eba5\n#define ICON_MS_ROCKET_LAUNCH \"\\xee\\xae\\x9b\"\t// U+eb9b\n#define ICON_MS_ROLLER_SHADES \"\\xee\\xb0\\x92\"\t// U+ec12\n#define ICON_MS_ROLLER_SHADES_CLOSED \"\\xee\\xb0\\x91\"\t// U+ec11\n#define ICON_MS_ROLLER_SKATING \"\\xee\\xaf\\x8d\"\t// U+ebcd\n#define ICON_MS_ROOFING \"\\xef\\x88\\x81\"\t// U+f201\n#define ICON_MS_ROOM \"\\xef\\x87\\x9b\"\t// U+f1db\n#define ICON_MS_ROOM_PREFERENCES \"\\xef\\x86\\xb8\"\t// U+f1b8\n#define ICON_MS_ROOM_SERVICE \"\\xee\\xad\\x89\"\t// U+eb49\n#define ICON_MS_ROTATE_90_DEGREES_CCW \"\\xee\\x90\\x98\"\t// U+e418\n#define ICON_MS_ROTATE_90_DEGREES_CW \"\\xee\\xaa\\xab\"\t// U+eaab\n#define ICON_MS_ROTATE_LEFT \"\\xee\\x90\\x99\"\t// U+e419\n#define ICON_MS_ROTATE_RIGHT \"\\xee\\x90\\x9a\"\t// U+e41a\n#define ICON_MS_ROUNDABOUT_LEFT \"\\xee\\xae\\x99\"\t// U+eb99\n#define ICON_MS_ROUNDABOUT_RIGHT \"\\xee\\xae\\xa3\"\t// U+eba3\n#define ICON_MS_ROUNDED_CORNER \"\\xee\\xa4\\xa0\"\t// U+e920\n#define ICON_MS_ROUTE \"\\xee\\xab\\x8d\"\t// U+eacd\n#define ICON_MS_ROUTER \"\\xee\\x8c\\xa8\"\t// U+e328\n#define ICON_MS_ROUTINE \"\\xee\\x88\\x8c\"\t// U+e20c\n#define ICON_MS_ROWING \"\\xee\\xa4\\xa1\"\t// U+e921\n#define ICON_MS_RSS_FEED \"\\xee\\x83\\xa5\"\t// U+e0e5\n#define ICON_MS_RSVP \"\\xef\\x81\\x95\"\t// U+f055\n#define ICON_MS_RTT \"\\xee\\xa6\\xad\"\t// U+e9ad\n#define ICON_MS_RUBRIC \"\\xee\\xac\\xa7\"\t// U+eb27\n#define ICON_MS_RULE \"\\xef\\x87\\x82\"\t// U+f1c2\n#define ICON_MS_RULE_FOLDER \"\\xef\\x87\\x89\"\t// U+f1c9\n#define ICON_MS_RULE_SETTINGS \"\\xef\\x99\\x8c\"\t// U+f64c\n#define ICON_MS_RUN_CIRCLE \"\\xee\\xbd\\xaf\"\t// U+ef6f\n#define ICON_MS_RUNNING_WITH_ERRORS \"\\xee\\x94\\x9d\"\t// U+e51d\n#define ICON_MS_RV_HOOKUP \"\\xee\\x99\\x82\"\t// U+e642\n#define ICON_MS_SAFETY_CHECK \"\\xee\\xaf\\xaf\"\t// U+ebef\n#define ICON_MS_SAFETY_CHECK_OFF \"\\xef\\x96\\x9d\"\t// U+f59d\n#define ICON_MS_SAFETY_DIVIDER \"\\xee\\x87\\x8c\"\t// U+e1cc\n#define ICON_MS_SAILING \"\\xee\\x94\\x82\"\t// U+e502\n#define ICON_MS_SALINITY \"\\xef\\xa1\\xb6\"\t// U+f876\n#define ICON_MS_SANITIZER \"\\xef\\x88\\x9d\"\t// U+f21d\n#define ICON_MS_SATELLITE \"\\xee\\x95\\xa2\"\t// U+e562\n#define ICON_MS_SATELLITE_ALT \"\\xee\\xac\\xba\"\t// U+eb3a\n#define ICON_MS_SAUNA \"\\xef\\x9b\\xb7\"\t// U+f6f7\n#define ICON_MS_SAVE \"\\xee\\x85\\xa1\"\t// U+e161\n#define ICON_MS_SAVE_ALT \"\\xef\\x82\\x90\"\t// U+f090\n#define ICON_MS_SAVE_AS \"\\xee\\xad\\xa0\"\t// U+eb60\n#define ICON_MS_SAVED_SEARCH \"\\xee\\xa8\\x91\"\t// U+ea11\n#define ICON_MS_SAVINGS \"\\xee\\x8b\\xab\"\t// U+e2eb\n#define ICON_MS_SCALE \"\\xee\\xad\\x9f\"\t// U+eb5f\n#define ICON_MS_SCAN \"\\xef\\x9d\\x8e\"\t// U+f74e\n#define ICON_MS_SCAN_DELETE \"\\xef\\x9d\\x8f\"\t// U+f74f\n#define ICON_MS_SCANNER \"\\xee\\x8c\\xa9\"\t// U+e329\n#define ICON_MS_SCATTER_PLOT \"\\xee\\x89\\xa8\"\t// U+e268\n#define ICON_MS_SCENE \"\\xee\\x8a\\xa7\"\t// U+e2a7\n#define ICON_MS_SCHEDULE \"\\xee\\xbf\\x96\"\t// U+efd6\n#define ICON_MS_SCHEDULE_SEND \"\\xee\\xa8\\x8a\"\t// U+ea0a\n#define ICON_MS_SCHEMA \"\\xee\\x93\\xbd\"\t// U+e4fd\n#define ICON_MS_SCHOOL \"\\xee\\xa0\\x8c\"\t// U+e80c\n#define ICON_MS_SCIENCE \"\\xee\\xa9\\x8b\"\t// U+ea4b\n#define ICON_MS_SCIENCE_OFF \"\\xef\\x95\\x82\"\t// U+f542\n#define ICON_MS_SCORE \"\\xee\\x89\\xa9\"\t// U+e269\n#define ICON_MS_SCOREBOARD \"\\xee\\xaf\\x90\"\t// U+ebd0\n#define ICON_MS_SCREEN_LOCK_LANDSCAPE \"\\xee\\x86\\xbe\"\t// U+e1be\n#define ICON_MS_SCREEN_LOCK_PORTRAIT \"\\xee\\x86\\xbf\"\t// U+e1bf\n#define ICON_MS_SCREEN_LOCK_ROTATION \"\\xee\\x87\\x80\"\t// U+e1c0\n#define ICON_MS_SCREEN_RECORD \"\\xef\\x99\\xb9\"\t// U+f679\n#define ICON_MS_SCREEN_ROTATION \"\\xee\\x87\\x81\"\t// U+e1c1\n#define ICON_MS_SCREEN_ROTATION_ALT \"\\xee\\xaf\\xae\"\t// U+ebee\n#define ICON_MS_SCREEN_ROTATION_UP \"\\xef\\x99\\xb8\"\t// U+f678\n#define ICON_MS_SCREEN_SEARCH_DESKTOP \"\\xee\\xbd\\xb0\"\t// U+ef70\n#define ICON_MS_SCREEN_SHARE \"\\xee\\x83\\xa2\"\t// U+e0e2\n#define ICON_MS_SCREENSHOT \"\\xef\\x81\\x96\"\t// U+f056\n#define ICON_MS_SCREENSHOT_FRAME \"\\xef\\x99\\xb7\"\t// U+f677\n#define ICON_MS_SCREENSHOT_KEYBOARD \"\\xef\\x9f\\x93\"\t// U+f7d3\n#define ICON_MS_SCREENSHOT_MONITOR \"\\xee\\xb0\\x88\"\t// U+ec08\n#define ICON_MS_SCREENSHOT_REGION \"\\xef\\x9f\\x92\"\t// U+f7d2\n#define ICON_MS_SCREENSHOT_TABLET \"\\xef\\x9a\\x97\"\t// U+f697\n#define ICON_MS_SCROLLABLE_HEADER \"\\xee\\xa7\\x9c\"\t// U+e9dc\n#define ICON_MS_SCUBA_DIVING \"\\xee\\xaf\\x8e\"\t// U+ebce\n#define ICON_MS_SD \"\\xee\\xa7\\x9d\"\t// U+e9dd\n#define ICON_MS_SD_CARD \"\\xee\\x98\\xa3\"\t// U+e623\n#define ICON_MS_SD_CARD_ALERT \"\\xef\\x81\\x97\"\t// U+f057\n#define ICON_MS_SD_STORAGE \"\\xee\\x98\\xa3\"\t// U+e623\n#define ICON_MS_SDK \"\\xee\\x9c\\xa0\"\t// U+e720\n#define ICON_MS_SEARCH \"\\xee\\xa2\\xb6\"\t// U+e8b6\n#define ICON_MS_SEARCH_CHECK \"\\xef\\xa0\\x80\"\t// U+f800\n#define ICON_MS_SEARCH_HANDS_FREE \"\\xee\\x9a\\x96\"\t// U+e696\n#define ICON_MS_SEARCH_INSIGHTS \"\\xef\\x92\\xbc\"\t// U+f4bc\n#define ICON_MS_SEARCH_OFF \"\\xee\\xa9\\xb6\"\t// U+ea76\n#define ICON_MS_SECURITY \"\\xee\\x8c\\xaa\"\t// U+e32a\n#define ICON_MS_SECURITY_KEY \"\\xef\\x94\\x83\"\t// U+f503\n#define ICON_MS_SECURITY_UPDATE \"\\xef\\x81\\xb2\"\t// U+f072\n#define ICON_MS_SECURITY_UPDATE_GOOD \"\\xef\\x81\\xb3\"\t// U+f073\n#define ICON_MS_SECURITY_UPDATE_WARNING \"\\xef\\x81\\xb4\"\t// U+f074\n#define ICON_MS_SEGMENT \"\\xee\\xa5\\x8b\"\t// U+e94b\n#define ICON_MS_SELECT \"\\xef\\x9d\\x8d\"\t// U+f74d\n#define ICON_MS_SELECT_ALL \"\\xee\\x85\\xa2\"\t// U+e162\n#define ICON_MS_SELECT_CHECK_BOX \"\\xef\\x87\\xbe\"\t// U+f1fe\n#define ICON_MS_SELECT_TO_SPEAK \"\\xef\\x9f\\x8f\"\t// U+f7cf\n#define ICON_MS_SELECT_WINDOW \"\\xee\\x9b\\xba\"\t// U+e6fa\n#define ICON_MS_SELECT_WINDOW_2 \"\\xef\\x93\\x88\"\t// U+f4c8\n#define ICON_MS_SELECT_WINDOW_OFF \"\\xee\\x94\\x86\"\t// U+e506\n#define ICON_MS_SELF_CARE \"\\xef\\xa1\\xad\"\t// U+f86d\n#define ICON_MS_SELF_IMPROVEMENT \"\\xee\\xa9\\xb8\"\t// U+ea78\n#define ICON_MS_SELL \"\\xef\\x81\\x9b\"\t// U+f05b\n#define ICON_MS_SEND \"\\xee\\x85\\xa3\"\t// U+e163\n#define ICON_MS_SEND_AND_ARCHIVE \"\\xee\\xa8\\x8c\"\t// U+ea0c\n#define ICON_MS_SEND_MONEY \"\\xee\\xa2\\xb7\"\t// U+e8b7\n#define ICON_MS_SEND_TIME_EXTENSION \"\\xee\\xab\\x9b\"\t// U+eadb\n#define ICON_MS_SEND_TO_MOBILE \"\\xef\\x81\\x9c\"\t// U+f05c\n#define ICON_MS_SENSOR_DOOR \"\\xef\\x86\\xb5\"\t// U+f1b5\n#define ICON_MS_SENSOR_OCCUPIED \"\\xee\\xb0\\x90\"\t// U+ec10\n#define ICON_MS_SENSOR_WINDOW \"\\xef\\x86\\xb4\"\t// U+f1b4\n#define ICON_MS_SENSORS \"\\xee\\x94\\x9e\"\t// U+e51e\n#define ICON_MS_SENSORS_KRX \"\\xef\\x95\\x96\"\t// U+f556\n#define ICON_MS_SENSORS_KRX_OFF \"\\xef\\x94\\x95\"\t// U+f515\n#define ICON_MS_SENSORS_OFF \"\\xee\\x94\\x9f\"\t// U+e51f\n#define ICON_MS_SENTIMENT_CALM \"\\xef\\x9a\\xa7\"\t// U+f6a7\n#define ICON_MS_SENTIMENT_CONTENT \"\\xef\\x9a\\xa6\"\t// U+f6a6\n#define ICON_MS_SENTIMENT_DISSATISFIED \"\\xee\\xa0\\x91\"\t// U+e811\n#define ICON_MS_SENTIMENT_EXCITED \"\\xef\\x9a\\xa5\"\t// U+f6a5\n#define ICON_MS_SENTIMENT_EXTREMELY_DISSATISFIED \"\\xef\\x86\\x94\"\t// U+f194\n#define ICON_MS_SENTIMENT_FRUSTRATED \"\\xef\\x9a\\xa4\"\t// U+f6a4\n#define ICON_MS_SENTIMENT_NEUTRAL \"\\xee\\xa0\\x92\"\t// U+e812\n#define ICON_MS_SENTIMENT_SAD \"\\xef\\x9a\\xa3\"\t// U+f6a3\n#define ICON_MS_SENTIMENT_SATISFIED \"\\xee\\xa0\\x93\"\t// U+e813\n#define ICON_MS_SENTIMENT_SATISFIED_ALT \"\\xee\\xa0\\x93\"\t// U+e813\n#define ICON_MS_SENTIMENT_STRESSED \"\\xef\\x9a\\xa2\"\t// U+f6a2\n#define ICON_MS_SENTIMENT_VERY_DISSATISFIED \"\\xee\\xa0\\x94\"\t// U+e814\n#define ICON_MS_SENTIMENT_VERY_SATISFIED \"\\xee\\xa0\\x95\"\t// U+e815\n#define ICON_MS_SENTIMENT_WORRIED \"\\xef\\x9a\\xa1\"\t// U+f6a1\n#define ICON_MS_SERIF \"\\xef\\x92\\xac\"\t// U+f4ac\n#define ICON_MS_SERVICE_TOOLBOX \"\\xee\\x9c\\x97\"\t// U+e717\n#define ICON_MS_SET_MEAL \"\\xef\\x87\\xaa\"\t// U+f1ea\n#define ICON_MS_SETTINGS \"\\xee\\xa2\\xb8\"\t// U+e8b8\n#define ICON_MS_SETTINGS_ACCESSIBILITY \"\\xef\\x81\\x9d\"\t// U+f05d\n#define ICON_MS_SETTINGS_ACCOUNT_BOX \"\\xef\\xa0\\xb5\"\t// U+f835\n#define ICON_MS_SETTINGS_ALERT \"\\xef\\x85\\x83\"\t// U+f143\n#define ICON_MS_SETTINGS_APPLICATIONS \"\\xee\\xa2\\xb9\"\t// U+e8b9\n#define ICON_MS_SETTINGS_B_ROLL \"\\xef\\x98\\xa5\"\t// U+f625\n#define ICON_MS_SETTINGS_BACKUP_RESTORE \"\\xee\\xa2\\xba\"\t// U+e8ba\n#define ICON_MS_SETTINGS_BLUETOOTH \"\\xee\\xa2\\xbb\"\t// U+e8bb\n#define ICON_MS_SETTINGS_BRIGHTNESS \"\\xee\\xa2\\xbd\"\t// U+e8bd\n#define ICON_MS_SETTINGS_CELL \"\\xee\\xa2\\xbc\"\t// U+e8bc\n#define ICON_MS_SETTINGS_CINEMATIC_BLUR \"\\xef\\x98\\xa4\"\t// U+f624\n#define ICON_MS_SETTINGS_ETHERNET \"\\xee\\xa2\\xbe\"\t// U+e8be\n#define ICON_MS_SETTINGS_HEART \"\\xef\\x94\\xa2\"\t// U+f522\n#define ICON_MS_SETTINGS_INPUT_ANTENNA \"\\xee\\xa2\\xbf\"\t// U+e8bf\n#define ICON_MS_SETTINGS_INPUT_COMPONENT \"\\xee\\xa3\\x81\"\t// U+e8c1\n#define ICON_MS_SETTINGS_INPUT_COMPOSITE \"\\xee\\xa3\\x81\"\t// U+e8c1\n#define ICON_MS_SETTINGS_INPUT_HDMI \"\\xee\\xa3\\x82\"\t// U+e8c2\n#define ICON_MS_SETTINGS_INPUT_SVIDEO \"\\xee\\xa3\\x83\"\t// U+e8c3\n#define ICON_MS_SETTINGS_MOTION_MODE \"\\xef\\xa0\\xb3\"\t// U+f833\n#define ICON_MS_SETTINGS_NIGHT_SIGHT \"\\xef\\xa0\\xb2\"\t// U+f832\n#define ICON_MS_SETTINGS_OVERSCAN \"\\xee\\xa3\\x84\"\t// U+e8c4\n#define ICON_MS_SETTINGS_PANORAMA \"\\xef\\xa0\\xb1\"\t// U+f831\n#define ICON_MS_SETTINGS_PHONE \"\\xee\\xa3\\x85\"\t// U+e8c5\n#define ICON_MS_SETTINGS_PHOTO_CAMERA \"\\xef\\xa0\\xb4\"\t// U+f834\n#define ICON_MS_SETTINGS_POWER \"\\xee\\xa3\\x86\"\t// U+e8c6\n#define ICON_MS_SETTINGS_REMOTE \"\\xee\\xa3\\x87\"\t// U+e8c7\n#define ICON_MS_SETTINGS_SLOW_MOTION \"\\xef\\x98\\xa3\"\t// U+f623\n#define ICON_MS_SETTINGS_SUGGEST \"\\xef\\x81\\x9e\"\t// U+f05e\n#define ICON_MS_SETTINGS_SYSTEM_DAYDREAM \"\\xee\\x87\\x83\"\t// U+e1c3\n#define ICON_MS_SETTINGS_TIMELAPSE \"\\xef\\x98\\xa2\"\t// U+f622\n#define ICON_MS_SETTINGS_VIDEO_CAMERA \"\\xef\\x98\\xa1\"\t// U+f621\n#define ICON_MS_SETTINGS_VOICE \"\\xee\\xa3\\x88\"\t// U+e8c8\n#define ICON_MS_SETTOP_COMPONENT \"\\xee\\x8a\\xac\"\t// U+e2ac\n#define ICON_MS_SEVERE_COLD \"\\xee\\xaf\\x93\"\t// U+ebd3\n#define ICON_MS_SHADOW \"\\xee\\xa7\\x9f\"\t// U+e9df\n#define ICON_MS_SHADOW_ADD \"\\xef\\x96\\x84\"\t// U+f584\n#define ICON_MS_SHADOW_MINUS \"\\xef\\x96\\x83\"\t// U+f583\n#define ICON_MS_SHAPE_LINE \"\\xef\\xa3\\x93\"\t// U+f8d3\n#define ICON_MS_SHAPE_RECOGNITION \"\\xee\\xac\\x81\"\t// U+eb01\n#define ICON_MS_SHAPES \"\\xee\\x98\\x82\"\t// U+e602\n#define ICON_MS_SHARE \"\\xee\\xa0\\x8d\"\t// U+e80d\n#define ICON_MS_SHARE_LOCATION \"\\xef\\x81\\x9f\"\t// U+f05f\n#define ICON_MS_SHARE_OFF \"\\xef\\x9b\\x8b\"\t// U+f6cb\n#define ICON_MS_SHARE_REVIEWS \"\\xef\\xa2\\xa4\"\t// U+f8a4\n#define ICON_MS_SHARE_WINDOWS \"\\xef\\x98\\x93\"\t// U+f613\n#define ICON_MS_SHEETS_RTL \"\\xef\\xa0\\xa3\"\t// U+f823\n#define ICON_MS_SHELF_AUTO_HIDE \"\\xef\\x9c\\x83\"\t// U+f703\n#define ICON_MS_SHELF_POSITION \"\\xef\\x9c\\x82\"\t// U+f702\n#define ICON_MS_SHELVES \"\\xef\\xa1\\xae\"\t// U+f86e\n#define ICON_MS_SHIELD \"\\xee\\xa7\\xa0\"\t// U+e9e0\n#define ICON_MS_SHIELD_LOCK \"\\xef\\x9a\\x86\"\t// U+f686\n#define ICON_MS_SHIELD_LOCKED \"\\xef\\x96\\x92\"\t// U+f592\n#define ICON_MS_SHIELD_MOON \"\\xee\\xaa\\xa9\"\t// U+eaa9\n#define ICON_MS_SHIELD_PERSON \"\\xef\\x99\\x90\"\t// U+f650\n#define ICON_MS_SHIELD_QUESTION \"\\xef\\x94\\xa9\"\t// U+f529\n#define ICON_MS_SHIELD_WITH_HEART \"\\xee\\x9e\\x8f\"\t// U+e78f\n#define ICON_MS_SHIELD_WITH_HOUSE \"\\xee\\x9e\\x8d\"\t// U+e78d\n#define ICON_MS_SHIFT \"\\xee\\x97\\xb2\"\t// U+e5f2\n#define ICON_MS_SHIFT_LOCK \"\\xef\\x9e\\xae\"\t// U+f7ae\n#define ICON_MS_SHIFT_LOCK_OFF \"\\xef\\x92\\x83\"\t// U+f483\n#define ICON_MS_SHOP \"\\xee\\xa3\\x89\"\t// U+e8c9\n#define ICON_MS_SHOP_2 \"\\xee\\xa3\\x8a\"\t// U+e8ca\n#define ICON_MS_SHOP_TWO \"\\xee\\xa3\\x8a\"\t// U+e8ca\n#define ICON_MS_SHOPPING_BAG \"\\xef\\x87\\x8c\"\t// U+f1cc\n#define ICON_MS_SHOPPING_BASKET \"\\xee\\xa3\\x8b\"\t// U+e8cb\n#define ICON_MS_SHOPPING_CART \"\\xee\\xa3\\x8c\"\t// U+e8cc\n#define ICON_MS_SHOPPING_CART_CHECKOUT \"\\xee\\xae\\x88\"\t// U+eb88\n#define ICON_MS_SHOPPING_CART_OFF \"\\xef\\x93\\xb7\"\t// U+f4f7\n#define ICON_MS_SHOPPINGMODE \"\\xee\\xbe\\xb7\"\t// U+efb7\n#define ICON_MS_SHORT_STAY \"\\xee\\x93\\x90\"\t// U+e4d0\n#define ICON_MS_SHORT_TEXT \"\\xee\\x89\\xa1\"\t// U+e261\n#define ICON_MS_SHORTCUT \"\\xef\\x95\\xba\"\t// U+f57a\n#define ICON_MS_SHOW_CHART \"\\xee\\x9b\\xa1\"\t// U+e6e1\n#define ICON_MS_SHOWER \"\\xef\\x81\\xa1\"\t// U+f061\n#define ICON_MS_SHUFFLE \"\\xee\\x81\\x83\"\t// U+e043\n#define ICON_MS_SHUFFLE_ON \"\\xee\\xa7\\xa1\"\t// U+e9e1\n#define ICON_MS_SHUTTER_SPEED \"\\xee\\x90\\xbd\"\t// U+e43d\n#define ICON_MS_SHUTTER_SPEED_ADD \"\\xef\\x95\\xbe\"\t// U+f57e\n#define ICON_MS_SHUTTER_SPEED_MINUS \"\\xef\\x95\\xbd\"\t// U+f57d\n#define ICON_MS_SICK \"\\xef\\x88\\xa0\"\t// U+f220\n#define ICON_MS_SIDE_NAVIGATION \"\\xee\\xa7\\xa2\"\t// U+e9e2\n#define ICON_MS_SIGN_LANGUAGE \"\\xee\\xaf\\xa5\"\t// U+ebe5\n#define ICON_MS_SIGNAL_CELLULAR_0_BAR \"\\xef\\x82\\xa8\"\t// U+f0a8\n#define ICON_MS_SIGNAL_CELLULAR_1_BAR \"\\xef\\x82\\xa9\"\t// U+f0a9\n#define ICON_MS_SIGNAL_CELLULAR_2_BAR \"\\xef\\x82\\xaa\"\t// U+f0aa\n#define ICON_MS_SIGNAL_CELLULAR_3_BAR \"\\xef\\x82\\xab\"\t// U+f0ab\n#define ICON_MS_SIGNAL_CELLULAR_4_BAR \"\\xee\\x87\\x88\"\t// U+e1c8\n#define ICON_MS_SIGNAL_CELLULAR_ADD \"\\xef\\x9e\\xa9\"\t// U+f7a9\n#define ICON_MS_SIGNAL_CELLULAR_ALT \"\\xee\\x88\\x82\"\t// U+e202\n#define ICON_MS_SIGNAL_CELLULAR_ALT_1_BAR \"\\xee\\xaf\\x9f\"\t// U+ebdf\n#define ICON_MS_SIGNAL_CELLULAR_ALT_2_BAR \"\\xee\\xaf\\xa3\"\t// U+ebe3\n#define ICON_MS_SIGNAL_CELLULAR_CONNECTED_NO_INTERNET_0_BAR \"\\xef\\x82\\xac\"\t// U+f0ac\n#define ICON_MS_SIGNAL_CELLULAR_CONNECTED_NO_INTERNET_4_BAR \"\\xee\\x87\\x8d\"\t// U+e1cd\n#define ICON_MS_SIGNAL_CELLULAR_NO_SIM \"\\xee\\x87\\x8e\"\t// U+e1ce\n#define ICON_MS_SIGNAL_CELLULAR_NODATA \"\\xef\\x81\\xa2\"\t// U+f062\n#define ICON_MS_SIGNAL_CELLULAR_NULL \"\\xee\\x87\\x8f\"\t// U+e1cf\n#define ICON_MS_SIGNAL_CELLULAR_OFF \"\\xee\\x87\\x90\"\t// U+e1d0\n#define ICON_MS_SIGNAL_CELLULAR_PAUSE \"\\xef\\x96\\xa7\"\t// U+f5a7\n#define ICON_MS_SIGNAL_DISCONNECTED \"\\xef\\x88\\xb9\"\t// U+f239\n#define ICON_MS_SIGNAL_WIFI_0_BAR \"\\xef\\x82\\xb0\"\t// U+f0b0\n#define ICON_MS_SIGNAL_WIFI_4_BAR \"\\xef\\x81\\xa5\"\t// U+f065\n#define ICON_MS_SIGNAL_WIFI_4_BAR_LOCK \"\\xee\\x87\\xa1\"\t// U+e1e1\n#define ICON_MS_SIGNAL_WIFI_BAD \"\\xef\\x81\\xa4\"\t// U+f064\n#define ICON_MS_SIGNAL_WIFI_CONNECTED_NO_INTERNET_4 \"\\xef\\x81\\xa4\"\t// U+f064\n#define ICON_MS_SIGNAL_WIFI_OFF \"\\xee\\x87\\x9a\"\t// U+e1da\n#define ICON_MS_SIGNAL_WIFI_STATUSBAR_4_BAR \"\\xef\\x81\\xa5\"\t// U+f065\n#define ICON_MS_SIGNAL_WIFI_STATUSBAR_NOT_CONNECTED \"\\xef\\x83\\xaf\"\t// U+f0ef\n#define ICON_MS_SIGNAL_WIFI_STATUSBAR_NULL \"\\xef\\x81\\xa7\"\t// U+f067\n#define ICON_MS_SIGNATURE \"\\xef\\x9d\\x8c\"\t// U+f74c\n#define ICON_MS_SIGNPOST \"\\xee\\xae\\x91\"\t// U+eb91\n#define ICON_MS_SIM_CARD \"\\xee\\x8c\\xab\"\t// U+e32b\n#define ICON_MS_SIM_CARD_ALERT \"\\xef\\x81\\x97\"\t// U+f057\n#define ICON_MS_SIM_CARD_DOWNLOAD \"\\xef\\x81\\xa8\"\t// U+f068\n#define ICON_MS_SINGLE_BED \"\\xee\\xa9\\x88\"\t// U+ea48\n#define ICON_MS_SIP \"\\xef\\x81\\xa9\"\t// U+f069\n#define ICON_MS_SKATEBOARDING \"\\xee\\x94\\x91\"\t// U+e511\n#define ICON_MS_SKELETON \"\\xef\\xa2\\x99\"\t// U+f899\n#define ICON_MS_SKILLET \"\\xef\\x95\\x83\"\t// U+f543\n#define ICON_MS_SKILLET_COOKTOP \"\\xef\\x95\\x84\"\t// U+f544\n#define ICON_MS_SKIP_NEXT \"\\xee\\x81\\x84\"\t// U+e044\n#define ICON_MS_SKIP_PREVIOUS \"\\xee\\x81\\x85\"\t// U+e045\n#define ICON_MS_SKULL \"\\xef\\xa2\\x9a\"\t// U+f89a\n#define ICON_MS_SLAB_SERIF \"\\xef\\x92\\xab\"\t// U+f4ab\n#define ICON_MS_SLEDDING \"\\xee\\x94\\x92\"\t// U+e512\n#define ICON_MS_SLEEP \"\\xee\\x88\\x93\"\t// U+e213\n#define ICON_MS_SLEEP_SCORE \"\\xef\\x9a\\xb7\"\t// U+f6b7\n#define ICON_MS_SLIDE_LIBRARY \"\\xef\\xa0\\xa2\"\t// U+f822\n#define ICON_MS_SLIDERS \"\\xee\\xa7\\xa3\"\t// U+e9e3\n#define ICON_MS_SLIDESHOW \"\\xee\\x90\\x9b\"\t// U+e41b\n#define ICON_MS_SLOW_MOTION_VIDEO \"\\xee\\x81\\xa8\"\t// U+e068\n#define ICON_MS_SMART_BUTTON \"\\xef\\x87\\x81\"\t// U+f1c1\n#define ICON_MS_SMART_CARD_READER \"\\xef\\x92\\xa5\"\t// U+f4a5\n#define ICON_MS_SMART_CARD_READER_OFF \"\\xef\\x92\\xa6\"\t// U+f4a6\n#define ICON_MS_SMART_DISPLAY \"\\xef\\x81\\xaa\"\t// U+f06a\n#define ICON_MS_SMART_OUTLET \"\\xee\\xa1\\x84\"\t// U+e844\n#define ICON_MS_SMART_SCREEN \"\\xef\\x81\\xab\"\t// U+f06b\n#define ICON_MS_SMART_TOY \"\\xef\\x81\\xac\"\t// U+f06c\n#define ICON_MS_SMARTPHONE \"\\xee\\x8c\\xac\"\t// U+e32c\n#define ICON_MS_SMB_SHARE \"\\xef\\x9d\\x8b\"\t// U+f74b\n#define ICON_MS_SMOKE_FREE \"\\xee\\xad\\x8a\"\t// U+eb4a\n#define ICON_MS_SMOKING_ROOMS \"\\xee\\xad\\x8b\"\t// U+eb4b\n#define ICON_MS_SMS \"\\xee\\x98\\xa5\"\t// U+e625\n#define ICON_MS_SMS_FAILED \"\\xee\\xa1\\xbf\"\t// U+e87f\n#define ICON_MS_SNIPPET_FOLDER \"\\xef\\x87\\x87\"\t// U+f1c7\n#define ICON_MS_SNOOZE \"\\xee\\x81\\x86\"\t// U+e046\n#define ICON_MS_SNOWBOARDING \"\\xee\\x94\\x93\"\t// U+e513\n#define ICON_MS_SNOWING \"\\xee\\xa0\\x8f\"\t// U+e80f\n#define ICON_MS_SNOWING_HEAVY \"\\xef\\x98\\x9c\"\t// U+f61c\n#define ICON_MS_SNOWMOBILE \"\\xee\\x94\\x83\"\t// U+e503\n#define ICON_MS_SNOWSHOEING \"\\xee\\x94\\x94\"\t// U+e514\n#define ICON_MS_SOAP \"\\xef\\x86\\xb2\"\t// U+f1b2\n#define ICON_MS_SOCIAL_DISTANCE \"\\xee\\x87\\x8b\"\t// U+e1cb\n#define ICON_MS_SOCIAL_LEADERBOARD \"\\xef\\x9a\\xa0\"\t// U+f6a0\n#define ICON_MS_SOLAR_POWER \"\\xee\\xb0\\x8f\"\t// U+ec0f\n#define ICON_MS_SORT \"\\xee\\x85\\xa4\"\t// U+e164\n#define ICON_MS_SORT_BY_ALPHA \"\\xee\\x81\\x93\"\t// U+e053\n#define ICON_MS_SOS \"\\xee\\xaf\\xb7\"\t// U+ebf7\n#define ICON_MS_SOUND_DETECTION_DOG_BARKING \"\\xef\\x85\\x89\"\t// U+f149\n#define ICON_MS_SOUND_DETECTION_GLASS_BREAK \"\\xef\\x85\\x8a\"\t// U+f14a\n#define ICON_MS_SOUND_DETECTION_LOUD_SOUND \"\\xef\\x85\\x8b\"\t// U+f14b\n#define ICON_MS_SOUND_SAMPLER \"\\xef\\x9a\\xb4\"\t// U+f6b4\n#define ICON_MS_SOUP_KITCHEN \"\\xee\\x9f\\x93\"\t// U+e7d3\n#define ICON_MS_SOURCE \"\\xef\\x87\\x88\"\t// U+f1c8\n#define ICON_MS_SOURCE_ENVIRONMENT \"\\xee\\x94\\xa7\"\t// U+e527\n#define ICON_MS_SOURCE_NOTES \"\\xee\\x84\\xad\"\t// U+e12d\n#define ICON_MS_SOUTH \"\\xef\\x87\\xa3\"\t// U+f1e3\n#define ICON_MS_SOUTH_AMERICA \"\\xee\\x9f\\xa4\"\t// U+e7e4\n#define ICON_MS_SOUTH_EAST \"\\xef\\x87\\xa4\"\t// U+f1e4\n#define ICON_MS_SOUTH_WEST \"\\xef\\x87\\xa5\"\t// U+f1e5\n#define ICON_MS_SPA \"\\xee\\xad\\x8c\"\t// U+eb4c\n#define ICON_MS_SPACE_BAR \"\\xee\\x89\\x96\"\t// U+e256\n#define ICON_MS_SPACE_DASHBOARD \"\\xee\\x99\\xab\"\t// U+e66b\n#define ICON_MS_SPATIAL_AUDIO \"\\xee\\xaf\\xab\"\t// U+ebeb\n#define ICON_MS_SPATIAL_AUDIO_OFF \"\\xee\\xaf\\xa8\"\t// U+ebe8\n#define ICON_MS_SPATIAL_SPEAKER \"\\xef\\x93\\x8f\"\t// U+f4cf\n#define ICON_MS_SPATIAL_TRACKING \"\\xee\\xaf\\xaa\"\t// U+ebea\n#define ICON_MS_SPEAKER \"\\xee\\x8c\\xad\"\t// U+e32d\n#define ICON_MS_SPEAKER_GROUP \"\\xee\\x8c\\xae\"\t// U+e32e\n#define ICON_MS_SPEAKER_NOTES \"\\xee\\xa3\\x8d\"\t// U+e8cd\n#define ICON_MS_SPEAKER_NOTES_OFF \"\\xee\\xa4\\xaa\"\t// U+e92a\n#define ICON_MS_SPEAKER_PHONE \"\\xee\\x83\\x92\"\t// U+e0d2\n#define ICON_MS_SPECIAL_CHARACTER \"\\xef\\x9d\\x8a\"\t// U+f74a\n#define ICON_MS_SPECIFIC_GRAVITY \"\\xef\\xa1\\xb2\"\t// U+f872\n#define ICON_MS_SPEECH_TO_TEXT \"\\xef\\xa2\\xa7\"\t// U+f8a7\n#define ICON_MS_SPEED \"\\xee\\xa7\\xa4\"\t// U+e9e4\n#define ICON_MS_SPEED_0_25 \"\\xef\\x93\\x94\"\t// U+f4d4\n#define ICON_MS_SPEED_0_2X \"\\xef\\x92\\x98\"\t// U+f498\n#define ICON_MS_SPEED_0_5 \"\\xef\\x93\\xa2\"\t// U+f4e2\n#define ICON_MS_SPEED_0_5X \"\\xef\\x92\\x97\"\t// U+f497\n#define ICON_MS_SPEED_0_75 \"\\xef\\x93\\x93\"\t// U+f4d3\n#define ICON_MS_SPEED_0_7X \"\\xef\\x92\\x96\"\t// U+f496\n#define ICON_MS_SPEED_1_2 \"\\xef\\x93\\xa1\"\t// U+f4e1\n#define ICON_MS_SPEED_1_25 \"\\xef\\x93\\x92\"\t// U+f4d2\n#define ICON_MS_SPEED_1_2X \"\\xef\\x92\\x95\"\t// U+f495\n#define ICON_MS_SPEED_1_5 \"\\xef\\x93\\xa0\"\t// U+f4e0\n#define ICON_MS_SPEED_1_5X \"\\xef\\x92\\x94\"\t// U+f494\n#define ICON_MS_SPEED_1_75 \"\\xef\\x93\\x91\"\t// U+f4d1\n#define ICON_MS_SPEED_1_7X \"\\xef\\x92\\x93\"\t// U+f493\n#define ICON_MS_SPEED_2X \"\\xef\\x93\\xab\"\t// U+f4eb\n#define ICON_MS_SPELLCHECK \"\\xee\\xa3\\x8e\"\t// U+e8ce\n#define ICON_MS_SPLITSCREEN \"\\xef\\x81\\xad\"\t// U+f06d\n#define ICON_MS_SPLITSCREEN_ADD \"\\xef\\x93\\xbd\"\t// U+f4fd\n#define ICON_MS_SPLITSCREEN_BOTTOM \"\\xef\\x99\\xb6\"\t// U+f676\n#define ICON_MS_SPLITSCREEN_LEFT \"\\xef\\x99\\xb5\"\t// U+f675\n#define ICON_MS_SPLITSCREEN_RIGHT \"\\xef\\x99\\xb4\"\t// U+f674\n#define ICON_MS_SPLITSCREEN_TOP \"\\xef\\x99\\xb3\"\t// U+f673\n#define ICON_MS_SPLITSCREEN_VERTICAL_ADD \"\\xef\\x93\\xbc\"\t// U+f4fc\n#define ICON_MS_SPO2 \"\\xef\\x9b\\x9b\"\t// U+f6db\n#define ICON_MS_SPOKE \"\\xee\\xa6\\xa7\"\t// U+e9a7\n#define ICON_MS_SPORTS \"\\xee\\xa8\\xb0\"\t// U+ea30\n#define ICON_MS_SPORTS_AND_OUTDOORS \"\\xee\\xbe\\xb8\"\t// U+efb8\n#define ICON_MS_SPORTS_BAR \"\\xef\\x87\\xb3\"\t// U+f1f3\n#define ICON_MS_SPORTS_BASEBALL \"\\xee\\xa9\\x91\"\t// U+ea51\n#define ICON_MS_SPORTS_BASKETBALL \"\\xee\\xa8\\xa6\"\t// U+ea26\n#define ICON_MS_SPORTS_CRICKET \"\\xee\\xa8\\xa7\"\t// U+ea27\n#define ICON_MS_SPORTS_ESPORTS \"\\xee\\xa8\\xa8\"\t// U+ea28\n#define ICON_MS_SPORTS_FOOTBALL \"\\xee\\xa8\\xa9\"\t// U+ea29\n#define ICON_MS_SPORTS_GOLF \"\\xee\\xa8\\xaa\"\t// U+ea2a\n#define ICON_MS_SPORTS_GYMNASTICS \"\\xee\\xaf\\x84\"\t// U+ebc4\n#define ICON_MS_SPORTS_HANDBALL \"\\xee\\xa8\\xb3\"\t// U+ea33\n#define ICON_MS_SPORTS_HOCKEY \"\\xee\\xa8\\xab\"\t// U+ea2b\n#define ICON_MS_SPORTS_KABADDI \"\\xee\\xa8\\xb4\"\t// U+ea34\n#define ICON_MS_SPORTS_MARTIAL_ARTS \"\\xee\\xab\\xa9\"\t// U+eae9\n#define ICON_MS_SPORTS_MMA \"\\xee\\xa8\\xac\"\t// U+ea2c\n#define ICON_MS_SPORTS_MOTORSPORTS \"\\xee\\xa8\\xad\"\t// U+ea2d\n#define ICON_MS_SPORTS_RUGBY \"\\xee\\xa8\\xae\"\t// U+ea2e\n#define ICON_MS_SPORTS_SCORE \"\\xef\\x81\\xae\"\t// U+f06e\n#define ICON_MS_SPORTS_SOCCER \"\\xee\\xa8\\xaf\"\t// U+ea2f\n#define ICON_MS_SPORTS_TENNIS \"\\xee\\xa8\\xb2\"\t// U+ea32\n#define ICON_MS_SPORTS_VOLLEYBALL \"\\xee\\xa8\\xb1\"\t// U+ea31\n#define ICON_MS_SPRINKLER \"\\xee\\x8a\\x9a\"\t// U+e29a\n#define ICON_MS_SPRINT \"\\xef\\xa0\\x9f\"\t// U+f81f\n#define ICON_MS_SQUARE \"\\xee\\xac\\xb6\"\t// U+eb36\n#define ICON_MS_SQUARE_FOOT \"\\xee\\xa9\\x89\"\t// U+ea49\n#define ICON_MS_SSID_CHART \"\\xee\\xad\\xa6\"\t// U+eb66\n#define ICON_MS_STACK \"\\xef\\x98\\x89\"\t// U+f609\n#define ICON_MS_STACK_OFF \"\\xef\\x98\\x88\"\t// U+f608\n#define ICON_MS_STACK_STAR \"\\xef\\x98\\x87\"\t// U+f607\n#define ICON_MS_STACKED_BAR_CHART \"\\xee\\xa7\\xa6\"\t// U+e9e6\n#define ICON_MS_STACKED_EMAIL \"\\xee\\x9b\\x87\"\t// U+e6c7\n#define ICON_MS_STACKED_INBOX \"\\xee\\x9b\\x89\"\t// U+e6c9\n#define ICON_MS_STACKED_LINE_CHART \"\\xef\\x88\\xab\"\t// U+f22b\n#define ICON_MS_STACKS \"\\xef\\x94\\x80\"\t// U+f500\n#define ICON_MS_STADIA_CONTROLLER \"\\xef\\x84\\xb5\"\t// U+f135\n#define ICON_MS_STADIUM \"\\xee\\xae\\x90\"\t// U+eb90\n#define ICON_MS_STAIRS \"\\xef\\x86\\xa9\"\t// U+f1a9\n#define ICON_MS_STAR \"\\xef\\x82\\x9a\"\t// U+f09a\n#define ICON_MS_STAR_BORDER \"\\xef\\x82\\x9a\"\t// U+f09a\n#define ICON_MS_STAR_BORDER_PURPLE500 \"\\xef\\x82\\x9a\"\t// U+f09a\n#define ICON_MS_STAR_HALF \"\\xee\\xa0\\xb9\"\t// U+e839\n#define ICON_MS_STAR_OUTLINE \"\\xef\\x82\\x9a\"\t// U+f09a\n#define ICON_MS_STAR_PURPLE500 \"\\xef\\x82\\x9a\"\t// U+f09a\n#define ICON_MS_STAR_RATE \"\\xef\\x83\\xac\"\t// U+f0ec\n#define ICON_MS_STAR_RATE_HALF \"\\xee\\xb1\\x85\"\t// U+ec45\n#define ICON_MS_STARS \"\\xee\\xa3\\x90\"\t// U+e8d0\n#define ICON_MS_START \"\\xee\\x82\\x89\"\t// U+e089\n#define ICON_MS_STAT_0 \"\\xee\\x9a\\x97\"\t// U+e697\n#define ICON_MS_STAT_1 \"\\xee\\x9a\\x98\"\t// U+e698\n#define ICON_MS_STAT_2 \"\\xee\\x9a\\x99\"\t// U+e699\n#define ICON_MS_STAT_3 \"\\xee\\x9a\\x9a\"\t// U+e69a\n#define ICON_MS_STAT_MINUS_1 \"\\xee\\x9a\\x9b\"\t// U+e69b\n#define ICON_MS_STAT_MINUS_2 \"\\xee\\x9a\\x9c\"\t// U+e69c\n#define ICON_MS_STAT_MINUS_3 \"\\xee\\x9a\\x9d\"\t// U+e69d\n#define ICON_MS_STAY_CURRENT_LANDSCAPE \"\\xee\\x83\\x93\"\t// U+e0d3\n#define ICON_MS_STAY_CURRENT_PORTRAIT \"\\xee\\x83\\x94\"\t// U+e0d4\n#define ICON_MS_STAY_PRIMARY_LANDSCAPE \"\\xee\\x83\\x95\"\t// U+e0d5\n#define ICON_MS_STAY_PRIMARY_PORTRAIT \"\\xee\\x83\\x96\"\t// U+e0d6\n#define ICON_MS_STEP \"\\xef\\x9b\\xbe\"\t// U+f6fe\n#define ICON_MS_STEP_INTO \"\\xef\\x9c\\x81\"\t// U+f701\n#define ICON_MS_STEP_OUT \"\\xef\\x9c\\x80\"\t// U+f700\n#define ICON_MS_STEP_OVER \"\\xef\\x9b\\xbf\"\t// U+f6ff\n#define ICON_MS_STEPPERS \"\\xee\\xa7\\xa7\"\t// U+e9e7\n#define ICON_MS_STEPS \"\\xef\\x9b\\x9a\"\t// U+f6da\n#define ICON_MS_STETHOSCOPE \"\\xef\\xa0\\x85\"\t// U+f805\n#define ICON_MS_STETHOSCOPE_ARROW \"\\xef\\xa0\\x87\"\t// U+f807\n#define ICON_MS_STETHOSCOPE_CHECK \"\\xef\\xa0\\x86\"\t// U+f806\n#define ICON_MS_STICKY_NOTE \"\\xee\\xa7\\xa8\"\t// U+e9e8\n#define ICON_MS_STICKY_NOTE_2 \"\\xef\\x87\\xbc\"\t// U+f1fc\n#define ICON_MS_STOCK_MEDIA \"\\xef\\x95\\xb0\"\t// U+f570\n#define ICON_MS_STOCKPOT \"\\xef\\x95\\x85\"\t// U+f545\n#define ICON_MS_STOP \"\\xee\\x81\\x87\"\t// U+e047\n#define ICON_MS_STOP_CIRCLE \"\\xee\\xbd\\xb1\"\t// U+ef71\n#define ICON_MS_STOP_SCREEN_SHARE \"\\xee\\x83\\xa3\"\t// U+e0e3\n#define ICON_MS_STORAGE \"\\xee\\x87\\x9b\"\t// U+e1db\n#define ICON_MS_STORE \"\\xee\\xa3\\x91\"\t// U+e8d1\n#define ICON_MS_STORE_MALL_DIRECTORY \"\\xee\\xa3\\x91\"\t// U+e8d1\n#define ICON_MS_STOREFRONT \"\\xee\\xa8\\x92\"\t// U+ea12\n#define ICON_MS_STORM \"\\xef\\x81\\xb0\"\t// U+f070\n#define ICON_MS_STRAIGHT \"\\xee\\xae\\x95\"\t// U+eb95\n#define ICON_MS_STRAIGHTEN \"\\xee\\x90\\x9c\"\t// U+e41c\n#define ICON_MS_STRATEGY \"\\xef\\x97\\x9f\"\t// U+f5df\n#define ICON_MS_STREAM \"\\xee\\xa7\\xa9\"\t// U+e9e9\n#define ICON_MS_STREAM_APPS \"\\xef\\x9e\\x9f\"\t// U+f79f\n#define ICON_MS_STREETVIEW \"\\xee\\x95\\xae\"\t// U+e56e\n#define ICON_MS_STRESS_MANAGEMENT \"\\xef\\x9b\\x99\"\t// U+f6d9\n#define ICON_MS_STRIKETHROUGH_S \"\\xee\\x89\\x97\"\t// U+e257\n#define ICON_MS_STROKE_FULL \"\\xef\\x9d\\x89\"\t// U+f749\n#define ICON_MS_STROKE_PARTIAL \"\\xef\\x9d\\x88\"\t// U+f748\n#define ICON_MS_STROLLER \"\\xef\\x86\\xae\"\t// U+f1ae\n#define ICON_MS_STYLE \"\\xee\\x90\\x9d\"\t// U+e41d\n#define ICON_MS_STYLER \"\\xee\\x89\\xb3\"\t// U+e273\n#define ICON_MS_STYLUS \"\\xef\\x98\\x84\"\t// U+f604\n#define ICON_MS_STYLUS_LASER_POINTER \"\\xef\\x9d\\x87\"\t// U+f747\n#define ICON_MS_STYLUS_NOTE \"\\xef\\x98\\x83\"\t// U+f603\n#define ICON_MS_SUBDIRECTORY_ARROW_LEFT \"\\xee\\x97\\x99\"\t// U+e5d9\n#define ICON_MS_SUBDIRECTORY_ARROW_RIGHT \"\\xee\\x97\\x9a\"\t// U+e5da\n#define ICON_MS_SUBHEADER \"\\xee\\xa7\\xaa\"\t// U+e9ea\n#define ICON_MS_SUBJECT \"\\xee\\xa3\\x92\"\t// U+e8d2\n#define ICON_MS_SUBSCRIPT \"\\xef\\x84\\x91\"\t// U+f111\n#define ICON_MS_SUBSCRIPTIONS \"\\xee\\x81\\xa4\"\t// U+e064\n#define ICON_MS_SUBTITLES \"\\xee\\x81\\x88\"\t// U+e048\n#define ICON_MS_SUBTITLES_OFF \"\\xee\\xbd\\xb2\"\t// U+ef72\n#define ICON_MS_SUBWAY \"\\xee\\x95\\xaf\"\t// U+e56f\n#define ICON_MS_SUMMARIZE \"\\xef\\x81\\xb1\"\t// U+f071\n#define ICON_MS_SUNNY \"\\xee\\xa0\\x9a\"\t// U+e81a\n#define ICON_MS_SUNNY_SNOWING \"\\xee\\xa0\\x99\"\t// U+e819\n#define ICON_MS_SUPERSCRIPT \"\\xef\\x84\\x92\"\t// U+f112\n#define ICON_MS_SUPERVISED_USER_CIRCLE \"\\xee\\xa4\\xb9\"\t// U+e939\n#define ICON_MS_SUPERVISED_USER_CIRCLE_OFF \"\\xef\\x98\\x8e\"\t// U+f60e\n#define ICON_MS_SUPERVISOR_ACCOUNT \"\\xee\\xa3\\x93\"\t// U+e8d3\n#define ICON_MS_SUPPORT \"\\xee\\xbd\\xb3\"\t// U+ef73\n#define ICON_MS_SUPPORT_AGENT \"\\xef\\x83\\xa2\"\t// U+f0e2\n#define ICON_MS_SURFING \"\\xee\\x94\\x95\"\t// U+e515\n#define ICON_MS_SURGICAL \"\\xee\\x84\\xb1\"\t// U+e131\n#define ICON_MS_SURROUND_SOUND \"\\xee\\x81\\x89\"\t// U+e049\n#define ICON_MS_SWAP_CALLS \"\\xee\\x83\\x97\"\t// U+e0d7\n#define ICON_MS_SWAP_DRIVING_APPS \"\\xee\\x9a\\x9e\"\t// U+e69e\n#define ICON_MS_SWAP_DRIVING_APPS_WHEEL \"\\xee\\x9a\\x9f\"\t// U+e69f\n#define ICON_MS_SWAP_HORIZ \"\\xee\\xa3\\x94\"\t// U+e8d4\n#define ICON_MS_SWAP_HORIZONTAL_CIRCLE \"\\xee\\xa4\\xb3\"\t// U+e933\n#define ICON_MS_SWAP_VERT \"\\xee\\xa3\\x95\"\t// U+e8d5\n#define ICON_MS_SWAP_VERTICAL_CIRCLE \"\\xee\\xa3\\x96\"\t// U+e8d6\n#define ICON_MS_SWEEP \"\\xee\\x9a\\xac\"\t// U+e6ac\n#define ICON_MS_SWIPE \"\\xee\\xa7\\xac\"\t// U+e9ec\n#define ICON_MS_SWIPE_DOWN \"\\xee\\xad\\x93\"\t// U+eb53\n#define ICON_MS_SWIPE_DOWN_ALT \"\\xee\\xac\\xb0\"\t// U+eb30\n#define ICON_MS_SWIPE_LEFT \"\\xee\\xad\\x99\"\t// U+eb59\n#define ICON_MS_SWIPE_LEFT_ALT \"\\xee\\xac\\xb3\"\t// U+eb33\n#define ICON_MS_SWIPE_RIGHT \"\\xee\\xad\\x92\"\t// U+eb52\n#define ICON_MS_SWIPE_RIGHT_ALT \"\\xee\\xad\\x96\"\t// U+eb56\n#define ICON_MS_SWIPE_UP \"\\xee\\xac\\xae\"\t// U+eb2e\n#define ICON_MS_SWIPE_UP_ALT \"\\xee\\xac\\xb5\"\t// U+eb35\n#define ICON_MS_SWIPE_VERTICAL \"\\xee\\xad\\x91\"\t// U+eb51\n#define ICON_MS_SWITCH \"\\xee\\x87\\xb4\"\t// U+e1f4\n#define ICON_MS_SWITCH_ACCESS \"\\xef\\x9b\\xbd\"\t// U+f6fd\n#define ICON_MS_SWITCH_ACCESS_2 \"\\xef\\x94\\x86\"\t// U+f506\n#define ICON_MS_SWITCH_ACCESS_SHORTCUT \"\\xee\\x9f\\xa1\"\t// U+e7e1\n#define ICON_MS_SWITCH_ACCESS_SHORTCUT_ADD \"\\xee\\x9f\\xa2\"\t// U+e7e2\n#define ICON_MS_SWITCH_ACCOUNT \"\\xee\\xa7\\xad\"\t// U+e9ed\n#define ICON_MS_SWITCH_CAMERA \"\\xee\\x90\\x9e\"\t// U+e41e\n#define ICON_MS_SWITCH_LEFT \"\\xef\\x87\\x91\"\t// U+f1d1\n#define ICON_MS_SWITCH_RIGHT \"\\xef\\x87\\x92\"\t// U+f1d2\n#define ICON_MS_SWITCH_VIDEO \"\\xee\\x90\\x9f\"\t// U+e41f\n#define ICON_MS_SWITCHES \"\\xee\\x9c\\xb3\"\t// U+e733\n#define ICON_MS_SWORD_ROSE \"\\xef\\x97\\x9e\"\t// U+f5de\n#define ICON_MS_SWORDS \"\\xef\\xa2\\x89\"\t// U+f889\n#define ICON_MS_SYMPTOMS \"\\xee\\x84\\xb2\"\t// U+e132\n#define ICON_MS_SYNAGOGUE \"\\xee\\xaa\\xb0\"\t// U+eab0\n#define ICON_MS_SYNC \"\\xee\\x98\\xa7\"\t// U+e627\n#define ICON_MS_SYNC_ALT \"\\xee\\xa8\\x98\"\t// U+ea18\n#define ICON_MS_SYNC_DISABLED \"\\xee\\x98\\xa8\"\t// U+e628\n#define ICON_MS_SYNC_LOCK \"\\xee\\xab\\xae\"\t// U+eaee\n#define ICON_MS_SYNC_PROBLEM \"\\xee\\x98\\xa9\"\t// U+e629\n#define ICON_MS_SYNC_SAVED_LOCALLY \"\\xef\\xa0\\xa0\"\t// U+f820\n#define ICON_MS_SYRINGE \"\\xee\\x84\\xb3\"\t// U+e133\n#define ICON_MS_SYSTEM_SECURITY_UPDATE \"\\xef\\x81\\xb2\"\t// U+f072\n#define ICON_MS_SYSTEM_SECURITY_UPDATE_GOOD \"\\xef\\x81\\xb3\"\t// U+f073\n#define ICON_MS_SYSTEM_SECURITY_UPDATE_WARNING \"\\xef\\x81\\xb4\"\t// U+f074\n#define ICON_MS_SYSTEM_UPDATE \"\\xef\\x81\\xb2\"\t// U+f072\n#define ICON_MS_SYSTEM_UPDATE_ALT \"\\xee\\xa3\\x97\"\t// U+e8d7\n#define ICON_MS_TAB \"\\xee\\xa3\\x98\"\t// U+e8d8\n#define ICON_MS_TAB_CLOSE \"\\xef\\x9d\\x85\"\t// U+f745\n#define ICON_MS_TAB_CLOSE_RIGHT \"\\xef\\x9d\\x86\"\t// U+f746\n#define ICON_MS_TAB_DUPLICATE \"\\xef\\x9d\\x84\"\t// U+f744\n#define ICON_MS_TAB_GROUP \"\\xef\\x9d\\x83\"\t// U+f743\n#define ICON_MS_TAB_MOVE \"\\xef\\x9d\\x82\"\t// U+f742\n#define ICON_MS_TAB_NEW_RIGHT \"\\xef\\x9d\\x81\"\t// U+f741\n#define ICON_MS_TAB_RECENT \"\\xef\\x9d\\x80\"\t// U+f740\n#define ICON_MS_TAB_UNSELECTED \"\\xee\\xa3\\x99\"\t// U+e8d9\n#define ICON_MS_TABLE \"\\xef\\x86\\x91\"\t// U+f191\n#define ICON_MS_TABLE_BAR \"\\xee\\xab\\x92\"\t// U+ead2\n#define ICON_MS_TABLE_CHART \"\\xee\\x89\\xa5\"\t// U+e265\n#define ICON_MS_TABLE_CHART_VIEW \"\\xef\\x9b\\xaf\"\t// U+f6ef\n#define ICON_MS_TABLE_LAMP \"\\xee\\x87\\xb2\"\t// U+e1f2\n#define ICON_MS_TABLE_RESTAURANT \"\\xee\\xab\\x86\"\t// U+eac6\n#define ICON_MS_TABLE_ROWS \"\\xef\\x84\\x81\"\t// U+f101\n#define ICON_MS_TABLE_ROWS_NARROW \"\\xef\\x9c\\xbf\"\t// U+f73f\n#define ICON_MS_TABLE_VIEW \"\\xef\\x86\\xbe\"\t// U+f1be\n#define ICON_MS_TABLET \"\\xee\\x8c\\xaf\"\t// U+e32f\n#define ICON_MS_TABLET_ANDROID \"\\xee\\x8c\\xb0\"\t// U+e330\n#define ICON_MS_TABLET_MAC \"\\xee\\x8c\\xb1\"\t// U+e331\n#define ICON_MS_TABS \"\\xee\\xa7\\xae\"\t// U+e9ee\n#define ICON_MS_TACTIC \"\\xef\\x95\\xa4\"\t// U+f564\n#define ICON_MS_TAG \"\\xee\\xa7\\xaf\"\t// U+e9ef\n#define ICON_MS_TAG_FACES \"\\xee\\xa8\\xa2\"\t// U+ea22\n#define ICON_MS_TAKEOUT_DINING \"\\xee\\xa9\\xb4\"\t// U+ea74\n#define ICON_MS_TAMPER_DETECTION_OFF \"\\xee\\xa0\\xae\"\t// U+e82e\n#define ICON_MS_TAMPER_DETECTION_ON \"\\xef\\xa3\\x88\"\t// U+f8c8\n#define ICON_MS_TAP_AND_PLAY \"\\xee\\x98\\xab\"\t// U+e62b\n#define ICON_MS_TAPAS \"\\xef\\x87\\xa9\"\t// U+f1e9\n#define ICON_MS_TARGET \"\\xee\\x9c\\x99\"\t// U+e719\n#define ICON_MS_TASK \"\\xef\\x81\\xb5\"\t// U+f075\n#define ICON_MS_TASK_ALT \"\\xee\\x8b\\xa6\"\t// U+e2e6\n#define ICON_MS_TAUNT \"\\xef\\x9a\\x9f\"\t// U+f69f\n#define ICON_MS_TAXI_ALERT \"\\xee\\xbd\\xb4\"\t// U+ef74\n#define ICON_MS_TEAM_DASHBOARD \"\\xee\\x80\\x93\"\t// U+e013\n#define ICON_MS_TEMP_PREFERENCES_CUSTOM \"\\xef\\xa3\\x89\"\t// U+f8c9\n#define ICON_MS_TEMP_PREFERENCES_ECO \"\\xef\\xa3\\x8a\"\t// U+f8ca\n#define ICON_MS_TEMPLE_BUDDHIST \"\\xee\\xaa\\xb3\"\t// U+eab3\n#define ICON_MS_TEMPLE_HINDU \"\\xee\\xaa\\xaf\"\t// U+eaaf\n#define ICON_MS_TENANCY \"\\xef\\x83\\xa3\"\t// U+f0e3\n#define ICON_MS_TERMINAL \"\\xee\\xae\\x8e\"\t// U+eb8e\n#define ICON_MS_TERRAIN \"\\xee\\x95\\xa4\"\t// U+e564\n#define ICON_MS_TEXT_AD \"\\xee\\x9c\\xa8\"\t// U+e728\n#define ICON_MS_TEXT_DECREASE \"\\xee\\xab\\x9d\"\t// U+eadd\n#define ICON_MS_TEXT_FIELDS \"\\xee\\x89\\xa2\"\t// U+e262\n#define ICON_MS_TEXT_FIELDS_ALT \"\\xee\\xa7\\xb1\"\t// U+e9f1\n#define ICON_MS_TEXT_FORMAT \"\\xee\\x85\\xa5\"\t// U+e165\n#define ICON_MS_TEXT_INCREASE \"\\xee\\xab\\xa2\"\t// U+eae2\n#define ICON_MS_TEXT_ROTATE_UP \"\\xee\\xa4\\xba\"\t// U+e93a\n#define ICON_MS_TEXT_ROTATE_VERTICAL \"\\xee\\xa4\\xbb\"\t// U+e93b\n#define ICON_MS_TEXT_ROTATION_ANGLEDOWN \"\\xee\\xa4\\xbc\"\t// U+e93c\n#define ICON_MS_TEXT_ROTATION_ANGLEUP \"\\xee\\xa4\\xbd\"\t// U+e93d\n#define ICON_MS_TEXT_ROTATION_DOWN \"\\xee\\xa4\\xbe\"\t// U+e93e\n#define ICON_MS_TEXT_ROTATION_NONE \"\\xee\\xa4\\xbf\"\t// U+e93f\n#define ICON_MS_TEXT_SELECT_END \"\\xef\\x9c\\xbe\"\t// U+f73e\n#define ICON_MS_TEXT_SELECT_JUMP_TO_BEGINNING \"\\xef\\x9c\\xbd\"\t// U+f73d\n#define ICON_MS_TEXT_SELECT_JUMP_TO_END \"\\xef\\x9c\\xbc\"\t// U+f73c\n#define ICON_MS_TEXT_SELECT_MOVE_BACK_CHARACTER \"\\xef\\x9c\\xbb\"\t// U+f73b\n#define ICON_MS_TEXT_SELECT_MOVE_BACK_WORD \"\\xef\\x9c\\xba\"\t// U+f73a\n#define ICON_MS_TEXT_SELECT_MOVE_DOWN \"\\xef\\x9c\\xb9\"\t// U+f739\n#define ICON_MS_TEXT_SELECT_MOVE_FORWARD_CHARACTER \"\\xef\\x9c\\xb8\"\t// U+f738\n#define ICON_MS_TEXT_SELECT_MOVE_FORWARD_WORD \"\\xef\\x9c\\xb7\"\t// U+f737\n#define ICON_MS_TEXT_SELECT_MOVE_UP \"\\xef\\x9c\\xb6\"\t// U+f736\n#define ICON_MS_TEXT_SELECT_START \"\\xef\\x9c\\xb5\"\t// U+f735\n#define ICON_MS_TEXT_SNIPPET \"\\xef\\x87\\x86\"\t// U+f1c6\n#define ICON_MS_TEXT_TO_SPEECH \"\\xef\\x86\\xbc\"\t// U+f1bc\n#define ICON_MS_TEXT_UP \"\\xef\\x92\\x9e\"\t// U+f49e\n#define ICON_MS_TEXTSMS \"\\xee\\x98\\xa5\"\t// U+e625\n#define ICON_MS_TEXTURE \"\\xee\\x90\\xa1\"\t// U+e421\n#define ICON_MS_TEXTURE_ADD \"\\xef\\x95\\xbc\"\t// U+f57c\n#define ICON_MS_TEXTURE_MINUS \"\\xef\\x95\\xbb\"\t// U+f57b\n#define ICON_MS_THEATER_COMEDY \"\\xee\\xa9\\xa6\"\t// U+ea66\n#define ICON_MS_THEATERS \"\\xee\\xa3\\x9a\"\t// U+e8da\n#define ICON_MS_THERMOMETER \"\\xee\\xa1\\x86\"\t// U+e846\n#define ICON_MS_THERMOMETER_ADD \"\\xef\\x96\\x82\"\t// U+f582\n#define ICON_MS_THERMOMETER_GAIN \"\\xef\\x9b\\x98\"\t// U+f6d8\n#define ICON_MS_THERMOMETER_LOSS \"\\xef\\x9b\\x97\"\t// U+f6d7\n#define ICON_MS_THERMOMETER_MINUS \"\\xef\\x96\\x81\"\t// U+f581\n#define ICON_MS_THERMOSTAT \"\\xef\\x81\\xb6\"\t// U+f076\n#define ICON_MS_THERMOSTAT_AUTO \"\\xef\\x81\\xb7\"\t// U+f077\n#define ICON_MS_THERMOSTAT_CARBON \"\\xef\\x85\\xb8\"\t// U+f178\n#define ICON_MS_THINGS_TO_DO \"\\xee\\xac\\xaa\"\t// U+eb2a\n#define ICON_MS_THREAD_UNREAD \"\\xef\\x93\\xb9\"\t// U+f4f9\n#define ICON_MS_THUMB_DOWN \"\\xef\\x95\\xb8\"\t// U+f578\n#define ICON_MS_THUMB_DOWN_ALT \"\\xef\\x95\\xb8\"\t// U+f578\n#define ICON_MS_THUMB_DOWN_FILLED \"\\xef\\x95\\xb8\"\t// U+f578\n#define ICON_MS_THUMB_DOWN_OFF \"\\xef\\x95\\xb8\"\t// U+f578\n#define ICON_MS_THUMB_DOWN_OFF_ALT \"\\xef\\x95\\xb8\"\t// U+f578\n#define ICON_MS_THUMB_UP \"\\xef\\x95\\xb7\"\t// U+f577\n#define ICON_MS_THUMB_UP_ALT \"\\xef\\x95\\xb7\"\t// U+f577\n#define ICON_MS_THUMB_UP_FILLED \"\\xef\\x95\\xb7\"\t// U+f577\n#define ICON_MS_THUMB_UP_OFF \"\\xef\\x95\\xb7\"\t// U+f577\n#define ICON_MS_THUMB_UP_OFF_ALT \"\\xef\\x95\\xb7\"\t// U+f577\n#define ICON_MS_THUMBNAIL_BAR \"\\xef\\x9c\\xb4\"\t// U+f734\n#define ICON_MS_THUMBS_UP_DOWN \"\\xee\\xa3\\x9d\"\t// U+e8dd\n#define ICON_MS_THUNDERSTORM \"\\xee\\xaf\\x9b\"\t// U+ebdb\n#define ICON_MS_TIBIA \"\\xef\\xa2\\x9b\"\t// U+f89b\n#define ICON_MS_TIBIA_ALT \"\\xef\\xa2\\x9c\"\t// U+f89c\n#define ICON_MS_TIME_AUTO \"\\xef\\x83\\xa4\"\t// U+f0e4\n#define ICON_MS_TIME_TO_LEAVE \"\\xee\\xbf\\xb7\"\t// U+eff7\n#define ICON_MS_TIMELAPSE \"\\xee\\x90\\xa2\"\t// U+e422\n#define ICON_MS_TIMELINE \"\\xee\\xa4\\xa2\"\t// U+e922\n#define ICON_MS_TIMER \"\\xee\\x90\\xa5\"\t// U+e425\n#define ICON_MS_TIMER_10 \"\\xee\\x90\\xa3\"\t// U+e423\n#define ICON_MS_TIMER_10_ALT_1 \"\\xee\\xbe\\xbf\"\t// U+efbf\n#define ICON_MS_TIMER_10_SELECT \"\\xef\\x81\\xba\"\t// U+f07a\n#define ICON_MS_TIMER_3 \"\\xee\\x90\\xa4\"\t// U+e424\n#define ICON_MS_TIMER_3_ALT_1 \"\\xee\\xbf\\x80\"\t// U+efc0\n#define ICON_MS_TIMER_3_SELECT \"\\xef\\x81\\xbb\"\t// U+f07b\n#define ICON_MS_TIMER_5 \"\\xef\\x92\\xb1\"\t// U+f4b1\n#define ICON_MS_TIMER_5_SHUTTER \"\\xef\\x92\\xb2\"\t// U+f4b2\n#define ICON_MS_TIMER_OFF \"\\xee\\x90\\xa6\"\t// U+e426\n#define ICON_MS_TIMER_PAUSE \"\\xef\\x92\\xbb\"\t// U+f4bb\n#define ICON_MS_TIMER_PLAY \"\\xef\\x92\\xba\"\t// U+f4ba\n#define ICON_MS_TIPS_AND_UPDATES \"\\xee\\x9e\\x9a\"\t// U+e79a\n#define ICON_MS_TIRE_REPAIR \"\\xee\\xaf\\x88\"\t// U+ebc8\n#define ICON_MS_TITLE \"\\xee\\x89\\xa4\"\t// U+e264\n#define ICON_MS_TITLECASE \"\\xef\\x92\\x89\"\t// U+f489\n#define ICON_MS_TOAST \"\\xee\\xbf\\x81\"\t// U+efc1\n#define ICON_MS_TOC \"\\xee\\xa3\\x9e\"\t// U+e8de\n#define ICON_MS_TODAY \"\\xee\\xa3\\x9f\"\t// U+e8df\n#define ICON_MS_TOGGLE_OFF \"\\xee\\xa7\\xb5\"\t// U+e9f5\n#define ICON_MS_TOGGLE_ON \"\\xee\\xa7\\xb6\"\t// U+e9f6\n#define ICON_MS_TOKEN \"\\xee\\xa8\\xa5\"\t// U+ea25\n#define ICON_MS_TOLL \"\\xee\\xa3\\xa0\"\t// U+e8e0\n#define ICON_MS_TONALITY \"\\xee\\x90\\xa7\"\t// U+e427\n#define ICON_MS_TOOLBAR \"\\xee\\xa7\\xb7\"\t// U+e9f7\n#define ICON_MS_TOOLS_FLAT_HEAD \"\\xef\\xa3\\x8b\"\t// U+f8cb\n#define ICON_MS_TOOLS_INSTALLATION_KIT \"\\xee\\x8a\\xab\"\t// U+e2ab\n#define ICON_MS_TOOLS_LADDER \"\\xee\\x8b\\x8b\"\t// U+e2cb\n#define ICON_MS_TOOLS_LEVEL \"\\xee\\x9d\\xbb\"\t// U+e77b\n#define ICON_MS_TOOLS_PHILLIPS \"\\xef\\xa3\\x8c\"\t// U+f8cc\n#define ICON_MS_TOOLS_PLIERS_WIRE_STRIPPER \"\\xee\\x8a\\xaa\"\t// U+e2aa\n#define ICON_MS_TOOLS_POWER_DRILL \"\\xee\\x87\\xa9\"\t// U+e1e9\n#define ICON_MS_TOOLS_WRENCH \"\\xef\\xa3\\x8d\"\t// U+f8cd\n#define ICON_MS_TOOLTIP \"\\xee\\xa7\\xb8\"\t// U+e9f8\n#define ICON_MS_TOP_PANEL_CLOSE \"\\xef\\x9c\\xb3\"\t// U+f733\n#define ICON_MS_TOP_PANEL_OPEN \"\\xef\\x9c\\xb2\"\t// U+f732\n#define ICON_MS_TOPIC \"\\xef\\x87\\x88\"\t// U+f1c8\n#define ICON_MS_TORNADO \"\\xee\\x86\\x99\"\t// U+e199\n#define ICON_MS_TOTAL_DISSOLVED_SOLIDS \"\\xef\\xa1\\xb7\"\t// U+f877\n#define ICON_MS_TOUCH_APP \"\\xee\\xa4\\x93\"\t// U+e913\n#define ICON_MS_TOUCHPAD_MOUSE \"\\xef\\x9a\\x87\"\t// U+f687\n#define ICON_MS_TOUCHPAD_MOUSE_OFF \"\\xef\\x93\\xa6\"\t// U+f4e6\n#define ICON_MS_TOUR \"\\xee\\xbd\\xb5\"\t// U+ef75\n#define ICON_MS_TOYS \"\\xee\\x8c\\xb2\"\t// U+e332\n#define ICON_MS_TOYS_AND_GAMES \"\\xee\\xbf\\x82\"\t// U+efc2\n#define ICON_MS_TOYS_FAN \"\\xef\\xa2\\x87\"\t// U+f887\n#define ICON_MS_TRACK_CHANGES \"\\xee\\xa3\\xa1\"\t// U+e8e1\n#define ICON_MS_TRACKPAD_INPUT \"\\xef\\x93\\x87\"\t// U+f4c7\n#define ICON_MS_TRAFFIC \"\\xee\\x95\\xa5\"\t// U+e565\n#define ICON_MS_TRAIL_LENGTH \"\\xee\\xad\\x9e\"\t// U+eb5e\n#define ICON_MS_TRAIL_LENGTH_MEDIUM \"\\xee\\xad\\xa3\"\t// U+eb63\n#define ICON_MS_TRAIL_LENGTH_SHORT \"\\xee\\xad\\xad\"\t// U+eb6d\n#define ICON_MS_TRAIN \"\\xee\\x95\\xb0\"\t// U+e570\n#define ICON_MS_TRAM \"\\xee\\x95\\xb1\"\t// U+e571\n#define ICON_MS_TRANSCRIBE \"\\xef\\xa3\\xac\"\t// U+f8ec\n#define ICON_MS_TRANSFER_WITHIN_A_STATION \"\\xee\\x95\\xb2\"\t// U+e572\n#define ICON_MS_TRANSFORM \"\\xee\\x90\\xa8\"\t// U+e428\n#define ICON_MS_TRANSGENDER \"\\xee\\x96\\x8d\"\t// U+e58d\n#define ICON_MS_TRANSIT_ENTEREXIT \"\\xee\\x95\\xb9\"\t// U+e579\n#define ICON_MS_TRANSITION_CHOP \"\\xef\\x94\\x8e\"\t// U+f50e\n#define ICON_MS_TRANSITION_DISSOLVE \"\\xef\\x94\\x8d\"\t// U+f50d\n#define ICON_MS_TRANSITION_FADE \"\\xef\\x94\\x8c\"\t// U+f50c\n#define ICON_MS_TRANSITION_PUSH \"\\xef\\x94\\x8b\"\t// U+f50b\n#define ICON_MS_TRANSITION_SLIDE \"\\xef\\x94\\x8a\"\t// U+f50a\n#define ICON_MS_TRANSLATE \"\\xee\\xa3\\xa2\"\t// U+e8e2\n#define ICON_MS_TRANSPORTATION \"\\xee\\x88\\x9d\"\t// U+e21d\n#define ICON_MS_TRAVEL \"\\xee\\xbe\\x93\"\t// U+ef93\n#define ICON_MS_TRAVEL_EXPLORE \"\\xee\\x8b\\x9b\"\t// U+e2db\n#define ICON_MS_TRAVEL_LUGGAGE_AND_BAGS \"\\xee\\xbf\\x83\"\t// U+efc3\n#define ICON_MS_TRENDING_DOWN \"\\xee\\xa3\\xa3\"\t// U+e8e3\n#define ICON_MS_TRENDING_FLAT \"\\xee\\xa3\\xa4\"\t// U+e8e4\n#define ICON_MS_TRENDING_UP \"\\xee\\xa3\\xa5\"\t// U+e8e5\n#define ICON_MS_TRIP \"\\xee\\x9b\\xbb\"\t// U+e6fb\n#define ICON_MS_TRIP_ORIGIN \"\\xee\\x95\\xbb\"\t// U+e57b\n#define ICON_MS_TROLLEY \"\\xef\\xa1\\xab\"\t// U+f86b\n#define ICON_MS_TROPHY \"\\xee\\xa8\\xa3\"\t// U+ea23\n#define ICON_MS_TROUBLESHOOT \"\\xee\\x87\\x92\"\t// U+e1d2\n#define ICON_MS_TRY \"\\xef\\x81\\xbc\"\t// U+f07c\n#define ICON_MS_TSUNAMI \"\\xee\\xaf\\x98\"\t// U+ebd8\n#define ICON_MS_TSV \"\\xee\\x9b\\x96\"\t// U+e6d6\n#define ICON_MS_TTY \"\\xef\\x86\\xaa\"\t// U+f1aa\n#define ICON_MS_TUNE \"\\xee\\x90\\xa9\"\t// U+e429\n#define ICON_MS_TUNGSTEN \"\\xef\\x81\\xbd\"\t// U+f07d\n#define ICON_MS_TURN_LEFT \"\\xee\\xae\\xa6\"\t// U+eba6\n#define ICON_MS_TURN_RIGHT \"\\xee\\xae\\xab\"\t// U+ebab\n#define ICON_MS_TURN_SHARP_LEFT \"\\xee\\xae\\xa7\"\t// U+eba7\n#define ICON_MS_TURN_SHARP_RIGHT \"\\xee\\xae\\xaa\"\t// U+ebaa\n#define ICON_MS_TURN_SLIGHT_LEFT \"\\xee\\xae\\xa4\"\t// U+eba4\n#define ICON_MS_TURN_SLIGHT_RIGHT \"\\xee\\xae\\x9a\"\t// U+eb9a\n#define ICON_MS_TURNED_IN \"\\xee\\xa3\\xa7\"\t// U+e8e7\n#define ICON_MS_TURNED_IN_NOT \"\\xee\\xa3\\xa7\"\t// U+e8e7\n#define ICON_MS_TV \"\\xee\\x98\\xbb\"\t// U+e63b\n#define ICON_MS_TV_GEN \"\\xee\\xa0\\xb0\"\t// U+e830\n#define ICON_MS_TV_GUIDE \"\\xee\\x87\\x9c\"\t// U+e1dc\n#define ICON_MS_TV_OFF \"\\xee\\x99\\x87\"\t// U+e647\n#define ICON_MS_TV_OPTIONS_EDIT_CHANNELS \"\\xee\\x87\\x9d\"\t// U+e1dd\n#define ICON_MS_TV_OPTIONS_INPUT_SETTINGS \"\\xee\\x87\\x9e\"\t// U+e1de\n#define ICON_MS_TV_REMOTE \"\\xef\\x97\\x99\"\t// U+f5d9\n#define ICON_MS_TV_SIGNIN \"\\xee\\x9c\\x9b\"\t// U+e71b\n#define ICON_MS_TV_WITH_ASSISTANT \"\\xee\\x9e\\x85\"\t// U+e785\n#define ICON_MS_TWO_PAGER \"\\xef\\x94\\x9f\"\t// U+f51f\n#define ICON_MS_TWO_WHEELER \"\\xee\\xa7\\xb9\"\t// U+e9f9\n#define ICON_MS_TYPE_SPECIMEN \"\\xef\\xa3\\xb0\"\t// U+f8f0\n#define ICON_MS_U_TURN_LEFT \"\\xee\\xae\\xa1\"\t// U+eba1\n#define ICON_MS_U_TURN_RIGHT \"\\xee\\xae\\xa2\"\t// U+eba2\n#define ICON_MS_ULNA_RADIUS \"\\xef\\xa2\\x9d\"\t// U+f89d\n#define ICON_MS_ULNA_RADIUS_ALT \"\\xef\\xa2\\x9e\"\t// U+f89e\n#define ICON_MS_UMBRELLA \"\\xef\\x86\\xad\"\t// U+f1ad\n#define ICON_MS_UNARCHIVE \"\\xee\\x85\\xa9\"\t// U+e169\n#define ICON_MS_UNDO \"\\xee\\x85\\xa6\"\t// U+e166\n#define ICON_MS_UNFOLD_LESS \"\\xee\\x97\\x96\"\t// U+e5d6\n#define ICON_MS_UNFOLD_LESS_DOUBLE \"\\xef\\xa3\\x8f\"\t// U+f8cf\n#define ICON_MS_UNFOLD_MORE \"\\xee\\x97\\x97\"\t// U+e5d7\n#define ICON_MS_UNFOLD_MORE_DOUBLE \"\\xef\\xa3\\x90\"\t// U+f8d0\n#define ICON_MS_UNGROUP \"\\xef\\x9c\\xb1\"\t// U+f731\n#define ICON_MS_UNIVERSAL_CURRENCY \"\\xee\\xa7\\xba\"\t// U+e9fa\n#define ICON_MS_UNIVERSAL_CURRENCY_ALT \"\\xee\\x9c\\xb4\"\t// U+e734\n#define ICON_MS_UNIVERSAL_LOCAL \"\\xee\\xa7\\xbb\"\t// U+e9fb\n#define ICON_MS_UNKNOWN_2 \"\\xef\\x92\\x9f\"\t// U+f49f\n#define ICON_MS_UNKNOWN_5 \"\\xee\\x9a\\xa5\"\t// U+e6a5\n#define ICON_MS_UNKNOWN_7 \"\\xef\\x92\\x9e\"\t// U+f49e\n#define ICON_MS_UNKNOWN_DOCUMENT \"\\xef\\xa0\\x84\"\t// U+f804\n#define ICON_MS_UNKNOWN_MED \"\\xee\\xaa\\xbd\"\t// U+eabd\n#define ICON_MS_UNLICENSE \"\\xee\\xac\\x85\"\t// U+eb05\n#define ICON_MS_UNPIN \"\\xee\\x9b\\xb9\"\t// U+e6f9\n#define ICON_MS_UNPUBLISHED \"\\xef\\x88\\xb6\"\t// U+f236\n#define ICON_MS_UNSUBSCRIBE \"\\xee\\x83\\xab\"\t// U+e0eb\n#define ICON_MS_UPCOMING \"\\xef\\x81\\xbe\"\t// U+f07e\n#define ICON_MS_UPDATE \"\\xee\\xa4\\xa3\"\t// U+e923\n#define ICON_MS_UPDATE_DISABLED \"\\xee\\x81\\xb5\"\t// U+e075\n#define ICON_MS_UPGRADE \"\\xef\\x83\\xbb\"\t// U+f0fb\n#define ICON_MS_UPLOAD \"\\xef\\x82\\x9b\"\t// U+f09b\n#define ICON_MS_UPLOAD_2 \"\\xef\\x94\\xa1\"\t// U+f521\n#define ICON_MS_UPLOAD_FILE \"\\xee\\xa7\\xbc\"\t// U+e9fc\n#define ICON_MS_UPPERCASE \"\\xef\\x92\\x88\"\t// U+f488\n#define ICON_MS_UROLOGY \"\\xee\\x84\\xb7\"\t// U+e137\n#define ICON_MS_USB \"\\xee\\x87\\xa0\"\t// U+e1e0\n#define ICON_MS_USB_OFF \"\\xee\\x93\\xba\"\t// U+e4fa\n#define ICON_MS_USER_ATTRIBUTES \"\\xee\\x9c\\x88\"\t// U+e708\n#define ICON_MS_VACCINES \"\\xee\\x84\\xb8\"\t// U+e138\n#define ICON_MS_VACUUM \"\\xee\\xbf\\x85\"\t// U+efc5\n#define ICON_MS_VALVE \"\\xee\\x88\\xa4\"\t// U+e224\n#define ICON_MS_VAPE_FREE \"\\xee\\xaf\\x86\"\t// U+ebc6\n#define ICON_MS_VAPING_ROOMS \"\\xee\\xaf\\x8f\"\t// U+ebcf\n#define ICON_MS_VARIABLE_ADD \"\\xef\\x94\\x9e\"\t// U+f51e\n#define ICON_MS_VARIABLE_INSERT \"\\xef\\x94\\x9d\"\t// U+f51d\n#define ICON_MS_VARIABLE_REMOVE \"\\xef\\x94\\x9c\"\t// U+f51c\n#define ICON_MS_VARIABLES \"\\xef\\xa1\\x91\"\t// U+f851\n#define ICON_MS_VENTILATOR \"\\xee\\x84\\xb9\"\t// U+e139\n#define ICON_MS_VERIFIED \"\\xee\\xbd\\xb6\"\t// U+ef76\n#define ICON_MS_VERIFIED_USER \"\\xef\\x80\\x93\"\t// U+f013\n#define ICON_MS_VERTICAL_ALIGN_BOTTOM \"\\xee\\x89\\x98\"\t// U+e258\n#define ICON_MS_VERTICAL_ALIGN_CENTER \"\\xee\\x89\\x99\"\t// U+e259\n#define ICON_MS_VERTICAL_ALIGN_TOP \"\\xee\\x89\\x9a\"\t// U+e25a\n#define ICON_MS_VERTICAL_DISTRIBUTE \"\\xee\\x81\\xb6\"\t// U+e076\n#define ICON_MS_VERTICAL_SHADES \"\\xee\\xb0\\x8e\"\t// U+ec0e\n#define ICON_MS_VERTICAL_SHADES_CLOSED \"\\xee\\xb0\\x8d\"\t// U+ec0d\n#define ICON_MS_VERTICAL_SPLIT \"\\xee\\xa5\\x89\"\t// U+e949\n#define ICON_MS_VIBRATION \"\\xee\\x98\\xad\"\t// U+e62d\n#define ICON_MS_VIDEO_CALL \"\\xee\\x81\\xb0\"\t// U+e070\n#define ICON_MS_VIDEO_CAMERA_BACK \"\\xef\\x81\\xbf\"\t// U+f07f\n#define ICON_MS_VIDEO_CAMERA_FRONT \"\\xef\\x82\\x80\"\t// U+f080\n#define ICON_MS_VIDEO_CAMERA_FRONT_OFF \"\\xef\\xa0\\xbb\"\t// U+f83b\n#define ICON_MS_VIDEO_CHAT \"\\xef\\xa2\\xa0\"\t// U+f8a0\n#define ICON_MS_VIDEO_FILE \"\\xee\\xae\\x87\"\t// U+eb87\n#define ICON_MS_VIDEO_LABEL \"\\xee\\x81\\xb1\"\t// U+e071\n#define ICON_MS_VIDEO_LIBRARY \"\\xee\\x81\\x8a\"\t// U+e04a\n#define ICON_MS_VIDEO_SEARCH \"\\xee\\xbf\\x86\"\t// U+efc6\n#define ICON_MS_VIDEO_SETTINGS \"\\xee\\xa9\\xb5\"\t// U+ea75\n#define ICON_MS_VIDEO_STABLE \"\\xef\\x82\\x81\"\t// U+f081\n#define ICON_MS_VIDEOCAM \"\\xee\\x81\\x8b\"\t// U+e04b\n#define ICON_MS_VIDEOCAM_OFF \"\\xee\\x81\\x8c\"\t// U+e04c\n#define ICON_MS_VIDEOGAME_ASSET \"\\xee\\x8c\\xb8\"\t// U+e338\n#define ICON_MS_VIDEOGAME_ASSET_OFF \"\\xee\\x94\\x80\"\t// U+e500\n#define ICON_MS_VIEW_AGENDA \"\\xee\\xa3\\xa9\"\t// U+e8e9\n#define ICON_MS_VIEW_ARRAY \"\\xee\\xa3\\xaa\"\t// U+e8ea\n#define ICON_MS_VIEW_CAROUSEL \"\\xee\\xa3\\xab\"\t// U+e8eb\n#define ICON_MS_VIEW_COLUMN \"\\xee\\xa3\\xac\"\t// U+e8ec\n#define ICON_MS_VIEW_COLUMN_2 \"\\xef\\xa1\\x87\"\t// U+f847\n#define ICON_MS_VIEW_COMFY \"\\xee\\x90\\xaa\"\t// U+e42a\n#define ICON_MS_VIEW_COMFY_ALT \"\\xee\\xad\\xb3\"\t// U+eb73\n#define ICON_MS_VIEW_COMPACT \"\\xee\\x90\\xab\"\t// U+e42b\n#define ICON_MS_VIEW_COMPACT_ALT \"\\xee\\xad\\xb4\"\t// U+eb74\n#define ICON_MS_VIEW_COZY \"\\xee\\xad\\xb5\"\t// U+eb75\n#define ICON_MS_VIEW_DAY \"\\xee\\xa3\\xad\"\t// U+e8ed\n#define ICON_MS_VIEW_HEADLINE \"\\xee\\xa3\\xae\"\t// U+e8ee\n#define ICON_MS_VIEW_IN_AR \"\\xee\\xbf\\x89\"\t// U+efc9\n#define ICON_MS_VIEW_IN_AR_NEW \"\\xee\\xbf\\x89\"\t// U+efc9\n#define ICON_MS_VIEW_IN_AR_OFF \"\\xef\\x98\\x9b\"\t// U+f61b\n#define ICON_MS_VIEW_KANBAN \"\\xee\\xad\\xbf\"\t// U+eb7f\n#define ICON_MS_VIEW_LIST \"\\xee\\xa3\\xaf\"\t// U+e8ef\n#define ICON_MS_VIEW_MODULE \"\\xee\\xa3\\xb0\"\t// U+e8f0\n#define ICON_MS_VIEW_QUILT \"\\xee\\xa3\\xb1\"\t// U+e8f1\n#define ICON_MS_VIEW_REAL_SIZE \"\\xef\\x93\\x82\"\t// U+f4c2\n#define ICON_MS_VIEW_SIDEBAR \"\\xef\\x84\\x94\"\t// U+f114\n#define ICON_MS_VIEW_STREAM \"\\xee\\xa3\\xb2\"\t// U+e8f2\n#define ICON_MS_VIEW_TIMELINE \"\\xee\\xae\\x85\"\t// U+eb85\n#define ICON_MS_VIEW_WEEK \"\\xee\\xa3\\xb3\"\t// U+e8f3\n#define ICON_MS_VIGNETTE \"\\xee\\x90\\xb5\"\t// U+e435\n#define ICON_MS_VILLA \"\\xee\\x96\\x86\"\t// U+e586\n#define ICON_MS_VISIBILITY \"\\xee\\xa3\\xb4\"\t// U+e8f4\n#define ICON_MS_VISIBILITY_LOCK \"\\xef\\x99\\x93\"\t// U+f653\n#define ICON_MS_VISIBILITY_OFF \"\\xee\\xa3\\xb5\"\t// U+e8f5\n#define ICON_MS_VITAL_SIGNS \"\\xee\\x99\\x90\"\t// U+e650\n#define ICON_MS_VITALS \"\\xee\\x84\\xbb\"\t// U+e13b\n#define ICON_MS_VO2_MAX \"\\xef\\x92\\xaa\"\t// U+f4aa\n#define ICON_MS_VOICE_CHAT \"\\xee\\x98\\xae\"\t// U+e62e\n#define ICON_MS_VOICE_OVER_OFF \"\\xee\\xa5\\x8a\"\t// U+e94a\n#define ICON_MS_VOICE_SELECTION \"\\xef\\x96\\x8a\"\t// U+f58a\n#define ICON_MS_VOICEMAIL \"\\xee\\x83\\x99\"\t// U+e0d9\n#define ICON_MS_VOLCANO \"\\xee\\xaf\\x9a\"\t// U+ebda\n#define ICON_MS_VOLUME_DOWN \"\\xee\\x81\\x8d\"\t// U+e04d\n#define ICON_MS_VOLUME_DOWN_ALT \"\\xee\\x9e\\x9c\"\t// U+e79c\n#define ICON_MS_VOLUME_MUTE \"\\xee\\x81\\x8e\"\t// U+e04e\n#define ICON_MS_VOLUME_OFF \"\\xee\\x81\\x8f\"\t// U+e04f\n#define ICON_MS_VOLUME_UP \"\\xee\\x81\\x90\"\t// U+e050\n#define ICON_MS_VOLUNTEER_ACTIVISM \"\\xee\\xa9\\xb0\"\t// U+ea70\n#define ICON_MS_VOTING_CHIP \"\\xef\\xa1\\x92\"\t// U+f852\n#define ICON_MS_VPN_KEY \"\\xee\\x83\\x9a\"\t// U+e0da\n#define ICON_MS_VPN_KEY_ALERT \"\\xef\\x9b\\x8c\"\t// U+f6cc\n#define ICON_MS_VPN_KEY_OFF \"\\xee\\xad\\xba\"\t// U+eb7a\n#define ICON_MS_VPN_LOCK \"\\xee\\x98\\xaf\"\t// U+e62f\n#define ICON_MS_VR180_CREATE2D \"\\xee\\xbf\\x8a\"\t// U+efca\n#define ICON_MS_VR180_CREATE2D_OFF \"\\xef\\x95\\xb1\"\t// U+f571\n#define ICON_MS_VRPANO \"\\xef\\x82\\x82\"\t// U+f082\n#define ICON_MS_WALL_ART \"\\xee\\xbf\\x8b\"\t// U+efcb\n#define ICON_MS_WALL_LAMP \"\\xee\\x8a\\xb4\"\t// U+e2b4\n#define ICON_MS_WALLET \"\\xef\\xa3\\xbf\"\t// U+f8ff\n#define ICON_MS_WALLPAPER \"\\xee\\x86\\xbc\"\t// U+e1bc\n#define ICON_MS_WALLPAPER_SLIDESHOW \"\\xef\\x99\\xb2\"\t// U+f672\n#define ICON_MS_WARD \"\\xee\\x84\\xbc\"\t// U+e13c\n#define ICON_MS_WAREHOUSE \"\\xee\\xae\\xb8\"\t// U+ebb8\n#define ICON_MS_WARNING \"\\xef\\x82\\x83\"\t// U+f083\n#define ICON_MS_WARNING_AMBER \"\\xef\\x82\\x83\"\t// U+f083\n#define ICON_MS_WARNING_OFF \"\\xef\\x9e\\xad\"\t// U+f7ad\n#define ICON_MS_WASH \"\\xef\\x86\\xb1\"\t// U+f1b1\n#define ICON_MS_WATCH \"\\xee\\x8c\\xb4\"\t// U+e334\n#define ICON_MS_WATCH_BUTTON_PRESS \"\\xef\\x9a\\xaa\"\t// U+f6aa\n#define ICON_MS_WATCH_LATER \"\\xee\\xbf\\x96\"\t// U+efd6\n#define ICON_MS_WATCH_OFF \"\\xee\\xab\\xa3\"\t// U+eae3\n#define ICON_MS_WATCH_SCREENTIME \"\\xef\\x9a\\xae\"\t// U+f6ae\n#define ICON_MS_WATCH_WAKE \"\\xef\\x9a\\xa9\"\t// U+f6a9\n#define ICON_MS_WATER \"\\xef\\x82\\x84\"\t// U+f084\n#define ICON_MS_WATER_BOTTLE \"\\xef\\x9a\\x9d\"\t// U+f69d\n#define ICON_MS_WATER_BOTTLE_LARGE \"\\xef\\x9a\\x9e\"\t// U+f69e\n#define ICON_MS_WATER_DAMAGE \"\\xef\\x88\\x83\"\t// U+f203\n#define ICON_MS_WATER_DO \"\\xef\\xa1\\xb0\"\t// U+f870\n#define ICON_MS_WATER_DROP \"\\xee\\x9e\\x98\"\t// U+e798\n#define ICON_MS_WATER_EC \"\\xef\\xa1\\xb5\"\t// U+f875\n#define ICON_MS_WATER_FULL \"\\xef\\x9b\\x96\"\t// U+f6d6\n#define ICON_MS_WATER_HEATER \"\\xee\\x8a\\x84\"\t// U+e284\n#define ICON_MS_WATER_LOCK \"\\xef\\x9a\\xad\"\t// U+f6ad\n#define ICON_MS_WATER_LOSS \"\\xef\\x9b\\x95\"\t// U+f6d5\n#define ICON_MS_WATER_LUX \"\\xef\\xa1\\xb4\"\t// U+f874\n#define ICON_MS_WATER_MEDIUM \"\\xef\\x9b\\x94\"\t// U+f6d4\n#define ICON_MS_WATER_ORP \"\\xef\\xa1\\xb8\"\t// U+f878\n#define ICON_MS_WATER_PH \"\\xef\\xa1\\xba\"\t// U+f87a\n#define ICON_MS_WATER_PUMP \"\\xef\\x97\\x98\"\t// U+f5d8\n#define ICON_MS_WATER_VOC \"\\xef\\xa1\\xbb\"\t// U+f87b\n#define ICON_MS_WATERFALL_CHART \"\\xee\\xa8\\x80\"\t// U+ea00\n#define ICON_MS_WAVES \"\\xee\\x85\\xb6\"\t// U+e176\n#define ICON_MS_WAVING_HAND \"\\xee\\x9d\\xa6\"\t// U+e766\n#define ICON_MS_WB_AUTO \"\\xee\\x90\\xac\"\t// U+e42c\n#define ICON_MS_WB_CLOUDY \"\\xef\\x85\\x9c\"\t// U+f15c\n#define ICON_MS_WB_INCANDESCENT \"\\xee\\x90\\xae\"\t// U+e42e\n#define ICON_MS_WB_IRIDESCENT \"\\xef\\x81\\xbd\"\t// U+f07d\n#define ICON_MS_WB_SHADE \"\\xee\\xa8\\x81\"\t// U+ea01\n#define ICON_MS_WB_SUNNY \"\\xee\\x90\\xb0\"\t// U+e430\n#define ICON_MS_WB_TWILIGHT \"\\xee\\x87\\x86\"\t// U+e1c6\n#define ICON_MS_WC \"\\xee\\x98\\xbd\"\t// U+e63d\n#define ICON_MS_WEATHER_HAIL \"\\xef\\x99\\xbf\"\t// U+f67f\n#define ICON_MS_WEATHER_MIX \"\\xef\\x98\\x8b\"\t// U+f60b\n#define ICON_MS_WEATHER_SNOWY \"\\xee\\x8b\\x8d\"\t// U+e2cd\n#define ICON_MS_WEB \"\\xee\\x81\\x91\"\t// U+e051\n#define ICON_MS_WEB_ASSET \"\\xee\\x81\\xa9\"\t// U+e069\n#define ICON_MS_WEB_ASSET_OFF \"\\xee\\xbd\\x87\"\t// U+ef47\n#define ICON_MS_WEB_STORIES \"\\xee\\x96\\x95\"\t// U+e595\n#define ICON_MS_WEB_TRAFFIC \"\\xee\\xa8\\x83\"\t// U+ea03\n#define ICON_MS_WEBHOOK \"\\xee\\xae\\x92\"\t// U+eb92\n#define ICON_MS_WEEKEND \"\\xee\\x85\\xab\"\t// U+e16b\n#define ICON_MS_WEIGHT \"\\xee\\x84\\xbd\"\t// U+e13d\n#define ICON_MS_WEST \"\\xef\\x87\\xa6\"\t// U+f1e6\n#define ICON_MS_WHATSHOT \"\\xee\\xa0\\x8e\"\t// U+e80e\n#define ICON_MS_WHEELCHAIR_PICKUP \"\\xef\\x86\\xab\"\t// U+f1ab\n#define ICON_MS_WHERE_TO_VOTE \"\\xee\\x85\\xb7\"\t// U+e177\n#define ICON_MS_WIDGETS \"\\xee\\x86\\xbd\"\t// U+e1bd\n#define ICON_MS_WIDTH \"\\xef\\x9c\\xb0\"\t// U+f730\n#define ICON_MS_WIDTH_FULL \"\\xef\\xa3\\xb5\"\t// U+f8f5\n#define ICON_MS_WIDTH_NORMAL \"\\xef\\xa3\\xb6\"\t// U+f8f6\n#define ICON_MS_WIDTH_WIDE \"\\xef\\xa3\\xb7\"\t// U+f8f7\n#define ICON_MS_WIFI \"\\xee\\x98\\xbe\"\t// U+e63e\n#define ICON_MS_WIFI_1_BAR \"\\xee\\x93\\x8a\"\t// U+e4ca\n#define ICON_MS_WIFI_2_BAR \"\\xee\\x93\\x99\"\t// U+e4d9\n#define ICON_MS_WIFI_ADD \"\\xef\\x9e\\xa8\"\t// U+f7a8\n#define ICON_MS_WIFI_CALLING \"\\xee\\xbd\\xb7\"\t// U+ef77\n#define ICON_MS_WIFI_CALLING_1 \"\\xef\\x83\\xb6\"\t// U+f0f6\n#define ICON_MS_WIFI_CALLING_2 \"\\xef\\x83\\xb6\"\t// U+f0f6\n#define ICON_MS_WIFI_CALLING_3 \"\\xef\\x83\\xb6\"\t// U+f0f6\n#define ICON_MS_WIFI_CHANNEL \"\\xee\\xad\\xaa\"\t// U+eb6a\n#define ICON_MS_WIFI_FIND \"\\xee\\xac\\xb1\"\t// U+eb31\n#define ICON_MS_WIFI_HOME \"\\xef\\x99\\xb1\"\t// U+f671\n#define ICON_MS_WIFI_LOCK \"\\xee\\x87\\xa1\"\t// U+e1e1\n#define ICON_MS_WIFI_NOTIFICATION \"\\xef\\x99\\xb0\"\t// U+f670\n#define ICON_MS_WIFI_OFF \"\\xee\\x99\\x88\"\t// U+e648\n#define ICON_MS_WIFI_PASSWORD \"\\xee\\xad\\xab\"\t// U+eb6b\n#define ICON_MS_WIFI_PROTECTED_SETUP \"\\xef\\x83\\xbc\"\t// U+f0fc\n#define ICON_MS_WIFI_PROXY \"\\xef\\x9e\\xa7\"\t// U+f7a7\n#define ICON_MS_WIFI_TETHERING \"\\xee\\x87\\xa2\"\t// U+e1e2\n#define ICON_MS_WIFI_TETHERING_ERROR \"\\xee\\xab\\x99\"\t// U+ead9\n#define ICON_MS_WIFI_TETHERING_OFF \"\\xef\\x82\\x87\"\t// U+f087\n#define ICON_MS_WIND_POWER \"\\xee\\xb0\\x8c\"\t// U+ec0c\n#define ICON_MS_WINDOW \"\\xef\\x82\\x88\"\t// U+f088\n#define ICON_MS_WINDOW_CLOSED \"\\xee\\x9d\\xbe\"\t// U+e77e\n#define ICON_MS_WINDOW_OPEN \"\\xee\\x9e\\x8c\"\t// U+e78c\n#define ICON_MS_WINDOW_SENSOR \"\\xee\\x8a\\xbb\"\t// U+e2bb\n#define ICON_MS_WINE_BAR \"\\xef\\x87\\xa8\"\t// U+f1e8\n#define ICON_MS_WOMAN \"\\xee\\x84\\xbe\"\t// U+e13e\n#define ICON_MS_WOMAN_2 \"\\xef\\xa3\\xa7\"\t// U+f8e7\n#define ICON_MS_WORK \"\\xee\\xa5\\x83\"\t// U+e943\n#define ICON_MS_WORK_ALERT \"\\xef\\x97\\xb7\"\t// U+f5f7\n#define ICON_MS_WORK_HISTORY \"\\xee\\xb0\\x89\"\t// U+ec09\n#define ICON_MS_WORK_OFF \"\\xee\\xa5\\x82\"\t// U+e942\n#define ICON_MS_WORK_OUTLINE \"\\xee\\xa5\\x83\"\t// U+e943\n#define ICON_MS_WORK_UPDATE \"\\xef\\x97\\xb8\"\t// U+f5f8\n#define ICON_MS_WORKFLOW \"\\xee\\xa8\\x84\"\t// U+ea04\n#define ICON_MS_WORKSPACE_PREMIUM \"\\xee\\x9e\\xaf\"\t// U+e7af\n#define ICON_MS_WORKSPACES \"\\xee\\xa8\\x8f\"\t// U+ea0f\n#define ICON_MS_WORKSPACES_OUTLINE \"\\xee\\xa8\\x8f\"\t// U+ea0f\n#define ICON_MS_WOUNDS_INJURIES \"\\xee\\x84\\xbf\"\t// U+e13f\n#define ICON_MS_WRAP_TEXT \"\\xee\\x89\\x9b\"\t// U+e25b\n#define ICON_MS_WRIST \"\\xef\\x9a\\x9c\"\t// U+f69c\n#define ICON_MS_WRONG_LOCATION \"\\xee\\xbd\\xb8\"\t// U+ef78\n#define ICON_MS_WYSIWYG \"\\xef\\x87\\x83\"\t// U+f1c3\n#define ICON_MS_YARD \"\\xef\\x82\\x89\"\t// U+f089\n#define ICON_MS_YOUR_TRIPS \"\\xee\\xac\\xab\"\t// U+eb2b\n#define ICON_MS_YOUTUBE_ACTIVITY \"\\xef\\xa1\\x9a\"\t// U+f85a\n#define ICON_MS_YOUTUBE_SEARCHED_FOR \"\\xee\\xa3\\xba\"\t// U+e8fa\n#define ICON_MS_ZONE_PERSON_ALERT \"\\xee\\x9e\\x81\"\t// U+e781\n#define ICON_MS_ZONE_PERSON_IDLE \"\\xee\\x9d\\xba\"\t// U+e77a\n#define ICON_MS_ZONE_PERSON_URGENT \"\\xee\\x9e\\x88\"\t// U+e788\n#define ICON_MS_ZOOM_IN \"\\xee\\xa3\\xbf\"\t// U+e8ff\n#define ICON_MS_ZOOM_IN_MAP \"\\xee\\xac\\xad\"\t// U+eb2d\n#define ICON_MS_ZOOM_OUT \"\\xee\\xa4\\x80\"\t// U+e900\n#define ICON_MS_ZOOM_OUT_MAP \"\\xee\\x95\\xab\"\t// U+e56b\n"
  },
  {
    "path": "frontend/settings.cpp",
    "content": "#include \"iris.hpp\"\n#include \"config.hpp\"\n\n#include \"ps2_elf.h\"\n#include \"ps2_iso9660.h\"\n\n#define TOML_EXCEPTIONS 0\n#include <toml++/toml.hpp>\n\nnamespace iris::settings {\n\nvoid print_version() {\n    puts(\n        \"iris (\" STR(_IRIS_VERSION) \" \" STR(_IRIS_OSVERSION) \")\\n\"\n        \"Copyright (C) 2026 Allkern/Lisandro Alarcon\\n\\n\"\n        \"MIT License\\n\"\n        \"THE SOFTWARE IS PROVIDED \\\"AS IS\\\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\\n\"\n        \"IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\\n\"\n        \"FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\\n\"\n        \"AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\\n\"\n        \"LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\\n\"\n        \"OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\\n\"\n        \"SOFTWARE.\"\n    );\n}\n\nvoid print_help() {\n    puts(\n        \"Usage: iris [OPTION]... <path-to-disc-image>\\n\"\n        \"\\n\"\n        \"  -b, --bios               Specify a PlayStation 2 BIOS dump file\\n\"\n        \"      --rom1               Specify a DVD player dump file\\n\"\n        \"      --rom2               Specify a ROM2 dump file\\n\"\n        \"  -d, --boot               Specify a direct kernel boot path\\n\"\n        \"  -i, --disc               Specify a path to a disc image file\\n\"\n        \"  -x, --executable         Specify a path to an ELF executable to be\\n\"\n        \"                             loaded on system startup\\n\"\n        \"      --slot1              Specify a path to a memory card file to\\n\"\n        \"                             be inserted on slot 1\\n\"\n        \"      --slot2              Specify a path to a memory card file to\\n\"\n        \"                             be inserted on slot 2\\n\"\n        \"      --snap               Specify a directory for storing screenshots\\n\"\n        \"  -h, --help               Display this help and exit\\n\"\n        \"  -v, --version            Output version information and exit\\n\"\n    );\n}\n\nbool parse_mappings_file(iris::instance* iris) {\n    iris->mappings_path = iris->pref_path + \"mappings.toml\";\n\n    std::ifstream mappings_file(iris->mappings_path);\n\n    if (!mappings_file.is_open())\n        return false;\n\n    toml::parse_result result = toml::parse(mappings_file);\n\n    if (!result) {\n        std::string desc(result.error().description());\n\n        printf(\"iris: Couldn't parse mappings file: %s\\n\", desc.c_str());\n\n        return false;\n    }\n\n    toml::table& tbl = result.table();\n\n    for (auto& map : tbl) {\n        printf(\"input: Parsing input map \\\"%s\\\"...\\n\", map.first.data());\n\n        mapping input_mapping {};\n\n        input_mapping.name = map.first.data();\n        input_mapping.map = bidirectional_map<uint64_t, input_action>();\n\n        for (auto& input_map : *map.second.as_table()) {\n            uint64_t key = std::stoull(input_map.first.data());\n            uint64_t value = input_map.second.as_integer()->get();\n\n            input_mapping.map.insert(key, static_cast<input_action>(value));\n\n            // printf(\"entry: %d -> %d\\n\", std::stoull(input_map.first.data()), input_map.second.as_integer()->get());\n        }\n\n        iris->input_maps.push_back(input_mapping);\n    }\n\n    return true;\n}\n\nbool parse_toml_settings(iris::instance* iris) {\n    iris->settings_path = iris->pref_path + \"settings.toml\";\n\n    toml::parse_result result = toml::parse_file(iris->settings_path);\n\n    if (!result) {\n        std::string desc(result.error().description());\n\n        printf(\"iris: Couldn't parse settings file: %s\\n\", desc.c_str());\n\n        return false;\n    }\n\n    toml::table& tbl = result.table();\n\n    auto paths = tbl[\"paths\"];\n    iris->bios_path = paths[\"bios_path\"].value_or(\"\");\n    iris->rom1_path = paths[\"rom1_path\"].value_or(\"\");\n    iris->rom2_path = paths[\"rom2_path\"].value_or(\"\");\n    iris->nvram_path = paths[\"nvram_path\"].value_or(\"\");\n    iris->mcd0_path = paths[\"mcd0_path\"].value_or(\"\");\n    iris->mcd1_path = paths[\"mcd1_path\"].value_or(\"\");\n    iris->snap_path = paths[\"snap_path\"].value_or(\"snap\");\n    iris->flash_path = paths[\"flash_path\"].value_or(\"\");\n    iris->gcdb_path = paths[\"gcdb_path\"].value_or(\"\");\n\n    auto window = tbl[\"window\"];\n    iris->window_width = window[\"window_width\"].value_or(960);\n    iris->window_height = window[\"window_height\"].value_or(720);\n    iris->fullscreen = window[\"fullscreen\"].value_or(0);\n\n    auto display = tbl[\"display\"];\n    iris->aspect_mode = display[\"aspect_mode\"].value_or(RENDER_ASPECT_AUTO);\n    iris->filter = display[\"filter\"].value_or(true);\n    iris->integer_scaling = display[\"integer_scaling\"].value_or(false);\n    iris->scale = display[\"scale\"].value_or(1.5f);\n    iris->renderer_backend = display[\"renderer\"].value_or(RENDERER_BACKEND_HARDWARE);\n    iris->window_width = display[\"window_width\"].value_or(960);\n    iris->window_height = display[\"window_height\"].value_or(720);\n    iris->menubar_height = display[\"menubar_height\"].value_or(0);\n    iris->angle = display[\"angle\"].value_or(0);\n    iris->flip_x = display[\"flip_x\"].value_or(false);\n    iris->flip_y = display[\"flip_y\"].value_or(false);\n    iris->vsync = display[\"vsync\"].value_or(true);\n\n    auto audio = tbl[\"audio\"];\n    iris->mute = audio[\"mute\"].value_or(false);\n    iris->volume = audio[\"volume\"].value_or(1.0);\n    iris->mute_adma = audio[\"mute_adma\"].value_or(true);\n\n    auto debugger = tbl[\"debugger\"];\n    iris->show_ee_control = debugger[\"show_ee_control\"].value_or(false);\n    iris->show_ee_state = debugger[\"show_ee_state\"].value_or(false);\n    iris->show_ee_logs = debugger[\"show_ee_logs\"].value_or(false);\n    iris->show_ee_interrupts = debugger[\"show_ee_interrupts\"].value_or(false);\n    iris->show_ee_dmac = debugger[\"show_ee_dmac\"].value_or(false);\n    iris->show_iop_control = debugger[\"show_iop_control\"].value_or(false);\n    iris->show_iop_state = debugger[\"show_iop_state\"].value_or(false);\n    iris->show_iop_logs = debugger[\"show_iop_logs\"].value_or(false);\n    iris->show_iop_interrupts = debugger[\"show_iop_interrupts\"].value_or(false);\n    iris->show_iop_modules = debugger[\"show_iop_modules\"].value_or(false);\n    iris->show_iop_dma = debugger[\"show_iop_dma\"].value_or(false);\n    iris->show_gs_debugger = debugger[\"show_gs_debugger\"].value_or(false);\n    iris->show_spu2_debugger = debugger[\"show_spu2_debugger\"].value_or(false);\n    iris->show_memory_viewer = debugger[\"show_memory_viewer\"].value_or(false);\n    iris->show_memory_search = debugger[\"show_memory_search\"].value_or(false);\n    iris->show_vu_disassembler = debugger[\"show_vu_disassembler\"].value_or(false);\n    iris->show_status_bar = debugger[\"show_status_bar\"].value_or(true);\n    iris->show_pad_debugger = debugger[\"show_pad_debugger\"].value_or(false);\n    iris->show_threads = debugger[\"show_threads\"].value_or(false);\n    iris->show_sysmem_logs = debugger[\"show_sysmem_logs\"].value_or(false);\n    iris->show_overlay = debugger[\"show_overlay\"].value_or(false);\n\n    // iris->show_symbols = debugger[\"show_symbols\"].value_or(false);\n    iris->show_breakpoints = debugger[\"show_breakpoints\"].value_or(false);\n    iris->show_imgui_demo = debugger[\"show_imgui_demo\"].value_or(false);\n    iris->skip_fmv = debugger[\"skip_fmv\"].value_or(false);\n    iris->timescale = debugger[\"timescale\"].value_or(8);\n\n    auto system = tbl[\"system\"];\n    iris->system = system[\"model\"].value_or(PS2_SYSTEM_AUTO);\n    iris->autostart = system[\"autostart\"].value_or(true);\n\n    toml::array* mac_array = system[\"mac_address\"].as_array();\n\n    if (mac_array && mac_array->size() == 6) {\n        for (int i = 0; i < 6; i++) {\n            iris->mac_address[i] = static_cast<uint8_t>(mac_array->at(i).as_integer()->get());\n        }\n    } else {\n        // Default MAC address\n        iris->mac_address[0] = 0x00;\n        iris->mac_address[1] = 0x1A;\n        iris->mac_address[2] = 0x2B;\n        iris->mac_address[3] = 0x3C;\n        iris->mac_address[4] = 0x4D;\n        iris->mac_address[5] = 0x5E;\n    }\n\n    auto screenshots = tbl[\"screenshots\"];\n    iris->screenshot_format = screenshots[\"format\"].value_or(IRIS_SCREENSHOT_FORMAT_PNG);\n    iris->screenshot_jpg_quality_mode = screenshots[\"jpg_quality_mode\"].value_or(IRIS_SCREENSHOT_JPG_QUALITY_MAXIMUM);\n    iris->screenshot_jpg_quality = screenshots[\"jpg_quality\"].value_or(50);\n    iris->screenshot_mode = screenshots[\"mode\"].value_or(IRIS_SCREENSHOT_MODE_INTERNAL);\n    iris->screenshot_shader_processing = screenshots[\"shader_processing\"].value_or(false);\n\n    auto hardware = tbl[\"hardware\"];\n    iris->hardware_backend_config.super_sampling = hardware[\"super_sampling\"].value_or(0);\n    iris->hardware_backend_config.force_progressive = hardware[\"force_progressive\"].value_or(false);\n    iris->hardware_backend_config.overscan = hardware[\"overscan\"].value_or(false);\n    iris->hardware_backend_config.crtc_offsets = hardware[\"crtc_offsets\"].value_or(false);\n    iris->hardware_backend_config.disable_mipmaps = hardware[\"disable_mipmaps\"].value_or(false);\n    iris->hardware_backend_config.unsynced_readbacks = hardware[\"unsynced_readbacks\"].value_or(false);\n    iris->hardware_backend_config.backbuffer_promotion = hardware[\"backbuffer_promotion\"].value_or(false);\n    iris->hardware_backend_config.allow_blend_demote = hardware[\"allow_blend_demote\"].value_or(false);\n\n    auto vulkan = tbl[\"vulkan\"];\n    iris->vulkan_physical_device = vulkan[\"physical_device\"].value_or(-1);\n    iris->vulkan_enable_validation_layers = vulkan[\"enable_validation_layers\"].value_or(false);\n\n    auto ui = tbl[\"ui\"];\n    iris->theme = ui[\"theme\"].value_or(IRIS_THEME_GRANITE);\n    iris->codeview_font_scale = ui[\"codeview_font_scale\"].value_or(1.0f);\n    iris->codeview_color_scheme = ui[\"codeview_color_scheme\"].value_or(IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_DARK);\n    iris->codeview_use_theme_background = ui[\"codeview_use_theme_background\"].value_or(true);\n    iris->ui_scale = ui[\"scale\"].value_or(1.0f);\n    iris->imgui_enable_viewports = ui[\"enable_viewports\"].value_or(false);\n\n    toml::array* bgcolor = tbl[\"ui\"][\"bgcolor\"].as_array();\n\n    if (bgcolor && bgcolor->size() == 3) {\n        iris->clear_value.color.float32[0] = (float)bgcolor->at(0).as_floating_point()->get();\n        iris->clear_value.color.float32[1] = (float)bgcolor->at(1).as_floating_point()->get();\n        iris->clear_value.color.float32[2] = (float)bgcolor->at(2).as_floating_point()->get();\n    } else {\n        iris->clear_value.color.float32[0] = 0.11f;\n        iris->clear_value.color.float32[1] = 0.11f;\n        iris->clear_value.color.float32[2] = 0.11f;\n    }\n\n#ifdef _WIN32\n    iris->windows_titlebar_style = tbl[\"ui\"][\"windows_titlebar_style\"].value_or(IRIS_TITLEBAR_DEFAULT);\n    iris->windows_enable_borders = tbl[\"ui\"][\"windows_enable_borders\"].value_or(true);\n    iris->windows_dark_mode = tbl[\"ui\"][\"windows_dark_mode\"].value_or(true);\n#endif\n\n    toml::array* recents = tbl[\"recents\"][\"array\"].as_array();\n\n    if (recents) {\n        for (int i = 0; i < recents->size(); i++) {\n            toml::table* entry = recents->at(i).as_table();\n\n            if (!entry) {\n                // Provided for backcompat with older settings files\n                std::string str = recents->at(i).as_string()->get();\n\n                iris->recents.push_back({ str, 0 });\n\n                continue;\n            }\n\n            iris::recent r = {\n                entry->operator[](\"path\").value_or(std::string()),\n                entry->operator[](\"type\").value_or(0)\n            };\n\n            iris->recents.push_back(r);\n        }\n    }\n\n    toml::array* shaders = tbl[\"shaders\"][\"array\"].as_array();\n    iris->enable_shaders = tbl[\"shaders\"][\"enable\"].value_or(false);\n\n    if (shaders) {\n        for (int i = 0; i < shaders->size(); i++)\n            iris->shader_passes_pending.push_back(shaders->at(i).as_string()->get());\n    }\n\n    return parse_mappings_file(iris);\n}\n\nbool check_for_quick_exit(int argc, const char* argv[]) {\n    for (int i = 1; i < argc; i++) {\n        std::string a(argv[i]);\n\n        if (a == \"-h\" || a == \"--help\") {\n            print_help();\n\n            return true;\n        } else if (a == \"-v\" || a == \"--version\") {\n            print_version();\n\n            return true;\n        }\n    }\n\n    return false;\n}\n\nvoid parse_cli_settings(iris::instance* iris, int argc, const char* argv[]) {\n    std::string bios_path;\n    std::string rom1_path;\n    std::string rom2_path;\n\n    for (int i = 1; i < argc; i++) {\n        std::string a(argv[i]);\n\n        if (a == \"-x\" || a == \"--executable\") {\n            iris->elf_path = argv[i+1];\n\n            ++i;\n        } else if (a == \"-d\" || a == \"--boot\") {\n            iris->boot_path = argv[i+1];\n\n            ++i;\n        } else if (a == \"-b\" || a == \"--bios\") {\n            bios_path = argv[i+1];\n\n            ++i;\n        } else if (a == \"--rom1\") {\n            rom1_path = argv[i+1];\n\n            ++i;\n        } else if (a == \"--rom2\") {\n            rom2_path = argv[i+1];\n\n            ++i;\n        } else if (a == \"-i\" || a == \"--disc\") {\n            iris->disc_path = argv[i+1];\n\n            ++i;\n        } else if (a == \"--slot1\") {\n            iris->mcd0_path = argv[i+1];\n\n            ++i;\n        } else if (a == \"--slot2\") {\n            iris->mcd1_path = argv[i+1];\n\n            ++i;\n        } else {\n            iris->disc_path = argv[i];\n        }\n    }\n\n    if (bios_path.size()) {\n        if (!ps2_load_bios(iris->ps2, bios_path.c_str())) {\n            // push_info(iris, \"Couldn't load BIOS\");\n\n            iris->show_bios_setting_window = true;\n        }\n    } else {\n        if (iris->bios_path.size()) {\n            if (!ps2_load_bios(iris->ps2, iris->bios_path.c_str())) {\n                // push_info(iris, \"Couldn't load BIOS\");\n\n                iris->show_bios_setting_window = true;\n            }\n        } else {\n            iris->show_bios_setting_window = true;\n        }\n    }\n\n    if (rom1_path.size()) {\n        if (!ps2_load_rom1(iris->ps2, rom1_path.c_str())) {\n            // push_info(iris, \"Couldn't load ROM1\");\n        }\n    } else {\n        if (iris->rom1_path.size()) {\n            if (!ps2_load_rom1(iris->ps2, iris->rom1_path.c_str())) {\n                // push_info(iris, \"Couldn't load ROM1\");\n            }\n        }\n    }\n\n    if (rom2_path.size()) {\n        if (!ps2_load_rom2(iris->ps2, rom2_path.c_str())) {\n            // push_info(iris, \"Couldn't load ROM2\");\n        }\n    } else {\n        if (iris->rom2_path.size()) {\n            if (!ps2_load_rom2(iris->ps2, iris->rom2_path.c_str())) {\n                // push_info(iris, \"Couldn't load ROM2\");\n            }\n        }\n    }\n\n    if (iris->elf_path.size()) {\n        ps2_set_system(iris->ps2, iris->system);\n        ps2_load_bios(iris->ps2, iris->bios_path.c_str());\n        ps2_elf_load(iris->ps2, iris->elf_path.c_str());\n\n        iris->loaded = iris->elf_path;\n    }\n\n    if (iris->boot_path.size()) {\n        ps2_set_system(iris->ps2, iris->system);\n        ps2_load_bios(iris->ps2, iris->bios_path.c_str());\n        ps2_boot_file(iris->ps2, iris->boot_path.c_str());\n\n        iris->loaded = iris->boot_path;\n    }\n\n    if (iris->disc_path.size()) {\n        if (ps2_cdvd_open(iris->ps2->cdvd, iris->disc_path.c_str(), 0))\n            return;\n\n        char* boot_file = disc_get_boot_path(iris->ps2->cdvd->disc);\n\n        if (!boot_file)\n            return;\n\n        ps2_set_system(iris->ps2, iris->system);\n        ps2_load_bios(iris->ps2, iris->bios_path.c_str());\n        ps2_boot_file(iris->ps2, boot_file);\n\n        iris->loaded = iris->disc_path;\n    }\n}\n\nbool init(iris::instance* iris, int argc, const char* argv[]) {\n    int r = parse_toml_settings(iris);\n\n    parse_cli_settings(iris, argc, argv);\n\n    if (iris->nvram_path.size())\n        ps2_cdvd_load_nvram(iris->ps2->cdvd, iris->nvram_path.c_str());\n\n    if (iris->mcd0_path.size())\n        emu::attach_memory_card(iris, 0, iris->mcd0_path.c_str());\n\n    if (iris->mcd1_path.size())\n        emu::attach_memory_card(iris, 1, iris->mcd1_path.c_str());\n\n    // Apply settings loaded from file/CLI\n    ps2_set_timescale(iris->ps2, iris->timescale);\n\n    ee_set_fmv_skip(iris->ps2->ee, iris->skip_fmv);\n\n    ps2_set_system(iris->ps2, iris->system);\n    ps2_speed_load_flash(iris->ps2->speed, iris->flash_path.c_str());\n    ps2_speed_set_mac_address(iris->ps2->speed, iris->mac_address);\n\n    return true;\n}\n\nvoid close(iris::instance* iris) {\n    if (!iris->dump_to_file)\n        return;\n\n    std::ofstream file(iris->settings_path);\n    std::ofstream mappings_file(iris->pref_path + \"mappings.toml\");\n\n    file << \"# File auto-generated by \" IRIS_TITLE \"\\n\\n\";\n\n    auto tbl = toml::table {\n        { \"system\", toml::table {\n            { \"model\", iris->system },\n            { \"mac_address\", toml::array {\n                iris->mac_address[0],\n                iris->mac_address[1],\n                iris->mac_address[2],\n                iris->mac_address[3],\n                iris->mac_address[4],\n                iris->mac_address[5]\n            } },\n            { \"autostart\", iris->autostart }\n        } },\n        { \"input\", toml::table {\n            { \"slot1_device\", iris->input_devices[0] ? iris->input_devices[0]->get_type() : 0 },\n            { \"slot2_device\", iris->input_devices[1] ? iris->input_devices[1]->get_type() : 0 },\n            { \"slot1_mapping\", iris->input_map[0] },\n            { \"slot2_mapping\", iris->input_map[1] }\n        } },\n        { \"screenshots\", toml::table {\n            { \"format\", iris->screenshot_format },\n            { \"mode\", iris->screenshot_mode },\n            { \"jpg_quality_mode\", iris->screenshot_jpg_quality_mode },\n            { \"jpg_quality\", iris->screenshot_jpg_quality },\n            { \"shader_processing\", iris->screenshot_shader_processing }\n        } },\n\n        // To-do: Change this to \"backends\" and use dotted entries\n        // e.g.\n        // [backend_settings]\n        // hardware.super_sampling = 2\n        // etc.\n        { \"hardware\", toml::table {\n            { \"super_sampling\", iris->hardware_backend_config.super_sampling },\n            { \"force_progressive\", iris->hardware_backend_config.force_progressive },\n            { \"overscan\", iris->hardware_backend_config.overscan },\n            { \"crtc_offsets\", iris->hardware_backend_config.crtc_offsets },\n            { \"disable_mipmaps\", iris->hardware_backend_config.disable_mipmaps },\n            { \"unsynced_readbacks\", iris->hardware_backend_config.unsynced_readbacks },\n            { \"backbuffer_promotion\", iris->hardware_backend_config.backbuffer_promotion },\n            { \"allow_blend_demote\", iris->hardware_backend_config.allow_blend_demote }\n        } },\n        { \"vulkan\", toml::table {\n            { \"physical_device\", iris->vulkan_physical_device },\n            { \"enable_validation_layers\", iris->vulkan_enable_validation_layers }\n        } },\n        { \"debugger\", toml::table {\n            { \"show_ee_control\", iris->show_ee_control },\n            { \"show_ee_state\", iris->show_ee_state },\n            { \"show_ee_logs\", iris->show_ee_logs },\n            { \"show_ee_interrupts\", iris->show_ee_interrupts },\n            { \"show_ee_dmac\", iris->show_ee_dmac },\n            { \"show_iop_control\", iris->show_iop_control },\n            { \"show_iop_state\", iris->show_iop_state },\n            { \"show_iop_logs\", iris->show_iop_logs },\n            { \"show_iop_interrupts\", iris->show_iop_interrupts },\n            { \"show_iop_modules\", iris->show_iop_modules},\n            { \"show_iop_dma\", iris->show_iop_dma },\n            { \"show_gs_debugger\", iris->show_gs_debugger },\n            { \"show_spu2_debugger\", iris->show_spu2_debugger },\n            { \"show_memory_viewer\", iris->show_memory_viewer },\n            { \"show_memory_search\", iris->show_memory_search },\n            { \"show_vu_disassembler\", iris->show_vu_disassembler },\n            { \"show_status_bar\", iris->show_status_bar },\n            { \"show_pad_debugger\", iris->show_pad_debugger },\n            { \"show_breakpoints\", iris->show_breakpoints },\n            { \"show_threads\", iris->show_threads },\n            { \"show_sysmem_logs\", iris->show_sysmem_logs },\n            { \"show_imgui_demo\", iris->show_imgui_demo },\n            { \"show_overlay\", iris->show_overlay },\n            { \"skip_fmv\", iris->skip_fmv },\n            { \"timescale\", iris->timescale }\n        } },\n        { \"display\", toml::table {\n            { \"scale\", iris->scale },\n            { \"aspect_mode\", iris->aspect_mode },\n            { \"integer_scaling\", iris->integer_scaling },\n            { \"fullscreen\", iris->fullscreen },\n            { \"filter\", iris->filter },\n            { \"renderer\", iris->renderer_backend },\n            { \"window_width\", iris->window_width },\n            { \"window_height\", iris->window_height },\n            { \"menubar_height\", iris->menubar_height },\n            { \"angle\", iris->angle },\n            { \"flip_x\", iris->flip_x },\n            { \"flip_y\", iris->flip_y },\n            { \"vsync\", iris->vsync }\n        } },\n        { \"ui\", toml::table {\n            { \"theme\", iris->theme },\n            { \"codeview_color_scheme\", iris->codeview_color_scheme },\n            { \"codeview_font_scale\", iris->codeview_font_scale },\n            { \"codeview_use_theme_background\", iris->codeview_use_theme_background },\n            { \"scale\", iris->ui_scale },\n            { \"bgcolor\", toml::array {\n                iris->clear_value.color.float32[0],\n                iris->clear_value.color.float32[1],\n                iris->clear_value.color.float32[2]\n            } },\n            { \"enable_viewports\", iris->imgui_enable_viewports },\n#ifdef _WIN32\n            { \"windows_titlebar_style\", iris->windows_titlebar_style },\n            { \"windows_enable_borders\", iris->windows_enable_borders },\n            { \"windows_dark_mode\", iris->windows_dark_mode },\n#endif\n        } },\n        { \"audio\", toml::table {\n            { \"mute\", iris->mute },\n            { \"mute_adma\", iris->mute_adma },\n            { \"volume\", iris->volume }\n        } },\n        { \"paths\", toml::table {\n            { \"bios_path\", iris->bios_path },\n            { \"rom1_path\", iris->rom1_path },\n            { \"rom2_path\", iris->rom2_path },\n            { \"nvram_path\", iris->nvram_path },\n            { \"mcd0_path\", iris->mcd0_path },\n            { \"mcd1_path\", iris->mcd1_path },\n            { \"snap_path\", iris->snap_path },\n            { \"flash_path\", iris->flash_path },\n            { \"gcdb_path\", iris->gcdb_path },\n        } },\n        { \"recents\", toml::table {\n            { \"array\", toml::array() }\n        } },\n        { \"shaders\", toml::table {\n            { \"enable\", iris->enable_shaders },\n            { \"array\", toml::array() }\n        } },\n    };\n\n    toml::array* recents = tbl[\"recents\"][\"array\"].as_array();\n\n    for (const auto& s : iris->recents)\n        recents->push_back(toml::table { { \"type\", s.type }, { \"path\", s.path } });\n\n    toml::array* shaders = tbl[\"shaders\"][\"array\"].as_array();\n\n    for (auto& s : shaders::vector(iris))\n        shaders->push_back(s->get_id());\n\n    // Generate input mappings file\n    mappings_file << \"# File auto-generated by \" IRIS_TITLE \"\\n\\n\";\n\n    toml::table mappings_tbl {};\n\n    for (auto& map : iris->input_maps) {\n        toml::table map_tbl { { map.name, toml::table {} } };\n\n        for (auto& entry : map.map.forward_map()) {\n            toml::table t {\n                { std::to_string(entry.first), entry.second }\n            };\n\n            t.is_inline(true);\n\n            map_tbl[map.name].as_table()->insert(std::to_string(entry.first), entry.second);\n        }\n\n        mappings_tbl.insert(map.name, map_tbl[map.name]);\n    }\n\n    file << tbl;\n    mappings_file << mappings_tbl;\n}\n\n}\n"
  },
  {
    "path": "frontend/shaders.cpp",
    "content": "#include <unordered_map>\n#include <algorithm>\n\n#include \"config.hpp\"\n#include \"iris.hpp\"\n\n// INCBIN stuff\n#define INCBIN_PREFIX g_\n#define INCBIN_STYLE INCBIN_STYLE_SNAKE\n\n#include \"incbin.h\"\n\nINCBIN(encoder_frag_shader, \"../shaders/encoder.spv\");\nINCBIN(decoder_frag_shader, \"../shaders/decoder.spv\");\nINCBIN(curvature_frag_shader, \"../shaders/curvature.spv\");\nINCBIN(scanlines_frag_shader, \"../shaders/scanlines.spv\");\nINCBIN(noise_frag_shader, \"../shaders/noise.spv\");\n\nnamespace iris::shaders {\n\nbool pass::init(iris::instance* iris, const void* data, size_t size, std::string id) {\n    m_vert_shader = iris->default_vert_shader;\n    m_iris = iris;\n    m_id = id;\n\n    VkShaderModuleCreateInfo create_info = {};\n    create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO;\n    create_info.pCode = (const uint32_t*)data;\n    create_info.codeSize = size;\n\n    if (vkCreateShaderModule(m_iris->device, &create_info, nullptr, &m_frag_shader) != VK_SUCCESS) {\n        fprintf(stderr, \"render: Failed to create fragment shader module\\n\");\n\n        return false;\n    }\n\n    return rebuild();\n}\n\npass::pass(iris::instance* iris, const void* data, size_t size, std::string id) {\n    init(iris, data, size, id);\n}\n\npass::pass(pass&& other) {\n    m_pipeline_layout = other.m_pipeline_layout;\n    m_pipeline = other.m_pipeline;\n    m_render_pass = other.m_render_pass;\n    m_input = other.m_input;\n    m_vert_shader = other.m_vert_shader;\n    m_frag_shader = other.m_frag_shader;\n    m_iris = other.m_iris;\n    m_id = other.m_id;\n    bypass = other.bypass;\n\n    other.m_pipeline_layout = VK_NULL_HANDLE;\n    other.m_pipeline = VK_NULL_HANDLE;\n    other.m_render_pass = VK_NULL_HANDLE;\n    other.m_input = VK_NULL_HANDLE;\n    other.m_vert_shader = VK_NULL_HANDLE;\n    other.m_frag_shader = VK_NULL_HANDLE;\n    other.m_iris = nullptr;\n    other.m_id = \"\";\n    other.bypass = false;\n}\n\npass& pass::operator=(pass&& other) {\n    if (this != &other) {\n        m_pipeline_layout = other.m_pipeline_layout;\n        m_pipeline = other.m_pipeline;\n        m_render_pass = other.m_render_pass;\n        m_input = other.m_input;\n        m_vert_shader = other.m_vert_shader;\n        m_frag_shader = other.m_frag_shader;\n        m_iris = other.m_iris;\n        m_id = other.m_id;\n        bypass = other.bypass;\n    }\n\n    other.m_pipeline_layout = VK_NULL_HANDLE;\n    other.m_pipeline = VK_NULL_HANDLE;\n    other.m_render_pass = VK_NULL_HANDLE;\n    other.m_input = VK_NULL_HANDLE;\n    other.m_vert_shader = VK_NULL_HANDLE;\n    other.m_frag_shader = VK_NULL_HANDLE;\n    other.m_iris = nullptr;\n    other.m_id = \"\";\n    other.bypass = false;\n\n    return *this;\n}\n\nvoid pass::destroy() {\n    if (!m_iris)\n        return;\n\n    vulkan::wait_idle(m_iris);\n\n    if (m_pipeline_layout) vkDestroyPipelineLayout(m_iris->device, m_pipeline_layout, nullptr);\n    if (m_pipeline) vkDestroyPipeline(m_iris->device, m_pipeline, nullptr);\n    if (m_render_pass) vkDestroyRenderPass(m_iris->device, m_render_pass, nullptr);\n    if (m_frag_shader) vkDestroyShaderModule(m_iris->device, m_frag_shader, nullptr);\n\n    if (m_vert_shader != m_iris->default_vert_shader)\n        if (m_vert_shader) vkDestroyShaderModule(m_iris->device, m_vert_shader, nullptr);\n}\n\npass::~pass() {\n    destroy();\n}\n\nbool pass::rebuild() {\n    vulkan::wait_idle(m_iris);\n\n    if (m_pipeline_layout) vkDestroyPipelineLayout(m_iris->device, m_pipeline_layout, nullptr);\n    if (m_pipeline) vkDestroyPipeline(m_iris->device, m_pipeline, nullptr);\n    if (m_render_pass) vkDestroyRenderPass(m_iris->device, m_render_pass, nullptr);\n\n    VkPushConstantRange push_constant_range = {};\n    push_constant_range.offset = 0;\n    push_constant_range.size = sizeof(push_constants);\n    push_constant_range.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT;\n\n    VkPipelineLayoutCreateInfo pipeline_layout_info = {};\n    pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO;\n    pipeline_layout_info.setLayoutCount = 1;\n    pipeline_layout_info.pSetLayouts = &m_iris->shader_descriptor_set_layout;\n    pipeline_layout_info.pushConstantRangeCount = 1;\n    pipeline_layout_info.pPushConstantRanges = &push_constant_range;\n\n    if (vkCreatePipelineLayout(m_iris->device, &pipeline_layout_info, nullptr, &m_pipeline_layout) != VK_SUCCESS) {\n        fprintf(stderr, \"render: Failed to create shader pipeline layout\\n\");\n\n        destroy();\n\n        return false;\n    }\n\n    // Create render pass\n    VkAttachmentDescription color_attachment = {};\n    color_attachment.format = m_iris->image.format;\n    color_attachment.samples = VK_SAMPLE_COUNT_1_BIT;\n    color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE;\n    color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE;\n    color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE;\n    color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    color_attachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n\n    VkAttachmentReference color_attachment_ref = {};\n    color_attachment_ref.attachment = 0;\n    color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL;\n\n    VkSubpassDescription subpass = {};\n    subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS;\n    subpass.colorAttachmentCount = 1;\n    subpass.pColorAttachments = &color_attachment_ref;\n\n    VkSubpassDependency dependency = {};\n    dependency.srcSubpass = VK_SUBPASS_EXTERNAL;\n    dependency.dstSubpass = 0;\n    dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n    dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT;\n    dependency.srcAccessMask = 0;\n    dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT;\n\n    VkRenderPass render_pass = VK_NULL_HANDLE;\n\n    VkRenderPassCreateInfo render_pass_info = {};\n    render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO;\n    render_pass_info.attachmentCount = 1;\n    render_pass_info.pAttachments = &color_attachment;\n    render_pass_info.subpassCount = 1;\n    render_pass_info.pSubpasses = &subpass;\n    render_pass_info.dependencyCount = 1;\n    render_pass_info.pDependencies = &dependency;\n\n    if (vkCreateRenderPass(m_iris->device, &render_pass_info, nullptr, &m_render_pass) != VK_SUCCESS) {\n        fprintf(stderr, \"render: Failed to create shader render pass\\n\");\n\n        destroy();\n\n        return false;\n    }\n\n    // Create graphics pipeline\n    VkPipelineShaderStageCreateInfo shader_stages[2] = {};\n    shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n    shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT;\n    shader_stages[0].module = m_iris->default_vert_shader;\n    shader_stages[0].pName = \"main\";\n    shader_stages[0].pNext = VK_NULL_HANDLE;\n    shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO;\n    shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT;\n    shader_stages[1].module = m_frag_shader;\n    shader_stages[1].pName = \"main\";\n    shader_stages[1].pNext = VK_NULL_HANDLE;\n\n    static const VkDynamicState dynamic_states[] = {\n        VK_DYNAMIC_STATE_VIEWPORT,\n        VK_DYNAMIC_STATE_SCISSOR\n    };\n\n    VkPipelineDynamicStateCreateInfo dynamic_state_info = {};\n    dynamic_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO;\n    dynamic_state_info.dynamicStateCount = 2;\n    dynamic_state_info.pDynamicStates = dynamic_states;\n\n    VkPipelineVertexInputStateCreateInfo vertex_input_info = {};\n    vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO;\n    vertex_input_info.vertexBindingDescriptionCount = 0;\n    vertex_input_info.pVertexBindingDescriptions = VK_NULL_HANDLE;\n    vertex_input_info.vertexAttributeDescriptionCount = 0;\n    vertex_input_info.pVertexAttributeDescriptions = VK_NULL_HANDLE;\n\n    VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {};\n    input_assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO;\n    input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST;\n    input_assembly_info.primitiveRestartEnable = VK_FALSE;\n\n    VkViewport viewport = {};\n    viewport.x = 0.0f;\n    viewport.y = 0.0f;\n    viewport.width = (float)m_iris->image.width;\n    viewport.height = (float)m_iris->image.height;\n    viewport.minDepth = 0.0f;\n    viewport.maxDepth = 1.0f;\n\n    VkExtent2D extent = {};\n    extent.width = m_iris->image.width;\n    extent.height = m_iris->image.height;\n\n    VkRect2D scissor = {};\n    scissor.offset = {0, 0};\n    scissor.extent = extent;\n\n    VkPipelineViewportStateCreateInfo viewport_state_info = {};\n    viewport_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO;\n    viewport_state_info.viewportCount = 1;\n    viewport_state_info.pViewports = &viewport;\n    viewport_state_info.scissorCount = 1;\n    viewport_state_info.pScissors = &scissor;\n\n    VkPipelineRasterizationStateCreateInfo rasterizer_info = {};\n    rasterizer_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO;\n    rasterizer_info.depthClampEnable = VK_FALSE;\n    rasterizer_info.rasterizerDiscardEnable = VK_FALSE;\n    rasterizer_info.polygonMode = VK_POLYGON_MODE_FILL;\n    rasterizer_info.lineWidth = 1.0f;\n    rasterizer_info.cullMode = VK_CULL_MODE_NONE;\n    rasterizer_info.frontFace = VK_FRONT_FACE_CLOCKWISE;\n    rasterizer_info.depthBiasEnable = VK_FALSE;\n\n    VkPipelineColorBlendAttachmentState blend_attachment_state = {};\n    blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT;\n    blend_attachment_state.blendEnable = VK_FALSE;\n\n    VkPipelineColorBlendStateCreateInfo blend_state_info{};\n    blend_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO;\n    blend_state_info.logicOpEnable = VK_FALSE;\n    blend_state_info.attachmentCount = 1;\n    blend_state_info.pAttachments = &blend_attachment_state;\n\n    VkPipelineMultisampleStateCreateInfo multisampling_state_info = {};\n    multisampling_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO;\n    multisampling_state_info.sampleShadingEnable = VK_FALSE;\n    multisampling_state_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT;\n\n    VkGraphicsPipelineCreateInfo pipeline_info = {};\n    pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO;\n    pipeline_info.stageCount = 2;\n    pipeline_info.pStages = shader_stages;\n    pipeline_info.pVertexInputState = &vertex_input_info;\n    pipeline_info.pInputAssemblyState = &input_assembly_info;\n    pipeline_info.pViewportState = &viewport_state_info;\n    pipeline_info.pRasterizationState = &rasterizer_info;\n    pipeline_info.pMultisampleState = &multisampling_state_info;\n    pipeline_info.pDepthStencilState = nullptr; // Optional\n    pipeline_info.pColorBlendState = &blend_state_info;\n    pipeline_info.pDynamicState = &dynamic_state_info;\n    pipeline_info.layout = m_pipeline_layout;\n    pipeline_info.renderPass = m_render_pass;\n    pipeline_info.subpass = 0;\n    pipeline_info.pTessellationState = VK_NULL_HANDLE;\n    pipeline_info.basePipelineHandle = VK_NULL_HANDLE; // Optional\n    pipeline_info.basePipelineIndex = -1; // Optional\n\n    if (vkCreateGraphicsPipelines(m_iris->device, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &m_pipeline) != VK_SUCCESS) {\n        fprintf(stderr, \"render: Failed to create shader pipeline\\n\");\n\n        destroy();\n\n        return false;\n    }\n\n    return true;\n}\n\nbool pass::ready() {\n    return m_pipeline_layout &&\n           m_pipeline &&\n           m_render_pass &&\n           m_vert_shader &&\n           m_frag_shader &&\n           m_iris;\n}\n\nVkPipelineLayout& pass::get_pipeline_layout() {\n    return m_pipeline_layout;\n}\n\nVkPipeline& pass::get_pipeline() {\n    return m_pipeline;\n}\n\nVkRenderPass& pass::get_render_pass() {\n    return m_render_pass;\n}\n\nVkImageView& pass::get_input() {\n    return m_input;\n}\n\nVkShaderModule& pass::get_vert_shader() {\n    return m_vert_shader;\n}\n\nVkShaderModule& pass::get_frag_shader() {\n    return m_frag_shader;\n}\n\nstd::string pass::get_id() const {\n    return m_id;\n}\n\nstd::unordered_map <std::string, std::pair <void*, size_t>> g_builtin_shaders = {\n    { \"iris-ntsc-encoder\", { (void*)g_encoder_frag_shader_data, (size_t)g_encoder_frag_shader_size } },\n    { \"iris-ntsc-decoder\", { (void*)g_decoder_frag_shader_data, (size_t)g_decoder_frag_shader_size } },\n    { \"iris-ntsc-curvature\", { (void*)g_curvature_frag_shader_data, (size_t)g_curvature_frag_shader_size } },\n    { \"iris-ntsc-scanlines\", { (void*)g_scanlines_frag_shader_data, (size_t)g_scanlines_frag_shader_size } },\n    { \"iris-ntsc-noise\", { (void*)g_noise_frag_shader_data, (size_t)g_noise_frag_shader_size } }\n};\n\nvoid push(iris::instance* iris, void* data, size_t size, std::string id) {\n    iris->shader_passes.push_back(new pass(iris, data, size, id));\n}\n\nvoid push(iris::instance* iris, std::string id) {\n    auto s = g_builtin_shaders.find(id);\n\n    if (s != g_builtin_shaders.end()) {\n        push(iris, s->second.first, s->second.second, id);\n    }\n}\n\nvoid pop(iris::instance* iris) {\n    delete iris->shader_passes.back();\n\n    iris->shader_passes.pop_back();\n}\n\nvoid insert(iris::instance* iris, int i, void* data, size_t size, std::string id) {\n    iris->shader_passes.insert(\n        iris->shader_passes.begin() + i,\n        new pass(iris, data, size, id)\n    );\n}\n\nvoid erase(iris::instance* iris, int i) {\n    delete iris->shader_passes.at(i);\n\n    iris->shader_passes.erase(iris->shader_passes.begin() + i);\n}\n\npass* at(iris::instance* iris, int i) {\n    return iris->shader_passes.at(i);\n}\n\nvoid swap(iris::instance* iris, int a, int b) {\n    pass* t = iris->shader_passes.at(a);\n\n    iris->shader_passes[a] = iris->shader_passes[b];\n    iris->shader_passes[b] = t;\n}\n\npass* front(iris::instance* iris) {\n    return iris->shader_passes.front();\n}\n\npass* back(iris::instance* iris) {\n    return iris->shader_passes.back();\n}\n\nsize_t count(iris::instance* iris) {\n    return iris->shader_passes.size();\n}\n\nvoid clear(iris::instance* iris) {\n    for (auto& pass : iris->shader_passes) {\n        delete pass;\n    }\n\n    iris->shader_passes.clear();\n}\n\nstd::vector <shaders::pass*>& vector(iris::instance* iris) {\n    return iris->shader_passes;\n}\n\n}\n"
  },
  {
    "path": "frontend/ui/about.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n#include \"config.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nvoid show_about_window(iris::instance* iris) {\n    using namespace ImGui;\n\n    static ImGuiWindowFlags flags =\n        ImGuiWindowFlags_NoResize |\n        ImGuiWindowFlags_NoCollapse |\n        ImGuiWindowFlags_NoDocking |\n        ImGuiWindowFlags_AlwaysAutoResize;\n\n    if (GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable && !GetIO().ConfigViewportsNoDecoration)\n        flags |= ImGuiWindowFlags_NoTitleBar;\n\n    if (Begin(\"About\", &iris->show_about_window, flags)) {\n        if (BeginChild(\"##iconchild\", ImVec2(100.0, 220.0), ImGuiChildFlags_AutoResizeY)) {\n            Image((ImTextureID)(intptr_t)iris->iris_icon.descriptor_set, ImVec2(100.0, 100.0));\n        } EndChild(); SameLine(0.0, 10.0);\n\n        if (BeginChild(\"##textchild\", ImVec2(350.0, 0.0))) {\n            PushFont(iris->font_heading);\n            Text(IRIS_TITLE);\n            PopFont();\n\n            Separator();\n\n            Text(\"Experimental PlayStation 2 emulator\");\n            Text(\"\");\n            Text(\"Available at \"); SameLine(0.0, 0.0);\n            TextLinkOpenURL(\"https://github.com/allkern/iris\", \"https://github.com/allkern/iris\");\n            Text(\"\");\n            TextWrapped(\n                \"Special thanks to: The emudev Discord server, Ziemas, \"\n                \"refraction, ncarrillo, cakehonolulu, Layle, and \"\n                \"the PCSX2 team for their kind support.\"\n            );\n            Text(\"\");\n            Text(\"Please file any issues to \"); SameLine(0.0, 0.0);\n            TextLinkOpenURL(\"our GitHub issues page\", \"https://github.com/allkern/iris/issues\"); SameLine(0.0, 0.0);\n            Text(\".\");\n        } EndChild();\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/bios_setting.cpp",
    "content": "#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n#include \"portable-file-dialogs.h\"\n\n#include \"rom.h\"\n\nnamespace iris {\n\nint stage = 0;\n\nbool bios_checked = false;\nint bios_valid = false;\n\nint is_valid(const char* path) {\n    FILE* f = fopen(path, \"rb\");\n\n    if (!f)\n        return 0;\n\n    fseek(f, 0, SEEK_END);\n\n    size_t size = ftell(f);\n\n    fseek(f, 0, SEEK_SET);\n\n    uint8_t* buf = new uint8_t[size];\n\n    fread(buf, 1, size, f);\n\n    int valid = ps2_rom0_is_valid(buf, size);\n\n    fclose(f);\n\n    delete[] buf;\n\n    return valid ? 2 : 1;\n}\n\nvoid show_memory_card_stage(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (BeginChild(\"##iconchild\", ImVec2(100.0, 0.0), ImGuiChildFlags_AutoResizeY)) {\n        Image((ImTextureID)(intptr_t)iris->iris_icon.descriptor_set, ImVec2(100.0, 100.0));\n    } EndChild(); SameLine(0.0, 10.0);\n\n    if (BeginChild(\"##textchild\", ImVec2(360.0, 0.0), ImGuiChildFlags_AutoResizeY)) {\n        PushFont(iris->font_heading);\n        Text(\"Done!\");\n        PopFont();\n\n        Separator();\n\n        Text(\"You may now close this window and start using Iris.\\n\\n\"\n            \"Additionally you might want to check out the settings\\n\"\n            \"menu to configure memory cards, display, audio, and\\n\"\n            \"input options.\\n\\n\"\n        );\n\n        Text(ICON_MS_WARNING \" Please note that Iris is a work-in-progress emulator.\\n\"\n            \"You might encounter bugs or experience poor performance\\n\"\n            \"in general. We will appreciate any feedback or bug reports\\n\"\n            \"you may provide.\\n\\n\"\n        );\n\n        Text(\"You can report issues or provide feedback at:\");\n\n        TextLinkOpenURL(\"https://github.com/allkern/iris/issues\");\n\n        Separator();\n\n        static bool open_settings = true;\n\n        if (Button(\"Done\")) {\n            CloseCurrentPopup();\n\n            iris->show_bios_setting_window = false;\n\n            if (open_settings) {\n                iris->show_settings = true;\n            }\n        } SameLine();\n\n        Checkbox(\"Open settings menu\", &open_settings);\n    } EndChild();\n}\n\nvoid show_bios_stage(iris::instance* iris) {\n    using namespace ImGui;\n\n    static char buf[512];\n\n    PushFont(iris->font_heading);\n    Text(\"Welcome to Iris!\");\n    PopFont();\n    Separator();\n    Text(\n        \"Iris requires a PlayStation 2 BIOS file in order to run.\\n\\n\"\n        \"Please select one using the box below or provide one using the\\n\"\n        \"command line arguments.\"\n    );\n\n    if (InputTextWithHint(\"##BIOS\", \"e.g. scph10000.bin\", buf, 512, ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_EnterReturnsTrue)) {\n        bios_checked = true;\n        bios_valid = is_valid(buf);\n    }\n\n    SameLine();\n\n    if (Button(ICON_MS_FOLDER)) {\n        audio::mute(iris);\n\n        auto f = pfd::open_file(\"Select BIOS file\", \"\", {\n            \"All File Types (*.bin; *.rom0)\", \"*.bin *.rom0\",\n            \"All Files (*.*)\", \"*\"\n        });\n\n        while (!f.ready());\n\n        audio::unmute(iris);\n\n        if (f.result().size()) {\n            strncpy(buf, f.result().at(0).c_str(), 512);\n\n            bios_checked = true;\n            bios_valid = is_valid(buf);\n        }\n    }\n\n    if (bios_checked) {\n        ImVec4 col = bios_valid ? ImVec4(0.0f, 1.0f, 0.0f, 1.0f) : ImVec4(1.0f, 1.0f, 0.0f, 1.0f);\n        const char* text = nullptr;\n\n        switch (bios_valid) {\n            case 0: {\n                col = ImVec4(0.86f, 0.19f, 0.18f, 1.0f);\n                text = ICON_MS_CLOSE \" Couldn't open the specified file.\";\n            } break;\n\n            case 1: {\n                col = ImVec4(0.90f, 0.73f, 0.2f, 1.0f);\n                text = ICON_MS_WARNING \" BIOS is unknown, it might not work as expected.\";\n            } break;\n\n            case 2: {\n                col = ImVec4(0.42f, 0.85f, 0.1f, 1.0f);\n                text = ICON_MS_CHECK \" BIOS is known!\";\n            } break;\n        }\n\n        TextColored(col, text);\n    }\n\n    // To-do: Add file validation\n\n    Separator();\n\n    BeginDisabled(!bios_valid || !buf[0]);\n\n    if (Button(\"Next\")) {\n        iris->bios_path = buf;\n        iris->dump_to_file = true;\n\n        if (!ps2_load_bios(iris->ps2, iris->bios_path.c_str())) {\n            push_info(iris, \"Couldn't load BIOS\");\n\n            stage = 0;\n        } else {\n            stage = 1;\n        }\n    }\n\n    EndDisabled();\n}\n\nvoid show_bios_setting_window(iris::instance* iris) {\n    using namespace ImGui;\n\n    OpenPopup(\"Welcome\");\n\n    // Always center this window when appearing\n    ImVec2 center = GetMainViewport()->GetCenter();\n\n    SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f));\n\n    if (BeginPopupModal(\"Welcome\", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {\n        switch (stage) {\n            case 0: show_bios_stage(iris); break;\n            case 1: show_memory_card_stage(iris); break;\n        }\n\n        EndPopup();\n    }\n}\n\n}"
  },
  {
    "path": "frontend/ui/breakpoints.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nconst char* cpu_names[] = {\n    \"EE\",\n    \"IOP\"\n};\n\nstatic breakpoint* selected = nullptr;\nstatic breakpoint editable;\n\nvoid show_breakpoints_table(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (BeginTable(\"##breakpoints\", 5, ImGuiTableFlags_RowBg | ImGuiTableFlags_Sortable)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Address\");\n        TableSetupColumn(\"CPU\");\n        TableSetupColumn(\"Flags\");\n        TableSetupColumn(\"Size\");\n        TableSetupColumn(\"Actions\");\n        TableHeadersRow();\n        PopFont();\n\n        TableNextRow();\n\n        int i = 0;\n\n        for (breakpoint& b : iris->breakpoints) {\n            TableSetColumnIndex(0);\n\n            char buf[16]; sprintf(buf, \"##d%x\", i);\n\n            if (Selectable(buf, &b == selected, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns)) {\n                selected = &b;\n            } SameLine(0.0, 0.0);\n\n            PushFont(iris->font_code);\n\n            if (b.symbol) {\n                Text(\"%s\", b.symbol);\n            } else {\n                Text(\"%08x\", b.addr);\n            }\n\n            PopFont();\n\n            TableSetColumnIndex(1);\n\n            Text(b.cpu == BKPT_CPU_EE ? \"EE\" : \"IOP\");\n\n            TableSetColumnIndex(2);\n\n            PushFont(iris->font_code);\n\n            Text(\"%c%c%c\",\n                b.cond_r ? 'R' : '.',\n                b.cond_w ? 'W' : '.',\n                b.cond_x ? 'X' : '.'\n            );\n\n            PopFont();\n\n            TableSetColumnIndex(3);\n\n            Text(\"%d\", b.size);\n\n            TableSetColumnIndex(4);\n\n            sprintf(buf, b.enabled ? ICON_MS_CHECK \"##%x\" : \"##%x\", i);\n\n            if (Selectable(buf, false, 0, ImVec2(20, 0))) {\n                b.enabled = !b.enabled;\n            } SameLine();\n\n            sprintf(buf, ICON_MS_DELETE \"##%x\", i);\n\n            if (Selectable(buf, false, 0, ImVec2(20, 0))) {\n                selected = nullptr;\n\n                iris->breakpoints.erase(iris->breakpoints.begin() + i);\n            }\n\n            i++;\n\n            TableNextRow();\n        }\n\n        EndTable();\n    }\n}\n\nuint32_t parse_address(iris::instance* iris, const char* buf, const char** name) {\n    if (!buf || !buf[0])\n        return 0;\n\n    if (isalpha(buf[0]) || buf[0] == '_') {\n        for (iris::elf_symbol& sym : iris->symbols) {\n            if (strcmp(sym.name, buf) == 0) {\n                *name = sym.name;\n\n                return sym.addr;\n            }\n        }\n    } else {\n        *name = nullptr;\n\n        return strtoul(buf, NULL, 16);\n    }\n\n    return 0;\n}\n\nvoid show_breakpoint_editor(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (BeginCombo(\"CPU\", cpu_names[editable.cpu], ImGuiComboFlags_HeightSmall)) {\n        for (int i = 0; i < 2; i++) {\n            if (Selectable(cpu_names[i], editable.cpu == i)) {\n                editable.cpu = i;\n            }\n        }\n\n        EndCombo();\n    }\n\n    static char buf[512];\n\n    PushFont(iris->font_code);\n\n    if (InputText(\"##address\", buf, 512, ImGuiInputTextFlags_EnterReturnsTrue)) {\n        editable.addr = parse_address(iris, buf, &editable.symbol);\n    } SameLine();\n\n    PopFont();\n\n    Text(\"Address\");\n\n    Checkbox(\"##enabled\", &editable.enabled); SameLine();\n    Checkbox(\"##r\", &editable.cond_r); SameLine();\n    Checkbox(\"##w\", &editable.cond_w); SameLine();\n    Checkbox(\"Flags\", &editable.cond_x);\n\n    BeginDisabled(!selected);\n\n    if (Button(\"Edit breakpoint\")) {\n        editable.addr = parse_address(iris, buf, &editable.symbol);\n\n        *selected = editable;\n    } SameLine();\n\n    EndDisabled();\n\n    if (Button(\"New breakpoint\")) {\n        editable.addr = parse_address(iris, buf, &editable.symbol);\n\n        iris->breakpoints.push_back(editable);\n\n        selected = &iris->breakpoints.back();\n    }\n}\n\nvoid show_breakpoints(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (imgui::BeginEx(\"Breakpoints\", &iris->show_breakpoints, ImGuiWindowFlags_MenuBar)) {\n        if (BeginMenuBar()) {\n            MenuItem(\"Settings\");\n\n            EndMenuBar();\n        }\n\n        if (Button(ICON_MS_DELETE, ImVec2(50, 0))) {\n            selected = nullptr;\n\n            iris->breakpoints.clear();\n        } SameLine();\n\n        if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {\n            SetTooltip(\"Clear all\");\n        }\n\n        if (Button(ICON_MS_REMOVE_SELECTION)) {\n            for (breakpoint& b : iris->breakpoints) {\n                b.enabled = false;\n            }\n        } SameLine();\n\n        if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {\n            SetTooltip(\"Disable all\");\n        }\n\n        if (Button(ICON_MS_SELECT)) {\n            for (breakpoint& b : iris->breakpoints) {\n                b.enabled = true;\n            }\n        }\n\n        if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {\n            SetTooltip(\"Enable all\");\n        }\n\n        Separator();\n\n        if (BeginChild(\"##tablechild\", ImVec2(0, GetContentRegionAvail().y / 2.0f))) {\n            show_breakpoints_table(iris);\n        } EndChild();\n\n        SeparatorText(\"Add breakpoint\");\n\n        if (BeginChild(\"##tablechild2\")) {\n            show_breakpoint_editor(iris);\n        } EndChild();\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/control.cpp",
    "content": "#include <algorithm>\n#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\n#include \"ee/ee_dis.h\"\n#include \"ee/ee_def.hpp\"\n#include \"ee/vu_def.hpp\"\n#include \"iop/iop_dis.h\"\n\n#define IM_RGB(r, g, b) ImVec4(((float)r / 255.0f), ((float)g / 255.0f), ((float)b / 255.0f), 1.0)\n\nnamespace iris {\n\nstruct ee_dis_state g_ee_dis_state;\nstruct iop_dis_state g_iop_dis_state;\n\nvoid print_highlighted(iris::instance* iris, const char* buf) {\n    using namespace ImGui;\n\n    std::vector <std::string> tokens;\n\n    std::string text;\n\n    while (*buf) {\n        text.clear();        \n\n        if (isalpha(*buf)) {\n            while (isalpha(*buf) || isdigit(*buf) || (*buf == '.'))\n                text.push_back(*buf++);\n        } else if (isxdigit(*buf) || (*buf == '-')) {\n            while (isxdigit(*buf) || (*buf == 'x') || (*buf == '-'))\n                text.push_back(*buf++);\n        } else if (*buf == '$') {\n            while (*buf == '$' || isdigit(*buf) || isalpha(*buf) || *buf == '_')\n                text.push_back(*buf++);\n        } else if (*buf == ',') {\n            while (*buf == ',')\n                text.push_back(*buf++);\n        } else if (*buf == '(') {\n            while (*buf == '(')\n                text.push_back(*buf++);\n        } else if (*buf == ')') {\n            while (*buf == ')')\n                text.push_back(*buf++);\n        } else if (*buf == '<') {\n            while (*buf != '>')\n                text.push_back(*buf++);\n\n            text.push_back(*buf++);\n        } else if (*buf == '_') {\n            text.push_back(*buf++);\n        } else if (*buf == '.') {\n            text.push_back(*buf++);\n        } else {\n            printf(\"unhandled char %c (%d) \\\"%s\\\"\\n\", *buf, *buf, buf);\n\n            exit(1);\n        }\n\n        while (isspace(*buf))\n            text.push_back(*buf++);\n\n        tokens.push_back(text);\n    }\n\n    for (const std::string& t : tokens) {\n        if (isalpha(t[0])) {\n            ImVec4 col = ImVec4(\n                iris->codeview_color_mnemonic.Value.x,\n                iris->codeview_color_mnemonic.Value.y,\n                iris->codeview_color_mnemonic.Value.z,\n                iris->codeview_color_mnemonic.Value.w\n            );\n\n            TextColored(col, \"%s\", t.c_str());\n        } else if (isdigit(t[0]) || t[0] == '-') {\n            ImVec4 col = ImVec4(\n                iris->codeview_color_number.Value.x,\n                iris->codeview_color_number.Value.y,\n                iris->codeview_color_number.Value.z,\n                iris->codeview_color_number.Value.w\n            );\n\n            TextColored(col, \"%s\", t.c_str());\n        } else if (t[0] == '$') {\n            ImVec4 col = ImVec4(\n                iris->codeview_color_register.Value.x,\n                iris->codeview_color_register.Value.y,\n                iris->codeview_color_register.Value.z,\n                iris->codeview_color_register.Value.w\n            );\n\n            TextColored(col, \"%s\", t.c_str());\n        } else if (t[0] == '<') {\n            ImVec4 col = ImVec4(\n                iris->codeview_color_other.Value.x,\n                iris->codeview_color_other.Value.y,\n                iris->codeview_color_other.Value.z,\n                iris->codeview_color_other.Value.w\n            );\n\n            TextColored(col, \"%s\", t.c_str());\n        } else {\n            Text(\"%s\", t.c_str());\n        }\n\n        SameLine(0.0f, 0.0f);\n    }\n\n    NewLine();\n}\n\nstatic void show_ee_disassembly_view(iris::instance* iris) {\n    using namespace ImGui;\n\n    float font_scale = GetStyle().FontScaleMain;\n\n    GetStyle().FontScaleMain = iris->codeview_font_scale;\n\n    PushFont(iris->font_code);\n\n    if (!iris->codeview_use_theme_background) {\n        PushStyleColor(ImGuiCol_TableRowBg, ImVec4(\n            iris->codeview_color_background.Value.x,\n            iris->codeview_color_background.Value.y,\n            iris->codeview_color_background.Value.z,\n            iris->codeview_color_background.Value.w\n        ));\n        PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4(\n            iris->codeview_color_background.Value.x,\n            iris->codeview_color_background.Value.y,\n            iris->codeview_color_background.Value.z,\n            iris->codeview_color_background.Value.w\n        ));\n        PushStyleColor(ImGuiCol_Text, ImVec4(\n            iris->codeview_color_text.Value.x,\n            iris->codeview_color_text.Value.y,\n            iris->codeview_color_text.Value.z,\n            iris->codeview_color_text.Value.w\n        ));\n        PushStyleColor(ImGuiCol_TextDisabled, ImVec4(\n            iris->codeview_color_comment.Value.x,\n            iris->codeview_color_comment.Value.y,\n            iris->codeview_color_comment.Value.z,\n            iris->codeview_color_comment.Value.w\n        ));\n    }\n\n    if (BeginTable(\"table1\", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {\n        TableSetupColumn(\"a\", ImGuiTableColumnFlags_NoResize, 15.0f);\n        TableSetupColumn(\"b\", ImGuiTableColumnFlags_NoResize, 15.0f);\n        TableSetupColumn(\"c\", ImGuiTableColumnFlags_NoResize);\n\n        for (int row = -64; row < 64; row++) {\n            if (iris->ee_control_follow_pc) {\n                g_ee_dis_state.pc = iris->ps2->ee->pc + (row * 4);\n            } else {\n                g_ee_dis_state.pc = iris->ee_control_address + (row * 4);\n            }\n\n            TableNextRow();\n            TableSetColumnIndex(0);\n\n            PushFont(iris->font_icons);\n\n            auto v = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) {\n                return a.addr == g_ee_dis_state.pc && a.cpu == BKPT_CPU_EE;\n            });\n\n            if (v != iris->breakpoints.end()) {\n                Text(\" \" ICON_MS_FIBER_MANUAL_RECORD \" \");\n            }\n\n            TableSetColumnIndex(1);\n\n            if (g_ee_dis_state.pc == iris->ps2->ee->pc)\n                Text(ICON_MS_CHEVRON_RIGHT \" \");\n\n            PopFont();\n\n            TableSetColumnIndex(2);\n\n            for (elf_symbol& sym : iris->symbols) {\n                if (sym.addr == g_ee_dis_state.pc) {\n                    ImVec4 col = ImVec4(\n                        iris->codeview_color_mnemonic.Value.x,\n                        iris->codeview_color_mnemonic.Value.y,\n                        iris->codeview_color_mnemonic.Value.z,\n                        iris->codeview_color_mnemonic.Value.w\n                    );\n\n                    PushFont(iris->font_icons);\n                    TextColored(col, ICON_MS_STAT_0); SameLine();\n                    PopFont();\n                    TextColored(col, \"%s\", sym.name);\n\n                    break;\n                }\n            }\n\n            uint32_t opcode = ee_bus_read32(iris->ps2->ee_bus, g_ee_dis_state.pc & 0x1fffffff);\n\n            char buf[128], id[16];\n\n            char addr_str[9]; sprintf(addr_str, \"%08x\", g_ee_dis_state.pc);\n            char opcode_str[9]; sprintf(opcode_str, \"%08x\", opcode);\n            char* disassembly = ee_disassemble(buf, opcode, &g_ee_dis_state);\n\n            sprintf(id, \"##%d\", row);\n\n            Selectable(id, false, ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns);\n\n            if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left)) {\n                breakpoint b;\n\n                b.addr = g_ee_dis_state.pc;\n                b.cond_r = false;\n                b.cond_w = false;\n                b.cond_x = true;\n                b.cpu = BKPT_CPU_EE;\n                b.size = 4;\n                b.enabled = true;\n\n                auto addr = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) {\n                    return a.addr == g_ee_dis_state.pc && a.cpu == BKPT_CPU_EE;\n                });\n\n                if (addr == iris->breakpoints.end()) {\n                    iris->breakpoints.push_back(b);\n                } else {\n                    iris->breakpoints.erase(addr);\n                }\n            } SameLine();\n\n            if (BeginPopupContextItem()) {\n                PushFont(iris->font_small_code);\n                TextDisabled(\"0x%08x\", g_ee_dis_state.pc);\n                PopFont();\n\n                PushFont(iris->font_body);\n\n                if (BeginMenu(ICON_MS_CONTENT_COPY \"  Copy\")) {\n                    if (Selectable(ICON_MS_SORT \"  Address\")) {\n                        SDL_SetClipboardText(addr_str);\n                    }\n\n                    if (Selectable(ICON_MS_SORT \"  Opcode\")) {\n                        SDL_SetClipboardText(opcode_str);\n                    }\n\n                    if (Selectable(ICON_MS_SORT \"  Disassembly\")) {\n                        SDL_SetClipboardText(disassembly);\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                auto addr = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) {\n                    return a.addr == g_ee_dis_state.pc && a.cpu == BKPT_CPU_EE;\n                });\n\n                if (addr != iris->breakpoints.end()) {\n                    if (MenuItem(ICON_MS_CANCEL \"  Remove this breakpoint\")) {\n                        iris->breakpoints.erase(addr);\n                    }\n                } else {\n                    if (MenuItem(ICON_MS_ADD_CIRCLE \"  Add breakpoint here\")) {\n                        breakpoint b;\n\n                        b.addr = g_ee_dis_state.pc;\n                        b.cond_r = false;\n                        b.cond_w = false;\n                        b.cond_x = true;\n                        b.cpu = BKPT_CPU_EE;\n                        b.size = 4;\n                        b.enabled = true;\n\n                        iris->breakpoints.push_back(b);\n                    }\n                }\n\n                PopFont();\n                EndPopup();\n            }\n\n            Text(\"%s \", addr_str); SameLine();\n            TextDisabled(\"%s \", opcode_str); SameLine();\n\n            if (true) {\n                print_highlighted(iris, disassembly);\n            } else {\n                Text(\"%s\", disassembly);\n            }\n\n            if (iris->ee_control_follow_pc) {\n                if (g_ee_dis_state.pc == iris->ps2->ee->pc)\n                    SetScrollHereY(0.5f);\n            } else {\n                if (g_ee_dis_state.pc == iris->ee_control_address)\n                    SetScrollHereY(0.5f);\n            }\n        }\n\n        EndTable();\n    }\n\n    if (!iris->codeview_use_theme_background) {\n        PopStyleColor(4);\n    }\n\n    PopFont();\n\n    GetStyle().FontScaleMain = font_scale;\n}\n\nstatic void show_iop_disassembly_view(iris::instance* iris) {\n    using namespace ImGui;\n\n    float font_scale = GetStyle().FontScaleMain;\n\n    GetStyle().FontScaleMain = iris->codeview_font_scale;\n\n    PushFont(iris->font_code);\n\n    if (!iris->codeview_use_theme_background) {\n        PushStyleColor(ImGuiCol_TableRowBg, ImVec4(\n            iris->codeview_color_background.Value.x,\n            iris->codeview_color_background.Value.y,\n            iris->codeview_color_background.Value.z,\n            iris->codeview_color_background.Value.w\n        ));\n        PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4(\n            iris->codeview_color_background.Value.x,\n            iris->codeview_color_background.Value.y,\n            iris->codeview_color_background.Value.z,\n            iris->codeview_color_background.Value.w\n        ));\n        PushStyleColor(ImGuiCol_Text, ImVec4(\n            iris->codeview_color_text.Value.x,\n            iris->codeview_color_text.Value.y,\n            iris->codeview_color_text.Value.z,\n            iris->codeview_color_text.Value.w\n        ));\n        PushStyleColor(ImGuiCol_TextDisabled, ImVec4(\n            iris->codeview_color_comment.Value.x,\n            iris->codeview_color_comment.Value.y,\n            iris->codeview_color_comment.Value.z,\n            iris->codeview_color_comment.Value.w\n        ));\n    }\n\n    if (BeginTable(\"table2\", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {\n        TableSetupColumn(\"a\", ImGuiTableColumnFlags_NoResize, 15.0f);\n        TableSetupColumn(\"b\", ImGuiTableColumnFlags_NoResize, 15.0f);\n        TableSetupColumn(\"c\", ImGuiTableColumnFlags_NoResize);\n\n        for (int row = -64; row < 64; row++) {\n            if (iris->iop_control_follow_pc) {\n                g_iop_dis_state.addr = iris->ps2->iop->pc + (row * 4);\n            } else {\n                g_iop_dis_state.addr = iris->iop_control_address + (row * 4);\n            }\n\n            TableNextRow();\n            TableSetColumnIndex(0);\n\n            PushFont(iris->font_icons);\n\n            auto v = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) {\n                return a.addr == g_iop_dis_state.addr && a.cpu == BKPT_CPU_IOP;\n            });\n\n            if (v != iris->breakpoints.end()) {\n                Text(\" \" ICON_MS_FIBER_MANUAL_RECORD \" \");\n            }\n\n            TableSetColumnIndex(1);\n\n            if (g_iop_dis_state.addr == iris->ps2->iop->pc)\n                Text(ICON_MS_CHEVRON_RIGHT \" \");\n\n            PopFont();\n\n            TableSetColumnIndex(2);\n\n            uint32_t opcode = iop_bus_read32(iris->ps2->iop_bus, g_iop_dis_state.addr & 0x1fffffff);\n\n            char buf[128];\n\n            char addr_str[9]; sprintf(addr_str, \"%08x\", g_iop_dis_state.addr);\n            char opcode_str[9]; sprintf(opcode_str, \"%08x\", opcode);\n            char* disassembly = iop_disassemble(buf, opcode, &g_iop_dis_state);\n\n            Text(\"%s \", addr_str); SameLine();\n            TextDisabled(\"%s \", opcode_str); SameLine();\n\n            char id[16];\n\n            sprintf(id, \"##%d\", row);\n\n            Selectable(id, false, ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns);\n\n            if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left)) {\n                breakpoint b;\n\n                b.addr = g_iop_dis_state.addr;\n                b.cond_r = false;\n                b.cond_w = false;\n                b.cond_x = true;\n                b.cpu = BKPT_CPU_IOP;\n                b.size = 4;\n                b.enabled = true;\n\n                auto addr = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) {\n                    return a.addr == g_iop_dis_state.addr && a.cpu == BKPT_CPU_IOP;\n                });\n\n                if (addr == iris->breakpoints.end()) {\n                    iris->breakpoints.push_back(b);\n                } else {\n                    iris->breakpoints.erase(addr);\n                }\n            } SameLine();\n\n            if (BeginPopupContextItem()) {\n                PushFont(iris->font_small_code);\n                TextDisabled(\"0x%08x\", g_iop_dis_state.addr);\n                PopFont();\n\n                PushFont(iris->font_body);\n\n                if (BeginMenu(ICON_MS_CONTENT_COPY \"  Copy\")) {\n                    if (Selectable(ICON_MS_SORT \"  Address\")) {\n                        SDL_SetClipboardText(addr_str);\n                    }\n\n                    if (Selectable(ICON_MS_SORT \"  Opcode\")) {\n                        SDL_SetClipboardText(opcode_str);\n                    }\n\n                    if (Selectable(ICON_MS_SORT \"  Disassembly\")) {\n                        SDL_SetClipboardText(disassembly);\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                auto addr = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) {\n                    return a.addr == g_iop_dis_state.addr && a.cpu == BKPT_CPU_IOP;\n                });\n\n                if (addr != iris->breakpoints.end()) {\n                    if (MenuItem(ICON_MS_CANCEL \"  Remove this breakpoint\")) {\n                        iris->breakpoints.erase(addr);\n                    }\n                } else {\n                    if (MenuItem(ICON_MS_ADD_CIRCLE \"  Add breakpoint here\")) {\n                        breakpoint b;\n\n                        b.addr = g_iop_dis_state.addr;\n                        b.cond_r = false;\n                        b.cond_w = false;\n                        b.cond_x = true;\n                        b.cpu = BKPT_CPU_IOP;\n                        b.size = 4;\n                        b.enabled = true;\n\n                        iris->breakpoints.push_back(b);\n                    }\n                }\n\n                PopFont();\n                EndPopup();\n            }\n\n            if (true) {\n                print_highlighted(iris, disassembly);\n            } else {\n                Text(\"%s\", disassembly);\n            }\n\n            if (iris->iop_control_follow_pc) {\n                if (g_iop_dis_state.addr == iris->ps2->iop->pc)\n                    SetScrollHereY(0.5f);\n            } else {\n                if (g_iop_dis_state.addr == iris->iop_control_address)\n                    SetScrollHereY(0.5f);\n            }\n        } EndTable();\n    }\n\n    PopFont();\n\n    if (!iris->codeview_use_theme_background) {\n        PopStyleColor(4);\n    }\n\n    GetStyle().FontScaleMain = font_scale;\n}\n\nvoid show_ee_control(iris::instance* iris) {\n    using namespace ImGui;\n\n    PushFont(iris->font_icons);\n\n    if (imgui::BeginEx(\"EE (R5900)\", &iris->show_ee_control)) {\n        if (Button(iris->pause ? ICON_MS_PLAY_ARROW : ICON_MS_PAUSE, ImVec2(50, 0))) {\n            iris->pause = !iris->pause;\n        } SameLine();\n\n        if (Button(ICON_MS_STEP_INTO)) {\n            iris->pause = true;\n            iris->step = true;\n        } SameLine();\n\n        if (Button(ICON_MS_STEP_OVER)) {\n            iris->step_over = true;\n            iris->step_over_addr = iris->ps2->ee->pc + 4;\n            iris->pause = false;\n        } SameLine();\n\n        if (Button(ICON_MS_STEP_OUT)) {\n            iris->step_out = true;\n            iris->pause = false;\n        } SameLine();\n\n        if (Button(ICON_MS_MOVE_DOWN)) {\n            iris->ee_control_follow_pc = true;\n        } SameLine();\n\n        if (Button(ICON_MS_AUTORENEW)) {\n            ee_flush_cache(iris->ps2->ee);\n        }\n\n        if (iris->symbols.size()) {\n            TextDisabled(\"Current function:\"); SameLine();\n\n            const char* func = \"<unknown>\";\n\n            for (elf_symbol& sym : iris->symbols) {\n                if (iris->ps2->ee->pc >= sym.addr && iris->ps2->ee->pc < (sym.addr + sym.size)) {\n                    func = sym.name;\n\n                    break;\n                }\n            }\n\n            Text(\"%s\", func);\n        }\n\n        SeparatorText(\"Disassembly\");\n\n        if (BeginChild(\"ee##disassembly\")) {\n            show_ee_disassembly_view(iris);\n        } EndChild();\n    } End();\n\n    PopFont();\n}\n\nvoid show_iop_control(iris::instance* iris) {\n    using namespace ImGui;\n\n    PushFont(iris->font_icons);\n\n    if (imgui::BeginEx(\"IOP (R3000)\", &iris->show_iop_control)) {\n        if (Button(iris->pause ? ICON_MS_PLAY_ARROW : ICON_MS_PAUSE, ImVec2(50, 0))) {\n            iris->pause = !iris->pause;\n        } SameLine();\n\n        if (Button(ICON_MS_STEP)) {\n            iris->pause = true;\n\n            ps2_step_iop(iris->ps2);\n        }\n\n        if (InputInt(\"Address\", (int32_t*)&iris->iop_control_address, 0, 0, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {\n            iris->iop_control_follow_pc = false;\n        }\n\n        if (Button(ICON_MS_MOVE_DOWN)) {\n            iris->iop_control_follow_pc = true;\n        } SameLine();\n\n        SeparatorText(\"Disassembly\");\n\n        if (BeginChild(\"iop##disassembly\")) {\n            show_iop_disassembly_view(iris);\n        } EndChild();\n    } End();\n\n    PopFont();\n}\n\n}"
  },
  {
    "path": "frontend/ui/dma.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nvoid show_ee_dmac(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (imgui::BeginEx(\"EE DMAC\", &iris->show_ee_dmac)) {\n\n    } End();\n}\n\nvoid show_iop_dma(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (imgui::BeginEx(\"IOP DMA\", &iris->show_iop_dma)) {\n\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/gs.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nstatic const char* gs_internal_reg_names[] = {\n    \"prim\",\n    \"rgbaq\",\n    \"st\",\n    \"uv\",\n    \"xyzf2\",\n    \"xyz2\",\n    \"fog\",\n    \"xyzf3\",\n    \"xyz3\",\n    \"prmodecont\",\n    \"prmode\",\n    \"texclut\",\n    \"scanmsk\",\n    \"texa\",\n    \"fogcol\",\n    \"texflush\",\n    \"dimx\",\n    \"dthe\",\n    \"colclamp\",\n    \"pabe\",\n    \"bitbltbuf\",\n    \"trxpos\",\n    \"trxreg\",\n    \"trxdir\",\n    \"hwreg\",\n    \"signal\",\n    \"finish\",\n    \"label\"\n};\n\nstatic const char* gs_gp_reg_names[] = {\n    \"frame\",\n    \"zbuf\",\n    \"tex0\",\n    \"tex1\",\n    \"tex2\",\n    \"miptbp1\",\n    \"miptbp2\",\n    \"clamp\",\n    \"test\",\n    \"alpha\",\n    \"xyoffset\",\n    \"scissor\",\n    \"fba\"\n};\n\nstatic const char* gs_privileged_reg_names[] = {\n    \"pmode\",\n    \"smode1\",\n    \"smode2\",\n    \"srfsh\",\n    \"synch1\",\n    \"synch2\",\n    \"syncv\",\n    \"dispfb1\",\n    \"display1\",\n    \"dispfb2\",\n    \"display2\",\n    \"extbuf\",\n    \"extdata\",\n    \"extwrite\",\n    \"bgcolor\",\n    \"csr\",\n    \"imr\",\n    \"busdir\",\n    \"siglblid\"\n};\n\nstatic const char* gs_debug_names[] = {\n    \"Registers\",\n    \"Buffers\",\n    \"Textures\",\n    \"Queue\",\n    \"Memory\"\n};\n\nstatic const char* sizing_combo_items[] = {\n    ICON_MS_FIT_WIDTH \" Fixed fit\",\n    ICON_MS_FULLSCREEN \" Fixed same\",\n    ICON_MS_FULLSCREEN \" Stretch prop\",\n    ICON_MS_FULLSCREEN \" Stretch same\"\n};\n\nstatic ImGuiTableFlags table_sizing_flags[] = {\n    ImGuiTableFlags_SizingFixedFit,\n    ImGuiTableFlags_SizingFixedSame,\n    ImGuiTableFlags_SizingStretchProp,\n    ImGuiTableFlags_SizingStretchSame\n};\n\nstatic int gs_table_sizing = 3;\nstatic int gs_sizing_index = 0;\nstatic int gs_debug_index = 0;\n\nstatic const char* gs_context_names[] = {\n    \"Privileged\",\n    \"Context 1\",\n    \"Context 2\",\n    \"Internal\"\n};\n\nstatic int gs_context = 0;\n\nvoid show_privileged_registers(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ps2_gs* gs = iris->ps2->gs;\n\n    PushFont(iris->font_code);\n\n    uint64_t* regs = &gs->pmode;\n\n    if (BeginTable(\"table6\", 2, ImGuiTableFlags_RowBg)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Register\");\n        TableSetupColumn(\"Value\");\n        TableHeadersRow();\n        PopFont();\n\n        for (int i = 0; i < 19; i++) {\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            Text(\"%s\", gs_privileged_reg_names[i]);\n\n            TableSetColumnIndex(1);\n\n            Text(\"%08lx%08lx\", regs[i] >> 32, regs[i] & 0xffffffff);\n        }\n\n        EndTable();\n    }\n\n    PopFont();\n}\n\nvoid show_context_registers(iris::instance* iris, int ctx) {\n    using namespace ImGui;\n\n    struct ps2_gs* gs = iris->ps2->gs;\n\n    PushFont(iris->font_code);\n\n    uint64_t* regs = &gs->context[ctx].frame;\n\n    if (BeginTable(\"table9\", 2, ImGuiTableFlags_RowBg)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Register\");\n        TableSetupColumn(\"Value\");\n        TableHeadersRow();\n        PopFont();\n\n        for (int i = 0; i < 13; i++) {\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            Text(\"%s\", gs_gp_reg_names[i]);\n\n            TableSetColumnIndex(1);\n\n            Text(\"%08lx%08lx\", regs[i] >> 32, regs[i] & 0xffffffff);\n        }\n\n        EndTable();\n    }\n\n    PopFont();\n}\n\nvoid show_internal_registers(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ps2_gs* gs = iris->ps2->gs;\n\n    PushFont(iris->font_code);\n\n    uint64_t* regs = &gs->prim;\n\n    if (BeginTable(\"table6\", 2, ImGuiTableFlags_RowBg)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Register\");\n        TableSetupColumn(\"Value\");\n        TableHeadersRow();\n        PopFont();\n\n        for (int i = 0; i < 28; i++) {\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            Text(\"%s\", gs_internal_reg_names[i]);\n\n            TableSetColumnIndex(1);\n\n            Text(\"%08lx%08lx\", regs[i] >> 32, regs[i] & 0xffffffff);\n        }\n\n        EndTable();\n    }\n\n    PopFont();\n}\n\nstatic inline void show_gs_vertex_xy(iris::instance* iris, const gs_vertex* vtx) {\n    using namespace ImGui;\n\n    TableSetColumnIndex(0);\n\n    Text(\"Position (XY)\");\n\n    TableSetColumnIndex(1);\n\n    PushFont(iris->font_code);\n\n    Text(\"0x%04lx, 0x%04lx\", vtx->xyz & 0xffff, (vtx->xyz >> 16) & 0xffff);\n\n    TableSetColumnIndex(2);\n\n    Text(\"%d.%04ld, %d.%04ld\", vtx->x, (vtx->xyz & 0xf) * 625, vtx->y, ((vtx->xyz >> 16) & 0xf) * 625);\n\n    PopFont();\n\n    TableNextRow();\n}\n\nstatic inline void show_gs_vertex_z(iris::instance* iris, const gs_vertex* vtx) {\n    using namespace ImGui;\n\n    TableSetColumnIndex(0);\n\n    Text(\"Depth (Z)\");\n\n    TableSetColumnIndex(1);\n\n    PushFont(iris->font_code);\n\n    Text(\"0x%08x\", vtx->z);\n\n    TableSetColumnIndex(2);\n\n    Text(\"%d\", vtx->z);\n\n    PopFont();\n\n    TableNextRow();\n}\n\nstatic inline void show_gs_vertex_stq(iris::instance* iris, const gs_vertex* vtx) {\n    using namespace ImGui;\n\n    TableSetColumnIndex(0);\n\n    Text(\"STQ\");\n\n    TableSetColumnIndex(1);\n\n    PushFont(iris->font_code);\n\n    Text(\"0x%08lx, 0x%08lx, 0x%08lx\", vtx->st & 0xffffffff, vtx->st >> 32, vtx->rgbaq >> 32);\n\n    TableSetColumnIndex(2);\n\n    Text(\"%f, %f, %f\", vtx->s, vtx->t, vtx->q);\n\n    PopFont();\n\n    TableNextRow();\n}\n\nstatic inline void show_gs_vertex_uv(iris::instance* iris, const gs_vertex* vtx) {\n    using namespace ImGui;\n\n    TableSetColumnIndex(0);\n\n    Text(\"UV\");\n\n    TableSetColumnIndex(1);\n\n    PushFont(iris->font_code);\n\n    Text(\"0x%08lx, 0x%08lx\",\n        vtx->uv & 0x3fff,\n        (vtx->uv >> 16) & 0x3fff\n    );\n\n    TableSetColumnIndex(2);\n\n    Text(\"%d.%04ld, %d.%04ld\",\n        vtx->u, (vtx->uv & 0xf) * 625,\n        vtx->v, ((vtx->uv >> 16) & 0xf) * 625\n    );\n\n    PopFont();\n\n    TableNextRow();\n}\n\nstatic inline void show_gs_vertex_rgba(iris::instance* iris, const gs_vertex* vtx) {\n    using namespace ImGui;\n\n    TableSetColumnIndex(0);\n\n    Text(\"Color (RGBA)\");\n\n    TableSetColumnIndex(1);\n\n    PushFont(iris->font_code);\n\n    Text(\"0x%08lx\", vtx->rgbaq & 0xffffffff);\n\n    TableSetColumnIndex(2);\n\n    Text(\"0x%02lx, 0x%02lx, 0x%02lx, 0x%02lx\",\n        vtx->rgbaq & 0xff,\n        (vtx->rgbaq >> 8) & 0xff,\n        (vtx->rgbaq >> 16) & 0xff,\n        (vtx->rgbaq >> 24) & 0xff\n    ); SameLine();\n\n    ImVec4 color = ImVec4(\n        (float)(vtx->rgbaq & 0xff) / 255.0f,\n        (float)((vtx->rgbaq >> 8) & 0xff) / 255.0f,\n        (float)((vtx->rgbaq >> 16) & 0xff) / 255.0f,\n        (float)((vtx->rgbaq >> 24) & 0xff) / 255.0f\n    );\n\n    ColorEdit4(\"MyColor##3\", (float*)&color,\n        ImGuiColorEditFlags_NoInputs |\n        ImGuiColorEditFlags_NoLabel |\n        ImGuiColorEditFlags_NoPicker |\n        ImGuiColorEditFlags_AlphaPreviewHalf\n    );\n\n    PopFont();\n\n    TableNextRow();\n}\n\nvoid show_gs_vertex(iris::instance* iris, const gs_vertex* vtx) {\n    using namespace ImGui;\n\n    if (BeginTable(\"table10\", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Attribute\");\n        TableSetupColumn(\"Raw\");\n        TableSetupColumn(\"Value\");\n        TableHeadersRow();\n        PopFont();\n\n        TableNextRow();\n\n        show_gs_vertex_xy(iris, vtx);\n        show_gs_vertex_z(iris, vtx);\n        show_gs_vertex_stq(iris, vtx);\n        show_gs_vertex_uv(iris, vtx);\n        show_gs_vertex_rgba(iris, vtx);\n\n        EndTable();\n    }\n}\n\nvoid show_gs_queue(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ps2_gs* gs = iris->ps2->gs;\n\n    for (unsigned int i = 0; i < gs->vqi; i++) {\n        char buf[32]; sprintf(buf, \"Vertex %d\", i+1);\n\n        if (TreeNode(buf)) {\n            show_gs_vertex(iris, &gs->vq[i]);\n\n            TreePop();\n        }\n    }\n\n    if (TreeNode(\"Uninitialized\")) {\n        for (int i = gs->vqi; i < 4; i++) {\n            char buf[32]; sprintf(buf, \"Vertex %d\", i+1);\n\n            if (TreeNode(buf)) {\n                show_gs_vertex(iris, &gs->vq[i]);\n\n                TreePop();\n            }\n        }\n\n        TreePop();\n    }\n}\n\nvoid show_gs_registers(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (BeginTable(\"table6\", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV)) {\n        TableNextRow();\n\n        for (int i = 0; i < 4; i++) {\n            TableSetColumnIndex(i);\n\n            if (Selectable(gs_context_names[i], gs_context == i)) {\n                gs_context = i;\n            }\n        }\n\n        EndTable();\n    }\n\n    if (BeginChild(\"##gsr\")) {\n        switch (gs_context) {\n            case 0: {\n                show_privileged_registers(iris);\n            } break;\n\n            case 1: {\n                show_context_registers(iris, 0);\n            } break;\n\n            case 2: {\n                show_context_registers(iris, 1);\n            } break;\n\n            case 3: {\n                show_internal_registers(iris);\n            } break;\n        }\n    } EndChild();\n}\n\nstatic uint32_t addr = 0, width = 0, height = 0;\nstatic bool unswizzle = false;\nstatic float scale = 1.0f;\n\nconst char* format_names[] = {\n    \"32-bit/24-bit\",\n    \"16-bit\"\n};\n\nint format = 0;\nstatic SDL_GPUTexture* tex = nullptr;\n\nvoid show_gs_memory(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ps2_gs* gs = iris->ps2->gs;\n\n    static char buf0[16];\n    static char buf1[16];\n    static char buf2[16];\n    static char buf3[16];\n\n    if (InputText(\"Address##\", buf0, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {\n        if (buf0[0]) {\n            addr = strtoul(buf0, NULL, 16);\n        }\n    } \n\n    if (InputText(\"Width##\", buf1, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {\n        if (buf1[0]) {\n            width = strtoul(buf1, NULL, 0);\n        }\n    }\n\n    if (InputText(\"Height##\", buf2, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {\n        if (buf2[0]) {\n            height = strtoul(buf2, NULL, 0);\n        }\n    }\n\n    sprintf(buf3, \"%.1f\", scale);\n\n    if (BeginCombo(\"Scale\", buf3, ImGuiComboFlags_HeightSmall)) {\n        char buf4[16];\n\n        for (int i = 0; i < 6; i++) {\n            sprintf(buf4, \"%.1f\", 1.0f + (0.5f * i));\n\n            if (Selectable(buf4, scale == 1.0f + (0.5f * i))) {\n                scale = 1.0f + (0.5f * i);\n            }\n        }\n        \n        EndCombo();\n    }\n\n    if (BeginCombo(\"Format\", format_names[format], ImGuiComboFlags_HeightSmall)) {\n        for (int i = 0; i < 2; i++) {\n            if (Selectable(format_names[i], i == format)) {\n                format = i;\n            }\n        }\n\n        EndCombo();\n    }\n\n    SDL_GPUTextureFormat fmt;\n    int stride = 0;\n\n    switch (format) {\n        case 0: {\n            fmt = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;\n            stride = 4;\n        } break;\n\n        case 1: {\n            fmt = SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM;\n            stride = 2;\n        } break;\n    }\n\n    // To-do: GS memory viewer\n\n    // if (Button(\"View\")) {\n    //     addr = strtoul(buf0, NULL, 16);\n    //     width = strtoul(buf1, NULL, 0);\n    //     height = strtoul(buf2, NULL, 0);\n\n    //     if (tex) {\n    //         SDL_ReleaseGPUTexture(iris->device, tex);\n    //     }\n\n    //     SDL_GPUTextureCreateInfo tci = {};\n\n    //     tci.format = fmt;\n    //     tci.type = SDL_GPU_TEXTURETYPE_2D;\n    //     tci.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER;\n    //     tci.width = width;\n    //     tci.height = height;\n    //     tci.layer_count_or_depth = 1;\n    //     tci.num_levels = 1;\n    //     tci.sample_count = SDL_GPU_SAMPLECOUNT_1;\n\n    //     tex = SDL_CreateGPUTexture(iris->device, &tci);\n\n    //     SDL_GPUSamplerCreateInfo sci = {\n    //         .min_filter = SDL_GPU_FILTER_NEAREST,\n    //         .mag_filter = SDL_GPU_FILTER_NEAREST,\n    //         .mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST,\n    //         .address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,\n    //         .address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,\n    //         .address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,\n    //     };\n    // }\n\n    // if (!tex)\n    //     return;\n\n    // SDL_GPUCommandBuffer* cb = SDL_AcquireGPUCommandBuffer(iris->device);\n    // SDL_GPUCopyPass* cp = SDL_BeginGPUCopyPass(cb);\n\n    // SDL_GPUTransferBufferCreateInfo ttbci = {\n    //     .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,\n    //     .size = (width * stride) * height\n    // };\n\n    // SDL_GPUTransferBuffer* ttb = SDL_CreateGPUTransferBuffer(iris->device, &ttbci);\n\n    // // fill the transfer buffer\n    // switch (format) {\n    //     case 0: {\n    //         uint32_t* ptr = (uint32_t*)(((uint8_t*)gs->vram) + addr);\n    //         uint32_t* data = (uint32_t*)SDL_MapGPUTransferBuffer(iris->device, ttb, false);\n\n    //         for (int y = 0; y < height; y++) {\n    //             for (int x = 0; x < width; x++) {\n    //                 data[x + (y * width)] = ptr[x + (y * width)] | 0xff000000;\n    //             }\n    //         }\n    //     } break;\n\n    //     case 1: {\n    //         uint16_t* ptr = (uint16_t*)(((uint8_t*)gs->vram) + addr);\n    //         uint16_t* data = (uint16_t*)SDL_MapGPUTransferBuffer(iris->device, ttb, false);\n\n    //         for (int y = 0; y < height; y++) {\n    //             for (int x = 0; x < width; x++) {\n    //                 data[x + (y * width)] = ptr[x + (y * width)] | 0x8000;\n    //             }\n    //         }\n    //     } break;\n    // }\n\n    // // SDL_memcpy(data, ptr, (width * stride) * height);\n\n    // SDL_UnmapGPUTransferBuffer(iris->device, ttb);\n\n    // SDL_GPUTextureTransferInfo tti = {\n    //     .transfer_buffer = ttb,\n    //     .offset = 0,\n    // };\n\n    // SDL_GPUTextureRegion tr = {\n    //     .texture = tex,\n    //     .w = width,\n    //     .h = height,\n    //     .d = 1,\n    // };\n\n    // SDL_UploadToGPUTexture(cp, &tti, &tr, false);\n\n    // // end the copy pass\n    // SDL_EndGPUCopyPass(cp);\n    // SDL_SubmitGPUCommandBuffer(cb);\n\n    // ImageWithBg((ImTextureID)(intptr_t)tex, ImVec2(width*scale, height*scale), ImVec2(0, 0), ImVec2(1, 1), ImVec4(0, 0, 0, 1), ImVec4(1, 1, 1, 1));\n}\n\nvoid show_gs_debugger(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (imgui::BeginEx(\"GS\", &iris->show_gs_debugger, ImGuiWindowFlags_MenuBar)) {\n        if (BeginMenuBar()) {\n            if (BeginMenu(\"Settings\")) {\n                if (BeginMenu(ICON_MS_CROP \" Sizing\")) {\n                    for (int i = 0; i < 4; i++) {\n                        if (Selectable(sizing_combo_items[i], i == gs_sizing_index)) {\n                            gs_table_sizing = table_sizing_flags[i];\n                            gs_sizing_index = i;\n                        }\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                ImGui::EndMenu();\n            }\n\n            EndMenuBar();\n        }\n\n        if (BeginTable(\"table5\", 5, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV)) {\n            TableNextRow();\n\n            for (int i = 0; i < 5; i++) {\n                TableSetColumnIndex(i);\n\n                if (Selectable(gs_debug_names[i], gs_debug_index == i)) {\n                    gs_debug_index = i;\n                }\n            }\n\n            EndTable();\n        }\n\n        if (BeginChild(\"##gschild\")) {\n            switch (gs_debug_index) {\n                case 0: {\n                    show_gs_registers(iris);\n                } break;\n\n                case 3: {\n                    show_gs_queue(iris);\n                } break;\n\n                case 4: {\n                    show_gs_memory(iris);\n                } break;\n            }\n        } EndChild();\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/intc.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nconst char* ee_irq_sources[] = {\n    \"GS\",\n    \"SBUS\",\n    \"Vblank In\",\n    \"Vblank Out\",\n    \"VIF0\",\n    \"VIF1\",\n    \"VU0\",\n    \"VU1\",\n    \"IPU\",\n    \"Timer 0\",\n    \"Timer 1\",\n    \"Timer 2\",\n    \"Timer 3\",\n    \"SFIFO\",\n    \"VU0 Watchdog\"\n};\n\nconst char* iop_irq_sources[] = {\n    \"Vblank In\",\n    \"GPU\",\n    \"CDVD\",\n    \"DMA\",\n    \"Timer 0\",\n    \"Timer 1\",\n    \"Timer 2\",\n    \"SIO0\",\n    \"SIO1\",\n    \"SPU2\",\n    \"PIO\",\n    \"Vblank Out\",\n    \"DVD\",\n    \"PCMCIA\",\n    \"Timer 3\",\n    \"Timer 4\",\n    \"Timer 5\",\n    \"SIO2\",\n    \"HTR0\",\n    \"HTR1\",\n    \"HTR2\",\n    \"HTR3\",\n    \"USB\",\n    \"EXTR\",\n    \"FWRE\",\n    \"FDMA\"\n};\n\nvoid show_ee_intc_interrupts(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ps2_intc* intc = iris->ps2->ee_intc;\n\n    if (BeginTable(\"##eeintc\", 3, ImGuiTableFlags_RowBg)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Source\");\n        TableSetupColumn(\"Status\");\n        TableSetupColumn(\"Mask\");\n        TableHeadersRow();\n        PopFont();\n\n        for (int i = 0; i < 15; i++) {\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            Text(\"%s\", ee_irq_sources[i]);\n\n            TableSetColumnIndex(1);\n\n            int status = intc->stat & (1 << i);\n            int mask = intc->mask & (1 << i);\n\n            char label[16];\n\n            sprintf(label, \"%s##s%x\", status ? ICON_MS_CHECK : \"\", i);\n\n            if (Selectable(label)) {\n                intc->stat ^= 1 << i;\n            }\n\n            TableSetColumnIndex(2);\n\n            sprintf(label, \"%s##m%x\", mask ? ICON_MS_CHECK : \"\", i);\n\n            if (Selectable(label)) {\n                intc->mask ^= 1 << i;\n            }\n        }\n\n        EndTable();\n    }\n}\n\nvoid show_ee_interrupts(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ps2_intc* intc = iris->ps2->ee_intc;\n\n    if (imgui::BeginEx(\"EE Interrupts\", &iris->show_ee_interrupts)) {\n        if (Button(ICON_MS_REMOVE_SELECTION)) {\n            intc->mask = 0;\n        } SameLine();\n\n        if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {\n            SetTooltip(\"Disable all\");\n        }\n\n        if (Button(ICON_MS_SELECT)) {\n            intc->mask |= 0xffff;\n        }\n\n        if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {\n            SetTooltip(\"Enable all\");\n        }\n\n        if (BeginChild(\"##eeintcchild\")) {\n            show_ee_intc_interrupts(iris);\n        } EndChild();\n    } End();\n}\n\nvoid show_iop_intc_interrupts(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ps2_iop_intc* intc = iris->ps2->iop_intc;\n\n    if (BeginTable(\"##iopintc\", 3, ImGuiTableFlags_RowBg)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Source\");\n        TableSetupColumn(\"Status\");\n        TableSetupColumn(\"Mask\");\n        TableHeadersRow();\n        PopFont();\n\n        for (int i = 0; i < 26; i++) {\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            Text(\"%s\", iop_irq_sources[i]);\n\n            TableSetColumnIndex(1);\n\n            int status = intc->stat & (1 << i);\n            int mask = intc->mask & (1 << i);\n\n            char label[16];\n\n            sprintf(label, \"%s##s%x\", status ? ICON_MS_CHECK : \"\", i);\n\n            if (Selectable(label)) {\n                intc->stat ^= 1 << i;\n            }\n\n            TableSetColumnIndex(2);\n\n            sprintf(label, \"%s##m%x\", mask ? ICON_MS_CHECK : \"\", i);\n\n            if (Selectable(label)) {\n                intc->mask ^= 1 << i;\n            }\n        }\n\n        EndTable();\n    }\n}\n\nvoid show_iop_interrupts(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ps2_iop_intc* intc = iris->ps2->iop_intc;\n\n    if (imgui::BeginEx(\"IOP Interrupts\", &iris->show_iop_interrupts)) {\n        if (Button(ICON_MS_REMOVE_SELECTION)) {\n            intc->mask = 0;\n        } SameLine();\n\n        if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {\n            SetTooltip(\"Disable all\");\n        }\n\n        if (Button(ICON_MS_SELECT)) {\n            intc->mask |= 0xffff;\n        }\n\n        if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) {\n            SetTooltip(\"Enable all\");\n        }\n\n        if (BeginChild(\"##iopintcchild\")) {\n            show_iop_intc_interrupts(iris);\n        } EndChild();\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/logs.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nbool ee_follow = true;\nbool iop_follow = true;\nbool sysmem_follow = true;\n\nvoid show_logs(iris::instance* iris, const std::vector <std::string>& logs, bool follow) {\n    using namespace ImGui;\n\n    PushFont(iris->font_code);\n\n    if (BeginTable(\"##logstable\", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) {\n        for (unsigned int i = 0; i < logs.size(); i++) {\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            Text(\"  %-3d \", i+1);\n\n            TableSetColumnIndex(1);\n\n            char buf[16]; sprintf(buf, \"##l%d\", i);\n\n            if (Selectable(buf, false, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns)) {\n                // Do something with text\n            } SameLine(0.0, 0.0);\n\n            Text(\"%s\", logs[i].c_str());\n        }\n\n        if (follow) {\n            SetScrollHereY(1.0f);\n        }\n\n        EndTable();\n    }\n\n    PopFont();\n}\n\nvoid show_ee_logs(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (imgui::BeginEx(\"EE logs\", &iris->show_ee_logs)) {\n        if (Button(ICON_MS_DELETE)) {\n            iris->ee_log.clear();\n        } SameLine();\n\n        if (Button(ICON_MS_CONTENT_COPY)) {\n            std::string buf;\n\n            for (const std::string& s : iris->ee_log) {\n                buf.append(s);\n                buf.push_back('\\n');\n            }\n\n            SDL_SetClipboardText(buf.c_str());\n        }\n\n        if (BeginChild(\"##eelog\")) {\n            show_logs(iris, iris->ee_log, ee_follow);\n        } EndChild();\n    } End();\n}\n\nvoid show_iop_logs(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (imgui::BeginEx(\"IOP logs\", &iris->show_iop_logs, ImGuiWindowFlags_MenuBar)) {\n        if (BeginMenuBar()) {\n            if (BeginMenu(\"Settings\")) {\n                if (MenuItem(iop_follow ? ICON_MS_CHECK_BOX \" Follow\" : ICON_MS_CHECK_BOX_OUTLINE_BLANK \" Follow\", nullptr)) {\n                    iop_follow = !iop_follow;\n                }\n\n                ImGui::EndMenu();\n            }\n\n            EndMenuBar();\n        }\n\n        if (Button(ICON_MS_DELETE)) {\n            iris->iop_log.clear();\n        } SameLine();\n\n        if (Button(ICON_MS_CONTENT_COPY)) {\n            std::string buf;\n\n            for (const std::string& s : iris->iop_log) {\n                buf.append(s);\n                buf.push_back('\\n');\n            }\n\n            SDL_SetClipboardText(buf.c_str());\n        }\n\n        if (BeginChild(\"##ioplog\")) {\n            show_logs(iris, iris->iop_log, iop_follow);\n        } EndChild();\n    } End();\n}\n\nvoid show_sysmem_logs(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (imgui::BeginEx(\"SYSMEM logs\", &iris->show_sysmem_logs, ImGuiWindowFlags_MenuBar)) {\n        if (BeginMenuBar()) {\n            if (BeginMenu(\"Settings\")) {\n                if (MenuItem(sysmem_follow ? ICON_MS_CHECK_BOX \" Follow\" : ICON_MS_CHECK_BOX_OUTLINE_BLANK \" Follow\", nullptr)) {\n                    sysmem_follow = !sysmem_follow;\n                }\n\n                ImGui::EndMenu();\n            }\n\n            EndMenuBar();\n        }\n\n        if (Button(ICON_MS_DELETE)) {\n            iris->sysmem_log.clear();\n        } SameLine();\n\n        if (Button(ICON_MS_CONTENT_COPY)) {\n            std::string buf;\n\n            for (const std::string& s : iris->sysmem_log) {\n                buf.append(s);\n                buf.push_back('\\n');\n            }\n\n            SDL_SetClipboardText(buf.c_str());\n        }\n\n        if (BeginChild(\"##sysmemlog\")) {\n            show_logs(iris, iris->sysmem_log, sysmem_follow);\n        } EndChild();\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/memory.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n#include \"ee/ee_def.hpp\"\n#include \"ee/vu_def.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n#include \"memory_viewer.h\"\n\nnamespace iris {\n\nstatic MemoryEditor editor;\n\nvoid show_memory_viewer(iris::instance* iris) {\n    using namespace ImGui;\n\n    editor.FontOptions = iris->font_body;\n\n    struct ps2_state* ps2 = iris->ps2;\n\n    if (imgui::BeginEx(\"Memory\", &iris->show_memory_viewer)) {\n        if (BeginTabBar(\"##tabbar\")) {\n            if (BeginTabItem(\"EE\")) {\n                PushFont(iris->font_code);\n\n                editor.DrawContents(ps2->ee_ram->buf, ps2->ee_ram->size, 0);\n\n                PopFont();\n\n                EndTabItem();\n            }\n\n            if (BeginTabItem(\"EE SPR\")) {\n                PushFont(iris->font_code);\n\n                editor.DrawContents(ps2->ee->spr->buf, 0x4000, 0);\n\n                PopFont();\n\n                EndTabItem();\n            }\n\n            if (BeginTabItem(\"IOP\")) {\n                PushFont(iris->font_code);\n\n                editor.DrawContents(ps2->iop_ram->buf, ps2->iop_ram->size, 0);\n\n                PopFont();\n\n                EndTabItem();\n            }\n\n            if (BeginTabItem(\"IOP SPR\")) {\n                PushFont(iris->font_code);\n\n                editor.DrawContents(ps2->iop_spr->buf, RAM_SIZE_1KB, 0);\n\n                PopFont();\n\n                EndTabItem();\n            }\n\n            if (BeginTabItem(\"VRAM\")) {\n                PushFont(iris->font_code);\n\n                editor.DrawContents(ps2->gs->vram, 0x400000, 0);\n\n                PopFont();\n\n                EndTabItem();\n            }\n\n            if (BeginTabItem(\"SPU2\")) {\n                PushFont(iris->font_code);\n\n                editor.DrawContents(ps2->spu2->ram, RAM_SIZE_2MB, 0);\n\n                PopFont();\n\n                EndTabItem();\n            }\n\n            if (BeginTabItem(\"VU0 IMEM\")) {\n                PushFont(iris->font_code);\n\n                editor.DrawContents(ps2->vu0->micro_mem, 0x1000, 0);\n\n                PopFont();\n\n                EndTabItem();\n            }\n\n            if (BeginTabItem(\"VU0 DMEM\")) {\n                PushFont(iris->font_code);\n\n                editor.DrawContents(ps2->vu0->vu_mem, 0x1000, 0);\n\n                PopFont();\n\n                EndTabItem();\n            }\n\n            if (BeginTabItem(\"VU1 IMEM\")) {\n                PushFont(iris->font_code);\n\n                editor.DrawContents(ps2->vu1->micro_mem, 0x4000, 0);\n\n                PopFont();\n\n                EndTabItem();\n            }\n\n            if (BeginTabItem(\"VU1 DMEM\")) {\n                PushFont(iris->font_code);\n\n                editor.DrawContents(ps2->vu1->vu_mem, 0x4000, 0);\n\n                PopFont();\n\n                EndTabItem();\n            }\n\n            if (ps2->s14x_sram) {\n                if (BeginTabItem(\"S14X SRAM\")) {\n                    PushFont(iris->font_code);\n\n                    editor.DrawContents(ps2->s14x_sram->buf, 0x8000, 0);\n\n                    PopFont();\n\n                    EndTabItem();\n                }\n            }\n\n            if (ps2->s14x_link) {\n                if (BeginTabItem(\"CircLink RAM\")) {\n                    PushFont(iris->font_code);\n\n                    editor.DrawContents(ps2->s14x_link->ram, 1024, 0);\n\n                    PopFont();\n\n                    EndTabItem();\n                }\n            }\n\n            EndTabBar();\n        }\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/memory_card_tool.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n#include <cstdlib>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n#include \"portable-file-dialogs.h\"\n\nnamespace iris {\n\nenum : int {\n    MEMCARD_TYPE_PS1,\n    MEMCARD_TYPE_PS2,\n    MEMCARD_TYPE_POCKETSTATION\n};\n\nstatic const char* const type_names[] = {\n    \"PS1 Memory Card\",\n    \"PS2 Memory Card\",\n    \"PocketStation\"\n};\n\nint size = 0;\nint type = MEMCARD_TYPE_PS2;\nint slot = 0;\nconst char* fpath = nullptr;\n\nvoid show_memory_card_tool(iris::instance* iris) {\n    using namespace ImGui;\n\n    SetNextWindowSizeConstraints(ImVec2(350, 320), ImVec2(FLT_MAX, FLT_MAX));\n\n    if (imgui::BeginEx(\"Create memory card\", &iris->show_memory_card_tool, ImGuiWindowFlags_NoCollapse)) {\n        Text(\"Type\");\n\n        if (BeginCombo(\"##type\", type_names[type])) {\n            for (int i = 0; i < 3; i++) {\n                if (Selectable(type_names[i], i == type)) {\n                    type = i;\n                }\n            }\n\n            EndCombo();\n        }\n\n        Text(\"Size\");\n\n        if (type == MEMCARD_TYPE_PS2) {\n            char buf[16]; sprintf(buf, \"%d MB\", 8 << size);\n\n            if (BeginCombo(\"##size\", buf)) {\n                for (int i = 0; i < 5; i++) {\n                    sprintf(buf, \"%d MB\", 8 << i);\n\n                    if (Selectable(buf, i == size)) {\n                        size = i;\n                    }\n                }\n\n                EndCombo();\n            }\n        } else {\n            BeginDisabled(true);\n            BeginCombo(\"##size\", \"128 KiB\");\n            EndDisabled();\n        }\n\n        Text(\"Attach to\");\n\n        if (BeginCombo(\"##slot\", slot == -1 ? \"None\" : slot == 0 ? \"Slot 1\" : \"Slot 2\")) {\n            if (Selectable(\"None\", slot == -1)) {\n                slot = -1;\n            }\n\n            if (Selectable(\"Slot 1\", slot == 0)) {\n                slot = 0;\n            }\n\n            if (Selectable(\"Slot 2\", slot == 1)) {\n                slot = 1;\n            }\n\n            EndCombo();\n        }\n\n        if (Button(\"Create\")) {\n            // Create memory card file\n            int size_in_bytes;\n\n            if (type == MEMCARD_TYPE_PS2) {\n                // Calculate data + ECC area (nsects*512 + nsects*16)\n                size_in_bytes = 0x840000 << size;\n            } else {\n                size_in_bytes = 128 * 1024;\n            }\n\n            audio::mute(iris);\n\n            std::string default_path = iris->pref_path + \"image.mcd\";\n\n            if (type == MEMCARD_TYPE_POCKETSTATION) {\n                default_path = iris->pref_path + \"image.psm\";\n            }\n\n            auto f = pfd::save_file(\"Save Memory Card image\", default_path, {\n                \"Iris Memory Card Image (*.mcd)\", \"*.mcd\",\n                \"PCSX2 Memory Card Image (*.ps2)\", \"*.ps2\",\n                \"PocketStation Image (*.psm; *.pocket)\", \"*.psm *.pocket\",\n                \"All Files (*.*)\", \"*\"\n            });\n\n            while (!f.ready());\n\n            audio::unmute(iris);\n\n            if (f.result().size()) {\n                FILE* file = fopen(f.result().c_str(), \"wb\");\n\n                const int shift = 4;\n\n                void* buf = malloc(size_in_bytes >> shift);\n\n                memset(buf, 0xff, size_in_bytes >> shift);\n\n                fseek(file, 0, SEEK_SET);\n\n                for (int i = 0; i < 1 << shift; i++) {\n                    fwrite(buf, size_in_bytes >> shift, 1, file);\n                }\n\n                fclose(file);\n\n                char msg[1024];\n\n                sprintf(msg, \"Created memory card image: \\\"%s\\\"\", f.result().c_str());\n\n                push_info(iris, std::string(msg));\n\n                free(buf);\n\n                if (slot != -1) {\n                    if (iris->mcd_slot_type[slot]) {\n                        fpath = f.result().c_str();\n\n                        OpenPopup(\"Confirm detach\");\n                    } else {\n                        // Attach memory card to slot\n                        if (emu::attach_memory_card(iris, slot, f.result().c_str())) {\n                            push_info(iris, \"Memory card attached successfully.\");\n\n                            if (slot == 0) {\n                                iris->mcd0_path = f.result();\n                            } else {\n                                iris->mcd1_path = f.result();\n                            }\n                        } else {\n                            push_info(iris, \"Failed to attach memory card.\");\n                        }\n                    }\n                }\n            }\n        }\n\n        if (fpath && imgui::BeginEx(\"Confirm detach\", NULL, ImGuiWindowFlags_AlwaysAutoResize)) {\n            Text(\"A memory card is already attached to this slot. Do you want to detach it?\");\n\n            if (Button(\"Yes\")) {\n                if (emu::attach_memory_card(iris, slot, fpath)) {\n                    if (slot == 0) {\n                        iris->mcd0_path = std::string(fpath);\n                    } else {\n                        iris->mcd1_path = std::string(fpath);\n                    }\n\n                    push_info(iris, \"Memory card attached successfully.\");\n                } else {\n                    push_info(iris, \"Failed to attach memory card.\");\n                }\n\n                fpath = nullptr;\n            }\n\n            SameLine();\n\n            if (Button(\"No\")) {\n                fpath = nullptr;\n            }\n\n            End();\n        }\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/memory_search.cpp",
    "content": "#include <sstream>\n#include <fstream>\n#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n#include \"ee/ee_def.hpp\"\n#include \"ee/vu_def.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n#include \"portable-file-dialogs.h\"\n\nnamespace iris {\n\nenum {\n    SEARCH_CMP_EQUAL,\n    SEARCH_CMP_NOT_EQUAL,\n    SEARCH_CMP_LESS_THAN,\n    SEARCH_CMP_GREATER_THAN,\n    SEARCH_CMP_LESS_EQUAL,\n    SEARCH_CMP_GREATER_EQUAL\n};\n\nconst char* search_cmp_names[] = {\n    \"Equal\",\n    \"Not Equal\",\n    \"Less Than\",\n    \"Greater Than\",\n    \"Less Than or Equal\",\n    \"Greater Than or Equal\"\n};\n\nenum {\n    SEARCH_CPU_EE,\n    SEARCH_CPU_IOP\n};\n\nconst char* search_cpu_names[] = {\n    \"EE\",\n    \"IOP\"\n};\n\nenum {\n    SEARCH_TYPE_U8,\n    SEARCH_TYPE_U16,\n    SEARCH_TYPE_U32,\n    SEARCH_TYPE_U64,\n    SEARCH_TYPE_S8,\n    SEARCH_TYPE_S16,\n    SEARCH_TYPE_S32,\n    SEARCH_TYPE_S64,\n    SEARCH_TYPE_F32,\n    SEARCH_TYPE_F64\n};\n\nconst char* search_type_names[] = {\n    \"Uint8\",\n    \"Uint16\",\n    \"Uint32\",\n    \"Uint64\",\n    \"Sint8\",\n    \"Sint16\",\n    \"Sint32\",\n    \"Sint64\",\n    \"Float32\",\n    \"Float64\"\n};\n\nunion value {\n    uint8_t u8[8];\n    uint16_t u16[4];\n    uint32_t u32[2];\n    uint64_t u64;\n    int8_t s8[8];\n    int16_t s16[4];\n    int32_t s32[2];\n    int64_t s64;\n    float f32[2];\n    double f64;\n};\n\nstruct match {\n    uint32_t address;\n    value prev_value, curr_value;\n    std::string description;\n    int cpu;\n    int type;\n};\n\nstd::vector <match> search_matches;\nstd::vector <match> address_list;\n\nint search_type = SEARCH_TYPE_U32;\nint search_cmp = SEARCH_CMP_EQUAL;\nint search_cpu = SEARCH_CPU_EE;\nbool display_hex = false;\nbool search_aligned = true;\n\ntemplate <typename T> bool compare_values(int cmp, T a, T b) {\n    switch (cmp) {\n        case SEARCH_CMP_EQUAL:\n            return a == b;\n        case SEARCH_CMP_NOT_EQUAL:\n            return a != b;\n        case SEARCH_CMP_LESS_THAN:\n            return a < b;\n        case SEARCH_CMP_GREATER_THAN:\n            return a > b;\n        case SEARCH_CMP_LESS_EQUAL:\n            return a <= b;\n        case SEARCH_CMP_GREATER_EQUAL:\n            return a >= b;\n    }\n\n    return false;\n}\n\nvoid search_memory(struct ps2_state* ps2, int cpu, int type, int cmp, const char* value_str, bool aligned) {\n    search_matches.clear();\n\n    struct ps2_ram* mem = cpu == SEARCH_CPU_EE ? ps2->ee_ram : ps2->iop_ram;\n\n    int size = 0;\n\n    switch (type) {\n        case SEARCH_TYPE_U8:\n        case SEARCH_TYPE_S8:\n            size = 1;\n            break;\n        case SEARCH_TYPE_U16:\n        case SEARCH_TYPE_S16:\n            size = 2;\n            break;\n        case SEARCH_TYPE_U32:\n        case SEARCH_TYPE_S32:\n        case SEARCH_TYPE_F32:\n            size = 4;\n            break;\n        case SEARCH_TYPE_U64:\n        case SEARCH_TYPE_S64:\n        case SEARCH_TYPE_F64:\n            size = 8;\n            break;\n    }\n\n    for (int addr = 0; addr < mem->size - size; addr += (aligned ? size : 1)) {\n        match m;\n    \n        m.address = addr;\n        m.curr_value.u64 = *(uint64_t*)&mem->buf[addr];\n        m.prev_value = m.curr_value;\n        m.cpu = cpu;\n        m.type = type;\n\n        switch (type) {\n            case SEARCH_TYPE_U8: {\n                uint8_t val = (uint8_t)std::strtoul(value_str, nullptr, 0);\n                if (compare_values<uint8_t>(cmp, m.curr_value.u8[0], val)) {\n                    search_matches.push_back(m);\n                }\n            } break;\n            case SEARCH_TYPE_U16: {\n                uint16_t val = (uint16_t)std::strtoul(value_str, nullptr, 0);\n                if (compare_values<uint16_t>(cmp, m.curr_value.u16[0], val)) {\n                    search_matches.push_back(m);\n                }\n            } break;\n            case SEARCH_TYPE_U32: {\n                uint32_t val = (uint32_t)std::strtoul(value_str, nullptr, 0);\n                if (compare_values<uint32_t>(cmp, m.curr_value.u32[0], val)) {\n                    search_matches.push_back(m);\n                }\n            } break;\n            case SEARCH_TYPE_U64: {\n                uint64_t val = (uint64_t)std::strtoull(value_str, nullptr, 0);\n                if (compare_values<uint64_t>(cmp, m.curr_value.u64, val)) {\n                    search_matches.push_back(m);\n                }\n            } break;\n            case SEARCH_TYPE_S8: {\n                int8_t val = (int8_t)std::strtol(value_str, nullptr, 0);\n                if (compare_values<int8_t>(cmp, m.curr_value.s8[0], val)) {\n                    search_matches.push_back(m);\n                }\n            } break;\n            case SEARCH_TYPE_S16: {\n                int16_t val = (int16_t)std::strtol(value_str, nullptr, 0);\n                if (compare_values<int16_t>(cmp, m.curr_value.s16[0], val)) {\n                    search_matches.push_back(m);\n                }\n            } break;\n            case SEARCH_TYPE_S32: {\n                int32_t val = (int32_t)std::strtol(value_str, nullptr, 0);\n                if (compare_values<int32_t>(cmp, m.curr_value.s32[0], val)) {\n                    search_matches.push_back(m);\n                }\n            } break;\n            case SEARCH_TYPE_S64: {\n                int64_t val = (int64_t)std::strtoll(value_str, nullptr, 0);\n                if (compare_values<int64_t>(cmp, m.curr_value.s64, val)) {\n                    search_matches.push_back(m);\n                }\n            } break;\n            case SEARCH_TYPE_F32: {\n                float val = std::strtof(value_str, nullptr);\n                if (compare_values<float>(cmp, m.curr_value.f32[0], val)) {\n                    search_matches.push_back(m);\n                }\n            } break;\n            case SEARCH_TYPE_F64: {\n                double val = std::strtod(value_str, nullptr);\n                if (compare_values<double>(cmp, m.curr_value.f64, val)) {\n                    search_matches.push_back(m);\n                }\n            } break;\n        }\n    }\n}\n\nvoid filter_results(int type, int cmp, const char* value_str) {\n    // TODO\n    switch (type) {\n        case SEARCH_TYPE_U8: {\n            uint8_t val = (uint8_t)std::strtoul(value_str, nullptr, 0);\n\n            std::erase_if(search_matches, [cmp, val](const match& m) {\n                return !compare_values<uint8_t>(cmp, m.curr_value.u8[0], val);\n            });\n        } break;\n\n        case SEARCH_TYPE_U16: {\n            uint16_t val = (uint16_t)std::strtoul(value_str, nullptr, 0);\n\n            std::erase_if(search_matches, [cmp, val](const match& m) {\n                return !compare_values<uint16_t>(cmp, m.curr_value.u16[0], val);\n            });\n        } break;\n\n        case SEARCH_TYPE_U32: {\n            uint32_t val = (uint32_t)std::strtoul(value_str, nullptr, 0);\n\n            std::erase_if(search_matches, [cmp, val](const match& m) {\n                return !compare_values<uint32_t>(cmp, m.curr_value.u32[0], val);\n            });\n        } break;\n\n        case SEARCH_TYPE_U64: {\n            uint64_t val = (uint64_t)std::strtoull(value_str, nullptr, 0);\n\n            std::erase_if(search_matches, [cmp, val](const match& m) {\n                return !compare_values<uint64_t>(cmp, m.curr_value.u64, val);\n            });\n        } break;\n\n        case SEARCH_TYPE_S8: {\n            int8_t val = (int8_t)std::strtol(value_str, nullptr, 0);\n\n            std::erase_if(search_matches, [cmp, val](const match& m) {\n                return !compare_values<int8_t>(cmp, m.curr_value.s8[0], val);\n            });\n        } break;\n\n        case SEARCH_TYPE_S16: {\n            int16_t val = (int16_t)std::strtol(value_str, nullptr, 0);\n\n            std::erase_if(search_matches, [cmp, val](const match& m) {\n                return !compare_values<int16_t>(cmp, m.curr_value.s16[0], val);\n            });\n        } break;\n\n        case SEARCH_TYPE_S32: {\n            int32_t val = (int32_t)std::strtol(value_str, nullptr, 0);\n\n            std::erase_if(search_matches, [cmp, val](const match& m) {\n                return !compare_values<int32_t>(cmp, m.curr_value.s32[0], val);\n            });\n        } break;\n\n        case SEARCH_TYPE_S64: {\n            int64_t val = (int64_t)std::strtoll(value_str, nullptr, 0);\n\n            std::erase_if(search_matches, [cmp, val](const match& m) {\n                return !compare_values<int64_t>(cmp, m.curr_value.s64, val);\n            });\n        } break;\n\n        case SEARCH_TYPE_F32: {\n            float val = std::strtof(value_str, nullptr);\n\n            std::erase_if(search_matches, [cmp, val](const match& m) {\n                return !compare_values<float>(cmp, m.curr_value.f32[0], val);\n            });\n        } break;\n\n        case SEARCH_TYPE_F64: {\n            double val = std::strtod(value_str, nullptr);\n\n            std::erase_if(search_matches, [cmp, val](const match& m) {\n                return !compare_values<double>(cmp, m.curr_value.f64, val);\n            });\n        } break;\n    }\n}\n\nvoid write_match_value(struct ps2_state* ps2, int cpu, match& m, int type) {\n    struct ps2_ram* mem = cpu == SEARCH_CPU_EE ? ps2->ee_ram : ps2->iop_ram;\n\n    switch (type) {\n        case SEARCH_TYPE_U8:\n        case SEARCH_TYPE_S8: {\n            *(uint8_t*)&mem->buf[m.address] = m.curr_value.u8[0];\n        } break;\n        case SEARCH_TYPE_U16:\n        case SEARCH_TYPE_S16: {\n            *(uint16_t*)&mem->buf[m.address] = m.curr_value.u16[0];\n        } break;\n        case SEARCH_TYPE_U32:\n        case SEARCH_TYPE_F32:\n        case SEARCH_TYPE_S32: {\n            *(uint32_t*)&mem->buf[m.address] = m.curr_value.u32[0];\n        } break;\n        case SEARCH_TYPE_U64:\n        case SEARCH_TYPE_F64:\n        case SEARCH_TYPE_S64: {\n            *(uint64_t*)&mem->buf[m.address] = m.curr_value.u64;\n        } break;\n    }\n}\n\nvoid sprintf_match(const value& v, char* buf, size_t size, int type, int hex) {\n    switch (type) {\n        case SEARCH_TYPE_U8:\n            snprintf(buf, size, hex ? \"0x%02x\" : \"%u\", v.u8[0]);\n            break;\n        case SEARCH_TYPE_U16:\n            snprintf(buf, size, hex ? \"0x%04x\" : \"%u\", v.u16[0]);\n            break;\n        case SEARCH_TYPE_U32:\n            snprintf(buf, size, hex ? \"0x%08x\" : \"%u\", v.u32[0]);\n            break;\n        case SEARCH_TYPE_U64:\n            snprintf(buf, size, hex ? \"0x%016llx\" : \"%llu\", v.u64);\n            break;\n        case SEARCH_TYPE_S8:\n            snprintf(buf, size, \"%d\", v.s8[0]);\n            break;\n        case SEARCH_TYPE_S16:\n            snprintf(buf, size, \"%d\", v.s16[0]);\n            break;\n        case SEARCH_TYPE_S32:\n            snprintf(buf, size, \"%d\", v.s32[0]);\n            break;\n        case SEARCH_TYPE_S64:\n            snprintf(buf, size, \"%lld\", v.s64);\n            break;\n        case SEARCH_TYPE_F32:\n            snprintf(buf, size, \"%f\", v.f32[0]);\n            break;\n        case SEARCH_TYPE_F64:\n            snprintf(buf, size, \"%lf\", v.f64);\n            break;\n    }\n}\n\nvoid show_match_change_dialog(iris::instance* iris, match& m, char* label, int search_type, int search_cpu) {\n    using namespace ImGui;\n\n    struct ps2_state* ps2 = iris->ps2;\n\n    static char new_value[32];\n\n    PushFont(iris->font_small);\n    TextDisabled(\"Edit \"); SameLine(0.0, 0.0);\n    PushFont(iris->font_small_code);\n    Text(\"%s\", label);\n    PopFont();\n    PopFont();\n\n    PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));\n    PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));\n\n    PushFont(iris->font_body);\n    PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));\n\n    AlignTextToFramePadding();\n    Text(ICON_MS_EDIT); SameLine();\n\n    SetNextItemWidth(100);\n    PushFont(iris->font_code);\n\n    if (InputText(\"##\", new_value, 32, ImGuiInputTextFlags_EnterReturnsTrue)) {\n        if (new_value[0]) {\n            if (strchr(new_value, '.')) {\n                if (search_type == SEARCH_TYPE_F64) {\n                    m.curr_value.f64 = std::strtod(new_value, nullptr);\n                } else {\n                    m.curr_value.f32[0] = std::strtof(new_value, nullptr);\n                }\n            } else {\n                m.curr_value.u64 = std::strtoull(new_value, nullptr, 0);\n            }\n\n            write_match_value(ps2, search_cpu, const_cast<match&>(m), search_type);\n        }\n\n        CloseCurrentPopup();\n    }\n\n    PopFont();\n\n    if (Button(\"Change\")) {\n        if (new_value[0]) {\n            if (strchr(new_value, '.')) {\n                if (search_type == SEARCH_TYPE_F64) {\n                    m.curr_value.f64 = std::strtod(new_value, nullptr);\n                } else {\n                    m.curr_value.f32[0] = std::strtof(new_value, nullptr);\n                }\n            } else {\n                m.curr_value.u64 = std::strtoull(new_value, nullptr, 0);\n            }\n\n            write_match_value(ps2, search_cpu, const_cast<match&>(m), search_type);\n        }\n\n        CloseCurrentPopup();\n    } SameLine();\n\n    if (Button(\"Cancel\"))\n        CloseCurrentPopup();\n\n    PopStyleColor();\n    PopStyleVar(2);\n\n    PopFont();\n}\n\nvoid show_description_change_dialog(iris::instance* iris, match& m) {\n    using namespace ImGui;\n\n    struct ps2_state* ps2 = iris->ps2;\n\n    static char new_value[32];\n\n    PushFont(iris->font_small);\n    TextDisabled(\"Edit description\");\n    PopFont();\n\n    PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));\n    PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));\n\n    PushFont(iris->font_body);\n    PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));\n\n    AlignTextToFramePadding();\n    Text(ICON_MS_EDIT); SameLine();\n\n    SetNextItemWidth(100);\n\n    static char desc_buf[512];\n\n    if (desc_buf[0] == '\\0') {\n        strncpy(desc_buf, m.description.c_str(), sizeof(desc_buf));\n    }\n\n    desc_buf[sizeof(desc_buf) - 1] = '\\0';\n\n    if (InputText(\"##desc\", desc_buf, sizeof(desc_buf), ImGuiInputTextFlags_EnterReturnsTrue)) {\n        m.description = \"\\\"\" + std::string(desc_buf) + \"\\\"\";\n\n        CloseCurrentPopup();\n    }\n\n    if (Button(\"Change\")) {\n        m.description = \"\\\"\" + std::string(desc_buf) + \"\\\"\";\n\n        CloseCurrentPopup();\n    } SameLine();\n\n    if (Button(\"Cancel\"))\n        CloseCurrentPopup();\n\n    PopStyleColor();\n    PopStyleVar(2);\n\n    PopFont();\n}\n\nint frame = 0;\n\nvoid show_search_table(iris::instance* iris, struct ps2_state* ps2, int type, int cpu) {\n    using namespace ImGui;\n\n    static uint32_t selected_address = 0;\n\n    if (search_matches.empty()) {\n        SeparatorText(\"Search results\");\n    } else {\n        char buf[256];\n\n        if (search_matches.size() > 9999) {\n            snprintf(buf, sizeof(buf), \"Search results (9999+ matches)\");\n        } else {\n            snprintf(buf, sizeof(buf), \"Search results (%zu matches)\", search_matches.size());\n        }\n\n        SeparatorText(buf);\n    }\n\n    if (BeginTable(\"Matches\", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingStretchProp)) {\n        TableSetupColumn(\"Address\");\n        TableSetupColumn(\"Previous Value\");\n        TableSetupColumn(\"Current Value\");\n        TableHeadersRow();\n\n        for (match& m : search_matches) {\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            char addr_label[16];\n            char prev_value_label[64];\n            char curr_value_label[64];\n\n            sprintf(addr_label, \"0x%08x\", m.address);\n            sprintf_match(m.prev_value, prev_value_label, sizeof(prev_value_label), type, display_hex);\n            sprintf_match(m.curr_value, curr_value_label, sizeof(curr_value_label), type, display_hex);\n\n            PushFont(iris->font_code);\n\n            Selectable(addr_label, false, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns);\n\n            if (IsItemClicked(ImGuiMouseButton_Right)) {\n                OpenPopup(\"context_menu\");\n\n                selected_address = m.address;\n            }\n\n            if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left)) {\n                OpenPopup(\"edit_value_popup\");\n\n                selected_address = m.address;\n            }\n\n            if (selected_address == m.address) if (BeginPopup(\"context_menu\")) {\n                PushFont(iris->font_small_code);\n                TextDisabled(\"0x%08x\", m.address);\n                PopFont();\n\n                PushFont(iris->font_body);\n                if (BeginMenu(ICON_MS_CONTENT_COPY \" Copy\")) {\n                    if (Selectable(\"Address\")) {\n                        ImGui::SetClipboardText(addr_label);\n                    }\n\n                    if (Selectable(\"Previous Value\")) {\n                        ImGui::SetClipboardText(prev_value_label);\n                    }\n\n                    if (Selectable(\"Current Value\")) {\n                        ImGui::SetClipboardText(curr_value_label);\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                if (BeginMenu(ICON_MS_EDIT \" Edit value\")) {\n                    show_match_change_dialog(iris, m, addr_label, type, cpu);\n\n                    ImGui::EndMenu();\n                }\n\n                bool in_address_list = false;\n\n                for (const match& am : address_list) {\n                    if (am.address == m.address) {\n                        in_address_list = true;\n                        break;\n                    }\n                }\n\n                if (Selectable(in_address_list ? ICON_MS_REMOVE \" Remove from address list\" : ICON_MS_ADD \" Add to address list\")) {\n                    if (in_address_list) {\n                        std::erase_if(address_list, [m](const match& am) {\n                            return am.address == m.address;\n                        });\n                    } else {\n                        m.description = \"\\\"No description\\\"\";\n                        m.cpu = cpu;\n                        m.type = type;\n\n                        address_list.push_back(m);\n                    }\n                }\n\n                PopFont();\n\n                EndPopup();\n            }\n\n            if (selected_address == m.address) if (BeginPopup(\"edit_value_popup\")) {\n                show_match_change_dialog(iris, m, addr_label, type, cpu);\n\n                EndPopup();\n            }\n\n            TableSetColumnIndex(1);\n\n            Text(\"%s\", prev_value_label);\n\n            TableSetColumnIndex(2);\n\n            Text(\"%s\", curr_value_label);\n\n            PopFont();\n        }\n\n        EndTable();\n    }\n}\n\nvoid show_address_list(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ps2_state* ps2 = iris->ps2;\n    static uint32_t selected_address = 0;\n\n    if (BeginTable(\"Addresses\", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable)) {\n        TableSetupColumn(\"Address\", ImGuiTableColumnFlags_WidthFixed, 100.0f);\n        TableSetupColumn(\"CPU\", ImGuiTableColumnFlags_WidthFixed, 20.0f);\n        TableSetupColumn(\"Type\", ImGuiTableColumnFlags_WidthFixed, 30.0f);\n        TableSetupColumn(\"Description\");\n        TableSetupColumn(\"Previous Value\", ImGuiTableColumnFlags_WidthFixed, 150.0f);\n        TableSetupColumn(\"Current Value\");\n        TableHeadersRow();\n\n        for (match& m : address_list) {\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            char addr_label[16];\n            char prev_value_label[64];\n            char curr_value_label[64];\n\n            sprintf(addr_label, \"0x%08x\", m.address);\n            sprintf_match(m.prev_value, prev_value_label, sizeof(prev_value_label), m.type, display_hex);\n            sprintf_match(m.curr_value, curr_value_label, sizeof(curr_value_label), m.type, display_hex);\n\n            PushFont(iris->font_code);\n\n            Selectable(addr_label, false, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns);\n\n            if (IsItemClicked(ImGuiMouseButton_Right)) {\n                OpenPopup(\"context_menu_al\");\n\n                selected_address = m.address;\n            }\n\n            if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left)) {\n                OpenPopup(\"edit_value_popup_al\");\n\n                selected_address = m.address;\n            }\n\n            if (selected_address == m.address) if (BeginPopup(\"context_menu_al\")) {\n                PushFont(iris->font_small_code);\n                TextDisabled(\"0x%08x\", m.address);\n                PopFont();\n\n                PushFont(iris->font_body);\n                if (BeginMenu(ICON_MS_CONTENT_COPY \" Copy\")) {\n                    if (Selectable(\"Address\")) {\n                        ImGui::SetClipboardText(addr_label);\n                    }\n\n                    if (Selectable(\"Previous Value\")) {\n                        ImGui::SetClipboardText(prev_value_label);\n                    }\n\n                    if (Selectable(\"Current Value\")) {\n                        ImGui::SetClipboardText(curr_value_label);\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                if (BeginMenu(ICON_MS_EDIT \" Edit value\")) {\n                    show_match_change_dialog(iris, m, addr_label, m.type, m.cpu);\n\n                    ImGui::EndMenu();\n                }\n\n                if (BeginMenu(ICON_MS_EDIT \" Edit description\")) {\n                    show_description_change_dialog(iris, m);\n\n                    ImGui::EndMenu();\n                }\n\n                if (BeginMenu(ICON_MS_EDIT \" Edit type\")) {\n                    for (int i = 0; i < IM_ARRAYSIZE(search_type_names); i++) {\n                        if (MenuItem(search_type_names[i], nullptr, m.type == i)) {\n                            m.type = i;\n                        }\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                if (Selectable(ICON_MS_REMOVE \" Remove from address list\")) {\n                    std::erase_if(address_list, [m](const match& am) {\n                        return am.address == m.address;\n                    });\n                }\n\n                PopFont();\n\n                EndPopup();\n            }\n\n            if (selected_address == m.address) if (BeginPopup(\"edit_value_popup_al\")) {\n                show_match_change_dialog(iris, m, addr_label, m.type, m.cpu);\n\n                EndPopup();\n            }\n\n            TableSetColumnIndex(1);\n\n            Text(\"%s\", m.cpu == SEARCH_CPU_EE ? \"EE\" : \"IOP\");\n\n            TableSetColumnIndex(2);\n\n            Text(\"%s\", search_type_names[m.type]);\n\n            TableSetColumnIndex(3);\n\n            PushFont(iris->font_body);\n            Text(\"%s\", m.description.c_str());\n            PopFont();\n\n            TableSetColumnIndex(4);\n\n            Text(\"%s\", prev_value_label);\n\n            TableSetColumnIndex(5);\n\n            Text(\"%s\", curr_value_label);\n\n            PopFont();\n        }\n    }\n\n    EndTable();\n}\n\nvoid show_search_options(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ps2_state* ps2 = iris->ps2;\n\n    SeparatorText(\"Search options\");\n\n    PushItemWidth(-FLT_MIN);\n\n    Text(\"Type\");\n    if (BeginCombo(\"##type\", search_type_names[search_type])) {\n        for (int i = 0; i < IM_ARRAYSIZE(search_type_names); i++) {\n            if (Selectable(search_type_names[i], search_type == i)) {\n                search_type = i;\n            }\n        }\n\n        EndCombo();\n    }\n\n    PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0f);\n    Checkbox(\"Aligned\", &search_aligned);\n    PopStyleVar();\n\n    Text(\"Comparison\");\n    if (BeginCombo(\"##comparison\", search_cmp_names[search_cmp])) {\n        for (int i = 0; i < IM_ARRAYSIZE(search_cmp_names); i++) {\n            if (Selectable(search_cmp_names[i], search_cmp == i)) {\n                search_cmp = i;\n            }\n        }\n\n        EndCombo();\n    }\n\n    Text(\"Memory\");\n    if (BeginCombo(\"##memory\", search_cpu_names[search_cpu])) {\n        for (int i = 0; i < IM_ARRAYSIZE(search_cpu_names); i++) {\n            if (Selectable(search_cpu_names[i], search_cpu == i)) {\n                search_cpu = i;\n            }\n        }\n\n        EndCombo();\n    }\n\n    static char buf[64];\n\n    Text(\"Value\");\n    if (InputText(\"##value\", buf, sizeof(buf), ImGuiInputTextFlags_EnterReturnsTrue)) {\n        search_memory(ps2, search_cpu, search_type, search_cmp, buf, search_aligned);\n    }\n\n    PopItemWidth();\n\n    BeginDisabled(buf[0] == '\\0');\n    if (Button(\"Search\")) {\n        search_memory(ps2, search_cpu, search_type, search_cmp, buf, search_aligned);\n    } SameLine();\n    EndDisabled();\n\n    BeginDisabled(buf[0] == '\\0' || search_matches.empty());\n    if (Button(\"Filter\")) {\n        filter_results(search_type, search_cmp, buf);\n    }\n    EndDisabled();\n}\n\nvoid update_search_matches(struct ps2_state* ps2, int cpu) {\n    if (frame != 4) {\n        frame++;\n\n        return;\n    }\n\n    for (match& m : search_matches) {\n        struct ps2_ram* mem = m.cpu == SEARCH_CPU_EE ? ps2->ee_ram : ps2->iop_ram;\n\n        m.curr_value.u64 = *(uint64_t*)&mem->buf[m.address];\n    }\n\n    for (match& m : address_list) {\n        struct ps2_ram* mem = m.cpu == SEARCH_CPU_EE ? ps2->ee_ram : ps2->iop_ram;\n\n        m.curr_value.u64 = *(uint64_t*)&mem->buf[m.address];\n    }\n\n\n    frame = 0;\n}\n\nstd::string serialize_address_list() {\n    std::stringstream ss;\n\n    for (const match& m : address_list) {\n        ss << \"0x\" << std::hex << m.address << \",\" << m.description << \",\" << m.cpu << \",\" << m.type << \"\\n\";\n    }\n\n    return ss.str();\n}\n\nvoid import_address_list_from_stream(std::istream& stream) {\n    address_list.clear();\n\n    std::string line;\n\n    while (std::getline(stream, line)) {\n        std::stringstream linestream(line);\n        std::string address_str;\n        std::string description;\n        std::string cpu_str;\n        std::string type_str;\n\n        bool valid = true;\n\n        valid = std::getline(linestream, address_str, ',') &&\n                std::getline(linestream, description, ',') &&\n                std::getline(linestream, cpu_str, ',') &&\n                std::getline(linestream, type_str);\n\n        if (valid) {\n            match m;\n\n            m.address = (uint32_t)std::strtoul(address_str.c_str(), nullptr, 0);\n            m.description = description;\n            m.prev_value.u64 = 0;\n            m.curr_value.u64 = 0;\n            m.cpu = std::strtoul(cpu_str.c_str(), nullptr, 0);\n            m.type = std::strtoul(type_str.c_str(), nullptr, 0);\n\n            // To-do: Remove double quotes from description if present\n\n            address_list.push_back(m);\n        }\n    }\n}\n\nvoid show_memory_search(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ps2_state* ps2 = iris->ps2;\n\n    update_search_matches(ps2, search_cpu);\n\n    SetNextWindowSizeConstraints(ImVec2(600, 560), ImVec2(FLT_MAX, FLT_MAX));\n\n    int top_shelf_height = GetContentRegionAvail().y - 220;\n\n    if (imgui::BeginEx(\"Memory search\", &iris->show_memory_search, ImGuiWindowFlags_MenuBar)) {\n        if (BeginMenuBar()) {\n            if (BeginMenu(\"File\")) {\n                if (BeginMenu(\"Address list\")) {\n                    if (MenuItem(\"Export to file...\")) {\n                        std::string str = serialize_address_list();\n\n                        pfd::save_file file(\"Export Address List\", \"address_list.csv\", { \"CSV Files (*.csv)\", \"*.csv\", \"All files (*.*)\", \"\" });\n\n                        if (!file.result().empty()) {\n                            FILE* f = fopen(file.result().c_str(), \"w\");\n\n                            if (f) {\n                                fwrite(str.c_str(), 1, str.size(), f);\n\n                                fclose(f);\n                            } else {\n                                push_info(iris, \"Failed to open file for writing address list\");\n                            }\n                        }\n                    }\n\n                    if (MenuItem(\"Import from file...\")) {\n                        pfd::open_file file(\"Import Address List\", \"\", { \"CSV Files (*.csv)\", \"*.csv\", \"All Files (*.*)\", \"*\" });\n\n                        if (!file.result().empty()) {\n                            std::ifstream f(file.result().at(0));\n\n                            if (f.is_open()) {\n                                import_address_list_from_stream(f);\n                            } else {\n                                push_info(iris, \"Failed to open file for reading address list\");\n                            }\n                        }\n                    }\n\n                    if (MenuItem(\"Export to clipboard\")) {\n                        std::string str = serialize_address_list();\n\n                        SDL_SetClipboardText(str.c_str());\n                    }\n\n                    if (MenuItem(\"Import from clipboard\")) {\n                        if (SDL_HasClipboardText()) {\n                            std::string clip_text = SDL_GetClipboardText();\n\n                            std::istringstream iss(clip_text);\n\n                            import_address_list_from_stream(iss);\n                        }\n                    }\n\n                    if (MenuItem(\"Clear\")) {\n                        address_list.clear();\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                ImGui::EndMenu();\n            }\n\n            if (BeginMenu(\"View\")) {\n                MenuItem(\"Display as hex\", nullptr, &display_hex);\n\n                ImGui::EndMenu();\n            }\n\n            EndMenuBar();\n        }\n\n        if (BeginChild(\"##search_table\", ImVec2(GetContentRegionAvail().x - 225, GetContentRegionAvail().y - 220))) {\n            show_search_table(iris, ps2, search_type, search_cpu);\n        } EndChild(); SameLine();\n\n        if (BeginChild(\"##search_options\", ImVec2(0, GetContentRegionAvail().y - 220))) {\n            show_search_options(iris);\n        } EndChild();\n\n        SeparatorText(\"Address list\");\n\n        if (BeginChild(\"##address_list\")) {\n            show_address_list(iris);\n        } EndChild();\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/memory_viewer.h",
    "content": "// Mini memory editor for Dear ImGui (to embed in your game/tools)\n// Get latest version at http://www.github.com/ocornut/imgui_club\n// Licensed under The MIT License (MIT)\n\n// Right-click anywhere to access the Options menu!\n// You can adjust the keyboard repeat delay/rate in ImGuiIO.\n// The code assume a mono-space font for simplicity!\n// If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before calling this.\n//\n// Usage:\n//   // Create a window and draw memory editor inside it:\n//   static MemoryEditor mem_edit_1;\n//   static char data[0x10000];\n//   size_t data_size = 0x10000;\n//   mem_edit_1.DrawWindow(\"Memory Editor\", data, data_size);\n//\n// Usage:\n//   // If you already have a window, use DrawContents() instead:\n//   static MemoryEditor mem_edit_2;\n//   ImGui::Begin(\"MyWindow\")\n//   mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this);\n//   ImGui::End();\n//\n// Changelog:\n// - v0.10: initial version\n// - v0.23 (2017/08/17): added to github. fixed right-arrow triggering a byte write.\n// - v0.24 (2018/06/02): changed DragInt(\"Rows\" to use a %d data format (which is desirable since imgui 1.61).\n// - v0.25 (2018/07/11): fixed wording: all occurrences of \"Rows\" renamed to \"Columns\".\n// - v0.26 (2018/08/02): fixed clicking on hex region\n// - v0.30 (2018/08/02): added data preview for common data types\n// - v0.31 (2018/10/10): added OptUpperCaseHex option to select lower/upper casing display [@samhocevar]\n// - v0.32 (2018/10/10): changed signatures to use void* instead of unsigned char*\n// - v0.33 (2018/10/10): added OptShowOptions option to hide all the interactive option setting.\n// - v0.34 (2019/05/07): binary preview now applies endianness setting [@nicolasnoble]\n// - v0.35 (2020/01/29): using ImGuiDataType available since Dear ImGui 1.69.\n// - v0.36 (2020/05/05): minor tweaks, minor refactor.\n// - v0.40 (2020/10/04): fix misuse of ImGuiListClipper API, broke with Dear ImGui 1.79. made cursor position appears on left-side of edit box. option popup appears on mouse release. fix MSVC warnings where _CRT_SECURE_NO_WARNINGS wasn't working in recent versions.\n// - v0.41 (2020/10/05): fix when using with keyboard/gamepad navigation enabled.\n// - v0.42 (2020/10/14): fix for . character in ASCII view always being greyed out.\n// - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the editor [@leiradel]\n// - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width.\n// - v0.50 (2021/11/12): various fixes for recent dear imgui versions (fixed misuse of clipper, relying on SetKeyboardFocusHere() handling scrolling from 1.85). added default size.\n// - v0.51 (2024/02/22): fix for layout change in 1.89 when using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#34)\n// - v0.52 (2024/03/08): removed unnecessary GetKeyIndex() calls, they are a no-op since 1.87.\n// - v0.53 (2024/05/27): fixed right-click popup from not appearing when using DrawContents(). warning fixes. (#35)\n// - v0.54 (2024/07/29): allow ReadOnly mode to still select and preview data. (#46) [@DeltaGW2])\n// - v0.55 (2024/08/19): added BgColorFn to allow setting background colors independently from highlighted selection. (#27) [@StrikerX3]\n//                       added MouseHoveredAddr public readable field. (#47, #27) [@StrikerX3]\n//                       fixed a data preview crash with 1.91.0 WIP. fixed contiguous highlight color when using data preview.\n//                       *BREAKING* added UserData field passed to all optional function handlers: ReadFn, WriteFn, HighlightFn, BgColorFn. (#50) [@silverweed]\n// - v0.56 (2024/11/04): fixed MouseHovered, MouseHoveredAddr not being set when hovering a byte being edited. (#54)\n//\n// TODO:\n// - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some day.\n// - PageUp/PageDown are not supported because we use _NoNav. This is a good test scenario for working out idioms of how to mix natural nav and our own...\n// - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame.\n// - Using InputText() is awkward and maybe overkill here, consider implementing something custom.\n\n#pragma once\n\n#include <stdio.h>      // sprintf, scanf\n#include <stdint.h>     // uint8_t, etc.\n\n#if defined(_MSC_VER) || defined(_UCRT)\n#define _PRISizeT   \"I\"\n#define ImSnprintf  _snprintf\n#else\n#define _PRISizeT   \"z\"\n#define ImSnprintf  snprintf\n#endif\n\n#if defined(_MSC_VER) || defined(_UCRT)\n#pragma warning (push)\n#pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe.\n#endif\n\nstruct MemoryEditor\n{\n    enum DataFormat\n    {\n        DataFormat_Bin = 0,\n        DataFormat_Dec = 1,\n        DataFormat_Hex = 2,\n        DataFormat_COUNT\n    };\n\n    ImFont*         FontOptions = nullptr;\n\n    // Settings\n    bool            Open;                                       // = true   // set to false when DrawWindow() was closed. ignore if not using DrawWindow().\n    bool            ReadOnly;                                   // = false  // disable any editing.\n    int             Cols;                                       // = 16     // number of columns to display.\n    bool            OptShowOptions;                             // = true   // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them.\n    bool            OptShowDataPreview;                         // = false  // display a footer previewing the decimal/binary/hex/float representation of the currently selected bytes.\n    bool            OptShowHexII;                               // = false  // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as \".X\".\n    bool            OptShowAscii;                               // = true   // display ASCII representation on the right side.\n    bool            OptGreyOutZeroes;                           // = true   // display null/zero bytes using the TextDisabled color.\n    bool            OptUpperCaseHex;                            // = true   // display hexadecimal values as \"FF\" instead of \"ff\".\n    int             OptMidColsCount;                            // = 8      // set to 0 to disable extra spacing between every mid-cols.\n    int             OptAddrDigitsCount;                         // = 0      // number of addr digits to display (default calculated based on maximum displayed addr).\n    float           OptFooterExtraHeight;                       // = 0      // space to reserve at the bottom of the widget to add custom widgets\n    ImU32           HighlightColor;                             //          // background color of highlighted bytes.\n\n    // Function handlers\n    ImU8            (*ReadFn)(const ImU8* mem, size_t off, void* user_data);      // = 0      // optional handler to read bytes.\n    void            (*WriteFn)(ImU8* mem, size_t off, ImU8 d, void* user_data);   // = 0      // optional handler to write bytes.\n    bool            (*HighlightFn)(const ImU8* mem, size_t off, void* user_data); // = 0      // optional handler to return Highlight property (to support non-contiguous highlighting).\n    ImU32           (*BgColorFn)(const ImU8* mem, size_t off, void* user_data);   // = 0      // optional handler to return custom background color of individual bytes.\n    void*           UserData;                                                     // = NULL   // user data forwarded to the function handlers\n\n    // Public read-only data\n    bool            MouseHovered;                               // set when mouse is hovering a value.\n    size_t          MouseHoveredAddr;                           // the address currently being hovered if MouseHovered is set.\n\n    // [Internal State]\n    bool            ContentsWidthChanged;\n    size_t          DataPreviewAddr;\n    size_t          DataEditingAddr;\n    bool            DataEditingTakeFocus;\n    char            DataInputBuf[32];\n    char            AddrInputBuf[32];\n    size_t          GotoAddr;\n    size_t          HighlightMin, HighlightMax;\n    int             PreviewEndianness;\n    ImGuiDataType   PreviewDataType;\n\n    MemoryEditor()\n    {\n        // Settings\n        Open = true;\n        ReadOnly = false;\n        Cols = 16;\n        OptShowOptions = true;\n        OptShowDataPreview = true;\n        OptShowHexII = false;\n        OptShowAscii = true;\n        OptGreyOutZeroes = true;\n        OptUpperCaseHex = true;\n        OptMidColsCount = 8;\n        OptAddrDigitsCount = 0;\n        OptFooterExtraHeight = 0.0f;\n        ReadFn = nullptr;\n        WriteFn = nullptr;\n        HighlightFn = nullptr;\n        BgColorFn = nullptr;\n        UserData = nullptr;\n        FontOptions = nullptr;\n\n        // State/Internals\n        ContentsWidthChanged = false;\n        DataPreviewAddr = DataEditingAddr = (size_t)-1;\n        DataEditingTakeFocus = false;\n        memset(DataInputBuf, 0, sizeof(DataInputBuf));\n        memset(AddrInputBuf, 0, sizeof(AddrInputBuf));\n        GotoAddr = (size_t)-1;\n        MouseHovered = false;\n        MouseHoveredAddr = 0;\n        HighlightMin = HighlightMax = (size_t)-1;\n        PreviewEndianness = 0;\n        PreviewDataType = ImGuiDataType_S32;\n    }\n\n    void GotoAddrAndHighlight(size_t addr_min, size_t addr_max)\n    {\n        GotoAddr = addr_min;\n        HighlightMin = addr_min;\n        HighlightMax = addr_max;\n    }\n\n    struct Sizes\n    {\n        int     AddrDigitsCount;\n        float   LineHeight;\n        float   GlyphWidth;\n        float   HexCellWidth;\n        float   SpacingBetweenMidCols;\n        float   PosHexStart;\n        float   PosHexEnd;\n        float   PosAsciiStart;\n        float   PosAsciiEnd;\n        float   WindowWidth;\n\n        Sizes() { memset(this, 0, sizeof(*this)); }\n    };\n\n    void CalcSizes(Sizes& s, size_t mem_size, size_t base_display_addr)\n    {\n        ImGuiStyle& style = ImGui::GetStyle();\n        s.AddrDigitsCount = OptAddrDigitsCount;\n        if (s.AddrDigitsCount == 0)\n            for (size_t n = base_display_addr + mem_size - 1; n > 0; n >>= 4)\n                s.AddrDigitsCount++;\n        s.LineHeight = ImGui::GetTextLineHeight();\n        s.GlyphWidth = ImGui::CalcTextSize(\"F\").x + 1;                  // We assume the font is mono-space\n        s.HexCellWidth = (float)(int)(s.GlyphWidth * 2.5f);             // \"FF \" we include trailing space in the width to easily catch clicks everywhere\n        s.SpacingBetweenMidCols = (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns we add a bit of extra spacing\n        s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth;\n        s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols);\n        s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd;\n        if (OptShowAscii)\n        {\n            s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1;\n            if (OptMidColsCount > 0)\n                s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols;\n            s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth;\n        }\n        s.WindowWidth = s.PosAsciiEnd + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth;\n    }\n\n    // Standalone Memory Editor window\n    void DrawWindow(const char* title, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000)\n    {\n        Sizes s;\n        CalcSizes(s, mem_size, base_display_addr);\n        ImGui::SetNextWindowSize(ImVec2(s.WindowWidth, s.WindowWidth * 0.60f), ImGuiCond_FirstUseEver);\n        ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX));\n\n        Open = true;\n        if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar))\n        {\n            DrawContents(mem_data, mem_size, base_display_addr);\n            if (ContentsWidthChanged)\n            {\n                CalcSizes(s, mem_size, base_display_addr);\n                ImGui::SetWindowSize(ImVec2(s.WindowWidth, ImGui::GetWindowSize().y));\n            }\n        }\n        ImGui::End();\n    }\n\n    // Memory Editor contents only\n    void DrawContents(void* mem_data_void, size_t mem_size, size_t base_display_addr = 0x0000)\n    {\n        HighlightColor = ImGui::GetColorU32(ImGuiCol_Text, 0.25f);\n\n        if (Cols < 1)\n            Cols = 1;\n\n        ImU8* mem_data = (ImU8*)mem_data_void;\n        Sizes s;\n        CalcSizes(s, mem_size, base_display_addr);\n        ImGuiStyle& style = ImGui::GetStyle();\n\n        const ImVec2 contents_pos_start = ImGui::GetCursorScreenPos();\n\n        // We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window.\n        // This is used as a facility since our main click detection code doesn't assign an ActiveId so the click would normally be caught as a window-move.\n        const float height_separator = style.ItemSpacing.y + 4.0f;\n        float footer_height = OptFooterExtraHeight;\n        if (OptShowOptions)\n            footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1;\n        if (OptShowDataPreview)\n            footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3;\n        ImGui::BeginChild(\"##scrolling\", ImVec2(-FLT_MIN, -footer_height), ImGuiChildFlags_None, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav);\n        ImDrawList* draw_list = ImGui::GetWindowDrawList();\n\n        ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0));\n        ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0));\n\n        // We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function.\n        const int line_total_count = (int)((mem_size + Cols - 1) / Cols);\n        ImGuiListClipper clipper;\n        clipper.Begin(line_total_count, s.LineHeight);\n\n        bool data_next = false;\n\n        if (DataEditingAddr >= mem_size)\n            DataEditingAddr = (size_t)-1;\n        if (DataPreviewAddr >= mem_size)\n            DataPreviewAddr = (size_t)-1;\n\n        size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0;\n\n        size_t data_editing_addr_next = (size_t)-1;\n        if (DataEditingAddr != (size_t)-1)\n        {\n            // Move cursor but only apply on next frame so scrolling with be synchronized (because currently we can't change the scrolling while the window is being rendered)\n            if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols)                 { data_editing_addr_next = DataEditingAddr - Cols; }\n            else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols){ data_editing_addr_next = DataEditingAddr + Cols; }\n            else if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0)              { data_editing_addr_next = DataEditingAddr - 1; }\n            else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1)  { data_editing_addr_next = DataEditingAddr + 1; }\n        }\n\n        // Draw vertical separator\n        ImVec2 window_pos = ImGui::GetWindowPos();\n        if (OptShowAscii)\n            draw_list->AddLine(ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y), ImVec2(window_pos.x + s.PosAsciiStart - s.GlyphWidth, window_pos.y + 9999), ImGui::GetColorU32(ImGuiCol_Border));\n\n        const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text);\n        const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text;\n\n        const char* format_address = OptUpperCaseHex ? \"%0*\" _PRISizeT \"X \" : \"%0*\" _PRISizeT \"x \";\n        const char* format_data = OptUpperCaseHex ? \"%0*\" _PRISizeT \"X\" : \"%0*\" _PRISizeT \"x\";\n        const char* format_byte = OptUpperCaseHex ? \"%02X\" : \"%02x\";\n        const char* format_byte_space = OptUpperCaseHex ? \"%02X \" : \"%02x \";\n\n        MouseHovered = false;\n        MouseHoveredAddr = 0;\n\n        while (clipper.Step())\n            for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines\n            {\n                size_t addr = (size_t)line_i * Cols;\n                ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr);\n\n                // Draw Hexadecimal\n                for (int n = 0; n < Cols && addr < mem_size; n++, addr++)\n                {\n                    float byte_pos_x = s.PosHexStart + s.HexCellWidth * n;\n                    if (OptMidColsCount > 0)\n                        byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols;\n                    ImGui::SameLine(byte_pos_x);\n\n                    // Draw highlight or custom background color\n                    const bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax);\n                    const bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, UserData));\n                    const bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size);\n\n                    ImU32 bg_color = 0;\n                    bool is_next_byte_highlighted = false;\n                    if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview)\n                    {\n                        is_next_byte_highlighted = (addr + 1 < mem_size) && ((HighlightMax != (size_t)-1 && addr + 1 < HighlightMax) || (HighlightFn && HighlightFn(mem_data, addr + 1, UserData)) || (addr + 1 < DataPreviewAddr + preview_data_type_size));\n                        bg_color = HighlightColor;\n                    }\n                    else if (BgColorFn != nullptr)\n                    {\n                        is_next_byte_highlighted = (addr + 1 < mem_size) && ((BgColorFn(mem_data, addr + 1, UserData) & IM_COL32_A_MASK) != 0);\n                        bg_color = BgColorFn(mem_data, addr, UserData);\n                    }\n                    if (bg_color != 0)\n                    {\n                        float bg_width = s.GlyphWidth * 2;\n                        if (is_next_byte_highlighted || (n + 1 == Cols))\n                        {\n                            bg_width = s.HexCellWidth;\n                            if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0)\n                                bg_width += s.SpacingBetweenMidCols;\n                        }\n                        ImVec2 pos = ImGui::GetCursorScreenPos();\n                        draw_list->AddRectFilled(pos, ImVec2(pos.x + bg_width, pos.y + s.LineHeight), bg_color);\n                    }\n\n                    if (DataEditingAddr == addr)\n                    {\n                        // Display text input on current byte\n                        bool data_write = false;\n                        ImGui::PushID((void*)addr);\n                        if (DataEditingTakeFocus)\n                        {\n                            ImGui::SetKeyboardFocusHere(0);\n                            ImSnprintf(AddrInputBuf, 32, format_data, s.AddrDigitsCount, base_display_addr + addr);\n                            ImSnprintf(DataInputBuf, 32, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);\n                        }\n                        struct InputTextUserData\n                        {\n                            // FIXME: We should have a way to retrieve the text edit cursor position more easily in the API, this is rather tedious. This is such a ugly mess we may be better off not using InputText() at all here.\n                            static int Callback(ImGuiInputTextCallbackData* data)\n                            {\n                                InputTextUserData* user_data = (InputTextUserData*)data->UserData;\n                                if (!data->HasSelection())\n                                    user_data->CursorPos = data->CursorPos;\n#if IMGUI_VERSION_NUM < 19102\n                                if (data->Flags & ImGuiInputTextFlags_ReadOnly)\n                                    return 0;\n#endif\n                                if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen)\n                                {\n                                    // When not editing a byte, always refresh its InputText content pulled from underlying memory data\n                                    // (this is a bit tricky, since InputText technically \"owns\" the master copy of the buffer we edit it in there)\n                                    data->DeleteChars(0, data->BufTextLen);\n                                    data->InsertChars(0, user_data->CurrentBufOverwrite);\n                                    data->SelectionStart = 0;\n                                    data->SelectionEnd = 2;\n                                    data->CursorPos = 0;\n                                }\n                                return 0;\n                            }\n                            char   CurrentBufOverwrite[3];  // Input\n                            int    CursorPos;               // Output\n                        };\n                        InputTextUserData input_text_user_data;\n                        input_text_user_data.CursorPos = -1;\n                        ImSnprintf(input_text_user_data.CurrentBufOverwrite, 3, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]);\n                        ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways;\n                        if (ReadOnly)\n                            flags |= ImGuiInputTextFlags_ReadOnly;\n                        flags |= ImGuiInputTextFlags_AlwaysOverwrite; // was ImGuiInputTextFlags_AlwaysInsertMode\n                        ImGui::SetNextItemWidth(s.GlyphWidth * 2);\n                        if (ImGui::InputText(\"##data\", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, InputTextUserData::Callback, &input_text_user_data))\n                            data_write = data_next = true;\n                        else if (!DataEditingTakeFocus && !ImGui::IsItemActive())\n                            DataEditingAddr = data_editing_addr_next = (size_t)-1;\n                        DataEditingTakeFocus = false;\n                        if (input_text_user_data.CursorPos >= 2)\n                            data_write = data_next = true;\n                        if (data_editing_addr_next != (size_t)-1)\n                            data_write = data_next = false;\n                        unsigned int data_input_value = 0;\n                        if (!ReadOnly && data_write && sscanf(DataInputBuf, \"%X\", &data_input_value) == 1)\n                        {\n                            if (WriteFn)\n                                WriteFn(mem_data, addr, (ImU8)data_input_value, UserData);\n                            else\n                                mem_data[addr] = (ImU8)data_input_value;\n                        }\n                        if (ImGui::IsItemHovered())\n                        {\n                            MouseHovered = true;\n                            MouseHoveredAddr = addr;\n                        }\n                        ImGui::PopID();\n                    }\n                    else\n                    {\n                        // NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on.\n                        ImU8 b = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];\n\n                        if (OptShowHexII)\n                        {\n                            if ((b >= 32 && b < 128))\n                                ImGui::Text(\".%c \", b);\n                            else if (b == 0xFF && OptGreyOutZeroes)\n                                ImGui::TextDisabled(\"## \");\n                            else if (b == 0x00)\n                                ImGui::Text(\"   \");\n                            else\n                                ImGui::Text(format_byte_space, b);\n                        }\n                        else\n                        {\n                            if (b == 0 && OptGreyOutZeroes)\n                                ImGui::TextDisabled(\"00 \");\n                            else\n                                ImGui::Text(format_byte_space, b);\n                        }\n                        if (ImGui::IsItemHovered())\n                        {\n                            MouseHovered = true;\n                            MouseHoveredAddr = addr;\n                            if (ImGui::IsMouseClicked(0))\n                            {\n                                DataEditingTakeFocus = true;\n                                data_editing_addr_next = addr;\n                            }\n                        }\n                    }\n                }\n\n                if (OptShowAscii)\n                {\n                    // Draw ASCII values\n                    ImGui::SameLine(s.PosAsciiStart);\n                    ImVec2 pos = ImGui::GetCursorScreenPos();\n                    addr = (size_t)line_i * Cols;\n\n                    const float mouse_off_x = ImGui::GetIO().MousePos.x - pos.x;\n                    const size_t mouse_addr = (mouse_off_x >= 0.0f && mouse_off_x < s.PosAsciiEnd - s.PosAsciiStart) ? addr + (size_t)(mouse_off_x / s.GlyphWidth) : (size_t)-1;\n\n                    ImGui::PushID(line_i);\n                    if (ImGui::InvisibleButton(\"ascii\", ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight)))\n                    {\n                        DataEditingAddr = DataPreviewAddr = mouse_addr;\n                        DataEditingTakeFocus = true;\n                    }\n                    if (ImGui::IsItemHovered())\n                    {\n                        MouseHovered = true;\n                        MouseHoveredAddr = mouse_addr;\n                    }\n                    ImGui::PopID();\n                    for (int n = 0; n < Cols && addr < mem_size; n++, addr++)\n                    {\n                        if (addr == DataEditingAddr)\n                        {\n                            draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg));\n                            draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg));\n                        }\n                        else if (BgColorFn)\n                        {\n                            draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), BgColorFn(mem_data, addr, UserData));\n                        }\n                        unsigned char c = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr];\n                        char display_c = (c < 32 || c >= 128) ? '.' : c;\n                        draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1);\n                        pos.x += s.GlyphWidth;\n                    }\n                }\n            }\n        ImGui::PopStyleVar(2);\n        const float child_width = ImGui::GetWindowSize().x;\n        ImGui::EndChild();\n\n        // Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child)\n        ImGui::SetCursorPosX(s.WindowWidth);\n        ImGui::Dummy(ImVec2(0.0f, 0.0f));\n\n        if (data_next && DataEditingAddr + 1 < mem_size)\n        {\n            DataEditingAddr = DataPreviewAddr = DataEditingAddr + 1;\n            DataEditingTakeFocus = true;\n        }\n        else if (data_editing_addr_next != (size_t)-1)\n        {\n            DataEditingAddr = DataPreviewAddr = data_editing_addr_next;\n            DataEditingTakeFocus = true;\n        }\n\n        const bool lock_show_data_preview = OptShowDataPreview;\n    \n        if (FontOptions) ImGui::PushFont(FontOptions);\n\n        if (OptShowOptions)\n        {\n            ImGui::Separator();\n            \n            DrawOptionsLine(s, mem_data, mem_size, base_display_addr);\n        }\n\n        if (lock_show_data_preview)\n        {\n            ImGui::Separator();\n            DrawPreviewLine(s, mem_data, mem_size, base_display_addr);\n        }\n\n        if (FontOptions) ImGui::PopFont();\n\n        const ImVec2 contents_pos_end(contents_pos_start.x + child_width, ImGui::GetCursorScreenPos().y);\n        //ImGui::GetForegroundDrawList()->AddRect(contents_pos_start, contents_pos_end, IM_COL32(255, 0, 0, 255));\n        if (OptShowOptions)\n            if (ImGui::IsMouseHoveringRect(contents_pos_start, contents_pos_end))\n                if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right))\n                    ImGui::OpenPopup(\"OptionsPopup\");\n\n        if (ImGui::BeginPopup(\"OptionsPopup\"))\n        {\n            if (FontOptions) ImGui::PushFont(FontOptions);\n            ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f);\n            if (ImGui::DragInt(\"##cols\", &Cols, 0.2f, 4, 32, \"%d cols\")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; }\n\n            ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0f);\n            ImGui::Checkbox(\"Show Data Preview\", &OptShowDataPreview);\n            ImGui::Checkbox(\"Show HexII\", &OptShowHexII);\n            if (ImGui::Checkbox(\"Show Ascii\", &OptShowAscii)) { ContentsWidthChanged = true; }\n            ImGui::Checkbox(\"Grey out zeroes\", &OptGreyOutZeroes);\n            ImGui::Checkbox(\"Uppercase Hex\", &OptUpperCaseHex);\n            ImGui::PopStyleVar();\n            ImGui::PopFont();\n            ImGui::EndPopup();\n        }\n    }\n\n    void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr)\n    {\n        IM_UNUSED(mem_data);\n        ImGuiStyle& style = ImGui::GetStyle();\n        const char* format_range = OptUpperCaseHex ? \"%0*\" _PRISizeT \"X..%0*\" _PRISizeT \"X\" : \"%0*\" _PRISizeT \"x..%0*\" _PRISizeT \"x\";\n\n        // Options menu\n        if (ImGui::Button(\"Options\"))\n            ImGui::OpenPopup(\"OptionsPopup\");\n\n        ImGui::SameLine();\n        ImGui::Text(\"Range \"); ImGui::SameLine(0.0, 0.0);\n        if (FontOptions) ImGui::PopFont();\n        ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1);\n        ImGui::SameLine();\n        ImGui::SetNextItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth + style.FramePadding.x * 2.0f);\n        if (ImGui::InputText(\"##addr\", AddrInputBuf, IM_ARRAYSIZE(AddrInputBuf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue))\n        {\n            size_t goto_addr;\n            if (sscanf(AddrInputBuf, \"%\" _PRISizeT \"X\", &goto_addr) == 1)\n            {\n                GotoAddr = goto_addr - base_display_addr;\n                HighlightMin = HighlightMax = (size_t)-1;\n            }\n        }\n\n        if (FontOptions) ImGui::PushFont(FontOptions);\n\n        if (GotoAddr != (size_t)-1)\n        {\n            if (GotoAddr < mem_size)\n            {\n                ImGui::BeginChild(\"##scrolling\");\n                ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + (GotoAddr / Cols) * ImGui::GetTextLineHeight());\n                ImGui::EndChild();\n                DataEditingAddr = DataPreviewAddr = GotoAddr;\n                DataEditingTakeFocus = true;\n            }\n            GotoAddr = (size_t)-1;\n        }\n\n        //if (MouseHovered)\n        //{\n        //    ImGui::SameLine();\n        //    ImGui::Text(\"Hovered: %p\", MouseHoveredAddr);\n        //}\n    }\n\n    void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr)\n    {\n        IM_UNUSED(base_display_addr);\n        ImU8* mem_data = (ImU8*)mem_data_void;\n        ImGuiStyle& style = ImGui::GetStyle();\n        ImGui::AlignTextToFramePadding();\n        ImGui::Text(\"Preview as:\");\n        ImGui::SameLine();\n        ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);\n\n        static const ImGuiDataType supported_data_types[] = { ImGuiDataType_S8, ImGuiDataType_U8, ImGuiDataType_S16, ImGuiDataType_U16, ImGuiDataType_S32, ImGuiDataType_U32, ImGuiDataType_S64, ImGuiDataType_U64, ImGuiDataType_Float, ImGuiDataType_Double };\n        if (ImGui::BeginCombo(\"##combo_type\", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest))\n        {\n            for (int n = 0; n < IM_ARRAYSIZE(supported_data_types); n++)\n            {\n                ImGuiDataType data_type = supported_data_types[n];\n                if (ImGui::Selectable(DataTypeGetDesc(data_type), PreviewDataType == data_type))\n                    PreviewDataType = data_type;\n            }\n            ImGui::EndCombo();\n        }\n        ImGui::SameLine();\n        ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x);\n        ImGui::Combo(\"##combo_endianness\", &PreviewEndianness, \"LE\\0BE\\0\\0\");\n\n        char buf[128] = \"\";\n        float x = s.GlyphWidth * 6.0f;\n        bool has_value = DataPreviewAddr != (size_t)-1;\n        if (FontOptions) ImGui::PopFont();\n        if (has_value)\n        DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Dec, buf, (size_t)IM_ARRAYSIZE(buf));\n        ImGui::Text(\"Dec\"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : \"N/A\");\n        if (has_value)\n        DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Hex, buf, (size_t)IM_ARRAYSIZE(buf));\n        ImGui::Text(\"Hex\"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : \"N/A\");\n        if (has_value)\n        DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Bin, buf, (size_t)IM_ARRAYSIZE(buf));\n        buf[IM_ARRAYSIZE(buf) - 1] = 0;\n        ImGui::Text(\"Bin\"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : \"N/A\");\n        if (FontOptions) ImGui::PushFont(FontOptions);\n    }\n\n    // Utilities for Data Preview (since we don't access imgui_internal.h)\n    // FIXME: This technically depends on ImGuiDataType order.\n    const char* DataTypeGetDesc(ImGuiDataType data_type) const\n    {\n        const char* descs[] = { \"Int8\", \"Uint8\", \"Int16\", \"Uint16\", \"Int32\", \"Uint32\", \"Int64\", \"Uint64\", \"Float\", \"Double\" };\n        IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(descs));\n        return descs[data_type];\n    }\n\n    size_t DataTypeGetSize(ImGuiDataType data_type) const\n    {\n        const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) };\n        IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(sizes));\n        return sizes[data_type];\n    }\n\n    const char* DataFormatGetDesc(DataFormat data_format) const\n    {\n        const char* descs[] = { \"Bin\", \"Dec\", \"Hex\" };\n        IM_ASSERT(data_format >= 0 && data_format < DataFormat_COUNT);\n        return descs[data_format];\n    }\n\n    bool IsBigEndian() const\n    {\n        uint16_t x = 1;\n        char c[2];\n        memcpy(c, &x, 2);\n        return c[0] != 0;\n    }\n\n    static void* EndiannessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian)\n    {\n        if (is_little_endian)\n        {\n            uint8_t* dst = (uint8_t*)_dst;\n            uint8_t* src = (uint8_t*)_src + s - 1;\n            for (int i = 0, n = (int)s; i < n; ++i)\n                memcpy(dst++, src--, 1);\n            return _dst;\n        }\n        else\n        {\n            return memcpy(_dst, _src, s);\n        }\n    }\n\n    static void* EndiannessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian)\n    {\n        if (is_little_endian)\n        {\n            return memcpy(_dst, _src, s);\n        }\n        else\n        {\n            uint8_t* dst = (uint8_t*)_dst;\n            uint8_t* src = (uint8_t*)_src + s - 1;\n            for (int i = 0, n = (int)s; i < n; ++i)\n                memcpy(dst++, src--, 1);\n            return _dst;\n        }\n    }\n\n    void* EndiannessCopy(void* dst, void* src, size_t size) const\n    {\n        static void* (*fp)(void*, void*, size_t, int) = nullptr;\n        if (fp == nullptr)\n            fp = IsBigEndian() ? EndiannessCopyBigEndian : EndiannessCopyLittleEndian;\n        return fp(dst, src, size, PreviewEndianness);\n    }\n\n    const char* FormatBinary(const uint8_t* buf, int width) const\n    {\n        IM_ASSERT(width <= 64);\n        size_t out_n = 0;\n        static char out_buf[64 + 8 + 1];\n        int n = width / 8;\n        for (int j = n - 1; j >= 0; --j)\n        {\n            for (int i = 0; i < 8; ++i)\n                out_buf[out_n++] = (buf[j] & (1 << (7 - i))) ? '1' : '0';\n            out_buf[out_n++] = ' ';\n        }\n        IM_ASSERT(out_n < IM_ARRAYSIZE(out_buf));\n        out_buf[out_n] = 0;\n        return out_buf;\n    }\n\n    // [Internal]\n    void DrawPreviewData(size_t addr, const ImU8* mem_data, size_t mem_size, ImGuiDataType data_type, DataFormat data_format, char* out_buf, size_t out_buf_size) const\n    {\n        uint8_t buf[8];\n        size_t elem_size = DataTypeGetSize(data_type);\n        size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size;\n        if (ReadFn)\n            for (int i = 0, n = (int)size; i < n; ++i)\n                buf[i] = ReadFn(mem_data, addr + i, UserData);\n        else\n            memcpy(buf, mem_data + addr, size);\n\n        if (data_format == DataFormat_Bin)\n        {\n            uint8_t binbuf[8];\n            EndiannessCopy(binbuf, buf, size);\n            ImSnprintf(out_buf, out_buf_size, \"%s\", FormatBinary(binbuf, (int)size * 8));\n            return;\n        }\n\n        out_buf[0] = 0;\n        switch (data_type)\n        {\n        case ImGuiDataType_S8:\n        {\n            int8_t data = 0;\n            EndiannessCopy(&data, buf, size);\n            if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, \"%hhd\", data); return; }\n            if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, \"0x%02x\", data & 0xFF); return; }\n            break;\n        }\n        case ImGuiDataType_U8:\n        {\n            uint8_t data = 0;\n            EndiannessCopy(&data, buf, size);\n            if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, \"%hhu\", data); return; }\n            if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, \"0x%02x\", data & 0XFF); return; }\n            break;\n        }\n        case ImGuiDataType_S16:\n        {\n            int16_t data = 0;\n            EndiannessCopy(&data, buf, size);\n            if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, \"%hd\", data); return; }\n            if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, \"0x%04x\", data & 0xFFFF); return; }\n            break;\n        }\n        case ImGuiDataType_U16:\n        {\n            uint16_t data = 0;\n            EndiannessCopy(&data, buf, size);\n            if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, \"%hu\", data); return; }\n            if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, \"0x%04x\", data & 0xFFFF); return; }\n            break;\n        }\n        case ImGuiDataType_S32:\n        {\n            int32_t data = 0;\n            EndiannessCopy(&data, buf, size);\n            if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, \"%d\", data); return; }\n            if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, \"0x%08x\", data); return; }\n            break;\n        }\n        case ImGuiDataType_U32:\n        {\n            uint32_t data = 0;\n            EndiannessCopy(&data, buf, size);\n            if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, \"%u\", data); return; }\n            if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, \"0x%08x\", data); return; }\n            break;\n        }\n        case ImGuiDataType_S64:\n        {\n            int64_t data = 0;\n            EndiannessCopy(&data, buf, size);\n            if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, \"%lld\", (long long)data); return; }\n            if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, \"0x%016llx\", (long long)data); return; }\n            break;\n        }\n        case ImGuiDataType_U64:\n        {\n            uint64_t data = 0;\n            EndiannessCopy(&data, buf, size);\n            if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, \"%llu\", (long long)data); return; }\n            if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, \"0x%016llx\", (long long)data); return; }\n            break;\n        }\n        case ImGuiDataType_Float:\n        {\n            float data = 0.0f;\n            EndiannessCopy(&data, buf, size);\n            if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, \"%f\", data); return; }\n            if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, \"%a\", data); return; }\n            break;\n        }\n        case ImGuiDataType_Double:\n        {\n            double data = 0.0;\n            EndiannessCopy(&data, buf, size);\n            if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, \"%f\", data); return; }\n            if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, \"%a\", data); return; }\n            break;\n        }\n        default:\n        case ImGuiDataType_COUNT:\n            break;\n        } // Switch\n        IM_ASSERT(0); // Shouldn't reach\n    }\n};\n\n#undef _PRISizeT\n#undef ImSnprintf\n\n#ifdef _MSC_VER\n#pragma warning (pop)\n#endif"
  },
  {
    "path": "frontend/ui/menubar.cpp",
    "content": "#include <filesystem>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n#include \"portable-file-dialogs.h\"\n\n#include \"ps2_elf.h\"\n#include \"ps2_iso9660.h\"\n\nnamespace iris {\n\nconst char* aspect_mode_names[] = {\n    \"Native\",\n    \"Stretch\",\n    \"Stretch (Keep aspect ratio)\",\n    \"Force 4:3 (NTSC)\",\n    \"Force 16:9 (Widescreen)\",\n    \"Force 5:4 (PAL)\",\n    \"Auto\"\n};\n\nconst char* renderer_names[] = {\n    \"Null\",\n    \"Software\",\n    \"Hardware (Vulkan)\"\n};\n\nconst char* fullscreen_names[] = {\n    \"Windowed\",\n    \"Fullscreen\"\n};\n\nconst char* rotation_names[] = {\n    \"0 degrees\",\n    \"90 degrees\",\n    \"180 degrees\",\n    \"270 degrees\"\n};\n\nint fullscreen_flags[] = {\n    0,\n    SDL_WINDOW_FULLSCREEN\n};\n\nvoid show_main_menubar(iris::instance* iris) {\n    using namespace ImGui;\n\n    PushFont(iris->font_icons);\n    PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 7.0));\n\n    if (BeginMainMenuBar()) {\n        ImVec2 size = GetWindowSize();\n\n        if (BeginMenu(\"Iris\")) {\n            if (MenuItem(ICON_MS_DRIVE_FILE_MOVE \" Open...\")) {\n                audio::mute(iris);\n\n                auto f = pfd::open_file(\"Select a file to load\", \"\", {\n                    \"All File Types (*.iso; *.bin; *.cue; *.chd; *.cso; *.zso; *.elf)\", \"*.iso *.bin *.cue *.chd *.cso *.zso *.elf\",\n                    \"Disc Images (*.iso; *.bin; *.cue; *.chd; *.cso; *.zso)\", \"*.iso *.bin *.cue *.chd *.cso *.zso\",\n                    \"CD Images (*.bin; *.cue; *.chd)\", \"*.bin *.cue *.chd\",\n                    \"DVD Images (*.iso; *.chd; *.cso; *.zso)\", \"*.iso *.chd *.cso *.zso\",\n                    \"ISO Files (*.iso)\", \"*.iso\",\n                    \"CUE Files (*.cue)\", \"*.cue\",\n                    \"BIN Files (*.bin)\", \"*.bin\",\n                    \"CHD Files (*.chd)\", \"*.chd\",\n                    \"CSO/ZSO Files (*.cso; *.zso)\", \"*.cso *.zso\",\n                    \"ELF Executables (*.elf)\", \"*.elf\",\n                    \"All Files (*.*)\", \"*\"\n                });\n\n                while (!f.ready());\n\n                audio::unmute(iris);\n\n                if (f.result().size()) {\n                    std::string path = f.result().at(0);\n\n                    if (path.size()) {\n                        if (open_file(iris, path)) {\n                            push_info(iris, \"Failed to open file: \" + path);\n                        } else {\n                            add_recent(iris, path, RECENT_TYPE_PS2);\n                        }\n                    }\n                }\n            }\n\n            if (BeginMenu(ICON_MS_HISTORY \" Open Recent\", iris->recents.size())) {\n                for (const auto& recent : iris->recents) {\n                    if (MenuItem(recent.path.c_str())) {\n                        if (recent.type == RECENT_TYPE_PS2) {\n                            if (open_file(iris, recent.path)) {\n                                push_info(iris, \"Failed to open file: \" + recent.path);\n                            } else {\n                                add_recent(iris, recent.path, recent.type);\n                            }\n                        } else {\n                            if (!emu::load_arcade(iris, recent.path)) {\n                                push_info(iris, \"Failed to boot arcade: \" + recent.path);\n                            } else {\n                                add_recent(iris, recent.path, RECENT_TYPE_ARCADE);\n                            }\n                        }\n                    }\n                }\n\n                Separator();\n\n                if (MenuItem(ICON_MS_DELETE_HISTORY \" Clear all recents\")) {\n                    iris->recents.clear();\n                }\n\n                // To-do: Use try_open_file\n                // if (MenuItem(\"Clear invalid recents\")) {\n                //     iris->recents.clear();\n                // }\n\n                // To-do\n                // if (MenuItem(\"Stop recents history\")) {\n                // }\n\n                ImGui::EndMenu();\n            }\n\n            if (MenuItem(ICON_MS_JOYSTICK \" Open Arcade...\")) {\n                audio::mute(iris);\n\n                auto f = pfd::select_folder(\"Select arcade game folder\", \"\", pfd::opt::none);\n\n                while (!f.ready());\n\n                audio::unmute(iris);\n\n                if (f.result().size()) {\n                    std::string path = f.result();\n\n                    if (path.size()) {\n                        if (!emu::load_arcade(iris, path)) {\n                            push_info(iris, \"Failed to boot arcade: \" + path);\n                        } else {\n                            add_recent(iris, path, RECENT_TYPE_ARCADE);\n                        }\n                    }\n                }\n            }\n\n            // if (MenuItem(ICON_MS_DRIVE_FILE_MOVE \" Load disc...\")) {\n            //     const char* patterns[3] = { \"*.iso\", \"*.bin\", \"*.cue\" };\n\n            //     const char* file = tinyfd_openFileDialog(\n            //         \"Select CD/DVD image\",\n            //         \"\",\n            //         3,\n            //         patterns,\n            //         \"Disc images\",\n            //         0\n            //     );\n\n            //     if (file) {\n            //         struct iso9660_state* iso = iso9660_open(file);\n\n            //         if (!iso) {\n            //             printf(\"iris: Couldn't open disc image \\\"%s\\\"\\n\", file);\n\n            //             exit(1);\n\n            //             return;\n            //         }\n\n            //         char* boot_file = iso9660_get_boot_path(iso);\n\n            //         if (!boot_file)\n            //             return;\n\n            //         // Temporarily disable window updates\n            //         struct gs_callback cb = *ps2_gs_get_callback(iris->ps2->gs, GS_EVENT_VBLANK);\n\n            //         ps2_gs_remove_callback(iris->ps2->gs, GS_EVENT_VBLANK);\n            //         ps2_boot_file(iris->ps2, boot_file);\n\n            //         // Re-enable window updates\n            //         ps2_gs_init_callback(iris->ps2->gs, GS_EVENT_VBLANK, cb.func, cb.udata);\n\n            //         ps2_cdvd_open(iris->ps2->cdvd, file);\n\n            //         iso9660_close(iso);\n\n            //         iris->loaded = file;\n            //     }\n            // }\n\n            // if (MenuItem(ICON_MS_DRAFT \" Load executable...\")) {\n            //     const char* patterns[3] = { \"*.elf\" };\n\n            //     const char* file = tinyfd_openFileDialog(\n            //         \"Select ELF executable\",\n            //         \"\",\n            //         1,\n            //         patterns,\n            //         \"ELF executables\",\n            //         0\n            //     );\n\n            //     if (file) {\n            //         std::string str(file);\n\n            //         str = \"host:\" + str;\n\n            //         // Temporarily disable window updates\n            //         struct gs_callback cb = *ps2_gs_get_callback(iris->ps2->gs, GS_EVENT_VBLANK);\n\n            //         ps2_gs_remove_callback(iris->ps2->gs, GS_EVENT_VBLANK);\n\n            //         ps2_boot_file(iris->ps2, str.c_str());\n\n            //         // ps2_elf_load(iris->ps2, file);\n\n            //         // Re-enable window updates\n            //         ps2_gs_init_callback(iris->ps2->gs, GS_EVENT_VBLANK, cb.func, cb.udata);\n\n            //         iris->loaded = file;\n            //     }\n            // }\n\n            Separator();\n\n            if (MenuItem(iris->pause ? ICON_MS_PLAY_ARROW \" Run\" : ICON_MS_PAUSE \" Pause\", \"Space\")) {\n                iris->pause = !iris->pause;\n            }\n\n            // To-do: Show confirm dialog maybe?\n            if (MenuItem(ICON_MS_REFRESH \" Reset\")) {\n                ps2_reset(iris->ps2);\n            }\n\n            if (MenuItem(ICON_MS_FOLDER \" Change disc...\")) {\n                audio::mute(iris);\n\n                auto f = pfd::open_file(\"Select CD/DVD image\", \"\", {\n                    \"Disc Images (*.iso; *.bin; *.cue; *.chd; *.cso; *.zso)\", \"*.iso *.bin *.cue *.chd *.cso *.zso\",\n                    \"CD Images (*.bin; *.cue; *.chd)\", \"*.bin *.cue *.chd\",\n                    \"DVD Images (*.iso; *.chd; *.cso; *.zso)\", \"*.iso *.chd *.cso *.zso\",\n                    \"ISO Files (*.iso)\", \"*.iso\",\n                    \"CUE Files (*.cue)\", \"*.cue\",\n                    \"BIN Files (*.bin)\", \"*.bin\",\n                    \"CHD Files (*.chd)\", \"*.chd\",\n                    \"CSO/ZSO Files (*.cso; *.zso)\", \"*.cso *.zso\",\n                    \"All Files (*.*)\", \"*\"\n                });\n\n                while (!f.ready());\n\n                audio::unmute(iris);\n\n                if (f.result().size()) {\n                    // 2-second delay to allow the disc to spin up\n                    if (!ps2_cdvd_open(iris->ps2->cdvd, f.result().at(0).c_str(), 38860800*2)) {\n                        iris->loaded = f.result().at(0);\n                    }\n                }\n            }\n\n            if (MenuItem(ICON_MS_EJECT \" Eject disc\")) {\n                iris->loaded = \"\";\n\n                ps2_cdvd_close(iris->ps2->cdvd);\n            }\n\n            ImGui::EndMenu();\n        }\n        if (BeginMenu(\"Settings\")) {\n            if (BeginMenu(ICON_MS_MONITOR \" Display\")) {\n                if (BeginMenu(ICON_MS_BRUSH \" Renderer\")) {\n                    for (int i = 0; i < 3; i++) {\n                        BeginDisabled(i == RENDERER_BACKEND_SOFTWARE);\n\n                        if (MenuItem(renderer_names[i], nullptr, i == iris->renderer_backend)) {\n                            render::switch_backend(iris, i);\n                        }\n\n                        EndDisabled();\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                if (BeginMenu(ICON_MS_CROP \" Scale\")) {\n                    for (int i = 2; i <= 6; i++) {\n                        char buf[16]; snprintf(buf, 16, \"%.1fx\", (float)i * 0.5f);\n\n                        if (MenuItem(buf, nullptr, ((float)i * 0.5f) == iris->scale)) {\n                            iris->scale = (float)i * 0.5f;\n\n                            // renderer_set_scale(iris->ctx, iris->scale);\n                        }\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                if (BeginMenu(ICON_MS_ASPECT_RATIO \" Aspect mode\")) {\n                    for (int i = 0; i < 7; i++) {\n                        if (MenuItem(aspect_mode_names[i], nullptr, iris->aspect_mode == i)) {\n                            iris->aspect_mode = i;\n\n                            // renderer_set_aspect_mode(iris->ctx, iris->aspect_mode);\n                        }\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                if (BeginMenu(ICON_MS_FILTER \" Scaling filter\")) {\n                    const char* filter_names[] = {\n                        \"Nearest\",\n                        \"Bilinear\",\n                        \"Cubic\"\n                    };\n\n                    for (int i = 0; i < 3; i++) {\n                        BeginDisabled(i == 2 && !iris->cubic_supported);\n                        if (MenuItem(filter_names[i], nullptr, iris->filter == i)) {\n                            iris->filter = i;\n                        }\n                        EndDisabled();\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                if (BeginMenu(ICON_MS_SCREEN_ROTATION \" Rotation\")) {\n                    const int normalized_angle = ((iris->angle % 360) + 360) % 360;\n                    const int rotation_index = normalized_angle / 90;\n\n                    for (int i = 0; i < 4; i++) {\n                        if (MenuItem(rotation_names[i], nullptr, rotation_index == i)) {\n                            iris->angle = i * 90;\n                        }\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                if (BeginMenu(ICON_MS_ASPECT_RATIO \" Window size\")) {\n                    const char* sizes[] = {\n                        \"640x480\",\n                        \"800x600\",\n                        \"960x720\",\n                        \"1024x768\",\n                        \"1280x720\",\n                        \"1280x960\"\n                    };\n\n                    int widths[] = {\n                        640, 800, 960, 1024, 1280, 1280\n                    };\n\n                    int heights[] = {\n                        480, 600, 720, 768, 720, 960\n                    };\n\n                    for (int i = 0; i < 6; i++) {\n                        bool selected = iris->window_width == widths[i] && iris->window_height == heights[i];\n\n                        if (MenuItem(sizes[i], nullptr, selected)) {\n                            iris->window_width = widths[i];\n                            iris->window_height = heights[i];\n\n                            SDL_SetWindowSize(iris->window, iris->window_width, iris->window_height + get_menubar_height(iris));\n                        }\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                if (MenuItem(ICON_MS_SPEED_2X \" Integer scaling\", nullptr, &iris->integer_scaling)) {\n                    // renderer_set_integer_scaling(iris->ctx, iris->integer_scaling);\n                }\n\n                MenuItem(ICON_MS_FLIP \" Flip horizontally\", nullptr, &iris->flip_x);\n                MenuItem(ICON_MS_FLIP \" Flip vertically\", nullptr, &iris->flip_y);\n\n                if (MenuItem(ICON_MS_FULLSCREEN \" Fullscreen\", \"F11\", &iris->fullscreen)) {\n                    SDL_SetWindowFullscreen(iris->window, iris->fullscreen);\n                }\n\n                if (MenuItem(ICON_MS_SYNC \" VSync\", nullptr, &iris->vsync)) {\n                    imgui::set_vsync(iris, iris->vsync);\n                    iris->swapchain_rebuild = true;\n                }\n\n                if (MenuItem(ICON_MS_IMAGE \" Enable shaders\", nullptr, &iris->enable_shaders)) {\n                    // renderer_set_shaders_enabled(iris->ctx, iris->enable_shaders);\n                }\n\n                ImGui::EndMenu();\n            }\n\n            if (BeginMenu(ICON_MS_MUSIC_NOTE \" Audio\")) {\n                PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0f);\n                AlignTextToFramePadding();\n\n                const char* icon = ICON_MS_VOLUME_UP;\n\n                if (iris->volume == 0.0f) {\n                    icon = ICON_MS_VOLUME_MUTE;\n                } else if (iris->volume <= 0.5f) {\n                    icon = ICON_MS_VOLUME_DOWN;\n                }\n\n                Text(icon); SameLine();\n                \n                SetNextItemWidth(100.0f);\n                SliderFloat(\"Volume\", &iris->volume, 0.0f, 1.0f, \"%.1f\");\n                PopStyleVar();\n \n                MenuItem(ICON_MS_VOLUME_OFF \" Mute\", nullptr, &iris->mute);\n                MenuItem(ICON_MS_MUSIC_OFF \" Mute ADMA\", nullptr, &iris->mute_adma);\n\n                ImGui::EndMenu();\n            }\n\n            if (MenuItem(ICON_MS_DOCK_TO_BOTTOM \" Show status bar\", nullptr, &iris->show_status_bar)) {\n                SDL_SetWindowSize(iris->window, iris->window_width, iris->window_height + get_menubar_height(iris));\n            }\n\n            if (MenuItem(ICON_MS_OPEN_IN_NEW \" Open data folder\")) {\n                SDL_OpenURL(iris->pref_path.c_str());\n            }\n\n            Separator();\n\n            if (MenuItem(ICON_MS_MANUFACTURING \" Settings...\")) {\n                iris->show_settings = true;\n            }\n\n            ImGui::EndMenu();\n        }\n        if (BeginMenu(\"Tools\")) {\n            if (MenuItem(ICON_MS_BUILD \" ImGui Demo\", NULL, &iris->show_imgui_demo));\n            if (MenuItem(ICON_MS_SEARCH \" Memory search\", NULL, &iris->show_memory_search));\n            if (MenuItem(ICON_MS_PHOTO_CAMERA \" Take screenshot...\", \"F9\")) {\n                audio::mute(iris);\n\n                std::string filename = input::get_default_screenshot_filename(iris);\n\n                auto f = pfd::save_file(\"Save screenshot\", filename, {\n                    \"PNG (*.png)\", \"*.png\",\n                    \"JPG (*.jpg)\", \"*.jpg\",\n                    \"BMP (*.bmp)\", \"*.bmp\",\n                    \"TGA (*.tga)\", \"*.tga\",\n                    \"All Files (*.*)\", \"*\"\n                });\n\n                while (!f.ready());\n\n                audio::unmute(iris);\n\n                if (f.result().size()) {\n                    input::save_screenshot(iris, f.result());\n                }\n            }\n\n            if (MenuItem(ICON_MS_SD_CARD \" Memory Card tool\")) {\n                iris->show_memory_card_tool = true;\n            }\n\n            ImGui::EndMenu();\n        }\n        if (BeginMenu(\"Debug\")) {\n            SeparatorText(\"EE\");\n            // if (BeginMenu(ICON_MS_BUG_REPORT \" EE\")) {\n                if (MenuItem(ICON_MS_SETTINGS \" Control##ee\", NULL, &iris->show_ee_control));\n                if (MenuItem(ICON_MS_EDIT_NOTE \" State##ee\", NULL, &iris->show_ee_state));\n                if (MenuItem(ICON_MS_TERMINAL \" Logs##ee\", NULL, &iris->show_ee_logs));\n                if (MenuItem(ICON_MS_BOLT \" Interrupts##ee\", NULL, &iris->show_ee_interrupts));\n\n                BeginDisabled(iris->symbols.empty());\n                if (MenuItem(ICON_MS_CODE \" Symbols##ee\", NULL, &iris->show_symbols));\n                EndDisabled();\n\n                if (MenuItem(ICON_MS_ACCOUNT_TREE \" Threads##ee\", NULL, &iris->show_threads));\n\n                // ImGui::EndMenu();\n            // }\n\n            SeparatorText(\"IOP\");\n            // if (BeginMenu(ICON_MS_BUG_REPORT \" IOP\")) {\n                if (MenuItem(ICON_MS_SETTINGS \" Control##iop\", NULL, &iris->show_iop_control));\n                if (MenuItem(ICON_MS_EDIT_NOTE \" State##iop\", NULL, &iris->show_iop_state));\n                if (MenuItem(ICON_MS_TERMINAL \" Logs##iop\", NULL, &iris->show_iop_logs));\n                if (MenuItem(ICON_MS_BOLT \" Interrupts##iop\", NULL, &iris->show_iop_interrupts));\n                if (MenuItem(ICON_MS_EXTENSION \" Modules##iop\", NULL, &iris->show_iop_modules));\n\n            //     ImGui::EndMenu();\n            // }\n\n            Separator();\n\n            if (MenuItem(ICON_MS_BUG_REPORT \" Breakpoints\", NULL, &iris->show_breakpoints));\n            if (MenuItem(ICON_MS_BRUSH \" GS debugger\", NULL, &iris->show_gs_debugger));\n            if (MenuItem(ICON_MS_MUSIC_NOTE \" SPU2 debugger\", NULL, &iris->show_spu2_debugger));\n            if (MenuItem(ICON_MS_MEMORY \" Memory viewer\", NULL, &iris->show_memory_viewer));\n            if (MenuItem(ICON_MS_VIEW_IN_AR \" VU disassembler\", NULL, &iris->show_vu_disassembler));\n            if (MenuItem(ICON_MS_GAMEPAD \" DualShock debugger\", NULL, &iris->show_pad_debugger));\n            if (MenuItem(ICON_MS_BUG_REPORT \" Performance overlay\", NULL, &iris->show_overlay));\n            if (MenuItem(ICON_MS_TERMINAL \" SYSMEM logs\", NULL, &iris->show_sysmem_logs));\n\n            Separator();\n\n            if (BeginMenu(ICON_MS_MORE_TIME \" Timescale\")) {\n                for (int i = 0; i < 9; i++) {\n                    char buf[16]; snprintf(buf, 16, \"%dx\", 1 << i);\n\n                    if (MenuItem(buf, nullptr, iris->timescale == (1 << i))) {\n                        iris->timescale = (1 << i);\n\n                        ps2_set_timescale(iris->ps2, iris->timescale);\n                    }\n                }\n\n                ImGui::EndMenu();\n            }\n\n            if (MenuItem(ICON_MS_SKIP_NEXT \" Skip FMVs\", NULL, &iris->skip_fmv)) {\n                printf(\"Skip FMVs: %d\\n\", iris->skip_fmv);\n                ee_set_fmv_skip(iris->ps2->ee, iris->skip_fmv);\n            }\n\n            if (MenuItem(ICON_MS_CLOSE \" Close all\")) {\n                iris->show_ee_control = false;\n                iris->show_ee_state = false;\n                iris->show_ee_logs = false;\n                iris->show_ee_interrupts = false;\n                iris->show_ee_dmac = false;\n                iris->show_iop_control = false;\n                iris->show_iop_state = false;\n                iris->show_iop_logs = false;\n                iris->show_iop_interrupts = false;\n                iris->show_iop_modules = false;\n                iris->show_iop_dma = false;\n                iris->show_gs_debugger = false;\n                iris->show_spu2_debugger = false;\n                iris->show_memory_viewer = false;\n                iris->show_memory_search = false;\n                iris->show_vu_disassembler = false;\n                iris->show_status_bar = false;\n                iris->show_breakpoints = false;\n                iris->show_threads = false;\n                iris->show_sysmem_logs = false;\n                iris->show_imgui_demo = false;\n                iris->show_overlay = false;\n            }\n            \n            ImGui::EndMenu();\n        }\n        if (BeginMenu(\"Help\")) {\n            if (MenuItem(ICON_MS_INFO \" About\")) {\n                iris->show_about_window = true;\n            }\n\n            if (MenuItem(ICON_MS_EXCLAMATION \" Report an issue\")) {\n                SDL_OpenURL(\"https://github.com/allkern/iris/issues/new\");\n            }\n\n            ImGui::EndMenu();\n        }\n\n        EndMainMenuBar();\n    }\n\n    PopStyleVar();\n    PopFont();\n}\n\n}\n"
  },
  {
    "path": "frontend/ui/modules.cpp",
    "content": "#include \"iris.hpp\"\n\n#include \"iop/hle/loadcore.h\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nstatic const char* sizing_combo_items[] = {\n    ICON_MS_FIT_WIDTH \" Fixed fit\",\n    ICON_MS_FULLSCREEN \" Fixed same\",\n    ICON_MS_FULLSCREEN \" Stretch prop\",\n    ICON_MS_FULLSCREEN \" Stretch same\"\n};\n\nstatic ImGuiTableFlags table_sizing_flags[] = {\n    ImGuiTableFlags_SizingFixedFit,\n    ImGuiTableFlags_SizingFixedSame,\n    ImGuiTableFlags_SizingStretchProp,\n    ImGuiTableFlags_SizingStretchSame\n};\n\nstatic int table_sizing_combo = 0;\nstatic int table_sizing = ImGuiTableFlags_SizingStretchProp;\n\nstatic inline void show_modules_table(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct iop_state* iop = iris->ps2->iop;\n\n    if (BeginTable(\"##iopmodules\", 4, ImGuiTableFlags_RowBg | table_sizing)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Name\");\n        TableSetupColumn(\"Text Start\");\n        TableSetupColumn(\"Data Start\");\n        TableSetupColumn(\"BSS Start\");\n        TableHeadersRow();\n        PopFont();\n\n        for (int i = 0; i < iop->module_count; i++) {\n            struct iop_module *mod = &iop->module_list[i];\n\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n            Text(\"%s\", mod->name);\n\n            // text section\n            TableSetColumnIndex(1);\n            \n            PushFont(iris->font_code);\n\n            uint32_t addr = mod->text_addr;\n            Text(\"0x%08x\", addr);\n\n            // data section\n            TableSetColumnIndex(2);\n            addr += mod->text_size;\n            Text(\"0x%08x\", addr);\n\n            // bss section\n            TableSetColumnIndex(3);\n            addr += mod->data_size;\n            Text(\"0x%08x\", addr);\n\n            PopFont();\n        }\n\n        EndTable();\n    }\n}\n\nstatic inline struct iop_module* find_iop_module(iris::instance* iris, uint32_t addr) {\n    struct iop_state* iop = iris->ps2->iop;\n\n    for (int i = 0; i < iop->module_count; i++) {\n        struct iop_module* mod = &iop->module_list[i];\n\n        if ((addr >= mod->text_addr) && (addr < (mod->text_addr + mod->text_size)))\n            return mod;\n    }\n\n    return NULL;\n}\n\nvoid show_iop_modules(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct iop_state* iop = iris->ps2->iop;\n\n    if (imgui::BeginEx(\"IOP Modules\", &iris->show_iop_modules, ImGuiWindowFlags_MenuBar)) {\n        if (BeginMenuBar()) {\n            if (BeginMenu(\"Settings\")) {\n                if (BeginMenu(ICON_MS_CROP \" Sizing\")) {\n                    for (int i = 0; i < 4; i++) {\n                        if (Selectable(sizing_combo_items[i], i == table_sizing_combo)) {\n                            table_sizing = table_sizing_flags[i];\n                            table_sizing_combo = i;\n                        }\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                ImGui::EndMenu();\n            }\n\n            EndMenuBar();\n        }\n\n        if (Button(ICON_MS_REFRESH)) {\n            refresh_module_list(iris->ps2->iop);\n        }\n\n        Separator();\n\n        if (BeginChild(\"##tablechild\", ImVec2(0, GetContentRegionAvail().y * 0.9))) {\n            show_modules_table(iris);\n\n            EndChild();\n        }\n\n        Separator();\n\n        const struct iop_module* mod = find_iop_module(iris, iop->pc);\n\n        if (!mod) {\n            Text(\"Current module: <unknown>\\n\");\n        } else {\n            Text(\"Current module: %s (%08x-%08x)\\n\", mod->name, mod->text_addr, mod->text_addr + mod->text_size);\n        }\n    }\n\n    End();\n}\n\n}\n"
  },
  {
    "path": "frontend/ui/overlay.cpp",
    "content": "#include <vector>\n#include <cmath>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\n#include \"implot.h\"\n\n#define MAX_SAMPLES 100\n\nnamespace iris {\n\nstd::vector <float> fps_history = { 0 };\nstd::vector <float> fps_history_avg = { 0 };\n\nImVec2 pos = ImVec2(10, 10);\nconst ImVec2 padding = ImVec2(5, 5);\nconst ImVec2 size = ImVec2(250, 100);\nconst float opacity = 0.75f;\n\nfloat max = 0.0;\n\nvoid update_overlay(iris::instance* iris) {\n    // if (fps_history.size() == MAX_SAMPLES) {\n    //     if (fps_history.front() >= max) {\n    //         max = 0.0;\n\n    //         for (int i = 1; i < MAX_SAMPLES; i++) {\n    //             if (fps_history[i] > max) {\n    //                 max = fps_history[i];\n    //             }\n    //         }\n    //     }\n\n    //     fps_history.pop_front();\n    // }\n\n    float sample = 1.0 / ImGui::GetIO().DeltaTime;\n\n    if (!iris->pause) {\n        if (fps_history.size() == MAX_SAMPLES)\n            fps_history.erase(fps_history.begin());\n\n        fps_history.push_back(sample);\n\n        if (fps_history_avg.size() == MAX_SAMPLES)\n            fps_history_avg.erase(fps_history_avg.begin());\n\n        fps_history_avg.push_back(std::roundf(ImGui::GetIO().Framerate));\n    }\n}\n\nvoid show_overlay(iris::instance* iris) {\n    using namespace ImGui;\n    using namespace ImPlot;\n\n    SetNextWindowBgAlpha(0.5f);\n\n    ImVec2 pos = ImVec2(10.0f, 10.0f + iris->menubar_height);\n\n    if (GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) {\n        pos.x = GetMainViewport()->Pos.x + GetMainViewport()->Size.x + 10.0f;\n        pos.y = GetMainViewport()->Pos.y;\n    }\n\n    SetNextWindowPos(pos, ImGuiCond_Always);\n    SetNextWindowViewport(0);\n\n    if (Begin(\"Overlay\", nullptr,\n        ImGuiWindowFlags_NoDecoration |\n        ImGuiWindowFlags_NoMove |\n        ImGuiWindowFlags_NoSavedSettings |\n        ImGuiWindowFlags_NoFocusOnAppearing |\n        ImGuiWindowFlags_NoNav |\n        ImGuiWindowFlags_NoDocking)) {\n        update_overlay(iris);\n\n        ImPlotFlags flags =\n            ImPlotFlags_NoTitle |\n            ImPlotFlags_NoLegend |\n            ImPlotFlags_NoMouseText |\n            ImPlotFlags_NoBoxSelect |\n            ImPlotFlags_NoFrame |\n            ImPlotFlags_NoMenus |\n            ImPlotFlags_CanvasOnly |\n            ImPlotFlags_NoInputs;\n\n        ImPlotAxisFlags axis_flags =\n            ImPlotAxisFlags_NoTickLabels |\n            ImPlotAxisFlags_NoGridLines;\n\n        if (BeginPlot(\"##overlay_plot\", ImVec2(0, 0), flags)) {\n            SetupAxes(nullptr, nullptr, axis_flags, axis_flags);\n            SetupAxesLimits(0, (double)MAX_SAMPLES - 1, 0, 60, ImGuiCond_Always);\n            PlotLine(\"FPS\", fps_history.data(), (int)fps_history.size());\n\n            EndPlot();\n        }\n\n        renderer_stats* stats; // = renderer_get_debug_stats(iris->ctx);\n\n        PushFont(iris->font_black);\n        Text(\"%d fps\", (int)std::roundf(1.0 / ImGui::GetIO().DeltaTime));\n        PopFont();\n        // Text(\"Primitives: %d\", stats->primitives);\n        // Text(\"Texture uploads: %d\", stats->texture_uploads);\n        // Text(\"Texture blits: %d\", stats->texture_blits);\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/pad.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nvoid show_pad_debugger(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (imgui::BeginEx(\"DualShock 2\", &iris->show_pad_debugger)) {\n        if (BeginTabBar(\"##padtabbar\")) {\n            if (BeginTabItem(\"Slot 1\")) {\n                struct ds_state* ds = (struct ds_state*)iris->ps2->sio2->port[0].udata;\n\n                if (!ds) {\n                    Text(\"No controller connected\");\n                } else {\n                    Text(\"Buttons: %04x\", ds->buttons);\n                    Text(\"AxisRH: %04x\", ds->ax_right_x);\n                    Text(\"AxisLH: %04x\", ds->ax_left_x);\n                    Text(\"AxisRV: %04x\", ds->ax_right_y);\n                    Text(\"AxisLV: %04x\", ds->ax_left_y);\n                    Text(\"Config Mode: %d\", ds->config_mode);\n                    Text(\"Active Index: %d\", ds->act_index);\n                    Text(\"Mode Index: %d\", ds->mode_index);\n                    Text(\"Mode: %d\", ds->mode);\n                    Text(\"Vibration 0: %d\", ds->vibration[0]);\n                    Text(\"Vibration 1: %d\", ds->vibration[1]);\n                    Text(\"Mask 0: %d\", ds->mask[0]);\n                    Text(\"Mask 1: %d\", ds->mask[1]);\n                    Text(\"Lock: %d\", ds->lock);\n                }\n\n                EndTabItem();\n            }\n\n            if (BeginTabItem(\"Slot 2\")) {\n                struct ds_state* ds = (struct ds_state*)iris->ps2->sio2->port[1].udata;\n\n                if (!ds) {\n                    Text(\"No controller connected\");\n                } else {\n                    Text(\"Buttons: %04x\", ds->buttons);\n                    Text(\"AxisRH: %04x\", ds->ax_right_x);\n                    Text(\"AxisLH: %04x\", ds->ax_left_x);\n                    Text(\"AxisRV: %04x\", ds->ax_right_y);\n                    Text(\"AxisLV: %04x\", ds->ax_left_y);\n                    Text(\"Config Mode: %d\", ds->config_mode);\n                    Text(\"Active Index: %d\", ds->act_index);\n                    Text(\"Mode Index: %d\", ds->mode_index);\n                    Text(\"Mode: %d\", ds->mode);\n                    Text(\"Vibration 0: %d\", ds->vibration[0]);\n                    Text(\"Vibration 1: %d\", ds->vibration[1]);\n                    Text(\"Mask 0: %d\", ds->mask[0]);\n                    Text(\"Mask 1: %d\", ds->mask[1]);\n                    Text(\"Lock: %d\", ds->lock);\n                }\n\n                EndTabItem();\n            }\n\n            EndTabBar();\n        }\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/settings.cpp",
    "content": "#include <algorithm>\n#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n#include \"portable-file-dialogs.h\"\n\nnamespace iris {\n\nbool hovered = false;\nstd::string tooltip = \"\";\nint selected_settings = 0;\nint saved = 0;\n\nmapping* get_input_mapping(iris::instance* iris, int slot) {\n    if (iris->input_map[slot] == -1)\n        return nullptr;\n\n    return &iris->input_maps[iris->input_map[slot]];\n}\n\nconst char* get_input_name(input_action action) {\n    switch (action) {\n        case IRIS_DS_BT_SELECT: return \"Select\";\n        case IRIS_DS_BT_L3: return \"L3\";\n        case IRIS_DS_BT_R3: return \"R3\";\n        case IRIS_DS_BT_START: return \"Start\";\n        case IRIS_DS_BT_UP: return \"D-pad Up\";\n        case IRIS_DS_BT_RIGHT: return \"D-pad Right\";\n        case IRIS_DS_BT_DOWN: return \"D-pad Down\";\n        case IRIS_DS_BT_LEFT: return \"D-pad Left\";\n        case IRIS_DS_BT_L2: return \"L2\";\n        case IRIS_DS_BT_R2: return \"R2\";\n        case IRIS_DS_BT_L1: return \"L1\";\n        case IRIS_DS_BT_R1: return \"R1\";\n        case IRIS_DS_BT_TRIANGLE: return \"Triangle\";\n        case IRIS_DS_BT_CIRCLE: return \"Circle\";\n        case IRIS_DS_BT_CROSS: return \"Cross\";\n        case IRIS_DS_BT_SQUARE: return \"Square\";\n        case IRIS_DS_BT_ANALOG: return \"Analog\";\n        case IRIS_DS_AX_RIGHTV_POS: return \"Right Stick Vertical+\";\n        case IRIS_DS_AX_RIGHTV_NEG: return \"Right Stick Vertical-\";\n        case IRIS_DS_AX_RIGHTH_POS: return \"Right Stick Horizontal+\";\n        case IRIS_DS_AX_RIGHTH_NEG: return \"Right Stick Horizontal-\";\n        case IRIS_DS_AX_LEFTV_POS: return \"Left Stick Vertical+\";\n        case IRIS_DS_AX_LEFTV_NEG: return \"Left Stick Vertical-\";\n        case IRIS_DS_AX_LEFTH_POS: return \"Left Stick Horizontal+\";\n        case IRIS_DS_AX_LEFTH_NEG: return \"Left Stick Horizontal-\";\n\n        case IRIS_S14X_SW_DOWN: return \"System 147/148 Down\";\n        case IRIS_S14X_SW_UP: return \"System 147/148 Up\";\n        case IRIS_S14X_SW_ENTER: return \"System 147/148 Enter\";\n        case IRIS_S14X_SW_TEST: return \"System 147/148 Test\";\n        case IRIS_S14X_SW_SERVICE: return \"System 147/148 Service\";\n        case IRIS_S14X_SW_P1_START: return \"System 147/148 P1 Start\";\n        case IRIS_S14X_SW_P2_START: return \"System 147/148 P2 Start\";\n        case IRIS_S14X_SW_P3_START: return \"System 147/148 P3 Start\";\n        case IRIS_S14X_SW_P4_START: return \"System 147/148 P4 Start\";\n    }\n\n    return \"\";\n}\n\nstd::string get_event_name(const input_event& event) {\n    std::string name;\n\n    switch (event.type) {\n        case IRIS_EVENT_KEYBOARD: {\n            SDL_Keycode keycode = static_cast<SDL_Keycode>(event.id);\n\n            name = SDL_GetKeyName(keycode & 0xf0000fff);\n\n            // Append modifier names\n            if ((keycode >> 12) & SDL_KMOD_LSHIFT) name = \"Left Shift + \" + name;\n            if ((keycode >> 12) & SDL_KMOD_RSHIFT) name = \"Right Shift + \" + name;\n            if ((keycode >> 12) & SDL_KMOD_LCTRL) name = \"Left Ctrl + \" + name;\n            if ((keycode >> 12) & SDL_KMOD_RCTRL) name = \"Right Ctrl + \" + name;\n            if ((keycode >> 12) & SDL_KMOD_LALT) name = \"Left Alt + \" + name;\n            if ((keycode >> 12) & SDL_KMOD_RALT) name = \"Right Alt + \" + name;\n        } break;\n\n        case IRIS_EVENT_GAMEPAD_BUTTON: {\n            SDL_GamepadButton button = static_cast<SDL_GamepadButton>(event.id);\n\n            name = SDL_GetGamepadStringForButton(button);\n        } break;\n\n        case IRIS_EVENT_GAMEPAD_AXIS_POS: {\n            SDL_GamepadAxis axis = static_cast<SDL_GamepadAxis>(event.id);\n\n            name = SDL_GetGamepadStringForAxis(axis) + std::string(\"+\");\n        } break;\n\n        case IRIS_EVENT_GAMEPAD_AXIS_NEG: {\n            SDL_GamepadAxis axis = static_cast<SDL_GamepadAxis>(event.id);\n\n            name = SDL_GetGamepadStringForAxis(axis) + std::string(\"-\");\n        } break;\n\n        default: {\n            name = \"unknown\";\n        } break;\n    }\n\n    // Capitalize first letter\n    if (!name.empty()) {\n        name[0] = std::toupper(name[0]);\n    }\n\n    return name;\n}\n\nconst char* settings_renderer_names[] = {\n    \"Null\",\n    \"Software\",\n    \"Software (Threaded)\"\n};\n\nconst char* settings_aspect_mode_names[] = {\n    \"Native\",\n    \"Stretch\",\n    \"Stretch (Keep aspect ratio)\",\n    \"Force 4:3 (NTSC)\",\n    \"Force 16:9 (Widescreen)\",\n    \"Force 5:4 (PAL)\",\n    \"Auto\"\n};\n\nconst char* settings_fullscreen_names[] = {\n    \"Windowed\",\n    \"Fullscreen (Desktop)\",\n};\n\nconst char* settings_rotation_names[] = {\n    \"0 degrees\",\n    \"90 degrees\",\n    \"180 degrees\",\n    \"270 degrees\"\n};\n\nint settings_fullscreen_flags[] = {\n    0,\n    SDL_WINDOW_FULLSCREEN\n};\n\nconst char* settings_buttons[] = {\n    \" \" ICON_MS_DEPLOYED_CODE \"  System\",\n    \" \" ICON_MS_FOLDER \"  Paths\",\n    \" \" ICON_MS_MONITOR \"  Graphics\",\n    \" \" ICON_MS_BRUSH \"  Shaders\",\n    \" \" ICON_MS_STADIA_CONTROLLER \"  Input\",\n    \" \" ICON_MS_SD_CARD \"  Memory cards\",\n    \" \" ICON_MS_MORE_HORIZ \"  Misc.\",\n    nullptr\n};\n\nconst char* system_names[] = {\n    \"Auto\",\n    \"Retail (Fat)\",\n    \"Retail (Slim)\",\n    \"PSX DESR\",\n    \"TEST unit (DTL-H)\",\n    \"TOOL unit (DTL-T)\",\n    \"Konami Python\",\n    \"Konami Python 2\",\n    \"Namco System 147\",\n    \"Namco System 148\",\n    \"Namco System 246\",\n    \"Namco System 256\"\n};\n\nconst char* mechacon_model_names[] = {\n    \"SPC970\",\n    \"Dragon\"\n};\n\nvoid show_system_settings(iris::instance* iris) {\n    using namespace ImGui;\n\n    Text(\"Model\");\n\n    if (BeginCombo(\"##combo\", system_names[iris->system])) {\n        for (int i = 0; i < IM_ARRAYSIZE(system_names); i++) {\n            if (Selectable(system_names[i], i == iris->system)) {\n                iris->system = i;\n\n                ps2_set_system(iris->ps2, i);\n            }\n        }\n\n        EndCombo();\n    }\n\n    if (BeginTable(\"##specs-table\", 2, ImGuiTableFlags_SizingFixedSame)) {\n        TableNextRow();\n\n        if (iris->system == 0) {\n            TableSetColumnIndex(0);\n            TextDisabled(\"Detected system\");\n            TableSetColumnIndex(1);\n            Text(\"%s\", system_names[iris->ps2->detected_system]);\n            TableNextRow();\n        }\n\n        TableSetColumnIndex(0);\n        TextDisabled(\"Main RAM\");\n        TableSetColumnIndex(1);\n        Text(\"%d MB\", iris->ps2->ee_ram->size / (1024 * 1024));\n\n        TableNextRow();\n        TableSetColumnIndex(0);\n        TextDisabled(\"IOP RAM\");\n        TableSetColumnIndex(1);\n        Text(\"%d MB\", iris->ps2->iop_ram->size / (1024 * 1024));\n\n        TableNextRow();\n        TableSetColumnIndex(0);\n        TextDisabled(\"MechaCon Model\");\n        TableSetColumnIndex(1);\n        Text(\"%s\", mechacon_model_names[iris->ps2->cdvd->mechacon_model]);\n\n        EndTable();\n    }\n\n    Text(\"\\nTimescale\");\n\n    char buf[16];\n\n    sprintf(buf, \"%dx\", iris->timescale);\n\n    if (BeginCombo(\"##timescale\", buf)) {\n        for (int i = 0; i < 9; i++) {\n            char buf[16]; snprintf(buf, 16, \"%dx\", 1 << i);\n\n            if (Selectable(buf, iris->timescale == (1 << i))) {\n                iris->timescale = (1 << i);\n\n                ps2_set_timescale(iris->ps2, iris->timescale);\n            }\n        }\n\n        EndCombo();\n    }\n\n    if (BeginTable(\"##effective-clock\", 2, ImGuiTableFlags_SizingFixedSame)) {\n        TableNextRow();\n\n        TableSetColumnIndex(0);\n        TextDisabled(\"Effective frequency\");\n        TableSetColumnIndex(1);\n        Text(\"%.3f MHz\", 294.912f / iris->timescale);\n\n        EndTable();\n    }\n\n    SeparatorText(\"Network\");\n\n    // To-do: Improve MAC address input by using a single text input\n    //        that fills in the colons automatically\n\n    Text(\"MAC Address\");\n\n    PushFont(iris->font_code);\n\n    float w = CalcTextSize(\"FFFFFFFFFFFF\").x;\n\n    SetNextItemWidth(w * 2.0);\n\n    if (InputScalarN(\"##macaddress\", ImGuiDataType_U8, iris->mac_address, 6, nullptr, nullptr, \"%02X\", ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) {\n        ps2_set_mac_address(iris->ps2, iris->mac_address);\n    } SameLine();\n\n    PopFont();\n\n    if (Button(ICON_MS_REFRESH \"##macaddress\")) {\n        ps2_set_mac_address(iris->ps2, iris->mac_address);\n    }\n\n    SeparatorText(\"Misc.\");\n\n    PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);\n    Checkbox(\"Start games automatically\", &iris->autostart);\n    Checkbox(\"Skip FMVs\", &iris->skip_fmv);\n    PopStyleVar();\n}\n\nconst char* ssaa_names[] = {\n    \"Disabled\",\n    \"2x\",\n    \"4x\",\n    \"8x\",\n    \"16x\"\n};\n\nvoid show_hardware_renderer_settings(iris::instance* iris) {\n    using namespace ImGui;\n\n    Text(\"SSAA\");\n\n    if (BeginCombo(\"##ssaa\", ssaa_names[iris->hardware_backend_config.super_sampling])) {\n        for (int i = 0; i < IM_ARRAYSIZE(ssaa_names); i++) {\n            if (Selectable(ssaa_names[i], iris->hardware_backend_config.super_sampling == i)) {\n                iris->hardware_backend_config.super_sampling = i;\n\n                if (i != 0) {\n                    iris->hardware_backend_config.force_progressive = true;\n                }\n\n                render::refresh(iris);\n            }\n        }\n\n        EndCombo();\n    }\n\n    PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);\n    // BeginDisabled(iris->hardware_backend_config.super_sampling != 0);\n    if (Checkbox(\" Force progressive scan\", &iris->hardware_backend_config.force_progressive)) {\n        render::refresh(iris);\n    }\n    // EndDisabled();\n\n    if (Checkbox(\" Overscan\", &iris->hardware_backend_config.overscan)) {\n        render::refresh(iris);\n    }\n    PopStyleVar();\n\n    SeparatorText(\"Advanced\");\n\n    PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);\n    if (Checkbox(\" CRTC Offsets\", &iris->hardware_backend_config.crtc_offsets)) {\n        render::refresh(iris);\n    }\n\n    if (Checkbox(\" Disable Mipmaps\", &iris->hardware_backend_config.disable_mipmaps)) {\n        render::refresh(iris);\n    }\n\n    if (Checkbox(\" Unsynced Readbacks\", &iris->hardware_backend_config.unsynced_readbacks)) {\n        render::refresh(iris);\n    }\n\n    if (Checkbox(\" Backbuffer Promotion\", &iris->hardware_backend_config.backbuffer_promotion)) {\n        render::refresh(iris);\n    }\n\n    if (Checkbox(\" Allow Blend Demote\", &iris->hardware_backend_config.allow_blend_demote)) {\n        render::refresh(iris);\n    }\n    PopStyleVar();\n}\n\nvoid show_graphics_settings(iris::instance* iris) {\n    using namespace ImGui;\n\n    static const char* settings_renderer_names[] = {\n        \"Null\",\n        \"Software\",\n        \"Hardware\"\n    };\n\n    Text(\"Renderer\");\n\n    if (BeginCombo(\"##renderer\", settings_renderer_names[iris->renderer_backend], ImGuiComboFlags_HeightSmall)) {\n        for (int i = 0; i < 3; i++) {\n            BeginDisabled(i == RENDERER_BACKEND_SOFTWARE);\n\n            if (Selectable(settings_renderer_names[i], i == iris->renderer_backend)) {\n                render::switch_backend(iris, i);\n            }\n\n            EndDisabled();\n        }\n\n        EndCombo();\n    }\n\n    Text(\"Aspect mode\");\n\n    if (BeginCombo(\"##aspectmode\", settings_aspect_mode_names[iris->aspect_mode])) {\n        for (int i = 0; i < 7; i++) {\n            if (Selectable(settings_aspect_mode_names[i], iris->aspect_mode == i)) {\n                iris->aspect_mode = i;\n            }\n        }\n\n        EndCombo();\n    }\n\n    BeginDisabled(\n        iris->aspect_mode == RENDER_ASPECT_AUTO ||\n        iris->aspect_mode == RENDER_ASPECT_STRETCH ||\n        iris->aspect_mode == RENDER_ASPECT_STRETCH_KEEP\n    );\n\n    Text(\"Scale\");\n\n    char buf[16]; snprintf(buf, 16, \"%.1fx\", (float)iris->scale);\n\n    if (BeginCombo(\"##scale\", buf, ImGuiComboFlags_HeightSmall)) {\n        for (int i = 2; i <= 6; i++) {\n            snprintf(buf, 16, \"%.1fx\", (float)i * 0.5f);\n\n            if (Selectable(buf, ((float)i * 0.5f) == iris->scale)) {\n                iris->scale = (float)i * 0.5f;\n            }\n        }\n\n        EndCombo();\n    }\n\n    EndDisabled();\n\n    Text(\"Scaling\");\n\n    const char* filter_names[] = {\n        \"Nearest\",\n        \"Bilinear\",\n        \"Cubic\"\n    };\n\n    if (BeginCombo(\"##scalingfilter\", filter_names[iris->filter])) {\n        for (int i = 0; i < 3; i++) {\n            BeginDisabled(i == 2 && !iris->cubic_supported);\n            if (Selectable(filter_names[i], iris->filter == i)) {\n                iris->filter = i;\n            }\n            EndDisabled();\n        }\n\n        EndCombo();\n    }\n\n    const int normalized_angle = ((iris->angle % 360) + 360) % 360;\n    const int rotation_index = normalized_angle / 90;\n\n    Text(\"Rotation\");\n\n    if (BeginCombo(\"##rotation\", settings_rotation_names[rotation_index])) {\n        for (int i = 0; i < 4; i++) {\n            if (Selectable(settings_rotation_names[i], rotation_index == i)) {\n                iris->angle = i * 90;\n            }\n        }\n\n        EndCombo();\n    }\n\n    Text(\"Window mode\");\n\n    if (BeginCombo(\"##windowmode\", settings_fullscreen_names[iris->fullscreen])) {\n        for (int i = 0; i < 2; i++) {\n            if (Selectable(settings_fullscreen_names[i], iris->fullscreen == i)) {\n                iris->fullscreen = i;\n\n                SDL_SetWindowFullscreen(iris->window, settings_fullscreen_flags[i]);\n            }\n        }\n\n        EndCombo();\n    }\n\n    SeparatorText(\"Misc.\");\n\n    PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);\n    if (Checkbox(\" VSync\", &iris->vsync)) {\n        imgui::set_vsync(iris, iris->vsync);\n        iris->swapchain_rebuild = true;\n    }\n    Checkbox(\" Integer scaling\", &iris->integer_scaling);\n    Checkbox(\" Flip horizontally\", &iris->flip_x);\n    Checkbox(\" Flip vertically\", &iris->flip_y);\n    PopStyleVar();\n\n    if (iris->renderer_backend == RENDERER_BACKEND_HARDWARE) {\n        SeparatorText(\"Renderer settings\");\n\n        show_hardware_renderer_settings(iris);\n    }\n\n    SeparatorText(\"Vulkan settings\");\n\n    Text(\"GPU\");\n\n    static bool changed = false;\n    const char* hint;\n    const auto& selected_device = iris->vulkan_gpus[iris->vulkan_selected_device_index];\n\n    if (iris->vulkan_physical_device < 0) {\n        hint = \"Auto\";\n    } else {\n        hint = iris->vulkan_gpus[iris->vulkan_physical_device].name.c_str();\n    }\n\n    if (changed) {\n        SameLine();\n        TextColored(ImVec4(211.0/255.0, 167.0/255.0, 30.0/255.0, 1.0), ICON_MS_WARNING \" Restart the emulator to apply these changes\");\n    }\n\n    PushStyleVarY(ImGuiStyleVar_ItemSpacing, 5.0F);\n\n    if (BeginCombo(\"##gpu\", hint)) {\n        if (Selectable(\"Auto\", iris->vulkan_physical_device < 0)) {\n            iris->vulkan_physical_device = -1;\n        }\n\n        for (int i = 0; i < iris->vulkan_gpus.size(); i++) {\n            const auto& device = iris->vulkan_gpus[i];\n\n            std::string name = device.name;\n\n            if (device.device == selected_device.device) {\n                name += \" (Current)\";\n            }\n\n            if (Selectable(name.c_str(), device.device == selected_device.device)) {\n                changed = iris->vulkan_physical_device != i;\n\n                iris->vulkan_physical_device = i;\n            }\n        }\n\n        EndCombo();\n    }\n\n    PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);\n    if (Checkbox(\" Enable validation layers\", &iris->vulkan_enable_validation_layers)) {\n        changed = true;\n    }\n    PopStyleVar(2);\n}\n\nvoid show_controller_slot(iris::instance* iris, int slot) {\n    using namespace ImGui;\n\n    char label[9] = \"Slot #\";\n\n    label[5] = '1' + slot;\n\n    ImVec4 col = GetStyleColorVec4(iris->ds[slot] ? ImGuiCol_Text : ImGuiCol_TextDisabled);\n\n    col.w = 1.0;\n\n    if (BeginChild(label, ImVec2(GetContentRegionAvail().x / 2.0 - 10.0, 50 * iris->ui_scale))) {\n        Text(\"Controller\");\n\n        std::string controller_name = \"None\";\n\n        if (iris->ds[slot]) {\n            controller_name = \"DualShock 2\";\n        }\n\n        float avail_width = GetContentRegionAvail().x;\n\n        SetNextItemWidth(avail_width);\n\n        if (BeginCombo(\"##controller\", controller_name.c_str())) {\n            if (Selectable(\"None\")) {\n                if (iris->ds[slot]) {\n                    ps2_sio2_detach_device(iris->ps2->sio2, slot);\n\n                    iris->ds[slot] = nullptr;\n                }\n            }\n\n            if (Selectable(\"DualShock 2\")) {\n                if (!iris->ds[slot]) {\n                    iris->ds[slot] = ds_attach(iris->ps2->sio2, slot);\n                }\n            }\n\n            EndCombo();\n        }\n    } EndChild(); SameLine(0.0, 10.0);\n\n    if (BeginChild((std::string(label) + \"##icon\").c_str(), ImVec2(0, 50 * iris->ui_scale))) {\n        BeginDisabled(!iris->ds[slot]);\n\n        float avail_width = GetContentRegionAvail().x;\n\n        Text(\"Input device\");\n\n        std::string name = \"None\";\n\n        if (!iris->input_devices[slot]) {\n            name = \"None\";\n        } else if (iris->input_devices[slot]->get_type() == 0) {\n            name = \"Keyboard\";\n        } else if (iris->input_devices[slot]->get_type() == 1) {\n            gamepad_device* gp = static_cast<gamepad_device*>(iris->input_devices[slot]);\n\n            name = SDL_GetGamepadNameForID(gp->get_id());\n        }\n\n        SetNextItemWidth(avail_width);\n\n        if (BeginCombo(\"##devicetype\", name.c_str())) {\n            if (Selectable(\"None\")) {\n                if (iris->input_devices[slot]) {\n                    delete iris->input_devices[slot];\n\n                    iris->input_devices[slot] = nullptr;\n                }\n            }\n\n            if (Selectable(\"Keyboard\")) {\n                if (iris->input_devices[slot]) {\n                    delete iris->input_devices[slot];\n\n                    iris->input_devices[slot] = nullptr;\n                }\n\n                iris->input_devices[slot] = new keyboard_device();\n                iris->input_devices[slot]->set_slot(slot);\n\n                if (iris->input_map[slot] <= 1) {\n                    iris->input_map[slot] = 0;\n                }\n            }\n\n            for (auto gamepad : iris->gamepads) {\n                if (Selectable(SDL_GetGamepadNameForID(gamepad.first))) {\n                    if (iris->input_devices[slot]) {\n                        delete iris->input_devices[slot];\n\n                        iris->input_devices[slot] = nullptr;\n                    }\n\n                    iris->input_devices[slot] = new gamepad_device(gamepad.first);\n                    iris->input_devices[slot]->set_slot(slot);\n\n                    if (iris->input_map[slot] <= 1) {\n                        iris->input_map[slot] = 1;\n                    }\n                }\n            }\n\n            EndCombo();\n        }\n\n        EndDisabled();\n    } EndChild();\n\n    InvisibleButton(\"##slot0\", ImVec2(10, 10));\n\n    texture* tex = &iris->dualshock2_icon;\n\n    float width = 250.0f;\n    float height = (tex->height * width) / tex->width;\n\n    SetCursorPosX((GetContentRegionAvail().x / 2.0) - (width / 2.0));\n\n    Image(\n        (ImTextureID)(intptr_t)tex->descriptor_set,\n        ImVec2(width, height),\n        ImVec2(0, 0), ImVec2(1, 1),\n        col,\n        ImVec4(0.0, 0.0, 0.0, 0.0)\n    );\n\n    InvisibleButton(\"##pad1\", ImVec2(10, 10));\n\n    Text(\"Mapping\");\n\n    SetNextItemWidth(GetContentRegionAvail().x / 2.0 - 10.0);\n\n    mapping* mapping = get_input_mapping(iris, slot);\n\n    if (BeginCombo(\"##mapping\", mapping ? mapping->name.c_str() : \"None\")) {\n        if (Selectable(\"None\", mapping == nullptr)) {\n            iris->input_map[slot] = -1;\n        }\n\n        int i = 0;\n\n        for (auto& map : iris->input_maps) {\n            if (Selectable(map.name.c_str(), mapping == &map)) {\n                iris->input_map[slot] = i;\n            }\n\n            i++;\n        }\n\n        EndCombo();\n    }\n}\n\nbool event_is_mod_key(const input_event& event) {\n    if (event.type != IRIS_EVENT_KEYBOARD) {\n        return false;\n    }\n\n    SDL_Keycode keycode = static_cast<SDL_Keycode>(event.id);\n\n    return (keycode & 0xf0000fff) == SDLK_LSHIFT ||\n           (keycode & 0xf0000fff) == SDLK_RSHIFT ||\n           (keycode & 0xf0000fff) == SDLK_LCTRL ||\n           (keycode & 0xf0000fff) == SDLK_RCTRL ||\n           (keycode & 0xf0000fff) == SDLK_LALT ||\n           (keycode & 0xf0000fff) == SDLK_RALT;\n}\n\nint selected_mapping = 0;\nbool waiting_for_input = false;\nuint64_t mapping_editing = 0;\n\nvoid show_mappings_editor(iris::instance* iris) {\n    using namespace ImGui;\n\n    static char buf[1024] = { 0 };\n    const char* hint = iris->gcdb_path.size() ? iris->gcdb_path.c_str() : \"Not configured (using default)\";\n\n    Text(\"Game controller DB\");\n\n    SetNextItemWidth(300);\n\n    if (InputTextWithHint(\"##gcdbinput\", hint, buf, 1024, ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_EnterReturnsTrue)) {\n        iris->gcdb_path = std::string(buf);\n\n        // To-do: Check return value\n        input::load_db_from_file(iris, iris->gcdb_path.c_str());\n    }\n\n    SameLine();\n\n    if (Button(ICON_MS_FOLDER \"##gcdbbtn\")) {\n        audio::mute(iris);\n\n        auto f = pfd::open_file(\"Select Game controller DB file\", \"\", {\n            \"Game controller DB (*.txt)\", \"*.txt\",\n            \"All Files (*.*)\", \"*\"\n        });\n\n        while (!f.ready());\n\n        audio::unmute(iris);\n\n        if (f.result().size()) {\n            strncpy(buf, f.result().at(0).c_str(), 1024);\n\n            iris->gcdb_path = std::string(buf);\n\n            // To-do: Check return value\n            input::load_db_from_file(iris, iris->gcdb_path.c_str());\n        }\n    } SameLine();\n\n    if (Button(ICON_MS_CLEAR \"##gcdbclear\")) {\n        iris->gcdb_path = \"\";\n\n        memset(buf, 0, 1024);\n\n        input::load_db_default(iris);\n    }\n\n    Text(\"Mapping\");\n\n    if (BeginCombo(\"##mapping\", iris->input_maps[selected_mapping].name.c_str())) {\n        int i = 0;\n\n        for (auto& map : iris->input_maps) {\n            if (Selectable(map.name.c_str(), selected_mapping == i)) {\n                selected_mapping = i;\n            }\n\n            i++;\n        }\n\n        EndCombo();\n    } SameLine();\n\n    if (selected_mapping <= 1) {\n        if (Button(ICON_MS_REFRESH \" Default\")) {\n            input::init_default_mapping(iris, selected_mapping);\n        }\n    }\n\n    SetNextItemWidth(GetContentRegionAvail().x);\n\n    if (BeginTable(\"##mappingeditor\", 2, ImGuiTableFlags_SizingStretchProp)) {\n        TableSetupColumn(\"Input\");\n        TableSetupColumn(\"Mapping\");\n\n        std::vector<std::pair<uint64_t, input_action>> elems(\n            iris->input_maps[selected_mapping].map.forward_map().begin(),\n            iris->input_maps[selected_mapping].map.forward_map().end());\n\n        std::sort(elems.begin(), elems.end(), [](const std::pair<uint64_t, input_action>& a, const std::pair<uint64_t, input_action>& b) {\n            return a.second < b.second;\n        });\n\n        for (auto& entry : elems) {\n            TableNextRow();\n\n            std::string key_name = get_input_name(static_cast<input_action>(entry.second));\n\n            TableSetColumnIndex(0);\n            AlignTextToFramePadding();\n            Text(\"%s\", key_name.c_str());\n\n            TableSetColumnIndex(1);\n\n            input_event event;\n            event.u64 = entry.first;\n\n            std::string value_name = get_event_name(event) + \"##\" + key_name;\n\n            if (waiting_for_input && (mapping_editing == entry.first)) {\n                PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled));\n\n                if (Button(\"Press a key or button...\", ImVec2(GetContentRegionAvail().x, 0))) {\n                    waiting_for_input = false;\n                }\n\n                PopStyleColor();\n\n                if (iris->last_input_event_read == false && iris->last_input_event_value > 0.5f && !event_is_mod_key(iris->last_input_event)) {\n                    iris->last_input_event_read = true;\n\n                    waiting_for_input = false;\n                    mapping_editing = 0;\n\n                    auto event = iris->last_input_event;\n                    auto action = entry.second;\n\n                    // printf(\"Mapping input event %s (%llu) to action %s (%llu)\\n\",\n                    //     get_event_name(iris->last_input_event).c_str(),\n                    //     iris->last_input_event.u64,\n                    //     get_input_name(action),\n                    //     static_cast<uint64_t>(entry.second)\n                    // );\n\n                    auto* value_ptr = iris->input_maps[selected_mapping].map.get_value(event.u64);\n\n                    if (value_ptr != nullptr) {\n                        // Remove previous mapping for this input event\n                        auto value = *value_ptr;\n                        auto key = *iris->input_maps[selected_mapping].map.get_key(action);\n\n                        // printf(\"Removing previous mapping of event %s (%llu) to action %s (%llu)\\n\",\n                        //     get_event_name(event).c_str(),\n                        //     event.u64,\n                        //     get_input_name(value),\n                        //     static_cast<uint64_t>(value)\n                        // );\n\n                        iris->input_maps[selected_mapping].map.erase_by_key(event.u64);\n                        iris->input_maps[selected_mapping].map.erase_by_value(action);\n                        iris->input_maps[selected_mapping].map.insert(event.u64, action);\n                        iris->input_maps[selected_mapping].map.insert(key, value);\n                    } else {\n                        iris->input_maps[selected_mapping].map.erase_by_value(action);\n                        iris->input_maps[selected_mapping].map.insert(event.u64, action);\n                    }\n                }\n            } else {\n                if (Button(value_name.c_str(), ImVec2(GetContentRegionAvail().x, 0))) {\n                    iris->last_input_event_read = true;\n                    waiting_for_input = true;\n                    mapping_editing = entry.first;\n                }\n            }\n\n            // if (IsMouseDoubleClicked(ImGuiMouseButton_Left) && IsItemHovered()) {\n            //     iris->last_input_event_read = true;\n            //     waiting_for_input = true;\n            //     mapping_editing = entry.first;\n            // }\n        }\n\n        EndTable();\n    }\n}\n\nvoid show_input_settings(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (BeginTabBar(\"##inputtabs\")) {\n        if (BeginTabItem(\"Slot 1\")) {\n            show_controller_slot(iris, 0);\n\n            EndTabItem();\n        }\n\n        if (BeginTabItem(\"Slot 2\")) {\n            show_controller_slot(iris, 1);\n\n            EndTabItem();\n        }\n\n        if (BeginTabItem(\"Mappings\")) {\n            show_mappings_editor(iris);\n\n            EndTabItem();\n        }\n\n        EndTabBar();\n    }\n}\n\nvoid show_paths_settings(iris::instance* iris) {\n    using namespace ImGui;\n\n    static char buf[512];\n    static char dvd_buf[512];\n    static char rom2_buf[512];\n    static char nvram_buf[512];\n    static char flash_buf[512];\n\n    Text(\"BIOS (rom0)\");\n\n    if (IsItemHovered()) {\n        hovered = true;\n\n        tooltip = ICON_MS_INFO \" Select a BIOS file, this is required for the emulator to function properly\";\n    }\n\n    const char* bios_hint = iris->bios_path.size() ? iris->bios_path.c_str() : \"e.g. scph10000.bin\";\n    const char* rom1_hint = iris->rom1_path.size() ? iris->rom1_path.c_str() : \"Not configured\";\n    const char* rom2_hint = iris->rom2_path.size() ? iris->rom2_path.c_str() : \"Not configured\";\n    const char* nvram_hint = iris->nvram_path.size() ? iris->nvram_path.c_str() : \"Not configured\";\n    const char* flash_hint = iris->flash_path.size() ? iris->flash_path.c_str() : \"Not configured\";\n\n    SetNextItemWidth(300);\n\n    InputTextWithHint(\"##rom0\", bios_hint, buf, 512, ImGuiInputTextFlags_EscapeClearsAll);\n    SameLine();\n\n    if (Button(ICON_MS_FOLDER \"##rom0\")) {\n        audio::mute(iris);\n\n        auto f = pfd::open_file(\"Select BIOS file\", \"\", {\n            \"BIOS dumps (*.bin; *.rom0)\", \"*.bin *.rom0\",\n            \"All Files (*.*)\", \"*\"\n        });\n\n        while (!f.ready());\n\n        audio::unmute(iris);\n\n        if (f.result().size()) {\n            strncpy(buf, f.result().at(0).c_str(), 512);\n\n            ps2_load_bios(iris->ps2, buf);\n        }\n    }\n\n    if (BeginTable(\"##rom-info\", 2, ImGuiTableFlags_SizingFixedFit)) {\n        TableNextRow();\n        TableSetColumnIndex(0);\n        TextDisabled(\"Model\" \" \");\n        TableSetColumnIndex(1);\n        Text(\"%s\", iris->ps2->rom0_info.model);\n\n        TableNextRow();\n        TableSetColumnIndex(0);\n        TextDisabled(\"Version\" \" \");\n        TableSetColumnIndex(1);\n        Text(\"%s\", iris->ps2->rom0_info.version);\n\n        TableNextRow();\n        TableSetColumnIndex(0);\n        TextDisabled(\"Region\" \" \");\n        TableSetColumnIndex(1);\n        Text(\"%s\", iris->ps2->rom0_info.region);\n\n        TableNextRow();\n        TableSetColumnIndex(0);\n        TextDisabled(\"MD5 hash\" \" \");\n        TableSetColumnIndex(1);\n        Text(\"%s\", iris->ps2->rom0_info.md5hash); SameLine();\n        if (SmallButton(ICON_MS_CONTENT_COPY)) {\n            SDL_SetClipboardText(iris->ps2->rom0_info.md5hash);\n        }\n\n        EndTable();\n    }\n\n    Separator();\n\n    Text(\"DVD Player (rom1)\");\n\n    SetNextItemWidth(300);\n\n    InputTextWithHint(\"##rom1\", rom1_hint, dvd_buf, 512, ImGuiInputTextFlags_EscapeClearsAll);\n    SameLine();\n\n    if (Button(ICON_MS_FOLDER \"##rom1\")) {\n        audio::mute(iris);\n\n        auto f = pfd::open_file(\"Select DVD BIOS file\", \"\", {\n            \"DVD BIOS dumps (*.bin; *.rom1)\", \"*.bin *.rom1\",\n            \"All Files (*.*)\", \"*\"\n        });\n\n        while (!f.ready());\n\n        audio::unmute(iris);\n\n        if (f.result().size()) {\n            strncpy(dvd_buf, f.result().at(0).c_str(), 512);\n\n            ps2_load_rom1(iris->ps2, dvd_buf);\n        }\n    } SameLine();\n\n    if (Button(ICON_MS_CLEAR \"##rom1\")) {\n        iris->rom1_path = \"\";\n\n        memset(dvd_buf, 0, 512);\n    }\n\n    if (iris->rom1_path.size()) {\n        if (BeginTable(\"##rom1-info\", 2, ImGuiTableFlags_SizingFixedFit)) {\n            TableNextRow();\n            TableSetColumnIndex(0);\n            TextDisabled(\"Version\" \" \");\n            TableSetColumnIndex(1);\n            Text(\"%s\", iris->ps2->rom1_info.version);\n\n            TableNextRow();\n            TableSetColumnIndex(0);\n            TextDisabled(\"MD5 hash\" \" \");\n            TableSetColumnIndex(1);\n            Text(\"%s\", iris->ps2->rom1_info.md5hash); SameLine();\n            if (SmallButton(ICON_MS_CONTENT_COPY)) {\n                SDL_SetClipboardText(iris->ps2->rom1_info.md5hash);\n            }\n\n            EndTable();\n        }\n\n        Separator();\n    }\n\n    Text(\"Chinese extensions (rom2)\");\n\n    SetNextItemWidth(300);\n\n    InputTextWithHint(\"##rom2\", rom2_hint, rom2_buf, 512, ImGuiInputTextFlags_EscapeClearsAll);\n    SameLine();\n\n    if (Button(ICON_MS_FOLDER \"##rom2\")) {\n        audio::mute(iris);\n\n        auto f = pfd::open_file(\"Select ROM2 file\", \"\", {\n            \"ROM2 dumps (*.bin; *.rom2)\", \"*.bin *.rom2\",\n            \"All Files (*.*)\", \"*\"\n        });\n\n        while (!f.ready());\n\n        audio::unmute(iris);\n\n        if (f.result().size()) {\n            strncpy(rom2_buf, f.result().at(0).c_str(), 512);\n        }\n    } SameLine();\n\n    if (Button(ICON_MS_CLEAR \"##rom2\")) {\n        iris->rom2_path = \"\";\n\n        memset(rom2_buf, 0, 512);\n    } \n\n    Text(\"EEPROM memory (nvram)\");\n\n    SetNextItemWidth(300);\n\n    InputTextWithHint(\"##nvram\", nvram_hint, nvram_buf, 512, ImGuiInputTextFlags_EscapeClearsAll);\n    SameLine();\n\n    if (Button(ICON_MS_FOLDER \"##nvram\")) {\n        audio::mute(iris);\n\n        auto f = pfd::open_file(\"Select NVRAM file\", \"\", {\n            \"NVRAM dumps (*.nvm; *.bin)\", \"*.nvm *.bin\",\n            \"All Files (*.*)\", \"*\"\n        });\n\n        while (!f.ready());\n\n        audio::unmute(iris);\n\n        if (f.result().size()) {\n            strncpy(nvram_buf, f.result().at(0).c_str(), 512);\n        }\n    } SameLine();\n\n    if (Button(ICON_MS_CLEAR \"##nvram\")) {\n        iris->nvram_path = \"\";\n\n        memset(nvram_buf, 0, 512);\n    } \n\n    Text(\"Flash memory (xfrom)\");\n\n    SetNextItemWidth(300);\n\n    InputTextWithHint(\"##flash\", flash_hint, flash_buf, 512, ImGuiInputTextFlags_EscapeClearsAll);\n    SameLine();\n\n    if (Button(ICON_MS_FOLDER \"##flash\")) {\n        audio::mute(iris);\n\n        auto f = pfd::open_file(\"Select Flash/XFROM dump file\", \"\", {\n            \"XFROM dumps (*.bin)\", \"*.bin\",\n            \"All Files (*.*)\", \"*\"\n        });\n\n        while (!f.ready());\n\n        audio::unmute(iris);\n\n        if (f.result().size()) {\n            strncpy(flash_buf, f.result().at(0).c_str(), 512);\n        }\n    } SameLine();\n\n    if (Button(ICON_MS_CLEAR \"##xfrom\")) {\n        iris->flash_path = \"\";\n\n        memset(flash_buf, 0, 512);\n    } \n\n    if (Button(ICON_MS_SAVE \" Save\")) {\n        std::string bios_path = buf;\n        std::string rom1_path = dvd_buf;\n        std::string rom2_path = rom2_buf;\n        std::string flash_path = flash_buf;\n        std::string nvram_path = nvram_buf;\n\n        if (bios_path.size()) iris->bios_path = bios_path;\n        if (rom1_path.size()) iris->rom1_path = rom1_path;\n        if (rom2_path.size()) iris->rom2_path = rom2_path;\n        if (flash_path.size()) iris->flash_path = flash_path;\n        if (nvram_path.size()) iris->nvram_path = nvram_path;\n\n        saved = 1;\n    } SameLine();\n\n    if (saved) {\n        TextColored(ImVec4(211.0/255.0, 167.0/255.0, 30.0/255.0, 1.0), ICON_MS_WARNING \" Restart the emulator to apply these changes\");\n    }\n}\n\nstatic char slot0_buf[1024];\nstatic char slot1_buf[1024];\n\nvoid show_memory_card(iris::instance* iris, int slot) {\n    using namespace ImGui;\n\n    char label[9] = \"##mcard0\";\n\n    label[7] = '0' + slot;\n\n    if (BeginChild(label, ImVec2(GetContentRegionAvail().x / (slot ? 1.0 : 2.0) - 10.0, 0))) {\n        std::string& path = slot ? iris->mcd1_path : iris->mcd0_path;\n\n        ImVec4 col = GetStyleColorVec4(iris->mcd_slot_type[slot] ? ImGuiCol_Text : ImGuiCol_TextDisabled);\n\n        col.w = 1.0;\n\n        InvisibleButton(\"##pad0\", ImVec2(10, 10));\n\n        texture* tex = &iris->ps2_memory_card_icon;\n\n        if (iris->mcd_slot_type[slot] == 2) {\n            tex = &iris->ps1_memory_card_icon;\n        } else if (iris->mcd_slot_type[slot] == 3) {\n            tex = &iris->pocketstation_icon;\n        }\n\n        SetCursorPosX((GetContentRegionAvail().x / 2.0) - (tex->width / 2.0));\n\n        Image(\n            (ImTextureID)(intptr_t)tex->descriptor_set,\n            ImVec2(tex->width, tex->height),\n            ImVec2(0, 0), ImVec2(1, 1),\n            col,\n            ImVec4(0.0, 0.0, 0.0, 0.0)\n        );\n\n        InvisibleButton(\"##pad1\", ImVec2(10, 10));\n\n        if (path.size() && !iris->mcd_slot_type[slot]) {\n            TextColored(ImVec4(211.0/255.0, 167.0/255.0, 30.0/255.0, 1.0), ICON_MS_WARNING \" Check file\");\n\n            if (IsItemHovered(ImGuiHoveredFlags_DelayNormal)) {\n                if (BeginTooltip()) {\n                    Text(\"Please check files \");\n    \n                    EndTooltip();\n                }\n            }\n        }\n\n        PushFont(iris->font_heading);\n        Text(\"Slot %d\", slot+1);\n        PopFont();\n\n        char* buf = slot ? slot1_buf : slot0_buf;\n        const char* hint = path.size() ? path.c_str() : \"Not configured\";\n\n        char it_label[7] = \"##mcd0\";\n        char bt_label[10] = ICON_MS_FOLDER \"##mcd0\";\n        char ed_label[10];\n\n        snprintf(ed_label, 10, \"%s##mcd0\", iris->mcd_slot_type[slot] ? ICON_MS_ARROW_DOWNWARD : ICON_MS_ARROW_UPWARD);\n\n        it_label[5] = '0' + slot;\n        bt_label[8] = '0' + slot;\n        ed_label[8] = '0' + slot;\n\n        InputTextWithHint(it_label, hint, buf, 512, ImGuiInputTextFlags_EscapeClearsAll);\n        SameLine();\n\n        if (Button(bt_label)) {\n            audio::mute(iris);\n\n            auto f = pfd::open_file(\"Select Memory Card file for Slot 1\", iris->pref_path, {\n                \"Memory Card files (*.ps2; *.mcd; *.bin; *.psm; *.pocket)\", \"*.ps2 *.mcd *.bin *.psm *.pocket\",\n                \"All Files (*.*)\", \"*\"\n            });\n\n            while (!f.ready());\n\n            audio::unmute(iris);\n\n            if (f.result().size()) {\n                strncpy(buf, f.result().at(0).c_str(), 512);\n\n                path = f.result().at(0);\n\n                emu::attach_memory_card(iris, slot, path.c_str());\n            }\n        }\n\n        SameLine();\n\n        BeginDisabled((!iris->mcd_slot_type[slot]) && (!path.size()));\n\n        if (Button(ed_label)) {\n            if (iris->mcd_slot_type[slot]) {\n                emu::detach_memory_card(iris, slot);\n            } else {\n                emu::attach_memory_card(iris, slot, path.c_str());\n            }\n        }\n\n        EndDisabled();\n    } EndChild();\n}\n\nvoid show_memory_card_settings(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (Button(ICON_MS_EDIT \" Create memory cards...\")) {\n        // Launch memory card utility\n        iris->show_memory_card_tool = true;\n    }\n\n    Separator();\n\n    show_memory_card(iris, 0); SameLine(0.0, 10.0);\n    show_memory_card(iris, 1);\n}\n\nstatic const char* const theme_names[] = {\n    \"Granite\",\n    \"ImGui Dark\",\n    \"ImGui Light\",\n    \"ImGui Classic\",\n    \"Cherry\",\n    \"Source\"\n};\n\nstatic const char* const codeview_color_scheme_names[] = {\n    \"Solarized Dark\",\n    \"Solarized Light\",\n    \"One Dark Pro\",\n    \"Catppuccin Latte\",\n    \"Catppuccin Frappé\",\n    \"Catppuccin Macchiato\",\n    \"Catppuccin Mocha\"\n};\n\n#ifdef _WIN32\nstatic const char* titlebar_style_names[] = {\n    \"Default\",\n    \"Seamless\"\n};\n#endif\n\nvoid show_misc_settings(iris::instance* iris) {\n    using namespace ImGui;\n\n    SeparatorText(\"Style\");\n\n    Text(\"Theme\");\n\n    if (BeginCombo(\"##theme\", theme_names[iris->theme])) {\n        for (int i = 0; i < IM_ARRAYSIZE(theme_names); i++) {\n            if (Selectable(theme_names[i], iris->theme == i)) {\n                iris->theme = i;\n\n                imgui::set_theme(iris, i);\n                platform::apply_settings(iris);\n            }\n        }\n\n        EndCombo();\n    }\n\n    Text(\"Background color\");\n\n    ColorEdit3(\"##bgcolor\", (float*)&iris->clear_value.color);\n\n    Text(\"UI scale\");\n\n    DragFloat(\"##uiscale\", &iris->ui_scale, 0.05f, 0.5f, 1.5f, \"%.1f\");\n\n    GetStyle().FontScaleMain = iris->ui_scale;\n\n    PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);\n    Checkbox(\"Enable viewports\", &iris->imgui_enable_viewports); SameLine();\n    PopStyleVar();\n\n    TextDisabled(ICON_MS_WARNING \" Experimental feature, requires restart\");\n\n#ifdef _WIN32\n    Text(\"Titlebar style (Windows only)\");\n\n    if (BeginCombo(\"##titlebar_style\", titlebar_style_names[iris->windows_titlebar_style])) {\n        for (int i = 0; i < 2; i++) {\n            if (Selectable(titlebar_style_names[i], iris->windows_titlebar_style == i)) {\n                iris->windows_titlebar_style = i;\n\n                platform::apply_settings(iris);\n            }\n        }\n\n        EndCombo();\n    }\n\n    PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);\n\n    BeginDisabled(iris->windows_titlebar_style != IRIS_TITLEBAR_DEFAULT);\n    if (Checkbox(\" Immersive dark mode\", &iris->windows_dark_mode)) {\n        platform::apply_settings(iris);\n    }\n    EndDisabled();\n\n    if (Checkbox(\" Show window borders\", &iris->windows_enable_borders)) {\n        platform::apply_settings(iris);\n    }\n\n    PopStyleVar();\n#endif\n\n    SeparatorText(\"Codeview\");\n\n#define SCHEME(str, id) \\\n    if (Selectable(str, iris->codeview_color_scheme == id)) { \\\n        iris->codeview_color_scheme = id; \\\n        imgui::set_codeview_scheme(iris, id); \\\n    }\n\n    Text(\"Color scheme\");\n\n    if (BeginCombo(\"##codeview_color_scheme\", codeview_color_scheme_names[iris->codeview_color_scheme])) {\n        PushFont(iris->font_small);\n        TextDisabled(\"Dark\");\n        PopFont();\n\n        SCHEME(\"Solarized Dark\", IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_DARK);\n        SCHEME(\"One Dark Pro\", IRIS_CODEVIEW_COLOR_SCHEME_ONE_DARK_PRO);\n        SCHEME(\"Catppuccin Mocha\", IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MOCHA);\n        SCHEME(\"Catppuccin Macchiato\", IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MACCHIATO);\n        SCHEME(\"Catppuccin Frappé\", IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_FRAPPE);\n\n        PushFont(iris->font_small);\n        TextDisabled(\"Light\");\n        PopFont();\n\n        SCHEME(\"Solarized Light\", IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_LIGHT);\n        SCHEME(\"Catppuccin Latte\", IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_LATTE);\n\n        EndCombo();\n    }\n\n#undef SCHEME\n\n    static bool use_theme_background = !iris->codeview_use_theme_background;\n\n    PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);\n\n    if (Checkbox(\"Use scheme background\", &use_theme_background))  {\n        iris->codeview_use_theme_background = !use_theme_background;\n    }\n\n    PopStyleVar();\n\n    Text(\"Font scale\");\n\n    DragFloat(\"##codeview_font_scale\", &iris->codeview_font_scale, 0.05f, 0.75f, 1.5f, \"%.1f\");\n\n    SeparatorText(\"Screenshots\");\n\n    const char* format_names[] = {\n        \"PNG\",\n        \"BMP\",\n        \"JPG\",\n        \"TGA\"\n    };\n\n    const char* jpg_quality_names[] = {\n        \"Minimum\", // 1\n        \"Low\", // 25\n        \"Medium\", // 50\n        \"High\", // 90\n        \"Maximum\", // 100\n        \"Custom...\"\n    };\n\n    const char* mode_names[] = {\n        \"Internal\",\n        \"Display\"\n    };\n\n    Text(\"Format\");\n\n    if (BeginCombo(\"##screenshotformat\", format_names[iris->screenshot_format])) {\n        for (int i = 0; i < 4; i++) {\n            if (Selectable(format_names[i], iris->screenshot_format == i)) {\n                iris->screenshot_format = i;\n            }\n        }\n\n        EndCombo();\n    }\n\n    Text(\"Resolution mode\");\n\n    if (BeginCombo(\"##screenshotmode\", mode_names[iris->screenshot_mode])) {\n        for (int i = 0; i < 2; i++) {\n            if (Selectable(mode_names[i], iris->screenshot_mode == i)) {\n                iris->screenshot_mode = i;\n            }\n        }\n\n        EndCombo();\n    }\n\n    if (iris->screenshot_format == IRIS_SCREENSHOT_FORMAT_JPG) {\n        Text(\"JPG Quality\");\n\n        if (BeginCombo(\"##jpgquality\", jpg_quality_names[iris->screenshot_jpg_quality_mode])) {\n            for (int i = 0; i < 6; i++) {\n                if (Selectable(jpg_quality_names[i], iris->screenshot_jpg_quality_mode == i)) {\n                    iris->screenshot_jpg_quality_mode = i;\n                }\n            }\n\n            EndCombo();\n        }\n\n        if (iris->screenshot_jpg_quality_mode == IRIS_SCREENSHOT_JPG_QUALITY_CUSTOM) {\n            SliderInt(\"Quality##jpgqualitycustom\", &iris->screenshot_jpg_quality, 1, 100, \"%d\", ImGuiSliderFlags_AlwaysClamp);\n        }\n    }\n\n    PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F);\n    Checkbox(\" Include shader processing\", &iris->screenshot_shader_processing);\n    PopStyleVar();\n}\n\nconst char* builtin_shader_names[] = {\n    \"iris-ntsc-encoder\",\n    \"iris-ntsc-decoder\",\n    \"iris-ntsc-curvature\",\n    \"iris-ntsc-scanlines\",\n    \"iris-ntsc-noise\",\n};\n\nconst char* presets[] = {\n    \"NTSC codec\",\n    0\n};\n\nvoid show_shader_settings(iris::instance* iris) {\n    using namespace ImGui;\n\n    static const char* selected_shader = \"\";\n\n    PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0f);\n    Checkbox(\" Enable shaders\", &iris->enable_shaders);\n    PopStyleVar();\n\n    Separator();\n\n    Text(\"Add shader\");\n    if (BeginCombo(\"##combo\", selected_shader)) {\n        for (int i = 0; i < IM_ARRAYSIZE(builtin_shader_names); i++) {\n            if (Selectable(builtin_shader_names[i], selected_shader == builtin_shader_names[i])) {\n                selected_shader = builtin_shader_names[i];\n            }\n        }\n\n        EndCombo();\n    } SameLine();\n\n    if (Button(ICON_MS_ADD)) {\n        if (selected_shader && selected_shader[0]) {\n            std::string shader(selected_shader);\n\n            shaders::push(iris, selected_shader);\n        }\n    } SameLine();\n\n    if (Button(ICON_MS_REMOVE_SELECTION)) {\n        shaders::clear(iris);\n    }\n\n    // Text(\"Preset\");\n\n    // if (BeginCombo(\"##presets\", selected_shader)) {\n    //     for (int i = 0; i < 3; i++) {\n    //         if (Selectable(presets[i], selected_shader == builtin_shader_names[i])) {\n    //             selected_shader = builtin_shader_names[i];\n    //         }\n    //     }\n\n    //     EndCombo();\n    // }\n\n    if (BeginTable(\"##shaders\", 1, ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_RowBg)) {\n        for (int i = 0; i < shaders::count(iris); i++) {\n            TableNextRow();\n\n            char bypass[16];\n            char del[16];\n            char id[1024];\n\n            sprintf(bypass, \"%s##%d\", shaders::at(iris, i)->bypass ? ICON_MS_CHECK_BOX_OUTLINE_BLANK : ICON_MS_CHECK_BOX, i);\n            sprintf(del, ICON_MS_DELETE \"##%d\", i);\n            sprintf(id, \"%s##%d\", shaders::at(iris, i)->get_id().c_str(), i);\n\n            TableSetColumnIndex(0);\n            if (SmallButton(del)) {\n                iris->shader_passes.erase(iris->shader_passes.begin() + i);\n\n                break;\n            } SameLine();\n\n            if (SmallButton(bypass)) {\n                shaders::at(iris, i)->bypass = !shaders::at(iris, i)->bypass;\n            } SameLine();\n\n            Selectable(id, false, ImGuiSelectableFlags_SpanAllColumns);\n\n            if (BeginDragDropSource()) {\n                SetDragDropPayload(\"SHADER_DND_PAYLOAD\", &i, sizeof(int));\n\n                EndDragDropSource();\n            }\n\n            if (BeginDragDropTarget()) {\n                if (const ImGuiPayload* payload = AcceptDragDropPayload(\"SHADER_DND_PAYLOAD\")) {\n                    int src = *(int*)payload->Data;\n\n                    shaders::swap(iris, src, i);\n                }\n\n                EndDragDropTarget();\n            }\n        }\n\n        EndTable();\n    }\n}\n\nvoid show_settings(iris::instance* iris) {\n    using namespace ImGui;\n\n    hovered = false;\n\n    static ImGuiWindowFlags flags =\n        ImGuiWindowFlags_NoCollapse |\n        ImGuiWindowFlags_NoDocking;\n\n    SetNextWindowSize(ImVec2(675, 500), ImGuiCond_FirstUseEver);\n    PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(675, 500));\n\n    if (GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable && !GetIO().ConfigViewportsNoDecoration)\n        flags |= ImGuiWindowFlags_NoTitleBar;\n\n    if (Begin(\"Settings\", &iris->show_settings, flags)) {\n        PushStyleVarX(ImGuiStyleVar_ButtonTextAlign, 0.0);\n        PushStyleVarY(ImGuiStyleVar_ItemSpacing, 6.0);\n\n        if (BeginChild(\"##sidebar\", ImVec2(175, GetContentRegionAvail().y), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Borders)) {\n            for (int i = 0; settings_buttons[i]; i++) {\n                if (selected_settings == i) PushStyleColor(ImGuiCol_Button, GetStyle().Colors[ImGuiCol_ButtonHovered]);\n\n                bool pressed = Button(settings_buttons[i], ImVec2(175, 35));\n                \n                if (selected_settings == i) PopStyleColor();\n\n                if (pressed) {\n                    selected_settings = i;\n                }\n            }\n        } EndChild(); SameLine(0.0, 10.0);\n\n        PopStyleVar(2);\n\n        if (BeginChild(\"##content\", ImVec2(0, GetContentRegionAvail().y), ImGuiChildFlags_AutoResizeY)) {\n            switch (selected_settings) {\n                case 0: show_system_settings(iris); break;\n                case 1: show_paths_settings(iris); break;\n                case 2: show_graphics_settings(iris); break;\n                case 3: show_shader_settings(iris); break;\n                case 4: show_input_settings(iris); break;\n                case 5: show_memory_card_settings(iris); break;\n                case 6: show_misc_settings(iris); break;\n            }\n        } EndChild();\n\n        // Separator();\n\n        // if (hovered) {\n        //     TextWrapped(tooltip.c_str());\n        // } else {\n        //     Text(\"Hover over an item to get more information\");\n        // }\n    } End();\n\n    PopStyleVar();\n}\n\n}"
  },
  {
    "path": "frontend/ui/spu2.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nint core0_selected = -1;\nint core1_selected = -1;\n\nfloat selected_adsr[120];\nint adsr_index;\n\nconst char* get_adsr_stage_name(int s) {\n    switch (s) {\n        case 0: return \"Attack\";\n        case 1: return \"Decay\";\n        case 2: return \"Sustain\";\n        case 3: return \"Release\";\n        case 4: return \"End\";\n    }\n\n    return \"None\";\n}\n\nvoid show_spu2_core(iris::instance* iris, int c) {\n    using namespace ImGui;\n\n    const struct spu2_core* core = &iris->ps2->spu2->c[c];\n\n    bool* mute = c ? iris->core1_mute : iris->core0_mute;\n    int* solo = c ? &iris->core1_solo : &iris->core0_solo;\n    int* selected = c ? &core1_selected : &core0_selected;\n\n    Text(\"IRQ address: %08x\", core->irqa);\n\n    char id[] = \"##spu2coreX\";\n\n    id[10] = '0' + c;\n\n    if (BeginTable(id, 7, ImGuiTableFlags_RowBg)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Voice\");\n        TableSetupColumn(\"ENVX\");\n        TableSetupColumn(\"NAX\");\n        TableSetupColumn(\"SSA\");\n        TableSetupColumn(\"LSAX\");\n        TableSetupColumn(\"Stage\");\n        TableSetupColumn(\"Cycles\");\n        TableHeadersRow();\n        PopFont();\n\n        for (int i = 0; i < 24; i++) {\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            char voice[9]; sprintf(voice, \"Voice %d\", i);\n\n            if (Selectable(voice, i == *selected)) {\n                if (i == *selected) {\n                    *selected = -1;\n                } else {\n                    *selected = i;\n                }\n            }\n\n            TableSetColumnIndex(1);\n\n            PushFont(iris->font_code);\n            Text(\"%04x\", core->v[i].envx);\n\n            TableSetColumnIndex(2);\n\n            Text(\"%08x\", core->v[i].nax);\n            \n            TableSetColumnIndex(3);\n            \n            Text(\"%08x\", core->v[i].ssa);\n            \n            TableSetColumnIndex(4);\n            \n            Text(\"%08x\", core->v[i].lsax);\n            \n            TableSetColumnIndex(5);\n            PopFont();\n            Text(\"%s\", get_adsr_stage_name(core->v[i].adsr_phase));\n            \n            TableSetColumnIndex(6);\n            Text(\"%d\", core->v[i].adsr_cycles);\n        }\n\n        EndTable();\n    }\n}\n\nvoid show_spu2_tab(iris::instance* iris, int c) {\n    using namespace ImGui;\n\n    const struct spu2_core* core = &iris->ps2->spu2->c[c];\n\n    show_spu2_core(iris, c);\n\n    Separator();\n\n    int* selected = c ? &core1_selected : &core0_selected;\n\n    if (*selected == -1) {\n        Text(\"No voice selected\");\n\n        return;\n    }\n\n    if (adsr_index == 119) {\n        for (int i = 0; i < 119; i++)\n            selected_adsr[i] = selected_adsr[i+1];\n\n        selected_adsr[119] = core->v[*selected].envx / 32767.0f;\n    } else {\n        selected_adsr[adsr_index++] = core->v[*selected].envx / 32767.0f;\n    }\n\n    PlotLines(\"ADSR\", selected_adsr, IM_ARRAYSIZE(selected_adsr), 0, NULL, 0.0f, 1.0f, { 250.0, 80.0 });\n}\n\nvoid show_spu2_debugger(iris::instance* iris) {\n    using namespace ImGui;\n\n    const struct ps2_spu2* spu2 = iris->ps2->spu2;\n\n    if (imgui::BeginEx(\"SPU2\", &iris->show_spu2_debugger)) {\n        if (BeginTabBar(\"##spu2tabbar\")) {\n            if (BeginTabItem(\"CORE0\")) {\n                show_spu2_tab(iris, 0);\n\n                EndTabItem();\n            }\n\n            if (BeginTabItem(\"CORE1\")) {\n                show_spu2_tab(iris, 1);\n\n                EndTabItem();\n            }\n\n            EndTabBar();\n        }\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/state.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\n#include \"ee/ee_dis.h\"\n#include \"ee/ee_def.hpp\"\n#include \"ee/vu_def.hpp\"\n#include \"iop/iop_dis.h\"\n\n#define IM_RGB(r, g, b) ImVec4(((float)r / 255.0f), ((float)g / 255.0f), ((float)b / 255.0f), 1.0)\n\nnamespace iris {\n\nstatic const char* ee_cop0_r[] = {\n    \"Index\",\n    \"Random\",\n    \"EntryLo0\",\n    \"EntryLo1\",\n    \"Context\",\n    \"PageMask\",\n    \"Wired\",\n    \"Unused7\",\n    \"BadVAddr\",\n    \"Count\",\n    \"EntryHi\",\n    \"Compare\",\n    \"Status\",\n    \"Cause\",\n    \"EPC\",\n    \"PRId\",\n    \"Config\",\n    \"Unused17\",\n    \"Unused18\",\n    \"Unused19\",\n    \"Unused20\",\n    \"Unused21\",\n    \"Unused22\",\n    \"BadPAddr\",\n    \"Debug\",\n    \"Perf\",\n    \"Unused26\",\n    \"Unused27\",\n    \"TagLo\",\n    \"TagHi\",\n    \"ErrorEPC\",\n    \"Unused31\"\n};\n\nstatic const char* mips_cc_r[] = {\n    \"r0\", \"at\", \"v0\", \"v1\", \"a0\", \"a1\", \"a2\", \"a3\",\n    \"t0\", \"t1\", \"t2\", \"t3\", \"t4\", \"t5\", \"t6\", \"t7\",\n    \"s0\", \"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\", \"s7\",\n    \"t8\", \"t9\", \"k0\", \"k1\", \"gp\", \"sp\", \"fp\", \"ra\"\n};\n\nstatic const char* vu0i_regs[] = {\n    \"vi00\",\n    \"vi01\",\n    \"vi02\",\n    \"vi03\",\n    \"vi04\",\n    \"vi05\",\n    \"vi06\",\n    \"vi07\",\n    \"vi08\",\n    \"vi09\",\n    \"vi10\",\n    \"vi11\",\n    \"vi12\",\n    \"vi13\",\n    \"vi14\",\n    \"vi15\",\n    \"Status\",\n    \"MAC\",\n    \"Clip\",\n    \"Reserved\",\n    \"R\",\n    \"I\",\n    \"Q\",\n    \"Reserved\",\n    \"Reserved\",\n    \"Reserved\",\n    \"TPC\",\n    \"CMSAR0\",\n    \"FBRST\",\n    \"VPUSTAT\",\n    \"Reserved\",\n    \"CMSAR1\"\n};\n\nuint128_t ee_prev[32];\nuint128_t ee_frames[32];\nuint32_t ee_cop0_prev[32];\nuint32_t ee_cop0_frames[32];\nuint32_t vu0i_prev[32];\nuint32_t vu0i_frames[32];\nuint32_t ee_fpu_prev[32];\nuint32_t ee_fpu_frames[32];\nstruct vu_reg128 vu0f_prev[32];\nuint128_t vu0f_frames[32];\nuint32_t iop_prev[32];\nuint32_t iop_frames[32];\n\nbool vu0f_float;\n\nstatic ImGuiTableFlags ee_table_sizing = ImGuiTableFlags_SizingStretchSame;\nstatic ImGuiTableFlags iop_table_sizing = ImGuiTableFlags_SizingStretchProp;\n\nstatic inline void show_ee_main_registers(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ee_state* ee = iris->ps2->ee;\n\n    for (int i = 0; i < 32; i++) {\n        for (int j = 0; j < 4; j++) {\n            if (ee_prev[i].u32[j] != ee->r[i].u32[j])\n                ee_frames[i].u32[j] = 60;\n        }\n\n        ee_prev[i] = ee->r[i];\n    }\n\n    PushFont(iris->font_code);\n\n    if (BeginTable(\"ee#registers\", 5, ImGuiTableFlags_RowBg | ee_table_sizing)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Reg\");\n        TableSetupColumn(\"96-127\");\n        TableSetupColumn(\"64-95\");\n        TableSetupColumn(\"32-63\");\n        TableSetupColumn(\"0-31\");\n        TableHeadersRow();\n        PopFont();\n\n        for (int i = 0; i < 32; i++) {\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            Text(\"%s\", mips_cc_r[i]);\n\n            for (int j = 0; j < 4; j++) {\n                float a = (float)ee_frames[i].u32[j] / 60.0f;\n\n                TableSetColumnIndex(1+(3-j));\n\n                char label[5]; sprintf(label, \"##%02x\", (i << 2) | j);\n\n                if (Selectable(label, false, ImGuiSelectableFlags_AllowOverlap)) {\n                    OpenPopup(\"Change register value\");\n                } SameLine(0.0, 0.0);\n\n                if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {\n                    static char new_value[9];\n\n                    PushFont(iris->font_small_code);\n                    TextDisabled(\"Edit \"); SameLine(0.0, 0.0);\n                    Text(\"%s\", mips_cc_r[i]); SameLine(0.0, 0.0);\n                    TextDisabled(\" (bits %d-%d)\", j*32, ((j+1)*32)-1);\n                    PopFont();\n\n                    PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));\n                    PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));\n\n                    PushFont(iris->font_body);\n                    PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));\n\n                    AlignTextToFramePadding();\n                    Text(ICON_MS_EDIT); SameLine();\n\n                    SetNextItemWidth(100);\n                    PushFont(iris->font_code);\n\n                    if (InputText(\"##\", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {\n                        if (new_value[0])\n                            ee->r[i].u32[j] = strtoul(new_value, NULL, 16);\n\n                        CloseCurrentPopup();\n                    }\n\n                    PopFont();\n\n                    if (Button(\"Change\")) {\n                        if (new_value[0])\n                            ee->r[i].u32[j] = strtoul(new_value, NULL, 16);\n\n                        CloseCurrentPopup();\n                    } SameLine();\n\n                    if (Button(\"Cancel\"))\n                        CloseCurrentPopup();\n\n                    PopStyleColor();\n                    PopStyleVar(2);\n\n                    PopFont();\n                    EndPopup();\n                }\n\n                TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), \"%08x\", ee->r[i].u32[j]);\n            }\n        }\n    }\n\n    EndTable();\n\n    PopFont();\n}\n\nstatic inline void show_ee_cop0_registers(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ee_state* ee = iris->ps2->ee;\n\n    for (int i = 0; i < 32; i++) {\n        if (ee_cop0_prev[i] != ee->cop0_r[i])\n            ee_cop0_frames[i] = 60;\n\n        ee_cop0_prev[i] = ee->cop0_r[i];\n    }\n\n    PushFont(iris->font_code);\n\n    if (BeginTable(\"ee#cop0registers\", 5, ImGuiTableFlags_RowBg | ee_table_sizing)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Reg\");\n        TableSetupColumn(\"96-127\");\n        TableSetupColumn(\"64-95\");\n        TableSetupColumn(\"32-63\");\n        TableSetupColumn(\"0-31\");\n        TableHeadersRow();\n        PopFont();\n\n        for (int i = 0; i < 32; i++) {\n            float a = (float)ee_cop0_frames[i] / 60.0f;\n\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            Text(\"%s\", ee_cop0_r[i]);\n\n            TableSetColumnIndex(4);\n\n            char label[8]; sprintf(label, \"##e%d\", (i << 2));\n\n            if (Selectable(label, false, ImGuiSelectableFlags_AllowOverlap)) {\n                OpenPopup(\"Change register value\");\n            } SameLine(0.0, 0.0);\n\n            if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {\n                static char new_value[9];\n\n                PushFont(iris->font_small_code);\n                TextDisabled(\"Edit \"); SameLine(0.0, 0.0);\n                Text(\"%s\", ee_cop0_r[i]);\n                PopFont();\n\n                PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));\n                PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));\n\n                PushFont(iris->font_body);\n                PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));\n\n                AlignTextToFramePadding();\n                Text(ICON_MS_EDIT); SameLine();\n\n                SetNextItemWidth(100);\n                PushFont(iris->font_code);\n\n                if (InputText(\"##\", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {\n                    if (new_value[0])\n                        ee->cop0_r[i] = strtoul(new_value, NULL, 16);\n\n                    CloseCurrentPopup();\n                }\n\n                PopFont();\n\n                if (Button(\"Change\")) {\n                    if (new_value[0])\n                        ee->cop0_r[i] = strtoul(new_value, NULL, 16);\n\n                    CloseCurrentPopup();\n                } SameLine();\n\n                if (Button(\"Cancel\"))\n                    CloseCurrentPopup();\n\n                PopStyleColor();\n                PopStyleVar(2);\n\n                PopFont();\n                EndPopup();\n            }\n\n            TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), \"%08x\", ee->cop0_r[i]);\n        }\n    }\n\n    EndTable();\n\n    PopFont();\n}\n\nstatic inline void show_ee_fpu_registers(iris::instance* iris) {\nusing namespace ImGui;\n\n    struct ee_state* ee = iris->ps2->ee;\n\n    for (int i = 0; i < 32; i++) {\n        if (ee_fpu_prev[i] != ee->f[i].u32)\n            ee_fpu_frames[i] = 60;\n\n        ee_fpu_prev[i] = ee->f[i].u32;\n    }\n\n    PushFont(iris->font_code);\n\n    if (BeginTable(\"ee#fpuregisters\", 3, ImGuiTableFlags_RowBg | ee_table_sizing)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Reg\");\n        TableSetupColumn(\"u32\");\n        TableSetupColumn(\"float\");\n        TableHeadersRow();\n        PopFont();\n\n        for (int i = 0; i < 32; i++) {\n            float a = (float)ee_fpu_frames[i] / 60.0f;\n\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            Text(\"f%-2d\", i);\n\n            TableSetColumnIndex(1);\n\n            char label1[8]; sprintf(label1, \"##u%d\", i);\n\n            if (Selectable(label1, false, ImGuiSelectableFlags_AllowOverlap)) {\n                OpenPopup(\"Change register value\");\n            } SameLine(0.0, 0.0);\n\n            if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {\n                static char new_value[9];\n\n                PushFont(iris->font_small_code);\n                TextDisabled(\"Edit \"); SameLine(0.0, 0.0);\n                Text(\"f%d \", i); SameLine(0.0, 0.0);\n                TextDisabled(\"(as u32)\");\n                PopFont();\n\n                PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));\n                PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));\n\n                PushFont(iris->font_body);\n                PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));\n\n                AlignTextToFramePadding();\n                Text(ICON_MS_EDIT); SameLine();\n\n                SetNextItemWidth(100);\n                PushFont(iris->font_code);\n\n                if (InputText(\"##\", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {\n                    if (new_value[0])\n                        ee->f[i].u32 = strtoul(new_value, NULL, 16);\n\n                    CloseCurrentPopup();\n                }\n\n                PopFont();\n\n                if (Button(\"Change\")) {\n                    if (new_value[0])\n                        ee->f[i].u32 = strtoul(new_value, NULL, 16);\n\n                    CloseCurrentPopup();\n                } SameLine();\n\n                if (Button(\"Cancel\"))\n                    CloseCurrentPopup();\n\n                PopStyleColor();\n                PopStyleVar(2);\n\n                PopFont();\n                EndPopup();\n            }\n\n            TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), \"%08x\", ee->f[i].u32);\n\n            TableSetColumnIndex(2);\n\n            char label2[8]; sprintf(label2, \"##f%d\", i);\n\n            if (Selectable(label2, false, ImGuiSelectableFlags_AllowOverlap)) {\n                OpenPopup(\"Change register value\");\n            } SameLine(0.0, 0.0);\n\n            if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {\n                static char new_value[9];\n\n                PushFont(iris->font_small_code);\n                TextDisabled(\"Edit \"); SameLine(0.0, 0.0);\n                Text(\"f%d \", i); SameLine(0.0, 0.0);\n                TextDisabled(\"(as float)\");\n                PopFont();\n\n                PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));\n                PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));\n\n                PushFont(iris->font_body);\n                PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));\n\n                AlignTextToFramePadding();\n                Text(ICON_MS_EDIT); SameLine();\n\n                SetNextItemWidth(100);\n                PushFont(iris->font_code);\n\n                if (InputText(\"##\", new_value, 9, ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_EnterReturnsTrue)) {\n                    if (new_value[0])\n                        ee->f[i].f = strtof(new_value, NULL);\n\n                    CloseCurrentPopup();\n                }\n\n                PopFont();\n\n                if (Button(\"Change\")) {\n                    if (new_value[0])\n                        ee->f[i].f = strtof(new_value, NULL);\n\n                    CloseCurrentPopup();\n                } SameLine();\n\n                if (Button(\"Cancel\"))\n                    CloseCurrentPopup();\n\n                PopStyleColor();\n                PopStyleVar(2);\n\n                PopFont();\n                EndPopup();\n            }\n\n            TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), \"%.7f\", ee->f[i].f);\n        }\n    }\n\n    EndTable();\n\n    PopFont();\n}\n\nstatic inline void show_vu0_float(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct vu_state* vu0 = iris->ps2->ee->vu0;\n\n    for (int i = 0; i < 32; i++) {\n        for (int j = 0; j < 4; j++) {\n            if (vu0f_prev[i].u32[j] != vu0->vf[i].u32[j])\n                vu0f_frames[i].u32[j] = 60;\n        }\n\n        vu0f_prev[i] = vu0->vf[i];\n    }\n\n    PushFont(iris->font_code);\n\n    if (BeginTable(\"ee#registers\", 5, ImGuiTableFlags_RowBg | ee_table_sizing)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Reg\");\n        TableSetupColumn(\"W\");\n        TableSetupColumn(\"Z\");\n        TableSetupColumn(\"Y\");\n        TableSetupColumn(\"X\");\n        TableHeadersRow();\n        PopFont();\n\n        for (int i = 0; i < 32; i++) {\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            Text(\"vf%02d\", i);\n\n            for (int j = 0; j < 4; j++) {\n                float a = (float)vu0f_frames[i].u32[j] / 60.0f;\n\n                TableSetColumnIndex(1+(3-j));\n\n                char label[5]; sprintf(label, \"##%02x\", (i << 2) | j);\n\n                if (Selectable(label, false, ImGuiSelectableFlags_AllowOverlap)) {\n                    OpenPopup(\"Change register value\");\n                } SameLine(0.0, 0.0);\n\n                if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {\n                    static char new_value[9];\n\n                    PushFont(iris->font_small_code);\n                    TextDisabled(\"Edit \"); SameLine(0.0, 0.0);\n                    Text(\"vf%02d\", i); SameLine(0.0, 0.0);\n                    TextDisabled(\" (%c field)\", \"XYZW\"[j]);\n                    PopFont();\n\n                    PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));\n                    PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));\n\n                    PushFont(iris->font_body);\n                    PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));\n\n                    AlignTextToFramePadding();\n                    Text(ICON_MS_EDIT); SameLine();\n\n                    SetNextItemWidth(100);\n                    PushFont(iris->font_code);\n\n                    if (vu0f_float) {\n                        if (InputText(\"##\", new_value, 9, ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_EnterReturnsTrue)) {\n                            if (new_value[0])\n                                vu0->vf[i].f[j] = strtof(new_value, NULL);\n\n                            CloseCurrentPopup();\n                        }\n                    } else {\n                        if (InputText(\"##\", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {\n                            if (new_value[0])\n                                vu0->vf[i].u32[j] = strtoul(new_value, NULL, 16);\n\n                            CloseCurrentPopup();\n                        }\n                    }\n\n                    PopFont();\n\n                    if (Button(\"Change\")) {\n                        if (vu0f_float) {\n                            if (new_value[0])\n                                vu0->vf[i].f[j] = strtof(new_value, NULL);\n                        } else {\n                            if (new_value[0])\n                                vu0->vf[i].u32[j] = strtoul(new_value, NULL, 16);\n                        }\n                        CloseCurrentPopup();\n                    } SameLine();\n\n                    if (Button(\"Cancel\"))\n                        CloseCurrentPopup();\n\n                    PopStyleColor();\n                    PopStyleVar(2);\n\n                    PopFont();\n                    EndPopup();\n                }\n\n                if (vu0f_float) {\n                    TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), \"%.7f\", vu0->vf[i].f[j]);\n                } else {\n                    TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), \"%08x\", vu0->vf[i].u32[j]);\n                }\n            }\n        }\n\n        EndTable();\n    }\n\n    PopFont();\n}\n\nstatic inline void show_vu0_integer(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct vu_state* vu0 = iris->ps2->ee->vu0;\n\n    for (int i = 0; i < 32; i++) {\n        if (vu0i_prev[i] != (i < 16 ? vu0->vi[i] : vu0->cr[i-16]))\n            vu0i_frames[i] = 60;\n\n        vu0i_prev[i] = i < 16 ? vu0->vi[i] : vu0->cr[i-16];\n    }\n\n    PushFont(iris->font_code);\n\n    if (BeginTable(\"ee#cop0registers\", 5, ImGuiTableFlags_RowBg | ee_table_sizing)) {\n        PushFont(iris->font_small_code);\n        TableSetupColumn(\"Reg\");\n        TableSetupColumn(\"96-127\");\n        TableSetupColumn(\"64-95\");\n        TableSetupColumn(\"32-63\");\n        TableSetupColumn(\"0-31\");\n        TableHeadersRow();\n        PopFont();\n\n        for (int i = 0; i < 32; i++) {\n            float a = (float)vu0i_frames[i] / 60.0f;\n\n            TableNextRow();\n\n            TableSetColumnIndex(0);\n\n            Text(\"%s\", vu0i_regs[i]);\n\n            TableSetColumnIndex(4);\n\n            char label[8]; sprintf(label, \"##e%d\", (i << 2));\n\n            if (Selectable(label, false, ImGuiSelectableFlags_AllowOverlap)) {\n                OpenPopup(\"Change register value\");\n            } SameLine(0.0, 0.0);\n\n            if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {\n                static char new_value[9];\n\n                PushFont(iris->font_small_code);\n                TextDisabled(\"Edit \"); SameLine(0.0, 0.0);\n                Text(\"%s\", vu0i_regs[i]);\n                PopFont();\n\n                PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));\n                PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));\n\n                PushFont(iris->font_body);\n                PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));\n\n                AlignTextToFramePadding();\n                Text(ICON_MS_EDIT); SameLine();\n\n                SetNextItemWidth(100);\n                PushFont(iris->font_code);\n\n                if (InputText(\"##\", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {\n                    if (new_value[0]) {\n                        if (i < 16) {\n                            vu0->vi[i] = strtoul(new_value, NULL, 16);\n                        } else {\n                            vu0->cr[i-16] = strtoul(new_value, NULL, 16);\n                        }\n                    }\n\n                    CloseCurrentPopup();\n                }\n\n                PopFont();\n\n                if (Button(\"Change\")) {\n                    if (i < 16) {\n                        vu0->vi[i] = strtoul(new_value, NULL, 16);\n                    } else {\n                        vu0->cr[i-16] = strtoul(new_value, NULL, 16);\n                    }\n\n                    CloseCurrentPopup();\n                } SameLine();\n\n                if (Button(\"Cancel\"))\n                    CloseCurrentPopup();\n\n                PopStyleColor();\n                PopStyleVar(2);\n\n                PopFont();\n                EndPopup();\n            }\n\n            TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), \"%08x\", i < 16 ? vu0->vi[i] : vu0->cr[i-16]);\n        }\n    }\n\n    EndTable();\n\n    PopFont();\n}\n\nstatic inline void show_iop_main_registers(iris::instance* iris) {\n    using namespace ImGui;\n\n    PushFont(iris->font_code);\n\n    struct iop_state* iop = iris->ps2->iop;\n\n    for (int i = 0; i < 32; i++) {\n        if (iop_prev[i] != iop->r[i])\n            iop_frames[i] = 60;\n\n        iop_prev[i] = iop->r[i];\n    }\n\n    if (BeginTable(\"table3\", 4, ImGuiTableFlags_RowBg | iop_table_sizing)) {\n        for (int i = 0; i < 32;) {\n            TableNextRow();\n\n            for (int x = 0; (x < 4) && (i < 32); x++) {\n                float a = (float)iop_frames[i] / 60.0f;\n\n                char id[5]; sprintf(id, \"##%02u\", i);\n\n                TableSetColumnIndex(x);\n\n                if (Selectable(id, false, ImGuiSelectableFlags_AllowOverlap)) {\n                    OpenPopup(\"Change register value\");\n                } SameLine(0.0, 0.0);\n\n                if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) {\n                    static char new_value[9];\n\n                    PushFont(iris->font_small_code);\n                    TextDisabled(\"Edit %s\", mips_cc_r[i]);\n                    PopFont();\n\n                    PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0));\n                    PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0));\n\n                    PushFont(iris->font_body);\n                    PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35));\n\n                    AlignTextToFramePadding();\n                    Text(ICON_MS_EDIT); SameLine();\n\n                    SetNextItemWidth(100);\n                    PushFont(iris->font_code);\n\n                    if (InputText(\"##\", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) {\n                        if (new_value[0])\n                            iop->r[i] = strtoul(new_value, NULL, 16);\n\n                        CloseCurrentPopup();\n                    }\n\n                    PopFont();\n\n                    if (Button(\"Change\")) {\n                        if (new_value[0])\n                            iop->r[i] = strtoul(new_value, NULL, 16);\n\n                        CloseCurrentPopup();\n                    } SameLine();\n\n                    if (Button(\"Cancel\"))\n                        CloseCurrentPopup();\n\n                    PopStyleColor();\n                    PopStyleVar(2);\n\n                    PopFont();\n                    EndPopup();\n                }\n\n                SameLine(0.0, 0.0);\n\n                Text(\"%2s\", mips_cc_r[i]); SameLine();\n                TextColored(ImVec4(0.6 + a, 0.6, 0.6, 1.0), \"%08x\", iop->r[i]);\n\n                ++i;\n            }\n        }\n\n        EndTable();\n    }\n\n    PopFont();\n}\n\nstatic inline void show_work_in_progress(iris::instance* iris) {\n    using namespace ImGui;\n\n    Text(\"Work-in-progress!\");\n}\n\nstatic const char* sizing_combo_items[] = {\n    ICON_MS_FIT_WIDTH \" Fixed fit\",\n    ICON_MS_FULLSCREEN \" Fixed same\",\n    ICON_MS_FULLSCREEN \" Stretch prop\",\n    ICON_MS_FULLSCREEN \" Stretch same\"\n};\n\nstatic ImGuiTableFlags table_sizing_flags[] = {\n    ImGuiTableFlags_SizingFixedFit,\n    ImGuiTableFlags_SizingFixedSame,\n    ImGuiTableFlags_SizingStretchProp,\n    ImGuiTableFlags_SizingStretchSame\n};\n\nstatic int ee_sizing_combo = 3;\nstatic int iop_sizing_combo = 0;\n\nstatic const char* ee_reg_group_names[] = {\n    \"Main\",\n    \"COP0\",\n    \"FPU\",\n    \"VU0f\",\n    \"VU0i\"\n};\n\nstatic int ee_reg_group = 0;\n\nvoid show_ee_state(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (imgui::BeginEx(\"EE state\", &iris->show_ee_state, ImGuiWindowFlags_MenuBar)) {\n        if (BeginMenuBar()) {\n            if (BeginMenu(\"Settings\")) {\n                if (BeginMenu(ICON_MS_CROP \" Sizing\")) {\n                    for (int i = 0; i < 4; i++) {\n                        if (Selectable(sizing_combo_items[i], i == ee_sizing_combo)) {\n                            ee_table_sizing = table_sizing_flags[i];\n                            ee_sizing_combo = i;\n                        }\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                if (MenuItem(\"Display VU0f as floats\", nullptr, &vu0f_float));\n\n                ImGui::EndMenu();\n            }\n\n            EndMenuBar();\n        }\n\n        if (BeginTable(\"table4\", 5, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV)) {\n            TableNextRow();\n\n            for (int i = 0; i < 5; i++) {\n                TableSetColumnIndex(i);\n\n                if (Selectable(ee_reg_group_names[i], ee_reg_group == i)) {\n                    ee_reg_group = i;\n                }\n            }\n\n            EndTable();\n        }\n\n        if (BeginChild(\"ee#child\")) {\n            switch (ee_reg_group) {\n                case 0: {\n                    show_ee_main_registers(iris);\n                } break;\n\n                case 1: {\n                    show_ee_cop0_registers(iris);\n                } break;\n\n                case 2: {\n                    show_ee_fpu_registers(iris);\n                } break;\n\n                case 3: {\n                    show_vu0_float(iris);\n                } break;\n\n                case 4: {\n                    show_vu0_integer(iris);\n                } break;\n            }\n\n            EndChild();\n        }\n    } End();\n\n    for (int i = 0; i < 32; i++) {\n        if (ee_fpu_frames[i]) \n            ee_fpu_frames[i]--;\n\n        if (ee_cop0_frames[i])\n            ee_cop0_frames[i]--;\n\n        if (vu0i_frames[i])\n            vu0i_frames[i]--;\n\n        for (int j = 0; j < 4; j++)\n            if (ee_frames[i].u32[j])\n                ee_frames[i].u32[j]--;\n\n        for (int j = 0; j < 4; j++)\n            if (vu0f_frames[i].u32[j])\n                vu0f_frames[i].u32[j]--;\n    }\n}\n\nvoid show_iop_state(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (imgui::BeginEx(\"IOP state\", &iris->show_iop_state, ImGuiWindowFlags_MenuBar)) {\n        if (BeginMenuBar()) {\n            if (BeginMenu(\"Settings\")) {\n                if (BeginMenu(ICON_MS_CROP \" Sizing\")) {\n                    for (int i = 0; i < 4; i++) {\n                        if (Selectable(sizing_combo_items[i], i == iop_sizing_combo)) {\n                            iop_table_sizing = table_sizing_flags[i];\n                            iop_sizing_combo = i;\n                        }\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                ImGui::EndMenu();\n            }\n\n            EndMenuBar();\n        }\n        if (BeginChild(\"iop#child\")) {\n            show_iop_main_registers(iris);\n\n            EndChild();\n        }\n    } End();\n\n    for (int i = 0; i < 32; i++)\n        if (iop_frames[i])\n            iop_frames[i]--;\n}\n\n}"
  },
  {
    "path": "frontend/ui/statusbar.cpp",
    "content": "#include \"imgui_internal.h\"\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace ImGui {\n\nbool BeginMainStatusBar()\n{\n    ImGuiContext& g = *GetCurrentContext();\n    ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport();\n\n    // Notify of viewport change so GetFrameHeight() can be accurate in case of DPI change\n    SetCurrentViewport(NULL, viewport);\n\n    // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set.\n    // FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea?\n    // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings.\n    g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f));\n    ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar;\n    float height = GetFrameHeight();\n    bool is_open = BeginViewportSideBar(\"##MainStatusBar\", viewport, ImGuiDir_Down, height, window_flags);\n    g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f);\n\n    if (is_open)\n        BeginMenuBar();\n    else\n        End();\n\n    return is_open;\n}\n\nvoid EndMainStatusBar()\n{\n    EndMenuBar();\n\n    // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window\n    // FIXME: With this strategy we won't be able to restore a NULL focus.\n    ImGuiContext& g = *GImGui;\n    if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest)\n        FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild);\n\n    End();\n}\n\n}\n\nnamespace iris {\n\nint get_format_bpp(VkFormat fmt) {\n    switch (fmt) {\n        case GS_PSMCT32: return 32;\n        case GS_PSMCT24: return 24;\n        case GS_PSMCT16:\n        case GS_PSMCT16S: return 16;\n    }\n\n    return 0;\n}\n\nvoid show_status_bar(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (BeginMainStatusBar()) {\n        static const char* const modes[] = {\n            \"Progressive\",\n            \"Interlaced (Field)\",\n            \"Progressive\",\n            \"Interlaced (Frame)\"\n        };\n\n        static const char* const renderers[] = {\n            \"Null\",\n            \"Software\",\n            \"Hardware (Vulkan)\"\n        };\n\n        int dispfb = 0;\n\n        char buf[128];\n\n        sprintf(buf, \"%s\", emu::get_current_system_name(iris));\n\n        float width = CalcTextSize(buf).x;\n\n        ImVec4 col = GetStyleColorVec4(ImGuiCol_Text);\n\n        col.w = 0.2;\n\n        PushStyleColor(ImGuiCol_Separator, col);\n        PushStyleVarX(ImGuiStyleVar_ItemSpacing, 4.0f);\n\n        SetCursorPosX(5.0);\n\n        if (!iris->image.image) {\n            Text(\"%s\", renderers[iris->renderer_backend]);\n            SeparatorEx(ImGuiSeparatorFlags_Vertical);\n            Text(\"No image\");\n            SeparatorEx(ImGuiSeparatorFlags_Vertical);\n            Text(\"%.1f fps\", GetIO().Framerate);\n\n            SetCursorPosX(GetWindowWidth() - width - 5);\n            Text(buf);\n        } else {\n            // | %dx%d | %dx%d | %s | %dbpp | %.1f fps\",\n            Text(\"%s\", renderers[iris->renderer_backend]);\n            SeparatorEx(ImGuiSeparatorFlags_Vertical);\n            Text(\"%dx%d\", iris->render_width, iris->render_height);\n            SeparatorEx(ImGuiSeparatorFlags_Vertical);\n            Text(\"%dx%d\", iris->image.width, iris->image.height);\n            SeparatorEx(ImGuiSeparatorFlags_Vertical);\n            Text(\"%s\", modes[iris->ps2->gs->smode2 & 3]);\n            SeparatorEx(ImGuiSeparatorFlags_Vertical);\n            Text(\"%.1f fps\", GetIO().Framerate);\n\n            SetCursorPosX(GetWindowWidth() - width - 5);\n            Text(buf);\n        }\n\n\n        PopStyleColor();\n        PopStyleVar();\n\n        // if (vp_w) {\n        //     Text(ICON_MS_MONITOR \" %s | %dx%d | %dx%d | %s | %dbpp | %.1f fps\",\n        //         \"None\", // renderer_get_name(iris->ctx),\n        //         disp_w, disp_h,\n        //         vp_w, vp_h,\n        //         mode == 3 ? \"Interlaced\" : \"Progressive\",\n        //         get_format_bpp(disp_fmt),\n        //         GetIO().Framerate\n        //     );\n\n        //     // iris->avg_frames++;\n        //     // iris->avg_fps += iris->fps;\n        // } else {\n        //     Text(ICON_MS_MONITOR \" %s | No image\",\n        //         \"None\" // renderer_get_name(iris->ctx)\n        //     );\n        // }\n\n        EndMainStatusBar();\n    }\n}\n    \n}"
  },
  {
    "path": "frontend/ui/symbols.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n#include <algorithm>\n#include <regex>\n\n#include \"iris.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nstatic const char* symbols_sizing_combo_items[] = {\n    ICON_MS_FIT_WIDTH \" Fixed fit\",\n    ICON_MS_FULLSCREEN \" Fixed same\",\n    ICON_MS_FULLSCREEN \" Stretch prop\",\n    ICON_MS_FULLSCREEN \" Stretch same\"\n};\n\nstatic ImGuiTableFlags symbols_table_sizing_flags[] = {\n    ImGuiTableFlags_SizingFixedFit,\n    ImGuiTableFlags_SizingFixedSame,\n    ImGuiTableFlags_SizingStretchProp,\n    ImGuiTableFlags_SizingStretchSame\n};\n\nstatic int symbols_table_sizing_combo = 0;\nstatic int symbols_table_sizing = ImGuiTableFlags_SizingStretchProp;\n\nstd::vector <iris::elf_symbol> symbols_list;\n\nbool regex = false;\nbool case_sensitive = false;\nbool autosearch = true;\n\nvoid filter_symbols(iris::instance* iris, const char* filter, bool regex, bool case_sensitive) {\n    symbols_list.clear();\n\n    if (filter[0] == '\\0') {\n        symbols_list = iris->symbols;\n\n        return;\n    }\n\n    std::string filter_str(filter);\n\n    for (const iris::elf_symbol& sym : iris->symbols) {\n        if (regex) {\n            std::regex r(filter_str, std::regex::ECMAScript | (case_sensitive ? std::regex_constants::syntax_option_type(0) : std::regex::icase));\n\n            if (std::regex_match(sym.name, r)) {\n                symbols_list.push_back(sym);\n            }\n        } else {\n            std::string sym_name_str(sym.name);\n\n            if (!case_sensitive) {\n                std::transform(sym_name_str.begin(), sym_name_str.end(), sym_name_str.begin(), tolower);\n                std::transform(filter_str.begin(), filter_str.end(), filter_str.begin(), tolower);\n            }\n\n            auto it = sym_name_str.find(filter_str);\n\n            if (it != std::string::npos) {\n                symbols_list.push_back(sym);\n            }\n        }\n    }\n}\n\nint edit_callback(ImGuiInputTextCallbackData* data) {\n    iris::instance* iris = (iris::instance*)data->UserData;\n\n    filter_symbols(iris, data->Buf, regex, case_sensitive);\n\n    return 0;\n}\n\nvoid show_symbols(iris::instance* iris) {\n    using namespace ImGui;\n\n    if (imgui::BeginEx(\"Symbols\", &iris->show_symbols, ImGuiWindowFlags_MenuBar)) {\n        if (BeginMenuBar()) {\n            if (BeginMenu(\"Settings\")) {\n                if (BeginMenu(ICON_MS_CROP \" Sizing\")) {\n                    for (int i = 0; i < 4; i++) {\n                        if (Selectable(symbols_sizing_combo_items[i], i == symbols_table_sizing_combo)) {\n                            symbols_table_sizing = symbols_table_sizing_flags[i];\n                            symbols_table_sizing_combo = i;\n                        }\n                    }\n\n                    ImGui::EndMenu();\n                }\n\n                MenuItem(ICON_MS_SEARCH_CHECK \" Auto search\", NULL, &autosearch);\n\n                ImGui::EndMenu();\n            }\n\n            EndMenuBar();\n        }\n\n        static char buf[512];\n\n        SetNextItemWidth(200.0f);\n\n        ImGuiInputFlags flags = ImGuiInputTextFlags_EnterReturnsTrue;\n\n        if (autosearch) {\n            flags |= ImGuiInputTextFlags_CallbackEdit;\n        }\n\n        if (InputTextWithHint(\"##search\", \"Search symbols...\", buf, 512, flags, edit_callback, (void*)iris)) {\n            filter_symbols(iris, buf, regex, case_sensitive);\n        } SameLine();\n\n        if (Button(ICON_MS_SEARCH)) {\n            filter_symbols(iris, buf, regex, case_sensitive);\n        } SameLine();\n\n        if (BeginPopupContextItem(\"symbols_settings\")) {\n            if (MenuItem(ICON_MS_REGULAR_EXPRESSION \" Regex mode\", NULL, &regex)) {\n                filter_symbols(iris, buf, regex, case_sensitive);\n            }\n\n            if (MenuItem(ICON_MS_MATCH_CASE \" Case-sensitive\", NULL, &case_sensitive)) {\n                filter_symbols(iris, buf, regex, case_sensitive);\n            }\n\n            EndPopup();\n        }\n\n        if (Button(ICON_MS_SETTINGS, ImVec2(50, 0))) {\n            OpenPopup(\"symbols_settings\");\n        }\n\n        int text_height = GetTextLineHeightWithSpacing();\n        ImVec2 outer_size = ImVec2(0, GetContentRegionAvail().y - text_height);\n\n        ImGuiTableFlags table_flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Sortable | ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY;\n\n        table_flags |= symbols_table_sizing;\n\n        if (BeginTable(\"##symbolstb\", 3, table_flags, outer_size)) {\n            if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) {\n                if (sort_specs->SpecsDirty) {\n                    if (buf[0] == '\\0') {\n                        symbols_list = iris->symbols;\n                    }\n\n                    // Sort by symbol\n                    if (sort_specs->Specs->ColumnIndex == 0) {\n                        std::sort(symbols_list.begin(), symbols_list.end(), [=](const iris::elf_symbol& a, const iris::elf_symbol& b) {\n                            return sort_specs->Specs->SortDirection == ImGuiSortDirection_Ascending ? std::string(a.name) < std::string(b.name) : std::string(a.name) > std::string(b.name);\n                        });\n                    }\n\n                    // Sort by address\n                    if (sort_specs->Specs->ColumnIndex == 1) {\n                        std::sort(symbols_list.begin(), symbols_list.end(), [=](const iris::elf_symbol& a, const iris::elf_symbol& b) {\n                            return sort_specs->Specs->SortDirection == ImGuiSortDirection_Ascending ? a.addr < b.addr : a.addr > b.addr;\n                        });\n                    }\n\n                    // Sort by size\n                    if (sort_specs->Specs->ColumnIndex == 2) {\n                        std::sort(symbols_list.begin(), symbols_list.end(), [=](const iris::elf_symbol& a, const iris::elf_symbol& b) {\n                            return sort_specs->Specs->SortDirection == ImGuiSortDirection_Ascending ? a.size < b.size : a.size > b.size;\n                        });\n                    }\n\n                    sort_specs->SpecsDirty = false;\n                }\n            }\n\n            TableSetupScrollFreeze(0, 1);\n            TableSetupColumn(\"Symbol\");\n            TableSetupColumn(\"Address\");\n            TableSetupColumn(\"Size\");\n            PushFont(iris->font_small_code);\n            TableHeadersRow();\n            PopFont();\n\n            PushFont(iris->font_code);\n\n            int index = 0;\n\n            for (const iris::elf_symbol& symbol : symbols_list) {\n                TableNextRow();\n\n                TableSetColumnIndex(0);\n                Text(\"%s\", symbol.name);\n                TableSetColumnIndex(1);\n                Text(\"%08x\", symbol.addr);\n                TableSetColumnIndex(2);\n\n                char label[64];\n\n                sprintf(label, \"%zu##%d\", symbol.size, index++);\n\n                if (Selectable(label, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick)) {\n                    if (IsMouseDoubleClicked(ImGuiMouseButton_Left)) {\n                        iris->show_ee_control = true;\n                        iris->ee_control_follow_pc = false;\n                        iris->ee_control_address = symbol.addr;\n                    }\n                }\n\n                if (BeginPopupContextItem()) {\n                    PushFont(iris->font_small_code);\n                    TextDisabled(\"%s\", symbol.name);\n                    PopFont();\n\n                    PushFont(iris->font_body);\n\n                    if (Selectable(ICON_MS_ARROW_FORWARD \" Go to this address\")) {\n                        iris->show_ee_control = true;\n                        iris->ee_control_follow_pc = false;\n                        iris->ee_control_address = symbol.addr;\n                    }\n\n                    if (Selectable(ICON_MS_ADD_CIRCLE \" Set a breakpoint here\")) {\n                        breakpoint b;\n\n                        b.addr = symbol.addr;\n                        b.cond_r = false;\n                        b.cond_w = false;\n                        b.cond_x = true;\n                        b.cpu = BKPT_CPU_EE;\n                        b.size = 4;\n                        b.enabled = true;\n\n                        iris->breakpoints.push_back(b);\n                    }\n\n                    PopFont();\n                    EndPopup();\n                }\n            }\n\n            PopFont();\n            EndTable();\n        }\n\n        Text(\"%zu \", symbols_list.size()); SameLine(0.0, 0.0);\n        TextDisabled(\"symbols found \"); SameLine(0.0, 0.0); Text(\"| \"); SameLine(0.0, 0.0);\n        TextDisabled(\"%s \", regex ? \"Regex mode\" : \"Normal mode\"); SameLine(0.0, 0.0); Text(\"| \"); SameLine(0.0, 0.0);\n        TextDisabled(\"%s \", case_sensitive ? \"Case-sensitive\" : \"Case-insensitive\");\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/threads.cpp",
    "content": "#include <vector>\n#include <string>\n#include <cctype>\n#include <algorithm>\n#include <regex>\n\n#include \"iris.hpp\"\n#include \"ee/ee_def.hpp\"\n\n#include \"res/IconsMaterialSymbols.h\"\n\nnamespace iris {\n\nstatic inline const char* get_status_string(int status) {\n    switch (status) {\n        case THS_RUN: return \"RUN\";\n        case THS_READY: return \"READY\";\n        case THS_WAIT: return \"WAIT\";\n        case THS_SUSPEND: return \"SUSPEND\";\n        case THS_WAITSUSPEND: return \"WAIT/SUSPEND\";\n        case THS_DORMANT: return \"DORMANT\";\n    }\n\n    return \"<unknown>\";\n}\n\nstatic const char* get_entry_symbol(iris::instance* iris, uint32_t addr) {\n    // Look up the address in the symbol table\n    if (addr == 0x81fc0) return \"EE Idle Thread\";\n\n    for (const iris::elf_symbol& sym : iris->symbols) {\n        if ((sym.addr >= addr) && (sym.addr < (addr + sym.size))) {\n            return sym.name;\n        }\n    }\n\n    return nullptr;\n}\n\nvoid show_thread_list(iris::instance* iris) {\n    using namespace ImGui;\n\n    struct ee_state* ee = iris->ps2->ee;\n\n    ImGuiTableFlags table_flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Sortable | ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY;\n\n    if (BeginTable(\"##threadlist\", 5, table_flags)) {\n        TableSetupScrollFreeze(0, 1);\n        TableSetupColumn(\"ID\");\n        TableSetupColumn(\"Priority\");\n        TableSetupColumn(\"Entry\");\n        TableSetupColumn(\"PC\");\n        TableSetupColumn(\"Status\");\n        PushFont(iris->font_small_code);\n        TableHeadersRow();\n        PopFont();\n\n        struct ee_thread* thr = (struct ee_thread*)&iris->ps2->ee_ram->buf[ee->thread_list_base & 0x1fffffff];\n\n        int id = 0;\n\n        while (thr->status) {\n            TableNextRow();\n            TableSetColumnIndex(0);\n            Text(\"%d\", id++);\n            TableSetColumnIndex(1);\n            Text(\"%d\", thr->current_priority);\n            TableSetColumnIndex(2);\n\n            const char* symbol = get_entry_symbol(iris, thr->func);\n\n            if (symbol) {\n                Text(\"%s\", symbol);\n            } else {\n                Text(\"0x%08X\", thr->func);\n            }\n\n            uint32_t argv = *(uint32_t*)&iris->ps2->ee_ram->buf[(thr->argv + 4) & 0x1fffffff];\n\n            // if (thr->argc) {\n            //     printf(\"argv=%08x *argv=%08x\\n\", thr->argv, argv);\n            // }\n\n            TableSetColumnIndex(3);\n            Text(\"%s\", thr->argc ? (char*)&iris->ps2->ee_ram->buf[argv & 0x1fffffff] : \"NULL\");\n            TableSetColumnIndex(4);\n            Text(\"%s\", get_status_string(thr->status));\n\n            thr++;\n        }\n\n        EndTable();\n    }\n}\n\nvoid show_threads(iris::instance* iris) {\n    using namespace ImGui;\n    \n    if (imgui::BeginEx(\"Threads\", &iris->show_threads)) {\n        if (!iris->ps2->ee->thread_list_base) {\n            ImVec2 size = CalcTextSize(ICON_MS_WARNING \" Thread list hasn't been initialized yet\");\n            ImVec2 pos = ImVec2(GetContentRegionAvail().x / 2 - size.x / 2, GetContentRegionAvail().y / 2 - size.y / 2);\n            ImVec4 col = GetStyle().Colors[ImGuiCol_Text];\n\n            SetCursorPos(pos);\n            TextDisabled(ICON_MS_WARNING \" Thread list hasn't been initialized yet\");\n\n            End();\n\n            return;\n        }\n\n        show_thread_list(iris);\n    } End();\n}\n\n}"
  },
  {
    "path": "frontend/ui/vu_disassembly.cpp",
    "content": "#include <algorithm>\n#include <vector>\n#include <string>\n#include <cctype>\n\n#include \"iris.hpp\"\n\n#include \"portable-file-dialogs.h\"\n#include \"res/IconsMaterialSymbols.h\"\n\n#include \"ee/vu_dis.h\"\n#include \"ee/vu_def.hpp\"\n\n#include <algorithm> \n#include <cctype>\n#include <locale>\n\n// Trim from the start (in place)\ninline void ltrim_impl(std::string &s) {\n    s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) {\n        return !std::isspace(ch);\n    }));\n}\n\n// Trim from the end (in place)\ninline void rtrim_impl(std::string &s) {\n    s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) {\n        return !std::isspace(ch);\n    }).base(), s.end());\n}\n\n// Trim from both ends (in place)\ninline void trim_impl(std::string &s) {\n    rtrim_impl(s);\n    ltrim_impl(s);\n}\n\n// Trim from the start (copying)\ninline std::string ltrim(std::string s) {\n    ltrim_impl(s);\n    return s;\n}\n\n// Trim from the end (copying)\ninline std::string rtrim(std::string s) {\n    rtrim_impl(s);\n    return s;\n}\n\n// Trim from both ends (copying)\ninline std::string trim(std::string s) {\n    trim_impl(s);\n    return s;\n}\n\n#define IM_RGB(r, g, b) ImVec4(((float)r / 255.0f), ((float)g / 255.0f), ((float)b / 255.0f), 1.0)\n\nnamespace iris {\n\nstruct vu_dis_state g_vu_dis_state = { 0 };\n\nuint32_t addr = 0;\nbool stop_at_e_bit = false;\nbool disassemble_all = false;\nbool add_padding = true;\nbool compact_view = false;\nbool show_address_opcode = true;\n\nvoid print_highlighted_vu1(iris::instance* iris, const char* buf) {\n    using namespace ImGui;\n\n    std::vector <std::string> tokens;\n\n    std::string text;\n\n    while (*buf) {\n        text.clear();        \n\n        if (isalpha(*buf)) {\n            while (isalpha(*buf) || isdigit(*buf) || (*buf == '.'))\n                text.push_back(*buf++);\n        } else if (isxdigit(*buf) || (*buf == '-')) {\n            while (isxdigit(*buf) || (*buf == 'x') || (*buf == '-'))\n                text.push_back(*buf++);\n        } else if (*buf == '$') {\n            while (*buf == '$' || isdigit(*buf) || isalpha(*buf) || *buf == '_')\n                text.push_back(*buf++);\n        } else if (*buf == ',') {\n            while (*buf == ',')\n                text.push_back(*buf++);\n        } else if (*buf == '(') {\n            while (*buf == '(')\n                text.push_back(*buf++);\n        } else if (*buf == ')') {\n            while (*buf == ')')\n                text.push_back(*buf++);\n        } else if (*buf == '<') {\n            while (*buf != '>')\n                text.push_back(*buf++);\n\n            text.push_back(*buf++);\n        } else if (*buf == '_') {\n            text.push_back(*buf++);\n        } else if (*buf == '.') {\n            text.push_back(*buf++);\n        } else if (*buf == '+') {\n            text.push_back(*buf++);\n        } else if (*buf == '-') {\n            text.push_back(*buf++);\n        } else {\n            printf(\"unhandled char %c (%d) \\\"%s\\\"\\n\", *buf, *buf, buf);\n\n            exit(1);\n        }\n\n        while (isspace(*buf))\n            text.push_back(*buf++);\n\n        tokens.push_back(text);\n    }\n\n    for (const std::string& t : tokens) {\n        ImVec4 col = ImVec4(\n            iris->codeview_color_register.Value.x,\n            iris->codeview_color_register.Value.y,\n            iris->codeview_color_register.Value.z,\n            iris->codeview_color_register.Value.w\n        );\n\n        if (t[0] == 'v' && (t[1] == 'f' || t[1] == 'i')) {\n            TextColored(col, \"%s\", t.c_str());\n        } else if (rtrim(t) == \"acc\") {\n            TextColored(col, \"%s\", t.c_str());\n        } else if (rtrim(t) == \"q\") {\n            TextColored(col, \"%s\", t.c_str());\n        } else if (rtrim(t) == \"p\") {\n            TextColored(col, \"%s\", t.c_str());\n        } else if (rtrim(t) == \"i\") {\n            TextColored(col, \"%s\", t.c_str());\n        } else if (rtrim(t) == \"r\") {\n            TextColored(col, \"%s\", t.c_str());\n        } else if (isalpha(t[0])) {\n            col = ImVec4(\n                iris->codeview_color_mnemonic.Value.x,\n                iris->codeview_color_mnemonic.Value.y,\n                iris->codeview_color_mnemonic.Value.z,\n                iris->codeview_color_mnemonic.Value.w\n            );\n\n            TextColored(col, \"%s\", t.c_str());\n        } else if (isdigit(t[0]) || t[0] == '-') {\n            col = ImVec4(\n                iris->codeview_color_number.Value.x,\n                iris->codeview_color_number.Value.y,\n                iris->codeview_color_number.Value.z,\n                iris->codeview_color_number.Value.w\n            );\n\n            TextColored(col, \"%s\", t.c_str());\n        } else if (t[0] == '<') {\n            col = ImVec4(\n                iris->codeview_color_other.Value.x,\n                iris->codeview_color_other.Value.y,\n                iris->codeview_color_other.Value.z,\n                iris->codeview_color_other.Value.w\n            );\n\n            TextColored(col, \"%s\", t.c_str());\n        } else {\n            Text(\"%s\", t.c_str());\n        }\n\n        SameLine(0.0f, 0.0f);\n    }\n\n    NewLine();\n}\n\nstatic void show_vu_disassembly_view(iris::instance* iris, uint64_t* mem, size_t size) {\n    using namespace ImGui;\n\n    float font_scale = GetStyle().FontScaleMain;\n\n    GetStyle().FontScaleMain = iris->codeview_font_scale;\n\n    PushFont(iris->font_code);\n\n    if (!iris->codeview_use_theme_background) {\n        PushStyleColor(ImGuiCol_TableRowBg, ImVec4(\n            iris->codeview_color_background.Value.x,\n            iris->codeview_color_background.Value.y,\n            iris->codeview_color_background.Value.z,\n            iris->codeview_color_background.Value.w\n        ));\n        PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4(\n            iris->codeview_color_background.Value.x,\n            iris->codeview_color_background.Value.y,\n            iris->codeview_color_background.Value.z,\n            iris->codeview_color_background.Value.w\n        ));\n        PushStyleColor(ImGuiCol_Text, ImVec4(\n            iris->codeview_color_text.Value.x,\n            iris->codeview_color_text.Value.y,\n            iris->codeview_color_text.Value.z,\n            iris->codeview_color_text.Value.w\n        ));\n        PushStyleColor(ImGuiCol_TextDisabled, ImVec4(\n            iris->codeview_color_comment.Value.x,\n            iris->codeview_color_comment.Value.y,\n            iris->codeview_color_comment.Value.z,\n            iris->codeview_color_comment.Value.w\n        ));\n    }\n\n    if (BeginTable(\"table1\", compact_view ? 2 : 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Hideable)) {\n        PushFont(iris->font_small_code);\n\n        TableSetupColumn(\" Address/Opcode\");\n        TableSetupColumn(compact_view ? \"Upper/Lower\" : \"Upper\");\n\n        if (!compact_view) {\n            TableSetupColumn(\"Lower\");\n        }\n\n        TableHeadersRow();\n        PopFont();\n\n        int e_bit = 0;\n\n        for (int row = disassemble_all ? 0 : addr; row < size; row++) {\n            g_vu_dis_state.addr = row * 8;\n\n            TableNextRow();\n            TableSetColumnIndex(0);\n\n            uint64_t u = mem[row] >> 32;\n            uint64_t l = mem[row] & 0xffffffff;\n\n            if (!compact_view) {\n                TextDisabled(\"%04x: %08x %08x\", row, u, l); SameLine();\n            } else {\n                TextDisabled(\"%04x: %08x\", row, u); SameLine();\n            }\n\n            TableSetColumnIndex(1);\n\n            char upper[512], lower[512];\n\n            g_vu_dis_state.addr = row;\n\n            vu_disassemble_upper(upper, u, &g_vu_dis_state);\n            vu_disassemble_lower(lower, l, &g_vu_dis_state);\n\n            if (add_padding && !compact_view) {\n#ifdef _WIN32\n                sprintf_s(upper, \"%-40s\", upper);\n                sprintf_s(lower, \"%-40s\", lower);\n#else\n                sprintf(upper, \"%-40s\", upper);\n                sprintf(lower, \"%-40s\", lower);\n#endif\n            }\n\n            print_highlighted_vu1(iris, upper);\n\n            if (!compact_view) {\n                TableSetColumnIndex(2);\n            }\n\n            print_highlighted_vu1(iris, lower);\n\n            if (e_bit && stop_at_e_bit && !disassemble_all) break;\n\n            e_bit = (u & 0x40000000) ? 1 : 0;\n        }\n\n        EndTable();\n    }\n\n    PopFont();\n\n    if (!iris->codeview_use_theme_background) {\n        PopStyleColor(4);\n    }\n\n    GetStyle().FontScaleMain = font_scale;\n}\n\nvoid save_disassembly(FILE* file, uint64_t* mem, size_t size) {\n    int e_bit = 0;\n\n    for (int row = disassemble_all ? 0 : addr; row < size; row++) {\n        g_vu_dis_state.addr = row * 8;\n\n        uint64_t u = mem[row] >> 32;\n        uint64_t l = mem[row] & 0xffffffff;\n\n        char upper[512], lower[512];\n\n        g_vu_dis_state.addr = row;\n\n        vu_disassemble_upper(upper, u, &g_vu_dis_state);\n        vu_disassemble_lower(lower, l, &g_vu_dis_state);\n\n        if (add_padding && !compact_view) {\n#ifdef _WIN32\n            sprintf_s(upper, \"%-40s\", upper);\n            sprintf_s(lower, \"%-40s\", lower);\n#else\n            sprintf(upper, \"%-40s\", upper);\n            sprintf(lower, \"%-40s\", lower);\n#endif\n        }\n\n        if (compact_view) {\n            fprintf(file, \"%04x: %08x %s\\n\", row, u, upper);\n            fprintf(file, \"      %08x %s\\n\", l, lower);\n        } else {\n            fprintf(file, \"%04x: %08x %08x %s %s\\n\", row, u, l, upper, lower);\n        }\n\n        if (e_bit && stop_at_e_bit && !disassemble_all) break;\n\n        e_bit = (u & 0x40000000) ? 1 : 0;\n    }\n}\n\nvoid show_vu_disassembler(iris::instance* iris) {\n    using namespace ImGui;\n\n    PushFont(iris->font_icons);\n\n    if (imgui::BeginEx(\"VU disassembler\", &iris->show_vu_disassembler, ImGuiWindowFlags_MenuBar)) {\n        if (BeginMenuBar()) {\n            if (BeginMenu(\"File\")) {\n                if (MenuItem(ICON_MS_FILE_SAVE \" Save disassembly as...\", NULL)) {\n                    \n                }\n\n                ImGui::EndMenu();\n            }\n\n            if (BeginMenu(\"Settings\")) {\n                MenuItem(ICON_MS_FORMAT_LETTER_SPACING_WIDER \" Add padding\", NULL, &add_padding);\n                MenuItem(ICON_MS_COLLAPSE_ALL \" Compact view\", NULL, &compact_view);\n\n                ImGui::EndMenu();\n            }\n\n            EndMenuBar();\n        }\n\n        if (BeginTabBar(\"##vudistabbar\", ImGuiTabBarFlags_Reorderable)) {\n            if (BeginTabItem(\"VU0\")) {\n                BeginDisabled(disassemble_all);\n                AlignTextToFramePadding();\n                Text(\"Address\"); SameLine();\n                \n                SetNextItemWidth(100.0f);\n                PushFont(iris->font_code);\n\n                if (InputInt(\"##address\", (int*)&addr, 0, 0, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll));\n\n                PopFont();\n\n                SameLine();\n                Checkbox(\"Stop at E bit\", &stop_at_e_bit);\n\n                EndDisabled();\n\n                SameLine();\n                Checkbox(\"Disassemble all\", &disassemble_all); SameLine();\n                if (Button(ICON_MS_SAVE)) {\n                    pfd::save_file file(\"Save VU0 disassembly\", \"vu0.s\", { \"Text files\", \"*.txt\" });\n\n                    if (!file.result().empty()) {\n                        FILE* f = fopen(file.result().c_str(), \"w\");\n\n                        if (f) {\n                            uint64_t* ptr = vu_get_micro_mem_ptr(iris->ps2->vu0, 0);\n\n                            save_disassembly(f, ptr, 512);\n\n                            fclose(f);\n                        } else {\n                            pfd::message(\"Error\", \"Failed to open file for writing.\", pfd::choice::ok, pfd::icon::error);\n                        }\n                    }\n                } SameLine();\n\n                BeginDisabled(!stop_at_e_bit);\n                if (Button(ICON_MS_ARROW_RIGHT_ALT \" Next program\")) {\n                    int prev = addr;\n\n                    addr = 0;\n\n                    for (int i = prev; i < 512; i++) {\n                        uint32_t upper = *vu_get_micro_mem_ptr(iris->ps2->vu0, i) >> 32;\n\n                        if (upper & 0x40000000) {\n                            addr = i + 2;\n\n                            break;\n                        }\n                    }\n                }\n                EndDisabled();\n\n                SeparatorText(\"Disassembly\");\n\n                if (BeginChild(\"vu0##disassembly\")) {\n                    uint64_t* ptr = vu_get_micro_mem_ptr(iris->ps2->vu0, 0);\n\n                    show_vu_disassembly_view(iris, ptr, 512);\n                } EndChild();\n            \n                EndTabItem();\n            }\n\n            if (BeginTabItem(\"VU1\")) {\n                BeginDisabled(disassemble_all);\n                AlignTextToFramePadding();\n                Text(\"Address\"); SameLine();\n\n                SetNextItemWidth(100.0f);\n                PushFont(iris->font_code);\n\n                if (InputInt(\"##address\", (int*)&addr, 0, 0, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll));\n\n                PopFont();\n\n                SameLine();\n                Checkbox(\"Stop at E bit\", &stop_at_e_bit);\n\n                EndDisabled();\n\n                SameLine();\n                Checkbox(\"Disassemble all\", &disassemble_all); SameLine();\n                if (Button(ICON_MS_SAVE)) {\n                    pfd::save_file file(\"Save VU1 disassembly\", \"vu1.s\", { \"Text files\", \"*.txt\" });\n\n                    if (!file.result().empty()) {\n                        FILE* f = fopen(file.result().c_str(), \"w\");\n\n                        if (f) {\n                            save_disassembly(f, iris->ps2->vu1->micro_mem, 2048);\n                            fclose(f);\n                        } else {\n                            pfd::message(\"Error\", \"Failed to open file for writing.\", pfd::choice::ok, pfd::icon::error);\n                        }\n                    }\n                } SameLine();\n\n                BeginDisabled(!stop_at_e_bit);\n                if (Button(ICON_MS_ARROW_RIGHT_ALT \" Next program\")) {\n                    int prev = addr;\n\n                    addr = 0;\n\n                    for (int i = prev; i < 2048; i++) {\n                        uint32_t upper = iris->ps2->vu1->micro_mem[i] >> 32;\n\n                        if (upper & 0x40000000) {\n                            addr = i + 2;\n\n                            break;\n                        }\n                    }\n                }\n                EndDisabled();\n\n                SeparatorText(\"Disassembly\");\n\n                if (BeginChild(\"vu1##disassembly\")) {\n                    show_vu_disassembly_view(iris, iris->ps2->vu1->micro_mem, 2048);\n                } EndChild();\n\n                EndTabItem();\n            }\n\n            EndTabBar();\n        }\n    } End();\n\n    PopFont();\n}\n\n}"
  },
  {
    "path": "frontend/vulkan.cpp",
    "content": "#include <algorithm>\n\n#include \"config.hpp\"\n#include \"iris.hpp\"\n\n#include <SDL3/SDL_vulkan.h>\n\n#include <volk.h>\n\nnamespace iris::vulkan {\n\nstd::vector <VkExtensionProperties> get_instance_extensions() {\n    std::vector <VkExtensionProperties> extensions;\n\n    uint32_t count;\n\n    vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr);\n\n    extensions.resize(count);\n\n    if (vkEnumerateInstanceExtensionProperties(nullptr, &count, extensions.data()) != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to enumerate instance extensions\\n\");\n\n        return {};\n    }\n\n    return extensions;\n}\n\nstd::vector <VkLayerProperties> get_instance_layers() {\n    std::vector <VkLayerProperties> layers;\n\n    uint32_t count;\n\n    vkEnumerateInstanceLayerProperties(&count, nullptr);\n\n    layers.resize(count);\n\n    if (vkEnumerateInstanceLayerProperties(&count, layers.data()) != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to enumerate instance layers\\n\");\n\n        return {};\n    }\n\n    return layers;\n}\n\nbool is_instance_extension_supported(iris::instance* iris, const char* name) {\n    return std::find_if(\n        iris->instance_extensions.begin(),\n        iris->instance_extensions.end(),\n        [name](const VkExtensionProperties& ext) {\n            return strncmp(ext.extensionName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0;\n        }\n    ) != iris->instance_extensions.end();\n}\n\nbool is_instance_layer_supported(iris::instance* iris, const char* name) {\n    return std::find_if(\n        iris->instance_layers.begin(),\n        iris->instance_layers.end(),\n        [name](const VkLayerProperties& layer) {\n            return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0;\n        }\n    ) != iris->instance_layers.end();\n}\n\nbool is_device_extension_supported(iris::instance* iris, const char* name) {\n    return std::find_if(\n        iris->device_extensions.begin(),\n        iris->device_extensions.end(),\n        [name](const VkExtensionProperties& ext) {\n            return strncmp(ext.extensionName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0;\n        }\n    ) != iris->device_extensions.end();\n}\n\nbool is_device_layer_supported(iris::instance* iris, const char* name) {\n    return std::find_if(\n        iris->device_layers.begin(),\n        iris->device_layers.end(),\n        [name](const VkLayerProperties& layer) {\n            return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0;\n        }\n    ) != iris->device_layers.end();\n}\n\nstd::vector <VkExtensionProperties> get_device_extensions(iris::instance* iris) {\n    std::vector <VkExtensionProperties> extensions;\n\n    uint32_t count;\n\n    vkEnumerateDeviceExtensionProperties(iris->physical_device, nullptr, &count, nullptr);\n\n    extensions.resize(count);\n\n    if (vkEnumerateDeviceExtensionProperties(iris->physical_device, nullptr, &count, extensions.data()) != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to enumerate device extensions\\n\");\n\n        return {};\n    }\n\n    return extensions;\n}\n\nstd::vector <VkLayerProperties> get_device_layers(iris::instance* iris) {\n    std::vector <VkLayerProperties> layers;\n\n    uint32_t count;\n\n    vkEnumerateDeviceLayerProperties(iris->physical_device, &count, nullptr);\n\n    layers.resize(count);\n\n    if (vkEnumerateDeviceLayerProperties(iris->physical_device, &count, layers.data()) != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to enumerate device layers\\n\");\n\n        return {};\n    }\n\n    return layers;\n}\n\nstruct instance_create_info {\n    std::vector <const char*> enabled_extensions;\n    std::vector <const char*> enabled_layers;\n    VkInstanceCreateFlags flags = 0;\n};\n\nVkInstance create_instance(iris::instance* iris, const instance_create_info& info) {\n    VkInstance instance = VK_NULL_HANDLE;\n\n    for (const char* ext : info.enabled_extensions) {\n        if (!is_instance_extension_supported(iris, ext)) {\n            fprintf(stderr, \"vulkan: Requested instance extension not supported: %s\\n\", ext);\n\n            continue;\n        }\n\n        iris->enabled_instance_extensions.push_back(ext);\n    }\n\n    for (const char* layer : info.enabled_layers) {\n        if (!is_instance_layer_supported(iris, layer)) {\n            fprintf(stderr, \"vulkan: Requested instance layer not supported: %s\\n\", layer);\n\n            continue;\n        }\n\n        iris->enabled_instance_layers.push_back(layer);\n    }\n\n    iris->app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO;\n    iris->app_info.pApplicationName = IRIS_TITLE;\n    iris->app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0);\n    iris->app_info.pEngineName = \"Vulkan\";\n    iris->app_info.engineVersion = VK_MAKE_VERSION(1, 1, 0);\n    iris->app_info.apiVersion = IRIS_VULKAN_API_VERSION;\n    iris->app_info.pNext = VK_NULL_HANDLE;\n\n    iris->instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO;\n    iris->instance_create_info.pApplicationInfo = &iris->app_info;\n    iris->instance_create_info.enabledExtensionCount = iris->enabled_instance_extensions.size();\n    iris->instance_create_info.ppEnabledExtensionNames = iris->enabled_instance_extensions.data();\n\n    iris->instance_create_info.enabledLayerCount = iris->enabled_instance_layers.size();\n    iris->instance_create_info.ppEnabledLayerNames = iris->enabled_instance_layers.data();\n    iris->instance_create_info.flags = info.flags;\n\n    if (vkCreateInstance(&iris->instance_create_info, nullptr, &instance) != VK_SUCCESS) {\n        return VK_NULL_HANDLE;\n    }\n\n    return instance;\n}\n\nstatic inline uint32_t find_memory_type(iris::instance* iris, uint32_t filter, VkMemoryPropertyFlags properties) {\n    VkPhysicalDeviceMemoryProperties mp;\n    vkGetPhysicalDeviceMemoryProperties(iris->physical_device, &mp);\n\n    for (uint32_t i = 0; i < mp.memoryTypeCount; i++) {\n        if ((filter & (1 << i)) && (mp.memoryTypes[i].propertyFlags & properties) == properties) {\n            return i;\n        }\n    }\n\n    return 0;\n}\n\nstruct device_create_info {\n    std::vector <const char*> enabled_extensions;\n    std::vector <const char*> enabled_layers;\n    VkPhysicalDeviceFeatures enabled_features = {};\n    void* data;\n};\n\nVkDevice create_device(iris::instance* iris, const device_create_info& info) {\n    VkDevice device = VK_NULL_HANDLE;\n\n    for (const char* ext : info.enabled_extensions) {\n        if (!is_device_extension_supported(iris, ext)) {\n            fprintf(stderr, \"vulkan: Requested device extension not supported: %s\\n\", ext);\n\n            continue;\n        }\n\n        iris->enabled_device_extensions.push_back(ext);\n    }\n\n    iris->cubic_supported = is_device_extension_supported(iris, VK_EXT_FILTER_CUBIC_EXTENSION_NAME);\n\n    for (const char* layer : info.enabled_layers) {\n        if (!is_device_layer_supported(iris, layer)) {\n            fprintf(stderr, \"vulkan: Requested device layer not supported: %s\\n\", layer);\n\n            continue;\n        }\n\n        iris->enabled_device_layers.push_back(layer);\n    }\n\n    iris->device_features = {};\n    iris->device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;\n    iris->device_features.pNext = info.data;\n\n    VkPhysicalDeviceFeatures2 supported_features = {};\n    supported_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2;\n    supported_features.pNext = nullptr;\n\n    vkGetPhysicalDeviceFeatures2(iris->physical_device, &supported_features);\n\n#define SET_FEATURE(f) \\\n    iris->device_features.features.f = (supported_features.features.f && info.enabled_features.f)\n\n    SET_FEATURE(robustBufferAccess);\n    SET_FEATURE(fullDrawIndexUint32);\n    SET_FEATURE(imageCubeArray);\n    SET_FEATURE(independentBlend);\n    SET_FEATURE(geometryShader);\n    SET_FEATURE(tessellationShader);\n    SET_FEATURE(sampleRateShading);\n    SET_FEATURE(dualSrcBlend);\n    SET_FEATURE(logicOp);\n    SET_FEATURE(multiDrawIndirect);\n    SET_FEATURE(drawIndirectFirstInstance);\n    SET_FEATURE(depthClamp);\n    SET_FEATURE(depthBiasClamp);\n    SET_FEATURE(fillModeNonSolid);\n    SET_FEATURE(depthBounds);\n    SET_FEATURE(wideLines);\n    SET_FEATURE(largePoints);\n    SET_FEATURE(alphaToOne);\n    SET_FEATURE(multiViewport);\n    SET_FEATURE(samplerAnisotropy);\n    SET_FEATURE(textureCompressionETC2);\n    SET_FEATURE(textureCompressionASTC_LDR);\n    SET_FEATURE(textureCompressionBC);\n    SET_FEATURE(occlusionQueryPrecise);\n    SET_FEATURE(pipelineStatisticsQuery);\n    SET_FEATURE(vertexPipelineStoresAndAtomics);\n    SET_FEATURE(fragmentStoresAndAtomics);\n    SET_FEATURE(shaderTessellationAndGeometryPointSize);\n    SET_FEATURE(shaderImageGatherExtended);\n    SET_FEATURE(shaderStorageImageExtendedFormats);\n    SET_FEATURE(shaderStorageImageMultisample);\n    SET_FEATURE(shaderStorageImageReadWithoutFormat);\n    SET_FEATURE(shaderStorageImageWriteWithoutFormat);\n    SET_FEATURE(shaderUniformBufferArrayDynamicIndexing);\n    SET_FEATURE(shaderSampledImageArrayDynamicIndexing);\n    SET_FEATURE(shaderStorageBufferArrayDynamicIndexing);\n    SET_FEATURE(shaderStorageImageArrayDynamicIndexing);\n    SET_FEATURE(shaderClipDistance);\n    SET_FEATURE(shaderCullDistance);\n    SET_FEATURE(shaderFloat64);\n    SET_FEATURE(shaderInt64);\n    SET_FEATURE(shaderInt16);\n    SET_FEATURE(shaderResourceResidency);\n    SET_FEATURE(shaderResourceMinLod);\n    SET_FEATURE(sparseBinding);\n    SET_FEATURE(sparseResidencyBuffer);\n    SET_FEATURE(sparseResidencyImage2D);\n    SET_FEATURE(sparseResidencyImage3D);\n    SET_FEATURE(sparseResidency2Samples);\n    SET_FEATURE(sparseResidency4Samples);\n    SET_FEATURE(sparseResidency8Samples);\n    SET_FEATURE(sparseResidency16Samples);\n    SET_FEATURE(sparseResidencyAliased);\n    SET_FEATURE(variableMultisampleRate);\n    SET_FEATURE(inheritedQueries);\n\n#undef SET_FEATURE\n\n    const float queue_priority[] = { 1.0f };\n\n    iris->queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO;\n    iris->queue_create_info.queueFamilyIndex = iris->queue_family;\n    iris->queue_create_info.queueCount = 1;\n    iris->queue_create_info.pQueuePriorities = queue_priority;\n\n    iris->device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO;\n    iris->device_create_info.queueCreateInfoCount = 1;\n    iris->device_create_info.pQueueCreateInfos = &iris->queue_create_info;\n    iris->device_create_info.enabledExtensionCount = iris->enabled_device_extensions.size();\n    iris->device_create_info.ppEnabledExtensionNames = iris->enabled_device_extensions.data();\n    iris->device_create_info.enabledLayerCount = iris->enabled_device_layers.size();\n    iris->device_create_info.ppEnabledLayerNames = iris->enabled_device_layers.data();\n    iris->device_create_info.pEnabledFeatures = VK_NULL_HANDLE;\n    iris->device_create_info.pNext = &iris->device_features;\n\n    if (vkCreateDevice(iris->physical_device, &iris->device_create_info, nullptr, &device) != VK_SUCCESS) {\n        return VK_NULL_HANDLE;\n    }\n\n    return device;\n}\n\nvoid enumerate_physical_devices(iris::instance* iris) {\n    uint32_t count = 0;\n\n    vkEnumeratePhysicalDevices(iris->instance, &count, nullptr);\n\n    if (!count) {\n        return;\n    }\n\n    std::vector <VkPhysicalDevice> devices(count);\n\n    vkEnumeratePhysicalDevices(iris->instance, &count, devices.data());\n\n    iris->vulkan_gpus.clear();\n\n    for (const VkPhysicalDevice& device : devices) {\n        VkPhysicalDeviceProperties properties;\n\n        vkGetPhysicalDeviceProperties(device, &properties);\n\n        iris::vulkan_gpu gpu;\n\n        gpu.device = device;\n        gpu.type = properties.deviceType;\n        gpu.name = properties.deviceName;\n        gpu.api_version = properties.apiVersion;\n\n        iris->vulkan_gpus.push_back(gpu);\n    }\n}\n\nVkPhysicalDevice find_suitable_physical_device(iris::instance* iris) {\n    if (!iris->vulkan_gpus.size())\n        return VK_NULL_HANDLE;\n\n    for (int i = 0; i < iris->vulkan_gpus.size(); i++) {\n        auto& dev = iris->vulkan_gpus[i];\n\n        if (dev.type == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) {\n            iris->vulkan_selected_device_index = i;\n\n            return dev.device;\n        }\n    }\n\n    iris->vulkan_selected_device_index = 0;\n\n    // Just pick the first device for now\n    return iris->vulkan_gpus[0].device;\n}\n\nint find_graphics_queue_family_index(iris::instance* iris) {\n    uint32_t count = 0;\n\n    vkGetPhysicalDeviceQueueFamilyProperties(iris->physical_device, &count, nullptr);\n\n    if (!count) {\n        return -1;\n    }\n\n    std::vector <VkQueueFamilyProperties> queue_families(count);\n\n    vkGetPhysicalDeviceQueueFamilyProperties(iris->physical_device, &count, queue_families.data());\n\n    // Just return the first graphics-capable queue family, we should\n    // actually be looking for dedicated compute/transfer queues\n    for (uint32_t i = 0; i < count; i++) {\n        if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) {\n            return i;\n        }\n    }\n\n    return -1;\n}\n\nVkBuffer create_buffer(iris::instance* iris, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkDeviceMemory& buffer_memory) {\n    VkBuffer buffer = VK_NULL_HANDLE;\n\n    VkBufferCreateInfo buffer_info = {};\n    buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO;\n    buffer_info.size = size;\n    buffer_info.usage = usage;\n    buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n\n    if (vkCreateBuffer(iris->device, &buffer_info, nullptr, &buffer) != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to create buffer\\n\");\n\n        return VK_NULL_HANDLE;\n    }\n\n    VkMemoryRequirements memory_requirements;\n    vkGetBufferMemoryRequirements(iris->device, buffer, &memory_requirements);\n\n    VkMemoryAllocateInfo alloc_info = {};\n    alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n    alloc_info.allocationSize = memory_requirements.size;\n\n    VkPhysicalDeviceMemoryProperties memory_properties;\n    vkGetPhysicalDeviceMemoryProperties(iris->physical_device, &memory_properties);\n\n    for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) {\n        if ((memory_requirements.memoryTypeBits & (1 << i)) &&\n            (memory_properties.memoryTypes[i].propertyFlags & properties) == properties) {\n            alloc_info.memoryTypeIndex = i;\n            break;\n        }\n    }\n\n    if (vkAllocateMemory(iris->device, &alloc_info, nullptr, &buffer_memory) != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to allocate buffer memory\\n\");\n\n        vkDestroyBuffer(iris->device, buffer, nullptr);\n\n        return VK_NULL_HANDLE;\n    }\n\n    vkBindBufferMemory(iris->device, buffer, buffer_memory, 0);\n\n    return buffer;\n}\n\nvoid load_buffer(iris::instance* iris, VkDeviceMemory buffer_memory, void* data, VkDeviceSize size) {\n    void* ptr;\n\n    vkMapMemory(iris->device, buffer_memory, 0, size, 0, &ptr);\n    memcpy(ptr, data, (size_t)size);\n    vkUnmapMemory(iris->device, buffer_memory);\n}\n\nbool copy_buffer(iris::instance* iris, VkBuffer src, VkBuffer dst, VkDeviceSize size) {\n    VkCommandPool command_pool = VK_NULL_HANDLE;\n\n    VkCommandPoolCreateInfo info = {};\n    info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;\n    info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;\n    info.queueFamilyIndex = iris->queue_family;\n\n    if (vkCreateCommandPool(iris->device, &info, VK_NULL_HANDLE, &command_pool) != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to create command pool\\n\");\n    \n        return false;\n    }\n\n    VkCommandBufferAllocateInfo alloc_info = {};\n    alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;\n    alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n    alloc_info.commandPool = command_pool;\n    alloc_info.commandBufferCount = 1;\n\n    VkCommandBuffer command_buffer;\n    vkAllocateCommandBuffers(iris->device, &alloc_info, &command_buffer);\n\n    VkCommandBufferBeginInfo begin_info{};\n    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;\n    begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;\n\n    vkBeginCommandBuffer(command_buffer, &begin_info);\n\n    VkBufferCopy copy_region{};\n    copy_region.size = size;\n    vkCmdCopyBuffer(command_buffer, src, dst, 1, &copy_region);\n\n    vkEndCommandBuffer(command_buffer);\n\n    VkSubmitInfo submit_info{};\n    submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n    submit_info.commandBufferCount = 1;\n    submit_info.pCommandBuffers = &command_buffer;\n\n    vkQueueSubmit(iris->queue, 1, &submit_info, VK_NULL_HANDLE);\n    vkQueueWaitIdle(iris->queue);\n\n    vkFreeCommandBuffers(iris->device, command_pool, 1, &command_buffer);\n    vkDestroyCommandPool(iris->device, command_pool, VK_NULL_HANDLE);\n\n    return true;\n}\n\nbool create_descriptor_pool(iris::instance* iris) {\n    VkDescriptorPoolSize pool_sizes[] = {\n        { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 512 }\n    };\n\n    VkDescriptorPoolCreateInfo pool_info = {};\n    pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO;\n    pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT;\n    pool_info.maxSets = 0;\n\n    for (VkDescriptorPoolSize& pool_size : pool_sizes)\n        pool_info.maxSets += pool_size.descriptorCount;\n\n    pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes);\n    pool_info.pPoolSizes = pool_sizes;\n\n    if (vkCreateDescriptorPool(iris->device, &pool_info, VK_NULL_HANDLE, &iris->descriptor_pool) != VK_SUCCESS) {\n        fprintf(stderr, \"imgui: Failed to create descriptor pool\\n\");\n\n        return false;\n    }\n\n    return true;\n}\n\ntexture upload_texture(iris::instance* iris, void* pixels, int width, int height, int stride) {\n    texture tex = {};\n\n    tex.width = width;\n    tex.height = height;\n    tex.stride = stride;\n    tex.image_size = width * height * 4;\n\n    VkDeviceMemory staging_buffer_memory = VK_NULL_HANDLE;\n    VkBuffer staging_buffer = create_buffer(\n        iris,\n        tex.image_size,\n        VK_BUFFER_USAGE_TRANSFER_SRC_BIT,\n        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,\n        staging_buffer_memory\n    );\n\n    if (staging_buffer == VK_NULL_HANDLE)\n        return {};\n\n    load_buffer(iris, staging_buffer_memory, pixels, tex.image_size);\n\n    // To-do: Transition image layout and copy buffer to image\n    // Create the Vulkan image.\n    {\n        VkImageCreateInfo info = {};\n        info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO;\n        info.imageType = VK_IMAGE_TYPE_2D;\n        info.format = VK_FORMAT_R8G8B8A8_UNORM;\n        info.extent.width = width;\n        info.extent.height = height;\n        info.extent.depth = 1;\n        info.mipLevels = 1;\n        info.arrayLayers = 1;\n        info.samples = VK_SAMPLE_COUNT_1_BIT;\n        info.tiling = VK_IMAGE_TILING_OPTIMAL;\n        info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT;\n        info.sharingMode = VK_SHARING_MODE_EXCLUSIVE;\n        info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n        \n        if (vkCreateImage(iris->device, &info, VK_NULL_HANDLE, &tex.image) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to create image\\n\");\n\n            return {};\n        }\n\n        VkMemoryRequirements req;\n        vkGetImageMemoryRequirements(iris->device, tex.image, &req);\n        VkMemoryAllocateInfo alloc_info = {};\n        alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n        alloc_info.allocationSize = req.size;\n        VkPhysicalDeviceMemoryProperties memory_properties;\n        vkGetPhysicalDeviceMemoryProperties(iris->physical_device, &memory_properties);\n\n        for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) {\n            if ((req.memoryTypeBits & (1 << i)) &&\n                (memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) {\n                alloc_info.memoryTypeIndex = i;\n                break;\n            }\n        }\n        if (vkAllocateMemory(iris->device, &alloc_info, VK_NULL_HANDLE, &tex.image_memory) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to allocate image memory\\n\");\n\n            return {};\n        }\n\n        if (vkBindImageMemory(iris->device, tex.image, tex.image_memory, 0) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to bind image memory\\n\");\n\n            return {};\n        }\n    }\n\n    // Create the Image View\n    {\n        VkImageViewCreateInfo info = {};\n        info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO;\n        info.image = tex.image;\n        info.viewType = VK_IMAGE_VIEW_TYPE_2D;\n        info.format = VK_FORMAT_R8G8B8A8_UNORM;\n        info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n        info.subresourceRange.levelCount = 1;\n        info.subresourceRange.layerCount = 1;\n        if (vkCreateImageView(iris->device, &info, VK_NULL_HANDLE, &tex.image_view) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to create image view\\n\");\n\n            return {};\n        }\n    }\n\n    // Create Sampler\n    {\n        VkSamplerCreateInfo sampler_info{};\n        sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO;\n        sampler_info.magFilter = VK_FILTER_LINEAR;\n        sampler_info.minFilter = VK_FILTER_LINEAR;\n        sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR;\n        sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; // outside image bounds just use border color\n        sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT;\n        sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT;\n        sampler_info.minLod = -1000;\n        sampler_info.maxLod = 1000;\n        sampler_info.maxAnisotropy = 1.0f;\n        if (vkCreateSampler(iris->device, &sampler_info, VK_NULL_HANDLE, &tex.sampler) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to create sampler\\n\");\n\n            return {};\n        }\n    }\n\n    {\n        VkDescriptorSetAllocateInfo alloc_info = {};\n        alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO;\n        alloc_info.descriptorPool = iris->descriptor_pool;\n        alloc_info.descriptorSetCount = 1;\n        alloc_info.pSetLayouts = &iris->descriptor_set_layout;\n        if (vkAllocateDescriptorSets(iris->device, &alloc_info, &tex.descriptor_set) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to allocate descriptor sets\\n\");\n\n            return {};\n        }\n    }\n\n    // Update the Descriptor Set:\n    {\n        VkDescriptorImageInfo desc_image[1] = {};\n        desc_image[0].sampler = tex.sampler;\n        desc_image[0].imageView = tex.image_view;\n        desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n        VkWriteDescriptorSet write_desc[1] = {};\n        write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET;\n        write_desc[0].dstSet = tex.descriptor_set;\n        write_desc[0].descriptorCount = 1;\n        write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER;\n        write_desc[0].pImageInfo = desc_image;\n        vkUpdateDescriptorSets(iris->device, 1, write_desc, 0, nullptr);\n    }\n\n    VkCommandPool command_pool = VK_NULL_HANDLE;\n\n    VkCommandPoolCreateInfo info = {};\n    info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;\n    info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;\n    info.queueFamilyIndex = iris->queue_family;\n\n    if (vkCreateCommandPool(iris->device, &info, VK_NULL_HANDLE, &command_pool) != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to create command pool\\n\");\n    \n        return {};\n    }\n\n    VkCommandBuffer command_buffer;\n\n    {\n        VkCommandBufferAllocateInfo alloc_info{};\n        alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;\n        alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n        alloc_info.commandPool = command_pool;\n        alloc_info.commandBufferCount = 1;\n\n        if (vkAllocateCommandBuffers(iris->device, &alloc_info, &command_buffer) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to allocate command buffers\\n\");\n\n            return {};\n        }\n\n        VkCommandBufferBeginInfo begin_info = {};\n        begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;\n        begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;\n        \n        if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {\n            printf(\"vulkan: Failed to begin command buffer\\n\");\n\n            return {};\n        }\n    }\n\n    // Copy to Image\n    {\n        VkImageMemoryBarrier copy_barrier[1] = {};\n        copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n        copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n        copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n        copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;\n        copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n        copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n        copy_barrier[0].image = tex.image;\n        copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n        copy_barrier[0].subresourceRange.levelCount = 1;\n        copy_barrier[0].subresourceRange.layerCount = 1;\n        vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, copy_barrier);\n\n        VkBufferImageCopy region = {};\n        region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n        region.imageSubresource.layerCount = 1;\n        region.imageExtent.width = width;\n        region.imageExtent.height = height;\n        region.imageExtent.depth = 1;\n        vkCmdCopyBufferToImage(command_buffer, staging_buffer, tex.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &region);\n\n        VkImageMemoryBarrier use_barrier[1] = {};\n        use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n        use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT;\n        use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT;\n        use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL;\n        use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n        use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n        use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED;\n        use_barrier[0].image = tex.image;\n        use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n        use_barrier[0].subresourceRange.levelCount = 1;\n        use_barrier[0].subresourceRange.layerCount = 1;\n        vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, use_barrier);\n    }\n\n    // End command buffer\n    {\n        VkSubmitInfo end_info = {};\n        end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n        end_info.commandBufferCount = 1;\n        end_info.pCommandBuffers = &command_buffer;\n\n        if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to end command buffer\\n\");\n\n            return {};\n        }\n\n\n        if (vkQueueSubmit(iris->queue, 1, &end_info, VK_NULL_HANDLE) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to submit queue\\n\");\n\n            return {};\n        } \n\n        \n        if (vkDeviceWaitIdle(iris->device) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to wait device idle\\n\");\n\n            return {};\n        } \n    }\n\n    vkDestroyCommandPool(iris->device, command_pool, nullptr);\n    vkDestroyBuffer(iris->device, staging_buffer, nullptr);\n    vkFreeMemory(iris->device, staging_buffer_memory, nullptr);\n\n    return tex;\n}\n\nvoid free_texture(iris::instance* iris, texture& tex) {\n    if (!iris->device)\n        return;\n\n    if (tex.sampler) vkDestroySampler(iris->device, tex.sampler, nullptr);\n    if (tex.image_view) vkDestroyImageView(iris->device, tex.image_view, nullptr);\n    if (tex.image) vkDestroyImage(iris->device, tex.image, nullptr);\n    if (tex.image_memory) vkFreeMemory(iris->device, tex.image_memory, nullptr);\n}\n\nbool init(iris::instance* iris, bool enable_validation) {\n    if (volkInitialize() != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to initialize volk loader\\n\");\n\n        return false;\n    }\n\n    iris->instance_extensions = get_instance_extensions();\n    iris->instance_layers = get_instance_layers();\n\n    std::vector <const char*> extensions = {\n        VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME\n    };\n\n    std::vector <const char*> layers;\n\n    if (enable_validation) {\n        layers.push_back(\"VK_LAYER_KHRONOS_validation\");\n    }\n\n    // Push SDL extensions\n    uint32_t sdl_extension_count;\n    auto sdl_extensions = SDL_Vulkan_GetInstanceExtensions(&sdl_extension_count);\n\n    for (uint32_t i = 0; i < sdl_extension_count; i++) {\n        extensions.push_back(sdl_extensions[i]);\n    }\n\n    VkInstanceCreateFlags flags = 0;\n\n    // Needed for MoltenVK on macOS\n#if defined(__APPLE__) && defined(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME)\n    extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME);\n\n    flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR;\n#endif\n\n    instance_create_info instance_info = {};\n    instance_info.enabled_extensions = extensions;\n    instance_info.enabled_layers = layers;\n    instance_info.flags = flags;\n\n    iris->instance = create_instance(iris, instance_info);\n\n    if (!iris->instance) {\n        fprintf(stderr, \"vulkan: Failed to create Vulkan instance\\n\");\n\n        return false;\n    }\n\n    volkLoadInstance(iris->instance);\n\n    // Find a suitable Vulkan physical device (GPU)\n    enumerate_physical_devices(iris);\n\n    iris->vulkan_selected_device_index = 0;\n\n    if (iris->vulkan_physical_device < 0) {\n        iris->physical_device = find_suitable_physical_device(iris);\n    } else {\n        if (iris->vulkan_physical_device >= iris->vulkan_gpus.size()) {\n            iris->physical_device = find_suitable_physical_device(iris);\n            iris->vulkan_physical_device = iris->vulkan_selected_device_index;\n        } else {\n            iris->physical_device = iris->vulkan_gpus[iris->vulkan_physical_device].device;\n            iris->vulkan_selected_device_index = iris->vulkan_physical_device;\n        }\n    }\n\n    if (!iris->physical_device) {\n        fprintf(stderr, \"vulkan: Failed to find a suitable Vulkan device\\n\");\n\n        return false;\n    }\n\n    VkPhysicalDeviceProperties properties;\n    vkGetPhysicalDeviceProperties(iris->physical_device, &properties);\n\n    printf(\"vulkan: Using Vulkan device \\\"%s\\\". API version %d.%d.%d.%d Driver version %x\\n\",\n        properties.deviceName,\n        VK_API_VERSION_MAJOR(properties.apiVersion),\n        VK_API_VERSION_MINOR(properties.apiVersion),\n        VK_API_VERSION_PATCH(properties.apiVersion),\n        VK_API_VERSION_VARIANT(properties.apiVersion),\n        properties.driverVersion\n    );\n\n    iris->device_extensions = get_device_extensions(iris);\n    iris->device_layers = get_device_layers(iris);\n\n    // Find a graphics-capable queue family\n    int queue_family = find_graphics_queue_family_index(iris);\n\n    if (queue_family == -1) {\n        fprintf(stderr, \"vulkan: Failed to find a graphics-capable queue family\\n\");\n\n        return false;\n    }\n\n    iris->queue_family = queue_family;\n\n    // To-do: Query required extensions/features from backends here.\n    //        For now we'll just initialize a fixed set of extensions\n    //        and features.\n\n    device_create_info device_info = {};\n    device_info.enabled_extensions = {\n        VK_KHR_SWAPCHAIN_EXTENSION_NAME,\n        VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME,\n        VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME,\n        VK_KHR_8BIT_STORAGE_EXTENSION_NAME,\n        // VK_KHR_16BIT_STORAGE_EXTENSION_NAME,\n        VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME,\n        VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME,\n        VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME,\n        VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME,\n        VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME,\n        VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME,\n        VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME,\n        VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME,\n        VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME,\n        VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME,\n        // VK_KHR_MAINTENANCE_5_EXTENSION_NAME,\n        VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME,\n        VK_EXT_MEMORY_BUDGET_EXTENSION_NAME,\n        VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME,\n        // VK_EXT_MESH_SHADER_EXTENSION_NAME,\n        VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME,\n        VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME,\n        VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME,\n        VK_EXT_FILTER_CUBIC_EXTENSION_NAME\n    };\n\n    device_info.enabled_layers = {};\n\n#ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME\n    device_info.enabled_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME);\n#endif\n\n    device_info.enabled_features = {};\n    device_info.enabled_features.shaderInt16 = VK_TRUE;\n\n    iris->vulkan_11_features.pNext = &iris->vulkan_12_features;\n    iris->vulkan_12_features.pNext = &iris->subgroup_size_control_features;\n    iris->subgroup_size_control_features.pNext = VK_NULL_HANDLE;\n\n    iris->vulkan_11_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES;\n    iris->vulkan_12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES;\n    iris->subgroup_size_control_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES;\n\n    iris->vulkan_11_features.storageBuffer16BitAccess = VK_TRUE;\n    iris->vulkan_11_features.uniformAndStorageBuffer16BitAccess = VK_TRUE;\n    iris->vulkan_12_features.descriptorIndexing = VK_TRUE;\n    iris->vulkan_12_features.descriptorBindingPartiallyBound = VK_TRUE;\n    iris->vulkan_12_features.descriptorBindingVariableDescriptorCount = VK_TRUE;\n    iris->vulkan_12_features.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE;\n    iris->vulkan_12_features.runtimeDescriptorArray = VK_TRUE;\n    iris->vulkan_12_features.timelineSemaphore = VK_TRUE;\n    iris->vulkan_12_features.bufferDeviceAddress = VK_TRUE;\n    iris->vulkan_12_features.scalarBlockLayout = VK_TRUE;\n    iris->vulkan_12_features.storageBuffer8BitAccess = VK_TRUE;\n    iris->vulkan_12_features.uniformAndStorageBuffer8BitAccess = VK_TRUE;\n    \n    iris->subgroup_size_control_features.subgroupSizeControl = VK_TRUE;\n    iris->subgroup_size_control_features.computeFullSubgroups = VK_TRUE;\n\n    // Chain in all feature structs\n    device_info.data = &iris->vulkan_11_features;\n\n    iris->device = create_device(iris, device_info);\n\n    if (!iris->device) {\n        fprintf(stderr, \"vulkan: Failed to create Vulkan device\\n\");\n\n        return false;\n    }\n\n    vkGetDeviceQueue(iris->device, iris->queue_family, 0, &iris->queue);\n\n    iris->indices = { 0, 1, 2, 2, 3, 0 };\n\n    iris->vertex_buffer_size = sizeof(vertex) * iris->vertices.size();\n\n    // Create vertex and index buffers\n    // Create and populate index buffer immediately by creating\n    // a staging buffer, filling it, and then copying it over to\n    // the device local index buffer.\n    // The vertex buffer will be updated dynamically each frame.\n    VkDeviceMemory index_staging_buffer_memory;\n    VkDeviceSize index_buffer_size = sizeof(uint16_t) * iris->indices.size();\n\n    iris->index_buffer = create_buffer(\n        iris,\n        sizeof(uint16_t) * iris->indices.size(),\n        VK_BUFFER_USAGE_TRANSFER_DST_BIT |\n        VK_BUFFER_USAGE_INDEX_BUFFER_BIT,\n        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,\n        iris->index_buffer_memory\n    );\n\n    VkBuffer index_staging_buffer = create_buffer(\n        iris,\n        index_buffer_size,\n        VK_BUFFER_USAGE_TRANSFER_SRC_BIT,\n        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |\n        VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, \n        index_staging_buffer_memory\n    );\n\n    load_buffer(iris, index_staging_buffer_memory, iris->indices.data(), index_buffer_size);\n    copy_buffer(iris, index_staging_buffer, iris->index_buffer, index_buffer_size);\n\n    iris->vertex_buffer = create_buffer(\n        iris,\n        iris->vertex_buffer_size,\n        VK_BUFFER_USAGE_TRANSFER_DST_BIT |\n        VK_BUFFER_USAGE_VERTEX_BUFFER_BIT,\n        VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT,\n        iris->vertex_buffer_memory\n    );\n\n    iris->vertex_staging_buffer = create_buffer(\n        iris,\n        iris->vertex_buffer_size,\n        VK_BUFFER_USAGE_TRANSFER_SRC_BIT,\n        VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT |\n        VK_MEMORY_PROPERTY_HOST_COHERENT_BIT,\n        iris->vertex_staging_buffer_memory\n    );\n\n    // We don't need the staging buffer anymore\n    vkFreeMemory(iris->device, index_staging_buffer_memory, nullptr);\n    vkDestroyBuffer(iris->device, index_staging_buffer, nullptr);\n\n    create_descriptor_pool(iris);\n\n    return true;\n}\n\nstatic int ic = 0;\n\nvoid cleanup(iris::instance* iris) {\n    vulkan::wait_idle(iris);\n\n    if (iris->descriptor_set_layout) vkDestroyDescriptorSetLayout(iris->device, iris->descriptor_set_layout, nullptr);\n    if (iris->descriptor_pool) vkDestroyDescriptorPool(iris->device, iris->descriptor_pool, nullptr);\n\n    for (int i = 0; i < 3; i++)\n        if (iris->sampler[i]) vkDestroySampler(iris->device, iris->sampler[i], nullptr);\n\n    if (iris->vertex_buffer) vkDestroyBuffer(iris->device, iris->vertex_buffer, nullptr);\n    if (iris->vertex_staging_buffer) vkDestroyBuffer(iris->device, iris->vertex_staging_buffer, nullptr);\n    if (iris->index_buffer) vkDestroyBuffer(iris->device, iris->index_buffer, nullptr);\n    if (iris->vertex_staging_buffer_memory) vkFreeMemory(iris->device, iris->vertex_staging_buffer_memory, nullptr);\n    if (iris->vertex_buffer_memory) vkFreeMemory(iris->device, iris->vertex_buffer_memory, nullptr);\n    if (iris->index_buffer_memory) vkFreeMemory(iris->device, iris->index_buffer_memory, nullptr);\n    if (iris->pipeline) vkDestroyPipeline(iris->device, iris->pipeline, nullptr);\n    // ImGui takes care of this apparently (probably shouldn't)\n    // if (iris->surface) vkDestroySurfaceKHR(iris->instance, iris->surface, nullptr);\n    if (iris->render_pass) vkDestroyRenderPass(iris->device, iris->render_pass, nullptr);\n    if (iris->pipeline_layout) vkDestroyPipelineLayout(iris->device, iris->pipeline_layout, nullptr);\n    if (iris->device) vkDestroyDevice(iris->device, nullptr);\n    if (iris->instance) vkDestroyInstance(iris->instance, nullptr);\n}\n\nvoid insert_image_memory_barrier(\n    VkCommandBuffer buffer,\n    VkImage image,\n    VkAccessFlags src_access_mask,\n    VkAccessFlags dst_access_mask,\n    VkImageLayout old_image_layout,\n    VkImageLayout new_image_layout,\n    VkPipelineStageFlags src_stage_mask,\n    VkPipelineStageFlags dst_stage_mask,\n    VkImageSubresourceRange subresource_range)\n{\n    VkImageMemoryBarrier image_memory_barrier = {};\n    image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER;\n    image_memory_barrier.srcAccessMask = src_access_mask;\n    image_memory_barrier.dstAccessMask = dst_access_mask;\n    image_memory_barrier.oldLayout = old_image_layout;\n    image_memory_barrier.newLayout = new_image_layout;\n    image_memory_barrier.image = image;\n    image_memory_barrier.subresourceRange = subresource_range;\n\n    vkCmdPipelineBarrier(\n        buffer,\n        src_stage_mask,\n        dst_stage_mask,\n        0,\n        0, nullptr,\n        0, nullptr,\n        1, &image_memory_barrier\n    );\n}\n\nvoid* read_image(iris::instance* iris, VkImage src_image, VkFormat format, int width, int height) {\n    if (src_image == VK_NULL_HANDLE) {\n        printf(\"vulkan: Source image is null\\n\");\n\n        return nullptr;\n    }\n\n    if (!width || !height) {\n        printf(\"vulkan: Invalid image dimensions for readback (%dx%d)\\n\", width, height);\n\n        return nullptr;\n    }\n\n    bool supports_blit = true;\n\n    // Check blit support for source and destination\n    VkFormatProperties format_props;\n\n    // Check if the device supports blitting from optimal images (the swapchain images are in optimal format)\n    vkGetPhysicalDeviceFormatProperties(iris->physical_device, format, &format_props);\n\n    if (!(format_props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) {\n        printf(\"Device does not support blitting from optimal tiled images, using copy instead of blit!\\n\");\n\n        supports_blit = false;\n    }\n\n    // Check if the device supports blitting to linear images\n    vkGetPhysicalDeviceFormatProperties(iris->physical_device, VK_FORMAT_R8G8B8A8_UNORM, &format_props);\n\n    if (!(format_props.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) {\n        printf(\"Device does not support blitting to linear tiled images, using copy instead of blit!\\n\");\n\n        supports_blit = false;\n    }\n\n    // Create the linear tiled destination image to copy to and to read the memory from\n    VkImageCreateInfo image_create_info = {};\n    image_create_info.imageType = VK_IMAGE_TYPE_2D;\n    image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM;\n    image_create_info.extent.width = width;\n    image_create_info.extent.height = height;\n    image_create_info.extent.depth = 1;\n    image_create_info.arrayLayers = 1;\n    image_create_info.mipLevels = 1;\n    image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED;\n    image_create_info.samples = VK_SAMPLE_COUNT_1_BIT;\n    image_create_info.tiling = VK_IMAGE_TILING_LINEAR;\n    image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT;\n\n    // Create the image\n    VkImage dst_image;\n\n    if (vkCreateImage(iris->device, &image_create_info, nullptr, &dst_image) != VK_SUCCESS) {\n        printf(\"Failed to create image for readback\\n\");\n\n        return nullptr;\n    }\n\n    VkDeviceMemory dst_image_memory;\n    VkMemoryRequirements req;\n    vkGetImageMemoryRequirements(iris->device, dst_image, &req);\n    VkMemoryAllocateInfo alloc_info = {};\n    alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO;\n    alloc_info.allocationSize = req.size;\n    alloc_info.memoryTypeIndex = find_memory_type(iris, req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT);\n\n    if (vkAllocateMemory(iris->device, &alloc_info, VK_NULL_HANDLE, &dst_image_memory) != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to allocate image memory for readback\\n\");\n\n        return {};\n    }\n\n    if (vkBindImageMemory(iris->device, dst_image, dst_image_memory, 0) != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to bind image memory for readback\\n\");\n\n        return {};\n    }\n\n    // Do the actual blit from the swapchain image to our host visible destination image\n    VkCommandPool command_pool = VK_NULL_HANDLE;\n\n    VkCommandPoolCreateInfo info = {};\n    info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO;\n    info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT;\n    info.queueFamilyIndex = iris->queue_family;\n\n    if (vkCreateCommandPool(iris->device, &info, VK_NULL_HANDLE, &command_pool) != VK_SUCCESS) {\n        fprintf(stderr, \"vulkan: Failed to create command pool for readback\\n\");\n    \n        return {};\n    }\n\n    VkCommandBufferAllocateInfo cmd_buffer_alloc_info = {};\n    cmd_buffer_alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO;\n    cmd_buffer_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY;\n    cmd_buffer_alloc_info.commandPool = command_pool;\n    cmd_buffer_alloc_info.commandBufferCount = 1;\n\n    VkCommandBuffer command_buffer;\n    vkAllocateCommandBuffers(iris->device, &cmd_buffer_alloc_info, &command_buffer);\n\n    VkCommandBufferBeginInfo begin_info{};\n    begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO;\n    begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT;\n\n    if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) {\n        printf(\"vulkan: Failed to begin command buffer for readback\\n\");\n\n        return {};\n    }\n\n    // Transition destination image to transfer destination layout\n    insert_image_memory_barrier(\n        command_buffer,\n        dst_image,\n        0,\n        VK_ACCESS_TRANSFER_WRITE_BIT,\n        VK_IMAGE_LAYOUT_UNDEFINED,\n        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,\n        VK_PIPELINE_STAGE_TRANSFER_BIT,\n        VK_PIPELINE_STAGE_TRANSFER_BIT,\n        VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }\n    );\n\n    // Transition swapchain image from present to transfer source layout\n    insert_image_memory_barrier(\n        command_buffer,\n        src_image,\n        VK_ACCESS_MEMORY_READ_BIT,\n        VK_ACCESS_TRANSFER_READ_BIT,\n        VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,\n        VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,\n        VK_PIPELINE_STAGE_TRANSFER_BIT,\n        VK_PIPELINE_STAGE_TRANSFER_BIT,\n        VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }\n    );\n\n    // If source and destination support blit we'll blit as this also does automatic format conversion (e.g. from BGR to RGB)\n    if (supports_blit) {\n        // Define the region to blit (we will blit the whole swapchain image)\n        VkOffset3D blitSize;\n        blitSize.x = width;\n        blitSize.y = height;\n        blitSize.z = 1;\n        VkImageBlit imageBlitRegion{};\n        imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n        imageBlitRegion.srcSubresource.layerCount = 1;\n        imageBlitRegion.srcOffsets[1] = blitSize;\n        imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n        imageBlitRegion.dstSubresource.layerCount = 1;\n        imageBlitRegion.dstOffsets[1] = blitSize;\n\n        // Issue the blit command\n        vkCmdBlitImage(\n            command_buffer,\n            src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,\n            dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,\n            1,\n            &imageBlitRegion,\n            VK_FILTER_NEAREST\n        );\n    } else {\n        // Otherwise use image copy (requires us to manually flip components)\n        VkImageCopy imageCopyRegion = {};\n        imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n        imageCopyRegion.srcSubresource.layerCount = 1;\n        imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT;\n        imageCopyRegion.dstSubresource.layerCount = 1;\n        imageCopyRegion.extent.width = width;\n        imageCopyRegion.extent.height = height;\n        imageCopyRegion.extent.depth = 1;\n\n        // Issue the copy command\n        vkCmdCopyImage(\n            command_buffer,\n            src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,\n            dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,\n            1,\n            &imageCopyRegion\n        );\n    }\n\n    // Transition destination image to general layout, which is the required layout for mapping the image memory later on\n    insert_image_memory_barrier(\n        command_buffer,\n        dst_image,\n        VK_ACCESS_TRANSFER_WRITE_BIT,\n        VK_ACCESS_MEMORY_READ_BIT,\n        VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL,\n        VK_IMAGE_LAYOUT_GENERAL,\n        VK_PIPELINE_STAGE_TRANSFER_BIT,\n        VK_PIPELINE_STAGE_TRANSFER_BIT,\n        VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }\n    );\n\n    // Transition back the swap chain image after the blit is done\n    insert_image_memory_barrier(\n        command_buffer,\n        src_image,\n        VK_ACCESS_TRANSFER_READ_BIT,\n        VK_ACCESS_MEMORY_READ_BIT,\n        VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL,\n        VK_IMAGE_LAYOUT_PRESENT_SRC_KHR,\n        VK_PIPELINE_STAGE_TRANSFER_BIT,\n        VK_PIPELINE_STAGE_TRANSFER_BIT,\n        VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 }\n    );\n\n    // End command buffer\n    {\n        VkSubmitInfo end_info = {};\n        end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO;\n        end_info.commandBufferCount = 1;\n        end_info.pCommandBuffers = &command_buffer;\n\n        if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to end command buffer\\n\");\n\n            return {};\n        }\n\n\n        if (vkQueueSubmit(iris->queue, 1, &end_info, VK_NULL_HANDLE) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to submit queue\\n\");\n\n            return {};\n        } \n\n        \n        if (vkDeviceWaitIdle(iris->device) != VK_SUCCESS) {\n            fprintf(stderr, \"vulkan: Failed to wait device idle\\n\");\n\n            return {};\n        } \n    }\n\n    vkDestroyCommandPool(iris->device, command_pool, nullptr);\n\n    // Get layout of the image (including row pitch)\n    VkImageSubresource subresource { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 };\n    VkSubresourceLayout subresource_layout;\n    vkGetImageSubresourceLayout(iris->device, dst_image, &subresource, &subresource_layout);\n\n    // Map image memory so we can start copying from it\n    const char* data;\n    vkMapMemory(iris->device, dst_image_memory, 0, VK_WHOLE_SIZE, 0, (void**)&data);\n    data += subresource_layout.offset;\n\n    void* buf = malloc(width * height * 4);\n\n    memcpy(buf, data, width * height * 4);\n\n    // Clean up resources\n    vkUnmapMemory(iris->device, dst_image_memory);\n    vkFreeMemory(iris->device, dst_image_memory, nullptr);\n    vkDestroyImage(iris->device, dst_image, nullptr);\n\n    return buf;\n}\n\nvoid wait_idle(iris::instance* iris) {\n    if (iris->device) {\n        vkDeviceWaitIdle(iris->device);\n    } else if (iris->queue) {\n        vkQueueWaitIdle(iris->queue);\n    }\n}\n\n}"
  },
  {
    "path": "main.cpp",
    "content": "// Standard includes\n#include <filesystem>\n#include <algorithm>\n#include <cstdlib>\n#include <cstdio>\n\n// Iris includes\n#include \"iris.hpp\"\n#include \"config.hpp\"\n#include \"ee/ee_def.hpp\"\n#include \"ee/vu_def.hpp\"\n\n// ImGui includes\n#include \"imgui.h\"\n#include \"imgui_impl_sdl3.h\"\n#include \"imgui_impl_vulkan.h\"\n#include \"implot.h\"\n\n// SDL3 includes\n#include <SDL3/SDL.h>\n\n// stb_image stuff\n#define STB_IMAGE_IMPLEMENTATION\n#include \"stb_image.h\"\n\n#define SDL_MAIN_USE_CALLBACKS\n#include <SDL3/SDL_main.h>\n\nSDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) {\n    SDL_SetAppMetadata(\"Iris\", STR(_IRIS_VERSION), \"com.allkern.iris\");\n\n    // Check if we got --help or --version in the commandline args\n    // if so, don't do anything else.\n    if (iris::settings::check_for_quick_exit(argc, (const char**)argv)) {\n        return SDL_APP_SUCCESS;\n    }\n\n    iris::instance* iris = iris::create();\n\n    if (!iris::init(iris, argc, (const char**)argv)) {\n        fprintf(stderr, \"iris: Failed to initialize instance\\n\");\n\n        return SDL_APP_FAILURE;\n    }\n\n    // Initialize appstate\n    *appstate = iris;\n\n    return SDL_APP_CONTINUE;\n}\n\nSDL_AppResult SDL_AppIterate(void* appstate) {\n    iris::instance* iris = (iris::instance*)appstate;\n\n    return iris::update(iris);\n}\n\nSDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) {\n    iris::instance* iris = (iris::instance*)appstate;\n\n    return iris::handle_events(iris, event);\n}\n\nvoid SDL_AppQuit(void* appstate, SDL_AppResult result) {\n    iris::instance* iris = (iris::instance*)appstate;\n\n    iris::destroy(iris);\n}"
  },
  {
    "path": "res/iris.rc",
    "content": "id ICON \"iris.ico\"\n1 VERSIONINFO\nFILEVERSION     1,0,0,0\nPRODUCTVERSION  1,0,0,0\nBEGIN\n  BLOCK \"StringFileInfo\"\n  BEGIN\n    BLOCK \"080904E4\"\n    BEGIN\n      VALUE \"CompanyName\", \"Allkern\"\n      VALUE \"FileDescription\", \"An experimental PlayStation 2 Emulator\"\n      VALUE \"FileVersion\", \"0.10\"\n      VALUE \"ProductName\", \"Iris\"\n      VALUE \"ProductVersion\", \"0.10\"\n    END\n  END\nEND"
  },
  {
    "path": "shaders/curvature.frag",
    "content": "#version 460\n\nlayout (location = 0) in vec2 TexCoord;\nlayout (location = 0) out vec4 FragColor;\nlayout (binding = 0) uniform sampler2D input_tex;\n\nlayout(push_constant) uniform constants {\n    vec2 resolution;\n    int frame;\n} PushConstants;\n\n#define PI 3.14159265358979323846\n#define FIR_WIDTH 1\n\nvec2 curvature(vec2 coord, float bend) {\n\t// put in symmetrical coords\n\tcoord = (coord - 0.5) * 2.0;\n\tcoord *= 1.1;\t\n\n\t// deform coords\n\tcoord.x *= 1.0 + pow((abs(coord.y) / bend), 2.0);\n\tcoord.y *= 1.0 + pow((abs(coord.x) / bend), 2.0);\n\n\t// transform back to 0.0 - 1.0 space\n\tcoord  = (coord / 2.0) + 0.5;\n\n\treturn coord;\n}\n\nconst float a0 = 0.215578950;\nconst float a1 = 0.416631580;\nconst float a2 = 0.277263158;\nconst float a3 = 0.083578947;\nconst float a4 = 0.006947368;\n\nfloat flat_top(float x, float n) {\n    return a0 - (a1 * cos((2.0 * PI * x) / n)) + (a2 * cos((4.0 * PI * x) / n)) - (a3 * cos((6.0 * PI * x) / n)) + (a4 * cos((8.0 * PI * x) / n));\n}\n\nconst float freq = 9.0;\n\nfloat sinc(float x) {\n    return sin(freq * PI * (x - 0.5)) / (freq * PI * (x - 0.5));\n}\n\nvoid main() {\n    vec2 uv = curvature(TexCoord, 5.5);\n\n\tif (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) {\n        FragColor = vec4(0.0);\n\n        return;\n    }\n\n    FragColor = texture(input_tex, uv).xyzw;\n}"
  },
  {
    "path": "shaders/decoder.frag",
    "content": "#version 460\n\nlayout (location = 0) in vec2 TexCoord;\nlayout (location = 0) out vec4 FragColor;\nlayout (binding = 0) uniform sampler2D input_tex;\n\nlayout(push_constant) uniform constants {\n    vec2 resolution;\n    int frame;\n} PushConstants;\n\n// Decoder or Demodulator\n// This pass takes the Composite signal generated on Buffer B\n// and decodes it\n\n#define PI   3.14159265358979323846\n#define TAU  6.28318530717958647693\n\n#define BRIGHTNESS_FACTOR        2.0\n\n// The decoded IQ signals get multiplied by this\n// factor. Bigger values yield more color saturation\n#define CHROMA_SATURATION_FACTOR 2.0\n\n// Size of the decoding FIR filter. bigger values\n// yield more smudgy video and are more expensive\n#define CHROMA_DECODER_FIR_SIZE  8\n#define LUMA_DECODER_FIR_SIZE  4\n\nfloat blackman(float n, float N) {\n    return 0.42 - 0.5 * cos((2.0 * PI * n) / (N - 1.0)) +\n           0.08 * cos((4.0 * PI * n) / (N - 1.0));\n}\n\nfloat sinc(float x) {\n    if (x == 0.0) return 1.0;\n\n    float pix = PI * x;\n\n    return sin(pix) / pix;\n}\n\nfloat sinc_f(float cutoff, float n, float N) {\n    float cut2 = cutoff * 2.0;\n\n    return cut2 * sinc(cut2 * (n - ((N - 1.0) / 2.0))) * 48.0;\n}\n\nfloat lowpass(float cutoff, float n, float N) {\n    return (sinc_f(cutoff, n, N) * 2.0 - 2.0) * blackman(n, N);\n}\n\nfloat highpass(float cutoff, float n, float N) {\n    return -(sinc_f(cutoff, n, N) * blackman(n, N));\n}\n\n// YIQ to RGB matrix\nconst mat3 yiq_to_rgb = mat3(\n    1.000,  1.000,  1.000,\n    0.956, -0.272, -1.106,\n    0.621, -0.647,  1.703\n);\n\nvoid main() {\n    vec2 uv = TexCoord * PushConstants.resolution;\n\n    // Chroma decoder oscillator frequency \n    float fc = 128.0;\n\n    float counter = 0.0;\n\n    // Sum and decode NTSC samples\n    // This is essentially a simple averaging filter\n    // that happens to be weighted by two cos and sin\n    // oscillators at a very specific frequency\n    vec3 yiq = vec3(0.0);\n    \n    // Decode Luma first\n    for (int d = 0; d < 51; d++) {\n        vec2 p = vec2(uv.x + float(d) - 26.0, uv.y);\n        vec3 s = texture(input_tex, p / PushConstants.resolution).rgb;\n\n        float filt = lowpass(1.0/6.0, float(d), 51.0);\n\n        yiq.x += s.x * filt;\n    }\n\n    yiq.x /= 51.0;\n    \n    // Then decode chroma\n    for (int d = 0; d < 51; d++) {\n        vec2 p = vec2(uv.x + float(d) - 26.0, uv.y);\n        vec3 s = texture(input_tex, p / PushConstants.resolution).rgb;\n        float t = fc * (uv.x + float(d) - 26.0) + ((PI / 2.0) * uv.y) + (PI * float((PushConstants.frame >> 1) & 1));\n\n        // Apply Blackman window for smoother colors\n        float filt = -highpass(0.25, float(d), 51.0);\n\n        yiq.yz += s.yz * filt * vec2(cos(t), sin(t));\n    }\n\n    yiq.yz /= 51.0;\n    yiq.yz *= CHROMA_SATURATION_FACTOR;\n\n    FragColor = vec4((yiq_to_rgb * yiq), 1.0);\n}"
  },
  {
    "path": "shaders/default.frag",
    "content": "#version 460\n\nlayout (location = 0) in vec2 TexCoord;\nlayout (location = 0) out vec4 FragColor;\nlayout (binding = 0) uniform sampler2D input_tex;\n\nvoid main() {\n    FragColor = vec4(texture(input_tex, TexCoord).xyz, 1.0);\n}"
  },
  {
    "path": "shaders/default.vert",
    "content": "#version 460\n\nlayout (location = 0) in vec2 position;\nlayout (location = 1) in vec2 in_uv;\nlayout (location = 0) out vec2 out_uv;\n\nvoid main() {\n    gl_Position = vec4(position, 0.0f, 1.0f);\n    out_uv = vec2(in_uv.x, 1.0 - in_uv.y);\n}"
  },
  {
    "path": "shaders/encoder.frag",
    "content": "#version 460\n\nlayout (location = 0) in vec2 TexCoord;\nlayout (location = 0) out vec4 FragColor;\nlayout (binding = 0) uniform sampler2D input_tex;\n\nlayout(push_constant) uniform constants {\n    vec2 resolution;\n    int frame;\n} PushConstants;\n\n// Encoder or Modulator\n// This pass converts RGB colors on iChannel0 to\n// a YIQ (NTSC) Composite signal.\n\n#define PI   3.14159265358979323846\n#define TAU  6.28318530717958647693\n\nconst mat3 rgb_to_yiq = mat3(\n    0.299,  0.596,  0.211,\n    0.587, -0.274, -0.523,\n    0.114, -0.322,  0.312\n);\n\nfloat hash11(float p) {\n    p = fract(p * .1031);\n    p *= p + 33.33;\n    p *= p + p;\n    return fract(p);\n}\n\nfloat hash12(vec2 p) {\n\tvec3 p3  = fract(vec3(p.xyx) * .1031);\n    p3 += dot(p3, p3.yzx + 33.33);\n    return fract((p3.x + p3.y) * p3.z);\n}\n\nfloat hash13(vec3 p3) {\n\tp3  = fract(p3 * .1031);\n    p3 += dot(p3, p3.zyx + 31.32);\n    return fract((p3.x + p3.y) * p3.z);\n}\n\nvoid main() {\n    vec2 uv = TexCoord * PushConstants.resolution;\n\n    float noise_strength = 0.0;\n\n    // Get a pixel from iChannel0\n    // float noise_y = hash12(vec2(float(int(uv.y) >> 1), float(frame))) * noise_strength;\n    float noise_x = hash13(vec3(uv.xy, float(PushConstants.frame))) * noise_strength;\n    // float noise = noise_x + noise_y;\n\n    // Chroma encoder oscillator frequency \n    float fc = 128.0;\n\n    // Base oscillator angle for this dot\n    float t = uv.x;\n    \n    // Get a pixel from input_texture\n    vec3 rgb = texture(input_tex, uv / PushConstants.resolution).rgb;\n\n    // Convert to YIQ\n    vec3 yiq = rgb_to_yiq * rgb;\n    \n    // Final oscillator angle\n    float f = fc * t + ((PI / 2.0) * uv.y) + (PI * float((PushConstants.frame >> 1) & 1));\n\n    // Modulate IQ signals\n    float i = yiq.y * cos(f), // I signal\n          q = yiq.z * sin(f); // Q signal\n    \n    // Add to Y to get the composite signal\n    float c = yiq.x + (i + q); // Composite\n\n    FragColor = vec4(vec3(c), 1.0);\n}"
  },
  {
    "path": "shaders/noise.frag",
    "content": "#version 460\n\nlayout (location = 0) in vec2 TexCoord;\nlayout (location = 0) out vec4 FragColor;\nlayout (binding = 0) uniform sampler2D input_tex;\n\nlayout(push_constant) uniform constants {\n    vec2 resolution;\n    int frame;\n} PushConstants;\n\nfloat hash11(float p) {\n    p = fract(p * .1031);\n    p *= p + 33.33;\n    p *= p + p;\n    return fract(p);\n}\n\nfloat hash12(vec2 p) {\n\tvec3 p3  = fract(vec3(p.xyx) * .1031);\n    p3 += dot(p3, p3.yzx + 33.33);\n    return fract((p3.x + p3.y) * p3.z);\n}\n\nfloat hash13(vec3 p3) {\n\tp3  = fract(p3 * .1031);\n    p3 += dot(p3, p3.zyx + 31.32);\n    return fract((p3.x + p3.y) * p3.z);\n}\n\nvoid main() {\n    vec3 tex = texture(input_tex, TexCoord).xyz;\n\n    float noise = hash13(vec3(TexCoord * PushConstants.resolution, float(PushConstants.frame)));\n\n    if (noise < 0.1) {\n        if (noise < 0.05) {\n            tex += noise * 10.0;\n        } else {\n            tex -= noise * 10.0;\n        }\n    }\n\n    FragColor = vec4(tex, 1.0);\n}"
  },
  {
    "path": "shaders/scanlines.frag",
    "content": "#version 460\n\nlayout (location = 0) in vec2 TexCoord;\nlayout (location = 0) out vec4 FragColor;\nlayout (binding = 0) uniform sampler2D input_tex;\n\nlayout(push_constant) uniform constants {\n    vec2 resolution;\n    int frame;\n} PushConstants;\n\n// NTSC-like shader\n// Simulates scanlines, horizontal blur, and basic color bleeding\n\nvoid main()\n{ \n    vec2 uv = TexCoord;\n    vec2 oneX = vec2(1.0 / PushConstants.resolution.x, 0.0);\n    \n    // YIQ / RGB Matrices\n    // RGB to YIQ\n    const vec3 kY = vec3(0.299, 0.587, 0.114);\n    const vec3 kI = vec3(0.596, -0.274, -0.322);\n    const vec3 kQ = vec3(0.211, -0.523, 0.312);\n\n    // 1. & 3. Bandwidth extraction (Luma) & Bleed (Chroma)\n    // Sample a window for Chroma (Low Bandwidth)\n    // Sample center for Luma (High Bandwidth)\n    \n\tvec3 centerRGB = texture(input_tex, uv).rgb;\n    float y = dot(centerRGB, kY);\n    \n    float iSum = 0.0;\n    float qSum = 0.0;\n    float weightSum = 0.0;\n    \n    // NTSC color carrier is ~3.58MHz, bandwidth allows for ~1/3 PushConstants.resolution of luma.\n    // We smear chroma over a few pixels.\n    float bleed = 2.0; \n    for(float x = -bleed; x <= bleed; x += 1.0)\n    {\n        vec3 rgb = texture(input_tex, uv + oneX * x).rgb;\n        float i = dot(rgb, kI);\n        float q = dot(rgb, kQ);\n        \n        // Gaussianish weight\n        float w = 1.0 / (1.0 + abs(x));\n        \n        iSum += i * w;\n        qSum += q * w;\n        weightSum += w;\n    }\n    \n    float finalI = iSum / weightSum;\n    float finalQ = qSum / weightSum;\n    \n    // Convert back to RGB\n    // R = Y + 0.956I + 0.621Q\n    // G = Y - 0.272I - 0.647Q\n    // B = Y - 1.106I + 1.703Q\n    \n    vec3 color = vec3(\n        y + 0.956 * finalI + 0.621 * finalQ,\n        y - 0.272 * finalI - 0.647 * finalQ,\n        y - 1.106 * finalI + 1.703 * finalQ\n    );\n\n    // 2. Scanlines\n    // Based on UV.y. 240 lines.\n    float scanline = sin(uv.y * 240.0 * 3.14159 * 2.0);\n    color *= (0.95 + 0.05 * scanline);\n    \n    // 4. Gamma\n    // Input is usually linear or sRGB. Output to sRGB.\n    // Assuming input acts as sRGB for now.\n    \n    FragColor = vec4(color, 1.0);\n}\n"
  },
  {
    "path": "shaders/shader.vert",
    "content": "#version 460\n\nlayout (location = 0) out vec2 out_uv;\n\nconst vec2 vertices[4] = vec2[](\n    vec2(-1.0, -1.0),\n    vec2( 1.0, -1.0),\n    vec2( 1.0,  1.0),\n    vec2(-1.0,  1.0)\n);\n\nconst vec2 uvs[4] = vec2[](\n    vec2(0.0, 1.0),\n    vec2(1.0, 1.0),\n    vec2(1.0, 0.0),\n    vec2(0.0, 0.0)\n);\n\nvoid main() {\n    gl_Position = vec4(vertices[gl_VertexIndex], 0.0f, 1.0f);\n\n    out_uv = uvs[gl_VertexIndex];\n    out_uv.y = 1.0 - out_uv.y;\n}"
  },
  {
    "path": "src/dev/ds.c",
    "content": "#include \"ds.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\n#define printf(fmt, ...)(0)\n\nstatic inline uint8_t ds_get_model_byte(struct ds_state* ds) {\n    switch (ds->mode) {\n        case 0: return 0x41;\n        case 1: return 0x73;\n        case 2: return 0x79;\n    }\n}\nstatic inline void ds_cmd_set_vref_param(struct ps2_sio2* sio2, struct ds_state* ds) {\n    printf(\"ds: ds_cmd_set_vref_param\\n\");\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x02);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x5a);\n}\nstatic inline void ds_cmd_query_masked(struct ps2_sio2* sio2, struct ds_state* ds) {\n    printf(\"ds: ds_cmd_query_masked\\n\");\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n\n    if (!ds->mode) {\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n    } else {\n        queue_push(sio2->out, ds->mask[0]);\n        queue_push(sio2->out, ds->mask[1]);\n        queue_push(sio2->out, 0x03);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x5a);\n    }\n}\nstatic inline void ds_cmd_read_data(struct ps2_sio2* sio2, struct ds_state* ds) {\n    printf(\"ds: ds_cmd_read_data(%04x)\\n\", ds->buttons);\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, ds_get_model_byte(ds));\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, ds->buttons & 0xff);\n    queue_push(sio2->out, ds->buttons >> 8);\n\n    if (ds->mode) {\n        queue_push(sio2->out, ds->ax_right_x);\n        queue_push(sio2->out, ds->ax_right_y);\n        queue_push(sio2->out, ds->ax_left_x);\n        queue_push(sio2->out, ds->ax_left_y);\n\n        // Push pressure bytes (only in DualShock 2 mode)\n        // Note: Some games (e.g. OutRun 2 SP/2006) won't register inputs\n        //       if the pressure values are 0, so we push the max value\n        //       instead\n        if (ds->mode == 2) {\n            queue_push(sio2->out, 0xff);\n            queue_push(sio2->out, 0xff);\n            queue_push(sio2->out, 0xff);\n            queue_push(sio2->out, 0xff);\n            queue_push(sio2->out, 0xff);\n            queue_push(sio2->out, 0xff);\n            queue_push(sio2->out, 0xff);\n            queue_push(sio2->out, 0xff);\n            queue_push(sio2->out, 0xff);\n            queue_push(sio2->out, 0xff);\n            queue_push(sio2->out, 0xff);\n            queue_push(sio2->out, 0xff);\n        }\n    }\n}\nstatic inline void ds_cmd_config_mode(struct ps2_sio2* sio2, struct ds_state* ds) {\n    printf(\"ds: ds_cmd_config_mode(%02x)\\n\", queue_at(sio2->in, 3));\n\n    // Same as read_data, but without pressure data in DualShock 2 mode\n    if (!ds->config_mode) {\n        queue_push(sio2->out, 0xff);\n\n        // We don't use the model byte here because\n        // config_mode returns the same data as analog (DS1)\n        // when not in config mode regardless of the model\n        queue_push(sio2->out, ds->mode ? 0x73 : 0x41);\n        queue_push(sio2->out, 0x5a);\n        queue_push(sio2->out, ds->buttons & 0xff);\n        queue_push(sio2->out, ds->buttons >> 8);\n\n        if (ds->mode) {\n            queue_push(sio2->out, ds->ax_right_x);\n            queue_push(sio2->out, ds->ax_right_y);\n            queue_push(sio2->out, ds->ax_left_x);\n            queue_push(sio2->out, ds->ax_left_y);\n        }\n    } else {\n        queue_push(sio2->out, 0xff);\n        queue_push(sio2->out, 0xf3);\n        queue_push(sio2->out, 0x5a);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n    }\n\n    ds->config_mode = queue_at(sio2->in, 3);\n}\nstatic inline void ds_cmd_set_mode(struct ps2_sio2* sio2, struct ds_state* ds) {\n    printf(\"ds: ds_cmd_set_mode(%02x, %02x)\\n\", queue_at(sio2->in, 3), queue_at(sio2->in, 4));\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n\n    int mode = queue_at(sio2->in, 3);\n    int lock = queue_at(sio2->in, 4);\n\n    if (mode < 2 && !ds->lock) {\n        ds->mode = mode ? 1 : 0;\n    }\n\n    ds->lock = lock == 3;\n}\nstatic inline void ds_cmd_query_model(struct ps2_sio2* sio2, struct ds_state* ds) {\n    printf(\"ds: ds_cmd_query_model\\n\");\n\n    queue_push(sio2->out, 0xff); // Header\n    queue_push(sio2->out, 0xf3); // Mode (F3=Config)\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x03); // Model (01=Dualshock/Digital 03=Dualshock 2)\n    queue_push(sio2->out, 0x02);\n    queue_push(sio2->out, !!ds->mode); // Analog (00=no 01=yes)\n    queue_push(sio2->out, 0x02);\n    queue_push(sio2->out, 0x01);\n    queue_push(sio2->out, 0x00);\n}\nstatic inline void ds_cmd_query_act(struct ps2_sio2* sio2, struct ds_state* ds) {\n    printf(\"ds: ds_cmd_query_act(%02x)\\n\", queue_at(sio2->in, 3));\n\n    int index = queue_at(sio2->in, 3);\n\n    if (!index) {\n        queue_push(sio2->out, 0xff);\n        queue_push(sio2->out, 0xf3);\n        queue_push(sio2->out, 0x5a);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x01);\n        queue_push(sio2->out, 0x02);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x0a);\n    } else {\n        queue_push(sio2->out, 0xff);\n        queue_push(sio2->out, 0xf3);\n        queue_push(sio2->out, 0x5a);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x01);\n        queue_push(sio2->out, 0x01);\n        queue_push(sio2->out, 0x01);\n        queue_push(sio2->out, 0x14);\n    }\n}\nstatic inline void ds_cmd_query_comb(struct ps2_sio2* sio2, struct ds_state* ds) {\n    printf(\"ds: ds_cmd_query_comb\\n\");\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x02);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x01);\n    queue_push(sio2->out, 0x00);\n}\nstatic inline void ds_cmd_query_mode(struct ps2_sio2* sio2, struct ds_state* ds) {\n    printf(\"ds: ds_cmd_query_mode\\n\");\n\n    int index = queue_at(sio2->in, 3);\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, index ? 7 : 4);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n}\nstatic inline void ds_cmd_vibration_toggle(struct ps2_sio2* sio2, struct ds_state* ds) {\n    printf(\"ds: ds_cmd_vibration_toggle\\n\");\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, ds->vibration[0]);\n    queue_push(sio2->out, ds->vibration[1]);\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xff);\n\n    ds->vibration[0] = queue_at(sio2->in, 3);\n    ds->vibration[1] = queue_at(sio2->in, 4);\n}\nstatic inline void ds_cmd_set_native_mode(struct ps2_sio2* sio2, struct ds_state* ds) {\n    printf(\"ds: ds_cmd_set_native_mode(%02x, %02x, %02x, %02x)\\n\",\n        queue_at(sio2->in, 3),\n        queue_at(sio2->in, 4),\n        queue_at(sio2->in, 5),\n        queue_at(sio2->in, 6)\n    );\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x5a);\n\n    ds->mask[0] = queue_at(sio2->in, 3);\n    ds->mask[1] = queue_at(sio2->in, 4);\n\n    int value = queue_at(sio2->in, 5);\n\n    if ((value & 1) == 0) {\n        // Digital mode\n        ds->mode = 0;\n    } else if ((value & 2) == 0) {\n        // Analog mode\n        ds->mode = 1;\n    } else {\n        // DualShock 2 mode\n        ds->mode = 2;\n    }\n}\n\nvoid ds_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) {\n    struct ds_state* ds = (struct ds_state*)udata;\n\n    switch (cmd) {\n        case 0x40: ds_cmd_set_vref_param(sio2, ds); return;\n        case 0x41: ds_cmd_query_masked(sio2, ds); return;\n        case 0x42: ds_cmd_read_data(sio2, ds); return;\n        case 0x43: ds_cmd_config_mode(sio2, ds); return;\n        case 0x44: ds_cmd_set_mode(sio2, ds); return;\n        case 0x45: ds_cmd_query_model(sio2, ds); return;\n        case 0x46: ds_cmd_query_act(sio2, ds); return;\n        case 0x47: ds_cmd_query_comb(sio2, ds); return;\n        case 0x4C: ds_cmd_query_mode(sio2, ds); return;\n        case 0x4D: ds_cmd_vibration_toggle(sio2, ds); return;\n        case 0x4F: ds_cmd_set_native_mode(sio2, ds); return;\n    }\n\n    fprintf(stderr, \"ds: Unhandled command %02x\\n\", cmd);\n\n    // exit(1);\n}\n\nstruct ds_state* ds_attach(struct ps2_sio2* sio2, int port) {\n    struct ds_state* ds = malloc(sizeof(struct ds_state));\n    struct sio2_device dev;\n\n    dev.detach = ds_detach;\n    dev.handle_command = ds_handle_command;\n    dev.udata = ds;\n\n    memset(ds, 0, sizeof(struct ds_state));\n\n    ds->port = port;\n    ds->ax_right_y = 0x7f;\n    ds->ax_right_x = 0x7f;\n    ds->ax_left_y = 0x7f;\n    ds->ax_left_x = 0x7f;\n    ds->buttons = 0xffff;\n    ds->vibration[0] = 0xff;\n    ds->vibration[1] = 0xff;\n    ds->mask[0] = 0xff;\n    ds->mask[1] = 0xff;\n\n    // Start in digital mode\n    ds->mode = 0;\n    ds->lock = 0;\n\n    ps2_sio2_attach_device(sio2, dev, port);\n\n    return ds;\n}\n\nvoid ds_button_press(struct ds_state* ds, uint32_t mask) {\n    if (mask == DS_BT_ANALOG) {\n        if (!ds->lock)\n            ds->mode = ds->mode ? 0 : 1;\n\n        return;\n    }\n\n    ds->buttons &= ~mask;\n}\n\nvoid ds_button_release(struct ds_state* ds, uint32_t mask) {\n    ds->buttons |= mask;\n}\n\nvoid ds_analog_change(struct ds_state* ds, int axis, uint8_t value) {\n    switch (axis) {\n        case 0: ds->ax_right_y = value; break;\n        case 1: ds->ax_right_x = value; break;\n        case 2: ds->ax_left_y = value; break;\n        case 3: ds->ax_left_x = value; break;\n    }\n}\n\nvoid ds_detach(void* udata) {\n    struct ds_state* ds = (struct ds_state*)udata;\n\n    free(ds);\n}"
  },
  {
    "path": "src/dev/ds.h",
    "content": "#ifndef DS_H\n#define DS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdlib.h>\n\n#include \"iop/sio2.h\"\n\n#define DS_BT_SELECT   0x0001\n#define DS_BT_L3       0x0002\n#define DS_BT_R3       0x0004\n#define DS_BT_START    0x0008\n#define DS_BT_UP       0x0010\n#define DS_BT_RIGHT    0x0020\n#define DS_BT_DOWN     0x0040\n#define DS_BT_LEFT     0x0080\n#define DS_BT_L2       0x0100\n#define DS_BT_R2       0x0200\n#define DS_BT_L1       0x0400\n#define DS_BT_R1       0x0800\n#define DS_BT_TRIANGLE 0x1000\n#define DS_BT_CIRCLE   0x2000\n#define DS_BT_CROSS    0x4000\n#define DS_BT_SQUARE   0x8000\n#define DS_BT_ANALOG   0x10000\n\n#define DS_AX_RIGHT_V 0\n#define DS_AX_RIGHT_H 1\n#define DS_AX_LEFT_V 2\n#define DS_AX_LEFT_H 3\n\nstruct ds_state {\n    int port;\n    uint16_t buttons;\n    uint8_t ax_right_y;\n    uint8_t ax_right_x;\n    uint8_t ax_left_y;\n    uint8_t ax_left_x;\n    int config_mode;\n    int act_index;\n    int mode_index;\n    int mode;\n    int vibration[2];\n    int mask[2];\n    int lock;\n};\n\nstruct ds_state* ds_attach(struct ps2_sio2* sio2, int port);\nvoid ds_button_press(struct ds_state* ds, uint32_t mask);\nvoid ds_button_release(struct ds_state* ds, uint32_t mask);\nvoid ds_analog_change(struct ds_state* ds, int axis, uint8_t value);\nvoid ds_detach(void* udata);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/dev/guncon.c",
    "content": "#include \"guncon.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\n// #define printf(fmt, ...)(0)\n\nstatic inline uint8_t guncon_get_model_byte(struct guncon_state* guncon) {\n    return 0x63;\n}\nstatic inline void guncon_cmd_set_vref_param(struct ps2_sio2* sio2, struct guncon_state* guncon) {\n    printf(\"guncon: guncon_cmd_set_vref_param\\n\");\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x02);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x5a);\n}\nstatic inline void guncon_cmd_query_masked(struct ps2_sio2* sio2, struct guncon_state* guncon) {\n    printf(\"guncon: guncon_cmd_query_masked\\n\");\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n}\nstatic inline void guncon_cmd_read_data(struct ps2_sio2* sio2, struct guncon_state* guncon) {\n    printf(\"guncon: guncon_cmd_read_data(%04x)\\n\", guncon->buttons);\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, guncon_get_model_byte(guncon));\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, guncon->buttons & 0xff);\n    queue_push(sio2->out, guncon->buttons >> 8);\n    queue_push(sio2->out, guncon->x & 0xff);\n    queue_push(sio2->out, guncon->x >> 8);\n    queue_push(sio2->out, guncon->y & 0xff);\n    queue_push(sio2->out, guncon->y >> 8);\n}\nstatic inline void guncon_cmd_config_mode(struct ps2_sio2* sio2, struct guncon_state* guncon) {\n    printf(\"guncon: guncon_cmd_config_mode(%02x)\\n\", queue_at(sio2->in, 3));\n\n    // Same as read_data, but without pressure data in DualShock 2 mode\n    if (!guncon->config_mode) {\n        queue_push(sio2->out, 0xff);\n\n        // We don't use the model byte here because\n        // config_mode returns the same data as analog (GUNCON1)\n        // when not in config mode regardless of the model\n        queue_push(sio2->out, 0x63);\n        queue_push(sio2->out, 0x5a);\n        queue_push(sio2->out, guncon->buttons & 0xff);\n        queue_push(sio2->out, guncon->buttons >> 8);\n        queue_push(sio2->out, guncon->x & 0xff);\n        queue_push(sio2->out, guncon->x >> 8);\n        queue_push(sio2->out, guncon->y & 0xff);\n        queue_push(sio2->out, guncon->y >> 8);\n    } else {\n        queue_push(sio2->out, 0xff);\n        queue_push(sio2->out, 0xf3);\n        queue_push(sio2->out, 0x5a);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n    }\n\n    guncon->config_mode = queue_at(sio2->in, 3);\n}\nstatic inline void guncon_cmd_set_mode(struct ps2_sio2* sio2, struct guncon_state* guncon) {\n    printf(\"guncon: guncon_cmd_set_mode(%02x, %02x)\\n\", queue_at(sio2->in, 3), queue_at(sio2->in, 4));\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n}\nstatic inline void guncon_cmd_query_model(struct ps2_sio2* sio2, struct guncon_state* guncon) {\n    printf(\"guncon: guncon_cmd_query_model\\n\");\n\n    queue_push(sio2->out, 0xff); // Header\n    queue_push(sio2->out, 0xf3); // Mode (F3=Config)\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x03); // Model (01=Dualshock/Digital 03=Dualshock 2)\n    queue_push(sio2->out, 0x02);\n    queue_push(sio2->out, 0x00); // Analog (00=no 01=yes)\n    queue_push(sio2->out, 0x02);\n    queue_push(sio2->out, 0x01);\n    queue_push(sio2->out, 0x00);\n}\nstatic inline void guncon_cmd_query_act(struct ps2_sio2* sio2, struct guncon_state* guncon) {\n    printf(\"guncon: guncon_cmd_query_act(%02x)\\n\", queue_at(sio2->in, 3));\n\n    int index = queue_at(sio2->in, 3);\n\n    if (!index) {\n        queue_push(sio2->out, 0xff);\n        queue_push(sio2->out, 0xf3);\n        queue_push(sio2->out, 0x5a);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x01);\n        queue_push(sio2->out, 0x02);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x0a);\n    } else {\n        queue_push(sio2->out, 0xff);\n        queue_push(sio2->out, 0xf3);\n        queue_push(sio2->out, 0x5a);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x00);\n        queue_push(sio2->out, 0x01);\n        queue_push(sio2->out, 0x01);\n        queue_push(sio2->out, 0x01);\n        queue_push(sio2->out, 0x14);\n    }\n}\nstatic inline void guncon_cmd_query_comb(struct ps2_sio2* sio2, struct guncon_state* guncon) {\n    printf(\"guncon: guncon_cmd_query_comb\\n\");\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x02);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x01);\n    queue_push(sio2->out, 0x00);\n}\nstatic inline void guncon_cmd_query_mode(struct ps2_sio2* sio2, struct guncon_state* guncon) {\n    printf(\"guncon: guncon_cmd_query_mode\\n\");\n\n    int index = queue_at(sio2->in, 3);\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, index ? 7 : 4);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n}\nstatic inline void guncon_cmd_vibration_toggle(struct ps2_sio2* sio2, struct guncon_state* guncon) {\n    printf(\"guncon: guncon_cmd_vibration_toggle\\n\");\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xff);\n}\nstatic inline void guncon_cmd_set_native_mode(struct ps2_sio2* sio2, struct guncon_state* guncon) {\n    printf(\"guncon: guncon_cmd_set_native_mode(%02x, %02x, %02x, %02x)\\n\",\n        queue_at(sio2->in, 3),\n        queue_at(sio2->in, 4),\n        queue_at(sio2->in, 5),\n        queue_at(sio2->in, 6)\n    );\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xf3);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x5a);\n}\n\nvoid guncon_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) {\n    struct guncon_state* guncon = (struct guncon_state*)udata;\n\n    switch (cmd) {\n        case 0x40: guncon_cmd_set_vref_param(sio2, guncon); return;\n        case 0x41: guncon_cmd_query_masked(sio2, guncon); return;\n        case 0x42: guncon_cmd_read_data(sio2, guncon); return;\n        case 0x43: guncon_cmd_config_mode(sio2, guncon); return;\n        case 0x44: guncon_cmd_set_mode(sio2, guncon); return;\n        case 0x45: guncon_cmd_query_model(sio2, guncon); return;\n        case 0x46: guncon_cmd_query_act(sio2, guncon); return;\n        case 0x47: guncon_cmd_query_comb(sio2, guncon); return;\n        case 0x4C: guncon_cmd_query_mode(sio2, guncon); return;\n        case 0x4D: guncon_cmd_vibration_toggle(sio2, guncon); return;\n        case 0x4F: guncon_cmd_set_native_mode(sio2, guncon); return;\n    }\n\n    printf(\"guncon: Unhandled command %02x\\n\", cmd);\n\n    exit(1);\n}\n\nstruct guncon_state* guncon_attach(struct ps2_sio2* sio2, int port) {\n    struct guncon_state* guncon = malloc(sizeof(struct guncon_state));\n    struct sio2_device dev;\n\n    dev.detach = guncon_detach;\n    dev.handle_command = guncon_handle_command;\n    dev.udata = guncon;\n\n    memset(guncon, 0, sizeof(struct guncon_state));\n\n    guncon->port = port;\n    guncon->config_mode = 0;\n    guncon->x = 0x10d;\n    guncon->y = 0x88;\n    guncon->buttons = 0xffff;\n\n    ps2_sio2_attach_device(sio2, dev, port);\n\n    return guncon;\n}\n\nvoid guncon_button_press(struct guncon_state* guncon, uint16_t mask) {\n    guncon->buttons &= ~mask;\n}\n\nvoid guncon_button_release(struct guncon_state* guncon, uint16_t mask) {\n    guncon->buttons |= mask;\n}\n\nvoid guncon_analog_change(struct guncon_state* guncon, int axis, uint8_t value) {\n    switch (axis) {\n        case 0: guncon->x = value; break;\n        case 1: guncon->y = value; break;\n    }\n}\n\nvoid guncon_detach(void* udata) {\n    struct guncon_state* guncon = (struct guncon_state*)udata;\n\n    free(guncon);\n}"
  },
  {
    "path": "src/dev/guncon.h",
    "content": "#ifndef GUNCON_H\n#define GUNCON_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdlib.h>\n\n#include \"iop/sio2.h\"\n\n#define GUNCON_BT_START  0x0008\n#define GUNCON_BT_CIRCLE 0x2000\n#define GUNCON_BT_CROSS  0x4000\n\n#define GUNCON_AX_X 0\n#define GUNCON_AX_Y 1\n\nstruct guncon_state {\n    int port;\n    uint16_t buttons;\n    uint16_t x;\n    uint16_t y;\n    int config_mode;\n};\n\nstruct guncon_state* guncon_attach(struct ps2_sio2* sio2, int port);\nvoid guncon_button_press(struct guncon_state* guncon, uint16_t mask);\nvoid guncon_button_release(struct guncon_state* guncon, uint16_t mask);\nvoid guncon_analog_change(struct guncon_state* guncon, int axis, uint8_t value);\nvoid guncon_detach(void* udata);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/dev/mcd.c",
    "content": "#include \"mcd.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n#define printf(fmt,...)(0)\n\nvoid mcd_flush_block(struct mcd_state* mcd, int addr, int size) {\n    fseek(mcd->file, addr, SEEK_SET);\n    fwrite(&mcd->buf[addr], 1, size, mcd->file);\n}\n\nvoid mcd_cmd_probe(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    printf(\"mcd: mcd_cmd_probe\\n\");\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n}\nvoid mcd_cmd_unk_12(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    printf(\"mcd: mcd_cmd_unk_12\\n\");\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n}\nvoid mcd_cmd_start_erase(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    uint32_t lba =\n        (sio2->in->buf[sio2->in->index + 2]) |\n        (sio2->in->buf[sio2->in->index + 3] << 8) |\n        (sio2->in->buf[sio2->in->index + 4] << 16) |\n        (sio2->in->buf[sio2->in->index + 5] << 24);\n\n    printf(\"mcd: mcd_cmd_start_erase(%08x)\\n\", lba);\n\n    mcd->addr = lba * MCD_SECTOR_SIZE;\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n}\nvoid mcd_cmd_start_write(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    uint32_t lba =\n        (sio2->in->buf[sio2->in->index + 2]) |\n        (sio2->in->buf[sio2->in->index + 3] << 8) |\n        (sio2->in->buf[sio2->in->index + 4] << 16) |\n        (sio2->in->buf[sio2->in->index + 5] << 24);\n\n    printf(\"mcd: mcd_cmd_start_write(%08x)\\n\", lba);\n\n    mcd->addr = lba * MCD_SECTOR_SIZE;\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n}\nvoid mcd_cmd_start_read(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    uint32_t lba =\n        (sio2->in->buf[sio2->in->index + 2]) |\n        (sio2->in->buf[sio2->in->index + 3] << 8) |\n        (sio2->in->buf[sio2->in->index + 4] << 16) |\n        (sio2->in->buf[sio2->in->index + 5] << 24);\n\n    printf(\"mcd: mcd_cmd_start_read(%08x)\\n\", lba);\n\n    mcd->addr = lba * MCD_SECTOR_SIZE;\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n}\nvoid mcd_cmd_get_specs(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    printf(\"mcd: mcd_cmd_get_specs\\n\", sio2->in->buf[2]);\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, 0x00); // Sector size (2-byte)\n    queue_push(sio2->out, 0x02);\n    queue_push(sio2->out, 0x10); // Erase block size (2-byte)\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, (mcd->size >> 0) & 0xff); // Sector count (4-byte)\n    queue_push(sio2->out, (mcd->size >> 8) & 0xff);\n    queue_push(sio2->out, (mcd->size >> 16) & 0xff);\n    queue_push(sio2->out, (mcd->size >> 24) & 0xff);\n    queue_push(sio2->out, mcd->checksum); // Checksum\n    queue_push(sio2->out, mcd->term);\n}\nvoid mcd_cmd_set_terminator(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    printf(\"mcd: mcd_cmd_set_terminator(%02x)\\n\", sio2->in->buf[2]);\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n\n    mcd->term = sio2->in->buf[2];\n}\nvoid mcd_cmd_get_terminator(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    printf(\"mcd: mcd_cmd_get_terminator\\n\");\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n    queue_push(sio2->out, 0x55);\n}\nvoid mcd_cmd_write_data(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    uint8_t size = queue_at(sio2->in, 2);\n\n    printf(\"mcd: mcd_cmd_write_data(%02x)\\n\", size);\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n\n    uint32_t addr = mcd->addr;\n\n    for (int i = 0; i < size; i++) {\n        mcd->buf[mcd->addr++] = queue_at(sio2->in, 3 + i);\n\n        queue_push(sio2->out, 0);\n    }\n\n    mcd_flush_block(mcd, addr, size);\n\n    queue_push(sio2->out, 0);\n    queue_push(sio2->out, mcd->term);\n}\nvoid mcd_cmd_read_data(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    printf(\"mcd: mcd_cmd_read_data(%02x)\\n\", queue_at(sio2->in, 2));\n\n    // assert(queue_at(sio2->in, 2) == 0x80);\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n\n    uint8_t checksum = 0;\n\n    for (int i = 0; i < queue_at(sio2->in, 2); i++) {\n        uint8_t data = mcd->buf[mcd->addr++];\n\n        checksum ^= data;\n\n        queue_push(sio2->out, data);\n    }\n\n    queue_push(sio2->out, checksum); // XOR checksum\n    queue_push(sio2->out, mcd->term);\n}\nvoid mcd_cmd_rw_end(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    printf(\"mcd: mcd_cmd_rw_end\\n\");\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n}\nvoid mcd_cmd_erase_block(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    printf(\"mcd: mcd_cmd_erase_block\\n\");\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n\n    uint32_t addr = mcd->addr;\n\n    for (int i = 0; i < MCD_SECTOR_SIZE * 16; i++) {\n        mcd->buf[mcd->addr++] = 0xff;\n    }\n\n    mcd_flush_block(mcd, addr, MCD_SECTOR_SIZE * 16);\n}\nvoid mcd_cmd_auth_f0(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    printf(\"mcd: mcd_cmd_auth_f0\\n\");\n\n    uint8_t param = sio2->in->buf[2];\n\n    switch (param) {\n        case 0x01:\n        case 0x02:\n        case 0x04:\n        case 0x0f:\n        case 0x11:\n        case 0x13: {\n            // Handle checksum\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x00);\n\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x2b);\n\n            uint8_t checksum = 0;\n\n            for (int i = 0; i < 8; i++) {\n                checksum ^= sio2->in->buf[i+3];\n\n                queue_push(sio2->out, 0x00);\n            }\n\n            queue_push(sio2->out, checksum);\n            queue_push(sio2->out, mcd->term);\n        } break;\n\n        case 0x06:\n        case 0x07:\n        case 0x0b: {\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x00);\n\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x2b);\n            queue_push(sio2->out, mcd->term);\n        } break;\n\n        default: {\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x00);\n\n            queue_push(sio2->out, 0x00);\n            queue_push(sio2->out, 0x2b);\n            queue_push(sio2->out, mcd->term);\n        } break;\n    }\n}\nvoid mcd_cmd_auth_f1(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    fprintf(stderr, \"mcd: mcd_cmd_auth_f1\\n\");\n    fprintf(stderr, \"mcd: params=\");\n\n    for (int i = 0; i < 16; i++) {\n        fprintf(stderr, \"%02x \", sio2->in->buf[2 + i]);\n    }\n\n    fprintf(stderr, \"\\n\");\n\n    exit(1);\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n}\nvoid mcd_cmd_auth_f3(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    printf(\"mcd: mcd_cmd_auth_f3\\n\");\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n}\nvoid mcd_cmd_auth_f7(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    printf(\"mcd: mcd_cmd_auth_f7\\n\");\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n}\nvoid mcd_cmd_unk_bf(struct ps2_sio2* sio2, struct mcd_state* mcd) {\n    printf(\"mcd: mcd_cmd_unk_bf\\n\");\n\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x2b);\n    queue_push(sio2->out, mcd->term);\n}\n\nvoid mcd_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) {\n    struct mcd_state* mcd = (struct mcd_state*)udata;\n\n    switch (cmd) {\n        case 0x11: mcd_cmd_probe(sio2, mcd); return;\n        case 0x12: mcd_cmd_unk_12(sio2, mcd); return;\n        case 0x21: mcd_cmd_start_erase(sio2, mcd); return;\n        case 0x22: mcd_cmd_start_write(sio2, mcd); return;\n        case 0x23: mcd_cmd_start_read(sio2, mcd); return;\n        case 0x26: mcd_cmd_get_specs(sio2, mcd); return;\n        case 0x27: mcd_cmd_set_terminator(sio2, mcd); return;\n        case 0x28: mcd_cmd_get_terminator(sio2, mcd); return;\n        case 0x42: mcd_cmd_write_data(sio2, mcd); return;\n        case 0x43: mcd_cmd_read_data(sio2, mcd); return;\n        // case 0x52: mcd_cmd_ps1_read(sio2, mcd); return;\n        case 0x81: mcd_cmd_rw_end(sio2, mcd); return;\n        case 0x82: mcd_cmd_erase_block(sio2, mcd); return;\n        case 0xf0: mcd_cmd_auth_f0(sio2, mcd); return;\n        case 0xf1: mcd_cmd_auth_f1(sio2, mcd); return;\n        case 0xf3: mcd_cmd_auth_f3(sio2, mcd); return;\n        case 0xf7: mcd_cmd_auth_f7(sio2, mcd); return;\n        case 0xbf: mcd_cmd_unk_bf(sio2, mcd); return;\n    }\n\n    printf(\"mcd: Unhandled command %02x\\n\", cmd);\n\n    exit(1);\n}\n\nstruct mcd_state* mcd_attach(struct ps2_sio2* sio2, int port, const char* path) {\n    FILE* file = fopen(path, \"r+b\");\n\n    if (!file)\n        return NULL;\n\n    struct mcd_state* mcd = malloc(sizeof(struct mcd_state));\n    struct sio2_device dev;\n\n    memset(mcd, 0, sizeof(struct mcd_state));\n\n    // Get memcard size\n    fseek(file, 0, SEEK_END);\n\n    mcd->buf_size = ftell(file);\n\n    fseek(file, 0, SEEK_SET);\n\n    mcd->buf = (uint8_t*)malloc(mcd->buf_size);\n\n    fread(mcd->buf, 1, mcd->buf_size, file);\n\n    // Init card state\n    mcd->term = 0x55;\n    mcd->file = file;\n    mcd->size = (1 << (31 - __builtin_clz(mcd->buf_size))) >> 9;\n\n    mcd->checksum = 0x02 ^ 0x10;\n\n    for (int i = 0; i < 4; i++)\n        mcd->checksum ^= (mcd->size >> (i * 8)) & 0xff;\n\n    printf(\"mcd: Memory card at \\'%s\\' initialized.\\n\\tTotal size: %x (%d)\\n\\tSize (in sectors): %x (%d)\\n\\tChecksum: %02x\\n\",\n        path, mcd->buf_size, mcd->buf_size,\n        mcd->size, mcd->size,\n        mcd->checksum\n    );\n\n    dev.detach = mcd_detach;\n    dev.handle_command = mcd_handle_command;\n    dev.udata = mcd;\n\n    ps2_sio2_attach_device(sio2, dev, port);\n\n    return mcd;\n}\n\nvoid mcd_detach(void* udata) {\n    struct mcd_state* mcd = (struct mcd_state*)udata;\n\n    // Flush buffer back to file\n    fseek(mcd->file, 0, SEEK_SET);\n    fwrite(mcd->buf, 1, mcd->buf_size, mcd->file);\n\n    fclose(mcd->file);\n    free(mcd->buf);\n    free(mcd);\n}"
  },
  {
    "path": "src/dev/mcd.h",
    "content": "#ifndef MCD_H\n#define MCD_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdlib.h>\n\n#include \"iop/sio2.h\"\n\n#define MCD_SIZE_8MB 0x4000\n#define MCD_SIZE_16MB 0x8000\n#define MCD_SIZE_32MB 0x10000\n#define MCD_SIZE_64MB 0x20000\n\n// 512 bytes data + 16 bytes ECC\n#define MCD_SECTOR_SIZE (512+16)\n\nstruct mcd_state {\n    int port;\n    uint8_t term;\n    uint16_t buttons;\n    uint8_t ax_right_y;\n    uint8_t ax_right_x;\n    uint8_t ax_left_y;\n    uint8_t ax_left_x;\n    int config_mode;\n    int act_index;\n    int mode_index;\n    uint32_t size;\n    uint8_t checksum;\n    uint32_t addr;\n    uint32_t buf_size;\n    uint8_t* buf;\n\n    FILE* file;\n};\n\nstruct mcd_state* mcd_attach(struct ps2_sio2* sio2, int port, const char* path);\nvoid mcd_detach(void* udata);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/dev/mtap.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n\n#include \"mtap.h\"\n\n#define printf(fmt,...)(0)\n\nvoid mtap_cmd_probe(struct ps2_sio2* sio2, struct mtap_state* mtap) {\n    printf(\"mtap: mtap_cmd_probe\\n\");\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0x80);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x04);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, 0x5a);\n}\n\nvoid mtap_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) {\n    struct mtap_state* mtap = (struct mtap_state*)udata;\n\n    switch (cmd) {\n        case 0x12: mtap_cmd_probe(sio2, mtap); return;\n    }\n\n    printf(\"mtap: Unhandled command %02x\\n\", cmd);\n\n    exit(1);\n}\n\nstruct mtap_state* mtap_attach(struct ps2_sio2* sio2, int port) {\n    struct mtap_state* mtap = malloc(sizeof(struct mtap_state));\n    struct sio2_device dev;\n\n    dev.detach = mtap_detach;\n    dev.handle_command = mtap_handle_command;\n    dev.udata = mtap;\n\n    memset(mtap, 0, sizeof(struct mtap_state));\n\n    ps2_sio2_attach_device(sio2, dev, port);\n\n    return mtap;\n}\n\nvoid mtap_detach(void* udata) {\n    struct mtap_state* mtap = (struct mtap_state*)udata;\n\n    free(mtap);\n}"
  },
  {
    "path": "src/dev/mtap.h",
    "content": "#ifndef MTAP_H\n#define MTAP_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdlib.h>\n\n#include \"iop/sio2.h\"\n\nstruct mtap_state {\n    struct sio2_device port[8];\n};\n\nstruct mtap_state* mtap_attach(struct ps2_sio2* sio2, int port);\nvoid mtap_detach(void* udata);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/dev/ps1_mcd.c",
    "content": "#include \"ps1_mcd.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n#define printf(fmt,...)(0)\n\nvoid ps1_mcd_flush_block(struct ps1_mcd_state* mcd, int addr) {\n    fseek(mcd->file, addr, SEEK_SET);\n    fwrite(&mcd->buf[addr], 1, 128, mcd->file);\n}\n\n/*\n  Send Reply Comment\n  81h  N/A   Memory card address\n  52h  FLAG  Send Read Command (ASCII \"R\"), Receive FLAG Byte\n  00h  5Ah   Receive Memory Card ID1\n  00h  5Dh   Receive Memory Card ID2\n  MSB  (00h) Send Address MSB  ;\\sector number (0..3FFh)\n  LSB  (pre) Send Address LSB  ;/\n  00h  5Ch   Receive Command Acknowledge 1  ;<-- late /ACK after this byte-pair\n  00h  5Dh   Receive Command Acknowledge 2\n  00h  MSB   Receive Confirmed Address MSB\n  00h  LSB   Receive Confirmed Address LSB\n  00h  ...   Receive Data Sector (128 bytes)\n  00h  CHK   Receive Checksum (MSB xor LSB xor Data bytes)\n  00h  47h   Receive Memory End Byte (should be always 47h=\"G\"=Good for Read)\n*/\nvoid ps1_mcd_cmd_read(struct ps2_sio2* sio2, struct ps1_mcd_state* mcd) {\n    uint16_t msb = queue_at(sio2->in, 4);\n    uint16_t lsb = queue_at(sio2->in, 5);\n\n    uint16_t addr = ((msb << 8) | lsb) * 128;\n\n    printf(\"ps1_mcd: ps1_mcd_cmd_read(%04x)\\n\", (msb << 8) | lsb);\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, mcd->flag);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x5d);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, msb); // (pre)\n    queue_push(sio2->out, 0x5c);\n    queue_push(sio2->out, 0x5d);\n    queue_push(sio2->out, msb);\n    queue_push(sio2->out, lsb);\n\n    uint8_t checksum = msb ^ lsb;\n\n    for (int i = 0; i < 128; i++) {\n        checksum ^= mcd->buf[addr + i];\n\n        queue_push(sio2->out, mcd->buf[addr + i]);\n    }\n\n    queue_push(sio2->out, checksum);\n    queue_push(sio2->out, 'G'); // 0x47\n}\n\n/*\n  Send Reply Comment\n  81h  N/A   Memory card address\n  57h  FLAG  Send Write Command (ASCII \"W\"), Receive FLAG Byte\n  00h  5Ah   Receive Memory Card ID1\n  00h  5Dh   Receive Memory Card ID2\n  MSB  (00h) Send Address MSB  ;\\sector number (0..3FFh)\n  LSB  (pre) Send Address LSB  ;/\n  ...  (pre) Send Data Sector (128 bytes)\n  CHK  (pre) Send Checksum (MSB xor LSB xor Data bytes)\n  00h  5Ch   Receive Command Acknowledge 1\n  00h  5Dh   Receive Command Acknowledge 2\n  00h  4xh   Receive Memory End Byte (47h=Good, 4Eh=BadChecksum, FFh=BadSector)\n*/\nvoid ps1_mcd_cmd_write(struct ps2_sio2* sio2, struct ps1_mcd_state* mcd) {\n    uint16_t msb = queue_at(sio2->in, 4);\n    uint16_t lsb = queue_at(sio2->in, 5);\n\n    uint16_t addr = ((msb << 8) | lsb) * 128;\n\n    printf(\"ps1_mcd: ps1_mcd_cmd_write(%04x)\\n\", (msb << 8) | lsb);\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, mcd->flag);\n    queue_push(sio2->out, 0x5a);\n    queue_push(sio2->out, 0x5d);\n    queue_push(sio2->out, 0x00);\n    queue_push(sio2->out, msb); // (pre)\n\n    for (int i = 0; i < 128; i++) {\n        mcd->buf[addr+i] = queue_at(sio2->in, 6+i);\n\n        queue_push(sio2->out, queue_at(sio2->in, 5+i)); // (pre)\n    }\n\n    ps1_mcd_flush_block(mcd, addr);\n\n    queue_push(sio2->out, queue_at(sio2->in, 133)); // (pre)\n    queue_push(sio2->out, 0x5c);\n    queue_push(sio2->out, 0x5d);\n    queue_push(sio2->out, 'G');\n\n    // Reset directory read flag\n    mcd->flag &= ~0x08;\n}\nvoid ps1_mcd_cmd_get_id(struct ps2_sio2* sio2, struct ps1_mcd_state* mcd) {\n    printf(\"ps1_mcd: ps1_mcd_cmd_get_id\\n\");\n\n    exit(1);\n}\nvoid ps1_mcd_cmd_invalid(struct ps2_sio2* sio2, struct ps1_mcd_state* mcd) {\n    printf(\"ps1_mcd: ps1_mcd_cmd_invalid(%02x)\\n\", queue_size(sio2->in));\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, mcd->flag);\n\n    for (int i = 2; i < queue_size(sio2->in); i++)\n        queue_push(sio2->out, 0xff);\n}\nvoid ps1_mcd_cmd_detect_pocketstation(struct ps2_sio2* sio2, struct ps1_mcd_state* mcd) {\n    printf(\"ps1_mcd: ps1_mcd_cmd_detect_pocketstation\\n\");\n\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, mcd->flag);\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xff);\n    queue_push(sio2->out, 0xff);\n}\n\nvoid ps1_mcd_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) {\n    struct ps1_mcd_state* mcd = (struct ps1_mcd_state*)udata;\n\n    switch (cmd) {\n        case 0x52: ps1_mcd_cmd_read(sio2, mcd); return;\n        case 0x53: ps1_mcd_cmd_get_id(sio2, mcd); return;\n        case 0x57: ps1_mcd_cmd_write(sio2, mcd); return;\n        case 0x58: {\n            if (mcd->type == 0)\n                break;\n\n            ps1_mcd_cmd_detect_pocketstation(sio2, mcd); return;\n        }\n    }\n\n    sio2->recv1 |= 0x2000;\n\n    ps1_mcd_cmd_invalid(sio2, mcd);\n}\n\nvoid ps1_mcd_set_type(struct ps1_mcd_state* mcd, int type) {\n    mcd->type = type;\n}\n\nstruct ps1_mcd_state* ps1_mcd_attach(struct ps2_sio2* sio2, int port, const char* path) {\n    FILE* file = fopen(path, \"r+b\");\n\n    if (!file)\n        return NULL;\n\n    struct ps1_mcd_state* mcd = malloc(sizeof(struct ps1_mcd_state));\n    struct sio2_device dev;\n\n    memset(mcd, 0, sizeof(struct ps1_mcd_state));\n\n    mcd->file = file;\n    mcd->flag = 0x08;\n\n    fread(mcd->buf, 1, PS1_MCD_SIZE, file);\n\n    printf(\"ps1_mcd: Memory card at \\'%s\\' initialized.\\n\",\n        path\n    );\n\n    dev.detach = ps1_mcd_detach;\n    dev.handle_command = ps1_mcd_handle_command;\n    dev.udata = mcd;\n\n    ps2_sio2_attach_device(sio2, dev, port);\n\n    return mcd;\n}\n\nvoid ps1_mcd_detach(void* udata) {\n    struct ps1_mcd_state* mcd = (struct ps1_mcd_state*)udata;\n\n    // Flush buffer back to file\n    fseek(mcd->file, 0, SEEK_SET);\n    fwrite(mcd->buf, 1, PS1_MCD_SIZE, mcd->file);\n\n    fclose(mcd->file);\n    free(mcd);\n}"
  },
  {
    "path": "src/dev/ps1_mcd.h",
    "content": "#ifndef PS1_MCD_H\n#define PS1_MCD_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdlib.h>\n\n#include \"iop/sio2.h\"\n\n// 128 bytes data\n#define PS1_MCD_SECTOR_SIZE (128)\n#define PS1_MCD_SIZE 0x20000\n\nstruct ps1_mcd_state {\n    // 128 KiB\n    uint8_t buf[PS1_MCD_SIZE];\n    uint8_t flag;\n    int type;\n\n    FILE* file;\n};\n\nstruct ps1_mcd_state* ps1_mcd_attach(struct ps2_sio2* sio2, int port, const char* path);\nvoid ps1_mcd_set_type(struct ps1_mcd_state* mcd, int type);\nvoid ps1_mcd_detach(void* udata);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/bus.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"bus.h\"\n#include \"bus_decl.h\"\n\nstruct ee_bus* ee_bus_create(void) {\n    return malloc(sizeof(struct ee_bus));\n}\n\n#define likely(x) __builtin_expect(!!(x), 1)\n#define unlikely(x) __builtin_expect(!!(x), 0)\n\nvoid ee_bus_init(struct ee_bus* bus, const char* bios_path) {\n    memset(bus, 0, sizeof(struct ee_bus));\n\n    for (int i = 0; i < 0x10000; i++) {\n        bus->fastmem_r_table[i] = NULL;\n        bus->fastmem_w_table[i] = NULL;\n    }\n}\n\nvoid ee_bus_init_fastmem(struct ee_bus* bus, int ee_ram_size, int iop_ram_size) {\n    memset(bus->fastmem_r_table, 0, sizeof(bus->fastmem_r_table));\n    memset(bus->fastmem_w_table, 0, sizeof(bus->fastmem_w_table));\n\n    // BIOS\n    for (int i = 0; i < 0x200; i++) {\n        bus->fastmem_r_table[i+0xfe00] = bus->bios->buf + (i * 0x2000);\n    }\n\n    // Main RAM\n    for (int i = 0; i < (ee_ram_size / 0x2000); i++) {\n        bus->fastmem_r_table[i+0x0000] = bus->ee_ram->buf + (i * 0x2000);\n        bus->fastmem_w_table[i+0x0000] = bus->ee_ram->buf + (i * 0x2000);\n    }\n\n    // IOP RAM\n    for (int i = 0; i < (iop_ram_size / 0x2000); i++) {\n        bus->fastmem_r_table[i+0xe000] = bus->iop_ram->buf + (i * 0x2000);\n        bus->fastmem_w_table[i+0xe000] = bus->iop_ram->buf + (i * 0x2000);\n    }\n}\n\nvoid ee_bus_init_bios(struct ee_bus* bus, struct ps2_bios* bios) {\n    bus->bios = bios;\n}\n\nvoid ee_bus_init_rom1(struct ee_bus* bus, struct ps2_bios* rom1) {\n    bus->rom1 = rom1;\n}\n\nvoid ee_bus_init_rom2(struct ee_bus* bus, struct ps2_bios* rom2) {\n    bus->rom2 = rom2;\n}\n\nvoid ee_bus_init_iop_ram(struct ee_bus* bus, struct ps2_ram* iop_ram) {\n    bus->iop_ram = iop_ram;\n}\n\nvoid ee_bus_init_sif(struct ee_bus* bus, struct ps2_sif* sif) {\n    bus->sif = sif;\n}\n\nvoid ee_bus_init_ram(struct ee_bus* bus, struct ps2_ram* ram) {\n    bus->ee_ram = ram;\n}\n\nvoid ee_bus_init_dmac(struct ee_bus* bus, struct ps2_dmac* dmac) {\n    bus->dmac = dmac;\n}\n\nvoid ee_bus_init_intc(struct ee_bus* bus, struct ps2_intc* intc) {\n    bus->intc = intc;\n}\n\nvoid ee_bus_init_gif(struct ee_bus* bus, struct ps2_gif* gif) {\n    bus->gif = gif;\n}\n\nvoid ee_bus_init_vif0(struct ee_bus* bus, struct ps2_vif* vif0) {\n    bus->vif0 = vif0;\n}\n\nvoid ee_bus_init_vif1(struct ee_bus* bus, struct ps2_vif* vif1) {\n    bus->vif1 = vif1;\n}\n\nvoid ee_bus_init_gs(struct ee_bus* bus, struct ps2_gs* gs) {\n    bus->gs = gs;\n}\n\nvoid ee_bus_init_ipu(struct ee_bus* bus, struct ps2_ipu* ipu) {\n    bus->ipu = ipu;\n}\n\nvoid ee_bus_init_timers(struct ee_bus* bus, struct ps2_ee_timers* timers) {\n    bus->timers = timers;\n}\n\nvoid ee_bus_init_cdvd(struct ee_bus* bus, struct ps2_cdvd* cdvd) {\n    bus->cdvd = cdvd;\n}\n\nvoid ee_bus_init_usb(struct ee_bus* bus, struct ps2_usb* usb) {\n    bus->usb = usb;\n}\n\nvoid ee_bus_init_sbus(struct ee_bus* bus, struct ps2_sbus* sbus) {\n    bus->sbus = sbus;\n}\n\nvoid ee_bus_init_dev9(struct ee_bus* bus, struct ps2_dev9* dev9) {\n    bus->dev9 = dev9;\n}\n\nvoid ee_bus_init_speed(struct ee_bus* bus, struct ps2_speed* speed) {\n    bus->speed = speed;\n}\n\nvoid ee_bus_init_vu0(struct ee_bus* bus, struct vu_state* vu) {\n    bus->vu0 = vu;\n}\n\nvoid ee_bus_init_vu1(struct ee_bus* bus, struct vu_state* vu) {\n    bus->vu1 = vu;\n}\n\nvoid ee_bus_init_kputchar(struct ee_bus* bus, void (*kputchar)(void*, char), void* udata) {\n    bus->kputchar = kputchar;\n    bus->kputchar_udata = udata;\n}\n\nvoid ee_bus_destroy(struct ee_bus* bus) {\n    free(bus);\n}\n\n#define MAP_MEM_READ(b, l, u, d, n) \\\n    if ((addr >= l) && (addr <= u)) return ps2_ ## d ## _read ## b(bus->n, addr - l);\n\n#define MAP_MEM_WRITE(b, l, u, d, n) \\\n    if ((addr >= l) && (addr <= u)) { ps2_ ## d ## _write ## b(bus->n, addr - l, data); return; }\n\n#define MAP_REG_READ(b, l, u, d, n) \\\n    if ((addr >= l) && (addr <= u)) return ps2_ ## d ## _read ## b(bus->n, addr);\n\n#define MAP_REG_WRITE(b, l, u, d, n) \\\n    if ((addr >= l) && (addr <= u)) { ps2_ ## d ## _write ## b(bus->n, addr, data); return; }\n\n// Fast ranges:\n// - RAM   00000000-01FFFFFF -> 0000-0fff (1000)\n// - BIOS  1FC00000-1FFFFFFF -> fe00-ffff (200)\n// - VU    11000000-1100FFFF -> 8800-8807 (8)\n// - IOP   1C000000-1C1FFFFF -> e000-e0ff (100)\n\nuint64_t ee_bus_read8(void* udata, uint32_t addr) {\n    struct ee_bus* bus = (struct ee_bus*)udata;\n\n    void* ptr = bus->fastmem_r_table[addr >> 13];\n\n    if (likely(ptr)) return *((uint8_t*)(((uint8_t*)ptr) + (addr & 0x1fff)));\n\n    // MAP_MEM_READ(8, 0x00000000, 0x01FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(8, 0x20000000, 0x21FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(8, 0x30000000, 0x31FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(8, 0x1C000000, 0x1C1FFFFF, ram, iop_ram);\n    // MAP_MEM_READ(8, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_MEM_READ(8, 0x11000000, 0x11007FFF, vu, vu0);\n    MAP_MEM_READ(8, 0x11008000, 0x1100FFFF, vu, vu1);\n    MAP_REG_READ(8, 0x10008000, 0x1000EFFF, dmac, dmac);\n    MAP_REG_READ(8, 0x1000F520, 0x1000F5FF, dmac, dmac);\n    MAP_REG_READ(8, 0x1F402004, 0x1F402018, cdvd, cdvd);\n    MAP_MEM_READ(8, 0x1E000000, 0x1E3FFFFF, bios, rom1);\n    MAP_MEM_READ(8, 0x1E400000, 0x1E7FFFFF, bios, rom2);\n    MAP_REG_READ(64, 0x12000000, 0x12001FFF, gs, gs); // Reuse 64-bit function\n    MAP_REG_READ(8, 0x1F801460, 0x1F80147F, dev9, dev9);\n    MAP_REG_READ(8, 0x14000000, 0x1400FFFF, speed, speed);\n\n    if ((addr >> 16) == 0x1f80) return 0;\n\n    // printf(\"bus: Unhandled 8-bit read from physical address 0x%08x\\n\", addr); // *(int*)0 = 0;\n\n    return 0;\n}\n\nuint64_t ee_bus_read16(void* udata, uint32_t addr) {\n    struct ee_bus* bus = (struct ee_bus*)udata;\n\n    void* ptr = bus->fastmem_r_table[addr >> 13];\n\n    if (likely(ptr)) return *((uint16_t*)(((uint8_t*)ptr) + (addr & 0x1fff)));\n\n    // MAP_MEM_READ(16, 0x00000000, 0x01FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(16, 0x20000000, 0x21FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(16, 0x30000000, 0x31FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(16, 0x1C000000, 0x1C1FFFFF, ram, iop_ram);\n    // MAP_MEM_READ(16, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_REG_READ(16, 0x10008000, 0x1000EFFF, dmac, dmac);\n    MAP_REG_READ(16, 0x1000F520, 0x1000F5FF, dmac, dmac);\n    MAP_MEM_READ(16, 0x11000000, 0x11007FFF, vu, vu0);\n    MAP_MEM_READ(16, 0x11008000, 0x1100FFFF, vu, vu1);\n    MAP_MEM_READ(16, 0x1E000000, 0x1E3FFFFF, bios, rom1);\n    MAP_MEM_READ(16, 0x1E400000, 0x1E7FFFFF, bios, rom2);\n    MAP_REG_READ(16, 0x10000000, 0x10001FFF, ee_timers, timers);\n    MAP_REG_READ(16, 0x1F801460, 0x1F80147F, dev9, dev9);\n    MAP_REG_READ(16, 0x14000000, 0x1400FFFF, speed, speed);\n\n    if (addr == 0x1a000010) return 0xffff;\n\n    switch (addr) {\n        case 0x1f803800: return 0;\n\n        // SCPH-39001 stub\n        case 0x1a000006: return 2;\n    }\n\n    fprintf(stderr, \"bus: Unhandled 16-bit read from physical address 0x%08x\\n\", addr); // exit(1);\n\n    return 0;\n}\n\nuint64_t ee_bus_read32(void* udata, uint32_t addr) {\n    struct ee_bus* bus = (struct ee_bus*)udata;\n\n    // pacmanbr\n    // if (addr == 0x00189A40) return 0x34630009;\n\n    // umilucky\n    // if (addr == 0x001A2834) return 0x0;\n    // if (addr == 0x001AAC9C) return 0x24040002;\n    // if (addr == 0x001AAE08) return 0x0;\n\n    // akaiser\n    // if (addr == 0x00102110) return 0x0;\n    // if (addr == 0x00102130) return 0x0;\n    // if (addr == 0x0013e388) return 0x0;\n    // if (addr == 0x001c2b0c) return 0x24027FFF;\n    // if (addr == 0x00104d7c) return 0x240F0064;\n\n    // akaievo\n    // if (addr == 0x001021E8) return 0x0;\n    // if (addr == 0x00102208) return 0x0;\n    // if (addr == 0x0015FA18) return 0x0;\n    // if (addr == 0x0015FC20) return 0x0;\n    // if (addr == 0x001D8A0C) return 0x24027FFF;\n    // if (addr == 0x001051b4) return 0x240F02BC;\n\n    void* ptr = bus->fastmem_r_table[addr >> 13];\n\n    if (likely(ptr)) return *((uint32_t*)(((uint8_t*)ptr) + (addr & 0x1fff)));\n\n    // MAP_MEM_READ(32, 0x00000000, 0x01FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(32, 0x20000000, 0x21FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(32, 0x30000000, 0x31FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(32, 0x1C000000, 0x1C1FFFFF, ram, iop_ram);\n    // MAP_MEM_READ(32, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_REG_READ(32, 0x1000F200, 0x1000F26F, sif, sif);\n    MAP_REG_READ(32, 0x10008000, 0x1000EFFF, dmac, dmac);\n    MAP_REG_READ(32, 0x1000F520, 0x1000F5FF, dmac, dmac);\n    MAP_REG_READ(64, 0x10002000, 0x1000203F, ipu, ipu);\n    MAP_REG_READ(64, 0x10007000, 0x1000701F, ipu, ipu);\n    MAP_REG_READ(32, 0x10003000, 0x100037FF, gif, gif);\n    MAP_REG_READ(32, 0x10003800, 0x10003BFF, vif, vif0);\n    MAP_REG_READ(32, 0x10003C00, 0x10003FFF, vif, vif1);\n    MAP_REG_READ(32, 0x10004000, 0x10004FFF, vif, vif0);\n    MAP_REG_READ(32, 0x10005000, 0x10005FFF, vif, vif1);\n    MAP_REG_READ(32, 0x1000F000, 0x1000F01F, intc, intc);\n    MAP_REG_READ(64, 0x12000000, 0x12001FFF, gs, gs); // Reuse 64-bit function\n    MAP_REG_READ(32, 0x10000000, 0x10001FFF, ee_timers, timers);\n    MAP_MEM_READ(32, 0x11000000, 0x11007FFF, vu, vu0);\n    MAP_MEM_READ(32, 0x11008000, 0x1100FFFF, vu, vu1);\n    MAP_MEM_READ(32, 0x1E000000, 0x1E3FFFFF, bios, rom1);\n    MAP_MEM_READ(32, 0x1E400000, 0x1E7FFFFF, bios, rom2);\n    MAP_REG_READ(32, 0x1F801600, 0x1F8016FF, usb, usb);\n    MAP_REG_READ(32, 0x1F801460, 0x1F80147F, dev9, dev9);\n    MAP_REG_READ(32, 0x14000000, 0x1400FFFF, speed, speed);\n\n    switch (addr) {\n        case 0x1000F440: {\n            uint8_t sop = (bus->mch_ricm >> 6) & 0xF;\n            uint8_t sa = (bus->mch_ricm >> 16) & 0xFFF;\n\n            if (!sop) {\n                switch (sa) {\n                    case 0x21: {\n                        if (bus->rdram_sdevid < 2) {\n                            bus->rdram_sdevid++;\n\n                            return 0x1F;\n                        }\n\n                        return 0;\n                    } break;\n\n                    case 0x23: return 0x0D0D;\n                    case 0x24: return 0x0090;\n                    case 0x40: return bus->mch_ricm & 0x1F;\n                }\n            }\n\n            return 0;\n        } break;\n        case 0x1000f130:\n        case 0x1000f400:\n        case 0x1000f410:\n        case 0x1000f430:\n        case 0x1f80141c: {\n            return 0;\n        } break;\n    }\n\n    // printf(\"bus: Unhandled 32-bit read from physical address 0x%08x\\n\", addr);\n    \n    if ((addr & 0xffff0000) == 0xfffe0000)\n        exit(1);\n\n    return 0;\n}\n\nuint64_t ee_bus_read64(void* udata, uint32_t addr) {\n    struct ee_bus* bus = (struct ee_bus*)udata;\n\n    void* ptr = bus->fastmem_r_table[addr >> 13];\n\n    if (likely(ptr)) return *((uint64_t*)(((uint8_t*)ptr) + (addr & 0x1fff)));\n\n    // MAP_MEM_READ(64, 0x00000000, 0x01FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(64, 0x20000000, 0x21FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(64, 0x30000000, 0x31FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(64, 0x1C000000, 0x1C1FFFFF, ram, iop_ram);\n    // MAP_MEM_READ(64, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_REG_READ(64, 0x12000000, 0x12001FFF, gs, gs);\n    MAP_REG_READ(64, 0x10002000, 0x1000203F, ipu, ipu);\n    MAP_REG_READ(64, 0x10007000, 0x1000701F, ipu, ipu);\n    MAP_REG_READ(32, 0x10008000, 0x1000EFFF, dmac, dmac);\n    MAP_REG_READ(32, 0x1000F520, 0x1000F5FF, dmac, dmac);\n    MAP_REG_READ(32, 0x10000000, 0x10001FFF, ee_timers, timers); // Reuse 32-bit function\n    MAP_MEM_READ(64, 0x11000000, 0x11007FFF, vu, vu0);\n    MAP_MEM_READ(64, 0x11008000, 0x1100FFFF, vu, vu1);\n    MAP_MEM_READ(64, 0x1E000000, 0x1E3FFFFF, bios, rom1);\n    MAP_MEM_READ(64, 0x1E400000, 0x1E7FFFFF, bios, rom2);\n\n    fprintf(stderr, \"bus: Unhandled 64-bit read from physical address 0x%08x\\n\", addr); // exit(1);\n\n    return 0;\n}\n\nuint128_t ee_bus_read128(void* udata, uint32_t addr) {\n    struct ee_bus* bus = (struct ee_bus*)udata;\n\n    void* ptr = bus->fastmem_r_table[addr >> 13];\n\n    if (likely(ptr)) return *((uint128_t*)(((uint8_t*)ptr) + (addr & 0x1fff)));\n\n    // MAP_MEM_READ(128, 0x00000000, 0x01FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(128, 0x20000000, 0x21FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(128, 0x30000000, 0x31FFFFFF, ram, ee_ram);\n    // MAP_MEM_READ(128, 0x1C000000, 0x1C1FFFFF, ram, iop_ram);\n    // MAP_MEM_READ(128, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_REG_READ(128, 0x10004000, 0x10004FFF, vif, vif0);\n    MAP_REG_READ(128, 0x10005000, 0x10005FFF, vif, vif1);\n    MAP_REG_READ(128, 0x10007000, 0x1000701F, ipu, ipu);\n    MAP_MEM_READ(128, 0x11000000, 0x11007FFF, vu, vu0);\n    MAP_MEM_READ(128, 0x11008000, 0x1100FFFF, vu, vu1);\n    MAP_MEM_READ(128, 0x1E000000, 0x1E3FFFFF, bios, rom1);\n    MAP_MEM_READ(128, 0x1E400000, 0x1E7FFFFF, bios, rom2);\n\n    fprintf(stderr, \"bus: Unhandled 128-bit read from physical address 0x%08x\\n\", addr); // exit(1);\n\n    // *(int*)0 = 0;\n\n    return (uint128_t){ .u64[0] = 0, .u64[1] = 0 };\n}\n\nvoid ee_bus_write8(void* udata, uint32_t addr, uint64_t data) {\n    struct ee_bus* bus = (struct ee_bus*)udata;\n\n    void* ptr = bus->fastmem_w_table[addr >> 13];\n\n    if (likely(ptr)) {\n        *((uint8_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data;\n\n        return;\n    }\n\n    // MAP_MEM_WRITE(8, 0x00000000, 0x01FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(8, 0x20000000, 0x21FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(8, 0x30000000, 0x31FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(8, 0x1C000000, 0x1C1FFFFF, ram, iop_ram);\n    // MAP_MEM_WRITE(8, 0x1FC00000, 0x1FFFFFFF, bios, bios); // BIOS Firmware update\n    MAP_REG_WRITE(8, 0x10008000, 0x1000EFFF, dmac, dmac);\n    MAP_REG_WRITE(8, 0x1000F520, 0x1000F5FF, dmac, dmac);\n    MAP_REG_WRITE(8, 0x1F402004, 0x1F402018, cdvd, cdvd);\n    MAP_MEM_WRITE(8, 0x11000000, 0x11007FFF, vu, vu0);\n    MAP_MEM_WRITE(8, 0x11008000, 0x1100FFFF, vu, vu1);\n    MAP_REG_WRITE(8, 0x1000F000, 0x1000F01F, intc, intc);\n    MAP_REG_WRITE(8, 0x1F801460, 0x1F80147F, dev9, dev9);\n    MAP_REG_WRITE(8, 0x14000000, 0x1400FFFF, speed, speed);\n\n    if (addr == 0x1000f180) { bus->kputchar(bus->kputchar_udata, data & 0xff); return; }\n\n    // printf(\"bus: Unhandled 8-bit write to physical address 0x%08x (0x%02lx)\\n\", addr, data);\n}\n\nvoid ee_bus_write16(void* udata, uint32_t addr, uint64_t data) {\n    struct ee_bus* bus = (struct ee_bus*)udata;\n\n    void* ptr = bus->fastmem_w_table[addr >> 13];\n\n    if (likely(ptr)) {\n        *((uint16_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data;\n\n        return;\n    }\n\n    // MAP_MEM_WRITE(16, 0x00000000, 0x01FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(16, 0x20000000, 0x21FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(16, 0x30000000, 0x31FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(16, 0x1C000000, 0x1C1FFFFF, ram, iop_ram);\n    // MAP_MEM_WRITE(16, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_REG_WRITE(16, 0x10008000, 0x1000EFFF, dmac, dmac);\n    MAP_REG_WRITE(16, 0x1000F520, 0x1000F5FF, dmac, dmac);\n    MAP_MEM_WRITE(16, 0x11000000, 0x11007FFF, vu, vu0);\n    MAP_MEM_WRITE(16, 0x11008000, 0x1100FFFF, vu, vu1);\n    MAP_REG_WRITE(16, 0x1000F000, 0x1000F01F, intc, intc);\n    MAP_REG_WRITE(16, 0x10000000, 0x10001FFF, ee_timers, timers);\n    MAP_REG_WRITE(16, 0x1F801460, 0x1F80147F, dev9, dev9);\n    MAP_REG_WRITE(16, 0x14000000, 0x1400FFFF, speed, speed);\n\n    switch (addr) {\n        case 0x1a000008:\n        case 0x1f801470:\n        case 0x1f801472: return;\n    }\n\n    printf(\"bus: Unhandled 16-bit write to physical address 0x%08x (0x%04lx)\\n\", addr, data);\n}\n\nvoid ee_bus_write32(void* udata, uint32_t addr, uint64_t data) {\n    struct ee_bus* bus = (struct ee_bus*)udata;\n\n    void* ptr = bus->fastmem_w_table[addr >> 13];\n\n    if (likely(ptr)) {\n        *((uint32_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data;\n\n        return;\n    }\n\n    // MAP_MEM_WRITE(32, 0x00000000, 0x01FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(32, 0x20000000, 0x21FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(32, 0x30000000, 0x31FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(32, 0x1C000000, 0x1C1FFFFF, ram, iop_ram);\n    // MAP_MEM_WRITE(32, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_REG_WRITE(32, 0x10000000, 0x10001FFF, ee_timers, timers);\n    MAP_REG_WRITE(64, 0x10002000, 0x1000203F, ipu, ipu);\n    MAP_REG_WRITE(32, 0x10003000, 0x100037FF, gif, gif);\n    MAP_REG_WRITE(64, 0x10007000, 0x1000701F, ipu, ipu);\n    MAP_REG_WRITE(32, 0x10008000, 0x1000EFFF, dmac, dmac);\n    MAP_REG_WRITE(32, 0x1000F000, 0x1000F01F, intc, intc);\n    MAP_REG_WRITE(32, 0x1000F200, 0x1000F26F, sif, sif);\n    MAP_REG_WRITE(32, 0x10003800, 0x10003BFF, vif, vif0);\n    MAP_REG_WRITE(32, 0x10003C00, 0x10003FFF, vif, vif1);\n    MAP_REG_WRITE(32, 0x10004000, 0x10004FFF, vif, vif0);\n    MAP_REG_WRITE(32, 0x10005000, 0x10005FFF, vif, vif1);\n    MAP_REG_WRITE(32, 0x1000F520, 0x1000F5FF, dmac, dmac);\n    MAP_REG_WRITE(64, 0x12000000, 0x12001FFF, gs, gs); // Reuse 64-bit function\n    MAP_MEM_WRITE(32, 0x11000000, 0x11007FFF, vu, vu0);\n    MAP_MEM_WRITE(32, 0x11008000, 0x1100FFFF, vu, vu1);\n    MAP_REG_WRITE(32, 0x1F801600, 0x1F8016FF, usb, usb);\n    MAP_REG_WRITE(32, 0x1F801460, 0x1F80147F, dev9, dev9);\n    MAP_REG_WRITE(32, 0x14000000, 0x1400FFFF, speed, speed);\n\n    switch (addr) {\n        case 0x1000f430: {\n            uint8_t sa = (data >> 16) & 0xFFF;\n            uint8_t sbc = (data >> 6) & 0xF;\n\n            if ((sa == 0x21) && (sbc == 0x1) && ((bus->mch_drd >> 7) & 1) == 0)\n                bus->rdram_sdevid = 0;\n\n            bus->mch_ricm = data & ~0x80000000;\n        } return;\n        case 0x1000f440: {\n            bus->mch_drd = data;\n        } return;\n        case 0x1000f100:\n        case 0x1000f120:\n        case 0x1000f140:\n        case 0x1000f150:\n        case 0x1000f400:\n        case 0x1000f410:\n        case 0x1000f420:\n        case 0x1000f450:\n        case 0x1000f460:\n        case 0x1000f480:\n        case 0x1000f490:\n        case 0x1000f500:\n        case 0x1000f510:\n        case 0x1f80141c: return;\n    }\n\n    // fprintf(stderr, \"bus: Unhandled 32-bit write to physical address 0x%08x (0x%08lx)\\n\", addr, data); if ((addr & 0xff000000) == 0x02000000) exit(1);\n}\n\nvoid ee_bus_write64(void* udata, uint32_t addr, uint64_t data) {\n    struct ee_bus* bus = (struct ee_bus*)udata;\n\n    void* ptr = bus->fastmem_w_table[addr >> 13];\n\n    if (likely(ptr)) {\n        *((uint64_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data;\n\n        return;\n    }\n\n    // MAP_MEM_WRITE(64, 0x00000000, 0x01FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(64, 0x20000000, 0x21FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(64, 0x30000000, 0x31FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(64, 0x1C000000, 0x1C1FFFFF, ram, iop_ram);\n    // MAP_MEM_WRITE(64, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_REG_WRITE(64, 0x12000000, 0x12002000, gs, gs);\n    MAP_REG_WRITE(64, 0x10002000, 0x1000203F, ipu, ipu);\n    MAP_REG_WRITE(64, 0x10007000, 0x1000701F, ipu, ipu);\n    MAP_REG_WRITE(32, 0x10008000, 0x1000EFFF, dmac, dmac);\n    MAP_REG_WRITE(32, 0x1000F520, 0x1000F5FF, dmac, dmac);\n    MAP_REG_WRITE(32, 0x10000000, 0x10001FFF, ee_timers, timers); // Reuse 32-bit function\n    MAP_MEM_WRITE(64, 0x11000000, 0x11007FFF, vu, vu0);\n    MAP_MEM_WRITE(64, 0x11008000, 0x1100FFFF, vu, vu1);\n    MAP_MEM_WRITE(64, 0x1000F000, 0x1000F01F, intc, intc);\n\n    printf(\"bus: Unhandled 64-bit write to physical address 0x%08x (0x%08lx%08lx)\\n\", addr, data >> 32, data & 0xffffffff);\n}\n\nvoid ee_bus_write128(void* udata, uint32_t addr, uint128_t data) {\n    struct ee_bus* bus = (struct ee_bus*)udata;\n\n    void* ptr = bus->fastmem_w_table[addr >> 13];\n\n    if (likely(ptr)) {\n        *((uint128_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data;\n\n        return;\n    }\n\n    // MAP_MEM_WRITE(128, 0x00000000, 0x01FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(128, 0x20000000, 0x21FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(128, 0x30000000, 0x31FFFFFF, ram, ee_ram);\n    // MAP_MEM_WRITE(128, 0x1C000000, 0x1C1FFFFF, ram, iop_ram);\n    // MAP_MEM_WRITE(128, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_REG_WRITE(128, 0x10006000, 0x10006FFF, gif, gif);\n    MAP_REG_WRITE(128, 0x10007000, 0x1000701F, ipu, ipu);\n    MAP_REG_WRITE(128, 0x10004000, 0x10004FFF, vif, vif0);\n    MAP_REG_WRITE(128, 0x10005000, 0x10005FFF, vif, vif1);\n    MAP_MEM_WRITE(128, 0x11000000, 0x11007FFF, vu, vu0);\n    MAP_MEM_WRITE(128, 0x11008000, 0x1100FFFF, vu, vu1);\n\n    // printf(\"bus: Unhandled 128-bit write to physical address 0x%08x (0x%08x%08x%08x%08x)\\n\", addr, data.u32[3], data.u32[2], data.u32[1], data.u32[0]);\n}\n"
  },
  {
    "path": "src/ee/bus.h",
    "content": "#ifndef EE_BUS_H\n#define EE_BUS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"u128.h\"\n\n#include \"dmac.h\"\n#include \"gif.h\"\n#include \"intc.h\"\n#include \"timers.h\"\n#include \"vif.h\"\n#include \"ipu/ipu.h\"\n#include \"../iop/cdvd.h\"\n#include \"../iop/usb.h\"\n\n#include \"shared/ram.h\"\n#include \"shared/sif.h\"\n#include \"shared/bios.h\"\n#include \"shared/sbus.h\"\n#include \"shared/dev9.h\"\n#include \"shared/speed.h\"\n\nstruct ee_bus {\n    // EE-only\n    struct ps2_ram* ee_ram;\n    struct ps2_dmac* dmac;\n    struct ps2_gif* gif;\n    struct ps2_gs* gs;\n    struct ps2_ipu* ipu;\n    struct ps2_intc* intc;\n    struct ps2_vif* vif0;\n    struct ps2_vif* vif1;\n    struct vu_state* vu0;\n    struct vu_state* vu1;\n    struct ps2_ee_timers* timers;\n    \n    // EE/IOP\n    struct ps2_cdvd* cdvd;\n    struct ps2_usb* usb;\n    struct ps2_bios* bios;\n    struct ps2_bios* rom1;\n    struct ps2_bios* rom2;\n    struct ps2_ram* iop_ram;\n    struct ps2_sif* sif;\n    struct ps2_sbus* sbus;\n    struct ps2_dev9* dev9;\n    struct ps2_speed* speed;\n\n    void* fastmem_r_table[0x10000];\n    void* fastmem_w_table[0x10000];\n\n    uint32_t mch_ricm;\n    uint32_t mch_drd;\n    uint32_t rdram_sdevid;\n\n    void (*kputchar)(void*, char);\n    void* kputchar_udata;\n};\n\nvoid ee_bus_init_ram(struct ee_bus* bus, struct ps2_ram* ram);\nvoid ee_bus_init_dmac(struct ee_bus* bus, struct ps2_dmac* dmac);\nvoid ee_bus_init_intc(struct ee_bus* bus, struct ps2_intc* intc);\nvoid ee_bus_init_gif(struct ee_bus* bus, struct ps2_gif* gif);\nvoid ee_bus_init_vif0(struct ee_bus* bus, struct ps2_vif* vif0);\nvoid ee_bus_init_vif1(struct ee_bus* bus, struct ps2_vif* vif1);\nvoid ee_bus_init_gs(struct ee_bus* bus, struct ps2_gs* gs);\nvoid ee_bus_init_ipu(struct ee_bus* bus, struct ps2_ipu* ipu);\nvoid ee_bus_init_timers(struct ee_bus* bus, struct ps2_ee_timers* timers);\nvoid ee_bus_init_bios(struct ee_bus* bus, struct ps2_bios* bios);\nvoid ee_bus_init_rom1(struct ee_bus* bus, struct ps2_bios* rom1);\nvoid ee_bus_init_rom2(struct ee_bus* bus, struct ps2_bios* rom2);\nvoid ee_bus_init_iop_ram(struct ee_bus* bus, struct ps2_ram* iop_ram);\nvoid ee_bus_init_sif(struct ee_bus* bus, struct ps2_sif* sif);\nvoid ee_bus_init_cdvd(struct ee_bus* bus, struct ps2_cdvd* cdvd);\nvoid ee_bus_init_usb(struct ee_bus* bus, struct ps2_usb* usb);\nvoid ee_bus_init_sbus(struct ee_bus* bus, struct ps2_sbus* sbus);\nvoid ee_bus_init_dev9(struct ee_bus* bus, struct ps2_dev9* dev9);\nvoid ee_bus_init_speed(struct ee_bus* bus, struct ps2_speed* speed);\nvoid ee_bus_init_vu0(struct ee_bus* bus, struct vu_state* vu);\nvoid ee_bus_init_vu1(struct ee_bus* bus, struct vu_state* vu);\nvoid ee_bus_init_kputchar(struct ee_bus* bus, void (*kputchar)(void*, char), void* udata);\nvoid ee_bus_init_fastmem(struct ee_bus* bus, int ee_ram_size, int iop_ram_size);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/bus_decl.h",
    "content": "#ifndef EE_BUS_DECL_H\n#define EE_BUS_DECL_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"u128.h\"\n\nstruct ee_bus;\n\nstruct ee_bus* ee_bus_create(void);\nvoid ee_bus_init(struct ee_bus* bus, const char* bios_path);\nvoid ee_bus_destroy(struct ee_bus* bus);\n\nuint64_t ee_bus_read8(void* udata, uint32_t addr);\nuint64_t ee_bus_read16(void* udata, uint32_t addr);\nuint64_t ee_bus_read32(void* udata, uint32_t addr);\nuint64_t ee_bus_read64(void* udata, uint32_t addr);\nuint128_t ee_bus_read128(void* udata, uint32_t addr);\nvoid ee_bus_write8(void* udata, uint32_t addr, uint64_t data);\nvoid ee_bus_write16(void* udata, uint32_t addr, uint64_t data);\nvoid ee_bus_write32(void* udata, uint32_t addr, uint64_t data);\nvoid ee_bus_write64(void* udata, uint32_t addr, uint64_t data);\nvoid ee_bus_write128(void* udata, uint32_t addr, uint128_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/dmac.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n\n#include \"dmac.h\"\n\n#define printf(fmt, ...)(0)\n\nstatic inline uint128_t dmac_read_qword(struct ps2_dmac* dmac, uint32_t addr) {\n    int spr = addr & 0x80000000;\n\n    if (!spr)\n        return ee_bus_read128(dmac->bus, addr & 0xfffffff0);\n\n    return ps2_ram_read128(dmac->spr, addr & 0x3ff0);\n}\n\nstatic inline void dmac_write_qword(struct ps2_dmac* dmac, uint32_t addr, int mem, uint128_t value) {\n    int spr = mem || (addr & 0x80000000);\n\n    if (!spr) {\n        ee_bus_write128(dmac->bus, addr & 0xfffffff0, value);\n\n        return;\n    }\n\n    ps2_ram_write128(dmac->spr, addr & 0x3ff0, value);\n}\n\nstruct ps2_dmac* ps2_dmac_create(void) {\n    return malloc(sizeof(struct ps2_dmac));\n}\n\nvoid ps2_dmac_init(struct ps2_dmac* dmac, struct ps2_sif* sif, struct ps2_iop_dma* iop_dma, struct ps2_ram* spr, struct ee_state* ee, struct sched_state* sched, struct ee_bus* bus) {\n    memset(dmac, 0, sizeof(struct ps2_dmac));\n\n    dmac->bus = bus;\n    dmac->sif = sif;\n    dmac->spr = spr;\n    dmac->iop_dma = iop_dma;\n    dmac->ee = ee;\n    dmac->sched = sched;\n\n    // v2+ BIOSes need this value on boot (smh...)\n    dmac->enable = 0x1201;\n}\n\nvoid ps2_dmac_destroy(struct ps2_dmac* dmac) {\n    free(dmac);\n}\n\nstatic inline struct dmac_channel* dmac_get_channel(struct ps2_dmac* dmac, uint32_t addr) {\n    switch (addr & 0xff00) {\n        case 0x8000: return &dmac->vif0;\n        case 0x9000: return &dmac->vif1;\n        case 0xA000: return &dmac->gif;\n        case 0xB000: return &dmac->ipu_from;\n        case 0xB400: return &dmac->ipu_to;\n        case 0xC000: return &dmac->sif0;\n        case 0xC400: return &dmac->sif1;\n        case 0xC800: return &dmac->sif2;\n        case 0xD000: return &dmac->spr_from;\n        case 0xD400: return &dmac->spr_to;\n    }\n\n    return NULL;\n}\n\nstatic inline const char* dmac_get_channel_name(struct ps2_dmac* dmac, uint32_t addr) {\n    switch (addr & 0xff00) {\n        case 0x8000: return \"vif0\";\n        case 0x9000: return \"vif1\";\n        case 0xA000: return \"gif\";\n        case 0xB000: return \"ipu_from\";\n        case 0xB400: return \"ipu_to\";\n        case 0xC000: return \"sif0\";\n        case 0xC400: return \"sif1\";\n        case 0xC800: return \"sif2\";\n        case 0xD000: return \"spr_from\";\n        case 0xD400: return \"spr_to\";\n    }\n\n    return NULL;\n}\n\nstatic inline int channel_is_done(struct dmac_channel* ch) {\n    return ch->tag.end || (ch->tag.irq && (ch->chcr & 0x80));\n}\n\nuint64_t ps2_dmac_read32(struct ps2_dmac* dmac, uint32_t addr) {\n    struct dmac_channel* c = dmac_get_channel(dmac, addr);\n\n    if (c) {\n        switch (addr & 0xff) {\n            case 0x00: return c->chcr;\n            case 0x10: return c->madr;\n            case 0x20: return c->qwc;\n            case 0x30: return c->tadr;\n            case 0x40: return c->asr0;\n            case 0x50: return c->asr1;\n            case 0x80: return c->sadr;\n        }\n\n        // printf(\"dmac: Unknown channel register %02x\\n\", addr & 0xff);\n\n        return 0;\n    }\n\n    switch (addr) {\n        case 0x1000E000: return dmac->ctrl;\n        case 0x1000E010: return dmac->stat;\n        case 0x1000E020: return dmac->pcr;\n        case 0x1000E030: return dmac->sqwc;\n        case 0x1000E040: return dmac->rbsr;\n        case 0x1000E050: return dmac->rbor;\n        case 0x1000F520: return dmac->enable;\n        case 0x1000F590: break; // ENABLEW (W)\n    }\n\n    return 0;\n}\n\nstatic inline void dmac_process_source_tag(struct ps2_dmac* dmac, struct dmac_channel* c, uint128_t tag) {\n    // Set CHCR TAG bytes\n    c->chcr &= 0xffff;\n    c->chcr |= tag.u32[0] & 0xffff0000;\n\n    c->tag.qwc = TAG_QWC(tag);\n    c->tag.pct = TAG_PCT(tag);\n    c->tag.id = TAG_ID(tag);\n    c->tag.irq = TAG_IRQ(tag);\n    c->tag.addr = TAG_ADDR(tag);\n    c->tag.data = TAG_DATA(tag);\n\n    // if (dmac->mfifo_drain)\n    // printf(\"ee: dmac tag %016lx %016lx qwc=%08x id=%d irq=%d addr=%08x mem=%d data=%016lx\\n\",\n    //     tag.u64[1], tag.u64[0],\n    //     c->tag.qwc,\n    //     c->tag.id,\n    //     c->tag.irq,\n    //     c->tag.addr,\n    //     c->tag.mem,\n    //     c->tag.data\n    // );\n\n    c->tag.end = 0;\n    c->qwc = c->tag.qwc;\n\n    switch (c->tag.id) {\n        case 0: { // REFE tag\n            c->madr = c->tag.addr;\n            c->tadr += 16;\n            c->tag.end = 1;\n        } break;\n\n        case 1: {\n            c->madr = c->tadr + 16;\n            c->tadr = c->madr;\n        } break;\n\n        case 2: {\n            c->madr = c->tadr + 16;\n            c->tadr = c->tag.addr;\n        } break;\n\n        case 3: {\n            c->madr = c->tag.addr;\n            c->tadr += 16;\n        } break;\n\n        case 4: {\n            c->madr = c->tag.addr;\n            c->tadr += 16;\n        } break;\n\n        case 5: {\n            c->madr = c->tadr + 16;\n\n            int asp = (c->chcr >> 4) & 3;\n\n            if (!asp) {\n                c->asr0 = c->madr + (c->tag.qwc * 16);\n            } else if (asp == 1) {\n                c->asr1 = c->madr + (c->tag.qwc * 16);\n            }\n\n            c->tadr = c->tag.addr;\n            c->chcr += 0x10;\n        } break;\n\n        case 6: {\n            c->madr = c->tadr + 16;\n\n            int asp = (c->chcr >> 4) & 3;\n\n            if (asp == 2) {\n                c->tadr = c->asr1;\n                c->chcr -= 0x10;\n            } else if (asp == 1) {\n                c->tadr = c->asr0;\n                c->chcr -= 0x10;\n            } else {\n                c->tag.end = 1;\n            }\n        } break;\n  \n        case 7: {\n            c->madr = c->tadr + 16;\n            c->tag.end = 1;\n        } break;\n    }\n\n    // If TIE and TAG.IRQ are set, then end transfer\n    if ((c->chcr & 0x80) && c->tag.irq)\n        c->tag.end = 1;\n}\n\nstatic inline void dmac_process_dest_tag(struct ps2_dmac* dmac, struct dmac_channel* c, uint128_t tag) {\n    // Set CHCR TAG bytes\n    c->chcr &= 0xffff;\n    c->chcr |= tag.u32[0] & 0xffff0000;\n\n    c->tag.qwc = TAG_QWC(tag);\n    c->tag.pct = TAG_PCT(tag);\n    c->tag.id = TAG_ID(tag);\n    c->tag.irq = TAG_IRQ(tag);\n    c->tag.addr = TAG_ADDR(tag);\n    c->tag.data = TAG_DATA(tag);\n\n    c->qwc = c->tag.qwc;\n\n    c->tag.end = dmac->sif0.tag.irq && (dmac->sif0.chcr & 0x80);\n\n    switch (c->tag.id) {\n        case 7:\n            c->tag.end = 1;\n        case 0:\n        case 1:\n            c->madr = c->tag.addr;\n    }\n}\n\nstatic inline void dmac_test_cpcond0(struct ps2_dmac* dmac) {\n    ee_set_cpcond0(dmac->ee, (((~dmac->pcr) | dmac->stat) & 0x3ff) == 0x3ff);\n}\n\nstatic inline void dmac_test_irq(struct ps2_dmac* dmac) {\n    dmac_test_cpcond0(dmac);\n\n    int meis = ((dmac->stat >> 14) & 1) & ((dmac->stat >> 30) & 1);\n    int chirq = (dmac->stat & 0x3ff) & ((dmac->stat >> 16) & 0x3ff);\n\n    ee_set_int1(dmac->ee, chirq || meis);\n}\n\nstatic inline void dmac_set_irq(struct ps2_dmac* dmac, int ch) {\n    dmac->stat |= 1 << ch;\n\n    // printf(\"dmac: channel=%d flag=%08x mask=%08x irq=%08x\\n\", ch, dmac->stat & 0x3ff, (dmac->stat >> 16) & 0x3ff, (dmac->stat & 0x3ff) & ((dmac->stat >> 16) & 0x3ff));\n\n    dmac_test_irq(dmac);\n}\n\nvoid dmac_handle_vif0_transfer(struct ps2_dmac* dmac) {\n    // printf(\"ee: VIF0 DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x\\n\",\n    //     dmac->vif0.chcr & 1,\n    //     (dmac->vif0.chcr >> 2) & 3,\n    //     (dmac->vif0.chcr >> 6) & 1,\n    //     (dmac->vif0.chcr >> 7) & 1,\n    //     dmac->vif0.qwc,\n    //     dmac->vif0.madr,\n    //     dmac->vif0.tadr\n    // );\n\n    int mode = (dmac->vif0.chcr >> 2) & 3;\n\n    for (int i = 0; i < dmac->vif0.qwc; i++) {\n        uint128_t q = dmac_read_qword(dmac, dmac->vif0.madr);\n\n        // VIF0 FIFO address\n        ee_bus_write128(dmac->bus, 0x10004000, q);\n\n        dmac->vif0.madr += 16;\n    }\n\n    if (mode == 0) {\n        dmac->vif0.chcr &= ~0x100;\n        dmac->vif0.qwc = 0;\n\n        dmac_set_irq(dmac, DMAC_VIF0);\n\n        return;\n    }\n\n    // Chain mode\n    do {\n        uint128_t tag = dmac_read_qword(dmac, dmac->vif0.tadr);\n\n        dmac_process_source_tag(dmac, &dmac->vif0, tag);\n\n        // printf(\"ee: vif0 tag qwc=%08x madr=%08x tadr=%08x id=%d addr=%08x\\n\",\n        //     dmac->vif0.tag.qwc,\n        //     dmac->vif0.madr,\n        //     dmac->vif0.tadr,\n        //     dmac->vif0.tag.id,\n        //     dmac->vif0.tag.addr\n        // );\n\n        // CHCR.TTE: Transfer tag DATA field\n        if ((dmac->vif0.chcr >> 6) & 1) {\n            ee_bus_write32(dmac->bus, 0x10004000, dmac->vif0.tag.data & 0xffffffff);\n            ee_bus_write32(dmac->bus, 0x10004000, dmac->vif0.tag.data >> 32);\n        }\n\n        for (int i = 0; i < dmac->vif0.qwc; i++) {\n            uint128_t q = dmac_read_qword(dmac, dmac->vif0.madr);\n\n            // printf(\"ee: Sending %016lx%016lx from %08x to VIF0 FIFO\\n\",\n            //     q.u64[1], q.u64[0],\n            //     dmac->vif0.madr\n            // );\n\n            ee_bus_write128(dmac->bus, 0x10004000, q);\n\n            dmac->vif0.madr += 16;\n        }\n\n        if (dmac->vif0.tag.id == 1) {\n            dmac->vif0.tadr = dmac->vif0.madr;\n        }\n    } while (!channel_is_done(&dmac->vif0));\n\n    dmac->vif0.chcr &= ~0x100;\n    dmac->vif0.qwc = 0;\n\n    dmac_set_irq(dmac, DMAC_VIF0);\n}\n\nvoid dmac_send_vif1_irq(void* udata, int overshoot) {\n    struct ps2_dmac* dmac = (struct ps2_dmac*)udata;\n\n    dmac->vif1.chcr &= ~0x100;\n    dmac->vif1.qwc = 0;\n\n    // printf(\"dmac: VIF1 interrupt\\n\");\n\n    dmac_set_irq(dmac, DMAC_VIF1);\n}\n\nvoid mfifo_handle_ref_tag(struct ps2_dmac* dmac) {\n    struct dmac_channel* c = dmac->mfifo_drain;\n\n    while (c->qwc) {\n        uint128_t q = dmac_read_qword(dmac, c->madr);\n\n        if (c == &dmac->vif1) {\n            // VIF1 FIFO\n            ee_bus_write128(dmac->bus, 0x10005000, q);\n        } else {\n            // GIF FIFO\n            ps2_gif_fifo_write(dmac->bus->gif, q, GIF_PATH3);\n        }\n\n        c->madr += 16;\n        c->qwc--;\n    }\n\n    if (channel_is_done(c)) {\n        // fprintf(stdout, \"dmac: mfifo channel done end=%d tte-irq=%d\\n\", c->tag.end, c->tag.irq && (c->chcr & 0x80));\n        dmac_set_irq(dmac, c == &dmac->vif1 ? DMAC_VIF1 : DMAC_GIF);\n\n        c->chcr &= ~0x100;\n\n        return;\n    }\n\n    if (c->tag.id == 1) {\n        c->tadr = dmac->rbor | (c->madr & dmac->rbsr);\n    }\n}\n\nvoid mfifo_write_qword(struct ps2_dmac* dmac, uint128_t q) {\n    struct dmac_channel* c = dmac->mfifo_drain;\n\n    if (c->qwc) {\n        uint128_t q = dmac_read_qword(dmac, c->madr);\n\n        if (c == &dmac->vif1) {\n            // VIF1 FIFO\n            ee_bus_write128(dmac->bus, 0x10005000, q);\n        } else {\n            // GIF FIFO\n            ps2_gif_fifo_write(dmac->bus->gif, q, GIF_PATH3);\n        }\n\n        c->madr += 16;\n        c->qwc--;\n\n        // fprintf(stdout, \"dmac: mfifo channel qwc=%d\\n\", c->qwc);\n\n        if (c->qwc == 0) {\n            if (channel_is_done(c)) {\n                // fprintf(stdout, \"dmac: mfifo channel done end=%d tte-irq=%d\\n\", c->tag.end, c->tag.irq && (c->chcr & 0x80));\n                dmac_set_irq(dmac, c == &dmac->vif1 ? DMAC_VIF1 : DMAC_GIF);\n\n                c->chcr &= ~0x100;\n\n                return;\n            }\n\n            if (c->tag.id == 1) {\n                c->tadr = dmac->rbor | (c->madr & dmac->rbsr);\n            }\n        }\n\n        return;\n    }\n\n    uint128_t tag = dmac_read_qword(dmac, c->tadr);\n\n    dmac_process_source_tag(dmac, c, tag);\n\n    if ((c->chcr >> 6) & 1) {\n        ee_bus_write32(dmac->bus, 0x10005000, c->tag.data & 0xffffffff);\n        ee_bus_write32(dmac->bus, 0x10005000, c->tag.data >> 32);\n    }\n\n    c->tadr = dmac->rbor | (c->tadr & dmac->rbsr);\n\n    // fprintf(stdout, \"dmac: tadr=%08x madr=%08x qwc=%d tagid=%d end=%d\\n\", c->tadr, c->madr, c->qwc, c->tag.id, c->tag.end);\n\n    switch (c->tag.id) {\n        case 1:\n        case 2:\n        case 5:\n        case 6:\n        case 7: {\n            c->madr = dmac->rbor | (c->madr & dmac->rbsr);\n        } break;\n\n        default: {\n            mfifo_handle_ref_tag(dmac);\n\n            if (c->tadr == dmac->spr_from.madr) {\n                // fprintf(stdout, \"dmac: MFIFO empty\\n\");\n\n                dmac_set_irq(dmac, DMAC_MEIS);\n            }\n        } return;\n    }\n\n    if (c->qwc == 0) {\n        if (channel_is_done(c)) {\n            // fprintf(stdout, \"dmac: mfifo channel done end=%d tte-irq=%d\\n\", c->tag.end, c->tag.irq && (c->chcr & 0x80));\n            dmac_set_irq(dmac, c == &dmac->vif1 ? DMAC_VIF1 : DMAC_GIF);\n\n            c->chcr &= ~0x100;\n\n            return;\n        }\n    }\n\n    if (c->tadr == dmac->spr_from.madr) {\n        // fprintf(stdout, \"dmac: MFIFO empty\\n\");\n\n        dmac_set_irq(dmac, DMAC_MEIS);\n    }\n}\n\nvoid dmac_handle_vif1_transfer(struct ps2_dmac* dmac) {\n    // fprintf(stdout, \"dmac: VIF1 DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x end=%d\\n\",\n    //     dmac->vif1.chcr & 1,\n    //     (dmac->vif1.chcr >> 2) & 3,\n    //     (dmac->vif1.chcr >> 6) & 1,\n    //     (dmac->vif1.chcr >> 7) & 1,\n    //     dmac->vif1.qwc,\n    //     dmac->vif1.madr,\n    //     dmac->vif1.tadr,\n    //     dmac->vif1.tag.end\n    // );\n\n    int mfifo_drain = (dmac->ctrl >> 2) & 3;\n\n    if (mfifo_drain == 2) {\n        return;\n    }\n\n    int tte = (dmac->vif1.chcr >> 6) & 1;\n    int mode = (dmac->vif1.chcr >> 2) & 3;\n\n    if (mode == 3)\n        mode = 1;\n\n    struct sched_event event;\n\n    event.name = \"VIF1 DMA IRQ\";\n    event.udata = dmac;\n    event.callback = dmac_send_vif1_irq;\n    event.cycles = 4000;\n\n    // We don't handle VIF1 reads\n    if ((dmac->vif1.chcr & 1) == 0) {\n        // Gran Turismo 3 sends a VIF1 read with QWC=0, presumably to\n        // wait until the GIF FIFO is actually full, so we shouldn't send\n        // an interrupt there.\n        if (dmac->vif1.qwc == 0)\n            return;\n\n        dmac->vif1.qwc = 0;\n\n        sched_schedule(dmac->sched, event);\n\n        return;\n    }\n\n    for (int i = 0; i < dmac->vif1.qwc; i++) {\n        uint128_t q = dmac_read_qword(dmac, dmac->vif1.madr);\n\n        // VIF1 FIFO address\n        ee_bus_write32(dmac->bus, 0x10005000, q.u32[0]);\n        ee_bus_write32(dmac->bus, 0x10005000, q.u32[1]);\n        ee_bus_write32(dmac->bus, 0x10005000, q.u32[2]);\n        ee_bus_write32(dmac->bus, 0x10005000, q.u32[3]);\n\n        dmac->vif1.madr += 16;\n    }\n\n    dmac->vif1.qwc = 0;\n\n    if (dmac->vif1.tag.end) {\n        sched_schedule(dmac->sched, event);\n\n        return;\n    }\n\n    // Chain mode\n    do {\n        uint128_t tag = dmac_read_qword(dmac, dmac->vif1.tadr);\n\n        dmac_process_source_tag(dmac, &dmac->vif1, tag);\n\n        // fprintf(stdout, \"ee: vif1 tag qwc=%08x madr=%08x tadr=%08x id=%d addr=%08x data=%08x %08x tte=%d mem=%d\\n\",\n        //     dmac->vif1.tag.qwc,\n        //     dmac->vif1.madr,\n        //     dmac->vif1.tadr,\n        //     dmac->vif1.tag.id,\n        //     dmac->vif1.tag.addr,\n        //     dmac->vif1.tag.data & 0xffffffff,\n        //     dmac->vif1.tag.data >> 32,\n        //     (dmac->vif1.chcr >> 6) & 1,\n        //     dmac->vif1.tag.mem\n        // );\n\n        // CHCR.TTE: Transfer tag DATA field\n        if ((dmac->vif1.chcr >> 6) & 1) {\n            ee_bus_write32(dmac->bus, 0x10005000, dmac->vif1.tag.data & 0xffffffff);\n            ee_bus_write32(dmac->bus, 0x10005000, dmac->vif1.tag.data >> 32);\n        }\n\n        for (int i = 0; i < dmac->vif1.qwc; i++) {\n            uint128_t q = dmac_read_qword(dmac, dmac->vif1.madr);\n\n            ee_bus_write32(dmac->bus, 0x10005000, q.u32[0]);\n            ee_bus_write32(dmac->bus, 0x10005000, q.u32[1]);\n            ee_bus_write32(dmac->bus, 0x10005000, q.u32[2]);\n            ee_bus_write32(dmac->bus, 0x10005000, q.u32[3]);\n\n            dmac->vif1.madr += 16;\n        }\n\n        if (dmac->vif1.tag.id == 1) {\n            dmac->vif1.tadr = dmac->vif1.madr;\n        }\n    } while (!channel_is_done(&dmac->vif1));\n\n    sched_schedule(dmac->sched, event);\n}\n\nvoid dmac_send_gif_irq(void* udata, int overshoot) {\n    struct ps2_dmac* dmac = (struct ps2_dmac*)udata;\n\n    dmac_set_irq(dmac, DMAC_GIF);\n\n    dmac->gif.chcr &= ~0x100;\n    dmac->gif.qwc = 0;\n}\n\nvoid dmac_handle_gif_transfer(struct ps2_dmac* dmac) {\n    struct sched_event event;\n\n    assert(((dmac->gif.chcr >> 6) & 1) == 0);\n\n    int mode = (dmac->gif.chcr >> 2) & 3;\n\n    event.name = \"GIF DMA IRQ\";\n    event.udata = dmac;\n    event.callback = dmac_send_gif_irq;\n    event.cycles = 1000;\n\n    sched_schedule(dmac->sched, event);\n\n    // fprintf(stderr, \"dmac: GIF DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x\\n\",\n    //     dmac->gif.chcr & 1,\n    //     (dmac->gif.chcr >> 2) & 3,\n    //     (dmac->gif.chcr >> 6) & 1,\n    //     (dmac->gif.chcr >> 7) & 1,\n    //     dmac->gif.qwc,\n    //     dmac->gif.madr,\n    //     dmac->gif.tadr,\n    //     dmac->rbor,\n    //     dmac->rbsr,\n    //     dmac->spr_from.madr\n    // );\n\n    int mfifo_drain = (dmac->ctrl >> 2) & 3;\n\n    if (mfifo_drain == 3)\n        return;\n\n    // printf(\"ee: GIF DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x\\n\",\n    //     dmac->gif.chcr & 1,\n    //     (dmac->gif.chcr >> 2) & 3,\n    //     (dmac->gif.chcr >> 6) & 1,\n    //     (dmac->gif.chcr >> 7) & 1,\n    //     dmac->gif.qwc,\n    //     dmac->gif.madr,\n    //     dmac->gif.tadr\n    // );\n\n    for (int i = 0; i < dmac->gif.qwc; i++) {\n        uint128_t q = dmac_read_qword(dmac, dmac->gif.madr);\n\n        // fprintf(file, \"ee: Sending %016lx%016lx from %08x to GIF FIFO (burst)\\n\",\n        //     q.u64[1], q.u64[0],\n        //     dmac->gif.madr\n        // );\n\n        // GIF FIFO address\n        ps2_gif_fifo_write(dmac->bus->gif, q, GIF_PATH3);\n\n        dmac->gif.madr += 16;\n    }\n\n    if (dmac->gif.tag.end) {\n        return;\n    }\n\n    // int id = (dmac->gif.chcr >> 28) & 7;\n\n    // if ((mode == 1) && (id == 0 || id == 7) && dmac->gif.qwc) {\n    //     return;\n    // }\n\n    // Chain mode\n    do {\n        uint128_t tag = dmac_read_qword(dmac, dmac->gif.tadr);\n\n        dmac_process_source_tag(dmac, &dmac->gif, tag);\n\n        // printf(\"ee: gif tag qwc=%08x madr=%08x tadr=%08x mem=%d\\n\", dmac->gif.qwc, dmac->gif.madr, dmac->gif.tadr, dmac->gif.tag.mem);\n\n        for (int i = 0; i < dmac->gif.qwc; i++) {\n            uint128_t q = dmac_read_qword(dmac, dmac->gif.madr);\n\n            // fprintf(file, \"ee: Sending %016lx%016lx from %08x to GIF FIFO (chain)\\n\",\n            //     q.u64[1], q.u64[0],\n            //     dmac->gif.madr\n            // );\n\n            ps2_gif_fifo_write(dmac->bus->gif, q, GIF_PATH3);\n\n            dmac->gif.madr += 16;\n        }\n\n        if (dmac->gif.tag.id == 1) {\n            dmac->gif.tadr = dmac->gif.madr;\n        }\n    } while (!channel_is_done(&dmac->gif));\n}\n\nvoid dmac_handle_ipu_from_transfer(struct ps2_dmac* dmac) {\n    if ((dmac->ipu_from.chcr & 0x100) == 0) {\n        // printf(\"dmac: ipu_from channel not started\\n\");\n\n        return;\n    }\n\n    int mode = (dmac->ipu_from.chcr >> 2) & 3;\n\n    // printf(\"dmac: ipu_from start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x dreq=%d\\n\",\n    //     dmac->ipu_from.chcr,\n    //     dmac->ipu_from.chcr & 1,\n    //     (dmac->ipu_from.chcr >> 2) & 3,\n    //     !!(dmac->ipu_from.chcr & 0x40),\n    //     dmac->ipu_from.madr,\n    //     dmac->ipu_from.qwc,\n    //     dmac->ipu_from.tadr,\n    //     dmac->ipu_from.dreq\n    // );\n\n    if (mode != 0) {\n        fprintf(stderr, \"dmac: ipu_from mode %d not supported\\n\", mode);\n\n        exit(1);\n\n        return;\n    }\n\n    while (dmac->ipu_from.dreq && dmac->ipu_from.qwc) {\n        uint128_t q = ee_bus_read128(dmac->bus, 0x10007000);\n\n        dmac_write_qword(dmac, dmac->ipu_from.madr, 0, q);\n\n        dmac->ipu_from.madr += 16;\n        dmac->ipu_from.qwc--;\n    }\n\n    if (dmac->ipu_from.qwc == 0) {\n        dmac_set_irq(dmac, DMAC_IPU_FROM);\n\n        dmac->ipu_from.chcr &= ~0x100;\n        dmac->ipu_from.qwc = 0;\n    }\n}\n\nint dmac_transfer_ipu_to_qword(struct ps2_dmac* dmac) {\n    if ((dmac->ipu_to.chcr & 0x100) == 0) {\n        // printf(\"dmac: ipu_to channel not started\\n\");\n\n        return 0;\n    }\n\n    if (!dmac->ipu_to.dreq) {\n        // printf(\"dmac: ipu_to dreq cleared\\n\");\n\n        return 0;\n    }\n\n    if (dmac->ipu_to.qwc) {\n        uint128_t q = dmac_read_qword(dmac, dmac->ipu_to.madr);\n\n        ee_bus_write128(dmac->bus, 0x10007010, q);\n\n        dmac->ipu_to.madr += 16;\n        dmac->ipu_to.qwc--;\n\n        return 1;\n    }\n\n    if (channel_is_done(&dmac->ipu_to)) {\n        dmac_set_irq(dmac, DMAC_IPU_TO);\n\n        dmac->ipu_to.chcr &= ~0x100;\n        dmac->ipu_to.qwc = 0;\n\n        return 0;\n    }\n\n    if (dmac->ipu_to.tag.id == 1) {\n        dmac->ipu_to.tadr = dmac->ipu_to.madr;\n        fprintf(stderr, \"dmac: ipu_to tag id=1, setting tadr to %08x\\n\", dmac->ipu_to.tadr);\n        exit(1);\n    }\n\n    uint128_t tag = dmac_read_qword(dmac, dmac->ipu_to.tadr);\n\n    dmac_process_source_tag(dmac, &dmac->ipu_to, tag);\n\n    // printf(\"dmac: ipu_to tag tag.qwc=%08lx qwc=%08lx id=%ld irq=%ld addr=%08lx mem=%ld data=%016lx end=%d tte=%d\\n\",\n    //     dmac->ipu_to.tag.qwc,\n    //     dmac->ipu_to.qwc,\n    //     dmac->ipu_to.tag.id,\n    //     dmac->ipu_to.tag.irq,\n    //     dmac->ipu_to.tag.addr,\n    //     dmac->ipu_to.tag.mem,\n    //     dmac->ipu_to.tag.data,\n    //     dmac->ipu_to.tag.end,\n    //     (dmac->ipu_to.chcr >> 7) & 1\n    // );\n\n    return 1;\n}\nvoid dmac_handle_ipu_to_transfer(struct ps2_dmac* dmac) {\n    if ((dmac->ipu_to.chcr & 0x100) == 0) {\n        // printf(\"dmac: ipu_to channel not started\\n\");\n\n        return;\n    }\n\n    // printf(\"dmac: ipu_to start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x\\n\",\n    //     dmac->ipu_to.chcr,\n    //     dmac->ipu_to.chcr & 1,\n    //     (dmac->ipu_to.chcr >> 2) & 3,\n    //     !!(dmac->ipu_to.chcr & 0x40),\n    //     dmac->ipu_to.madr,\n    //     dmac->ipu_to.qwc,\n    //     dmac->ipu_to.tadr\n    // );\n\n    while (dmac_transfer_ipu_to_qword(dmac)) {\n        // Keep transferring until we run out of QWC or DREQ is cleared\n    }\n}\nvoid dmac_handle_sif0_transfer(struct ps2_dmac* dmac) {\n    // SIF FIFO is empty, keep waiting\n    if (ps2_sif0_is_empty(dmac->sif)) {\n        return;\n    }\n\n    // Data ready but channel isn't ready yet, keep waiting\n    if (!(dmac->sif0.chcr & 0x100)) {\n        return;\n    }\n    // fprintf(stdout, \"dmac: sif0 start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x\\n\",\n    //     dmac->sif0.chcr,\n    //     dmac->sif0.chcr & 1,\n    //     (dmac->sif0.chcr >> 2) & 3,\n    //     !!(dmac->sif0.chcr & 0x40),\n    //     dmac->sif0.madr,\n    //     dmac->sif0.qwc,\n    //     dmac->sif0.tadr\n    // );\n\n    while (!ps2_sif0_is_empty(dmac->sif)) {\n        uint128_t tag = ps2_sif0_read(dmac->sif);\n\n        dmac_process_dest_tag(dmac, &dmac->sif0, tag);\n\n        // printf(\"ee: sif0 tag qwc=%08lx madr=%08lx id=%ld irq=%ld addr=%08lx mem=%ld data=%016lx tte=%d\\n\",\n        //     dmac->sif0.tag.qwc,\n        //     dmac->sif0.madr,\n        //     dmac->sif0.tag.id,\n        //     dmac->sif0.tag.irq,\n        //     dmac->sif0.tag.addr,\n        //     dmac->sif0.tag.mem,\n        //     dmac->sif0.tag.data,\n        //     dmac->sif0.chcr\n        // );\n\n        for (int i = 0; i < dmac->sif0.qwc; i++) {\n            if (ps2_sif0_is_empty(dmac->sif)) {\n                printf(\"dmac: qwc != 0 FIFO empty\\n\");\n\n                if (channel_is_done(&dmac->sif0)) {\n                    printf(\"dmac: qwc != 0 FIFO empty\\n\");\n\n                    dmac->sif0.chcr &= ~0x100;\n                    dmac->sif0.qwc = 0;\n        \n                    dmac_set_irq(dmac, DMAC_SIF0);\n        \n                    return;\n                }\n            }\n\n            uint128_t q = ps2_sif0_read(dmac->sif);\n\n            // printf(\"%08x: \", dmac->sif0.madr);\n\n            // for (int i = 0; i < 16; i++) {\n            //     printf(\"%02x \", q.u8[i]);\n            // }\n\n            // putchar('|');\n\n            // for (int i = 0; i < 16; i++) {\n            //     printf(\"%c\", isprint(q.u8[i]) ? q.u8[i] : '.');\n            // }\n\n            // puts(\"|\");\n\n            // printf(\"ee: Writing %016lx %016lx to %08x\\n\", q.u64[1], q.u64[0], dmac->sif0.madr);\n\n            dmac_write_qword(dmac, dmac->sif0.madr, 0, q);\n\n            dmac->sif0.madr += 16;\n        }\n\n        if (channel_is_done(&dmac->sif0)) {\n            dmac->sif0.chcr &= ~0x100;\n            dmac->sif0.qwc = 0;\n\n            dmac_set_irq(dmac, DMAC_SIF0);\n\n            // ps2_sif_fifo_reset(dmac->sif);\n\n            return;\n        }\n    }\n\n    // dmac->sif0.chcr &= ~0x100;\n\n    // dmac_set_irq(dmac, DMAC_SIF0);\n\n    // ps2_sif_fifo_reset(dmac->sif);\n\n    // We shouldn't send an interrupt if tag end or irq/tie weren't\n    // set\n}\nvoid dmac_handle_sif1_transfer(struct ps2_dmac* dmac) {\n    assert(!dmac->sif1.qwc);\n    assert(((dmac->sif1.chcr >> 2) & 3) == 1);\n\n    // This should be ok?\n    // if (!ps2_sif_fifo_is_empty(dmac->sif)) {\n    //     printf(\"dmac: WARNING!!! SIF FIFO not empty\\n\");\n    // }\n\n    do {\n        uint128_t tag = dmac_read_qword(dmac, dmac->sif1.tadr);\n\n        dmac_process_source_tag(dmac, &dmac->sif1, tag);\n\n        // printf(\"ee: sif1 tag qwc=%08lx id=%ld irq=%ld addr=%08lx mem=%ld data=%016lx end=%d tte=%d\\n\",\n        //     dmac->sif1.tag.qwc,\n        //     dmac->sif1.tag.id,\n        //     dmac->sif1.tag.irq,\n        //     dmac->sif1.tag.addr,\n        //     dmac->sif1.tag.mem,\n        //     dmac->sif1.tag.data,\n        //     dmac->sif1.tag.end,\n        //     (dmac->sif1.chcr >> 7) & 1\n        // );\n        // printf(\"ee: SIF1 tag madr=%08x\\n\", dmac->sif1.madr);\n\n        for (int i = 0; i < dmac->sif1.qwc; i++) {\n            uint128_t q = dmac_read_qword(dmac, dmac->sif1.madr);\n\n            // printf(\"%08x: \", dmac->sif1.madr);\n\n            // for (int i = 0; i < 16; i++) {\n            //     printf(\"%02x \", q.u8[i]);\n            // }\n\n            // putchar('|');\n\n            // for (int i = 0; i < 16; i++) {\n            //     printf(\"%c\", isprint(q.u8[i]) ? q.u8[i] : '.');\n            // }\n\n            // puts(\"|\");\n\n            ps2_sif1_write(dmac->sif, q);\n\n            dmac->sif1.madr += 16;\n        }\n\n        if (dmac->sif1.tag.id == 1) {\n            dmac->sif1.tadr = dmac->sif1.madr;\n\n            fprintf(stderr, \"dmac: SIF1 tag id=1, setting TADR to MADR=%08x\\n\", dmac->sif1.madr);\n\n            exit(1);\n        }\n    } while (!channel_is_done(&dmac->sif1));\n\n    iop_dma_handle_sif1_transfer(dmac->iop_dma);\n\n    dmac_set_irq(dmac, DMAC_SIF1);\n\n    dmac->sif1.chcr &= ~0x100;\n    dmac->sif1.qwc = 0;\n}\nvoid dmac_handle_sif2_transfer(struct ps2_dmac* dmac) {\n    fprintf(stderr, \"ee: SIF2 channel unimplemented\\n\"); exit(1);\n}\n\nvoid dmac_spr_from_interleave(struct ps2_dmac* dmac) {\n    uint32_t sqwc = dmac->sqwc & 0xff;\n    uint32_t tqwc = (dmac->sqwc >> 16) & 0xff;\n\n    // Note: When TQWC=0, it is set to QWC instead (undocumented)\n    if (tqwc == 0)\n        tqwc = dmac->spr_from.qwc;\n\n    while (dmac->spr_from.qwc) {\n        for (int i = 0; i < tqwc && dmac->spr_from.qwc; i++) {\n            uint128_t q = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr);\n\n            ee_bus_write128(dmac->bus, dmac->spr_from.madr, q);\n\n            dmac->spr_from.madr += 0x10;\n            dmac->spr_from.sadr += 0x10;\n            dmac->spr_from.sadr &= 0x3ff0;\n            dmac->spr_from.qwc--;\n        }\n\n        dmac->spr_from.madr += sqwc * 16;\n    }\n}\nvoid dmac_handle_spr_from_transfer(struct ps2_dmac* dmac) {\n    dmac_set_irq(dmac, DMAC_SPR_FROM);\n\n    dmac->spr_from.chcr &= ~0x100;\n\n    // fprintf(stdout, \"dmac: spr_from start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x sadr=%08x rbor=%08x rbsr=%08x\\n\",\n    //     dmac->spr_from.chcr,\n    //     dmac->spr_from.chcr & 1,\n    //     (dmac->spr_from.chcr >> 2) & 3,\n    //     !!(dmac->spr_from.chcr & 0x40),\n    //     dmac->spr_from.madr,\n    //     dmac->spr_from.qwc,\n    //     dmac->spr_from.tadr,\n    //     dmac->spr_from.sadr,\n    //     dmac->rbor,\n    //     dmac->rbsr\n    // );\n\n    // exit(1);\n\n    int mode = (dmac->spr_from.chcr >> 2) & 3;\n\n    if (dmac->mfifo_drain) {\n        assert(mode == 0);\n\n        dmac->spr_from.madr = dmac->rbor | (dmac->spr_from.madr & dmac->rbsr);\n\n        for (int i = 0; i < dmac->spr_from.qwc; i++) {\n            uint128_t q = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr & 0x3ff0);\n\n            ee_bus_write128(dmac->bus, dmac->spr_from.madr, q);\n            \n            mfifo_write_qword(dmac, q);\n\n            dmac->spr_from.madr = dmac->rbor | ((dmac->spr_from.madr + 0x10) & dmac->rbsr);\n            dmac->spr_from.sadr += 0x10;\n            dmac->spr_from.sadr &= 0x3ff0;\n        }\n\n        dmac->spr_from.qwc = 0;\n\n        return;\n    }\n\n    if (mode == 2) {\n        dmac_spr_from_interleave(dmac);\n\n        return;\n    }\n\n    for (int i = 0; i < dmac->spr_from.qwc; i++) {\n        uint128_t q = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr & 0x3ff0);\n\n        ee_bus_write128(dmac->bus, dmac->spr_from.madr, q);\n\n        dmac->spr_from.madr += 0x10;\n        dmac->spr_from.sadr += 0x10;\n        dmac->spr_from.sadr &= 0x3ff0;\n    }\n\n    dmac->spr_from.qwc = 0;\n\n    if (dmac->spr_from.tag.end) {\n        return;\n    }\n\n    // Chain mode\n    do {\n        uint128_t tag = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr & 0x3ff0);\n\n        dmac->spr_from.sadr += 0x10;\n        dmac->spr_from.sadr &= 0x3ff0;\n\n        dmac->spr_from.qwc = tag.u32[0] & 0xffff;\n        dmac->spr_from.tag.id = (tag.u32[0] >> 28) & 0x7;\n        dmac->spr_from.tag.irq = tag.u32[0] & 0x80000000;\n        dmac->spr_from.tag.end = dmac->spr_from.tag.id == 0 || dmac->spr_from.tag.id == 7;\n        dmac->spr_from.madr = tag.u32[1];\n\n        // printf(\"ee: spr_from tag qwc=%08lx madr=%08lx sadr=%08x tadr=%08lx id=%ld addr=%08lx mem=%ld data=%016lx irq=%d end=%d tte=%d\\n\",\n        //     dmac->spr_from.tag.qwc,\n        //     dmac->spr_from.madr,\n        //     dmac->spr_from.sadr,\n        //     dmac->spr_from.tadr,\n        //     dmac->spr_from.tag.id,\n        //     dmac->spr_from.tag.addr,\n        //     dmac->spr_from.tag.mem,\n        //     dmac->spr_from.tag.data,\n        //     dmac->spr_from.tag.irq,\n        //     dmac->spr_from.tag.end,\n        //     (dmac->spr_from.chcr >> 7) & 1\n        // );\n\n        for (int i = 0; i < dmac->spr_from.qwc; i++) {\n            uint128_t q = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr & 0x3ff0);\n\n            ee_bus_write128(dmac->bus, dmac->spr_from.madr, q);\n\n            dmac->spr_from.madr += 0x10;\n            dmac->spr_from.sadr += 0x10;\n            dmac->spr_from.sadr &= 0x3ff0;\n        }\n    } while (!channel_is_done(&dmac->spr_from));\n}\n\nvoid dmac_spr_to_interleave(struct ps2_dmac* dmac) {\n    uint32_t sqwc = dmac->sqwc & 0xff;\n    uint32_t tqwc = (dmac->sqwc >> 16) & 0xff;\n\n    // Note: When TQWC=0, it is set to QWC instead (undocumented)\n    if (tqwc == 0)\n        tqwc = dmac->spr_to.qwc;\n\n    while (dmac->spr_to.qwc) {\n        for (int i = 0; i < tqwc && dmac->spr_to.qwc; i++) {\n            uint128_t q = dmac_read_qword(dmac, dmac->spr_to.madr);\n\n            ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, q);\n\n            dmac->spr_to.madr += 0x10;\n            dmac->spr_to.sadr += 0x10;\n            dmac->spr_to.sadr &= 0x3ff0;\n            dmac->spr_to.qwc--;\n        }\n\n        dmac->spr_to.madr += sqwc * 16;\n    }\n}\nvoid dmac_handle_spr_to_transfer(struct ps2_dmac* dmac) {\n    dmac_set_irq(dmac, DMAC_SPR_TO);\n\n    dmac->spr_to.chcr &= ~0x100;\n\n    int mode = (dmac->spr_to.chcr >> 2) & 3;\n\n    // printf(\"ee: spr_to start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x sadr=%08x\\n\",\n    //     dmac->spr_to.chcr,\n    //     dmac->spr_to.chcr & 1,\n    //     (dmac->spr_to.chcr >> 2) & 3,\n    //     !!(dmac->spr_to.chcr & 0x40),\n    //     dmac->spr_to.madr,\n    //     dmac->spr_to.qwc,\n    //     dmac->spr_to.tadr,\n    //     dmac->spr_to.sadr\n    // );\n\n    if (mode == 2) {\n        dmac_spr_to_interleave(dmac);\n\n        return;\n    }\n\n    for (int i = 0; i < dmac->spr_to.qwc; i++) {\n        uint128_t q = dmac_read_qword(dmac, dmac->spr_to.madr);\n\n        ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, q);\n\n        dmac->spr_to.madr += 0x10;\n        dmac->spr_to.sadr += 0x10;\n        dmac->spr_to.sadr &= 0x3ff0;\n    }\n\n    dmac->spr_to.qwc = 0;\n\n    // We're done\n    if (dmac->spr_to.tag.end)\n        return;\n\n    // Chain mode\n    do {\n        uint128_t tag = dmac_read_qword(dmac, dmac->spr_to.tadr);\n\n        if ((dmac->spr_to.chcr >> 6) & 1) {\n            ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, tag);\n\n            dmac->spr_to.sadr += 0x10;\n        }\n\n        dmac_process_source_tag(dmac, &dmac->spr_to, tag);\n        \n        // printf(\"ee: spr_to tag qwc=%08lx madr=%08lx tadr=%08lx id=%ld addr=%08lx mem=%ld end=%d data=%08x%08x\\n\",\n        //     dmac->spr_to.tag.qwc,\n        //     dmac->spr_to.madr,\n        //     dmac->spr_to.tadr,\n        //     dmac->spr_to.tag.id,\n        //     dmac->spr_to.tag.addr,\n        //     dmac->spr_to.tag.mem,\n        //     dmac->spr_to.tag.end,\n        //     tag.u32[1], tag.u32[0]\n        // );\n\n        for (int i = 0; i < dmac->spr_to.qwc; i++) {\n            uint128_t q = dmac_read_qword(dmac, dmac->spr_to.madr);\n\n            ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, q);\n\n            dmac->spr_to.madr += 0x10;\n            dmac->spr_to.sadr += 0x10;\n            dmac->spr_to.sadr &= 0x3ff0;\n        }\n\n        if (dmac->spr_to.tag.id == 1) {\n            dmac->spr_to.tadr = dmac->spr_to.madr;\n        }\n    } while (!channel_is_done(&dmac->spr_to));\n}\nstatic inline void dmac_handle_channel_start(struct ps2_dmac* dmac, uint32_t addr) {\n    struct dmac_channel* c = dmac_get_channel(dmac, addr);\n\n    // if (c == &dmac->ipu_to || c == &dmac->ipu_from)\n    // printf(\"dmac: %s start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x rbsr=%08x rbor=%08x\\n\",\n    //     dmac_get_channel_name(dmac, addr),\n    //     c->chcr,\n    //     c->chcr & 1,\n    //     (c->chcr >> 2) & 3,\n    //     !!(c->chcr & 0x40),\n    //     c->madr,\n    //     c->qwc,\n    //     c->tadr,\n    //     dmac->rbsr,\n    //     dmac->rbor\n    // );\n\n    // if (c == &dmac->ipu_to && c->qwc != 0) {\n    //     int mode = (c->chcr >> 2) & 3;\n\n    //     if (mode == 1) {\n    //         uint128_t tag;\n\n    //         tag.u32[0] = (c->chcr & 0xffff0000) | (c->qwc & 0xffff);\n\n    //         dmac_process_source_tag(dmac, c, tag);\n    //     } else {\n    //         c->tag.end = 1;\n    //     }\n    // }\n\n    int mode = (c->chcr >> 2) & 3;\n\n    // Modes 1 and 3 are chain modes\n    if ((mode & 1) == 0) {\n        c->tag.end = 1;\n    } else if (c->qwc != 0) {\n        int id = c->chcr >> 28 & 7;\n        int tie = (c->chcr >> 7) & 1;\n        int irq = c->chcr & 0x80000000;\n\n        c->tag.end = (id == 0 || id == 7) || (tie && irq);\n\n        // printf(\"dmac: %s qwc != 0, madr=%08x tadr=%08x qwc=%08x tag=%08x end=%d\\n\", dmac_get_channel_name(dmac, addr),\n        //     c->madr, c->tadr, c->qwc, c->chcr >> 16, c->tag.end\n        // );\n    } else {\n        c->tag.end = 0;\n    }\n\n    switch (addr & 0xff00) {\n        case 0x8000: dmac_handle_vif0_transfer(dmac); return;\n        case 0x9000: dmac_handle_vif1_transfer(dmac); return;\n        case 0xA000: dmac_handle_gif_transfer(dmac); return;\n        case 0xB000: dmac_handle_ipu_from_transfer(dmac); return;\n        case 0xB400: dmac_handle_ipu_to_transfer(dmac); return;\n        case 0xC000: dmac_handle_sif0_transfer(dmac); return;\n        case 0xC400: dmac_handle_sif1_transfer(dmac); return;\n        case 0xC800: dmac_handle_sif2_transfer(dmac); return;\n        case 0xD000: dmac_handle_spr_from_transfer(dmac); return;\n        case 0xD400: dmac_handle_spr_to_transfer(dmac); return;\n    }\n}\n\nvoid dmac_write_stat(struct ps2_dmac* dmac, uint32_t data) {\n    uint32_t istat = data & 0x0000ffff;\n    uint32_t imask = data & 0xffff0000;\n\n    dmac->stat &= ~istat;\n    dmac->stat ^= imask;\n\n    // printf(\"dmac: stat=%08x istat=%08x imask=%08x\\n\", dmac->stat, istat, imask);\n\n    dmac_test_irq(dmac);\n}\n\nvoid ps2_dmac_write32(struct ps2_dmac* dmac, uint32_t addr, uint64_t data) {\n    struct dmac_channel* c = dmac_get_channel(dmac, addr);\n\n    switch (addr) {\n        case 0x1000E000: {\n            dmac->ctrl = data;\n\n            int mfifo_drain = (dmac->ctrl >> 2) & 3;\n            int stall_ctrl = (dmac->ctrl >> 4) & 3;\n            int stall_drain = (dmac->ctrl >> 6) & 3;\n\n            if (mfifo_drain || stall_ctrl || stall_drain) {\n                // fprintf(stdout, \"dmac: 32-bit mfifo_drain=%d stall_ctrl=%d stall_drain=%d\\n\",\n                //     mfifo_drain, stall_ctrl, stall_drain\n                // );\n\n                switch (mfifo_drain) {\n                    case 0: dmac->mfifo_drain = NULL; break;\n                    case 2: dmac->mfifo_drain = &dmac->vif1; break;\n                    case 3: dmac->mfifo_drain = &dmac->gif; break;\n                    default: fprintf(stderr, \"dmac: Invalid MFIFO drain channel %d\\n\", mfifo_drain); exit(1);\n                }\n            }\n        } return;\n        case 0x1000E010: dmac_write_stat(dmac, data); return;\n        case 0x1000E020: dmac->pcr = data; dmac_test_cpcond0(dmac); return;\n        case 0x1000E030: dmac->sqwc = data; return;\n        case 0x1000E040: dmac->rbsr = data; return;\n        case 0x1000E050: dmac->rbor = data; return;\n        case 0x1000F520: return; // ENABLER (R)\n        case 0x1000F590: dmac->enable = data; return;\n    }\n\n    if (!c)\n        return;\n\n    switch (addr & 0xff) {\n        case 0x00: {\n            // Behavior required for IPU FMVs to work\n            if ((c->chcr & 0x100) == 0) {\n                c->chcr = data;\n\n                if (data & 0x100) {\n                    dmac_handle_channel_start(dmac, addr);\n                }\n            } else {\n                // printf(\"dmac: channel %s value=%08x chcr=%08x\\n\", dmac_get_channel_name(dmac, addr), data, c->chcr);\n                c->chcr &= (data & 0x100) | 0xfffffeff;\n            }\n        } return;\n        case 0x10: {\n            c->madr = data;\n\n            // Clear MADR's MSB on SPR channels\n            if (c == &dmac->spr_to || c == &dmac->spr_from) {\n                c->madr &= 0x7fffffff;\n            }\n        } return;\n        case 0x20: c->qwc = data & 0xffff; return;\n        case 0x30: c->tadr = data; return;\n        case 0x40: c->asr0 = data; return;\n        case 0x50: c->asr1 = data; return;\n        case 0x80: c->sadr = data & 0x3ff0; return;\n    }\n\n    // printf(\"dmac: Unknown channel register %02x\\n\", addr & 0xff);\n\n    return;\n}\n\nuint64_t ps2_dmac_read8(struct ps2_dmac* dmac, uint32_t addr) {\n    if (addr == 0x10009000) {\n        // printf(\"dmac: 8-bit read from chcr (%08x)\\n\", dmac->vif1.chcr & 0xff);\n\n        return dmac->vif1.chcr & 0xff;\n    }\n\n    int shift = (addr & 0x3) * 8;\n\n    return (ps2_dmac_read32(dmac, addr & ~0x3) >> shift) & 0xff;\n\n    // struct dmac_channel* c = dmac_get_channel(dmac, addr & ~3);\n\n    // if (!c) {\n    //     switch (addr) {\n    //         case 0x1000e000: {\n    //             return dmac->ctrl & 0xff;\n    //         } break;\n    //     }\n\n    //     printf(\"dmac: Unknown channel read8 at %08x\\n\", addr);\n\n    //     return 0;\n    // }\n\n    // switch (addr) {\n    //     case 0x10009000:\n    //     case 0x1000a000:\n    //     case 0x10008000: {\n    //         return c->chcr & 0xff;\n    //     }\n\n    //     case 0x10008001:\n    //     case 0x10009001:\n    //     case 0x1000a001: {\n    //         return (c->chcr >> 8) & 0xff;\n    //     }\n    // }\n\n    // printf(\"dmac: Unhandled 8-bit read from %08x\\n\", addr);\n\n    // exit(1);\n\n    // return 0;\n}\n\nvoid ps2_dmac_write8(struct ps2_dmac* dmac, uint32_t addr, uint64_t data) {\n    struct dmac_channel* c = dmac_get_channel(dmac, addr & ~3);\n\n    switch (addr) {\n        case 0x10008000:\n        case 0x10009000:\n        case 0x1000a000:\n        case 0x1000b000:\n        case 0x1000b400:\n        case 0x1000d400: {\n            c->chcr &= 0xffffff00;\n            c->chcr |= data & 0xff;\n\n            return;\n        } break;\n\n        case 0x10008001:\n        case 0x10009001: {\n            ps2_dmac_write32(dmac, addr & ~0x3, (ps2_dmac_read32(dmac, addr & ~0x3) & 0xffff00ff) | ((data & 0xff) << 8));\n            // c->chcr &= 0xffff00ff;\n            // c->chcr |= (data & 0xff) << 8;\n\n            // if (c->chcr & 0x100) {\n            //     dmac_handle_channel_start(dmac, addr);\n            // }\n\n            return;\n        } break;\n\n        case 0x1000e000: {\n            dmac->ctrl &= 0xffffff00;\n            dmac->ctrl |= data;\n\n            int mfifo_drain = (dmac->ctrl >> 2) & 3;\n            int stall_ctrl = (dmac->ctrl >> 4) & 3;\n            int stall_drain = (dmac->ctrl >> 6) & 3;\n\n            if (mfifo_drain || stall_ctrl || stall_drain) {\n                // fprintf(stdout, \"dmac: 8-bit mfifo_drain=%d stall_ctrl=%d stall_drain=%d\\n\",\n                //     mfifo_drain, stall_ctrl, stall_drain\n                // );\n\n                switch (mfifo_drain) {\n                    case 0: dmac->mfifo_drain = NULL; break;\n                    case 2: dmac->mfifo_drain = &dmac->vif1; break;\n                    case 3: dmac->mfifo_drain = &dmac->gif; break;\n                    default: fprintf(stderr, \"dmac: Invalid MFIFO drain channel %d\\n\", mfifo_drain); exit(1);\n                }\n            }\n        } return;\n\n        // ENABLEW (byte 2)\n        case 0x1000f592: {\n            dmac->enable &= 0xff00ffff;\n            dmac->enable |= (data & 0xff) << 16;\n        } return;\n    }\n\n    fprintf(stderr, \"dmac: 8-bit write to %08x (%02x)\\n\", addr, data);\n\n    // exit(1);\n\n    return;\n}\n\nuint64_t ps2_dmac_read16(struct ps2_dmac* dmac, uint32_t addr) {\n    int shift = (addr & 2) * 16;\n    addr = addr & ~3;\n\n    return (ps2_dmac_read32(dmac, addr) >> shift) & 0xffff;\n}\n\nvoid ps2_dmac_write16(struct ps2_dmac* dmac, uint32_t addr, uint64_t data) {\n    struct dmac_channel* c = dmac_get_channel(dmac, addr & ~3);\n\n    switch (addr) {\n        case 0x10008000:\n        case 0x1000a000:\n        case 0x1000d000:\n        case 0x1000d400:\n        case 0x1000d800:\n        case 0x10009000: {\n            if ((c->chcr & 0x100) == 0) {\n                c->chcr &= 0xffff0000;\n                c->chcr |= data & 0xffff;\n\n                if (data & 0x100) {\n                    dmac_handle_channel_start(dmac, addr);\n                }\n            } else {\n                // printf(\"dmac: channel %s value=%08x chcr=%08x\\n\", dmac_get_channel_name(dmac, addr), data, c->chcr);\n                c->chcr &= (data & 0x100) | 0xfffffeff;\n            }\n        } return;\n    }\n\n    fprintf(stderr, \"dmac: 16-bit write to %08x (%04x)\\n\", addr, data & 0xffff);\n\n    exit(1);\n}"
  },
  {
    "path": "src/ee/dmac.h",
    "content": "struct ps2_dmac;\n\n#ifndef DMAC_H\n#define DMAC_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"u128.h\"\n\n#include \"shared/sif.h\"\n\n#include \"bus_decl.h\"\n#include \"ee.h\"\n\n#include \"iop/dma.h\"\n\n#define TAG_QWC(d) (d.u64[0] & 0xffff)\n#define TAG_PCT(d) ((d.u64[0] >> 26) & 3)\n#define TAG_ID(d) ((d.u64[0] >> 28) & 7)\n#define TAG_IRQ(d) ((d.u64[0] >> 31) & 1)\n#define TAG_ADDR(d) ((d.u64[0] >> 32) & 0xfffffff0)\n#define TAG_DATA(d) (d.u64[1])\n\n#define DMAC_VIF0 0\n#define DMAC_VIF1 1\n#define DMAC_GIF 2\n#define DMAC_IPU_FROM 3\n#define DMAC_IPU_TO 4\n#define DMAC_SIF0 5\n#define DMAC_SIF1 6\n#define DMAC_SIF2 7\n#define DMAC_SPR_FROM 8\n#define DMAC_SPR_TO 9\n#define DMAC_MEIS 14\n\nstruct dmac_tag {\n    uint64_t qwc;\n    uint64_t pct;\n    uint64_t id;\n    uint64_t irq;\n    uint64_t addr;\n    uint64_t data;\n    int end;\n};\n\nstruct dmac_channel {\n    uint32_t chcr;\n    uint32_t madr;\n    uint32_t tadr;\n    uint32_t qwc;\n    uint32_t asr0;\n    uint32_t asr1;\n    uint32_t sadr;\n\n    int dreq;\n\n    struct dmac_tag tag;\n};\n\nstruct ps2_dmac {\n    struct ee_bus* bus;\n\n    struct dmac_channel vif0;\n    struct dmac_channel vif1;\n    struct dmac_channel gif;\n    struct dmac_channel ipu_from;\n    struct dmac_channel ipu_to;\n    struct dmac_channel sif0;\n    struct dmac_channel sif1;\n    struct dmac_channel sif2;\n    struct dmac_channel spr_from;\n    struct dmac_channel spr_to;\n    struct dmac_channel* mfifo_drain;\n\n    uint32_t ctrl;\n    uint32_t stat;\n    uint32_t pcr;\n    uint32_t sqwc;\n    uint32_t rbsr;\n    uint32_t rbor;\n    uint32_t enable;\n\n    struct ps2_ram* spr;\n    struct ps2_sif* sif;\n    struct ps2_iop_dma* iop_dma;\n    struct ee_state* ee;\n    struct sched_state* sched;\n};\n\nstruct ps2_dmac* ps2_dmac_create(void);\nvoid ps2_dmac_init(struct ps2_dmac* dmac, struct ps2_sif* sif, struct ps2_iop_dma* iop_dma, struct ps2_ram* spr, struct ee_state* ee, struct sched_state* sched, struct ee_bus* bus);\nvoid ps2_dmac_destroy(struct ps2_dmac* dmac);\nuint64_t ps2_dmac_read8(struct ps2_dmac* dmac, uint32_t addr);\nuint64_t ps2_dmac_read16(struct ps2_dmac* dmac, uint32_t addr);\nuint64_t ps2_dmac_read32(struct ps2_dmac* dmac, uint32_t addr);\nvoid ps2_dmac_write8(struct ps2_dmac* dmac, uint32_t addr, uint64_t data);\nvoid ps2_dmac_write16(struct ps2_dmac* dmac, uint32_t addr, uint64_t data);\nvoid ps2_dmac_write32(struct ps2_dmac* dmac, uint32_t addr, uint64_t data);\n\nvoid dmac_handle_vif0_transfer(struct ps2_dmac* dmac);\nvoid dmac_handle_vif1_transfer(struct ps2_dmac* dmac);\nvoid dmac_handle_gif_transfer(struct ps2_dmac* dmac);\nvoid dmac_handle_ipu_from_transfer(struct ps2_dmac* dmac);\nvoid dmac_handle_ipu_to_transfer(struct ps2_dmac* dmac);\nvoid dmac_handle_sif0_transfer(struct ps2_dmac* dmac);\nvoid dmac_handle_sif1_transfer(struct ps2_dmac* dmac);\nvoid dmac_handle_sif2_transfer(struct ps2_dmac* dmac);\nvoid dmac_handle_spr_from_transfer(struct ps2_dmac* dmac);\nvoid dmac_handle_spr_to_transfer(struct ps2_dmac* dmac);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/ee.h",
    "content": "#ifndef EE_H\n#define EE_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"shared/ram.h\"\n\n#include \"u128.h\"\n\n#include \"vu.h\"\n\nstruct ee_bus_s {\n    void* udata;\n    uint64_t (*read8)(void* udata, uint32_t addr);\n    uint64_t (*read16)(void* udata, uint32_t addr);\n    uint64_t (*read32)(void* udata, uint32_t addr);\n    uint64_t (*read64)(void* udata, uint32_t addr);\n    uint128_t (*read128)(void* udata, uint32_t addr);\n    void (*write8)(void* udata, uint32_t addr, uint64_t data);\n    void (*write16)(void* udata, uint32_t addr, uint64_t data);\n    void (*write32)(void* udata, uint32_t addr, uint64_t data);\n    void (*write64)(void* udata, uint32_t addr, uint64_t data);\n    void (*write128)(void* udata, uint32_t addr, uint128_t data);\n};\n\n#define EE_SR_CU  0xf0000000\n#define EE_SR_DEV 0x08000000\n#define EE_SR_BEV 0x04000000\n#define EE_SR_CH  0x00040000\n#define EE_SR_EDI 0x00020000\n#define EE_SR_EIE 0x00010000\n#define EE_SR_IM7 0x00008000\n#define EE_SR_BEM 0x00001000\n#define EE_SR_IM3 0x00000800\n#define EE_SR_IM2 0x00000400\n#define EE_SR_KSU 0x00000018\n#define EE_SR_ERL 0x00000004\n#define EE_SR_EXL 0x00000002\n#define EE_SR_IE  0x00000001\n\n#define EE_CAUSE_BD   0x80000000\n#define EE_CAUSE_BD2  0x40000000\n#define EE_CAUSE_CE   0x30000000\n#define EE_CAUSE_EXC2 0x00070000\n#define EE_CAUSE_IP7  0x00008000\n#define EE_CAUSE_IP3  0x00000800\n#define EE_CAUSE_IP2  0x00000400\n#define EE_CAUSE_EXC  0x0000007c\n\n#define CAUSE_EXC1_INT  (0 << 2)\n#define CAUSE_EXC1_MOD  (1 << 2)\n#define CAUSE_EXC1_TLBL (2 << 2)\n#define CAUSE_EXC1_TLBS (3 << 2)\n#define CAUSE_EXC1_ADEL (4 << 2)\n#define CAUSE_EXC1_ADES (5 << 2)\n#define CAUSE_EXC1_IBE  (6 << 2)\n#define CAUSE_EXC1_DBE  (7 << 2)\n#define CAUSE_EXC1_SYS  (8 << 2)\n#define CAUSE_EXC1_BP   (9 << 2)\n#define CAUSE_EXC1_RI   (10 << 2)\n#define CAUSE_EXC1_CPU  (11 << 2)\n#define CAUSE_EXC1_OV   (12 << 2)\n#define CAUSE_EXC1_TR   (13 << 2)\n#define CAUSE_EXC1_TLBIL (2 << 0)\n#define CAUSE_EXC1_TLBIS (3 << 0)\n\n#define CAUSE_EXC2_RES   (0 << 16)\n#define CAUSE_EXC2_NMI   (1 << 16)\n#define CAUSE_EXC2_PERFC (2 << 16)\n#define CAUSE_EXC2_DBG   (3 << 16)\n\n#define EE_VEC_RESET   0xbfc00000\n#define EE_VEC_TLB     0x00000000\n#define EE_VEC_COUNTER 0x00000080\n#define EE_VEC_DEBUG   0x00000100\n#define EE_VEC_COMMON  0x00000180\n#define EE_VEC_IRQ     0x00000200\n\n#define FPU_FLG_C 0x00800000\n#define FPU_FLG_I 0x00020000\n#define FPU_FLG_D 0x00010000\n#define FPU_FLG_O 0x00008000\n#define FPU_FLG_U 0x00004000\n#define FPU_FLG_SI 0x00000040\n#define FPU_FLG_SD 0x00000020\n#define FPU_FLG_SO 0x00000010\n#define FPU_FLG_SU 0x00000008\n\n/*\n    1       V0 - Even page valid. When not set, the memory referenced in this entry is not mapped.\n    2       D0 - Even page dirty. When not set, writes cause an exception.\n    3-5     C0 - Even page cache mode.\n            2=Uncached\n            3=Cached\n            7=Uncached accelerated\n    6-25    PFN0 - Even page frame number.\n    33      V1 - Odd page valid.\n    34      D1 - Odd page dirty.\n    35      C1 - Odd page cache mode.\n    38-57   PFN1 - Odd page frame number.\n    63      S - Scratchpad. When set, the virtual mapping goes to scratchpad instead of main memory.\n    64-71   ASID - Address Space ID.\n    76      G - Global. When set, ASID is ignored.\n    77-95   VPN2 - Virtual page number / 2.\n            Even pages have a VPN of (VPN2 * 2) and odd pages have a VPN of (VPN2 * 2) + 1\n    109-120 MASK - Size of an even/odd page.\n*/\n\nstruct ee_vtlb_entry {\n    int v0;\n    int d0;\n    int c0;\n    uint32_t pfn0;\n    int v1;\n    int d1;\n    int c1;\n    uint32_t pfn1;\n    int s;\n    int asid;\n    int g;\n    uint32_t vpn2;\n    uint32_t mask;\n};\n\n// Taken from PCSX2\nstruct ee_osd_config {\n    /** 0=enabled, 1=disabled */\n    uint32_t spdif_mode : 1; /*00*/\n    /** 0=4:3, 1=fullscreen, 2=16:9 */\n    uint32_t screen_type : 2; /*01*/\n    /** 0=rgb(scart), 1=component */\n    uint32_t video_output : 1; /*03*/\n    /** 0=japanese, 1=english(non-japanese) */\n    /*04*/uint32_t jap_language : 1;\n    /** Playstation driver settings. */\n    uint32_t ps1drv_config : 8; /*05*/\n    /** 0 = early Japanese OSD, 1 = OSD2, 2 = OSD2 with extended languages.\n     * Early kernels cannot retain the value set in this field (Hence always 0). */\n    uint32_t version : 3; /*13*/\n    /** LANGUAGE_??? value */\n    uint32_t language : 5; /*16*/\n    /** timezone minutes offset from gmt */\n    uint32_t timezone_offset : 11; /*21*/\n};\n\nunion ee_fpu_reg {\n    float f;\n    uint32_t u32;\n    int32_t s32;\n};\n\nstruct ee_state;\n\nstruct ee_state* ee_create(void);\nvoid ee_init(struct ee_state* ee, struct vu_state* vu0, struct vu_state* vu1, int ram_size, struct ee_bus_s bus);\nvoid ee_reset(struct ee_state* ee);\nvoid ee_destroy(struct ee_state* ee);\nvoid ee_set_int0(struct ee_state* ee, int v);\nvoid ee_set_int1(struct ee_state* ee, int v);\nvoid ee_set_cpcond0(struct ee_state* ee, int v);\nuint32_t ee_get_pc(struct ee_state* ee);\nstruct ps2_ram* ee_get_spr(struct ee_state* ee);\nint ee_run_block(struct ee_state* ee, int cycles);\nint ee_step(struct ee_state* ee);\nvoid ee_set_fmv_skip(struct ee_state* ee, int v);\nvoid ee_reset_intc_reads(struct ee_state* ee);\nvoid ee_reset_csr_reads(struct ee_state* ee);\nvoid ee_flush_cache(struct ee_state* ee);\nvoid ee_set_ram_size(struct ee_state* ee, int ram_size);\nvoid ee_set_osd_config(struct ee_state* ee, struct ee_osd_config config);\nstruct ee_osd_config ee_get_osd_config(struct ee_state* ee);\n\n#undef EE_ALIGNED16\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/ee_cached.cpp",
    "content": "#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <signal.h>\n#include <assert.h>\n#include <math.h>\n#include <fenv.h>\n\n#ifdef _EE_USE_INTRINSICS\n#include <immintrin.h>\n#include <tmmintrin.h>\n#include <emmintrin.h>\n#include <smmintrin.h>\n#endif\n\n#include \"ee.h\"\n#include \"vu.h\"\n#include \"ee_dis.h\"\n#include \"ee_def.hpp\"\n\n#define max(a, b) ((a) > (b) ? (a) : (b))\n#define min(a, b) ((a) < (b) ? (a) : (b))\n\n#if defined(__has_builtin)\n#if __has_builtin(__builtin_saddll_overflow) && __has_builtin(__builtin_saddll_overflow)\n#define SADDOVF64 __builtin_saddll_overflow\n#define SSUBOVF64 __builtin_ssubll_overflow\n#else\n#define SADDOVF64 __builtin_saddl_overflow\n#define SSUBOVF64 __builtin_ssubl_overflow\n#endif\n#else\n#define SADDOVF64 __builtin_saddll_overflow\n#define SSUBOVF64 __builtin_ssubll_overflow\n#endif\n\n#define VU_D_FLD (0x01e00000)\n#define VU_D_X (0x01000000)\n#define VU_D_Y (0x00800000)\n#define VU_D_Z (0x00400000)\n#define VU_D_W (0x00200000)\n\n// file = fopen(\"vu.dump\", \"a\"); fprintf(file, #ins \"\\n\"); fclose(file);\n#define VU_LOWER(ins) { ps2_vu_decode_lower(ee->vu0, i.opcode); vu_i_ ## ins(ee->vu0, &ee->vu0->lower); }\n#define VU_UPPER(ins) { ps2_vu_decode_upper(ee->vu0, i.opcode); vu_i_ ## ins(ee->vu0, &ee->vu0->upper); }\n#define VU_LOWER_TEMPLATE(ins) { \\\n    ps2_vu_decode_lower(ee->vu0, i.opcode); \\\n    switch ((i.opcode >> 21) & 0xf) { \\\n        case 0: vu_i_ ## ins <0>(ee->vu0, &ee->vu0->lower); break; \\\n        case 1: vu_i_ ## ins <VU_D_W>(ee->vu0, &ee->vu0->lower); break; \\\n        case 2: vu_i_ ## ins <VU_D_Z>(ee->vu0, &ee->vu0->lower); break; \\\n        case 3: vu_i_ ## ins <VU_D_Z | VU_D_W>(ee->vu0, &ee->vu0->lower); break; \\\n        case 4: vu_i_ ## ins <VU_D_Y>(ee->vu0, &ee->vu0->lower); break; \\\n        case 5: vu_i_ ## ins <VU_D_Y | VU_D_W>(ee->vu0, &ee->vu0->lower); break; \\\n        case 6: vu_i_ ## ins <VU_D_Y | VU_D_Z>(ee->vu0, &ee->vu0->lower); break; \\\n        case 7: vu_i_ ## ins <VU_D_Y | VU_D_Z | VU_D_W>(ee->vu0, &ee->vu0->lower); break; \\\n        case 8: vu_i_ ## ins <VU_D_X>(ee->vu0, &ee->vu0->lower); break; \\\n        case 9: vu_i_ ## ins <VU_D_X | VU_D_W>(ee->vu0, &ee->vu0->lower); break; \\\n        case 10: vu_i_ ## ins <VU_D_X | VU_D_Z>(ee->vu0, &ee->vu0->lower); break; \\\n        case 11: vu_i_ ## ins <VU_D_X | VU_D_Z | VU_D_W>(ee->vu0, &ee->vu0->lower); break; \\\n        case 12: vu_i_ ## ins <VU_D_X | VU_D_Y>(ee->vu0, &ee->vu0->lower); break; \\\n        case 13: vu_i_ ## ins <VU_D_X | VU_D_Y | VU_D_W>(ee->vu0, &ee->vu0->lower); break; \\\n        case 14: vu_i_ ## ins <VU_D_X | VU_D_Y | VU_D_Z>(ee->vu0, &ee->vu0->lower); break; \\\n        case 15: vu_i_ ## ins <VU_D_X | VU_D_Y | VU_D_Z | VU_D_W>(ee->vu0, &ee->vu0->lower); break; \\\n        default: __builtin_unreachable(); \\\n    } }\n#define VU_UPPER_TEMPLATE(ins) { \\\n    ps2_vu_decode_upper(ee->vu0, i.opcode); \\\n    switch ((i.opcode >> 21) & 0xf) { \\\n        case 0: vu_i_ ## ins <0>(ee->vu0, &ee->vu0->upper); break; \\\n        case 1: vu_i_ ## ins <VU_D_W>(ee->vu0, &ee->vu0->upper); break; \\\n        case 2: vu_i_ ## ins <VU_D_Z>(ee->vu0, &ee->vu0->upper); break; \\\n        case 3: vu_i_ ## ins <VU_D_Z | VU_D_W>(ee->vu0, &ee->vu0->upper); break; \\\n        case 4: vu_i_ ## ins <VU_D_Y>(ee->vu0, &ee->vu0->upper); break; \\\n        case 5: vu_i_ ## ins <VU_D_Y | VU_D_W>(ee->vu0, &ee->vu0->upper); break; \\\n        case 6: vu_i_ ## ins <VU_D_Y | VU_D_Z>(ee->vu0, &ee->vu0->upper); break; \\\n        case 7: vu_i_ ## ins <VU_D_Y | VU_D_Z | VU_D_W>(ee->vu0, &ee->vu0->upper); break; \\\n        case 8: vu_i_ ## ins <VU_D_X>(ee->vu0, &ee->vu0->upper); break; \\\n        case 9: vu_i_ ## ins <VU_D_X | VU_D_W>(ee->vu0, &ee->vu0->upper); break; \\\n        case 10: vu_i_ ## ins <VU_D_X | VU_D_Z>(ee->vu0, &ee->vu0->upper); break; \\\n        case 11: vu_i_ ## ins <VU_D_X | VU_D_Z | VU_D_W>(ee->vu0, &ee->vu0->upper); break; \\\n        case 12: vu_i_ ## ins <VU_D_X | VU_D_Y>(ee->vu0, &ee->vu0->upper); break; \\\n        case 13: vu_i_ ## ins <VU_D_X | VU_D_Y | VU_D_W>(ee->vu0, &ee->vu0->upper); break; \\\n        case 14: vu_i_ ## ins <VU_D_X | VU_D_Y | VU_D_Z>(ee->vu0, &ee->vu0->upper); break; \\\n        case 15: vu_i_ ## ins <VU_D_X | VU_D_Y | VU_D_Z | VU_D_W>(ee->vu0, &ee->vu0->upper); break; \\\n        default: __builtin_unreachable(); \\\n    } }\n\nstatic inline int fast_abs32(int a) {\n    uint32_t m = a >> 31;\n\n    return (a ^ m) + (m & 1);\n}\n\nstatic inline int16_t fast_abs16(int16_t a) {\n    uint16_t m = a >> 15;\n\n    return (a ^ m) + (m & 1);\n}\n\nstatic inline int16_t saturate16(int32_t word) {\n    if (word > (int32_t)0x00007FFF) {\n        return 0x7FFF;\n    } else if (word < (int32_t)0xFFFF80000) {\n        return 0x8000;\n    } else {\n        return (int16_t)word;\n    }\n}\n\nstatic inline int32_t saturate32(int64_t word) {\n    if (word > (int32_t)0x7FFFFFFF) {\n        return 0x7FFFFFFF;\n    } else if (word < (int32_t)0x80000000) {\n        return 0x80000000;\n    } else {\n        return (int32_t)word;\n    }\n}\n\n#ifdef _EE_USE_INTRINSICS\nstatic inline __m128i _mm_adds_epi32(__m128i a, __m128i b) {\n    const __m128i m = _mm_set1_epi32(0x7fffffff);\n    __m128i r = _mm_add_epi32(a, b);\n    __m128i sb = _mm_srli_epi32(a, 31);\n    __m128i sat = _mm_add_epi32(m, sb);\n    __m128i sx = _mm_xor_si128(a, b);\n    __m128i o = _mm_andnot_si128(sx, _mm_xor_si128(a, r));\n\n    // To-do: Use SSE3 version when SSE4.1 isn't available\n    return _mm_castps_si128(_mm_blendv_ps(_mm_castsi128_ps(r),\n                                          _mm_castsi128_ps(sat),\n                                          _mm_castsi128_ps(o)));\n}\n\nstatic inline __m128i _mm_adds_epu32(__m128i a, __m128i b) {\n    const __m128i m = _mm_set1_epi32(0xffffffff);\n\n    __m128i x = _mm_xor_si128(a, m);\n    __m128i c = _mm_min_epu32(b, x);\n\n    return _mm_add_epi32(a, c);\n}\n#endif\n\nstatic inline uint32_t unpack_5551_8888(uint32_t v) {\n    return ((v & 0x001f) << 3) |\n           ((v & 0x03e0) << 6) |\n           ((v & 0x7c00) << 9) |\n           ((v & 0x8000) << 16);\n}\n\n#define EE_KUSEG 0\n#define EE_KSEG0 1\n#define EE_KSEG1 2\n#define EE_KSSEG 3\n#define EE_KSEG3 4\n\n/*\n    i.rs = (opcode >> 21) & 0x1f;\n    i.rt = (opcode >> 16) & 0x1f;\n    i.rd = (opcode >> 11) & 0x1f;\n    i.fd = (opcode >> 6) & 0x1f;\n    i.i15 = (opcode >> 6) & 0x7fff;\n    i.i16 = opcode & 0xffff;\n    i.i26 = opcode & 0x3ffffff;\n*/\n#define EE_D_RS (i.rs)\n#define EE_D_FS (i.rd)\n#define EE_D_RT (i.rt)\n#define EE_D_RD (i.rd)\n#define EE_D_FD (i.sa)\n#define EE_D_SA (i.sa)\n#define EE_D_I15 (i.i15)\n#define EE_D_I16 (i.i16)\n#define EE_D_I26 (i.i26)\n#define EE_D_SI26 ((int32_t)(EE_D_I26 << 6) >> 4)\n#define EE_D_SI16 ((int32_t)(EE_D_I16 << 16) >> 14)\n\n#define EE_RT ee->r[EE_D_RT].ul64\n#define EE_RD ee->r[EE_D_RD].ul64\n#define EE_RS ee->r[EE_D_RS].ul64\n#define EE_RT32 ee->r[EE_D_RT].ul32\n#define EE_RD32 ee->r[EE_D_RD].ul32\n#define EE_RS32 ee->r[EE_D_RS].ul32\n#define EE_FD ee->f[EE_D_FD].f\n#define EE_FT (fpu_cvtf(ee->f[EE_D_RT].f))\n#define EE_FS (fpu_cvtf(ee->f[EE_D_FS].f))\n#define EE_FT32 ee->f[EE_D_RT].u32\n#define EE_FD32 ee->f[EE_D_FD].u32\n#define EE_FS32 ee->f[EE_D_FS].u32\n\n#define EE_HI0 ee->hi.u64[0]\n#define EE_LO0 ee->lo.u64[0]\n#define EE_HI1 ee->hi.u64[1]\n#define EE_LO1 ee->lo.u64[1]\n\n#define BRANCH(cond, offset) if (cond) { \\\n    ee->next_pc = ee->next_pc + (offset); \\\n    ee->next_pc = ee->next_pc - 4; \\\n    ee->branch = 1; \\\n    ee->branch_taken = 1; }\n\n#define BRANCH_LIKELY(cond, offset) \\\n    BRANCH(cond, offset) else { ee->exception = 1; ee->pc += 4; ee->next_pc += 4; }\n\n#define SE6432(v) ((int64_t)((int32_t)(v)))\n#define SE6416(v) ((int64_t)((int16_t)(v)))\n#define SE648(v) ((int64_t)((int8_t)(v)))\n#define SE3216(v) ((int32_t)((int16_t)(v)))\n\nstatic inline void ee_print_disassembly(struct ee_state* ee, const ee_instruction& i) {\n    char buf[128];\n    struct ee_dis_state ds;\n\n    ds.print_address = 1;\n    ds.print_opcode = 1;\n    ds.pc = ee->pc;\n\n    puts(ee_disassemble(buf, i.opcode, &ds));\n}\n\nstatic inline int ee_get_segment(uint32_t virt) {\n    switch (virt & 0xe0000000) {\n        case 0x00000000: return EE_KUSEG;\n        case 0x20000000: return EE_KUSEG;\n        case 0x40000000: return EE_KUSEG;\n        case 0x60000000: return EE_KUSEG;\n        case 0x80000000: return EE_KSEG0;\n        case 0xa0000000: return EE_KSEG1;\n        case 0xc0000000: return EE_KSSEG;\n        case 0xe0000000: return EE_KSEG3;\n    }\n\n    return EE_KUSEG;\n}\n\nconst uint32_t ee_bus_region_mask_table[] = {\n    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n    0x7fffffff, 0x1fffffff, 0xffffffff, 0xffffffff\n};\n\nstatic inline float fpu_cvtf(float f) {\n    uint32_t u32 = *(uint32_t*)&f;\n\n    switch (u32 & 0x7f800000) {\n        case 0x0: {\n            u32 &= 0x80000000;\n\n            return *(float*)&u32;\n        } break;\n\n        case 0x7f800000: {\n            uint32_t result = (u32 & 0x80000000) | 0x7f7fffff;\n\n            return *(float*)&result;\n        }\n    }\n\n    return *(float*)&u32;\n}\n\nstatic inline float fpu_cvtsw(union ee_fpu_reg* reg) {\n    switch (reg->u32 & 0x7F800000) {\n        case 0x0: {\n            reg->u32 &= 0x80000000;\n        } break;\n\n        case 0x7F800000: {\n            reg->u32 = (reg->u32 & 0x80000000) | 0x7F7FFFFF;\n        } break;\n    }\n\n    return reg->f;\n}\n\nstatic inline void fpu_cvtws(union ee_fpu_reg* d, union ee_fpu_reg* s) {\n    if ((s->u32 & 0x7F800000) <= 0x4E800000)\n        d->s32 = (int32_t)fpu_cvtf(s->f);\n    else if ((s->u32 & 0x80000000) == 0)\n        d->u32 = 0x7FFFFFFF;\n    else\n        d->u32 = 0x80000000;\n}\n\nstatic inline int fpu_check_overflow(struct ee_state* ee, union ee_fpu_reg* reg) {\n    if ((reg->u32 & ~0x80000000) == 0x7f800000) {\n        reg->u32 = (reg->u32 & 0x80000000) | 0x7f7fffff;\n        ee->fcr |= FPU_FLG_O | FPU_FLG_SO;\n\n        return 1;\n    }\n\n    ee->fcr &= ~FPU_FLG_O;\n\n    return 0;\n}\n\nstatic inline int fpu_check_underflow(struct ee_state* ee, union ee_fpu_reg* reg) {\n    if (((reg->u32 & 0x7F800000) == 0) && ((reg->u32 & 0x007FFFFF) != 0)) {\n        reg->u32 &= 0x80000000;\n        ee->fcr |= FPU_FLG_U | FPU_FLG_SU;\n\n        return 1;\n    }\n\n    ee->fcr &= ~FPU_FLG_U;\n\n    return 0;\n}\n\nstatic inline int fpu_check_overflow_no_flags(struct ee_state* ee, union ee_fpu_reg* reg) {\n    if ((reg->u32 & ~0x80000000) == 0x7f800000) {\n        reg->u32 = (reg->u32 & 0x80000000) | 0x7f7fffff;\n\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic inline int fpu_check_underflow_no_flags(struct ee_state* ee, union ee_fpu_reg* reg) {\n    if (((reg->u32 & 0x7F800000) == 0) && ((reg->u32 & 0x007FFFFF) != 0)) {\n        reg->u32 &= 0x80000000;\n\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic inline int fpu_max(int32_t a, int32_t b) {\n    return (a < 0 && b < 0) ? min(a, b) : max(a, b);\n}\n\nstatic inline int fpu_min(int32_t a, int32_t b) {\n    return (a < 0 && b < 0) ? max(a, b) : min(a, b);\n}\n\nvoid ee_exception_level1(struct ee_state* ee, uint32_t cause);\n\n#ifdef _EE_USE_MMU\nstatic inline struct ee_vtlb_entry* ee_search_vtlb(struct ee_state* ee, uint32_t virt) {\n    for (int i = 0; i < 48; i++) {\n        struct ee_vtlb_entry* e = &ee->vtlb[i];\n\n        if (e->s) {\n            uint32_t mask = 0xffffc000;\n\n            if ((virt & mask) == (e->vpn2 & mask)) {\n                return e;\n            }\n        }\n\n        uint32_t mask = (~e->mask) & 0xffffe000;\n\n        // printf(\"ee: TLB search index=%d virt=%08x vpn2=%08x mask=%08x\\n\",\n        //     i,\n        //     virt,\n        //     e->vpn2,\n        //     mask\n        // );\n\n        if ((virt & mask) == (e->vpn2 & mask)) {\n            return e;\n        }\n    }\n\n    return nullptr;\n}\n\nstatic inline int ee_translate_virt(struct ee_state* ee, uint32_t virt, uint32_t* phys, int load) {\n    int seg = ee_get_segment(virt);\n\n    // Assume we're in kernel mode\n    if (seg == EE_KSEG0 || seg == EE_KSEG1) {\n        *phys = virt & 0x1fffffff;\n\n        return 0;\n    }\n\n    struct ee_vtlb_entry* entry = ee_search_vtlb(ee, virt);\n\n    if (!entry) {\n        ee_exception_level1(ee, load ? CAUSE_EXC1_TLBL : CAUSE_EXC1_TLBS);\n\n        ee->context &= 0x7ffff0;\n        ee->context |= (virt & 0xFFFFE000) >> 9;\n\n        printf(\"ee: TLB miss on %s at virt=%08x\\n\", load ? \"load\" : \"store\", virt);\n\n        return -1;\n    }\n\n    if (entry->s) {\n        *phys = virt & 0x00003fff;\n\n        return 1;\n    }\n\n    // printf(\"ee: virt=%08x vpn2=%08x even={pfn=%08x v=%d d=%d} odd={pfn=%08x v=%d d=%d} mask=%08x s=%d g=%d\\n\",\n    //     virt,\n    //     entry->vpn2,\n    //     entry->pfn0,\n    //     entry->v0,\n    //     entry->d0,\n    //     entry->pfn1,\n    //     entry->v1,\n    //     entry->d1,\n    //     entry->mask,\n    //     entry->s,\n    //     entry->g\n    // );\n\n    uint32_t nmask = 0xfffff000 & ~(entry->mask >> 1);\n    uint32_t pfn = entry->pfn0;\n\n    int odd = (virt & ((entry->mask >> 1) + 0x1000)) ? 1 : 0;\n\n    if (odd) {\n        pfn = entry->pfn1;\n    }\n\n    *phys = pfn | (virt & ~nmask);\n\n    // printf(\"ee: Translated virt=%08x to phys=%08x\\n\", virt, *phys);\n\n    // if (odd) exit(1);\n    return 0;\n}\n\n#define BUS_READ_FUNC(b)                                                        \\\n    static inline uint64_t bus_read ## b(struct ee_state* ee, uint32_t addr) {  \\\n        uint32_t phys;                                                          \\\n        if (ee_translate_virt(ee, addr, &phys, 1) == 1)                         \\\n            return ps2_ram_read ## b(ee->spr, phys);                            \\\n        if (phys == 0x1000f000) ee->intc_reads++;                               \\\n        if (phys == 0x12001000) ee->csr_reads++;                                \\\n        return ee->bus.read ## b(ee->bus.udata, phys);                          \\\n    }\n\n#define BUS_WRITE_FUNC(b)                                                                   \\\n    static inline void bus_write ## b(struct ee_state* ee, uint32_t addr, uint64_t data) {  \\\n        uint32_t phys;                                                                      \\\n        if (ee_translate_virt(ee, addr, &phys, 0) == 1)                                     \\\n            { ps2_ram_write ## b(ee->spr, phys, data); return; }                            \\\n        ee->bus.write ## b(ee->bus.udata, phys, data);                                      \\\n    }\n\nBUS_READ_FUNC(8)\nBUS_READ_FUNC(16)\nBUS_READ_FUNC(32)\nBUS_READ_FUNC(64)\n\nstatic inline uint128_t bus_read128(struct ee_state* ee, uint32_t addr) {\n    uint32_t phys;\n\n    if (ee_translate_virt(ee, addr, &phys, 1) == 1)\n        return ps2_ram_read128(ee->spr, phys);\n\n    return ee->bus.read128(ee->bus.udata, phys);\n}\n\nBUS_WRITE_FUNC(8)\nBUS_WRITE_FUNC(16)\nBUS_WRITE_FUNC(32)\nBUS_WRITE_FUNC(64)\n\nstatic inline void bus_write128(struct ee_state* ee, uint32_t addr, uint128_t data) {\n    uint32_t phys;\n\n    if (ee_translate_virt(ee, addr, &phys, 0) == 1)\n        { ps2_ram_write128(ee->spr, phys, data); return; }\n\n    ee->bus.write128(ee->bus.udata, phys, data);\n}\n#else\nstatic inline int ee_translate_virt(struct ee_state* ee, uint32_t virt, uint32_t* phys) {\n    int seg = ee_get_segment(virt);\n\n    // Assume we're in kernel mode\n    if (seg == EE_KSEG0 || seg == EE_KSEG1) {\n        *phys = virt & 0x1fffffff;\n\n        return 0;\n    }\n\n    if (virt >= 0x00000000 && virt <= ee->ram_size) {\n        *phys = virt & ee->ram_size;\n\n        return 0;\n    }\n\n    if (virt >= 0x10000000 && virt <= 0x1FFFFFFF) {\n        *phys = virt & 0x1FFFFFFF;\n\n        return 0;\n    }\n\n    if (virt >= 0x20000000 && virt <= (0x20000000 | ee->ram_size)) {\n        *phys = virt & ee->ram_size;\n\n        return 0;\n    }\n\n    if (virt >= 0x30000000 && virt <= (0x30000000 | ee->ram_size)) {\n        *phys = virt & ee->ram_size;\n\n        return 0;\n    }\n\n    // DECI2 area\n    if (virt >= 0xFFFF8000) {\n        *phys = (virt - 0xFFFF8000) + 0x78000;\n\n        return 0;\n    }\n\n    *phys = virt & ee_bus_region_mask_table[virt >> 29];\n\n    // printf(\"ee: Unhandled virtual address %08x @ cyc=%ld pc=%08x\\n\", virt, ee->total_cycles, ee->pc);\n\n    // *(int*)0 = 0;\n\n    // exit(1);\n\n    // To-do: MMU mapping\n    *phys = virt & 0x1fffffff;\n\n    return 0;\n}\n\n#ifndef _EE_CACHE_PAGESIZE\n#define _EE_CACHE_PAGESIZE 512\n#endif\n\n#define EE_CACHE_PAGECOUNT (0x100000000ull / _EE_CACHE_PAGESIZE)\n\n#define INVALIDATE_CACHE_PAGE(addr) { \\\n    if (ee->block_cache[(addr) / _EE_CACHE_PAGESIZE]) { \\\n        delete[] ee->block_cache[(addr) / _EE_CACHE_PAGESIZE]; \\\n        ee->block_cache[(addr) / _EE_CACHE_PAGESIZE] = nullptr; \\\n    } \\\n}\n\n#define BUS_READ_FUNC(b)                                                       \\\n    static inline uint64_t bus_read ## b(struct ee_state* ee, uint32_t addr) { \\\n        if ((addr & 0xf0000000) == 0x70000000)                                 \\\n            return ps2_ram_read ## b(ee->spr, addr & 0x3fff);                  \\\n        uint32_t phys;                                                         \\\n        ee_translate_virt(ee, addr, &phys);                                    \\\n        if (phys == 0x1000f000) ee->intc_reads++;                              \\\n        if (phys == 0x12001000) ee->csr_reads++;                               \\\n        return ee->bus.read ## b(ee->bus.udata, phys);                         \\\n    }\n\n#define BUS_WRITE_FUNC(b)                                                                  \\\n    static inline void bus_write ## b(struct ee_state* ee, uint32_t addr, uint64_t data) { \\\n        INVALIDATE_CACHE_PAGE(addr);                                                       \\\n        if ((addr & 0xf0000000) == 0x70000000)                                             \\\n            { ps2_ram_write ## b(ee->spr, addr & 0x3fff, data); return; }                  \\\n        uint32_t phys;                                                                     \\\n        ee_translate_virt(ee, addr, &phys);                                                \\\n        ee->bus.write ## b(ee->bus.udata, phys, data);                                     \\\n    }\n\nBUS_READ_FUNC(8)\nBUS_READ_FUNC(16)\nBUS_READ_FUNC(32)\nBUS_READ_FUNC(64)\n\nstatic inline uint128_t bus_read128(struct ee_state* ee, uint32_t addr) {\n    if ((addr & 0xf0000000) == 0x70000000)\n        return ps2_ram_read128(ee->spr, addr & 0x3ff0);\n\n    uint32_t phys;\n\n    ee_translate_virt(ee, addr, &phys);\n\n    return ee->bus.read128(ee->bus.udata, phys);\n}\n\nBUS_WRITE_FUNC(8)\nBUS_WRITE_FUNC(16)\nBUS_WRITE_FUNC(32)\nBUS_WRITE_FUNC(64)\n\nstatic inline void bus_write128(struct ee_state* ee, uint32_t addr, uint128_t data) {\n    INVALIDATE_CACHE_PAGE(addr);\n\n    if ((addr & 0xf0000000) == 0x70000000) {\n        ps2_ram_write128(ee->spr, addr & 0x3ff0, data);\n\n        return;\n    }\n\n    uint32_t phys;\n\n    ee_translate_virt(ee, addr, &phys);\n\n    ee->bus.write128(ee->bus.udata, phys, data);\n}\n#endif\n\n#undef BUS_READ_FUNC\n#undef BUS_WRITE_FUNC\n\nstatic inline int ee_skip_fmv(struct ee_state* ee, uint32_t addr) {\n    if (bus_read32(ee, addr + 4) != 0x03E00008)\n        return 0;\n\n    uint32_t code = bus_read32(ee, addr);\n    uint32_t p1 = 0x8c800040;\n    uint32_t p2 = 0x8c020000 | (code & 0x1f0000) << 5;\n\n    if ((code & 0xffe0ffff) != p1) {\n        return 0;\n    }\n\n    if (bus_read32(ee, addr + 8) != p2) {\n        return 0;\n    }\n\n    printf(\"ee: Skipping FMV\\n\");\n\n    return 1;\n}\n\nstatic inline void ee_set_pc(struct ee_state* ee, uint32_t addr) {\n    if (ee->fmv_skip) {\n        if (ee_skip_fmv(ee, addr)) return;\n    }\n\n    ee->pc = addr;\n    ee->next_pc = addr + 4;\n}\n\nstatic inline void ee_set_pc_delayed(struct ee_state* ee, uint32_t addr) {\n    if (ee->fmv_skip) {\n        if (ee_skip_fmv(ee, addr)) return;\n    }\n\n    ee->next_pc = addr;\n    ee->branch = 1;\n}\n\nvoid ee_exception_level1(struct ee_state* ee, uint32_t cause) {\n    ee->exception = 1;\n\n    uint32_t vec = EE_VEC_COMMON;\n\n    switch (cause) {\n        case CAUSE_EXC1_TLBL:\n        case CAUSE_EXC1_TLBS:\n            vec = EE_VEC_TLB;\n            break;\n        case CAUSE_EXC1_TLBIL:\n        case CAUSE_EXC1_TLBIS:\n            cause <<= 2;\n            break;\n        case CAUSE_EXC1_INT:\n            vec = EE_VEC_IRQ;\n            break;\n    }\n\n    ee->cause &= ~EE_CAUSE_EXC;\n    ee->cause |= cause;\n\n    if (!(ee->status & EE_SR_EXL)) {\n        ee->epc = ee->pc - 4;\n\n        if (ee->delay_slot) {\n            ee->epc -= 4;\n            ee->cause |= EE_CAUSE_BD;\n        } else {\n            ee->cause &= ~EE_CAUSE_BD;\n        }\n    }\n\n    ee->status |= EE_SR_EXL;\n\n    uint32_t addr = ((ee->status & EE_SR_BEV) ? 0xbfc00200 : 0x80000000) + vec;\n\n    ee_set_pc(ee, addr);\n}\n\nstatic inline void ee_exception_level2(struct ee_state* ee, uint32_t cause) {\n    uint32_t vec;\n\n    ee->cause &= ~EE_CAUSE_EXC2;\n    ee->cause |= cause;\n\n    ee->errorepc = ee->pc - 4;\n\n    if (ee->delay_slot) {\n        ee->errorepc -= 4;\n        ee->cause |= EE_CAUSE_BD2;\n    } else {\n        ee->cause &= ~EE_CAUSE_BD2;\n    }\n\n    ee->status |= EE_SR_ERL;\n\n    if ((cause == CAUSE_EXC2_RES) | (cause == CAUSE_EXC2_NMI)) {\n        ee_set_pc(ee, EE_VEC_RESET);\n\n        return;\n    }\n\n    if (cause == CAUSE_EXC2_PERFC) {\n        vec = EE_VEC_COUNTER;\n    } else {\n        vec = EE_VEC_DEBUG;\n    }\n\n    ee_set_pc(ee, ((ee->status & EE_SR_DEV) ? 0xbfc00200 : 0x80000000) + vec);\n}\n\nstatic inline int ee_check_irq(struct ee_state* ee) {\n    int irq_enabled = (ee->status & EE_SR_IE) && (ee->status & EE_SR_EIE) &&\n        (!(ee->status & EE_SR_EXL)) && (!(ee->status & EE_SR_ERL));\n    int int0_pending = (ee->status & EE_SR_IM2) && (ee->cause & EE_CAUSE_IP2);\n    int int1_pending = (ee->status & EE_SR_IM3) && (ee->cause & EE_CAUSE_IP3);\n\n    if (irq_enabled && (int0_pending || int1_pending)) {\n        ee->pc += 4;\n\n        // printf(\"ee: Handling irq at pc=%08x (int0=%d (%d) int1=%d (%d)) sr=%08x delay_slot=%d\\n\",\n        //     ee->pc,\n        //     int0_pending, !!(ee->status & EE_SR_IM2),\n        //     int1_pending, !!(ee->status & EE_SR_IM3),\n        //     ee->status,\n        //     ee->delay_slot\n        // );\n\n        ee->intc_reads = 0;\n\n        ee_exception_level1(ee, CAUSE_EXC1_INT);\n\n        return 1;\n    }\n\n    return 0;\n}\n\nvoid ee_set_int0(struct ee_state* ee, int v) {\n    if (v) {\n        ee->cause |= EE_CAUSE_IP2;\n    } else {\n        ee->cause &= ~EE_CAUSE_IP2;\n    }\n}\n\nvoid ee_set_int1(struct ee_state* ee, int v) {\n    if (v) {\n        ee->cause |= EE_CAUSE_IP3;\n    } else {\n        ee->cause &= ~EE_CAUSE_IP3;\n    }\n}\n\nvoid ee_set_cpcond0(struct ee_state* ee, int v) {\n    ee->cpcond0 = v;\n}\n\nstatic inline void ee_i_abss(struct ee_state* ee, const ee_instruction& i) {\n    ee->f[EE_D_FD].u32 = ee->f[EE_D_FS].u32 & 0x7fffffff;\n    // EE_FD = fabsf(EE_FS);\n}\nstatic inline void ee_i_add(struct ee_state* ee, const ee_instruction& i) {\n    int32_t s = EE_RS;\n    int32_t t = EE_RT;\n\n    int32_t r = s + t;\n    uint32_t o = (s ^ r) & (t ^ r);\n\n    if (o & 0x80000000) {\n        ee_exception_level1(ee, CAUSE_EXC1_OV);\n    } else {\n        EE_RD = SE6432(r);\n    }\n}\nstatic inline void ee_i_addas(struct ee_state* ee, const ee_instruction& i) {\n    ee->a.f = EE_FS + EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->a))\n        return;\n\n    fpu_check_underflow(ee, &ee->a);\n}\nstatic inline void ee_i_addi(struct ee_state* ee, const ee_instruction& i) {\n    int32_t s = EE_RS;\n    int32_t t = SE3216(EE_D_I16);\n    int32_t r;\n\n    if (__builtin_sadd_overflow(s, t, &r)) {\n        ee_exception_level1(ee, CAUSE_EXC1_OV);\n    } else {\n        EE_RT = SE6432(r);\n    }\n}\nstatic inline void ee_i_addiu(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = SE6432(EE_RS32 + SE3216(EE_D_I16));\n}\nstatic inline void ee_i_adds(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_FD;\n\n    ee->f[d].f = EE_FS + EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow(ee, &ee->f[d]);\n}\nstatic inline void ee_i_addu(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = SE6432(EE_RS + EE_RT);\n}\nstatic inline void ee_i_and(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_RS & EE_RT;\n}\nstatic inline void ee_i_andi(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = EE_RS & EE_D_I16;\n}\nstatic inline void ee_i_bc0f(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH(!ee->cpcond0, EE_D_SI16);\n}\nstatic inline void ee_i_bc0fl(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH_LIKELY(!ee->cpcond0, EE_D_SI16);\n}\nstatic inline void ee_i_bc0t(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH(ee->cpcond0, EE_D_SI16);\n}\nstatic inline void ee_i_bc0tl(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH_LIKELY(ee->cpcond0, EE_D_SI16);\n}\nstatic inline void ee_i_bc1f(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH((ee->fcr & FPU_FLG_C) == 0, EE_D_SI16);\n}\nstatic inline void ee_i_bc1fl(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH_LIKELY((ee->fcr & FPU_FLG_C) == 0, EE_D_SI16);\n}\nstatic inline void ee_i_bc1t(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH((ee->fcr & FPU_FLG_C) != 0, EE_D_SI16);\n}\nstatic inline void ee_i_bc1tl(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH_LIKELY((ee->fcr & FPU_FLG_C) != 0, EE_D_SI16);\n}\nstatic inline void ee_i_bc2f(struct ee_state* ee, const ee_instruction& i) { BRANCH(1, EE_D_SI16); }\nstatic inline void ee_i_bc2fl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY(1, EE_D_SI16); }\nstatic inline void ee_i_bc2t(struct ee_state* ee, const ee_instruction& i) { BRANCH(0, EE_D_SI16); }\nstatic inline void ee_i_bc2tl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY(0, EE_D_SI16); }\nstatic inline void ee_i_beq(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH(EE_RS == EE_RT, EE_D_SI16);\n}\nstatic inline void ee_i_beql(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH_LIKELY(EE_RS == EE_RT, EE_D_SI16);\n}\nstatic inline void ee_i_bgez(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH((int64_t)EE_RS >= (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bgezal(struct ee_state* ee, const ee_instruction& i) {\n    ee->r[31].ul64 = ee->next_pc;\n\n    BRANCH((int64_t)EE_RS >= (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bgezall(struct ee_state* ee, const ee_instruction& i) {\n    ee->r[31].ul64 = ee->next_pc;\n\n    BRANCH_LIKELY((int64_t)EE_RS >= (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bgezl(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH_LIKELY((int64_t)EE_RS >= (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bgtz(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH((int64_t)EE_RS > (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bgtzl(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH_LIKELY((int64_t)EE_RS > (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_blez(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH((int64_t)EE_RS <= (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_blezl(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH_LIKELY((int64_t)EE_RS <= (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bltz(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH((int64_t)EE_RS < (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bltzal(struct ee_state* ee, const ee_instruction& i) {\n    ee->r[31].ul64 = ee->next_pc;\n\n    BRANCH((int64_t)EE_RS < (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bltzall(struct ee_state* ee, const ee_instruction& i) {\n    ee->r[31].ul64 = ee->next_pc;\n\n    BRANCH_LIKELY((int64_t)EE_RS < (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bltzl(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH_LIKELY((int64_t)EE_RS < (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bne(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH(EE_RS != EE_RT, EE_D_SI16);\n}\nstatic inline void ee_i_bnel(struct ee_state* ee, const ee_instruction& i) {\n    BRANCH_LIKELY(EE_RS != EE_RT, EE_D_SI16);\n}\nstatic inline void ee_i_break(struct ee_state* ee, const ee_instruction& i) {\n    ee_exception_level1(ee, CAUSE_EXC1_BP);\n}\nstatic inline void ee_i_cache(struct ee_state* ee, const ee_instruction& i) {\n    /* To-do: Cache emulation */\n    switch (EE_D_RT) {\n        // CACHE.IXIN\n        case 0x07: {\n            ee->block_cache.clear();\n        } break;\n    } \n} \nstatic inline void ee_i_ceq(struct ee_state* ee, const ee_instruction& i) {\n    if (EE_FS == EE_FT) {\n        ee->fcr |= FPU_FLG_C;\n    } else {\n        ee->fcr &= ~FPU_FLG_C;\n    }\n}\nstatic inline void ee_i_cf(struct ee_state* ee, const ee_instruction& i) {\n    ee->fcr &= ~FPU_FLG_C; \n}\nstatic inline void ee_i_cfc1(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = SE6432((EE_D_FS >= 16) ? ee->fcr : 0x2e30);\n}\nstatic inline void ee_i_cfc2(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = SE6432(ps2_vu_read_vi(ee->vu0, EE_D_RD));\n}\nstatic inline void ee_i_cle(struct ee_state* ee, const ee_instruction& i) {\n    if (EE_FS <= EE_FT) {\n        ee->fcr |= FPU_FLG_C;\n    } else {\n        ee->fcr &= ~FPU_FLG_C;\n    }\n}\nstatic inline void ee_i_clt(struct ee_state* ee, const ee_instruction& i) {\n    if (EE_FS < EE_FT) {\n        ee->fcr |= FPU_FLG_C;\n    } else {\n        ee->fcr &= ~FPU_FLG_C;\n    }\n}\nstatic inline void ee_i_ctc1(struct ee_state* ee, const ee_instruction& i) {\n    if (EE_D_FS < 16)\n        return;\n\n    ee->fcr = EE_RT32; // (ee->fcr & ~(0x83c078)) | (EE_RT & 0x83c078);\n}\nstatic inline void ee_i_ctc2(struct ee_state* ee, const ee_instruction& i) {\n    // To-do: Handle FBRST, VPU_STAT, CMSAR1\n    int d = EE_D_RD;\n\n    static const char* regs[] = {\n        \"Status flag\",\n        \"MAC flag\",\n        \"clipping flag\",\n        \"reserved\",\n        \"R\",\n        \"I\",\n        \"Q\",\n        \"reserved\",\n        \"reserved\",\n        \"reserved\",\n        \"TPC\",\n        \"CMSAR0\",\n        \"FBRST\",\n        \"VPU-STAT\",\n        \"reserved\",\n        \"CMSAR1\",\n    };\n\n    ps2_vu_write_vi(ee->vu0, d, EE_RT32);\n}\nstatic inline void ee_i_cvts(struct ee_state* ee, const ee_instruction& i) {\n    EE_FD = (float)ee->f[EE_D_FS].s32;\n    EE_FD = fpu_cvtsw(&ee->f[EE_D_FD]);\n}\nstatic inline void ee_i_cvtw(struct ee_state* ee, const ee_instruction& i) {\n    fpu_cvtws(&ee->f[EE_D_FD], &ee->f[EE_D_FS]);\n}\nstatic inline void ee_i_dadd(struct ee_state* ee, const ee_instruction& i) {\n    long long r;\n\n    if (SADDOVF64((int64_t)EE_RS, (int64_t)EE_RT, &r)) {\n        ee_exception_level1(ee, CAUSE_EXC1_OV);\n    } else {\n        EE_RD = r;\n    }\n}\nstatic inline void ee_i_daddi(struct ee_state* ee, const ee_instruction& i) {\n    long long r;\n\n    if (SADDOVF64((int64_t)EE_RS, SE6416(EE_D_I16), &r)) {\n        ee_exception_level1(ee, CAUSE_EXC1_OV);\n    } else {\n        EE_RT = r;\n    }\n}\nstatic inline void ee_i_daddiu(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = EE_RS + SE6416(EE_D_I16);\n}\nstatic inline void ee_i_daddu(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_RS + EE_RT;\n}\nstatic inline void ee_i_di(struct ee_state* ee, const ee_instruction& i) {\n    int edi = ee->status & EE_SR_EDI;\n    int exl = ee->status & EE_SR_EXL;\n    int erl = ee->status & EE_SR_ERL;\n    int ksu = ee->status & EE_SR_KSU;\n    \n    if (edi || exl || erl || !ksu)\n        ee->status &= ~EE_SR_EIE;\n}\nstatic inline void ee_i_div(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int s = EE_D_RS;\n\n    if (ee->r[s].ul32 == 0x80000000 && ee->r[t].ul32 == 0xffffffff) {\n        EE_LO0 = (int32_t)0x80000000;\n        EE_HI0 = 0;\n    } else if (ee->r[t].ul32 != 0) {\n        EE_HI0 = SE6432(ee->r[s].sl32 % ee->r[t].sl32);\n        EE_LO0 = SE6432(ee->r[s].sl32 / ee->r[t].sl32);\n    } else {\n        EE_HI0 = SE6432(ee->r[s].ul32);\n        EE_LO0 = ((int32_t)ee->r[s].ul32 < 0) ? 1 : -1;\n    }\n}\nstatic inline void ee_i_div1(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int s = EE_D_RS;\n\n    if (ee->r[s].ul32 == 0x80000000 && ee->r[t].ul32 == 0xffffffff) {\n        EE_LO1 = (int32_t)0x80000000;\n        EE_HI1 = 0;\n    } else if (ee->r[t].ul32 != 0) {\n        EE_HI1 = SE6432(ee->r[s].sl32 % ee->r[t].sl32);\n        EE_LO1 = SE6432(ee->r[s].sl32 / ee->r[t].sl32);\n    } else {\n        EE_HI1 = SE6432(ee->r[s].ul32);\n        EE_LO1 = ((int32_t)ee->r[s].ul32 < 0) ? 1 : -1;\n    }\n}\nstatic inline void ee_i_divs(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int d = EE_D_FD;\n    int s = EE_D_FS;\n\n    ee->fcr &= ~(FPU_FLG_I | FPU_FLG_D);\n\n    // If both the dividend and divisor are zero, set I/SI,\n    // else set D/SD\n    if ((ee->f[t].u32 & 0x7F800000) == 0) {\n        if ((ee->f[s].u32 & 0x7F800000) == 0) {\n            ee->fcr |= FPU_FLG_I | FPU_FLG_SI;\n        } else {\n            ee->fcr |= FPU_FLG_D | FPU_FLG_SD;\n        }\n\n        ee->f[d].u32 = ((ee->f[t].u32 ^ ee->f[s].u32) & 0x80000000) | 0x7f7fffff;\n\n        return;\n    }\n\n    ee->f[d].f = EE_FS / EE_FT;\n\n    if (fpu_check_overflow_no_flags(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow_no_flags(ee, &ee->f[d]);\n}\nstatic inline void ee_i_divu(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int s = EE_D_RS;\n\n    if (!ee->r[t].ul32) {\n        EE_LO0 = -1;\n        EE_HI0 = SE6432(ee->r[s].ul32);\n\n        return;\n    }\n\n    EE_HI0 = SE6432(ee->r[s].ul32 % ee->r[t].ul32);\n    EE_LO0 = SE6432(ee->r[s].ul32 / ee->r[t].ul32);\n}\nstatic inline void ee_i_divu1(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int s = EE_D_RS;\n\n    if (!ee->r[t].ul32) {\n        EE_LO1 = -1;\n        EE_HI1 = SE6432(ee->r[s].ul32);\n\n        return;\n    }\n\n    EE_HI1 = SE6432(ee->r[s].ul32 % ee->r[t].ul32);\n    EE_LO1 = SE6432(ee->r[s].ul32 / ee->r[t].ul32);\n}\nstatic inline void ee_i_dsll(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_RT << EE_D_SA;\n}\nstatic inline void ee_i_dsll32(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_RT << (EE_D_SA + 32);\n}\nstatic inline void ee_i_dsllv(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_RT << (EE_RS & 0x3f);\n}\nstatic inline void ee_i_dsra(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = ((int64_t)EE_RT) >> EE_D_SA;\n}\nstatic inline void ee_i_dsra32(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = ((int64_t)EE_RT) >> (EE_D_SA + 32);\n}\nstatic inline void ee_i_dsrav(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = ((int64_t)EE_RT) >> (EE_RS & 0x3f);\n}\nstatic inline void ee_i_dsrl(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_RT >> EE_D_SA;\n}\nstatic inline void ee_i_dsrl32(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_RT >> (EE_D_SA + 32);\n}\nstatic inline void ee_i_dsrlv(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_RT >> (EE_RS & 0x3f);\n}\nstatic inline void ee_i_dsub(struct ee_state* ee, const ee_instruction& i) {\n    long long r;\n\n    if (SSUBOVF64((int64_t)EE_RS, (int64_t)EE_RT, &r)) {\n        ee_exception_level1(ee, CAUSE_EXC1_OV);\n    } else {\n        EE_RD = r;\n    }\n}\nstatic inline void ee_i_dsubu(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_RS - EE_RT;\n}\nstatic inline void ee_i_ei(struct ee_state* ee, const ee_instruction& i) {\n    int edi = ee->status & EE_SR_EDI;\n    int exl = ee->status & EE_SR_EXL;\n    int erl = ee->status & EE_SR_ERL;\n    int ksu = ee->status & EE_SR_KSU;\n    \n    if (edi || exl || erl || !ksu)\n        ee->status |= EE_SR_EIE;\n}\nstatic inline void ee_i_eret(struct ee_state* ee, const ee_instruction& i) {\n    if (ee->status & EE_SR_ERL) {\n        ee_set_pc(ee, ee->errorepc);\n\n        ee->status &= ~EE_SR_ERL;\n    } else {\n        ee_set_pc(ee, ee->epc);\n\n        ee->status &= ~EE_SR_EXL;\n    }\n}\nstatic inline void ee_i_j(struct ee_state* ee, const ee_instruction& i) {\n    ee_set_pc_delayed(ee, (ee->next_pc & 0xf0000000) | (EE_D_I26 << 2));\n}\nstatic inline void ee_i_jal(struct ee_state* ee, const ee_instruction& i) {\n    ee->r[31].ul64 = ee->next_pc;\n\n    ee_set_pc_delayed(ee, (ee->next_pc & 0xf0000000) | (EE_D_I26 << 2));\n}\nstatic inline void ee_i_jalr(struct ee_state* ee, const ee_instruction& i) {\n    uint32_t next_pc = ee->next_pc;\n\n    ee_set_pc_delayed(ee, EE_RS32);\n\n    EE_RD = next_pc;\n}\nstatic inline void ee_i_jr(struct ee_state* ee, const ee_instruction& i) {\n    ee_set_pc_delayed(ee, EE_RS32);\n}\nstatic inline void ee_i_lb(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = SE648(bus_read8(ee, EE_RS32 + SE3216(EE_D_I16)));\n}\nstatic inline void ee_i_lbu(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = bus_read8(ee, EE_RS32 + SE3216(EE_D_I16));\n}\nstatic inline void ee_i_ld(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = bus_read64(ee, EE_RS32 + SE3216(EE_D_I16));\n}\nstatic inline void ee_i_ldl(struct ee_state* ee, const ee_instruction& i) {\n    static const uint8_t ldl_shift[8] = { 56, 48, 40, 32, 24, 16, 8, 0 };\n    static const uint64_t ldl_mask[8] = {\n        0x00ffffffffffffffULL, 0x0000ffffffffffffULL, 0x000000ffffffffffULL, 0x00000000ffffffffULL,\n        0x0000000000ffffffULL, 0x000000000000ffffULL, 0x00000000000000ffULL, 0x0000000000000000ULL\n    };\n\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t shift = addr & 7;\n    uint64_t data = bus_read64(ee, addr & ~7);\n\n    EE_RT = (EE_RT & ldl_mask[shift]) | (data << ldl_shift[shift]);\n}\nstatic inline void ee_i_ldr(struct ee_state* ee, const ee_instruction& i) {\n    static const uint8_t ldr_shift[8] = { 0, 8, 16, 24, 32, 40, 48, 56 };\n    static const uint64_t ldr_mask[8] = {\n        0x0000000000000000ULL, 0xff00000000000000ULL, 0xffff000000000000ULL, 0xffffff0000000000ULL,\n        0xffffffff00000000ULL, 0xffffffffff000000ULL, 0xffffffffffff0000ULL, 0xffffffffffffff00ULL\n    };\n\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t shift = addr & 7;\n    uint64_t data = bus_read64(ee, addr & ~7);\n\n    EE_RT = (EE_RT & ldr_mask[shift]) | (data >> ldr_shift[shift]);\n}\nstatic inline void ee_i_lh(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = SE6416(bus_read16(ee, EE_RS32 + SE3216(EE_D_I16)));\n}\nstatic inline void ee_i_lhu(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = bus_read16(ee, EE_RS32 + SE3216(EE_D_I16));\n}\nstatic inline void ee_i_lq(struct ee_state* ee, const ee_instruction& i) {\n    ee->r[EE_D_RT] = bus_read128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf);\n}\nstatic inline void ee_i_lqc2(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RT;\n\n    if (!d) return;\n\n    ee->vu0->vf[EE_D_RT].u128 = bus_read128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf);\n}\nstatic inline void ee_i_lui(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = SE6432(EE_D_I16 << 16);\n}\nstatic inline void ee_i_lw(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = SE6432(bus_read32(ee, EE_RS32 + SE3216(EE_D_I16)));\n}\nstatic inline void ee_i_lwc1(struct ee_state* ee, const ee_instruction& i) {\n    EE_FT32 = bus_read32(ee, EE_RS32 + SE3216(EE_D_I16));\n}\n\nstatic const uint32_t LWL_MASK[4] = { 0x00ffffff, 0x0000ffff, 0x000000ff, 0x00000000 };\nstatic const uint32_t LWR_MASK[4] = { 0x00000000, 0xff000000, 0xffff0000, 0xffffff00 };\nstatic const int LWL_SHIFT[4] = { 24, 16, 8, 0 };\nstatic const int LWR_SHIFT[4] = { 0, 8, 16, 24 };\n\nstatic inline void ee_i_lwl(struct ee_state* ee, const ee_instruction& i) {\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t shift = addr & 3;\n    uint32_t mem = bus_read32(ee, addr & ~3);\n\n    // ensure the compiler does correct sign extension into 64 bits by using s32\n    EE_RT = (int32_t)((EE_RT32 & LWL_MASK[shift]) | (mem << LWL_SHIFT[shift]));\n}\n\nstatic inline void ee_i_lwr(struct ee_state* ee, const ee_instruction& i) {\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t shift = addr & 3;\n    uint32_t data = bus_read32(ee, addr & ~3);\n\n    // Use unsigned math here, and conditionally sign extend below, when needed.\n    data = (EE_RT32 & LWR_MASK[shift]) | (data >> LWR_SHIFT[shift]);\n\n    if (!shift) {\n        // This special case requires sign extension into the full 64 bit dest.\n        EE_RT = (int32_t)data;\n    } else {\n        // This case sets the lower 32 bits of the target register.  Upper\n        // 32 bits are always preserved.\n        EE_RT32 = data;\n    }\n\n    // printf(\"lwr mem=%08x reg=%016lx addr=%08x shift=%d\\n\", data, ee->r[EE_D_RT].u64[0], addr, shift);\n}\nstatic inline void ee_i_lwu(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = bus_read32(ee, EE_RS32 + SE3216(EE_D_I16));\n}\nstatic inline void ee_i_madd(struct ee_state* ee, const ee_instruction& i) {\n    uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32);\n    uint64_t d = (uint64_t)ee->lo.u32[0] | (ee->hi.u64[0] << 32);\n\n    d += r;\n\n    EE_LO0 = SE6432(d & 0xffffffff);\n    EE_HI0 = SE6432(d >> 32);\n\n    EE_RD = EE_LO0;\n}\nstatic inline void ee_i_madd1(struct ee_state* ee, const ee_instruction& i) {\n    uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32);\n    uint64_t d = (EE_LO1 & 0xffffffff) | (EE_HI1 << 32);\n\n    d += r;\n\n    EE_LO1 = SE6432(d & 0xffffffff);\n    EE_HI1 = SE6432(d >> 32);\n\n    EE_RD = EE_LO1;\n}\nstatic inline void ee_i_maddas(struct ee_state* ee, const ee_instruction& i) {\n    ee->a.f += EE_FS * EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->a))\n        return;\n\n    fpu_check_underflow(ee, &ee->a);\n}\nstatic inline void ee_i_madds(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int d = EE_D_FD;\n    int s = EE_D_FS;\n\n    float temp = fpu_cvtf(ee->f[s].f) * fpu_cvtf(ee->f[t].f);\n\n    ee->f[d].f = fpu_cvtf(ee->a.f) + fpu_cvtf(temp);\n\n    if (fpu_check_overflow(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow(ee, &ee->f[d]);\n}\nstatic inline void ee_i_maddu(struct ee_state* ee, const ee_instruction& i) {\n    uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32;\n    uint64_t d = (uint64_t)ee->lo.u32[0] | (ee->hi.u64[0] << 32);\n\n    d += r;\n\n    EE_LO0 = SE6432(d & 0xffffffff);\n    EE_HI0 = SE6432(d >> 32);\n\n    EE_RD = EE_LO0;\n}\nstatic inline void ee_i_maddu1(struct ee_state* ee, const ee_instruction& i) {\n    uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32;\n    uint64_t d = (uint64_t)ee->lo.u32[2] | (ee->hi.u64[1] << 32);\n\n    d += r;\n\n    EE_LO1 = SE6432(d & 0xffffffff);\n    EE_HI1 = SE6432(d >> 32);\n\n    EE_RD = EE_LO1;\n}\nstatic inline void ee_i_maxs(struct ee_state* ee, const ee_instruction& i) {\n    ee->f[EE_D_FD].u32 = fpu_max(ee->f[EE_D_FS].u32, ee->f[EE_D_RT].u32);\n\n    ee->fcr &= ~(FPU_FLG_O | FPU_FLG_U);\n}\nstatic inline void ee_i_mfc0(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = SE6432(ee->cop0_r[EE_D_RD]);\n}\nstatic inline void ee_i_mfc1(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = SE6432(EE_FS32);\n}\nstatic inline void ee_i_mfhi(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_HI0;\n}\nstatic inline void ee_i_mfhi1(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_HI1;\n}\nstatic inline void ee_i_mflo(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_LO0;\n}\nstatic inline void ee_i_mflo1(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_LO1;\n}\nstatic inline void ee_i_mfsa(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = ee->sa & 0xf;\n}\nstatic inline void ee_i_mins(struct ee_state* ee, const ee_instruction& i) {\n    ee->f[EE_D_FD].u32 = fpu_min(ee->f[EE_D_FS].u32, ee->f[EE_D_RT].u32);\n\n    ee->fcr &= ~(FPU_FLG_O | FPU_FLG_U);\n}\nstatic inline void ee_i_movn(struct ee_state* ee, const ee_instruction& i) {\n    if (EE_RT) EE_RD = EE_RS;\n}\nstatic inline void ee_i_movs(struct ee_state* ee, const ee_instruction& i) {\n    EE_FD32 = EE_FS32;\n}\nstatic inline void ee_i_movz(struct ee_state* ee, const ee_instruction& i) {\n    if (!EE_RT) EE_RD = EE_RS;\n}\nstatic inline void ee_i_msubas(struct ee_state* ee, const ee_instruction& i) {\n    ee->a.f -= EE_FS * EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->a))\n        return;\n\n    fpu_check_underflow(ee, &ee->a);\n}\nstatic inline void ee_i_msubs(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int d = EE_D_FD;\n    int s = EE_D_FS;\n\n    float temp = fpu_cvtf(ee->f[s].f) * fpu_cvtf(ee->f[t].f);\n\n    ee->f[d].f = fpu_cvtf(ee->a.f) - fpu_cvtf(temp);\n\n    if (fpu_check_overflow(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow(ee, &ee->f[d]);\n}\nstatic inline void ee_i_mtc0(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n\n    if (d == 4) {\n        ee->cop0_r[4] &= 0x7ffff0;\n        ee->cop0_r[4] |= (EE_RT32 & 0xff800000);\n    } else {\n        ee->cop0_r[EE_D_RD] = EE_RT32;\n    }\n}\nstatic inline void ee_i_mtc1(struct ee_state* ee, const ee_instruction& i) {\n    EE_FS32 = EE_RT32;\n}\nstatic inline void ee_i_mthi(struct ee_state* ee, const ee_instruction& i) {\n    EE_HI0 = EE_RS;\n}\nstatic inline void ee_i_mthi1(struct ee_state* ee, const ee_instruction& i) {\n    EE_HI1 = EE_RS;\n}\nstatic inline void ee_i_mtlo(struct ee_state* ee, const ee_instruction& i) {\n    EE_LO0 = EE_RS;\n}\nstatic inline void ee_i_mtlo1(struct ee_state* ee, const ee_instruction& i) {\n    EE_LO1 = EE_RS;\n}\nstatic inline void ee_i_mtsa(struct ee_state* ee, const ee_instruction& i) {\n    ee->sa = ((uint32_t)EE_RS) & 0xf;\n}\nstatic inline void ee_i_mtsab(struct ee_state* ee, const ee_instruction& i) {\n    ee->sa = (EE_RS ^ EE_D_I16) & 15;\n}\nstatic inline void ee_i_mtsah(struct ee_state* ee, const ee_instruction& i) {\n    ee->sa = ((EE_RS ^ EE_D_I16) & 7) << 1;\n}\nstatic inline void ee_i_mulas(struct ee_state* ee, const ee_instruction& i) {\n    ee->a.f = EE_FS * EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->a))\n        return;\n\n    fpu_check_underflow(ee, &ee->a);\n}\nstatic inline void ee_i_muls(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_FD;\n\n    ee->f[d].f = EE_FS * EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow(ee, &ee->f[d]);\n}\nstatic inline void ee_i_mult(struct ee_state* ee, const ee_instruction& i) {\n    uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32);\n\n    EE_LO0 = SE6432(r & 0xffffffff);\n    EE_HI0 = SE6432(r >> 32);\n\n    EE_RD = EE_LO0;\n}\nstatic inline void ee_i_mult1(struct ee_state* ee, const ee_instruction& i) {\n    uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32);\n\n    EE_LO1 = SE6432(r & 0xffffffff);\n    EE_HI1 = SE6432(r >> 32);\n\n    EE_RD = EE_LO1;\n}\nstatic inline void ee_i_multu(struct ee_state* ee, const ee_instruction& i) {\n    uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32;\n\n    EE_LO0 = SE6432(r & 0xffffffff);\n    EE_HI0 = SE6432(r >> 32);\n\n    EE_RD = EE_LO0;\n}\nstatic inline void ee_i_multu1(struct ee_state* ee, const ee_instruction& i) {\n    uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32;\n\n    EE_LO1 = SE6432(r & 0xffffffff);\n    EE_HI1 = SE6432(r >> 32);\n\n    EE_RD = EE_LO1;\n}\nstatic inline void ee_i_negs(struct ee_state* ee, const ee_instruction& i) {\n    ee->f[EE_D_FD].u32 = ee->f[EE_D_FS].u32 ^ 0x80000000;\n\n    ee->fcr &= ~(FPU_FLG_O | FPU_FLG_U);\n}\nstatic inline void ee_i_nor(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = ~(EE_RS | EE_RT);\n}\nstatic inline void ee_i_or(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_RS | EE_RT;\n}\nstatic inline void ee_i_ori(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = EE_RS | EE_D_I16;\n}\nstatic inline void ee_i_pabsh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 8; i++) {\n        d->u16[i] = (t->u16[i] == 0x8000) ? 0x7fff : fast_abs16(t->u16[i]);\n    }\n#else\n    __m128i b = _mm_set1_epi16((unsigned short)0x8000);\n    __m128i a = _mm_load_si128((const __m128i*)t);\n    __m128i f = _mm_cmpeq_epi16(a, b);\n    __m128i r = _mm_add_epi16(_mm_abs_epi16(a), f);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_pabsw(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 4; i++) {\n        d->u32[i] = (t->u32[i] == 0x80000000) ? 0x7fffffff : fast_abs32(t->u32[i]);\n    }\n#else\n    __m128i b = _mm_set1_epi32(0x80000000);\n    __m128i a = _mm_load_si128((const __m128i*)t);\n    __m128i f = _mm_cmpeq_epi32(a, b);\n    __m128i r = _mm_add_epi32(_mm_abs_epi32(a), f);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_paddb(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 16; i++) {\n        d->u8[i] = s->u8[i] + t->u8[i];\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_add_epi8(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_paddh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 8; i++) {\n        d->u16[i] = s->u16[i] + t->u16[i];\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_add_epi16(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_paddsb(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 16; i++) {\n        int32_t r = ((int32_t)(int8_t)s->u8[i]) + ((int32_t)(int8_t)t->u8[i]);\n        d->u8[i] = (r > 0x7f) ? 0x7f : ((r < -128) ? 0x80 : r);\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_adds_epi8(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_paddsh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 8; i++) {\n        int32_t r = (SE3216(s->u16[i])) + (SE3216(t->u16[i]));\n        d->u16[i] = (r > 0x7fff) ? 0x7fff : ((r < -0x8000) ? 0x8000 : r);\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_adds_epi16(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_paddsw(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 4; i++) {\n        int64_t r = (SE6432(s->u32[i])) + (SE6432(t->u32[i]));\n        d->u32[i] = (r >= 0x7fffffff) ? 0x7fffffff : ((r < (int32_t)0x80000000) ? 0x80000000 : r);\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_adds_epi32(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_paddub(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 16; i++) {\n        uint32_t r = (uint32_t)s->u8[i] + (uint32_t)t->u8[i];\n        d->u8[i] = (r > 0xff) ? 0xff : r;\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_adds_epu8(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_padduh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 8; i++) {\n        uint32_t r = (uint32_t)s->u16[i] + (uint32_t)t->u16[i];\n        d->u16[i] = (r > 0xffff) ? 0xffff : r;\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_adds_epu16(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_padduw(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 4; i++) {\n        uint64_t r = (uint64_t)s->u32[i] + (uint64_t)t->u32[i];\n        d->u32[i] = (r > 0xffffffff) ? 0xffffffff : r;\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_adds_epu32(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_paddw(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 4; i++) {\n        d->u32[i] = s->u32[i] + t->u32[i];\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_add_epi32(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_padsbh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    d->u16[0] = s->u16[0] - t->u16[0];\n    d->u16[1] = s->u16[1] - t->u16[1];\n    d->u16[2] = s->u16[2] - t->u16[2];\n    d->u16[3] = s->u16[3] - t->u16[3];\n    d->u16[4] = s->u16[4] + t->u16[4];\n    d->u16[5] = s->u16[5] + t->u16[5];\n    d->u16[6] = s->u16[6] + t->u16[6];\n    d->u16[7] = s->u16[7] + t->u16[7];\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i x = _mm_sub_epi16(a, b);\n    __m128i y = _mm_add_epi16(a, b);\n    __m128i r = _mm_blend_epi16(x, y, 15);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_pand(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    d->u64[0] = s->u64[0] & t->u64[0];\n    d->u64[1] = s->u64[1] & t->u64[1];\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_and_si128(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_pceqb(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 16; i++) {\n        d->u8[i] = (s->u8[i] == t->u8[i]) ? 0xff : 0;\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_cmpeq_epi8(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_pceqh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 8; i++) {\n        d->u16[i] = (s->u16[i] == t->u16[i]) ? 0xffff : 0;\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_cmpeq_epi16(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_pceqw(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 4; i++) {\n        d->u32[i] = (s->u32[i] == t->u32[i]) ? 0xffffffff : 0;\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_cmpeq_epi32(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_pcgtb(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 16; i++) {\n        d->u8[i] = ((int8_t)s->u8[i] > (int8_t)t->u8[i]) ? 0xff : 0;\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_cmpgt_epi8(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_pcgth(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 8; i++) {\n        d->u16[i] = ((int16_t)s->u16[i] > (int16_t)t->u16[i]) ? 0xffff : 0;\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_cmpgt_epi16(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_pcgtw(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 4; i++) {\n        d->u32[i] = ((int32_t)s->u32[i] > (int32_t)t->u32[i]) ? 0xffffffff : 0;\n    }\n#else\n    __m128i a = _mm_load_si128((__m128i*)s);\n    __m128i b = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_cmpgt_epi32(a, b);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_pcpyh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    uint128_t tc = *t;\n\n    d->u16[0] = tc.u16[0];\n    d->u16[1] = tc.u16[0];\n    d->u16[2] = tc.u16[0];\n    d->u16[3] = tc.u16[0];\n    d->u16[4] = tc.u16[4];\n    d->u16[5] = tc.u16[4];\n    d->u16[6] = tc.u16[4];\n    d->u16[7] = tc.u16[4];\n#else\n    static const uint64_t mask[] = {\n        0x0100010001000100,\n        0x0908090809080908\n    };\n\n    __m128i m = _mm_load_si128((__m128i*)mask);\n    __m128i a = _mm_load_si128((__m128i*)t);\n    __m128i r = _mm_shuffle_epi8(a, m);\n\n    _mm_store_si128((__m128i*)d, r);\n#endif\n}\nstatic inline void ee_i_pcpyld(struct ee_state* ee, const ee_instruction& i) {\n#ifndef _EE_USE_INTRINSICS\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = rt.u64[0];\n    ee->r[d].u64[1] = rs.u64[0];\n#else\n    __m128i a = _mm_load_si128((__m128i*)&ee->r[EE_D_RT]);\n    __m128i b = _mm_load_si128((__m128i*)&ee->r[EE_D_RS]);\n    __m128i r = _mm_unpacklo_epi64(a, b);\n\n    _mm_store_si128((__m128i*)&ee->r[EE_D_RD], r);\n#endif\n}\nstatic inline void ee_i_pcpyud(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = rs.u64[1];\n    ee->r[d].u64[1] = rt.u64[1];\n}\nstatic inline void ee_i_pdivbw(struct ee_state* ee, const ee_instruction& i) {\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    for (int i = 0; i < 4; i++) {\n        if (ee->r[s].u32[i] == 0x80000000 && ee->r[t].u16[0] == 0xffff) {\n            ee->lo.u32[i] = 0x80000000;\n            ee->hi.u32[i] = 0;\n        } else if (ee->r[t].u16[0] != 0) {\n            ee->lo.u32[i] = ee->r[s].s32[i] / ee->r[t].s16[0];\n            ee->hi.u32[i] = ee->r[s].s32[i] % ee->r[t].s16[0];\n        } else {\n            if (ee->r[s].s32[i] < 0) {\n                ee->lo.u32[i] = 1;\n            } else {\n                ee->lo.u32[i] = -1;\n            }\n\n            ee->hi.u32[i] = ee->r[s].s32[i];\n        }\n    }\n\n    // ee->hi.u32[0] = SE3216(ee->r[s].s32[0] % ee->r[t].s16[0]);\n    // ee->hi.u32[1] = SE3216(ee->r[s].s32[1] % ee->r[t].s16[0]);\n    // ee->hi.u32[2] = SE3216(ee->r[s].s32[2] % ee->r[t].s16[0]);\n    // ee->hi.u32[3] = SE3216(ee->r[s].s32[3] % ee->r[t].s16[0]);\n    // ee->lo.u32[0] = ee->r[s].s32[0] / ee->r[t].s16[0];\n    // ee->lo.u32[1] = ee->r[s].s32[1] / ee->r[t].s16[0];\n    // ee->lo.u32[2] = ee->r[s].s32[2] / ee->r[t].s16[0];\n    // ee->lo.u32[3] = ee->r[s].s32[3] / ee->r[t].s16[0];\n}\nstatic inline void ee_i_pdivuw(struct ee_state* ee, const ee_instruction& i) {\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    for (int i = 0; i < 4; i += 2) {\n        if (ee->r[t].u32[i] != 0) {\n            ee->lo.u64[i/2] = SE6432(ee->r[s].u32[i] / ee->r[t].u32[i]);\n            ee->hi.u64[i/2] = SE6432(ee->r[s].u32[i] % ee->r[t].u32[i]);\n        } else {\n            ee->lo.u64[i/2] = (int64_t)-1;\n            ee->hi.u64[i/2] = (int64_t)ee->r[s].s32[i];\n        }\n    }\n}\nstatic inline void ee_i_pdivw(struct ee_state* ee, const ee_instruction& i) {\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    for (int i = 0; i < 4; i += 2) {\n        if (ee->r[s].u32[i] == 0x80000000 && ee->r[t].u32[i] == 0xffffffff) {\n            ee->lo.u64[i/2] = (int64_t)(int32_t)0x80000000;\n            ee->hi.u64[i/2] = 0;\n        } else if (ee->r[t].u32[i] != 0) {\n            ee->lo.u64[i/2] = SE6432(ee->r[s].s32[i] / ee->r[t].s32[i]);\n            ee->hi.u64[i/2] = SE6432(ee->r[s].s32[i] % ee->r[t].s32[i]);\n        } else {\n            if (ee->r[s].s32[i] < 0) {\n                ee->lo.u64[i/2] = 1;\n            } else {\n                ee->lo.u64[i/2] = (int64_t)-1;\n            }\n\n            ee->hi.u64[i/2] = (int64_t)ee->r[s].s32[i];\n        }\n    }\n\n    // ee->hi.u64[0] = SE6432(ee->r[s].s32[0] % ee->r[t].s32[0]);\n    // ee->hi.u64[1] = SE6432(ee->r[s].s32[2] % ee->r[t].s32[2]);\n    // ee->lo.u64[0] = SE6432(ee->r[s].s32[0] / ee->r[t].s32[0]);\n    // ee->lo.u64[1] = SE6432(ee->r[s].s32[2] / ee->r[t].s32[2]);\n}\nstatic inline void ee_i_pexch(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[0];\n    ee->r[d].u16[1] = rt.u16[2];\n    ee->r[d].u16[2] = rt.u16[1];\n    ee->r[d].u16[3] = rt.u16[3];\n    ee->r[d].u16[4] = rt.u16[4];\n    ee->r[d].u16[5] = rt.u16[6];\n    ee->r[d].u16[6] = rt.u16[5];\n    ee->r[d].u16[7] = rt.u16[7];\n}\nstatic inline void ee_i_pexcw(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = rt.u32[0];\n    ee->r[d].u32[1] = rt.u32[2];\n    ee->r[d].u32[2] = rt.u32[1];\n    ee->r[d].u32[3] = rt.u32[3];\n}\nstatic inline void ee_i_pexeh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[2];\n    ee->r[d].u16[1] = rt.u16[1];\n    ee->r[d].u16[2] = rt.u16[0];\n    ee->r[d].u16[3] = rt.u16[3];\n    ee->r[d].u16[4] = rt.u16[6];\n    ee->r[d].u16[5] = rt.u16[5];\n    ee->r[d].u16[6] = rt.u16[4];\n    ee->r[d].u16[7] = rt.u16[7];\n}\nstatic inline void ee_i_pexew(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = rt.u32[2];\n    ee->r[d].u32[1] = rt.u32[1];\n    ee->r[d].u32[2] = rt.u32[0];\n    ee->r[d].u32[3] = rt.u32[3];\n}\nstatic inline void ee_i_pext5(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = unpack_5551_8888(rt.u32[0]);\n    ee->r[d].u32[1] = unpack_5551_8888(rt.u32[1]);\n    ee->r[d].u32[2] = unpack_5551_8888(rt.u32[2]);\n    ee->r[d].u32[3] = unpack_5551_8888(rt.u32[3]);\n}\nstatic inline void ee_i_pextlb(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u8[ 0] = rt.u8[0];\n    ee->r[d].u8[ 1] = rs.u8[0];\n    ee->r[d].u8[ 2] = rt.u8[1];\n    ee->r[d].u8[ 3] = rs.u8[1];\n    ee->r[d].u8[ 4] = rt.u8[2];\n    ee->r[d].u8[ 5] = rs.u8[2];\n    ee->r[d].u8[ 6] = rt.u8[3];\n    ee->r[d].u8[ 7] = rs.u8[3];\n    ee->r[d].u8[ 8] = rt.u8[4];\n    ee->r[d].u8[ 9] = rs.u8[4];\n    ee->r[d].u8[10] = rt.u8[5];\n    ee->r[d].u8[11] = rs.u8[5];\n    ee->r[d].u8[12] = rt.u8[6];\n    ee->r[d].u8[13] = rs.u8[6];\n    ee->r[d].u8[14] = rt.u8[7];\n    ee->r[d].u8[15] = rs.u8[7];\n}\nstatic inline void ee_i_pextlh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[0];\n    ee->r[d].u16[1] = rs.u16[0];\n    ee->r[d].u16[2] = rt.u16[1];\n    ee->r[d].u16[3] = rs.u16[1];\n    ee->r[d].u16[4] = rt.u16[2];\n    ee->r[d].u16[5] = rs.u16[2];\n    ee->r[d].u16[6] = rt.u16[3];\n    ee->r[d].u16[7] = rs.u16[3];\n}\nstatic inline void ee_i_pextlw(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = rt.u32[0];\n    ee->r[d].u32[1] = rs.u32[0];\n    ee->r[d].u32[2] = rt.u32[1];\n    ee->r[d].u32[3] = rs.u32[1];\n}\nstatic inline void ee_i_pextub(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u8[ 0] = rt.u8[ 8];\n    ee->r[d].u8[ 1] = rs.u8[ 8];\n    ee->r[d].u8[ 2] = rt.u8[ 9];\n    ee->r[d].u8[ 3] = rs.u8[ 9];\n    ee->r[d].u8[ 4] = rt.u8[10];\n    ee->r[d].u8[ 5] = rs.u8[10];\n    ee->r[d].u8[ 6] = rt.u8[11];\n    ee->r[d].u8[ 7] = rs.u8[11];\n    ee->r[d].u8[ 8] = rt.u8[12];\n    ee->r[d].u8[ 9] = rs.u8[12];\n    ee->r[d].u8[10] = rt.u8[13];\n    ee->r[d].u8[11] = rs.u8[13];\n    ee->r[d].u8[12] = rt.u8[14];\n    ee->r[d].u8[13] = rs.u8[14];\n    ee->r[d].u8[14] = rt.u8[15];\n    ee->r[d].u8[15] = rs.u8[15];\n}\nstatic inline void ee_i_pextuh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[4];\n    ee->r[d].u16[1] = rs.u16[4];\n    ee->r[d].u16[2] = rt.u16[5];\n    ee->r[d].u16[3] = rs.u16[5];\n    ee->r[d].u16[4] = rt.u16[6];\n    ee->r[d].u16[5] = rs.u16[6];\n    ee->r[d].u16[6] = rt.u16[7];\n    ee->r[d].u16[7] = rs.u16[7];\n}\nstatic inline void ee_i_pextuw(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = rt.u32[2];\n    ee->r[d].u32[1] = rs.u32[2];\n    ee->r[d].u32[2] = rt.u32[3];\n    ee->r[d].u32[3] = rs.u32[3];\n}\nstatic inline void ee_i_phmadh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    int32_t r0 = (int32_t)(rs.s16[1] * rt.s16[1]);\n    int32_t r1 = (int32_t)(rs.s16[3] * rt.s16[3]);\n    int32_t r2 = (int32_t)(rs.s16[5] * rt.s16[5]);\n    int32_t r3 = (int32_t)(rs.s16[7] * rt.s16[7]);\n\n    ee->r[d].u32[0] = r0 + ((int16_t)rs.u16[0] * (int16_t)rt.u16[0]);\n    ee->r[d].u32[1] = r1 + ((int16_t)rs.u16[2] * (int16_t)rt.u16[2]);\n    ee->r[d].u32[2] = r2 + ((int16_t)rs.u16[4] * (int16_t)rt.u16[4]);\n    ee->r[d].u32[3] = r3 + ((int16_t)rs.u16[6] * (int16_t)rt.u16[6]);\n    ee->lo.u32[0] = ee->r[d].u32[0];\n    ee->lo.u32[1] = r0;\n    ee->hi.u32[0] = ee->r[d].u32[1];\n    ee->hi.u32[1] = r1;\n    ee->lo.u32[2] = ee->r[d].u32[2];\n    ee->lo.u32[3] = r2;\n    ee->hi.u32[2] = ee->r[d].u32[3];\n    ee->hi.u32[3] = r3;\n}\nstatic inline void ee_i_phmsbh(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int32_t r1 = ee->r[s].s16[1] * ee->r[t].s16[1];\n    int32_t r3 = ee->r[s].s16[3] * ee->r[t].s16[3];\n    int32_t r5 = ee->r[s].s16[5] * ee->r[t].s16[5];\n    int32_t r7 = ee->r[s].s16[7] * ee->r[t].s16[7];\n\n    ee->r[d].u32[0] = (int32_t)(r1 - (ee->r[s].s16[0] * ee->r[t].s16[0]));\n    ee->r[d].u32[1] = (int32_t)(r3 - (ee->r[s].s16[2] * ee->r[t].s16[2]));\n    ee->r[d].u32[2] = (int32_t)(r5 - (ee->r[s].s16[4] * ee->r[t].s16[4]));\n    ee->r[d].u32[3] = (int32_t)(r7 - (ee->r[s].s16[6] * ee->r[t].s16[6]));\n    ee->lo.u32[0] = ee->r[d].u32[0];\n    ee->lo.u32[1] = ~r1;\n    ee->hi.u32[0] = ee->r[d].u32[1];\n    ee->hi.u32[1] = ~r3;\n    ee->lo.u32[2] = ee->r[d].u32[2];\n    ee->lo.u32[3] = ~r5;\n    ee->hi.u32[2] = ee->r[d].u32[3];\n    ee->hi.u32[3] = ~r7;\n}\nstatic inline void ee_i_pinteh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[0];\n    ee->r[d].u16[1] = rs.u16[0];\n    ee->r[d].u16[2] = rt.u16[2];\n    ee->r[d].u16[3] = rs.u16[2];\n    ee->r[d].u16[4] = rt.u16[4];\n    ee->r[d].u16[5] = rs.u16[4];\n    ee->r[d].u16[6] = rt.u16[6];\n    ee->r[d].u16[7] = rs.u16[6];\n}\nstatic inline void ee_i_pinth(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[0];\n    ee->r[d].u16[1] = rs.u16[4];\n    ee->r[d].u16[2] = rt.u16[1];\n    ee->r[d].u16[3] = rs.u16[5];\n    ee->r[d].u16[4] = rt.u16[2];\n    ee->r[d].u16[5] = rs.u16[6];\n    ee->r[d].u16[6] = rt.u16[3];\n    ee->r[d].u16[7] = rs.u16[7];\n}\nstatic inline void ee_i_plzcw(struct ee_state* ee, const ee_instruction& i) { \n    for (int j = 0; j < 2; j++) {\n        uint32_t word = ee->r[EE_D_RS].u32[j];\n\n        int msb = word & 0x80000000;\n\n        word = (msb ? ~word : word);\n\n        ee->r[EE_D_RD].u32[j] = (word ? (__builtin_clz(word) - 1) : 0x1f);\n    }\n}\nstatic inline void ee_i_pmaddh(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    uint32_t r0 = SE3216(ee->r[s].u16[0]) * SE3216(ee->r[t].u16[0]);\n    uint32_t r1 = SE3216(ee->r[s].u16[1]) * SE3216(ee->r[t].u16[1]);\n    uint32_t r2 = SE3216(ee->r[s].u16[2]) * SE3216(ee->r[t].u16[2]);\n    uint32_t r3 = SE3216(ee->r[s].u16[3]) * SE3216(ee->r[t].u16[3]);\n    uint32_t r4 = SE3216(ee->r[s].u16[4]) * SE3216(ee->r[t].u16[4]);\n    uint32_t r5 = SE3216(ee->r[s].u16[5]) * SE3216(ee->r[t].u16[5]);\n    uint32_t r6 = SE3216(ee->r[s].u16[6]) * SE3216(ee->r[t].u16[6]);\n    uint32_t r7 = SE3216(ee->r[s].u16[7]) * SE3216(ee->r[t].u16[7]);\n    uint32_t c0 = ee->lo.u32[0];\n    uint32_t c1 = ee->lo.u32[1];\n    uint32_t c2 = ee->hi.u32[0];\n    uint32_t c3 = ee->hi.u32[1];\n    uint32_t c4 = ee->lo.u32[2];\n    uint32_t c5 = ee->lo.u32[3];\n    uint32_t c6 = ee->hi.u32[2];\n    uint32_t c7 = ee->hi.u32[3];\n\n    ee->r[d].u32[0] = r0 + c0;\n    ee->lo.u32[1] = r1 + c1;\n    ee->r[d].u32[1] = r2 + c2;\n    ee->hi.u32[1] = r3 + c3;\n    ee->r[d].u32[2] = r4 + c4;\n    ee->lo.u32[3] = r5 + c5;\n    ee->r[d].u32[3] = r6 + c6;\n    ee->hi.u32[3] = r7 + c7;\n\n    ee->lo.u32[0] = ee->r[d].u32[0];\n    ee->hi.u32[0] = ee->r[d].u32[1];\n    ee->lo.u32[2] = ee->r[d].u32[2];\n    ee->hi.u32[2] = ee->r[d].u32[3];\n}\nstatic inline void ee_i_pmadduw(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    uint64_t r0 = (uint64_t)ee->r[s].u32[0] * (uint64_t)ee->r[t].u32[0];\n    uint64_t r1 = (uint64_t)ee->r[s].u32[2] * (uint64_t)ee->r[t].u32[2];\n\n    ee->r[d].u64[0] = r0 + ((ee->hi.u64[0] << 32) | (uint64_t)ee->lo.u32[0]);\n    ee->r[d].u64[1] = r1 + ((ee->hi.u64[1] << 32) | (uint64_t)ee->lo.u32[2]);\n    ee->lo.u64[0] = SE6432(ee->r[d].u32[0]);\n    ee->hi.u64[0] = SE6432(ee->r[d].u32[1]);\n    ee->lo.u64[1] = SE6432(ee->r[d].u32[2]);\n    ee->hi.u64[1] = SE6432(ee->r[d].u32[3]);\n}\nstatic inline void ee_i_pmaddw(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    uint64_t r0 = (int64_t)ee->r[s].s32[0] * (int64_t)ee->r[t].s32[0];\n    uint64_t r1 = (int64_t)ee->r[s].s32[2] * (int64_t)ee->r[t].s32[2];\n\n    ee->r[d].u64[0] = r0 + ((((uint64_t)ee->hi.u32[0]) << 32) | (uint64_t)ee->lo.u32[0]);\n    ee->r[d].u64[1] = r1 + ((((uint64_t)ee->hi.u32[2]) << 32) | (uint64_t)ee->lo.u32[2]);\n    ee->lo.u64[0] = SE6432(ee->r[d].u32[0]);\n    ee->hi.u64[0] = SE6432(ee->r[d].u32[1]);\n    ee->lo.u64[1] = SE6432(ee->r[d].u32[2]);\n    ee->hi.u64[1] = SE6432(ee->r[d].u32[3]);\n}\nstatic inline void ee_i_pmaxh(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u16[0] = ((int16_t)ee->r[s].u16[0] > (int16_t)ee->r[t].u16[0]) ? ee->r[s].u16[0] : ee->r[t].u16[0];\n    ee->r[d].u16[1] = ((int16_t)ee->r[s].u16[1] > (int16_t)ee->r[t].u16[1]) ? ee->r[s].u16[1] : ee->r[t].u16[1];\n    ee->r[d].u16[2] = ((int16_t)ee->r[s].u16[2] > (int16_t)ee->r[t].u16[2]) ? ee->r[s].u16[2] : ee->r[t].u16[2];\n    ee->r[d].u16[3] = ((int16_t)ee->r[s].u16[3] > (int16_t)ee->r[t].u16[3]) ? ee->r[s].u16[3] : ee->r[t].u16[3];\n    ee->r[d].u16[4] = ((int16_t)ee->r[s].u16[4] > (int16_t)ee->r[t].u16[4]) ? ee->r[s].u16[4] : ee->r[t].u16[4];\n    ee->r[d].u16[5] = ((int16_t)ee->r[s].u16[5] > (int16_t)ee->r[t].u16[5]) ? ee->r[s].u16[5] : ee->r[t].u16[5];\n    ee->r[d].u16[6] = ((int16_t)ee->r[s].u16[6] > (int16_t)ee->r[t].u16[6]) ? ee->r[s].u16[6] : ee->r[t].u16[6];\n    ee->r[d].u16[7] = ((int16_t)ee->r[s].u16[7] > (int16_t)ee->r[t].u16[7]) ? ee->r[s].u16[7] : ee->r[t].u16[7];\n}\nstatic inline void ee_i_pmaxw(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u32[0] = ((int32_t)ee->r[s].u32[0] > (int32_t)ee->r[t].u32[0]) ? ee->r[s].u32[0] : ee->r[t].u32[0];\n    ee->r[d].u32[1] = ((int32_t)ee->r[s].u32[1] > (int32_t)ee->r[t].u32[1]) ? ee->r[s].u32[1] : ee->r[t].u32[1];\n    ee->r[d].u32[2] = ((int32_t)ee->r[s].u32[2] > (int32_t)ee->r[t].u32[2]) ? ee->r[s].u32[2] : ee->r[t].u32[2];\n    ee->r[d].u32[3] = ((int32_t)ee->r[s].u32[3] > (int32_t)ee->r[t].u32[3]) ? ee->r[s].u32[3] : ee->r[t].u32[3];\n}\nstatic inline void ee_i_pmfhi(struct ee_state* ee, const ee_instruction& i) {\n    ee->r[EE_D_RD] = ee->hi;\n}\nstatic inline void ee_i_pmfhllw(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = ee->lo.u32[0];\n    ee->r[d].u32[1] = ee->hi.u32[0];\n    ee->r[d].u32[2] = ee->lo.u32[2];\n    ee->r[d].u32[3] = ee->hi.u32[2];\n}\nstatic inline void ee_i_pmfhluw(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = ee->lo.u32[1];\n    ee->r[d].u32[1] = ee->hi.u32[1];\n    ee->r[d].u32[2] = ee->lo.u32[3];\n    ee->r[d].u32[3] = ee->hi.u32[3];\n}\nstatic inline void ee_i_pmfhlslw(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = SE6432(saturate32(((uint64_t)ee->lo.u32[0]) | (ee->hi.u64[0] << 32)));\n    ee->r[d].u64[1] = SE6432(saturate32(((uint64_t)ee->lo.u32[2]) | (ee->hi.u64[1] << 32)));\n}\nstatic inline void ee_i_pmfhllh(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = ee->lo.u16[0];\n    ee->r[d].u16[1] = ee->lo.u16[2];\n    ee->r[d].u16[2] = ee->hi.u16[0];\n    ee->r[d].u16[3] = ee->hi.u16[2];\n    ee->r[d].u16[4] = ee->lo.u16[4];\n    ee->r[d].u16[5] = ee->lo.u16[6];\n    ee->r[d].u16[6] = ee->hi.u16[4];\n    ee->r[d].u16[7] = ee->hi.u16[6];\n    \n}\nstatic inline void ee_i_pmfhlsh(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = saturate16(ee->lo.u32[0]);\n    ee->r[d].u16[1] = saturate16(ee->lo.u32[1]);\n    ee->r[d].u16[2] = saturate16(ee->hi.u32[0]);\n    ee->r[d].u16[3] = saturate16(ee->hi.u32[1]);\n    ee->r[d].u16[4] = saturate16(ee->lo.u32[2]);\n    ee->r[d].u16[5] = saturate16(ee->lo.u32[3]);\n    ee->r[d].u16[6] = saturate16(ee->hi.u32[2]);\n    ee->r[d].u16[7] = saturate16(ee->hi.u32[3]);\n}\nstatic inline void ee_i_pmflo(struct ee_state* ee, const ee_instruction& i) {\n    ee->r[EE_D_RD] = ee->lo;\n}\nstatic inline void ee_i_pminh(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u16[0] = ((int16_t)ee->r[s].u16[0] < (int16_t)ee->r[t].u16[0]) ? ee->r[s].u16[0] : ee->r[t].u16[0];\n    ee->r[d].u16[1] = ((int16_t)ee->r[s].u16[1] < (int16_t)ee->r[t].u16[1]) ? ee->r[s].u16[1] : ee->r[t].u16[1];\n    ee->r[d].u16[2] = ((int16_t)ee->r[s].u16[2] < (int16_t)ee->r[t].u16[2]) ? ee->r[s].u16[2] : ee->r[t].u16[2];\n    ee->r[d].u16[3] = ((int16_t)ee->r[s].u16[3] < (int16_t)ee->r[t].u16[3]) ? ee->r[s].u16[3] : ee->r[t].u16[3];\n    ee->r[d].u16[4] = ((int16_t)ee->r[s].u16[4] < (int16_t)ee->r[t].u16[4]) ? ee->r[s].u16[4] : ee->r[t].u16[4];\n    ee->r[d].u16[5] = ((int16_t)ee->r[s].u16[5] < (int16_t)ee->r[t].u16[5]) ? ee->r[s].u16[5] : ee->r[t].u16[5];\n    ee->r[d].u16[6] = ((int16_t)ee->r[s].u16[6] < (int16_t)ee->r[t].u16[6]) ? ee->r[s].u16[6] : ee->r[t].u16[6];\n    ee->r[d].u16[7] = ((int16_t)ee->r[s].u16[7] < (int16_t)ee->r[t].u16[7]) ? ee->r[s].u16[7] : ee->r[t].u16[7];\n}\nstatic inline void ee_i_pminw(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u32[0] = (ee->r[s].s32[0] < ee->r[t].s32[0]) ? ee->r[s].u32[0] : ee->r[t].u32[0];\n    ee->r[d].u32[1] = (ee->r[s].s32[1] < ee->r[t].s32[1]) ? ee->r[s].u32[1] : ee->r[t].u32[1];\n    ee->r[d].u32[2] = (ee->r[s].s32[2] < ee->r[t].s32[2]) ? ee->r[s].u32[2] : ee->r[t].u32[2];\n    ee->r[d].u32[3] = (ee->r[s].s32[3] < ee->r[t].s32[3]) ? ee->r[s].u32[3] : ee->r[t].u32[3];\n}\nstatic inline void ee_i_pmsubh(struct ee_state* ee, const ee_instruction& i) {\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    int32_t r0 = (int32_t)ee->r[s].s16[0] * (int32_t)ee->r[t].s16[0];\n    int32_t r1 = (int32_t)ee->r[s].s16[1] * (int32_t)ee->r[t].s16[1];\n    int32_t r2 = (int32_t)ee->r[s].s16[2] * (int32_t)ee->r[t].s16[2];\n    int32_t r3 = (int32_t)ee->r[s].s16[3] * (int32_t)ee->r[t].s16[3];\n    int32_t r4 = (int32_t)ee->r[s].s16[4] * (int32_t)ee->r[t].s16[4];\n    int32_t r5 = (int32_t)ee->r[s].s16[5] * (int32_t)ee->r[t].s16[5];\n    int32_t r6 = (int32_t)ee->r[s].s16[6] * (int32_t)ee->r[t].s16[6];\n    int32_t r7 = (int32_t)ee->r[s].s16[7] * (int32_t)ee->r[t].s16[7];\n\n    ee->r[d].u32[0] = ee->lo.u32[0] - r0;\n    ee->r[d].u32[1] = ee->hi.u32[0] - r2;\n    ee->r[d].u32[2] = ee->lo.u32[2] - r4;\n    ee->r[d].u32[3] = ee->hi.u32[2] - r6;\n    ee->lo.u32[0] = ee->r[d].u32[0];\n    ee->hi.u32[0] = ee->r[d].u32[1];\n    ee->lo.u32[2] = ee->r[d].u32[2];\n    ee->hi.u32[2] = ee->r[d].u32[3];\n\n    ee->lo.u32[1] = ee->lo.u32[1] - r1;\n    ee->lo.u32[3] = ee->lo.u32[3] - r5;\n    ee->hi.u32[1] = ee->hi.u32[1] - r3;\n    ee->hi.u32[3] = ee->hi.u32[3] - r7;\n}\nstatic inline void ee_i_pmsubw(struct ee_state* ee, const ee_instruction& i) {\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    // int64_t r0 = ee->r[s].s32[0] * ee->r[t].s32[0];\n    // int64_t r1 = ee->r[s].s32[2] * ee->r[t].s32[2];\n\n    // ee->r[d].u64[0] = ((ee->hi.u64[0] << 32) | (uint64_t)ee->lo.u32[0]) - r0;\n    // ee->r[d].u64[1] = ((ee->hi.u64[1] << 32) | (uint64_t)ee->lo.u32[2]) - r0;\n\n    // ee->lo.u64[0] = SE6432(ee->r[d].u32[0]);\n    // ee->hi.u64[0] = SE6432(ee->r[d].u32[1]);\n    // ee->lo.u64[1] = SE6432(ee->r[d].u32[2]);\n    // ee->hi.u64[1] = SE6432(ee->r[d].u32[3]);\n\n    int64_t op1 = (int64_t)ee->r[s].s32[0];\n    int64_t op2 = (int64_t)ee->r[t].s32[0];\n    int64_t result = op1 * op2;\n    int64_t result2 = ((int64_t)ee->hi.s32[0] << 32) - result;\n\n    result2 = (int32_t)(result2 / 4294967295);\n\n    ee->lo.s64[0] = ee->lo.s32[0] - (int32_t)(result & 0xffffffff);\n    ee->hi.s64[0] = (int32_t)result2;\n\n    op1 = (int64_t)ee->r[s].s32[2];\n    op2 = (int64_t)ee->r[t].s32[2];\n    result = op1 * op2;\n\n    result2 = ((int64_t)ee->hi.s32[2] << 32) - result;\n    result2 = (int32_t)(result2 / 4294967295);\n\n    ee->lo.s64[1] = ee->lo.s32[2] - (int32_t)(result & 0xffffffff);\n    ee->hi.s64[1] = (int32_t)result2;\n\n    ee->r[d].u32[0] = ee->lo.u32[0];\n    ee->r[d].u32[1] = ee->hi.u32[0];\n    ee->r[d].u32[2] = ee->lo.u32[2];\n    ee->r[d].u32[3] = ee->hi.u32[2];\n}\nstatic inline void ee_i_pmthi(struct ee_state* ee, const ee_instruction& i) {\n    ee->hi = ee->r[EE_D_RS];\n}\nstatic inline void ee_i_pmthl(struct ee_state* ee, const ee_instruction& i) {\n    int s = EE_D_RS;\n\n    ee->lo.u32[0] = ee->r[s].u32[0];\n    ee->lo.u32[2] = ee->r[s].u32[2];\n    ee->hi.u32[0] = ee->r[s].u32[1];\n    ee->hi.u32[2] = ee->r[s].u32[3];\n}\nstatic inline void ee_i_pmtlo(struct ee_state* ee, const ee_instruction& i) {\n    ee->lo = ee->r[EE_D_RS];\n}\nstatic inline void ee_i_pmulth(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->lo.u32[0] = (int32_t)(int16_t)ee->r[s].u16[0] * (int32_t)(int16_t)ee->r[t].u16[0];\n    ee->lo.u32[1] = (int32_t)(int16_t)ee->r[s].u16[1] * (int32_t)(int16_t)ee->r[t].u16[1];\n    ee->hi.u32[0] = (int32_t)(int16_t)ee->r[s].u16[2] * (int32_t)(int16_t)ee->r[t].u16[2];\n    ee->hi.u32[1] = (int32_t)(int16_t)ee->r[s].u16[3] * (int32_t)(int16_t)ee->r[t].u16[3];\n    ee->lo.u32[2] = (int32_t)(int16_t)ee->r[s].u16[4] * (int32_t)(int16_t)ee->r[t].u16[4];\n    ee->lo.u32[3] = (int32_t)(int16_t)ee->r[s].u16[5] * (int32_t)(int16_t)ee->r[t].u16[5];\n    ee->hi.u32[2] = (int32_t)(int16_t)ee->r[s].u16[6] * (int32_t)(int16_t)ee->r[t].u16[6];\n    ee->hi.u32[3] = (int32_t)(int16_t)ee->r[s].u16[7] * (int32_t)(int16_t)ee->r[t].u16[7];\n    ee->r[d].u32[0] = ee->lo.u32[0];\n    ee->r[d].u32[1] = ee->hi.u32[0];\n    ee->r[d].u32[2] = ee->lo.u32[2];\n    ee->r[d].u32[3] = ee->hi.u32[2];\n}\nstatic inline void ee_i_pmultuw(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u64[0] = (uint64_t)ee->r[s].u32[0] * (uint64_t)ee->r[t].u32[0];\n    ee->r[d].u64[1] = (uint64_t)ee->r[s].u32[2] * (uint64_t)ee->r[t].u32[2];\n\n    ee->lo.u64[0] = SE6432(ee->r[d].u32[0]);\n    ee->lo.u64[1] = SE6432(ee->r[d].u32[2]);\n    ee->hi.u64[0] = SE6432(ee->r[d].u32[1]);\n    ee->hi.u64[1] = SE6432(ee->r[d].u32[3]);\n}\nstatic inline void ee_i_pmultw(struct ee_state* ee, const ee_instruction& i) {\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = SE6432(ee->r[s].u32[0]) * SE6432(ee->r[t].u32[0]);\n    ee->r[d].u64[1] = SE6432(ee->r[s].u32[2]) * SE6432(ee->r[t].u32[2]);\n\n    ee->lo.u64[0] = SE6432(ee->r[d].u32[0]);\n    ee->lo.u64[1] = SE6432(ee->r[d].u32[2]);\n    ee->hi.u64[0] = SE6432(ee->r[d].u32[1]);\n    ee->hi.u64[1] = SE6432(ee->r[d].u32[3]);\n}\nstatic inline void ee_i_pnor(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = ~(rs.u64[0] | rt.u64[0]);\n    ee->r[d].u64[1] = ~(rs.u64[1] | rt.u64[1]);\n}\nstatic inline void ee_i_por(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = rs.u64[0] | rt.u64[0];\n    ee->r[d].u64[1] = rs.u64[1] | rt.u64[1];\n}\nstatic inline void ee_i_ppac5(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = ((rt.u32[0] & 0x000000f8) >> 3) |\n                      ((rt.u32[0] & 0x0000f800) >> 6) |\n                      ((rt.u32[0] & 0x00f80000) >> 9) |\n                      ((rt.u32[0] & 0x80000000) >> 16);\n    ee->r[d].u32[1] = ((rt.u32[1] & 0x000000f8) >> 3) |\n                      ((rt.u32[1] & 0x0000f800) >> 6) |\n                      ((rt.u32[1] & 0x00f80000) >> 9) |\n                      ((rt.u32[1] & 0x80000000) >> 16);\n    ee->r[d].u32[2] = ((rt.u32[2] & 0x000000f8) >> 3) |\n                      ((rt.u32[2] & 0x0000f800) >> 6) |\n                      ((rt.u32[2] & 0x00f80000) >> 9) |\n                      ((rt.u32[2] & 0x80000000) >> 16);\n    ee->r[d].u32[3] = ((rt.u32[3] & 0x000000f8) >> 3) |\n                      ((rt.u32[3] & 0x0000f800) >> 6) |\n                      ((rt.u32[3] & 0x00f80000) >> 9) |\n                      ((rt.u32[3] & 0x80000000) >> 16);\n}\nstatic inline void ee_i_ppacb(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u8[0 ] = rt.u8[ 0];\n    ee->r[d].u8[1 ] = rt.u8[ 2];\n    ee->r[d].u8[2 ] = rt.u8[ 4];\n    ee->r[d].u8[3 ] = rt.u8[ 6];\n    ee->r[d].u8[4 ] = rt.u8[ 8];\n    ee->r[d].u8[5 ] = rt.u8[10];\n    ee->r[d].u8[6 ] = rt.u8[12];\n    ee->r[d].u8[7 ] = rt.u8[14];\n    ee->r[d].u8[8 ] = rs.u8[ 0];\n    ee->r[d].u8[9 ] = rs.u8[ 2];\n    ee->r[d].u8[10] = rs.u8[ 4];\n    ee->r[d].u8[11] = rs.u8[ 6];\n    ee->r[d].u8[12] = rs.u8[ 8];\n    ee->r[d].u8[13] = rs.u8[10];\n    ee->r[d].u8[14] = rs.u8[12];\n    ee->r[d].u8[15] = rs.u8[14];\n}\nstatic inline void ee_i_ppach(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[0];\n    ee->r[d].u16[1] = rt.u16[2];\n    ee->r[d].u16[2] = rt.u16[4];\n    ee->r[d].u16[3] = rt.u16[6];\n    ee->r[d].u16[4] = rs.u16[0];\n    ee->r[d].u16[5] = rs.u16[2];\n    ee->r[d].u16[6] = rs.u16[4];\n    ee->r[d].u16[7] = rs.u16[6];\n}\nstatic inline void ee_i_ppacw(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = rt.u32[0];\n    ee->r[d].u32[1] = rt.u32[2];\n    ee->r[d].u32[2] = rs.u32[0];\n    ee->r[d].u32[3] = rs.u32[2];\n}\nstatic inline void ee_i_pref(struct ee_state* ee, const ee_instruction& i) {\n    // Does nothing\n}\nstatic inline void ee_i_prevh(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[3];\n    ee->r[d].u16[1] = rt.u16[2];\n    ee->r[d].u16[2] = rt.u16[1];\n    ee->r[d].u16[3] = rt.u16[0];\n    ee->r[d].u16[4] = rt.u16[7];\n    ee->r[d].u16[5] = rt.u16[6];\n    ee->r[d].u16[6] = rt.u16[5];\n    ee->r[d].u16[7] = rt.u16[4];\n}\nstatic inline void ee_i_prot3w(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = rt.u32[1];\n    ee->r[d].u32[1] = rt.u32[2];\n    ee->r[d].u32[2] = rt.u32[0];\n    ee->r[d].u32[3] = rt.u32[3];\n}\nstatic inline void ee_i_psllh(struct ee_state* ee, const ee_instruction& i) {\n    int sa = EE_D_SA & 0xf;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = ee->r[t].u16[0] << sa;\n    ee->r[d].u16[1] = ee->r[t].u16[1] << sa;\n    ee->r[d].u16[2] = ee->r[t].u16[2] << sa;\n    ee->r[d].u16[3] = ee->r[t].u16[3] << sa;\n    ee->r[d].u16[4] = ee->r[t].u16[4] << sa;\n    ee->r[d].u16[5] = ee->r[t].u16[5] << sa;\n    ee->r[d].u16[6] = ee->r[t].u16[6] << sa;\n    ee->r[d].u16[7] = ee->r[t].u16[7] << sa;\n}\nstatic inline void ee_i_psllvw(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n\n    ee->r[d].u64[0] = SE6432(ee->r[t].u32[0] << (ee->r[s].u32[0] & 31));\n    ee->r[d].u64[1] = SE6432(ee->r[t].u32[2] << (ee->r[s].u32[2] & 31));\n}\nstatic inline void ee_i_psllw(struct ee_state* ee, const ee_instruction& i) {\n    int sa = EE_D_SA;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = ee->r[t].u32[0] << sa;\n    ee->r[d].u32[1] = ee->r[t].u32[1] << sa;\n    ee->r[d].u32[2] = ee->r[t].u32[2] << sa;\n    ee->r[d].u32[3] = ee->r[t].u32[3] << sa;\n}\nstatic inline void ee_i_psrah(struct ee_state* ee, const ee_instruction& i) {\n    int sa = EE_D_SA & 0xf;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = ((int16_t)ee->r[t].u16[0]) >> sa;\n    ee->r[d].u16[1] = ((int16_t)ee->r[t].u16[1]) >> sa;\n    ee->r[d].u16[2] = ((int16_t)ee->r[t].u16[2]) >> sa;\n    ee->r[d].u16[3] = ((int16_t)ee->r[t].u16[3]) >> sa;\n    ee->r[d].u16[4] = ((int16_t)ee->r[t].u16[4]) >> sa;\n    ee->r[d].u16[5] = ((int16_t)ee->r[t].u16[5]) >> sa;\n    ee->r[d].u16[6] = ((int16_t)ee->r[t].u16[6]) >> sa;\n    ee->r[d].u16[7] = ((int16_t)ee->r[t].u16[7]) >> sa;\n}\nstatic inline void ee_i_psravw(struct ee_state* ee, const ee_instruction& i) {\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = SE6432((int32_t)ee->r[t].u32[0] >> (ee->r[s].u32[0] & 31));\n    ee->r[d].u64[1] = SE6432((int32_t)ee->r[t].u32[2] >> (ee->r[s].u32[2] & 31));\n}\nstatic inline void ee_i_psraw(struct ee_state* ee, const ee_instruction& i) {\n    int sa = EE_D_SA;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = ((int32_t)ee->r[t].u32[0]) >> sa;\n    ee->r[d].u32[1] = ((int32_t)ee->r[t].u32[1]) >> sa;\n    ee->r[d].u32[2] = ((int32_t)ee->r[t].u32[2]) >> sa;\n    ee->r[d].u32[3] = ((int32_t)ee->r[t].u32[3]) >> sa;\n}\nstatic inline void ee_i_psrlh(struct ee_state* ee, const ee_instruction& i) {\n    int sa = EE_D_SA & 0xf;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = ee->r[t].u16[0] >> sa;\n    ee->r[d].u16[1] = ee->r[t].u16[1] >> sa;\n    ee->r[d].u16[2] = ee->r[t].u16[2] >> sa;\n    ee->r[d].u16[3] = ee->r[t].u16[3] >> sa;\n    ee->r[d].u16[4] = ee->r[t].u16[4] >> sa;\n    ee->r[d].u16[5] = ee->r[t].u16[5] >> sa;\n    ee->r[d].u16[6] = ee->r[t].u16[6] >> sa;\n    ee->r[d].u16[7] = ee->r[t].u16[7] >> sa;\n}\nstatic inline void ee_i_psrlvw(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u64[0] = SE6432(ee->r[t].u32[0] >> (ee->r[s].u32[0] & 31));\n    ee->r[d].u64[1] = SE6432(ee->r[t].u32[2] >> (ee->r[s].u32[2] & 31));\n}\nstatic inline void ee_i_psrlw(struct ee_state* ee, const ee_instruction& i) {\n    int sa = EE_D_SA;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = ee->r[t].u32[0] >> sa;\n    ee->r[d].u32[1] = ee->r[t].u32[1] >> sa;\n    ee->r[d].u32[2] = ee->r[t].u32[2] >> sa;\n    ee->r[d].u32[3] = ee->r[t].u32[3] >> sa;\n}\nstatic inline void ee_i_psubb(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int s = EE_D_RS;\n    int d = EE_D_RD;\n\n    ee->r[d].u8[0 ] = ee->r[s].u8[0 ] - ee->r[t].u8[0 ];\n    ee->r[d].u8[1 ] = ee->r[s].u8[1 ] - ee->r[t].u8[1 ];\n    ee->r[d].u8[2 ] = ee->r[s].u8[2 ] - ee->r[t].u8[2 ];\n    ee->r[d].u8[3 ] = ee->r[s].u8[3 ] - ee->r[t].u8[3 ];\n    ee->r[d].u8[4 ] = ee->r[s].u8[4 ] - ee->r[t].u8[4 ];\n    ee->r[d].u8[5 ] = ee->r[s].u8[5 ] - ee->r[t].u8[5 ];\n    ee->r[d].u8[6 ] = ee->r[s].u8[6 ] - ee->r[t].u8[6 ];\n    ee->r[d].u8[7 ] = ee->r[s].u8[7 ] - ee->r[t].u8[7 ];\n    ee->r[d].u8[8 ] = ee->r[s].u8[8 ] - ee->r[t].u8[8 ];\n    ee->r[d].u8[9 ] = ee->r[s].u8[9 ] - ee->r[t].u8[9 ];\n    ee->r[d].u8[10] = ee->r[s].u8[10] - ee->r[t].u8[10];\n    ee->r[d].u8[11] = ee->r[s].u8[11] - ee->r[t].u8[11];\n    ee->r[d].u8[12] = ee->r[s].u8[12] - ee->r[t].u8[12];\n    ee->r[d].u8[13] = ee->r[s].u8[13] - ee->r[t].u8[13];\n    ee->r[d].u8[14] = ee->r[s].u8[14] - ee->r[t].u8[14];\n    ee->r[d].u8[15] = ee->r[s].u8[15] - ee->r[t].u8[15];\n}\nstatic inline void ee_i_psubh(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int s = EE_D_RS;\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = ee->r[s].u16[0] - ee->r[t].u16[0];\n    ee->r[d].u16[1] = ee->r[s].u16[1] - ee->r[t].u16[1];\n    ee->r[d].u16[2] = ee->r[s].u16[2] - ee->r[t].u16[2];\n    ee->r[d].u16[3] = ee->r[s].u16[3] - ee->r[t].u16[3];\n    ee->r[d].u16[4] = ee->r[s].u16[4] - ee->r[t].u16[4];\n    ee->r[d].u16[5] = ee->r[s].u16[5] - ee->r[t].u16[5];\n    ee->r[d].u16[6] = ee->r[s].u16[6] - ee->r[t].u16[6];\n    ee->r[d].u16[7] = ee->r[s].u16[7] - ee->r[t].u16[7];\n}\nstatic inline void ee_i_psubsb(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int32_t r0  = ((int32_t)(int8_t)ee->r[s].u8[0 ]) - ((int32_t)(int8_t)ee->r[t].u8[0 ]);\n    int32_t r1  = ((int32_t)(int8_t)ee->r[s].u8[1 ]) - ((int32_t)(int8_t)ee->r[t].u8[1 ]);\n    int32_t r2  = ((int32_t)(int8_t)ee->r[s].u8[2 ]) - ((int32_t)(int8_t)ee->r[t].u8[2 ]);\n    int32_t r3  = ((int32_t)(int8_t)ee->r[s].u8[3 ]) - ((int32_t)(int8_t)ee->r[t].u8[3 ]);\n    int32_t r4  = ((int32_t)(int8_t)ee->r[s].u8[4 ]) - ((int32_t)(int8_t)ee->r[t].u8[4 ]);\n    int32_t r5  = ((int32_t)(int8_t)ee->r[s].u8[5 ]) - ((int32_t)(int8_t)ee->r[t].u8[5 ]);\n    int32_t r6  = ((int32_t)(int8_t)ee->r[s].u8[6 ]) - ((int32_t)(int8_t)ee->r[t].u8[6 ]);\n    int32_t r7  = ((int32_t)(int8_t)ee->r[s].u8[7 ]) - ((int32_t)(int8_t)ee->r[t].u8[7 ]);\n    int32_t r8  = ((int32_t)(int8_t)ee->r[s].u8[8 ]) - ((int32_t)(int8_t)ee->r[t].u8[8 ]);\n    int32_t r9  = ((int32_t)(int8_t)ee->r[s].u8[9 ]) - ((int32_t)(int8_t)ee->r[t].u8[9 ]);\n    int32_t r10 = ((int32_t)(int8_t)ee->r[s].u8[10]) - ((int32_t)(int8_t)ee->r[t].u8[10]);\n    int32_t r11 = ((int32_t)(int8_t)ee->r[s].u8[11]) - ((int32_t)(int8_t)ee->r[t].u8[11]);\n    int32_t r12 = ((int32_t)(int8_t)ee->r[s].u8[12]) - ((int32_t)(int8_t)ee->r[t].u8[12]);\n    int32_t r13 = ((int32_t)(int8_t)ee->r[s].u8[13]) - ((int32_t)(int8_t)ee->r[t].u8[13]);\n    int32_t r14 = ((int32_t)(int8_t)ee->r[s].u8[14]) - ((int32_t)(int8_t)ee->r[t].u8[14]);\n    int32_t r15 = ((int32_t)(int8_t)ee->r[s].u8[15]) - ((int32_t)(int8_t)ee->r[t].u8[15]);\n\n    ee->r[d].u8[0 ] = (r0 >= 0x7f) ? 0x7f : ((r0 < -0x80) ? 0x80 : r0);\n    ee->r[d].u8[1 ] = (r1 >= 0x7f) ? 0x7f : ((r1 < -0x80) ? 0x80 : r1);\n    ee->r[d].u8[2 ] = (r2 >= 0x7f) ? 0x7f : ((r2 < -0x80) ? 0x80 : r2);\n    ee->r[d].u8[3 ] = (r3 >= 0x7f) ? 0x7f : ((r3 < -0x80) ? 0x80 : r3);\n    ee->r[d].u8[4 ] = (r4 >= 0x7f) ? 0x7f : ((r4 < -0x80) ? 0x80 : r4);\n    ee->r[d].u8[5 ] = (r5 >= 0x7f) ? 0x7f : ((r5 < -0x80) ? 0x80 : r5);\n    ee->r[d].u8[6 ] = (r6 >= 0x7f) ? 0x7f : ((r6 < -0x80) ? 0x80 : r6);\n    ee->r[d].u8[7 ] = (r7 >= 0x7f) ? 0x7f : ((r7 < -0x80) ? 0x80 : r7);\n    ee->r[d].u8[8 ] = (r8 >= 0x7f) ? 0x7f : ((r8 < -0x80) ? 0x80 : r8);\n    ee->r[d].u8[9 ] = (r9 >= 0x7f) ? 0x7f : ((r9 < -0x80) ? 0x80 : r9);\n    ee->r[d].u8[10] = (r10 >= 0x7f) ? 0x7f : ((r10 < -0x80) ? 0x80 : r10);\n    ee->r[d].u8[11] = (r11 >= 0x7f) ? 0x7f : ((r11 < -0x80) ? 0x80 : r11);\n    ee->r[d].u8[12] = (r12 >= 0x7f) ? 0x7f : ((r12 < -0x80) ? 0x80 : r12);\n    ee->r[d].u8[13] = (r13 >= 0x7f) ? 0x7f : ((r13 < -0x80) ? 0x80 : r13);\n    ee->r[d].u8[14] = (r14 >= 0x7f) ? 0x7f : ((r14 < -0x80) ? 0x80 : r14);\n    ee->r[d].u8[15] = (r15 >= 0x7f) ? 0x7f : ((r15 < -0x80) ? 0x80 : r15);\n}\nstatic inline void ee_i_psubsh(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int32_t r0 = SE3216(ee->r[s].u16[0]) - SE3216(ee->r[t].u16[0]);\n    int32_t r1 = SE3216(ee->r[s].u16[1]) - SE3216(ee->r[t].u16[1]);\n    int32_t r2 = SE3216(ee->r[s].u16[2]) - SE3216(ee->r[t].u16[2]);\n    int32_t r3 = SE3216(ee->r[s].u16[3]) - SE3216(ee->r[t].u16[3]);\n    int32_t r4 = SE3216(ee->r[s].u16[4]) - SE3216(ee->r[t].u16[4]);\n    int32_t r5 = SE3216(ee->r[s].u16[5]) - SE3216(ee->r[t].u16[5]);\n    int32_t r6 = SE3216(ee->r[s].u16[6]) - SE3216(ee->r[t].u16[6]);\n    int32_t r7 = SE3216(ee->r[s].u16[7]) - SE3216(ee->r[t].u16[7]);\n\n    ee->r[d].u16[0] = (r0 >= 0x7fff) ? 0x7fff : ((r0 < -0x8000) ? 0x8000 : r0);\n    ee->r[d].u16[1] = (r1 >= 0x7fff) ? 0x7fff : ((r1 < -0x8000) ? 0x8000 : r1);\n    ee->r[d].u16[2] = (r2 >= 0x7fff) ? 0x7fff : ((r2 < -0x8000) ? 0x8000 : r2);\n    ee->r[d].u16[3] = (r3 >= 0x7fff) ? 0x7fff : ((r3 < -0x8000) ? 0x8000 : r3);\n    ee->r[d].u16[4] = (r4 >= 0x7fff) ? 0x7fff : ((r4 < -0x8000) ? 0x8000 : r4);\n    ee->r[d].u16[5] = (r5 >= 0x7fff) ? 0x7fff : ((r5 < -0x8000) ? 0x8000 : r5);\n    ee->r[d].u16[6] = (r6 >= 0x7fff) ? 0x7fff : ((r6 < -0x8000) ? 0x8000 : r6);\n    ee->r[d].u16[7] = (r7 >= 0x7fff) ? 0x7fff : ((r7 < -0x8000) ? 0x8000 : r7);\n}\nstatic inline void ee_i_psubsw(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int64_t r0 = SE6432(ee->r[s].u32[0]) - SE6432(ee->r[t].u32[0]);\n    int64_t r1 = SE6432(ee->r[s].u32[1]) - SE6432(ee->r[t].u32[1]);\n    int64_t r2 = SE6432(ee->r[s].u32[2]) - SE6432(ee->r[t].u32[2]);\n    int64_t r3 = SE6432(ee->r[s].u32[3]) - SE6432(ee->r[t].u32[3]);\n\n    ee->r[d].u32[0] = (r0 >= 0x7fffffff) ? 0x7fffffff : ((r0 < (int32_t)0x80000000) ? 0x80000000 : r0);\n    ee->r[d].u32[1] = (r1 >= 0x7fffffff) ? 0x7fffffff : ((r1 < (int32_t)0x80000000) ? 0x80000000 : r1);\n    ee->r[d].u32[2] = (r2 >= 0x7fffffff) ? 0x7fffffff : ((r2 < (int32_t)0x80000000) ? 0x80000000 : r2);\n    ee->r[d].u32[3] = (r3 >= 0x7fffffff) ? 0x7fffffff : ((r3 < (int32_t)0x80000000) ? 0x80000000 : r3);\n}\nstatic inline void ee_i_psubub(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int32_t r0  = ((int32_t)ee->r[s].u8[0 ]) - ((int32_t)ee->r[t].u8[0 ]);\n    int32_t r1  = ((int32_t)ee->r[s].u8[1 ]) - ((int32_t)ee->r[t].u8[1 ]);\n    int32_t r2  = ((int32_t)ee->r[s].u8[2 ]) - ((int32_t)ee->r[t].u8[2 ]);\n    int32_t r3  = ((int32_t)ee->r[s].u8[3 ]) - ((int32_t)ee->r[t].u8[3 ]);\n    int32_t r4  = ((int32_t)ee->r[s].u8[4 ]) - ((int32_t)ee->r[t].u8[4 ]);\n    int32_t r5  = ((int32_t)ee->r[s].u8[5 ]) - ((int32_t)ee->r[t].u8[5 ]);\n    int32_t r6  = ((int32_t)ee->r[s].u8[6 ]) - ((int32_t)ee->r[t].u8[6 ]);\n    int32_t r7  = ((int32_t)ee->r[s].u8[7 ]) - ((int32_t)ee->r[t].u8[7 ]);\n    int32_t r8  = ((int32_t)ee->r[s].u8[8 ]) - ((int32_t)ee->r[t].u8[8 ]);\n    int32_t r9  = ((int32_t)ee->r[s].u8[9 ]) - ((int32_t)ee->r[t].u8[9 ]);\n    int32_t r10 = ((int32_t)ee->r[s].u8[10]) - ((int32_t)ee->r[t].u8[10]);\n    int32_t r11 = ((int32_t)ee->r[s].u8[11]) - ((int32_t)ee->r[t].u8[11]);\n    int32_t r12 = ((int32_t)ee->r[s].u8[12]) - ((int32_t)ee->r[t].u8[12]);\n    int32_t r13 = ((int32_t)ee->r[s].u8[13]) - ((int32_t)ee->r[t].u8[13]);\n    int32_t r14 = ((int32_t)ee->r[s].u8[14]) - ((int32_t)ee->r[t].u8[14]);\n    int32_t r15 = ((int32_t)ee->r[s].u8[15]) - ((int32_t)ee->r[t].u8[15]);\n\n    ee->r[d].u8[0 ] = (r0 < 0) ? 0 : r0;\n    ee->r[d].u8[1 ] = (r1 < 0) ? 0 : r1;\n    ee->r[d].u8[2 ] = (r2 < 0) ? 0 : r2;\n    ee->r[d].u8[3 ] = (r3 < 0) ? 0 : r3;\n    ee->r[d].u8[4 ] = (r4 < 0) ? 0 : r4;\n    ee->r[d].u8[5 ] = (r5 < 0) ? 0 : r5;\n    ee->r[d].u8[6 ] = (r6 < 0) ? 0 : r6;\n    ee->r[d].u8[7 ] = (r7 < 0) ? 0 : r7;\n    ee->r[d].u8[8 ] = (r8 < 0) ? 0 : r8;\n    ee->r[d].u8[9 ] = (r9 < 0) ? 0 : r9;\n    ee->r[d].u8[10] = (r10 < 0) ? 0 : r10;\n    ee->r[d].u8[11] = (r11 < 0) ? 0 : r11;\n    ee->r[d].u8[12] = (r12 < 0) ? 0 : r12;\n    ee->r[d].u8[13] = (r13 < 0) ? 0 : r13;\n    ee->r[d].u8[14] = (r14 < 0) ? 0 : r14;\n    ee->r[d].u8[15] = (r15 < 0) ? 0 : r15;\n}\nstatic inline void ee_i_psubuh(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int32_t r0 = (int32_t)(ee->r[s].u16[0]) - (int32_t)(ee->r[t].u16[0]);\n    int32_t r1 = (int32_t)(ee->r[s].u16[1]) - (int32_t)(ee->r[t].u16[1]);\n    int32_t r2 = (int32_t)(ee->r[s].u16[2]) - (int32_t)(ee->r[t].u16[2]);\n    int32_t r3 = (int32_t)(ee->r[s].u16[3]) - (int32_t)(ee->r[t].u16[3]);\n    int32_t r4 = (int32_t)(ee->r[s].u16[4]) - (int32_t)(ee->r[t].u16[4]);\n    int32_t r5 = (int32_t)(ee->r[s].u16[5]) - (int32_t)(ee->r[t].u16[5]);\n    int32_t r6 = (int32_t)(ee->r[s].u16[6]) - (int32_t)(ee->r[t].u16[6]);\n    int32_t r7 = (int32_t)(ee->r[s].u16[7]) - (int32_t)(ee->r[t].u16[7]);\n\n    ee->r[d].u16[0] = (r0 < 0) ? 0 : r0;\n    ee->r[d].u16[1] = (r1 < 0) ? 0 : r1;\n    ee->r[d].u16[2] = (r2 < 0) ? 0 : r2;\n    ee->r[d].u16[3] = (r3 < 0) ? 0 : r3;\n    ee->r[d].u16[4] = (r4 < 0) ? 0 : r4;\n    ee->r[d].u16[5] = (r5 < 0) ? 0 : r5;\n    ee->r[d].u16[6] = (r6 < 0) ? 0 : r6;\n    ee->r[d].u16[7] = (r7 < 0) ? 0 : r7;\n}\nstatic inline void ee_i_psubuw(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int64_t r0 = (int64_t)ee->r[s].u32[0] - (int64_t)ee->r[t].u32[0];\n    int64_t r1 = (int64_t)ee->r[s].u32[1] - (int64_t)ee->r[t].u32[1];\n    int64_t r2 = (int64_t)ee->r[s].u32[2] - (int64_t)ee->r[t].u32[2];\n    int64_t r3 = (int64_t)ee->r[s].u32[3] - (int64_t)ee->r[t].u32[3];\n\n    ee->r[d].u32[0] = (r0 < 0) ? 0 : r0;\n    ee->r[d].u32[1] = (r1 < 0) ? 0 : r1;\n    ee->r[d].u32[2] = (r2 < 0) ? 0 : r2;\n    ee->r[d].u32[3] = (r3 < 0) ? 0 : r3;\n}\nstatic inline void ee_i_psubw(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u32[0] = ee->r[s].u32[0] - ee->r[t].u32[0];\n    ee->r[d].u32[1] = ee->r[s].u32[1] - ee->r[t].u32[1];\n    ee->r[d].u32[2] = ee->r[s].u32[2] - ee->r[t].u32[2];\n    ee->r[d].u32[3] = ee->r[s].u32[3] - ee->r[t].u32[3];\n}\nstatic inline void ee_i_pxor(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = rs.u64[0] ^ rt.u64[0];\n    ee->r[d].u64[1] = rs.u64[1] ^ rt.u64[1];\n}\nstatic inline void ee_i_qfsrv(struct ee_state* ee, const ee_instruction& i) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    int shift = ee->sa * 8;\n\n    uint128_t v;\n\n    if (!shift) {\n        v = rt;\n    } else {\n        if (shift < 64) {\n            v.u64[0] = rt.u64[0] >> shift;\n            v.u64[1] = rt.u64[1] >> shift;\n            v.u64[0] |= rt.u64[1] << (64 - shift);\n            v.u64[1] |= rs.u64[0] << (64 - shift);\n        } else {\n            v.u64[0] = rt.u64[1] >> (shift - 64);\n            v.u64[1] = rs.u64[0] >> (shift - 64);\n\n            if (shift != 64) {\n                v.u64[0] |= rs.u64[0] << (128u - shift);\n                v.u64[1] |= rs.u64[1] << (128u - shift);\n            }\n        }\n    }\n\n    ee->r[d] = v;\n}\nstatic inline void ee_i_qmfc2(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[t].u64[0] = ee->vu0->vf[d].u64[0];\n    ee->r[t].u64[1] = ee->vu0->vf[d].u64[1];\n}\nstatic inline void ee_i_qmtc2(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    if (!d) return;\n\n    ee->vu0->vf[d].u128 = ee->r[t];\n}\nstatic inline void ee_i_rsqrts(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int d = EE_D_FD;\n\n    ee->fcr &= ~(FPU_FLG_I | FPU_FLG_D);\n\n    if ((ee->f[t].u32 & 0x7f800000) == 0) {\n        ee->fcr |= FPU_FLG_D | FPU_FLG_SD;\n        ee->f[d].u32 = (ee->f[t].u32 & 0x80000000) | 0x7f7fffff;\n        \n        return;\n    } else if (ee->f[t].u32 & 0x80000000) {\n        ee->fcr |= FPU_FLG_I | FPU_FLG_SI;\n\n        ee->f[d].f = EE_FS / sqrtf(fabsf(fpu_cvtf(ee->f[t].f)));\n    } else {\n        ee->f[d].f = EE_FS / sqrtf(fpu_cvtf(ee->f[t].f));\n    }\n\n    if (fpu_check_overflow_no_flags(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow_no_flags(ee, &ee->f[d]);\n}\nstatic inline void ee_i_sb(struct ee_state* ee, const ee_instruction& i) {\n    bus_write8(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT);\n}\nstatic inline void ee_i_sd(struct ee_state* ee, const ee_instruction& i) {\n    bus_write64(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT);\n}\nstatic inline void ee_i_sdl(struct ee_state* ee, const ee_instruction& i) {\n    static const uint8_t sdl_shift[8] = { 56, 48, 40, 32, 24, 16, 8, 0 };\n    static const uint64_t sdl_mask[8] = {\n        0xffffffffffffff00ULL, 0xffffffffffff0000ULL, 0xffffffffff000000ULL, 0xffffffff00000000ULL,\n        0xffffff0000000000ULL, 0xffff000000000000ULL, 0xff00000000000000ULL, 0x0000000000000000ULL\n    };\n\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t shift = addr & 7;\n    uint64_t data = bus_read64(ee, addr & ~7);\n\n    bus_write64(ee, addr & ~7, (EE_RT >> sdl_shift[shift]) | (data & sdl_mask[shift]));\n}\nstatic inline void ee_i_sdr(struct ee_state* ee, const ee_instruction& i) {\n    static const uint8_t sdr_shift[8] = { 0, 8, 16, 24, 32, 40, 48, 56 };\n    static const uint64_t sdr_mask[8] = {\n        0x0000000000000000ULL, 0x00000000000000ffULL, 0x000000000000ffffULL, 0x0000000000ffffffULL,\n        0x00000000ffffffffULL, 0x000000ffffffffffULL, 0x0000ffffffffffffULL, 0x00ffffffffffffffULL\n    };\n\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t shift = addr & 7;\n    uint64_t data = bus_read64(ee, addr & ~7);\n\n    bus_write64(ee, addr & ~7, (EE_RT << sdr_shift[shift]) | (data & sdr_mask[shift]));\n}\nstatic inline void ee_i_sh(struct ee_state* ee, const ee_instruction& i) {\n    bus_write16(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT);\n}\nstatic inline void ee_i_sll(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = SE6432(EE_RT32 << EE_D_SA);\n}\nstatic inline void ee_i_sllv(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = SE6432(EE_RT32 << (EE_RS & 0x1f));\n}\nstatic inline void ee_i_slt(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = (int64_t)EE_RS < (int64_t)EE_RT;\n}\nstatic inline void ee_i_slti(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = ((int64_t)EE_RS) < SE6416(EE_D_I16);\n}\nstatic inline void ee_i_sltiu(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = EE_RS < (uint64_t)(SE6416(EE_D_I16));\n}\nstatic inline void ee_i_sltu(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_RS < EE_RT;\n}\nstatic inline void ee_i_sq(struct ee_state* ee, const ee_instruction& i) {\n    bus_write128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf, ee->r[EE_D_RT]);\n}\nstatic inline void ee_i_sqc2(struct ee_state* ee, const ee_instruction& i) {\n    bus_write128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf, ee->vu0->vf[EE_D_RT].u128);\n}\nstatic inline void ee_i_sqrts(struct ee_state* ee, const ee_instruction& i) {\n    int t = EE_D_RT;\n    int d = EE_D_FD;\n\n    ee->fcr &= ~(FPU_FLG_I | FPU_FLG_D);\n\n    if ((ee->f[t].u32 & 0x7f800000) == 0) {\n        ee->f[d].u32 = ee->f[t].u32 & 0x80000000;\n    } else if (ee->f[t].u32 & 0x80000000) {\n        ee->fcr |= FPU_FLG_I | FPU_FLG_SI;\n\n        ee->f[d].f = sqrtf(fabsf(fpu_cvtf(ee->f[t].f)));\n    } else {\n        ee->f[d].f = sqrtf(fpu_cvtf(ee->f[t].f));\n    }\n}\nstatic inline void ee_i_sra(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = SE6432(((int32_t)EE_RT32) >> EE_D_SA);\n}\nstatic inline void ee_i_srav(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = SE6432(((int32_t)EE_RT32) >> (EE_RS & 0x1f));\n}\nstatic inline void ee_i_srl(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = SE6432(EE_RT32 >> EE_D_SA);\n}\nstatic inline void ee_i_srlv(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = SE6432(EE_RT32 >> (EE_RS & 0x1f));\n}\nstatic inline void ee_i_sub(struct ee_state* ee, const ee_instruction& i) {\n    int32_t r;\n\n    int o = __builtin_ssub_overflow(EE_RS32, EE_RT32, &r);\n\n    if (o) {\n        ee_exception_level1(ee, CAUSE_EXC1_OV);\n    } else {\n        EE_RD = SE6432(r);\n    }\n}\nstatic inline void ee_i_subas(struct ee_state* ee, const ee_instruction& i) {\n    ee->a.f = EE_FS - EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->a))\n        return;\n\n    fpu_check_underflow(ee, &ee->a);\n}\nstatic inline void ee_i_subs(struct ee_state* ee, const ee_instruction& i) {\n    int d = EE_D_FD;\n\n    ee->f[d].f = EE_FS - EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow(ee, &ee->f[d]);\n}\nstatic inline void ee_i_subu(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = SE6432(EE_RS - EE_RT);\n}\nstatic inline void ee_i_sw(struct ee_state* ee, const ee_instruction& i) {\n    bus_write32(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT32);\n}\nstatic inline void ee_i_swc1(struct ee_state* ee, const ee_instruction& i) {\n    bus_write32(ee, EE_RS32 + SE3216(EE_D_I16), EE_FT32);\n}\nstatic inline void ee_i_swl(struct ee_state* ee, const ee_instruction& i) {\n    static const uint32_t swl_mask[4] = { 0xffffff00, 0xffff0000, 0xff000000, 0x00000000 };\n    static const uint8_t swl_shift[4] = { 24, 16, 8, 0 };\n\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t mem = bus_read32(ee, addr & ~3);\n\n    int shift = addr & 3;\n\n    bus_write32(ee, addr & ~3, (EE_RT32 >> swl_shift[shift] | (mem & swl_mask[shift])));\n\n    // printf(\"swl mem=%08x reg=%016lx addr=%08x shift=%d rs=%08x i16=%04x\\n\", mem, ee->r[EE_D_RT].u64[0], addr, shift, EE_RS32, EE_D_I16);\n}\nstatic inline void ee_i_swr(struct ee_state* ee, const ee_instruction& i) {\n    static const uint32_t swr_mask[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff };\n    static const uint8_t swr_shift[4] = { 0, 8, 16, 24 };\n\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t mem = bus_read32(ee, addr & ~3);\n\n    int shift = addr & 3;\n\n    bus_write32(ee, addr & ~3, (EE_RT32 << swr_shift[shift]) | (mem & swr_mask[shift]));\n\n    // printf(\"swl mem=%08x reg=%016lx addr=%08x shift=%d rs=%08x i16=%04x\\n\", mem, ee->r[EE_D_RT].u64[0], addr, shift, EE_RS32, EE_D_I16);\n}\nstatic inline void ee_i_sync(struct ee_state* ee, const ee_instruction& i) {\n    /* Do nothing */\n}\n\n// #include \"syscall.h\"\n\nstatic inline void ee_get_thread_list(struct ee_state* ee) {\n    if (ee->thread_list_base) return;\n\n    uint32_t offset = 0;\n\n    while (offset < 0x5000) {\n        uint32_t inst[3];\n        uint32_t addr = 0x80000000 + offset;\n\n        inst[0] = bus_read32(ee, addr);\n        inst[1] = bus_read32(ee, addr + 4);\n        inst[2] = bus_read32(ee, addr + 8);\n\n        if (inst[0] == 0xac420000 && inst[1] == 0 && inst[2] == 0) {\n            uint32_t op = bus_read32(ee, 0x80000000 + offset + (4 * 6));\n\n            ee->thread_list_base = 0x80010000 + (op & 0xffff) - 8;\n\n            printf(\"ee: Found thread list base at %08x\\n\", ee->thread_list_base);\n\n            break;\n        }\n\n        offset += 4;\n    }\n}\n\nstatic inline void ee_i_syscall(struct ee_state* ee, const ee_instruction& i) {\n    uint32_t id = ee->r[3].ul64;\n\n    if (id & 0x80000000) {\n        id = (~id) + 1;\n    }\n\n    // printf(\"ee: %s (%d, %08x) a0=%08x (%d)\\n\", ee_get_syscall(n), ee->r[3].ul32, ee->r[3].ul32, ee->r[4].ul32, ee->r[4].ul32);\n\n    switch (id) {\n        // ChangeThreadPriority\n        case 0x29: {\n            ee_get_thread_list(ee);\n        } break;\n\n        // SetOsdConfigParam\n        case 0x4a: {\n            uint32_t ptr = ee->r[4].u32[0];\n            uint32_t value = bus_read32(ee, ptr);\n\n            memcpy(&ee->osd_config, &value, 4);\n\n            return;\n        } break;\n\n        // GetOsdConfigParam\n        case 0x4b: {\n            uint32_t ptr = ee->r[4].u32[0];\n            uint32_t value;\n\n            memcpy(&value, &ee->osd_config, 4);\n\n            bus_write32(ee, ptr, value);\n\n            return;\n        } break;\n\n        // FlushCache\n        case 0x64: {\n            // printf(\"ee: Flushed %zd blocks\\n\", ee->block_cache.size());\n\n            ee->block_cache.clear();\n        } break;\n    }\n\n    ee_exception_level1(ee, CAUSE_EXC1_SYS);\n}\nstatic inline void ee_i_teq(struct ee_state* ee, const ee_instruction& i) {\n    if (EE_RS == EE_RT) ee_exception_level1(ee, CAUSE_EXC1_TR);\n}\nstatic inline void ee_i_teqi(struct ee_state* ee, const ee_instruction& i) {\n    if (EE_RS == SE6416(EE_D_I16)) ee_exception_level1(ee, CAUSE_EXC1_TR);\n}\nstatic inline void ee_i_tge(struct ee_state* ee, const ee_instruction& i) {\n    if (EE_RS >= EE_RT) ee_exception_level1(ee, CAUSE_EXC1_TR);\n}\nstatic inline void ee_i_tgei(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, \"ee: tgei unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tgeiu(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, \"ee: tgeiu unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tgeu(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, \"ee: tgeu unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tlbp(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, \"ee: tlbp unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tlbr(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, \"ee: tlbr unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tlbwi(struct ee_state* ee, const ee_instruction& i) {\n    struct ee_vtlb_entry* entry = &ee->vtlb[ee->index & 0x3f];\n\n    entry->asid = ee->entryhi & 0xff;\n    entry->pfn0 = (ee->entrylo0 & 0x3ffffc0) << 6;\n    entry->pfn1 = (ee->entrylo1 & 0x3ffffc0) << 6;\n    entry->mask = ee->pagemask & 0x1ffe000;\n    entry->vpn2 = ee->entryhi & 0xffffe000;\n    entry->v0 = (ee->entrylo0 >> 1) & 1;\n    entry->d0 = (ee->entrylo0 >> 2) & 1;\n    entry->c0 = (ee->entrylo0 >> 3) & 7;\n    entry->v1 = (ee->entrylo1 >> 1) & 1;\n    entry->d1 = (ee->entrylo1 >> 2) & 1;\n    entry->c1 = (ee->entrylo1 >> 3) & 7;\n    entry->s = (ee->entrylo0 >> 31) & 1;\n    entry->g = (ee->entrylo0 & 1) && (ee->entrylo1 & 1);\n\n    printf(\"ee: Index=%d vpn2=%08x even={pfn=%08x v=%d d=%d} odd={pfn=%08x v=%d d=%d} mask=%08x s=%d g=%d\\n\",\n        ee->index,\n        entry->vpn2,\n        entry->pfn0,\n        entry->v0,\n        entry->d0,\n        entry->pfn1,\n        entry->v1,\n        entry->d1,\n        entry->mask,\n        entry->s,\n        entry->g\n    );\n}\nstatic inline void ee_i_tlbwr(struct ee_state* ee, const ee_instruction& i) {\n    int index = (ee->count % (48 - ee->wired)) + ee->wired;\n\n    struct ee_vtlb_entry* entry = &ee->vtlb[index];\n\n    entry->asid = ee->entryhi & 0xff;\n    entry->pfn0 = (ee->entrylo0 & 0x3ffffc0) << 6;\n    entry->pfn1 = (ee->entrylo1 & 0x3ffffc0) << 6;\n    entry->mask = ee->pagemask & 0x1ffe000;\n    entry->vpn2 = ee->entryhi & 0xffffe000;\n    entry->v0 = (ee->entrylo0 >> 1) & 1;\n    entry->d0 = (ee->entrylo0 >> 2) & 1;\n    entry->c0 = (ee->entrylo0 >> 3) & 7;\n    entry->v1 = (ee->entrylo1 >> 1) & 1;\n    entry->d1 = (ee->entrylo1 >> 2) & 1;\n    entry->c1 = (ee->entrylo1 >> 3) & 7;\n    entry->s = (ee->entrylo0 >> 31) & 1;\n    entry->g = (ee->entrylo0 & 1) && (ee->entrylo1 & 1);\n\n    printf(\"ee: tlbwr Index=%d vpn2=%08x even={pfn=%08x v=%d d=%d} odd={pfn=%08x v=%d d=%d} mask=%08x s=%d g=%d\\n\",\n        index,\n        entry->vpn2,\n        entry->pfn0,\n        entry->v0,\n        entry->d0,\n        entry->pfn1,\n        entry->v1,\n        entry->d1,\n        entry->mask,\n        entry->s,\n        entry->g\n    );\n}\nstatic inline void ee_i_tlt(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, \"ee: tlt unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tlti(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, \"ee: tlti unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tltiu(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, \"ee: tltiu unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tltu(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, \"ee: tltu unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tne(struct ee_state* ee, const ee_instruction& i) {\n    if (EE_RS != EE_RT) ee_exception_level1(ee, CAUSE_EXC1_TR);\n}\nstatic inline void ee_i_tnei(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, \"ee: tnei unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_vabs(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(abs) }\nstatic inline void ee_i_vadd(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(add) }\nstatic inline void ee_i_vadda(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(adda) }\nstatic inline void ee_i_vaddai(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addai) }\nstatic inline void ee_i_vaddaq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addaq) }\nstatic inline void ee_i_vaddaw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addaw) }\nstatic inline void ee_i_vaddax(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addax) }\nstatic inline void ee_i_vadday(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(adday) }\nstatic inline void ee_i_vaddaz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addaz) }\nstatic inline void ee_i_vaddi(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addi) }\nstatic inline void ee_i_vaddq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addq) }\nstatic inline void ee_i_vaddw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addw) }\nstatic inline void ee_i_vaddx(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addx) }\nstatic inline void ee_i_vaddy(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addy) }\nstatic inline void ee_i_vaddz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addz) }\nstatic inline void ee_i_vcallms(struct ee_state* ee, const ee_instruction& i) {\n    vu_execute_program(ee->vu0, EE_D_I15);\n}\nstatic inline void ee_i_vcallmsr(struct ee_state* ee, const ee_instruction& i) {\n    vu_execute_program(ee->vu0, ee->vu0->cmsar0);\n}\nstatic inline void ee_i_vclipw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER(clip) }\nstatic inline void ee_i_vdiv(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(div) }\nstatic inline void ee_i_vftoi0(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(ftoi0) }\nstatic inline void ee_i_vftoi12(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(ftoi12) }\nstatic inline void ee_i_vftoi15(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(ftoi15) }\nstatic inline void ee_i_vftoi4(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(ftoi4) }\nstatic inline void ee_i_viadd(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(iadd) }\nstatic inline void ee_i_viaddi(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(iaddi) }\nstatic inline void ee_i_viand(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(iand) }\nstatic inline void ee_i_vilwr(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(ilwr) }\nstatic inline void ee_i_vior(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(ior) }\nstatic inline void ee_i_visub(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(isub) }\nstatic inline void ee_i_viswr(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(iswr) }\nstatic inline void ee_i_vitof0(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(itof0) }\nstatic inline void ee_i_vitof12(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(itof12) }\nstatic inline void ee_i_vitof15(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(itof15) }\nstatic inline void ee_i_vitof4(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(itof4) }\nstatic inline void ee_i_vlqd(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(lqd) }\nstatic inline void ee_i_vlqi(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(lqi) }\nstatic inline void ee_i_vmadd(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(madd) }\nstatic inline void ee_i_vmadda(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(madda) }\nstatic inline void ee_i_vmaddai(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddai) }\nstatic inline void ee_i_vmaddaq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddaq) }\nstatic inline void ee_i_vmaddaw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddaw) }\nstatic inline void ee_i_vmaddax(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddax) }\nstatic inline void ee_i_vmadday(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(madday) }\nstatic inline void ee_i_vmaddaz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddaz) }\nstatic inline void ee_i_vmaddi(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddi) }\nstatic inline void ee_i_vmaddq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddq) }\nstatic inline void ee_i_vmaddw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddw) }\nstatic inline void ee_i_vmaddx(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddx) }\nstatic inline void ee_i_vmaddy(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddy) }\nstatic inline void ee_i_vmaddz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddz) }\nstatic inline void ee_i_vmax(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(max) }\nstatic inline void ee_i_vmaxi(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maxi) }\nstatic inline void ee_i_vmaxw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maxw) }\nstatic inline void ee_i_vmaxx(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maxx) }\nstatic inline void ee_i_vmaxy(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maxy) }\nstatic inline void ee_i_vmaxz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maxz) }\nstatic inline void ee_i_vmfir(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mfir) }\nstatic inline void ee_i_vmini(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mini) }\nstatic inline void ee_i_vminii(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(minii) }\nstatic inline void ee_i_vminiw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(miniw) }\nstatic inline void ee_i_vminix(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(minix) }\nstatic inline void ee_i_vminiy(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(miniy) }\nstatic inline void ee_i_vminiz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(miniz) }\nstatic inline void ee_i_vmove(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(move) }\nstatic inline void ee_i_vmr32(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(mr32) }\nstatic inline void ee_i_vmsub(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msub) }\nstatic inline void ee_i_vmsuba(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msuba) }\nstatic inline void ee_i_vmsubai(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubai) }\nstatic inline void ee_i_vmsubaq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubaq) }\nstatic inline void ee_i_vmsubaw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubaw) }\nstatic inline void ee_i_vmsubax(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubax) }\nstatic inline void ee_i_vmsubay(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubay) }\nstatic inline void ee_i_vmsubaz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubaz) }\nstatic inline void ee_i_vmsubi(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubi) }\nstatic inline void ee_i_vmsubq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubq) }\nstatic inline void ee_i_vmsubw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubw) }\nstatic inline void ee_i_vmsubx(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubx) }\nstatic inline void ee_i_vmsuby(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msuby) }\nstatic inline void ee_i_vmsubz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubz) }\nstatic inline void ee_i_vmtir(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(mtir) }\nstatic inline void ee_i_vmul(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mul) }\nstatic inline void ee_i_vmula(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mula) }\nstatic inline void ee_i_vmulai(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulai) }\nstatic inline void ee_i_vmulaq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulaq) }\nstatic inline void ee_i_vmulaw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulaw) }\nstatic inline void ee_i_vmulax(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulax) }\nstatic inline void ee_i_vmulay(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulay) }\nstatic inline void ee_i_vmulaz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulaz) }\nstatic inline void ee_i_vmuli(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(muli) }\nstatic inline void ee_i_vmulq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulq) }\nstatic inline void ee_i_vmulw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulw) }\nstatic inline void ee_i_vmulx(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulx) }\nstatic inline void ee_i_vmuly(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(muly) }\nstatic inline void ee_i_vmulz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulz) }\nstatic inline void ee_i_vnop(struct ee_state* ee, const ee_instruction& i) { VU_UPPER(nop) }\nstatic inline void ee_i_vopmsub(struct ee_state* ee, const ee_instruction& i) { VU_UPPER(opmsub) }\nstatic inline void ee_i_vopmula(struct ee_state* ee, const ee_instruction& i) { VU_UPPER(opmula) }\nstatic inline void ee_i_vrget(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(rget) }\nstatic inline void ee_i_vrinit(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(rinit) }\nstatic inline void ee_i_vrnext(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(rnext) }\nstatic inline void ee_i_vrsqrt(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(rsqrt) }\nstatic inline void ee_i_vrxor(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(rxor) }\nstatic inline void ee_i_vsqd(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(sqd) }\nstatic inline void ee_i_vsqi(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(sqi) }\nstatic inline void ee_i_vsqrt(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(sqrt) }\nstatic inline void ee_i_vsub(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(sub) }\nstatic inline void ee_i_vsuba(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(suba) }\nstatic inline void ee_i_vsubai(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subai) }\nstatic inline void ee_i_vsubaq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subaq) }\nstatic inline void ee_i_vsubaw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subaw) }\nstatic inline void ee_i_vsubax(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subax) }\nstatic inline void ee_i_vsubay(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subay) }\nstatic inline void ee_i_vsubaz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subaz) }\nstatic inline void ee_i_vsubi(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subi) }\nstatic inline void ee_i_vsubq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subq) }\nstatic inline void ee_i_vsubw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subw) }\nstatic inline void ee_i_vsubx(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subx) }\nstatic inline void ee_i_vsuby(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(suby) }\nstatic inline void ee_i_vsubz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subz) }\nstatic inline void ee_i_vwaitq(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(waitq) }\nstatic inline void ee_i_xor(struct ee_state* ee, const ee_instruction& i) {\n    EE_RD = EE_RS ^ EE_RT;\n}\nstatic inline void ee_i_xori(struct ee_state* ee, const ee_instruction& i) {\n    EE_RT = EE_RS ^ EE_D_I16;\n}\nstatic inline void ee_i_invalid(struct ee_state* ee, const ee_instruction& i) {\n    fprintf(stderr, \"ee: Invalid instruction %08x at PC=%08x\\n\", i.opcode, ee->pc);\n\n    exit(1);\n}\nstatic inline void ee_i_nop(struct ee_state* ee, const ee_instruction& i) {\n}\n\nstruct ee_state* ee_create(void) {\n    return new ee_state();\n}\n\nvoid ee_init(struct ee_state* ee, struct vu_state* vu0, struct vu_state* vu1, int ram_size, struct ee_bus_s bus) {\n    ee->prid = 0x2e20;\n    ee->pc = EE_VEC_RESET;\n    ee->next_pc = ee->pc + 4;\n    ee->bus = bus;\n    ee->vu0 = vu0;\n    ee->vu1 = vu1;\n\n    // Initialize block lookup cache (intentionally mismatches so first real lookup succeeds)\n    ee->last_block_lookup_pc = ~0u;\n    ee->last_block_ptr = nullptr;\n\n    // To-do: Set SR\n\n    ee->spr = ps2_ram_create();\n    ps2_ram_init(ee->spr, 0x4000);\n\n    // EE's FPU uses round to zero by default\n    fesetround(FE_TOWARDZERO);\n\n    ee->fcr = 0x01000001;\n    ee->ram_size = ram_size - 1;\n\n    ee->osd_config.screen_type = 0; // 4:3\n    ee->osd_config.ps1drv_config = 0; // ???\n    ee->osd_config.spdif_mode = 0; // Enabled\n    ee->osd_config.timezone_offset = 0;\n    ee->osd_config.video_output = 0; // RGB\n    ee->osd_config.jap_language = 1; // Indicates not Japanese\n    ee->osd_config.language = 1; // English\n    ee->osd_config.version = 1; // Indicates normal kernel without extended language settings\n\n    ee->block_cache.clear();\n    ee->block_cache.resize(EE_CACHE_PAGECOUNT);\n}\n\nvoid ee_reset(struct ee_state* ee) {\n    for (int i = 0; i < 32; i++)\n        ee->r[i] = { 0 };\n\n    for (int i = 0; i < 32; i++)\n        ee->f[i].u32 = 0;\n\n    for (int i = 0; i < 32; i++)\n        ee->cop0_r[i] = 0;\n\n    ee->a.u32 = 0;\n\n    ee->hi = { 0 };\n    ee->lo = { 0 };\n    ee->pc = 0xbfc00000;\n    ee->next_pc = ee->pc + 4;\n    ee->opcode = 0;\n    ee->sa = 0;\n    ee->branch = 0;\n    ee->branch_taken = 0;\n    ee->delay_slot = 0;\n    ee->prid = 0x2e20;\n    ee->pc = EE_VEC_RESET;\n    ee->next_pc = ee->pc + 4;\n    ee->intc_reads = 0;\n    ee->csr_reads = 0;\n\n    ee->block_cache.clear();\n    ee->block_cache.resize(EE_CACHE_PAGECOUNT);\n\n    // Clear block lookup cache\n    ee->last_block_lookup_pc = ~0u;\n    ee->last_block_ptr = nullptr;\n\n    fesetround(FE_TOWARDZERO);\n\n    ps2_ram_reset(ee->spr);\n\n    ee->fcr = 0x01000001;\n}\n\nvoid ee_destroy(struct ee_state* ee) {\n    ps2_ram_destroy(ee->spr);\n\n    delete ee;\n}\n\n#define EE_BRANCH_NORMAL 1\n#define EE_BRANCH_IMMEDIATE 2\n#define EE_BRANCH_LIKELY 3\n#define EE_BRANCH_COND 4\n\nee_instruction ee_decode(uint32_t opcode) {\n    ee_instruction i;\n\n    i.opcode = opcode;\n    i.rs = (opcode >> 21) & 0x1f;\n    i.rt = (opcode >> 16) & 0x1f;\n    i.rd = (opcode >> 11) & 0x1f;\n    i.sa = (opcode >> 6) & 0x1f;\n    i.i15 = (opcode >> 6) & 0x7fff;\n    i.i16 = opcode & 0xffff;\n    i.i26 = opcode & 0x3ffffff;\n\n    i.branch = 0;\n    i.cycles = 0;\n\n    switch ((opcode & 0xFC000000) >> 26) {\n        case 0x00000000 >> 26: { // special\n            switch (opcode & 0x0000003F) {\n                case 0x00000000: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sll; return i;\n                case 0x00000002: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_srl; return i;\n                case 0x00000003: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sra; return i;\n                case 0x00000004: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sllv; return i;\n                case 0x00000006: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_srlv; return i;\n                case 0x00000007: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_srav; return i;\n                case 0x00000008: i.cycles = EE_CYC_DEFAULT; i.branch = 1; i.func = ee_i_jr; return i;\n                case 0x00000009: i.cycles = EE_CYC_DEFAULT; i.branch = 1; i.func = ee_i_jalr; return i;\n                case 0x0000000A: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_movz; return i;\n                case 0x0000000B: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_movn; return i;\n                case 0x0000000C: i.cycles = EE_CYC_DEFAULT; i.branch = 2; i.func = ee_i_syscall; return i;\n                case 0x0000000D: i.cycles = EE_CYC_DEFAULT; i.branch = 2; i.func = ee_i_break; return i;\n                case 0x0000000F: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sync; return i;\n                case 0x00000010: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mfhi; return i;\n                case 0x00000011: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mthi; return i;\n                case 0x00000012: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mflo; return i;\n                case 0x00000013: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mtlo; return i;\n                case 0x00000014: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsllv; return i;\n                case 0x00000016: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsrlv; return i;\n                case 0x00000017: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsrav; return i;\n                case 0x00000018: i.cycles = EE_CYC_MULT; i.func = ee_i_mult; return i;\n                case 0x00000019: i.cycles = EE_CYC_MULT; i.func = ee_i_multu; return i;\n                case 0x0000001A: i.cycles = EE_CYC_DIV; i.func = ee_i_div; return i;\n                case 0x0000001B: i.cycles = EE_CYC_DIV; i.func = ee_i_divu; return i;\n                case 0x00000020: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_add; return i;\n                case 0x00000021: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_addu; return i;\n                case 0x00000022: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_sub; return i;\n                case 0x00000023: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_subu; return i;\n                case 0x00000024: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_and; return i;\n                case 0x00000025: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_or; return i;\n                case 0x00000026: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_xor; return i;\n                case 0x00000027: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_nor; return i;\n                case 0x00000028: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mfsa; return i;\n                case 0x00000029: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mtsa; return i;\n                case 0x0000002A: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_slt; return i;\n                case 0x0000002B: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sltu; return i;\n                case 0x0000002C: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_dadd; return i;\n                case 0x0000002D: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_daddu; return i;\n                case 0x0000002E: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_dsub; return i;\n                case 0x0000002F: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsubu; return i;\n                case 0x00000030: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tge; return i;\n                case 0x00000031: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tgeu; return i;\n                case 0x00000032: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tlt; return i;\n                case 0x00000033: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tltu; return i;\n                case 0x00000034: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_teq; return i;\n                case 0x00000036: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tne; return i;\n                case 0x00000038: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsll; return i;\n                case 0x0000003A: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsrl; return i;\n                case 0x0000003B: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsra; return i;\n                case 0x0000003C: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsll32; return i;\n                case 0x0000003E: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsrl32; return i;\n                case 0x0000003F: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsra32; return i;\n            }\n        } break;\n        case 0x04000000 >> 26: { // regimm\n            switch ((opcode & 0x001F0000) >> 16) {\n                case 0x00000000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bltz; return i;\n                case 0x00010000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bgez; return i;\n                case 0x00020000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bltzl; return i;\n                case 0x00030000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bgezl; return i;\n                case 0x00080000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tgei; return i;\n                case 0x00090000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tgeiu; return i;\n                case 0x000A0000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tlti; return i;\n                case 0x000B0000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tltiu; return i;\n                case 0x000C0000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_teqi; return i;\n                case 0x000E0000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tnei; return i;\n                case 0x00100000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bltzal; return i;\n                case 0x00110000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bgezal; return i;\n                case 0x00120000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bltzall; return i;\n                case 0x00130000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bgezall; return i;\n                case 0x00180000 >> 16: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mtsab; return i;\n                case 0x00190000 >> 16: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mtsah; return i;\n            }\n        } break;\n        case 0x08000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 1; i.func = ee_i_j; return i;\n        case 0x0C000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 1; i.func = ee_i_jal; return i;\n        case 0x10000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_beq; return i;\n        case 0x14000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bne; return i;\n        case 0x18000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_blez; return i;\n        case 0x1C000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bgtz; return i;\n        case 0x20000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_addi; return i;\n        case 0x24000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_addiu; return i;\n        case 0x28000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_slti; return i;\n        case 0x2C000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sltiu; return i;\n        case 0x30000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_andi; return i;\n        case 0x34000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_ori; return i;\n        case 0x38000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_xori; return i;\n        case 0x3C000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_lui; return i;\n        case 0x40000000 >> 26: { // cop0\n            switch ((opcode & 0x03E00000) >> 21) {\n                case 0x00000000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mfc0; return i;\n                case 0x00800000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mtc0; return i;\n                case 0x01000000 >> 21: {\n                    switch ((opcode & 0x001F0000) >> 16) {\n                        case 0x00000000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc0f; return i;\n                        case 0x00010000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc0t; return i;\n                        case 0x00020000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc0fl; return i;\n                        case 0x00030000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc0tl; return i;\n                    }\n                } break;\n                case 0x02000000 >> 21: {\n                    switch (opcode & 0x0000003F) {\n                        case 0x00000001: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_tlbr; return i;\n                        case 0x00000002: i.cycles = EE_CYC_COP_DEFAULT; i.branch = 2; i.func = ee_i_tlbwi; return i;\n                        case 0x00000006: i.cycles = EE_CYC_COP_DEFAULT; i.branch = 2; i.func = ee_i_tlbwr; return i;\n                        case 0x00000008: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_tlbp; return i;\n                        case 0x00000018: i.cycles = EE_CYC_COP_DEFAULT; i.branch = 2; i.func = ee_i_eret; return i;\n                        case 0x00000038: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_ei; return i;\n                        case 0x00000039: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_di; return i;\n                    }\n                } break;\n            }\n        } break;\n        case 0x44000000 >> 26: { // cop1\n            switch ((opcode & 0x03E00000) >> 21) {\n                case 0x00000000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mfc1; return i;\n                case 0x00400000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cfc1; return i;\n                case 0x00800000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mtc1; return i;\n                case 0x00C00000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_ctc1; return i;\n                case 0x01000000 >> 21: {\n                    switch ((opcode & 0x001F0000) >> 16) {\n                        case 0x00000000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc1f; return i;\n                        case 0x00010000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc1t; return i;\n                        case 0x00020000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc1fl; return i;\n                        case 0x00030000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc1tl; return i;\n                    }\n                } break;\n                case 0x02000000 >> 21: {\n                    switch (opcode & 0x0000003F) {\n                        case 0x00000000: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_adds; return i;\n                        case 0x00000001: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_subs; return i;\n                        case 0x00000002: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_muls; return i;\n                        case 0x00000003: i.cycles = EE_CYC_FPU_DIV; i.func = ee_i_divs; return i;\n                        case 0x00000004: i.cycles = EE_CYC_FPU_DIV; i.func = ee_i_sqrts; return i;\n                        case 0x00000005: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_abss; return i;\n                        case 0x00000006: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_movs; return i;\n                        case 0x00000007: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_negs; return i;\n                        case 0x00000016: i.cycles = EE_CYC_FPU_DIV; i.func = ee_i_rsqrts; return i;\n                        case 0x00000018: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_addas; return i;\n                        case 0x00000019: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_subas; return i;\n                        case 0x0000001A: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_mulas; return i;\n                        case 0x0000001C: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_madds; return i;\n                        case 0x0000001D: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_msubs; return i;\n                        case 0x0000001E: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_maddas; return i;\n                        case 0x0000001F: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_msubas; return i;\n                        case 0x00000024: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cvtw; return i;\n                        case 0x00000028: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_maxs; return i;\n                        case 0x00000029: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mins; return i;\n                        case 0x00000030: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cf; return i;\n                        case 0x00000032: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_ceq; return i;\n                        case 0x00000034: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_clt; return i;\n                        case 0x00000036: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cle; return i;\n                    }\n                } break;\n                case 0x02800000 >> 21: {\n                    switch (opcode & 0x0000003F) {\n                        case 0x00000020: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cvts; return i;\n                    }\n                } break;\n            }\n        } break;\n        case 0x48000000 >> 26: { // cop2\n            switch ((opcode & 0x03E00000) >> 21) {\n                case 0x00200000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_qmfc2; return i;\n                case 0x00400000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cfc2; return i;\n                case 0x00A00000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_qmtc2; return i;\n                case 0x00C00000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_ctc2; return i;\n                case 0x01000000 >> 21: {\n                    switch ((opcode & 0x001F0000) >> 16) {\n                        case 0x00000000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc2f; return i;\n                        case 0x00010000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc2t; return i;\n                        case 0x00020000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc2fl; return i;\n                        case 0x00030000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc2tl; return i;\n                    }\n                } break;\n                case 0x02000000 >> 21:\n                case 0x02200000 >> 21:\n                case 0x02400000 >> 21:\n                case 0x02600000 >> 21:\n                case 0x02800000 >> 21:\n                case 0x02A00000 >> 21:\n                case 0x02C00000 >> 21:\n                case 0x02E00000 >> 21:\n                case 0x03000000 >> 21:\n                case 0x03200000 >> 21:\n                case 0x03400000 >> 21:\n                case 0x03600000 >> 21:\n                case 0x03800000 >> 21:\n                case 0x03A00000 >> 21:\n                case 0x03C00000 >> 21:\n                case 0x03E00000 >> 21: {\n                    switch (opcode & 0x0000003F) {\n                        case 0x00000000: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddx; return i;\n                        case 0x00000001: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddy; return i;\n                        case 0x00000002: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddz; return i;\n                        case 0x00000003: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddw; return i;\n                        case 0x00000004: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubx; return i;\n                        case 0x00000005: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsuby; return i;\n                        case 0x00000006: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubz; return i;\n                        case 0x00000007: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubw; return i;\n                        case 0x00000008: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddx; return i;\n                        case 0x00000009: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddy; return i;\n                        case 0x0000000A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddz; return i;\n                        case 0x0000000B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddw; return i;\n                        case 0x0000000C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubx; return i;\n                        case 0x0000000D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsuby; return i;\n                        case 0x0000000E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubz; return i;\n                        case 0x0000000F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubw; return i;\n                        case 0x00000010: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxx; return i;\n                        case 0x00000011: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxy; return i;\n                        case 0x00000012: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxz; return i;\n                        case 0x00000013: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxw; return i;\n                        case 0x00000014: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminix; return i;\n                        case 0x00000015: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminiy; return i;\n                        case 0x00000016: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminiz; return i;\n                        case 0x00000017: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminiw; return i;\n                        case 0x00000018: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulx; return i;\n                        case 0x00000019: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmuly; return i;\n                        case 0x0000001A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulz; return i;\n                        case 0x0000001B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulw; return i;\n                        case 0x0000001C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulq; return i;\n                        case 0x0000001D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxi; return i;\n                        case 0x0000001E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmuli; return i;\n                        case 0x0000001F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminii; return i;\n                        case 0x00000020: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddq; return i;\n                        case 0x00000021: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddq; return i;\n                        case 0x00000022: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddi; return i;\n                        case 0x00000023: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddi; return i;\n                        case 0x00000024: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubq; return i;\n                        case 0x00000025: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubq; return i;\n                        case 0x00000026: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubi; return i;\n                        case 0x00000027: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubi; return i;\n                        case 0x00000028: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vadd; return i;\n                        case 0x00000029: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmadd; return i;\n                        case 0x0000002A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmul; return i;\n                        case 0x0000002B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmax; return i;\n                        case 0x0000002C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsub; return i;\n                        case 0x0000002D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsub; return i;\n                        case 0x0000002E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vopmsub; return i;\n                        case 0x0000002F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmini; return i;\n                        case 0x00000030: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_viadd; return i;\n                        case 0x00000031: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_visub; return i;\n                        case 0x00000032: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_viaddi; return i;\n                        case 0x00000034: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_viand; return i;\n                        case 0x00000035: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vior; return i;\n                        case 0x00000038: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vcallms; return i;\n                        case 0x00000039: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vcallmsr; return i;\n                        case 0x0000003C:\n                        case 0x0000003D:\n                        case 0x0000003E:\n                        case 0x0000003F: {\n                            uint32_t func = (opcode & 3) | ((opcode & 0x7c0) >> 4);\n\n                            switch (func) {\n                                case 0x00000000: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddax; return i;\n                                case 0x00000001: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vadday; return i;\n                                case 0x00000002: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddaz; return i;\n                                case 0x00000003: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddaw; return i;\n                                case 0x00000004: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubax; return i;\n                                case 0x00000005: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubay; return i;\n                                case 0x00000006: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubaz; return i;\n                                case 0x00000007: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubaw; return i;\n                                case 0x00000008: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddax; return i;\n                                case 0x00000009: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmadday; return i;\n                                case 0x0000000A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddaz; return i;\n                                case 0x0000000B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddaw; return i;\n                                case 0x0000000C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubax; return i;\n                                case 0x0000000D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubay; return i;\n                                case 0x0000000E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubaz; return i;\n                                case 0x0000000F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubaw; return i;\n                                case 0x00000010: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vitof0; return i;\n                                case 0x00000011: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vitof4; return i;\n                                case 0x00000012: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vitof12; return i;\n                                case 0x00000013: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vitof15; return i;\n                                case 0x00000014: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vftoi0; return i;\n                                case 0x00000015: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vftoi4; return i;\n                                case 0x00000016: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vftoi12; return i;\n                                case 0x00000017: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vftoi15; return i;\n                                case 0x00000018: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulax; return i;\n                                case 0x00000019: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulay; return i;\n                                case 0x0000001A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulaz; return i;\n                                case 0x0000001B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulaw; return i;\n                                case 0x0000001C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulaq; return i;\n                                case 0x0000001D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vabs; return i;\n                                case 0x0000001E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulai; return i;\n                                case 0x0000001F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vclipw; return i;\n                                case 0x00000020: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddaq; return i;\n                                case 0x00000021: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddaq; return i;\n                                case 0x00000022: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddai; return i;\n                                case 0x00000023: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddai; return i;\n                                case 0x00000024: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubaq; return i;\n                                case 0x00000025: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubaq; return i;\n                                case 0x00000026: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubai; return i;\n                                case 0x00000027: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubai; return i;\n                                case 0x00000028: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vadda; return i;\n                                case 0x00000029: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmadda; return i;\n                                case 0x0000002A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmula; return i;\n                                case 0x0000002C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsuba; return i;\n                                case 0x0000002D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsuba; return i;\n                                case 0x0000002E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vopmula; return i;\n                                case 0x0000002F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vnop; return i;\n                                case 0x00000030: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmove; return i;\n                                case 0x00000031: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmr32; return i;\n                                case 0x00000034: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vlqi; return i;\n                                case 0x00000035: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsqi; return i;\n                                case 0x00000036: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vlqd; return i;\n                                case 0x00000037: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsqd; return i;\n                                case 0x00000038: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vdiv; return i;\n                                case 0x00000039: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsqrt; return i;\n                                case 0x0000003A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrsqrt; return i;\n                                case 0x0000003B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vwaitq; return i;\n                                case 0x0000003C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmtir; return i;\n                                case 0x0000003D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmfir; return i;\n                                case 0x0000003E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vilwr; return i;\n                                case 0x0000003F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_viswr; return i;\n                                case 0x00000040: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrnext; return i;\n                                case 0x00000041: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrget; return i;\n                                case 0x00000042: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrinit; return i;\n                                case 0x00000043: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrxor; return i;\n                            }\n                        } break;\n                    }\n                } break;\n            }\n        } break;\n        case 0x50000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_beql; return i;\n        case 0x54000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bnel; return i;\n        case 0x58000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_blezl; return i;\n        case 0x5C000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bgtzl; return i;\n        case 0x60000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_daddi; return i;\n        case 0x64000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_daddiu; return i;\n        case 0x68000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_ldl; return i;\n        case 0x6C000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_ldr; return i;\n        case 0x70000000 >> 26: { // mmi\n            switch (opcode & 0x0000003F) {\n                case 0x00000000: i.cycles = EE_CYC_MULT; i.func = ee_i_madd; return i;\n                case 0x00000001: i.cycles = EE_CYC_MULT; i.func = ee_i_maddu; return i;\n                case 0x00000004: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_plzcw; return i;\n                case 0x00000008: {\n                    switch ((opcode & 0x000007C0) >> 6) {\n                        case 0x00000000 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddw; return i;\n                        case 0x00000040 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubw; return i;\n                        case 0x00000080 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcgtw; return i;\n                        case 0x000000C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmaxw; return i;\n                        case 0x00000100 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddh; return i;\n                        case 0x00000140 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubh; return i;\n                        case 0x00000180 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcgth; return i;\n                        case 0x000001C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmaxh; return i;\n                        case 0x00000200 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddb; return i;\n                        case 0x00000240 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubb; return i;\n                        case 0x00000280 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcgtb; return i;\n                        case 0x00000400 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddsw; return i;\n                        case 0x00000440 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubsw; return i;\n                        case 0x00000480 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextlw; return i;\n                        case 0x000004C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_ppacw; return i;\n                        case 0x00000500 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddsh; return i;\n                        case 0x00000540 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubsh; return i;\n                        case 0x00000580 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextlh; return i;\n                        case 0x000005C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_ppach; return i;\n                        case 0x00000600 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddsb; return i;\n                        case 0x00000640 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubsb; return i;\n                        case 0x00000680 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextlb; return i;\n                        case 0x000006C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_ppacb; return i;\n                        case 0x00000780 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pext5; return i;\n                        case 0x000007C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_ppac5; return i;\n                    }\n                } break;\n                case 0x00000009: {\n                    switch ((opcode & 0x000007C0) >> 6) {\n                        case 0x00000000 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmaddw; return i;\n                        case 0x00000080 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psllvw; return i;\n                        case 0x000000C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psrlvw; return i;\n                        case 0x00000100 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmsubw; return i;\n                        case 0x00000200 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhi; return i;\n                        case 0x00000240 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmflo; return i;\n                        case 0x00000280 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pinth; return i;\n                        case 0x00000300 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmultw; return i;\n                        case 0x00000340 >> 6: i.cycles = EE_CYC_MMI_DIV; i.func = ee_i_pdivw; return i;\n                        case 0x00000380 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcpyld; return i;\n                        case 0x00000400 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmaddh; return i;\n                        case 0x00000440 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_phmadh; return i;\n                        case 0x00000480 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pand; return i;\n                        case 0x000004C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pxor; return i;\n                        case 0x00000500 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmsubh; return i;\n                        case 0x00000540 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_phmsbh; return i;\n                        case 0x00000680 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pexeh; return i;\n                        case 0x000006C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_prevh; return i;\n                        case 0x00000700 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmulth; return i;\n                        case 0x00000740 >> 6: i.cycles = EE_CYC_MMI_DIV; i.func = ee_i_pdivbw; return i;\n                        case 0x00000780 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pexew; return i;\n                        case 0x000007C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_prot3w; return i;\n                    }\n                } break;\n                case 0x00000010: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mfhi1; return i;\n                case 0x00000011: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mthi1; return i;\n                case 0x00000012: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mflo1; return i;\n                case 0x00000013: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mtlo1; return i;\n                case 0x00000018: i.cycles = EE_CYC_MULT; i.func = ee_i_mult1; return i;\n                case 0x00000019: i.cycles = EE_CYC_MULT; i.func = ee_i_multu1; return i;\n                case 0x0000001A: i.cycles = EE_CYC_DIV; i.func = ee_i_div1; return i;\n                case 0x0000001B: i.cycles = EE_CYC_DIV; i.func = ee_i_divu1; return i;\n                case 0x00000020: i.cycles = EE_CYC_MULT; i.func = ee_i_madd1; return i;\n                case 0x00000021: i.cycles = EE_CYC_MULT; i.func = ee_i_maddu1; return i;\n                case 0x00000028: {\n                    switch ((opcode & 0x000007C0) >> 6) {\n                        case 0x00000040 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pabsw; return i;\n                        case 0x00000080 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pceqw; return i;\n                        case 0x000000C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pminw; return i;\n                        case 0x00000100 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_padsbh; return i;\n                        case 0x00000140 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pabsh; return i;\n                        case 0x00000180 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pceqh; return i;\n                        case 0x000001C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pminh; return i;\n                        case 0x00000280 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pceqb; return i;\n                        case 0x00000400 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_padduw; return i;\n                        case 0x00000440 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubuw; return i;\n                        case 0x00000480 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextuw; return i;\n                        case 0x00000500 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_padduh; return i;\n                        case 0x00000540 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubuh; return i;\n                        case 0x00000580 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextuh; return i;\n                        case 0x00000600 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddub; return i;\n                        case 0x00000640 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubub; return i;\n                        case 0x00000680 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextub; return i;\n                        case 0x000006C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_qfsrv; return i;\n                    }\n                } break;\n                case 0x00000029: {\n                    switch ((opcode & 0x000007C0) >> 6) {\n                        case 0x00000000 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmadduw; return i;\n                        case 0x000000C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psravw; return i;\n                        case 0x00000200 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmthi; return i;\n                        case 0x00000240 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmtlo; return i;\n                        case 0x00000280 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pinteh; return i;\n                        case 0x00000300 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmultuw; return i;\n                        case 0x00000340 >> 6: i.cycles = EE_CYC_MMI_DIV; i.func = ee_i_pdivuw; return i;\n                        case 0x00000380 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcpyud; return i;\n                        case 0x00000480 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_por; return i;\n                        case 0x000004C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pnor; return i;\n                        case 0x00000680 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pexch; return i;\n                        case 0x000006C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcpyh; return i;\n                        case 0x00000780 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pexcw; return i;\n                    }\n                } break;\n                case 0x00000030: {\n                    switch ((opcode & 0x000007C0) >> 6) {\n                        case 0x00000000 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhllw; return i;\n                        case 0x00000040 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhluw; return i;\n                        case 0x00000080 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhlslw; return i;\n                        case 0x000000c0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhllh; return i;\n                        case 0x00000100 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhlsh; return i;\n                    }\n                } break;\n                case 0x00000031: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmthl; return i;\n                case 0x00000034: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psllh; return i;\n                case 0x00000036: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psrlh; return i;\n                case 0x00000037: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psrah; return i;\n                case 0x0000003C: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psllw; return i;\n                case 0x0000003E: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psrlw; return i;\n                case 0x0000003F: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psraw; return i;\n            }\n        } break;\n        case 0x78000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lq; return i;\n        case 0x7C000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_sq; return i;\n        case 0x80000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lb; return i;\n        case 0x84000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lh; return i;\n        case 0x88000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lwl; return i;\n        case 0x8C000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lw; return i;\n        case 0x90000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lbu; return i;\n        case 0x94000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lhu; return i;\n        case 0x98000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lwr; return i;\n        case 0x9C000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lwu; return i;\n        case 0xA0000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sb; return i;\n        case 0xA4000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sh; return i;\n        case 0xA8000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_swl; return i;\n        case 0xAC000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sw; return i;\n        case 0xB0000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sdl; return i;\n        case 0xB4000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sdr; return i;\n        case 0xB8000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_swr; return i;\n        case 0xBC000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 2; i.func = ee_i_cache; return i;\n        case 0xC4000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lwc1; return i;\n        case 0xCC000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_pref; return i;\n        case 0xD8000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lqc2; return i;\n        case 0xDC000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_ld; return i;\n        case 0xE4000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_swc1; return i;\n        case 0xF8000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sqc2; return i;\n        case 0xFC000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sd; return i;\n    }\n\n    i.func = ee_i_invalid;\n\n    return i;\n}\n\nstatic inline struct ee_block* ee_cache_block(struct ee_state* ee, int max_cycles) {\n    uint32_t page = ee->pc / _EE_CACHE_PAGESIZE;\n    uint32_t offset = (ee->pc & (_EE_CACHE_PAGESIZE - 1)) >> 2;\n\n    if (!ee->block_cache[page]) {\n        ee->block_cache[page] = new struct ee_block[_EE_CACHE_PAGESIZE >> 2];\n    }\n\n    struct ee_block& block = ee->block_cache[page][offset];\n\n    uint32_t pc = ee->pc;\n    uint32_t block_pc = ee->pc;\n    ee_instruction i;\n\n    block.cycles = 0;\n    block.instructions.reserve(max_cycles);\n\n    while (max_cycles) {\n        ee->opcode = bus_read32(ee, pc);\n\n        if (ee->exception) {\n            // An exception occurred while fetching the instruction\n            // Stop caching the block here\n            ee->exception = 0;\n\n            // Cache at the new location (handler)\n            return ee_cache_block(ee, max_cycles);\n        }\n\n        if (ee->opcode != 0) {\n            i = ee_decode(ee->opcode);\n\n            block.instructions.push_back(i);\n        } else {\n            i.func = ee_i_nop;\n            i.branch = 0;\n\n            block.instructions.push_back(i);\n        }\n\n        block.cycles += i.cycles;\n\n        if (i.branch == 1 || i.branch == 3) {\n            max_cycles = 2;\n        } else if (i.branch != 0) {\n            max_cycles = 1;\n        }\n\n        max_cycles--;\n\n        pc += 4;\n    }\n\n    return &block;\n}\n\nstatic inline struct ee_block* ee_find_block(struct ee_state* ee, uint32_t pc) {\n    // Fast path: check single-entry cache first (avoids hash computation)\n    if (pc == ee->last_block_lookup_pc) {\n        return ee->last_block_ptr;\n    }\n\n    uint32_t page = pc / _EE_CACHE_PAGESIZE;\n\n    if (!ee->block_cache[page]) {\n        return nullptr;\n    }\n\n    uint32_t offset = (pc & (_EE_CACHE_PAGESIZE - 1)) >> 2;\n\n    struct ee_block& block = ee->block_cache[page][offset];\n\n    if (!block.cycles) {\n        return nullptr;\n    }\n\n    // Update cache for next lookup\n    ee->last_block_lookup_pc = pc;\n    ee->last_block_ptr = &block;\n\n    return &block;\n}\n\nint ee_run_block(struct ee_state* ee, int max_cycles) {\n    // This is the entrypoint to the EENULL thread.\n    // If we hit this address, the program is basically idling\n    // so we \"fast-forward\" 1024 cycles\n\n    ee->branch = 0;\n    ee->delay_slot = 0;\n\n    if (ee_check_irq(ee))\n        return 0;\n\n    if (ee->pc == 0x81fc0 || ee->intc_reads >= 10000) { // ee->csr_reads >= 1000\n        ee->total_cycles += 2048;\n        ee->count += 2048;\n        // ee->eenull_counter += 8 * 64;\n\n        ee->idle_skips++;\n\n        return 2048;\n    }\n\n    struct ee_block* block = ee_find_block(ee, ee->pc);\n\n    if (!block) {\n        ee->cache_misses++;\n\n        block = ee_cache_block(ee, max_cycles);\n    }\n\n    ee->block_pc = ee->pc;\n\n    int cycles = 0;\n\n    for (const auto& i : block->instructions) {\n        ee->delay_slot = ee->branch;\n        ee->branch = 0;\n\n        // If we need to handle an interrupt, break out of the loop\n        // if (ee_check_irq(ee))\n        //     break;\n\n        ee->pc = ee->next_pc;\n        ee->next_pc += 4;\n\n        i.func(ee, i);\n\n        ee->count++;\n        ee->r[0] = { 0 };\n\n        cycles++;\n\n        // An exception occurred or likely branch was taken\n        // break immediately and clear the exception flag\n        if (ee->exception) {\n            ee->exception = 0;\n\n            break;\n        }\n    }\n\n    // printf(\"ee: Block executed with %d cycles pc=%08x\\n\", cycles, ee->pc);\n\n    return cycles;\n}\n\nint ee_step(struct ee_state* ee) {\n    static ee_instruction i;\n\n    ee->delay_slot = ee->branch;\n    ee->branch = 0;\n\n    // Would check for interrupts here, but we do this outside of the core\n    // to reduce overhead\n    ee_check_irq(ee);\n\n    ee->prev_pc = ee->pc;\n    ee->opcode = bus_read32(ee, ee->pc);\n    ee->pc = ee->next_pc;\n    ee->next_pc += 4;\n\n    i = ee_decode(ee->opcode);\n\n    i.func(ee, i);\n\n    ++ee->total_cycles;\n    ++ee->count;\n\n    ee->r[0].u64[0] = 0;\n    ee->r[0].u64[1] = 0;\n\n    return 1;\n}\n\nvoid ee_flush_cache(struct ee_state* ee) {\n    for (auto& page : ee->block_cache) {\n        delete[] page;\n\n        page = nullptr;\n    }\n\n    ee->last_block_lookup_pc = ~0u;\n    ee->last_block_ptr = nullptr;\n}\n\nuint32_t ee_get_pc(struct ee_state* ee) {\n    return ee->pc;\n}\n\nstruct ps2_ram* ee_get_spr(struct ee_state* ee) {\n    return ee->spr;\n}\n\nvoid ee_set_fmv_skip(struct ee_state* ee, int v) {\n    ee->fmv_skip = v;\n}\n\nvoid ee_reset_intc_reads(struct ee_state* ee) {\n    ee->intc_reads = 0;\n}\n\nvoid ee_reset_csr_reads(struct ee_state* ee) {\n    ee->csr_reads = 0;\n}\n\nvoid ee_set_ram_size(struct ee_state* ee, int ram_size) {\n    ee->ram_size = ram_size - 1;\n}\n\nvoid ee_set_osd_config(struct ee_state* ee, struct ee_osd_config config) {\n    ee->osd_config = config;\n}\n\nstruct ee_osd_config ee_get_osd_config(struct ee_state* ee) {\n    return ee->osd_config;\n}"
  },
  {
    "path": "src/ee/ee_def.hpp",
    "content": "#pragma once\n\n#include <cstdint>\n\n#include \"shared/ram.h\"\n\n#include \"u128.h\"\n\n#include \"vu.h\"\n#include \"vu_def.hpp\"\n\n#include <unordered_map>\n#include <vector>\n\n#ifdef _EE_USE_INTRINSICS\n#ifdef _MSC_VER\n#define EE_ALIGNED16 __declspec(align(16))\n#else\n#define EE_ALIGNED16 __attribute__((aligned(16)))\n#endif\n#else\n#define EE_ALIGNED16\n#endif\n\n#define EE_CYC_DEFAULT 9\n#define EE_CYC_BRANCH 11\n#define EE_CYC_COP_DEFAULT 7\n#define EE_CYC_MULT 2*8\n#define EE_CYC_DIV 14*8\n#define EE_CYC_MMI_MULT 3*8\n#define EE_CYC_MMI_DIV 22*8\n#define EE_CYC_MMI_DEFAULT 14\n#define EE_CYC_FPU_MULT 4*8\n#define EE_CYC_FPU_DIV 6*8\n#define EE_CYC_STORE 14\n#define EE_CYC_LOAD 14\n\nstruct ee_instruction {\n    uint32_t opcode;\n    int32_t rs;\n    int32_t rt;\n    int32_t rd;\n    int32_t sa;\n    int32_t i15;\n    int32_t i16;\n    int32_t i26;\n\n    // 0 - no branch\n    // 1 - delayed branch\n    // 2 - immediate branch\n    // 3 - likely branch\n    // 4 - conditional exception\n    int branch;\n    int cycles;\n\n    void (*func)(struct ee_state*, const ee_instruction&); \n};\n\nstruct ee_block {\n    std::vector <ee_instruction> instructions;\n    uint32_t cycles = 0;\n};\n\nstruct ee_state {\n    struct ee_bus_s bus;\n\n    uint32_t block_pc;\n\n    std::vector <ee_block*> block_cache;\n    \n    // Single-entry block cache for fast lookup (avoid hash computation)\n    // Exploits temporal locality since we execute the same block repeatedly\n    uint32_t last_block_lookup_pc;\n    struct ee_block* last_block_ptr;\n\n    uint128_t r[32] EE_ALIGNED16;\n    uint128_t hi EE_ALIGNED16;\n    uint128_t lo EE_ALIGNED16;\n\n    uint64_t total_cycles;\n\n    int exception;\n\n    int fmv_skip;\n    uint32_t prev_pc;\n    uint32_t pc;\n    uint32_t next_pc;\n    uint32_t opcode;\n    uint64_t sa;\n    int branch, branch_taken, delay_slot;\n\n    struct ps2_ram* spr;\n\n    int cpcond0;\n\n    union {\n        uint32_t cop0_r[32];\n    \n        struct {\n            uint32_t index;\n            uint32_t random;\n            uint32_t entrylo0;\n            uint32_t entrylo1;\n            uint32_t context;\n            uint32_t pagemask;\n            uint32_t wired;\n            uint32_t unused7;\n            uint32_t badvaddr;\n            uint32_t count;\n            uint32_t entryhi;\n            uint32_t compare;\n            uint32_t status;\n            uint32_t cause;\n            uint32_t epc;\n            uint32_t prid;\n            uint32_t config;\n            uint32_t unused16;\n            uint32_t unused17;\n            uint32_t unused18;\n            uint32_t unused19;\n            uint32_t unused20;\n            uint32_t unused21;\n            uint32_t badpaddr;\n            uint32_t debug;\n            uint32_t perf;\n            uint32_t unused25;\n            uint32_t unused26;\n            uint32_t taglo;\n            uint32_t taghi;\n            uint32_t errorepc;\n            uint32_t unused30;\n            uint32_t unused31;\n        };\n    };\n\n    uint32_t thread_list_base;\n\n    union ee_fpu_reg f[32];\n    union ee_fpu_reg a;\n\n    uint32_t fcr;\n\n    struct vu_state* vu0;\n    struct vu_state* vu1;\n\n    struct ee_vtlb_entry vtlb[48];\n    struct ee_osd_config osd_config;\n\n    int eenull_counter;\n    int csr_reads;\n    int intc_reads;\n    int ram_size;\n\n    // Stats\n    uint64_t cache_misses;\n    uint64_t cache_hits;\n    uint64_t idle_skips;\n};\n\n#define THS_RUN 0x01\n#define THS_READY 0x02\n#define THS_WAIT 0x04\n#define THS_SUSPEND 0x08\n#define THS_WAITSUSPEND 0x0C // THS_WAIT | THS_SUSPEND\n#define THS_DORMANT 0x10\n\nstruct ee_thread {\n    uint32_t prev; // TCB*\n    uint32_t next; // TCB*\n    int status;\n    uint32_t func; // void*\n    uint32_t current_stack; // void*\n    uint32_t gp_reg; // void*\n    short current_priority;\n    short init_priority;\n    int wait_type; //0=not waiting, 1=sleeping, 2=waiting on semaphore\n    int sema_id;\n    int wakeup_count;\n    int attr;\n    int option;\n    uint32_t func_; // void* ??? \n    int argc;\n    uint32_t argv; // char**\n    uint32_t initial_stack; // void*\n    int stack_size;\n    uint32_t root; // int* function to return to when exiting thread? \n    uint32_t heap_base; // void*\n};"
  },
  {
    "path": "src/ee/ee_dis.c",
    "content": "#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"ee_dis.h\"\n\nstatic struct ee_dis_state *s;\nchar *ptr;\n\n#define EE_D_RS ((opcode >> 21) & 0x1f)\n#define EE_D_RT ((opcode >> 16) & 0x1f)\n#define EE_D_RD ((opcode >> 11) & 0x1f)\n#define EE_D_SA ((opcode >> 6) & 0x1f)\n#define EE_D_I16 (opcode & 0xffff)\n#define EE_D_I26 (opcode & 0x3ffffff)\n#define EE_D_SI26 ((int32_t)(EE_D_I26 << 6) >> 4)\n#define EE_D_SI16 ((int32_t)(EE_D_I16 << 16) >> 14)\n\nstatic const char *ee_cop0_r[] = {\n    \"Index\",\n    \"Random\",\n    \"EntryLo0\",\n    \"EntryLo1\",\n    \"Context\",\n    \"PageMask\",\n    \"Wired\",\n    \"Unused7\",\n    \"BadVAddr\",\n    \"Count\",\n    \"EntryHi\",\n    \"Compare\",\n    \"Status\",\n    \"Cause\",\n    \"EPC\",\n    \"PRId\",\n    \"Config\",\n    \"Unused17\",\n    \"Unused18\",\n    \"Unused19\",\n    \"Unused20\",\n    \"Unused21\",\n    \"Unused22\",\n    \"BadPAddr\",\n    \"Debug\",\n    \"Perf\",\n    \"Unused26\",\n    \"Unused27\",\n    \"TagLo\",\n    \"TagHi\",\n    \"ErrorEPC\",\n    \"Unused31\"};\n\nstatic const char *ee_cc_r[] = {\n    \"r0\", \"at\", \"v0\", \"v1\", \"a0\", \"a1\", \"a2\", \"a3\",\n    \"t0\", \"t1\", \"t2\", \"t3\", \"t4\", \"t5\", \"t6\", \"t7\",\n    \"s0\", \"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\", \"s7\",\n    \"t8\", \"t9\", \"k0\", \"k1\", \"gp\", \"sp\", \"fp\", \"ra\"\n};\n\nstatic inline void ee_d_abss(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"abs.s\", EE_D_RD, EE_D_RS); }\nstatic inline void ee_d_add(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"add\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_addas(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"adda.s\", EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_addi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"addi\", ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_addiu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"addiu\", ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_adds(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d, $f%d\", \"add.s\", EE_D_RD, EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_addu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"addu\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_and(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"and\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_andi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"andi\", ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_bc0f(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s 0x%x\", \"bc0f\", s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bc0fl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s 0x%x\", \"bc0fl\", s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bc0t(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s 0x%x\", \"bc0t\", s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bc0tl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s 0x%x\", \"bc0tl\", s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bc1f(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s 0x%x\", \"bc1f\", s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bc1fl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s 0x%x\", \"bc1fl\", s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bc1t(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s 0x%x\", \"bc1t\", s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bc1tl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s 0x%x\", \"bc1tl\", s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bc2f(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"bc2f\"); }\nstatic inline void ee_d_bc2fl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"bc2fl\"); }\nstatic inline void ee_d_bc2t(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"bc2t\"); }\nstatic inline void ee_d_bc2tl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"bc2tl\"); }\nstatic inline void ee_d_beq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, 0x%x\", \"beq\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_beql(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, 0x%x\", \"beql\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bgez(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, 0x%x\", \"bgez\", ee_cc_r[EE_D_RS], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bgezal(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, 0x%x\", \"bgezal\", ee_cc_r[EE_D_RS], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bgezall(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, 0x%x\", \"bgezall\", ee_cc_r[EE_D_RS], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bgezl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, 0x%x\", \"bgezl\", ee_cc_r[EE_D_RS], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bgtz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, 0x%x\", \"bgtz\", ee_cc_r[EE_D_RS], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bgtzl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, 0x%x\", \"bgtzl\", ee_cc_r[EE_D_RS], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_blez(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, 0x%x\", \"blez\", ee_cc_r[EE_D_RS], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_blezl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, 0x%x\", \"blezl\", ee_cc_r[EE_D_RS], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bltz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, 0x%x\", \"bltz\", ee_cc_r[EE_D_RS], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bltzal(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, 0x%x\", \"bltzal\", ee_cc_r[EE_D_RS], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bltzall(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, 0x%x\", \"bltzall\", ee_cc_r[EE_D_RS], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bltzl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, 0x%x\", \"bltzl\", ee_cc_r[EE_D_RS], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bne(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, 0x%x\", \"bne\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_bnel(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, 0x%x\", \"bnel\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT], s->pc + 4 + EE_D_SI16); }\nstatic inline void ee_d_break(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"break\"); }\nstatic inline void ee_d_cache(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s 0x%02x, %d($%s)\", \"cache\", EE_D_RT, (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_callmsr(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"callmsr\"); }\nstatic inline void ee_d_ceq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"c.eq.s\", EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_cfc1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $f%d\", \"cfc1\", ee_cc_r[EE_D_RT], EE_D_RS); }\nstatic inline void ee_d_cfc2(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"cfc2\"); }\nstatic inline void ee_d_cf(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"c.f.s\", EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_cle(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"c.le.s\", EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_clt(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"c.lt.s\", EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_ctc1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $f%d\", \"ctc1\", ee_cc_r[EE_D_RT], EE_D_RS); }\nstatic inline void ee_d_ctc2(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"ctc2\"); }\nstatic inline void ee_d_cvts(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"cvt.s.w\", EE_D_RD, EE_D_RS); }\nstatic inline void ee_d_cvtw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"cvt.w.s\", EE_D_RD, EE_D_RS); }\nstatic inline void ee_d_dadd(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"dadd\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_daddi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"daddi\", ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_daddiu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"daddiu\", ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_daddu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"daddu\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_di(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"di\"); }\nstatic inline void ee_d_div(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"div\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_div1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"div1\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_divs(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d, $f%d\", \"div.s\", EE_D_RD, EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_divu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"divu\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_divu1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"divu1\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_dsll(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"dsll\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_dsll32(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"dsll32\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_dsllv(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"dsllv\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_dsra(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"dsra\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_dsra32(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"dsra32\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_dsrav(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"dsrav\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_dsrl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"dsrl\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_dsrl32(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"dsrl32\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_dsrlv(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"dsrlv\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_dsub(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"dsub\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_dsubu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"dsubu\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_ei(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"ei\"); }\nstatic inline void ee_d_eret(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"eret\"); }\nstatic inline void ee_d_j(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s 0x%08x\", \"j\", ((s->pc + 4) & 0xf0000000) | (EE_D_I26 << 2)); }\nstatic inline void ee_d_jal(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s 0x%08x\", \"jal\", ((s->pc + 4) & 0xf0000000) | (EE_D_I26 << 2)); }\nstatic inline void ee_d_jalr(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"jalr\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_jr(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"jr\", ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_lb(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"lb\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_lbu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"lbu\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_ld(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"ld\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_ldl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"ldl\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_ldr(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"ldr\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_lh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"lh\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_lhu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"lhu\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_lq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"lq\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_lqc2(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"lqc2\"); }\nstatic inline void ee_d_lui(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d\", \"lui\", ee_cc_r[EE_D_RT], EE_D_I16); }\nstatic inline void ee_d_lw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"lw\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_lwc1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, %d($%s)\", \"lwc1\", EE_D_RT, (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_lwl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"lwl\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_lwr(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"lwr\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_lwu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"lwu\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_madd(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"madd\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_madd1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"madd1\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_maddas(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"madda.s\", EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_madds(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d, $f%d\", \"madd.s\", EE_D_RD, EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_maddu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"maddu\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_maddu1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"maddu1\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_maxs(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d, $f%d\", \"max.s\", EE_D_RD, EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_mfc0(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"mfc0\", ee_cc_r[EE_D_RT], ee_cop0_r[EE_D_RD]); }\nstatic inline void ee_d_mfc1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $f%d\", \"mfc1\", ee_cc_r[EE_D_RT], EE_D_RS); }\nstatic inline void ee_d_mfhi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"mfhi\", ee_cc_r[EE_D_RD]); }\nstatic inline void ee_d_mfhi1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"mfhi1\", ee_cc_r[EE_D_RD]); }\nstatic inline void ee_d_mflo(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"mflo\", ee_cc_r[EE_D_RD]); }\nstatic inline void ee_d_mflo1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"mflo1\", ee_cc_r[EE_D_RD]); }\nstatic inline void ee_d_mfsa(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"mfsa\", ee_cc_r[EE_D_RD]); }\nstatic inline void ee_d_mins(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d, $f%d\", \"min.s\", EE_D_RD, EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_movn(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"movn\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_movs(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"mov.s\", EE_D_RD, EE_D_RS); }\nstatic inline void ee_d_movz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"movz\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_msubas(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"msuba.s\", EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_msubs(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d, $f%d\", \"msub.s\", EE_D_RD, EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_mtc0(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"mtc0\", ee_cc_r[EE_D_RT], ee_cop0_r[EE_D_RD]); }\nstatic inline void ee_d_mtc1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $f%d\", \"mtc1\", ee_cc_r[EE_D_RT], EE_D_RS); }\nstatic inline void ee_d_mthi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"mthi\", ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_mthi1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"mthi1\", ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_mtlo(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"mtlo\", ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_mtlo1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"mtlo1\", ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_mtsa(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"mtsa\", ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_mtsab(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d\", \"mtsab\", ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_mtsah(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d\", \"mtsah\", ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_mulas(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"mula.s\", EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_muls(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d, $f%d\", \"mul.s\", EE_D_RD, EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_mult(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"mult\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_mult1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"mult1\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_multu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"multu\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_multu1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"multu1\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_negs(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"neg.s\", EE_D_RD, EE_D_RS); }\nstatic inline void ee_d_nor(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"nor\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_or(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"or\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_ori(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"ori\", ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_pabsh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"pabsh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pabsw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"pabsw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_paddb(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"paddb\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_paddh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"paddh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_paddsb(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"paddsb\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_paddsh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"paddsh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_paddsw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"paddsw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_paddub(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"paddub\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_padduh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"padduh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_padduw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"padduw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_paddw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"paddw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_padsbh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"padsbh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pand(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pand\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pceqb(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pceqb\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pceqh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pceqh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pceqw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pceqw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pcgtb(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pcgtb\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pcgth(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pcgth\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pcgtw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pcgtw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pcpyh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"pcpyh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pcpyld(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pcpyld\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pcpyud(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pcpyud\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pdivbw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"pdivbw\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pdivuw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"pdivuw\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pdivw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"pdivw\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pexch(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"pexch\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pexcw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"pexcw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pexeh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"pexeh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pexew(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"pexew\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pext5(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"pext5\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pextlb(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pextlb\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pextlh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pextlh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pextlw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pextlw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pextub(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pextub\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pextuh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pextuh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pextuw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pextuw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_phmadh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"phmadh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_phmsbh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"phmsbh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pinteh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pinteh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pinth(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pinth\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_plzcw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"plzcw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_pmaddh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pmaddh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pmadduw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pmadduw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pmaddw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pmaddw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pmaxh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pmaxh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pmaxw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pmaxw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pmfhi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"pmfhi\", ee_cc_r[EE_D_RD]); }\nstatic inline void ee_d_pmfhllw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"pmfhl.lw\", ee_cc_r[EE_D_RD]); }\nstatic inline void ee_d_pmfhluw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"pmfhl.uw\", ee_cc_r[EE_D_RD]); }\nstatic inline void ee_d_pmfhlslw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"pmfhl.slw\", ee_cc_r[EE_D_RD]); }\nstatic inline void ee_d_pmfhllh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"pmfhl.lh\", ee_cc_r[EE_D_RD]); }\nstatic inline void ee_d_pmfhlsh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"pmfhl.sh\", ee_cc_r[EE_D_RD]); }\nstatic inline void ee_d_pmflo(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"pmflo\", ee_cc_r[EE_D_RD]); }\nstatic inline void ee_d_pminh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pminh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pminw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pminw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pmsubh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pmsubh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pmsubw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pmsubw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pmthi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"pmthi\", ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_pmthl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"pmthl\", ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_pmtlo(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s\", \"pmtlo\", ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_pmulth(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pmulth\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pmultuw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pmultuw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pmultw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pmultw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pnor(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pnor\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_por(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"por\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_ppac5(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"ppac5\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_ppacb(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"ppacb\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_ppach(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"ppach\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_ppacw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"ppacw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pref(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s %d($%s)\", \"pref\", (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_prevh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"prevh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_prot3w(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"prot3w\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_psllh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"psllh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_psllvw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"psllvw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_psllw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"psllw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_psrah(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"psrah\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_psravw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"psravw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_psraw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"psraw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_psrlh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"psrlh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_psrlvw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"psrlvw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_psrlw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"psrlw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_psubb(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"psubb\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_psubh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"psubh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_psubsb(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"psubsb\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_psubsh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"psubsh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_psubsw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"psubsw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_psubub(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"psubub\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_psubuh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"psubuh\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_psubuw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"psubuw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_psubw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"psubw\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_pxor(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"pxor\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_qfsrv(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"qfsrv\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_qmfc2(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"qmfc2\"); }\nstatic inline void ee_d_qmtc2(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"qmtc2\"); }\nstatic inline void ee_d_rsqrts(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d, $f%d\", \"rsqrt.s\", EE_D_RD, EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_sb(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"sb\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_sd(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"sd\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_sdl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"sdl\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_sdr(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"sdr\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_sh(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"sh\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_sll(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"sll\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_sllv(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"sllv\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_slt(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"slt\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_slti(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"slti\", ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_sltiu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"sltiu\", ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_sltu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"sltu\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_sq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"sq\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_sqc2(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"sqc2\"); }\nstatic inline void ee_d_sqrts(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"sqrt.s\", EE_D_RD, EE_D_RT); }\nstatic inline void ee_d_sra(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"sra\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_srav(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"srav\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_srl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"srl\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], EE_D_SA); }\nstatic inline void ee_d_srlv(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"srlv\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_sub(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"sub\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_subas(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d\", \"suba.s\", EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_subs(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, $f%d, $f%d\", \"sub.s\", EE_D_RD, EE_D_RS, EE_D_RT); }\nstatic inline void ee_d_subu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"subu\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_sw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"sw\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_swc1(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $f%d, %d($%s)\", \"swc1\", EE_D_RT, (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_swl(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"swl\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_swr(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d($%s)\", \"swr\", ee_cc_r[EE_D_RT], (int16_t)EE_D_I16, ee_cc_r[EE_D_RS]); }\nstatic inline void ee_d_sync(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"sync\"); }\nstatic inline void ee_d_syscall(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"syscall\"); }\nstatic inline void ee_d_teq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"teq\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_teqi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d\", \"teqi\", ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_tge(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"tge\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_tgei(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d\", \"tgei\", ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_tgeiu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d\", \"tgeiu\", ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_tgeu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"tgeu\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_tlbp(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"tlbp\"); }\nstatic inline void ee_d_tlbr(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"tlbr\"); }\nstatic inline void ee_d_tlbwi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"tlbwi\"); }\nstatic inline void ee_d_tlbwr(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"tlbwr\"); }\nstatic inline void ee_d_tlt(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"tlt\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_tlti(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d\", \"tlti\", ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_tltiu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d\", \"tltiu\", ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_tltu(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"tltu\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_tne(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s\", \"tne\", ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_tnei(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, %d\", \"tnei\", ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_vabs(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vabs\"); }\nstatic inline void ee_d_vadd(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vadd\"); }\nstatic inline void ee_d_vadda(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vadda\"); }\nstatic inline void ee_d_vaddai(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vaddai\"); }\nstatic inline void ee_d_vaddaq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vaddaq\"); }\nstatic inline void ee_d_vaddaw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vaddaw\"); }\nstatic inline void ee_d_vaddax(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vaddax\"); }\nstatic inline void ee_d_vadday(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vadday\"); }\nstatic inline void ee_d_vaddaz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vaddaz\"); }\nstatic inline void ee_d_vaddi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vaddi\"); }\nstatic inline void ee_d_vaddq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vaddq\"); }\nstatic inline void ee_d_vaddw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vaddw\"); }\nstatic inline void ee_d_vaddx(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vaddx\"); }\nstatic inline void ee_d_vaddy(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vaddy\"); }\nstatic inline void ee_d_vaddz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vaddz\"); }\nstatic inline void ee_d_vcallms(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vcallms\"); }\nstatic inline void ee_d_vclipw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vclipw\"); }\nstatic inline void ee_d_vdiv(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vdiv\"); }\nstatic inline void ee_d_vftoi0(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vftoi0\"); }\nstatic inline void ee_d_vftoi12(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vftoi12\"); }\nstatic inline void ee_d_vftoi15(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vftoi15\"); }\nstatic inline void ee_d_vftoi4(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vftoi4\"); }\nstatic inline void ee_d_viadd(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"viadd\"); }\nstatic inline void ee_d_viaddi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"viaddi\"); }\nstatic inline void ee_d_viand(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"viand\"); }\nstatic inline void ee_d_vilwr(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vilwr\"); }\nstatic inline void ee_d_vior(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vior\"); }\nstatic inline void ee_d_visub(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"visub\"); }\nstatic inline void ee_d_viswr(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"viswr\"); }\nstatic inline void ee_d_vitof0(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vitof0\"); }\nstatic inline void ee_d_vitof12(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vitof12\"); }\nstatic inline void ee_d_vitof15(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vitof15\"); }\nstatic inline void ee_d_vitof4(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vitof4\"); }\nstatic inline void ee_d_vlqd(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vlqd\"); }\nstatic inline void ee_d_vlqi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vlqi\"); }\nstatic inline void ee_d_vmadd(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmadd\"); }\nstatic inline void ee_d_vmadda(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmadda\"); }\nstatic inline void ee_d_vmaddai(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaddai\"); }\nstatic inline void ee_d_vmaddaq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaddaq\"); }\nstatic inline void ee_d_vmaddaw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaddaw\"); }\nstatic inline void ee_d_vmaddax(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaddax\"); }\nstatic inline void ee_d_vmadday(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmadday\"); }\nstatic inline void ee_d_vmaddaz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaddaz\"); }\nstatic inline void ee_d_vmaddi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaddi\"); }\nstatic inline void ee_d_vmaddq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaddq\"); }\nstatic inline void ee_d_vmaddw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaddw\"); }\nstatic inline void ee_d_vmaddx(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaddx\"); }\nstatic inline void ee_d_vmaddy(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaddy\"); }\nstatic inline void ee_d_vmaddz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaddz\"); }\nstatic inline void ee_d_vmax(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmax\"); }\nstatic inline void ee_d_vmaxi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaxi\"); }\nstatic inline void ee_d_vmaxw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaxw\"); }\nstatic inline void ee_d_vmaxx(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaxx\"); }\nstatic inline void ee_d_vmaxy(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaxy\"); }\nstatic inline void ee_d_vmaxz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmaxz\"); }\nstatic inline void ee_d_vmfir(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmfir\"); }\nstatic inline void ee_d_vmini(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmini\"); }\nstatic inline void ee_d_vminii(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vminii\"); }\nstatic inline void ee_d_vminiw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vminiw\"); }\nstatic inline void ee_d_vminix(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vminix\"); }\nstatic inline void ee_d_vminiy(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vminiy\"); }\nstatic inline void ee_d_vminiz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vminiz\"); }\nstatic inline void ee_d_vmove(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmove\"); }\nstatic inline void ee_d_vmr32(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmr32\"); }\nstatic inline void ee_d_vmsub(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsub\"); }\nstatic inline void ee_d_vmsuba(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsuba\"); }\nstatic inline void ee_d_vmsubai(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsubai\"); }\nstatic inline void ee_d_vmsubaq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsubaq\"); }\nstatic inline void ee_d_vmsubaw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsubaw\"); }\nstatic inline void ee_d_vmsubax(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsubax\"); }\nstatic inline void ee_d_vmsubay(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsubay\"); }\nstatic inline void ee_d_vmsubaz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsubaz\"); }\nstatic inline void ee_d_vmsubi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsubi\"); }\nstatic inline void ee_d_vmsubq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsubq\"); }\nstatic inline void ee_d_vmsubw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsubw\"); }\nstatic inline void ee_d_vmsubx(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsubx\"); }\nstatic inline void ee_d_vmsuby(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsuby\"); }\nstatic inline void ee_d_vmsubz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmsubz\"); }\nstatic inline void ee_d_vmtir(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmtir\"); }\nstatic inline void ee_d_vmul(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmul\"); }\nstatic inline void ee_d_vmula(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmula\"); }\nstatic inline void ee_d_vmulai(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmulai\"); }\nstatic inline void ee_d_vmulaq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmulaq\"); }\nstatic inline void ee_d_vmulaw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmulaw\"); }\nstatic inline void ee_d_vmulax(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmulax\"); }\nstatic inline void ee_d_vmulay(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmulay\"); }\nstatic inline void ee_d_vmulaz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmulaz\"); }\nstatic inline void ee_d_vmuli(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmuli\"); }\nstatic inline void ee_d_vmulq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmulq\"); }\nstatic inline void ee_d_vmulw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmulw\"); }\nstatic inline void ee_d_vmulx(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmulx\"); }\nstatic inline void ee_d_vmuly(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmuly\"); }\nstatic inline void ee_d_vmulz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vmulz\"); }\nstatic inline void ee_d_vnop(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vnop\"); }\nstatic inline void ee_d_vopmsub(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vopmsub\"); }\nstatic inline void ee_d_vopmula(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vopmula\"); }\nstatic inline void ee_d_vrget(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vrget\"); }\nstatic inline void ee_d_vrinit(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vrinit\"); }\nstatic inline void ee_d_vrnext(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vrnext\"); }\nstatic inline void ee_d_vrsqrt(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vrsqrt\"); }\nstatic inline void ee_d_vrxor(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vrxor\"); }\nstatic inline void ee_d_vsqd(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsqd\"); }\nstatic inline void ee_d_vsqi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsqi\"); }\nstatic inline void ee_d_vsqrt(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsqrt\"); }\nstatic inline void ee_d_vsub(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsub\"); }\nstatic inline void ee_d_vsuba(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsuba\"); }\nstatic inline void ee_d_vsubai(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsubai\"); }\nstatic inline void ee_d_vsubaq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsubaq\"); }\nstatic inline void ee_d_vsubaw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsubaw\"); }\nstatic inline void ee_d_vsubax(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsubax\"); }\nstatic inline void ee_d_vsubay(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsubay\"); }\nstatic inline void ee_d_vsubaz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsubaz\"); }\nstatic inline void ee_d_vsubi(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsubi\"); }\nstatic inline void ee_d_vsubq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsubq\"); }\nstatic inline void ee_d_vsubw(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsubw\"); }\nstatic inline void ee_d_vsubx(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsubx\"); }\nstatic inline void ee_d_vsuby(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsuby\"); }\nstatic inline void ee_d_vsubz(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vsubz\"); }\nstatic inline void ee_d_vwaitq(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"vwaitq\"); }\nstatic inline void ee_d_xor(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, $%s\", \"xor\", ee_cc_r[EE_D_RD], ee_cc_r[EE_D_RS], ee_cc_r[EE_D_RT]); }\nstatic inline void ee_d_xori(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s $%s, $%s, %d\", \"xori\", ee_cc_r[EE_D_RT], ee_cc_r[EE_D_RS], EE_D_I16); }\nstatic inline void ee_d_invalid(uint32_t opcode) { ptr += sprintf(ptr, \"%-8s\", \"<invalid>\"); }\n\nchar *ee_disassemble(char *buf, uint32_t opcode, struct ee_dis_state *dis_state) {\n    s = dis_state;\n\n    ptr = buf;\n\n    if (dis_state) if (dis_state->print_address)\n        ptr += sprintf(ptr, \"%08x: \", dis_state->pc);\n\n    if (dis_state) if (dis_state->print_opcode)\n        ptr += sprintf(ptr, \"%08x \", opcode);\n\n    switch (opcode & 0xFC000000) {\n        case 0x00000000: { // special\n            switch (opcode & 0x0000003F) {\n                case 0x00000000: ee_d_sll(opcode); return buf;\n                case 0x00000002: ee_d_srl(opcode); return buf;\n                case 0x00000003: ee_d_sra(opcode); return buf;\n                case 0x00000004: ee_d_sllv(opcode); return buf;\n                case 0x00000006: ee_d_srlv(opcode); return buf;\n                case 0x00000007: ee_d_srav(opcode); return buf;\n                case 0x00000008: ee_d_jr(opcode); return buf;\n                case 0x00000009: ee_d_jalr(opcode); return buf;\n                case 0x0000000A: ee_d_movz(opcode); return buf;\n                case 0x0000000B: ee_d_movn(opcode); return buf;\n                case 0x0000000C: ee_d_syscall(opcode); return buf;\n                case 0x0000000D: ee_d_break(opcode); return buf;\n                case 0x0000000F: ee_d_sync(opcode); return buf;\n                case 0x00000010: ee_d_mfhi(opcode); return buf;\n                case 0x00000011: ee_d_mthi(opcode); return buf;\n                case 0x00000012: ee_d_mflo(opcode); return buf;\n                case 0x00000013: ee_d_mtlo(opcode); return buf;\n                case 0x00000014: ee_d_dsllv(opcode); return buf;\n                case 0x00000016: ee_d_dsrlv(opcode); return buf;\n                case 0x00000017: ee_d_dsrav(opcode); return buf;\n                case 0x00000018: ee_d_mult(opcode); return buf;\n                case 0x00000019: ee_d_multu(opcode); return buf;\n                case 0x0000001A: ee_d_div(opcode); return buf;\n                case 0x0000001B: ee_d_divu(opcode); return buf;\n                case 0x00000020: ee_d_add(opcode); return buf;\n                case 0x00000021: ee_d_addu(opcode); return buf;\n                case 0x00000022: ee_d_sub(opcode); return buf;\n                case 0x00000023: ee_d_subu(opcode); return buf;\n                case 0x00000024: ee_d_and(opcode); return buf;\n                case 0x00000025: ee_d_or(opcode); return buf;\n                case 0x00000026: ee_d_xor(opcode); return buf;\n                case 0x00000027: ee_d_nor(opcode); return buf;\n                case 0x00000028: ee_d_mfsa(opcode); return buf;\n                case 0x00000029: ee_d_mtsa(opcode); return buf;\n                case 0x0000002A: ee_d_slt(opcode); return buf;\n                case 0x0000002B: ee_d_sltu(opcode); return buf;\n                case 0x0000002C: ee_d_dadd(opcode); return buf;\n                case 0x0000002D: ee_d_daddu(opcode); return buf;\n                case 0x0000002E: ee_d_dsub(opcode); return buf;\n                case 0x0000002F: ee_d_dsubu(opcode); return buf;\n                case 0x00000030: ee_d_tge(opcode); return buf;\n                case 0x00000031: ee_d_tgeu(opcode); return buf;\n                case 0x00000032: ee_d_tlt(opcode); return buf;\n                case 0x00000033: ee_d_tltu(opcode); return buf;\n                case 0x00000034: ee_d_teq(opcode); return buf;\n                case 0x00000036: ee_d_tne(opcode); return buf;\n                case 0x00000038: ee_d_dsll(opcode); return buf;\n                case 0x0000003A: ee_d_dsrl(opcode); return buf;\n                case 0x0000003B: ee_d_dsra(opcode); return buf;\n                case 0x0000003C: ee_d_dsll32(opcode); return buf;\n                case 0x0000003E: ee_d_dsrl32(opcode); return buf;\n                case 0x0000003F: ee_d_dsra32(opcode); return buf;\n            }\n        } break;\n        case 0x04000000: { // regimm\n            switch (opcode & 0x001F0000) {\n                case 0x00000000: ee_d_bltz(opcode); return buf;\n                case 0x00010000: ee_d_bgez(opcode); return buf;\n                case 0x00020000: ee_d_bltzl(opcode); return buf;\n                case 0x00030000: ee_d_bgezl(opcode); return buf;\n                case 0x00080000: ee_d_tgei(opcode); return buf;\n                case 0x00090000: ee_d_tgeiu(opcode); return buf;\n                case 0x000A0000: ee_d_tlti(opcode); return buf;\n                case 0x000B0000: ee_d_tltiu(opcode); return buf;\n                case 0x000C0000: ee_d_teqi(opcode); return buf;\n                case 0x000E0000: ee_d_tnei(opcode); return buf;\n                case 0x00100000: ee_d_bltzal(opcode); return buf;\n                case 0x00110000: ee_d_bgezal(opcode); return buf;\n                case 0x00120000: ee_d_bltzall(opcode); return buf;\n                case 0x00130000: ee_d_bgezall(opcode); return buf;\n                case 0x00180000: ee_d_mtsab(opcode); return buf;\n                case 0x00190000: ee_d_mtsah(opcode); return buf;\n            }\n        } break;\n        case 0x08000000: ee_d_j(opcode); return buf;\n        case 0x0C000000: ee_d_jal(opcode); return buf;\n        case 0x10000000: ee_d_beq(opcode); return buf;\n        case 0x14000000: ee_d_bne(opcode); return buf;\n        case 0x18000000: ee_d_blez(opcode); return buf;\n        case 0x1C000000: ee_d_bgtz(opcode); return buf;\n        case 0x20000000: ee_d_addi(opcode); return buf;\n        case 0x24000000: ee_d_addiu(opcode); return buf;\n        case 0x28000000: ee_d_slti(opcode); return buf;\n        case 0x2C000000: ee_d_sltiu(opcode); return buf;\n        case 0x30000000: ee_d_andi(opcode); return buf;\n        case 0x34000000: ee_d_ori(opcode); return buf;\n        case 0x38000000: ee_d_xori(opcode); return buf;\n        case 0x3C000000: ee_d_lui(opcode); return buf;\n        case 0x40000000: { // cop0\n            switch (opcode & 0x03E00000) {\n                case 0x00000000: ee_d_mfc0(opcode); return buf;\n                case 0x00800000: ee_d_mtc0(opcode); return buf;\n                case 0x01000000: {\n                    switch (opcode & 0x001F0000) {\n                        case 0x00000000: ee_d_bc0f(opcode); return buf;\n                        case 0x00010000: ee_d_bc0t(opcode); return buf;\n                        case 0x00020000: ee_d_bc0fl(opcode); return buf;\n                        case 0x00030000: ee_d_bc0tl(opcode); return buf;\n                    }\n                } break;\n                case 0x02000000: {\n                    switch (opcode & 0x0000003F) {\n                    case 0x00000001: ee_d_tlbr(opcode); return buf;\n                    case 0x00000002: ee_d_tlbwi(opcode); return buf;\n                    case 0x00000006: ee_d_tlbwr(opcode); return buf;\n                    case 0x00000008: ee_d_tlbp(opcode); return buf;\n                    case 0x00000018: ee_d_eret(opcode); return buf;\n                    case 0x00000038: ee_d_ei(opcode); return buf;\n                    case 0x00000039: ee_d_di(opcode); return buf;\n                    }\n                } break;\n            }\n        } break;\n        case 0x44000000: { // cop1\n            switch (opcode & 0x03E00000) {\n                case 0x00000000: ee_d_mfc1(opcode); return buf;\n                case 0x00400000: ee_d_cfc1(opcode); return buf;\n                case 0x00800000: ee_d_mtc1(opcode); return buf;\n                case 0x00C00000: ee_d_ctc1(opcode); return buf;\n                case 0x01000000: {\n                    switch (opcode & 0x001F0000) {\n                        case 0x00000000: ee_d_bc1f(opcode); return buf;\n                        case 0x00010000: ee_d_bc1t(opcode); return buf;\n                        case 0x00020000: ee_d_bc1fl(opcode); return buf;\n                        case 0x00030000: ee_d_bc1tl(opcode); return buf;\n                    }\n                } break;\n                case 0x02000000: {\n                    switch (opcode & 0x0000003F) {\n                        case 0x00000000: ee_d_adds(opcode); return buf;\n                        case 0x00000001: ee_d_subs(opcode); return buf;\n                        case 0x00000002: ee_d_muls(opcode); return buf;\n                        case 0x00000003: ee_d_divs(opcode); return buf;\n                        case 0x00000004: ee_d_sqrts(opcode); return buf;\n                        case 0x00000005: ee_d_abss(opcode); return buf;\n                        case 0x00000006: ee_d_movs(opcode); return buf;\n                        case 0x00000007: ee_d_negs(opcode); return buf;\n                        case 0x00000016: ee_d_rsqrts(opcode); return buf;\n                        case 0x00000018: ee_d_addas(opcode); return buf;\n                        case 0x00000019: ee_d_subas(opcode); return buf;\n                        case 0x0000001A: ee_d_mulas(opcode); return buf;\n                        case 0x0000001C: ee_d_madds(opcode); return buf;\n                        case 0x0000001D: ee_d_msubs(opcode); return buf;\n                        case 0x0000001E: ee_d_maddas(opcode); return buf;\n                        case 0x0000001F: ee_d_msubas(opcode); return buf;\n                        case 0x00000024: ee_d_cvtw(opcode); return buf;\n                        case 0x00000028: ee_d_maxs(opcode); return buf;\n                        case 0x00000029: ee_d_mins(opcode); return buf;\n                        case 0x00000030: ee_d_cf(opcode); return buf;\n                        case 0x00000032: ee_d_ceq(opcode); return buf;\n                        case 0x00000034: ee_d_clt(opcode); return buf;\n                        case 0x00000036: ee_d_cle(opcode); return buf;\n                    }\n                } break;\n                case 0x02800000: {\n                    switch (opcode & 0x0000003F) {\n                        case 0x00000020: ee_d_cvts(opcode); return buf;\n                    }\n                } break;\n            }\n        } break;\n        case 0x48000000: { // cop2\n            switch (opcode & 0x03E00000) {\n                case 0x00200000: ee_d_qmfc2(opcode); return buf;\n                case 0x00400000: ee_d_cfc2(opcode); return buf;\n                case 0x00A00000: ee_d_qmtc2(opcode); return buf;\n                case 0x00C00000: ee_d_ctc2(opcode); return buf;\n                case 0x01000000: {\n                    switch (opcode & 0x001F0000) {\n                    case 0x00000000: ee_d_bc2f(opcode); return buf;\n                    case 0x00010000: ee_d_bc2t(opcode); return buf;\n                    case 0x00020000: ee_d_bc2fl(opcode); return buf;\n                    case 0x00030000: ee_d_bc2tl(opcode); return buf;\n                    }\n                }\n                break;\n                case 0x02000000:\n                case 0x02200000:\n                case 0x02400000:\n                case 0x02600000:\n                case 0x02800000:\n                case 0x02A00000:\n                case 0x02C00000:\n                case 0x02E00000:\n                case 0x03000000:\n                case 0x03200000:\n                case 0x03400000:\n                case 0x03600000:\n                case 0x03800000:\n                case 0x03A00000:\n                case 0x03C00000:\n                case 0x03E00000: {\n                    switch (opcode & 0x0000003F) {\n                        case 0x00000000: ee_d_vaddx(opcode); return buf;\n                        case 0x00000001: ee_d_vaddy(opcode); return buf;\n                        case 0x00000002: ee_d_vaddz(opcode); return buf;\n                        case 0x00000003: ee_d_vaddw(opcode); return buf;\n                        case 0x00000004: ee_d_vsubx(opcode); return buf;\n                        case 0x00000005: ee_d_vsuby(opcode); return buf;\n                        case 0x00000006: ee_d_vsubz(opcode); return buf;\n                        case 0x00000007: ee_d_vsubw(opcode); return buf;\n                        case 0x00000008: ee_d_vmaddx(opcode); return buf;\n                        case 0x00000009: ee_d_vmaddy(opcode); return buf;\n                        case 0x0000000A: ee_d_vmaddz(opcode); return buf;\n                        case 0x0000000B: ee_d_vmaddw(opcode); return buf;\n                        case 0x0000000C: ee_d_vmsubx(opcode); return buf;\n                        case 0x0000000D: ee_d_vmsuby(opcode); return buf;\n                        case 0x0000000E: ee_d_vmsubz(opcode); return buf;\n                        case 0x0000000F: ee_d_vmsubw(opcode); return buf;\n                        case 0x00000010: ee_d_vmaxx(opcode); return buf;\n                        case 0x00000011: ee_d_vmaxy(opcode); return buf;\n                        case 0x00000012: ee_d_vmaxz(opcode); return buf;\n                        case 0x00000013: ee_d_vmaxw(opcode); return buf;\n                        case 0x00000014: ee_d_vminix(opcode); return buf;\n                        case 0x00000015: ee_d_vminiy(opcode); return buf;\n                        case 0x00000016: ee_d_vminiz(opcode); return buf;\n                        case 0x00000017: ee_d_vminiw(opcode); return buf;\n                        case 0x00000018: ee_d_vmulx(opcode); return buf;\n                        case 0x00000019: ee_d_vmuly(opcode); return buf;\n                        case 0x0000001A: ee_d_vmulz(opcode); return buf;\n                        case 0x0000001B: ee_d_vmulw(opcode); return buf;\n                        case 0x0000001C: ee_d_vmulq(opcode); return buf;\n                        case 0x0000001D: ee_d_vmaxi(opcode); return buf;\n                        case 0x0000001E: ee_d_vmuli(opcode); return buf;\n                        case 0x0000001F: ee_d_vminii(opcode); return buf;\n                        case 0x00000020: ee_d_vaddq(opcode); return buf;\n                        case 0x00000021: ee_d_vmaddq(opcode); return buf;\n                        case 0x00000022: ee_d_vaddi(opcode); return buf;\n                        case 0x00000023: ee_d_vmaddi(opcode); return buf;\n                        case 0x00000024: ee_d_vsubq(opcode); return buf;\n                        case 0x00000025: ee_d_vmsubq(opcode); return buf;\n                        case 0x00000026: ee_d_vsubi(opcode); return buf;\n                        case 0x00000027: ee_d_vmsubi(opcode); return buf;\n                        case 0x00000028: ee_d_vadd(opcode); return buf;\n                        case 0x00000029: ee_d_vmadd(opcode); return buf;\n                        case 0x0000002A: ee_d_vmul(opcode); return buf;\n                        case 0x0000002B: ee_d_vmax(opcode); return buf;\n                        case 0x0000002C: ee_d_vsub(opcode); return buf;\n                        case 0x0000002D: ee_d_vmsub(opcode); return buf;\n                        case 0x0000002E: ee_d_vopmsub(opcode); return buf;\n                        case 0x0000002F: ee_d_vmini(opcode); return buf;\n                        case 0x00000030: ee_d_viadd(opcode); return buf;\n                        case 0x00000031: ee_d_visub(opcode); return buf;\n                        case 0x00000032: ee_d_viaddi(opcode); return buf;\n                        case 0x00000034: ee_d_viand(opcode); return buf;\n                        case 0x00000035: ee_d_vior(opcode); return buf;\n                        case 0x00000038: ee_d_vcallms(opcode); return buf;\n                        case 0x00000039: ee_d_callmsr(opcode); return buf;\n                        case 0x0000003C:\n                        case 0x0000003D:\n                        case 0x0000003E:\n                        case 0x0000003F: {\n                            uint32_t func = (opcode & 3) | ((opcode & 0x7c0) >> 4);\n\n                            switch (func) {\n                                case 0x00000000: ee_d_vaddax(opcode); return buf;\n                                case 0x00000001: ee_d_vadday(opcode); return buf;\n                                case 0x00000002: ee_d_vaddaz(opcode); return buf;\n                                case 0x00000003: ee_d_vaddaw(opcode); return buf;\n                                case 0x00000004: ee_d_vsubax(opcode); return buf;\n                                case 0x00000005: ee_d_vsubay(opcode); return buf;\n                                case 0x00000006: ee_d_vsubaz(opcode); return buf;\n                                case 0x00000007: ee_d_vsubaw(opcode); return buf;\n                                case 0x00000008: ee_d_vmaddax(opcode); return buf;\n                                case 0x00000009: ee_d_vmadday(opcode); return buf;\n                                case 0x0000000A: ee_d_vmaddaz(opcode); return buf;\n                                case 0x0000000B: ee_d_vmaddaw(opcode); return buf;\n                                case 0x0000000C: ee_d_vmsubax(opcode); return buf;\n                                case 0x0000000D: ee_d_vmsubay(opcode); return buf;\n                                case 0x0000000E: ee_d_vmsubaz(opcode); return buf;\n                                case 0x0000000F: ee_d_vmsubaw(opcode); return buf;\n                                case 0x00000010: ee_d_vitof0(opcode); return buf;\n                                case 0x00000011: ee_d_vitof4(opcode); return buf;\n                                case 0x00000012: ee_d_vitof12(opcode); return buf;\n                                case 0x00000013: ee_d_vitof15(opcode); return buf;\n                                case 0x00000014: ee_d_vftoi0(opcode); return buf;\n                                case 0x00000015: ee_d_vftoi4(opcode); return buf;\n                                case 0x00000016: ee_d_vftoi12(opcode); return buf;\n                                case 0x00000017: ee_d_vftoi15(opcode); return buf;\n                                case 0x00000018: ee_d_vmulax(opcode); return buf;\n                                case 0x00000019: ee_d_vmulay(opcode); return buf;\n                                case 0x0000001A: ee_d_vmulaz(opcode); return buf;\n                                case 0x0000001B: ee_d_vmulaw(opcode); return buf;\n                                case 0x0000001C: ee_d_vmulaq(opcode); return buf;\n                                case 0x0000001D: ee_d_vabs(opcode); return buf;\n                                case 0x0000001E: ee_d_vmulai(opcode); return buf;\n                                case 0x0000001F: ee_d_vclipw(opcode); return buf;\n                                case 0x00000020: ee_d_vaddaq(opcode); return buf;\n                                case 0x00000021: ee_d_vmaddaq(opcode); return buf;\n                                case 0x00000022: ee_d_vaddai(opcode); return buf;\n                                case 0x00000023: ee_d_vmaddai(opcode); return buf;\n                                case 0x00000024: ee_d_vsubaq(opcode); return buf;\n                                case 0x00000025: ee_d_vmsubaq(opcode); return buf;\n                                case 0x00000026: ee_d_vsubai(opcode); return buf;\n                                case 0x00000027: ee_d_vmsubai(opcode); return buf;\n                                case 0x00000028: ee_d_vadda(opcode); return buf;\n                                case 0x00000029: ee_d_vmadda(opcode); return buf;\n                                case 0x0000002A: ee_d_vmula(opcode); return buf;\n                                case 0x0000002C: ee_d_vsuba(opcode); return buf;\n                                case 0x0000002D: ee_d_vmsuba(opcode); return buf;\n                                case 0x0000002E: ee_d_vopmula(opcode); return buf;\n                                case 0x0000002F: ee_d_vnop(opcode); return buf;\n                                case 0x00000030: ee_d_vmove(opcode); return buf;\n                                case 0x00000031: ee_d_vmr32(opcode); return buf;\n                                case 0x00000034: ee_d_vlqi(opcode); return buf;\n                                case 0x00000035: ee_d_vsqi(opcode); return buf;\n                                case 0x00000036: ee_d_vlqd(opcode); return buf;\n                                case 0x00000037: ee_d_vsqd(opcode); return buf;\n                                case 0x00000038: ee_d_vdiv(opcode); return buf;\n                                case 0x00000039: ee_d_vsqrt(opcode); return buf;\n                                case 0x0000003A: ee_d_vrsqrt(opcode); return buf;\n                                case 0x0000003B: ee_d_vwaitq(opcode); return buf;\n                                case 0x0000003C: ee_d_vmtir(opcode); return buf;\n                                case 0x0000003D: ee_d_vmfir(opcode); return buf;\n                                case 0x0000003E: ee_d_vilwr(opcode); return buf;\n                                case 0x0000003F: ee_d_viswr(opcode); return buf;\n                                case 0x00000040: ee_d_vrnext(opcode); return buf;\n                                case 0x00000041: ee_d_vrget(opcode); return buf;\n                                case 0x00000042: ee_d_vrinit(opcode); return buf;\n                                case 0x00000043: ee_d_vrxor(opcode); return buf;\n                            }\n                        } break;\n                    }\n                } break;\n            }\n        } break;\n        case 0x50000000: ee_d_beql(opcode); return buf;\n        case 0x54000000: ee_d_bnel(opcode); return buf;\n        case 0x58000000: ee_d_blezl(opcode); return buf;\n        case 0x5C000000: ee_d_bgtzl(opcode); return buf;\n        case 0x60000000: ee_d_daddi(opcode); return buf;\n        case 0x64000000: ee_d_daddiu(opcode); return buf;\n        case 0x68000000: ee_d_ldl(opcode); return buf;\n        case 0x6C000000: ee_d_ldr(opcode); return buf;\n        case 0x70000000: { // mmi\n            switch (opcode & 0x0000003F) {\n                case 0x00000000: ee_d_madd(opcode); return buf;\n                case 0x00000001: ee_d_maddu(opcode); return buf;\n                case 0x00000004: ee_d_plzcw(opcode); return buf;\n                case 0x00000008: {\n                    switch (opcode & 0x000007C0) {\n                        case 0x00000000: ee_d_paddw(opcode); return buf;\n                        case 0x00000040: ee_d_psubw(opcode); return buf;\n                        case 0x00000080: ee_d_pcgtw(opcode); return buf;\n                        case 0x000000C0: ee_d_pmaxw(opcode); return buf;\n                        case 0x00000100: ee_d_paddh(opcode); return buf;\n                        case 0x00000140: ee_d_psubh(opcode); return buf;\n                        case 0x00000180: ee_d_pcgth(opcode); return buf;\n                        case 0x000001C0: ee_d_pmaxh(opcode); return buf;\n                        case 0x00000200: ee_d_paddb(opcode); return buf;\n                        case 0x00000240: ee_d_psubb(opcode); return buf;\n                        case 0x00000280: ee_d_pcgtb(opcode); return buf;\n                        case 0x00000400: ee_d_paddsw(opcode); return buf;\n                        case 0x00000440: ee_d_psubsw(opcode); return buf;\n                        case 0x00000480: ee_d_pextlw(opcode); return buf;\n                        case 0x000004C0: ee_d_ppacw(opcode); return buf;\n                        case 0x00000500: ee_d_paddsh(opcode); return buf;\n                        case 0x00000540: ee_d_psubsh(opcode); return buf;\n                        case 0x00000580: ee_d_pextlh(opcode); return buf;\n                        case 0x000005C0: ee_d_ppach(opcode); return buf;\n                        case 0x00000600: ee_d_paddsb(opcode); return buf;\n                        case 0x00000640: ee_d_psubsb(opcode); return buf;\n                        case 0x00000680: ee_d_pextlb(opcode); return buf;\n                        case 0x000006C0: ee_d_ppacb(opcode); return buf;\n                        case 0x00000780: ee_d_pext5(opcode); return buf;\n                        case 0x000007C0: ee_d_ppac5(opcode); return buf;\n                    }\n                } break;\n                case 0x00000009: {\n                    switch (opcode & 0x000007C0) {\n                        case 0x00000000: ee_d_pmaddw(opcode); return buf;\n                        case 0x00000080: ee_d_psllvw(opcode); return buf;\n                        case 0x000000C0: ee_d_psrlvw(opcode); return buf;\n                        case 0x00000100: ee_d_pmsubw(opcode); return buf;\n                        case 0x00000200: ee_d_pmfhi(opcode); return buf;\n                        case 0x00000240: ee_d_pmflo(opcode); return buf;\n                        case 0x00000280: ee_d_pinth(opcode); return buf;\n                        case 0x00000300: ee_d_pmultw(opcode); return buf;\n                        case 0x00000340: ee_d_pdivw(opcode); return buf;\n                        case 0x00000380: ee_d_pcpyld(opcode); return buf;\n                        case 0x00000400: ee_d_pmaddh(opcode); return buf;\n                        case 0x00000440: ee_d_phmadh(opcode); return buf;\n                        case 0x00000480: ee_d_pand(opcode); return buf;\n                        case 0x000004C0: ee_d_pxor(opcode); return buf;\n                        case 0x00000500: ee_d_pmsubh(opcode); return buf;\n                        case 0x00000540: ee_d_phmsbh(opcode); return buf;\n                        case 0x00000680: ee_d_pexeh(opcode); return buf;\n                        case 0x000006C0: ee_d_prevh(opcode); return buf;\n                        case 0x00000700: ee_d_pmulth(opcode); return buf;\n                        case 0x00000740: ee_d_pdivbw(opcode); return buf;\n                        case 0x00000780: ee_d_pexew(opcode); return buf;\n                        case 0x000007C0: ee_d_prot3w(opcode); return buf;\n                    }\n                } break;\n                case 0x00000010: ee_d_mfhi1(opcode); return buf;\n                case 0x00000011: ee_d_mthi1(opcode); return buf;\n                case 0x00000012: ee_d_mflo1(opcode); return buf;\n                case 0x00000013: ee_d_mtlo1(opcode); return buf;\n                case 0x00000018: ee_d_mult1(opcode); return buf;\n                case 0x00000019: ee_d_multu1(opcode); return buf;\n                case 0x0000001A: ee_d_div1(opcode); return buf;\n                case 0x0000001B: ee_d_divu1(opcode); return buf;\n                case 0x00000020: ee_d_madd1(opcode); return buf;\n                case 0x00000021: ee_d_maddu1(opcode); return buf;\n                case 0x00000028: {\n                    switch (opcode & 0x000007C0) {\n                        case 0x00000040: ee_d_pabsw(opcode); return buf;\n                        case 0x00000080: ee_d_pceqw(opcode); return buf;\n                        case 0x000000C0: ee_d_pminw(opcode); return buf;\n                        case 0x00000100: ee_d_padsbh(opcode); return buf;\n                        case 0x00000140: ee_d_pabsh(opcode); return buf;\n                        case 0x00000180: ee_d_pceqh(opcode); return buf;\n                        case 0x000001C0: ee_d_pminh(opcode); return buf;\n                        case 0x00000280: ee_d_pceqb(opcode); return buf;\n                        case 0x00000400: ee_d_padduw(opcode); return buf;\n                        case 0x00000440: ee_d_psubuw(opcode); return buf;\n                        case 0x00000480: ee_d_pextuw(opcode); return buf;\n                        case 0x00000500: ee_d_padduh(opcode); return buf;\n                        case 0x00000540: ee_d_psubuh(opcode); return buf;\n                        case 0x00000580: ee_d_pextuh(opcode); return buf;\n                        case 0x00000600: ee_d_paddub(opcode); return buf;\n                        case 0x00000640: ee_d_psubub(opcode); return buf;\n                        case 0x00000680: ee_d_pextub(opcode); return buf;\n                        case 0x000006C0: ee_d_qfsrv(opcode); return buf;\n                    }\n                } break;\n                case 0x00000029: {\n                    switch (opcode & 0x000007C0) {\n                        case 0x00000000: ee_d_pmadduw(opcode); return buf;\n                        case 0x000000C0: ee_d_psravw(opcode); return buf;\n                        case 0x00000200: ee_d_pmthi(opcode); return buf;\n                        case 0x00000240: ee_d_pmtlo(opcode); return buf;\n                        case 0x00000280: ee_d_pinteh(opcode); return buf;\n                        case 0x00000300: ee_d_pmultuw(opcode); return buf;\n                        case 0x00000340: ee_d_pdivuw(opcode); return buf;\n                        case 0x00000380: ee_d_pcpyud(opcode); return buf;\n                        case 0x00000480: ee_d_por(opcode); return buf;\n                        case 0x000004C0: ee_d_pnor(opcode); return buf;\n                        case 0x00000680: ee_d_pexch(opcode); return buf;\n                        case 0x000006C0: ee_d_pcpyh(opcode); return buf;\n                        case 0x00000780: ee_d_pexcw(opcode); return buf;\n                    }\n                } break;\n                case 0x00000030: {\n                    switch (opcode & 0x000007C0) {\n                        case 0x00000000: ee_d_pmfhllw(opcode); return buf;\n                        case 0x00000040: ee_d_pmfhluw(opcode); return buf;\n                        case 0x00000080: ee_d_pmfhlslw(opcode); return buf;\n                        case 0x000000c0: ee_d_pmfhllh(opcode); return buf;\n                        case 0x00000100: ee_d_pmfhlsh(opcode); return buf;\n                    }\n                } break;\n                case 0x00000031: ee_d_pmthl(opcode); return buf;\n                case 0x00000034: ee_d_psllh(opcode); return buf;\n                case 0x00000036: ee_d_psrlh(opcode); return buf;\n                case 0x00000037: ee_d_psrah(opcode); return buf;\n                case 0x0000003C: ee_d_psllw(opcode); return buf;\n                case 0x0000003E: ee_d_psrlw(opcode); return buf;\n                case 0x0000003F: ee_d_psraw(opcode); return buf;\n            }\n        } break;\n        case 0x78000000: ee_d_lq(opcode); return buf;\n        case 0x7C000000: ee_d_sq(opcode); return buf;\n        case 0x80000000: ee_d_lb(opcode); return buf;\n        case 0x84000000: ee_d_lh(opcode); return buf;\n        case 0x88000000: ee_d_lwl(opcode); return buf;\n        case 0x8C000000: ee_d_lw(opcode); return buf;\n        case 0x90000000: ee_d_lbu(opcode); return buf;\n        case 0x94000000: ee_d_lhu(opcode); return buf;\n        case 0x98000000: ee_d_lwr(opcode); return buf;\n        case 0x9C000000: ee_d_lwu(opcode); return buf;\n        case 0xA0000000: ee_d_sb(opcode); return buf;\n        case 0xA4000000: ee_d_sh(opcode); return buf;\n        case 0xA8000000: ee_d_swl(opcode); return buf;\n        case 0xAC000000: ee_d_sw(opcode); return buf;\n        case 0xB0000000: ee_d_sdl(opcode); return buf;\n        case 0xB4000000: ee_d_sdr(opcode); return buf;\n        case 0xB8000000: ee_d_swr(opcode); return buf;\n        case 0xBC000000: ee_d_cache(opcode); return buf;\n        case 0xC4000000: ee_d_lwc1(opcode); return buf;\n        case 0xCC000000: ee_d_pref(opcode); return buf;\n        case 0xD8000000: ee_d_lqc2(opcode); return buf;\n        case 0xDC000000: ee_d_ld(opcode); return buf;\n        case 0xE4000000: ee_d_swc1(opcode); return buf;\n        case 0xF8000000: ee_d_sqc2(opcode); return buf;\n        case 0xFC000000: ee_d_sd(opcode); return buf;\n    }\n\n    ee_d_invalid(opcode);\n    \n    return buf;\n}"
  },
  {
    "path": "src/ee/ee_dis.h",
    "content": "#ifndef EE_DIS_H\n#define EE_DIS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\nstruct ee_dis_state {\n    int print_address;\n    int print_opcode;\n    uint32_t pc;\n};\n\nchar* ee_disassemble(char* buf, uint32_t opcode, struct ee_dis_state* dis_state);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/ee_uncached.c",
    "content": "#include <stdio.h>\n#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <math.h>\n#include <signal.h>\n#include <fenv.h>\n\n#ifdef _EE_USE_INTRINSICS\n#include <immintrin.h>\n#include <tmmintrin.h>\n#include <emmintrin.h>\n#include <smmintrin.h>\n#endif\n\n#include \"ee_uncached.h\"\n#include \"ee_dis.h\"\n\n#define max(a, b) ((a) > (b) ? (a) : (b))\n#define min(a, b) ((a) < (b) ? (a) : (b))\n\n#ifdef _WIN32\n#define SSUBOVF64 __builtin_ssubll_overflow\n#define SADDOVF64 __builtin_saddll_overflow\n#else\n#define SSUBOVF64 __builtin_ssubl_overflow\n#define SADDOVF64 __builtin_saddl_overflow\n#endif\n\n// file = fopen(\"vu.dump\", \"a\"); fprintf(file, #ins \"\\n\"); fclose(file);\n#define VU_LOWER(ins) { ee->vu0->lower = ee->opcode; vu_i_ ## ins(ee->vu0); }\n#define VU_UPPER(ins) { ee->vu0->upper = ee->opcode; vu_i_ ## ins(ee->vu0); }\n\nstatic FILE* file;\nstatic int p = 0;\n\nstatic inline int fast_abs32(int a) {\n    uint32_t m = a >> 31;\n\n    return (a ^ m) + (m & 1);\n}\n\nstatic inline int16_t fast_abs16(int16_t a) {\n    uint16_t m = a >> 15;\n\n    return (a ^ m) + (m & 1);\n}\n\nstatic inline int16_t saturate16(int32_t word) {\n    if (word > (int32_t)0x00007FFF) {\n        return 0x7FFF;\n    } else if (word < (int32_t)0xFFFF80000) {\n        return 0x8000;\n    } else {\n        return (int16_t)word;\n    }\n}\n\n#ifdef _EE_USE_INTRINSICS\nstatic inline __m128i _mm_adds_epi32(__m128i a, __m128i b) {\n    const __m128i m = _mm_set1_epi32(0x7fffffff);\n    __m128i r = _mm_add_epi32(a, b);\n    __m128i sb = _mm_srli_epi32(a, 31);\n    __m128i sat = _mm_add_epi32(m, sb);\n    __m128i sx = _mm_xor_si128(a, b);\n    __m128i o = _mm_andnot_si128(sx, _mm_xor_si128(a, r));\n\n    // To-do: Use SSE3 version when SSE4.1 isn't available\n    return _mm_castps_si128(_mm_blendv_ps(_mm_castsi128_ps(r),\n                                          _mm_castsi128_ps(sat),\n                                          _mm_castsi128_ps(o)));\n}\n\nstatic inline __m128i _mm_adds_epu32(__m128i a, __m128i b) {\n    const __m128i m = _mm_set1_epi32(0xffffffff);\n\n    __m128i x = _mm_xor_si128(a, m);\n    __m128i c = _mm_min_epu32(b, x);\n\n    return _mm_add_epi32(a, c);\n}\n#endif\n\nstatic inline uint32_t unpack_5551_8888(uint32_t v) {\n    return ((v & 0x001f) << 3) |\n           ((v & 0x03e0) << 6) |\n           ((v & 0x7c00) << 9) |\n           ((v & 0x8000) << 16);\n}\n\n#define EE_KUSEG 0\n#define EE_KSEG0 1\n#define EE_KSEG1 2\n#define EE_KSSEG 3\n#define EE_KSEG3 4\n\n#define EE_D_RS ((ee->opcode >> 21) & 0x1f)\n#define EE_D_FS ((ee->opcode >> 11) & 0x1f)\n#define EE_D_RT ((ee->opcode >> 16) & 0x1f)\n#define EE_D_RD ((ee->opcode >> 11) & 0x1f)\n#define EE_D_FD ((ee->opcode >> 6) & 0x1f)\n#define EE_D_SA ((ee->opcode >> 6) & 0x1f)\n#define EE_D_I15 ((ee->opcode >> 6) & 0x7fff)\n#define EE_D_I16 (ee->opcode & 0xffff)\n#define EE_D_I26 (ee->opcode & 0x3ffffff)\n#define EE_D_SI26 ((int32_t)(EE_D_I26 << 6) >> 4)\n#define EE_D_SI16 ((int32_t)(EE_D_I16 << 16) >> 14)\n\n#define EE_RT ee->r[EE_D_RT].ul64\n#define EE_RD ee->r[EE_D_RD].ul64\n#define EE_RS ee->r[EE_D_RS].ul64\n#define EE_RT32 ee->r[EE_D_RT].ul32\n#define EE_RD32 ee->r[EE_D_RD].ul32\n#define EE_RS32 ee->r[EE_D_RS].ul32\n#define EE_FD ee->f[EE_D_FD].f\n#define EE_FT (fpu_cvtf(ee->f[EE_D_RT].f))\n#define EE_FS (fpu_cvtf(ee->f[EE_D_FS].f))\n#define EE_FT32 ee->f[EE_D_RT].u32\n#define EE_FD32 ee->f[EE_D_FD].u32\n#define EE_FS32 ee->f[EE_D_FS].u32\n\n#define EE_HI0 ee->hi.u64[0]\n#define EE_LO0 ee->lo.u64[0]\n#define EE_HI1 ee->hi.u64[1]\n#define EE_LO1 ee->lo.u64[1]\n\n#define BRANCH(cond, offset) if (cond) { \\\n    ee->next_pc = ee->next_pc + (offset); \\\n    ee->next_pc = ee->next_pc - 4; \\\n    ee->branch = 1; \\\n    ee->branch_taken = 1; }\n\n#define BRANCH_LIKELY(cond, offset) \\\n    BRANCH(cond, offset) else { ee->pc += 4; ee->next_pc += 4; }\n\n#define SE6432(v) ((int64_t)((int32_t)(v)))\n#define SE6416(v) ((int64_t)((int16_t)(v)))\n#define SE648(v) ((int64_t)((int8_t)(v)))\n#define SE3216(v) ((int32_t)((int16_t)(v)))\n\nstatic inline void ee_print_disassembly(struct ee_state* ee) {\n    char buf[128];\n    struct ee_dis_state ds;\n\n    ds.print_address = 1;\n    ds.print_opcode = 1;\n    ds.pc = ee->pc;\n\n    puts(ee_disassemble(buf, ee->opcode, &ds));\n}\n\nstatic inline int ee_get_segment(uint32_t virt) {\n    switch (virt & 0xe0000000) {\n        case 0x00000000: return EE_KUSEG;\n        case 0x20000000: return EE_KUSEG;\n        case 0x40000000: return EE_KUSEG;\n        case 0x60000000: return EE_KUSEG;\n        case 0x80000000: return EE_KSEG0;\n        case 0xa0000000: return EE_KSEG1;\n        case 0xc0000000: return EE_KSSEG;\n        case 0xe0000000: return EE_KSEG3;\n    }\n\n    return EE_KUSEG;\n}\n\nconst uint32_t ee_bus_region_mask_table[] = {\n    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\n    0x7fffffff, 0x1fffffff, 0xffffffff, 0xffffffff\n};\n\nstatic inline float fpu_cvtf(float f) {\n    uint32_t u32 = *(uint32_t*)&f;\n\n    switch (u32 & 0x7f800000) {\n        case 0x0: {\n            u32 &= 0x80000000;\n\n            return *(float*)&u32;\n        } break;\n\n        case 0x7f800000: {\n            uint32_t result = (u32 & 0x80000000) | 0x7f7fffff;\n\n            return *(float*)&result;\n        }\n    }\n\n    return *(float*)&u32;\n}\n\nstatic inline float fpu_cvtsw(union ee_fpu_reg* reg) {\n    switch (reg->u32 & 0x7F800000) {\n        case 0x0: {\n            reg->u32 &= 0x80000000;\n        } break;\n\n        case 0x7F800000: {\n            reg->u32 = (reg->u32 & 0x80000000) | 0x7F7FFFFF;\n        } break;\n    }\n\n    return reg->f;\n}\n\nstatic inline void fpu_cvtws(union ee_fpu_reg* d, union ee_fpu_reg* s) {\n    if ((s->u32 & 0x7F800000) <= 0x4E800000)\n        d->s32 = (int32_t)fpu_cvtf(s->f);\n    else if ((s->u32 & 0x80000000) == 0)\n        d->u32 = 0x7FFFFFFF;\n    else\n        d->u32 = 0x80000000;\n}\n\nstatic inline int fpu_check_overflow(struct ee_state* ee, union ee_fpu_reg* reg) {\n    if ((reg->u32 & ~0x80000000) == 0x7f800000) {\n        reg->u32 = (reg->u32 & 0x80000000) | 0x7f7fffff;\n        ee->fcr |= FPU_FLG_O | FPU_FLG_SO;\n\n        return 1;\n    }\n\n    ee->fcr &= ~FPU_FLG_O;\n\n    return 0;\n}\n\nstatic inline int fpu_check_underflow(struct ee_state* ee, union ee_fpu_reg* reg) {\n    if (((reg->u32 & 0x7F800000) == 0) && ((reg->u32 & 0x007FFFFF) != 0)) {\n        reg->u32 &= 0x80000000;\n        ee->fcr |= FPU_FLG_U | FPU_FLG_SU;\n\n        return 1;\n    }\n\n    ee->fcr &= ~FPU_FLG_U;\n\n    return 0;\n}\n\nstatic inline int fpu_check_overflow_no_flags(struct ee_state* ee, union ee_fpu_reg* reg) {\n    if ((reg->u32 & ~0x80000000) == 0x7f800000) {\n        reg->u32 = (reg->u32 & 0x80000000) | 0x7f7fffff;\n\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic inline int fpu_check_underflow_no_flags(struct ee_state* ee, union ee_fpu_reg* reg) {\n    if (((reg->u32 & 0x7F800000) == 0) && ((reg->u32 & 0x007FFFFF) != 0)) {\n        reg->u32 &= 0x80000000;\n\n        return 1;\n    }\n\n    return 0;\n}\n\nstatic inline int fpu_max(int32_t a, int32_t b) {\n    return (a < 0 && b < 0) ? min(a, b) : max(a, b);\n}\n\nstatic inline int fpu_min(int32_t a, int32_t b) {\n    return (a < 0 && b < 0) ? max(a, b) : min(a, b);\n}\n\nstatic inline struct ee_vtlb_entry* ee_search_vtlb(struct ee_state* ee, uint32_t virt) {\n    struct ee_vtlb_entry* entry = NULL;\n\n    for (int i = 0; i < 48; i++) {\n        ee->vtlb[i].mask;\n    }\n}\n\nstatic inline int ee_translate_virt(struct ee_state* ee, uint32_t virt, uint32_t* phys) {\n    int seg = ee_get_segment(virt);\n\n    // Assume we're in kernel mode\n    if (seg == EE_KSEG0 || seg == EE_KSEG1) {\n        *phys = virt & 0x1fffffff;\n\n        return 0;\n    }\n\n    if (virt >= 0x00000000 && virt <= 0x01FFFFFF) {\n        *phys = virt & 0x1FFFFFF;\n\n        return 0;\n    }\n\n    if (virt >= 0x10000000 && virt <= 0x1FFFFFFF) {\n        *phys = virt & 0x1FFFFFFF;\n\n        return 0;\n    }\n\n    if (virt >= 0x20000000 && virt <= 0x21FFFFFF) {\n        *phys = virt & 0x1FFFFFF;\n\n        return 0;\n    }\n\n    if (virt >= 0x30000000 && virt <= 0x31FFFFFF) {\n        *phys = virt & 0x1FFFFFF;\n\n        return 0;\n    }\n\n    // DECI2 area\n    if (virt >= 0xFFFF8000) {\n        *phys = (virt - 0xFFFF8000) + 0x78000;\n\n        return 0;\n    }\n\n    *phys = virt & ee_bus_region_mask_table[virt >> 29];\n\n    // printf(\"ee: Unhandled virtual address %08x @ cyc=%ld\\n\", virt, ee->total_cycles);\n\n    // *(int*)0 = 0;\n\n    // exit(1);\n\n    // To-do: MMU mapping\n    *phys = virt & 0x1fffffff;\n\n    return 0;\n}\n\n#define BUS_READ_FUNC(b)                                                        \\\n    static inline uint64_t bus_read ## b(struct ee_state* ee, uint32_t addr) {  \\\n        if ((addr & 0xf0000000) == 0x70000000)                                  \\\n            return ps2_ram_read ## b(ee->scratchpad, addr & 0x3fff);            \\\n        uint32_t phys;                                                          \\\n        if (ee_translate_virt(ee, addr, &phys)) {                               \\\n            printf(\"ee: TLB mapping error\\n\");                                  \\\n            exit(1);                                                            \\\n            return 0;                                                           \\\n        }                                                                       \\\n        return ee->bus.read ## b(ee->bus.udata, phys);                          \\\n    }\n\n#define BUS_WRITE_FUNC(b)                                                                   \\\n    static inline void bus_write ## b(struct ee_state* ee, uint32_t addr, uint64_t data) {  \\\n        if ((addr & 0xf0000000) == 0x70000000)                                              \\\n            { ps2_ram_write ## b(ee->scratchpad, addr & 0x3fff, data); return; }            \\\n        uint32_t phys;                                                                      \\\n        if (ee_translate_virt(ee, addr, &phys)) {                                           \\\n            printf(\"ee: TLB mapping error\\n\");                                              \\\n            exit(1);                                                                        \\\n            return;                                                                         \\\n        }                                                                                   \\\n        ee->bus.write ## b(ee->bus.udata, phys, data);                                      \\\n    }\n\nBUS_READ_FUNC(8)\nBUS_READ_FUNC(16)\nBUS_READ_FUNC(32)\nBUS_READ_FUNC(64)\n\nstatic inline uint128_t bus_read128(struct ee_state* ee, uint32_t addr) {\n    if ((addr & 0xf0000000) == 0x70000000)\n        return ps2_ram_read128(ee->scratchpad, addr & 0x3ff0);\n\n    uint32_t phys;\n\n    if (ee_translate_virt(ee, addr, &phys)) {\n        printf(\"ee: TLB mapping error\\n\");\n\n        exit(1);\n\n        return (uint128_t){ .u64[0] = 0, .u64[1] = 0 };\n    }\n\n    return ee->bus.read128(ee->bus.udata, phys);\n}\n\nBUS_WRITE_FUNC(8)\nBUS_WRITE_FUNC(16)\nBUS_WRITE_FUNC(32)\nBUS_WRITE_FUNC(64)\n\nstatic inline void bus_write128(struct ee_state* ee, uint32_t addr, uint128_t data) {\n    if ((addr & 0xf0000000) == 0x70000000) {\n        ps2_ram_write128(ee->scratchpad, addr & 0x3ff0, data);\n\n        return;\n    }\n\n    uint32_t phys;\n\n    if (ee_translate_virt(ee, addr, &phys)) {\n        printf(\"ee: TLB mapping error\\n\");\n\n        exit(1);\n    }\n\n    ee->bus.write128(ee->bus.udata, phys, data);\n}\n\n#undef BUS_READ_FUNC\n#undef BUS_WRITE_FUNC\n\nstatic inline int ee_skip_fmv(struct ee_state* ee, uint32_t addr) {\n    if (bus_read32(ee, addr + 4) != 0x03E00008)\n        return 0;\n\n    uint32_t code = bus_read32(ee, addr);\n    uint32_t p1 = 0x8c800040;\n    uint32_t p2 = 0x8c020000 | (code & 0x1f0000) << 5;\n\n    if ((code & 0xffe0ffff) != p1) {\n        return 0;\n    }\n\n    if (bus_read32(ee, addr + 8) != p2) {\n        return 0;\n    }\n\n    printf(\"ee: Skipping FMV\\n\");\n\n    return 1;\n}\n\nstatic inline void ee_set_pc(struct ee_state* ee, uint32_t addr) {\n    if (ee_skip_fmv(ee, addr))\n        return;\n\n    ee->pc = addr;\n    ee->next_pc = addr + 4;\n}\n\nstatic inline void ee_set_pc_delayed(struct ee_state* ee, uint32_t addr) {\n    if (ee_skip_fmv(ee, addr))\n        return;\n\n    ee->next_pc = addr;\n    ee->branch = 1;\n}\n\nvoid ee_exception_level1(struct ee_state* ee, uint32_t cause) {\n    uint32_t vec = EE_VEC_COMMON;\n\n    ee->cause &= ~EE_CAUSE_EXC;\n    ee->cause |= cause;\n\n    if (!(ee->status & EE_SR_EXL)) {\n        ee->epc = ee->pc - 4;\n\n        if (ee->delay_slot) {\n            ee->epc -= 4;\n            ee->cause |= EE_CAUSE_BD;\n        } else {\n            ee->cause &= ~EE_CAUSE_BD;\n        }\n    }\n\n    ee->status |= EE_SR_EXL;\n\n    if (cause == CAUSE_EXC1_INT)\n        vec = EE_VEC_IRQ;\n\n    uint32_t addr = ((ee->status & EE_SR_BEV) ? 0xbfc00200 : 0x80000000) + vec;\n\n    ee_set_pc(ee, addr);\n}\n\nstatic inline void ee_exception_level2(struct ee_state* ee, uint32_t cause) {\n    uint32_t vec;\n\n    ee->cause &= ~EE_CAUSE_EXC2;\n    ee->cause |= cause;\n\n    ee->errorepc = ee->pc - 4;\n\n    if (ee->delay_slot) {\n        ee->errorepc -= 4;\n        ee->cause |= EE_CAUSE_BD2;\n    } else {\n        ee->cause &= ~EE_CAUSE_BD2;\n    }\n\n    ee->status |= EE_SR_ERL;\n\n    if ((cause == CAUSE_EXC2_RES) | (cause == CAUSE_EXC2_NMI)) {\n        ee_set_pc(ee, EE_VEC_RESET);\n\n        return;\n    }\n\n    if (cause == CAUSE_EXC2_PERFC) {\n        vec = EE_VEC_COUNTER;\n    } else {\n        vec = EE_VEC_DEBUG;\n    }\n\n    ee_set_pc(ee, ((ee->status & EE_SR_DEV) ? 0xbfc00200 : 0x80000000) + vec);\n}\n\nstatic inline void ee_check_irq(struct ee_state* ee) {\n    int irq_enabled = (ee->status & EE_SR_IE) && (ee->status & EE_SR_EIE) &&\n        (!(ee->status & EE_SR_EXL)) && (!(ee->status & EE_SR_ERL));\n    int int0_pending = (ee->status & EE_SR_IM2) && (ee->cause & EE_CAUSE_IP2);\n    int int1_pending = (ee->status & EE_SR_IM3) && (ee->cause & EE_CAUSE_IP3);\n\n    if (irq_enabled && (int0_pending || int1_pending)) {\n        ee->pc += 4;\n\n        // printf(\"ee: Handling irq at pc=%08x (int0=%d (%d) int1=%d (%d)) sr=%08x delay_slot=%d\\n\",\n        //     ee->pc,\n        //     int0_pending, !!(ee->status & EE_SR_IM2),\n        //     int1_pending, !!(ee->status & EE_SR_IM3),\n        //     ee->status,\n        //     ee->delay_slot\n        // );\n\n        ee_exception_level1(ee, CAUSE_EXC1_INT);\n    }\n}\n\nvoid ee_set_int0(struct ee_state* ee, int v) {\n    if (v) {\n        ee->cause |= EE_CAUSE_IP2;\n    } else {\n        ee->cause &= ~EE_CAUSE_IP2;\n    }\n}\n\nvoid ee_set_int1(struct ee_state* ee, int v) {\n    if (v) {\n        ee->cause |= EE_CAUSE_IP3;\n    } else {\n        ee->cause &= ~EE_CAUSE_IP3;\n    }\n}\n\nvoid ee_set_cpcond0(struct ee_state* ee, int v) {\n    ee->cpcond0 = v;\n}\n\nstatic inline void ee_i_abss(struct ee_state* ee) {\n    ee->f[EE_D_FD].u32 = ee->f[EE_D_FS].u32 & 0x7fffffff;\n    // EE_FD = fabsf(EE_FS);\n}\nstatic inline void ee_i_add(struct ee_state* ee) {\n    int32_t s = EE_RS;\n    int32_t t = EE_RT;\n\n    int32_t r = s + t;\n    uint32_t o = (s ^ r) & (t ^ r);\n\n    if (o & 0x80000000) {\n        ee_exception_level1(ee, CAUSE_EXC1_OV);\n    } else {\n        EE_RD = SE6432(r);\n    }\n}\nstatic inline void ee_i_addas(struct ee_state* ee) {\n    ee->a.f = EE_FS + EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->a))\n        return;\n\n    fpu_check_underflow(ee, &ee->a);\n}\nstatic inline void ee_i_addi(struct ee_state* ee) {\n    int32_t s = EE_RS;\n    int32_t t = SE3216(EE_D_I16);\n    int32_t r;\n\n    if (__builtin_sadd_overflow(s, t, &r)) {\n        ee_exception_level1(ee, CAUSE_EXC1_OV);\n    } else {\n        EE_RT = SE6432(r);\n    }\n}\nstatic inline void ee_i_addiu(struct ee_state* ee) {\n    EE_RT = SE6432(EE_RS32 + SE3216(EE_D_I16));\n}\nstatic inline void ee_i_adds(struct ee_state* ee) {\n    int d = EE_D_FD;\n\n    ee->f[d].f = EE_FS + EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow(ee, &ee->f[d]);\n}\nstatic inline void ee_i_addu(struct ee_state* ee) {\n    EE_RD = SE6432(EE_RS + EE_RT);\n}\nstatic inline void ee_i_and(struct ee_state* ee) {\n    EE_RD = EE_RS & EE_RT;\n}\nstatic inline void ee_i_andi(struct ee_state* ee) {\n    EE_RT = EE_RS & EE_D_I16;\n}\nstatic inline void ee_i_bc0f(struct ee_state* ee) {\n    BRANCH(!ee->cpcond0, EE_D_SI16);\n}\nstatic inline void ee_i_bc0fl(struct ee_state* ee) {\n    BRANCH_LIKELY(!ee->cpcond0, EE_D_SI16);\n}\nstatic inline void ee_i_bc0t(struct ee_state* ee) {\n    BRANCH(ee->cpcond0, EE_D_SI16);\n}\nstatic inline void ee_i_bc0tl(struct ee_state* ee) {\n    BRANCH_LIKELY(ee->cpcond0, EE_D_SI16);\n}\nstatic inline void ee_i_bc1f(struct ee_state* ee) {\n    BRANCH((ee->fcr & (1 << 23)) == 0, EE_D_SI16);\n}\nstatic inline void ee_i_bc1fl(struct ee_state* ee) {\n    BRANCH_LIKELY((ee->fcr & (1 << 23)) == 0, EE_D_SI16);\n}\nstatic inline void ee_i_bc1t(struct ee_state* ee) {\n    BRANCH((ee->fcr & (1 << 23)) != 0, EE_D_SI16);\n}\nstatic inline void ee_i_bc1tl(struct ee_state* ee) {\n    BRANCH_LIKELY((ee->fcr & (1 << 23)) != 0, EE_D_SI16);\n}\nstatic inline void ee_i_bc2f(struct ee_state* ee) { BRANCH(1, EE_D_SI16); }\nstatic inline void ee_i_bc2fl(struct ee_state* ee) { BRANCH_LIKELY(1, EE_D_SI16); }\nstatic inline void ee_i_bc2t(struct ee_state* ee) { BRANCH(0, EE_D_SI16); }\nstatic inline void ee_i_bc2tl(struct ee_state* ee) { BRANCH_LIKELY(0, EE_D_SI16); }\nstatic inline void ee_i_beq(struct ee_state* ee) {\n    BRANCH(EE_RS == EE_RT, EE_D_SI16);\n}\nstatic inline void ee_i_beql(struct ee_state* ee) {\n    BRANCH_LIKELY(EE_RS == EE_RT, EE_D_SI16);\n}\nstatic inline void ee_i_bgez(struct ee_state* ee) {\n    BRANCH((int64_t)EE_RS >= (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bgezal(struct ee_state* ee) {\n    ee->r[31].ul64 = ee->next_pc;\n\n    BRANCH((int64_t)EE_RS >= (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bgezall(struct ee_state* ee) {\n    ee->r[31].ul64 = ee->next_pc;\n\n    BRANCH_LIKELY((int64_t)EE_RS >= (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bgezl(struct ee_state* ee) {\n    BRANCH_LIKELY((int64_t)EE_RS >= (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bgtz(struct ee_state* ee) {\n    BRANCH((int64_t)EE_RS > (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bgtzl(struct ee_state* ee) {\n    BRANCH_LIKELY((int64_t)EE_RS > (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_blez(struct ee_state* ee) {\n    BRANCH((int64_t)EE_RS <= (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_blezl(struct ee_state* ee) {\n    BRANCH_LIKELY((int64_t)EE_RS <= (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bltz(struct ee_state* ee) {\n    BRANCH((int64_t)EE_RS < (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bltzal(struct ee_state* ee) {\n    ee->r[31].ul64 = ee->next_pc;\n\n    BRANCH((int64_t)EE_RS < (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bltzall(struct ee_state* ee) {\n    ee->r[31].ul64 = ee->next_pc;\n\n    BRANCH_LIKELY((int64_t)EE_RS < (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bltzl(struct ee_state* ee) {\n    BRANCH_LIKELY((int64_t)EE_RS < (int64_t)0, EE_D_SI16);\n}\nstatic inline void ee_i_bne(struct ee_state* ee) {\n    BRANCH(EE_RS != EE_RT, EE_D_SI16);\n}\nstatic inline void ee_i_bnel(struct ee_state* ee) {\n    BRANCH_LIKELY(EE_RS != EE_RT, EE_D_SI16);\n}\nstatic inline void ee_i_break(struct ee_state* ee) {\n    ee_exception_level1(ee, CAUSE_EXC1_BP);\n}\nstatic inline void ee_i_cache(struct ee_state* ee) {\n    /* To-do: Cache emulation */\n} \nstatic inline void ee_i_ceq(struct ee_state* ee) {\n    if (EE_FS == EE_FT) {\n        ee->fcr |= 1 << 23;    \n    } else {\n        ee->fcr &= ~(1 << 23);\n    }\n}\nstatic inline void ee_i_cf(struct ee_state* ee) {\n    ee->fcr &= ~(1 << 23);\n}\nstatic inline void ee_i_cfc1(struct ee_state* ee) {\n    EE_RT = (EE_D_FS >= 16) ? ee->fcr : 0x2e30;\n}\nstatic inline void ee_i_cfc2(struct ee_state* ee) {\n    EE_RT = SE6432(ps2_vu_read_vi(ee->vu0, EE_D_RD));\n}\nstatic inline void ee_i_cle(struct ee_state* ee) {\n    if (EE_FS <= EE_FT) {\n        ee->fcr |= 1 << 23;\n    } else {\n        ee->fcr &= ~(1 << 23);\n    }\n}\nstatic inline void ee_i_clt(struct ee_state* ee) {\n    if (EE_FS < EE_FT) {\n        ee->fcr |= 1 << 23;\n    } else {\n        ee->fcr &= ~(1 << 23);\n    }\n}\nstatic inline void ee_i_ctc1(struct ee_state* ee) {\n    if (EE_D_FS < 16)\n        return;\n\n    ee->fcr = (ee->fcr & ~(0x83c078)) | (EE_RT & 0x83c078);\n}\nstatic inline void ee_i_ctc2(struct ee_state* ee) {\n    // To-do: Handle FBRST, VPU_STAT, CMSAR1\n    int d = EE_D_RD;\n\n    static const char* regs[] = {\n        \"Status flag\",\n        \"MAC flag\",\n        \"clipping flag\",\n        \"reserved\",\n        \"R\",\n        \"I\",\n        \"Q\",\n        \"reserved\",\n        \"reserved\",\n        \"reserved\",\n        \"TPC\",\n        \"CMSAR0\",\n        \"FBRST\",\n        \"VPU-STAT\",\n        \"reserved\",\n        \"CMSAR1\",\n    };\n\n    ps2_vu_write_vi(ee->vu0, d, EE_RT32);\n}\nstatic inline void ee_i_cvts(struct ee_state* ee) {\n    EE_FD = (float)ee->f[EE_D_FS].s32;\n    EE_FD = fpu_cvtsw(&ee->f[EE_D_FD]);\n}\nstatic inline void ee_i_cvtw(struct ee_state* ee) {\n    fpu_cvtws(&ee->f[EE_D_FD], &ee->f[EE_D_FS]);\n}\nstatic inline void ee_i_dadd(struct ee_state* ee) {\n    int64_t r;\n\n    if (SADDOVF64((int64_t)EE_RS, (int64_t)EE_RT, &r)) {\n        ee_exception_level1(ee, CAUSE_EXC1_OV);\n    } else {\n        EE_RD = r;\n    }\n}\nstatic inline void ee_i_daddi(struct ee_state* ee) {\n    int64_t r;\n\n    if (SADDOVF64((int64_t)EE_RS, SE6416(EE_D_I16), &r)) {\n        ee_exception_level1(ee, CAUSE_EXC1_OV);\n    } else {\n        EE_RT = r;\n    }\n}\nstatic inline void ee_i_daddiu(struct ee_state* ee) {\n    EE_RT = EE_RS + SE6416(EE_D_I16);\n}\nstatic inline void ee_i_daddu(struct ee_state* ee) {\n    EE_RD = EE_RS + EE_RT;\n}\nstatic inline void ee_i_di(struct ee_state* ee) {\n    int edi = ee->status & EE_SR_EDI;\n    int exl = ee->status & EE_SR_EXL;\n    int erl = ee->status & EE_SR_ERL;\n    int ksu = ee->status & EE_SR_KSU;\n    \n    if (edi || exl || erl || !ksu)\n        ee->status &= ~EE_SR_EIE;\n}\nstatic inline void ee_i_div(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int s = EE_D_RS;\n\n    if (ee->r[s].ul32 == 0x80000000 && ee->r[t].ul32 == 0xffffffff) {\n        EE_LO0 = (int32_t)0x80000000;\n        EE_HI0 = 0;\n    } else if (ee->r[t].ul32 != 0) {\n        EE_HI0 = SE6432(ee->r[s].sl32 % ee->r[t].sl32);\n        EE_LO0 = SE6432(ee->r[s].sl32 / ee->r[t].sl32);\n    } else {\n        EE_HI0 = SE6432(ee->r[s].ul32);\n        EE_LO0 = ((int32_t)ee->r[s].ul32 < 0) ? 1 : -1;\n    }\n}\nstatic inline void ee_i_div1(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int s = EE_D_RS;\n\n    if (ee->r[s].ul32 == 0x80000000 && ee->r[t].ul32 == 0xffffffff) {\n        EE_LO1 = (int32_t)0x80000000;\n        EE_HI1 = 0;\n    } else if (ee->r[t].ul32 != 0) {\n        EE_HI1 = SE6432(ee->r[s].sl32 % ee->r[t].sl32);\n        EE_LO1 = SE6432(ee->r[s].sl32 / ee->r[t].sl32);\n    } else {\n        EE_HI1 = SE6432(ee->r[s].ul32);\n        EE_LO1 = ((int32_t)ee->r[s].ul32 < 0) ? 1 : -1;\n    }\n}\nstatic inline void ee_i_divs(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int d = EE_D_FD;\n    int s = EE_D_FS;\n\n    ee->fcr &= ~(FPU_FLG_I | FPU_FLG_D);\n\n    // If both the dividend and divisor are zero, set I/SI,\n    // else set D/SD\n    if ((ee->f[t].u32 & 0x7F800000) == 0) {\n        if ((ee->f[s].u32 & 0x7F800000) == 0) {\n            ee->fcr |= FPU_FLG_I | FPU_FLG_SI;\n        } else {\n            ee->fcr |= FPU_FLG_D | FPU_FLG_SD;\n        }\n\n        ee->f[d].u32 = ((ee->f[t].u32 ^ ee->f[s].u32) & 0x80000000) | 0x7f7fffff;\n\n        return;\n    }\n\n    ee->f[d].f = EE_FS / EE_FT;\n\n    if (fpu_check_overflow_no_flags(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow_no_flags(ee, &ee->f[d]);\n}\nstatic inline void ee_i_divu(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int s = EE_D_RS;\n\n    if (!ee->r[t].ul32) {\n        EE_LO0 = -1;\n        EE_HI0 = SE6432(ee->r[s].ul32);\n\n        return;\n    }\n\n    EE_HI0 = SE6432(ee->r[s].ul32 % ee->r[t].ul32);\n    EE_LO0 = SE6432(ee->r[s].ul32 / ee->r[t].ul32);\n}\nstatic inline void ee_i_divu1(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int s = EE_D_RS;\n\n    if (!ee->r[t].ul32) {\n        EE_LO1 = -1;\n        EE_HI1 = SE6432(ee->r[s].ul32);\n\n        return;\n    }\n\n    EE_HI1 = SE6432(ee->r[s].ul32 % ee->r[t].ul32);\n    EE_LO1 = SE6432(ee->r[s].ul32 / ee->r[t].ul32);\n}\nstatic inline void ee_i_dsll(struct ee_state* ee) {\n    EE_RD = EE_RT << EE_D_SA;\n}\nstatic inline void ee_i_dsll32(struct ee_state* ee) {\n    EE_RD = EE_RT << (EE_D_SA + 32);\n}\nstatic inline void ee_i_dsllv(struct ee_state* ee) {\n    EE_RD = EE_RT << (EE_RS & 0x3f);\n}\nstatic inline void ee_i_dsra(struct ee_state* ee) {\n    EE_RD = ((int64_t)EE_RT) >> EE_D_SA;\n}\nstatic inline void ee_i_dsra32(struct ee_state* ee) {\n    EE_RD = ((int64_t)EE_RT) >> (EE_D_SA + 32);\n}\nstatic inline void ee_i_dsrav(struct ee_state* ee) {\n    EE_RD = ((int64_t)EE_RT) >> (EE_RS & 0x3f);\n}\nstatic inline void ee_i_dsrl(struct ee_state* ee) {\n    EE_RD = EE_RT >> EE_D_SA;\n}\nstatic inline void ee_i_dsrl32(struct ee_state* ee) {\n    EE_RD = EE_RT >> (EE_D_SA + 32);\n}\nstatic inline void ee_i_dsrlv(struct ee_state* ee) {\n    EE_RD = EE_RT >> (EE_RS & 0x3f);\n}\nstatic inline void ee_i_dsub(struct ee_state* ee) {\n    int64_t r;\n\n    if (SSUBOVF64((int64_t)EE_RS, (int64_t)EE_RT, &r)) {\n        ee_exception_level1(ee, CAUSE_EXC1_OV);\n    } else {\n        EE_RD = r;\n    }\n}\nstatic inline void ee_i_dsubu(struct ee_state* ee) {\n    EE_RD = EE_RS - EE_RT;\n}\nstatic inline void ee_i_ei(struct ee_state* ee) {\n    int edi = ee->status & EE_SR_EDI;\n    int exl = ee->status & EE_SR_EXL;\n    int erl = ee->status & EE_SR_ERL;\n    int ksu = ee->status & EE_SR_KSU;\n    \n    if (edi || exl || erl || !ksu)\n        ee->status |= EE_SR_EIE;\n}\nstatic inline void ee_i_eret(struct ee_state* ee) {\n    if (ee->status & EE_SR_ERL) {\n        ee_set_pc(ee, ee->errorepc);\n\n        ee->status &= ~EE_SR_ERL;\n    } else {\n        ee_set_pc(ee, ee->epc);\n\n        ee->status &= ~EE_SR_EXL;\n    }\n}\nstatic inline void ee_i_j(struct ee_state* ee) {\n    ee_set_pc_delayed(ee, (ee->next_pc & 0xf0000000) | (EE_D_I26 << 2));\n}\nstatic inline void ee_i_jal(struct ee_state* ee) {\n    ee->r[31].ul64 = ee->next_pc;\n\n    ee_set_pc_delayed(ee, (ee->next_pc & 0xf0000000) | (EE_D_I26 << 2));\n}\nstatic inline void ee_i_jalr(struct ee_state* ee) {\n    uint32_t next_pc = ee->next_pc;\n\n    ee_set_pc_delayed(ee, EE_RS32);\n\n    EE_RD = next_pc;\n}\nstatic inline void ee_i_jr(struct ee_state* ee) {\n    ee_set_pc_delayed(ee, EE_RS32);\n}\nstatic inline void ee_i_lb(struct ee_state* ee) {\n    EE_RT = SE648(bus_read8(ee, EE_RS32 + SE3216(EE_D_I16)));\n}\nstatic inline void ee_i_lbu(struct ee_state* ee) {\n    EE_RT = bus_read8(ee, EE_RS32 + SE3216(EE_D_I16));\n}\nstatic inline void ee_i_ld(struct ee_state* ee) {\n    EE_RT = bus_read64(ee, EE_RS32 + SE3216(EE_D_I16));\n}\nstatic inline void ee_i_ldl(struct ee_state* ee) {\n    static const uint8_t ldl_shift[8] = { 56, 48, 40, 32, 24, 16, 8, 0 };\n    static const uint64_t ldl_mask[8] = {\n        0x00ffffffffffffffULL, 0x0000ffffffffffffULL, 0x000000ffffffffffULL, 0x00000000ffffffffULL,\n        0x0000000000ffffffULL, 0x000000000000ffffULL, 0x00000000000000ffULL, 0x0000000000000000ULL\n    };\n\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t shift = addr & 7;\n    uint64_t data = bus_read64(ee, addr & ~7);\n\n    EE_RT = (EE_RT & ldl_mask[shift]) | (data << ldl_shift[shift]);\n}\nstatic inline void ee_i_ldr(struct ee_state* ee) {\n    static const uint8_t ldr_shift[8] = { 0, 8, 16, 24, 32, 40, 48, 56 };\n    static const uint64_t ldr_mask[8] = {\n        0x0000000000000000ULL, 0xff00000000000000ULL, 0xffff000000000000ULL, 0xffffff0000000000ULL,\n        0xffffffff00000000ULL, 0xffffffffff000000ULL, 0xffffffffffff0000ULL, 0xffffffffffffff00ULL\n    };\n\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t shift = addr & 7;\n    uint64_t data = bus_read64(ee, addr & ~7);\n\n    EE_RT = (EE_RT & ldr_mask[shift]) | (data >> ldr_shift[shift]);\n}\nstatic inline void ee_i_lh(struct ee_state* ee) {\n    EE_RT = SE6416(bus_read16(ee, EE_RS32 + SE3216(EE_D_I16)));\n}\nstatic inline void ee_i_lhu(struct ee_state* ee) {\n    EE_RT = bus_read16(ee, EE_RS32 + SE3216(EE_D_I16));\n}\nstatic inline void ee_i_lq(struct ee_state* ee) {\n    ee->r[EE_D_RT] = bus_read128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf);\n}\nstatic inline void ee_i_lqc2(struct ee_state* ee) {\n    int d = EE_D_RT;\n\n    if (!d) return;\n\n    ee->vu0->vf[EE_D_RT].u128 = bus_read128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf);\n}\nstatic inline void ee_i_lui(struct ee_state* ee) {\n    EE_RT = SE6432(EE_D_I16 << 16);\n}\nstatic inline void ee_i_lw(struct ee_state* ee) {\n    EE_RT = SE6432(bus_read32(ee, EE_RS32 + SE3216(EE_D_I16)));\n}\nstatic inline void ee_i_lwc1(struct ee_state* ee) {\n    EE_FT32 = bus_read32(ee, EE_RS32 + SE3216(EE_D_I16));\n}\n\nstatic const uint32_t LWL_MASK[4] = { 0x00ffffff, 0x0000ffff, 0x000000ff, 0x00000000 };\nstatic const uint32_t LWR_MASK[4] = { 0x00000000, 0xff000000, 0xffff0000, 0xffffff00 };\nstatic const int LWL_SHIFT[4] = { 24, 16, 8, 0 };\nstatic const int LWR_SHIFT[4] = { 0, 8, 16, 24 };\n\nstatic inline void ee_i_lwl(struct ee_state* ee) {\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t shift = addr & 3;\n    uint32_t mem = bus_read32(ee, addr & ~3);\n\n    // ensure the compiler does correct sign extension into 64 bits by using s32\n    EE_RT = (int32_t)((EE_RT32 & LWL_MASK[shift]) | (mem << LWL_SHIFT[shift]));\n}\n\nstatic inline void ee_i_lwr(struct ee_state* ee) {\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t shift = addr & 3;\n    uint32_t data = bus_read32(ee, addr & ~3);\n\n    // Use unsigned math here, and conditionally sign extend below, when needed.\n    data = (EE_RT32 & LWR_MASK[shift]) | (data >> LWR_SHIFT[shift]);\n\n    if (!shift) {\n        // This special case requires sign extension into the full 64 bit dest.\n        EE_RT = (int32_t)data;\n    } else {\n        // This case sets the lower 32 bits of the target register.  Upper\n        // 32 bits are always preserved.\n        EE_RT32 = data;\n    }\n\n    // printf(\"lwr mem=%08x reg=%016lx addr=%08x shift=%d\\n\", data, ee->r[EE_D_RT].u64[0], addr, shift);\n}\nstatic inline void ee_i_lwu(struct ee_state* ee) {\n    EE_RT = bus_read32(ee, EE_RS32 + SE3216(EE_D_I16));\n}\nstatic inline void ee_i_madd(struct ee_state* ee) {\n    uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32);\n    uint64_t d = (uint64_t)ee->lo.u32[0] | (ee->hi.u64[0] << 32);\n\n    d += r;\n\n    EE_LO0 = SE6432(d & 0xffffffff);\n    EE_HI0 = SE6432(d >> 32);\n\n    EE_RD = EE_LO0;\n}\nstatic inline void ee_i_madd1(struct ee_state* ee) {\n    uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32);\n    uint64_t d = (EE_LO1 & 0xffffffff) | (EE_HI1 << 32);\n\n    d += r;\n\n    EE_LO1 = SE6432(d & 0xffffffff);\n    EE_HI1 = SE6432(d >> 32);\n\n    EE_RD = EE_LO1;\n}\nstatic inline void ee_i_maddas(struct ee_state* ee) {\n    ee->a.f += EE_FS * EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->a))\n        return;\n\n    fpu_check_underflow(ee, &ee->a);\n}\nstatic inline void ee_i_madds(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int d = EE_D_FD;\n    int s = EE_D_FS;\n\n    float temp = fpu_cvtf(ee->f[s].f) * fpu_cvtf(ee->f[t].f);\n\n    ee->f[d].f = fpu_cvtf(ee->a.f) + fpu_cvtf(temp);\n\n    if (fpu_check_overflow(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow(ee, &ee->f[d]);\n}\nstatic inline void ee_i_maddu(struct ee_state* ee) {\n    uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32;\n    uint64_t d = (uint64_t)ee->lo.u32[0] | (ee->hi.u64[0] << 32);\n\n    d += r;\n\n    EE_LO0 = SE6432(d & 0xffffffff);\n    EE_HI0 = SE6432(d >> 32);\n\n    EE_RD = EE_LO0;\n}\nstatic inline void ee_i_maddu1(struct ee_state* ee) {\n    uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32;\n    uint64_t d = (uint64_t)ee->lo.u32[2] | (ee->hi.u64[1] << 32);\n\n    d += r;\n\n    EE_LO1 = SE6432(d & 0xffffffff);\n    EE_HI1 = SE6432(d >> 32);\n\n    EE_RD = EE_LO1;\n}\nstatic inline void ee_i_maxs(struct ee_state* ee) {\n    ee->f[EE_D_FD].u32 = fpu_max(ee->f[EE_D_FS].u32, ee->f[EE_D_RT].u32);\n\n    ee->fcr &= ~(FPU_FLG_O | FPU_FLG_U);\n}\nstatic inline void ee_i_mfc0(struct ee_state* ee) {\n    EE_RT = SE6432(ee->cop0_r[EE_D_RD]);\n}\nstatic inline void ee_i_mfc1(struct ee_state* ee) {\n    EE_RT = SE6432(EE_FS32);\n}\nstatic inline void ee_i_mfhi(struct ee_state* ee) {\n    EE_RD = EE_HI0;\n}\nstatic inline void ee_i_mfhi1(struct ee_state* ee) {\n    EE_RD = EE_HI1;\n}\nstatic inline void ee_i_mflo(struct ee_state* ee) {\n    EE_RD = EE_LO0;\n}\nstatic inline void ee_i_mflo1(struct ee_state* ee) {\n    EE_RD = EE_LO1;\n}\nstatic inline void ee_i_mfsa(struct ee_state* ee) {\n    EE_RD = ee->sa & 0xf;\n}\nstatic inline void ee_i_mins(struct ee_state* ee) {\n    ee->f[EE_D_FD].u32 = fpu_min(ee->f[EE_D_FS].u32, ee->f[EE_D_RT].u32);\n\n    ee->fcr &= ~(FPU_FLG_O | FPU_FLG_U);\n}\nstatic inline void ee_i_movn(struct ee_state* ee) {\n    if (EE_RT) EE_RD = EE_RS;\n}\nstatic inline void ee_i_movs(struct ee_state* ee) {\n    EE_FD32 = EE_FS32;\n}\nstatic inline void ee_i_movz(struct ee_state* ee) {\n    if (!EE_RT) EE_RD = EE_RS;\n}\nstatic inline void ee_i_msubas(struct ee_state* ee) {\n    ee->a.f -= EE_FS * EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->a))\n        return;\n\n    fpu_check_underflow(ee, &ee->a);\n}\nstatic inline void ee_i_msubs(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int d = EE_D_FD;\n    int s = EE_D_FS;\n\n    float temp = fpu_cvtf(ee->f[s].f) * fpu_cvtf(ee->f[t].f);\n\n    ee->f[d].f = fpu_cvtf(ee->a.f) - fpu_cvtf(temp);\n\n    if (fpu_check_overflow(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow(ee, &ee->f[d]);\n}\nstatic inline void ee_i_mtc0(struct ee_state* ee) {\n    ee->cop0_r[EE_D_RD] = EE_RT32;\n}\nstatic inline void ee_i_mtc1(struct ee_state* ee) {\n    EE_FS32 = EE_RT32;\n}\nstatic inline void ee_i_mthi(struct ee_state* ee) {\n    EE_HI0 = EE_RS;\n}\nstatic inline void ee_i_mthi1(struct ee_state* ee) {\n    EE_HI1 = EE_RS;\n}\nstatic inline void ee_i_mtlo(struct ee_state* ee) {\n    EE_LO0 = EE_RS;\n}\nstatic inline void ee_i_mtlo1(struct ee_state* ee) {\n    EE_LO1 = EE_RS;\n}\nstatic inline void ee_i_mtsa(struct ee_state* ee) {\n    ee->sa = ((uint32_t)EE_RS) & 0xf;\n}\nstatic inline void ee_i_mtsab(struct ee_state* ee) {\n    ee->sa = (EE_RS ^ EE_D_I16) & 15;\n}\nstatic inline void ee_i_mtsah(struct ee_state* ee) {\n    ee->sa = ((EE_RS ^ EE_D_I16) & 7) << 1;\n}\nstatic inline void ee_i_mulas(struct ee_state* ee) {\n    ee->a.f = EE_FS * EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->a))\n        return;\n\n    fpu_check_underflow(ee, &ee->a);\n}\nstatic inline void ee_i_muls(struct ee_state* ee) {\n    int d = EE_D_FD;\n\n    ee->f[d].f = EE_FS * EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow(ee, &ee->f[d]);\n}\nstatic inline void ee_i_mult(struct ee_state* ee) {\n    uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32);\n\n    EE_LO0 = SE6432(r & 0xffffffff);\n    EE_HI0 = SE6432(r >> 32);\n\n    EE_RD = EE_LO0;\n}\nstatic inline void ee_i_mult1(struct ee_state* ee) {\n    uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32);\n\n    EE_LO1 = SE6432(r & 0xffffffff);\n    EE_HI1 = SE6432(r >> 32);\n\n    EE_RD = EE_LO1;\n}\nstatic inline void ee_i_multu(struct ee_state* ee) {\n    uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32;\n\n    EE_LO0 = SE6432(r & 0xffffffff);\n    EE_HI0 = SE6432(r >> 32);\n\n    EE_RD = EE_LO0;\n}\nstatic inline void ee_i_multu1(struct ee_state* ee) {\n    uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32;\n\n    EE_LO1 = SE6432(r & 0xffffffff);\n    EE_HI1 = SE6432(r >> 32);\n\n    EE_RD = EE_LO1;\n}\nstatic inline void ee_i_negs(struct ee_state* ee) {\n    ee->f[EE_D_FD].u32 = ee->f[EE_D_FS].u32 ^ 0x80000000;\n\n    ee->fcr &= ~(FPU_FLG_O | FPU_FLG_U);\n}\nstatic inline void ee_i_nor(struct ee_state* ee) {\n    EE_RD = ~(EE_RS | EE_RT);\n}\nstatic inline void ee_i_or(struct ee_state* ee) {\n    EE_RD = EE_RS | EE_RT;\n}\nstatic inline void ee_i_ori(struct ee_state* ee) {\n    EE_RT = EE_RS | EE_D_I16;\n}\nstatic inline void ee_i_pabsh(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 8; i++) {\n        d->u16[i] = (t->u16[i] == 0x8000) ? 0x7fff : fast_abs16(t->u16[i]);\n    }\n#else\n    __m128i b = _mm_set1_epi16((unsigned short)0x8000);\n    __m128i a = _mm_load_si128((void*)t);\n    __m128i f = _mm_cmpeq_epi16(a, b);\n    __m128i r = _mm_add_epi16(_mm_abs_epi16(a), f);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_pabsw(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 4; i++) {\n        d->u32[i] = (t->u32[i] == 0x80000000) ? 0x7fffffff : fast_abs32(t->u32[i]);\n    }\n#else\n    __m128i b = _mm_set1_epi32(0x80000000);\n    __m128i a = _mm_load_si128((void*)t);\n    __m128i f = _mm_cmpeq_epi32(a, b);\n    __m128i r = _mm_add_epi32(_mm_abs_epi32(a), f);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_paddb(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 16; i++) {\n        d->u8[i] = s->u8[i] + t->u8[i];\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_add_epi8(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_paddh(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 8; i++) {\n        d->u16[i] = s->u16[i] + t->u16[i];\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_add_epi16(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_paddsb(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 16; i++) {\n        int32_t r = ((int32_t)(int8_t)s->u8[i]) + ((int32_t)(int8_t)t->u8[i]);\n        d->u8[i] = (r > 0x7f) ? 0x7f : ((r < -128) ? 0x80 : r);\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_adds_epi8(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_paddsh(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 8; i++) {\n        int32_t r = (SE3216(s->u16[i])) + (SE3216(t->u16[i]));\n        d->u16[i] = (r > 0x7fff) ? 0x7fff : ((r < -0x8000) ? 0x8000 : r);\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_adds_epi16(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_paddsw(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 4; i++) {\n        int64_t r = (SE6432(s->u32[i])) + (SE6432(t->u32[i]));\n        d->u32[i] = (r >= 0x7fffffff) ? 0x7fffffff : ((r < (int32_t)0x80000000) ? 0x80000000 : r);\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_adds_epi32(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_paddub(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 16; i++) {\n        uint32_t r = (uint32_t)s->u8[i] + (uint32_t)t->u8[i];\n        d->u8[i] = (r > 0xff) ? 0xff : r;\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_adds_epu8(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_padduh(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 8; i++) {\n        uint32_t r = (uint32_t)s->u16[i] + (uint32_t)t->u16[i];\n        d->u16[i] = (r > 0xffff) ? 0xffff : r;\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_adds_epu16(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_padduw(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 4; i++) {\n        uint64_t r = (uint64_t)s->u32[i] + (uint64_t)t->u32[i];\n        d->u32[i] = (r > 0xffffffff) ? 0xffffffff : r;\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_adds_epu32(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_paddw(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 4; i++) {\n        d->u32[i] = s->u32[i] + t->u32[i];\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_add_epi32(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_padsbh(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    d->u16[0] = s->u16[0] - t->u16[0];\n    d->u16[1] = s->u16[1] - t->u16[1];\n    d->u16[2] = s->u16[2] - t->u16[2];\n    d->u16[3] = s->u16[3] - t->u16[3];\n    d->u16[4] = s->u16[4] + t->u16[4];\n    d->u16[5] = s->u16[5] + t->u16[5];\n    d->u16[6] = s->u16[6] + t->u16[6];\n    d->u16[7] = s->u16[7] + t->u16[7];\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i x = _mm_sub_epi16(a, b);\n    __m128i y = _mm_add_epi16(a, b);\n    __m128i r = _mm_blend_epi16(x, y, 15);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_pand(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    d->u64[0] = s->u64[0] & t->u64[0];\n    d->u64[1] = s->u64[1] & t->u64[1];\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_and_si128(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_pceqb(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 16; i++) {\n        d->u8[i] = (s->u8[i] == t->u8[i]) ? 0xff : 0;\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_cmpeq_epi8(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_pceqh(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 8; i++) {\n        d->u16[i] = (s->u16[i] == t->u16[i]) ? 0xffff : 0;\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_cmpeq_epi16(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_pceqw(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 4; i++) {\n        d->u32[i] = (s->u32[i] == t->u32[i]) ? 0xffffffff : 0;\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_cmpeq_epi32(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_pcgtb(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 16; i++) {\n        d->u8[i] = ((int8_t)s->u8[i] > (int8_t)t->u8[i]) ? 0xff : 0;\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_cmpgt_epi8(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_pcgth(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 8; i++) {\n        d->u16[i] = ((int16_t)s->u16[i] > (int16_t)t->u16[i]) ? 0xffff : 0;\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_cmpgt_epi16(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_pcgtw(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* s = &ee->r[EE_D_RS];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    for (int i = 0; i < 4; i++) {\n        d->u32[i] = ((int32_t)s->u32[i] > (int32_t)t->u32[i]) ? 0xffffffff : 0;\n    }\n#else\n    __m128i a = _mm_load_si128((void*)s);\n    __m128i b = _mm_load_si128((void*)t);\n    __m128i r = _mm_cmpgt_epi32(a, b);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_pcpyh(struct ee_state* ee) {\n    uint128_t* d = &ee->r[EE_D_RD];\n    uint128_t* t = &ee->r[EE_D_RT];\n\n#ifndef _EE_USE_INTRINSICS\n    uint128_t tc = *t;\n\n    d->u16[0] = tc.u16[0];\n    d->u16[1] = tc.u16[0];\n    d->u16[2] = tc.u16[0];\n    d->u16[3] = tc.u16[0];\n    d->u16[4] = tc.u16[4];\n    d->u16[5] = tc.u16[4];\n    d->u16[6] = tc.u16[4];\n    d->u16[7] = tc.u16[4];\n#else\n    static const uint64_t mask[] = {\n        0x0100010001000100,\n        0x0908090809080908\n    };\n\n    __m128i m = _mm_load_si128((void*)mask);\n    __m128i a = _mm_load_si128((void*)t);\n    __m128i r = _mm_shuffle_epi8(a, m);\n\n    _mm_store_si128((void*)d, r);\n#endif\n}\nstatic inline void ee_i_pcpyld(struct ee_state* ee) {\n#ifndef _EE_USE_INTRINSICS\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = rt.u64[0];\n    ee->r[d].u64[1] = rs.u64[0];\n#else\n    __m128i a = _mm_load_si128((void*)&ee->r[EE_D_RT]);\n    __m128i b = _mm_load_si128((void*)&ee->r[EE_D_RS]);\n    __m128i r = _mm_unpacklo_epi64(a, b);\n\n    _mm_store_si128((void*)&ee->r[EE_D_RD], r);\n#endif\n}\nstatic inline void ee_i_pcpyud(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = rs.u64[1];\n    ee->r[d].u64[1] = rt.u64[1];\n}\nstatic inline void ee_i_pdivbw(struct ee_state* ee) { printf(\"ee: pdivbw unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_pdivuw(struct ee_state* ee) { printf(\"ee: pdivuw unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_pdivw(struct ee_state* ee) { printf(\"ee: pdivw unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_pexch(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[0];\n    ee->r[d].u16[1] = rt.u16[2];\n    ee->r[d].u16[2] = rt.u16[1];\n    ee->r[d].u16[3] = rt.u16[3];\n    ee->r[d].u16[4] = rt.u16[4];\n    ee->r[d].u16[5] = rt.u16[6];\n    ee->r[d].u16[6] = rt.u16[5];\n    ee->r[d].u16[7] = rt.u16[7];\n}\nstatic inline void ee_i_pexcw(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = rt.u32[0];\n    ee->r[d].u32[1] = rt.u32[2];\n    ee->r[d].u32[2] = rt.u32[1];\n    ee->r[d].u32[3] = rt.u32[3];\n}\nstatic inline void ee_i_pexeh(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[2];\n    ee->r[d].u16[1] = rt.u16[1];\n    ee->r[d].u16[2] = rt.u16[0];\n    ee->r[d].u16[3] = rt.u16[3];\n    ee->r[d].u16[4] = rt.u16[6];\n    ee->r[d].u16[5] = rt.u16[5];\n    ee->r[d].u16[6] = rt.u16[4];\n    ee->r[d].u16[7] = rt.u16[7];\n}\nstatic inline void ee_i_pexew(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = rt.u32[2];\n    ee->r[d].u32[1] = rt.u32[1];\n    ee->r[d].u32[2] = rt.u32[0];\n    ee->r[d].u32[3] = rt.u32[3];\n}\nstatic inline void ee_i_pext5(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = unpack_5551_8888(rt.u32[0]);\n    ee->r[d].u32[1] = unpack_5551_8888(rt.u32[1]);\n    ee->r[d].u32[2] = unpack_5551_8888(rt.u32[2]);\n    ee->r[d].u32[3] = unpack_5551_8888(rt.u32[3]);\n}\nstatic inline void ee_i_pextlb(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u8[ 0] = rt.u8[0];\n    ee->r[d].u8[ 1] = rs.u8[0];\n    ee->r[d].u8[ 2] = rt.u8[1];\n    ee->r[d].u8[ 3] = rs.u8[1];\n    ee->r[d].u8[ 4] = rt.u8[2];\n    ee->r[d].u8[ 5] = rs.u8[2];\n    ee->r[d].u8[ 6] = rt.u8[3];\n    ee->r[d].u8[ 7] = rs.u8[3];\n    ee->r[d].u8[ 8] = rt.u8[4];\n    ee->r[d].u8[ 9] = rs.u8[4];\n    ee->r[d].u8[10] = rt.u8[5];\n    ee->r[d].u8[11] = rs.u8[5];\n    ee->r[d].u8[12] = rt.u8[6];\n    ee->r[d].u8[13] = rs.u8[6];\n    ee->r[d].u8[14] = rt.u8[7];\n    ee->r[d].u8[15] = rs.u8[7];\n}\nstatic inline void ee_i_pextlh(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[0];\n    ee->r[d].u16[1] = rs.u16[0];\n    ee->r[d].u16[2] = rt.u16[1];\n    ee->r[d].u16[3] = rs.u16[1];\n    ee->r[d].u16[4] = rt.u16[2];\n    ee->r[d].u16[5] = rs.u16[2];\n    ee->r[d].u16[6] = rt.u16[3];\n    ee->r[d].u16[7] = rs.u16[3];\n}\nstatic inline void ee_i_pextlw(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = rt.u32[0];\n    ee->r[d].u32[1] = rs.u32[0];\n    ee->r[d].u32[2] = rt.u32[1];\n    ee->r[d].u32[3] = rs.u32[1];\n}\nstatic inline void ee_i_pextub(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u8[ 0] = rt.u8[ 8];\n    ee->r[d].u8[ 1] = rs.u8[ 8];\n    ee->r[d].u8[ 2] = rt.u8[ 9];\n    ee->r[d].u8[ 3] = rs.u8[ 9];\n    ee->r[d].u8[ 4] = rt.u8[10];\n    ee->r[d].u8[ 5] = rs.u8[10];\n    ee->r[d].u8[ 6] = rt.u8[11];\n    ee->r[d].u8[ 7] = rs.u8[11];\n    ee->r[d].u8[ 8] = rt.u8[12];\n    ee->r[d].u8[ 9] = rs.u8[12];\n    ee->r[d].u8[10] = rt.u8[13];\n    ee->r[d].u8[11] = rs.u8[13];\n    ee->r[d].u8[12] = rt.u8[14];\n    ee->r[d].u8[13] = rs.u8[14];\n    ee->r[d].u8[14] = rt.u8[15];\n    ee->r[d].u8[15] = rs.u8[15];\n}\nstatic inline void ee_i_pextuh(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[4];\n    ee->r[d].u16[1] = rs.u16[4];\n    ee->r[d].u16[2] = rt.u16[5];\n    ee->r[d].u16[3] = rs.u16[5];\n    ee->r[d].u16[4] = rt.u16[6];\n    ee->r[d].u16[5] = rs.u16[6];\n    ee->r[d].u16[6] = rt.u16[7];\n    ee->r[d].u16[7] = rs.u16[7];\n}\nstatic inline void ee_i_pextuw(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = rt.u32[2];\n    ee->r[d].u32[1] = rs.u32[2];\n    ee->r[d].u32[2] = rt.u32[3];\n    ee->r[d].u32[3] = rs.u32[3];\n}\nstatic inline void ee_i_phmadh(struct ee_state* ee) { printf(\"ee: phmadh unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_phmsbh(struct ee_state* ee) { printf(\"ee: phmsbh unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_pinteh(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[0];\n    ee->r[d].u16[1] = rs.u16[0];\n    ee->r[d].u16[2] = rt.u16[2];\n    ee->r[d].u16[3] = rs.u16[2];\n    ee->r[d].u16[4] = rt.u16[4];\n    ee->r[d].u16[5] = rs.u16[4];\n    ee->r[d].u16[6] = rt.u16[6];\n    ee->r[d].u16[7] = rs.u16[6];\n}\nstatic inline void ee_i_pinth(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    uint128_t rs = ee->r[EE_D_RS];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[0];\n    ee->r[d].u16[1] = rs.u16[4];\n    ee->r[d].u16[2] = rt.u16[1];\n    ee->r[d].u16[3] = rs.u16[5];\n    ee->r[d].u16[4] = rt.u16[2];\n    ee->r[d].u16[5] = rs.u16[6];\n    ee->r[d].u16[6] = rt.u16[3];\n    ee->r[d].u16[7] = rs.u16[7];\n}\nstatic inline void ee_i_plzcw(struct ee_state* ee) { \n    for (int i = 0; i < 2; i++) {\n        uint32_t word = ee->r[EE_D_RS].u32[i];\n\n        int msb = word & 0x80000000;\n\n        word = (msb ? ~word : word);\n\n        ee->r[EE_D_RD].u32[i] = (word ? (__builtin_clz(word) - 1) : 0x1f);\n    }\n}\nstatic inline void ee_i_pmaddh(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    uint32_t r0 = SE3216(ee->r[s].u16[0]) * SE3216(ee->r[t].u16[0]);\n    uint32_t r1 = SE3216(ee->r[s].u16[1]) * SE3216(ee->r[t].u16[1]);\n    uint32_t r2 = SE3216(ee->r[s].u16[2]) * SE3216(ee->r[t].u16[2]);\n    uint32_t r3 = SE3216(ee->r[s].u16[3]) * SE3216(ee->r[t].u16[3]);\n    uint32_t r4 = SE3216(ee->r[s].u16[4]) * SE3216(ee->r[t].u16[4]);\n    uint32_t r5 = SE3216(ee->r[s].u16[5]) * SE3216(ee->r[t].u16[5]);\n    uint32_t r6 = SE3216(ee->r[s].u16[6]) * SE3216(ee->r[t].u16[6]);\n    uint32_t r7 = SE3216(ee->r[s].u16[7]) * SE3216(ee->r[t].u16[7]);\n    uint32_t c0 = ee->lo.u32[0];\n    uint32_t c1 = ee->lo.u32[1];\n    uint32_t c2 = ee->hi.u32[0];\n    uint32_t c3 = ee->hi.u32[1];\n    uint32_t c4 = ee->lo.u32[2];\n    uint32_t c5 = ee->lo.u32[3];\n    uint32_t c6 = ee->hi.u32[2];\n    uint32_t c7 = ee->hi.u32[3];\n\n    ee->r[d].u32[0] = r0 + c0;\n    ee->lo.u32[1] = r1 + c1;\n    ee->r[d].u32[1] = r2 + c2;\n    ee->hi.u32[1] = r3 + c3;\n    ee->r[d].u32[2] = r4 + c4;\n    ee->lo.u32[3] = r5 + c5;\n    ee->r[d].u32[3] = r6 + c6;\n    ee->hi.u32[3] = r7 + c7;\n\n    ee->lo.u32[0] = ee->r[d].u32[0];\n    ee->hi.u32[0] = ee->r[d].u32[1];\n    ee->lo.u32[2] = ee->r[d].u32[2];\n    ee->hi.u32[2] = ee->r[d].u32[3];\n}\nstatic inline void ee_i_pmadduw(struct ee_state* ee) { printf(\"ee: pmadduw unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_pmaddw(struct ee_state* ee) { printf(\"ee: pmaddw unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_pmaxh(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u16[0] = ((int16_t)ee->r[s].u16[0] > (int16_t)ee->r[t].u16[0]) ? ee->r[s].u16[0] : ee->r[t].u16[0];\n    ee->r[d].u16[1] = ((int16_t)ee->r[s].u16[1] > (int16_t)ee->r[t].u16[1]) ? ee->r[s].u16[1] : ee->r[t].u16[1];\n    ee->r[d].u16[2] = ((int16_t)ee->r[s].u16[2] > (int16_t)ee->r[t].u16[2]) ? ee->r[s].u16[2] : ee->r[t].u16[2];\n    ee->r[d].u16[3] = ((int16_t)ee->r[s].u16[3] > (int16_t)ee->r[t].u16[3]) ? ee->r[s].u16[3] : ee->r[t].u16[3];\n    ee->r[d].u16[4] = ((int16_t)ee->r[s].u16[4] > (int16_t)ee->r[t].u16[4]) ? ee->r[s].u16[4] : ee->r[t].u16[4];\n    ee->r[d].u16[5] = ((int16_t)ee->r[s].u16[5] > (int16_t)ee->r[t].u16[5]) ? ee->r[s].u16[5] : ee->r[t].u16[5];\n    ee->r[d].u16[6] = ((int16_t)ee->r[s].u16[6] > (int16_t)ee->r[t].u16[6]) ? ee->r[s].u16[6] : ee->r[t].u16[6];\n    ee->r[d].u16[7] = ((int16_t)ee->r[s].u16[7] > (int16_t)ee->r[t].u16[7]) ? ee->r[s].u16[7] : ee->r[t].u16[7];\n}\nstatic inline void ee_i_pmaxw(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u32[0] = ((int32_t)ee->r[s].u32[0] > (int32_t)ee->r[t].u32[0]) ? ee->r[s].u32[0] : ee->r[t].u32[0];\n    ee->r[d].u32[1] = ((int32_t)ee->r[s].u32[1] > (int32_t)ee->r[t].u32[1]) ? ee->r[s].u32[1] : ee->r[t].u32[1];\n    ee->r[d].u32[2] = ((int32_t)ee->r[s].u32[2] > (int32_t)ee->r[t].u32[2]) ? ee->r[s].u32[2] : ee->r[t].u32[2];\n    ee->r[d].u32[3] = ((int32_t)ee->r[s].u32[3] > (int32_t)ee->r[t].u32[3]) ? ee->r[s].u32[3] : ee->r[t].u32[3];\n}\nstatic inline void ee_i_pmfhi(struct ee_state* ee) {\n    ee->r[EE_D_RD] = ee->hi;\n}\nstatic inline void ee_i_pmfhllw(struct ee_state* ee) {\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = ee->lo.u32[0];\n    ee->r[d].u32[1] = ee->hi.u32[0];\n    ee->r[d].u32[2] = ee->lo.u32[2];\n    ee->r[d].u32[3] = ee->hi.u32[2];\n}\nstatic inline void ee_i_pmfhluw(struct ee_state* ee) {\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = ee->lo.u32[1];\n    ee->r[d].u32[1] = ee->hi.u32[1];\n    ee->r[d].u32[2] = ee->lo.u32[3];\n    ee->r[d].u32[3] = ee->hi.u32[3];\n}\nstatic inline void ee_i_pmfhlslw(struct ee_state* ee) { printf(\"ee: pmfhlslw unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_pmfhllh(struct ee_state* ee) {\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = ee->lo.u16[0];\n    ee->r[d].u16[1] = ee->lo.u16[2];\n    ee->r[d].u16[2] = ee->hi.u16[0];\n    ee->r[d].u16[3] = ee->hi.u16[2];\n    ee->r[d].u16[4] = ee->lo.u16[4];\n    ee->r[d].u16[5] = ee->lo.u16[6];\n    ee->r[d].u16[6] = ee->hi.u16[4];\n    ee->r[d].u16[7] = ee->hi.u16[6];\n    \n}\nstatic inline void ee_i_pmfhlsh(struct ee_state* ee) {\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = saturate16(ee->lo.u32[0]);\n    ee->r[d].u16[1] = saturate16(ee->lo.u32[1]);\n    ee->r[d].u16[2] = saturate16(ee->hi.u32[0]);\n    ee->r[d].u16[3] = saturate16(ee->hi.u32[1]);\n    ee->r[d].u16[4] = saturate16(ee->lo.u32[2]);\n    ee->r[d].u16[5] = saturate16(ee->lo.u32[3]);\n    ee->r[d].u16[6] = saturate16(ee->hi.u32[2]);\n    ee->r[d].u16[7] = saturate16(ee->hi.u32[3]);\n}\nstatic inline void ee_i_pmflo(struct ee_state* ee) {\n    ee->r[EE_D_RD] = ee->lo;\n}\nstatic inline void ee_i_pminh(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u16[0] = ((int16_t)ee->r[s].u16[0] < (int16_t)ee->r[t].u16[0]) ? ee->r[s].u16[0] : ee->r[t].u16[0];\n    ee->r[d].u16[1] = ((int16_t)ee->r[s].u16[1] < (int16_t)ee->r[t].u16[1]) ? ee->r[s].u16[1] : ee->r[t].u16[1];\n    ee->r[d].u16[2] = ((int16_t)ee->r[s].u16[2] < (int16_t)ee->r[t].u16[2]) ? ee->r[s].u16[2] : ee->r[t].u16[2];\n    ee->r[d].u16[3] = ((int16_t)ee->r[s].u16[3] < (int16_t)ee->r[t].u16[3]) ? ee->r[s].u16[3] : ee->r[t].u16[3];\n    ee->r[d].u16[4] = ((int16_t)ee->r[s].u16[4] < (int16_t)ee->r[t].u16[4]) ? ee->r[s].u16[4] : ee->r[t].u16[4];\n    ee->r[d].u16[5] = ((int16_t)ee->r[s].u16[5] < (int16_t)ee->r[t].u16[5]) ? ee->r[s].u16[5] : ee->r[t].u16[5];\n    ee->r[d].u16[6] = ((int16_t)ee->r[s].u16[6] < (int16_t)ee->r[t].u16[6]) ? ee->r[s].u16[6] : ee->r[t].u16[6];\n    ee->r[d].u16[7] = ((int16_t)ee->r[s].u16[7] < (int16_t)ee->r[t].u16[7]) ? ee->r[s].u16[7] : ee->r[t].u16[7];\n}\nstatic inline void ee_i_pminw(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u32[0] = ((int32_t)ee->r[s].u32[0] < (int32_t)ee->r[t].u32[0]) ? ee->r[s].u32[0] : ee->r[t].u32[0];\n    ee->r[d].u32[1] = ((int32_t)ee->r[s].u32[1] < (int32_t)ee->r[t].u32[1]) ? ee->r[s].u32[1] : ee->r[t].u32[1];\n    ee->r[d].u32[2] = ((int32_t)ee->r[s].u32[2] < (int32_t)ee->r[t].u32[2]) ? ee->r[s].u32[2] : ee->r[t].u32[2];\n    ee->r[d].u32[3] = ((int32_t)ee->r[s].u32[3] < (int32_t)ee->r[t].u32[3]) ? ee->r[s].u32[3] : ee->r[t].u32[3];\n}\nstatic inline void ee_i_pmsubh(struct ee_state* ee) { printf(\"ee: pmsubh unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_pmsubw(struct ee_state* ee) { printf(\"ee: pmsubw unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_pmthi(struct ee_state* ee) {\n    ee->hi = ee->r[EE_D_RS];\n}\nstatic inline void ee_i_pmthl(struct ee_state* ee) { printf(\"ee: pmthl unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_pmtlo(struct ee_state* ee) {\n    ee->lo = ee->r[EE_D_RS];\n}\nstatic inline void ee_i_pmulth(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->lo.u32[0] = (int32_t)(int16_t)ee->r[s].u16[0] * (int32_t)(int16_t)ee->r[t].u16[0];\n    ee->lo.u32[1] = (int32_t)(int16_t)ee->r[s].u16[1] * (int32_t)(int16_t)ee->r[t].u16[1];\n    ee->hi.u32[0] = (int32_t)(int16_t)ee->r[s].u16[2] * (int32_t)(int16_t)ee->r[t].u16[2];\n    ee->hi.u32[1] = (int32_t)(int16_t)ee->r[s].u16[3] * (int32_t)(int16_t)ee->r[t].u16[3];\n    ee->lo.u32[2] = (int32_t)(int16_t)ee->r[s].u16[4] * (int32_t)(int16_t)ee->r[t].u16[4];\n    ee->lo.u32[3] = (int32_t)(int16_t)ee->r[s].u16[5] * (int32_t)(int16_t)ee->r[t].u16[5];\n    ee->hi.u32[2] = (int32_t)(int16_t)ee->r[s].u16[6] * (int32_t)(int16_t)ee->r[t].u16[6];\n    ee->hi.u32[3] = (int32_t)(int16_t)ee->r[s].u16[7] * (int32_t)(int16_t)ee->r[t].u16[7];\n    ee->r[d].u32[0] = ee->lo.u32[0];\n    ee->r[d].u32[1] = ee->hi.u32[0];\n    ee->r[d].u32[2] = ee->lo.u32[2];\n    ee->r[d].u32[3] = ee->hi.u32[2];\n}\nstatic inline void ee_i_pmultuw(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u64[0] = (uint64_t)ee->r[s].u32[0] * (uint64_t)ee->r[t].u32[0];\n    ee->r[d].u64[1] = (uint64_t)ee->r[s].u32[2] * (uint64_t)ee->r[t].u32[2];\n\n    ee->lo.u64[0] = SE6432(ee->r[d].u32[0]);\n    ee->lo.u64[1] = SE6432(ee->r[d].u32[2]);\n    ee->hi.u64[0] = SE6432(ee->r[d].u32[1]);\n    ee->hi.u64[1] = SE6432(ee->r[d].u32[3]);\n}\nstatic inline void ee_i_pmultw(struct ee_state* ee) {\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = SE6432(ee->r[s].u32[0]) * SE6432(ee->r[t].u32[0]);\n    ee->r[d].u64[1] = SE6432(ee->r[s].u32[2]) * SE6432(ee->r[t].u32[2]);\n\n    ee->lo.u64[0] = SE6432(ee->r[d].u32[0]);\n    ee->lo.u64[1] = SE6432(ee->r[d].u32[2]);\n    ee->hi.u64[0] = SE6432(ee->r[d].u32[1]);\n    ee->hi.u64[1] = SE6432(ee->r[d].u32[3]);\n}\nstatic inline void ee_i_pnor(struct ee_state* ee) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = ~(rs.u64[0] | rt.u64[0]);\n    ee->r[d].u64[1] = ~(rs.u64[1] | rt.u64[1]);\n}\nstatic inline void ee_i_por(struct ee_state* ee) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = rs.u64[0] | rt.u64[0];\n    ee->r[d].u64[1] = rs.u64[1] | rt.u64[1];\n}\nstatic inline void ee_i_ppac5(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = ((rt.u32[0] & 0x000000f8) >> 3) |\n                      ((rt.u32[0] & 0x0000f800) >> 6) |\n                      ((rt.u32[0] & 0x00f80000) >> 9) |\n                      ((rt.u32[0] & 0x80000000) >> 16);\n    ee->r[d].u32[1] = ((rt.u32[1] & 0x000000f8) >> 3) |\n                      ((rt.u32[1] & 0x0000f800) >> 6) |\n                      ((rt.u32[1] & 0x00f80000) >> 9) |\n                      ((rt.u32[1] & 0x80000000) >> 16);\n    ee->r[d].u32[2] = ((rt.u32[2] & 0x000000f8) >> 3) |\n                      ((rt.u32[2] & 0x0000f800) >> 6) |\n                      ((rt.u32[2] & 0x00f80000) >> 9) |\n                      ((rt.u32[2] & 0x80000000) >> 16);\n    ee->r[d].u32[3] = ((rt.u32[3] & 0x000000f8) >> 3) |\n                      ((rt.u32[3] & 0x0000f800) >> 6) |\n                      ((rt.u32[3] & 0x00f80000) >> 9) |\n                      ((rt.u32[3] & 0x80000000) >> 16);\n}\nstatic inline void ee_i_ppacb(struct ee_state* ee) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u8[0 ] = rt.u8[ 0];\n    ee->r[d].u8[1 ] = rt.u8[ 2];\n    ee->r[d].u8[2 ] = rt.u8[ 4];\n    ee->r[d].u8[3 ] = rt.u8[ 6];\n    ee->r[d].u8[4 ] = rt.u8[ 8];\n    ee->r[d].u8[5 ] = rt.u8[10];\n    ee->r[d].u8[6 ] = rt.u8[12];\n    ee->r[d].u8[7 ] = rt.u8[14];\n    ee->r[d].u8[8 ] = rs.u8[ 0];\n    ee->r[d].u8[9 ] = rs.u8[ 2];\n    ee->r[d].u8[10] = rs.u8[ 4];\n    ee->r[d].u8[11] = rs.u8[ 6];\n    ee->r[d].u8[12] = rs.u8[ 8];\n    ee->r[d].u8[13] = rs.u8[10];\n    ee->r[d].u8[14] = rs.u8[12];\n    ee->r[d].u8[15] = rs.u8[14];\n}\nstatic inline void ee_i_ppach(struct ee_state* ee) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[0];\n    ee->r[d].u16[1] = rt.u16[2];\n    ee->r[d].u16[2] = rt.u16[4];\n    ee->r[d].u16[3] = rt.u16[6];\n    ee->r[d].u16[4] = rs.u16[0];\n    ee->r[d].u16[5] = rs.u16[2];\n    ee->r[d].u16[6] = rs.u16[4];\n    ee->r[d].u16[7] = rs.u16[6];\n}\nstatic inline void ee_i_ppacw(struct ee_state* ee) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = rt.u32[0];\n    ee->r[d].u32[1] = rt.u32[2];\n    ee->r[d].u32[2] = rs.u32[0];\n    ee->r[d].u32[3] = rs.u32[2];\n}\nstatic inline void ee_i_pref(struct ee_state* ee) {\n    // Does nothing\n}\nstatic inline void ee_i_prevh(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = rt.u16[3];\n    ee->r[d].u16[1] = rt.u16[2];\n    ee->r[d].u16[2] = rt.u16[1];\n    ee->r[d].u16[3] = rt.u16[0];\n    ee->r[d].u16[4] = rt.u16[7];\n    ee->r[d].u16[5] = rt.u16[6];\n    ee->r[d].u16[6] = rt.u16[5];\n    ee->r[d].u16[7] = rt.u16[4];\n}\nstatic inline void ee_i_prot3w(struct ee_state* ee) {\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = rt.u32[1];\n    ee->r[d].u32[1] = rt.u32[2];\n    ee->r[d].u32[2] = rt.u32[0];\n    ee->r[d].u32[3] = rt.u32[3];\n}\nstatic inline void ee_i_psllh(struct ee_state* ee) {\n    int sa = EE_D_SA & 0xf;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = ee->r[t].u16[0] << sa;\n    ee->r[d].u16[1] = ee->r[t].u16[1] << sa;\n    ee->r[d].u16[2] = ee->r[t].u16[2] << sa;\n    ee->r[d].u16[3] = ee->r[t].u16[3] << sa;\n    ee->r[d].u16[4] = ee->r[t].u16[4] << sa;\n    ee->r[d].u16[5] = ee->r[t].u16[5] << sa;\n    ee->r[d].u16[6] = ee->r[t].u16[6] << sa;\n    ee->r[d].u16[7] = ee->r[t].u16[7] << sa;\n}\nstatic inline void ee_i_psllvw(struct ee_state* ee) { printf(\"ee: psllvw unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_psllw(struct ee_state* ee) {\n    int sa = EE_D_SA;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = ee->r[t].u32[0] << sa;\n    ee->r[d].u32[1] = ee->r[t].u32[1] << sa;\n    ee->r[d].u32[2] = ee->r[t].u32[2] << sa;\n    ee->r[d].u32[3] = ee->r[t].u32[3] << sa;\n}\nstatic inline void ee_i_psrah(struct ee_state* ee) {\n    int sa = EE_D_SA & 0xf;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = ((int16_t)ee->r[t].u16[0]) >> sa;\n    ee->r[d].u16[1] = ((int16_t)ee->r[t].u16[1]) >> sa;\n    ee->r[d].u16[2] = ((int16_t)ee->r[t].u16[2]) >> sa;\n    ee->r[d].u16[3] = ((int16_t)ee->r[t].u16[3]) >> sa;\n    ee->r[d].u16[4] = ((int16_t)ee->r[t].u16[4]) >> sa;\n    ee->r[d].u16[5] = ((int16_t)ee->r[t].u16[5]) >> sa;\n    ee->r[d].u16[6] = ((int16_t)ee->r[t].u16[6]) >> sa;\n    ee->r[d].u16[7] = ((int16_t)ee->r[t].u16[7]) >> sa;\n}\nstatic inline void ee_i_psravw(struct ee_state* ee) { printf(\"ee: psravw unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_psraw(struct ee_state* ee) {\n    int sa = EE_D_SA;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = ((int32_t)ee->r[t].u32[0]) >> sa;\n    ee->r[d].u32[1] = ((int32_t)ee->r[t].u32[1]) >> sa;\n    ee->r[d].u32[2] = ((int32_t)ee->r[t].u32[2]) >> sa;\n    ee->r[d].u32[3] = ((int32_t)ee->r[t].u32[3]) >> sa;\n}\nstatic inline void ee_i_psrlh(struct ee_state* ee) {\n    int sa = EE_D_SA & 0xf;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = ee->r[t].u16[0] >> sa;\n    ee->r[d].u16[1] = ee->r[t].u16[1] >> sa;\n    ee->r[d].u16[2] = ee->r[t].u16[2] >> sa;\n    ee->r[d].u16[3] = ee->r[t].u16[3] >> sa;\n    ee->r[d].u16[4] = ee->r[t].u16[4] >> sa;\n    ee->r[d].u16[5] = ee->r[t].u16[5] >> sa;\n    ee->r[d].u16[6] = ee->r[t].u16[6] >> sa;\n    ee->r[d].u16[7] = ee->r[t].u16[7] >> sa;\n}\nstatic inline void ee_i_psrlvw(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u64[0] = SE6432(ee->r[t].u32[0] >> (ee->r[s].u32[0] & 31));\n    ee->r[d].u64[1] = SE6432(ee->r[t].u32[2] >> (ee->r[s].u32[2] & 31));\n}\nstatic inline void ee_i_psrlw(struct ee_state* ee) {\n    int sa = EE_D_SA;\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[d].u32[0] = ee->r[t].u32[0] >> sa;\n    ee->r[d].u32[1] = ee->r[t].u32[1] >> sa;\n    ee->r[d].u32[2] = ee->r[t].u32[2] >> sa;\n    ee->r[d].u32[3] = ee->r[t].u32[3] >> sa;\n}\nstatic inline void ee_i_psubb(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int s = EE_D_RS;\n    int d = EE_D_RD;\n\n    ee->r[d].u8[0 ] = ee->r[s].u8[0 ] - ee->r[t].u8[0 ];\n    ee->r[d].u8[1 ] = ee->r[s].u8[1 ] - ee->r[t].u8[1 ];\n    ee->r[d].u8[2 ] = ee->r[s].u8[2 ] - ee->r[t].u8[2 ];\n    ee->r[d].u8[3 ] = ee->r[s].u8[3 ] - ee->r[t].u8[3 ];\n    ee->r[d].u8[4 ] = ee->r[s].u8[4 ] - ee->r[t].u8[4 ];\n    ee->r[d].u8[5 ] = ee->r[s].u8[5 ] - ee->r[t].u8[5 ];\n    ee->r[d].u8[6 ] = ee->r[s].u8[6 ] - ee->r[t].u8[6 ];\n    ee->r[d].u8[7 ] = ee->r[s].u8[7 ] - ee->r[t].u8[7 ];\n    ee->r[d].u8[8 ] = ee->r[s].u8[8 ] - ee->r[t].u8[8 ];\n    ee->r[d].u8[9 ] = ee->r[s].u8[9 ] - ee->r[t].u8[9 ];\n    ee->r[d].u8[10] = ee->r[s].u8[10] - ee->r[t].u8[10];\n    ee->r[d].u8[11] = ee->r[s].u8[11] - ee->r[t].u8[11];\n    ee->r[d].u8[12] = ee->r[s].u8[12] - ee->r[t].u8[12];\n    ee->r[d].u8[13] = ee->r[s].u8[13] - ee->r[t].u8[13];\n    ee->r[d].u8[14] = ee->r[s].u8[14] - ee->r[t].u8[14];\n    ee->r[d].u8[15] = ee->r[s].u8[15] - ee->r[t].u8[15];\n}\nstatic inline void ee_i_psubh(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int s = EE_D_RS;\n    int d = EE_D_RD;\n\n    ee->r[d].u16[0] = ee->r[s].u16[0] - ee->r[t].u16[0];\n    ee->r[d].u16[1] = ee->r[s].u16[1] - ee->r[t].u16[1];\n    ee->r[d].u16[2] = ee->r[s].u16[2] - ee->r[t].u16[2];\n    ee->r[d].u16[3] = ee->r[s].u16[3] - ee->r[t].u16[3];\n    ee->r[d].u16[4] = ee->r[s].u16[4] - ee->r[t].u16[4];\n    ee->r[d].u16[5] = ee->r[s].u16[5] - ee->r[t].u16[5];\n    ee->r[d].u16[6] = ee->r[s].u16[6] - ee->r[t].u16[6];\n    ee->r[d].u16[7] = ee->r[s].u16[7] - ee->r[t].u16[7];\n}\nstatic inline void ee_i_psubsb(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int32_t r0  = ((int32_t)(int8_t)ee->r[s].u8[0 ]) - ((int32_t)(int8_t)ee->r[t].u8[0 ]);\n    int32_t r1  = ((int32_t)(int8_t)ee->r[s].u8[1 ]) - ((int32_t)(int8_t)ee->r[t].u8[1 ]);\n    int32_t r2  = ((int32_t)(int8_t)ee->r[s].u8[2 ]) - ((int32_t)(int8_t)ee->r[t].u8[2 ]);\n    int32_t r3  = ((int32_t)(int8_t)ee->r[s].u8[3 ]) - ((int32_t)(int8_t)ee->r[t].u8[3 ]);\n    int32_t r4  = ((int32_t)(int8_t)ee->r[s].u8[4 ]) - ((int32_t)(int8_t)ee->r[t].u8[4 ]);\n    int32_t r5  = ((int32_t)(int8_t)ee->r[s].u8[5 ]) - ((int32_t)(int8_t)ee->r[t].u8[5 ]);\n    int32_t r6  = ((int32_t)(int8_t)ee->r[s].u8[6 ]) - ((int32_t)(int8_t)ee->r[t].u8[6 ]);\n    int32_t r7  = ((int32_t)(int8_t)ee->r[s].u8[7 ]) - ((int32_t)(int8_t)ee->r[t].u8[7 ]);\n    int32_t r8  = ((int32_t)(int8_t)ee->r[s].u8[8 ]) - ((int32_t)(int8_t)ee->r[t].u8[8 ]);\n    int32_t r9  = ((int32_t)(int8_t)ee->r[s].u8[9 ]) - ((int32_t)(int8_t)ee->r[t].u8[9 ]);\n    int32_t r10 = ((int32_t)(int8_t)ee->r[s].u8[10]) - ((int32_t)(int8_t)ee->r[t].u8[10]);\n    int32_t r11 = ((int32_t)(int8_t)ee->r[s].u8[11]) - ((int32_t)(int8_t)ee->r[t].u8[11]);\n    int32_t r12 = ((int32_t)(int8_t)ee->r[s].u8[12]) - ((int32_t)(int8_t)ee->r[t].u8[12]);\n    int32_t r13 = ((int32_t)(int8_t)ee->r[s].u8[13]) - ((int32_t)(int8_t)ee->r[t].u8[13]);\n    int32_t r14 = ((int32_t)(int8_t)ee->r[s].u8[14]) - ((int32_t)(int8_t)ee->r[t].u8[14]);\n    int32_t r15 = ((int32_t)(int8_t)ee->r[s].u8[15]) - ((int32_t)(int8_t)ee->r[t].u8[15]);\n\n    ee->r[d].u8[0 ] = (r0 >= 0x7f) ? 0x7f : ((r0 < -0x80) ? 0x80 : r0);\n    ee->r[d].u8[1 ] = (r1 >= 0x7f) ? 0x7f : ((r1 < -0x80) ? 0x80 : r1);\n    ee->r[d].u8[2 ] = (r2 >= 0x7f) ? 0x7f : ((r2 < -0x80) ? 0x80 : r2);\n    ee->r[d].u8[3 ] = (r3 >= 0x7f) ? 0x7f : ((r3 < -0x80) ? 0x80 : r3);\n    ee->r[d].u8[4 ] = (r4 >= 0x7f) ? 0x7f : ((r4 < -0x80) ? 0x80 : r4);\n    ee->r[d].u8[5 ] = (r5 >= 0x7f) ? 0x7f : ((r5 < -0x80) ? 0x80 : r5);\n    ee->r[d].u8[6 ] = (r6 >= 0x7f) ? 0x7f : ((r6 < -0x80) ? 0x80 : r6);\n    ee->r[d].u8[7 ] = (r7 >= 0x7f) ? 0x7f : ((r7 < -0x80) ? 0x80 : r7);\n    ee->r[d].u8[8 ] = (r8 >= 0x7f) ? 0x7f : ((r8 < -0x80) ? 0x80 : r8);\n    ee->r[d].u8[9 ] = (r9 >= 0x7f) ? 0x7f : ((r9 < -0x80) ? 0x80 : r9);\n    ee->r[d].u8[10] = (r10 >= 0x7f) ? 0x7f : ((r10 < -0x80) ? 0x80 : r10);\n    ee->r[d].u8[11] = (r11 >= 0x7f) ? 0x7f : ((r11 < -0x80) ? 0x80 : r11);\n    ee->r[d].u8[12] = (r12 >= 0x7f) ? 0x7f : ((r12 < -0x80) ? 0x80 : r12);\n    ee->r[d].u8[13] = (r13 >= 0x7f) ? 0x7f : ((r13 < -0x80) ? 0x80 : r13);\n    ee->r[d].u8[14] = (r14 >= 0x7f) ? 0x7f : ((r14 < -0x80) ? 0x80 : r14);\n    ee->r[d].u8[15] = (r15 >= 0x7f) ? 0x7f : ((r15 < -0x80) ? 0x80 : r15);\n}\nstatic inline void ee_i_psubsh(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int32_t r0 = SE3216(ee->r[s].u16[0]) - SE3216(ee->r[t].u16[0]);\n    int32_t r1 = SE3216(ee->r[s].u16[1]) - SE3216(ee->r[t].u16[1]);\n    int32_t r2 = SE3216(ee->r[s].u16[2]) - SE3216(ee->r[t].u16[2]);\n    int32_t r3 = SE3216(ee->r[s].u16[3]) - SE3216(ee->r[t].u16[3]);\n    int32_t r4 = SE3216(ee->r[s].u16[4]) - SE3216(ee->r[t].u16[4]);\n    int32_t r5 = SE3216(ee->r[s].u16[5]) - SE3216(ee->r[t].u16[5]);\n    int32_t r6 = SE3216(ee->r[s].u16[6]) - SE3216(ee->r[t].u16[6]);\n    int32_t r7 = SE3216(ee->r[s].u16[7]) - SE3216(ee->r[t].u16[7]);\n\n    ee->r[d].u16[0] = (r0 >= 0x7fff) ? 0x7fff : ((r0 < -0x8000) ? 0x8000 : r0);\n    ee->r[d].u16[1] = (r1 >= 0x7fff) ? 0x7fff : ((r1 < -0x8000) ? 0x8000 : r1);\n    ee->r[d].u16[2] = (r2 >= 0x7fff) ? 0x7fff : ((r2 < -0x8000) ? 0x8000 : r2);\n    ee->r[d].u16[3] = (r3 >= 0x7fff) ? 0x7fff : ((r3 < -0x8000) ? 0x8000 : r3);\n    ee->r[d].u16[4] = (r4 >= 0x7fff) ? 0x7fff : ((r4 < -0x8000) ? 0x8000 : r4);\n    ee->r[d].u16[5] = (r5 >= 0x7fff) ? 0x7fff : ((r5 < -0x8000) ? 0x8000 : r5);\n    ee->r[d].u16[6] = (r6 >= 0x7fff) ? 0x7fff : ((r6 < -0x8000) ? 0x8000 : r6);\n    ee->r[d].u16[7] = (r7 >= 0x7fff) ? 0x7fff : ((r7 < -0x8000) ? 0x8000 : r7);\n}\nstatic inline void ee_i_psubsw(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int64_t r0 = SE6432(ee->r[s].u32[0]) - SE6432(ee->r[t].u32[0]);\n    int64_t r1 = SE6432(ee->r[s].u32[1]) - SE6432(ee->r[t].u32[1]);\n    int64_t r2 = SE6432(ee->r[s].u32[2]) - SE6432(ee->r[t].u32[2]);\n    int64_t r3 = SE6432(ee->r[s].u32[3]) - SE6432(ee->r[t].u32[3]);\n\n    ee->r[d].u32[0] = (r0 >= 0x7fffffff) ? 0x7fffffff : ((r0 < (int32_t)0x80000000) ? 0x80000000 : r0);\n    ee->r[d].u32[1] = (r1 >= 0x7fffffff) ? 0x7fffffff : ((r1 < (int32_t)0x80000000) ? 0x80000000 : r1);\n    ee->r[d].u32[2] = (r2 >= 0x7fffffff) ? 0x7fffffff : ((r2 < (int32_t)0x80000000) ? 0x80000000 : r2);\n    ee->r[d].u32[3] = (r3 >= 0x7fffffff) ? 0x7fffffff : ((r3 < (int32_t)0x80000000) ? 0x80000000 : r3);\n}\nstatic inline void ee_i_psubub(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int32_t r0  = ((int32_t)ee->r[s].u8[0 ]) - ((int32_t)ee->r[t].u8[0 ]);\n    int32_t r1  = ((int32_t)ee->r[s].u8[1 ]) - ((int32_t)ee->r[t].u8[1 ]);\n    int32_t r2  = ((int32_t)ee->r[s].u8[2 ]) - ((int32_t)ee->r[t].u8[2 ]);\n    int32_t r3  = ((int32_t)ee->r[s].u8[3 ]) - ((int32_t)ee->r[t].u8[3 ]);\n    int32_t r4  = ((int32_t)ee->r[s].u8[4 ]) - ((int32_t)ee->r[t].u8[4 ]);\n    int32_t r5  = ((int32_t)ee->r[s].u8[5 ]) - ((int32_t)ee->r[t].u8[5 ]);\n    int32_t r6  = ((int32_t)ee->r[s].u8[6 ]) - ((int32_t)ee->r[t].u8[6 ]);\n    int32_t r7  = ((int32_t)ee->r[s].u8[7 ]) - ((int32_t)ee->r[t].u8[7 ]);\n    int32_t r8  = ((int32_t)ee->r[s].u8[8 ]) - ((int32_t)ee->r[t].u8[8 ]);\n    int32_t r9  = ((int32_t)ee->r[s].u8[9 ]) - ((int32_t)ee->r[t].u8[9 ]);\n    int32_t r10 = ((int32_t)ee->r[s].u8[10]) - ((int32_t)ee->r[t].u8[10]);\n    int32_t r11 = ((int32_t)ee->r[s].u8[11]) - ((int32_t)ee->r[t].u8[11]);\n    int32_t r12 = ((int32_t)ee->r[s].u8[12]) - ((int32_t)ee->r[t].u8[12]);\n    int32_t r13 = ((int32_t)ee->r[s].u8[13]) - ((int32_t)ee->r[t].u8[13]);\n    int32_t r14 = ((int32_t)ee->r[s].u8[14]) - ((int32_t)ee->r[t].u8[14]);\n    int32_t r15 = ((int32_t)ee->r[s].u8[15]) - ((int32_t)ee->r[t].u8[15]);\n\n    ee->r[d].u8[0 ] = (r0 < 0) ? 0 : r0;\n    ee->r[d].u8[1 ] = (r1 < 0) ? 0 : r1;\n    ee->r[d].u8[2 ] = (r2 < 0) ? 0 : r2;\n    ee->r[d].u8[3 ] = (r3 < 0) ? 0 : r3;\n    ee->r[d].u8[4 ] = (r4 < 0) ? 0 : r4;\n    ee->r[d].u8[5 ] = (r5 < 0) ? 0 : r5;\n    ee->r[d].u8[6 ] = (r6 < 0) ? 0 : r6;\n    ee->r[d].u8[7 ] = (r7 < 0) ? 0 : r7;\n    ee->r[d].u8[8 ] = (r8 < 0) ? 0 : r8;\n    ee->r[d].u8[9 ] = (r9 < 0) ? 0 : r9;\n    ee->r[d].u8[10] = (r10 < 0) ? 0 : r10;\n    ee->r[d].u8[11] = (r11 < 0) ? 0 : r11;\n    ee->r[d].u8[12] = (r12 < 0) ? 0 : r12;\n    ee->r[d].u8[13] = (r13 < 0) ? 0 : r13;\n    ee->r[d].u8[14] = (r14 < 0) ? 0 : r14;\n    ee->r[d].u8[15] = (r15 < 0) ? 0 : r15;\n}\nstatic inline void ee_i_psubuh(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int32_t r0 = (int32_t)(ee->r[s].u16[0]) - (int32_t)(ee->r[t].u16[0]);\n    int32_t r1 = (int32_t)(ee->r[s].u16[1]) - (int32_t)(ee->r[t].u16[1]);\n    int32_t r2 = (int32_t)(ee->r[s].u16[2]) - (int32_t)(ee->r[t].u16[2]);\n    int32_t r3 = (int32_t)(ee->r[s].u16[3]) - (int32_t)(ee->r[t].u16[3]);\n    int32_t r4 = (int32_t)(ee->r[s].u16[4]) - (int32_t)(ee->r[t].u16[4]);\n    int32_t r5 = (int32_t)(ee->r[s].u16[5]) - (int32_t)(ee->r[t].u16[5]);\n    int32_t r6 = (int32_t)(ee->r[s].u16[6]) - (int32_t)(ee->r[t].u16[6]);\n    int32_t r7 = (int32_t)(ee->r[s].u16[7]) - (int32_t)(ee->r[t].u16[7]);\n\n    ee->r[d].u16[0] = (r0 < 0) ? 0 : r0;\n    ee->r[d].u16[1] = (r1 < 0) ? 0 : r1;\n    ee->r[d].u16[2] = (r2 < 0) ? 0 : r2;\n    ee->r[d].u16[3] = (r3 < 0) ? 0 : r3;\n    ee->r[d].u16[4] = (r4 < 0) ? 0 : r4;\n    ee->r[d].u16[5] = (r5 < 0) ? 0 : r5;\n    ee->r[d].u16[6] = (r6 < 0) ? 0 : r6;\n    ee->r[d].u16[7] = (r7 < 0) ? 0 : r7;\n}\nstatic inline void ee_i_psubuw(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    int64_t r0 = (int64_t)ee->r[s].u32[0] - (int64_t)ee->r[t].u32[0];\n    int64_t r1 = (int64_t)ee->r[s].u32[1] - (int64_t)ee->r[t].u32[1];\n    int64_t r2 = (int64_t)ee->r[s].u32[2] - (int64_t)ee->r[t].u32[2];\n    int64_t r3 = (int64_t)ee->r[s].u32[3] - (int64_t)ee->r[t].u32[3];\n\n    ee->r[d].u32[0] = (r0 < 0) ? 0 : r0;\n    ee->r[d].u32[1] = (r1 < 0) ? 0 : r1;\n    ee->r[d].u32[2] = (r2 < 0) ? 0 : r2;\n    ee->r[d].u32[3] = (r3 < 0) ? 0 : r3;\n}\nstatic inline void ee_i_psubw(struct ee_state* ee) {\n    int d = EE_D_RD;\n    int s = EE_D_RS;\n    int t = EE_D_RT;\n\n    ee->r[d].u32[0] = ee->r[s].u32[0] - ee->r[t].u32[0];\n    ee->r[d].u32[1] = ee->r[s].u32[1] - ee->r[t].u32[1];\n    ee->r[d].u32[2] = ee->r[s].u32[2] - ee->r[t].u32[2];\n    ee->r[d].u32[3] = ee->r[s].u32[3] - ee->r[t].u32[3];\n}\nstatic inline void ee_i_pxor(struct ee_state* ee) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    ee->r[d].u64[0] = rs.u64[0] ^ rt.u64[0];\n    ee->r[d].u64[1] = rs.u64[1] ^ rt.u64[1];\n}\nstatic inline void ee_i_qfsrv(struct ee_state* ee) {\n    uint128_t rs = ee->r[EE_D_RS];\n    uint128_t rt = ee->r[EE_D_RT];\n    int d = EE_D_RD;\n\n    int shift = ee->sa * 8;\n\n    uint128_t v;\n\n    if (!shift) {\n        v = rt;\n    } else {\n        if (shift < 64) {\n            v.u64[0] = rt.u64[0] >> shift;\n            v.u64[1] = rt.u64[1] >> shift;\n            v.u64[0] |= rt.u64[1] << (64 - shift);\n            v.u64[1] |= rs.u64[0] << (64 - shift);\n        } else {\n            v.u64[0] = rt.u64[1] >> (shift - 64);\n            v.u64[1] = rs.u64[0] >> (shift - 64);\n\n            if (shift != 64) {\n                v.u64[0] |= rs.u64[0] << (128u - shift);\n                v.u64[1] |= rs.u64[1] << (128u - shift);\n            }\n        }\n    }\n\n    ee->r[d] = v;\n}\nstatic inline void ee_i_qmfc2(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    ee->r[t].u64[0] = ee->vu0->vf[d].u64[0];\n    ee->r[t].u64[1] = ee->vu0->vf[d].u64[1];\n}\nstatic inline void ee_i_qmtc2(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int d = EE_D_RD;\n\n    if (!d) return;\n\n    ee->vu0->vf[d].u128 = ee->r[t];\n}\nstatic inline void ee_i_rsqrts(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int d = EE_D_FD;\n\n    ee->fcr &= ~(FPU_FLG_I | FPU_FLG_D);\n\n    if ((ee->f[t].u32 & 0x7f800000) == 0) {\n        ee->fcr |= FPU_FLG_D | FPU_FLG_SD;\n        ee->f[d].u32 = (ee->f[t].u32 & 0x80000000) | 0x7f7fffff;\n        \n        return;\n    } else if (ee->f[t].u32 & 0x80000000) {\n        ee->fcr |= FPU_FLG_I | FPU_FLG_SI;\n\n        ee->f[d].f = EE_FS / sqrtf(fabsf(fpu_cvtf(ee->f[t].f)));\n    } else {\n        ee->f[d].f = EE_FS / sqrtf(fpu_cvtf(ee->f[t].f));\n    }\n\n    if (fpu_check_overflow_no_flags(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow_no_flags(ee, &ee->f[d]);\n}\nstatic inline void ee_i_sb(struct ee_state* ee) {\n    bus_write8(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT);\n}\nstatic inline void ee_i_sd(struct ee_state* ee) {\n    bus_write64(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT);\n}\nstatic inline void ee_i_sdl(struct ee_state* ee) {\n    static const uint8_t sdl_shift[8] = { 56, 48, 40, 32, 24, 16, 8, 0 };\n    static const uint64_t sdl_mask[8] = {\n        0xffffffffffffff00ULL, 0xffffffffffff0000ULL, 0xffffffffff000000ULL, 0xffffffff00000000ULL,\n        0xffffff0000000000ULL, 0xffff000000000000ULL, 0xff00000000000000ULL, 0x0000000000000000ULL\n    };\n\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t shift = addr & 7;\n    uint64_t data = bus_read64(ee, addr & ~7);\n\n    bus_write64(ee, addr & ~7, (EE_RT >> sdl_shift[shift]) | (data & sdl_mask[shift]));\n}\nstatic inline void ee_i_sdr(struct ee_state* ee) {\n    static const uint8_t sdr_shift[8] = { 0, 8, 16, 24, 32, 40, 48, 56 };\n    static const uint64_t sdr_mask[8] = {\n        0x0000000000000000ULL, 0x00000000000000ffULL, 0x000000000000ffffULL, 0x0000000000ffffffULL,\n        0x00000000ffffffffULL, 0x000000ffffffffffULL, 0x0000ffffffffffffULL, 0x00ffffffffffffffULL\n    };\n\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t shift = addr & 7;\n    uint64_t data = bus_read64(ee, addr & ~7);\n\n    bus_write64(ee, addr & ~7, (EE_RT << sdr_shift[shift]) | (data & sdr_mask[shift]));\n}\nstatic inline void ee_i_sh(struct ee_state* ee) {\n    bus_write16(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT);\n}\nstatic inline void ee_i_sll(struct ee_state* ee) {\n    EE_RD = SE6432(EE_RT32 << EE_D_SA);\n}\nstatic inline void ee_i_sllv(struct ee_state* ee) {\n    EE_RD = SE6432(EE_RT32 << (EE_RS & 0x1f));\n}\nstatic inline void ee_i_slt(struct ee_state* ee) {\n    EE_RD = (int64_t)EE_RS < (int64_t)EE_RT;\n}\nstatic inline void ee_i_slti(struct ee_state* ee) {\n    EE_RT = ((int64_t)EE_RS) < SE6416(EE_D_I16);\n}\nstatic inline void ee_i_sltiu(struct ee_state* ee) {\n    EE_RT = EE_RS < (uint64_t)(SE6416(EE_D_I16));\n}\nstatic inline void ee_i_sltu(struct ee_state* ee) {\n    EE_RD = EE_RS < EE_RT;\n}\nstatic inline void ee_i_sq(struct ee_state* ee) {\n    bus_write128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf, ee->r[EE_D_RT]);\n}\nstatic inline void ee_i_sqc2(struct ee_state* ee) {\n    bus_write128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf, ee->vu0->vf[EE_D_RT].u128);\n}\nstatic inline void ee_i_sqrts(struct ee_state* ee) {\n    int t = EE_D_RT;\n    int d = EE_D_FD;\n\n    ee->fcr &= ~(FPU_FLG_I | FPU_FLG_D);\n\n    if ((ee->f[t].u32 & 0x7f800000) == 0) {\n        ee->f[d].u32 = ee->f[t].u32 & 0x80000000;\n    } else if (ee->f[t].u32 & 0x80000000) {\n        ee->fcr |= FPU_FLG_I | FPU_FLG_SI;\n\n        ee->f[d].f = sqrtf(fabsf(fpu_cvtf(ee->f[t].f)));\n    } else {\n        ee->f[d].f = sqrtf(fpu_cvtf(ee->f[t].f));\n    }\n}\nstatic inline void ee_i_sra(struct ee_state* ee) {\n    EE_RD = SE6432(((int32_t)EE_RT32) >> EE_D_SA);\n}\nstatic inline void ee_i_srav(struct ee_state* ee) {\n    EE_RD = SE6432(((int32_t)EE_RT32) >> (EE_RS & 0x1f));\n}\nstatic inline void ee_i_srl(struct ee_state* ee) {\n    EE_RD = SE6432(EE_RT32 >> EE_D_SA);\n}\nstatic inline void ee_i_srlv(struct ee_state* ee) {\n    EE_RD = SE6432(EE_RT32 >> (EE_RS & 0x1f));\n}\nstatic inline void ee_i_sub(struct ee_state* ee) {\n    int32_t r;\n\n    int o = __builtin_ssub_overflow(EE_RS32, EE_RT32, &r);\n\n    if (o) {\n        ee_exception_level1(ee, CAUSE_EXC1_OV);\n    } else {\n        EE_RD = SE6432(r);\n    }\n}\nstatic inline void ee_i_subas(struct ee_state* ee) {\n    ee->a.f = EE_FS - EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->a))\n        return;\n\n    fpu_check_underflow(ee, &ee->a);\n}\nstatic inline void ee_i_subs(struct ee_state* ee) {\n    int d = EE_D_FD;\n\n    ee->f[d].f = EE_FS - EE_FT;\n\n    if (fpu_check_overflow(ee, &ee->f[d]))\n        return;\n\n    fpu_check_underflow(ee, &ee->f[d]);\n}\nstatic inline void ee_i_subu(struct ee_state* ee) {\n    EE_RD = SE6432(EE_RS - EE_RT);\n}\nstatic inline void ee_i_sw(struct ee_state* ee) {\n    bus_write32(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT32);\n}\nstatic inline void ee_i_swc1(struct ee_state* ee) {\n    bus_write32(ee, EE_RS32 + SE3216(EE_D_I16), EE_FT32);\n}\nstatic inline void ee_i_swl(struct ee_state* ee) {\n    static const uint32_t swl_mask[4] = { 0xffffff00, 0xffff0000, 0xff000000, 0x00000000 };\n    static const uint8_t swl_shift[4] = { 24, 16, 8, 0 };\n\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t mem = bus_read32(ee, addr & ~3);\n\n    int shift = addr & 3;\n\n    bus_write32(ee, addr & ~3, (EE_RT32 >> swl_shift[shift] | (mem & swl_mask[shift])));\n\n    // printf(\"swl mem=%08x reg=%016lx addr=%08x shift=%d rs=%08x i16=%04x\\n\", mem, ee->r[EE_D_RT].u64[0], addr, shift, EE_RS32, EE_D_I16);\n}\nstatic inline void ee_i_swr(struct ee_state* ee) {\n    static const uint32_t swr_mask[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff };\n    static const uint8_t swr_shift[4] = { 0, 8, 16, 24 };\n\n    uint32_t addr = EE_RS32 + SE3216(EE_D_I16);\n    uint32_t mem = bus_read32(ee, addr & ~3);\n\n    int shift = addr & 3;\n\n    bus_write32(ee, addr & ~3, (EE_RT32 << swr_shift[shift]) | (mem & swr_mask[shift]));\n\n    // printf(\"swl mem=%08x reg=%016lx addr=%08x shift=%d rs=%08x i16=%04x\\n\", mem, ee->r[EE_D_RT].u64[0], addr, shift, EE_RS32, EE_D_I16);\n}\nstatic inline void ee_i_sync(struct ee_state* ee) {\n    /* Do nothing */\n}\n\n// #include \"syscall.h\"\n\nstatic inline void ee_i_syscall(struct ee_state* ee) {\n    // uint32_t n = ee->r[3].ul64;\n\n    // if (n & 0x80000000) {\n    //     n = (~n) + 1;\n    // }\n\n    // printf(\"ee: %s (%d, %08x) a0=%08x (%d)\\n\", ee_get_syscall(n), ee->r[3].ul32, ee->r[3].ul32, ee->r[4].ul32, ee->r[4].ul32);\n\n    ee_exception_level1(ee, CAUSE_EXC1_SYS);\n}\nstatic inline void ee_i_teq(struct ee_state* ee) {\n    if (EE_RS == EE_RT) ee_exception_level1(ee, CAUSE_EXC1_TR);\n}\nstatic inline void ee_i_teqi(struct ee_state* ee) {\n    if (EE_RS == SE6416(EE_D_I16)) ee_exception_level1(ee, CAUSE_EXC1_TR);\n}\nstatic inline void ee_i_tge(struct ee_state* ee) { printf(\"ee: tge unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tgei(struct ee_state* ee) { printf(\"ee: tgei unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tgeiu(struct ee_state* ee) { printf(\"ee: tgeiu unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tgeu(struct ee_state* ee) { printf(\"ee: tgeu unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tlbp(struct ee_state* ee) { printf(\"ee: tlbp unimplemented\\n\"); return; exit(1); }\nstatic inline void ee_i_tlbr(struct ee_state* ee) { printf(\"ee: tlbr unimplemented\\n\"); return; exit(1); }\nstatic inline void ee_i_tlbwi(struct ee_state* ee) {\n    printf(\"ee: Index=%d EntryLo0=%08x EntryLo1=%08x EntryHi=%08x PageMask=%08x\\n\",\n        ee->index,\n        ee->entrylo0,\n        ee->entrylo1,\n        ee->entryhi,\n        ee->pagemask\n    );\n    /* To-do: MMU */\n}\nstatic inline void ee_i_tlbwr(struct ee_state* ee) { return; printf(\"ee: tlbwr unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tlt(struct ee_state* ee) { printf(\"ee: tlt unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tlti(struct ee_state* ee) { printf(\"ee: tlti unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tltiu(struct ee_state* ee) { printf(\"ee: tltiu unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tltu(struct ee_state* ee) { printf(\"ee: tltu unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_tne(struct ee_state* ee) {\n    if (EE_RS != EE_RT) ee_exception_level1(ee, CAUSE_EXC1_TR);\n}\nstatic inline void ee_i_tnei(struct ee_state* ee) { printf(\"ee: tnei unimplemented\\n\"); exit(1); }\nstatic inline void ee_i_vabs(struct ee_state* ee) { VU_UPPER(abs) }\nstatic inline void ee_i_vadd(struct ee_state* ee) { VU_UPPER(add) }\nstatic inline void ee_i_vadda(struct ee_state* ee) { VU_UPPER(adda) }\nstatic inline void ee_i_vaddai(struct ee_state* ee) { VU_UPPER(addai) }\nstatic inline void ee_i_vaddaq(struct ee_state* ee) { VU_UPPER(addaq) }\nstatic inline void ee_i_vaddaw(struct ee_state* ee) { VU_UPPER(addaw) }\nstatic inline void ee_i_vaddax(struct ee_state* ee) { VU_UPPER(addax) }\nstatic inline void ee_i_vadday(struct ee_state* ee) { VU_UPPER(adday) }\nstatic inline void ee_i_vaddaz(struct ee_state* ee) { VU_UPPER(addaz) }\nstatic inline void ee_i_vaddi(struct ee_state* ee) { VU_UPPER(addi) }\nstatic inline void ee_i_vaddq(struct ee_state* ee) { VU_UPPER(addq) }\nstatic inline void ee_i_vaddw(struct ee_state* ee) { VU_UPPER(addw) }\nstatic inline void ee_i_vaddx(struct ee_state* ee) { VU_UPPER(addx) }\nstatic inline void ee_i_vaddy(struct ee_state* ee) { VU_UPPER(addy) }\nstatic inline void ee_i_vaddz(struct ee_state* ee) { VU_UPPER(addz) }\nstatic inline void ee_i_vcallms(struct ee_state* ee) {\n    vu_execute_program(ee->vu0, EE_D_I15);\n}\nstatic inline void ee_i_vcallmsr(struct ee_state* ee) {\n    vu_execute_program(ee->vu0, ee->vu0->cmsar0);\n}\nstatic inline void ee_i_vclipw(struct ee_state* ee) { VU_UPPER(clip) }\nstatic inline void ee_i_vdiv(struct ee_state* ee) { VU_LOWER(div) }\nstatic inline void ee_i_vftoi0(struct ee_state* ee) { VU_UPPER(ftoi0) }\nstatic inline void ee_i_vftoi12(struct ee_state* ee) { VU_UPPER(ftoi12) }\nstatic inline void ee_i_vftoi15(struct ee_state* ee) { VU_UPPER(ftoi15) }\nstatic inline void ee_i_vftoi4(struct ee_state* ee) { VU_UPPER(ftoi4) }\nstatic inline void ee_i_viadd(struct ee_state* ee) { VU_LOWER(iadd) }\nstatic inline void ee_i_viaddi(struct ee_state* ee) { VU_LOWER(iaddi) }\nstatic inline void ee_i_viand(struct ee_state* ee) { VU_LOWER(iand) }\nstatic inline void ee_i_vilwr(struct ee_state* ee) { VU_LOWER(ilwr) }\nstatic inline void ee_i_vior(struct ee_state* ee) { VU_LOWER(ior) }\nstatic inline void ee_i_visub(struct ee_state* ee) { VU_LOWER(isub) }\nstatic inline void ee_i_viswr(struct ee_state* ee) { VU_LOWER(iswr) }\nstatic inline void ee_i_vitof0(struct ee_state* ee) { VU_UPPER(itof0) }\nstatic inline void ee_i_vitof12(struct ee_state* ee) { VU_UPPER(itof12) }\nstatic inline void ee_i_vitof15(struct ee_state* ee) { VU_UPPER(itof15) }\nstatic inline void ee_i_vitof4(struct ee_state* ee) { VU_UPPER(itof4) }\nstatic inline void ee_i_vlqd(struct ee_state* ee) { VU_LOWER(lqd) }\nstatic inline void ee_i_vlqi(struct ee_state* ee) { VU_LOWER(lqi) }\nstatic inline void ee_i_vmadd(struct ee_state* ee) { VU_UPPER(madd) }\nstatic inline void ee_i_vmadda(struct ee_state* ee) { VU_UPPER(madda) }\nstatic inline void ee_i_vmaddai(struct ee_state* ee) { VU_UPPER(maddai) }\nstatic inline void ee_i_vmaddaq(struct ee_state* ee) { VU_UPPER(maddaq) }\nstatic inline void ee_i_vmaddaw(struct ee_state* ee) { VU_UPPER(maddaw) }\nstatic inline void ee_i_vmaddax(struct ee_state* ee) { VU_UPPER(maddax) }\nstatic inline void ee_i_vmadday(struct ee_state* ee) { VU_UPPER(madday) }\nstatic inline void ee_i_vmaddaz(struct ee_state* ee) { VU_UPPER(maddaz) }\nstatic inline void ee_i_vmaddi(struct ee_state* ee) { VU_UPPER(maddi) }\nstatic inline void ee_i_vmaddq(struct ee_state* ee) { VU_UPPER(maddq) }\nstatic inline void ee_i_vmaddw(struct ee_state* ee) { VU_UPPER(maddw) }\nstatic inline void ee_i_vmaddx(struct ee_state* ee) { VU_UPPER(maddx) }\nstatic inline void ee_i_vmaddy(struct ee_state* ee) { VU_UPPER(maddy) }\nstatic inline void ee_i_vmaddz(struct ee_state* ee) { VU_UPPER(maddz) }\nstatic inline void ee_i_vmax(struct ee_state* ee) { VU_UPPER(max) }\nstatic inline void ee_i_vmaxi(struct ee_state* ee) { VU_UPPER(maxi) }\nstatic inline void ee_i_vmaxw(struct ee_state* ee) { VU_UPPER(maxw) }\nstatic inline void ee_i_vmaxx(struct ee_state* ee) { VU_UPPER(maxx) }\nstatic inline void ee_i_vmaxy(struct ee_state* ee) { VU_UPPER(maxy) }\nstatic inline void ee_i_vmaxz(struct ee_state* ee) { VU_UPPER(maxz) }\nstatic inline void ee_i_vmfir(struct ee_state* ee) { VU_UPPER(mfir) }\nstatic inline void ee_i_vmini(struct ee_state* ee) { VU_UPPER(mini) }\nstatic inline void ee_i_vminii(struct ee_state* ee) { VU_UPPER(minii) }\nstatic inline void ee_i_vminiw(struct ee_state* ee) { VU_UPPER(miniw) }\nstatic inline void ee_i_vminix(struct ee_state* ee) { VU_UPPER(minix) }\nstatic inline void ee_i_vminiy(struct ee_state* ee) { VU_UPPER(miniy) }\nstatic inline void ee_i_vminiz(struct ee_state* ee) { VU_UPPER(miniz) }\nstatic inline void ee_i_vmove(struct ee_state* ee) { VU_LOWER(move) }\nstatic inline void ee_i_vmr32(struct ee_state* ee) { VU_LOWER(mr32) }\nstatic inline void ee_i_vmsub(struct ee_state* ee) { VU_UPPER(msub) }\nstatic inline void ee_i_vmsuba(struct ee_state* ee) { VU_UPPER(msuba) }\nstatic inline void ee_i_vmsubai(struct ee_state* ee) { VU_UPPER(msubai) }\nstatic inline void ee_i_vmsubaq(struct ee_state* ee) { VU_UPPER(msubaq) }\nstatic inline void ee_i_vmsubaw(struct ee_state* ee) { VU_UPPER(msubaw) }\nstatic inline void ee_i_vmsubax(struct ee_state* ee) { VU_UPPER(msubax) }\nstatic inline void ee_i_vmsubay(struct ee_state* ee) { VU_UPPER(msubay) }\nstatic inline void ee_i_vmsubaz(struct ee_state* ee) { VU_UPPER(msubaz) }\nstatic inline void ee_i_vmsubi(struct ee_state* ee) { VU_UPPER(msubi) }\nstatic inline void ee_i_vmsubq(struct ee_state* ee) { VU_UPPER(msubq) }\nstatic inline void ee_i_vmsubw(struct ee_state* ee) { VU_UPPER(msubw) }\nstatic inline void ee_i_vmsubx(struct ee_state* ee) { VU_UPPER(msubx) }\nstatic inline void ee_i_vmsuby(struct ee_state* ee) { VU_UPPER(msuby) }\nstatic inline void ee_i_vmsubz(struct ee_state* ee) { VU_UPPER(msubz) }\nstatic inline void ee_i_vmtir(struct ee_state* ee) { VU_LOWER(mtir) }\nstatic inline void ee_i_vmul(struct ee_state* ee) { VU_UPPER(mul) }\nstatic inline void ee_i_vmula(struct ee_state* ee) { VU_UPPER(mula) }\nstatic inline void ee_i_vmulai(struct ee_state* ee) { VU_UPPER(mulai) }\nstatic inline void ee_i_vmulaq(struct ee_state* ee) { VU_UPPER(mulaq) }\nstatic inline void ee_i_vmulaw(struct ee_state* ee) { VU_UPPER(mulaw) }\nstatic inline void ee_i_vmulax(struct ee_state* ee) { VU_UPPER(mulax) }\nstatic inline void ee_i_vmulay(struct ee_state* ee) { VU_UPPER(mulay) }\nstatic inline void ee_i_vmulaz(struct ee_state* ee) { VU_UPPER(mulaz) }\nstatic inline void ee_i_vmuli(struct ee_state* ee) { VU_UPPER(muli) }\nstatic inline void ee_i_vmulq(struct ee_state* ee) { VU_UPPER(mulq) }\nstatic inline void ee_i_vmulw(struct ee_state* ee) { VU_UPPER(mulw) }\nstatic inline void ee_i_vmulx(struct ee_state* ee) { VU_UPPER(mulx) }\nstatic inline void ee_i_vmuly(struct ee_state* ee) { VU_UPPER(muly) }\nstatic inline void ee_i_vmulz(struct ee_state* ee) { VU_UPPER(mulz) }\nstatic inline void ee_i_vnop(struct ee_state* ee) { VU_UPPER(nop) }\nstatic inline void ee_i_vopmsub(struct ee_state* ee) { VU_UPPER(opmsub) }\nstatic inline void ee_i_vopmula(struct ee_state* ee) { VU_UPPER(opmula) }\nstatic inline void ee_i_vrget(struct ee_state* ee) { VU_LOWER(rget) }\nstatic inline void ee_i_vrinit(struct ee_state* ee) { VU_LOWER(rinit) }\nstatic inline void ee_i_vrnext(struct ee_state* ee) { VU_LOWER(rnext) }\nstatic inline void ee_i_vrsqrt(struct ee_state* ee) { VU_LOWER(rsqrt) }\nstatic inline void ee_i_vrxor(struct ee_state* ee) { VU_LOWER(rxor) }\nstatic inline void ee_i_vsqd(struct ee_state* ee) { VU_LOWER(sqd) }\nstatic inline void ee_i_vsqi(struct ee_state* ee) { VU_LOWER(sqi) }\nstatic inline void ee_i_vsqrt(struct ee_state* ee) { VU_LOWER(sqrt) }\nstatic inline void ee_i_vsub(struct ee_state* ee) { VU_UPPER(sub) }\nstatic inline void ee_i_vsuba(struct ee_state* ee) { VU_UPPER(suba) }\nstatic inline void ee_i_vsubai(struct ee_state* ee) { VU_UPPER(subai) }\nstatic inline void ee_i_vsubaq(struct ee_state* ee) { VU_UPPER(subaq) }\nstatic inline void ee_i_vsubaw(struct ee_state* ee) { VU_UPPER(subaw) }\nstatic inline void ee_i_vsubax(struct ee_state* ee) { VU_UPPER(subax) }\nstatic inline void ee_i_vsubay(struct ee_state* ee) { VU_UPPER(subay) }\nstatic inline void ee_i_vsubaz(struct ee_state* ee) { VU_UPPER(subaz) }\nstatic inline void ee_i_vsubi(struct ee_state* ee) { VU_UPPER(subi) }\nstatic inline void ee_i_vsubq(struct ee_state* ee) { VU_UPPER(subq) }\nstatic inline void ee_i_vsubw(struct ee_state* ee) { VU_UPPER(subw) }\nstatic inline void ee_i_vsubx(struct ee_state* ee) { VU_UPPER(subx) }\nstatic inline void ee_i_vsuby(struct ee_state* ee) { VU_UPPER(suby) }\nstatic inline void ee_i_vsubz(struct ee_state* ee) { VU_UPPER(subz) }\nstatic inline void ee_i_vwaitq(struct ee_state* ee) { VU_LOWER(waitq) }\nstatic inline void ee_i_xor(struct ee_state* ee) {\n    EE_RD = EE_RS ^ EE_RT;\n}\nstatic inline void ee_i_xori(struct ee_state* ee) {\n    EE_RT = EE_RS ^ EE_D_I16;\n}\n\nstruct ee_state* ee_create(void) {\n    return malloc(sizeof(struct ee_state));\n}\n\nvoid ee_init(struct ee_state* ee, struct vu_state* vu0, struct vu_state* vu1, struct ee_bus_s bus) {\n    memset(ee, 0, sizeof(struct ee_state));\n\n    ee->prid = 0x2e20;\n    ee->pc = EE_VEC_RESET;\n    ee->next_pc = ee->pc + 4;\n    ee->bus = bus;\n    ee->vu0 = vu0;\n    ee->vu1 = vu1;\n\n    // To-do: Set SR\n\n    ee->scratchpad = ps2_ram_create();\n    ps2_ram_init(ee->scratchpad, 0x4000);\n\n    // EE's FPU uses round to zero by default\n    fesetround(FE_TOWARDZERO);\n\n    ee->fcr = 0x01000001;\n}\n\nstatic inline void ee_execute(struct ee_state* ee) {\n    switch ((ee->opcode & 0xFC000000) >> 26) {\n        case 0x00000000 >> 26: { // special\n            switch (ee->opcode & 0x0000003F) {\n                case 0x00000000: ee_i_sll(ee); return;\n                case 0x00000002: ee_i_srl(ee); return;\n                case 0x00000003: ee_i_sra(ee); return;\n                case 0x00000004: ee_i_sllv(ee); return;\n                case 0x00000006: ee_i_srlv(ee); return;\n                case 0x00000007: ee_i_srav(ee); return;\n                case 0x00000008: ee_i_jr(ee); return;\n                case 0x00000009: ee_i_jalr(ee); return;\n                case 0x0000000A: ee_i_movz(ee); return;\n                case 0x0000000B: ee_i_movn(ee); return;\n                case 0x0000000C: ee_i_syscall(ee); return;\n                case 0x0000000D: ee_i_break(ee); return;\n                case 0x0000000F: ee_i_sync(ee); return;\n                case 0x00000010: ee_i_mfhi(ee); return;\n                case 0x00000011: ee_i_mthi(ee); return;\n                case 0x00000012: ee_i_mflo(ee); return;\n                case 0x00000013: ee_i_mtlo(ee); return;\n                case 0x00000014: ee_i_dsllv(ee); return;\n                case 0x00000016: ee_i_dsrlv(ee); return;\n                case 0x00000017: ee_i_dsrav(ee); return;\n                case 0x00000018: ee_i_mult(ee); return;\n                case 0x00000019: ee_i_multu(ee); return;\n                case 0x0000001A: ee_i_div(ee); return;\n                case 0x0000001B: ee_i_divu(ee); return;\n                case 0x00000020: ee_i_add(ee); return;\n                case 0x00000021: ee_i_addu(ee); return;\n                case 0x00000022: ee_i_sub(ee); return;\n                case 0x00000023: ee_i_subu(ee); return;\n                case 0x00000024: ee_i_and(ee); return;\n                case 0x00000025: ee_i_or(ee); return;\n                case 0x00000026: ee_i_xor(ee); return;\n                case 0x00000027: ee_i_nor(ee); return;\n                case 0x00000028: ee_i_mfsa(ee); return;\n                case 0x00000029: ee_i_mtsa(ee); return;\n                case 0x0000002A: ee_i_slt(ee); return;\n                case 0x0000002B: ee_i_sltu(ee); return;\n                case 0x0000002C: ee_i_dadd(ee); return;\n                case 0x0000002D: ee_i_daddu(ee); return;\n                case 0x0000002E: ee_i_dsub(ee); return;\n                case 0x0000002F: ee_i_dsubu(ee); return;\n                case 0x00000030: ee_i_tge(ee); return;\n                case 0x00000031: ee_i_tgeu(ee); return;\n                case 0x00000032: ee_i_tlt(ee); return;\n                case 0x00000033: ee_i_tltu(ee); return;\n                case 0x00000034: ee_i_teq(ee); return;\n                case 0x00000036: ee_i_tne(ee); return;\n                case 0x00000038: ee_i_dsll(ee); return;\n                case 0x0000003A: ee_i_dsrl(ee); return;\n                case 0x0000003B: ee_i_dsra(ee); return;\n                case 0x0000003C: ee_i_dsll32(ee); return;\n                case 0x0000003E: ee_i_dsrl32(ee); return;\n                case 0x0000003F: ee_i_dsra32(ee); return;\n            }\n        } break;\n        case 0x04000000 >> 26: { // regimm\n            switch ((ee->opcode & 0x001F0000) >> 16) {\n                case 0x00000000 >> 16: ee_i_bltz(ee); return;\n                case 0x00010000 >> 16: ee_i_bgez(ee); return;\n                case 0x00020000 >> 16: ee_i_bltzl(ee); return;\n                case 0x00030000 >> 16: ee_i_bgezl(ee); return;\n                case 0x00080000 >> 16: ee_i_tgei(ee); return;\n                case 0x00090000 >> 16: ee_i_tgeiu(ee); return;\n                case 0x000A0000 >> 16: ee_i_tlti(ee); return;\n                case 0x000B0000 >> 16: ee_i_tltiu(ee); return;\n                case 0x000C0000 >> 16: ee_i_teqi(ee); return;\n                case 0x000E0000 >> 16: ee_i_tnei(ee); return;\n                case 0x00100000 >> 16: ee_i_bltzal(ee); return;\n                case 0x00110000 >> 16: ee_i_bgezal(ee); return;\n                case 0x00120000 >> 16: ee_i_bltzall(ee); return;\n                case 0x00130000 >> 16: ee_i_bgezall(ee); return;\n                case 0x00180000 >> 16: ee_i_mtsab(ee); return;\n                case 0x00190000 >> 16: ee_i_mtsah(ee); return;\n            }\n        } break;\n        case 0x08000000 >> 26: ee_i_j(ee); return;\n        case 0x0C000000 >> 26: ee_i_jal(ee); return;\n        case 0x10000000 >> 26: ee_i_beq(ee); return;\n        case 0x14000000 >> 26: ee_i_bne(ee); return;\n        case 0x18000000 >> 26: ee_i_blez(ee); return;\n        case 0x1C000000 >> 26: ee_i_bgtz(ee); return;\n        case 0x20000000 >> 26: ee_i_addi(ee); return;\n        case 0x24000000 >> 26: ee_i_addiu(ee); return;\n        case 0x28000000 >> 26: ee_i_slti(ee); return;\n        case 0x2C000000 >> 26: ee_i_sltiu(ee); return;\n        case 0x30000000 >> 26: ee_i_andi(ee); return;\n        case 0x34000000 >> 26: ee_i_ori(ee); return;\n        case 0x38000000 >> 26: ee_i_xori(ee); return;\n        case 0x3C000000 >> 26: ee_i_lui(ee); return;\n        case 0x40000000 >> 26: { // cop0\n            switch ((ee->opcode & 0x03E00000) >> 21) {\n                case 0x00000000 >> 21: ee_i_mfc0(ee); return;\n                case 0x00800000 >> 21: ee_i_mtc0(ee); return;\n                case 0x01000000 >> 21: {\n                    switch ((ee->opcode & 0x001F0000) >> 16) {\n                        case 0x00000000 >> 16: ee_i_bc0f(ee); return;\n                        case 0x00010000 >> 16: ee_i_bc0t(ee); return;\n                        case 0x00020000 >> 16: ee_i_bc0fl(ee); return;\n                        case 0x00030000 >> 16: ee_i_bc0tl(ee); return;\n                    }\n                } break;\n                case 0x02000000 >> 21: {\n                    switch (ee->opcode & 0x0000003F) {\n                        case 0x00000001: ee_i_tlbr(ee); return;\n                        case 0x00000002: ee_i_tlbwi(ee); return;\n                        case 0x00000006: ee_i_tlbwr(ee); return;\n                        case 0x00000008: ee_i_tlbp(ee); return;\n                        case 0x00000018: ee_i_eret(ee); return;\n                        case 0x00000038: ee_i_ei(ee); return;\n                        case 0x00000039: ee_i_di(ee); return;\n                    }\n                } break;\n            }\n        } break;\n        case 0x44000000 >> 26: { // cop1\n            switch ((ee->opcode & 0x03E00000) >> 21) {\n                case 0x00000000 >> 21: ee_i_mfc1(ee); return;\n                case 0x00400000 >> 21: ee_i_cfc1(ee); return;\n                case 0x00800000 >> 21: ee_i_mtc1(ee); return;\n                case 0x00C00000 >> 21: ee_i_ctc1(ee); return;\n                case 0x01000000 >> 21: {\n                    switch ((ee->opcode & 0x001F0000) >> 16) {\n                        case 0x00000000 >> 16: ee_i_bc1f(ee); return;\n                        case 0x00010000 >> 16: ee_i_bc1t(ee); return;\n                        case 0x00020000 >> 16: ee_i_bc1fl(ee); return;\n                        case 0x00030000 >> 16: ee_i_bc1tl(ee); return;\n                    }\n                } break;\n                case 0x02000000 >> 21: {\n                    switch (ee->opcode & 0x0000003F) {\n                        case 0x00000000: ee_i_adds(ee); return;\n                        case 0x00000001: ee_i_subs(ee); return;\n                        case 0x00000002: ee_i_muls(ee); return;\n                        case 0x00000003: ee_i_divs(ee); return;\n                        case 0x00000004: ee_i_sqrts(ee); return;\n                        case 0x00000005: ee_i_abss(ee); return;\n                        case 0x00000006: ee_i_movs(ee); return;\n                        case 0x00000007: ee_i_negs(ee); return;\n                        case 0x00000016: ee_i_rsqrts(ee); return;\n                        case 0x00000018: ee_i_addas(ee); return;\n                        case 0x00000019: ee_i_subas(ee); return;\n                        case 0x0000001A: ee_i_mulas(ee); return;\n                        case 0x0000001C: ee_i_madds(ee); return;\n                        case 0x0000001D: ee_i_msubs(ee); return;\n                        case 0x0000001E: ee_i_maddas(ee); return;\n                        case 0x0000001F: ee_i_msubas(ee); return;\n                        case 0x00000024: ee_i_cvtw(ee); return;\n                        case 0x00000028: ee_i_maxs(ee); return;\n                        case 0x00000029: ee_i_mins(ee); return;\n                        case 0x00000030: ee_i_cf(ee); return;\n                        case 0x00000032: ee_i_ceq(ee); return;\n                        case 0x00000034: ee_i_clt(ee); return;\n                        case 0x00000036: ee_i_cle(ee); return;\n                    }\n                } break;\n                case 0x02800000 >> 21: {\n                    switch (ee->opcode & 0x0000003F) {\n                        case 0x00000020: ee_i_cvts(ee); return;\n                    }\n                } break;\n            }\n        } break;\n        case 0x48000000 >> 26: { // cop2\n            switch ((ee->opcode & 0x03E00000) >> 21) {\n                case 0x00200000 >> 21: ee_i_qmfc2(ee); return;\n                case 0x00400000 >> 21: ee_i_cfc2(ee); return;\n                case 0x00A00000 >> 21: ee_i_qmtc2(ee); return;\n                case 0x00C00000 >> 21: ee_i_ctc2(ee); return;\n                case 0x01000000 >> 21: {\n                    switch ((ee->opcode & 0x001F0000) >> 16) {\n                        case 0x00000000 >> 16: ee_i_bc2f(ee); return;\n                        case 0x00010000 >> 16: ee_i_bc2t(ee); return;\n                        case 0x00020000 >> 16: ee_i_bc2fl(ee); return;\n                        case 0x00030000 >> 16: ee_i_bc2tl(ee); return;\n                    }\n                } break;\n                case 0x02000000 >> 21:\n                case 0x02200000 >> 21:\n                case 0x02400000 >> 21:\n                case 0x02600000 >> 21:\n                case 0x02800000 >> 21:\n                case 0x02A00000 >> 21:\n                case 0x02C00000 >> 21:\n                case 0x02E00000 >> 21:\n                case 0x03000000 >> 21:\n                case 0x03200000 >> 21:\n                case 0x03400000 >> 21:\n                case 0x03600000 >> 21:\n                case 0x03800000 >> 21:\n                case 0x03A00000 >> 21:\n                case 0x03C00000 >> 21:\n                case 0x03E00000 >> 21: {\n                    switch (ee->opcode & 0x0000003F) {\n                        case 0x00000000: ee_i_vaddx(ee); return;\n                        case 0x00000001: ee_i_vaddy(ee); return;\n                        case 0x00000002: ee_i_vaddz(ee); return;\n                        case 0x00000003: ee_i_vaddw(ee); return;\n                        case 0x00000004: ee_i_vsubx(ee); return;\n                        case 0x00000005: ee_i_vsuby(ee); return;\n                        case 0x00000006: ee_i_vsubz(ee); return;\n                        case 0x00000007: ee_i_vsubw(ee); return;\n                        case 0x00000008: ee_i_vmaddx(ee); return;\n                        case 0x00000009: ee_i_vmaddy(ee); return;\n                        case 0x0000000A: ee_i_vmaddz(ee); return;\n                        case 0x0000000B: ee_i_vmaddw(ee); return;\n                        case 0x0000000C: ee_i_vmsubx(ee); return;\n                        case 0x0000000D: ee_i_vmsuby(ee); return;\n                        case 0x0000000E: ee_i_vmsubz(ee); return;\n                        case 0x0000000F: ee_i_vmsubw(ee); return;\n                        case 0x00000010: ee_i_vmaxx(ee); return;\n                        case 0x00000011: ee_i_vmaxy(ee); return;\n                        case 0x00000012: ee_i_vmaxz(ee); return;\n                        case 0x00000013: ee_i_vmaxw(ee); return;\n                        case 0x00000014: ee_i_vminix(ee); return;\n                        case 0x00000015: ee_i_vminiy(ee); return;\n                        case 0x00000016: ee_i_vminiz(ee); return;\n                        case 0x00000017: ee_i_vminiw(ee); return;\n                        case 0x00000018: ee_i_vmulx(ee); return;\n                        case 0x00000019: ee_i_vmuly(ee); return;\n                        case 0x0000001A: ee_i_vmulz(ee); return;\n                        case 0x0000001B: ee_i_vmulw(ee); return;\n                        case 0x0000001C: ee_i_vmulq(ee); return;\n                        case 0x0000001D: ee_i_vmaxi(ee); return;\n                        case 0x0000001E: ee_i_vmuli(ee); return;\n                        case 0x0000001F: ee_i_vminii(ee); return;\n                        case 0x00000020: ee_i_vaddq(ee); return;\n                        case 0x00000021: ee_i_vmaddq(ee); return;\n                        case 0x00000022: ee_i_vaddi(ee); return;\n                        case 0x00000023: ee_i_vmaddi(ee); return;\n                        case 0x00000024: ee_i_vsubq(ee); return;\n                        case 0x00000025: ee_i_vmsubq(ee); return;\n                        case 0x00000026: ee_i_vsubi(ee); return;\n                        case 0x00000027: ee_i_vmsubi(ee); return;\n                        case 0x00000028: ee_i_vadd(ee); return;\n                        case 0x00000029: ee_i_vmadd(ee); return;\n                        case 0x0000002A: ee_i_vmul(ee); return;\n                        case 0x0000002B: ee_i_vmax(ee); return;\n                        case 0x0000002C: ee_i_vsub(ee); return;\n                        case 0x0000002D: ee_i_vmsub(ee); return;\n                        case 0x0000002E: ee_i_vopmsub(ee); return;\n                        case 0x0000002F: ee_i_vmini(ee); return;\n                        case 0x00000030: ee_i_viadd(ee); return;\n                        case 0x00000031: ee_i_visub(ee); return;\n                        case 0x00000032: ee_i_viaddi(ee); return;\n                        case 0x00000034: ee_i_viand(ee); return;\n                        case 0x00000035: ee_i_vior(ee); return;\n                        case 0x00000038: ee_i_vcallms(ee); return;\n                        case 0x00000039: ee_i_vcallmsr(ee); return;\n                        case 0x0000003C:\n                        case 0x0000003D:\n                        case 0x0000003E:\n                        case 0x0000003F: {\n                            uint32_t func = (ee->opcode & 3) | ((ee->opcode & 0x7c0) >> 4);\n\n                            switch (func) {\n                                case 0x00000000: ee_i_vaddax(ee); return;\n                                case 0x00000001: ee_i_vadday(ee); return;\n                                case 0x00000002: ee_i_vaddaz(ee); return;\n                                case 0x00000003: ee_i_vaddaw(ee); return;\n                                case 0x00000004: ee_i_vsubax(ee); return;\n                                case 0x00000005: ee_i_vsubay(ee); return;\n                                case 0x00000006: ee_i_vsubaz(ee); return;\n                                case 0x00000007: ee_i_vsubaw(ee); return;\n                                case 0x00000008: ee_i_vmaddax(ee); return;\n                                case 0x00000009: ee_i_vmadday(ee); return;\n                                case 0x0000000A: ee_i_vmaddaz(ee); return;\n                                case 0x0000000B: ee_i_vmaddaw(ee); return;\n                                case 0x0000000C: ee_i_vmsubax(ee); return;\n                                case 0x0000000D: ee_i_vmsubay(ee); return;\n                                case 0x0000000E: ee_i_vmsubaz(ee); return;\n                                case 0x0000000F: ee_i_vmsubaw(ee); return;\n                                case 0x00000010: ee_i_vitof0(ee); return;\n                                case 0x00000011: ee_i_vitof4(ee); return;\n                                case 0x00000012: ee_i_vitof12(ee); return;\n                                case 0x00000013: ee_i_vitof15(ee); return;\n                                case 0x00000014: ee_i_vftoi0(ee); return;\n                                case 0x00000015: ee_i_vftoi4(ee); return;\n                                case 0x00000016: ee_i_vftoi12(ee); return;\n                                case 0x00000017: ee_i_vftoi15(ee); return;\n                                case 0x00000018: ee_i_vmulax(ee); return;\n                                case 0x00000019: ee_i_vmulay(ee); return;\n                                case 0x0000001A: ee_i_vmulaz(ee); return;\n                                case 0x0000001B: ee_i_vmulaw(ee); return;\n                                case 0x0000001C: ee_i_vmulaq(ee); return;\n                                case 0x0000001D: ee_i_vabs(ee); return;\n                                case 0x0000001E: ee_i_vmulai(ee); return;\n                                case 0x0000001F: ee_i_vclipw(ee); return;\n                                case 0x00000020: ee_i_vaddaq(ee); return;\n                                case 0x00000021: ee_i_vmaddaq(ee); return;\n                                case 0x00000022: ee_i_vaddai(ee); return;\n                                case 0x00000023: ee_i_vmaddai(ee); return;\n                                case 0x00000024: ee_i_vsubaq(ee); return;\n                                case 0x00000025: ee_i_vmsubaq(ee); return;\n                                case 0x00000026: ee_i_vsubai(ee); return;\n                                case 0x00000027: ee_i_vmsubai(ee); return;\n                                case 0x00000028: ee_i_vadda(ee); return;\n                                case 0x00000029: ee_i_vmadda(ee); return;\n                                case 0x0000002A: ee_i_vmula(ee); return;\n                                case 0x0000002C: ee_i_vsuba(ee); return;\n                                case 0x0000002D: ee_i_vmsuba(ee); return;\n                                case 0x0000002E: ee_i_vopmula(ee); return;\n                                case 0x0000002F: ee_i_vnop(ee); return;\n                                case 0x00000030: ee_i_vmove(ee); return;\n                                case 0x00000031: ee_i_vmr32(ee); return;\n                                case 0x00000034: ee_i_vlqi(ee); return;\n                                case 0x00000035: ee_i_vsqi(ee); return;\n                                case 0x00000036: ee_i_vlqd(ee); return;\n                                case 0x00000037: ee_i_vsqd(ee); return;\n                                case 0x00000038: ee_i_vdiv(ee); return;\n                                case 0x00000039: ee_i_vsqrt(ee); return;\n                                case 0x0000003A: ee_i_vrsqrt(ee); return;\n                                case 0x0000003B: ee_i_vwaitq(ee); return;\n                                case 0x0000003C: ee_i_vmtir(ee); return;\n                                case 0x0000003D: ee_i_vmfir(ee); return;\n                                case 0x0000003E: ee_i_vilwr(ee); return;\n                                case 0x0000003F: ee_i_viswr(ee); return;\n                                case 0x00000040: ee_i_vrnext(ee); return;\n                                case 0x00000041: ee_i_vrget(ee); return;\n                                case 0x00000042: ee_i_vrinit(ee); return;\n                                case 0x00000043: ee_i_vrxor(ee); return;\n                            }\n                        } break;\n                    }\n                } break;\n            }\n        } break;\n        case 0x50000000 >> 26: ee_i_beql(ee); return;\n        case 0x54000000 >> 26: ee_i_bnel(ee); return;\n        case 0x58000000 >> 26: ee_i_blezl(ee); return;\n        case 0x5C000000 >> 26: ee_i_bgtzl(ee); return;\n        case 0x60000000 >> 26: ee_i_daddi(ee); return;\n        case 0x64000000 >> 26: ee_i_daddiu(ee); return;\n        case 0x68000000 >> 26: ee_i_ldl(ee); return;\n        case 0x6C000000 >> 26: ee_i_ldr(ee); return;\n        case 0x70000000 >> 26: { // mmi\n            switch (ee->opcode & 0x0000003F) {\n                case 0x00000000: ee_i_madd(ee); return;\n                case 0x00000001: ee_i_maddu(ee); return;\n                case 0x00000004: ee_i_plzcw(ee); return;\n                case 0x00000008: {\n                    switch ((ee->opcode & 0x000007C0) >> 6) {\n                        case 0x00000000 >> 6: ee_i_paddw(ee); return;\n                        case 0x00000040 >> 6: ee_i_psubw(ee); return;\n                        case 0x00000080 >> 6: ee_i_pcgtw(ee); return;\n                        case 0x000000C0 >> 6: ee_i_pmaxw(ee); return;\n                        case 0x00000100 >> 6: ee_i_paddh(ee); return;\n                        case 0x00000140 >> 6: ee_i_psubh(ee); return;\n                        case 0x00000180 >> 6: ee_i_pcgth(ee); return;\n                        case 0x000001C0 >> 6: ee_i_pmaxh(ee); return;\n                        case 0x00000200 >> 6: ee_i_paddb(ee); return;\n                        case 0x00000240 >> 6: ee_i_psubb(ee); return;\n                        case 0x00000280 >> 6: ee_i_pcgtb(ee); return;\n                        case 0x00000400 >> 6: ee_i_paddsw(ee); return;\n                        case 0x00000440 >> 6: ee_i_psubsw(ee); return;\n                        case 0x00000480 >> 6: ee_i_pextlw(ee); return;\n                        case 0x000004C0 >> 6: ee_i_ppacw(ee); return;\n                        case 0x00000500 >> 6: ee_i_paddsh(ee); return;\n                        case 0x00000540 >> 6: ee_i_psubsh(ee); return;\n                        case 0x00000580 >> 6: ee_i_pextlh(ee); return;\n                        case 0x000005C0 >> 6: ee_i_ppach(ee); return;\n                        case 0x00000600 >> 6: ee_i_paddsb(ee); return;\n                        case 0x00000640 >> 6: ee_i_psubsb(ee); return;\n                        case 0x00000680 >> 6: ee_i_pextlb(ee); return;\n                        case 0x000006C0 >> 6: ee_i_ppacb(ee); return;\n                        case 0x00000780 >> 6: ee_i_pext5(ee); return;\n                        case 0x000007C0 >> 6: ee_i_ppac5(ee); return;\n                    }\n                } break;\n                case 0x00000009: {\n                    switch ((ee->opcode & 0x000007C0) >> 6) {\n                        case 0x00000000 >> 6: ee_i_pmaddw(ee); return;\n                        case 0x00000080 >> 6: ee_i_psllvw(ee); return;\n                        case 0x000000C0 >> 6: ee_i_psrlvw(ee); return;\n                        case 0x00000100 >> 6: ee_i_pmsubw(ee); return;\n                        case 0x00000200 >> 6: ee_i_pmfhi(ee); return;\n                        case 0x00000240 >> 6: ee_i_pmflo(ee); return;\n                        case 0x00000280 >> 6: ee_i_pinth(ee); return;\n                        case 0x00000300 >> 6: ee_i_pmultw(ee); return;\n                        case 0x00000340 >> 6: ee_i_pdivw(ee); return;\n                        case 0x00000380 >> 6: ee_i_pcpyld(ee); return;\n                        case 0x00000400 >> 6: ee_i_pmaddh(ee); return;\n                        case 0x00000440 >> 6: ee_i_phmadh(ee); return;\n                        case 0x00000480 >> 6: ee_i_pand(ee); return;\n                        case 0x000004C0 >> 6: ee_i_pxor(ee); return;\n                        case 0x00000500 >> 6: ee_i_pmsubh(ee); return;\n                        case 0x00000540 >> 6: ee_i_phmsbh(ee); return;\n                        case 0x00000680 >> 6: ee_i_pexeh(ee); return;\n                        case 0x000006C0 >> 6: ee_i_prevh(ee); return;\n                        case 0x00000700 >> 6: ee_i_pmulth(ee); return;\n                        case 0x00000740 >> 6: ee_i_pdivbw(ee); return;\n                        case 0x00000780 >> 6: ee_i_pexew(ee); return;\n                        case 0x000007C0 >> 6: ee_i_prot3w(ee); return;\n                    }\n                } break;\n                case 0x00000010: ee_i_mfhi1(ee); return;\n                case 0x00000011: ee_i_mthi1(ee); return;\n                case 0x00000012: ee_i_mflo1(ee); return;\n                case 0x00000013: ee_i_mtlo1(ee); return;\n                case 0x00000018: ee_i_mult1(ee); return;\n                case 0x00000019: ee_i_multu1(ee); return;\n                case 0x0000001A: ee_i_div1(ee); return;\n                case 0x0000001B: ee_i_divu1(ee); return;\n                case 0x00000020: ee_i_madd1(ee); return;\n                case 0x00000021: ee_i_maddu1(ee); return;\n                case 0x00000028: {\n                    switch ((ee->opcode & 0x000007C0) >> 6) {\n                        case 0x00000040 >> 6: ee_i_pabsw(ee); return;\n                        case 0x00000080 >> 6: ee_i_pceqw(ee); return;\n                        case 0x000000C0 >> 6: ee_i_pminw(ee); return;\n                        case 0x00000100 >> 6: ee_i_padsbh(ee); return;\n                        case 0x00000140 >> 6: ee_i_pabsh(ee); return;\n                        case 0x00000180 >> 6: ee_i_pceqh(ee); return;\n                        case 0x000001C0 >> 6: ee_i_pminh(ee); return;\n                        case 0x00000280 >> 6: ee_i_pceqb(ee); return;\n                        case 0x00000400 >> 6: ee_i_padduw(ee); return;\n                        case 0x00000440 >> 6: ee_i_psubuw(ee); return;\n                        case 0x00000480 >> 6: ee_i_pextuw(ee); return;\n                        case 0x00000500 >> 6: ee_i_padduh(ee); return;\n                        case 0x00000540 >> 6: ee_i_psubuh(ee); return;\n                        case 0x00000580 >> 6: ee_i_pextuh(ee); return;\n                        case 0x00000600 >> 6: ee_i_paddub(ee); return;\n                        case 0x00000640 >> 6: ee_i_psubub(ee); return;\n                        case 0x00000680 >> 6: ee_i_pextub(ee); return;\n                        case 0x000006C0 >> 6: ee_i_qfsrv(ee); return;\n                    }\n                } break;\n                case 0x00000029: {\n                    switch ((ee->opcode & 0x000007C0) >> 6) {\n                        case 0x00000000 >> 6: ee_i_pmadduw(ee); return;\n                        case 0x000000C0 >> 6: ee_i_psravw(ee); return;\n                        case 0x00000200 >> 6: ee_i_pmthi(ee); return;\n                        case 0x00000240 >> 6: ee_i_pmtlo(ee); return;\n                        case 0x00000280 >> 6: ee_i_pinteh(ee); return;\n                        case 0x00000300 >> 6: ee_i_pmultuw(ee); return;\n                        case 0x00000340 >> 6: ee_i_pdivuw(ee); return;\n                        case 0x00000380 >> 6: ee_i_pcpyud(ee); return;\n                        case 0x00000480 >> 6: ee_i_por(ee); return;\n                        case 0x000004C0 >> 6: ee_i_pnor(ee); return;\n                        case 0x00000680 >> 6: ee_i_pexch(ee); return;\n                        case 0x000006C0 >> 6: ee_i_pcpyh(ee); return;\n                        case 0x00000780 >> 6: ee_i_pexcw(ee); return;\n                    }\n                } break;\n                case 0x00000030: {\n                    switch ((ee->opcode & 0x000007C0) >> 6) {\n                        case 0x00000000 >> 6: ee_i_pmfhllw(ee); return;\n                        case 0x00000040 >> 6: ee_i_pmfhluw(ee); return;\n                        case 0x00000080 >> 6: ee_i_pmfhlslw(ee); return;\n                        case 0x000000c0 >> 6: ee_i_pmfhllh(ee); return;\n                        case 0x00000100 >> 6: ee_i_pmfhlsh(ee); return;\n                    }\n                } break;\n                case 0x00000031: ee_i_pmthl(ee); return;\n                case 0x00000034: ee_i_psllh(ee); return;\n                case 0x00000036: ee_i_psrlh(ee); return;\n                case 0x00000037: ee_i_psrah(ee); return;\n                case 0x0000003C: ee_i_psllw(ee); return;\n                case 0x0000003E: ee_i_psrlw(ee); return;\n                case 0x0000003F: ee_i_psraw(ee); return;\n            }\n        } break;\n        case 0x78000000 >> 26: ee_i_lq(ee); return;\n        case 0x7C000000 >> 26: ee_i_sq(ee); return;\n        case 0x80000000 >> 26: ee_i_lb(ee); return;\n        case 0x84000000 >> 26: ee_i_lh(ee); return;\n        case 0x88000000 >> 26: ee_i_lwl(ee); return;\n        case 0x8C000000 >> 26: ee_i_lw(ee); return;\n        case 0x90000000 >> 26: ee_i_lbu(ee); return;\n        case 0x94000000 >> 26: ee_i_lhu(ee); return;\n        case 0x98000000 >> 26: ee_i_lwr(ee); return;\n        case 0x9C000000 >> 26: ee_i_lwu(ee); return;\n        case 0xA0000000 >> 26: ee_i_sb(ee); return;\n        case 0xA4000000 >> 26: ee_i_sh(ee); return;\n        case 0xA8000000 >> 26: ee_i_swl(ee); return;\n        case 0xAC000000 >> 26: ee_i_sw(ee); return;\n        case 0xB0000000 >> 26: ee_i_sdl(ee); return;\n        case 0xB4000000 >> 26: ee_i_sdr(ee); return;\n        case 0xB8000000 >> 26: ee_i_swr(ee); return;\n        case 0xBC000000 >> 26: ee_i_cache(ee); return;\n        case 0xC4000000 >> 26: ee_i_lwc1(ee); return;\n        case 0xCC000000 >> 26: ee_i_pref(ee); return;\n        case 0xD8000000 >> 26: ee_i_lqc2(ee); return;\n        case 0xDC000000 >> 26: ee_i_ld(ee); return;\n        case 0xE4000000 >> 26: ee_i_swc1(ee); return;\n        case 0xF8000000 >> 26: ee_i_sqc2(ee); return;\n        case 0xFC000000 >> 26: ee_i_sd(ee); return;\n    }\n\n    printf(\"ee: Invalid instruction %08x @ pc=%08x (cyc=%ld)\\n\", ee->opcode, ee->prev_pc, ee->total_cycles);\n\n    exit(1);\n}\n\nint loop = 0;\n\nvoid ee_cycle(struct ee_state* ee) {\n    ee->delay_slot = ee->branch;\n    ee->branch = 0;\n\n    // Would check for interrupts here, but we do this outside of the core\n    // to reduce overhead\n    ee_check_irq(ee);\n\n    // if (ee->pc == 0x160700) {\n    //     p = 5000;\n    //     file = fopen(\"eegs.dump\", \"w\");\n    // }\n\n    // if (ee->pc == 0x1c14bc) {\n    //     p = 100;\n    //     printf(\"got here\\n\");\n    // }\n\n    // if (ee->pc == 0x1c1200) {\n    //     file = fopen(\"eegs.dump\", \"wb\");\n\n    //     p = 100;\n    // }\n    // if (ee->pc == 0x17e240) {\n    //     file = fopen(\"eegs.dump\", \"wb\");\n\n    //     p = 5000;\n    // }\n\n    // if (ee->pc == 0x17e2e4) {\n    //     if (ee->r[17].ul32 == 0x10000) {\n    //         file = fopen(\"eegs.dump\", \"w\");\n\n    //         p = 5000;\n    //     }\n    //     /// printf(\"s1=%08x s4=%08x\\n\", ee->r[17].ul32, ee->r[20].ul32);\n    // }\n\n    // if (ee->pc == 0x1000d8) {\n    //     p = 200000;\n\n    //     file = fopen(\"eegs.dump\", \"wb\");\n    // }\n\n    // // //if (ee->pc == 0x9fc411d8) {\n    // if (ee->pc == 0x9fc41430) {\n    //     printf(\"ee: out\\n\");\n\n    //     fclose(file);\n\n    //     exit(1);\n\n    //     // p = 510000;\n    // }\n\n    // vxprintf\n    // if (ee->pc == 0x00106a20) {\n    //     if (loop == 1) {\n    //         printf(\"logging\\n\");\n    //         file = fopen(\"eegs.dump\", \"w\");\n\n    //         p = 5000;\n    //     } else {\n    //         ++loop;\n    //     }\n    // }\n\n    // loop\n    // if (ee->pc == 0x0010907c) {\n    //     if (p) {\n    //         if (file) fclose(file);\n\n    //         exit(1);\n    //     }\n    // }\n\n    ee->prev_pc = ee->pc;\n    ee->opcode = bus_read32(ee, ee->pc);\n\n    // if (p) {\n    //     // fwrite(&ee->pc, 4, 1, file);\n    //     // fwrite(&ee->opcode, 4, 1, file);\n    //     fprintf(file, \"%08x %08x \", ee->pc, ee->opcode);\n\n    //     for (int i = 1; i < 32; i++) {\n    //         // fwrite(&ee->r[i].ul32, 4, 1, file);\n    //         fprintf(file, \"%08x \", ee->r[i].ul32);\n    //     }\n\n    //     fputc('\\n', file);\n    //     --p;\n\n    //     if (!p) {\n    //         if (file) fclose(file);\n\n    //         exit(1);\n    //     }\n    // }\n\n    // if (ee->total_cycles >= 250576836) {\n    //     ee_print_disassembly(ee);\n    // }\n\n    // if (p) {\n    //     ee_print_disassembly(ee);\n\n    //     --p;\n\n    //     if (!p) {\n    //         exit(1);\n    //     }\n    // }\n\n    ee->pc = ee->next_pc;\n    ee->next_pc += 4;\n\n    ee_execute(ee);\n\n    ++ee->total_cycles;\n    ++ee->count;\n\n    // if (ee->count == ee->compare)\n    //     ee->cause |= EE_CAUSE_IP7;\n\n    ee->r[0].u64[0] = 0;\n    ee->r[0].u64[1] = 0;\n}\n\nvoid ee_reset(struct ee_state* ee) {\n    for (int i = 0; i < 32; i++)\n        ee->r[i] = (uint128_t){ .u64[0] = 0, .u64[1] = 0 };\n\n    for (int i = 0; i < 32; i++)\n        ee->f[i].u32 = 0;\n\n    for (int i = 0; i < 32; i++)\n        ee->cop0_r[i] = 0;\n\n    ee->a.u32 = 0;\n\n    ee->hi = (uint128_t){ .u64[0] = 0, .u64[1] = 0 };\n    ee->lo = (uint128_t){ .u64[0] = 0, .u64[1] = 0 };\n    ee->pc = 0xbfc00000;\n    ee->next_pc = ee->pc + 4;\n    ee->opcode = 0;\n    ee->sa = 0;\n    ee->branch = 0;\n    ee->branch_taken = 0;\n    ee->delay_slot = 0;\n    ee->prid = 0x2e20;\n    ee->pc = EE_VEC_RESET;\n    ee->next_pc = ee->pc + 4;\n\n    fesetround(FE_TOWARDZERO);\n\n    ee->fcr = 0x01000001;\n}\n\nvoid ee_destroy(struct ee_state* ee) {\n    ps2_ram_destroy(ee->scratchpad);\n\n    free(ee);\n}\n\nvoid ee_run_block(struct ee_state* ee, int cycles) {\n    ee->delay_slot = ee->branch;\n\n    ee_check_irq(ee);\n\n    for (int i = 0; i < cycles; i++) {\n        ee->opcode = bus_read32(ee, ee->pc);\n        ee->branch = 0;\n\n        ee->pc = ee->next_pc;\n        ee->next_pc += 4;\n\n        ee_execute(ee);\n\n        ++ee->count;\n\n        ee->r[0].u64[0] = 0;\n        ee->r[0].u64[1] = 0;\n    }\n}"
  },
  {
    "path": "src/ee/ee_uncached.h",
    "content": "#ifndef EE_UNCACHED_H\n#define EE_UNCACHED_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"shared/ram.h\"\n\n#include \"u128.h\"\n\n#include \"vu.h\"\n\nstruct ee_bus_s {\n    void* udata;\n    uint64_t (*read8)(void* udata, uint32_t addr);\n    uint64_t (*read16)(void* udata, uint32_t addr);\n    uint64_t (*read32)(void* udata, uint32_t addr);\n    uint64_t (*read64)(void* udata, uint32_t addr);\n    uint128_t (*read128)(void* udata, uint32_t addr);\n    void (*write8)(void* udata, uint32_t addr, uint64_t data);\n    void (*write16)(void* udata, uint32_t addr, uint64_t data);\n    void (*write32)(void* udata, uint32_t addr, uint64_t data);\n    void (*write64)(void* udata, uint32_t addr, uint64_t data);\n    void (*write128)(void* udata, uint32_t addr, uint128_t data);\n};\n\n#define EE_SR_CU  0xf0000000\n#define EE_SR_DEV 0x08000000\n#define EE_SR_BEV 0x04000000\n#define EE_SR_CH  0x00040000\n#define EE_SR_EDI 0x00020000\n#define EE_SR_EIE 0x00010000\n#define EE_SR_IM7 0x00008000\n#define EE_SR_BEM 0x00001000\n#define EE_SR_IM3 0x00000800\n#define EE_SR_IM2 0x00000400\n#define EE_SR_KSU 0x00000018\n#define EE_SR_ERL 0x00000004\n#define EE_SR_EXL 0x00000002\n#define EE_SR_IE  0x00000001\n\n#define EE_CAUSE_BD   0x80000000\n#define EE_CAUSE_BD2  0x40000000\n#define EE_CAUSE_CE   0x30000000\n#define EE_CAUSE_EXC2 0x00070000\n#define EE_CAUSE_IP7  0x00008000\n#define EE_CAUSE_IP3  0x00000800\n#define EE_CAUSE_IP2  0x00000400\n#define EE_CAUSE_EXC  0x0000007c\n\n#define CAUSE_EXC1_INT  (0 << 2)\n#define CAUSE_EXC1_MOD  (1 << 2)\n#define CAUSE_EXC1_TLBL (2 << 2)\n#define CAUSE_EXC1_TLBS (3 << 2)\n#define CAUSE_EXC1_ADEL (4 << 2)\n#define CAUSE_EXC1_ADES (5 << 2)\n#define CAUSE_EXC1_IBE  (6 << 2)\n#define CAUSE_EXC1_DBE  (7 << 2)\n#define CAUSE_EXC1_SYS  (8 << 2)\n#define CAUSE_EXC1_BP   (9 << 2)\n#define CAUSE_EXC1_RI   (10 << 2)\n#define CAUSE_EXC1_CPU  (11 << 2)\n#define CAUSE_EXC1_OV   (12 << 2)\n#define CAUSE_EXC1_TR   (13 << 2)\n\n#define CAUSE_EXC2_RES   (0 << 16)\n#define CAUSE_EXC2_NMI   (1 << 16)\n#define CAUSE_EXC2_PERFC (2 << 16)\n#define CAUSE_EXC2_DBG   (3 << 16)\n\n#define EE_VEC_RESET   0xbfc00000\n#define EE_VEC_TLBR    0x00000000\n#define EE_VEC_COUNTER 0x00000080\n#define EE_VEC_DEBUG   0x00000100\n#define EE_VEC_COMMON  0x00000180\n#define EE_VEC_IRQ     0x00000200\n\n#define FPU_FLG_C 0x00800000\n#define FPU_FLG_I 0x00020000\n#define FPU_FLG_D 0x00010000\n#define FPU_FLG_O 0x00008000\n#define FPU_FLG_U 0x00004000\n#define FPU_FLG_SI 0x00000040\n#define FPU_FLG_SD 0x00000020\n#define FPU_FLG_SO 0x00000010\n#define FPU_FLG_SU 0x00000008\n\n/*\n    1       V0 - Even page valid. When not set, the memory referenced in this entry is not mapped.\n    2       D0 - Even page dirty. When not set, writes cause an exception.\n    3-5     C0 - Even page cache mode.\n            2=Uncached\n            3=Cached\n            7=Uncached accelerated\n    6-25    PFN0 - Even page frame number.\n    33      V1 - Odd page valid.\n    34      D1 - Odd page dirty.\n    35      C1 - Odd page cache mode.\n    38-57   PFN1 - Odd page frame number.\n    63      S - Scratchpad. When set, the virtual mapping goes to scratchpad instead of main memory.\n    64-71   ASID - Address Space ID.\n    76      G - Global. When set, ASID is ignored.\n    77-95   VPN2 - Virtual page number / 2.\n            Even pages have a VPN of (VPN2 * 2) and odd pages have a VPN of (VPN2 * 2) + 1\n    109-120 MASK - Size of an even/odd page.\n*/\n\nstruct ee_vtlb_entry {\n    int v0;\n    int d0;\n    int c0;\n    int pfn0;\n    int v1;\n    int d1;\n    int c1;\n    int pfn1;\n    int s;\n    int asid;\n    int g;\n    int vpn2;\n    int mask;\n};\n\nunion ee_fpu_reg {\n    float f;\n    uint32_t u32;\n    int32_t s32;\n};\n\n#ifdef _EE_USE_INTRINSICS\n#ifdef _MSC_VER\n#define EE_ALIGNED16 __declspec(align(16))\n#else\n#define EE_ALIGNED16 __attribute__((aligned(16)))\n#endif\n#else\n#define EE_ALIGNED16\n#endif\n\nstruct ee_state {\n    struct ee_bus_s bus;\n\n    uint128_t r[32] EE_ALIGNED16;\n    uint128_t hi EE_ALIGNED16;\n    uint128_t lo EE_ALIGNED16;\n\n    uint64_t total_cycles;\n\n    uint32_t prev_pc;\n    uint32_t pc;\n    uint32_t next_pc;\n    uint32_t opcode;\n    uint64_t sa;\n    int branch, branch_taken, delay_slot;\n\n    struct ps2_ram* scratchpad;\n\n    int cpcond0;\n\n    union {\n        uint32_t cop0_r[32];\n    \n        struct {\n            uint32_t index;\n            uint32_t random;\n            uint32_t entrylo0;\n            uint32_t entrylo1;\n            uint32_t context;\n            uint32_t pagemask;\n            uint32_t wired;\n            uint32_t unused7;\n            uint32_t badvaddr;\n            uint32_t count;\n            uint32_t entryhi;\n            uint32_t compare;\n            uint32_t status;\n            uint32_t cause;\n            uint32_t epc;\n            uint32_t prid;\n            uint32_t config;\n            uint32_t unused16;\n            uint32_t unused17;\n            uint32_t unused18;\n            uint32_t unused19;\n            uint32_t unused20;\n            uint32_t unused21;\n            uint32_t badpaddr;\n            uint32_t debug;\n            uint32_t perf;\n            uint32_t unused25;\n            uint32_t unused26;\n            uint32_t taglo;\n            uint32_t taghi;\n            uint32_t errorepc;\n            uint32_t unused30;\n            uint32_t unused31;\n        };\n    };\n\n    union ee_fpu_reg f[32];\n    union ee_fpu_reg a;\n\n    uint32_t fcr;\n\n    struct vu_state* vu0;\n    struct vu_state* vu1;\n\n    struct ee_vtlb_entry vtlb[48];\n};\n\nstruct ee_state* ee_create(void);\nvoid ee_init(struct ee_state* ee, struct vu_state* vu0, struct vu_state* vu1, struct ee_bus_s bus);\nvoid ee_cycle(struct ee_state* ee);\nvoid ee_reset(struct ee_state* ee);\nvoid ee_destroy(struct ee_state* ee);\nvoid ee_set_int0(struct ee_state* ee, int v);\nvoid ee_set_int1(struct ee_state* ee, int v);\nvoid ee_set_cpcond0(struct ee_state* ee, int v);\n\n#undef EE_ALIGNED16\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/gif.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"gif.h\"\n\n// Burnout games need the FQC field on GIF_STAT to change on\n// GIF DMA transfers, otherwise they'll hang on the initial\n// loading screen.\n\n// #define printf(fmt, ...)(0)\n\nstatic inline const char* gif_get_reg_name(uint8_t r) {\n    switch (r) {\n        case 0x00: return \"PRIM\";\n        case 0x01: return \"RGBAQ\";\n        case 0x02: return \"ST\";\n        case 0x03: return \"UV\";\n        case 0x04: return \"XYZF2\";\n        case 0x05: return \"XYZ2\";\n        case 0x06: return \"TEX0_1\";\n        case 0x07: return \"TEX0_2\";\n        case 0x08: return \"CLAMP_1\";\n        case 0x09: return \"CLAMP_2\";\n        case 0x0A: return \"FOG\";\n        case 0x0C: return \"XYZF3\";\n        case 0x0D: return \"XYZ3\";\n        case 0x14: return \"TEX1_1\";\n        case 0x15: return \"TEX1_2\";\n        case 0x16: return \"TEX2_1\";\n        case 0x17: return \"TEX2_2\";\n        case 0x18: return \"XYOFFSET_1\";\n        case 0x19: return \"XYOFFSET_2\";\n        case 0x1A: return \"PRMODECONT\";\n        case 0x1B: return \"PRMODE\";\n        case 0x1C: return \"TEXCLUT\";\n        case 0x22: return \"SCANMSK\";\n        case 0x34: return \"MIPTBP1_1\";\n        case 0x35: return \"MIPTBP1_2\";\n        case 0x36: return \"MIPTBP2_1\";\n        case 0x37: return \"MIPTBP2_2\";\n        case 0x3B: return \"TEXA\";\n        case 0x3D: return \"FOGCOL\";\n        case 0x3F: return \"TEXFLUSH\";\n        case 0x40: return \"SCISSOR_1\";\n        case 0x41: return \"SCISSOR_2\";\n        case 0x42: return \"ALPHA_1\";\n        case 0x43: return \"ALPHA_2\";\n        case 0x44: return \"DIMX\";\n        case 0x45: return \"DTHE\";\n        case 0x46: return \"COLCLAMP\";\n        case 0x47: return \"TEST_1\";\n        case 0x48: return \"TEST_2\";\n        case 0x49: return \"PABE\";\n        case 0x4A: return \"FBA_1\";\n        case 0x4B: return \"FBA_2\";\n        case 0x4C: return \"FRAME_1\";\n        case 0x4D: return \"FRAME_2\";\n        case 0x4E: return \"ZBUF_1\";\n        case 0x4F: return \"ZBUF_2\";\n        case 0x50: return \"BITBLTBUF\";\n        case 0x51: return \"TRXPOS\";\n        case 0x52: return \"TRXREG\";\n        case 0x53: return \"TRXDIR\";\n        case 0x54: return \"HWREG\";\n        case 0x60: return \"SIGNAL\";\n        case 0x61: return \"FINISH\";\n        case 0x62: return \"LABEL\";\n    }\n\n    return \"<unknown>\";\n}\n\nstruct ps2_gif* ps2_gif_create(void) {\n    return malloc(sizeof(struct ps2_gif));\n}\n\nvoid ps2_gif_init(struct ps2_gif* gif, struct vu_state* vu1, struct ps2_gs* gs) {\n    memset(gif, 0, sizeof(struct ps2_gif));\n\n    gif->gs = gs;\n    gif->vu1 = vu1;\n\n    // A queue for each PATH\n    for (int i = 0; i < 3; i++) {\n        gif->queue[i] = queue_create();\n\n        queue_init(gif->queue[i]);\n    }\n}\n\nvoid ps2_gif_reset(struct ps2_gif* gif) {\n    gif->ctrl = 0;\n    gif->mode = 0;\n    gif->stat = 0;\n    gif->tag0 = 0;\n    gif->tag1 = 0;\n    gif->tag2 = 0;\n    gif->tag3 = 0;\n    gif->cnt = 0;\n    gif->p3cnt = 0;\n    gif->p3tag = 0;\n    gif->state = 0;\n    gif->q = 0;\n\n    memset(&gif->tag, 0, sizeof(struct gif_tag));\n\n    for (int i = 0; i < 3; i++)\n        queue_clear(gif->queue[i]);\n}\n\nvoid ps2_gif_destroy(struct ps2_gif* gif) {\n    for (int i = 0; i < 3; i++)\n        queue_destroy(gif->queue[i]);\n\n    free(gif);\n}\n\nuint64_t ps2_gif_read32(struct ps2_gif* gif, uint32_t addr) {\n    switch (addr) {\n        case 0x10003020: {\n            // Clear FQC when reading GIF_STAT\n            uint32_t v = gif->stat;\n\n            gif->stat &= ~0x1f000000;\n\n            return v;\n        } break;\n        case 0x10003040: return gif->tag0;\n        case 0x10003050: return gif->tag1;\n        case 0x10003060: return gif->tag2;\n        case 0x10003070: return gif->tag3;\n        case 0x10003080: return gif->cnt;\n        case 0x10003090: return gif->p3cnt;\n        case 0x100030A0: return gif->p3tag;\n    }\n\n    return 0;\n}\n\nvoid ps2_gif_write32(struct ps2_gif* gif, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        case 0x10003000: {\n            if (data & 1) {\n                ps2_gif_reset(gif);\n            }\n        } return;\n        case 0x10003010: {\n            gif->mode = data;\n        } return;\n    }\n}\n\n// void gif_write_rgbaq(struct ps2_gif* gif, uint128_t data) {\n//     uint64_t r = data.u64[0] & 0xff;\n//     uint64_t g = (data.u64[0] >> 32) & 0xff;\n//     uint64_t b = data.u64[1] & 0xff;\n//     uint64_t a = (data.u64[1] >> 32) & 0xff;\n//     uint64_t v = r | (g << 8) | (b << 16) | (a << 24) | (gif->q << 32);\n\n//     ps2_gs_write_internal(gif->gs, GS_RGBAQ, v);\n// }\n\n// void gif_write_stq(struct ps2_gif* gif, uint128_t data) {\n//     gif->q = data.u64[1] & 0xffffffff;\n\n//     ps2_gs_write_internal(gif->gs, GS_ST, data.u64[0]);\n// }\n\n// void gif_write_uv(struct ps2_gif* gif, uint128_t data) {\n//     ps2_gs_write_internal(gif->gs, GS_UV, (data.u64[0] & 0x3fff) | (data.u64[0] >> 16));\n// }\n\n// void gif_write_xyzf23(struct ps2_gif* gif, uint128_t data) {\n//     uint64_t x = data.u64[0] & 0xffff;\n//     uint64_t y = (data.u64[0] >> 32) & 0xffff;\n//     uint64_t z = (data.u64[1] >> 4) & 0xffffff;\n//     uint64_t f = (data.u64[1] >> 36) & 0xff;\n//     uint64_t v = x | (y << 16) | (z << 32) | (f << 56);\n//     uint64_t adc = data.u64[1] & 0x800000000000ul;\n\n//     ps2_gs_write_internal(gif->gs, adc ? GS_XYZF3 : GS_XYZF2, v);\n// }\n\n// void gif_write_xyz23(struct ps2_gif* gif, uint128_t data) {\n//     uint64_t x = data.u64[0] & 0xffff;\n//     uint64_t y = (data.u64[0] >> 32) & 0xffff;\n//     uint64_t z = data.u64[1] & 0xffffffff;\n//     uint64_t v = x | (y << 16) | (z << 32);\n//     uint64_t adc = data.u64[1] & 0x800000000000ul;\n\n//     ps2_gs_write_internal(gif->gs, adc ? GS_XYZ3 : GS_XYZ2, v);\n// }\n\n// void gif_write_fog(struct ps2_gif* gif, uint128_t data) {\n//     ps2_gs_write_internal(gif->gs, GS_FOG, data.u64[1] << 20);\n// }\n\nvoid gif_handle_tag(struct ps2_gif* gif, uint128_t data) {\n    // 1.0f\n    gif->q = 0x3f800000;\n\n    gif->tag.nloop = data.u64[0] & 0x7fff;\n    gif->tag.prim = (data.u64[0] >> 47) & 0x3ff;\n    gif->tag.eop = !!(data.u64[0] & 0x8000);\n    gif->tag.pre = !!(data.u64[0] & 0x400000000000ull);\n    gif->tag.fmt = (data.u64[0] >> 58) & 3;\n    gif->tag.nregs = (data.u64[0] >> 60) & 0xf;\n    gif->tag.reg = data.u64[1];\n    gif->tag.index = 0;\n\n    if (gif->tag.nregs == 0)\n        gif->tag.nregs = 16;\n\n    switch (gif->tag.fmt) {\n        case 0: {\n            gif->tag.remaining = gif->tag.nregs * gif->tag.nloop;\n            gif->tag.qwc = gif->tag.nloop * gif->tag.nregs;\n        } break;\n        case 1: {\n            gif->tag.remaining = gif->tag.nregs * gif->tag.nloop;\n            gif->tag.qwc = (gif->tag.nloop * gif->tag.nregs + 1) / 2;\n        } break;\n        case 2:\n        case 3: {\n            gif->tag.remaining = gif->tag.nloop;\n            gif->tag.qwc = gif->tag.nloop;\n        } break;\n    }\n\n    // fprintf(stdout, \"giftag: nloop=%04lx eop=%d prim=%04x (pre=%d) fmt=%d nregs=%d reg=%08x%08x size=%d\\n\",\n    //     gif->tag.nloop, gif->tag.eop, gif->tag.prim, gif->tag.pre, gif->tag.fmt, gif->tag.nregs, gif->tag.reg >> 32, gif->tag.reg & 0xffffffff, gif->tag.qwc\n    // );\n\n    // if (gif->tag.pre) {\n    //     ps2_gs_write_internal(gif->gs, GS_PRIM, gif->tag.prim);\n    // }\n\n    if (gif->tag.remaining) {\n        gif->state = GIF_STATE_PROCESSING;\n    }\n}\n\n// void gif_handle_packed(struct ps2_gif* gif, uint128_t data) {\n//     int index = (gif->tag.index++) % gif->tag.nregs;\n//     int r = (gif->tag.reg >> (index * 4)) & 0xf;\n\n//     switch (r) {\n//         case 0x00: /* printf(\"gif: PRIM <- %016lx\\n\", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_PRIM, data.u64[0] & 0x3ff); break;\n//         case 0x01: /* printf(\"gif: RGBAQ <- %016lx\\n\", data.u64[0]); */ gif_write_rgbaq(gif, data); break;\n//         case 0x02: /* printf(\"gif: STQ <- %016lx\\n\", data.u64[0]); */ gif_write_stq(gif, data); break;\n//         case 0x03: /* printf(\"gif: UV <- %016lx\\n\", data.u64[0]); */ gif_write_uv(gif, data); break;\n//         case 0x04: /* printf(\"gif: XYZF23 <- %08x%08x %08x%08x\\n\", data.u32[3], data.u32[2], data.u32[1], data.u32[0]); */ gif_write_xyzf23(gif, data); break;\n//         case 0x05: /* printf(\"gif: XYZ23 <- %016lx\\n\", data.u64[0]); */ gif_write_xyz23(gif, data); break;\n//         case 0x06: /* printf(\"gif: TEX0_1 <- %016lx\\n\", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_TEX0_1, data.u64[0]); break;\n//         case 0x07: /* printf(\"gif: TEX0_2 <- %016lx\\n\", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_TEX0_2, data.u64[0]); break;\n//         case 0x08: /* printf(\"gif: CLAMP_1 <- %016lx\\n\", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_CLAMP_1, data.u64[0]); break;\n//         case 0x09: /* printf(\"gif: CLAMP_2 <- %016lx\\n\", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_CLAMP_2, data.u64[0]); break;\n//         case 0x0a: /* printf(\"gif: FOG <- %016lx\\n\", data.u64[0]); */ gif_write_fog(gif, data); break;\n//         case 0x0c: /* printf(\"gif: XYZF3 <- %016lx\\n\", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_XYZF3, data.u64[0]); break;\n//         case 0x0d: /* printf(\"gif: XYZ3 <- %016lx\\n\", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_XYZ3, data.u64[0]); break;\n\n//         // A+D\n//         case 0x0e: {\n//             // printf(\"gif: write %s (A+D)\\n\", gif_get_reg_name(data.u64[1]));\n//             ps2_gs_write_internal(gif->gs, data.u64[1], data.u64[0]); \n//         } break;\n\n//         // NOP\n//         case 0x0f: break;\n\n//         default: /* printf(\"gif: PACKED format for reg %d unimplemented\\n\", r); exit(1); */ break;\n//     }\n\n//     gif->tag.qwc--;\n\n//     if (gif->tag.qwc == 0) {\n//         gif->state = GIF_STATE_RECV_TAG;\n\n//         return;\n//     }\n// }\n\n// void gif_handle_reglist(struct ps2_gif* gif, uint128_t data) {\n//     for (int i = 0; i < 2; i++) {\n//         int index = (gif->tag.index++) % gif->tag.nregs;\n//         int r = (gif->tag.reg >> (index * 4)) & 0xf;\n\n//         switch (r) {\n//             case 0x00: ps2_gs_write_internal(gif->gs, GS_PRIM, data.u64[i]); break;\n//             case 0x01: ps2_gs_write_internal(gif->gs, GS_RGBAQ, data.u64[i]); break;\n//             case 0x02: ps2_gs_write_internal(gif->gs, GS_ST, data.u64[i]); break;\n//             case 0x03: ps2_gs_write_internal(gif->gs, GS_UV, data.u64[i]); break;\n//             case 0x04: ps2_gs_write_internal(gif->gs, GS_XYZF2, data.u64[i]); break;\n//             case 0x05: ps2_gs_write_internal(gif->gs, GS_XYZ2, data.u64[i]); break;\n//             case 0x06: ps2_gs_write_internal(gif->gs, GS_TEX0_1, data.u64[i]); break;\n//             case 0x07: ps2_gs_write_internal(gif->gs, GS_TEX0_2, data.u64[i]); break;\n//             case 0x08: ps2_gs_write_internal(gif->gs, GS_CLAMP_1, data.u64[i]); break;\n//             case 0x09: ps2_gs_write_internal(gif->gs, GS_CLAMP_2, data.u64[i]); break;\n//             case 0x0a: ps2_gs_write_internal(gif->gs, GS_FOG, data.u64[i]); break;\n//             case 0x0c: ps2_gs_write_internal(gif->gs, GS_XYZF3, data.u64[i]); break;\n//             case 0x0d: ps2_gs_write_internal(gif->gs, GS_XYZ3, data.u64[i]); break;\n\n//             // A+D\n//             // NOP\n//             case 0x0e:\n//             case 0x0f: break;\n\n//             // default: printf(\"gif: REGLIST format for reg %d unimplemented\\n\", r); break;\n//         }\n\n//         // Note: This handles odd NREGS*NLOOP case\n//         if (gif->tag.index == gif->tag.remaining)\n//             break;\n//     }\n\n//     gif->tag.qwc--;\n\n//     if (gif->tag.qwc == 0) {\n//         gif->state = GIF_STATE_RECV_TAG;\n\n//         return;\n//     }\n// }\n\n// void gif_handle_image(struct ps2_gif* gif, uint128_t data) {\n//     ps2_gs_write_internal(gif->gs, GS_HWREG, data.u64[0]);\n//     ps2_gs_write_internal(gif->gs, GS_HWREG, data.u64[1]);\n    \n//     gif->tag.qwc--;\n\n//     if (gif->tag.qwc == 0) {\n//         gif->state = GIF_STATE_RECV_TAG;\n//     }\n// }\n\nvoid ps2_gif_write128(struct ps2_gif* gif, uint32_t addr, uint128_t data) {\n    ps2_gif_fifo_write(gif, data, GIF_PATH3);\n}\n\nvoid ps2_gif_fifo_write(struct ps2_gif* gif, uint128_t data, int path) {\n    // Set FQC when getting GIF FIFO writes\n    gif->stat |= 0x1f000000;\n\n    if (gif->state == GIF_STATE_RECV_TAG) {\n        for (int i = 0; i < 4; i++)\n            queue_push(gif->queue[path], data.u32[i]);\n\n        gif_handle_tag(gif, data);\n\n        return;\n    }\n\n    if (gif->tag.qwc) {\n        struct queue_state* queue = gif->queue[path];\n\n        for (int i = 0; i < 4; i++)\n            queue_push(queue, data.u32[i]);\n\n        gif->tag.qwc--;\n\n        if (!gif->tag.qwc) {\n            gif->state = GIF_STATE_RECV_TAG;\n\n            if (gif->transfer)\n                gif->transfer(gif->udata, path, queue->buf, queue->size * sizeof(uint32_t));\n\n            queue_clear(queue);\n        }\n    }\n}\n\nvoid ps2_gif_set_backend(struct ps2_gif* gif, void* udata, void (*func)(void*, int, const void*, size_t)) {\n    gif->udata = udata;\n    gif->transfer = func; \n}\n\n#undef printf"
  },
  {
    "path": "src/ee/gif.h",
    "content": "#ifndef GIF_H\n#define GIF_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"gs/gs.h\"\n#include \"u128.h\"\n#include \"queue.h\"\n#include \"vu.h\"\n\n#define GIF_STATE_RECV_TAG 0\n#define GIF_STATE_PROCESSING 1\n\n#define GIF_PATH1 0\n#define GIF_PATH2 1\n#define GIF_PATH3 2\n\nstruct gif_tag {\n    uint64_t nloop;\n    uint32_t prim;\n    int eop;\n    int pre;\n    int fmt;\n    int nregs;\n    uint64_t reg;\n    uint64_t qwc;\n\n    int index;\n    int remaining;\n};\n\nstruct ps2_gif {\n    uint64_t ctrl;\n    uint64_t mode;\n    uint64_t stat;\n    uint64_t tag0;\n    uint64_t tag1;\n    uint64_t tag2;\n    uint64_t tag3;\n    uint64_t cnt;\n    uint64_t p3cnt;\n    uint64_t p3tag;\n\n    // Renderer state\n    void* udata;\n    void (*transfer)(void*, int, const void*, size_t);\n    struct queue_state* queue[3];\n\n    struct ps2_gs* gs;\n    struct vu_state* vu1;\n\n    int state;\n    struct gif_tag tag;\n\n    // From ST(Q) to RGBA(Q)\n    uint64_t q;\n};\n\nstruct ps2_gif* ps2_gif_create(void);\nvoid ps2_gif_init(struct ps2_gif* gif, struct vu_state* vu1, struct ps2_gs* gs);\nvoid ps2_gif_reset(struct ps2_gif* gif);\nvoid ps2_gif_destroy(struct ps2_gif* gif);\nuint64_t ps2_gif_read32(struct ps2_gif* gif, uint32_t addr);\nvoid ps2_gif_write32(struct ps2_gif* gif, uint32_t addr, uint64_t data);\nvoid ps2_gif_write128(struct ps2_gif* gif, uint32_t addr, uint128_t data);\nvoid ps2_gif_fifo_write(struct ps2_gif* gif, uint128_t data, int path);\nvoid ps2_gif_set_backend(struct ps2_gif* gif, void* udata, void (*func)(void*, int, const void*, size_t));\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/intc.c",
    "content": "#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"intc.h\"\n\nstatic inline void intc_check_irq(struct ps2_intc* intc) {\n    ee_set_int0(intc->ee, intc->stat & intc->mask);\n}\n\nstruct ps2_intc* ps2_intc_create(void) {\n    return malloc(sizeof(struct ps2_intc));\n}\n\nvoid ps2_intc_init(struct ps2_intc* intc, struct ee_state* ee, struct sched_state* sched) {\n    memset(intc, 0, sizeof(struct ps2_intc));\n\n    intc->ee = ee;\n    intc->sched = sched;\n}\n\nvoid ps2_intc_destroy(struct ps2_intc* intc) {\n    free(intc);\n}\n\nuint64_t ps2_intc_read32(struct ps2_intc* intc, uint32_t addr) {\n    switch (addr) {\n        case 0x1000f000: return intc->stat;\n        case 0x1000f010: return intc->mask;\n\n        default: printf(\"intc: Unhandled INTC read %08x\\n\", addr); exit(1);\n    }\n\n    return 0;\n}\n\nvoid ps2_intc_write8(struct ps2_intc* intc, uint32_t addr, uint64_t data) {\n    printf(\"intc: write8 %04lx\\n\", data); exit(1);\n\n    switch (addr) {\n        case 0x1000f000: intc->stat &= ~data; break;\n        case 0x1000f010: intc->mask ^= data; break;\n\n        default: printf(\"intc: Unhandled INTC write %08x %08lx\\n\", addr, data); exit(1);\n    }\n\n    intc_check_irq(intc);\n}\n\nvoid ps2_intc_write16(struct ps2_intc* intc, uint32_t addr, uint64_t data) {\n    printf(\"intc: write16 %04lx\\n\", data); exit(1);\n\n    switch (addr) {\n        case 0x1000f000: intc->stat &= ~data; break;\n        case 0x1000f010: intc->mask ^= data; break;\n\n        default: printf(\"intc: Unhandled INTC write %08x %08lx\\n\", addr, data); exit(1);\n    }\n\n    intc_check_irq(intc);\n}\n\nvoid intc_check_irq_event(void* udata, int overshoot) {\n    struct ps2_intc* intc = (struct ps2_intc*)udata;\n\n    intc_check_irq(intc);\n}\n\nvoid ps2_intc_write32(struct ps2_intc* intc, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        case 0x1000f000: intc->stat &= ~data; break;\n        case 0x1000f010: intc->mask ^= data; break;\n\n        default: printf(\"intc: Unhandled INTC write %08x %08lx\\n\", addr, data); exit(1);\n    }\n\n    struct sched_event event;\n\n    event.callback = intc_check_irq_event;\n    event.cycles = 16;\n    event.name = \"INTC IRQ check\";\n    event.udata = intc;\n\n    sched_schedule(intc->sched, event);\n}\n\nvoid ps2_intc_write64(struct ps2_intc* intc, uint32_t addr, uint64_t data) {\n    printf(\"intc: write64 %016lx\\n\", data); exit(1);\n\n    switch (addr) {\n        case 0x1000f000: intc->stat &= ~data; break;\n        case 0x1000f010: intc->mask ^= data; break;\n\n        default: printf(\"intc: Unhandled INTC write %08x %08lx\\n\", addr, data); exit(1);\n    }\n\n    intc_check_irq(intc);\n}\n\nvoid ps2_intc_irq(struct ps2_intc* intc, int dev) {\n    intc->stat |= 1 << dev;\n\n    static const char* dev_names[] = {\n        \"GS\",\n        \"SBUS\",\n        \"VBLANK_IN\",\n        \"VBLANK_OUT\",\n        \"VIF0\",\n        \"VIF1\",\n        \"VU0\",\n        \"VU1\",\n        \"IPU\",\n        \"TIMER0\",\n        \"TIMER1\",\n        \"TIMER2\",\n        \"TIMER3\",\n        \"SFIFO\",\n        \"VU0_WD\"\n    };\n\n    struct sched_event event;\n\n    event.callback = intc_check_irq_event;\n    event.cycles = 64;\n    event.name = \"INTC IRQ check\";\n    event.udata = intc;\n\n    // Notify EE that an interrupt has occurred\n    ee_reset_intc_reads(intc->ee);\n\n    sched_schedule(intc->sched, event);\n}"
  },
  {
    "path": "src/ee/intc.h",
    "content": "struct ps2_intc;\n\n#ifndef INTC_H\n#define INTC_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"u128.h\"\n#include \"ee.h\"\n#include \"scheduler.h\"\n\n#define EE_INTC_GS         0\n#define EE_INTC_SBUS       1\n#define EE_INTC_VBLANK_IN  2\n#define EE_INTC_VBLANK_OUT 3\n#define EE_INTC_VIF0       4\n#define EE_INTC_VIF1       5\n#define EE_INTC_VU0        6\n#define EE_INTC_VU1        7\n#define EE_INTC_IPU        8\n#define EE_INTC_TIMER0     9\n#define EE_INTC_TIMER1     10\n#define EE_INTC_TIMER2     11\n#define EE_INTC_TIMER3     12\n#define EE_INTC_SFIFO      13\n#define EE_INTC_VU0_WD     14\n\nstruct ps2_intc {\n    uint32_t stat;\n    uint32_t mask;\n\n    struct ee_state* ee;\n    struct sched_state* sched;\n};\n\nstruct ps2_intc* ps2_intc_create(void);\nvoid ps2_intc_init(struct ps2_intc* intc, struct ee_state* ee, struct sched_state* sched);\nvoid ps2_intc_destroy(struct ps2_intc* intc);\nuint64_t ps2_intc_read32(struct ps2_intc* intc, uint32_t addr);\nvoid ps2_intc_write8(struct ps2_intc* intc, uint32_t addr, uint64_t data);\nvoid ps2_intc_write16(struct ps2_intc* intc, uint32_t addr, uint64_t data);\nvoid ps2_intc_write32(struct ps2_intc* intc, uint32_t addr, uint64_t data);\nvoid ps2_intc_write64(struct ps2_intc* intc, uint32_t addr, uint64_t data);\nvoid ps2_intc_irq(struct ps2_intc* intc, int dev);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/syscall.h",
    "content": "static const char* ee_get_syscall(int n) {\n    switch (n) {\n        case 0x01: return \"void ResetEE(int reset_flag)\";\n        case 0x02: return \"void SetGsCrt(bool interlaced, int display_mode, bool frame)\";\n        case 0x04: return \"void Exit(int status)\";\n        case 0x05: return \"void _ExceptionEpilogue()\";\n        case 0x06: return \"void LoadExecPS2(const char* filename, int argc, char** argv)\";\n        case 0x07: return \"void ExecPS2(void* entry, void* gp, int argc, char** argv)\";\n        case 0x10: return \"int AddIntcHandler(int int_cause, int (*handler)(int), int next, void* arg, int flag)\";\n        case 0x11: return \"int RemoveIntcHandler(int int_cause, int handler_id)\";\n        case 0x12: return \"int AddDmacHandler(int dma_cause, int (*handler)(int), int next, void* arg, int flag)\";\n        case 0x13: return \"int RemoveDmacHandler(int dma_cause, int handler_id)\";\n        case 0x14: return \"bool _EnableIntc(int cause_bit)\";\n        case 0x15: return \"bool _DisableIntc(int cause_bit)\";\n        case 0x16: return \"bool _EnableDmac(int cause_bit)\";\n        case 0x17: return \"bool _DisableDmac(int cause_bit)\";\n        case 0x20: return \"int CreateThread(ThreadParam* t)\";\n        case 0x21: return \"void DeleteThread(int thread_id)\";\n        case 0x22: return \"void StartThread(int thread_id, void* arg)\";\n        case 0x23: return \"void ExitThread()\";\n        case 0x24: return \"void ExitDeleteThread()\";\n        case 0x25: return \"void TerminateThread(int thread_id)\";\n        case 0x26: return \"void iTerminateThread(int thread_id)\";\n        case 0x29: return \"int ChangeThreadPriority(int thread_id, int priority)\";\n        case 0x2A: return \"int iChangeThreadPriority(int thread_id, int priority)\";\n        case 0x2B: return \"void RotateThreadReadyQueue(int priority)\";\n        case 0x2C: return \"int _iRotateThreadReadyQueue(int priority)\";\n        case 0x2D: return \"void ReleaseWaitThread(int thread_id)\";\n        case 0x2E: return \"int iReleaseWaitThread(int thread_id)\";\n        case 0x2F: return \"int GetThreadId()\";\n        case 0x30: return \"int ReferThreadStatus(int thread_id, ThreadParam* status)\";\n        case 0x31: return \"int iReferThreadStatus(int thread_id, ThreadParam* status)\";\n        case 0x32: return \"void SleepThread()\";\n        case 0x33: return \"void WakeupThread(int thread_id)\";\n        case 0x34: return \"int iWakeupThread(int thread_id)\";\n        case 0x35: return \"int CancelWakeupThread(int thread_id)\";\n        case 0x36: return \"int iCancelWakeupThread(int thread_id)\";\n        case 0x37: return \"int SuspendThread(int thread_id)\";\n        case 0x38: return \"int iSuspendThread(int thread_id)\";\n        case 0x39: return \"void ResumeThread(int thread_id)\";\n        case 0x3A: return \"int iResumeThread(int thread_id)\";\n        case 0x3B: return \"void JoinThread()\";\n        case 0x3C: return \"void* InitMainThread(uint32 gp, void* stack, int stack_size, char* args, int root)\";\n        case 0x3D: return \"void* InitHeap(void* heap, int heap_size)\";\n        case 0x3E: return \"void* EndOfHeap()\";\n        case 0x40: return \"int CreateSema(SemaParam* s)\";\n        case 0x41: return \"int DeleteSema(int sema_id)\";\n        case 0x42: return \"int SignalSema(int sema_id)\";\n        case 0x43: return \"int iSignalSema(int sema_id)\";\n        case 0x44: return \"void WaitSema(int sema_id)\";\n        case 0x45: return \"int PollSema(int sema_id)\";\n        case 0x46: return \"int iPollSema(int sema_id)\";\n        case 0x64: return \"void FlushCache(int mode)\";\n        case 0x70: return \"uint64_t GsGetIMR()\";\n        case 0x71: return \"void GsPutIMR(uint64_t value)\";\n        case 0x73: return \"void SetVSyncFlag(int* vsync_occurred, u64* csr_stat_on_vsync)\";\n        case 0x74: return \"void SetSyscall(int index, int address)\";\n        case 0x76: return \"int SifDmaStat(unsigned int dma_id)\";\n        case 0x77: return \"unsigned int SifSetDma(SifDmaTransfer* trans, int len)\";\n        case 0x78: return \"void SifSetDChain()\";\n        case 0x7B: return \"void ExecOSD(int argc, char** argv)\";\n        case 0x7D: return \"void PSMode()\";\n        case 0x7E: return \"int MachineType()\";\n        case 0x7F: return \"int GetMemorySize()\";\n    }\n\n    return \"<unknown>\";\n}"
  },
  {
    "path": "src/ee/timers.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"timers.h\"\n\n#define EE_TIMER_SCHED_QUANTUM 64\n\nstatic void ee_timers_schedule_next_irq_event(struct ps2_ee_timers* timers);\n\nstruct ps2_ee_timers* ps2_ee_timers_create(void) {\n    return malloc(sizeof(struct ps2_ee_timers));\n}\n\nstatic inline void ee_timers_update_active_mask(struct ps2_ee_timers* timers, int t, int cue) {\n    uint8_t bit = 1u << t;\n\n    if (cue) {\n        timers->active_mask |= bit;\n    } else {\n        timers->active_mask &= ~bit;\n    }\n}\n\nvoid ps2_ee_timers_init(struct ps2_ee_timers* timers, struct ps2_intc* intc, struct sched_state* sched) {\n    memset(timers, 0, sizeof(struct ps2_ee_timers));\n\n    timers->intc = intc;\n    timers->sched = sched;\n    timers->current_cycle = 0;\n    timers->scheduler_advanced_cycles = 0;\n    timers->irq_event_pending = 0;\n\n    for (int i = 0; i < 4; i++) {\n        timers->timer[i].id = i;\n        timers->timer[i].last_sync_cycle = 0;\n    }\n}\n\nstatic inline void ee_timers_update_event(struct ee_timer* t) {\n    uint32_t counter = t->counter & 0xffff;\n    uint32_t compare = t->compare & 0xffff;\n\n    uint32_t cycles_until_compare;\n    uint32_t cycles_until_overflow = 0x10000 - counter;\n\n    if (counter < compare) {\n        cycles_until_compare = compare - counter;\n    } else {\n        cycles_until_compare = 0x10000 - counter + compare;\n    }\n\n    // Compare events are needed for IRQ and for ZRET.\n    if (!(t->cmpe || t->zret)) cycles_until_compare = 0x80000000;\n    if (!t->ovfe) cycles_until_overflow = 0x80000000;\n\n    t->cycles_until_check = cycles_until_compare < cycles_until_overflow ?\n        cycles_until_compare : cycles_until_overflow;\n\n    // printf(\"timer %d: cycles_until_check=%04x counter=%04x compare=%04x until_compare=%04x until_overflow=%04x\\n\",\n    //     t, t->cycles_until_check, t->counter, t->compare, cycles_until_compare, cycles_until_overflow\n    // );\n}\n\nstatic inline uint32_t ee_timers_cycles_until_check(struct ee_timer* t) {\n    if (!t->cue || !t->check_enabled)\n        return 0xffffffffu;\n\n    uint32_t period = t->delta_reload;\n\n    if (!period)\n        return 0xffffffffu;\n\n    uint64_t cycles = t->delta;\n\n    if (t->cycles_until_check > 1) {\n        cycles += (uint64_t)(t->cycles_until_check - 1) * period;\n    }\n\n    if (!cycles)\n        cycles = 1;\n\n    if (cycles > 0x7fffffffu)\n        cycles = 0x7fffffffu;\n\n    return (uint32_t)cycles;\n}\n\nstatic inline void ee_timers_advance_counter(struct ps2_ee_timers* timers, struct ee_timer* t, int i, uint32_t increments) {\n    if (!increments)\n        return;\n\n    if (!t->check_enabled) {\n        t->counter = (t->counter + increments) & 0xffff;\n        return;\n    }\n\n    while (increments) {\n        if (t->cycles_until_check > 1) {\n            uint32_t skip = t->cycles_until_check - 1;\n\n            if (skip > increments)\n                skip = increments;\n\n            t->counter = (t->counter + skip) & 0xffff;\n            t->cycles_until_check -= skip;\n            increments -= skip;\n\n            if (!increments)\n                break;\n        }\n\n        uint32_t counter = t->counter + 1;\n        uint32_t low_counter = counter & 0xffff;\n        int cmp = low_counter == (t->compare & 0xffff);\n        int ovf = counter == 0x10000;\n\n        if (!(cmp || ovf)) {\n            fprintf(stderr, \"timer %d: error counter=%04x compare=%04x cycles_until-check=%d\\n\", i, counter, t->compare, t->cycles_until_check);\n\n            exit(1);\n        }\n\n        if (cmp) {\n            if (t->cmpe)\n                t->mode |= 0x400;\n\n            if (t->zret) {\n                counter = 0;\n            }\n\n            if (t->cmpe) {\n                ps2_intc_irq(timers->intc, EE_INTC_TIMER0 + i);\n            }\n\n            t->counter = counter;\n            ee_timers_update_event(t);\n            counter = t->counter;\n        }\n\n        if (counter == 0x10000) {\n            if (t->ovfe)\n                t->mode |= 0x800;\n\n            if (t->ovfe) {\n                ps2_intc_irq(timers->intc, EE_INTC_TIMER0 + i);\n            }\n\n            t->counter = counter;\n            ee_timers_update_event(t);\n            counter = t->counter;\n        }\n\n        t->counter = counter & 0xffff;\n        increments--;\n    }\n}\n\nstatic inline void ee_timers_sync_timer(struct ps2_ee_timers* timers, struct ee_timer* t, int i) {\n    if (!t->cue) {\n        t->last_sync_cycle = timers->current_cycle;\n        return;\n    }\n\n    uint64_t cycles_since_sync = timers->current_cycle - t->last_sync_cycle;\n\n    if (!cycles_since_sync)\n        return;\n\n    t->last_sync_cycle = timers->current_cycle;\n\n    if (cycles_since_sync < t->delta) {\n        t->delta -= (uint32_t)cycles_since_sync;\n        return;\n    }\n\n    cycles_since_sync -= t->delta;\n\n    uint32_t increments = 1;\n    uint32_t period = t->delta_reload;\n\n    if (period) {\n        increments += (uint32_t)(cycles_since_sync / period);\n\n        uint32_t rem = (uint32_t)(cycles_since_sync % period);\n        t->delta = period - rem;\n\n        if (t->delta == 0)\n            t->delta = period;\n    } else {\n        t->delta = 0;\n    }\n\n    ee_timers_advance_counter(timers, t, i, increments);\n}\n\nstatic void ee_timers_irq_event_cb(void* udata, int overshoot) {\n    struct ps2_ee_timers* timers = (struct ps2_ee_timers*)udata;\n    uint64_t elapsed = EE_TIMER_SCHED_QUANTUM;\n\n    if (timers->sched && timers->sched->offset)\n        elapsed = timers->sched->offset;\n\n    if (overshoot < 0)\n        elapsed += (uint64_t)(-overshoot);\n\n    timers->irq_event_pending = 0;\n\n    timers->current_cycle += elapsed;\n    timers->scheduler_advanced_cycles += elapsed;\n\n    for (int i = 0; i < 4; i++) {\n        if (timers->timer[i].cue) {\n            ee_timers_sync_timer(timers, &timers->timer[i], i);\n        }\n    }\n\n    ee_timers_schedule_next_irq_event(timers);\n}\n\nstatic void ee_timers_schedule_next_irq_event(struct ps2_ee_timers* timers) {\n    if (!timers->sched)\n        return;\n\n    if (timers->irq_event_pending)\n        return;\n\n    uint32_t min_cycles = 0xffffffffu;\n\n    for (int i = 0; i < 4; i++) {\n        if (timers->timer[i].cue) {\n            ee_timers_sync_timer(timers, &timers->timer[i], i);\n\n            uint32_t wait = ee_timers_cycles_until_check(&timers->timer[i]);\n\n            if (wait < min_cycles)\n                min_cycles = wait;\n        }\n    }\n\n    if (min_cycles == 0xffffffffu)\n        return;\n\n    if (min_cycles > EE_TIMER_SCHED_QUANTUM)\n        min_cycles = EE_TIMER_SCHED_QUANTUM;\n\n    struct sched_event event;\n    event.name = \"EE Timer IRQ\";\n    event.udata = timers;\n    event.callback = ee_timers_irq_event_cb;\n    event.cycles = (long)min_cycles;\n\n    sched_schedule(timers->sched, event);\n    timers->irq_event_pending = 1;\n}\n\nvoid ee_timers_write_counter(struct ps2_ee_timers* timers, int t, uint32_t data) {\n    struct ee_timer* timer = &timers->timer[t];\n\n    // Sync to current cycle first\n    ee_timers_sync_timer(timers, timer, t);\n\n    // printf(\"timer %d: write counter=%04x data=%04x\\n\", t, timer->counter, data);\n\n    timer->counter = data;\n    timer->delta = timer->delta_reload;\n\n    if (timer->check_enabled) {\n        ee_timers_update_event(timer);\n    }\n\n    ee_timers_schedule_next_irq_event(timers);\n}\n\nvoid ee_timers_write_compare(struct ps2_ee_timers* timers, int t, uint32_t data) {\n    struct ee_timer* timer = &timers->timer[t];\n\n    // Sync to current cycle first\n    ee_timers_sync_timer(timers, timer, t);\n\n    // printf(\"timer %d: write counter=%04x data=%04x\\n\", t, timer->counter, data);\n\n    if (data < timer->counter) {\n        // printf(\"timer %d: compare %04x >= counter %04x\\n\", t, data, timer->counter);\n\n        // exit(1);\n    } else if (data == timer->counter) {\n        // printf(\"timer %d: compare %04x == counter %04x\\n\", t, data, timer->counter);\n\n        // exit(1);\n    }\n\n    // timer->cycles_until_check = data - timer->counter;\n    timer->compare = data;\n\n    if (timer->check_enabled) {\n        ee_timers_update_event(timer);\n    }\n\n    ee_timers_schedule_next_irq_event(timers);\n}\n\nvoid ps2_ee_timers_destroy(struct ps2_ee_timers* timers) {\n    free(timers);\n}\n\nstatic inline void ee_timers_write_mode(struct ps2_ee_timers* timers, int t, uint32_t data) {\n    struct ee_timer* timer = &timers->timer[t];\n\n    // Sync to current cycle first before changing mode\n    ee_timers_sync_timer(timers, timer, t);\n\n    timer->mode &= 0xc00;\n    timer->mode |= data & (~0xc00);\n    timer->mode &= ~(data & 0xc00);\n\n    timer->clks = data & 3;\n    timer->gate = (data >> 2) & 1;\n    timer->gats = (data >> 3) & 1;\n    timer->gatm = (data >> 4) & 3;\n    timer->zret = (data >> 6) & 1;\n    timer->cue = (data >> 7) & 1;\n    timer->cmpe = (data >> 8) & 1;\n    timer->ovfe = (data >> 9) & 1;\n\n    ee_timers_update_active_mask(timers, t, timer->cue);\n\n    if (!timer->cue) {\n        timer->check_enabled = 0;\n        return;\n    }\n\n    // Reset sync point when timer is enabled\n    timer->last_sync_cycle = timers->current_cycle;\n\n    if (timer->gate) {\n        printf(\"timers: Timer %d gate write %08x\\n\", t, data);\n\n        // exit(1);\n    }\n\n    // printf(\"timers: Timer %d mode write %08x mode=%08x counter=%04x compare=%04x clks=%d gate=%d gats=%d gatm=%d zret=%d cue=%d cmpe=%d ovfe=%d\\n\",\n    //     t, data,\n    //     timer->mode,\n    //     timer->counter,\n    //     timer->compare,\n    //     timer->clks, timer->gate, timer->gats, timer->gatm,\n    //     timer->zret, timer->cue, timer->cmpe, timer->ovfe\n    // );\n\n    switch (timer->clks) {\n        case 0: timer->delta = 1; break;\n        case 1: timer->delta = 16; break;\n        case 2: timer->delta = 256; break;\n        case 3: timer->delta = 9370; break;\n    }\n\n    timer->delta_reload = timer->delta;\n\n    if (timer->cmpe || timer->ovfe || timer->zret) {\n        timer->check_enabled = 1;\n\n        ee_timers_update_event(timer);\n    } else {\n        timer->check_enabled = 0;\n    }\n\n    ee_timers_schedule_next_irq_event(timers);\n}\n\nvoid ps2_ee_timers_tick(struct ps2_ee_timers* timers) {\n    ps2_ee_timers_tick_cycles(timers, 1);\n}\n\nvoid ps2_ee_timers_tick_cycles(struct ps2_ee_timers* timers, uint32_t cycles) {\n    // Lazy evaluation: just advance the global cycle counter\n    // No timer updates happen until reads/writes or event checks\n    if (timers->active_mask && cycles) {\n        uint64_t step = cycles;\n\n        if (timers->scheduler_advanced_cycles) {\n            if (timers->scheduler_advanced_cycles >= step) {\n                timers->scheduler_advanced_cycles -= step;\n                step = 0;\n            } else {\n                step -= timers->scheduler_advanced_cycles;\n                timers->scheduler_advanced_cycles = 0;\n            }\n        }\n\n        timers->current_cycle += step;\n    }\n}\n\nvoid ps2_ee_timers_write16(struct ps2_ee_timers* timers, uint32_t addr, uint64_t data) {\n    int t = (addr >> 11) & 3;\n\n    switch (addr & 0xff) {\n        case 0x00: ee_timers_write_counter(timers, t, data & 0xffff); return;\n        case 0x10: ee_timers_write_mode(timers, t, data & 0xffff); return;\n        case 0x20: ee_timers_write_compare(timers, t, data & 0xffff); return;\n        case 0x30: timers->timer[t].hold = data & 0xffff; return;\n    }\n\n    fprintf(stderr, \"ee: timer %d write %08x to %02x\\n\", t, data, addr & 0xff);\n\n    exit(1);\n}\n\nvoid ps2_ee_timers_write32(struct ps2_ee_timers* timers, uint32_t addr, uint64_t data) {\n    int t = (addr >> 11) & 3;\n\n    // printf(\"ee: timer %d write %08x to %02x\\n\", t, data, addr & 0xff);\n\n    switch (addr & 0xff) {\n        case 0x00: ee_timers_write_counter(timers, t, data & 0xffff); return;\n        case 0x10: ee_timers_write_mode(timers, t, data & 0xffff); return;\n        case 0x20: ee_timers_write_compare(timers, t, data & 0xffff); return;\n        case 0x30: timers->timer[t].hold = data & 0xffff; return;\n    }\n}\n\nuint64_t ps2_ee_timers_read16(struct ps2_ee_timers* timers, uint32_t addr) {\n    int t = (addr >> 11) & 3;\n\n    // printf(\"ee: timer %d read %08x\\n\", t, addr & 0xff);\n\n    // Sync timer to current cycle before reading\n    ee_timers_sync_timer(timers, &timers->timer[t], t);\n\n    switch (addr & 0xff) {\n        case 0x00: return timers->timer[t].counter & 0xffff;\n        case 0x10: return timers->timer[t].mode & 0xffff;\n        case 0x20: return timers->timer[t].compare & 0xffff;\n        case 0x30: return timers->timer[t].hold & 0xffff;\n    }\n\n    fprintf(stderr, \"ee: timers read16 %08x\\n\", addr);\n\n    exit(1);\n\n    return 0;\n}\n\nuint64_t ps2_ee_timers_read32(struct ps2_ee_timers* timers, uint32_t addr) {\n    int t = (addr >> 11) & 3;\n\n    // printf(\"ee: timer %d read %08x\\n\", t, addr & 0xff);\n\n    // Sync timer to current cycle before reading\n    ee_timers_sync_timer(timers, &timers->timer[t], t);\n\n    switch (addr & 0xff) {\n        case 0x00: return timers->timer[t].counter & 0xffff;\n        case 0x10: return timers->timer[t].mode;\n        case 0x20: return timers->timer[t].compare;\n        case 0x30: return timers->timer[t].hold;\n    }\n\n    return 0;\n}\n"
  },
  {
    "path": "src/ee/timers.h",
    "content": "#ifndef EE_TIMERS_H\n#define EE_TIMERS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"scheduler.h\"\n#include \"intc.h\"\n\nstruct ee_timer {\n    uint32_t counter;\n    uint16_t mode;\n    uint32_t compare;\n    uint16_t hold;\n    \n    // Internal state\n    int id;\n    uint32_t internal;\n    uint32_t delta;\n    uint32_t delta_reload;\n    uint32_t check_reload;\n    int cycles_until_compare;\n    int cycles_until_overflow;\n    int cycles_until_check;\n    int check_enabled;\n    \n    // Lazy evaluation\n    uint64_t last_sync_cycle;\n    \n    // Mode fields\n    int clks;\n    int gate;\n    int gats;\n    int gatm;\n    int zret;\n    int cue;\n    int cmpe;\n    int ovfe;\n};\n\nstruct ps2_ee_timers {\n    struct ee_timer timer[4];\n    uint8_t active_mask;\n    \n    // Global cycle tracking for lazy evaluation\n    uint64_t current_cycle;\n    uint64_t scheduler_advanced_cycles;\n    int irq_event_pending;\n\n    struct ps2_intc* intc;\n    struct sched_state* sched;\n};\n\nstruct ps2_ee_timers* ps2_ee_timers_create(void);\nvoid ps2_ee_timers_init(struct ps2_ee_timers* timers, struct ps2_intc* intc, struct sched_state* sched);\nvoid ps2_ee_timers_destroy(struct ps2_ee_timers* timers);\nuint64_t ps2_ee_timers_read16(struct ps2_ee_timers* timers, uint32_t addr);\nuint64_t ps2_ee_timers_read32(struct ps2_ee_timers* timers, uint32_t addr);\nvoid ps2_ee_timers_write32(struct ps2_ee_timers* timers, uint32_t addr, uint64_t data);\nvoid ps2_ee_timers_write16(struct ps2_ee_timers* timers, uint32_t addr, uint64_t data);\nvoid ps2_ee_timers_tick(struct ps2_ee_timers* timers);\nvoid ps2_ee_timers_tick_cycles(struct ps2_ee_timers* timers, uint32_t cycles);\nvoid ps2_ee_timers_handle_hblank(struct ps2_ee_timers* timers);\nvoid ps2_ee_timers_handle_vblank_in(struct ps2_ee_timers* timers);\nvoid ps2_ee_timers_handle_vblank_out(struct ps2_ee_timers* timers);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/vif.c",
    "content": "#include <assert.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <math.h>\n\n#include \"vif.h\"\n\n#define printf(fmt, ...)(0)\n\nstruct ps2_vif* ps2_vif_create(void) {\n    return malloc(sizeof(struct ps2_vif));\n}\n\nvoid ps2_vif_init(struct ps2_vif* vif, int id, struct vu_state* vu, struct ps2_gif* gif, struct ps2_intc* intc, struct sched_state* sched, struct ee_bus* bus) {\n    memset(vif, 0, sizeof(struct ps2_vif));\n\n    vif->sched = sched;\n    vif->intc = intc;\n    vif->gif = gif;\n    vif->bus = bus;\n    vif->vu = vu;\n    vif->id = id;\n}\n\nvoid ps2_vif_destroy(struct ps2_vif* vif) {\n    free(vif);\n}\n\nstatic inline void vif_write_vu_mem(struct ps2_vif* vif, uint128_t data) {\n    // Process mask\n    if (vif->unpack_mask) {\n        int cycle = (vif->unpack_cycle > 3) ? 3 : vif->unpack_cycle;\n        int m[4], shift = (cycle & 3) * 8;\n        uint32_t mask = (vif->mask >> shift) & 0xff;\n        m[0] = (mask >> 0) & 3;\n        m[1] = (mask >> 2) & 3;\n        m[2] = (mask >> 4) & 3;\n        m[3] = (mask >> 6) & 3;\n\n        // Note: Mode 3 is undocumented, it sets the row registers\n        //       to the value of the unpacked data, without changing\n        //       the unpacked data itself.\n        for (int i = 0; i < 4; i++) {\n            if (m[i] == 0) {\n                // Normal mode, m==0 -> write value as is\n                if (vif->mode == 0) {\n                    continue;\n                } else if (vif->mode == 1) {\n                    // Addition decompression\n                    data.u32[i] = vif->r[i] + data.u32[i];\n                } else if (vif->mode == 2) {\n                    // Subtraction decompression\n                    data.u32[i] = vif->r[i] + data.u32[i];\n                    vif->r[i] = data.u32[i];\n                } else if (vif->mode == 3) {\n                    vif->r[i] = data.u32[i];\n                }\n            } else if (m[i] == 1) {\n                data.u32[i] = vif->r[i];\n            } else if (m[i] == 2) {\n                data.u32[i] = vif->c[cycle];\n            } else {\n                // m=3 masks this fields' write, so we fetch\n                // the value from VU mem instead\n                data.u32[i] = vu_get_vu_mem_ptr(vif->vu, vif->addr)->u32[i];\n            }\n        }\n    } else {\n        // Do mode processing only\n        for (int i = 0; i < 4; i++) {\n            if (vif->mode == 0) {\n                continue;\n            } else if (vif->mode == 1) {\n                // Offset decompression\n                data.u32[i] = vif->r[i] + data.u32[i];\n            } else if (vif->mode == 2) {\n                // Difference decompression\n                data.u32[i] = vif->r[i] + data.u32[i];\n                vif->r[i] = data.u32[i];\n            } else if (vif->mode == 3) {\n                vif->r[i] = data.u32[i];\n            }\n        }\n    }\n\n    if (vif->unpack_cl == vif->unpack_wl) {\n        // Write data normally\n        *vu_get_vu_mem_ptr(vif->vu, vif->addr++) = data;\n    } else if (vif->unpack_cl > vif->unpack_wl) {\n        // Write data until unpack_wl is reached, then skip unpack_skip\n        *vu_get_vu_mem_ptr(vif->vu, vif->addr++) = data;\n    } else {\n        fprintf(stderr, \"vif%d: Unpack error: unpack_cl (%d) < unpack_wl (%d)\\n\", vif->id, vif->unpack_cl, vif->unpack_wl);\n        exit(1);\n    }\n\n    vif->unpack_cycle++;\n\n    if (vif->unpack_cycle == vif->unpack_wl) {\n        vif->addr += vif->unpack_skip;\n        vif->unpack_cycle = 0;\n    }\n}\n\nvoid vif0_send_irq(void* udata, int overshoot) {\n    struct ps2_vif* vif = (struct ps2_vif*)udata;\n\n    ps2_intc_irq(vif->intc, EE_INTC_VIF0);\n}\n\nvoid vif1_send_irq(void* udata, int overshoot) {\n    struct ps2_vif* vif = (struct ps2_vif*)udata;\n\n    ps2_intc_irq(vif->intc, EE_INTC_VIF1);\n}\n\nstatic inline void vif_handle_fifo_write(struct ps2_vif* vif, uint32_t data) {\n    if (vif->state == VIF_IDLE) {\n        vif->cmd = (data >> 24) & 0xff;\n\n        if (vif->cmd & 0x80) {\n            struct sched_event event;\n\n            event.callback = vif->id ? vif1_send_irq : vif0_send_irq;\n            event.cycles = 1000;\n            event.name = vif->id ? \"VIF1 Interrupt\" : \"VIF0 Interrupt\";\n            event.udata = vif;\n\n            sched_schedule(vif->sched, event);\n\n            // fprintf(stderr, \"vif%d: Requested IRQ command=%02x\\n\", vif->id, vif->cmd);\n            vif->stat |= 0x00000c02;\n            vif->stat ^= 0x0f000000;\n            vif->code = data;\n        }\n\n        switch ((data >> 24) & 0x7f) {\n            case VIF_CMD_NOP: {\n                // printf(\"vif%d: NOP\\n\", vif->id);\n            } break;\n            case VIF_CMD_STCYCL: {\n                // printf(\"vif%d: STCYCL(%04x)\\n\", vif->id, data & 0xffff);\n\n                vif->cycle = data & 0xffff;\n            } break;\n            case VIF_CMD_OFFSET: {\n                // printf(\"vif%d: OFFSET(%04x)\\n\", vif->id, data & 0xffff);\n\n                // Set DBF to 0\n                vif->stat &= ~0x80;\n\n                // Set TOPS to BASE\n                vif->tops = vif->base;\n\n                vif->ofst = data & 0x3ff;\n            } break;\n            case VIF_CMD_BASE: {\n                // printf(\"vif%d: BASE(%04x)\\n\", vif->id, data & 0xffff);\n\n                vif->base = data & 0x3ff;\n            } break;\n            case VIF_CMD_ITOP: {\n                // printf(\"vif%d: ITOP(%04x)\\n\", vif->id, data & 0xffff);\n\n                vif->itops = data & 0x3ff;\n            } break;\n            case VIF_CMD_STMOD: {\n                // printf(\"vif%d: STMOD(%04x)\\n\", vif->id, data & 0xffff);\n\n                vif->mode = data & 3;\n            } break;\n            case VIF_CMD_MSKPATH3: {\n                // fprintf(stdout, \"vif%d: MSKPATH3(%04x)\\n\", vif->id, data & 0xffff);\n\n                if (data & 0x8000) {\n                    vif->gif->stat |= 2;\n                } else {\n                    vif->gif->stat &= ~2;\n                }\n            } break;\n            case VIF_CMD_MARK: {\n                // printf(\"vif%d: MARK(%04x)\\n\", vif->id, data & 0xffff);\n\n                vif->mark = data & 0xffff;\n            } break;\n            case VIF_CMD_FLUSHE: {\n                // printf(\"vif%d: FLUSHE\\n\", vif->id);\n            } break;\n            case VIF_CMD_FLUSH: {\n                // Note: MASSIVE GRAN TURISMO HACK!\n                //       GT3/4 expect IBT and stall bits to be set when a\n                //       VIF IRQ occurs, CODE also needs to be set to the\n                //       last command that caused a stall.\n                //       This is admittedly a huge hack, but we can't really\n                //       emulate any of this without properly implementing\n                //       DMA timings.\n                // printf(\"vif%d: FLUSH\\n\", vif->id);\n            } break;\n            case VIF_CMD_FLUSHA: {\n                // printf(\"vif%d: FLUSHA\\n\", vif->id);\n            } break;\n            case VIF_CMD_MSCAL: {\n                // printf(\"vif%d: MSCAL(%04x)\\n\", vif->id, data & 0xffff);\n\n                vif->top = vif->tops;\n\n                // Toggle DBF\n                vif->stat ^= 0x80;\n                vif->tops = vif->base;\n                vif->itop = vif->itops;\n\n                if (vif->stat & 0x80) {\n                    vif->tops += vif->ofst;\n                }\n\n                vu_execute_program(vif->vu, data & 0xffff);\n            } break;\n            case VIF_CMD_MSCALF: {\n                // printf(\"vif%d: MSCALF(%04x)\\n\", vif->id, data & 0xffff);\n\n                vif->top = vif->tops;\n\n                // Toggle DBF\n                vif->stat ^= 0x80;\n                vif->tops = vif->base;\n                vif->itop = vif->itops;\n\n                if (vif->stat & 0x80) {\n                    vif->tops += vif->ofst;\n                }\n\n                vu_execute_program(vif->vu, data & 0xffff);\n            } break;\n            case VIF_CMD_MSCNT: {\n                // printf(\"vif%d: MSCNT(%08x)\\n\", vif->id, vu_get_tpc(vif->vu));\n\n                vif->top = vif->tops;\n\n                // Toggle DBF\n                vif->stat ^= 0x80;\n                vif->tops = vif->base;\n                vif->itop = vif->itops;\n\n                if (vif->stat & 0x80) {\n                    vif->tops += vif->ofst;\n                }\n\n                vu_execute_program_tpc(vif->vu);\n            } break;\n            case VIF_CMD_STMASK: {\n                // printf(\"vif%d: STMASK(%04x)\\n\", vif->id, data & 0xffff);\n\n                vif->state = VIF_RECV_DATA;\n                vif->pending_words = 1;\n            } break;\n            case VIF_CMD_STROW: {\n                // printf(\"vif%d: STROW(%04x)\\n\", vif->id, data & 0xffff);\n\n                vif->state = VIF_RECV_DATA;\n                vif->pending_words = 4;\n            } break;\n            case VIF_CMD_STCOL: {\n                // printf(\"vif%d: STCOL(%04x)\\n\", vif->id, data & 0xffff);\n\n                vif->state = VIF_RECV_DATA;\n                vif->pending_words = 4;\n            } break;\n            case VIF_CMD_MPG: {\n                // printf(\"vif%d: MPG(%04x, %04x)\\n\", vif->id, (data >> 16) & 0xff, data & 0xffff);\n\n                int num = (data >> 16) & 0xff;\n\n                if (!num) num = 256;\n\n                vif->addr = data & 0xffff;\n                vif->state = VIF_RECV_DATA;\n                vif->pending_words = num * 2;\n                vif->shift = 0;\n\n                vu_invalidate_range(vif->vu, vif->addr << 3, num << 3);\n            } break;\n            case VIF_CMD_DIRECT: {\n                // fprintf(stdout, \"vif%d: DIRECT(%04x)\\n\", vif->id, data & 0xffff);\n\n                int imm = data & 0xffff;\n\n                if (imm == 0) {\n                    imm = 0x10000;\n                }\n\n                vif->state = VIF_RECV_DATA;\n                vif->pending_words = imm * 4;\n                vif->shift = 0;\n            } break;\n            case VIF_CMD_DIRECTHL: {\n                // fprintf(stdout, \"vif%d: DIRECTHL(%04x)\\n\", vif->id, data & 0xffff);\n\n                int imm = data & 0xffff;\n\n                if (imm == 0) {\n                    imm = 0x10000;\n                }\n\n                vif->state = VIF_RECV_DATA;\n                vif->pending_words = imm * 4;\n                vif->shift = 0;\n            } break;\n\n            // UNPACK commands\n            case 0x60: case 0x61: case 0x62: case 0x63:\n            case 0x64: case 0x65: case 0x66: case 0x67:\n            case 0x68: case 0x69: case 0x6a: case 0x6b:\n            case 0x6c: case 0x6d: case 0x6e: case 0x6f:\n            case 0x70: case 0x71: case 0x72: case 0x73:\n            case 0x74: case 0x75: case 0x76: case 0x77:\n            case 0x78: case 0x79: case 0x7a: case 0x7b:\n            case 0x7c: case 0x7d: case 0x7e: case 0x7f: {\n                vif->unpack_fmt = (data >> 24) & 0xf;\n                vif->unpack_usn = (data >> 14) & 1;\n                vif->unpack_num = (data >> 16) & 0xff;\n                vif->unpack_cl = vif->cycle & 0xff;\n                vif->unpack_wl = (vif->cycle >> 8) & 0xff;\n                vif->unpack_mask = (data >> 28) & 1;\n                vif->unpack_cycle = 0;\n\n                int vl = (data >> 24) & 3;\n                int vn = (data >> 26) & 3;\n                int flg = (data >> 15) & 1;\n                int addr = data & 0x3ff;\n                int filling = vif->unpack_cl < vif->unpack_wl;\n\n                if (!vif->unpack_num) vif->unpack_num = 256;\n                if (flg) addr += vif->tops;\n\n                if (filling) {\n                    // fprintf(stderr, \"vif%d: Filling mode unimplemented\\n\", vif->id);\n\n                    return;\n                    // exit(1);\n                }\n\n                // To-do: Handle for filling\n                vif->unpack_skip = vif->unpack_cl - vif->unpack_wl;\n                vif->unpack_wl_count = 0;\n\n                uint32_t pack_size = 16;\n\n                if ((vl == 3 && vn == 3) == 0)\n                    pack_size = (32 >> vl) * (vn + 1);\n\n                vif->pending_words = pack_size * vif->unpack_num;\n                vif->pending_words = (vif->pending_words + 0x1F) & ~0x1F;\n                vif->pending_words /= 32;\n\n                vif->unpack_shift = 0;\n                vif->state = VIF_RECV_DATA;\n                vif->shift = 0;\n                vif->addr = addr;\n\n                // fprintf(stdout, \"vif%d: UNPACK %02x fmt=%02x flg=%d num=%02x addr=%08x tops=%08x usn=%d wr=%d mode=%d\\n\", vif->id, data >> 24, vif->unpack_fmt, flg, vif->unpack_num, addr, vif->tops, vif->unpack_usn, vif->pending_words, vif->mode);\n            } break;\n            default: {\n                // fprintf(stderr, \"vif%d: Unhandled command %02x\\n\", vif->id, vif->cmd);\n\n                // exit(1);\n            } break;\n        }\n    } else {\n        switch (vif->cmd) {\n            case VIF_CMD_STMASK: {\n                vif->mask = data;\n                vif->state = VIF_IDLE;\n            } break;\n            case VIF_CMD_STROW: {\n                vif->r[4 - (vif->pending_words--)] = data;\n\n                if (!vif->pending_words) {\n                    vif->state = VIF_IDLE;\n                }\n            } break;\n            case VIF_CMD_STCOL: {\n                vif->c[4 - (vif->pending_words--)] = data;\n\n                if (!vif->pending_words) {\n                    vif->state = VIF_IDLE;\n                }\n            } break;\n            case VIF_CMD_MPG: {\n                if (!vif->shift) {\n                    vif->data.u32[vif->shift++] = data;\n                } else {\n                    vif->data.u32[1] = data;\n\n                    // fprintf(stdout, \"vif%d: Writing %08x %08x to MicroMem addr=%04x\\n\", vif->id, vif->data.u32[0], vif->data.u32[1], vif->addr);\n\n                    *vu_get_micro_mem_ptr(vif->vu, vif->addr++) = vif->data.u64[0];\n\n                    vif->shift = 0;\n                }\n\n                if (!(--vif->pending_words)) {\n                    vif->state = VIF_IDLE;\n                }\n            } break;\n            case VIF_CMD_DIRECTHL:\n            case VIF_CMD_DIRECT: {\n                vif->data.u32[vif->shift++] = data;\n\n                vif->pending_words--;\n\n                if (vif->shift == 4) {\n                    // fprintf(stdout, \"vif%d: Writing %08x %08x %08x %08x to GIF FIFO pending=%d\\n\", vif->id, vif->data.u32[3], vif->data.u32[2], vif->data.u32[1], vif->data.u32[0], vif->pending_words);\n                    ps2_gif_fifo_write(vif->gif, vif->data, GIF_PATH2);\n\n                    vif->shift = 0;\n                }\n\n                if (!vif->pending_words) {\n                    // fprintf(stdout, \"vif%d: DIRECT complete\\n\", vif->id);\n\n                    vif->state = VIF_IDLE;\n                }\n            } break;\n\n            case 0x60: case 0x61: case 0x62: case 0x63:\n            case 0x64: case 0x65: case 0x66: case 0x67:\n            case 0x68: case 0x69: case 0x6a: case 0x6b:\n            case 0x6c: case 0x6d: case 0x6e: case 0x6f:\n            case 0x70: case 0x71: case 0x72: case 0x73:\n            case 0x74: case 0x75: case 0x76: case 0x77:\n            case 0x78: case 0x79: case 0x7a: case 0x7b:\n            case 0x7c: case 0x7d: case 0x7e: case 0x7f: {\n                switch (vif->unpack_fmt) {\n                    // S-32\n                    case 0x00: {\n                        vif->data.u32[0] = data;\n                        vif->data.u32[1] = data;\n                        vif->data.u32[2] = data;\n                        vif->data.u32[3] = data;\n\n                        vif_write_vu_mem(vif, vif->data);\n                    } break;\n\n                    // S-16\n                    case 0x01: {\n                        for (int i = 0; i < 2; i++) {\n                            uint128_t q = { 0 };\n\n                            q.u32[0] = (data >> (i * 16)) & 0xffff;\n\n                            if (!vif->unpack_usn) {\n                                q.u32[0] = (int32_t)((int16_t)q.u32[0]);\n                            }\n\n                            q.u32[1] = q.u32[0];\n                            q.u32[2] = q.u32[0];\n                            q.u32[3] = q.u32[0];\n\n                            vif_write_vu_mem(vif, q);\n\n                            vif->unpack_num--;\n\n                            if (!vif->unpack_num)\n                                break;\n                        }\n                    } break;\n\n                    // S-8\n                    case 0x02: {\n                        for (int i = 0; i < 4; i++) {\n                            uint128_t q = { 0 };\n\n                            q.u32[0] = (data >> (i * 8)) & 0xff;\n\n                            if (!vif->unpack_usn) {\n                                q.u32[0] = (int32_t)((int8_t)q.u32[0]);\n                            }\n\n                            q.u32[1] = q.u32[0];\n                            q.u32[2] = q.u32[0];\n                            q.u32[3] = q.u32[0];\n\n                            vif_write_vu_mem(vif, q);\n\n                            vif->unpack_num--;\n\n                            if (!vif->unpack_num)\n                                break;\n                        }\n                    } break;\n\n                    // V2-32\n                    case 0x04: {\n                        vif->unpack_buf[vif->shift++] = data;\n\n                        if (vif->shift == 2) {\n                            uint128_t q = { 0 };\n\n                            q.u32[0] = vif->unpack_buf[0];\n                            q.u32[1] = vif->unpack_buf[1];\n\n                            vif_write_vu_mem(vif, q);\n\n                            vif->shift = 0;\n\n                            vif->unpack_num--;\n\n                            if (!vif->unpack_num)\n                                break;\n                        }\n                    } break;\n\n                    // V2-16\n                    case 0x05: {\n                        uint128_t q = { 0 };\n\n                        q.u32[0] = data & 0xffff;\n                        q.u32[1] = data >> 16;\n\n                        if (!vif->unpack_usn) {\n                            q.u32[0] = (int32_t)((int16_t)q.u32[0]);\n                            q.u32[1] = (int32_t)((int16_t)q.u32[1]);\n                        }\n\n                        vif_write_vu_mem(vif, q);\n\n                        vif->unpack_num--;\n\n                        if (!vif->unpack_num)\n                            break;\n                    } break;\n\n                    // V2-8\n                    case 0x06: {\n                        for (int i = 0; i < 2; i++) {\n                            uint128_t q = { 0 };\n                            uint16_t d = data >> (i * 16);\n\n                            q.u32[0] = d & 0xff;\n                            q.u32[1] = d >> 8;\n\n                            if (!vif->unpack_usn) {\n                                q.u32[0] = (int32_t)((int8_t)q.u32[0]);\n                                q.u32[1] = (int32_t)((int8_t)q.u32[1]);\n                            }\n\n                            vif_write_vu_mem(vif, q);\n\n                            vif->unpack_num--;\n\n                            if (!vif->unpack_num)\n                                break;\n                        }\n                    } break;\n\n                    // V3-32\n                    case 0x08: {\n                        vif->unpack_buf[vif->shift++] = data;\n\n                        if (vif->shift == 3) {\n                            uint128_t q = { 0 };\n\n                            q.u32[0] = vif->unpack_buf[0];\n                            q.u32[1] = vif->unpack_buf[1];\n                            q.u32[2] = vif->unpack_buf[2];\n\n                            vif_write_vu_mem(vif, q);\n\n                            vif->shift = 0;\n                            vif->unpack_num--;\n\n                            if (!vif->unpack_num)\n                                break;\n                        }\n                    } break;\n\n                    // V3-16\n                    case 0x09: {\n                        vif->unpack_buf[vif->shift++] = data;\n\n                        if (vif->shift == (vif->unpack_shift ? 1 : 2)) {\n                            uint128_t q = { 0 };\n\n                            if (!vif->unpack_shift) {\n                                q.u32[0] = vif->unpack_buf[0] & 0xffff;\n                                q.u32[1] = (vif->unpack_buf[0] >> 16) & 0xffff;\n                                q.u32[2] = vif->unpack_buf[1] & 0xffff;\n                            } else {\n                                q.u32[0] = vif->unpack_data;\n                                q.u32[1] = vif->unpack_buf[0] & 0xffff;\n                                q.u32[2] = vif->unpack_buf[0] >> 16;\n                            }\n\n                            if (!vif->unpack_usn) {\n                                q.u32[0] = (int32_t)((int16_t)q.u32[0]);\n                                q.u32[1] = (int32_t)((int16_t)q.u32[1]);\n                                q.u32[2] = (int32_t)((int16_t)q.u32[2]);\n                            }\n\n                            vif_write_vu_mem(vif, q);\n\n                            vif->shift = 0;\n                            vif->unpack_num--;\n                            vif->unpack_shift ^= 1;\n                            vif->unpack_data = vif->unpack_buf[1] >> 16;\n\n                            if (!vif->unpack_num)\n                                break;\n                        }\n                    } break;\n\n                    // V3-8 (disgusting)\n                    case 0x0a: {\n                        uint128_t q = { 0 };\n\n                        switch (vif->unpack_shift) {\n                            case 0: {\n                                q.u32[0] = data & 0xff;\n                                q.u32[1] = (data >> 8) & 0xff;\n                                q.u32[2] = (data >> 16) & 0xff;\n\n                                vif->unpack_data = data >> 24;\n                                vif->unpack_shift++;\n\n                                if (!vif->unpack_usn) {\n                                    q.u32[0] = (int32_t)((int8_t)q.u32[0]);\n                                    q.u32[1] = (int32_t)((int8_t)q.u32[1]);\n                                    q.u32[2] = (int32_t)((int8_t)q.u32[2]);\n                                }\n\n                                vif_write_vu_mem(vif, q);\n\n                                vif->unpack_num--;\n\n                                if (!vif->unpack_num)\n                                    break;\n                            } break;\n\n                            case 1: {\n                                q.u32[0] = vif->unpack_data;\n                                q.u32[1] = data & 0xff;\n                                q.u32[2] = (data >> 8) & 0xff;\n\n                                vif->unpack_data = data >> 16;\n                                vif->unpack_shift++;\n\n                                if (!vif->unpack_usn) {\n                                    q.u32[0] = (int32_t)((int8_t)q.u32[0]);\n                                    q.u32[1] = (int32_t)((int8_t)q.u32[1]);\n                                    q.u32[2] = (int32_t)((int8_t)q.u32[2]);\n                                }\n\n                                vif_write_vu_mem(vif, q);\n\n                                vif->unpack_num--;\n\n                                if (!vif->unpack_num)\n                                    break;\n                            } break;\n\n                            case 2: {\n                                q.u32[0] = vif->unpack_data & 0xff;\n                                q.u32[1] = (vif->unpack_data >> 8) & 0xff;\n                                q.u32[2] = data & 0xff;\n\n                                vif->unpack_data = (data >> 8) & 0xffffff;\n                                vif->unpack_shift++;\n\n                                if (!vif->unpack_usn) {\n                                    q.u32[0] = (int32_t)((int8_t)q.u32[0]);\n                                    q.u32[1] = (int32_t)((int8_t)q.u32[1]);\n                                    q.u32[2] = (int32_t)((int8_t)q.u32[2]);\n                                }\n\n                                vif_write_vu_mem(vif, q);\n\n                                vif->unpack_num--;\n\n                                if (!vif->unpack_num)\n                                    break;\n\n                                q.u32[0] = (data >> 8) & 0xff;\n                                q.u32[1] = (data >> 16) & 0xff;\n                                q.u32[2] = (data >> 24) & 0xff;\n\n                                vif->unpack_shift = 0;\n\n                                if (!vif->unpack_usn) {\n                                    q.u32[0] = (int32_t)((int8_t)q.u32[0]);\n                                    q.u32[1] = (int32_t)((int8_t)q.u32[1]);\n                                    q.u32[2] = (int32_t)((int8_t)q.u32[2]);\n                                }\n\n                                vif_write_vu_mem(vif, q);\n\n                                vif->unpack_num--;\n\n                                if (!vif->unpack_num)\n                                    break;\n                            } break;\n                        }\n                    } break;\n\n                    // V4-32\n                    case 0x0c: {\n                        vif->unpack_buf[vif->shift++] = data;\n\n                        if (vif->shift == 4) {\n                            uint128_t q = { 0 };\n\n                            q.u32[0] = vif->unpack_buf[0];\n                            q.u32[1] = vif->unpack_buf[1];\n                            q.u32[2] = vif->unpack_buf[2];\n                            q.u32[3] = vif->unpack_buf[3];\n\n                            vif_write_vu_mem(vif, q);\n\n                            vif->shift = 0;\n                        }\n                    } break;\n\n                    // V4-16\n                    case 0x0d: {\n                        vif->unpack_buf[vif->shift++] = data;\n\n                        if (vif->shift == 2) {\n                            uint128_t q = { 0 };\n\n                            q.u32[0] = vif->unpack_buf[0] & 0xffff;\n                            q.u32[1] = vif->unpack_buf[0] >> 16;\n                            q.u32[2] = vif->unpack_buf[1] & 0xffff;\n                            q.u32[3] = vif->unpack_buf[1] >> 16;\n\n                            if (!vif->unpack_usn) {\n                                q.u32[0] = (int32_t)((int16_t)q.u32[0]);\n                                q.u32[1] = (int32_t)((int16_t)q.u32[1]);\n                                q.u32[2] = (int32_t)((int16_t)q.u32[2]);\n                                q.u32[3] = (int32_t)((int16_t)q.u32[3]);\n                            }\n\n                            vif_write_vu_mem(vif, q);\n\n                            vif->shift = 0;\n                        }\n                    } break;\n\n                    // V4-8\n                    case 0x0e: {\n                        uint128_t q = { 0 };\n\n                        q.u32[0] = data & 0xff;\n                        q.u32[1] = (data >> 8) & 0xff;\n                        q.u32[2] = (data >> 16) & 0xff;\n                        q.u32[3] = (data >> 24) & 0xff;\n\n                        if (!vif->unpack_usn) {\n                            q.u32[0] = (int32_t)((int8_t)q.u32[0]);\n                            q.u32[1] = (int32_t)((int8_t)q.u32[1]);\n                            q.u32[2] = (int32_t)((int8_t)q.u32[2]);\n                            q.u32[3] = (int32_t)((int8_t)q.u32[3]);\n                        }\n\n                        vif_write_vu_mem(vif, q);\n                    } break;\n\n                    // V4-5\n                    case 0x0f: {\n                        uint128_t q = { 0 };\n\n                        for (int i = 0; i < 2; i++) {\n                            uint16_t c = (data >> (i * 16)) & 0xffff;\n\n                            q.u32[0] = ((c >> 0) & 0x1f) << 3;\n                            q.u32[1] = ((c >> 5) & 0x1f) << 3;\n                            q.u32[2] = ((c >> 10) & 0x1f) << 3;\n                            q.u32[3] = ((c >> 15) & 1) << 7;\n\n                            vif_write_vu_mem(vif, q);\n\n                            vif->unpack_num--;\n\n                            if (!vif->unpack_num)\n                                break;\n                        }\n                    } break;\n\n                    default: {\n                        fprintf(stderr, \"vif%d: Unimplemented unpack format %02x\\n\", vif->id, vif->unpack_fmt);\n\n                        exit(1);\n                    } break;\n                }\n\n                if (!(--vif->pending_words)) {\n                    vif->state = VIF_IDLE;\n                }\n            } break;\n        }\n    }\n}\n\nuint64_t ps2_vif_read32(struct ps2_vif* vif, uint32_t addr) {\n    switch (addr) {\n        // VIF0 registers\n        case 0x10003800: return vif->stat;\n        // case 0x10003810: return vif->fbrst;\n        case 0x10003820: return vif->err;\n        case 0x10003830: return vif->mark;\n        case 0x10003840: return vif->cycle;\n        case 0x10003850: return vif->mode;\n        case 0x10003860: return vif->num;\n        case 0x10003870: return vif->mask;\n        case 0x10003880: return vif->code;\n        case 0x10003890: return vif->itops;\n        case 0x100038d0: return vif->itop;\n        case 0x10003900: return vif->r[0];\n        case 0x10003910: return vif->r[1];\n        case 0x10003920: return vif->r[2];\n        case 0x10003930: return vif->r[3];\n        case 0x10003940: return vif->c[0];\n        case 0x10003950: return vif->c[1];\n        case 0x10003960: return vif->c[2];\n        case 0x10003970: return vif->c[3];\n\n        // VIF1 registers\n        case 0x10003c00: {\n            uint32_t stat = vif->stat; vif->stat = 0;\n            \n            return stat; \n        } break;\n        case 0x10003c10: return vif->fbrst;\n        case 0x10003c20: return vif->err;\n        case 0x10003c30: return vif->mark;\n        case 0x10003c40: return vif->cycle;\n        case 0x10003c50: return vif->mode;\n        case 0x10003c60: return vif->num;\n        case 0x10003c70: return vif->mask;\n        case 0x10003c80: return vif->code;\n        case 0x10003c90: return vif->itops;\n        case 0x10003ca0: return vif->base;\n        case 0x10003cb0: return vif->ofst;\n        case 0x10003cc0: return vif->tops;\n        case 0x10003cd0: return vif->itop;\n        case 0x10003ce0: return vif->top;\n        case 0x10003d00: return vif->r[0];\n        case 0x10003d10: return vif->r[1];\n        case 0x10003d20: return vif->r[2];\n        case 0x10003d30: return vif->r[3];\n        case 0x10003d40: return vif->c[0];\n        case 0x10003d50: return vif->c[1];\n        case 0x10003d60: return vif->c[2];\n        case 0x10003d70: return vif->c[3];\n\n        // VIF FIFOs\n        case 0x10004000: // printf(\"vif%d: 32-bit FIFO read\\n\", vif->id); exit(1); break;\n        case 0x10005000: // printf(\"vif%d: 32-bit FIFO read\\n\", vif->id); exit(1); break;\n\n        default: {\n            fprintf(stderr, \"vif%d: Unhandled 32-bit read to %08x\\n\", vif->id, addr);\n\n            exit(1);\n        } break;\n    }\n\n    return 0;\n}\n\nvoid ps2_vif_write32(struct ps2_vif* vif, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        // VIF0 registers\n        case 0x10003810: {\n            vif->fbrst = data;\n            vif->state = VIF_IDLE;\n            vif->pending_words = 0;\n            vif->unpack_shift = 0;\n            vif->shift = 0;\n        } break;\n\n        case 0x10003820: vif->err = data; break;\n        case 0x10003830: vif->mark = data; break;\n\n        // VIF1 registers\n        case 0x10003c00: vif->stat &= 0x800000; vif->stat |= data & 0x800000; break;\n        case 0x10003c10: {\n            vif->fbrst = data;\n            vif->state = VIF_IDLE;\n            vif->pending_words = 0;\n            vif->unpack_shift = 0;\n            vif->shift = 0;\n        } break;\n\n        case 0x10003c20: vif->err = data; break;\n        case 0x10003c30: vif->mark = data; break;\n        case 0x10003c80: /* Unknown */ break;\n\n        // VIF FIFOs\n        case 0x10004000: vif_handle_fifo_write(vif, data); break;\n        case 0x10005000: vif_handle_fifo_write(vif, data); break;\n\n        default: {\n            fprintf(stderr, \"vif%d: Unhandled 32-bit write to %08x\\n\", vif->id, addr);\n\n            // exit(1);\n        } break;\n    }\n}\n\nuint128_t ps2_vif_read128(struct ps2_vif* vif, uint32_t addr) {\n    switch (addr) {\n        case 0x10004000: break; // printf(\"vif%d: 128-bit FIFO read\\n\", vif->id); exit(1); break;\n        case 0x10005000: break; // printf(\"vif%d: 128-bit FIFO read\\n\", vif->id); exit(1); break;\n\n        default: {\n            fprintf(stderr, \"vif%d: Unhandled 128-bit read to %08x\\n\", vif->id, addr);\n\n            exit(1);\n        } break;\n    }\n\n    return (uint128_t){ .u64[0] = 0, .u64[1] = 0 };\n}\n\nvoid ps2_vif_write128(struct ps2_vif* vif, uint32_t addr, uint128_t data) {\n    switch (addr) {\n        case 0x10004000: {\n            vif_handle_fifo_write(vif, data.u32[0]);\n            vif_handle_fifo_write(vif, data.u32[1]);\n            vif_handle_fifo_write(vif, data.u32[2]);\n            vif_handle_fifo_write(vif, data.u32[3]);\n        } break;\n\n        case 0x10005000: {\n            vif_handle_fifo_write(vif, data.u32[0]);\n            vif_handle_fifo_write(vif, data.u32[1]);\n            vif_handle_fifo_write(vif, data.u32[2]);\n            vif_handle_fifo_write(vif, data.u32[3]);\n        } break;\n\n        default: {\n            fprintf(stderr, \"vif%d: Unhandled 128-bit write to %08x\\n\", vif->id, addr);\n\n            exit(1);\n        } break;\n    }\n}\n\n#undef printf"
  },
  {
    "path": "src/ee/vif.h",
    "content": "#ifndef VIF_H\n#define VIF_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"u128.h\"\n#include \"bus.h\"\n\n#include \"ee/intc.h\"\n#include \"ee/vu.h\"\n#include \"scheduler.h\"\n\nenum {\n    VIF_IDLE,\n    VIF_RECV_DATA\n};\n\n#define VIF_CMD_NOP 0x00\n#define VIF_CMD_STCYCL 0x01\n#define VIF_CMD_OFFSET 0x02\n#define VIF_CMD_BASE 0x03\n#define VIF_CMD_ITOP 0x04\n#define VIF_CMD_STMOD 0x05\n#define VIF_CMD_MSKPATH3 0x06\n#define VIF_CMD_MARK 0x07\n#define VIF_CMD_FLUSHE 0x10\n#define VIF_CMD_FLUSH 0x11\n#define VIF_CMD_FLUSHA 0x13\n#define VIF_CMD_MSCAL 0x14\n#define VIF_CMD_MSCALF 0x15\n#define VIF_CMD_MSCNT 0x17\n#define VIF_CMD_STMASK 0x20\n#define VIF_CMD_STROW 0x30\n#define VIF_CMD_STCOL 0x31\n#define VIF_CMD_MPG 0x4A\n#define VIF_CMD_DIRECT 0x50\n#define VIF_CMD_DIRECTHL 0x51\n// 60h-7Fh UNPACK\n\n#define UNPACK_S_32  0\n#define UNPACK_S_16  1\n#define UNPACK_S_8   2\n#define UNPACK_V2_32 4\n#define UNPACK_V2_16 5\n#define UNPACK_V2_8  6\n#define UNPACK_V3_32 8\n#define UNPACK_V3_16 9\n#define UNPACK_V3_8  10\n#define UNPACK_V4_32 12\n#define UNPACK_V4_16 13\n#define UNPACK_V4_8  14\n#define UNPACK_V4_5  15\n\nstruct ps2_vif {\n    uint32_t stat;\n    uint32_t fbrst;\n    uint32_t err;\n    uint32_t mark;\n    uint32_t cycle;\n    uint32_t mode;\n    uint32_t num;\n    uint32_t mask;\n    uint32_t code;\n    uint32_t itops;\n    uint32_t base;\n    uint32_t ofst;\n    uint32_t tops;\n    uint32_t itop;\n    uint32_t top;\n    uint32_t r[4];\n    uint32_t c[4];\n\n    int state;\n    int pending_words;\n    int shift;\n    uint32_t cmd;\n    uint128_t data;\n\n    uint32_t addr;\n    uint32_t unpack_num;\n    uint32_t unpack_fmt;\n    uint32_t unpack_usn;\n    uint32_t unpack_cl;\n    uint32_t unpack_wl;\n    uint32_t unpack_skip;\n    uint32_t unpack_wl_count;\n    uint32_t unpack_buf[16];\n    uint32_t unpack_shift;\n    uint32_t unpack_data;\n    int unpack_mask;\n    int unpack_cycle;\n\n    int id;\n\n    struct vu_state* vu;\n    struct sched_state* sched;\n    struct ps2_gif* gif;\n    struct ps2_intc* intc;\n    struct ee_bus* bus;\n};\n\nstruct ps2_vif* ps2_vif_create(void);\nvoid ps2_vif_init(struct ps2_vif* vif, int id, struct vu_state* vu, struct ps2_gif* gif, struct ps2_intc* intc, struct sched_state* sched, struct ee_bus* bus);\nvoid ps2_vif_destroy(struct ps2_vif* vif);\nuint64_t ps2_vif_read32(struct ps2_vif* vif, uint32_t addr);\nvoid ps2_vif_write32(struct ps2_vif* vif, uint32_t addr, uint64_t data);\nuint128_t ps2_vif_read128(struct ps2_vif* vif, uint32_t addr);\nvoid ps2_vif_write128(struct ps2_vif* vif, uint32_t addr, uint128_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/vu.h",
    "content": "struct vu_state;\n\n#ifndef VU_H\n#define VU_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"u128.h\"\n#include \"vif.h\"\n#include \"gif.h\"\n\nstruct vu_reg128 {\n    union {\n        uint128_t u128;\n        uint64_t u64[2];\n        uint32_t u32[4];\n        int32_t s32[4];\n        float f[4];\n\n        // Named fields\n        struct {\n            float x;\n            float y;\n            float z;\n            float w;\n        };\n    };\n};\n\nstruct vu_reg32 {\n    union {\n        uint32_t u32;\n        int32_t s32;\n        float f;\n        uint16_t u16[2];\n        int16_t s16[2];\n        uint8_t u8[4];\n        int8_t s8[4];\n    };\n};\n\n#define VU_REG_R 32\n#define VU_REG_I 33\n#define VU_REG_Q 34\n#define VU_REG_P 35\n\nstruct vu_instruction {\n    uint32_t ld_di[4];\n    uint32_t ld_d;\n    uint32_t ld_s;\n    uint32_t ld_t;\n    uint32_t ld_sf;\n    uint32_t ld_tf;\n    int32_t ld_imm5;\n    int32_t ld_imm11;\n    uint32_t ld_imm12;\n    uint32_t ld_imm15;\n    uint32_t ld_imm24;\n    uint32_t ud_di[4];\n    uint32_t ud_d;\n    uint32_t ud_s;\n    uint32_t ud_t;\n    uint32_t opcode;\n    int branch;\n\n    struct {\n        int reg;\n        int field;\n    } dst, src[2];\n\n    int vi_dst;\n    int vi_src[2];\n\n    void (*func)(struct vu_state* vu, const struct vu_instruction* i);\n};\n\nstruct vu_state;\n\nstruct vu_state* vu_create(void);\nvoid vu_init(struct vu_state* vu, int id, struct ps2_gif* gif, struct ps2_vif* vif, struct vu_state* vu1);\nvoid vu_destroy(struct vu_state* vu);\n\n// VU mem bus interface\nuint64_t ps2_vu_read8(struct vu_state* vu, uint32_t addr);\nuint64_t ps2_vu_read16(struct vu_state* vu, uint32_t addr);\nuint64_t ps2_vu_read32(struct vu_state* vu, uint32_t addr);\nuint64_t ps2_vu_read64(struct vu_state* vu, uint32_t addr);\nuint128_t ps2_vu_read128(struct vu_state* vu, uint32_t addr);\nvoid ps2_vu_write8(struct vu_state* vu, uint32_t addr, uint64_t data);\nvoid ps2_vu_write16(struct vu_state* vu, uint32_t addr, uint64_t data);\nvoid ps2_vu_write32(struct vu_state* vu, uint32_t addr, uint64_t data);\nvoid ps2_vu_write64(struct vu_state* vu, uint32_t addr, uint64_t data);\nvoid ps2_vu_write128(struct vu_state* vu, uint32_t addr, uint128_t data);\nvoid ps2_vu_write_vi(struct vu_state* vu, int index, uint32_t value);\nuint32_t ps2_vu_read_vi(struct vu_state* vu, int index);\nvoid ps2_vu_reset(struct vu_state* vu);\nvoid ps2_vu_decode_upper(struct vu_state* vu, uint32_t opcode);\nvoid ps2_vu_decode_lower(struct vu_state* vu, uint32_t opcode);\nvoid ps2_vu_execute_lower(struct vu_state* vu, uint32_t opcode);\nvoid ps2_vu_execute_upper(struct vu_state* vu, uint32_t opcode);\n\nvoid vu_cycle(struct vu_state* vu);\nvoid vu_execute_program(struct vu_state* vu, uint32_t addr);\nvoid vu_execute_program_tpc(struct vu_state* vu);\nuint128_t* vu_get_vu_mem_ptr(struct vu_state* vu, uint32_t addr);\nuint64_t* vu_get_micro_mem_ptr(struct vu_state* vu, uint32_t addr);\nuint32_t vu_get_tpc(struct vu_state* vu);\nvoid vu_clear_block_cache(struct vu_state* vu);\nvoid vu_invalidate_range(struct vu_state* vu, uint32_t addr, uint32_t size);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/vu_cached.cpp",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <math.h>\n#include <fenv.h>\n#include <utility>\n\n#include \"vu.h\"\n#include \"vu_def.hpp\"\n#include \"vu_dis.h\"\n\n// #define printf(fmt, ...)(0)\n\n#define VU_LD_DI(i) (ins->ld_di[i])\n#define VU_LD_D (ins->ld_d)\n#define VU_LD_S (ins->ld_s)\n#define VU_LD_T (ins->ld_t)\n#define VU_LD_SF (ins->ld_sf)\n#define VU_LD_TF (ins->ld_tf)\n#define VU_LD_IMM5 (ins->ld_imm5)\n#define VU_LD_IMM11 (ins->ld_imm11)\n#define VU_LD_IMM12 (ins->ld_imm12)\n#define VU_LD_IMM15 (ins->ld_imm15)\n#define VU_LD_IMM24 (ins->ld_imm24)\n#define VU_ID vu->vi[VU_LD_D]\n#define VU_IS vu->vi[VU_LD_S]\n#define VU_IT vu->vi[VU_LD_T]\n#define VU_UD_DI(i) (ins->ud_di[i])\n#define VU_UD_D (ins->ud_d)\n#define VU_UD_S (ins->ud_s)\n#define VU_UD_T (ins->ud_t)\n#define VU_D_FLD (0x01e00000)\n#define VU_D_X (0x01000000)\n#define VU_D_Y (0x00800000)\n#define VU_D_Z (0x00400000)\n#define VU_D_W (0x00200000)\n\n[[noreturn]] inline void unreachable() {\n    // Uses compiler specific extensions if possible.\n    // Even if no extension is used, undefined behavior is still raised by\n    // an empty function body and the noreturn attribute.\n#if defined(_MSC_VER) && !defined(__clang__) // MSVC\n    __assume(false);\n#else // GCC, Clang\n    __builtin_unreachable();\n#endif\n}\n\nstruct vu_state* vu_create(void) {\n    return new struct vu_state;\n}\n\nvoid vu_init(struct vu_state* vu, int id, struct ps2_gif* gif, struct ps2_vif* vif, struct vu_state* vu1) {\n    vu->id = id;\n    vu->vu1 = vu1;\n    vu->vif = vif;\n    vu->gif = gif;\n\n    if (!id) {\n        vu->micro_mem_size = 0x1ff;\n        vu->vu_mem_size = 0xff;\n    } else {\n        vu->micro_mem_size = 0x7ff;\n        vu->vu_mem_size = 0x3ff;\n    }\n\n    vu->vf[0].x = 0.0;\n    vu->vf[0].y = 0.0;\n    vu->vf[0].z = 0.0;\n    vu->vf[0].w = 1.0;\n\n    ps2_vu_reset(vu);\n\n    // VU uses round to zero by default\n    fesetround(FE_TOWARDZERO);\n\n    vu->block_cache_size = 0;\n    vu->block_cache.clear();\n    vu->block_cache.resize(vu->micro_mem_size+1);\n}\n\nvoid vu_destroy(struct vu_state* vu) {\n    delete vu;\n}\n\n#define max(a, b) ((a) > (b) ? (a) : (b))\n#define min(a, b) ((a) < (b) ? (a) : (b))\n\nstatic inline uint32_t vu_max(int32_t a, int32_t b) {\n    return (a < 0 && b < 0) ? min(a, b) : max(a, b);\n}\n\nstatic inline uint32_t vu_min(int32_t a, int32_t b) {\n    return (a < 0 && b < 0) ? max(a, b) : min(a, b);\n}\n\nstatic inline float vu_atan(float t) {\n    //In reality, VU1 uses an approximation to derive the result. This is shown here.\n    const static float atan_const[] = {\n        0.999999344348907f, -0.333298563957214f,\n        0.199465364217758f, -0.139085337519646f,\n        0.096420042216778f, -0.055909886956215f,\n        0.021861229091883f, -0.004054057877511f\n    };\n\n    float result = 0.785398185253143f; // pi/4\n\n    for (int i = 0; i < 8; i++) {\n        result += atan_const[i] * powf(t, (i * 2) + 1);\n    }\n\n    return result;\n}\n\nstatic inline void vu_update_status(struct vu_state* vu) {\n    vu->status &= ~0x3f;\n\n    vu->status |= (vu->mac_pipeline[3] & 0x000f) ? 1 : 0;\n    vu->status |= (vu->mac_pipeline[3] & 0x00f0) ? 2 : 0;\n    vu->status |= (vu->mac_pipeline[3] & 0x0f00) ? 4 : 0;\n    vu->status |= (vu->mac_pipeline[3] & 0xf000) ? 8 : 0;\n\n    vu->status |= (vu->status & 0x3f) << 6;\n}\n\nstatic inline void vu_set_q(struct vu_state* vu, float value, int delay) {\n    if (vu->q_delay == 0) {\n        vu->prev_q.f = vu->q.f;\n    }\n\n    vu->q.f = value;\n    vu->q_delay = delay;\n}\n\nstatic inline struct vu_reg32 vu_get_q(struct vu_state* vu) {\n    if (!vu->q_delay) {\n        return vu->q;\n    }\n\n    return vu->prev_q;\n}\n\nstatic inline float vu_update_flags(struct vu_state* vu, float value, int index) {\n    uint32_t value_u = *(uint32_t*)&value;\n\n    int flag_id = 3 - index;\n\n    // Sign flag\n    if (value_u & 0x80000000)\n        vu->mac |= 0x10 << flag_id;\n    else\n        vu->mac &= ~(0x10 << flag_id);\n\n    // Zero flag, clear under/overflow\n    if ((value_u & 0x7FFFFFFF) == 0) {\n        vu->mac |= 1 << flag_id;\n        vu->mac &= ~(0x1100 << flag_id);\n\n        return value;\n    }\n\n    switch ((value_u >> 23) & 0xFF) {\n        //Underflow, set zero\n        case 0:\n            vu->mac |= 0x101 << flag_id;\n            vu->mac &= ~(0x1000 << flag_id);\n            value_u = value_u & 0x80000000;\n            break;\n        //Overflow\n        case 255:\n            vu->mac |= 0x1000 << flag_id;\n            vu->mac &= ~(0x101 << flag_id);\n            value_u = (value_u & 0x80000000) | 0x7F7FFFFF;\n            break;\n        //Clear all but sign\n        default:\n            vu->mac &= ~(0x1101 << flag_id);\n            break;\n    }\n\n    return *(float*)&value_u;\n}\n\nstatic inline void vu_clear_flags(struct vu_state* vu, int index) {\n    vu->mac &= ~(0x1111 << (3 - index));\n}\n\nstatic inline float vu_cvtf(uint32_t value) {\n    switch (value & 0x7f800000) {\n        case 0x0: {\n            value &= 0x80000000;\n\n            return *(float*)&value;\n        } break;\n\n        case 0x7f800000: {\n            uint32_t result = (value & 0x80000000) | 0x7f7fffff;\n\n            return *(float*)&result;\n        }\n    }\n\n    return *(float*)&value;\n}\n\nint32_t vu_cvti(float value) {\n    if (value >= 2147483647.0)\n        return 2147483647LL;\n\n    if (value <= -2147483648.0)\n        return -2147483648LL;\n\n    return (int32_t)value;\n}\n\nstatic inline void vu_set_vf(struct vu_state* vu, int r, int f, float v) {\n    if (r) vu->vf[r].f[f] = v;\n}\n\nstatic inline void vu_set_vfu(struct vu_state* vu, int r, int f, int32_t v) {\n    if (r) vu->vf[r].s32[f] = v;\n}\n\nstatic inline void vu_set_vf_x(struct vu_state* vu, int r, float v) {\n    if (r) vu->vf[r].x = v;\n}\n\nstatic inline void vu_set_vf_y(struct vu_state* vu, int r, float v) {\n    if (r) vu->vf[r].y = v;\n}\n\nstatic inline void vu_set_vf_z(struct vu_state* vu, int r, float v) {\n    if (r) vu->vf[r].z = v;\n}\n\nstatic inline void vu_set_vf_w(struct vu_state* vu, int r, float v) {\n    if (r) vu->vf[r].w = v;\n}\n\nstatic inline void vu_set_vi(struct vu_state* vu, int r, uint16_t v) {\n    if (r) vu->vi[r] = v;\n}\n\nstatic inline float vu_vf_i(struct vu_state* vu, int r, int i) {\n    return vu_cvtf(vu->vf[r].u32[i]);\n}\n\nstatic inline float vu_vf_x(struct vu_state* vu, int r) {\n    return vu_cvtf(vu->vf[r].u32[0]);\n}\n\nstatic inline float vu_vf_y(struct vu_state* vu, int r) {\n    return vu_cvtf(vu->vf[r].u32[1]);\n}\n\nstatic inline float vu_vf_z(struct vu_state* vu, int r) {\n    return vu_cvtf(vu->vf[r].u32[2]);\n}\n\nstatic inline float vu_vf_w(struct vu_state* vu, int r) {\n    return vu_cvtf(vu->vf[r].u32[3]);\n}\n\nstatic inline float vu_acc_i(struct vu_state* vu, int i) {\n    return vu_cvtf(vu->acc.u32[i]);\n}\n\nstatic inline void vu_mem_write(struct vu_state* vu, uint16_t addr, uint32_t data, int i) {\n    if (!vu->id) {\n        if (addr <= 0x3ff) {\n            vu->vu_mem[addr & 0xff].u32[i] = data;\n        } else {\n            if ((addr >= 0x400) && (addr <= 0x41f)) {\n                vu->vu1->vf[addr & 0x1f].u32[i] = data;\n            } else if ((addr >= 0x420) && (addr <= 0x42f)) {\n                vu->vu1->vi[addr & 0xf] = data;\n            } else if (addr == 0x430) {\n                vu->vu1->status = data;\n            } else if (addr == 0x431) {\n                vu->vu1->mac = data;\n            } else if (addr == 0x432) {\n                vu->vu1->clip = data;\n            } else if (addr == 0x434) {\n                vu->vu1->r.u32 = data;\n            } else if (addr == 0x435) {\n                vu->vu1->i.u32 = data;\n            } else if (addr == 0x436) {\n                vu->vu1->q.u32 = data;\n            } else if (addr == 0x437) {\n                vu->vu1->p.u32 = data;\n            } else if (addr == 0x43a) {\n                vu->vu1->tpc = data;\n            } else {\n                // printf(\"vu: oob write\\n\");\n\n                // exit(1);\n            }\n        }\n    } else {\n        // if (addr == 0x000001d3) *(int*)0 = 0;\n\n        vu->vu_mem[addr & 0x3ff].u32[i] = data;\n    }\n}\n\nstatic inline uint128_t vu_mem_read(struct vu_state* vu, uint32_t addr) {\n    if (!vu->id) {\n        if (addr <= 0x3ff) {\n            return vu->vu_mem[addr & 0xff];\n        } else {\n            if ((addr >= 0x400) && (addr <= 0x41f)) {\n                return vu->vu1->vf[addr & 0x1f].u128;\n            } else if ((addr >= 0x420) && (addr <= 0x42f)) {\n                uint128_t result; result.u32[0] = vu->vu1->vi[addr & 0xf];\n                return result;\n            } else if (addr == 0x430) {\n                uint128_t result; result.u32[0] = vu->vu1->status;\n                return result;\n            } else if (addr == 0x431) {\n                uint128_t result; result.u32[0] = vu->vu1->mac;\n                return result;\n            } else if (addr == 0x432) {\n                uint128_t result; result.u32[0] = vu->vu1->clip;\n                return result;\n            } else if (addr == 0x434) {\n                uint128_t result; result.u32[0] = vu->vu1->r.u32;\n                return result;\n            } else if (addr == 0x435) {\n                uint128_t result; result.u32[0] = vu->vu1->i.u32;\n                return result;\n            } else if (addr == 0x436) {\n                uint128_t result; result.u32[0] = vu->vu1->q.u32;\n                return result;\n            } else if (addr == 0x437) {\n                uint128_t result; result.u32[0] = vu->vu1->p.u32;\n                return result;\n            } else if (addr == 0x43a) {\n                uint128_t result; result.u32[0] = vu->vu1->tpc;\n                return result;\n            }\n        }\n    }\n\n    return vu->vu_mem[addr & 0x3ff];\n}\n\nstatic inline void vu_write_branch_pipeline(struct vu_state* vu, int dst) {\n    if (!dst)\n        return;\n\n    //On repeat writes we need to remember the value from before the chain\n    if (vu->vi_backup_cycles && dst == vu->vi_backup_reg) {\n        vu->vi_backup_cycles = 2;\n\n        return;\n    }\n\n    vu->vi_backup_cycles = 2;\n    vu->vi_backup_reg = dst;\n    vu->vi_backup_value = vu->vi[dst];\n\n    // printf(\"branch pipeline: dst=%d prev=%04x rw=%d\\n\",\n    //     vu->branch_pipeline_curr.reg, vu->branch_pipeline_curr.prev,\n    //     vu->branch_pipeline_curr.rw\n    // );\n}\n\nstatic inline uint16_t vu_get_branch_register(struct vu_state* vu, int reg) {\n    if (vu->vi_backup_cycles && (vu->vi_backup_reg == reg)) {\n        return vu->vi_backup_value;\n    }\n\n    return vu->vi[reg];\n}\n\nvoid vu_xgkick(struct vu_state* vu) {\n    uint16_t addr = vu->xgkick_addr;\n\n    int eop = 1;\n\n    do {\n        uint128_t tag = vu_mem_read(vu, addr++);\n\n        if ((tag.u64[0] | tag.u64[1]) == 0)\n            break;\n\n        // addr &= 0x3ff;\n\n        // if (addr == 0) break;\n\n        // printf(\"tag: addr=%08x %08x %08x %08x %08x\\n\", addr - 1, tag.u32[3], tag.u32[2], tag.u32[1], tag.u32[0]);\n\n        ps2_gif_fifo_write(vu->gif, tag, GIF_PATH1);\n\n        eop = (tag.u64[0] & 0x8000) != 0;\n\n        int nloop = tag.u64[0] & 0x7fff;\n        int flg = (tag.u64[0] >> 58) & 3;\n        int nregs = (tag.u64[0] >> 60) & 0xf;\n\n        if (!nloop)\n            continue;\n\n        // if (!nregs)\n        //     nregs = 16;\n\n        int qwc = 0;\n\n        switch (flg) {\n            case 0: {\n                qwc = nregs * nloop;\n            } break;\n            case 1: {\n                qwc = (nregs * nloop + 1) / 2; // Round up for odd cases\n            } break;\n            case 2:\n            case 3: {\n                qwc = nloop;\n            } break;\n        }\n\n        if (qwc >= 0x400) {\n            fprintf(stderr, \"vu: Weird xgkick tag nloop=%d nregs=%d eop=%d flg=%d qwc=%d\\n\",\n                nloop,\n                nregs,\n                eop,\n                flg,\n                qwc\n            ); \n\n            exit(1);\n        }\n\n        for (int i = 0; i < qwc; i++) {\n            // printf(\"vu: %08x: %08x %08x %08x %08x\\n\",\n            //     addr,\n            //     vu->vu_mem[addr].u32[3],\n            //     vu->vu_mem[addr].u32[2],\n            //     vu->vu_mem[addr].u32[1],\n            //     vu->vu_mem[addr].u32[0]\n            // );\n\n            ps2_gif_fifo_write(vu->gif, vu_mem_read(vu, addr++), GIF_PATH1);\n\n            addr &= 0x3ff;\n\n            // if (addr == 0) {\n            //     eop = 1;\n            //     break;\n            // }\n        }\n    } while (!eop);\n}\n\ntemplate <typename F, std::size_t... Is>\nvoid seq(F f, std::index_sequence<Is...>) {\n    // Parameter pack expansion\n    (f(std::integral_constant<std::size_t, Is>{}), ...);\n}\n\ntemplate <size_t N, typename F>\nvoid template_seq(F f) {\n    seq(f, std::make_index_sequence<N>{});\n}\n\n// Upper pipeline\ntemplate <uint32_t di>\nvoid vu_i_abs(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_set_vf(vu, t, i, fabsf(vu_vf_i(vu, s, i)));\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_add(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n         if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + vu_vf_i(vu, t, i);\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_addi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + vu->i.f;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_addq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + q;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_addx(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_addy(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_addz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_addw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_adda(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + vu_vf_i(vu, t, i);\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_addai(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + vu->i.f;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_addaq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + q;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_addax(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_adday(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_addaz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_addaw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_sub(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - vu_vf_i(vu, t, i);\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_subi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - vu->i.f;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_subq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - q;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_subx(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_suby(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_subz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_subw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_suba(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - vu_vf_i(vu, t, i);\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_subai(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - vu->i.f;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_subaq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - q;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_subax(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n            \n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_subay(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n            \n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_subaz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n            \n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_subaw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n            \n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_mul(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i);\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_muli(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * vu->i.f;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_mulq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * q;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_mulx(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_muly(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_mulz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_mulw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_mula(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i);\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_mulai(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * vu->i.f;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_mulaq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * q;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_mulax(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_mulay(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_mulaz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_mulaw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_madd(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i);\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_maddi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu->i.f;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_maddq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n    float q = vu_get_q(vu).f;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * q;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_maddx(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_x(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_maddy(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_y(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_maddz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_z(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_maddw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_w(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_madda(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i);\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_maddai(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu->i.f;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_maddaq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu->acc.f[i] + vu_vf_i(vu, s, i) * q;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_maddax(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_madday(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_maddaz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_maddaw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msub(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i);\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msubi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu->i.f;\n            \n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msubq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n    float q = vu_get_q(vu).f;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - (vu_vf_i(vu, s, i) * q);\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msubx(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_x(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n            \n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msuby(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_y(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n            \n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msubz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_z(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n            \n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msubw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_w(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n            \n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msuba(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i);\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msubai(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu->i.f;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msubaq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu->acc.f[i] - vu_vf_i(vu, s, i) * q;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msubax(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msubay(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msubaz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_msubaw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    });\n\n    vu_update_status(vu);\n}\ntemplate <uint32_t di>\nvoid vu_i_max(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], vu->vf[t].s32[i]);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_maxi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], vu->i.s32);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_maxx(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[0];\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float fs = vu_vf_i(vu, s, i);\n\n            vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_maxy(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[1];\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float fs = vu_vf_i(vu, s, i);\n\n            vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_maxz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[2];\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float fs = vu_vf_i(vu, s, i);\n\n            vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_maxw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[3];\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            float fs = vu_vf_i(vu, s, i);\n\n            vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_mini(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], vu->vf[t].s32[i]);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_minii(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], vu->i.s32);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_minix(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[0];\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_miniy(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[1];\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_miniz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[2];\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_miniw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[3];\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc);\n        }\n    });\n}\nvoid vu_i_opmula(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    vu->acc.x = vu_vf_y(vu, s) * vu_vf_z(vu, t);\n    vu->acc.y = vu_vf_z(vu, s) * vu_vf_x(vu, t);\n    vu->acc.z = vu_vf_x(vu, s) * vu_vf_y(vu, t);\n\n    vu->acc.x = vu_cvtf(vu->acc.u32[0]);\n    vu->acc.y = vu_cvtf(vu->acc.u32[1]);\n    vu->acc.z = vu_cvtf(vu->acc.u32[2]);\n\n    vu->acc.x = vu_update_flags(vu, vu->acc.x, 0);\n    vu->acc.y = vu_update_flags(vu, vu->acc.y, 1);\n    vu->acc.z = vu_update_flags(vu, vu->acc.z, 2);\n\n    // printf(\"s(%d)=%08x %08x %08x t(%d)=%08x %08x %08x acc=%08x %08x %08x prev=%08x %08x %08x\\n\",\n    //     s,\n    //     vu->vf[s].u32[0],\n    //     vu->vf[s].u32[1],\n    //     vu->vf[s].u32[2],\n    //     t,\n    //     vu->vf[t].u32[0],\n    //     vu->vf[t].u32[1],\n    //     vu->vf[t].u32[2],\n    //     vu->acc.u32[0],\n    //     vu->acc.u32[1],\n    //     vu->acc.u32[2],\n    //     acc.u32[0],\n    //     acc.u32[1],\n    //     acc.u32[2]\n    // );\n\n    vu_clear_flags(vu, 3);\n    vu_update_status(vu);\n}\nvoid vu_i_opmsub(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    struct vu_reg128 tmp;\n\n    tmp.f[0] = vu->acc.x - vu_vf_y(vu, s) * vu_vf_z(vu, t);\n    tmp.f[1] = vu->acc.y - vu_vf_z(vu, s) * vu_vf_x(vu, t);\n    tmp.f[2] = vu->acc.z - vu_vf_x(vu, s) * vu_vf_y(vu, t);\n\n    vu_set_vf_x(vu, d, vu_update_flags(vu, tmp.f[0], 0));\n    vu_set_vf_y(vu, d, vu_update_flags(vu, tmp.f[1], 1));\n    vu_set_vf_z(vu, d, vu_update_flags(vu, tmp.f[2], 2));\n\n    // printf(\"s(%d)=%08x %08x %08x t(%d)=%08x %08x %08x d=%08x %08x %08x dp=%08x %08x %08x\\n\",\n    //     s,\n    //     vu->vf[s].u32[0],\n    //     vu->vf[s].u32[1],\n    //     vu->vf[s].u32[2],\n    //     t,\n    //     vu->vf[t].u32[0],\n    //     vu->vf[t].u32[1],\n    //     vu->vf[t].u32[2],\n    //     vu->vf[d].u32[0],\n    //     vu->vf[d].u32[1],\n    //     vu->vf[d].u32[2],\n    //     dv.u32[0],\n    //     dv.u32[1],\n    //     dv.u32[2]\n    // );\n\n    vu_clear_flags(vu, 3);\n    vu_update_status(vu);\n}\nvoid vu_i_nop(struct vu_state* vu, const struct vu_instruction* ins) {\n    // No operation\n}\ntemplate <uint32_t di>\nvoid vu_i_ftoi0(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i)));\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_ftoi4(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.0625f)));\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_ftoi12(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.000244140625f)));\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_ftoi15(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.000030517578125f)));\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_itof0(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_set_vf(vu, t, i, (float)vu->vf[s].s32[i]);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_itof4(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.0625f));\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_itof12(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.000244140625f));\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_itof15(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.000030517578125f));\n        }\n    });\n}\nvoid vu_i_clip(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_UD_T;\n    int s = VU_UD_S;\n\n    vu->clip <<= 6;\n\n    float w = fabsf(vu_vf_w(vu, t));\n    float x = vu_vf_x(vu, s);\n    float y = vu_vf_y(vu, s);\n    float z = vu_vf_z(vu, s);\n\n    vu->clip |= (x > +w);\n    vu->clip |= (x < -w) << 1;\n    vu->clip |= (y > +w) << 2;\n    vu->clip |= (y < -w) << 3;\n    vu->clip |= (z > +w) << 4;\n    vu->clip |= (z < -w) << 5;\n    vu->clip &= 0xFFFFFF;\n}\n\n// Lower pipeline\nvoid vu_i_b(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->next_tpc = vu->tpc + VU_LD_IMM11;\n}\nvoid vu_i_bal(struct vu_state* vu, const struct vu_instruction* ins) {\n    // Instruction next to the delay slot\n    VU_IT = vu->tpc + 1;\n\n    vu->next_tpc = vu->tpc + VU_LD_IMM11;\n}\nvoid vu_i_div(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n    int s = VU_LD_S;\n    int tf = VU_LD_TF;\n    int sf = VU_LD_SF;\n\n    struct vu_reg32 q;\n\n    q.f = vu_vf_i(vu, s, sf) / vu_vf_i(vu, t, tf);\n    q.f = vu_cvtf(q.u32);\n\n    vu_set_q(vu, q.f, 7);\n}\nvoid vu_i_eatan(struct vu_state* vu, const struct vu_instruction* ins) {\n    float x = vu_vf_i(vu, VU_LD_S, VU_LD_SF);\n\n    if (x == -1.0f) {\n        vu->p.u32 = 0xFF7FFFFF;\n    } else {\n        x = (x - 1.0f) / (x + 1.0f);\n\n        vu->p.f = vu_atan(x);\n    }\n}\nvoid vu_i_eatanxy(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    float x = vu_vf_x(vu, s);\n    float y = vu_vf_y(vu, s);\n\n    if (y + x == 0.0f) {\n        vu->p.u32 = 0x7F7FFFFF | (vu->vf[s].u32[1] & 0x80000000);\n    } else {\n        x = (y - 1.0f) / (y + x);\n\n        vu->p.f = vu_atan(x);\n    }\n}\nvoid vu_i_eatanxz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    float x = vu_vf_x(vu, s);\n    float z = vu_vf_z(vu, s);\n\n    //P = atan(z/x)\n    if (z + x == 0.0f) {\n        vu->p.u32 = 0x7F7FFFFF | (vu->vf[s].u32[2] & 0x80000000);\n    } else {\n        x = (z - x) / (z + x);\n\n        vu->p.f = vu_atan(x);\n    }\n}\nvoid vu_i_eexp(struct vu_state* vu, const struct vu_instruction* ins) {\n    const static float coeffs[] = {\n        0.249998688697815f, 0.031257584691048f,\n        0.002591371303424f, 0.000171562001924f,\n        0.000005430199963f, 0.000000690600018f\n    };\n\n    int s = VU_LD_S;\n    int sf = VU_LD_SF;\n\n    if (vu->vf[s].u32[sf] & 0x80000000) {\n        vu->p.f = vu_vf_i(vu, s, sf);\n\n        return;\n    }\n\n    float value = 1;\n    float x = vu_vf_i(vu, s, sf);\n\n    for (int exp = 1; exp <= 6; exp++)\n        value += coeffs[exp - 1] * pow(x, exp);\n\n    vu->p.f = 1.0 / value;\n}\nvoid vu_i_eleng(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n\n    float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s);\n    float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s);\n    float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s);\n\n    vu->p.f = sqrtf(x2 + y2 + z2);\n}\nvoid vu_i_ercpr(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->p.f = 1.0f / vu_vf_i(vu, VU_LD_S, VU_LD_SF);\n}\nvoid vu_i_erleng(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n\n    float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s);\n    float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s);\n    float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s);\n\n    vu->p.f = 1.0f / sqrtf(x2 + y2 + z2);\n}\nvoid vu_i_ersadd(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n\n    float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s);\n    float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s);\n    float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s);\n\n    vu->p.f = 1.0f / (x2 + y2 + z2);\n}\nvoid vu_i_ersqrt(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->p.f = 1.0f / sqrtf(vu_vf_i(vu, VU_LD_S, VU_LD_SF));\n}\nvoid vu_i_esadd(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n\n    float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s);\n    float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s);\n    float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s);\n\n    vu->p.f = x2 + y2 + z2;\n}\nvoid vu_i_esin(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->p.f = sinf(vu_vf_i(vu, VU_LD_S, VU_LD_SF));\n}\nvoid vu_i_esqrt(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->p.f = sqrtf(vu_vf_i(vu, VU_LD_S, VU_LD_SF));\n}\nvoid vu_i_esum(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n\n    vu->p.f = vu_vf_x(vu, s) + vu_vf_y(vu, s) + vu_vf_z(vu, s) + vu_vf_w(vu, s);\n}\nvoid vu_i_fcand(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->vi[1] = ((vu->clip & 0xffffff) & VU_LD_IMM24) != 0;\n}\nvoid vu_i_fceq(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->vi[1] = (vu->clip & 0xffffff) == VU_LD_IMM24;\n}\nvoid vu_i_fcget(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    vu->vi[VU_LD_T] = vu->clip & 0xfff;\n}\nvoid vu_i_fcor(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->vi[1] = ((vu->clip & 0xffffff) | VU_LD_IMM24) == 0xffffff;\n}\nvoid vu_i_fcset(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->clip = VU_LD_IMM24;\n}\nvoid vu_i_fmand(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, vu->mac_pipeline[3] & VU_IS);\n}\nvoid vu_i_fmeq(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, (VU_IS & 0xffff) == (vu->mac_pipeline[3] & 0xffff));\n}\nvoid vu_i_fmor(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, (VU_IS & 0xffff) | (vu->mac_pipeline[3] & 0xffff));\n}\nvoid vu_i_fsand(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, vu->status & VU_LD_IMM12);\n}\nvoid vu_i_fseq(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, (vu->status & 0xfff) == VU_LD_IMM12);\n}\nvoid vu_i_fsor(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, (vu->status & 0xfff) | VU_LD_IMM12);\n}\nvoid vu_i_fsset(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->status &= 0x3f;\n    vu->status |= VU_LD_IMM12 & 0xfc0;\n}\nvoid vu_i_iadd(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_D);\n\n    vu_set_vi(vu, VU_LD_D, VU_IS + VU_IT);\n}\nvoid vu_i_iaddi(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_T);\n\n    vu_set_vi(vu, VU_LD_T, VU_IS + VU_LD_IMM5);\n}\nvoid vu_i_iaddiu(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_T);\n\n    vu_set_vi(vu, VU_LD_T, VU_IS + VU_LD_IMM15);\n}\nvoid vu_i_iand(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_T);\n\n    vu_set_vi(vu, VU_LD_D, VU_IS & VU_IT);\n}\nvoid vu_i_ibeq(struct vu_state* vu, const struct vu_instruction* ins) {\n    uint16_t t = vu_get_branch_register(vu, VU_LD_T);\n    uint16_t s = vu_get_branch_register(vu, VU_LD_S);\n\n    if (t == s) {\n        vu->next_tpc = vu->tpc + VU_LD_IMM11;\n    }\n}\nvoid vu_i_ibgez(struct vu_state* vu, const struct vu_instruction* ins) {\n    int16_t s = vu_get_branch_register(vu, VU_LD_S);\n\n    if (s >= 0) {\n        vu->next_tpc = vu->tpc + VU_LD_IMM11;\n    }\n}\nvoid vu_i_ibgtz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int16_t s = vu_get_branch_register(vu, VU_LD_S);\n\n    if (s > 0) {\n        vu->next_tpc = vu->tpc + VU_LD_IMM11;\n    }\n}\nvoid vu_i_iblez(struct vu_state* vu, const struct vu_instruction* ins) {\n    int16_t s = vu_get_branch_register(vu, VU_LD_S);\n\n    if (s <= 0) {\n        vu->next_tpc = vu->tpc + VU_LD_IMM11;\n    }\n}\nvoid vu_i_ibltz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int16_t s = vu_get_branch_register(vu, VU_LD_S);\n\n    if (s < 0) {\n        vu->next_tpc = vu->tpc + VU_LD_IMM11;\n    }\n}\nvoid vu_i_ibne(struct vu_state* vu, const struct vu_instruction* ins) {\n    uint16_t t = vu_get_branch_register(vu, VU_LD_T);\n    uint16_t s = vu_get_branch_register(vu, VU_LD_S);\n\n    // printf(\"ibne vi%02u (%04x), vi%02u (%04x), 0x%08x\\n\", VU_LD_T, t, VU_LD_S, s, vu->tpc + VU_LD_IMM11);\n\n    if (t != s) {\n        vu->next_tpc = vu->tpc + VU_LD_IMM11;\n    }\n}\ntemplate <uint32_t di>\nvoid vu_i_ilw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    uint32_t addr = VU_IS + VU_LD_IMM11;\n    uint128_t data = vu_mem_read(vu, addr);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vi[t] = data.u32[i];\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_ilwr(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    uint32_t addr = vu->vi[s];\n    uint128_t data = vu_mem_read(vu, addr);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vi[t] = data.u32[i];\n        }\n    });\n}\nvoid vu_i_ior(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_D);\n\n    vu_set_vi(vu, VU_LD_D, VU_IS | VU_IT);\n}\nvoid vu_i_isub(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_D);\n\n    vu_set_vi(vu, VU_LD_D, VU_IS - VU_IT);\n}\nvoid vu_i_isubiu(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_T);\n\n    vu_set_vi(vu, VU_LD_T, VU_IS - VU_LD_IMM15);\n}\ntemplate <uint32_t di>\nvoid vu_i_isw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    uint32_t addr = vu->vi[s] + VU_LD_IMM11;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_mem_write(vu, addr, vu->vi[t], i);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_iswr(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    uint32_t addr = vu->vi[s];\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_mem_write(vu, addr, vu->vi[t], i);\n        }\n    });\n}\nvoid vu_i_jalr(struct vu_state* vu, const struct vu_instruction* ins) {\n    uint16_t s = VU_IS;\n\n    VU_IT = vu->tpc + 1;\n\n    vu->next_tpc = s;\n}\nvoid vu_i_jr(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->next_tpc = VU_IS;\n}\ntemplate <uint32_t di>\nvoid vu_i_lq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    uint32_t addr = vu->vi[s] + VU_LD_IMM11;\n    uint128_t data = vu_mem_read(vu, addr);\n\n    if (!t) return;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[t].u32[i] = data.u32[i];\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_lqd(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    vu_write_branch_pipeline(vu, s);\n\n    vu_set_vi(vu, s, vu->vi[s] - 1);\n\n    uint32_t addr = vu->vi[s];\n    uint128_t data = vu_mem_read(vu, addr);\n\n    if (!t) return;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[t].u32[i] = data.u32[i];\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_lqi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    vu_write_branch_pipeline(vu, s);\n\n    if (t) {\n        uint32_t addr = vu->vi[s];\n        uint128_t data = vu_mem_read(vu, addr);\n\n        template_seq<4>([&](auto i) {\n            if constexpr (di & (VU_D_X >> i)) {\n                vu->vf[t].u32[i] = data.u32[i];\n            }\n        });\n    }\n\n    vu_set_vi(vu, s, vu->vi[s] + 1);\n}\ntemplate <uint32_t di>\nvoid vu_i_mfir(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[t].u32[i] = (int32_t)(int16_t)VU_IS;\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_mfp(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[t].u32[i] = vu->p.f;\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_move(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[t].u32[i] = vu->vf[s].u32[i];\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_mr32(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    int s = VU_LD_S;\n\n    uint32_t x = vu->vf[s].u32[0];\n\n    // template_seq<4>([&](auto i) {\n    //     if constexpr (di & (VU_D_X >> i)) {\n    //         vu->vf[t].u32[i] = vu->vf[s].u32[(i + 1) & 3];\n    //     }\n    // });\n\n    if constexpr (di & VU_D_X) {\n        vu->vf[t].u32[0] = vu->vf[s].u32[1];\n    }\n    if constexpr (di & VU_D_Y) {\n        vu->vf[t].u32[1] = vu->vf[s].u32[2];\n    }\n    if constexpr (di & VU_D_Z) {\n        vu->vf[t].u32[2] = vu->vf[s].u32[3];\n    }\n    if constexpr (di & VU_D_W) {\n        vu->vf[t].u32[3] = x;\n    }\n}\nvoid vu_i_mtir(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_T);\n\n    vu_set_vi(vu, VU_LD_T, vu->vf[VU_LD_S].u32[VU_LD_SF] & 0xffff);\n}\ntemplate <uint32_t di>\nvoid vu_i_rget(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[t].u32[i] = vu->r.u32;\n        }\n    });\n}\nvoid vu_i_rinit(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n\n    vu->r.u32 = 0x3f800000;\n\n    if (!s) return;\n\n    vu->r.u32 |= vu->vf[s].u32[VU_LD_SF] & 0x007fffff;\n}\ntemplate <uint32_t di>\nvoid vu_i_rnext(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    int x = (vu->r.u32 >> 4) & 1;\n    int y = (vu->r.u32 >> 22) & 1;\n\n    vu->r.u32 <<= 1;\n    vu->r.u32 ^= x ^ y;\n    vu->r.u32 = (vu->r.u32 & 0x7FFFFF) | 0x3F800000;\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu->vf[t].u32[i] = vu->r.u32;\n        }\n    });\n}\nvoid vu_i_rsqrt(struct vu_state* vu, const struct vu_instruction* ins) {\n    struct vu_reg32 q;\n\n    q.f = vu_vf_i(vu, VU_LD_S, VU_LD_SF) / sqrtf(vu_vf_i(vu, VU_LD_T, VU_LD_TF));\n    q.f = vu_cvtf(q.u32);\n\n    vu_set_q(vu, q.f, 13);\n}\nvoid vu_i_rxor(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->r.u32 = 0x3F800000 | ((vu->r.u32 ^ vu->vf[VU_LD_S].u32[VU_LD_SF]) & 0x007FFFFF);\n}\ntemplate <uint32_t di>\nvoid vu_i_sq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    uint32_t addr = vu->vi[t] + VU_LD_IMM11;\n\n    // printf(\"vu: sq addr=%08x vf%02d=%08x %08x %08x %08x\\n\", addr, s, vu->vf[s].u32[3], vu->vf[s].u32[2], vu->vf[s].u32[1], vu->vf[s].u32[0]);\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_mem_write(vu, addr, vu->vf[s].u32[i], i);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_sqd(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    vu_write_branch_pipeline(vu, t);\n\n    vu_set_vi(vu, t, vu->vi[t] - 1);\n\n    uint32_t addr = vu->vi[t];\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_mem_write(vu, addr, vu->vf[s].u32[i], i);\n        }\n    });\n}\ntemplate <uint32_t di>\nvoid vu_i_sqi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    vu_write_branch_pipeline(vu, t);\n\n    uint32_t addr = vu->vi[t];\n\n    template_seq<4>([&](auto i) {\n        if constexpr (di & (VU_D_X >> i)) {\n            vu_mem_write(vu, addr, vu->vf[s].u32[i], i);\n        }\n    });\n\n    vu_set_vi(vu, t, vu->vi[t] + 1);\n}\nvoid vu_i_sqrt(struct vu_state* vu, const struct vu_instruction* ins) {\n    struct vu_reg32 q;\n\n    q.f = sqrtf(vu_vf_i(vu, VU_LD_T, VU_LD_TF));\n    q.f = vu_cvtf(q.u32);\n\n    vu_set_q(vu, q.f, 7);\n}\nvoid vu_i_waitp(struct vu_state* vu, const struct vu_instruction* ins) {\n    // No operation\n}\nvoid vu_i_waitq(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->q_delay = 0;\n}\n\nvoid vu_i_xgkick(struct vu_state* vu, const struct vu_instruction* ins) {\n    // vu_xgkick(vu);\n    // vu->xgkick_pending = 3;\n    // vu->xgkick_addr = VU_IS;\n\n    // return;\n\n    uint16_t addr = VU_IS;\n\n    int eop = 1;\n\n    do {\n        uint128_t tag = vu_mem_read(vu, addr++);\n\n        if ((tag.u64[0] | tag.u64[1]) == 0)\n            break;\n\n        // addr &= 0x3ff;\n\n        // if (addr == 0) break;\n\n        // printf(\"tag: addr=%08x %08x %08x %08x %08x\\n\", addr - 1, tag.u32[3], tag.u32[2], tag.u32[1], tag.u32[0]);\n\n        ps2_gif_fifo_write(vu->gif, tag, GIF_PATH1);\n\n        eop = (tag.u64[0] & 0x8000) != 0;\n\n        int nloop = tag.u64[0] & 0x7fff;\n        int flg = (tag.u64[0] >> 58) & 3;\n        int nregs = (tag.u64[0] >> 60) & 0xf;\n\n        if (!nloop)\n            continue;\n\n        // if (!nregs)\n        //     nregs = 16;\n\n        int qwc = 0;\n\n        switch (flg) {\n            case 0: {\n                qwc = nregs * nloop;\n            } break;\n            case 1: {\n                qwc = (nregs * nloop + 1) / 2; // Round up for odd cases\n            } break;\n            case 2:\n            case 3: {\n                qwc = nloop;\n            } break;\n        }\n\n        if (qwc >= 0x400) {\n            fprintf(stderr, \"vu: Weird xgkick tag nloop=%d nregs=%d eop=%d flg=%d qwc=%d\\n\",\n                nloop,\n                nregs,\n                eop,\n                flg,\n                qwc\n            ); \n\n            exit(1);\n        }\n\n        for (int i = 0; i < qwc; i++) {\n            // printf(\"vu: %08x: %08x %08x %08x %08x\\n\",\n            //     addr,\n            //     vu->vu_mem[addr].u32[3],\n            //     vu->vu_mem[addr].u32[2],\n            //     vu->vu_mem[addr].u32[1],\n            //     vu->vu_mem[addr].u32[0]\n            // );\n\n            ps2_gif_fifo_write(vu->gif, vu_mem_read(vu, addr++), GIF_PATH1);\n\n            addr &= 0x3ff;\n\n            // if (addr == 0) {\n            //     eop = 1;\n            //     break;\n            // }\n        }\n    } while (!eop);\n}\nvoid vu_i_xitop(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, vu->vif->itop);\n}\nvoid vu_i_xtop(struct vu_state* vu, const struct vu_instruction* ins) {\n    if (vu->id == 0) {\n        printf(\"vu: xtop used in VU0\\n\");\n\n        // exit(1);\n    }\n\n    vu_set_vi(vu, VU_LD_T, vu->vif->top);\n}\n\nuint64_t ps2_vu_read8(struct vu_state* vu, uint32_t addr) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        return *(uint8_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]);\n    }\n\n    uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n    return *(uint8_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]);\n}\nuint64_t ps2_vu_read16(struct vu_state* vu, uint32_t addr) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        return *(uint16_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]);\n    }\n\n    uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n    return *(uint16_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]);\n}\nuint64_t ps2_vu_read32(struct vu_state* vu, uint32_t addr) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        return *(uint32_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]);\n    }\n\n    uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n    return *(uint32_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]);\n}\nuint64_t ps2_vu_read64(struct vu_state* vu, uint32_t addr) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        return *(uint64_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]);\n    }\n\n    uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n    return *(uint64_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]);\n}\nuint128_t ps2_vu_read128(struct vu_state* vu, uint32_t addr) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        return *(uint128_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]);\n    }\n\n    uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n    return *(uint128_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]);\n}\nvoid ps2_vu_write8(struct vu_state* vu, uint32_t addr, uint64_t data) {\n    if (addr <= 0x3FFF) {\n        vu_invalidate_range(vu, addr, 1);\n\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        *(uint8_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data;\n    } else {\n        uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n        *(uint8_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data;\n    }\n}\nvoid ps2_vu_write16(struct vu_state* vu, uint32_t addr, uint64_t data) {\n    if (addr <= 0x3FFF) {\n        vu_invalidate_range(vu, addr, 2);\n\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        *(uint16_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data;\n    } else {\n        uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n        *(uint16_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data;\n    }\n}\nvoid ps2_vu_write32(struct vu_state* vu, uint32_t addr, uint64_t data) {\n    if (addr <= 0x3FFF) {\n        vu_invalidate_range(vu, addr, 4);\n\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        *(uint32_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data;\n    } else {\n        uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n        *(uint32_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data;\n    }\n}\nvoid ps2_vu_write64(struct vu_state* vu, uint32_t addr, uint64_t data) {\n    if (addr <= 0x3FFF) {\n        vu_invalidate_range(vu, addr, 8);\n\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        *(uint64_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data;\n    } else {\n        uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n        *(uint64_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data;\n    }\n}\nvoid ps2_vu_write128(struct vu_state* vu, uint32_t addr, uint128_t data) {\n    if (addr <= 0x3FFF) {\n        vu_invalidate_range(vu, addr, 16);\n\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        *(uint128_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data;\n    } else {\n        uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n        *(uint128_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data;\n    }\n}\n\n#define VU_FLD_X 1\n#define VU_FLD_Y 2\n#define VU_FLD_Z 4\n#define VU_FLD_W 8\n\n#define VU_DEC_UD_S_SRC_T_BROADCAST(bc, f) \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = bc; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(bc, f) \\\n    vu->upper.dst.reg = vu->upper.ud_d; \\\n    vu->upper.dst.field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = vu->upper.dst.field; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = bc; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_D_DST_S_SRC_T_SRC(f) \\\n    vu->upper.dst.reg = vu->upper.ud_d; \\\n    vu->upper.dst.field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = vu->upper.dst.field; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = vu->upper.dst.field; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_D_DST_S_SRC(f) \\\n    vu->upper.dst.reg = vu->upper.ud_d; \\\n    vu->upper.dst.field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = vu->upper.dst.field; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_D_DST_S_SRC_Q_SRC(f) \\\n    vu->upper.dst.reg = vu->upper.ud_d; \\\n    vu->upper.dst.field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = vu->upper.dst.field; \\\n    vu->upper.src[1].reg = VU_REG_Q; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_T_DST_S_SRC(f) \\\n    vu->upper.dst.reg = vu->upper.ud_t; \\\n    vu->upper.dst.field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = vu->upper.dst.field; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_S_SRC_T_SRC(f) \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = vu->upper.src[0].field; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_S_SRC(f) \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = (opcode >> 21) & 0xf; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_S_SRC_Q_SRC(f) \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[1].reg = VU_REG_Q; \\\n    vu->upper.func = f;\n\n#define VU_DEC_OPMULA() \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = vu->upper.src[0].field; \\\n    vu->upper.func = vu_i_opmula;\n\n#define VU_DEC_OPMSUB() \\\n    vu->upper.dst.reg = vu->upper.ud_d; \\\n    vu->upper.dst.field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = vu->upper.dst.field; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = vu->upper.dst.field; \\\n    vu->upper.func = vu_i_opmsub;\n\n#define VU_DEC_CLIP() \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = VU_FLD_W; \\\n    vu->upper.func = vu_i_clip;\n\n#define VU_DEC_LD_NONE(f) \\\n    vu->lower.func = f;\n\n#define VU_DEC_UD_NONE(f) \\\n    vu->upper.func = f;\n\n#define VU_DEC_LD_T_DST_S_VISRC(f) \\\n    vu->lower.dst.reg = vu->lower.ld_t; \\\n    vu->lower.dst.field = (opcode >> 21) & 0xf; \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_DST_S_VISRC_S_VIDST(f) \\\n    vu->lower.dst.reg = vu->lower.ld_t; \\\n    vu->lower.dst.field = (opcode >> 21) & 0xf; \\\n    vu->lower.vi_dst = vu->lower.ld_s; \\\n    vu->lower.vi_src[0] = vu->lower.vi_dst; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(f) \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = (opcode >> 21) & 0xf; \\\n    vu->lower.vi_dst = vu->lower.ld_t; \\\n    vu->lower.vi_src[0] = vu->lower.vi_dst; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_SF_SRC_T_TF_SRC(f) \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = vu->lower.ld_sf; \\\n    vu->lower.src[1].reg = vu->lower.ld_t; \\\n    vu->lower.src[1].field = vu->lower.ld_tf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_Q_DST_S_SF_SRC_T_TF_SRC(f) \\\n    vu->lower.dst.reg = VU_REG_Q; \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = vu->lower.ld_sf; \\\n    vu->lower.src[1].reg = vu->lower.ld_t; \\\n    vu->lower.src[1].field = vu->lower.ld_tf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_VIDST_S_SF_SRC(f) \\\n    vu->lower.vi_dst = vu->lower.ld_t; \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = vu->lower.ld_sf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_DST_S_VISRC(f) \\\n    vu->lower.dst.reg = vu->lower.ld_t; \\\n    vu->lower.dst.field = (opcode >> 21) & 0xf; \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_TF_SRC(f) \\\n    vu->lower.src[0].reg = vu->lower.ld_t; \\\n    vu->lower.src[0].field = vu->lower.ld_tf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_Q_DST_T_TF_SRC(f) \\\n    vu->lower.dst.reg = VU_REG_Q; \\\n    vu->lower.src[0].reg = vu->lower.ld_t; \\\n    vu->lower.src[0].field = vu->lower.ld_tf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_SRC_T_VISRC(f) \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = (opcode >> 21) & 0xf; \\\n    vu->lower.vi_src[0] = vu->lower.ld_t; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_VIDST_S_VISRC(f) \\\n    vu->lower.vi_dst = vu->lower.ld_t; \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_VISRC_T_VISRC(f) \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.vi_src[1] = vu->lower.ld_t; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_VIDST_S_VISRC(f) \\\n    vu->lower.vi_dst = vu->lower.ld_t; \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_VISRC_S_VISRC(f) \\\n    vu->lower.vi_src[0] = vu->lower.ld_t; \\\n    vu->lower.vi_src[1] = vu->lower.ld_s; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_VIDST(v, f) \\\n    vu->lower.vi_dst = v; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_VIDST(f) \\\n    vu->lower.vi_dst = vu->lower.ld_t; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_VISRC(f) \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_FLD_SRC(fld, f) \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = fld; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(f) \\\n    vu->lower.vi_dst = vu->lower.ld_d; \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.vi_src[1] = vu->lower.ld_t; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_DST_S_SRC(f) \\\n    vu->lower.dst.reg = vu->lower.ld_t; \\\n    vu->lower.dst.field = (opcode >> 21) & 0xf; \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = vu->lower.dst.field; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_DST(f) \\\n    vu->lower.dst.reg = vu->lower.ld_t; \\\n    vu->lower.dst.field = (opcode >> 21) & 0xf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_SF_SRC(f) \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = vu->lower.ld_sf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_MR32(f) \\\n    vu->lower.dst.reg = vu->lower.ld_t; \\\n    vu->lower.dst.field = (opcode >> 21) & 0xf; \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = (vu->lower.dst.field >> 1) | ((vu->lower.dst.field & 1) << 3); \\\n    vu->lower.func = f;\n\n#define GET_TEMPLATE_FN(i) \\\n    [&](uint32_t opcode) { \\\n        switch ((opcode >> 21) & 0xf) { \\\n            case 0: return &i<0>; \\\n            case 1: return &i<VU_D_W>; \\\n            case 2: return &i<VU_D_Z>; \\\n            case 3: return &i<VU_D_Z | VU_D_W>; \\\n            case 4: return &i<VU_D_Y>; \\\n            case 5: return &i<VU_D_Y | VU_D_W>; \\\n            case 6: return &i<VU_D_Y | VU_D_Z>; \\\n            case 7: return &i<VU_D_Y | VU_D_Z | VU_D_W>; \\\n            case 8: return &i<VU_D_X>; \\\n            case 9: return &i<VU_D_X | VU_D_W>; \\\n            case 10: return &i<VU_D_X | VU_D_Z>; \\\n            case 11: return &i<VU_D_X | VU_D_Z | VU_D_W>; \\\n            case 12: return &i<VU_D_X | VU_D_Y>; \\\n            case 13: return &i<VU_D_X | VU_D_Y | VU_D_W>; \\\n            case 14: return &i<VU_D_X | VU_D_Y | VU_D_Z>; \\\n            case 15: return &i<VU_D_X | VU_D_Y | VU_D_Z | VU_D_W>; \\\n            default: __builtin_unreachable(); \\\n        } \\\n    __builtin_unreachable(); }(opcode)\n\nvoid vu_decode_upper(struct vu_state* vu, uint32_t opcode) {\n    vu->upper.opcode = opcode;\n    vu->upper.ud_d = (opcode >> 6) & 0x1f;\n    vu->upper.ud_s = (opcode >> 11) & 0x1f;\n    vu->upper.ud_t = (opcode >> 16) & 0x1f;\n\n    for (int i = 0; i < 4; i++)\n        vu->upper.ud_di[i] = opcode & (1 << (24 - i));\n\n    vu->upper.func = NULL;\n    vu->upper.dst.reg = 0;\n    vu->upper.dst.field = 0;\n    vu->upper.src[0].reg = 0;\n    vu->upper.src[0].field = 0;\n    vu->upper.src[1].reg = 0;\n    vu->upper.src[1].field = 0;\n\n    // Decode 000007FF style instruction\n    if ((opcode & 0x3c) == 0x3c) {\n        // 0EEEE 1111 EE\n        // -0EE EE11 11EE\n        // --------------\n        // bit 10 is always 0\n        // bits 2-5 are always 1\n        // --------------\n        // bits 0-1 and bits 6-9 (6 bits) are enough to decode\n        // all of the following\n        switch (((opcode & 0x3c0) >> 4) | (opcode & 3)) {\n            case 0x00: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_addax)); return;\n            case 0x01: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_adday)); return;\n            case 0x02: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_addaz)); return;\n            case 0x03: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_addaw)); return;\n            case 0x04: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_subax)); return;\n            case 0x05: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_subay)); return;\n            case 0x06: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_subaz)); return;\n            case 0x07: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_subaw)); return;\n            case 0x08: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_maddax)); return;\n            case 0x09: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_madday)); return;\n            case 0x0A: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_maddaz)); return;\n            case 0x0B: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_maddaw)); return;\n            case 0x0C: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_msubax)); return;\n            case 0x0D: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_msubay)); return;\n            case 0x0E: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_msubaz)); return;\n            case 0x0F: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_msubaw)); return;\n            case 0x10: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_itof0)); return;\n            case 0x11: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_itof4)); return;\n            case 0x12: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_itof12)); return;\n            case 0x13: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_itof15)); return;\n            case 0x14: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_ftoi0)); return;\n            case 0x15: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_ftoi4)); return;\n            case 0x16: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_ftoi12)); return;\n            case 0x17: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_ftoi15)); return;\n            case 0x18: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_mulax)); return;\n            case 0x19: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_mulay)); return;\n            case 0x1A: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_mulaz)); return;\n            case 0x1B: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_mulaw)); return;\n            case 0x1C: VU_DEC_UD_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_mulaq)); return;\n            case 0x1D: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_abs)); return;\n            case 0x1E: VU_DEC_UD_S_SRC(GET_TEMPLATE_FN(vu_i_mulai)); return;\n            case 0x1F: VU_DEC_CLIP(); return;\n            case 0x20: VU_DEC_UD_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_addaq)); return;\n            case 0x21: VU_DEC_UD_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_maddaq)); return;\n            case 0x22: VU_DEC_UD_S_SRC(GET_TEMPLATE_FN(vu_i_addai)); return;\n            case 0x23: VU_DEC_UD_S_SRC(GET_TEMPLATE_FN(vu_i_maddai)); return;\n            case 0x24: VU_DEC_UD_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_subaq)); return;\n            case 0x25: VU_DEC_UD_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_msubaq)); return;\n            case 0x26: VU_DEC_UD_S_SRC(GET_TEMPLATE_FN(vu_i_subai)); return;\n            case 0x27: VU_DEC_UD_S_SRC(GET_TEMPLATE_FN(vu_i_msubai)); return;\n            case 0x28: VU_DEC_UD_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_adda)); return;\n            case 0x29: VU_DEC_UD_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_madda)); return;\n            case 0x2A: VU_DEC_UD_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_mula)); return;\n            case 0x2C: VU_DEC_UD_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_suba)); return;\n            case 0x2D: VU_DEC_UD_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_msuba)); return;\n            case 0x2E: VU_DEC_OPMULA(); return;\n            case 0x2F: VU_DEC_UD_NONE(vu_i_nop); return;\n        }\n    } else {\n        // Decode 0000003F style instruction\n        switch (opcode & 0x3f) {\n            case 0x00: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_addx)); return;\n            case 0x01: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_addy)); return;\n            case 0x02: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_addz)); return;\n            case 0x03: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_addw)); return;\n            case 0x04: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_subx)); return;\n            case 0x05: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_suby)); return;\n            case 0x06: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_subz)); return;\n            case 0x07: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_subw)); return;\n            case 0x08: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_maddx)); return;\n            case 0x09: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_maddy)); return;\n            case 0x0A: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_maddz)); return;\n            case 0x0B: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_maddw)); return;\n            case 0x0C: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_msubx)); return;\n            case 0x0D: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_msuby)); return;\n            case 0x0E: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_msubz)); return;\n            case 0x0F: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_msubw)); return;\n            case 0x10: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_maxx)); return;\n            case 0x11: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_maxy)); return;\n            case 0x12: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_maxz)); return;\n            case 0x13: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_maxw)); return;\n            case 0x14: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_minix)); return;\n            case 0x15: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_miniy)); return;\n            case 0x16: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_miniz)); return;\n            case 0x17: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_miniw)); return;\n            case 0x18: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_mulx)); return;\n            case 0x19: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_muly)); return;\n            case 0x1A: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_mulz)); return;\n            case 0x1B: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_mulw)); return;\n            case 0x1C: VU_DEC_UD_D_DST_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_mulq)); return;\n            case 0x1D: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_maxi)); return;\n            case 0x1E: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_muli)); return;\n            case 0x1F: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_minii)); return;\n            case 0x20: VU_DEC_UD_D_DST_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_addq)); return;\n            case 0x21: VU_DEC_UD_D_DST_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_maddq)); return;\n            case 0x22: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_addi)); return;\n            case 0x23: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_maddi)); return;\n            case 0x24: VU_DEC_UD_D_DST_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_subq)); return;\n            case 0x25: VU_DEC_UD_D_DST_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_msubq)); return;\n            case 0x26: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_subi)); return;\n            case 0x27: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_msubi)); return;\n            case 0x28: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_add)); return;\n            case 0x29: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_madd)); return;\n            case 0x2A: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_mul)); return;\n            case 0x2B: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_max)); return;\n            case 0x2C: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_sub)); return;\n            case 0x2D: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_msub)); return;\n            case 0x2E: VU_DEC_OPMSUB(); return;\n            case 0x2F: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_mini)); return;\n        }\n    }\n}\n\nvoid vu_decode_lower(struct vu_state* vu, uint32_t opcode) {\n    vu->lower.opcode = opcode;\n    vu->lower.ld_d = (opcode >> 6) & 0x1f;\n    vu->lower.ld_s = (opcode >> 11) & 0x1f;\n    vu->lower.ld_t = (opcode >> 16) & 0x1f;\n    vu->lower.ld_sf = (opcode >> 21) & 3;\n    vu->lower.ld_tf = (opcode >> 23) & 3;\n    vu->lower.ld_imm5 = ((int32_t)(((opcode >> 6) & 0x1f) << 27)) >> 27;\n    vu->lower.ld_imm11 = ((int32_t)((opcode & 0x7ff) << 21)) >> 21;\n    vu->lower.ld_imm12 = (((opcode >> 21) & 1) << 11) | (opcode & 0x7ff);\n    vu->lower.ld_imm15 = (opcode & 0x7ff) | ((opcode & 0x1e00000) >> 10);\n    vu->lower.ld_imm24 = opcode & 0xffffff;\n\n    for (int i = 0; i < 4; i++)\n        vu->lower.ld_di[i] = opcode & (1 << (24 - i));\n\n    vu->lower.func = NULL;\n    vu->lower.dst.reg = 0;\n    vu->lower.dst.field = 0;\n    vu->lower.src[0].reg = 0;\n    vu->lower.src[0].field = 0;\n    vu->lower.src[1].reg = 0;\n    vu->lower.src[1].field = 0;\n    vu->lower.vi_src[0] = 0;\n    vu->lower.vi_src[1] = 0;\n    vu->lower.vi_dst = 0;\n    vu->lower.branch = 0;\n\n    switch ((opcode & 0xFE000000) >> 25) {\n        case 0x00: VU_DEC_LD_T_DST_S_VISRC(GET_TEMPLATE_FN(vu_i_lq)); return;\n        case 0x01: VU_DEC_LD_S_SRC_T_VISRC(GET_TEMPLATE_FN(vu_i_sq)); return;\n        case 0x04: VU_DEC_LD_T_VIDST_S_VISRC(GET_TEMPLATE_FN(vu_i_ilw)); return;\n        case 0x05: VU_DEC_LD_S_VISRC_T_VISRC(GET_TEMPLATE_FN(vu_i_isw)); return;\n        case 0x08: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_iaddiu); return;\n        case 0x09: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_isubiu); return;\n\n        // Note: The flag check instructions clobber the destination register\n        //       \"immediately\", this means we don't actually need to generate\n        //       a dependency.\n        case 0x10: VU_DEC_LD_NONE(vu_i_fceq); return; // VU_DEC_LD_VIDST(1, vu_i_fceq); return;\n        case 0x11: VU_DEC_LD_NONE(vu_i_fcset); return;\n        case 0x12: VU_DEC_LD_NONE(vu_i_fcand); return; // VU_DEC_LD_VIDST(1, vu_i_fcand); return;\n        case 0x13: VU_DEC_LD_NONE(vu_i_fcor); return; // VU_DEC_LD_VIDST(1, vu_i_fcor); return;\n        case 0x14: VU_DEC_LD_NONE(vu_i_fseq); return; // VU_DEC_LD_T_VIDST(vu_i_fseq); return;\n        case 0x15: VU_DEC_LD_NONE(vu_i_fsset); return;\n        case 0x16: VU_DEC_LD_NONE(vu_i_fsand); return; // VU_DEC_LD_T_VIDST(vu_i_fsand); return;\n        case 0x17: VU_DEC_LD_NONE(vu_i_fsor); return; // VU_DEC_LD_T_VIDST(vu_i_fsor); return;\n        case 0x18: VU_DEC_LD_S_VISRC(vu_i_fmeq); return; // VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmeq); return;\n        case 0x1A: VU_DEC_LD_S_VISRC(vu_i_fmand); return; // VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmand); return;\n        case 0x1B: VU_DEC_LD_S_VISRC(vu_i_fmor); return; // VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmor); return;\n        case 0x1C: VU_DEC_LD_NONE(vu_i_fcget); return; // VU_DEC_LD_T_VIDST(vu_i_fcget); return;\n        case 0x20: vu->lower.branch = 1; VU_DEC_LD_NONE(vu_i_b); return;\n        case 0x21: vu->lower.branch = 1; VU_DEC_LD_T_VIDST(vu_i_bal); return;\n        case 0x24: vu->lower.branch = 1; VU_DEC_LD_S_VISRC(vu_i_jr); return;\n        case 0x25: vu->lower.branch = 1; VU_DEC_LD_T_VIDST_S_VISRC(vu_i_jalr); return;\n        case 0x28: vu->lower.branch = 1; VU_DEC_LD_S_VISRC_T_VISRC(vu_i_ibeq); return;\n        case 0x29: vu->lower.branch = 1; VU_DEC_LD_S_VISRC_T_VISRC(vu_i_ibne); return;\n        case 0x2C: vu->lower.branch = 1; VU_DEC_LD_S_VISRC(vu_i_ibltz); return;\n        case 0x2D: vu->lower.branch = 1; VU_DEC_LD_S_VISRC(vu_i_ibgtz); return;\n        case 0x2E: vu->lower.branch = 1; VU_DEC_LD_S_VISRC(vu_i_iblez); return;\n        case 0x2F: vu->lower.branch = 1; VU_DEC_LD_S_VISRC(vu_i_ibgez); return;\n        case 0x40: {\n            if ((opcode & 0x3C) == 0x3C) {\n                switch (((opcode & 0x7C0) >> 4) | (opcode & 3)) {\n                    case 0x30: VU_DEC_LD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_move)); return;\n                    case 0x31: VU_DEC_MR32(GET_TEMPLATE_FN(vu_i_mr32)); return;\n                    case 0x34: VU_DEC_LD_T_DST_S_VISRC_S_VIDST(GET_TEMPLATE_FN(vu_i_lqi)); return;\n                    case 0x35: VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(GET_TEMPLATE_FN(vu_i_sqi)); return;\n                    case 0x36: VU_DEC_LD_T_DST_S_VISRC_S_VIDST(GET_TEMPLATE_FN(vu_i_lqd)); return;\n                    case 0x37: VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(GET_TEMPLATE_FN(vu_i_sqd)); return;\n                    case 0x38: VU_DEC_LD_Q_DST_S_SF_SRC_T_TF_SRC(vu_i_div); return;\n                    case 0x39: VU_DEC_LD_Q_DST_T_TF_SRC(vu_i_sqrt); return;\n                    case 0x3A: VU_DEC_LD_Q_DST_S_SF_SRC_T_TF_SRC(vu_i_rsqrt); return;\n                    case 0x3B: VU_DEC_LD_NONE(vu_i_waitq); return;\n                    case 0x3C: VU_DEC_LD_T_VIDST_S_SF_SRC(vu_i_mtir); return;\n                    case 0x3D: VU_DEC_LD_T_DST_S_VISRC(GET_TEMPLATE_FN(vu_i_mfir)); return;\n                    case 0x3E: VU_DEC_LD_T_VIDST_S_VISRC(GET_TEMPLATE_FN(vu_i_ilwr)); return;\n                    case 0x3F: VU_DEC_LD_T_VISRC_S_VISRC(GET_TEMPLATE_FN(vu_i_iswr)); return;\n                    case 0x40: VU_DEC_LD_T_DST(GET_TEMPLATE_FN(vu_i_rnext)); return;\n                    case 0x41: VU_DEC_LD_T_DST(GET_TEMPLATE_FN(vu_i_rget)); return;\n                    case 0x42: VU_DEC_LD_S_SF_SRC(vu_i_rinit); return;\n                    case 0x43: VU_DEC_LD_S_SF_SRC(vu_i_rxor); return;\n                    case 0x64: VU_DEC_LD_T_DST(GET_TEMPLATE_FN(vu_i_mfp)); return;\n                    case 0x68: VU_DEC_LD_T_VIDST(vu_i_xtop); return;\n                    case 0x69: VU_DEC_LD_T_VIDST(vu_i_xitop); return;\n                    case 0x6C: VU_DEC_LD_S_VISRC(vu_i_xgkick); return;\n                    case 0x70: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_esadd); return;\n                    case 0x71: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_ersadd); return;\n                    case 0x72: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_eleng); return;\n                    case 0x73: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_erleng); return;\n                    case 0x74: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y, vu_i_eatanxy); return;\n                    case 0x75: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Z, vu_i_eatanxz); return;\n                    case 0x76: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z | VU_FLD_W, vu_i_esum); return;\n                    case 0x78: VU_DEC_LD_S_SF_SRC(vu_i_esqrt); return;\n                    case 0x79: VU_DEC_LD_S_SF_SRC(vu_i_ersqrt); return;\n                    case 0x7A: VU_DEC_LD_S_SF_SRC(vu_i_ercpr); return;\n                    case 0x7B: VU_DEC_LD_NONE(vu_i_waitp); return;\n                    case 0x7C: VU_DEC_LD_S_SF_SRC(vu_i_esin); return;\n                    case 0x7D: VU_DEC_LD_S_SF_SRC(vu_i_eatan); return;\n                    case 0x7E: VU_DEC_LD_S_SF_SRC(vu_i_eexp); return;\n                }\n            } else {\n                switch (opcode & 0x3F) {\n                    case 0x30: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_iadd); return;\n                    case 0x31: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_isub); return;\n                    case 0x32: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_iaddi); return;\n                    case 0x34: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_iand); return;\n                    case 0x35: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_ior); return;\n                }\n            }\n        } break;\n    }\n}\n\nstatic inline void vu_advance_fmac_pipeline(struct vu_state* vu) {\n    vu->upper_pipeline[3] = vu->upper_pipeline[2];\n    vu->upper_pipeline[2] = vu->upper_pipeline[1];\n    vu->upper_pipeline[1] = vu->upper_pipeline[0];\n    vu->upper_pipeline[0].dst.reg = vu->upper.dst.reg;\n    vu->upper_pipeline[0].dst.field = vu->upper.dst.field;\n    vu->lower_pipeline[3] = vu->lower_pipeline[2];\n    vu->lower_pipeline[2] = vu->lower_pipeline[1];\n    vu->lower_pipeline[1] = vu->lower_pipeline[0];\n    vu->lower_pipeline[0].dst.reg = vu->lower.dst.reg;\n    vu->lower_pipeline[0].dst.field = vu->lower.dst.field;\n}\n\nstatic inline int vu_get_fmac_stall_cycles(struct vu_state* vu) {\n    for (int i = 0; i < 0x4; i++) {\n        for (int j = 0; j < 2; j++) {\n            if (vu->upper.src[j].reg == vu->lower_pipeline[i].dst.reg) {\n                if (vu->upper.src[j].field & vu->lower_pipeline[i].dst.field) {\n                    return 4 - i;\n                }\n            }\n        }\n    }\n\n    return 0;\n}\n\nvu_block* vu_find_block(struct vu_state* vu, uint32_t tpc) {\n    if (tpc == vu->last_block_lookup_tpc) {\n        return vu->last_block_ptr;\n    }\n\n    vu_block* block = &vu->block_cache[tpc];\n\n    if (!block->cycles) {\n        return nullptr;\n    }\n\n    // Update cache for next lookup\n    vu->last_block_lookup_tpc = tpc;\n    vu->last_block_ptr = block;\n\n    return block;\n}\n\nstatic int c = 0;\n\nvu_block* vu_cache_block(struct vu_state* vu, uint32_t tpc, int max_cycles) {\n    vu_block* block = &vu->block_cache[tpc];\n\n    vu->block_cache_size++;\n\n    block->tpc = tpc;\n    block->cycles = 0;\n    block->entries.clear();\n\n    // printf(\"vu: caching block at %04x\\n\", tpc);\n\n    for (int i = 0; i < max_cycles; i++) {\n        vu_block_entry entry = { 0 };\n\n        uint64_t liw = vu->micro_mem[tpc++ & 0x7ff];\n        uint32_t upper = liw >> 32;\n        uint32_t lower = liw & 0xffffffff;\n\n        // LOI consumes the raw lower word when the i-bit is set.\n        entry.lower.opcode = lower;\n\n        entry.i_bit = (upper & 0x80000000) != 0;\n        entry.e_bit = (upper & 0x40000000) != 0;\n\n        vu_decode_upper(vu, upper & 0x7ffffff);\n\n        entry.upper = vu->upper;\n\n        if (!entry.i_bit) {\n            vu_decode_lower(vu, lower);\n\n            entry.lower = vu->lower;\n            entry.branch = vu->lower.branch;\n            entry.hazard0 = vu->upper.dst.reg == vu->lower.src[0].reg;\n            entry.hazard1 = vu->upper.dst.reg == vu->lower.src[1].reg;\n            entry.hazard2 = vu->upper.dst.reg == vu->lower.dst.reg;\n            entry.hazard3 = vu->lower.dst.reg == VU_REG_Q;\n        }\n\n        // If this entry is a branch or has the E bit set, we end the block here\n        if (entry.branch || entry.e_bit) {\n            i = max_cycles - 2;\n        }\n\n        block->cycles++;\n\n        block->entries.push_back(entry);\n    }\n\n    // vu_dis_state ds;\n\n    // ds.addr = block->tpc;\n    // ds.print_address = 0;\n    // ds.print_opcode = 0;\n\n    // for (const vu_block_entry& entry : block->entries) {\n    //     char upper_buf[512];\n    //     char lower_buf[512];\n\n    //     printf(\" %s %04x: %08x %08x %-40s %s\\n\",\n    //         entry.i_bit ? \"I\" : entry.e_bit ? \"E\" : \" \",\n    //         ds.addr++,\n    //         entry.upper.opcode,\n    //         entry.lower.opcode,\n    //         vu_disassemble_upper(upper_buf, entry.upper.opcode, &ds),\n    //         vu_disassemble_lower(lower_buf, entry.lower.opcode, &ds)\n    //     );\n    // }\n\n    // Prime fast lookup with a pointer known to be valid after this insertion.\n    vu->last_block_lookup_tpc = block->tpc;\n    vu->last_block_ptr = block;\n\n    return block;\n}\n\nbool vu_execute_block_entry(struct vu_state* vu, const vu_block_entry& entry) {\n    if (vu->q_delay)\n        vu->q_delay--;\n\n    vu_update_status(vu);\n\n    if (entry.i_bit) {\n        entry.upper.func(vu, &entry.upper);\n\n        // LOI\n        vu->i.u32 = entry.lower.opcode;\n    } else {\n        if (entry.hazard3 && vu->q_delay) vu->q_delay = 0;\n\n        bool waitq = entry.lower.func == vu_i_waitq;\n\n        if (!entry.upper.dst.reg) {\n            entry.upper.func(vu, &entry.upper);\n            entry.lower.func(vu, &entry.lower);\n        } else if (entry.hazard0 || entry.hazard1 || waitq) {\n            // Upper instruction writes to a register that the lower\n            // instruction reads from. In this case the lower instruction\n            // gets the previous value of the register, executing the lower\n            // instruction first does the trick.\n\n            // We also execute WAITQ first, since it will stall the pipeline\n            // if the upper instruction reads Q\n\n            entry.lower.func(vu, &entry.lower);\n            entry.upper.func(vu, &entry.upper);\n        } else if (entry.hazard2) {\n            // Upper and lower instructions write to the same register.\n            // In this case the upper instruction takes priority, so we\n            // restore the value of the register after executing the lower\n            // instruction.\n\n            entry.upper.func(vu, &entry.upper);\n\n            struct vu_reg128 tmp = vu->vf[entry.upper.dst.reg];\n\n            entry.lower.func(vu, &entry.lower);\n\n            vu->vf[entry.upper.dst.reg] = tmp;\n        } else {\n            entry.upper.func(vu, &entry.upper);\n            entry.lower.func(vu, &entry.lower);\n        }\n    }\n\n    // vu_advance_fmac_pipeline(vu);\n\n    vu->mac_pipeline[3] = vu->mac_pipeline[2];\n    vu->mac_pipeline[2] = vu->mac_pipeline[1];\n    vu->mac_pipeline[1] = vu->mac_pipeline[0];\n    vu->mac_pipeline[0] = vu->mac;\n    \n    vu->clip_pipeline[3] = vu->clip_pipeline[2];\n    vu->clip_pipeline[2] = vu->clip_pipeline[1];\n    vu->clip_pipeline[1] = vu->clip_pipeline[0];\n    vu->clip_pipeline[0] = vu->clip;\n\n    if (vu->vi_backup_cycles) {\n        vu->vi_backup_cycles--;\n\n        if (!vu->vi_backup_cycles) {\n            vu->vi_backup_reg = 0;\n            vu->vi_backup_value = 0;\n        }\n    }\n\n    return entry.e_bit;\n}\n\nbool vu_execute_block(struct vu_state* vu, vu_block* block) {\n    bool e_bit = false;\n\n    // printf(\"vu: Input TPC %04x\\n\", vu->tpc);\n\n    for (const vu_block_entry& entry : block->entries) {\n        vu->tpc = vu->next_tpc;\n        vu->next_tpc = vu->tpc + 1;\n        vu->tpc &= 0x7ff;\n        vu->next_tpc &= 0x7ff;\n\n        e_bit |= vu_execute_block_entry(vu, entry);\n    }\n\n    // printf(\"vu: Output TPC %04x\\n\", vu->tpc);\n\n    return e_bit;\n}\n\nvoid vu_execute_program(struct vu_state* vu, uint32_t addr) {\n    vu->tpc = addr;\n    vu->next_tpc = addr + 1;\n    vu->i_bit = 0;\n    vu->e_bit = 0;\n\n    while (true) {\n        vu_block* block = vu_find_block(vu, vu->tpc);\n\n        if (!block) {\n            vu->cache_misses++;\n\n            block = vu_cache_block(vu, vu->tpc, 64);\n        } else {\n            vu->cache_hits++;\n        }\n\n        if (vu_execute_block(vu, block))\n            break;\n    }\n}\n\nvoid ps2_vu_write_vi(struct vu_state* vu, int index, uint32_t value) {\n    switch (index) {\n        case 0: return;\n        case 1: case 2: case 3:\n        case 4: case 5: case 6: case 7:\n        case 8: case 9: case 10: case 11:\n        case 12: case 13: case 14: case 15: {\n            vu->vi[index] = value & 0xffff;\n        } break;\n\n        case 16: {\n            vu->status &= ~0xfc0;\n            vu->status |= value & 0xfc0;\n        } break;\n\n        case 17: return; // MAC flag register, read-only\n        case 18: {\n            vu->clip = value & 0xffffff;\n        } break;\n        \n        case 19: return; // VU revision register? read-only\n\n        case 20: {\n            vu->r.u32 = value & 0x7fffff;\n        } break;\n        case 21: {\n            vu->i.u32 = value;\n        } break;\n        case 22: {\n            vu->q.u32 = value;\n        } break;\n        case 23: return;\n        case 24: {\n            vu->cr[8] = value & 0xc0c;\n        } break;\n        case 25: return;\n        case 26: return; // VU TPC register, read-only\n        case 27: {\n            vu->cmsar0 = value & 0xffff;\n        } break;\n        case 28: {\n            // To-do: Handle FBRST\n            vu->fbrst = value & 0xc0c;\n\n            if (value & 2) {\n                // Reset VU0\n                ps2_vu_reset(vu);\n            }\n\n            if (value & 0x200) {\n                // Reset VU1\n                ps2_vu_reset(vu->vu1);\n            }\n        } break;\n        case 29: return; // VU VPU-STAT register, read-only\n        case 30: return; // VU reserved register, read-only\n        case 31: {\n            vu->cmsar1 = value & 0xffff;\n\n            vu_execute_program(vu->vu1, vu->cmsar1 >> 3);\n        } break;\n    }\n}\n\nuint32_t ps2_vu_read_vi(struct vu_state* vu, int index) {\n    switch (index) {\n        case 0: case 1: case 2: case 3:\n        case 4: case 5: case 6: case 7:\n        case 8: case 9: case 10: case 11:\n        case 12: case 13: case 14: case 15: {\n            return vu->vi[index];\n        } break;\n\n        case 19: { // VU revision register\n            return 0x2e30;\n        } break;\n\n        default: {\n            return vu->cr[index - 16];\n        } break;\n    }\n}\n\nvoid ps2_vu_reset(struct vu_state* vu) {\n    for (int i = 0; i < 16; i++)\n        vu->vi[i] = 0;\n\n    for (int i = 0; i < 32; i++) {\n        vu->vf[i].u32[0] = 0;\n        vu->vf[i].u32[1] = 0;\n        vu->vf[i].u32[2] = 0;\n        vu->vf[i].u32[3] = 0;\n    }\n\n    vu->r.u32 = 0x3f800000;\n    vu->i.u32 = 0;\n    vu->q.u32 = 0;\n    vu->clip = 0;\n    vu->status = 0;\n    vu->fbrst = 0;\n    vu->cmsar0 = 0;\n    vu->cmsar1 = 0;\n    vu->mac = 0;\n    vu->mac_pipeline[0] = 0;\n    vu->mac_pipeline[1] = 0;\n    vu->mac_pipeline[2] = 0;\n    vu->mac_pipeline[3] = 0;\n    vu->clip_pipeline[0] = 0;\n    vu->clip_pipeline[1] = 0;\n    vu->clip_pipeline[2] = 0;\n    vu->clip_pipeline[3] = 0;\n    vu->tpc = 0;\n    vu->next_tpc = 1;\n    vu->i_bit = 0;\n    vu->e_bit = 0;\n    vu->m_bit = 0;\n    vu->d_bit = 0;\n    vu->t_bit = 0;\n    vu->q_delay = 0;\n    vu->prev_q.u32 = 0;\n\n    vu->last_block_lookup_tpc = ~0u;\n    vu->last_block_ptr = nullptr;\n\n    vu->block_cache_size = 0;\n    vu->block_cache.clear();\n    vu->block_cache.resize(vu->micro_mem_size+1);\n\n    vu->vf[0].w = 1.0;\n}\n\nvoid ps2_vu_decode_upper(struct vu_state* vu, uint32_t opcode) {\n    vu_decode_upper(vu, opcode);\n}\n\nvoid ps2_vu_decode_lower(struct vu_state* vu, uint32_t opcode) {\n    vu_decode_lower(vu, opcode);\n}\n\nvoid vu_execute_program_tpc(struct vu_state* vu) {\n    vu_execute_program(vu, vu->tpc);\n}\n\nuint128_t* vu_get_vu_mem_ptr(struct vu_state* vu, uint32_t addr) {\n    return &vu->vu_mem[addr & vu->vu_mem_size];\n}\n\nuint64_t* vu_get_micro_mem_ptr(struct vu_state* vu, uint32_t addr) {\n    return &vu->micro_mem[addr & vu->micro_mem_size];\n}\n\nuint32_t vu_get_tpc(struct vu_state* vu) {\n    return vu->tpc;\n}\n\nvoid ps2_vu_execute_lower(struct vu_state* vu, uint32_t opcode) {\n    vu_decode_lower(vu, opcode);\n\n    vu->lower.func(vu, &vu->lower);\n}\n\nvoid ps2_vu_execute_upper(struct vu_state* vu, uint32_t opcode) {\n    vu_decode_upper(vu, opcode);\n\n    vu->upper.func(vu, &vu->upper);\n}\n\nvoid vu_clear_block_cache(struct vu_state* vu) {\n    vu->block_cache_size = 0;\n    vu->block_cache.clear();\n    vu->block_cache.resize(vu->micro_mem_size+1);\n\n    vu->last_block_lookup_tpc = ~0u;\n    vu->last_block_ptr = nullptr;\n}\n\nvoid vu_invalidate_range(struct vu_state* vu, uint32_t addr, uint32_t size) {\n    if (!size || vu->block_cache.empty()) {\n        return;\n    }\n\n    const uint32_t word_mask = (uint32_t)vu->micro_mem_size;\n    const uint32_t word_count = word_mask + 1;\n    const uint32_t byte_mask = (word_mask << 3) | 7;\n    const uint32_t byte_count = word_count << 3;\n\n    if (size >= byte_count) {\n        for (vu_block& block : vu->block_cache) {\n            if (!block.cycles) {\n                continue;\n            }\n\n            block.cycles = 0;\n            block.entries.clear();\n        }\n\n        vu->block_cache_size = 0;\n        vu->last_block_lookup_tpc = ~0u;\n        vu->last_block_ptr = nullptr;\n\n        return;\n    }\n\n    const uint32_t start_byte = addr & byte_mask;\n    const uint32_t start_word = start_byte >> 3;\n    const uint32_t offset_in_word = start_byte & 7;\n    const uint32_t invalid_word_count = (offset_in_word + size + 7) >> 3;\n\n    int invalidated = 0;\n\n    for (vu_block& block : vu->block_cache) {\n        if (!block.cycles) {\n            continue;\n        }\n\n        bool intersects = false;\n\n        for (int i = 0; i < block.cycles; i++) {\n            const uint32_t block_word = (block.tpc + (uint32_t)i) & word_mask;\n            const uint32_t rel = (block_word - start_word) & word_mask;\n\n            if (rel < invalid_word_count) {\n                intersects = true;\n                break;\n            }\n        }\n\n        if (!intersects) {\n            continue;\n        }\n\n        block.cycles = 0;\n        block.entries.clear();\n        invalidated++;\n    }\n\n    if (!invalidated) {\n        return;\n    }\n\n    vu->block_cache_size -= invalidated;\n\n    if (vu->block_cache_size < 0) {\n        vu->block_cache_size = 0;\n    }\n\n    vu->last_block_lookup_tpc = ~0u;\n    vu->last_block_ptr = nullptr;\n}\n\n// #undef printf"
  },
  {
    "path": "src/ee/vu_def.hpp",
    "content": "#pragma once\n\n#include <cstdint>\n#include <unordered_map>\n#include <vector>\n\n#include \"vu.h\"\n\nstruct vu_block_entry {\n    struct vu_instruction upper, lower;\n    int e_bit;\n    int i_bit;\n    int hazard0;\n    int hazard1;\n    int hazard2;\n    int hazard3;\n    int branch;\n};\n\nstruct vu_block {\n    std::vector <vu_block_entry> entries;\n\n    uint32_t tpc;\n    int cycles = 0;\n};\n\nstruct vu_state {\n    struct vu_reg128 vf[32];\n    uint16_t vi[16];\n    struct vu_reg128 acc;\n\n    std::vector <vu_block> block_cache;\n    int block_cache_size;\n\n    // Single-entry block cache for fast lookup (avoid hash computation)\n    uint32_t last_block_lookup_tpc;\n    struct vu_block* last_block_ptr;\n\n    uint64_t cache_hits;\n    uint64_t cache_misses;\n\n    struct vu_instruction upper, lower;\n\n    struct {\n        struct {\n            uint8_t reg;\n            uint8_t field;\n        } dst;\n    } upper_pipeline[4], lower_pipeline[4];\n\n    int vi_backup_cycles;\n    int vi_backup_reg;\n    int vi_backup_value;\n\n    uint64_t micro_mem[0x800];\n    uint128_t vu_mem[0x400];\n\n    int micro_mem_size;\n    int vu_mem_size;\n    int id;\n\n    int i_bit;\n    int e_bit;\n    int m_bit;\n    int d_bit;\n    int t_bit;\n    uint32_t next_tpc;\n\n    // MAC flags pipeline\n    uint32_t mac_pipeline[4];\n    uint32_t clip_pipeline[4];\n\n    int q_delay;\n    struct vu_reg32 prev_q;\n    struct vu_reg32 p;\n\n    int xgkick_pending;\n    int xgkick_addr;\n\n    union {\n        uint32_t cr[16];\n\n        struct {\n            uint32_t status;\n            uint32_t mac;\n            uint32_t clip;\n            uint32_t rsv0;\n            struct vu_reg32 r;\n            struct vu_reg32 i;\n            struct vu_reg32 q;\n            uint32_t rsv1;\n            uint32_t rsv2;\n            uint32_t rsv3;\n            uint32_t tpc;\n            uint32_t cmsar0;\n            uint32_t fbrst;\n            uint32_t vpu_stat;\n            uint32_t rsv4;\n            uint32_t cmsar1;\n        };\n    };\n\n    struct ps2_gif* gif;\n    struct ps2_vif* vif;\n    struct vu_state* vu1;\n};\n\n// Upper pipeline\ntemplate <uint32_t di> void vu_i_abs(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_add(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_addi(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_addq(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_addx(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_addy(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_addz(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_addw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_adda(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_addai(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_addaq(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_addax(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_adday(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_addaz(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_addaw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_sub(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_subi(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_subq(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_subx(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_suby(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_subz(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_subw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_suba(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_subai(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_subaq(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_subax(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_subay(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_subaz(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_subaw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mul(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_muli(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mulq(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mulx(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_muly(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mulz(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mulw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mula(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mulai(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mulaq(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mulax(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mulay(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mulaz(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mulaw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_madd(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maddi(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maddq(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maddx(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maddy(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maddz(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maddw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_madda(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maddai(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maddaq(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maddax(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_madday(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maddaz(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maddaw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msub(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msubi(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msubq(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msubx(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msuby(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msubz(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msubw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msuba(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msubai(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msubaq(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msubax(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msubay(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msubaz(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_msubaw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_max(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maxi(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maxx(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maxy(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maxz(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_maxw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mini(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_minii(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_minix(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_miniy(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_miniz(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_miniw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_ftoi0(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_ftoi4(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_ftoi12(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_ftoi15(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_itof0(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_itof4(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_itof12(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_itof15(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_opmula(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_opmsub(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_nop(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_clip(struct vu_state* vu, const struct vu_instruction* ins);\n\n// Lower pipeline\ntemplate <uint32_t di> void vu_i_ilw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_ilwr(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_isw(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_iswr(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_lq(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_lqd(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_lqi(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mfir(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mfp(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_move(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_mr32(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_rget(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_rnext(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_sq(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_sqd(struct vu_state* vu, const struct vu_instruction* ins);\ntemplate <uint32_t di> void vu_i_sqi(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_b(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_bal(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_div(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_eatan(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_eatanxy(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_eatanxz(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_eexp(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_eleng(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_ercpr(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_erleng(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_ersadd(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_ersqrt(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_esadd(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_esin(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_esqrt(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_esum(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_fcand(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_fceq(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_fcget(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_fcor(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_fcset(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_fmand(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_fmeq(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_fmor(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_fsand(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_fseq(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_fsor(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_fsset(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_iadd(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_iaddi(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_iaddiu(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_iand(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_ibeq(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_ibgez(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_ibgtz(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_iblez(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_ibltz(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_ibne(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_ior(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_isub(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_isubiu(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_jalr(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_jr(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_mtir(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_rinit(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_rsqrt(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_rxor(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_sqrt(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_waitp(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_waitq(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_xgkick(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_xitop(struct vu_state* vu, const struct vu_instruction* ins);\nvoid vu_i_xtop(struct vu_state* vu, const struct vu_instruction* ins);"
  },
  {
    "path": "src/ee/vu_dis.c",
    "content": "#include <stdio.h>\n\n#include \"vu_dis.h\"\n\n#define VU_LD_DEST ((o >> 21) & 0xf)\n#define VU_LD_DI(i) (o & (1 << (24 - i)))\n#define VU_LD_DX ((o >> 24) & 1)\n#define VU_LD_DY ((o >> 23) & 1)\n#define VU_LD_DZ ((o >> 22) & 1)\n#define VU_LD_DW ((o >> 21) & 1)\n#define VU_LD_D ((o >> 6) & 0x1f)\n#define VU_LD_S ((o >> 11) & 0x1f)\n#define VU_LD_T ((o >> 16) & 0x1f)\n#define VU_LD_SF ((o >> 21) & 3)\n#define VU_LD_TF ((o >> 23) & 3)\n#define VU_LD_IMM5 (((int32_t)(VU_LD_D << 27)) >> 27)\n#define VU_LD_IMM11 (((int32_t)((o & 0x7ff) << 21)) >> 21)\n#define VU_LD_IMM12 (o & 0x7ff)\n#define VU_LD_IMM15 ((o & 0x7ff) | ((o & 0x1e00000) >> 10))\n#define VU_LD_IMM24 (o & 0xffffff)\n#define VU_UD_DEST ((o >> 21) & 0xf)\n#define VU_UD_DI(i) (o & (1 << (24 - i)))\n#define VU_UD_DX ((o >> 24) & 1)\n#define VU_UD_DY ((o >> 23) & 1)\n#define VU_UD_DZ ((o >> 22) & 1)\n#define VU_UD_DW ((o >> 21) & 1)\n#define VU_UD_D ((o >> 6) & 0x1f)\n#define VU_UD_S ((o >> 11) & 0x1f)\n#define VU_UD_T ((o >> 16) & 0x1f)\n\n// Print broadcast fields\nstatic inline char* vu_d_bc(struct vu_dis_state* s, char* p, uint32_t bc) {\n    if (!bc)\n        return p;\n\n    *p++ = '.';\n\n    for (int i = 0; i < 4; i++) {\n        if (bc & (1 << (3 - i))) *p++ = \"xyzw\"[i];\n    }\n\n    *p = '\\0';\n\n    return p;\n}\n\nstatic inline char* vu_d_mnemonic(struct vu_dis_state* s, char* p, const char* m, uint32_t bc) {\n    char buf[32];\n    char* ptr = buf;\n\n    ptr += sprintf(ptr, \"%s\", m);\n    ptr = vu_d_bc(s, ptr, bc);\n\n    p += sprintf(p, \"%-12s\", buf);\n\n    return p;\n}\n\nstatic inline char* vu_d_mnemonic_nd(struct vu_dis_state* s, char* p, const char* m) {\n    p += sprintf(p, \"%-12s\", m);\n\n    return p;\n}\n\nstatic inline char* vu_d_addax(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"addax\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_adday(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"adday\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_addaz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"addaz\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_addaw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"addaw\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_subax(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"subax\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_subay(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"subay\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_subaz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"subaz\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_subaw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"subaw\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maddax(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maddax\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_madday(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"madday\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maddaz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maddaz\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maddaw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maddaw\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_msubax(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msubax\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_msubay(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msubay\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_msubaz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msubaz\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_msubaw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msubaw\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_itof0(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"itof0\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u\", VU_UD_T, VU_UD_S); return p;\n}\nstatic inline char* vu_d_itof4(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"itof4\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u\", VU_UD_T, VU_UD_S); return p;\n}\nstatic inline char* vu_d_itof12(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"itof12\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u\", VU_UD_T, VU_UD_S); return p;\n}\nstatic inline char* vu_d_itof15(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"itof15\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u\", VU_UD_T, VU_UD_S); return p;\n}\nstatic inline char* vu_d_ftoi0(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"ftoi0\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u\", VU_UD_T, VU_UD_S); return p;\n}\nstatic inline char* vu_d_ftoi4(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"ftoi4\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u\", VU_UD_T, VU_UD_S); return p;\n}\nstatic inline char* vu_d_ftoi12(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"ftoi12\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u\", VU_UD_T, VU_UD_S); return p;\n}\nstatic inline char* vu_d_ftoi15(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"ftoi15\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u\", VU_UD_T, VU_UD_S); return p;\n}\nstatic inline char* vu_d_mulax(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mulax\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_mulay(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mulay\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_mulaz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mulaz\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_mulaw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mulaw\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_mulaq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mulaq\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, q\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_abs(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"abs\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u\", VU_UD_T, VU_UD_S); return p;\n}\nstatic inline char* vu_d_mulai(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mulai\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, i\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_clip(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"clipw\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_addaq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"addaq\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, q\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maddaq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maddaq\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, q\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_addai(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"addai\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, i\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maddai(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maddai\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, i\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_subaq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"subaq\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, q\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_msubaq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msubaq\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, q\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_subai(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"subai\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, i\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_msubai(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msubai\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, i\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_adda(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"adda\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_madda(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"madda\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_mula(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mula\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_suba(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"suba\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_msuba(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msuba\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_opmula(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"opmula\", VU_UD_DEST); p += sprintf(p, \"acc, vf%02u, vf%02u\", VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_nop(struct vu_dis_state* s, char* p, uint32_t o) {\n    p += sprintf(p, \"%s\", \"nop\"); return p;\n}\nstatic inline char* vu_d_addx(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"addx\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_addy(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"addy\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_addz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"addz\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_addw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"addw\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_subx(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"subx\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_suby(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"suby\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_subz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"subz\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_subw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"subw\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maddx(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maddx\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maddy(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maddy\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maddz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maddz\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maddw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maddw\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_msubx(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msubx\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_msuby(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msuby\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_msubz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msubz\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_msubw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msubw\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maxx(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maxx\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maxy(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maxy\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maxz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maxz\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_maxw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maxw\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_minix(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"minix\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_miniy(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"miniy\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_miniz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"miniz\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_miniw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"miniw\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_mulx(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mulx\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_muly(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"muly\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_mulz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mulz\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_mulw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mulw\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_mulq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mulq\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, q\", VU_UD_D, VU_UD_S); return p;\n}\nstatic inline char* vu_d_maxi(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maxi\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, i\", VU_UD_D, VU_UD_S); return p;\n}\nstatic inline char* vu_d_muli(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"muli\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, i\", VU_UD_D, VU_UD_S); return p;\n}\nstatic inline char* vu_d_minii(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"minii\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, i\", VU_UD_D, VU_UD_S); return p;\n}\nstatic inline char* vu_d_addq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"addq\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, q\", VU_UD_D, VU_UD_S); return p;\n}\nstatic inline char* vu_d_maddq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maddq\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, q\", VU_UD_D, VU_UD_S); return p;\n}\nstatic inline char* vu_d_addi(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"addi\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, i\", VU_UD_D, VU_UD_S); return p;\n}\nstatic inline char* vu_d_maddi(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"maddi\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, i\", VU_UD_D, VU_UD_S); return p;\n}\nstatic inline char* vu_d_subq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"subq\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, q\", VU_UD_D, VU_UD_S); return p;\n}\nstatic inline char* vu_d_msubq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msubq\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, q\", VU_UD_D, VU_UD_S); return p;\n}\nstatic inline char* vu_d_subi(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"subi\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, i\", VU_UD_D, VU_UD_S); return p;\n}\nstatic inline char* vu_d_msubi(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msubi\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, i\", VU_UD_D, VU_UD_S); return p;\n}\nstatic inline char* vu_d_add(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"add\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_madd(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"madd\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_mul(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mul\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_max(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"max\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_sub(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"sub\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_msub(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"msub\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_opmsub(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"opmsub\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\nstatic inline char* vu_d_mini(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mini\", VU_UD_DEST); p += sprintf(p, \"vf%02u, vf%02u, vf%02u\", VU_UD_D, VU_UD_S, VU_UD_T); return p;\n}\n\nstatic inline char* vu_d_lq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"lq\", VU_LD_DEST); p += sprintf(p, \"vf%02u, %d(vi%02u)\", VU_LD_T, VU_LD_IMM11, VU_LD_S); return p;\n}\nstatic inline char* vu_d_sq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"sq\", VU_LD_DEST); p += sprintf(p, \"vf%02u, %d(vi%02u)\", VU_LD_S, VU_LD_IMM11, VU_LD_T); return p;\n}\nstatic inline char* vu_d_ilw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"ilw\", VU_LD_DEST); p += sprintf(p, \"vi%02u, %d(vi%02u)\", VU_LD_T, VU_LD_IMM11, VU_LD_S); return p;\n}\nstatic inline char* vu_d_isw(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"isw\", VU_LD_DEST); p += sprintf(p, \"vi%02u, %d(vi%02u)\", VU_LD_T, VU_LD_IMM11, VU_LD_S); return p;\n}\nstatic inline char* vu_d_iaddiu(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"iaddiu\"); p += sprintf(p, \"vi%02u, vi%02u, %d\", VU_LD_T, VU_LD_S, VU_LD_IMM15); return p;\n}\nstatic inline char* vu_d_isubiu(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"isubiu\"); p += sprintf(p, \"vi%02u, vi%02u, %d\", VU_LD_T, VU_LD_S, VU_LD_IMM15); return p;\n}\nstatic inline char* vu_d_fceq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"fceq\"); p += sprintf(p, \"vi01, 0x%06x\", VU_LD_IMM24); return p;\n}\nstatic inline char* vu_d_fcset(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"fcset\"); p += sprintf(p, \"0x%06x\", VU_LD_IMM24); return p;\n}\nstatic inline char* vu_d_fcand(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"fcand\"); p += sprintf(p, \"vi01, 0x%06x\", VU_LD_IMM24); return p;\n}\nstatic inline char* vu_d_fcor(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"fcor\"); p += sprintf(p, \"vi01, 0x%06x\", VU_LD_IMM24); return p;\n}\nstatic inline char* vu_d_fseq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"fseq\"); p += sprintf(p, \"0x%04x\", VU_LD_IMM12); return p;\n}\nstatic inline char* vu_d_fsset(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"fsset\"); p += sprintf(p, \"0x%04x\", VU_LD_IMM12); return p;\n}\nstatic inline char* vu_d_fsand(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"fsand\"); p += sprintf(p, \"0x%04x\", VU_LD_IMM12); return p;\n}\nstatic inline char* vu_d_fsor(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"fsor\"); p += sprintf(p, \"0x%04x\", VU_LD_IMM12); return p;\n}\nstatic inline char* vu_d_fmeq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"fmeq\"); p += sprintf(p, \"vi%02u, vi%02u\", VU_LD_T, VU_LD_S); return p;\n}\nstatic inline char* vu_d_fmand(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"fmand\"); p += sprintf(p, \"vi%02u, vi%02u\", VU_LD_T, VU_LD_S); return p;\n}\nstatic inline char* vu_d_fmor(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"fmor\"); p += sprintf(p, \"vi%02u, vi%02u\", VU_LD_T, VU_LD_S); return p;\n}\nstatic inline char* vu_d_fcget(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"fcget\"); p += sprintf(p, \"vi%02u\", VU_LD_T); return p;\n}\nstatic inline char* vu_d_b(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"b\"); p += sprintf(p, \"%04x\", s ? (s->addr + 1 + VU_LD_IMM11) : VU_LD_IMM11); return p;\n}\nstatic inline char* vu_d_bal(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"bal\"); p += sprintf(p, \"vi%02u, 0x%04x\", VU_LD_T, s ? (s->addr + 1 + VU_LD_IMM11) : VU_LD_IMM11); return p;\n}\nstatic inline char* vu_d_jr(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"jr\"); p += sprintf(p, \"vi%02u\", VU_LD_S); return p;\n}\nstatic inline char* vu_d_jalr(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"jalr\"); p += sprintf(p, \"vi%02u, vi%02u\", VU_LD_T, VU_LD_S); return p;\n}\nstatic inline char* vu_d_ibeq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"ibeq\"); p += sprintf(p, \"vi%02u, vi%02u, 0x%04x\", VU_LD_T, VU_LD_S, s ? (s->addr + 1 + VU_LD_IMM11) : VU_LD_IMM11); return p;\n}\nstatic inline char* vu_d_ibne(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"ibne\"); p += sprintf(p, \"vi%02u, vi%02u, 0x%04x\", VU_LD_T, VU_LD_S, s ? (s->addr + 1 + VU_LD_IMM11) : VU_LD_IMM11); return p;\n}\nstatic inline char* vu_d_ibltz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"ibltz\"); p += sprintf(p, \"vi%02u, 0x%04x\", VU_LD_S, s ? (s->addr + 1 + VU_LD_IMM11) : VU_LD_IMM11); return p;\n}\nstatic inline char* vu_d_ibgtz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"ibgtz\"); p += sprintf(p, \"vi%02u, 0x%04x\", VU_LD_S, s ? (s->addr + 1 + VU_LD_IMM11) : VU_LD_IMM11); return p;\n}\nstatic inline char* vu_d_iblez(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"iblez\"); p += sprintf(p, \"vi%02u, 0x%04x\", VU_LD_S, s ? (s->addr + 1 + VU_LD_IMM11) : VU_LD_IMM11); return p;\n}\nstatic inline char* vu_d_ibgez(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"ibgez\"); p += sprintf(p, \"vi%02u, 0x%04x\", VU_LD_S, s ? (s->addr + 1 + VU_LD_IMM11) : VU_LD_IMM11); return p;\n}\nstatic inline char* vu_d_move(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"move\", VU_LD_DEST); p += sprintf(p, \"vf%02u, vf%02u\", VU_LD_T, VU_LD_S); return p;\n}\nstatic inline char* vu_d_mr32(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mr32\", VU_LD_DEST); p += sprintf(p, \"vf%02u, vf%02u\", VU_LD_T, VU_LD_S); return p;\n}\nstatic inline char* vu_d_lqi(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"lqi\", VU_LD_DEST); p += sprintf(p, \"vf%02u, (vi%02u++)\", VU_LD_T, VU_LD_S); return p;\n}\nstatic inline char* vu_d_sqi(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"sqi\", VU_LD_DEST); p += sprintf(p, \"vf%02u, (vi%02u++)\", VU_LD_S, VU_LD_T); return p;\n}\nstatic inline char* vu_d_lqd(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"lqd\", VU_LD_DEST); p += sprintf(p, \"vf%02u, (--vi%02u)\", VU_LD_T, VU_LD_S); return p;\n}\nstatic inline char* vu_d_sqd(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"sqd\", VU_LD_DEST); p += sprintf(p, \"vf%02u, (--vi%02u)\", VU_LD_S, VU_LD_T); return p;\n}\nstatic inline char* vu_d_div(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"div\"); p += sprintf(p, \"q, vf%02u%c, vf%02u%c\", VU_LD_S, \"xyzw\"[VU_LD_SF], VU_LD_T, \"xyzw\"[VU_LD_TF]); return p;\n}\nstatic inline char* vu_d_sqrt(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"sqrt\"); p += sprintf(p, \"q, vf%02u%c\", VU_LD_T, \"xyzw\"[VU_LD_TF]); return p;\n}\nstatic inline char* vu_d_rsqrt(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"rsqrt\"); p += sprintf(p, \"q, vf%02u%c, vf%02u%c\", VU_LD_S, \"xyzw\"[VU_LD_SF], VU_LD_T, \"xyzw\"[VU_LD_TF]); return p;\n}\nstatic inline char* vu_d_waitq(struct vu_dis_state* s, char* p, uint32_t o) {\n    p += sprintf(p, \"waitq\"); return p;\n}\nstatic inline char* vu_d_mtir(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"mtir\"); p += sprintf(p, \"vi%02u, vf%02u%c\", VU_LD_T, VU_LD_S, \"xyzw\"[VU_LD_SF]); return p;\n}\nstatic inline char* vu_d_mfir(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mfir\", VU_LD_DEST); p += sprintf(p, \"vf%02u, vi%02u\", VU_LD_T, VU_LD_S); return p;\n}\nstatic inline char* vu_d_ilwr(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"ilwr\", VU_LD_DEST); p += sprintf(p, \"vi%02u, (vi%02u)\", VU_LD_T, VU_LD_S); return p;\n}\nstatic inline char* vu_d_iswr(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"iswr\", VU_LD_DEST); p += sprintf(p, \"vi%02u, (vi%02u)\", VU_LD_T, VU_LD_S); return p;\n}\nstatic inline char* vu_d_rnext(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"rnext\", VU_LD_DEST); p += sprintf(p, \"vf%02u, r\", VU_LD_T); return p;\n}\nstatic inline char* vu_d_rget(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"rget\", VU_LD_DEST); p += sprintf(p, \"vf%02u, r\", VU_LD_T); return p;\n}\nstatic inline char* vu_d_rinit(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"rinit\"); p += sprintf(p, \"r, vf%02u%c\", VU_LD_S, \"xyzw\"[VU_LD_SF]); return p;\n}\nstatic inline char* vu_d_rxor(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"rxor\"); p += sprintf(p, \"r, vf%02u%c\", VU_LD_S, \"xyzw\"[VU_LD_SF]); return p;\n}\nstatic inline char* vu_d_mfp(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic(s, p, \"mfp\", VU_LD_DEST); p += sprintf(p, \"vf%02u, p\", VU_LD_T); return p;\n}\nstatic inline char* vu_d_xtop(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"xtop\"); p += sprintf(p, \"vi%02u\", VU_LD_T); return p;\n}\nstatic inline char* vu_d_xitop(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"xitop\"); p += sprintf(p, \"vi%02u\", VU_LD_T); return p;\n}\nstatic inline char* vu_d_xgkick(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"xgkick\"); p += sprintf(p, \"vi%02u\", VU_LD_S); return p;\n}\nstatic inline char* vu_d_esadd(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"esadd\"); p += sprintf(p, \"p, vf%02u\", VU_LD_S); return p;\n}\nstatic inline char* vu_d_ersadd(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"ersadd\"); p += sprintf(p, \"p, vf%02u\", VU_LD_S); return p;\n}\nstatic inline char* vu_d_eleng(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"eleng\"); p += sprintf(p, \"p, vf%02u\", VU_LD_S); return p;\n}\nstatic inline char* vu_d_erleng(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"erleng\"); p += sprintf(p, \"p, vf%02u\", VU_LD_S); return p;\n}\nstatic inline char* vu_d_eatanxy(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"eatan.xy\"); p += sprintf(p, \"p, vf%02u\", VU_LD_S); return p;\n}\nstatic inline char* vu_d_eatanxz(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"eatan.xz\"); p += sprintf(p, \"p, vf%02u\", VU_LD_S); return p;\n}\nstatic inline char* vu_d_esum(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"esum\"); p += sprintf(p, \"p, vf%02u\", VU_LD_S); return p;\n}\nstatic inline char* vu_d_esqrt(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"esqrt\"); p += sprintf(p, \"p, vf%02u%c\", VU_LD_S, \"xyzw\"[VU_LD_SF]); return p;\n}\nstatic inline char* vu_d_ersqrt(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"ersqrt\"); p += sprintf(p, \"p, vf%02u%c\", VU_LD_S, \"xyzw\"[VU_LD_SF]); return p;\n}\nstatic inline char* vu_d_ercpr(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"ercpr\"); p += sprintf(p, \"p, vf%02u%c\", VU_LD_S, \"xyzw\"[VU_LD_SF]); return p;\n}\nstatic inline char* vu_d_waitp(struct vu_dis_state* s, char* p, uint32_t o) {\n    p += sprintf(p, \"waitp\"); return p;\n}\nstatic inline char* vu_d_esin(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"esin\"); p += sprintf(p, \"p, vf%02u%c\", VU_LD_S, \"xyzw\"[VU_LD_SF]); return p;\n}\nstatic inline char* vu_d_eatan(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"eatan\"); p += sprintf(p, \"p, vf%02u%c\", VU_LD_S, \"xyzw\"[VU_LD_SF]); return p;\n}\nstatic inline char* vu_d_eexp(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"eexp\"); p += sprintf(p, \"p, vf%02u%c\", VU_LD_S, \"xyzw\"[VU_LD_SF]); return p;\n}\nstatic inline char* vu_d_iadd(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"iadd\"); p += sprintf(p, \"vi%02u, vi%02u, vi%02u\", VU_LD_D, VU_LD_S, VU_LD_T); return p;\n}\nstatic inline char* vu_d_isub(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"isub\"); p += sprintf(p, \"vi%02u, vi%02u, vi%02u\", VU_LD_D, VU_LD_S, VU_LD_T); return p;\n}\nstatic inline char* vu_d_iaddi(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"iaddi\"); p += sprintf(p, \"vi%02u, vi%02u, %d\", VU_LD_D, VU_LD_S, VU_LD_IMM5); return p;\n}\nstatic inline char* vu_d_iand(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"iand\"); p += sprintf(p, \"vi%02u, vi%02u, vi%02u\", VU_LD_D, VU_LD_S, VU_LD_T); return p;\n}\nstatic inline char* vu_d_ior(struct vu_dis_state* s, char* p, uint32_t o) {\n    p = vu_d_mnemonic_nd(s, p, \"ior\"); p += sprintf(p, \"vi%02u, vi%02u, vi%02u\", VU_LD_D, VU_LD_S, VU_LD_T); return p;\n}\nstatic inline char* vu_d_invalid(struct vu_dis_state* s, char* p, uint32_t o) {\n    p += sprintf(p, \"<invalid>\"); return p;\n}\n\nchar* vu_disassemble_upper(char* buf, uint64_t opcode, struct vu_dis_state* s) {\n    char* ptr = buf;\n\n    ptr = buf;\n\n    if (s) if (s->print_address)\n        ptr += sprintf(ptr, \"%08x: \", s->addr);\n\n    if (s) if (s->print_opcode)\n        ptr += sprintf(ptr, \"%08x \", opcode);\n\n    // Decode 000007FF style instruction\n    if ((opcode & 0x3c) == 0x3c) {\n        switch (((opcode & 0x3c0) >> 4) | (opcode & 3)) {\n            case 0x00: vu_d_addax(s, ptr, opcode); return buf;\n            case 0x01: vu_d_adday(s, ptr, opcode); return buf;\n            case 0x02: vu_d_addaz(s, ptr, opcode); return buf;\n            case 0x03: vu_d_addaw(s, ptr, opcode); return buf;\n            case 0x04: vu_d_subax(s, ptr, opcode); return buf;\n            case 0x05: vu_d_subay(s, ptr, opcode); return buf;\n            case 0x06: vu_d_subaz(s, ptr, opcode); return buf;\n            case 0x07: vu_d_subaw(s, ptr, opcode); return buf;\n            case 0x08: vu_d_maddax(s, ptr, opcode); return buf;\n            case 0x09: vu_d_madday(s, ptr, opcode); return buf;\n            case 0x0A: vu_d_maddaz(s, ptr, opcode); return buf;\n            case 0x0B: vu_d_maddaw(s, ptr, opcode); return buf;\n            case 0x0C: vu_d_msubax(s, ptr, opcode); return buf;\n            case 0x0D: vu_d_msubay(s, ptr, opcode); return buf;\n            case 0x0E: vu_d_msubaz(s, ptr, opcode); return buf;\n            case 0x0F: vu_d_msubaw(s, ptr, opcode); return buf;\n            case 0x10: vu_d_itof0(s, ptr, opcode); return buf;\n            case 0x11: vu_d_itof4(s, ptr, opcode); return buf;\n            case 0x12: vu_d_itof12(s, ptr, opcode); return buf;\n            case 0x13: vu_d_itof15(s, ptr, opcode); return buf;\n            case 0x14: vu_d_ftoi0(s, ptr, opcode); return buf;\n            case 0x15: vu_d_ftoi4(s, ptr, opcode); return buf;\n            case 0x16: vu_d_ftoi12(s, ptr, opcode); return buf;\n            case 0x17: vu_d_ftoi15(s, ptr, opcode); return buf;\n            case 0x18: vu_d_mulax(s, ptr, opcode); return buf;\n            case 0x19: vu_d_mulay(s, ptr, opcode); return buf;\n            case 0x1A: vu_d_mulaz(s, ptr, opcode); return buf;\n            case 0x1B: vu_d_mulaw(s, ptr, opcode); return buf;\n            case 0x1C: vu_d_mulaq(s, ptr, opcode); return buf;\n            case 0x1D: vu_d_abs(s, ptr, opcode); return buf;\n            case 0x1E: vu_d_mulai(s, ptr, opcode); return buf;\n            case 0x1F: vu_d_clip(s, ptr, opcode); return buf;\n            case 0x20: vu_d_addaq(s, ptr, opcode); return buf;\n            case 0x21: vu_d_maddaq(s, ptr, opcode); return buf;\n            case 0x22: vu_d_addai(s, ptr, opcode); return buf;\n            case 0x23: vu_d_maddai(s, ptr, opcode); return buf;\n            case 0x24: vu_d_subaq(s, ptr, opcode); return buf;\n            case 0x25: vu_d_msubaq(s, ptr, opcode); return buf;\n            case 0x26: vu_d_subai(s, ptr, opcode); return buf;\n            case 0x27: vu_d_msubai(s, ptr, opcode); return buf;\n            case 0x28: vu_d_adda(s, ptr, opcode); return buf;\n            case 0x29: vu_d_madda(s, ptr, opcode); return buf;\n            case 0x2A: vu_d_mula(s, ptr, opcode); return buf;\n            case 0x2C: vu_d_suba(s, ptr, opcode); return buf;\n            case 0x2D: vu_d_msuba(s, ptr, opcode); return buf;\n            case 0x2E: vu_d_opmula(s, ptr, opcode); return buf;\n            case 0x2F: vu_d_nop(s, ptr, opcode); return buf;\n        }\n    } else {\n        // Decode 0000003F style instruction\n        switch (opcode & 0x3f) {\n            case 0x00: vu_d_addx(s, ptr, opcode); return buf;\n            case 0x01: vu_d_addy(s, ptr, opcode); return buf;\n            case 0x02: vu_d_addz(s, ptr, opcode); return buf;\n            case 0x03: vu_d_addw(s, ptr, opcode); return buf;\n            case 0x04: vu_d_subx(s, ptr, opcode); return buf;\n            case 0x05: vu_d_suby(s, ptr, opcode); return buf;\n            case 0x06: vu_d_subz(s, ptr, opcode); return buf;\n            case 0x07: vu_d_subw(s, ptr, opcode); return buf;\n            case 0x08: vu_d_maddx(s, ptr, opcode); return buf;\n            case 0x09: vu_d_maddy(s, ptr, opcode); return buf;\n            case 0x0A: vu_d_maddz(s, ptr, opcode); return buf;\n            case 0x0B: vu_d_maddw(s, ptr, opcode); return buf;\n            case 0x0C: vu_d_msubx(s, ptr, opcode); return buf;\n            case 0x0D: vu_d_msuby(s, ptr, opcode); return buf;\n            case 0x0E: vu_d_msubz(s, ptr, opcode); return buf;\n            case 0x0F: vu_d_msubw(s, ptr, opcode); return buf;\n            case 0x10: vu_d_maxx(s, ptr, opcode); return buf;\n            case 0x11: vu_d_maxy(s, ptr, opcode); return buf;\n            case 0x12: vu_d_maxz(s, ptr, opcode); return buf;\n            case 0x13: vu_d_maxw(s, ptr, opcode); return buf;\n            case 0x14: vu_d_minix(s, ptr, opcode); return buf;\n            case 0x15: vu_d_miniy(s, ptr, opcode); return buf;\n            case 0x16: vu_d_miniz(s, ptr, opcode); return buf;\n            case 0x17: vu_d_miniw(s, ptr, opcode); return buf;\n            case 0x18: vu_d_mulx(s, ptr, opcode); return buf;\n            case 0x19: vu_d_muly(s, ptr, opcode); return buf;\n            case 0x1A: vu_d_mulz(s, ptr, opcode); return buf;\n            case 0x1B: vu_d_mulw(s, ptr, opcode); return buf;\n            case 0x1C: vu_d_mulq(s, ptr, opcode); return buf;\n            case 0x1D: vu_d_maxi(s, ptr, opcode); return buf;\n            case 0x1E: vu_d_muli(s, ptr, opcode); return buf;\n            case 0x1F: vu_d_minii(s, ptr, opcode); return buf;\n            case 0x20: vu_d_addq(s, ptr, opcode); return buf;\n            case 0x21: vu_d_maddq(s, ptr, opcode); return buf;\n            case 0x22: vu_d_addi(s, ptr, opcode); return buf;\n            case 0x23: vu_d_maddi(s, ptr, opcode); return buf;\n            case 0x24: vu_d_subq(s, ptr, opcode); return buf;\n            case 0x25: vu_d_msubq(s, ptr, opcode); return buf;\n            case 0x26: vu_d_subi(s, ptr, opcode); return buf;\n            case 0x27: vu_d_msubi(s, ptr, opcode); return buf;\n            case 0x28: vu_d_add(s, ptr, opcode); return buf;\n            case 0x29: vu_d_madd(s, ptr, opcode); return buf;\n            case 0x2A: vu_d_mul(s, ptr, opcode); return buf;\n            case 0x2B: vu_d_max(s, ptr, opcode); return buf;\n            case 0x2C: vu_d_sub(s, ptr, opcode); return buf;\n            case 0x2D: vu_d_msub(s, ptr, opcode); return buf;\n            case 0x2E: vu_d_opmsub(s, ptr, opcode); return buf;\n            case 0x2F: vu_d_mini(s, ptr, opcode); return buf;\n        }\n    }\n\n    vu_d_invalid(s, ptr, opcode);\n\n    return buf;\n}\n\nchar* vu_disassemble_lower(char* buf, uint64_t opcode, struct vu_dis_state* s) {\n    char* ptr = buf;\n\n    ptr = buf;\n\n    if (s) if (s->print_address)\n        ptr += sprintf(ptr, \"%08x: \", s->addr);\n\n    if (s) if (s->print_opcode)\n        ptr += sprintf(ptr, \"%08x \", opcode);\n\n    switch ((opcode & 0xFE000000) >> 25) {\n        case 0x00: vu_d_lq(s, ptr, opcode); return buf;\n        case 0x01: vu_d_sq(s, ptr, opcode); return buf;\n        case 0x04: vu_d_ilw(s, ptr, opcode); return buf;\n        case 0x05: vu_d_isw(s, ptr, opcode); return buf;\n        case 0x08: vu_d_iaddiu(s, ptr, opcode); return buf;\n        case 0x09: vu_d_isubiu(s, ptr, opcode); return buf;\n        case 0x10: vu_d_fceq(s, ptr, opcode); return buf;\n        case 0x11: vu_d_fcset(s, ptr, opcode); return buf;\n        case 0x12: vu_d_fcand(s, ptr, opcode); return buf;\n        case 0x13: vu_d_fcor(s, ptr, opcode); return buf;\n        case 0x14: vu_d_fseq(s, ptr, opcode); return buf;\n        case 0x15: vu_d_fsset(s, ptr, opcode); return buf;\n        case 0x16: vu_d_fsand(s, ptr, opcode); return buf;\n        case 0x17: vu_d_fsor(s, ptr, opcode); return buf;\n        case 0x18: vu_d_fmeq(s, ptr, opcode); return buf;\n        case 0x1A: vu_d_fmand(s, ptr, opcode); return buf;\n        case 0x1B: vu_d_fmor(s, ptr, opcode); return buf;\n        case 0x1C: vu_d_fcget(s, ptr, opcode); return buf;\n        case 0x20: vu_d_b(s, ptr, opcode); return buf;\n        case 0x21: vu_d_bal(s, ptr, opcode); return buf;\n        case 0x24: vu_d_jr(s, ptr, opcode); return buf;\n        case 0x25: vu_d_jalr(s, ptr, opcode); return buf;\n        case 0x28: vu_d_ibeq(s, ptr, opcode); return buf;\n        case 0x29: vu_d_ibne(s, ptr, opcode); return buf;\n        case 0x2C: vu_d_ibltz(s, ptr, opcode); return buf;\n        case 0x2D: vu_d_ibgtz(s, ptr, opcode); return buf;\n        case 0x2E: vu_d_iblez(s, ptr, opcode); return buf;\n        case 0x2F: vu_d_ibgez(s, ptr, opcode); return buf;\n        case 0x40: {\n            if ((opcode & 0x3C) == 0x3C) {\n                switch (((opcode & 0x7C0) >> 4) | (opcode & 3)) {\n                    case 0x30: vu_d_move(s, ptr, opcode); return buf;\n                    case 0x31: vu_d_mr32(s, ptr, opcode); return buf;\n                    case 0x34: vu_d_lqi(s, ptr, opcode); return buf;\n                    case 0x35: vu_d_sqi(s, ptr, opcode); return buf;\n                    case 0x36: vu_d_lqd(s, ptr, opcode); return buf;\n                    case 0x37: vu_d_sqd(s, ptr, opcode); return buf;\n                    case 0x38: vu_d_div(s, ptr, opcode); return buf;\n                    case 0x39: vu_d_sqrt(s, ptr, opcode); return buf;\n                    case 0x3A: vu_d_rsqrt(s, ptr, opcode); return buf;\n                    case 0x3B: vu_d_waitq(s, ptr, opcode); return buf;\n                    case 0x3C: vu_d_mtir(s, ptr, opcode); return buf;\n                    case 0x3D: vu_d_mfir(s, ptr, opcode); return buf;\n                    case 0x3E: vu_d_ilwr(s, ptr, opcode); return buf;\n                    case 0x3F: vu_d_iswr(s, ptr, opcode); return buf;\n                    case 0x40: vu_d_rnext(s, ptr, opcode); return buf;\n                    case 0x41: vu_d_rget(s, ptr, opcode); return buf;\n                    case 0x42: vu_d_rinit(s, ptr, opcode); return buf;\n                    case 0x43: vu_d_rxor(s, ptr, opcode); return buf;\n                    case 0x64: vu_d_mfp(s, ptr, opcode); return buf;\n                    case 0x68: vu_d_xtop(s, ptr, opcode); return buf;\n                    case 0x69: vu_d_xitop(s, ptr, opcode); return buf;\n                    case 0x6C: vu_d_xgkick(s, ptr, opcode); return buf;\n                    case 0x70: vu_d_esadd(s, ptr, opcode); return buf;\n                    case 0x71: vu_d_ersadd(s, ptr, opcode); return buf;\n                    case 0x72: vu_d_eleng(s, ptr, opcode); return buf;\n                    case 0x73: vu_d_erleng(s, ptr, opcode); return buf;\n                    case 0x74: vu_d_eatanxy(s, ptr, opcode); return buf;\n                    case 0x75: vu_d_eatanxz(s, ptr, opcode); return buf;\n                    case 0x76: vu_d_esum(s, ptr, opcode); return buf;\n                    case 0x78: vu_d_esqrt(s, ptr, opcode); return buf;\n                    case 0x79: vu_d_ersqrt(s, ptr, opcode); return buf;\n                    case 0x7A: vu_d_ercpr(s, ptr, opcode); return buf;\n                    case 0x7B: vu_d_waitp(s, ptr, opcode); return buf;\n                    case 0x7C: vu_d_esin(s, ptr, opcode); return buf;\n                    case 0x7D: vu_d_eatan(s, ptr, opcode); return buf;\n                    case 0x7E: vu_d_eexp(s, ptr, opcode); return buf;\n                }\n            } else {\n                switch (opcode & 0x3F) {\n                    case 0x30: vu_d_iadd(s, ptr, opcode); return buf;\n                    case 0x31: vu_d_isub(s, ptr, opcode); return buf;\n                    case 0x32: vu_d_iaddi(s, ptr, opcode); return buf;\n                    case 0x34: vu_d_iand(s, ptr, opcode); return buf;\n                    case 0x35: vu_d_ior(s, ptr, opcode); return buf;\n                }\n            }\n        } break;\n    }\n\n    vu_d_invalid(s, ptr, opcode);\n\n    return buf;\n}"
  },
  {
    "path": "src/ee/vu_dis.h",
    "content": "#ifndef VU_DIS_H\n#define VU_DIS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\nstruct vu_dis_state {\n    int print_address;\n    int print_opcode;\n    uint32_t addr;\n};\n\nchar* vu_disassemble_upper(char* buf, uint64_t opcode, struct vu_dis_state* s);\nchar* vu_disassemble_lower(char* buf, uint64_t opcode, struct vu_dis_state* s);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ee/vu_uncached.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <math.h>\n#include <fenv.h>\n\n#include \"vu.h\"\n#include \"vu_dis.h\"\n\n// #define printf(fmt, ...)(0)\n\n#define VU_LD_DI(i) (ins->ld_di[i])\n#define VU_LD_D (ins->ld_d)\n#define VU_LD_S (ins->ld_s)\n#define VU_LD_T (ins->ld_t)\n#define VU_LD_SF (ins->ld_sf)\n#define VU_LD_TF (ins->ld_tf)\n#define VU_LD_IMM5 (ins->ld_imm5)\n#define VU_LD_IMM11 (ins->ld_imm11)\n#define VU_LD_IMM12 (ins->ld_imm12)\n#define VU_LD_IMM15 (ins->ld_imm15)\n#define VU_LD_IMM24 (ins->ld_imm24)\n#define VU_ID vu->vi[VU_LD_D]\n#define VU_IS vu->vi[VU_LD_S]\n#define VU_IT vu->vi[VU_LD_T]\n#define VU_UD_DI(i) (ins->ud_di[i])\n#define VU_UD_D (ins->ud_d)\n#define VU_UD_S (ins->ud_s)\n#define VU_UD_T (ins->ud_t)\n\nstruct vu_state* vu_create(void) {\n    return (struct vu_state*)malloc(sizeof(struct vu_state));\n}\n\nvoid vu_init(struct vu_state* vu, int id, struct ps2_gif* gif, struct ps2_vif* vif, struct vu_state* vu1) {\n    memset(vu, 0, sizeof(struct vu_state));\n\n    vu->id = id;\n    vu->vu1 = vu1;\n    vu->vif = vif;\n    vu->gif = gif;\n\n    if (!id) {\n        vu->micro_mem_size = 0x1ff;\n        vu->vu_mem_size = 0xff;\n    } else {\n        vu->micro_mem_size = 0x7ff;\n        vu->vu_mem_size = 0x3ff;\n    }\n\n    vu->vf[0].x = 0.0;\n    vu->vf[0].y = 0.0;\n    vu->vf[0].z = 0.0;\n    vu->vf[0].w = 1.0;\n\n    // VU uses round to zero by default\n    fesetround(FE_TOWARDZERO);\n}\n\nvoid vu_destroy(struct vu_state* vu) {\n    free(vu);\n}\n\n#define max(a, b) ((a) > (b) ? (a) : (b))\n#define min(a, b) ((a) < (b) ? (a) : (b))\n\nstatic inline uint32_t vu_max(int32_t a, int32_t b) {\n    return (a < 0 && b < 0) ? min(a, b) : max(a, b);\n}\n\nstatic inline uint32_t vu_min(int32_t a, int32_t b) {\n    return (a < 0 && b < 0) ? max(a, b) : min(a, b);\n}\n\nstatic inline float vu_atan(float t) {\n    //In reality, VU1 uses an approximation to derive the result. This is shown here.\n    const static float atan_const[] = {\n        0.999999344348907f, -0.333298563957214f,\n        0.199465364217758f, -0.139085337519646f,\n        0.096420042216778f, -0.055909886956215f,\n        0.021861229091883f, -0.004054057877511f\n    };\n\n    float result = 0.785398185253143f; // pi/4\n\n    for (int i = 0; i < 8; i++) {\n        result += atan_const[i] * powf(t, (i * 2) + 1);\n    }\n\n    return result;\n}\n\nstatic inline void vu_update_status(struct vu_state* vu) {\n    vu->status &= ~0x3f;\n\n    vu->status |= (vu->mac_pipeline[3] & 0x000f) ? 1 : 0;\n    vu->status |= (vu->mac_pipeline[3] & 0x00f0) ? 2 : 0;\n    vu->status |= (vu->mac_pipeline[3] & 0x0f00) ? 4 : 0;\n    vu->status |= (vu->mac_pipeline[3] & 0xf000) ? 8 : 0;\n\n    vu->status |= (vu->status & 0x3f) << 6;\n}\n\nstatic inline void vu_set_q(struct vu_state* vu, float value, int delay) {\n    if (vu->q_delay == 0) {\n        vu->prev_q.f = vu->q.f;\n    }\n\n    vu->q.f = value;\n    vu->q_delay = delay;\n}\n\nstatic inline struct vu_reg32 vu_get_q(struct vu_state* vu) {\n    if (!vu->q_delay) {\n        return vu->q;\n    }\n\n    return vu->prev_q;\n}\n\nstatic inline float vu_update_flags(struct vu_state* vu, float value, int index) {\n    uint32_t value_u = *(uint32_t*)&value;\n\n    int flag_id = 3 - index;\n\n    // Sign flag\n    if (value_u & 0x80000000)\n        vu->mac |= 0x10 << flag_id;\n    else\n        vu->mac &= ~(0x10 << flag_id);\n\n    // Zero flag, clear under/overflow\n    if ((value_u & 0x7FFFFFFF) == 0) {\n        vu->mac |= 1 << flag_id;\n        vu->mac &= ~(0x1100 << flag_id);\n\n        return value;\n    }\n\n    switch ((value_u >> 23) & 0xFF) {\n        //Underflow, set zero\n        case 0:\n            vu->mac |= 0x101 << flag_id;\n            vu->mac &= ~(0x1000 << flag_id);\n            value_u = value_u & 0x80000000;\n            break;\n        //Overflow\n        case 255:\n            vu->mac |= 0x1000 << flag_id;\n            vu->mac &= ~(0x101 << flag_id);\n            value_u = (value_u & 0x80000000) | 0x7F7FFFFF;\n            break;\n        //Clear all but sign\n        default:\n            vu->mac &= ~(0x1101 << flag_id);\n            break;\n    }\n\n    return *(float*)&value_u;\n}\n\nstatic inline void vu_clear_flags(struct vu_state* vu, int index) {\n    vu->mac &= ~(0x1111 << (3 - index));\n}\n\nstatic inline float vu_cvtf(uint32_t value) {\n    switch (value & 0x7f800000) {\n        case 0x0: {\n            value &= 0x80000000;\n\n            return *(float*)&value;\n        } break;\n\n        case 0x7f800000: {\n            uint32_t result = (value & 0x80000000) | 0x7f7fffff;\n\n            return *(float*)&result;\n        }\n    }\n\n    return *(float*)&value;\n}\n\nint32_t vu_cvti(float value) {\n    if (value >= 2147483647.0)\n        return 2147483647LL;\n\n    if (value <= -2147483648.0)\n        return -2147483648LL;\n\n    return (int32_t)value;\n}\n\nstatic inline void vu_set_vf(struct vu_state* vu, int r, int f, float v) {\n    if (r) vu->vf[r].f[f] = v;\n}\n\nstatic inline void vu_set_vfu(struct vu_state* vu, int r, int f, int32_t v) {\n    if (r) vu->vf[r].s32[f] = v;\n}\n\nstatic inline void vu_set_vf_x(struct vu_state* vu, int r, float v) {\n    if (r) vu->vf[r].x = v;\n}\n\nstatic inline void vu_set_vf_y(struct vu_state* vu, int r, float v) {\n    if (r) vu->vf[r].y = v;\n}\n\nstatic inline void vu_set_vf_z(struct vu_state* vu, int r, float v) {\n    if (r) vu->vf[r].z = v;\n}\n\nstatic inline void vu_set_vf_w(struct vu_state* vu, int r, float v) {\n    if (r) vu->vf[r].w = v;\n}\n\nstatic inline void vu_set_vi(struct vu_state* vu, int r, uint16_t v) {\n    if (r) vu->vi[r] = v;\n}\n\nstatic inline float vu_vf_i(struct vu_state* vu, int r, int i) {\n    return vu_cvtf(vu->vf[r].u32[i]);\n}\n\nstatic inline float vu_vf_x(struct vu_state* vu, int r) {\n    return vu_cvtf(vu->vf[r].u32[0]);\n}\n\nstatic inline float vu_vf_y(struct vu_state* vu, int r) {\n    return vu_cvtf(vu->vf[r].u32[1]);\n}\n\nstatic inline float vu_vf_z(struct vu_state* vu, int r) {\n    return vu_cvtf(vu->vf[r].u32[2]);\n}\n\nstatic inline float vu_vf_w(struct vu_state* vu, int r) {\n    return vu_cvtf(vu->vf[r].u32[3]);\n}\n\nstatic inline float vu_acc_i(struct vu_state* vu, int i) {\n    return vu_cvtf(vu->acc.u32[i]);\n}\n\nstatic inline void vu_mem_write(struct vu_state* vu, uint16_t addr, uint32_t data, int i) {\n    if (!vu->id) {\n        if (addr <= 0x3ff) {\n            vu->vu_mem[addr & 0xff].u32[i] = data;\n        } else {\n            if ((addr >= 0x400) && (addr <= 0x41f)) {\n                vu->vu1->vf[addr & 0x1f].u32[i] = data;\n            } else if ((addr >= 0x420) && (addr <= 0x42f)) {\n                vu->vu1->vi[addr & 0xf] = data;\n            } else if (addr == 0x430) {\n                vu->vu1->status = data;\n            } else if (addr == 0x431) {\n                vu->vu1->mac = data;\n            } else if (addr == 0x432) {\n                vu->vu1->clip = data;\n            } else if (addr == 0x434) {\n                vu->vu1->r.u32 = data;\n            } else if (addr == 0x435) {\n                vu->vu1->i.u32 = data;\n            } else if (addr == 0x436) {\n                vu->vu1->q.u32 = data;\n            } else if (addr == 0x437) {\n                vu->vu1->p.u32 = data;\n            } else if (addr == 0x43a) {\n                vu->vu1->tpc = data;\n            } else {\n                // printf(\"vu: oob write\\n\");\n\n                // exit(1);\n            }\n        }\n    } else {\n        // if (addr == 0x000001d3) *(int*)0 = 0;\n\n        vu->vu_mem[addr & 0x3ff].u32[i] = data;\n    }\n}\n\nstatic inline uint128_t vu_mem_read(struct vu_state* vu, uint32_t addr) {\n    if (!vu->id) {\n        if (addr <= 0x3ff) {\n            return vu->vu_mem[addr & 0xff];\n        } else {\n            if ((addr >= 0x400) && (addr <= 0x41f)) {\n                return vu->vu1->vf[addr & 0x1f].u128;\n            } else if ((addr >= 0x420) && (addr <= 0x42f)) {\n                uint128_t result; result.u32[0] = vu->vu1->vi[addr & 0xf];\n                return result;\n            } else if (addr == 0x430) {\n                uint128_t result; result.u32[0] = vu->vu1->status;\n                return result;\n            } else if (addr == 0x431) {\n                uint128_t result; result.u32[0] = vu->vu1->mac;\n                return result;\n            } else if (addr == 0x432) {\n                uint128_t result; result.u32[0] = vu->vu1->clip;\n                return result;\n            } else if (addr == 0x434) {\n                uint128_t result; result.u32[0] = vu->vu1->r.u32;\n                return result;\n            } else if (addr == 0x435) {\n                uint128_t result; result.u32[0] = vu->vu1->i.u32;\n                return result;\n            } else if (addr == 0x436) {\n                uint128_t result; result.u32[0] = vu->vu1->q.u32;\n                return result;\n            } else if (addr == 0x437) {\n                uint128_t result; result.u32[0] = vu->vu1->p.u32;\n                return result;\n            } else if (addr == 0x43a) {\n                uint128_t result; result.u32[0] = vu->vu1->tpc;\n                return result;\n            }\n        }\n    }\n\n    return vu->vu_mem[addr & 0x3ff];\n}\n\nstatic inline void vu_write_branch_pipeline(struct vu_state* vu, int dst) {\n    if (!dst)\n        return;\n\n    //On repeat writes we need to remember the value from before the chain\n    if (vu->vi_backup_cycles && dst == vu->vi_backup_reg) {\n        vu->vi_backup_cycles = 2;\n\n        return;\n    }\n\n    vu->vi_backup_cycles = 2;\n    vu->vi_backup_reg = dst;\n    vu->vi_backup_value = vu->vi[dst];\n\n    // printf(\"branch pipeline: dst=%d prev=%04x rw=%d\\n\",\n    //     vu->branch_pipeline_curr.reg, vu->branch_pipeline_curr.prev,\n    //     vu->branch_pipeline_curr.rw\n    // );\n}\n\nstatic inline uint16_t vu_get_branch_register(struct vu_state* vu, int reg) {\n    if (vu->vi_backup_cycles && (vu->vi_backup_reg == reg)) {\n        return vu->vi_backup_value;\n    }\n\n    return vu->vi[reg];\n}\n\nvoid vu_xgkick(struct vu_state* vu) {\n    uint16_t addr = vu->xgkick_addr;\n\n    int eop = 1;\n\n    do {\n        uint128_t tag = vu_mem_read(vu, addr++);\n\n        if ((tag.u64[0] | tag.u64[1]) == 0)\n            break;\n\n        // addr &= 0x3ff;\n\n        // if (addr == 0) break;\n\n        // printf(\"tag: addr=%08x %08x %08x %08x %08x\\n\", addr - 1, tag.u32[3], tag.u32[2], tag.u32[1], tag.u32[0]);\n\n        ps2_gif_fifo_write(vu->gif, tag, GIF_PATH1);\n\n        eop = (tag.u64[0] & 0x8000) != 0;\n\n        int nloop = tag.u64[0] & 0x7fff;\n        int flg = (tag.u64[0] >> 58) & 3;\n        int nregs = (tag.u64[0] >> 60) & 0xf;\n\n        if (!nloop)\n            continue;\n\n        // if (!nregs)\n        //     nregs = 16;\n\n        int qwc = 0;\n\n        switch (flg) {\n            case 0: {\n                qwc = nregs * nloop;\n            } break;\n            case 1: {\n                qwc = (nregs * nloop + 1) / 2; // Round up for odd cases\n            } break;\n            case 2:\n            case 3: {\n                qwc = nloop;\n            } break;\n        }\n\n        if (qwc >= 0x400) {\n            fprintf(stderr, \"vu: Weird xgkick tag nloop=%d nregs=%d eop=%d flg=%d qwc=%d\\n\",\n                nloop,\n                nregs,\n                eop,\n                flg,\n                qwc\n            ); \n\n            exit(1);\n        }\n\n        for (int i = 0; i < qwc; i++) {\n            // printf(\"vu: %08x: %08x %08x %08x %08x\\n\",\n            //     addr,\n            //     vu->vu_mem[addr].u32[3],\n            //     vu->vu_mem[addr].u32[2],\n            //     vu->vu_mem[addr].u32[1],\n            //     vu->vu_mem[addr].u32[0]\n            // );\n\n            ps2_gif_fifo_write(vu->gif, vu_mem_read(vu, addr++), GIF_PATH1);\n\n            addr &= 0x3ff;\n\n            // if (addr == 0) {\n            //     eop = 1;\n            //     break;\n            // }\n        }\n    } while (!eop);\n}\n\n// Upper pipeline\nvoid vu_i_abs(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) vu_set_vf(vu, t, i, fabsf(vu_vf_i(vu, s, i)));\n    }\n}\nvoid vu_i_add(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + vu_vf_i(vu, t, i);\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_addi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + vu->i.f;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_addq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + q;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_addx(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_addy(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_addz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_addw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_adda(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + vu_vf_i(vu, t, i);\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_addai(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + vu->i.f;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_addaq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + q;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_addax(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_adday(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_addaz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_addaw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) + bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_sub(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - vu_vf_i(vu, t, i);\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_subi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - vu->i.f;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_subq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - q;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_subx(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_suby(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_subz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_subw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_suba(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - vu_vf_i(vu, t, i);\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_subai(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - vu->i.f;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_subaq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - q;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_subax(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n            \n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_subay(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n            \n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_subaz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n            \n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_subaw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) - bc;\n            \n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_mul(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i);\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_muli(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * vu->i.f;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_mulq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * q;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_mulx(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_muly(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_mulz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_mulw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_mula(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i);\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_mulai(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * vu->i.f;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_mulaq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * q;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_mulax(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_mulay(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_mulaz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_mulaw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_madd(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i);\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_maddi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu->i.f;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_maddq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n    float q = vu_get_q(vu).f;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * q;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_maddx(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_x(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_maddy(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_y(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_maddz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_z(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_maddw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_w(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_madda(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i);\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_maddai(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu->i.f;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_maddaq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu->acc.f[i] + vu_vf_i(vu, s, i) * q;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_maddax(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_madday(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_maddaz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_maddaw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msub(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i);\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msubi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu->i.f;\n            \n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msubq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n    float q = vu_get_q(vu).f;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - (vu_vf_i(vu, s, i) * q);\n\n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msubx(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_x(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n            \n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msuby(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_y(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n            \n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msubz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_z(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n            \n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msubw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    float bc = vu_vf_w(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n            \n            vu_set_vf(vu, d, i, vu_update_flags(vu, result, i));\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msuba(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i);\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msubai(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu->i.f;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msubaq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    float q = vu_get_q(vu).f;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu->acc.f[i] - vu_vf_i(vu, s, i) * q;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msubax(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_x(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msubay(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_y(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msubaz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_z(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_msubaw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    float bc = vu_vf_w(vu, t);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc;\n\n            vu->acc.f[i] = vu_update_flags(vu, result, i);\n        } else {\n            vu_clear_flags(vu, i);\n        }\n    }\n\n    vu_update_status(vu);\n}\nvoid vu_i_max(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], vu->vf[t].s32[i]);\n        }\n    }\n}\nvoid vu_i_maxi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], vu->i.s32);\n        }\n    }\n}\nvoid vu_i_maxx(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[0];\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float fs = vu_vf_i(vu, s, i);\n\n            vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc);\n        }\n    }\n}\nvoid vu_i_maxy(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[1];\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float fs = vu_vf_i(vu, s, i);\n\n            vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc);\n        }\n    }\n}\nvoid vu_i_maxz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[2];\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float fs = vu_vf_i(vu, s, i);\n\n            vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc);\n        }\n    }\n}\nvoid vu_i_maxw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[3];\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            float fs = vu_vf_i(vu, s, i);\n\n            vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc);\n        }\n    }\n}\nvoid vu_i_mini(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], vu->vf[t].s32[i]);\n        }\n    }\n}\nvoid vu_i_minii(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], vu->i.s32);\n        }\n    }\n}\nvoid vu_i_minix(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[0];\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc);\n        }\n    }\n}\nvoid vu_i_miniy(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[1];\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc);\n        }\n    }\n}\nvoid vu_i_miniz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[2];\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc);\n        }\n    }\n}\nvoid vu_i_miniw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n    int d = VU_UD_D;\n\n    if (!d) return;\n\n    int32_t bc = vu->vf[t].s32[3];\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) {\n            vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc);\n        }\n    }\n}\nvoid vu_i_opmula(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    vu->acc.x = vu_vf_y(vu, s) * vu_vf_z(vu, t);\n    vu->acc.y = vu_vf_z(vu, s) * vu_vf_x(vu, t);\n    vu->acc.z = vu_vf_x(vu, s) * vu_vf_y(vu, t);\n\n    vu->acc.x = vu_cvtf(vu->acc.u32[0]);\n    vu->acc.y = vu_cvtf(vu->acc.u32[1]);\n    vu->acc.z = vu_cvtf(vu->acc.u32[2]);\n\n    vu->acc.x = vu_update_flags(vu, vu->acc.x, 0);\n    vu->acc.y = vu_update_flags(vu, vu->acc.y, 1);\n    vu->acc.z = vu_update_flags(vu, vu->acc.z, 2);\n\n    // printf(\"s(%d)=%08x %08x %08x t(%d)=%08x %08x %08x acc=%08x %08x %08x prev=%08x %08x %08x\\n\",\n    //     s,\n    //     vu->vf[s].u32[0],\n    //     vu->vf[s].u32[1],\n    //     vu->vf[s].u32[2],\n    //     t,\n    //     vu->vf[t].u32[0],\n    //     vu->vf[t].u32[1],\n    //     vu->vf[t].u32[2],\n    //     vu->acc.u32[0],\n    //     vu->acc.u32[1],\n    //     vu->acc.u32[2],\n    //     acc.u32[0],\n    //     acc.u32[1],\n    //     acc.u32[2]\n    // );\n\n    vu_clear_flags(vu, 3);\n    vu_update_status(vu);\n}\nvoid vu_i_opmsub(struct vu_state* vu, const struct vu_instruction* ins) {\n    int d = VU_UD_D;\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    struct vu_reg128 tmp;\n\n    tmp.f[0] = vu->acc.x - vu_vf_y(vu, s) * vu_vf_z(vu, t);\n    tmp.f[1] = vu->acc.y - vu_vf_z(vu, s) * vu_vf_x(vu, t);\n    tmp.f[2] = vu->acc.z - vu_vf_x(vu, s) * vu_vf_y(vu, t);\n\n    vu_set_vf_x(vu, d, vu_update_flags(vu, tmp.f[0], 0));\n    vu_set_vf_y(vu, d, vu_update_flags(vu, tmp.f[1], 1));\n    vu_set_vf_z(vu, d, vu_update_flags(vu, tmp.f[2], 2));\n\n    // printf(\"s(%d)=%08x %08x %08x t(%d)=%08x %08x %08x d=%08x %08x %08x dp=%08x %08x %08x\\n\",\n    //     s,\n    //     vu->vf[s].u32[0],\n    //     vu->vf[s].u32[1],\n    //     vu->vf[s].u32[2],\n    //     t,\n    //     vu->vf[t].u32[0],\n    //     vu->vf[t].u32[1],\n    //     vu->vf[t].u32[2],\n    //     vu->vf[d].u32[0],\n    //     vu->vf[d].u32[1],\n    //     vu->vf[d].u32[2],\n    //     dv.u32[0],\n    //     dv.u32[1],\n    //     dv.u32[2]\n    // );\n\n    vu_clear_flags(vu, 3);\n    vu_update_status(vu);\n}\nvoid vu_i_nop(struct vu_state* vu, const struct vu_instruction* ins) {\n    // No operation\n}\nvoid vu_i_ftoi0(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i)));\n    }\n}\nvoid vu_i_ftoi4(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.0625f)));\n    }\n}\nvoid vu_i_ftoi12(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.000244140625f)));\n    }\n}\nvoid vu_i_ftoi15(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.000030517578125f)));\n    }\n}\nvoid vu_i_itof0(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) vu_set_vf(vu, t, i, (float)vu->vf[s].s32[i]);\n    }\n}\nvoid vu_i_itof4(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.0625f));\n    }\n}\nvoid vu_i_itof12(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.000244140625f));\n    }\n}\nvoid vu_i_itof15(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_UD_S;\n    int t = VU_UD_T;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_UD_DI(i)) vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.000030517578125f));\n    }\n}\nvoid vu_i_clip(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_UD_T;\n    int s = VU_UD_S;\n\n    vu->clip <<= 6;\n\n    float w = fabsf(vu_vf_w(vu, t));\n    float x = vu_vf_x(vu, s);\n    float y = vu_vf_y(vu, s);\n    float z = vu_vf_z(vu, s);\n\n    vu->clip |= (x > +w);\n    vu->clip |= (x < -w) << 1;\n    vu->clip |= (y > +w) << 2;\n    vu->clip |= (y < -w) << 3;\n    vu->clip |= (z > +w) << 4;\n    vu->clip |= (z < -w) << 5;\n    vu->clip &= 0xFFFFFF;\n}\n\n// Lower pipeline\nvoid vu_i_b(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->next_tpc = vu->tpc + VU_LD_IMM11;\n}\nvoid vu_i_bal(struct vu_state* vu, const struct vu_instruction* ins) {\n    // Instruction next to the delay slot\n    VU_IT = vu->tpc + 1;\n\n    vu->next_tpc = vu->tpc + VU_LD_IMM11;\n}\nvoid vu_i_div(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n    int s = VU_LD_S;\n    int tf = VU_LD_TF;\n    int sf = VU_LD_SF;\n\n    struct vu_reg32 q;\n\n    q.f = vu_vf_i(vu, s, sf) / vu_vf_i(vu, t, tf);\n    q.f = vu_cvtf(q.u32);\n\n    vu_set_q(vu, q.f, 7);\n}\nvoid vu_i_eatan(struct vu_state* vu, const struct vu_instruction* ins) {\n    float x = vu_vf_i(vu, VU_LD_S, VU_LD_SF);\n\n    if (x == -1.0f) {\n        vu->p.u32 = 0xFF7FFFFF;\n    } else {\n        x = (x - 1.0f) / (x + 1.0f);\n\n        vu->p.f = vu_atan(x);\n    }\n}\nvoid vu_i_eatanxy(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    float x = vu_vf_x(vu, s);\n    float y = vu_vf_y(vu, s);\n\n    if (y + x == 0.0f) {\n        vu->p.u32 = 0x7F7FFFFF | (vu->vf[s].u32[1] & 0x80000000);\n    } else {\n        x = (y - 1.0f) / (y + x);\n\n        vu->p.f = vu_atan(x);\n    }\n}\nvoid vu_i_eatanxz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    float x = vu_vf_x(vu, s);\n    float z = vu_vf_z(vu, s);\n\n    //P = atan(z/x)\n    if (z + x == 0.0f) {\n        vu->p.u32 = 0x7F7FFFFF | (vu->vf[s].u32[2] & 0x80000000);\n    } else {\n        x = (z - x) / (z + x);\n\n        vu->p.f = vu_atan(x);\n    }\n}\nvoid vu_i_eexp(struct vu_state* vu, const struct vu_instruction* ins) {\n    const static float coeffs[] = {\n        0.249998688697815f, 0.031257584691048f,\n        0.002591371303424f, 0.000171562001924f,\n        0.000005430199963f, 0.000000690600018f\n    };\n\n    int s = VU_LD_S;\n    int sf = VU_LD_SF;\n\n    if (vu->vf[s].u32[sf] & 0x80000000) {\n        vu->p.f = vu_vf_i(vu, s, sf);\n\n        return;\n    }\n\n    float value = 1;\n    float x = vu_vf_i(vu, s, sf);\n\n    for (int exp = 1; exp <= 6; exp++)\n        value += coeffs[exp - 1] * pow(x, exp);\n\n    vu->p.f = 1.0 / value;\n}\nvoid vu_i_eleng(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n\n    float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s);\n    float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s);\n    float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s);\n\n    vu->p.f = sqrtf(x2 + y2 + z2);\n}\nvoid vu_i_ercpr(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->p.f = 1.0f / vu_vf_i(vu, VU_LD_S, VU_LD_SF);\n}\nvoid vu_i_erleng(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n\n    float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s);\n    float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s);\n    float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s);\n\n    vu->p.f = 1.0f / sqrtf(x2 + y2 + z2);\n}\nvoid vu_i_ersadd(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n\n    float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s);\n    float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s);\n    float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s);\n\n    vu->p.f = 1.0f / (x2 + y2 + z2);\n}\nvoid vu_i_ersqrt(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->p.f = 1.0f / sqrtf(vu_vf_i(vu, VU_LD_S, VU_LD_SF));\n}\nvoid vu_i_esadd(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n\n    float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s);\n    float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s);\n    float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s);\n\n    vu->p.f = x2 + y2 + z2;\n}\nvoid vu_i_esin(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->p.f = sinf(vu_vf_i(vu, VU_LD_S, VU_LD_SF));\n}\nvoid vu_i_esqrt(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->p.f = sqrtf(vu_vf_i(vu, VU_LD_S, VU_LD_SF));\n}\nvoid vu_i_esum(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n\n    vu->p.f = vu_vf_x(vu, s) + vu_vf_y(vu, s) + vu_vf_z(vu, s) + vu_vf_w(vu, s);\n}\nvoid vu_i_fcand(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->vi[1] = ((vu->clip & 0xffffff) & VU_LD_IMM24) != 0;\n}\nvoid vu_i_fceq(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->vi[1] = (vu->clip & 0xffffff) == VU_LD_IMM24;\n}\nvoid vu_i_fcget(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    vu->vi[VU_LD_T] = vu->clip & 0xfff;\n}\nvoid vu_i_fcor(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->vi[1] = ((vu->clip & 0xffffff) | VU_LD_IMM24) == 0xffffff;\n}\nvoid vu_i_fcset(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->clip = VU_LD_IMM24;\n}\nvoid vu_i_fmand(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, vu->mac_pipeline[3] & VU_IS);\n}\nvoid vu_i_fmeq(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, (VU_IS & 0xffff) == (vu->mac_pipeline[3] & 0xffff));\n}\nvoid vu_i_fmor(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, (VU_IS & 0xffff) | (vu->mac_pipeline[3] & 0xffff));\n}\nvoid vu_i_fsand(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, vu->status & VU_LD_IMM12);\n}\nvoid vu_i_fseq(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, (vu->status & 0xfff) == VU_LD_IMM12);\n}\nvoid vu_i_fsor(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, (vu->status & 0xfff) | VU_LD_IMM12);\n}\nvoid vu_i_fsset(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->status &= 0x3f;\n    vu->status |= VU_LD_IMM12 & 0xfc0;\n}\nvoid vu_i_iadd(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_D);\n\n    vu_set_vi(vu, VU_LD_D, VU_IS + VU_IT);\n}\nvoid vu_i_iaddi(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_T);\n\n    vu_set_vi(vu, VU_LD_T, VU_IS + VU_LD_IMM5);\n}\nvoid vu_i_iaddiu(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_T);\n\n    vu_set_vi(vu, VU_LD_T, VU_IS + VU_LD_IMM15);\n}\nvoid vu_i_iand(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_T);\n\n    vu_set_vi(vu, VU_LD_D, VU_IS & VU_IT);\n}\nvoid vu_i_ibeq(struct vu_state* vu, const struct vu_instruction* ins) {\n    uint16_t t = vu_get_branch_register(vu, VU_LD_T);\n    uint16_t s = vu_get_branch_register(vu, VU_LD_S);\n\n    if (t == s) {\n        vu->next_tpc = vu->tpc + VU_LD_IMM11;\n    }\n}\nvoid vu_i_ibgez(struct vu_state* vu, const struct vu_instruction* ins) {\n    int16_t s = vu_get_branch_register(vu, VU_LD_S);\n\n    if (s >= 0) {\n        vu->next_tpc = vu->tpc + VU_LD_IMM11;\n    }\n}\nvoid vu_i_ibgtz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int16_t s = vu_get_branch_register(vu, VU_LD_S);\n\n    if (s > 0) {\n        vu->next_tpc = vu->tpc + VU_LD_IMM11;\n    }\n}\nvoid vu_i_iblez(struct vu_state* vu, const struct vu_instruction* ins) {\n    int16_t s = vu_get_branch_register(vu, VU_LD_S);\n\n    if (s <= 0) {\n        vu->next_tpc = vu->tpc + VU_LD_IMM11;\n    }\n}\nvoid vu_i_ibltz(struct vu_state* vu, const struct vu_instruction* ins) {\n    int16_t s = vu_get_branch_register(vu, VU_LD_S);\n\n    if (s < 0) {\n        vu->next_tpc = vu->tpc + VU_LD_IMM11;\n    }\n}\nvoid vu_i_ibne(struct vu_state* vu, const struct vu_instruction* ins) {\n    uint16_t t = vu_get_branch_register(vu, VU_LD_T);\n    uint16_t s = vu_get_branch_register(vu, VU_LD_S);\n\n    // printf(\"ibne vi%02u (%04x), vi%02u (%04x), 0x%08x\\n\", VU_LD_T, t, VU_LD_S, s, vu->tpc + VU_LD_IMM11);\n\n    if (t != s) {\n        vu->next_tpc = vu->tpc + VU_LD_IMM11;\n    }\n}\nvoid vu_i_ilw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    uint32_t addr = VU_IS + VU_LD_IMM11;\n    uint128_t data = vu_mem_read(vu, addr);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu->vi[t] = data.u32[i];\n    }\n}\nvoid vu_i_ilwr(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    uint32_t addr = vu->vi[s];\n    uint128_t data = vu_mem_read(vu, addr);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu->vi[t] = data.u32[i];\n    }\n}\nvoid vu_i_ior(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_D);\n\n    vu_set_vi(vu, VU_LD_D, VU_IS | VU_IT);\n}\nvoid vu_i_isub(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_D);\n\n    vu_set_vi(vu, VU_LD_D, VU_IS - VU_IT);\n}\nvoid vu_i_isubiu(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_T);\n\n    vu_set_vi(vu, VU_LD_T, VU_IS - VU_LD_IMM15);\n}\nvoid vu_i_isw(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    uint32_t addr = vu->vi[s] + VU_LD_IMM11;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vi[t], i);\n    }\n}\nvoid vu_i_iswr(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    uint32_t addr = vu->vi[s];\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vi[t], i);\n    }\n}\nvoid vu_i_jalr(struct vu_state* vu, const struct vu_instruction* ins) {\n    uint16_t s = VU_IS;\n\n    VU_IT = vu->tpc + 1;\n\n    vu->next_tpc = s;\n}\nvoid vu_i_jr(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->next_tpc = VU_IS;\n}\nvoid vu_i_lq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    uint32_t addr = vu->vi[s] + VU_LD_IMM11;\n    uint128_t data = vu_mem_read(vu, addr);\n\n    if (!t) return;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu->vf[t].u32[i] = data.u32[i];\n    }\n}\nvoid vu_i_lqd(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    vu_write_branch_pipeline(vu, s);\n\n    vu_set_vi(vu, s, vu->vi[s] - 1);\n\n    uint32_t addr = vu->vi[s];\n    uint128_t data = vu_mem_read(vu, addr);\n\n    if (!t) return;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu->vf[t].u32[i] = data.u32[i];\n    }\n}\nvoid vu_i_lqi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    vu_write_branch_pipeline(vu, s);\n\n    if (t) {\n        uint32_t addr = vu->vi[s];\n        uint128_t data = vu_mem_read(vu, addr);\n\n        for (int i = 0; i < 4; i++) {\n            if (VU_LD_DI(i)) vu->vf[t].u32[i] = data.u32[i];\n        }\n    }\n\n    vu_set_vi(vu, s, vu->vi[s] + 1);\n}\nvoid vu_i_mfir(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu->vf[t].u32[i] = (int32_t)(int16_t)VU_IS;\n    }\n}\nvoid vu_i_mfp(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu->vf[t].u32[i] = vu->p.f;\n    }\n}\nvoid vu_i_move(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu->vf[t].u32[i] = vu->vf[s].u32[i];\n    }\n}\nvoid vu_i_mr32(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    int s = VU_LD_S;\n\n    uint32_t x = vu->vf[s].u32[0];\n\n    // for (int i = 0; i < 4; i++) {\n    //     if (VU_LD_DI(i)) vu->vf[t].u32[i] = rs.u32[(i + 1) & 3];\n    // }\n\n    if (VU_LD_DI(0)) vu->vf[t].u32[0] = vu->vf[s].u32[1];\n    if (VU_LD_DI(1)) vu->vf[t].u32[1] = vu->vf[s].u32[2];\n    if (VU_LD_DI(2)) vu->vf[t].u32[2] = vu->vf[s].u32[3];\n    if (VU_LD_DI(3)) vu->vf[t].u32[3] = x;\n}\nvoid vu_i_mtir(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_write_branch_pipeline(vu, VU_LD_T);\n\n    vu_set_vi(vu, VU_LD_T, vu->vf[VU_LD_S].u32[VU_LD_SF] & 0xffff);\n}\nvoid vu_i_rget(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu->vf[t].u32[i] = vu->r.u32;\n    }\n}\nvoid vu_i_rinit(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n\n    vu->r.u32 = 0x3f800000;\n\n    if (!s) return;\n\n    vu->r.u32 |= vu->vf[s].u32[VU_LD_SF] & 0x007fffff;\n}\nvoid vu_i_rnext(struct vu_state* vu, const struct vu_instruction* ins) {\n    int t = VU_LD_T;\n\n    if (!t) return;\n\n    int x = (vu->r.u32 >> 4) & 1;\n    int y = (vu->r.u32 >> 22) & 1;\n\n    vu->r.u32 <<= 1;\n    vu->r.u32 ^= x ^ y;\n    vu->r.u32 = (vu->r.u32 & 0x7FFFFF) | 0x3F800000;\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu->vf[t].u32[i] = vu->r.u32;\n    }\n}\nvoid vu_i_rsqrt(struct vu_state* vu, const struct vu_instruction* ins) {\n    struct vu_reg32 q;\n\n    q.f = vu_vf_i(vu, VU_LD_S, VU_LD_SF) / sqrtf(vu_vf_i(vu, VU_LD_T, VU_LD_TF));\n    q.f = vu_cvtf(q.u32);\n\n    vu_set_q(vu, q.f, 13);\n}\nvoid vu_i_rxor(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->r.u32 = 0x3F800000 | ((vu->r.u32 ^ vu->vf[VU_LD_S].u32[VU_LD_SF]) & 0x007FFFFF);\n}\nvoid vu_i_sq(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    uint32_t addr = vu->vi[t] + VU_LD_IMM11;\n\n    // printf(\"vu: sq addr=%08x vf%02d=%08x %08x %08x %08x\\n\", addr, s, vu->vf[s].u32[3], vu->vf[s].u32[2], vu->vf[s].u32[1], vu->vf[s].u32[0]);\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vf[s].u32[i], i);\n    }\n}\nvoid vu_i_sqd(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    vu_write_branch_pipeline(vu, t);\n\n    vu_set_vi(vu, t, vu->vi[t] - 1);\n\n    uint32_t addr = vu->vi[t];\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vf[s].u32[i], i);\n    }\n}\nvoid vu_i_sqi(struct vu_state* vu, const struct vu_instruction* ins) {\n    int s = VU_LD_S;\n    int t = VU_LD_T;\n\n    vu_write_branch_pipeline(vu, t);\n\n    uint32_t addr = vu->vi[t];\n\n    for (int i = 0; i < 4; i++) {\n        if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vf[s].u32[i], i);\n    }\n\n    vu_set_vi(vu, t, vu->vi[t] + 1);\n}\nvoid vu_i_sqrt(struct vu_state* vu, const struct vu_instruction* ins) {\n    struct vu_reg32 q;\n\n    q.f = sqrtf(vu_vf_i(vu, VU_LD_T, VU_LD_TF));\n    q.f = vu_cvtf(q.u32);\n\n    vu_set_q(vu, q.f, 7);\n}\nvoid vu_i_waitp(struct vu_state* vu, const struct vu_instruction* ins) {\n    // No operation\n}\nvoid vu_i_waitq(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu->q_delay = 0;\n}\n\nvoid vu_i_xgkick(struct vu_state* vu, const struct vu_instruction* ins) {\n    // vu_xgkick(vu);\n    // vu->xgkick_pending = 3;\n    // vu->xgkick_addr = VU_IS;\n\n    // return;\n\n    uint16_t addr = VU_IS;\n\n    int eop = 1;\n\n    do {\n        uint128_t tag = vu_mem_read(vu, addr++);\n\n        if ((tag.u64[0] | tag.u64[1]) == 0)\n            break;\n\n        // addr &= 0x3ff;\n\n        // if (addr == 0) break;\n\n        // printf(\"tag: addr=%08x %08x %08x %08x %08x\\n\", addr - 1, tag.u32[3], tag.u32[2], tag.u32[1], tag.u32[0]);\n\n        ps2_gif_fifo_write(vu->gif, tag, GIF_PATH1);\n\n        eop = (tag.u64[0] & 0x8000) != 0;\n\n        int nloop = tag.u64[0] & 0x7fff;\n        int flg = (tag.u64[0] >> 58) & 3;\n        int nregs = (tag.u64[0] >> 60) & 0xf;\n\n        if (!nloop)\n            continue;\n\n        // if (!nregs)\n        //     nregs = 16;\n\n        int qwc = 0;\n\n        switch (flg) {\n            case 0: {\n                qwc = nregs * nloop;\n            } break;\n            case 1: {\n                qwc = (nregs * nloop + 1) / 2; // Round up for odd cases\n            } break;\n            case 2:\n            case 3: {\n                qwc = nloop;\n            } break;\n        }\n\n        if (qwc >= 0x400) {\n            fprintf(stderr, \"vu: Weird xgkick tag nloop=%d nregs=%d eop=%d flg=%d qwc=%d\\n\",\n                nloop,\n                nregs,\n                eop,\n                flg,\n                qwc\n            ); \n\n            exit(1);\n        }\n\n        for (int i = 0; i < qwc; i++) {\n            // printf(\"vu: %08x: %08x %08x %08x %08x\\n\",\n            //     addr,\n            //     vu->vu_mem[addr].u32[3],\n            //     vu->vu_mem[addr].u32[2],\n            //     vu->vu_mem[addr].u32[1],\n            //     vu->vu_mem[addr].u32[0]\n            // );\n\n            ps2_gif_fifo_write(vu->gif, vu_mem_read(vu, addr++), GIF_PATH1);\n\n            addr &= 0x3ff;\n\n            // if (addr == 0) {\n            //     eop = 1;\n            //     break;\n            // }\n        }\n    } while (!eop);\n}\nvoid vu_i_xitop(struct vu_state* vu, const struct vu_instruction* ins) {\n    vu_set_vi(vu, VU_LD_T, vu->vif->itop);\n}\nvoid vu_i_xtop(struct vu_state* vu, const struct vu_instruction* ins) {\n    if (vu->id == 0) {\n        printf(\"vu: xtop used in VU0\\n\");\n\n        // exit(1);\n    }\n\n    vu_set_vi(vu, VU_LD_T, vu->vif->top);\n}\n\nuint64_t ps2_vu_read8(struct vu_state* vu, uint32_t addr) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        return *(uint8_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]);\n    }\n\n    uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n    return *(uint8_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]);\n}\nuint64_t ps2_vu_read16(struct vu_state* vu, uint32_t addr) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        return *(uint16_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]);\n    }\n\n    uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n    return *(uint16_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]);\n}\nuint64_t ps2_vu_read32(struct vu_state* vu, uint32_t addr) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        return *(uint32_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]);\n    }\n\n    uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n    return *(uint32_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]);\n}\nuint64_t ps2_vu_read64(struct vu_state* vu, uint32_t addr) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        return *(uint64_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]);\n    }\n\n    uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n    return *(uint64_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]);\n}\nuint128_t ps2_vu_read128(struct vu_state* vu, uint32_t addr) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        return *(uint128_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]);\n    }\n\n    uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n    return *(uint128_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]);\n}\nvoid ps2_vu_write8(struct vu_state* vu, uint32_t addr, uint64_t data) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        *(uint8_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data;\n    } else {\n        uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n        *(uint8_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data;\n    }\n}\nvoid ps2_vu_write16(struct vu_state* vu, uint32_t addr, uint64_t data) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        *(uint16_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data;\n    } else {\n        uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n        *(uint16_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data;\n    }\n}\nvoid ps2_vu_write32(struct vu_state* vu, uint32_t addr, uint64_t data) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        *(uint32_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data;\n    } else {\n        uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n        *(uint32_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data;\n    }\n}\nvoid ps2_vu_write64(struct vu_state* vu, uint32_t addr, uint64_t data) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        *(uint64_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data;\n    } else {\n        uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n        *(uint64_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data;\n    }\n}\nvoid ps2_vu_write128(struct vu_state* vu, uint32_t addr, uint128_t data) {\n    if (addr <= 0x3FFF) {\n        uint8_t* ptr = (uint8_t*)vu->micro_mem;\n\n        *(uint128_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data;\n    } else {\n        uint8_t* ptr = (uint8_t*)vu->vu_mem;\n\n        *(uint128_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data;\n    }\n}\n\n#define VU_FLD_X 1\n#define VU_FLD_Y 2\n#define VU_FLD_Z 4\n#define VU_FLD_W 8\n\n#define VU_DEC_UD_S_SRC_T_BROADCAST(bc, f) \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = bc; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(bc, f) \\\n    vu->upper.dst.reg = vu->upper.ud_d; \\\n    vu->upper.dst.field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = vu->upper.dst.field; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = bc; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_D_DST_S_SRC_T_SRC(f) \\\n    vu->upper.dst.reg = vu->upper.ud_d; \\\n    vu->upper.dst.field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = vu->upper.dst.field; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = vu->upper.dst.field; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_D_DST_S_SRC(f) \\\n    vu->upper.dst.reg = vu->upper.ud_d; \\\n    vu->upper.dst.field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = vu->upper.dst.field; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_D_DST_S_SRC_Q_SRC(f) \\\n    vu->upper.dst.reg = vu->upper.ud_d; \\\n    vu->upper.dst.field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = vu->upper.dst.field; \\\n    vu->upper.src[1].reg = VU_REG_Q; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_T_DST_S_SRC(f) \\\n    vu->upper.dst.reg = vu->upper.ud_t; \\\n    vu->upper.dst.field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = vu->upper.dst.field; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_S_SRC_T_SRC(f) \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = vu->upper.src[0].field; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_S_SRC(f) \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = (opcode >> 21) & 0xf; \\\n    vu->upper.func = f;\n\n#define VU_DEC_UD_S_SRC_Q_SRC(f) \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = (opcode >> 21) & 0xf; \\\n    vu->upper.src[1].reg = VU_REG_Q; \\\n    vu->upper.func = f;\n\n#define VU_DEC_OPMULA() \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = vu->upper.src[0].field; \\\n    vu->upper.func = vu_i_opmula;\n\n#define VU_DEC_OPMSUB() \\\n    vu->upper.dst.reg = vu->upper.ud_d; \\\n    vu->upper.dst.field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = vu->upper.dst.field; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = vu->upper.dst.field; \\\n    vu->upper.func = vu_i_opmsub;\n\n#define VU_DEC_CLIP() \\\n    vu->upper.src[0].reg = vu->upper.ud_s; \\\n    vu->upper.src[0].field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \\\n    vu->upper.src[1].reg = vu->upper.ud_t; \\\n    vu->upper.src[1].field = VU_FLD_W; \\\n    vu->upper.func = vu_i_clip;\n\n#define VU_DEC_LD_NONE(f) \\\n    vu->lower.func = f;\n\n#define VU_DEC_UD_NONE(f) \\\n    vu->upper.func = f;\n\n#define VU_DEC_LD_T_DST_S_VISRC(f) \\\n    vu->lower.dst.reg = vu->lower.ld_t; \\\n    vu->lower.dst.field = (opcode >> 21) & 0xf; \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_DST_S_VISRC_S_VIDST(f) \\\n    vu->lower.dst.reg = vu->lower.ld_t; \\\n    vu->lower.dst.field = (opcode >> 21) & 0xf; \\\n    vu->lower.vi_dst = vu->lower.ld_s; \\\n    vu->lower.vi_src[0] = vu->lower.vi_dst; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(f) \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = (opcode >> 21) & 0xf; \\\n    vu->lower.vi_dst = vu->lower.ld_t; \\\n    vu->lower.vi_src[0] = vu->lower.vi_dst; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_SF_SRC_T_TF_SRC(f) \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = vu->lower.ld_sf; \\\n    vu->lower.src[1].reg = vu->lower.ld_t; \\\n    vu->lower.src[1].field = vu->lower.ld_tf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_Q_DST_S_SF_SRC_T_TF_SRC(f) \\\n    vu->lower.dst.reg = VU_REG_Q; \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = vu->lower.ld_sf; \\\n    vu->lower.src[1].reg = vu->lower.ld_t; \\\n    vu->lower.src[1].field = vu->lower.ld_tf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_VIDST_S_SF_SRC(f) \\\n    vu->lower.vi_dst = vu->lower.ld_t; \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = vu->lower.ld_sf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_DST_S_VISRC(f) \\\n    vu->lower.dst.reg = vu->lower.ld_t; \\\n    vu->lower.dst.field = (opcode >> 21) & 0xf; \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_TF_SRC(f) \\\n    vu->lower.src[0].reg = vu->lower.ld_t; \\\n    vu->lower.src[0].field = vu->lower.ld_tf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_Q_DST_T_TF_SRC(f) \\\n    vu->lower.dst.reg = VU_REG_Q; \\\n    vu->lower.src[0].reg = vu->lower.ld_t; \\\n    vu->lower.src[0].field = vu->lower.ld_tf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_SRC_T_VISRC(f) \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = (opcode >> 21) & 0xf; \\\n    vu->lower.vi_src[0] = vu->lower.ld_t; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_VIDST_S_VISRC(f) \\\n    vu->lower.vi_dst = vu->lower.ld_t; \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_VISRC_T_VISRC(f) \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.vi_src[1] = vu->lower.ld_t; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_VIDST_S_VISRC(f) \\\n    vu->lower.vi_dst = vu->lower.ld_t; \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_VISRC_S_VISRC(f) \\\n    vu->lower.vi_src[0] = vu->lower.ld_t; \\\n    vu->lower.vi_src[1] = vu->lower.ld_s; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_VIDST(v, f) \\\n    vu->lower.vi_dst = v; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_VIDST(f) \\\n    vu->lower.vi_dst = vu->lower.ld_t; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_VISRC(f) \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_FLD_SRC(fld, f) \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = fld; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(f) \\\n    vu->lower.vi_dst = vu->lower.ld_d; \\\n    vu->lower.vi_src[0] = vu->lower.ld_s; \\\n    vu->lower.vi_src[1] = vu->lower.ld_t; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_DST_S_SRC(f) \\\n    vu->lower.dst.reg = vu->lower.ld_t; \\\n    vu->lower.dst.field = (opcode >> 21) & 0xf; \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = vu->lower.dst.field; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_T_DST(f) \\\n    vu->lower.dst.reg = vu->lower.ld_t; \\\n    vu->lower.dst.field = (opcode >> 21) & 0xf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_LD_S_SF_SRC(f) \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = vu->lower.ld_sf; \\\n    vu->lower.func = f;\n\n#define VU_DEC_MR32() \\\n    vu->lower.dst.reg = vu->lower.ld_t; \\\n    vu->lower.dst.field = (opcode >> 21) & 0xf; \\\n    vu->lower.src[0].reg = vu->lower.ld_s; \\\n    vu->lower.src[0].field = (vu->lower.dst.field >> 1) | ((vu->lower.dst.field & 1) << 3); \\\n    vu->lower.func = vu_i_mr32;\n\nvoid vu_decode_upper(struct vu_state* vu, uint32_t opcode) {\n    vu->upper.ud_d = (opcode >> 6) & 0x1f;\n    vu->upper.ud_s = (opcode >> 11) & 0x1f;\n    vu->upper.ud_t = (opcode >> 16) & 0x1f;\n\n    for (int i = 0; i < 4; i++)\n        vu->upper.ud_di[i] = opcode & (1 << (24 - i));\n\n    vu->upper.func = NULL;\n    vu->upper.dst.reg = 0;\n    vu->upper.dst.field = 0;\n    vu->upper.src[0].reg = 0;\n    vu->upper.src[0].field = 0;\n    vu->upper.src[1].reg = 0;\n    vu->upper.src[1].field = 0;\n\n    // Decode 000007FF style instruction\n    if ((opcode & 0x3c) == 0x3c) {\n        // 0EEEE 1111 EE\n        // -0EE EE11 11EE\n        // --------------\n        // bit 10 is always 0\n        // bits 2-5 are always 1\n        // --------------\n        // bits 0-1 and bits 6-9 (6 bits) are enough to decode\n        // all of the following\n        switch (((opcode & 0x3c0) >> 4) | (opcode & 3)) {\n            case 0x00: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_addax); return;\n            case 0x01: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_adday); return;\n            case 0x02: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_addaz); return;\n            case 0x03: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_addaw); return;\n            case 0x04: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_subax); return;\n            case 0x05: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_subay); return;\n            case 0x06: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_subaz); return;\n            case 0x07: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_subaw); return;\n            case 0x08: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_maddax); return;\n            case 0x09: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_madday); return;\n            case 0x0A: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_maddaz); return;\n            case 0x0B: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_maddaw); return;\n            case 0x0C: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_msubax); return;\n            case 0x0D: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_msubay); return;\n            case 0x0E: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_msubaz); return;\n            case 0x0F: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_msubaw); return;\n            case 0x10: VU_DEC_UD_T_DST_S_SRC(vu_i_itof0); return;\n            case 0x11: VU_DEC_UD_T_DST_S_SRC(vu_i_itof4); return;\n            case 0x12: VU_DEC_UD_T_DST_S_SRC(vu_i_itof12); return;\n            case 0x13: VU_DEC_UD_T_DST_S_SRC(vu_i_itof15); return;\n            case 0x14: VU_DEC_UD_T_DST_S_SRC(vu_i_ftoi0); return;\n            case 0x15: VU_DEC_UD_T_DST_S_SRC(vu_i_ftoi4); return;\n            case 0x16: VU_DEC_UD_T_DST_S_SRC(vu_i_ftoi12); return;\n            case 0x17: VU_DEC_UD_T_DST_S_SRC(vu_i_ftoi15); return;\n            case 0x18: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_mulax); return;\n            case 0x19: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_mulay); return;\n            case 0x1A: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_mulaz); return;\n            case 0x1B: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_mulaw); return;\n            case 0x1C: VU_DEC_UD_S_SRC_Q_SRC(vu_i_mulaq); return;\n            case 0x1D: VU_DEC_UD_T_DST_S_SRC(vu_i_abs); return;\n            case 0x1E: VU_DEC_UD_S_SRC(vu_i_mulai); return;\n            case 0x1F: VU_DEC_CLIP(); return;\n            case 0x20: VU_DEC_UD_S_SRC_Q_SRC(vu_i_addaq); return;\n            case 0x21: VU_DEC_UD_S_SRC_Q_SRC(vu_i_maddaq); return;\n            case 0x22: VU_DEC_UD_S_SRC(vu_i_addai); return;\n            case 0x23: VU_DEC_UD_S_SRC(vu_i_maddai); return;\n            case 0x24: VU_DEC_UD_S_SRC_Q_SRC(vu_i_subaq); return;\n            case 0x25: VU_DEC_UD_S_SRC_Q_SRC(vu_i_msubaq); return;\n            case 0x26: VU_DEC_UD_S_SRC(vu_i_subai); return;\n            case 0x27: VU_DEC_UD_S_SRC(vu_i_msubai); return;\n            case 0x28: VU_DEC_UD_S_SRC_T_SRC(vu_i_adda); return;\n            case 0x29: VU_DEC_UD_S_SRC_T_SRC(vu_i_madda); return;\n            case 0x2A: VU_DEC_UD_S_SRC_T_SRC(vu_i_mula); return;\n            case 0x2C: VU_DEC_UD_S_SRC_T_SRC(vu_i_suba); return;\n            case 0x2D: VU_DEC_UD_S_SRC_T_SRC(vu_i_msuba); return;\n            case 0x2E: VU_DEC_OPMULA(); return;\n            case 0x2F: VU_DEC_UD_NONE(vu_i_nop); return;\n        }\n    } else {\n        // Decode 0000003F style instruction\n        switch (opcode & 0x3f) {\n            case 0x00: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_addx); return;\n            case 0x01: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_addy); return;\n            case 0x02: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_addz); return;\n            case 0x03: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_addw); return;\n            case 0x04: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_subx); return;\n            case 0x05: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_suby); return;\n            case 0x06: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_subz); return;\n            case 0x07: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_subw); return;\n            case 0x08: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_maddx); return;\n            case 0x09: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_maddy); return;\n            case 0x0A: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_maddz); return;\n            case 0x0B: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_maddw); return;\n            case 0x0C: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_msubx); return;\n            case 0x0D: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_msuby); return;\n            case 0x0E: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_msubz); return;\n            case 0x0F: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_msubw); return;\n            case 0x10: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_maxx); return;\n            case 0x11: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_maxy); return;\n            case 0x12: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_maxz); return;\n            case 0x13: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_maxw); return;\n            case 0x14: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_minix); return;\n            case 0x15: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_miniy); return;\n            case 0x16: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_miniz); return;\n            case 0x17: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_miniw); return;\n            case 0x18: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_mulx); return;\n            case 0x19: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_muly); return;\n            case 0x1A: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_mulz); return;\n            case 0x1B: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_mulw); return;\n            case 0x1C: VU_DEC_UD_D_DST_S_SRC_Q_SRC(vu_i_mulq); return;\n            case 0x1D: VU_DEC_UD_D_DST_S_SRC(vu_i_maxi); return;\n            case 0x1E: VU_DEC_UD_D_DST_S_SRC(vu_i_muli); return;\n            case 0x1F: VU_DEC_UD_D_DST_S_SRC(vu_i_minii); return;\n            case 0x20: VU_DEC_UD_D_DST_S_SRC_Q_SRC(vu_i_addq); return;\n            case 0x21: VU_DEC_UD_D_DST_S_SRC_Q_SRC(vu_i_maddq); return;\n            case 0x22: VU_DEC_UD_D_DST_S_SRC(vu_i_addi); return;\n            case 0x23: VU_DEC_UD_D_DST_S_SRC(vu_i_maddi); return;\n            case 0x24: VU_DEC_UD_D_DST_S_SRC_Q_SRC(vu_i_subq); return;\n            case 0x25: VU_DEC_UD_D_DST_S_SRC_Q_SRC(vu_i_msubq); return;\n            case 0x26: VU_DEC_UD_D_DST_S_SRC(vu_i_subi); return;\n            case 0x27: VU_DEC_UD_D_DST_S_SRC(vu_i_msubi); return;\n            case 0x28: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_add); return;\n            case 0x29: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_madd); return;\n            case 0x2A: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_mul); return;\n            case 0x2B: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_max); return;\n            case 0x2C: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_sub); return;\n            case 0x2D: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_msub); return;\n            case 0x2E: VU_DEC_OPMSUB(); return;\n            case 0x2F: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_mini); return;\n        }\n    }\n}\n\nvoid vu_decode_lower(struct vu_state* vu, uint32_t opcode) {\n    vu->lower.ld_d = (opcode >> 6) & 0x1f;\n    vu->lower.ld_s = (opcode >> 11) & 0x1f;\n    vu->lower.ld_t = (opcode >> 16) & 0x1f;\n    vu->lower.ld_sf = (opcode >> 21) & 3;\n    vu->lower.ld_tf = (opcode >> 23) & 3;\n    vu->lower.ld_imm5 = ((int32_t)(((opcode >> 6) & 0x1f) << 27)) >> 27;\n    vu->lower.ld_imm11 = ((int32_t)((opcode & 0x7ff) << 21)) >> 21;\n    vu->lower.ld_imm12 = (((opcode >> 21) & 1) << 11) | (opcode & 0x7ff);\n    vu->lower.ld_imm15 = (opcode & 0x7ff) | ((opcode & 0x1e00000) >> 10);\n    vu->lower.ld_imm24 = opcode & 0xffffff;\n\n    for (int i = 0; i < 4; i++)\n        vu->lower.ld_di[i] = opcode & (1 << (24 - i));\n\n    vu->lower.func = NULL;\n    vu->lower.dst.reg = 0;\n    vu->lower.dst.field = 0;\n    vu->lower.src[0].reg = 0;\n    vu->lower.src[0].field = 0;\n    vu->lower.src[1].reg = 0;\n    vu->lower.src[1].field = 0;\n    vu->lower.vi_src[0] = 0;\n    vu->lower.vi_src[1] = 0;\n    vu->lower.vi_dst = 0;\n\n    switch ((opcode & 0xFE000000) >> 25) {\n        case 0x00: VU_DEC_LD_T_DST_S_VISRC(vu_i_lq); return;\n        case 0x01: VU_DEC_LD_S_SRC_T_VISRC(vu_i_sq); return;\n        case 0x04: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_ilw); return;\n        case 0x05: VU_DEC_LD_S_VISRC_T_VISRC(vu_i_isw); return;\n        case 0x08: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_iaddiu); return;\n        case 0x09: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_isubiu); return;\n\n        // Note: The flag check instructions clobber the destination register\n        //       \"immediately\", this means we don't actually need to generate\n        //       a dependency.\n        case 0x10: VU_DEC_LD_NONE(vu_i_fceq); return; // VU_DEC_LD_VIDST(1, vu_i_fceq); return;\n        case 0x11: VU_DEC_LD_NONE(vu_i_fcset); return;\n        case 0x12: VU_DEC_LD_NONE(vu_i_fcand); return; // VU_DEC_LD_VIDST(1, vu_i_fcand); return;\n        case 0x13: VU_DEC_LD_NONE(vu_i_fcor); return; // VU_DEC_LD_VIDST(1, vu_i_fcor); return;\n        case 0x14: VU_DEC_LD_NONE(vu_i_fseq); return; // VU_DEC_LD_T_VIDST(vu_i_fseq); return;\n        case 0x15: VU_DEC_LD_NONE(vu_i_fsset); return;\n        case 0x16: VU_DEC_LD_NONE(vu_i_fsand); return; // VU_DEC_LD_T_VIDST(vu_i_fsand); return;\n        case 0x17: VU_DEC_LD_NONE(vu_i_fsor); return; // VU_DEC_LD_T_VIDST(vu_i_fsor); return;\n        case 0x18: VU_DEC_LD_S_VISRC(vu_i_fmeq); return; // VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmeq); return;\n        case 0x1A: VU_DEC_LD_S_VISRC(vu_i_fmand); return; // VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmand); return;\n        case 0x1B: VU_DEC_LD_S_VISRC(vu_i_fmor); return; // VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmor); return;\n        case 0x1C: VU_DEC_LD_NONE(vu_i_fcget); return; // VU_DEC_LD_T_VIDST(vu_i_fcget); return;\n        case 0x20: VU_DEC_LD_NONE(vu_i_b); return;\n        case 0x21: VU_DEC_LD_T_VIDST(vu_i_bal); return;\n        case 0x24: VU_DEC_LD_S_VISRC(vu_i_jr); return;\n        case 0x25: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_jalr); return;\n        case 0x28: VU_DEC_LD_S_VISRC_T_VISRC(vu_i_ibeq); return;\n        case 0x29: VU_DEC_LD_S_VISRC_T_VISRC(vu_i_ibne); return;\n        case 0x2C: VU_DEC_LD_S_VISRC(vu_i_ibltz); return;\n        case 0x2D: VU_DEC_LD_S_VISRC(vu_i_ibgtz); return;\n        case 0x2E: VU_DEC_LD_S_VISRC(vu_i_iblez); return;\n        case 0x2F: VU_DEC_LD_S_VISRC(vu_i_ibgez); return;\n        case 0x40: {\n            if ((opcode & 0x3C) == 0x3C) {\n                switch (((opcode & 0x7C0) >> 4) | (opcode & 3)) {\n                    case 0x30: VU_DEC_LD_T_DST_S_SRC(vu_i_move); return;\n                    case 0x31: VU_DEC_MR32(); return;\n                    case 0x34: VU_DEC_LD_T_DST_S_VISRC_S_VIDST(vu_i_lqi); return;\n                    case 0x35: VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(vu_i_sqi); return;\n                    case 0x36: VU_DEC_LD_T_DST_S_VISRC_S_VIDST(vu_i_lqd); return;\n                    case 0x37: VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(vu_i_sqd); return;\n                    case 0x38: VU_DEC_LD_Q_DST_S_SF_SRC_T_TF_SRC(vu_i_div); return;\n                    case 0x39: VU_DEC_LD_Q_DST_T_TF_SRC(vu_i_sqrt); return;\n                    case 0x3A: VU_DEC_LD_Q_DST_S_SF_SRC_T_TF_SRC(vu_i_rsqrt); return;\n                    case 0x3B: VU_DEC_LD_NONE(vu_i_waitq); return;\n                    case 0x3C: VU_DEC_LD_T_VIDST_S_SF_SRC(vu_i_mtir); return;\n                    case 0x3D: VU_DEC_LD_T_DST_S_VISRC(vu_i_mfir); return;\n                    case 0x3E: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_ilwr); return;\n                    case 0x3F: VU_DEC_LD_T_VISRC_S_VISRC(vu_i_iswr); return;\n                    case 0x40: VU_DEC_LD_T_DST(vu_i_rnext); return;\n                    case 0x41: VU_DEC_LD_T_DST(vu_i_rget); return;\n                    case 0x42: VU_DEC_LD_S_SF_SRC(vu_i_rinit); return;\n                    case 0x43: VU_DEC_LD_S_SF_SRC(vu_i_rxor); return;\n                    case 0x64: VU_DEC_LD_T_DST(vu_i_mfp); return;\n                    case 0x68: VU_DEC_LD_T_VIDST(vu_i_xtop); return;\n                    case 0x69: VU_DEC_LD_T_VIDST(vu_i_xitop); return;\n                    case 0x6C: VU_DEC_LD_S_VISRC(vu_i_xgkick); return;\n                    case 0x70: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_esadd); return;\n                    case 0x71: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_ersadd); return;\n                    case 0x72: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_eleng); return;\n                    case 0x73: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_erleng); return;\n                    case 0x74: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y, vu_i_eatanxy); return;\n                    case 0x75: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Z, vu_i_eatanxz); return;\n                    case 0x76: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z | VU_FLD_W, vu_i_esum); return;\n                    case 0x78: VU_DEC_LD_S_SF_SRC(vu_i_esqrt); return;\n                    case 0x79: VU_DEC_LD_S_SF_SRC(vu_i_ersqrt); return;\n                    case 0x7A: VU_DEC_LD_S_SF_SRC(vu_i_ercpr); return;\n                    case 0x7B: VU_DEC_LD_NONE(vu_i_waitp); return;\n                    case 0x7C: VU_DEC_LD_S_SF_SRC(vu_i_esin); return;\n                    case 0x7D: VU_DEC_LD_S_SF_SRC(vu_i_eatan); return;\n                    case 0x7E: VU_DEC_LD_S_SF_SRC(vu_i_eexp); return;\n                }\n            } else {\n                switch (opcode & 0x3F) {\n                    case 0x30: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_iadd); return;\n                    case 0x31: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_isub); return;\n                    case 0x32: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_iaddi); return;\n                    case 0x34: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_iand); return;\n                    case 0x35: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_ior); return;\n                }\n            }\n        } break;\n    }\n}\n\nstatic inline void vu_advance_fmac_pipeline(struct vu_state* vu) {\n    vu->upper_pipeline[3] = vu->upper_pipeline[2];\n    vu->upper_pipeline[2] = vu->upper_pipeline[1];\n    vu->upper_pipeline[1] = vu->upper_pipeline[0];\n    vu->upper_pipeline[0].dst.reg = vu->upper.dst.reg;\n    vu->upper_pipeline[0].dst.field = vu->upper.dst.field;\n    vu->lower_pipeline[3] = vu->lower_pipeline[2];\n    vu->lower_pipeline[2] = vu->lower_pipeline[1];\n    vu->lower_pipeline[1] = vu->lower_pipeline[0];\n    vu->lower_pipeline[0].dst.reg = vu->lower.dst.reg;\n    vu->lower_pipeline[0].dst.field = vu->lower.dst.field;\n}\n\nstatic inline int vu_get_fmac_stall_cycles(struct vu_state* vu) {\n    for (int i = 0; i < 4; i++) {\n        for (int j = 0; j < 2; j++) {\n            if (vu->upper.src[j].reg == vu->lower_pipeline[i].dst.reg) {\n                if (vu->upper.src[j].field & vu->lower_pipeline[i].dst.field) {\n                    return 4 - i;\n                }\n            }\n        }\n    }\n\n    return 0;\n}\n\nvoid vu_execute_program(struct vu_state* vu, uint32_t addr) {\n    // printf(\"vu%d: Executing program at %08x (%08x) TOP=%08x\\n\", vu->id, addr, addr << 3, vu->vif->top);\n    // Disable VU1\n    // if (vu->id == 1)\n    //     return;\n\n    struct vu_dis_state ds;\n\n    ds.print_address = 0;\n    ds.print_opcode = 0;\n\n    vu->tpc = addr;\n    vu->next_tpc = addr + 1;\n\n    vu->i_bit = 0;\n    vu->e_bit = 0;\n    vu->m_bit = 0;\n    vu->d_bit = 0;\n    vu->t_bit = 0;\n\n    int delayed_e_bit = 0;\n\n    while (!delayed_e_bit) {\n        uint32_t tpc = vu->tpc;\n        uint64_t liw = vu->micro_mem[vu->tpc];\n\n        vu->tpc = vu->next_tpc;\n        vu->next_tpc = vu->tpc + 1;\n        vu->tpc &= 0x7ff;\n        vu->next_tpc &= 0x7ff;\n\n        ds.addr = tpc;\n\n        delayed_e_bit = vu->e_bit != 0;\n\n        uint32_t upper = liw >> 32;\n        uint32_t lower = liw & 0xffffffff;\n\n        vu->i_bit = (upper & 0x80000000) != 0;\n        vu->e_bit = (upper & 0x40000000) != 0;\n        vu->m_bit = (upper & 0x20000000) != 0;\n        vu->d_bit = (upper & 0x10000000) != 0;\n        vu->t_bit = (upper & 0x08000000) != 0;\n\n        if (vu->q_delay)\n            vu->q_delay--;\n\n        vu_update_status(vu);\n        \n        vu_decode_upper(vu, upper & 0x7ffffff);\n\n        // char ubuf[512];\n        // printf(\"%04x: %08x %08x \", tpc, upper, lower);\n        // printf(\"%-40s\", vu_disassemble_upper(ubuf, upper & 0x7ffffff, &ds));\n\n        if (vu->i_bit) {\n            // printf(\"%-12s0x%08x\\n\", \"loi\", lower);\n\n            vu->upper.func(vu, &vu->upper);\n\n            // LOI\n            vu->i.u32 = lower;\n            vu->lower.func = NULL;\n            vu->lower.dst.reg = 0;\n            vu->lower.dst.field = 0;\n            vu->lower.src[0].reg = 0;\n            vu->lower.src[0].field = 0;\n            vu->lower.src[1].reg = 0;\n            vu->lower.src[1].field = 0;\n            vu->lower.vi_src[0] = 0;\n            vu->lower.vi_src[1] = 0;\n            vu->lower.vi_dst = 0;\n        } else {\n            vu_decode_lower(vu, lower);\n\n            // char lbuf[512];\n            // printf(\"%-40s\\n\", vu_disassemble_lower(lbuf, lower & 0xffffffff, &ds));\n\n            int hazard0 = vu->upper.dst.reg == vu->lower.src[0].reg;\n            int hazard1 = vu->upper.dst.reg == vu->lower.src[1].reg;\n            int hazard2 = vu->upper.dst.reg == vu->lower.dst.reg;\n            int hazard3 = (vu->lower.dst.reg == VU_REG_Q) && vu->q_delay;\n            int waitq = vu->lower.func == vu_i_waitq;\n\n            // If the lower instruction writes to Q and Q is not ready yet,\n            // the VU stalls the pipeline until it is ready.\n            if (hazard3) vu->q_delay = 0;\n\n            // Note: This code checks hazards and stalls pipes when the FMAC pipe stalls.\n            //       It's absolutely disgusting, so I'm commenting it out for now.\n\n            // Fixes:\n            // - Raiden III\n\n            /*\n            int stall = vu_get_fmac_stall_cycles(vu);\n\n            vu->q_delay -= stall;\n\n            if (vu->q_delay < 0)\n                vu->q_delay = 0;\n\n            for (int k = 0; k < stall; k++) {\n                vu_advance_fmac_pipeline(vu);\n            }\n            */\n\n            if (!vu->upper.dst.reg) {\n                vu->upper.func(vu, &vu->upper);\n                vu->lower.func(vu, &vu->lower);\n            } else if (hazard0 || hazard1 || waitq) {\n                // Upper instruction writes to a register that the lower\n                // instruction reads from. In this case the lower instruction\n                // gets the previous value of the register, executing the lower\n                // instruction first does the trick.\n\n                // We also execute WAITQ first, since it will stall the pipeline\n                // if the upper instruction reads Q\n\n                vu->lower.func(vu, &vu->lower);\n                vu->upper.func(vu, &vu->upper);\n            } else if (hazard2) {\n                // Upper and lower instructions write to the same register.\n                // In this case the upper instruction takes priority, so we\n                // restore the value of the register after executing the lower\n                // instruction.\n\n                vu->upper.func(vu, &vu->upper);\n\n                struct vu_reg128 tmp = vu->vf[vu->upper.dst.reg];\n\n                vu->lower.func(vu, &vu->lower);\n\n                vu->vf[vu->upper.dst.reg] = tmp;\n            } else {\n                vu->upper.func(vu, &vu->upper);\n                vu->lower.func(vu, &vu->lower);\n            }\n        }\n\n        // if (vu_get_fmac_stall_cycles(vu)) {\n        //     printf(\"vu%d: FMAC hazard detected at %04x, stalling %d cycles (q_delay=%d)\\n\", vu->id, tpc, vu_get_fmac_stall_cycles(vu), vu->q_delay);\n\n        //     exit(1);\n        // }\n\n        vu_advance_fmac_pipeline(vu);\n\n        vu->mac_pipeline[3] = vu->mac_pipeline[2];\n        vu->mac_pipeline[2] = vu->mac_pipeline[1];\n        vu->mac_pipeline[1] = vu->mac_pipeline[0];\n        vu->mac_pipeline[0] = vu->mac;\n        \n        vu->clip_pipeline[3] = vu->clip_pipeline[2];\n        vu->clip_pipeline[2] = vu->clip_pipeline[1];\n        vu->clip_pipeline[1] = vu->clip_pipeline[0];\n        vu->clip_pipeline[0] = vu->clip;\n\n        if (vu->vi_backup_cycles) {\n            vu->vi_backup_cycles--;\n\n            if (!vu->vi_backup_cycles) {\n                vu->vi_backup_reg = 0;\n                vu->vi_backup_value = 0;\n            }\n        }\n\n        // if (vu->xgkick_pending) {\n        //     vu->xgkick_pending--;\n\n        //     if (!vu->xgkick_pending) {\n        //         vu_xgkick(vu);\n        //     }\n        // }\n    }\n}\n\nvoid ps2_vu_write_vi(struct vu_state* vu, int index, uint32_t value) {\n    switch (index) {\n        case 0: return;\n        case 1: case 2: case 3:\n        case 4: case 5: case 6: case 7:\n        case 8: case 9: case 10: case 11:\n        case 12: case 13: case 14: case 15: {\n            vu->vi[index] = value & 0xffff;\n        } break;\n\n        case 16: {\n            vu->status &= ~0xfc0;\n            vu->status |= value & 0xfc0;\n        } break;\n\n        case 17: return; // MAC flag register, read-only\n        case 18: {\n            vu->clip = value & 0xffffff;\n        } break;\n        \n        case 19: return; // VU revision register? read-only\n\n        case 20: {\n            vu->r.u32 = value & 0x7fffff;\n        } break;\n        case 21: {\n            vu->i.u32 = value;\n        } break;\n        case 22: {\n            vu->q.u32 = value;\n        } break;\n        case 23: return;\n        case 24: {\n            vu->cr[8] = value & 0xc0c;\n        } break;\n        case 25: return;\n        case 26: return; // VU TPC register, read-only\n        case 27: {\n            vu->cmsar0 = value & 0xffff;\n        } break;\n        case 28: {\n            // To-do: Handle FBRST\n            vu->fbrst = value & 0xc0c;\n\n            if (value & 2) {\n                // Reset VU0\n                ps2_vu_reset(vu);\n            }\n\n            if (value & 0x200) {\n                // Reset VU1\n                ps2_vu_reset(vu->vu1);\n            }\n        } break;\n        case 29: return; // VU VPU-STAT register, read-only\n        case 30: return; // VU reserved register, read-only\n        case 31: {\n            vu->cmsar1 = value & 0xffff;\n\n            vu_execute_program(vu->vu1, vu->cmsar1 >> 3);\n        } break;\n    }\n}\n\nuint32_t ps2_vu_read_vi(struct vu_state* vu, int index) {\n    switch (index) {\n        case 0: case 1: case 2: case 3:\n        case 4: case 5: case 6: case 7:\n        case 8: case 9: case 10: case 11:\n        case 12: case 13: case 14: case 15: {\n            return vu->vi[index];\n        } break;\n\n        case 19: { // VU revision register\n            return 0x2e30;\n        } break;\n\n        default: {\n            return vu->cr[index - 16];\n        } break;\n    }\n}\n\nvoid ps2_vu_reset(struct vu_state* vu) {\n    for (int i = 0; i < 16; i++)\n        vu->vi[i] = 0;\n\n    for (int i = 0; i < 32; i++) {\n        vu->vf[i].u32[0] = 0;\n        vu->vf[i].u32[1] = 0;\n        vu->vf[i].u32[2] = 0;\n        vu->vf[i].u32[3] = 0;\n    }\n\n    vu->r.u32 = 0x3f800000;\n    vu->i.u32 = 0;\n    vu->q.u32 = 0;\n    vu->clip = 0;\n    vu->status = 0;\n    vu->fbrst = 0;\n    vu->cmsar0 = 0;\n    vu->cmsar1 = 0;\n    vu->mac = 0;\n    vu->mac_pipeline[0] = 0;\n    vu->mac_pipeline[1] = 0;\n    vu->mac_pipeline[2] = 0;\n    vu->mac_pipeline[3] = 0;\n    vu->clip_pipeline[0] = 0;\n    vu->clip_pipeline[1] = 0;\n    vu->clip_pipeline[2] = 0;\n    vu->clip_pipeline[3] = 0;\n    vu->tpc = 0;\n    vu->next_tpc = 1;\n    vu->i_bit = 0;\n    vu->e_bit = 0;\n    vu->m_bit = 0;\n    vu->d_bit = 0;\n    vu->t_bit = 0;\n    vu->q_delay = 0;\n    vu->prev_q.u32 = 0;\n\n    vu->vf[0].w = 1.0;\n}\n\nvoid ps2_vu_decode_upper(struct vu_state* vu, uint32_t opcode) {\n    vu_decode_upper(vu, opcode);\n}\n\nvoid ps2_vu_decode_lower(struct vu_state* vu, uint32_t opcode) {\n    vu_decode_lower(vu, opcode);\n}\n\nvoid vu_execute_program_tpc(struct vu_state* vu) {\n    vu_execute_program(vu, vu->tpc);\n}\n\nuint128_t* vu_get_vu_mem_ptr(struct vu_state* vu, uint32_t addr) {\n    return &vu->vu_mem[addr & vu->vu_mem_size];\n}\n\nuint64_t* vu_get_micro_mem_ptr(struct vu_state* vu, uint32_t addr) {\n    return &vu->micro_mem[addr & vu->micro_mem_size];\n}\n\nuint32_t vu_get_tpc(struct vu_state* vu) {\n    return vu->tpc;\n}\n\n// #undef printf"
  },
  {
    "path": "src/elf.h",
    "content": "/* This is the original elf.h file from the GNU C Library; I only removed\n   the inclusion of feature.h and added definitions of __BEGIN_DECLS and\n   __END_DECLS as documented in\n   https://cmd.inp.nsk.su/old/cmd2/manuals/gnudocs/gnudocs/libtool/libtool_36.html\n\n   On macOS, simply copy the file to /usr/local/include/. \n\n   Mathias Lafeldt <mathias.lafeldt@gmail.com> */\n\n/* This file defines standard ELF types, structures, and macros.\n   Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011\n\tFree Software Foundation, Inc.\n   This file is part of the GNU C Library.\n\n   The GNU C Library is free software; you can redistribute it and/or\n   modify it under the terms of the GNU Lesser General Public\n   License as published by the Free Software Foundation; either\n   version 2.1 of the License, or (at your option) any later version.\n\n   The GNU C Library is distributed in the hope that it will be useful,\n   but WITHOUT ANY WARRANTY; without even the implied warranty of\n   MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU\n   Lesser General Public License for more details.\n\n   You should have received a copy of the GNU Lesser General Public\n   License along with the GNU C Library; if not, write to the Free\n   Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA\n   02111-1307 USA.  */\n\n#ifndef _ELF_H\n#define\t_ELF_H 1\n\n/* __BEGIN_DECLS should be used at the beginning of your declarations,\n   so that C++ compilers don't mangle their names.  Use __END_DECLS at\n   the end of C declarations. */\n#undef __BEGIN_DECLS\n#undef __END_DECLS\n#ifdef __cplusplus\n# define __BEGIN_DECLS extern \"C\" {\n# define __END_DECLS }\n#else\n# define __BEGIN_DECLS /* empty */\n# define __END_DECLS /* empty */\n#endif\n\n__BEGIN_DECLS\n\n/* Standard ELF types.  */\n\n#include <stdint.h>\n\n/* Type for a 16-bit quantity.  */\ntypedef uint16_t Elf32_Half;\ntypedef uint16_t Elf64_Half;\n\n/* Types for signed and unsigned 32-bit quantities.  */\ntypedef uint32_t Elf32_Word;\ntypedef\tint32_t  Elf32_Sword;\ntypedef uint32_t Elf64_Word;\ntypedef\tint32_t  Elf64_Sword;\n\n/* Types for signed and unsigned 64-bit quantities.  */\ntypedef uint64_t Elf32_Xword;\ntypedef\tint64_t  Elf32_Sxword;\ntypedef uint64_t Elf64_Xword;\ntypedef\tint64_t  Elf64_Sxword;\n\n/* Type of addresses.  */\ntypedef uint32_t Elf32_Addr;\ntypedef uint64_t Elf64_Addr;\n\n/* Type of file offsets.  */\ntypedef uint32_t Elf32_Off;\ntypedef uint64_t Elf64_Off;\n\n/* Type for section indices, which are 16-bit quantities.  */\ntypedef uint16_t Elf32_Section;\ntypedef uint16_t Elf64_Section;\n\n/* Type for version symbol information.  */\ntypedef Elf32_Half Elf32_Versym;\ntypedef Elf64_Half Elf64_Versym;\n\n\n/* The ELF file header.  This appears at the start of every ELF file.  */\n\n#define EI_NIDENT (16)\n\ntypedef struct\n{\n  unsigned char\te_ident[EI_NIDENT];\t/* Magic number and other info */\n  Elf32_Half\te_type;\t\t\t/* Object file type */\n  Elf32_Half\te_machine;\t\t/* Architecture */\n  Elf32_Word\te_version;\t\t/* Object file version */\n  Elf32_Addr\te_entry;\t\t/* Entry point virtual address */\n  Elf32_Off\te_phoff;\t\t/* Program header table file offset */\n  Elf32_Off\te_shoff;\t\t/* Section header table file offset */\n  Elf32_Word\te_flags;\t\t/* Processor-specific flags */\n  Elf32_Half\te_ehsize;\t\t/* ELF header size in bytes */\n  Elf32_Half\te_phentsize;\t\t/* Program header table entry size */\n  Elf32_Half\te_phnum;\t\t/* Program header table entry count */\n  Elf32_Half\te_shentsize;\t\t/* Section header table entry size */\n  Elf32_Half\te_shnum;\t\t/* Section header table entry count */\n  Elf32_Half\te_shstrndx;\t\t/* Section header string table index */\n} Elf32_Ehdr;\n\ntypedef struct\n{\n  unsigned char\te_ident[EI_NIDENT];\t/* Magic number and other info */\n  Elf64_Half\te_type;\t\t\t/* Object file type */\n  Elf64_Half\te_machine;\t\t/* Architecture */\n  Elf64_Word\te_version;\t\t/* Object file version */\n  Elf64_Addr\te_entry;\t\t/* Entry point virtual address */\n  Elf64_Off\te_phoff;\t\t/* Program header table file offset */\n  Elf64_Off\te_shoff;\t\t/* Section header table file offset */\n  Elf64_Word\te_flags;\t\t/* Processor-specific flags */\n  Elf64_Half\te_ehsize;\t\t/* ELF header size in bytes */\n  Elf64_Half\te_phentsize;\t\t/* Program header table entry size */\n  Elf64_Half\te_phnum;\t\t/* Program header table entry count */\n  Elf64_Half\te_shentsize;\t\t/* Section header table entry size */\n  Elf64_Half\te_shnum;\t\t/* Section header table entry count */\n  Elf64_Half\te_shstrndx;\t\t/* Section header string table index */\n} Elf64_Ehdr;\n\n/* Fields in the e_ident array.  The EI_* macros are indices into the\n   array.  The macros under each EI_* macro are the values the byte\n   may have.  */\n\n#define EI_MAG0\t\t0\t\t/* File identification byte 0 index */\n#define ELFMAG0\t\t0x7f\t\t/* Magic number byte 0 */\n\n#define EI_MAG1\t\t1\t\t/* File identification byte 1 index */\n#define ELFMAG1\t\t'E'\t\t/* Magic number byte 1 */\n\n#define EI_MAG2\t\t2\t\t/* File identification byte 2 index */\n#define ELFMAG2\t\t'L'\t\t/* Magic number byte 2 */\n\n#define EI_MAG3\t\t3\t\t/* File identification byte 3 index */\n#define ELFMAG3\t\t'F'\t\t/* Magic number byte 3 */\n\n/* Conglomeration of the identification bytes, for easy testing as a word.  */\n#define\tELFMAG\t\t\"\\177ELF\"\n#define\tSELFMAG\t\t4\n\n#define EI_CLASS\t4\t\t/* File class byte index */\n#define ELFCLASSNONE\t0\t\t/* Invalid class */\n#define ELFCLASS32\t1\t\t/* 32-bit objects */\n#define ELFCLASS64\t2\t\t/* 64-bit objects */\n#define ELFCLASSNUM\t3\n\n#define EI_DATA\t\t5\t\t/* Data encoding byte index */\n#define ELFDATANONE\t0\t\t/* Invalid data encoding */\n#define ELFDATA2LSB\t1\t\t/* 2's complement, little endian */\n#define ELFDATA2MSB\t2\t\t/* 2's complement, big endian */\n#define ELFDATANUM\t3\n\n#define EI_VERSION\t6\t\t/* File version byte index */\n\t\t\t\t\t/* Value must be EV_CURRENT */\n\n#define EI_OSABI\t7\t\t/* OS ABI identification */\n#define ELFOSABI_NONE\t\t0\t/* UNIX System V ABI */\n#define ELFOSABI_SYSV\t\t0\t/* Alias.  */\n#define ELFOSABI_HPUX\t\t1\t/* HP-UX */\n#define ELFOSABI_NETBSD\t\t2\t/* NetBSD.  */\n#define ELFOSABI_GNU\t\t3\t/* Object uses GNU ELF extensions.  */\n#define ELFOSABI_LINUX\t\tELFOSABI_GNU /* Compatibility alias.  */\n#define ELFOSABI_SOLARIS\t6\t/* Sun Solaris.  */\n#define ELFOSABI_AIX\t\t7\t/* IBM AIX.  */\n#define ELFOSABI_IRIX\t\t8\t/* SGI Irix.  */\n#define ELFOSABI_FREEBSD\t9\t/* FreeBSD.  */\n#define ELFOSABI_TRU64\t\t10\t/* Compaq TRU64 UNIX.  */\n#define ELFOSABI_MODESTO\t11\t/* Novell Modesto.  */\n#define ELFOSABI_OPENBSD\t12\t/* OpenBSD.  */\n#define ELFOSABI_ARM_AEABI\t64\t/* ARM EABI */\n#define ELFOSABI_ARM\t\t97\t/* ARM */\n#define ELFOSABI_STANDALONE\t255\t/* Standalone (embedded) application */\n\n#define EI_ABIVERSION\t8\t\t/* ABI version */\n\n#define EI_PAD\t\t9\t\t/* Byte index of padding bytes */\n\n/* Legal values for e_type (object file type).  */\n\n#define ET_NONE\t\t0\t\t/* No file type */\n#define ET_REL\t\t1\t\t/* Relocatable file */\n#define ET_EXEC\t\t2\t\t/* Executable file */\n#define ET_DYN\t\t3\t\t/* Shared object file */\n#define ET_CORE\t\t4\t\t/* Core file */\n#define\tET_NUM\t\t5\t\t/* Number of defined types */\n#define ET_LOOS\t\t0xfe00\t\t/* OS-specific range start */\n#define ET_HIOS\t\t0xfeff\t\t/* OS-specific range end */\n#define ET_LOPROC\t0xff00\t\t/* Processor-specific range start */\n#define ET_HIPROC\t0xffff\t\t/* Processor-specific range end */\n\n/* Legal values for e_machine (architecture).  */\n\n#define EM_NONE\t\t 0\t\t/* No machine */\n#define EM_M32\t\t 1\t\t/* AT&T WE 32100 */\n#define EM_SPARC\t 2\t\t/* SUN SPARC */\n#define EM_386\t\t 3\t\t/* Intel 80386 */\n#define EM_68K\t\t 4\t\t/* Motorola m68k family */\n#define EM_88K\t\t 5\t\t/* Motorola m88k family */\n#define EM_860\t\t 7\t\t/* Intel 80860 */\n#define EM_MIPS\t\t 8\t\t/* MIPS R3000 big-endian */\n#define EM_S370\t\t 9\t\t/* IBM System/370 */\n#define EM_MIPS_RS3_LE\t10\t\t/* MIPS R3000 little-endian */\n\n#define EM_PARISC\t15\t\t/* HPPA */\n#define EM_VPP500\t17\t\t/* Fujitsu VPP500 */\n#define EM_SPARC32PLUS\t18\t\t/* Sun's \"v8plus\" */\n#define EM_960\t\t19\t\t/* Intel 80960 */\n#define EM_PPC\t\t20\t\t/* PowerPC */\n#define EM_PPC64\t21\t\t/* PowerPC 64-bit */\n#define EM_S390\t\t22\t\t/* IBM S390 */\n\n#define EM_V800\t\t36\t\t/* NEC V800 series */\n#define EM_FR20\t\t37\t\t/* Fujitsu FR20 */\n#define EM_RH32\t\t38\t\t/* TRW RH-32 */\n#define EM_RCE\t\t39\t\t/* Motorola RCE */\n#define EM_ARM\t\t40\t\t/* ARM */\n#define EM_FAKE_ALPHA\t41\t\t/* Digital Alpha */\n#define EM_SH\t\t42\t\t/* Hitachi SH */\n#define EM_SPARCV9\t43\t\t/* SPARC v9 64-bit */\n#define EM_TRICORE\t44\t\t/* Siemens Tricore */\n#define EM_ARC\t\t45\t\t/* Argonaut RISC Core */\n#define EM_H8_300\t46\t\t/* Hitachi H8/300 */\n#define EM_H8_300H\t47\t\t/* Hitachi H8/300H */\n#define EM_H8S\t\t48\t\t/* Hitachi H8S */\n#define EM_H8_500\t49\t\t/* Hitachi H8/500 */\n#define EM_IA_64\t50\t\t/* Intel Merced */\n#define EM_MIPS_X\t51\t\t/* Stanford MIPS-X */\n#define EM_COLDFIRE\t52\t\t/* Motorola Coldfire */\n#define EM_68HC12\t53\t\t/* Motorola M68HC12 */\n#define EM_MMA\t\t54\t\t/* Fujitsu MMA Multimedia Accelerator*/\n#define EM_PCP\t\t55\t\t/* Siemens PCP */\n#define EM_NCPU\t\t56\t\t/* Sony nCPU embeeded RISC */\n#define EM_NDR1\t\t57\t\t/* Denso NDR1 microprocessor */\n#define EM_STARCORE\t58\t\t/* Motorola Start*Core processor */\n#define EM_ME16\t\t59\t\t/* Toyota ME16 processor */\n#define EM_ST100\t60\t\t/* STMicroelectronic ST100 processor */\n#define EM_TINYJ\t61\t\t/* Advanced Logic Corp. Tinyj emb.fam*/\n#define EM_X86_64\t62\t\t/* AMD x86-64 architecture */\n#define EM_PDSP\t\t63\t\t/* Sony DSP Processor */\n\n#define EM_FX66\t\t66\t\t/* Siemens FX66 microcontroller */\n#define EM_ST9PLUS\t67\t\t/* STMicroelectronics ST9+ 8/16 mc */\n#define EM_ST7\t\t68\t\t/* STmicroelectronics ST7 8 bit mc */\n#define EM_68HC16\t69\t\t/* Motorola MC68HC16 microcontroller */\n#define EM_68HC11\t70\t\t/* Motorola MC68HC11 microcontroller */\n#define EM_68HC08\t71\t\t/* Motorola MC68HC08 microcontroller */\n#define EM_68HC05\t72\t\t/* Motorola MC68HC05 microcontroller */\n#define EM_SVX\t\t73\t\t/* Silicon Graphics SVx */\n#define EM_ST19\t\t74\t\t/* STMicroelectronics ST19 8 bit mc */\n#define EM_VAX\t\t75\t\t/* Digital VAX */\n#define EM_CRIS\t\t76\t\t/* Axis Communications 32-bit embedded processor */\n#define EM_JAVELIN\t77\t\t/* Infineon Technologies 32-bit embedded processor */\n#define EM_FIREPATH\t78\t\t/* Element 14 64-bit DSP Processor */\n#define EM_ZSP\t\t79\t\t/* LSI Logic 16-bit DSP Processor */\n#define EM_MMIX\t\t80\t\t/* Donald Knuth's educational 64-bit processor */\n#define EM_HUANY\t81\t\t/* Harvard University machine-independent object files */\n#define EM_PRISM\t82\t\t/* SiTera Prism */\n#define EM_AVR\t\t83\t\t/* Atmel AVR 8-bit microcontroller */\n#define EM_FR30\t\t84\t\t/* Fujitsu FR30 */\n#define EM_D10V\t\t85\t\t/* Mitsubishi D10V */\n#define EM_D30V\t\t86\t\t/* Mitsubishi D30V */\n#define EM_V850\t\t87\t\t/* NEC v850 */\n#define EM_M32R\t\t88\t\t/* Mitsubishi M32R */\n#define EM_MN10300\t89\t\t/* Matsushita MN10300 */\n#define EM_MN10200\t90\t\t/* Matsushita MN10200 */\n#define EM_PJ\t\t91\t\t/* picoJava */\n#define EM_OPENRISC\t92\t\t/* OpenRISC 32-bit embedded processor */\n#define EM_ARC_A5\t93\t\t/* ARC Cores Tangent-A5 */\n#define EM_XTENSA\t94\t\t/* Tensilica Xtensa Architecture */\n#define EM_NUM\t\t95\n\n/* If it is necessary to assign new unofficial EM_* values, please\n   pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the\n   chances of collision with official or non-GNU unofficial values.  */\n\n#define EM_ALPHA\t0x9026\n\n/* Legal values for e_version (version).  */\n\n#define EV_NONE\t\t0\t\t/* Invalid ELF version */\n#define EV_CURRENT\t1\t\t/* Current version */\n#define EV_NUM\t\t2\n\n/* Section header.  */\n\ntypedef struct\n{\n  Elf32_Word\tsh_name;\t\t/* Section name (string tbl index) */\n  Elf32_Word\tsh_type;\t\t/* Section type */\n  Elf32_Word\tsh_flags;\t\t/* Section flags */\n  Elf32_Addr\tsh_addr;\t\t/* Section virtual addr at execution */\n  Elf32_Off\tsh_offset;\t\t/* Section file offset */\n  Elf32_Word\tsh_size;\t\t/* Section size in bytes */\n  Elf32_Word\tsh_link;\t\t/* Link to another section */\n  Elf32_Word\tsh_info;\t\t/* Additional section information */\n  Elf32_Word\tsh_addralign;\t\t/* Section alignment */\n  Elf32_Word\tsh_entsize;\t\t/* Entry size if section holds table */\n} Elf32_Shdr;\n\ntypedef struct\n{\n  Elf64_Word\tsh_name;\t\t/* Section name (string tbl index) */\n  Elf64_Word\tsh_type;\t\t/* Section type */\n  Elf64_Xword\tsh_flags;\t\t/* Section flags */\n  Elf64_Addr\tsh_addr;\t\t/* Section virtual addr at execution */\n  Elf64_Off\tsh_offset;\t\t/* Section file offset */\n  Elf64_Xword\tsh_size;\t\t/* Section size in bytes */\n  Elf64_Word\tsh_link;\t\t/* Link to another section */\n  Elf64_Word\tsh_info;\t\t/* Additional section information */\n  Elf64_Xword\tsh_addralign;\t\t/* Section alignment */\n  Elf64_Xword\tsh_entsize;\t\t/* Entry size if section holds table */\n} Elf64_Shdr;\n\n/* Special section indices.  */\n\n#define SHN_UNDEF\t0\t\t/* Undefined section */\n#define SHN_LORESERVE\t0xff00\t\t/* Start of reserved indices */\n#define SHN_LOPROC\t0xff00\t\t/* Start of processor-specific */\n#define SHN_BEFORE\t0xff00\t\t/* Order section before all others\n\t\t\t\t\t   (Solaris).  */\n#define SHN_AFTER\t0xff01\t\t/* Order section after all others\n\t\t\t\t\t   (Solaris).  */\n#define SHN_HIPROC\t0xff1f\t\t/* End of processor-specific */\n#define SHN_LOOS\t0xff20\t\t/* Start of OS-specific */\n#define SHN_HIOS\t0xff3f\t\t/* End of OS-specific */\n#define SHN_ABS\t\t0xfff1\t\t/* Associated symbol is absolute */\n#define SHN_COMMON\t0xfff2\t\t/* Associated symbol is common */\n#define SHN_XINDEX\t0xffff\t\t/* Index is in extra table.  */\n#define SHN_HIRESERVE\t0xffff\t\t/* End of reserved indices */\n\n/* Legal values for sh_type (section type).  */\n\n#define SHT_NULL\t  0\t\t/* Section header table entry unused */\n#define SHT_PROGBITS\t  1\t\t/* Program data */\n#define SHT_SYMTAB\t  2\t\t/* Symbol table */\n#define SHT_STRTAB\t  3\t\t/* String table */\n#define SHT_RELA\t  4\t\t/* Relocation entries with addends */\n#define SHT_HASH\t  5\t\t/* Symbol hash table */\n#define SHT_DYNAMIC\t  6\t\t/* Dynamic linking information */\n#define SHT_NOTE\t  7\t\t/* Notes */\n#define SHT_NOBITS\t  8\t\t/* Program space with no data (bss) */\n#define SHT_REL\t\t  9\t\t/* Relocation entries, no addends */\n#define SHT_SHLIB\t  10\t\t/* Reserved */\n#define SHT_DYNSYM\t  11\t\t/* Dynamic linker symbol table */\n#define SHT_INIT_ARRAY\t  14\t\t/* Array of constructors */\n#define SHT_FINI_ARRAY\t  15\t\t/* Array of destructors */\n#define SHT_PREINIT_ARRAY 16\t\t/* Array of pre-constructors */\n#define SHT_GROUP\t  17\t\t/* Section group */\n#define SHT_SYMTAB_SHNDX  18\t\t/* Extended section indeces */\n#define\tSHT_NUM\t\t  19\t\t/* Number of defined types.  */\n#define SHT_LOOS\t  0x60000000\t/* Start OS-specific.  */\n#define SHT_GNU_ATTRIBUTES 0x6ffffff5\t/* Object attributes.  */\n#define SHT_GNU_HASH\t  0x6ffffff6\t/* GNU-style hash table.  */\n#define SHT_GNU_LIBLIST\t  0x6ffffff7\t/* Prelink library list */\n#define SHT_CHECKSUM\t  0x6ffffff8\t/* Checksum for DSO content.  */\n#define SHT_LOSUNW\t  0x6ffffffa\t/* Sun-specific low bound.  */\n#define SHT_SUNW_move\t  0x6ffffffa\n#define SHT_SUNW_COMDAT   0x6ffffffb\n#define SHT_SUNW_syminfo  0x6ffffffc\n#define SHT_GNU_verdef\t  0x6ffffffd\t/* Version definition section.  */\n#define SHT_GNU_verneed\t  0x6ffffffe\t/* Version needs section.  */\n#define SHT_GNU_versym\t  0x6fffffff\t/* Version symbol table.  */\n#define SHT_HISUNW\t  0x6fffffff\t/* Sun-specific high bound.  */\n#define SHT_HIOS\t  0x6fffffff\t/* End OS-specific type */\n#define SHT_LOPROC\t  0x70000000\t/* Start of processor-specific */\n#define SHT_HIPROC\t  0x7fffffff\t/* End of processor-specific */\n#define SHT_LOUSER\t  0x80000000\t/* Start of application-specific */\n#define SHT_HIUSER\t  0x8fffffff\t/* End of application-specific */\n\n/* Legal values for sh_flags (section flags).  */\n\n#define SHF_WRITE\t     (1 << 0)\t/* Writable */\n#define SHF_ALLOC\t     (1 << 1)\t/* Occupies memory during execution */\n#define SHF_EXECINSTR\t     (1 << 2)\t/* Executable */\n#define SHF_MERGE\t     (1 << 4)\t/* Might be merged */\n#define SHF_STRINGS\t     (1 << 5)\t/* Contains nul-terminated strings */\n#define SHF_INFO_LINK\t     (1 << 6)\t/* `sh_info' contains SHT index */\n#define SHF_LINK_ORDER\t     (1 << 7)\t/* Preserve order after combining */\n#define SHF_OS_NONCONFORMING (1 << 8)\t/* Non-standard OS specific handling\n\t\t\t\t\t   required */\n#define SHF_GROUP\t     (1 << 9)\t/* Section is member of a group.  */\n#define SHF_TLS\t\t     (1 << 10)\t/* Section hold thread-local data.  */\n#define SHF_MASKOS\t     0x0ff00000\t/* OS-specific.  */\n#define SHF_MASKPROC\t     0xf0000000\t/* Processor-specific */\n#define SHF_ORDERED\t     (1 << 30)\t/* Special ordering requirement\n\t\t\t\t\t   (Solaris).  */\n#define SHF_EXCLUDE\t     (1 << 31)\t/* Section is excluded unless\n\t\t\t\t\t   referenced or allocated (Solaris).*/\n\n/* Section group handling.  */\n#define GRP_COMDAT\t0x1\t\t/* Mark group as COMDAT.  */\n\n/* Symbol table entry.  */\n\ntypedef struct\n{\n  Elf32_Word\tst_name;\t\t/* Symbol name (string tbl index) */\n  Elf32_Addr\tst_value;\t\t/* Symbol value */\n  Elf32_Word\tst_size;\t\t/* Symbol size */\n  unsigned char\tst_info;\t\t/* Symbol type and binding */\n  unsigned char\tst_other;\t\t/* Symbol visibility */\n  Elf32_Section\tst_shndx;\t\t/* Section index */\n} Elf32_Sym;\n\ntypedef struct\n{\n  Elf64_Word\tst_name;\t\t/* Symbol name (string tbl index) */\n  unsigned char\tst_info;\t\t/* Symbol type and binding */\n  unsigned char st_other;\t\t/* Symbol visibility */\n  Elf64_Section\tst_shndx;\t\t/* Section index */\n  Elf64_Addr\tst_value;\t\t/* Symbol value */\n  Elf64_Xword\tst_size;\t\t/* Symbol size */\n} Elf64_Sym;\n\n/* The syminfo section if available contains additional information about\n   every dynamic symbol.  */\n\ntypedef struct\n{\n  Elf32_Half si_boundto;\t\t/* Direct bindings, symbol bound to */\n  Elf32_Half si_flags;\t\t\t/* Per symbol flags */\n} Elf32_Syminfo;\n\ntypedef struct\n{\n  Elf64_Half si_boundto;\t\t/* Direct bindings, symbol bound to */\n  Elf64_Half si_flags;\t\t\t/* Per symbol flags */\n} Elf64_Syminfo;\n\n/* Possible values for si_boundto.  */\n#define SYMINFO_BT_SELF\t\t0xffff\t/* Symbol bound to self */\n#define SYMINFO_BT_PARENT\t0xfffe\t/* Symbol bound to parent */\n#define SYMINFO_BT_LOWRESERVE\t0xff00\t/* Beginning of reserved entries */\n\n/* Possible bitmasks for si_flags.  */\n#define SYMINFO_FLG_DIRECT\t0x0001\t/* Direct bound symbol */\n#define SYMINFO_FLG_PASSTHRU\t0x0002\t/* Pass-thru symbol for translator */\n#define SYMINFO_FLG_COPY\t0x0004\t/* Symbol is a copy-reloc */\n#define SYMINFO_FLG_LAZYLOAD\t0x0008\t/* Symbol bound to object to be lazy\n\t\t\t\t\t   loaded */\n/* Syminfo version values.  */\n#define SYMINFO_NONE\t\t0\n#define SYMINFO_CURRENT\t\t1\n#define SYMINFO_NUM\t\t2\n\n\n/* How to extract and insert information held in the st_info field.  */\n\n#define ELF32_ST_BIND(val)\t\t(((unsigned char) (val)) >> 4)\n#define ELF32_ST_TYPE(val)\t\t((val) & 0xf)\n#define ELF32_ST_INFO(bind, type)\t(((bind) << 4) + ((type) & 0xf))\n\n/* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field.  */\n#define ELF64_ST_BIND(val)\t\tELF32_ST_BIND (val)\n#define ELF64_ST_TYPE(val)\t\tELF32_ST_TYPE (val)\n#define ELF64_ST_INFO(bind, type)\tELF32_ST_INFO ((bind), (type))\n\n/* Legal values for ST_BIND subfield of st_info (symbol binding).  */\n\n#define STB_LOCAL\t0\t\t/* Local symbol */\n#define STB_GLOBAL\t1\t\t/* Global symbol */\n#define STB_WEAK\t2\t\t/* Weak symbol */\n#define\tSTB_NUM\t\t3\t\t/* Number of defined types.  */\n#define STB_LOOS\t10\t\t/* Start of OS-specific */\n#define STB_GNU_UNIQUE\t10\t\t/* Unique symbol.  */\n#define STB_HIOS\t12\t\t/* End of OS-specific */\n#define STB_LOPROC\t13\t\t/* Start of processor-specific */\n#define STB_HIPROC\t15\t\t/* End of processor-specific */\n\n/* Legal values for ST_TYPE subfield of st_info (symbol type).  */\n\n#define STT_NOTYPE\t0\t\t/* Symbol type is unspecified */\n#define STT_OBJECT\t1\t\t/* Symbol is a data object */\n#define STT_FUNC\t2\t\t/* Symbol is a code object */\n#define STT_SECTION\t3\t\t/* Symbol associated with a section */\n#define STT_FILE\t4\t\t/* Symbol's name is file name */\n#define STT_COMMON\t5\t\t/* Symbol is a common data object */\n#define STT_TLS\t\t6\t\t/* Symbol is thread-local data object*/\n#define\tSTT_NUM\t\t7\t\t/* Number of defined types.  */\n#define STT_LOOS\t10\t\t/* Start of OS-specific */\n#define STT_GNU_IFUNC\t10\t\t/* Symbol is indirect code object */\n#define STT_HIOS\t12\t\t/* End of OS-specific */\n#define STT_LOPROC\t13\t\t/* Start of processor-specific */\n#define STT_HIPROC\t15\t\t/* End of processor-specific */\n\n\n/* Symbol table indices are found in the hash buckets and chain table\n   of a symbol hash table section.  This special index value indicates\n   the end of a chain, meaning no further symbols are found in that bucket.  */\n\n#define STN_UNDEF\t0\t\t/* End of a chain.  */\n\n\n/* How to extract and insert information held in the st_other field.  */\n\n#define ELF32_ST_VISIBILITY(o)\t((o) & 0x03)\n\n/* For ELF64 the definitions are the same.  */\n#define ELF64_ST_VISIBILITY(o)\tELF32_ST_VISIBILITY (o)\n\n/* Symbol visibility specification encoded in the st_other field.  */\n#define STV_DEFAULT\t0\t\t/* Default symbol visibility rules */\n#define STV_INTERNAL\t1\t\t/* Processor specific hidden class */\n#define STV_HIDDEN\t2\t\t/* Sym unavailable in other modules */\n#define STV_PROTECTED\t3\t\t/* Not preemptible, not exported */\n\n\n/* Relocation table entry without addend (in section of type SHT_REL).  */\n\ntypedef struct\n{\n  Elf32_Addr\tr_offset;\t\t/* Address */\n  Elf32_Word\tr_info;\t\t\t/* Relocation type and symbol index */\n} Elf32_Rel;\n\n/* I have seen two different definitions of the Elf64_Rel and\n   Elf64_Rela structures, so we'll leave them out until Novell (or\n   whoever) gets their act together.  */\n/* The following, at least, is used on Sparc v9, MIPS, and Alpha.  */\n\ntypedef struct\n{\n  Elf64_Addr\tr_offset;\t\t/* Address */\n  Elf64_Xword\tr_info;\t\t\t/* Relocation type and symbol index */\n} Elf64_Rel;\n\n/* Relocation table entry with addend (in section of type SHT_RELA).  */\n\ntypedef struct\n{\n  Elf32_Addr\tr_offset;\t\t/* Address */\n  Elf32_Word\tr_info;\t\t\t/* Relocation type and symbol index */\n  Elf32_Sword\tr_addend;\t\t/* Addend */\n} Elf32_Rela;\n\ntypedef struct\n{\n  Elf64_Addr\tr_offset;\t\t/* Address */\n  Elf64_Xword\tr_info;\t\t\t/* Relocation type and symbol index */\n  Elf64_Sxword\tr_addend;\t\t/* Addend */\n} Elf64_Rela;\n\n/* How to extract and insert information held in the r_info field.  */\n\n#define ELF32_R_SYM(val)\t\t((val) >> 8)\n#define ELF32_R_TYPE(val)\t\t((val) & 0xff)\n#define ELF32_R_INFO(sym, type)\t\t(((sym) << 8) + ((type) & 0xff))\n\n#define ELF64_R_SYM(i)\t\t\t((i) >> 32)\n#define ELF64_R_TYPE(i)\t\t\t((i) & 0xffffffff)\n#define ELF64_R_INFO(sym,type)\t\t((((Elf64_Xword) (sym)) << 32) + (type))\n\n/* Program segment header.  */\n\ntypedef struct\n{\n  Elf32_Word\tp_type;\t\t\t/* Segment type */\n  Elf32_Off\tp_offset;\t\t/* Segment file offset */\n  Elf32_Addr\tp_vaddr;\t\t/* Segment virtual address */\n  Elf32_Addr\tp_paddr;\t\t/* Segment physical address */\n  Elf32_Word\tp_filesz;\t\t/* Segment size in file */\n  Elf32_Word\tp_memsz;\t\t/* Segment size in memory */\n  Elf32_Word\tp_flags;\t\t/* Segment flags */\n  Elf32_Word\tp_align;\t\t/* Segment alignment */\n} Elf32_Phdr;\n\ntypedef struct\n{\n  Elf64_Word\tp_type;\t\t\t/* Segment type */\n  Elf64_Word\tp_flags;\t\t/* Segment flags */\n  Elf64_Off\tp_offset;\t\t/* Segment file offset */\n  Elf64_Addr\tp_vaddr;\t\t/* Segment virtual address */\n  Elf64_Addr\tp_paddr;\t\t/* Segment physical address */\n  Elf64_Xword\tp_filesz;\t\t/* Segment size in file */\n  Elf64_Xword\tp_memsz;\t\t/* Segment size in memory */\n  Elf64_Xword\tp_align;\t\t/* Segment alignment */\n} Elf64_Phdr;\n\n/* Special value for e_phnum.  This indicates that the real number of\n   program headers is too large to fit into e_phnum.  Instead the real\n   value is in the field sh_info of section 0.  */\n\n#define PN_XNUM\t\t0xffff\n\n/* Legal values for p_type (segment type).  */\n\n#define\tPT_NULL\t\t0\t\t/* Program header table entry unused */\n#define PT_LOAD\t\t1\t\t/* Loadable program segment */\n#define PT_DYNAMIC\t2\t\t/* Dynamic linking information */\n#define PT_INTERP\t3\t\t/* Program interpreter */\n#define PT_NOTE\t\t4\t\t/* Auxiliary information */\n#define PT_SHLIB\t5\t\t/* Reserved */\n#define PT_PHDR\t\t6\t\t/* Entry for header table itself */\n#define PT_TLS\t\t7\t\t/* Thread-local storage segment */\n#define\tPT_NUM\t\t8\t\t/* Number of defined types */\n#define PT_LOOS\t\t0x60000000\t/* Start of OS-specific */\n#define PT_GNU_EH_FRAME\t0x6474e550\t/* GCC .eh_frame_hdr segment */\n#define PT_GNU_STACK\t0x6474e551\t/* Indicates stack executability */\n#define PT_GNU_RELRO\t0x6474e552\t/* Read-only after relocation */\n#define PT_LOSUNW\t0x6ffffffa\n#define PT_SUNWBSS\t0x6ffffffa\t/* Sun Specific segment */\n#define PT_SUNWSTACK\t0x6ffffffb\t/* Stack segment */\n#define PT_HISUNW\t0x6fffffff\n#define PT_HIOS\t\t0x6fffffff\t/* End of OS-specific */\n#define PT_LOPROC\t0x70000000\t/* Start of processor-specific */\n#define PT_HIPROC\t0x7fffffff\t/* End of processor-specific */\n\n/* Legal values for p_flags (segment flags).  */\n\n#define PF_X\t\t(1 << 0)\t/* Segment is executable */\n#define PF_W\t\t(1 << 1)\t/* Segment is writable */\n#define PF_R\t\t(1 << 2)\t/* Segment is readable */\n#define PF_MASKOS\t0x0ff00000\t/* OS-specific */\n#define PF_MASKPROC\t0xf0000000\t/* Processor-specific */\n\n/* Legal values for note segment descriptor types for core files. */\n\n#define NT_PRSTATUS\t1\t\t/* Contains copy of prstatus struct */\n#define NT_FPREGSET\t2\t\t/* Contains copy of fpregset struct */\n#define NT_PRPSINFO\t3\t\t/* Contains copy of prpsinfo struct */\n#define NT_PRXREG\t4\t\t/* Contains copy of prxregset struct */\n#define NT_TASKSTRUCT\t4\t\t/* Contains copy of task structure */\n#define NT_PLATFORM\t5\t\t/* String from sysinfo(SI_PLATFORM) */\n#define NT_AUXV\t\t6\t\t/* Contains copy of auxv array */\n#define NT_GWINDOWS\t7\t\t/* Contains copy of gwindows struct */\n#define NT_ASRS\t\t8\t\t/* Contains copy of asrset struct */\n#define NT_PSTATUS\t10\t\t/* Contains copy of pstatus struct */\n#define NT_PSINFO\t13\t\t/* Contains copy of psinfo struct */\n#define NT_PRCRED\t14\t\t/* Contains copy of prcred struct */\n#define NT_UTSNAME\t15\t\t/* Contains copy of utsname struct */\n#define NT_LWPSTATUS\t16\t\t/* Contains copy of lwpstatus struct */\n#define NT_LWPSINFO\t17\t\t/* Contains copy of lwpinfo struct */\n#define NT_PRFPXREG\t20\t\t/* Contains copy of fprxregset struct */\n#define NT_PRXFPREG\t0x46e62b7f\t/* Contains copy of user_fxsr_struct */\n#define NT_PPC_VMX\t0x100\t\t/* PowerPC Altivec/VMX registers */\n#define NT_PPC_SPE\t0x101\t\t/* PowerPC SPE/EVR registers */\n#define NT_PPC_VSX\t0x102\t\t/* PowerPC VSX registers */\n#define NT_386_TLS\t0x200\t\t/* i386 TLS slots (struct user_desc) */\n#define NT_386_IOPERM\t0x201\t\t/* x86 io permission bitmap (1=deny) */\n#define NT_X86_XSTATE\t0x202\t\t/* x86 extended state using xsave */\n\n/* Legal values for the note segment descriptor types for object files.  */\n\n#define NT_VERSION\t1\t\t/* Contains a version string.  */\n\n\n/* Dynamic section entry.  */\n\ntypedef struct\n{\n  Elf32_Sword\td_tag;\t\t\t/* Dynamic entry type */\n  union\n    {\n      Elf32_Word d_val;\t\t\t/* Integer value */\n      Elf32_Addr d_ptr;\t\t\t/* Address value */\n    } d_un;\n} Elf32_Dyn;\n\ntypedef struct\n{\n  Elf64_Sxword\td_tag;\t\t\t/* Dynamic entry type */\n  union\n    {\n      Elf64_Xword d_val;\t\t/* Integer value */\n      Elf64_Addr d_ptr;\t\t\t/* Address value */\n    } d_un;\n} Elf64_Dyn;\n\n/* Legal values for d_tag (dynamic entry type).  */\n\n#define DT_NULL\t\t0\t\t/* Marks end of dynamic section */\n#define DT_NEEDED\t1\t\t/* Name of needed library */\n#define DT_PLTRELSZ\t2\t\t/* Size in bytes of PLT relocs */\n#define DT_PLTGOT\t3\t\t/* Processor defined value */\n#define DT_HASH\t\t4\t\t/* Address of symbol hash table */\n#define DT_STRTAB\t5\t\t/* Address of string table */\n#define DT_SYMTAB\t6\t\t/* Address of symbol table */\n#define DT_RELA\t\t7\t\t/* Address of Rela relocs */\n#define DT_RELASZ\t8\t\t/* Total size of Rela relocs */\n#define DT_RELAENT\t9\t\t/* Size of one Rela reloc */\n#define DT_STRSZ\t10\t\t/* Size of string table */\n#define DT_SYMENT\t11\t\t/* Size of one symbol table entry */\n#define DT_INIT\t\t12\t\t/* Address of init function */\n#define DT_FINI\t\t13\t\t/* Address of termination function */\n#define DT_SONAME\t14\t\t/* Name of shared object */\n#define DT_RPATH\t15\t\t/* Library search path (deprecated) */\n#define DT_SYMBOLIC\t16\t\t/* Start symbol search here */\n#define DT_REL\t\t17\t\t/* Address of Rel relocs */\n#define DT_RELSZ\t18\t\t/* Total size of Rel relocs */\n#define DT_RELENT\t19\t\t/* Size of one Rel reloc */\n#define DT_PLTREL\t20\t\t/* Type of reloc in PLT */\n#define DT_DEBUG\t21\t\t/* For debugging; unspecified */\n#define DT_TEXTREL\t22\t\t/* Reloc might modify .text */\n#define DT_JMPREL\t23\t\t/* Address of PLT relocs */\n#define\tDT_BIND_NOW\t24\t\t/* Process relocations of object */\n#define\tDT_INIT_ARRAY\t25\t\t/* Array with addresses of init fct */\n#define\tDT_FINI_ARRAY\t26\t\t/* Array with addresses of fini fct */\n#define\tDT_INIT_ARRAYSZ\t27\t\t/* Size in bytes of DT_INIT_ARRAY */\n#define\tDT_FINI_ARRAYSZ\t28\t\t/* Size in bytes of DT_FINI_ARRAY */\n#define DT_RUNPATH\t29\t\t/* Library search path */\n#define DT_FLAGS\t30\t\t/* Flags for the object being loaded */\n#define DT_ENCODING\t32\t\t/* Start of encoded range */\n#define DT_PREINIT_ARRAY 32\t\t/* Array with addresses of preinit fct*/\n#define DT_PREINIT_ARRAYSZ 33\t\t/* size in bytes of DT_PREINIT_ARRAY */\n#define\tDT_NUM\t\t34\t\t/* Number used */\n#define DT_LOOS\t\t0x6000000d\t/* Start of OS-specific */\n#define DT_HIOS\t\t0x6ffff000\t/* End of OS-specific */\n#define DT_LOPROC\t0x70000000\t/* Start of processor-specific */\n#define DT_HIPROC\t0x7fffffff\t/* End of processor-specific */\n#define\tDT_PROCNUM\tDT_MIPS_NUM\t/* Most used by any processor */\n\n/* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the\n   Dyn.d_un.d_val field of the Elf*_Dyn structure.  This follows Sun's\n   approach.  */\n#define DT_VALRNGLO\t0x6ffffd00\n#define DT_GNU_PRELINKED 0x6ffffdf5\t/* Prelinking timestamp */\n#define DT_GNU_CONFLICTSZ 0x6ffffdf6\t/* Size of conflict section */\n#define DT_GNU_LIBLISTSZ 0x6ffffdf7\t/* Size of library list */\n#define DT_CHECKSUM\t0x6ffffdf8\n#define DT_PLTPADSZ\t0x6ffffdf9\n#define DT_MOVEENT\t0x6ffffdfa\n#define DT_MOVESZ\t0x6ffffdfb\n#define DT_FEATURE_1\t0x6ffffdfc\t/* Feature selection (DTF_*).  */\n#define DT_POSFLAG_1\t0x6ffffdfd\t/* Flags for DT_* entries, effecting\n\t\t\t\t\t   the following DT_* entry.  */\n#define DT_SYMINSZ\t0x6ffffdfe\t/* Size of syminfo table (in bytes) */\n#define DT_SYMINENT\t0x6ffffdff\t/* Entry size of syminfo */\n#define DT_VALRNGHI\t0x6ffffdff\n#define DT_VALTAGIDX(tag)\t(DT_VALRNGHI - (tag))\t/* Reverse order! */\n#define DT_VALNUM 12\n\n/* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the\n   Dyn.d_un.d_ptr field of the Elf*_Dyn structure.\n\n   If any adjustment is made to the ELF object after it has been\n   built these entries will need to be adjusted.  */\n#define DT_ADDRRNGLO\t0x6ffffe00\n#define DT_GNU_HASH\t0x6ffffef5\t/* GNU-style hash table.  */\n#define DT_TLSDESC_PLT\t0x6ffffef6\n#define DT_TLSDESC_GOT\t0x6ffffef7\n#define DT_GNU_CONFLICT\t0x6ffffef8\t/* Start of conflict section */\n#define DT_GNU_LIBLIST\t0x6ffffef9\t/* Library list */\n#define DT_CONFIG\t0x6ffffefa\t/* Configuration information.  */\n#define DT_DEPAUDIT\t0x6ffffefb\t/* Dependency auditing.  */\n#define DT_AUDIT\t0x6ffffefc\t/* Object auditing.  */\n#define\tDT_PLTPAD\t0x6ffffefd\t/* PLT padding.  */\n#define\tDT_MOVETAB\t0x6ffffefe\t/* Move table.  */\n#define DT_SYMINFO\t0x6ffffeff\t/* Syminfo table.  */\n#define DT_ADDRRNGHI\t0x6ffffeff\n#define DT_ADDRTAGIDX(tag)\t(DT_ADDRRNGHI - (tag))\t/* Reverse order! */\n#define DT_ADDRNUM 11\n\n/* The versioning entry types.  The next are defined as part of the\n   GNU extension.  */\n#define DT_VERSYM\t0x6ffffff0\n\n#define DT_RELACOUNT\t0x6ffffff9\n#define DT_RELCOUNT\t0x6ffffffa\n\n/* These were chosen by Sun.  */\n#define DT_FLAGS_1\t0x6ffffffb\t/* State flags, see DF_1_* below.  */\n#define\tDT_VERDEF\t0x6ffffffc\t/* Address of version definition\n\t\t\t\t\t   table */\n#define\tDT_VERDEFNUM\t0x6ffffffd\t/* Number of version definitions */\n#define\tDT_VERNEED\t0x6ffffffe\t/* Address of table with needed\n\t\t\t\t\t   versions */\n#define\tDT_VERNEEDNUM\t0x6fffffff\t/* Number of needed versions */\n#define DT_VERSIONTAGIDX(tag)\t(DT_VERNEEDNUM - (tag))\t/* Reverse order! */\n#define DT_VERSIONTAGNUM 16\n\n/* Sun added these machine-independent extensions in the \"processor-specific\"\n   range.  Be compatible.  */\n#define DT_AUXILIARY    0x7ffffffd      /* Shared object to load before self */\n#define DT_FILTER       0x7fffffff      /* Shared object to get values from */\n#define DT_EXTRATAGIDX(tag)\t((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1)\n#define DT_EXTRANUM\t3\n\n/* Values of `d_un.d_val' in the DT_FLAGS entry.  */\n#define DF_ORIGIN\t0x00000001\t/* Object may use DF_ORIGIN */\n#define DF_SYMBOLIC\t0x00000002\t/* Symbol resolutions starts here */\n#define DF_TEXTREL\t0x00000004\t/* Object contains text relocations */\n#define DF_BIND_NOW\t0x00000008\t/* No lazy binding for this object */\n#define DF_STATIC_TLS\t0x00000010\t/* Module uses the static TLS model */\n\n/* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1\n   entry in the dynamic section.  */\n#define DF_1_NOW\t0x00000001\t/* Set RTLD_NOW for this object.  */\n#define DF_1_GLOBAL\t0x00000002\t/* Set RTLD_GLOBAL for this object.  */\n#define DF_1_GROUP\t0x00000004\t/* Set RTLD_GROUP for this object.  */\n#define DF_1_NODELETE\t0x00000008\t/* Set RTLD_NODELETE for this object.*/\n#define DF_1_LOADFLTR\t0x00000010\t/* Trigger filtee loading at runtime.*/\n#define DF_1_INITFIRST\t0x00000020\t/* Set RTLD_INITFIRST for this object*/\n#define DF_1_NOOPEN\t0x00000040\t/* Set RTLD_NOOPEN for this object.  */\n#define DF_1_ORIGIN\t0x00000080\t/* $ORIGIN must be handled.  */\n#define DF_1_DIRECT\t0x00000100\t/* Direct binding enabled.  */\n#define DF_1_TRANS\t0x00000200\n#define DF_1_INTERPOSE\t0x00000400\t/* Object is used to interpose.  */\n#define DF_1_NODEFLIB\t0x00000800\t/* Ignore default lib search path.  */\n#define DF_1_NODUMP\t0x00001000\t/* Object can't be dldump'ed.  */\n#define DF_1_CONFALT\t0x00002000\t/* Configuration alternative created.*/\n#define DF_1_ENDFILTEE\t0x00004000\t/* Filtee terminates filters search. */\n#define\tDF_1_DISPRELDNE\t0x00008000\t/* Disp reloc applied at build time. */\n#define\tDF_1_DISPRELPND\t0x00010000\t/* Disp reloc applied at run-time.  */\n\n/* Flags for the feature selection in DT_FEATURE_1.  */\n#define DTF_1_PARINIT\t0x00000001\n#define DTF_1_CONFEXP\t0x00000002\n\n/* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry.  */\n#define DF_P1_LAZYLOAD\t0x00000001\t/* Lazyload following object.  */\n#define DF_P1_GROUPPERM\t0x00000002\t/* Symbols from next object are not\n\t\t\t\t\t   generally available.  */\n\n/* Version definition sections.  */\n\ntypedef struct\n{\n  Elf32_Half\tvd_version;\t\t/* Version revision */\n  Elf32_Half\tvd_flags;\t\t/* Version information */\n  Elf32_Half\tvd_ndx;\t\t\t/* Version Index */\n  Elf32_Half\tvd_cnt;\t\t\t/* Number of associated aux entries */\n  Elf32_Word\tvd_hash;\t\t/* Version name hash value */\n  Elf32_Word\tvd_aux;\t\t\t/* Offset in bytes to verdaux array */\n  Elf32_Word\tvd_next;\t\t/* Offset in bytes to next verdef\n\t\t\t\t\t   entry */\n} Elf32_Verdef;\n\ntypedef struct\n{\n  Elf64_Half\tvd_version;\t\t/* Version revision */\n  Elf64_Half\tvd_flags;\t\t/* Version information */\n  Elf64_Half\tvd_ndx;\t\t\t/* Version Index */\n  Elf64_Half\tvd_cnt;\t\t\t/* Number of associated aux entries */\n  Elf64_Word\tvd_hash;\t\t/* Version name hash value */\n  Elf64_Word\tvd_aux;\t\t\t/* Offset in bytes to verdaux array */\n  Elf64_Word\tvd_next;\t\t/* Offset in bytes to next verdef\n\t\t\t\t\t   entry */\n} Elf64_Verdef;\n\n\n/* Legal values for vd_version (version revision).  */\n#define VER_DEF_NONE\t0\t\t/* No version */\n#define VER_DEF_CURRENT\t1\t\t/* Current version */\n#define VER_DEF_NUM\t2\t\t/* Given version number */\n\n/* Legal values for vd_flags (version information flags).  */\n#define VER_FLG_BASE\t0x1\t\t/* Version definition of file itself */\n#define VER_FLG_WEAK\t0x2\t\t/* Weak version identifier */\n\n/* Versym symbol index values.  */\n#define\tVER_NDX_LOCAL\t\t0\t/* Symbol is local.  */\n#define\tVER_NDX_GLOBAL\t\t1\t/* Symbol is global.  */\n#define\tVER_NDX_LORESERVE\t0xff00\t/* Beginning of reserved entries.  */\n#define\tVER_NDX_ELIMINATE\t0xff01\t/* Symbol is to be eliminated.  */\n\n/* Auxialiary version information.  */\n\ntypedef struct\n{\n  Elf32_Word\tvda_name;\t\t/* Version or dependency names */\n  Elf32_Word\tvda_next;\t\t/* Offset in bytes to next verdaux\n\t\t\t\t\t   entry */\n} Elf32_Verdaux;\n\ntypedef struct\n{\n  Elf64_Word\tvda_name;\t\t/* Version or dependency names */\n  Elf64_Word\tvda_next;\t\t/* Offset in bytes to next verdaux\n\t\t\t\t\t   entry */\n} Elf64_Verdaux;\n\n\n/* Version dependency section.  */\n\ntypedef struct\n{\n  Elf32_Half\tvn_version;\t\t/* Version of structure */\n  Elf32_Half\tvn_cnt;\t\t\t/* Number of associated aux entries */\n  Elf32_Word\tvn_file;\t\t/* Offset of filename for this\n\t\t\t\t\t   dependency */\n  Elf32_Word\tvn_aux;\t\t\t/* Offset in bytes to vernaux array */\n  Elf32_Word\tvn_next;\t\t/* Offset in bytes to next verneed\n\t\t\t\t\t   entry */\n} Elf32_Verneed;\n\ntypedef struct\n{\n  Elf64_Half\tvn_version;\t\t/* Version of structure */\n  Elf64_Half\tvn_cnt;\t\t\t/* Number of associated aux entries */\n  Elf64_Word\tvn_file;\t\t/* Offset of filename for this\n\t\t\t\t\t   dependency */\n  Elf64_Word\tvn_aux;\t\t\t/* Offset in bytes to vernaux array */\n  Elf64_Word\tvn_next;\t\t/* Offset in bytes to next verneed\n\t\t\t\t\t   entry */\n} Elf64_Verneed;\n\n\n/* Legal values for vn_version (version revision).  */\n#define VER_NEED_NONE\t 0\t\t/* No version */\n#define VER_NEED_CURRENT 1\t\t/* Current version */\n#define VER_NEED_NUM\t 2\t\t/* Given version number */\n\n/* Auxiliary needed version information.  */\n\ntypedef struct\n{\n  Elf32_Word\tvna_hash;\t\t/* Hash value of dependency name */\n  Elf32_Half\tvna_flags;\t\t/* Dependency specific information */\n  Elf32_Half\tvna_other;\t\t/* Unused */\n  Elf32_Word\tvna_name;\t\t/* Dependency name string offset */\n  Elf32_Word\tvna_next;\t\t/* Offset in bytes to next vernaux\n\t\t\t\t\t   entry */\n} Elf32_Vernaux;\n\ntypedef struct\n{\n  Elf64_Word\tvna_hash;\t\t/* Hash value of dependency name */\n  Elf64_Half\tvna_flags;\t\t/* Dependency specific information */\n  Elf64_Half\tvna_other;\t\t/* Unused */\n  Elf64_Word\tvna_name;\t\t/* Dependency name string offset */\n  Elf64_Word\tvna_next;\t\t/* Offset in bytes to next vernaux\n\t\t\t\t\t   entry */\n} Elf64_Vernaux;\n\n\n/* Legal values for vna_flags.  */\n#define VER_FLG_WEAK\t0x2\t\t/* Weak version identifier */\n\n\n/* Auxiliary vector.  */\n\n/* This vector is normally only used by the program interpreter.  The\n   usual definition in an ABI supplement uses the name auxv_t.  The\n   vector is not usually defined in a standard <elf.h> file, but it\n   can't hurt.  We rename it to avoid conflicts.  The sizes of these\n   types are an arrangement between the exec server and the program\n   interpreter, so we don't fully specify them here.  */\n\ntypedef struct\n{\n  uint32_t a_type;\t\t/* Entry type */\n  union\n    {\n      uint32_t a_val;\t\t/* Integer value */\n      /* We use to have pointer elements added here.  We cannot do that,\n\t though, since it does not work when using 32-bit definitions\n\t on 64-bit platforms and vice versa.  */\n    } a_un;\n} Elf32_auxv_t;\n\ntypedef struct\n{\n  uint64_t a_type;\t\t/* Entry type */\n  union\n    {\n      uint64_t a_val;\t\t/* Integer value */\n      /* We use to have pointer elements added here.  We cannot do that,\n\t though, since it does not work when using 32-bit definitions\n\t on 64-bit platforms and vice versa.  */\n    } a_un;\n} Elf64_auxv_t;\n\n/* Legal values for a_type (entry type).  */\n\n#define AT_NULL\t\t0\t\t/* End of vector */\n#define AT_IGNORE\t1\t\t/* Entry should be ignored */\n#define AT_EXECFD\t2\t\t/* File descriptor of program */\n#define AT_PHDR\t\t3\t\t/* Program headers for program */\n#define AT_PHENT\t4\t\t/* Size of program header entry */\n#define AT_PHNUM\t5\t\t/* Number of program headers */\n#define AT_PAGESZ\t6\t\t/* System page size */\n#define AT_BASE\t\t7\t\t/* Base address of interpreter */\n#define AT_FLAGS\t8\t\t/* Flags */\n#define AT_ENTRY\t9\t\t/* Entry point of program */\n#define AT_NOTELF\t10\t\t/* Program is not ELF */\n#define AT_UID\t\t11\t\t/* Real uid */\n#define AT_EUID\t\t12\t\t/* Effective uid */\n#define AT_GID\t\t13\t\t/* Real gid */\n#define AT_EGID\t\t14\t\t/* Effective gid */\n#define AT_CLKTCK\t17\t\t/* Frequency of times() */\n\n/* Some more special a_type values describing the hardware.  */\n#define AT_PLATFORM\t15\t\t/* String identifying platform.  */\n#define AT_HWCAP\t16\t\t/* Machine dependent hints about\n\t\t\t\t\t   processor capabilities.  */\n\n/* This entry gives some information about the FPU initialization\n   performed by the kernel.  */\n#define AT_FPUCW\t18\t\t/* Used FPU control word.  */\n\n/* Cache block sizes.  */\n#define AT_DCACHEBSIZE\t19\t\t/* Data cache block size.  */\n#define AT_ICACHEBSIZE\t20\t\t/* Instruction cache block size.  */\n#define AT_UCACHEBSIZE\t21\t\t/* Unified cache block size.  */\n\n/* A special ignored value for PPC, used by the kernel to control the\n   interpretation of the AUXV. Must be > 16.  */\n#define AT_IGNOREPPC\t22\t\t/* Entry should be ignored.  */\n\n#define\tAT_SECURE\t23\t\t/* Boolean, was exec setuid-like?  */\n\n#define AT_BASE_PLATFORM 24\t\t/* String identifying real platforms.*/\n\n#define AT_RANDOM\t25\t\t/* Address of 16 random bytes.  */\n\n#define AT_EXECFN\t31\t\t/* Filename of executable.  */\n\n/* Pointer to the global system page used for system calls and other\n   nice things.  */\n#define AT_SYSINFO\t32\n#define AT_SYSINFO_EHDR\t33\n\n/* Shapes of the caches.  Bits 0-3 contains associativity; bits 4-7 contains\n   log2 of line size; mask those to get cache size.  */\n#define AT_L1I_CACHESHAPE\t34\n#define AT_L1D_CACHESHAPE\t35\n#define AT_L2_CACHESHAPE\t36\n#define AT_L3_CACHESHAPE\t37\n\n/* Note section contents.  Each entry in the note section begins with\n   a header of a fixed form.  */\n\ntypedef struct\n{\n  Elf32_Word n_namesz;\t\t\t/* Length of the note's name.  */\n  Elf32_Word n_descsz;\t\t\t/* Length of the note's descriptor.  */\n  Elf32_Word n_type;\t\t\t/* Type of the note.  */\n} Elf32_Nhdr;\n\ntypedef struct\n{\n  Elf64_Word n_namesz;\t\t\t/* Length of the note's name.  */\n  Elf64_Word n_descsz;\t\t\t/* Length of the note's descriptor.  */\n  Elf64_Word n_type;\t\t\t/* Type of the note.  */\n} Elf64_Nhdr;\n\n/* Known names of notes.  */\n\n/* Solaris entries in the note section have this name.  */\n#define ELF_NOTE_SOLARIS\t\"SUNW Solaris\"\n\n/* Note entries for GNU systems have this name.  */\n#define ELF_NOTE_GNU\t\t\"GNU\"\n\n\n/* Defined types of notes for Solaris.  */\n\n/* Value of descriptor (one word) is desired pagesize for the binary.  */\n#define ELF_NOTE_PAGESIZE_HINT\t1\n\n\n/* Defined note types for GNU systems.  */\n\n/* ABI information.  The descriptor consists of words:\n   word 0: OS descriptor\n   word 1: major version of the ABI\n   word 2: minor version of the ABI\n   word 3: subminor version of the ABI\n*/\n#define NT_GNU_ABI_TAG\t1\n#define ELF_NOTE_ABI\tNT_GNU_ABI_TAG /* Old name.  */\n\n/* Known OSes.  These values can appear in word 0 of an\n   NT_GNU_ABI_TAG note section entry.  */\n#define ELF_NOTE_OS_LINUX\t0\n#define ELF_NOTE_OS_GNU\t\t1\n#define ELF_NOTE_OS_SOLARIS2\t2\n#define ELF_NOTE_OS_FREEBSD\t3\n\n/* Synthetic hwcap information.  The descriptor begins with two words:\n   word 0: number of entries\n   word 1: bitmask of enabled entries\n   Then follow variable-length entries, one byte followed by a\n   '\\0'-terminated hwcap name string.  The byte gives the bit\n   number to test if enabled, (1U << bit) & bitmask.  */\n#define NT_GNU_HWCAP\t2\n\n/* Build ID bits as generated by ld --build-id.\n   The descriptor consists of any nonzero number of bytes.  */\n#define NT_GNU_BUILD_ID\t3\n\n/* Version note generated by GNU gold containing a version string.  */\n#define NT_GNU_GOLD_VERSION\t4\n\n\n/* Move records.  */\ntypedef struct\n{\n  Elf32_Xword m_value;\t\t/* Symbol value.  */\n  Elf32_Word m_info;\t\t/* Size and index.  */\n  Elf32_Word m_poffset;\t\t/* Symbol offset.  */\n  Elf32_Half m_repeat;\t\t/* Repeat count.  */\n  Elf32_Half m_stride;\t\t/* Stride info.  */\n} Elf32_Move;\n\ntypedef struct\n{\n  Elf64_Xword m_value;\t\t/* Symbol value.  */\n  Elf64_Xword m_info;\t\t/* Size and index.  */\n  Elf64_Xword m_poffset;\t/* Symbol offset.  */\n  Elf64_Half m_repeat;\t\t/* Repeat count.  */\n  Elf64_Half m_stride;\t\t/* Stride info.  */\n} Elf64_Move;\n\n/* Macro to construct move records.  */\n#define ELF32_M_SYM(info)\t((info) >> 8)\n#define ELF32_M_SIZE(info)\t((unsigned char) (info))\n#define ELF32_M_INFO(sym, size)\t(((sym) << 8) + (unsigned char) (size))\n\n#define ELF64_M_SYM(info)\tELF32_M_SYM (info)\n#define ELF64_M_SIZE(info)\tELF32_M_SIZE (info)\n#define ELF64_M_INFO(sym, size)\tELF32_M_INFO (sym, size)\n\n\n/* Motorola 68k specific definitions.  */\n\n/* Values for Elf32_Ehdr.e_flags.  */\n#define EF_CPU32\t0x00810000\n\n/* m68k relocs.  */\n\n#define R_68K_NONE\t0\t\t/* No reloc */\n#define R_68K_32\t1\t\t/* Direct 32 bit  */\n#define R_68K_16\t2\t\t/* Direct 16 bit  */\n#define R_68K_8\t\t3\t\t/* Direct 8 bit  */\n#define R_68K_PC32\t4\t\t/* PC relative 32 bit */\n#define R_68K_PC16\t5\t\t/* PC relative 16 bit */\n#define R_68K_PC8\t6\t\t/* PC relative 8 bit */\n#define R_68K_GOT32\t7\t\t/* 32 bit PC relative GOT entry */\n#define R_68K_GOT16\t8\t\t/* 16 bit PC relative GOT entry */\n#define R_68K_GOT8\t9\t\t/* 8 bit PC relative GOT entry */\n#define R_68K_GOT32O\t10\t\t/* 32 bit GOT offset */\n#define R_68K_GOT16O\t11\t\t/* 16 bit GOT offset */\n#define R_68K_GOT8O\t12\t\t/* 8 bit GOT offset */\n#define R_68K_PLT32\t13\t\t/* 32 bit PC relative PLT address */\n#define R_68K_PLT16\t14\t\t/* 16 bit PC relative PLT address */\n#define R_68K_PLT8\t15\t\t/* 8 bit PC relative PLT address */\n#define R_68K_PLT32O\t16\t\t/* 32 bit PLT offset */\n#define R_68K_PLT16O\t17\t\t/* 16 bit PLT offset */\n#define R_68K_PLT8O\t18\t\t/* 8 bit PLT offset */\n#define R_68K_COPY\t19\t\t/* Copy symbol at runtime */\n#define R_68K_GLOB_DAT\t20\t\t/* Create GOT entry */\n#define R_68K_JMP_SLOT\t21\t\t/* Create PLT entry */\n#define R_68K_RELATIVE\t22\t\t/* Adjust by program base */\n#define R_68K_TLS_GD32      25          /* 32 bit GOT offset for GD */\n#define R_68K_TLS_GD16      26          /* 16 bit GOT offset for GD */\n#define R_68K_TLS_GD8       27          /* 8 bit GOT offset for GD */\n#define R_68K_TLS_LDM32     28          /* 32 bit GOT offset for LDM */\n#define R_68K_TLS_LDM16     29          /* 16 bit GOT offset for LDM */\n#define R_68K_TLS_LDM8      30          /* 8 bit GOT offset for LDM */\n#define R_68K_TLS_LDO32     31          /* 32 bit module-relative offset */\n#define R_68K_TLS_LDO16     32          /* 16 bit module-relative offset */\n#define R_68K_TLS_LDO8      33          /* 8 bit module-relative offset */\n#define R_68K_TLS_IE32      34          /* 32 bit GOT offset for IE */\n#define R_68K_TLS_IE16      35          /* 16 bit GOT offset for IE */\n#define R_68K_TLS_IE8       36          /* 8 bit GOT offset for IE */\n#define R_68K_TLS_LE32      37          /* 32 bit offset relative to\n\t\t\t\t\t   static TLS block */\n#define R_68K_TLS_LE16      38          /* 16 bit offset relative to\n\t\t\t\t\t   static TLS block */\n#define R_68K_TLS_LE8       39          /* 8 bit offset relative to\n\t\t\t\t\t   static TLS block */\n#define R_68K_TLS_DTPMOD32  40          /* 32 bit module number */\n#define R_68K_TLS_DTPREL32  41          /* 32 bit module-relative offset */\n#define R_68K_TLS_TPREL32   42          /* 32 bit TP-relative offset */\n/* Keep this the last entry.  */\n#define R_68K_NUM\t43\n\n/* Intel 80386 specific definitions.  */\n\n/* i386 relocs.  */\n\n#define R_386_NONE\t   0\t\t/* No reloc */\n#define R_386_32\t   1\t\t/* Direct 32 bit  */\n#define R_386_PC32\t   2\t\t/* PC relative 32 bit */\n#define R_386_GOT32\t   3\t\t/* 32 bit GOT entry */\n#define R_386_PLT32\t   4\t\t/* 32 bit PLT address */\n#define R_386_COPY\t   5\t\t/* Copy symbol at runtime */\n#define R_386_GLOB_DAT\t   6\t\t/* Create GOT entry */\n#define R_386_JMP_SLOT\t   7\t\t/* Create PLT entry */\n#define R_386_RELATIVE\t   8\t\t/* Adjust by program base */\n#define R_386_GOTOFF\t   9\t\t/* 32 bit offset to GOT */\n#define R_386_GOTPC\t   10\t\t/* 32 bit PC relative offset to GOT */\n#define R_386_32PLT\t   11\n#define R_386_TLS_TPOFF\t   14\t\t/* Offset in static TLS block */\n#define R_386_TLS_IE\t   15\t\t/* Address of GOT entry for static TLS\n\t\t\t\t\t   block offset */\n#define R_386_TLS_GOTIE\t   16\t\t/* GOT entry for static TLS block\n\t\t\t\t\t   offset */\n#define R_386_TLS_LE\t   17\t\t/* Offset relative to static TLS\n\t\t\t\t\t   block */\n#define R_386_TLS_GD\t   18\t\t/* Direct 32 bit for GNU version of\n\t\t\t\t\t   general dynamic thread local data */\n#define R_386_TLS_LDM\t   19\t\t/* Direct 32 bit for GNU version of\n\t\t\t\t\t   local dynamic thread local data\n\t\t\t\t\t   in LE code */\n#define R_386_16\t   20\n#define R_386_PC16\t   21\n#define R_386_8\t\t   22\n#define R_386_PC8\t   23\n#define R_386_TLS_GD_32\t   24\t\t/* Direct 32 bit for general dynamic\n\t\t\t\t\t   thread local data */\n#define R_386_TLS_GD_PUSH  25\t\t/* Tag for pushl in GD TLS code */\n#define R_386_TLS_GD_CALL  26\t\t/* Relocation for call to\n\t\t\t\t\t   __tls_get_addr() */\n#define R_386_TLS_GD_POP   27\t\t/* Tag for popl in GD TLS code */\n#define R_386_TLS_LDM_32   28\t\t/* Direct 32 bit for local dynamic\n\t\t\t\t\t   thread local data in LE code */\n#define R_386_TLS_LDM_PUSH 29\t\t/* Tag for pushl in LDM TLS code */\n#define R_386_TLS_LDM_CALL 30\t\t/* Relocation for call to\n\t\t\t\t\t   __tls_get_addr() in LDM code */\n#define R_386_TLS_LDM_POP  31\t\t/* Tag for popl in LDM TLS code */\n#define R_386_TLS_LDO_32   32\t\t/* Offset relative to TLS block */\n#define R_386_TLS_IE_32\t   33\t\t/* GOT entry for negated static TLS\n\t\t\t\t\t   block offset */\n#define R_386_TLS_LE_32\t   34\t\t/* Negated offset relative to static\n\t\t\t\t\t   TLS block */\n#define R_386_TLS_DTPMOD32 35\t\t/* ID of module containing symbol */\n#define R_386_TLS_DTPOFF32 36\t\t/* Offset in TLS block */\n#define R_386_TLS_TPOFF32  37\t\t/* Negated offset in static TLS block */\n/* 38? */\n#define R_386_TLS_GOTDESC  39\t\t/* GOT offset for TLS descriptor.  */\n#define R_386_TLS_DESC_CALL 40\t\t/* Marker of call through TLS\n\t\t\t\t\t   descriptor for\n\t\t\t\t\t   relaxation.  */\n#define R_386_TLS_DESC     41\t\t/* TLS descriptor containing\n\t\t\t\t\t   pointer to code and to\n\t\t\t\t\t   argument, returning the TLS\n\t\t\t\t\t   offset for the symbol.  */\n#define R_386_IRELATIVE\t   42\t\t/* Adjust indirectly by program base */\n/* Keep this the last entry.  */\n#define R_386_NUM\t   43\n\n/* SUN SPARC specific definitions.  */\n\n/* Legal values for ST_TYPE subfield of st_info (symbol type).  */\n\n#define STT_SPARC_REGISTER\t13\t/* Global register reserved to app. */\n\n/* Values for Elf64_Ehdr.e_flags.  */\n\n#define EF_SPARCV9_MM\t\t3\n#define EF_SPARCV9_TSO\t\t0\n#define EF_SPARCV9_PSO\t\t1\n#define EF_SPARCV9_RMO\t\t2\n#define EF_SPARC_LEDATA\t\t0x800000 /* little endian data */\n#define EF_SPARC_EXT_MASK\t0xFFFF00\n#define EF_SPARC_32PLUS\t\t0x000100 /* generic V8+ features */\n#define EF_SPARC_SUN_US1\t0x000200 /* Sun UltraSPARC1 extensions */\n#define EF_SPARC_HAL_R1\t\t0x000400 /* HAL R1 extensions */\n#define EF_SPARC_SUN_US3\t0x000800 /* Sun UltraSPARCIII extensions */\n\n/* SPARC relocs.  */\n\n#define R_SPARC_NONE\t\t0\t/* No reloc */\n#define R_SPARC_8\t\t1\t/* Direct 8 bit */\n#define R_SPARC_16\t\t2\t/* Direct 16 bit */\n#define R_SPARC_32\t\t3\t/* Direct 32 bit */\n#define R_SPARC_DISP8\t\t4\t/* PC relative 8 bit */\n#define R_SPARC_DISP16\t\t5\t/* PC relative 16 bit */\n#define R_SPARC_DISP32\t\t6\t/* PC relative 32 bit */\n#define R_SPARC_WDISP30\t\t7\t/* PC relative 30 bit shifted */\n#define R_SPARC_WDISP22\t\t8\t/* PC relative 22 bit shifted */\n#define R_SPARC_HI22\t\t9\t/* High 22 bit */\n#define R_SPARC_22\t\t10\t/* Direct 22 bit */\n#define R_SPARC_13\t\t11\t/* Direct 13 bit */\n#define R_SPARC_LO10\t\t12\t/* Truncated 10 bit */\n#define R_SPARC_GOT10\t\t13\t/* Truncated 10 bit GOT entry */\n#define R_SPARC_GOT13\t\t14\t/* 13 bit GOT entry */\n#define R_SPARC_GOT22\t\t15\t/* 22 bit GOT entry shifted */\n#define R_SPARC_PC10\t\t16\t/* PC relative 10 bit truncated */\n#define R_SPARC_PC22\t\t17\t/* PC relative 22 bit shifted */\n#define R_SPARC_WPLT30\t\t18\t/* 30 bit PC relative PLT address */\n#define R_SPARC_COPY\t\t19\t/* Copy symbol at runtime */\n#define R_SPARC_GLOB_DAT\t20\t/* Create GOT entry */\n#define R_SPARC_JMP_SLOT\t21\t/* Create PLT entry */\n#define R_SPARC_RELATIVE\t22\t/* Adjust by program base */\n#define R_SPARC_UA32\t\t23\t/* Direct 32 bit unaligned */\n\n/* Additional Sparc64 relocs.  */\n\n#define R_SPARC_PLT32\t\t24\t/* Direct 32 bit ref to PLT entry */\n#define R_SPARC_HIPLT22\t\t25\t/* High 22 bit PLT entry */\n#define R_SPARC_LOPLT10\t\t26\t/* Truncated 10 bit PLT entry */\n#define R_SPARC_PCPLT32\t\t27\t/* PC rel 32 bit ref to PLT entry */\n#define R_SPARC_PCPLT22\t\t28\t/* PC rel high 22 bit PLT entry */\n#define R_SPARC_PCPLT10\t\t29\t/* PC rel trunc 10 bit PLT entry */\n#define R_SPARC_10\t\t30\t/* Direct 10 bit */\n#define R_SPARC_11\t\t31\t/* Direct 11 bit */\n#define R_SPARC_64\t\t32\t/* Direct 64 bit */\n#define R_SPARC_OLO10\t\t33\t/* 10bit with secondary 13bit addend */\n#define R_SPARC_HH22\t\t34\t/* Top 22 bits of direct 64 bit */\n#define R_SPARC_HM10\t\t35\t/* High middle 10 bits of ... */\n#define R_SPARC_LM22\t\t36\t/* Low middle 22 bits of ... */\n#define R_SPARC_PC_HH22\t\t37\t/* Top 22 bits of pc rel 64 bit */\n#define R_SPARC_PC_HM10\t\t38\t/* High middle 10 bit of ... */\n#define R_SPARC_PC_LM22\t\t39\t/* Low miggle 22 bits of ... */\n#define R_SPARC_WDISP16\t\t40\t/* PC relative 16 bit shifted */\n#define R_SPARC_WDISP19\t\t41\t/* PC relative 19 bit shifted */\n#define R_SPARC_GLOB_JMP\t42\t/* was part of v9 ABI but was removed */\n#define R_SPARC_7\t\t43\t/* Direct 7 bit */\n#define R_SPARC_5\t\t44\t/* Direct 5 bit */\n#define R_SPARC_6\t\t45\t/* Direct 6 bit */\n#define R_SPARC_DISP64\t\t46\t/* PC relative 64 bit */\n#define R_SPARC_PLT64\t\t47\t/* Direct 64 bit ref to PLT entry */\n#define R_SPARC_HIX22\t\t48\t/* High 22 bit complemented */\n#define R_SPARC_LOX10\t\t49\t/* Truncated 11 bit complemented */\n#define R_SPARC_H44\t\t50\t/* Direct high 12 of 44 bit */\n#define R_SPARC_M44\t\t51\t/* Direct mid 22 of 44 bit */\n#define R_SPARC_L44\t\t52\t/* Direct low 10 of 44 bit */\n#define R_SPARC_REGISTER\t53\t/* Global register usage */\n#define R_SPARC_UA64\t\t54\t/* Direct 64 bit unaligned */\n#define R_SPARC_UA16\t\t55\t/* Direct 16 bit unaligned */\n#define R_SPARC_TLS_GD_HI22\t56\n#define R_SPARC_TLS_GD_LO10\t57\n#define R_SPARC_TLS_GD_ADD\t58\n#define R_SPARC_TLS_GD_CALL\t59\n#define R_SPARC_TLS_LDM_HI22\t60\n#define R_SPARC_TLS_LDM_LO10\t61\n#define R_SPARC_TLS_LDM_ADD\t62\n#define R_SPARC_TLS_LDM_CALL\t63\n#define R_SPARC_TLS_LDO_HIX22\t64\n#define R_SPARC_TLS_LDO_LOX10\t65\n#define R_SPARC_TLS_LDO_ADD\t66\n#define R_SPARC_TLS_IE_HI22\t67\n#define R_SPARC_TLS_IE_LO10\t68\n#define R_SPARC_TLS_IE_LD\t69\n#define R_SPARC_TLS_IE_LDX\t70\n#define R_SPARC_TLS_IE_ADD\t71\n#define R_SPARC_TLS_LE_HIX22\t72\n#define R_SPARC_TLS_LE_LOX10\t73\n#define R_SPARC_TLS_DTPMOD32\t74\n#define R_SPARC_TLS_DTPMOD64\t75\n#define R_SPARC_TLS_DTPOFF32\t76\n#define R_SPARC_TLS_DTPOFF64\t77\n#define R_SPARC_TLS_TPOFF32\t78\n#define R_SPARC_TLS_TPOFF64\t79\n#define R_SPARC_GOTDATA_HIX22\t80\n#define R_SPARC_GOTDATA_LOX10\t81\n#define R_SPARC_GOTDATA_OP_HIX22\t82\n#define R_SPARC_GOTDATA_OP_LOX10\t83\n#define R_SPARC_GOTDATA_OP\t84\n#define R_SPARC_H34\t\t85\n#define R_SPARC_SIZE32\t\t86\n#define R_SPARC_SIZE64\t\t87\n#define R_SPARC_JMP_IREL\t248\n#define R_SPARC_IRELATIVE\t249\n#define R_SPARC_GNU_VTINHERIT\t250\n#define R_SPARC_GNU_VTENTRY\t251\n#define R_SPARC_REV32\t\t252\n/* Keep this the last entry.  */\n#define R_SPARC_NUM\t\t253\n\n/* For Sparc64, legal values for d_tag of Elf64_Dyn.  */\n\n#define DT_SPARC_REGISTER 0x70000001\n#define DT_SPARC_NUM\t2\n\n/* MIPS R3000 specific definitions.  */\n\n/* Legal values for e_flags field of Elf32_Ehdr.  */\n\n#define EF_MIPS_NOREORDER   1\t\t/* A .noreorder directive was used */\n#define EF_MIPS_PIC\t    2\t\t/* Contains PIC code */\n#define EF_MIPS_CPIC\t    4\t\t/* Uses PIC calling sequence */\n#define EF_MIPS_XGOT\t    8\n#define EF_MIPS_64BIT_WHIRL 16\n#define EF_MIPS_ABI2\t    32\n#define EF_MIPS_ABI_ON32    64\n#define EF_MIPS_ARCH\t    0xf0000000\t/* MIPS architecture level */\n\n/* Legal values for MIPS architecture level.  */\n\n#define EF_MIPS_ARCH_1\t    0x00000000\t/* -mips1 code.  */\n#define EF_MIPS_ARCH_2\t    0x10000000\t/* -mips2 code.  */\n#define EF_MIPS_ARCH_3\t    0x20000000\t/* -mips3 code.  */\n#define EF_MIPS_ARCH_4\t    0x30000000\t/* -mips4 code.  */\n#define EF_MIPS_ARCH_5\t    0x40000000\t/* -mips5 code.  */\n#define EF_MIPS_ARCH_32\t    0x60000000\t/* MIPS32 code.  */\n#define EF_MIPS_ARCH_64\t    0x70000000\t/* MIPS64 code.  */\n\n/* The following are non-official names and should not be used.  */\n\n#define E_MIPS_ARCH_1\t  0x00000000\t/* -mips1 code.  */\n#define E_MIPS_ARCH_2\t  0x10000000\t/* -mips2 code.  */\n#define E_MIPS_ARCH_3\t  0x20000000\t/* -mips3 code.  */\n#define E_MIPS_ARCH_4\t  0x30000000\t/* -mips4 code.  */\n#define E_MIPS_ARCH_5\t  0x40000000\t/* -mips5 code.  */\n#define E_MIPS_ARCH_32\t  0x60000000\t/* MIPS32 code.  */\n#define E_MIPS_ARCH_64\t  0x70000000\t/* MIPS64 code.  */\n\n/* Special section indices.  */\n\n#define SHN_MIPS_ACOMMON    0xff00\t/* Allocated common symbols */\n#define SHN_MIPS_TEXT\t    0xff01\t/* Allocated test symbols.  */\n#define SHN_MIPS_DATA\t    0xff02\t/* Allocated data symbols.  */\n#define SHN_MIPS_SCOMMON    0xff03\t/* Small common symbols */\n#define SHN_MIPS_SUNDEFINED 0xff04\t/* Small undefined symbols */\n\n/* Legal values for sh_type field of Elf32_Shdr.  */\n\n#define SHT_MIPS_LIBLIST       0x70000000 /* Shared objects used in link */\n#define SHT_MIPS_MSYM\t       0x70000001\n#define SHT_MIPS_CONFLICT      0x70000002 /* Conflicting symbols */\n#define SHT_MIPS_GPTAB\t       0x70000003 /* Global data area sizes */\n#define SHT_MIPS_UCODE\t       0x70000004 /* Reserved for SGI/MIPS compilers */\n#define SHT_MIPS_DEBUG\t       0x70000005 /* MIPS ECOFF debugging information*/\n#define SHT_MIPS_REGINFO       0x70000006 /* Register usage information */\n#define SHT_MIPS_PACKAGE       0x70000007\n#define SHT_MIPS_PACKSYM       0x70000008\n#define SHT_MIPS_RELD\t       0x70000009\n#define SHT_MIPS_IFACE         0x7000000b\n#define SHT_MIPS_CONTENT       0x7000000c\n#define SHT_MIPS_OPTIONS       0x7000000d /* Miscellaneous options.  */\n#define SHT_MIPS_SHDR\t       0x70000010\n#define SHT_MIPS_FDESC\t       0x70000011\n#define SHT_MIPS_EXTSYM\t       0x70000012\n#define SHT_MIPS_DENSE\t       0x70000013\n#define SHT_MIPS_PDESC\t       0x70000014\n#define SHT_MIPS_LOCSYM\t       0x70000015\n#define SHT_MIPS_AUXSYM\t       0x70000016\n#define SHT_MIPS_OPTSYM\t       0x70000017\n#define SHT_MIPS_LOCSTR\t       0x70000018\n#define SHT_MIPS_LINE\t       0x70000019\n#define SHT_MIPS_RFDESC\t       0x7000001a\n#define SHT_MIPS_DELTASYM      0x7000001b\n#define SHT_MIPS_DELTAINST     0x7000001c\n#define SHT_MIPS_DELTACLASS    0x7000001d\n#define SHT_MIPS_DWARF         0x7000001e /* DWARF debugging information.  */\n#define SHT_MIPS_DELTADECL     0x7000001f\n#define SHT_MIPS_SYMBOL_LIB    0x70000020\n#define SHT_MIPS_EVENTS\t       0x70000021 /* Event section.  */\n#define SHT_MIPS_TRANSLATE     0x70000022\n#define SHT_MIPS_PIXIE\t       0x70000023\n#define SHT_MIPS_XLATE\t       0x70000024\n#define SHT_MIPS_XLATE_DEBUG   0x70000025\n#define SHT_MIPS_WHIRL\t       0x70000026\n#define SHT_MIPS_EH_REGION     0x70000027\n#define SHT_MIPS_XLATE_OLD     0x70000028\n#define SHT_MIPS_PDR_EXCEPTION 0x70000029\n\n/* Legal values for sh_flags field of Elf32_Shdr.  */\n\n#define SHF_MIPS_GPREL\t 0x10000000\t/* Must be part of global data area */\n#define SHF_MIPS_MERGE\t 0x20000000\n#define SHF_MIPS_ADDR\t 0x40000000\n#define SHF_MIPS_STRINGS 0x80000000\n#define SHF_MIPS_NOSTRIP 0x08000000\n#define SHF_MIPS_LOCAL\t 0x04000000\n#define SHF_MIPS_NAMES\t 0x02000000\n#define SHF_MIPS_NODUPE\t 0x01000000\n\n\n/* Symbol tables.  */\n\n/* MIPS specific values for `st_other'.  */\n#define STO_MIPS_DEFAULT\t\t0x0\n#define STO_MIPS_INTERNAL\t\t0x1\n#define STO_MIPS_HIDDEN\t\t\t0x2\n#define STO_MIPS_PROTECTED\t\t0x3\n#define STO_MIPS_PLT\t\t\t0x8\n#define STO_MIPS_SC_ALIGN_UNUSED\t0xff\n\n/* MIPS specific values for `st_info'.  */\n#define STB_MIPS_SPLIT_COMMON\t\t13\n\n/* Entries found in sections of type SHT_MIPS_GPTAB.  */\n\ntypedef union\n{\n  struct\n    {\n      Elf32_Word gt_current_g_value;\t/* -G value used for compilation */\n      Elf32_Word gt_unused;\t\t/* Not used */\n    } gt_header;\t\t\t/* First entry in section */\n  struct\n    {\n      Elf32_Word gt_g_value;\t\t/* If this value were used for -G */\n      Elf32_Word gt_bytes;\t\t/* This many bytes would be used */\n    } gt_entry;\t\t\t\t/* Subsequent entries in section */\n} Elf32_gptab;\n\n/* Entry found in sections of type SHT_MIPS_REGINFO.  */\n\ntypedef struct\n{\n  Elf32_Word\tri_gprmask;\t\t/* General registers used */\n  Elf32_Word\tri_cprmask[4];\t\t/* Coprocessor registers used */\n  Elf32_Sword\tri_gp_value;\t\t/* $gp register value */\n} Elf32_RegInfo;\n\n/* Entries found in sections of type SHT_MIPS_OPTIONS.  */\n\ntypedef struct\n{\n  unsigned char kind;\t\t/* Determines interpretation of the\n\t\t\t\t   variable part of descriptor.  */\n  unsigned char size;\t\t/* Size of descriptor, including header.  */\n  Elf32_Section section;\t/* Section header index of section affected,\n\t\t\t\t   0 for global options.  */\n  Elf32_Word info;\t\t/* Kind-specific information.  */\n} Elf_Options;\n\n/* Values for `kind' field in Elf_Options.  */\n\n#define ODK_NULL\t0\t/* Undefined.  */\n#define ODK_REGINFO\t1\t/* Register usage information.  */\n#define ODK_EXCEPTIONS\t2\t/* Exception processing options.  */\n#define ODK_PAD\t\t3\t/* Section padding options.  */\n#define ODK_HWPATCH\t4\t/* Hardware workarounds performed */\n#define ODK_FILL\t5\t/* record the fill value used by the linker. */\n#define ODK_TAGS\t6\t/* reserve space for desktop tools to write. */\n#define ODK_HWAND\t7\t/* HW workarounds.  'AND' bits when merging. */\n#define ODK_HWOR\t8\t/* HW workarounds.  'OR' bits when merging.  */\n\n/* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries.  */\n\n#define OEX_FPU_MIN\t0x1f\t/* FPE's which MUST be enabled.  */\n#define OEX_FPU_MAX\t0x1f00\t/* FPE's which MAY be enabled.  */\n#define OEX_PAGE0\t0x10000\t/* page zero must be mapped.  */\n#define OEX_SMM\t\t0x20000\t/* Force sequential memory mode?  */\n#define OEX_FPDBUG\t0x40000\t/* Force floating point debug mode?  */\n#define OEX_PRECISEFP\tOEX_FPDBUG\n#define OEX_DISMISS\t0x80000\t/* Dismiss invalid address faults?  */\n\n#define OEX_FPU_INVAL\t0x10\n#define OEX_FPU_DIV0\t0x08\n#define OEX_FPU_OFLO\t0x04\n#define OEX_FPU_UFLO\t0x02\n#define OEX_FPU_INEX\t0x01\n\n/* Masks for `info' in Elf_Options for an ODK_HWPATCH entry.  */\n\n#define OHW_R4KEOP\t0x1\t/* R4000 end-of-page patch.  */\n#define OHW_R8KPFETCH\t0x2\t/* may need R8000 prefetch patch.  */\n#define OHW_R5KEOP\t0x4\t/* R5000 end-of-page patch.  */\n#define OHW_R5KCVTL\t0x8\t/* R5000 cvt.[ds].l bug.  clean=1.  */\n\n#define OPAD_PREFIX\t0x1\n#define OPAD_POSTFIX\t0x2\n#define OPAD_SYMBOL\t0x4\n\n/* Entry found in `.options' section.  */\n\ntypedef struct\n{\n  Elf32_Word hwp_flags1;\t/* Extra flags.  */\n  Elf32_Word hwp_flags2;\t/* Extra flags.  */\n} Elf_Options_Hw;\n\n/* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries.  */\n\n#define OHWA0_R4KEOP_CHECKED\t0x00000001\n#define OHWA1_R4KEOP_CLEAN\t0x00000002\n\n/* MIPS relocs.  */\n\n#define R_MIPS_NONE\t\t0\t/* No reloc */\n#define R_MIPS_16\t\t1\t/* Direct 16 bit */\n#define R_MIPS_32\t\t2\t/* Direct 32 bit */\n#define R_MIPS_REL32\t\t3\t/* PC relative 32 bit */\n#define R_MIPS_26\t\t4\t/* Direct 26 bit shifted */\n#define R_MIPS_HI16\t\t5\t/* High 16 bit */\n#define R_MIPS_LO16\t\t6\t/* Low 16 bit */\n#define R_MIPS_GPREL16\t\t7\t/* GP relative 16 bit */\n#define R_MIPS_LITERAL\t\t8\t/* 16 bit literal entry */\n#define R_MIPS_GOT16\t\t9\t/* 16 bit GOT entry */\n#define R_MIPS_PC16\t\t10\t/* PC relative 16 bit */\n#define R_MIPS_CALL16\t\t11\t/* 16 bit GOT entry for function */\n#define R_MIPS_GPREL32\t\t12\t/* GP relative 32 bit */\n\n#define R_MIPS_SHIFT5\t\t16\n#define R_MIPS_SHIFT6\t\t17\n#define R_MIPS_64\t\t18\n#define R_MIPS_GOT_DISP\t\t19\n#define R_MIPS_GOT_PAGE\t\t20\n#define R_MIPS_GOT_OFST\t\t21\n#define R_MIPS_GOT_HI16\t\t22\n#define R_MIPS_GOT_LO16\t\t23\n#define R_MIPS_SUB\t\t24\n#define R_MIPS_INSERT_A\t\t25\n#define R_MIPS_INSERT_B\t\t26\n#define R_MIPS_DELETE\t\t27\n#define R_MIPS_HIGHER\t\t28\n#define R_MIPS_HIGHEST\t\t29\n#define R_MIPS_CALL_HI16\t30\n#define R_MIPS_CALL_LO16\t31\n#define R_MIPS_SCN_DISP\t\t32\n#define R_MIPS_REL16\t\t33\n#define R_MIPS_ADD_IMMEDIATE\t34\n#define R_MIPS_PJUMP\t\t35\n#define R_MIPS_RELGOT\t\t36\n#define R_MIPS_JALR\t\t37\n#define R_MIPS_TLS_DTPMOD32\t38\t/* Module number 32 bit */\n#define R_MIPS_TLS_DTPREL32\t39\t/* Module-relative offset 32 bit */\n#define R_MIPS_TLS_DTPMOD64\t40\t/* Module number 64 bit */\n#define R_MIPS_TLS_DTPREL64\t41\t/* Module-relative offset 64 bit */\n#define R_MIPS_TLS_GD\t\t42\t/* 16 bit GOT offset for GD */\n#define R_MIPS_TLS_LDM\t\t43\t/* 16 bit GOT offset for LDM */\n#define R_MIPS_TLS_DTPREL_HI16\t44\t/* Module-relative offset, high 16 bits */\n#define R_MIPS_TLS_DTPREL_LO16\t45\t/* Module-relative offset, low 16 bits */\n#define R_MIPS_TLS_GOTTPREL\t46\t/* 16 bit GOT offset for IE */\n#define R_MIPS_TLS_TPREL32\t47\t/* TP-relative offset, 32 bit */\n#define R_MIPS_TLS_TPREL64\t48\t/* TP-relative offset, 64 bit */\n#define R_MIPS_TLS_TPREL_HI16\t49\t/* TP-relative offset, high 16 bits */\n#define R_MIPS_TLS_TPREL_LO16\t50\t/* TP-relative offset, low 16 bits */\n#define R_MIPS_GLOB_DAT\t\t51\n#define R_MIPS_COPY\t\t126\n#define R_MIPS_JUMP_SLOT        127\n/* Keep this the last entry.  */\n#define R_MIPS_NUM\t\t128\n\n/* Legal values for p_type field of Elf32_Phdr.  */\n\n#define PT_MIPS_REGINFO\t0x70000000\t/* Register usage information */\n#define PT_MIPS_RTPROC  0x70000001\t/* Runtime procedure table. */\n#define PT_MIPS_OPTIONS 0x70000002\n\n/* Special program header types.  */\n\n#define PF_MIPS_LOCAL\t0x10000000\n\n/* Legal values for d_tag field of Elf32_Dyn.  */\n\n#define DT_MIPS_RLD_VERSION  0x70000001\t/* Runtime linker interface version */\n#define DT_MIPS_TIME_STAMP   0x70000002\t/* Timestamp */\n#define DT_MIPS_ICHECKSUM    0x70000003\t/* Checksum */\n#define DT_MIPS_IVERSION     0x70000004\t/* Version string (string tbl index) */\n#define DT_MIPS_FLAGS\t     0x70000005\t/* Flags */\n#define DT_MIPS_BASE_ADDRESS 0x70000006\t/* Base address */\n#define DT_MIPS_MSYM\t     0x70000007\n#define DT_MIPS_CONFLICT     0x70000008\t/* Address of CONFLICT section */\n#define DT_MIPS_LIBLIST\t     0x70000009\t/* Address of LIBLIST section */\n#define DT_MIPS_LOCAL_GOTNO  0x7000000a\t/* Number of local GOT entries */\n#define DT_MIPS_CONFLICTNO   0x7000000b\t/* Number of CONFLICT entries */\n#define DT_MIPS_LIBLISTNO    0x70000010\t/* Number of LIBLIST entries */\n#define DT_MIPS_SYMTABNO     0x70000011\t/* Number of DYNSYM entries */\n#define DT_MIPS_UNREFEXTNO   0x70000012\t/* First external DYNSYM */\n#define DT_MIPS_GOTSYM\t     0x70000013\t/* First GOT entry in DYNSYM */\n#define DT_MIPS_HIPAGENO     0x70000014\t/* Number of GOT page table entries */\n#define DT_MIPS_RLD_MAP\t     0x70000016\t/* Address of run time loader map.  */\n#define DT_MIPS_DELTA_CLASS  0x70000017\t/* Delta C++ class definition.  */\n#define DT_MIPS_DELTA_CLASS_NO    0x70000018 /* Number of entries in\n\t\t\t\t\t\tDT_MIPS_DELTA_CLASS.  */\n#define DT_MIPS_DELTA_INSTANCE    0x70000019 /* Delta C++ class instances.  */\n#define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in\n\t\t\t\t\t\tDT_MIPS_DELTA_INSTANCE.  */\n#define DT_MIPS_DELTA_RELOC  0x7000001b /* Delta relocations.  */\n#define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in\n\t\t\t\t\t     DT_MIPS_DELTA_RELOC.  */\n#define DT_MIPS_DELTA_SYM    0x7000001d /* Delta symbols that Delta\n\t\t\t\t\t   relocations refer to.  */\n#define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in\n\t\t\t\t\t   DT_MIPS_DELTA_SYM.  */\n#define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the\n\t\t\t\t\t     class declaration.  */\n#define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in\n\t\t\t\t\t\tDT_MIPS_DELTA_CLASSSYM.  */\n#define DT_MIPS_CXX_FLAGS    0x70000022 /* Flags indicating for C++ flavor.  */\n#define DT_MIPS_PIXIE_INIT   0x70000023\n#define DT_MIPS_SYMBOL_LIB   0x70000024\n#define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025\n#define DT_MIPS_LOCAL_GOTIDX 0x70000026\n#define DT_MIPS_HIDDEN_GOTIDX 0x70000027\n#define DT_MIPS_PROTECTED_GOTIDX 0x70000028\n#define DT_MIPS_OPTIONS\t     0x70000029 /* Address of .options.  */\n#define DT_MIPS_INTERFACE    0x7000002a /* Address of .interface.  */\n#define DT_MIPS_DYNSTR_ALIGN 0x7000002b\n#define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */\n#define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve\n\t\t\t\t\t\t    function stored in GOT.  */\n#define DT_MIPS_PERF_SUFFIX  0x7000002e /* Default suffix of dso to be added\n\t\t\t\t\t   by rld on dlopen() calls.  */\n#define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */\n#define DT_MIPS_GP_VALUE     0x70000030 /* GP value for aux GOTs.  */\n#define DT_MIPS_AUX_DYNAMIC  0x70000031 /* Address of aux .dynamic.  */\n/* The address of .got.plt in an executable using the new non-PIC ABI.  */\n#define DT_MIPS_PLTGOT\t     0x70000032\n/* The base of the PLT in an executable using the new non-PIC ABI if that\n   PLT is writable.  For a non-writable PLT, this is omitted or has a zero\n   value.  */\n#define DT_MIPS_RWPLT        0x70000034\n#define DT_MIPS_NUM\t     0x35\n\n/* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry.  */\n\n#define RHF_NONE\t\t   0\t\t/* No flags */\n#define RHF_QUICKSTART\t\t   (1 << 0)\t/* Use quickstart */\n#define RHF_NOTPOT\t\t   (1 << 1)\t/* Hash size not power of 2 */\n#define RHF_NO_LIBRARY_REPLACEMENT (1 << 2)\t/* Ignore LD_LIBRARY_PATH */\n#define RHF_NO_MOVE\t\t   (1 << 3)\n#define RHF_SGI_ONLY\t\t   (1 << 4)\n#define RHF_GUARANTEE_INIT\t   (1 << 5)\n#define RHF_DELTA_C_PLUS_PLUS\t   (1 << 6)\n#define RHF_GUARANTEE_START_INIT   (1 << 7)\n#define RHF_PIXIE\t\t   (1 << 8)\n#define RHF_DEFAULT_DELAY_LOAD\t   (1 << 9)\n#define RHF_REQUICKSTART\t   (1 << 10)\n#define RHF_REQUICKSTARTED\t   (1 << 11)\n#define RHF_CORD\t\t   (1 << 12)\n#define RHF_NO_UNRES_UNDEF\t   (1 << 13)\n#define RHF_RLD_ORDER_SAFE\t   (1 << 14)\n\n/* Entries found in sections of type SHT_MIPS_LIBLIST.  */\n\ntypedef struct\n{\n  Elf32_Word l_name;\t\t/* Name (string table index) */\n  Elf32_Word l_time_stamp;\t/* Timestamp */\n  Elf32_Word l_checksum;\t/* Checksum */\n  Elf32_Word l_version;\t\t/* Interface version */\n  Elf32_Word l_flags;\t\t/* Flags */\n} Elf32_Lib;\n\ntypedef struct\n{\n  Elf64_Word l_name;\t\t/* Name (string table index) */\n  Elf64_Word l_time_stamp;\t/* Timestamp */\n  Elf64_Word l_checksum;\t/* Checksum */\n  Elf64_Word l_version;\t\t/* Interface version */\n  Elf64_Word l_flags;\t\t/* Flags */\n} Elf64_Lib;\n\n\n/* Legal values for l_flags.  */\n\n#define LL_NONE\t\t  0\n#define LL_EXACT_MATCH\t  (1 << 0)\t/* Require exact match */\n#define LL_IGNORE_INT_VER (1 << 1)\t/* Ignore interface version */\n#define LL_REQUIRE_MINOR  (1 << 2)\n#define LL_EXPORTS\t  (1 << 3)\n#define LL_DELAY_LOAD\t  (1 << 4)\n#define LL_DELTA\t  (1 << 5)\n\n/* Entries found in sections of type SHT_MIPS_CONFLICT.  */\n\ntypedef Elf32_Addr Elf32_Conflict;\n\n\n/* HPPA specific definitions.  */\n\n/* Legal values for e_flags field of Elf32_Ehdr.  */\n\n#define EF_PARISC_TRAPNIL\t0x00010000 /* Trap nil pointer dereference.  */\n#define EF_PARISC_EXT\t\t0x00020000 /* Program uses arch. extensions. */\n#define EF_PARISC_LSB\t\t0x00040000 /* Program expects little endian. */\n#define EF_PARISC_WIDE\t\t0x00080000 /* Program expects wide mode.  */\n#define EF_PARISC_NO_KABP\t0x00100000 /* No kernel assisted branch\n\t\t\t\t\t      prediction.  */\n#define EF_PARISC_LAZYSWAP\t0x00400000 /* Allow lazy swapping.  */\n#define EF_PARISC_ARCH\t\t0x0000ffff /* Architecture version.  */\n\n/* Defined values for `e_flags & EF_PARISC_ARCH' are:  */\n\n#define EFA_PARISC_1_0\t\t    0x020b /* PA-RISC 1.0 big-endian.  */\n#define EFA_PARISC_1_1\t\t    0x0210 /* PA-RISC 1.1 big-endian.  */\n#define EFA_PARISC_2_0\t\t    0x0214 /* PA-RISC 2.0 big-endian.  */\n\n/* Additional section indeces.  */\n\n#define SHN_PARISC_ANSI_COMMON\t0xff00\t   /* Section for tenatively declared\n\t\t\t\t\t      symbols in ANSI C.  */\n#define SHN_PARISC_HUGE_COMMON\t0xff01\t   /* Common blocks in huge model.  */\n\n/* Legal values for sh_type field of Elf32_Shdr.  */\n\n#define SHT_PARISC_EXT\t\t0x70000000 /* Contains product specific ext. */\n#define SHT_PARISC_UNWIND\t0x70000001 /* Unwind information.  */\n#define SHT_PARISC_DOC\t\t0x70000002 /* Debug info for optimized code. */\n\n/* Legal values for sh_flags field of Elf32_Shdr.  */\n\n#define SHF_PARISC_SHORT\t0x20000000 /* Section with short addressing. */\n#define SHF_PARISC_HUGE\t\t0x40000000 /* Section far from gp.  */\n#define SHF_PARISC_SBP\t\t0x80000000 /* Static branch prediction code. */\n\n/* Legal values for ST_TYPE subfield of st_info (symbol type).  */\n\n#define STT_PARISC_MILLICODE\t13\t/* Millicode function entry point.  */\n\n#define STT_HP_OPAQUE\t\t(STT_LOOS + 0x1)\n#define STT_HP_STUB\t\t(STT_LOOS + 0x2)\n\n/* HPPA relocs.  */\n\n#define R_PARISC_NONE\t\t0\t/* No reloc.  */\n#define R_PARISC_DIR32\t\t1\t/* Direct 32-bit reference.  */\n#define R_PARISC_DIR21L\t\t2\t/* Left 21 bits of eff. address.  */\n#define R_PARISC_DIR17R\t\t3\t/* Right 17 bits of eff. address.  */\n#define R_PARISC_DIR17F\t\t4\t/* 17 bits of eff. address.  */\n#define R_PARISC_DIR14R\t\t6\t/* Right 14 bits of eff. address.  */\n#define R_PARISC_PCREL32\t9\t/* 32-bit rel. address.  */\n#define R_PARISC_PCREL21L\t10\t/* Left 21 bits of rel. address.  */\n#define R_PARISC_PCREL17R\t11\t/* Right 17 bits of rel. address.  */\n#define R_PARISC_PCREL17F\t12\t/* 17 bits of rel. address.  */\n#define R_PARISC_PCREL14R\t14\t/* Right 14 bits of rel. address.  */\n#define R_PARISC_DPREL21L\t18\t/* Left 21 bits of rel. address.  */\n#define R_PARISC_DPREL14R\t22\t/* Right 14 bits of rel. address.  */\n#define R_PARISC_GPREL21L\t26\t/* GP-relative, left 21 bits.  */\n#define R_PARISC_GPREL14R\t30\t/* GP-relative, right 14 bits.  */\n#define R_PARISC_LTOFF21L\t34\t/* LT-relative, left 21 bits.  */\n#define R_PARISC_LTOFF14R\t38\t/* LT-relative, right 14 bits.  */\n#define R_PARISC_SECREL32\t41\t/* 32 bits section rel. address.  */\n#define R_PARISC_SEGBASE\t48\t/* No relocation, set segment base.  */\n#define R_PARISC_SEGREL32\t49\t/* 32 bits segment rel. address.  */\n#define R_PARISC_PLTOFF21L\t50\t/* PLT rel. address, left 21 bits.  */\n#define R_PARISC_PLTOFF14R\t54\t/* PLT rel. address, right 14 bits.  */\n#define R_PARISC_LTOFF_FPTR32\t57\t/* 32 bits LT-rel. function pointer. */\n#define R_PARISC_LTOFF_FPTR21L\t58\t/* LT-rel. fct ptr, left 21 bits. */\n#define R_PARISC_LTOFF_FPTR14R\t62\t/* LT-rel. fct ptr, right 14 bits. */\n#define R_PARISC_FPTR64\t\t64\t/* 64 bits function address.  */\n#define R_PARISC_PLABEL32\t65\t/* 32 bits function address.  */\n#define R_PARISC_PLABEL21L\t66\t/* Left 21 bits of fdesc address.  */\n#define R_PARISC_PLABEL14R\t70\t/* Right 14 bits of fdesc address.  */\n#define R_PARISC_PCREL64\t72\t/* 64 bits PC-rel. address.  */\n#define R_PARISC_PCREL22F\t74\t/* 22 bits PC-rel. address.  */\n#define R_PARISC_PCREL14WR\t75\t/* PC-rel. address, right 14 bits.  */\n#define R_PARISC_PCREL14DR\t76\t/* PC rel. address, right 14 bits.  */\n#define R_PARISC_PCREL16F\t77\t/* 16 bits PC-rel. address.  */\n#define R_PARISC_PCREL16WF\t78\t/* 16 bits PC-rel. address.  */\n#define R_PARISC_PCREL16DF\t79\t/* 16 bits PC-rel. address.  */\n#define R_PARISC_DIR64\t\t80\t/* 64 bits of eff. address.  */\n#define R_PARISC_DIR14WR\t83\t/* 14 bits of eff. address.  */\n#define R_PARISC_DIR14DR\t84\t/* 14 bits of eff. address.  */\n#define R_PARISC_DIR16F\t\t85\t/* 16 bits of eff. address.  */\n#define R_PARISC_DIR16WF\t86\t/* 16 bits of eff. address.  */\n#define R_PARISC_DIR16DF\t87\t/* 16 bits of eff. address.  */\n#define R_PARISC_GPREL64\t88\t/* 64 bits of GP-rel. address.  */\n#define R_PARISC_GPREL14WR\t91\t/* GP-rel. address, right 14 bits.  */\n#define R_PARISC_GPREL14DR\t92\t/* GP-rel. address, right 14 bits.  */\n#define R_PARISC_GPREL16F\t93\t/* 16 bits GP-rel. address.  */\n#define R_PARISC_GPREL16WF\t94\t/* 16 bits GP-rel. address.  */\n#define R_PARISC_GPREL16DF\t95\t/* 16 bits GP-rel. address.  */\n#define R_PARISC_LTOFF64\t96\t/* 64 bits LT-rel. address.  */\n#define R_PARISC_LTOFF14WR\t99\t/* LT-rel. address, right 14 bits.  */\n#define R_PARISC_LTOFF14DR\t100\t/* LT-rel. address, right 14 bits.  */\n#define R_PARISC_LTOFF16F\t101\t/* 16 bits LT-rel. address.  */\n#define R_PARISC_LTOFF16WF\t102\t/* 16 bits LT-rel. address.  */\n#define R_PARISC_LTOFF16DF\t103\t/* 16 bits LT-rel. address.  */\n#define R_PARISC_SECREL64\t104\t/* 64 bits section rel. address.  */\n#define R_PARISC_SEGREL64\t112\t/* 64 bits segment rel. address.  */\n#define R_PARISC_PLTOFF14WR\t115\t/* PLT-rel. address, right 14 bits.  */\n#define R_PARISC_PLTOFF14DR\t116\t/* PLT-rel. address, right 14 bits.  */\n#define R_PARISC_PLTOFF16F\t117\t/* 16 bits LT-rel. address.  */\n#define R_PARISC_PLTOFF16WF\t118\t/* 16 bits PLT-rel. address.  */\n#define R_PARISC_PLTOFF16DF\t119\t/* 16 bits PLT-rel. address.  */\n#define R_PARISC_LTOFF_FPTR64\t120\t/* 64 bits LT-rel. function ptr.  */\n#define R_PARISC_LTOFF_FPTR14WR\t123\t/* LT-rel. fct. ptr., right 14 bits. */\n#define R_PARISC_LTOFF_FPTR14DR\t124\t/* LT-rel. fct. ptr., right 14 bits. */\n#define R_PARISC_LTOFF_FPTR16F\t125\t/* 16 bits LT-rel. function ptr.  */\n#define R_PARISC_LTOFF_FPTR16WF\t126\t/* 16 bits LT-rel. function ptr.  */\n#define R_PARISC_LTOFF_FPTR16DF\t127\t/* 16 bits LT-rel. function ptr.  */\n#define R_PARISC_LORESERVE\t128\n#define R_PARISC_COPY\t\t128\t/* Copy relocation.  */\n#define R_PARISC_IPLT\t\t129\t/* Dynamic reloc, imported PLT */\n#define R_PARISC_EPLT\t\t130\t/* Dynamic reloc, exported PLT */\n#define R_PARISC_TPREL32\t153\t/* 32 bits TP-rel. address.  */\n#define R_PARISC_TPREL21L\t154\t/* TP-rel. address, left 21 bits.  */\n#define R_PARISC_TPREL14R\t158\t/* TP-rel. address, right 14 bits.  */\n#define R_PARISC_LTOFF_TP21L\t162\t/* LT-TP-rel. address, left 21 bits. */\n#define R_PARISC_LTOFF_TP14R\t166\t/* LT-TP-rel. address, right 14 bits.*/\n#define R_PARISC_LTOFF_TP14F\t167\t/* 14 bits LT-TP-rel. address.  */\n#define R_PARISC_TPREL64\t216\t/* 64 bits TP-rel. address.  */\n#define R_PARISC_TPREL14WR\t219\t/* TP-rel. address, right 14 bits.  */\n#define R_PARISC_TPREL14DR\t220\t/* TP-rel. address, right 14 bits.  */\n#define R_PARISC_TPREL16F\t221\t/* 16 bits TP-rel. address.  */\n#define R_PARISC_TPREL16WF\t222\t/* 16 bits TP-rel. address.  */\n#define R_PARISC_TPREL16DF\t223\t/* 16 bits TP-rel. address.  */\n#define R_PARISC_LTOFF_TP64\t224\t/* 64 bits LT-TP-rel. address.  */\n#define R_PARISC_LTOFF_TP14WR\t227\t/* LT-TP-rel. address, right 14 bits.*/\n#define R_PARISC_LTOFF_TP14DR\t228\t/* LT-TP-rel. address, right 14 bits.*/\n#define R_PARISC_LTOFF_TP16F\t229\t/* 16 bits LT-TP-rel. address.  */\n#define R_PARISC_LTOFF_TP16WF\t230\t/* 16 bits LT-TP-rel. address.  */\n#define R_PARISC_LTOFF_TP16DF\t231\t/* 16 bits LT-TP-rel. address.  */\n#define R_PARISC_GNU_VTENTRY\t232\n#define R_PARISC_GNU_VTINHERIT\t233\n#define R_PARISC_TLS_GD21L\t234\t/* GD 21-bit left.  */\n#define R_PARISC_TLS_GD14R\t235\t/* GD 14-bit right.  */\n#define R_PARISC_TLS_GDCALL\t236\t/* GD call to __t_g_a.  */\n#define R_PARISC_TLS_LDM21L\t237\t/* LD module 21-bit left.  */\n#define R_PARISC_TLS_LDM14R\t238\t/* LD module 14-bit right.  */\n#define R_PARISC_TLS_LDMCALL\t239\t/* LD module call to __t_g_a.  */\n#define R_PARISC_TLS_LDO21L\t240\t/* LD offset 21-bit left.  */\n#define R_PARISC_TLS_LDO14R\t241\t/* LD offset 14-bit right.  */\n#define R_PARISC_TLS_DTPMOD32\t242\t/* DTP module 32-bit.  */\n#define R_PARISC_TLS_DTPMOD64\t243\t/* DTP module 64-bit.  */\n#define R_PARISC_TLS_DTPOFF32\t244\t/* DTP offset 32-bit.  */\n#define R_PARISC_TLS_DTPOFF64\t245\t/* DTP offset 32-bit.  */\n#define R_PARISC_TLS_LE21L\tR_PARISC_TPREL21L\n#define R_PARISC_TLS_LE14R\tR_PARISC_TPREL14R\n#define R_PARISC_TLS_IE21L\tR_PARISC_LTOFF_TP21L\n#define R_PARISC_TLS_IE14R\tR_PARISC_LTOFF_TP14R\n#define R_PARISC_TLS_TPREL32\tR_PARISC_TPREL32\n#define R_PARISC_TLS_TPREL64\tR_PARISC_TPREL64\n#define R_PARISC_HIRESERVE\t255\n\n/* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr.  */\n\n#define PT_HP_TLS\t\t(PT_LOOS + 0x0)\n#define PT_HP_CORE_NONE\t\t(PT_LOOS + 0x1)\n#define PT_HP_CORE_VERSION\t(PT_LOOS + 0x2)\n#define PT_HP_CORE_KERNEL\t(PT_LOOS + 0x3)\n#define PT_HP_CORE_COMM\t\t(PT_LOOS + 0x4)\n#define PT_HP_CORE_PROC\t\t(PT_LOOS + 0x5)\n#define PT_HP_CORE_LOADABLE\t(PT_LOOS + 0x6)\n#define PT_HP_CORE_STACK\t(PT_LOOS + 0x7)\n#define PT_HP_CORE_SHM\t\t(PT_LOOS + 0x8)\n#define PT_HP_CORE_MMF\t\t(PT_LOOS + 0x9)\n#define PT_HP_PARALLEL\t\t(PT_LOOS + 0x10)\n#define PT_HP_FASTBIND\t\t(PT_LOOS + 0x11)\n#define PT_HP_OPT_ANNOT\t\t(PT_LOOS + 0x12)\n#define PT_HP_HSL_ANNOT\t\t(PT_LOOS + 0x13)\n#define PT_HP_STACK\t\t(PT_LOOS + 0x14)\n\n#define PT_PARISC_ARCHEXT\t0x70000000\n#define PT_PARISC_UNWIND\t0x70000001\n\n/* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr.  */\n\n#define PF_PARISC_SBP\t\t0x08000000\n\n#define PF_HP_PAGE_SIZE\t\t0x00100000\n#define PF_HP_FAR_SHARED\t0x00200000\n#define PF_HP_NEAR_SHARED\t0x00400000\n#define PF_HP_CODE\t\t0x01000000\n#define PF_HP_MODIFY\t\t0x02000000\n#define PF_HP_LAZYSWAP\t\t0x04000000\n#define PF_HP_SBP\t\t0x08000000\n\n\n/* Alpha specific definitions.  */\n\n/* Legal values for e_flags field of Elf64_Ehdr.  */\n\n#define EF_ALPHA_32BIT\t\t1\t/* All addresses must be < 2GB.  */\n#define EF_ALPHA_CANRELAX\t2\t/* Relocations for relaxing exist.  */\n\n/* Legal values for sh_type field of Elf64_Shdr.  */\n\n/* These two are primerily concerned with ECOFF debugging info.  */\n#define SHT_ALPHA_DEBUG\t\t0x70000001\n#define SHT_ALPHA_REGINFO\t0x70000002\n\n/* Legal values for sh_flags field of Elf64_Shdr.  */\n\n#define SHF_ALPHA_GPREL\t\t0x10000000\n\n/* Legal values for st_other field of Elf64_Sym.  */\n#define STO_ALPHA_NOPV\t\t0x80\t/* No PV required.  */\n#define STO_ALPHA_STD_GPLOAD\t0x88\t/* PV only used for initial ldgp.  */\n\n/* Alpha relocs.  */\n\n#define R_ALPHA_NONE\t\t0\t/* No reloc */\n#define R_ALPHA_REFLONG\t\t1\t/* Direct 32 bit */\n#define R_ALPHA_REFQUAD\t\t2\t/* Direct 64 bit */\n#define R_ALPHA_GPREL32\t\t3\t/* GP relative 32 bit */\n#define R_ALPHA_LITERAL\t\t4\t/* GP relative 16 bit w/optimization */\n#define R_ALPHA_LITUSE\t\t5\t/* Optimization hint for LITERAL */\n#define R_ALPHA_GPDISP\t\t6\t/* Add displacement to GP */\n#define R_ALPHA_BRADDR\t\t7\t/* PC+4 relative 23 bit shifted */\n#define R_ALPHA_HINT\t\t8\t/* PC+4 relative 16 bit shifted */\n#define R_ALPHA_SREL16\t\t9\t/* PC relative 16 bit */\n#define R_ALPHA_SREL32\t\t10\t/* PC relative 32 bit */\n#define R_ALPHA_SREL64\t\t11\t/* PC relative 64 bit */\n#define R_ALPHA_GPRELHIGH\t17\t/* GP relative 32 bit, high 16 bits */\n#define R_ALPHA_GPRELLOW\t18\t/* GP relative 32 bit, low 16 bits */\n#define R_ALPHA_GPREL16\t\t19\t/* GP relative 16 bit */\n#define R_ALPHA_COPY\t\t24\t/* Copy symbol at runtime */\n#define R_ALPHA_GLOB_DAT\t25\t/* Create GOT entry */\n#define R_ALPHA_JMP_SLOT\t26\t/* Create PLT entry */\n#define R_ALPHA_RELATIVE\t27\t/* Adjust by program base */\n#define R_ALPHA_TLS_GD_HI\t28\n#define R_ALPHA_TLSGD\t\t29\n#define R_ALPHA_TLS_LDM\t\t30\n#define R_ALPHA_DTPMOD64\t31\n#define R_ALPHA_GOTDTPREL\t32\n#define R_ALPHA_DTPREL64\t33\n#define R_ALPHA_DTPRELHI\t34\n#define R_ALPHA_DTPRELLO\t35\n#define R_ALPHA_DTPREL16\t36\n#define R_ALPHA_GOTTPREL\t37\n#define R_ALPHA_TPREL64\t\t38\n#define R_ALPHA_TPRELHI\t\t39\n#define R_ALPHA_TPRELLO\t\t40\n#define R_ALPHA_TPREL16\t\t41\n/* Keep this the last entry.  */\n#define R_ALPHA_NUM\t\t46\n\n/* Magic values of the LITUSE relocation addend.  */\n#define LITUSE_ALPHA_ADDR\t0\n#define LITUSE_ALPHA_BASE\t1\n#define LITUSE_ALPHA_BYTOFF\t2\n#define LITUSE_ALPHA_JSR\t3\n#define LITUSE_ALPHA_TLS_GD\t4\n#define LITUSE_ALPHA_TLS_LDM\t5\n\n/* Legal values for d_tag of Elf64_Dyn.  */\n#define DT_ALPHA_PLTRO\t\t(DT_LOPROC + 0)\n#define DT_ALPHA_NUM\t\t1\n\n/* PowerPC specific declarations */\n\n/* Values for Elf32/64_Ehdr.e_flags.  */\n#define EF_PPC_EMB\t\t0x80000000\t/* PowerPC embedded flag */\n\n/* Cygnus local bits below */\n#define EF_PPC_RELOCATABLE\t0x00010000\t/* PowerPC -mrelocatable flag*/\n#define EF_PPC_RELOCATABLE_LIB\t0x00008000\t/* PowerPC -mrelocatable-lib\n\t\t\t\t\t\t   flag */\n\n/* PowerPC relocations defined by the ABIs */\n#define R_PPC_NONE\t\t0\n#define R_PPC_ADDR32\t\t1\t/* 32bit absolute address */\n#define R_PPC_ADDR24\t\t2\t/* 26bit address, 2 bits ignored.  */\n#define R_PPC_ADDR16\t\t3\t/* 16bit absolute address */\n#define R_PPC_ADDR16_LO\t\t4\t/* lower 16bit of absolute address */\n#define R_PPC_ADDR16_HI\t\t5\t/* high 16bit of absolute address */\n#define R_PPC_ADDR16_HA\t\t6\t/* adjusted high 16bit */\n#define R_PPC_ADDR14\t\t7\t/* 16bit address, 2 bits ignored */\n#define R_PPC_ADDR14_BRTAKEN\t8\n#define R_PPC_ADDR14_BRNTAKEN\t9\n#define R_PPC_REL24\t\t10\t/* PC relative 26 bit */\n#define R_PPC_REL14\t\t11\t/* PC relative 16 bit */\n#define R_PPC_REL14_BRTAKEN\t12\n#define R_PPC_REL14_BRNTAKEN\t13\n#define R_PPC_GOT16\t\t14\n#define R_PPC_GOT16_LO\t\t15\n#define R_PPC_GOT16_HI\t\t16\n#define R_PPC_GOT16_HA\t\t17\n#define R_PPC_PLTREL24\t\t18\n#define R_PPC_COPY\t\t19\n#define R_PPC_GLOB_DAT\t\t20\n#define R_PPC_JMP_SLOT\t\t21\n#define R_PPC_RELATIVE\t\t22\n#define R_PPC_LOCAL24PC\t\t23\n#define R_PPC_UADDR32\t\t24\n#define R_PPC_UADDR16\t\t25\n#define R_PPC_REL32\t\t26\n#define R_PPC_PLT32\t\t27\n#define R_PPC_PLTREL32\t\t28\n#define R_PPC_PLT16_LO\t\t29\n#define R_PPC_PLT16_HI\t\t30\n#define R_PPC_PLT16_HA\t\t31\n#define R_PPC_SDAREL16\t\t32\n#define R_PPC_SECTOFF\t\t33\n#define R_PPC_SECTOFF_LO\t34\n#define R_PPC_SECTOFF_HI\t35\n#define R_PPC_SECTOFF_HA\t36\n\n/* PowerPC relocations defined for the TLS access ABI.  */\n#define R_PPC_TLS\t\t67 /* none\t(sym+add)@tls */\n#define R_PPC_DTPMOD32\t\t68 /* word32\t(sym+add)@dtpmod */\n#define R_PPC_TPREL16\t\t69 /* half16*\t(sym+add)@tprel */\n#define R_PPC_TPREL16_LO\t70 /* half16\t(sym+add)@tprel@l */\n#define R_PPC_TPREL16_HI\t71 /* half16\t(sym+add)@tprel@h */\n#define R_PPC_TPREL16_HA\t72 /* half16\t(sym+add)@tprel@ha */\n#define R_PPC_TPREL32\t\t73 /* word32\t(sym+add)@tprel */\n#define R_PPC_DTPREL16\t\t74 /* half16*\t(sym+add)@dtprel */\n#define R_PPC_DTPREL16_LO\t75 /* half16\t(sym+add)@dtprel@l */\n#define R_PPC_DTPREL16_HI\t76 /* half16\t(sym+add)@dtprel@h */\n#define R_PPC_DTPREL16_HA\t77 /* half16\t(sym+add)@dtprel@ha */\n#define R_PPC_DTPREL32\t\t78 /* word32\t(sym+add)@dtprel */\n#define R_PPC_GOT_TLSGD16\t79 /* half16*\t(sym+add)@got@tlsgd */\n#define R_PPC_GOT_TLSGD16_LO\t80 /* half16\t(sym+add)@got@tlsgd@l */\n#define R_PPC_GOT_TLSGD16_HI\t81 /* half16\t(sym+add)@got@tlsgd@h */\n#define R_PPC_GOT_TLSGD16_HA\t82 /* half16\t(sym+add)@got@tlsgd@ha */\n#define R_PPC_GOT_TLSLD16\t83 /* half16*\t(sym+add)@got@tlsld */\n#define R_PPC_GOT_TLSLD16_LO\t84 /* half16\t(sym+add)@got@tlsld@l */\n#define R_PPC_GOT_TLSLD16_HI\t85 /* half16\t(sym+add)@got@tlsld@h */\n#define R_PPC_GOT_TLSLD16_HA\t86 /* half16\t(sym+add)@got@tlsld@ha */\n#define R_PPC_GOT_TPREL16\t87 /* half16*\t(sym+add)@got@tprel */\n#define R_PPC_GOT_TPREL16_LO\t88 /* half16\t(sym+add)@got@tprel@l */\n#define R_PPC_GOT_TPREL16_HI\t89 /* half16\t(sym+add)@got@tprel@h */\n#define R_PPC_GOT_TPREL16_HA\t90 /* half16\t(sym+add)@got@tprel@ha */\n#define R_PPC_GOT_DTPREL16\t91 /* half16*\t(sym+add)@got@dtprel */\n#define R_PPC_GOT_DTPREL16_LO\t92 /* half16*\t(sym+add)@got@dtprel@l */\n#define R_PPC_GOT_DTPREL16_HI\t93 /* half16*\t(sym+add)@got@dtprel@h */\n#define R_PPC_GOT_DTPREL16_HA\t94 /* half16*\t(sym+add)@got@dtprel@ha */\n\n/* The remaining relocs are from the Embedded ELF ABI, and are not\n   in the SVR4 ELF ABI.  */\n#define R_PPC_EMB_NADDR32\t101\n#define R_PPC_EMB_NADDR16\t102\n#define R_PPC_EMB_NADDR16_LO\t103\n#define R_PPC_EMB_NADDR16_HI\t104\n#define R_PPC_EMB_NADDR16_HA\t105\n#define R_PPC_EMB_SDAI16\t106\n#define R_PPC_EMB_SDA2I16\t107\n#define R_PPC_EMB_SDA2REL\t108\n#define R_PPC_EMB_SDA21\t\t109\t/* 16 bit offset in SDA */\n#define R_PPC_EMB_MRKREF\t110\n#define R_PPC_EMB_RELSEC16\t111\n#define R_PPC_EMB_RELST_LO\t112\n#define R_PPC_EMB_RELST_HI\t113\n#define R_PPC_EMB_RELST_HA\t114\n#define R_PPC_EMB_BIT_FLD\t115\n#define R_PPC_EMB_RELSDA\t116\t/* 16 bit relative offset in SDA */\n\n/* Diab tool relocations.  */\n#define R_PPC_DIAB_SDA21_LO\t180\t/* like EMB_SDA21, but lower 16 bit */\n#define R_PPC_DIAB_SDA21_HI\t181\t/* like EMB_SDA21, but high 16 bit */\n#define R_PPC_DIAB_SDA21_HA\t182\t/* like EMB_SDA21, adjusted high 16 */\n#define R_PPC_DIAB_RELSDA_LO\t183\t/* like EMB_RELSDA, but lower 16 bit */\n#define R_PPC_DIAB_RELSDA_HI\t184\t/* like EMB_RELSDA, but high 16 bit */\n#define R_PPC_DIAB_RELSDA_HA\t185\t/* like EMB_RELSDA, adjusted high 16 */\n\n/* GNU extension to support local ifunc.  */\n#define R_PPC_IRELATIVE\t\t248\n\n/* GNU relocs used in PIC code sequences.  */\n#define R_PPC_REL16\t\t249\t/* half16   (sym+add-.) */\n#define R_PPC_REL16_LO\t\t250\t/* half16   (sym+add-.)@l */\n#define R_PPC_REL16_HI\t\t251\t/* half16   (sym+add-.)@h */\n#define R_PPC_REL16_HA\t\t252\t/* half16   (sym+add-.)@ha */\n\n/* This is a phony reloc to handle any old fashioned TOC16 references\n   that may still be in object files.  */\n#define R_PPC_TOC16\t\t255\n\n/* PowerPC specific values for the Dyn d_tag field.  */\n#define DT_PPC_GOT\t\t(DT_LOPROC + 0)\n#define DT_PPC_NUM\t\t1\n\n/* PowerPC64 relocations defined by the ABIs */\n#define R_PPC64_NONE\t\tR_PPC_NONE\n#define R_PPC64_ADDR32\t\tR_PPC_ADDR32 /* 32bit absolute address */\n#define R_PPC64_ADDR24\t\tR_PPC_ADDR24 /* 26bit address, word aligned */\n#define R_PPC64_ADDR16\t\tR_PPC_ADDR16 /* 16bit absolute address */\n#define R_PPC64_ADDR16_LO\tR_PPC_ADDR16_LO\t/* lower 16bits of address */\n#define R_PPC64_ADDR16_HI\tR_PPC_ADDR16_HI\t/* high 16bits of address. */\n#define R_PPC64_ADDR16_HA\tR_PPC_ADDR16_HA /* adjusted high 16bits.  */\n#define R_PPC64_ADDR14\t\tR_PPC_ADDR14 /* 16bit address, word aligned */\n#define R_PPC64_ADDR14_BRTAKEN\tR_PPC_ADDR14_BRTAKEN\n#define R_PPC64_ADDR14_BRNTAKEN\tR_PPC_ADDR14_BRNTAKEN\n#define R_PPC64_REL24\t\tR_PPC_REL24 /* PC-rel. 26 bit, word aligned */\n#define R_PPC64_REL14\t\tR_PPC_REL14 /* PC relative 16 bit */\n#define R_PPC64_REL14_BRTAKEN\tR_PPC_REL14_BRTAKEN\n#define R_PPC64_REL14_BRNTAKEN\tR_PPC_REL14_BRNTAKEN\n#define R_PPC64_GOT16\t\tR_PPC_GOT16\n#define R_PPC64_GOT16_LO\tR_PPC_GOT16_LO\n#define R_PPC64_GOT16_HI\tR_PPC_GOT16_HI\n#define R_PPC64_GOT16_HA\tR_PPC_GOT16_HA\n\n#define R_PPC64_COPY\t\tR_PPC_COPY\n#define R_PPC64_GLOB_DAT\tR_PPC_GLOB_DAT\n#define R_PPC64_JMP_SLOT\tR_PPC_JMP_SLOT\n#define R_PPC64_RELATIVE\tR_PPC_RELATIVE\n\n#define R_PPC64_UADDR32\t\tR_PPC_UADDR32\n#define R_PPC64_UADDR16\t\tR_PPC_UADDR16\n#define R_PPC64_REL32\t\tR_PPC_REL32\n#define R_PPC64_PLT32\t\tR_PPC_PLT32\n#define R_PPC64_PLTREL32\tR_PPC_PLTREL32\n#define R_PPC64_PLT16_LO\tR_PPC_PLT16_LO\n#define R_PPC64_PLT16_HI\tR_PPC_PLT16_HI\n#define R_PPC64_PLT16_HA\tR_PPC_PLT16_HA\n\n#define R_PPC64_SECTOFF\t\tR_PPC_SECTOFF\n#define R_PPC64_SECTOFF_LO\tR_PPC_SECTOFF_LO\n#define R_PPC64_SECTOFF_HI\tR_PPC_SECTOFF_HI\n#define R_PPC64_SECTOFF_HA\tR_PPC_SECTOFF_HA\n#define R_PPC64_ADDR30\t\t37 /* word30 (S + A - P) >> 2 */\n#define R_PPC64_ADDR64\t\t38 /* doubleword64 S + A */\n#define R_PPC64_ADDR16_HIGHER\t39 /* half16 #higher(S + A) */\n#define R_PPC64_ADDR16_HIGHERA\t40 /* half16 #highera(S + A) */\n#define R_PPC64_ADDR16_HIGHEST\t41 /* half16 #highest(S + A) */\n#define R_PPC64_ADDR16_HIGHESTA\t42 /* half16 #highesta(S + A) */\n#define R_PPC64_UADDR64\t\t43 /* doubleword64 S + A */\n#define R_PPC64_REL64\t\t44 /* doubleword64 S + A - P */\n#define R_PPC64_PLT64\t\t45 /* doubleword64 L + A */\n#define R_PPC64_PLTREL64\t46 /* doubleword64 L + A - P */\n#define R_PPC64_TOC16\t\t47 /* half16* S + A - .TOC */\n#define R_PPC64_TOC16_LO\t48 /* half16 #lo(S + A - .TOC.) */\n#define R_PPC64_TOC16_HI\t49 /* half16 #hi(S + A - .TOC.) */\n#define R_PPC64_TOC16_HA\t50 /* half16 #ha(S + A - .TOC.) */\n#define R_PPC64_TOC\t\t51 /* doubleword64 .TOC */\n#define R_PPC64_PLTGOT16\t52 /* half16* M + A */\n#define R_PPC64_PLTGOT16_LO\t53 /* half16 #lo(M + A) */\n#define R_PPC64_PLTGOT16_HI\t54 /* half16 #hi(M + A) */\n#define R_PPC64_PLTGOT16_HA\t55 /* half16 #ha(M + A) */\n\n#define R_PPC64_ADDR16_DS\t56 /* half16ds* (S + A) >> 2 */\n#define R_PPC64_ADDR16_LO_DS\t57 /* half16ds  #lo(S + A) >> 2 */\n#define R_PPC64_GOT16_DS\t58 /* half16ds* (G + A) >> 2 */\n#define R_PPC64_GOT16_LO_DS\t59 /* half16ds  #lo(G + A) >> 2 */\n#define R_PPC64_PLT16_LO_DS\t60 /* half16ds  #lo(L + A) >> 2 */\n#define R_PPC64_SECTOFF_DS\t61 /* half16ds* (R + A) >> 2 */\n#define R_PPC64_SECTOFF_LO_DS\t62 /* half16ds  #lo(R + A) >> 2 */\n#define R_PPC64_TOC16_DS\t63 /* half16ds* (S + A - .TOC.) >> 2 */\n#define R_PPC64_TOC16_LO_DS\t64 /* half16ds  #lo(S + A - .TOC.) >> 2 */\n#define R_PPC64_PLTGOT16_DS\t65 /* half16ds* (M + A) >> 2 */\n#define R_PPC64_PLTGOT16_LO_DS\t66 /* half16ds  #lo(M + A) >> 2 */\n\n/* PowerPC64 relocations defined for the TLS access ABI.  */\n#define R_PPC64_TLS\t\t67 /* none\t(sym+add)@tls */\n#define R_PPC64_DTPMOD64\t68 /* doubleword64 (sym+add)@dtpmod */\n#define R_PPC64_TPREL16\t\t69 /* half16*\t(sym+add)@tprel */\n#define R_PPC64_TPREL16_LO\t70 /* half16\t(sym+add)@tprel@l */\n#define R_PPC64_TPREL16_HI\t71 /* half16\t(sym+add)@tprel@h */\n#define R_PPC64_TPREL16_HA\t72 /* half16\t(sym+add)@tprel@ha */\n#define R_PPC64_TPREL64\t\t73 /* doubleword64 (sym+add)@tprel */\n#define R_PPC64_DTPREL16\t74 /* half16*\t(sym+add)@dtprel */\n#define R_PPC64_DTPREL16_LO\t75 /* half16\t(sym+add)@dtprel@l */\n#define R_PPC64_DTPREL16_HI\t76 /* half16\t(sym+add)@dtprel@h */\n#define R_PPC64_DTPREL16_HA\t77 /* half16\t(sym+add)@dtprel@ha */\n#define R_PPC64_DTPREL64\t78 /* doubleword64 (sym+add)@dtprel */\n#define R_PPC64_GOT_TLSGD16\t79 /* half16*\t(sym+add)@got@tlsgd */\n#define R_PPC64_GOT_TLSGD16_LO\t80 /* half16\t(sym+add)@got@tlsgd@l */\n#define R_PPC64_GOT_TLSGD16_HI\t81 /* half16\t(sym+add)@got@tlsgd@h */\n#define R_PPC64_GOT_TLSGD16_HA\t82 /* half16\t(sym+add)@got@tlsgd@ha */\n#define R_PPC64_GOT_TLSLD16\t83 /* half16*\t(sym+add)@got@tlsld */\n#define R_PPC64_GOT_TLSLD16_LO\t84 /* half16\t(sym+add)@got@tlsld@l */\n#define R_PPC64_GOT_TLSLD16_HI\t85 /* half16\t(sym+add)@got@tlsld@h */\n#define R_PPC64_GOT_TLSLD16_HA\t86 /* half16\t(sym+add)@got@tlsld@ha */\n#define R_PPC64_GOT_TPREL16_DS\t87 /* half16ds*\t(sym+add)@got@tprel */\n#define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */\n#define R_PPC64_GOT_TPREL16_HI\t89 /* half16\t(sym+add)@got@tprel@h */\n#define R_PPC64_GOT_TPREL16_HA\t90 /* half16\t(sym+add)@got@tprel@ha */\n#define R_PPC64_GOT_DTPREL16_DS\t91 /* half16ds*\t(sym+add)@got@dtprel */\n#define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */\n#define R_PPC64_GOT_DTPREL16_HI\t93 /* half16\t(sym+add)@got@dtprel@h */\n#define R_PPC64_GOT_DTPREL16_HA\t94 /* half16\t(sym+add)@got@dtprel@ha */\n#define R_PPC64_TPREL16_DS\t95 /* half16ds*\t(sym+add)@tprel */\n#define R_PPC64_TPREL16_LO_DS\t96 /* half16ds\t(sym+add)@tprel@l */\n#define R_PPC64_TPREL16_HIGHER\t97 /* half16\t(sym+add)@tprel@higher */\n#define R_PPC64_TPREL16_HIGHERA\t98 /* half16\t(sym+add)@tprel@highera */\n#define R_PPC64_TPREL16_HIGHEST\t99 /* half16\t(sym+add)@tprel@highest */\n#define R_PPC64_TPREL16_HIGHESTA 100 /* half16\t(sym+add)@tprel@highesta */\n#define R_PPC64_DTPREL16_DS\t101 /* half16ds* (sym+add)@dtprel */\n#define R_PPC64_DTPREL16_LO_DS\t102 /* half16ds\t(sym+add)@dtprel@l */\n#define R_PPC64_DTPREL16_HIGHER\t103 /* half16\t(sym+add)@dtprel@higher */\n#define R_PPC64_DTPREL16_HIGHERA 104 /* half16\t(sym+add)@dtprel@highera */\n#define R_PPC64_DTPREL16_HIGHEST 105 /* half16\t(sym+add)@dtprel@highest */\n#define R_PPC64_DTPREL16_HIGHESTA 106 /* half16\t(sym+add)@dtprel@highesta */\n\n/* GNU extension to support local ifunc.  */\n#define R_PPC64_JMP_IREL\t247\n#define R_PPC64_IRELATIVE\t248\n#define R_PPC64_REL16\t\t249\t/* half16   (sym+add-.) */\n#define R_PPC64_REL16_LO\t250\t/* half16   (sym+add-.)@l */\n#define R_PPC64_REL16_HI\t251\t/* half16   (sym+add-.)@h */\n#define R_PPC64_REL16_HA\t252\t/* half16   (sym+add-.)@ha */\n\n/* PowerPC64 specific values for the Dyn d_tag field.  */\n#define DT_PPC64_GLINK  (DT_LOPROC + 0)\n#define DT_PPC64_OPD\t(DT_LOPROC + 1)\n#define DT_PPC64_OPDSZ\t(DT_LOPROC + 2)\n#define DT_PPC64_NUM    3\n\n\n/* ARM specific declarations */\n\n/* Processor specific flags for the ELF header e_flags field.  */\n#define EF_ARM_RELEXEC\t\t0x01\n#define EF_ARM_HASENTRY\t\t0x02\n#define EF_ARM_INTERWORK\t0x04\n#define EF_ARM_APCS_26\t\t0x08\n#define EF_ARM_APCS_FLOAT\t0x10\n#define EF_ARM_PIC\t\t0x20\n#define EF_ARM_ALIGN8\t\t0x40 /* 8-bit structure alignment is in use */\n#define EF_ARM_NEW_ABI\t\t0x80\n#define EF_ARM_OLD_ABI\t\t0x100\n#define EF_ARM_SOFT_FLOAT\t0x200\n#define EF_ARM_VFP_FLOAT\t0x400\n#define EF_ARM_MAVERICK_FLOAT\t0x800\n\n\n/* Other constants defined in the ARM ELF spec. version B-01.  */\n/* NB. These conflict with values defined above.  */\n#define EF_ARM_SYMSARESORTED\t0x04\n#define EF_ARM_DYNSYMSUSESEGIDX\t0x08\n#define EF_ARM_MAPSYMSFIRST\t0x10\n#define EF_ARM_EABIMASK\t\t0XFF000000\n\n/* Constants defined in AAELF.  */\n#define EF_ARM_BE8\t    0x00800000\n#define EF_ARM_LE8\t    0x00400000\n\n#define EF_ARM_EABI_VERSION(flags)\t((flags) & EF_ARM_EABIMASK)\n#define EF_ARM_EABI_UNKNOWN\t0x00000000\n#define EF_ARM_EABI_VER1\t0x01000000\n#define EF_ARM_EABI_VER2\t0x02000000\n#define EF_ARM_EABI_VER3\t0x03000000\n#define EF_ARM_EABI_VER4\t0x04000000\n#define EF_ARM_EABI_VER5\t0x05000000\n\n/* Additional symbol types for Thumb.  */\n#define STT_ARM_TFUNC\t\tSTT_LOPROC /* A Thumb function.  */\n#define STT_ARM_16BIT\t\tSTT_HIPROC /* A Thumb label.  */\n\n/* ARM-specific values for sh_flags */\n#define SHF_ARM_ENTRYSECT\t0x10000000 /* Section contains an entry point */\n#define SHF_ARM_COMDEF\t\t0x80000000 /* Section may be multiply defined\n\t\t\t\t\t      in the input to a link step.  */\n\n/* ARM-specific program header flags */\n#define PF_ARM_SB\t\t0x10000000 /* Segment contains the location\n\t\t\t\t\t      addressed by the static base. */\n#define PF_ARM_PI\t\t0x20000000 /* Position-independent segment.  */\n#define PF_ARM_ABS\t\t0x40000000 /* Absolute segment.  */\n\n/* Processor specific values for the Phdr p_type field.  */\n#define PT_ARM_EXIDX\t\t(PT_LOPROC + 1)\t/* ARM unwind segment.  */\n\n/* Processor specific values for the Shdr sh_type field.  */\n#define SHT_ARM_EXIDX\t\t(SHT_LOPROC + 1) /* ARM unwind section.  */\n#define SHT_ARM_PREEMPTMAP\t(SHT_LOPROC + 2) /* Preemption details.  */\n#define SHT_ARM_ATTRIBUTES\t(SHT_LOPROC + 3) /* ARM attributes section.  */\n\n\n/* ARM relocs.  */\n\n#define R_ARM_NONE\t\t0\t/* No reloc */\n#define R_ARM_PC24\t\t1\t/* PC relative 26 bit branch */\n#define R_ARM_ABS32\t\t2\t/* Direct 32 bit  */\n#define R_ARM_REL32\t\t3\t/* PC relative 32 bit */\n#define R_ARM_PC13\t\t4\n#define R_ARM_ABS16\t\t5\t/* Direct 16 bit */\n#define R_ARM_ABS12\t\t6\t/* Direct 12 bit */\n#define R_ARM_THM_ABS5\t\t7\n#define R_ARM_ABS8\t\t8\t/* Direct 8 bit */\n#define R_ARM_SBREL32\t\t9\n#define R_ARM_THM_PC22\t\t10\n#define R_ARM_THM_PC8\t\t11\n#define R_ARM_AMP_VCALL9\t12\n#define R_ARM_SWI24\t\t13\t/* Obsolete static relocation.  */\n#define R_ARM_TLS_DESC\t\t13      /* Dynamic relocation.  */\n#define R_ARM_THM_SWI8\t\t14\n#define R_ARM_XPC25\t\t15\n#define R_ARM_THM_XPC22\t\t16\n#define R_ARM_TLS_DTPMOD32\t17\t/* ID of module containing symbol */\n#define R_ARM_TLS_DTPOFF32\t18\t/* Offset in TLS block */\n#define R_ARM_TLS_TPOFF32\t19\t/* Offset in static TLS block */\n#define R_ARM_COPY\t\t20\t/* Copy symbol at runtime */\n#define R_ARM_GLOB_DAT\t\t21\t/* Create GOT entry */\n#define R_ARM_JUMP_SLOT\t\t22\t/* Create PLT entry */\n#define R_ARM_RELATIVE\t\t23\t/* Adjust by program base */\n#define R_ARM_GOTOFF\t\t24\t/* 32 bit offset to GOT */\n#define R_ARM_GOTPC\t\t25\t/* 32 bit PC relative offset to GOT */\n#define R_ARM_GOT32\t\t26\t/* 32 bit GOT entry */\n#define R_ARM_PLT32\t\t27\t/* 32 bit PLT address */\n#define R_ARM_ALU_PCREL_7_0\t32\n#define R_ARM_ALU_PCREL_15_8\t33\n#define R_ARM_ALU_PCREL_23_15\t34\n#define R_ARM_LDR_SBREL_11_0\t35\n#define R_ARM_ALU_SBREL_19_12\t36\n#define R_ARM_ALU_SBREL_27_20\t37\n#define R_ARM_TLS_GOTDESC\t90\n#define R_ARM_TLS_CALL\t\t91\n#define R_ARM_TLS_DESCSEQ\t92\n#define R_ARM_THM_TLS_CALL\t93\n#define R_ARM_GNU_VTENTRY\t100\n#define R_ARM_GNU_VTINHERIT\t101\n#define R_ARM_THM_PC11\t\t102\t/* thumb unconditional branch */\n#define R_ARM_THM_PC9\t\t103\t/* thumb conditional branch */\n#define R_ARM_TLS_GD32\t\t104\t/* PC-rel 32 bit for global dynamic\n\t\t\t\t\t   thread local data */\n#define R_ARM_TLS_LDM32\t\t105\t/* PC-rel 32 bit for local dynamic\n\t\t\t\t\t   thread local data */\n#define R_ARM_TLS_LDO32\t\t106\t/* 32 bit offset relative to TLS\n\t\t\t\t\t   block */\n#define R_ARM_TLS_IE32\t\t107\t/* PC-rel 32 bit for GOT entry of\n\t\t\t\t\t   static TLS block offset */\n#define R_ARM_TLS_LE32\t\t108\t/* 32 bit offset relative to static\n\t\t\t\t\t   TLS block */\n#define\tR_ARM_THM_TLS_DESCSEQ\t129\n#define R_ARM_IRELATIVE\t\t160\n#define R_ARM_RXPC25\t\t249\n#define R_ARM_RSBREL32\t\t250\n#define R_ARM_THM_RPC22\t\t251\n#define R_ARM_RREL32\t\t252\n#define R_ARM_RABS22\t\t253\n#define R_ARM_RPC24\t\t254\n#define R_ARM_RBASE\t\t255\n/* Keep this the last entry.  */\n#define R_ARM_NUM\t\t256\n\n/* IA-64 specific declarations.  */\n\n/* Processor specific flags for the Ehdr e_flags field.  */\n#define EF_IA_64_MASKOS\t\t0x0000000f\t/* os-specific flags */\n#define EF_IA_64_ABI64\t\t0x00000010\t/* 64-bit ABI */\n#define EF_IA_64_ARCH\t\t0xff000000\t/* arch. version mask */\n\n/* Processor specific values for the Phdr p_type field.  */\n#define PT_IA_64_ARCHEXT\t(PT_LOPROC + 0)\t/* arch extension bits */\n#define PT_IA_64_UNWIND\t\t(PT_LOPROC + 1)\t/* ia64 unwind bits */\n#define PT_IA_64_HP_OPT_ANOT\t(PT_LOOS + 0x12)\n#define PT_IA_64_HP_HSL_ANOT\t(PT_LOOS + 0x13)\n#define PT_IA_64_HP_STACK\t(PT_LOOS + 0x14)\n\n/* Processor specific flags for the Phdr p_flags field.  */\n#define PF_IA_64_NORECOV\t0x80000000\t/* spec insns w/o recovery */\n\n/* Processor specific values for the Shdr sh_type field.  */\n#define SHT_IA_64_EXT\t\t(SHT_LOPROC + 0) /* extension bits */\n#define SHT_IA_64_UNWIND\t(SHT_LOPROC + 1) /* unwind bits */\n\n/* Processor specific flags for the Shdr sh_flags field.  */\n#define SHF_IA_64_SHORT\t\t0x10000000\t/* section near gp */\n#define SHF_IA_64_NORECOV\t0x20000000\t/* spec insns w/o recovery */\n\n/* Processor specific values for the Dyn d_tag field.  */\n#define DT_IA_64_PLT_RESERVE\t(DT_LOPROC + 0)\n#define DT_IA_64_NUM\t\t1\n\n/* IA-64 relocations.  */\n#define R_IA64_NONE\t\t0x00\t/* none */\n#define R_IA64_IMM14\t\t0x21\t/* symbol + addend, add imm14 */\n#define R_IA64_IMM22\t\t0x22\t/* symbol + addend, add imm22 */\n#define R_IA64_IMM64\t\t0x23\t/* symbol + addend, mov imm64 */\n#define R_IA64_DIR32MSB\t\t0x24\t/* symbol + addend, data4 MSB */\n#define R_IA64_DIR32LSB\t\t0x25\t/* symbol + addend, data4 LSB */\n#define R_IA64_DIR64MSB\t\t0x26\t/* symbol + addend, data8 MSB */\n#define R_IA64_DIR64LSB\t\t0x27\t/* symbol + addend, data8 LSB */\n#define R_IA64_GPREL22\t\t0x2a\t/* @gprel(sym + add), add imm22 */\n#define R_IA64_GPREL64I\t\t0x2b\t/* @gprel(sym + add), mov imm64 */\n#define R_IA64_GPREL32MSB\t0x2c\t/* @gprel(sym + add), data4 MSB */\n#define R_IA64_GPREL32LSB\t0x2d\t/* @gprel(sym + add), data4 LSB */\n#define R_IA64_GPREL64MSB\t0x2e\t/* @gprel(sym + add), data8 MSB */\n#define R_IA64_GPREL64LSB\t0x2f\t/* @gprel(sym + add), data8 LSB */\n#define R_IA64_LTOFF22\t\t0x32\t/* @ltoff(sym + add), add imm22 */\n#define R_IA64_LTOFF64I\t\t0x33\t/* @ltoff(sym + add), mov imm64 */\n#define R_IA64_PLTOFF22\t\t0x3a\t/* @pltoff(sym + add), add imm22 */\n#define R_IA64_PLTOFF64I\t0x3b\t/* @pltoff(sym + add), mov imm64 */\n#define R_IA64_PLTOFF64MSB\t0x3e\t/* @pltoff(sym + add), data8 MSB */\n#define R_IA64_PLTOFF64LSB\t0x3f\t/* @pltoff(sym + add), data8 LSB */\n#define R_IA64_FPTR64I\t\t0x43\t/* @fptr(sym + add), mov imm64 */\n#define R_IA64_FPTR32MSB\t0x44\t/* @fptr(sym + add), data4 MSB */\n#define R_IA64_FPTR32LSB\t0x45\t/* @fptr(sym + add), data4 LSB */\n#define R_IA64_FPTR64MSB\t0x46\t/* @fptr(sym + add), data8 MSB */\n#define R_IA64_FPTR64LSB\t0x47\t/* @fptr(sym + add), data8 LSB */\n#define R_IA64_PCREL60B\t\t0x48\t/* @pcrel(sym + add), brl */\n#define R_IA64_PCREL21B\t\t0x49\t/* @pcrel(sym + add), ptb, call */\n#define R_IA64_PCREL21M\t\t0x4a\t/* @pcrel(sym + add), chk.s */\n#define R_IA64_PCREL21F\t\t0x4b\t/* @pcrel(sym + add), fchkf */\n#define R_IA64_PCREL32MSB\t0x4c\t/* @pcrel(sym + add), data4 MSB */\n#define R_IA64_PCREL32LSB\t0x4d\t/* @pcrel(sym + add), data4 LSB */\n#define R_IA64_PCREL64MSB\t0x4e\t/* @pcrel(sym + add), data8 MSB */\n#define R_IA64_PCREL64LSB\t0x4f\t/* @pcrel(sym + add), data8 LSB */\n#define R_IA64_LTOFF_FPTR22\t0x52\t/* @ltoff(@fptr(s+a)), imm22 */\n#define R_IA64_LTOFF_FPTR64I\t0x53\t/* @ltoff(@fptr(s+a)), imm64 */\n#define R_IA64_LTOFF_FPTR32MSB\t0x54\t/* @ltoff(@fptr(s+a)), data4 MSB */\n#define R_IA64_LTOFF_FPTR32LSB\t0x55\t/* @ltoff(@fptr(s+a)), data4 LSB */\n#define R_IA64_LTOFF_FPTR64MSB\t0x56\t/* @ltoff(@fptr(s+a)), data8 MSB */\n#define R_IA64_LTOFF_FPTR64LSB\t0x57\t/* @ltoff(@fptr(s+a)), data8 LSB */\n#define R_IA64_SEGREL32MSB\t0x5c\t/* @segrel(sym + add), data4 MSB */\n#define R_IA64_SEGREL32LSB\t0x5d\t/* @segrel(sym + add), data4 LSB */\n#define R_IA64_SEGREL64MSB\t0x5e\t/* @segrel(sym + add), data8 MSB */\n#define R_IA64_SEGREL64LSB\t0x5f\t/* @segrel(sym + add), data8 LSB */\n#define R_IA64_SECREL32MSB\t0x64\t/* @secrel(sym + add), data4 MSB */\n#define R_IA64_SECREL32LSB\t0x65\t/* @secrel(sym + add), data4 LSB */\n#define R_IA64_SECREL64MSB\t0x66\t/* @secrel(sym + add), data8 MSB */\n#define R_IA64_SECREL64LSB\t0x67\t/* @secrel(sym + add), data8 LSB */\n#define R_IA64_REL32MSB\t\t0x6c\t/* data 4 + REL */\n#define R_IA64_REL32LSB\t\t0x6d\t/* data 4 + REL */\n#define R_IA64_REL64MSB\t\t0x6e\t/* data 8 + REL */\n#define R_IA64_REL64LSB\t\t0x6f\t/* data 8 + REL */\n#define R_IA64_LTV32MSB\t\t0x74\t/* symbol + addend, data4 MSB */\n#define R_IA64_LTV32LSB\t\t0x75\t/* symbol + addend, data4 LSB */\n#define R_IA64_LTV64MSB\t\t0x76\t/* symbol + addend, data8 MSB */\n#define R_IA64_LTV64LSB\t\t0x77\t/* symbol + addend, data8 LSB */\n#define R_IA64_PCREL21BI\t0x79\t/* @pcrel(sym + add), 21bit inst */\n#define R_IA64_PCREL22\t\t0x7a\t/* @pcrel(sym + add), 22bit inst */\n#define R_IA64_PCREL64I\t\t0x7b\t/* @pcrel(sym + add), 64bit inst */\n#define R_IA64_IPLTMSB\t\t0x80\t/* dynamic reloc, imported PLT, MSB */\n#define R_IA64_IPLTLSB\t\t0x81\t/* dynamic reloc, imported PLT, LSB */\n#define R_IA64_COPY\t\t0x84\t/* copy relocation */\n#define R_IA64_SUB\t\t0x85\t/* Addend and symbol difference */\n#define R_IA64_LTOFF22X\t\t0x86\t/* LTOFF22, relaxable.  */\n#define R_IA64_LDXMOV\t\t0x87\t/* Use of LTOFF22X.  */\n#define R_IA64_TPREL14\t\t0x91\t/* @tprel(sym + add), imm14 */\n#define R_IA64_TPREL22\t\t0x92\t/* @tprel(sym + add), imm22 */\n#define R_IA64_TPREL64I\t\t0x93\t/* @tprel(sym + add), imm64 */\n#define R_IA64_TPREL64MSB\t0x96\t/* @tprel(sym + add), data8 MSB */\n#define R_IA64_TPREL64LSB\t0x97\t/* @tprel(sym + add), data8 LSB */\n#define R_IA64_LTOFF_TPREL22\t0x9a\t/* @ltoff(@tprel(s+a)), imm2 */\n#define R_IA64_DTPMOD64MSB\t0xa6\t/* @dtpmod(sym + add), data8 MSB */\n#define R_IA64_DTPMOD64LSB\t0xa7\t/* @dtpmod(sym + add), data8 LSB */\n#define R_IA64_LTOFF_DTPMOD22\t0xaa\t/* @ltoff(@dtpmod(sym + add)), imm22 */\n#define R_IA64_DTPREL14\t\t0xb1\t/* @dtprel(sym + add), imm14 */\n#define R_IA64_DTPREL22\t\t0xb2\t/* @dtprel(sym + add), imm22 */\n#define R_IA64_DTPREL64I\t0xb3\t/* @dtprel(sym + add), imm64 */\n#define R_IA64_DTPREL32MSB\t0xb4\t/* @dtprel(sym + add), data4 MSB */\n#define R_IA64_DTPREL32LSB\t0xb5\t/* @dtprel(sym + add), data4 LSB */\n#define R_IA64_DTPREL64MSB\t0xb6\t/* @dtprel(sym + add), data8 MSB */\n#define R_IA64_DTPREL64LSB\t0xb7\t/* @dtprel(sym + add), data8 LSB */\n#define R_IA64_LTOFF_DTPREL22\t0xba\t/* @ltoff(@dtprel(s+a)), imm22 */\n\n/* SH specific declarations */\n\n/* Processor specific flags for the ELF header e_flags field.  */\n#define EF_SH_MACH_MASK\t\t0x1f\n#define EF_SH_UNKNOWN\t\t0x0\n#define EF_SH1\t\t\t0x1\n#define EF_SH2\t\t\t0x2\n#define EF_SH3\t\t\t0x3\n#define EF_SH_DSP\t\t0x4\n#define EF_SH3_DSP\t\t0x5\n#define EF_SH4AL_DSP\t\t0x6\n#define EF_SH3E\t\t\t0x8\n#define EF_SH4\t\t\t0x9\n#define EF_SH2E\t\t\t0xb\n#define EF_SH4A\t\t\t0xc\n#define EF_SH2A\t\t\t0xd\n#define EF_SH4_NOFPU\t\t0x10\n#define EF_SH4A_NOFPU\t\t0x11\n#define EF_SH4_NOMMU_NOFPU\t0x12\n#define EF_SH2A_NOFPU\t\t0x13\n#define EF_SH3_NOMMU\t\t0x14\n#define EF_SH2A_SH4_NOFPU\t0x15\n#define EF_SH2A_SH3_NOFPU\t0x16\n#define EF_SH2A_SH4\t\t0x17\n#define EF_SH2A_SH3E\t\t0x18\n\n/* SH relocs.  */\n#define\tR_SH_NONE\t\t0\n#define\tR_SH_DIR32\t\t1\n#define\tR_SH_REL32\t\t2\n#define\tR_SH_DIR8WPN\t\t3\n#define\tR_SH_IND12W\t\t4\n#define\tR_SH_DIR8WPL\t\t5\n#define\tR_SH_DIR8WPZ\t\t6\n#define\tR_SH_DIR8BP\t\t7\n#define\tR_SH_DIR8W\t\t8\n#define\tR_SH_DIR8L\t\t9\n#define\tR_SH_SWITCH16\t\t25\n#define\tR_SH_SWITCH32\t\t26\n#define\tR_SH_USES\t\t27\n#define\tR_SH_COUNT\t\t28\n#define\tR_SH_ALIGN\t\t29\n#define\tR_SH_CODE\t\t30\n#define\tR_SH_DATA\t\t31\n#define\tR_SH_LABEL\t\t32\n#define\tR_SH_SWITCH8\t\t33\n#define\tR_SH_GNU_VTINHERIT\t34\n#define\tR_SH_GNU_VTENTRY\t35\n#define\tR_SH_TLS_GD_32\t\t144\n#define\tR_SH_TLS_LD_32\t\t145\n#define\tR_SH_TLS_LDO_32\t\t146\n#define\tR_SH_TLS_IE_32\t\t147\n#define\tR_SH_TLS_LE_32\t\t148\n#define\tR_SH_TLS_DTPMOD32\t149\n#define\tR_SH_TLS_DTPOFF32\t150\n#define\tR_SH_TLS_TPOFF32\t151\n#define\tR_SH_GOT32\t\t160\n#define\tR_SH_PLT32\t\t161\n#define\tR_SH_COPY\t\t162\n#define\tR_SH_GLOB_DAT\t\t163\n#define\tR_SH_JMP_SLOT\t\t164\n#define\tR_SH_RELATIVE\t\t165\n#define\tR_SH_GOTOFF\t\t166\n#define\tR_SH_GOTPC\t\t167\n/* Keep this the last entry.  */\n#define\tR_SH_NUM\t\t256\n\n/* S/390 specific definitions.  */\n\n/* Valid values for the e_flags field.  */\n\n#define EF_S390_HIGH_GPRS    0x00000001  /* High GPRs kernel facility needed.  */\n\n/* Additional s390 relocs */\n\n#define R_390_NONE\t\t0\t/* No reloc.  */\n#define R_390_8\t\t\t1\t/* Direct 8 bit.  */\n#define R_390_12\t\t2\t/* Direct 12 bit.  */\n#define R_390_16\t\t3\t/* Direct 16 bit.  */\n#define R_390_32\t\t4\t/* Direct 32 bit.  */\n#define R_390_PC32\t\t5\t/* PC relative 32 bit.\t*/\n#define R_390_GOT12\t\t6\t/* 12 bit GOT offset.  */\n#define R_390_GOT32\t\t7\t/* 32 bit GOT offset.  */\n#define R_390_PLT32\t\t8\t/* 32 bit PC relative PLT address.  */\n#define R_390_COPY\t\t9\t/* Copy symbol at runtime.  */\n#define R_390_GLOB_DAT\t\t10\t/* Create GOT entry.  */\n#define R_390_JMP_SLOT\t\t11\t/* Create PLT entry.  */\n#define R_390_RELATIVE\t\t12\t/* Adjust by program base.  */\n#define R_390_GOTOFF32\t\t13\t/* 32 bit offset to GOT.\t */\n#define R_390_GOTPC\t\t14\t/* 32 bit PC relative offset to GOT.  */\n#define R_390_GOT16\t\t15\t/* 16 bit GOT offset.  */\n#define R_390_PC16\t\t16\t/* PC relative 16 bit.\t*/\n#define R_390_PC16DBL\t\t17\t/* PC relative 16 bit shifted by 1.  */\n#define R_390_PLT16DBL\t\t18\t/* 16 bit PC rel. PLT shifted by 1.  */\n#define R_390_PC32DBL\t\t19\t/* PC relative 32 bit shifted by 1.  */\n#define R_390_PLT32DBL\t\t20\t/* 32 bit PC rel. PLT shifted by 1.  */\n#define R_390_GOTPCDBL\t\t21\t/* 32 bit PC rel. GOT shifted by 1.  */\n#define R_390_64\t\t22\t/* Direct 64 bit.  */\n#define R_390_PC64\t\t23\t/* PC relative 64 bit.\t*/\n#define R_390_GOT64\t\t24\t/* 64 bit GOT offset.  */\n#define R_390_PLT64\t\t25\t/* 64 bit PC relative PLT address.  */\n#define R_390_GOTENT\t\t26\t/* 32 bit PC rel. to GOT entry >> 1. */\n#define R_390_GOTOFF16\t\t27\t/* 16 bit offset to GOT. */\n#define R_390_GOTOFF64\t\t28\t/* 64 bit offset to GOT. */\n#define R_390_GOTPLT12\t\t29\t/* 12 bit offset to jump slot.\t*/\n#define R_390_GOTPLT16\t\t30\t/* 16 bit offset to jump slot.\t*/\n#define R_390_GOTPLT32\t\t31\t/* 32 bit offset to jump slot.\t*/\n#define R_390_GOTPLT64\t\t32\t/* 64 bit offset to jump slot.\t*/\n#define R_390_GOTPLTENT\t\t33\t/* 32 bit rel. offset to jump slot.  */\n#define R_390_PLTOFF16\t\t34\t/* 16 bit offset from GOT to PLT. */\n#define R_390_PLTOFF32\t\t35\t/* 32 bit offset from GOT to PLT. */\n#define R_390_PLTOFF64\t\t36\t/* 16 bit offset from GOT to PLT. */\n#define R_390_TLS_LOAD\t\t37\t/* Tag for load insn in TLS code.  */\n#define R_390_TLS_GDCALL\t38\t/* Tag for function call in general\n\t\t\t\t\t   dynamic TLS code. */\n#define R_390_TLS_LDCALL\t39\t/* Tag for function call in local\n\t\t\t\t\t   dynamic TLS code. */\n#define R_390_TLS_GD32\t\t40\t/* Direct 32 bit for general dynamic\n\t\t\t\t\t   thread local data.  */\n#define R_390_TLS_GD64\t\t41\t/* Direct 64 bit for general dynamic\n\t\t\t\t\t  thread local data.  */\n#define R_390_TLS_GOTIE12\t42\t/* 12 bit GOT offset for static TLS\n\t\t\t\t\t   block offset.  */\n#define R_390_TLS_GOTIE32\t43\t/* 32 bit GOT offset for static TLS\n\t\t\t\t\t   block offset.  */\n#define R_390_TLS_GOTIE64\t44\t/* 64 bit GOT offset for static TLS\n\t\t\t\t\t   block offset. */\n#define R_390_TLS_LDM32\t\t45\t/* Direct 32 bit for local dynamic\n\t\t\t\t\t   thread local data in LE code.  */\n#define R_390_TLS_LDM64\t\t46\t/* Direct 64 bit for local dynamic\n\t\t\t\t\t   thread local data in LE code.  */\n#define R_390_TLS_IE32\t\t47\t/* 32 bit address of GOT entry for\n\t\t\t\t\t   negated static TLS block offset.  */\n#define R_390_TLS_IE64\t\t48\t/* 64 bit address of GOT entry for\n\t\t\t\t\t   negated static TLS block offset.  */\n#define R_390_TLS_IEENT\t\t49\t/* 32 bit rel. offset to GOT entry for\n\t\t\t\t\t   negated static TLS block offset.  */\n#define R_390_TLS_LE32\t\t50\t/* 32 bit negated offset relative to\n\t\t\t\t\t   static TLS block.  */\n#define R_390_TLS_LE64\t\t51\t/* 64 bit negated offset relative to\n\t\t\t\t\t   static TLS block.  */\n#define R_390_TLS_LDO32\t\t52\t/* 32 bit offset relative to TLS\n\t\t\t\t\t   block.  */\n#define R_390_TLS_LDO64\t\t53\t/* 64 bit offset relative to TLS\n\t\t\t\t\t   block.  */\n#define R_390_TLS_DTPMOD\t54\t/* ID of module containing symbol.  */\n#define R_390_TLS_DTPOFF\t55\t/* Offset in TLS block.\t */\n#define R_390_TLS_TPOFF\t\t56\t/* Negated offset in static TLS\n\t\t\t\t\t   block.  */\n#define R_390_20\t\t57\t/* Direct 20 bit.  */\n#define R_390_GOT20\t\t58\t/* 20 bit GOT offset.  */\n#define R_390_GOTPLT20\t\t59\t/* 20 bit offset to jump slot.  */\n#define R_390_TLS_GOTIE20\t60\t/* 20 bit GOT offset for static TLS\n\t\t\t\t\t   block offset.  */\n/* Keep this the last entry.  */\n#define R_390_NUM\t\t61\n\n\n/* CRIS relocations.  */\n#define R_CRIS_NONE\t\t0\n#define R_CRIS_8\t\t1\n#define R_CRIS_16\t\t2\n#define R_CRIS_32\t\t3\n#define R_CRIS_8_PCREL\t\t4\n#define R_CRIS_16_PCREL\t\t5\n#define R_CRIS_32_PCREL\t\t6\n#define R_CRIS_GNU_VTINHERIT\t7\n#define R_CRIS_GNU_VTENTRY\t8\n#define R_CRIS_COPY\t\t9\n#define R_CRIS_GLOB_DAT\t\t10\n#define R_CRIS_JUMP_SLOT\t11\n#define R_CRIS_RELATIVE\t\t12\n#define R_CRIS_16_GOT\t\t13\n#define R_CRIS_32_GOT\t\t14\n#define R_CRIS_16_GOTPLT\t15\n#define R_CRIS_32_GOTPLT\t16\n#define R_CRIS_32_GOTREL\t17\n#define R_CRIS_32_PLT_GOTREL\t18\n#define R_CRIS_32_PLT_PCREL\t19\n\n#define R_CRIS_NUM\t\t20\n\n\n/* AMD x86-64 relocations.  */\n#define R_X86_64_NONE\t\t0\t/* No reloc */\n#define R_X86_64_64\t\t1\t/* Direct 64 bit  */\n#define R_X86_64_PC32\t\t2\t/* PC relative 32 bit signed */\n#define R_X86_64_GOT32\t\t3\t/* 32 bit GOT entry */\n#define R_X86_64_PLT32\t\t4\t/* 32 bit PLT address */\n#define R_X86_64_COPY\t\t5\t/* Copy symbol at runtime */\n#define R_X86_64_GLOB_DAT\t6\t/* Create GOT entry */\n#define R_X86_64_JUMP_SLOT\t7\t/* Create PLT entry */\n#define R_X86_64_RELATIVE\t8\t/* Adjust by program base */\n#define R_X86_64_GOTPCREL\t9\t/* 32 bit signed PC relative\n\t\t\t\t\t   offset to GOT */\n#define R_X86_64_32\t\t10\t/* Direct 32 bit zero extended */\n#define R_X86_64_32S\t\t11\t/* Direct 32 bit sign extended */\n#define R_X86_64_16\t\t12\t/* Direct 16 bit zero extended */\n#define R_X86_64_PC16\t\t13\t/* 16 bit sign extended pc relative */\n#define R_X86_64_8\t\t14\t/* Direct 8 bit sign extended  */\n#define R_X86_64_PC8\t\t15\t/* 8 bit sign extended pc relative */\n#define R_X86_64_DTPMOD64\t16\t/* ID of module containing symbol */\n#define R_X86_64_DTPOFF64\t17\t/* Offset in module's TLS block */\n#define R_X86_64_TPOFF64\t18\t/* Offset in initial TLS block */\n#define R_X86_64_TLSGD\t\t19\t/* 32 bit signed PC relative offset\n\t\t\t\t\t   to two GOT entries for GD symbol */\n#define R_X86_64_TLSLD\t\t20\t/* 32 bit signed PC relative offset\n\t\t\t\t\t   to two GOT entries for LD symbol */\n#define R_X86_64_DTPOFF32\t21\t/* Offset in TLS block */\n#define R_X86_64_GOTTPOFF\t22\t/* 32 bit signed PC relative offset\n\t\t\t\t\t   to GOT entry for IE symbol */\n#define R_X86_64_TPOFF32\t23\t/* Offset in initial TLS block */\n#define R_X86_64_PC64\t\t24\t/* PC relative 64 bit */\n#define R_X86_64_GOTOFF64\t25\t/* 64 bit offset to GOT */\n#define R_X86_64_GOTPC32\t26\t/* 32 bit signed pc relative\n\t\t\t\t\t   offset to GOT */\n#define R_X86_64_GOT64\t\t27\t/* 64-bit GOT entry offset */\n#define R_X86_64_GOTPCREL64\t28\t/* 64-bit PC relative offset\n\t\t\t\t\t   to GOT entry */\n#define R_X86_64_GOTPC64\t29\t/* 64-bit PC relative offset to GOT */\n#define R_X86_64_GOTPLT64\t30 \t/* like GOT64, says PLT entry needed */\n#define R_X86_64_PLTOFF64\t31\t/* 64-bit GOT relative offset\n\t\t\t\t\t   to PLT entry */\n#define R_X86_64_SIZE32\t\t32\t/* Size of symbol plus 32-bit addend */\n#define R_X86_64_SIZE64\t\t33\t/* Size of symbol plus 64-bit addend */\n#define R_X86_64_GOTPC32_TLSDESC 34\t/* GOT offset for TLS descriptor.  */\n#define R_X86_64_TLSDESC_CALL   35\t/* Marker for call through TLS\n\t\t\t\t\t   descriptor.  */\n#define R_X86_64_TLSDESC        36\t/* TLS descriptor.  */\n#define R_X86_64_IRELATIVE\t37\t/* Adjust indirectly by program base */\n\n#define R_X86_64_NUM\t\t38\n\n\n/* AM33 relocations.  */\n#define R_MN10300_NONE\t\t0\t/* No reloc.  */\n#define R_MN10300_32\t\t1\t/* Direct 32 bit.  */\n#define R_MN10300_16\t\t2\t/* Direct 16 bit.  */\n#define R_MN10300_8\t\t3\t/* Direct 8 bit.  */\n#define R_MN10300_PCREL32\t4\t/* PC-relative 32-bit.  */\n#define R_MN10300_PCREL16\t5\t/* PC-relative 16-bit signed.  */\n#define R_MN10300_PCREL8\t6\t/* PC-relative 8-bit signed.  */\n#define R_MN10300_GNU_VTINHERIT\t7\t/* Ancient C++ vtable garbage... */\n#define R_MN10300_GNU_VTENTRY\t8\t/* ... collection annotation.  */\n#define R_MN10300_24\t\t9\t/* Direct 24 bit.  */\n#define R_MN10300_GOTPC32\t10\t/* 32-bit PCrel offset to GOT.  */\n#define R_MN10300_GOTPC16\t11\t/* 16-bit PCrel offset to GOT.  */\n#define R_MN10300_GOTOFF32\t12\t/* 32-bit offset from GOT.  */\n#define R_MN10300_GOTOFF24\t13\t/* 24-bit offset from GOT.  */\n#define R_MN10300_GOTOFF16\t14\t/* 16-bit offset from GOT.  */\n#define R_MN10300_PLT32\t\t15\t/* 32-bit PCrel to PLT entry.  */\n#define R_MN10300_PLT16\t\t16\t/* 16-bit PCrel to PLT entry.  */\n#define R_MN10300_GOT32\t\t17\t/* 32-bit offset to GOT entry.  */\n#define R_MN10300_GOT24\t\t18\t/* 24-bit offset to GOT entry.  */\n#define R_MN10300_GOT16\t\t19\t/* 16-bit offset to GOT entry.  */\n#define R_MN10300_COPY\t\t20\t/* Copy symbol at runtime.  */\n#define R_MN10300_GLOB_DAT\t21\t/* Create GOT entry.  */\n#define R_MN10300_JMP_SLOT\t22\t/* Create PLT entry.  */\n#define R_MN10300_RELATIVE\t23\t/* Adjust by program base.  */\n\n#define R_MN10300_NUM\t\t24\n\n\n/* M32R relocs.  */\n#define R_M32R_NONE\t\t0\t/* No reloc. */\n#define R_M32R_16\t\t1\t/* Direct 16 bit. */\n#define R_M32R_32\t\t2\t/* Direct 32 bit. */\n#define R_M32R_24\t\t3\t/* Direct 24 bit. */\n#define R_M32R_10_PCREL\t\t4\t/* PC relative 10 bit shifted. */\n#define R_M32R_18_PCREL\t\t5\t/* PC relative 18 bit shifted. */\n#define R_M32R_26_PCREL\t\t6\t/* PC relative 26 bit shifted. */\n#define R_M32R_HI16_ULO\t\t7\t/* High 16 bit with unsigned low. */\n#define R_M32R_HI16_SLO\t\t8\t/* High 16 bit with signed low. */\n#define R_M32R_LO16\t\t9\t/* Low 16 bit. */\n#define R_M32R_SDA16\t\t10\t/* 16 bit offset in SDA. */\n#define R_M32R_GNU_VTINHERIT\t11\n#define R_M32R_GNU_VTENTRY\t12\n/* M32R relocs use SHT_RELA.  */\n#define R_M32R_16_RELA\t\t33\t/* Direct 16 bit. */\n#define R_M32R_32_RELA\t\t34\t/* Direct 32 bit. */\n#define R_M32R_24_RELA\t\t35\t/* Direct 24 bit. */\n#define R_M32R_10_PCREL_RELA\t36\t/* PC relative 10 bit shifted. */\n#define R_M32R_18_PCREL_RELA\t37\t/* PC relative 18 bit shifted. */\n#define R_M32R_26_PCREL_RELA\t38\t/* PC relative 26 bit shifted. */\n#define R_M32R_HI16_ULO_RELA\t39\t/* High 16 bit with unsigned low */\n#define R_M32R_HI16_SLO_RELA\t40\t/* High 16 bit with signed low */\n#define R_M32R_LO16_RELA\t41\t/* Low 16 bit */\n#define R_M32R_SDA16_RELA\t42\t/* 16 bit offset in SDA */\n#define R_M32R_RELA_GNU_VTINHERIT\t43\n#define R_M32R_RELA_GNU_VTENTRY\t44\n#define R_M32R_REL32\t\t45\t/* PC relative 32 bit.  */\n\n#define R_M32R_GOT24\t\t48\t/* 24 bit GOT entry */\n#define R_M32R_26_PLTREL\t49\t/* 26 bit PC relative to PLT shifted */\n#define R_M32R_COPY\t\t50\t/* Copy symbol at runtime */\n#define R_M32R_GLOB_DAT\t\t51\t/* Create GOT entry */\n#define R_M32R_JMP_SLOT\t\t52\t/* Create PLT entry */\n#define R_M32R_RELATIVE\t\t53\t/* Adjust by program base */\n#define R_M32R_GOTOFF\t\t54\t/* 24 bit offset to GOT */\n#define R_M32R_GOTPC24\t\t55\t/* 24 bit PC relative offset to GOT */\n#define R_M32R_GOT16_HI_ULO\t56\t/* High 16 bit GOT entry with unsigned\n\t\t\t\t\t   low */\n#define R_M32R_GOT16_HI_SLO\t57\t/* High 16 bit GOT entry with signed\n\t\t\t\t\t   low */\n#define R_M32R_GOT16_LO\t\t58\t/* Low 16 bit GOT entry */\n#define R_M32R_GOTPC_HI_ULO\t59\t/* High 16 bit PC relative offset to\n\t\t\t\t\t   GOT with unsigned low */\n#define R_M32R_GOTPC_HI_SLO\t60\t/* High 16 bit PC relative offset to\n\t\t\t\t\t   GOT with signed low */\n#define R_M32R_GOTPC_LO\t\t61\t/* Low 16 bit PC relative offset to\n\t\t\t\t\t   GOT */\n#define R_M32R_GOTOFF_HI_ULO\t62\t/* High 16 bit offset to GOT\n\t\t\t\t\t   with unsigned low */\n#define R_M32R_GOTOFF_HI_SLO\t63\t/* High 16 bit offset to GOT\n\t\t\t\t\t   with signed low */\n#define R_M32R_GOTOFF_LO\t64\t/* Low 16 bit offset to GOT */\n#define R_M32R_NUM\t\t256\t/* Keep this the last entry. */\n\n\n__END_DECLS\n\n#endif\t/* elf.h */\n"
  },
  {
    "path": "src/gs/gs.c",
    "content": "#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"gs.h\"\n#include \"ee/intc.h\"\n#include \"iop/intc.h\"\n\nstruct ps2_gs* ps2_gs_create(void) {\n    return malloc(sizeof(struct ps2_gs));\n}\n\nstatic inline void gs_test_gs_irq(struct ps2_gs* gs) {\n    uint32_t mask = (gs->imr >> 8) & 0x1f;\n    uint32_t stat = gs->csr & 0x1f;\n\n    if (stat & (~mask)) {\n        // printf(\"gs: IRQ triggered! stat=%02x mask=%02x\\n\", stat, mask);\n\n        ps2_intc_irq(gs->ee_intc, EE_INTC_GS);\n    }\n}\n\nstatic inline int gs_assert_vblank(struct ps2_gs* gs) {\n    if ((gs->csr & 8) == 0) {\n        gs->csr |= 8;\n\n        return ((gs->imr >> 8) & 8) == 0;\n    }\n\n    return 0;\n}\n\n\nstatic inline int gs_assert_hblank(struct ps2_gs* gs) {\n    if ((gs->csr & 4) == 0) {\n        if (gs->csr_enable & 4)\n            gs->csr |= 4;\n\n        // printf(\"gs: Asserting Hblank imr.hsync=%d\\n\", (gs->imr >> 8) & 4);\n\n        return ((gs->imr >> 8) & 4) == 0;\n    }\n\n    return 0;\n}\n\nvoid gs_handle_vblank_in(void* udata, int overshoot);\nvoid gs_handle_hblank(void* udata, int overshoot);\n\nvoid gs_handle_vblank_out(void* udata, int overshoot) {\n    struct ps2_gs* gs = (struct ps2_gs*)udata;\n\n    struct sched_event vblank_in_event;\n\n    vblank_in_event.callback = gs_handle_vblank_in;\n    vblank_in_event.cycles = GS_FRAME_NTSC;\n    vblank_in_event.name = \"Vblank in event\";\n    vblank_in_event.udata = gs;\n\n    sched_schedule(gs->sched, vblank_in_event);\n\n    // Send Vblank IRQs through INTC\n    ps2_intc_irq(gs->ee_intc, EE_INTC_VBLANK_OUT);\n    ps2_iop_intc_irq(gs->iop_intc, IOP_INTC_VBLANK_OUT);\n\n    gs->vblank = 0;\n}\n\nvoid gs_flip_field(void* udata, int overshoot) {\n    struct ps2_gs* gs = (struct ps2_gs*)udata;\n\n    // Toggle field\n    gs->csr ^= (1 << 13) | (1 << 14);\n}\n\nvoid gs_handle_vblank_in(void* udata, int overshoot) {\n    struct ps2_gs* gs = (struct ps2_gs*)udata;\n\n    struct sched_event vblank_out_event;\n\n    vblank_out_event.callback = gs_handle_vblank_out;\n    vblank_out_event.cycles = GS_VBLANK_NTSC;\n    vblank_out_event.name = \"Vblank out event\";\n    vblank_out_event.udata = gs;\n\n    struct sched_event field_flip_event;\n\n    field_flip_event.callback = gs_flip_field;\n    field_flip_event.cycles = 65622;\n    field_flip_event.name = \"Field flip event\";\n    field_flip_event.udata = gs;\n\n    // Set Vblank and Hblank flag\n    if (gs_assert_vblank(gs)) {\n        ps2_intc_irq(gs->ee_intc, EE_INTC_GS);\n    }\n\n    // Send Vblank IRQ through INTC\n    ps2_intc_irq(gs->ee_intc, EE_INTC_VBLANK_IN);\n    ps2_iop_intc_irq(gs->iop_intc, IOP_INTC_VBLANK_IN);\n\n    // uint32_t mask = (gs->imr >> 8) & 0x1f;\n    // uint32_t stat = gs->csr & 0x1f;\n\n    // if (stat & (~mask)) {\n    //     ps2_intc_irq(gs->ee_intc, EE_INTC_GS);\n    // }\n\n    sched_schedule(gs->sched, vblank_out_event);\n    sched_schedule(gs->sched, field_flip_event);\n\n    gs->vblank = 1;\n}\n\nvoid gs_handle_hblank(void* udata, int overshoot) {\n    struct ps2_gs* gs = (struct ps2_gs*)udata;\n\n    struct sched_event hblank_event;\n\n    hblank_event.callback = gs_handle_hblank;\n    hblank_event.cycles = GS_SCANLINE_NTSC;\n    hblank_event.name = \"Hblank event\";\n    hblank_event.udata = gs;\n\n    if (gs_assert_hblank(gs)) {\n        ps2_intc_irq(gs->ee_intc, EE_INTC_GS);\n    }\n\n    gs->csr ^= 1 << 13 | 1 << 14;\n\n    sched_schedule(gs->sched, hblank_event);\n}\n\nvoid ps2_gs_init(struct ps2_gs* gs, struct ps2_intc* ee_intc, struct ps2_iop_intc* iop_intc, struct ps2_ee_timers* ee_timers, struct ps2_iop_timers* iop_timers, struct sched_state* sched) {\n    memset(gs, 0, sizeof(struct ps2_gs));\n\n    gs->sched = sched;\n    gs->ee_intc = ee_intc;\n    gs->iop_intc = iop_intc;\n    gs->ee_timers = ee_timers;\n    gs->iop_timers = iop_timers;\n    gs->vram = malloc(0x400000); // 4 MB\n\n    // Schedule Vblank event\n    struct sched_event vblank_event;\n    vblank_event.callback = gs_handle_vblank_in;\n    vblank_event.cycles = GS_FRAME_NTSC;\n    vblank_event.name = \"Vblank in event\";\n    vblank_event.udata = gs;\n\n    sched_schedule(gs->sched, vblank_event);\n\n    // struct sched_event hblank_event;\n    // hblank_event.callback = gs_handle_hblank;\n    // hblank_event.cycles = GS_SCANLINE_NTSC;\n    // hblank_event.name = \"Hblank event\";\n    // hblank_event.udata = gs;\n\n    // sched_schedule(gs->sched, hblank_event);\n\n    gs->ctx = &gs->context[0];\n    gs->imr = 0x00007f00;\n}\n\nvoid ps2_gs_reset(struct ps2_gs* gs) {\n    gs->ctx = &gs->context[0];\n    gs->csr |= 2;\n    gs->imr = 0x00007f00;\n\n    // Schedule Vblank event\n    struct sched_event vblank_event;\n    vblank_event.callback = gs_handle_vblank_in;\n    vblank_event.cycles = GS_FRAME_NTSC;\n    vblank_event.name = \"Vblank in event\";\n    vblank_event.udata = gs;\n\n    sched_schedule(gs->sched, vblank_event);\n\n    // struct sched_event hblank_event;\n    // hblank_event.callback = gs_handle_hblank;\n    // hblank_event.cycles = GS_SCANLINE_NTSC;\n    // hblank_event.name = \"Hblank event\";\n    // hblank_event.udata = gs;\n\n    // sched_schedule(gs->sched, hblank_event);\n\n    memset(gs->vram, 0, 0x400000);\n}\n\n// void gs_switch_context(struct ps2_gs* gs, int c) {\n//     gs->ctx = &gs->context[c];\n// }\n\nvoid ps2_gs_destroy(struct ps2_gs* gs) {\n    free(gs->vram);\n    free(gs);\n}\n\n// void gs_start_primitive(struct ps2_gs* gs) {\n//     if (gs->prim & ~0x7ff) {\n//         // printf(\"gs: Invalid prim value %016x\\n\", gs->prim);\n\n//         // exit(1);\n//     }\n\n//     gs->vqi = 0;\n// }\n\n// static inline void gs_unpack_vertex(struct ps2_gs* gs, struct gs_vertex* v) {\n//     v->x = v->xyz & 0xffff;\n//     v->y = (v->xyz >> 16) & 0xffff;\n//     v->z = v->xyz >> 32;\n//     v->r = v->rgbaq & 0xff;\n//     v->g = (v->rgbaq >> 8) & 0xff;\n//     v->b = (v->rgbaq >> 16) & 0xff;\n//     v->a = (v->rgbaq >> 24) & 0xff;\n\n//     union {\n//         uint32_t u32;\n//         float f;\n//     } s, t, q;\n\n//     s.u32 = v->st & 0xffffffff;\n//     t.u32 = v->st >> 32;\n//     q.u32 = v->rgbaq >> 32;\n\n//     v->s = s.f;\n//     v->t = t.f;\n//     v->q = q.f;\n//     v->u = v->uv & 0x3fff;\n//     v->v = (v->uv >> 16) & 0x3fff;\n// }\n\n// void gs_write_vertex(struct ps2_gs* gs, uint64_t data, int discard) {\n//     gs->vq[gs->vqi].xyz = data;\n//     gs->vq[gs->vqi].st = gs->st;\n//     gs->vq[gs->vqi].uv = gs->uv;\n//     gs->vq[gs->vqi].rgbaq = gs->rgbaq;\n\n//     gs->attr = (gs->prmodecont & 1) ? gs->prim : gs->prmode;\n\n//     // Cache PRIM/PRMODE fields\n//     gs->iip = (gs->attr >> 3) & 1;\n//     gs->tme = (gs->attr >> 4) & 1;\n//     gs->fge = (gs->attr >> 5) & 1;\n//     gs->abe = (gs->attr >> 6) & 1;\n//     gs->aa1 = (gs->attr >> 7) & 1;\n//     gs->fst = (gs->attr >> 8) & 1;\n//     gs->ctxt = (gs->attr >> 9) & 1;\n//     gs->fix = (gs->attr >> 10) & 1;\n\n//     gs_unpack_vertex(gs, &gs->vq[gs->vqi]);\n\n//     // printf(\"gs: Pushing vertex (%04x,%04x) to VQ[%d] discard=%d\\n\",\n//     //     gs->vq[gs->vqi].x, gs->vq[gs->vqi].y, gs->vqi, discard\n//     // );\n\n//     gs->vqi++;\n\n//     // for (int c = 0; c < 2; c++) {\n//     //     uint32_t fbp = (gs->context[c].frame & 0x1ff) << 11;\n//     //     uint32_t fbw = ((gs->context[c].frame >> 16) & 0x3f) << 6;\n//     //     uint32_t xoff = (gs->context[c].xyoffset & 0xffff);\n//     //     uint32_t yoff = ((gs->context[c].xyoffset >> 32) & 0xffff);\n//     //     int scax0 = gs->context[c].scissor & 0x3ff;\n//     //     int scay0 = (gs->context[c].scissor >> 32) & 0x3ff;\n//     //     int scax1 = (gs->context[c].scissor >> 16) & 0x3ff;\n//     //     int scay1 = (gs->context[c].scissor >> 48) & 0x3ff;\n\n//     //     printf(\"context %d: fbp=%08x fbw=%d xyoffset=(%d,%d) scissor=(%d,%d-%d,%d) prmodecont=%016lx prmode=%016lx prim=%016lx\\n\",\n//     //         c,\n//     //         fbp, fbw,\n//     //         xoff, yoff,\n//     //         scax0, scay0,\n//     //         scax1, scay1,\n//     //         gs->prmodecont,\n//     //         gs->prmode,\n//     //         gs->prim\n//     //     );\n//     // }\n\n//     gs_switch_context(gs, (gs->attr & GS_CTXT) ? 1 : 0);\n\n//     switch (gs->prim & 7) {\n//         case 0: if (gs->vqi == 1) { gs->backend.render_point(gs, gs->backend.udata); gs->vqi = 0; } break;\n//         case 1: if (gs->vqi == 2) { gs->backend.render_line(gs, gs->backend.udata); gs->vqi = 0; } break;\n//         case 2: {\n//             if (gs->vqi == 2) {\n//                 if (!discard)\n//                     gs->backend.render_line(gs, gs->backend.udata);\n//             } else if (gs->vqi == 3) {\n//                 gs->vq[0] = gs->vq[1];\n//                 gs->vq[1] = gs->vq[2];\n\n//                 if (!discard)\n//                     gs->backend.render_line(gs, gs->backend.udata);\n\n//                 gs->vqi = 2;\n//             }\n//         } break;\n//         case 3: if (gs->vqi == 3) { if (!discard) gs->backend.render_triangle(gs, gs->backend.udata); gs->vqi = 0; } break;\n//         case 4: {\n//             if (gs->vqi == 3) {\n//                 if (!discard) gs->backend.render_triangle(gs, gs->backend.udata);\n//             } else if (gs->vqi == 4) {\n//                 gs->vq[0] = gs->vq[1];\n//                 gs->vq[1] = gs->vq[2];\n//                 gs->vq[2] = gs->vq[3];\n\n//                 if (!discard) gs->backend.render_triangle(gs, gs->backend.udata);\n\n//                 gs->vqi = 3;\n//             }\n//         } break;\n//         case 5: {\n//             if (gs->vqi == 3) {\n//                 if (!discard) gs->backend.render_triangle(gs, gs->backend.udata);\n//             } else if (gs->vqi == 4) {\n//                 gs->vq[1] = gs->vq[2];\n//                 gs->vq[2] = gs->vq[3];\n\n//                 if (!discard) gs->backend.render_triangle(gs, gs->backend.udata);\n\n//                 gs->vqi = 3;\n//             }\n//         } break;\n//         case 6: if (gs->vqi == 2) { if (!discard) gs->backend.render_sprite(gs, gs->backend.udata); gs->vqi = 0; } break;\n//         case 7: if (gs->vqi == 2) { if (!discard) gs->backend.render_sprite(gs, gs->backend.udata); gs->vqi = 0; } break;\n//         default: {\n//             printf(\"gs: Reserved primitive %ld\\n\", gs->prim & 7);\n//         } break;\n//     }\n// }\n\n// void gs_write_vertex_fog(struct ps2_gs* gs, uint64_t data, int discard) {\n//     gs->vq[gs->vqi].fog = data >> 56;\n\n//     gs_write_vertex(gs, data & 0xffffffffffffffull, discard);\n// }\n\n// void gs_write_vertex_no_fog(struct ps2_gs* gs, uint64_t data, int discard) {\n//     gs->vq[gs->vqi].fog = gs->fog;\n\n//     gs_write_vertex(gs, data, discard);\n// }\n\nuint64_t ps2_gs_read64(struct ps2_gs* gs, uint32_t addr) {\n    // Hack toggle between FIFO empty and FIFO \"Neither Empty nor Almost Full\"\n    gs->csr ^= 0x4000;\n\n    addr = (addr & 0xfffff000) | (addr & 0x3ff);\n\n    switch (addr) {\n        case 0x12000000:\n        case 0x12000010:\n        case 0x12000020:\n        case 0x12000030:\n        case 0x12000040:\n        case 0x12000050:\n        case 0x12000060:\n        case 0x12000070:\n        case 0x12000080:\n        case 0x12000090:\n        case 0x120000A0:\n        case 0x120000B0:\n        case 0x120000C0:\n        case 0x120000D0:\n        case 0x120000E0:\n        case 0x12001000:\n        case 0x12001010:\n        case 0x12001040: {\n            return gs->csr | 0x551b0000;\n        }\n\n        case 0x12001080: return gs->siglblid;\n\n        case 0x12001001: return (gs->csr >> 8) & 0xff;\n    }\n\n    printf(\"gs: Unhandled read from %08x\\n\", addr);\n\n    return 0;\n}\n\nstatic inline void gs_unpack_dispfb1(struct ps2_gs* gs) {\n    gs->dfbp1 = (gs->dispfb1 & 0x1ff) << 5;\n    gs->dfbw1 = (gs->dispfb1 >> 9) & 0x3f;\n    gs->dfbpsm1 = (gs->dispfb1 >> 15) & 0x1f;\n}\n\nstatic inline void gs_unpack_dispfb2(struct ps2_gs* gs) {\n    gs->dfbp2 = (gs->dispfb2 & 0x1ff) << 5;\n    gs->dfbw2 = (gs->dispfb2 >> 9) & 0x3f;\n    gs->dfbpsm2 = (gs->dispfb2 >> 15) & 0x1f;\n}\n\nvoid ps2_gs_write64(struct ps2_gs* gs, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        case 0x12000000: gs->pmode = data; return;\n        case 0x12000010: gs->smode1 = data; return;\n        case 0x12000020: gs->smode2 = data; return;\n        case 0x12000030: gs->srfsh = data; return;\n        case 0x12000040: gs->synch1 = data; return;\n        case 0x12000050: gs->synch2 = data; return;\n        case 0x12000060: gs->syncv = data; return;\n        case 0x12000070: gs->dispfb1 = data; gs_unpack_dispfb1(gs); return;\n        case 0x12000080: gs->display1 = data; return;\n        case 0x12000090: gs->dispfb2 = data; gs_unpack_dispfb2(gs); return;\n        case 0x120000A0: gs->display2 = data; return;\n        case 0x120000B0: gs->extbuf = data; return;\n        case 0x120000C0: gs->extdata = data; return;\n        case 0x120000D0: gs->extwrite = data; return;\n        case 0x120000E0: gs->bgcolor = data; return;\n        case 0x12001000: {\n            if (data & 8) {\n                // Game is requesting vsync\n                // gs->vblank |= 1;\n            }\n\n            gs->csr = (gs->csr & 0xfffffe00) | (gs->csr & ~(data & 0xf));\n            gs->csr_enable = data;\n\n            if (data & 1) {\n                if (gs->signal_pending) {\n                    gs->siglblid &= ~0xffffffffull;\n                    gs->siglblid |= gs->stall_sigid;\n                }\n            }\n        } return;\n        case 0x12001010: {\n            int prev_signal = (gs->imr >> 8) & 1;\n            int new_signal = (data >> 8) & 1;\n\n            gs->imr = data;\n\n            if (gs->signal_pending && (prev_signal && !new_signal)) {\n                gs->signal_pending--;\n\n                ps2_intc_irq(gs->ee_intc, EE_INTC_GS);\n            }\n        } return;\n        case 0x12001040: gs->busdir = data; return;\n        case 0x12001080: gs->siglblid = data; return;\n    }\n\n    fprintf(stderr, \"gs: Unhandled write to %08x with data %016lx\\n\", addr, data);\n\n    exit(1);\n}\n\n// static inline void gs_load_clut_cache(struct ps2_gs* gs, int i) {\n//     // printf(\"tbpsm=%x cbpsm=%x csm=%d csa=%x (%d) cld=%d\\n\",\n//     //     gs->context[i].tbpsm,\n//     //     gs->context[i].cbpsm,\n//     //     gs->context[i].csm,\n//     //     gs->context[i].csa,\n//     //     gs->context[i].csa,\n//     //     gs->context[i].cld\n//     // );\n\n//     switch (gs->context[i].cld) {\n//         case 1: break;\n//         case 2: gs->cbp0 = gs->context[i].cbp; break;\n//         case 3: gs->cbp1 = gs->context[i].cbp; break;\n//         case 4: if (gs->context[i].cbp == gs->cbp0) return; break;\n//         case 5: if (gs->context[i].cbp == gs->cbp1) return; break;\n//         default: return;\n//     }\n\n//     switch (gs->context[i].tbpsm) {\n//         case GS_PSMT8H:\n//         case GS_PSMT8: {\n//             switch (gs->context[i].cbpsm) {\n//                 case GS_PSMCT32: {\n//                     for (int y = 0; y < 16; y++) {\n//                         for (int x = 0; x < 16; x++) {\n//                             uint32_t cache_addr = (gs->context[i].csa * 16) + (x + (y * 16));\n//                             uint32_t vram_addr = gs->context[i].cbp + (x + (y * 64));\n\n//                             gs->clut_cache[cache_addr] = gs->vram[vram_addr];\n//                         }\n//                     }\n//                 } break;\n\n//                 case GS_PSMCT16:\n//                 case GS_PSMCT16S: {\n//                     printf(\"16bpp 8-bit CLUT\\n\");\n\n//                     exit(1);\n//                 } break;\n//             }\n//         } break;\n\n//         case GS_PSMT4HH:\n//         case GS_PSMT4HL:\n//         case GS_PSMT4: {\n//             switch (gs->context[i].cbpsm) {\n//                 case GS_PSMCT32: {\n//                     for (int y = 0; y < 2; y++) {\n//                         for (int x = 0; x < 8; x++) {\n//                             uint32_t cache_addr = (gs->context[i].csa * 16) + (x + (y * 8));\n//                             uint32_t vram_addr = gs->context[i].cbp + (x + (y * 64));\n\n//                             gs->clut_cache[cache_addr] = gs->vram[vram_addr];\n//                         }\n//                     }\n//                 } break;\n\n//                 case GS_PSMCT16:\n//                 case GS_PSMCT16S: {\n//                     printf(\"16bpp 4-bit CLUT\\n\");\n\n//                     exit(1);\n//                 } break;\n//             }\n//         } break;\n//     }\n// }\n\n// static inline void gs_unpack_tex0(struct ps2_gs* gs, int i) {\n//     gs->context[i].tbp0 = gs->context[i].tex0 & 0x3fff;\n//     gs->context[i].tbw = (gs->context[i].tex0 >> 14) & 0x3f;\n//     gs->context[i].tbpsm = (gs->context[i].tex0 >> 20) & 0x3f;\n//     gs->context[i].usize = 1 << ((gs->context[i].tex0 >> 26) & 0xf); // tw\n//     gs->context[i].vsize = 1 << ((gs->context[i].tex0 >> 30) & 0xf); // th\n//     gs->context[i].tcc = (gs->context[i].tex0 >> 34) & 1;\n//     gs->context[i].tfx = (gs->context[i].tex0 >> 35) & 3;\n//     gs->context[i].cbp = (gs->context[i].tex0 >> 37) & 0x3fff;\n//     gs->context[i].cbpsm = (gs->context[i].tex0 >> 51) & 0xf;\n//     gs->context[i].csm = (gs->context[i].tex0 >> 55) & 1;\n//     gs->context[i].csa = (gs->context[i].tex0 >> 56) & 0x1f;\n//     gs->context[i].cld = (gs->context[i].tex0 >> 61) & 7;\n\n//     gs->context[i].usize = (gs->context[i].usize > 1024) ? 1024 : gs->context[i].usize;\n//     gs->context[i].vsize = (gs->context[i].vsize > 1024) ? 1024 : gs->context[i].vsize;\n\n//     if (gs->context[i].cld) {\n//         // printf(\"gs: CLUT cache load (mode %d, dbp=%08x)\\n\", gs->context[i].cld, gs->context[i].cbp);\n//         // gs_load_clut_cache(gs, i);\n//     }\n// }\n\n// static inline void gs_unpack_clamp(struct ps2_gs* gs, int i) {\n//     gs->context[i].wms = gs->context[i].clamp & 3;\n//     gs->context[i].wmt = (gs->context[i].clamp >> 2) & 3;\n//     gs->context[i].minu = (gs->context[i].clamp >> 4) & 0x3ff;\n//     gs->context[i].maxu = (gs->context[i].clamp >> 14) & 0x3ff;\n//     gs->context[i].minv = (gs->context[i].clamp >> 24) & 0x3ff;\n//     gs->context[i].maxv = (gs->context[i].clamp >> 34) & 0x3ff;\n// }\n\n// static inline void gs_unpack_tex1(struct ps2_gs* gs, int i) {\n//     gs->context[i].lcm = gs->context[i].tex1 & 1;\n//     gs->context[i].mxl = (gs->context[i].tex1 >> 2) & 7;\n//     gs->context[i].mmag = (gs->context[i].tex1 >> 5) & 1;\n//     gs->context[i].mmin = (gs->context[i].tex1 >> 6) & 7;\n//     gs->context[i].mtba = (gs->context[i].tex1 >> 9) & 1;\n//     gs->context[i].l = (gs->context[i].tex1 >> 19) & 3;\n//     gs->context[i].k = gs->context[i].tex1 >> 32;\n// }\n\n// static inline void gs_unpack_tex2(struct ps2_gs* gs, int i) {\n//     gs->context[i].tbpsm = (gs->context[i].tex2 >> 20) & 0x3f;\n//     gs->context[i].cbp = (gs->context[i].tex2 >> 37) & 0x3fff;\n//     gs->context[i].cbpsm = (gs->context[i].tex2 >> 51) & 0xf;\n//     gs->context[i].csm = (gs->context[i].tex2 >> 55) & 1;\n//     gs->context[i].csa = (gs->context[i].tex2 >> 56) & 0x1f;\n//     gs->context[i].cld = (gs->context[i].tex2 >> 61) & 7;\n\n//     if (gs->context[i].cld) {\n//         // printf(\"gs: CLUT cache load (mode %d, dbp=%08x)\\n\", gs->context[i].cld, gs->context[i].cbp);\n//         // gs_load_clut_cache(gs, i);\n//     }\n\n//     // if (gs->context[i].cld) {\n//     //     gs_load_clut_cache(gs, i);\n//     // }\n// }\n\n// static inline void gs_unpack_xyoffset(struct ps2_gs* gs, int i) {\n//     gs->context[i].ofx = gs->context[i].xyoffset & 0xffff;\n//     gs->context[i].ofy = (gs->context[i].xyoffset >> 32) & 0xffff;\n// }\n\n// static inline void gs_unpack_miptbp1(struct ps2_gs* gs, int i) {\n//     gs->context[i].mmtbp[0] = gs->context[i].miptbp1 & 0x3fff;\n//     gs->context[i].mmtbw[0] = (gs->context[i].miptbp1 >> 14) & 0x3f;\n//     gs->context[i].mmtbp[1] = (gs->context[i].miptbp1 >> 20) & 0x3fff;\n//     gs->context[i].mmtbw[1] = (gs->context[i].miptbp1 >> 34) & 0x3f;\n//     gs->context[i].mmtbp[2] = (gs->context[i].miptbp1 >> 40) & 0x3fff;\n//     gs->context[i].mmtbw[2] = (gs->context[i].miptbp1 >> 54) & 0x3f;\n// }\n\n// static inline void gs_unpack_miptbp2(struct ps2_gs* gs, int i) {\n//     gs->context[i].mmtbp[3] = gs->context[i].miptbp2 & 0x3fff;\n//     gs->context[i].mmtbw[3] = (gs->context[i].miptbp2 >> 14) & 0x3f;\n//     gs->context[i].mmtbp[4] = (gs->context[i].miptbp2 >> 20) & 0x3fff;\n//     gs->context[i].mmtbw[4] = (gs->context[i].miptbp2 >> 34) & 0x3f;\n//     gs->context[i].mmtbp[5] = (gs->context[i].miptbp2 >> 40) & 0x3fff;\n//     gs->context[i].mmtbw[5] = (gs->context[i].miptbp2 >> 54) & 0x3f;\n// }\n\n// static inline void gs_unpack_scissor(struct ps2_gs* gs, int i) {\n//     gs->context[i].scax0 = gs->context[i].scissor & 0x3ff;\n//     gs->context[i].scay0 = (gs->context[i].scissor >> 32) & 0x3ff;\n//     gs->context[i].scax1 = (gs->context[i].scissor >> 16) & 0x3ff;\n//     gs->context[i].scay1 = (gs->context[i].scissor >> 48) & 0x3ff;\n// }\n\n// static inline void gs_unpack_alpha(struct ps2_gs* gs, int i) {\n//     gs->context[i].a = gs->context[i].alpha & 3;\n//     gs->context[i].b = (gs->context[i].alpha >> 2) & 3;\n//     gs->context[i].c = (gs->context[i].alpha >> 4) & 3;\n//     gs->context[i].d = (gs->context[i].alpha >> 6) & 3;\n//     gs->context[i].fix = (gs->context[i].alpha >> 32) & 0xff;\n// }\n\n// static inline void gs_unpack_test(struct ps2_gs* gs, int i) {\n//     gs->context[i].ate = gs->context[i].test & 1;\n//     gs->context[i].atst = (gs->context[i].test >> 1) & 7;\n//     gs->context[i].aref = (gs->context[i].test >> 4) & 0xff;\n//     gs->context[i].afail = (gs->context[i].test >> 12) & 3;\n//     gs->context[i].date = (gs->context[i].test >> 14) & 1;\n//     gs->context[i].datm = (gs->context[i].test >> 15) & 1;\n//     gs->context[i].zte = (gs->context[i].test >> 16) & 1;\n//     gs->context[i].ztst = (gs->context[i].test >> 17) & 3;\n// }\n\n// static inline void gs_unpack_frame(struct ps2_gs* gs, int i) {\n//     gs->context[i].fbp = (gs->context[i].frame & 0x1ff) << 11;\n//     gs->context[i].fbw = ((gs->context[i].frame >> 16) & 0x3f) << 6;\n//     gs->context[i].fbpsm = (gs->context[i].frame >> 24) & 0x3f;\n//     gs->context[i].fbmsk = gs->context[i].frame >> 32;\n// }\n\n// static inline void gs_unpack_zbuf(struct ps2_gs* gs, int i) {\n//     gs->context[i].zbp = (gs->context[i].zbuf & 0x1ff) << 11;\n//     gs->context[i].zbpsm = (gs->context[i].zbuf >> 24) & 0xf;\n//     gs->context[i].zbmsk = (gs->context[i].zbuf >> 32) & 1;\n// }\n\n// static inline void gs_unpack_texclut(struct ps2_gs* gs) {\n//     gs->cbw = gs->texclut & 0x3f;\n//     gs->cou = ((gs->texclut >> 6) & 0x3f) << 4;\n//     gs->cov = (gs->texclut >> 12) & 0x3ff;\n// }\n\n// static inline void gs_unpack_texa(struct ps2_gs* gs) {\n//     gs->ta0 = gs->texa & 0xff;\n//     gs->aem = (gs->texa >> 15) & 1;\n//     gs->ta1 = (gs->texa >> 32) & 0xff;\n// }\n\n// static inline void gs_unpack_dimx(struct ps2_gs* gs) {\n//     gs->dither[0][0] = ((int32_t)(((gs->dimx >> 0 ) & 7) << 29)) >> 29;\n//     gs->dither[0][1] = ((int32_t)(((gs->dimx >> 4 ) & 7) << 29)) >> 29;\n//     gs->dither[0][2] = ((int32_t)(((gs->dimx >> 8 ) & 7) << 29)) >> 29;\n//     gs->dither[0][3] = ((int32_t)(((gs->dimx >> 12) & 7) << 29)) >> 29;\n//     gs->dither[1][0] = ((int32_t)(((gs->dimx >> 16) & 7) << 29)) >> 29;\n//     gs->dither[1][1] = ((int32_t)(((gs->dimx >> 20) & 7) << 29)) >> 29;\n//     gs->dither[1][2] = ((int32_t)(((gs->dimx >> 24) & 7) << 29)) >> 29;\n//     gs->dither[1][3] = ((int32_t)(((gs->dimx >> 28) & 7) << 29)) >> 29;\n//     gs->dither[2][0] = ((int32_t)(((gs->dimx >> 32) & 7) << 29)) >> 29;\n//     gs->dither[2][1] = ((int32_t)(((gs->dimx >> 36) & 7) << 29)) >> 29;\n//     gs->dither[2][2] = ((int32_t)(((gs->dimx >> 40) & 7) << 29)) >> 29;\n//     gs->dither[2][3] = ((int32_t)(((gs->dimx >> 44) & 7) << 29)) >> 29;\n//     gs->dither[3][0] = ((int32_t)(((gs->dimx >> 48) & 7) << 29)) >> 29;\n//     gs->dither[3][1] = ((int32_t)(((gs->dimx >> 52) & 7) << 29)) >> 29;\n//     gs->dither[3][2] = ((int32_t)(((gs->dimx >> 56) & 7) << 29)) >> 29;\n//     gs->dither[3][3] = ((int32_t)(((gs->dimx >> 60) & 7) << 29)) >> 29;\n// }\n\n// void ps2_gs_write_internal(struct ps2_gs* gs, int reg, uint64_t data) {\n//     switch (reg) {\n//         case 0x00: /* printf(\"gs: PRIM <- %016lx\\n\", data); */ gs->prim = data; gs_start_primitive(gs); return;\n//         case 0x01: /* printf(\"gs: RGBAQ <- %016lx\\n\", data); */ gs->rgbaq = data; return;\n//         case 0x02: /* printf(\"gs: ST <- %016lx\\n\", data); */ gs->st = data; return;\n//         case 0x03: /* printf(\"gs: UV <- %016lx\\n\", data); */ gs->uv = data; return;\n//         case 0x04: /* printf(\"gs: XYZF2 <- %016lx\\n\", data); */ gs->xyzf2 = data; gs_write_vertex_fog(gs, gs->xyzf2, 0); return;\n//         case 0x05: /* printf(\"gs: XYZ2 <- %016lx\\n\", data); */ gs->xyz2 = data; gs_write_vertex_no_fog(gs, gs->xyz2, 0); return;\n//         case 0x06: /* printf(\"gs: TEX0_1 <- %016lx\\n\", data); */ gs->context[0].tex0 = data; gs_unpack_tex0(gs, 0); return;\n//         case 0x07: /* printf(\"gs: TEX0_2 <- %016lx\\n\", data); */ gs->context[1].tex0 = data; gs_unpack_tex0(gs, 1); return;\n//         case 0x08: /* printf(\"gs: CLAMP_1 <- %016lx\\n\", data); */ gs->context[0].clamp = data; gs_unpack_clamp(gs, 0); return;\n//         case 0x09: /* printf(\"gs: CLAMP_2 <- %016lx\\n\", data); */ gs->context[1].clamp = data; gs_unpack_clamp(gs, 1); return;\n//         case 0x0A: /* printf(\"gs: FOG <- %016lx\\n\", data); */ gs->fog = data; return;\n//         case 0x0C: /* printf(\"gs: XYZF3 <- %016lx\\n\", data); */ gs->xyzf3 = data; gs_write_vertex_fog(gs, gs->xyzf3, 1); return;\n//         case 0x0D: /* printf(\"gs: XYZ3 <- %016lx\\n\", data); */ gs->xyz3 = data; gs_write_vertex_no_fog(gs, gs->xyz3, 1); return;\n//         case 0x14: /* printf(\"gs: TEX1_1 <- %016lx\\n\", data); */ gs->context[0].tex1 = data; gs_unpack_tex1(gs, 0); return;\n//         case 0x15: /* printf(\"gs: TEX1_2 <- %016lx\\n\", data); */ gs->context[1].tex1 = data; gs_unpack_tex1(gs, 1); return;\n//         case 0x16: /* printf(\"gs: TEX2_1 <- %016lx\\n\", data); */ gs->context[0].tex2 = data; gs_unpack_tex2(gs, 0); return;\n//         case 0x17: /* printf(\"gs: TEX2_2 <- %016lx\\n\", data); */ gs->context[1].tex2 = data; gs_unpack_tex2(gs, 1); return;\n//         case 0x18: /* printf(\"gs: XYOFFSET_1 <- %016lx\\n\", data); */ gs->context[0].xyoffset = data; gs_unpack_xyoffset(gs, 0); return;\n//         case 0x19: /* printf(\"gs: XYOFFSET_2 <- %016lx\\n\", data); */ gs->context[1].xyoffset = data; gs_unpack_xyoffset(gs, 1); return;\n//         case 0x1A: /* printf(\"gs: PRMODECONT <- %016lx\\n\", data); */ gs->prmodecont = data; return;\n//         case 0x1B: /* printf(\"gs: PRMODE <- %016lx\\n\", data); */ gs->prmode = data; return;\n//         case 0x1C: /* printf(\"gs: TEXCLUT <- %016lx\\n\", data); */ gs->texclut = data; gs_unpack_texclut(gs); return;\n//         case 0x22: /* printf(\"gs: SCANMSK <- %016lx\\n\", data); */ gs->scanmsk = data; return;\n//         case 0x34: /* printf(\"gs: MIPTBP1_1 <- %016lx\\n\", data); */ gs->context[0].miptbp1 = data; gs_unpack_miptbp1(gs, 0); return;\n//         case 0x35: /* printf(\"gs: MIPTBP1_2 <- %016lx\\n\", data); */ gs->context[1].miptbp1 = data; gs_unpack_miptbp1(gs, 1); return;\n//         case 0x36: /* printf(\"gs: MIPTBP2_1 <- %016lx\\n\", data); */ gs->context[0].miptbp2 = data; gs_unpack_miptbp2(gs, 0); return;\n//         case 0x37: /* printf(\"gs: MIPTBP2_2 <- %016lx\\n\", data); */ gs->context[1].miptbp2 = data; gs_unpack_miptbp2(gs, 1); return;\n//         case 0x3B: /* printf(\"gs: TEXA <- %016lx\\n\", data); */ gs->texa = data; gs_unpack_texa(gs); return;\n//         case 0x3D: /* printf(\"gs: FOGCOL <- %016lx\\n\", data); */ gs->fogcol = data; return;\n//         case 0x3F: /* printf(\"gs: TEXFLUSH <- %016lx\\n\", data); */ gs->texflush = data; return;\n//         case 0x40: /* printf(\"gs: SCISSOR_1 <- %016lx\\n\", data); */ gs->context[0].scissor = data; gs_unpack_scissor(gs, 0); gs_invoke_event_handler(gs, GS_EVENT_SCISSOR); return;\n//         case 0x41: /* printf(\"gs: SCISSOR_2 <- %016lx\\n\", data); */ gs->context[1].scissor = data; gs_unpack_scissor(gs, 1); gs_invoke_event_handler(gs, GS_EVENT_SCISSOR); return;\n//         case 0x42: /* printf(\"gs: ALPHA_1 <- %016lx\\n\", data); */ gs->context[0].alpha = data; gs_unpack_alpha(gs, 0); return;\n//         case 0x43: /* printf(\"gs: ALPHA_2 <- %016lx\\n\", data); */ gs->context[1].alpha = data; gs_unpack_alpha(gs, 1); return;\n//         case 0x44: /* printf(\"gs: DIMX <- %016lx\\n\", data); */ gs->dimx = data; gs_unpack_dimx(gs); return;\n//         case 0x45: /* printf(\"gs: DTHE <- %016lx\\n\", data); */ gs->dthe = data; return;\n//         case 0x46: /* printf(\"gs: COLCLAMP <- %016lx\\n\", data); */ gs->colclamp = data; return;\n//         case 0x47: /* printf(\"gs: TEST_1 <- %016lx\\n\", data); */ gs->context[0].test = data; gs_unpack_test(gs, 0); return;\n//         case 0x48: /* printf(\"gs: TEST_2 <- %016lx\\n\", data); */ gs->context[1].test = data; gs_unpack_test(gs, 1); return;\n//         case 0x49: /* printf(\"gs: PABE <- %016lx\\n\", data); */ gs->pabe = data; return;\n//         case 0x4A: /* printf(\"gs: FBA_1 <- %016lx\\n\", data); */ gs->context[0].fba = data; return;\n//         case 0x4B: /* printf(\"gs: FBA_2 <- %016lx\\n\", data); */ gs->context[1].fba = data; return;\n//         case 0x4C: /* printf(\"gs: FRAME_1 <- %016lx\\n\", data); */ gs->context[0].frame = data; gs_unpack_frame(gs, 0); return;\n//         case 0x4D: /* printf(\"gs: FRAME_2 <- %016lx\\n\", data); */ gs->context[1].frame = data; gs_unpack_frame(gs, 1); return;\n//         case 0x4E: /* printf(\"gs: ZBUF_1 <- %016lx\\n\", data); */ gs->context[0].zbuf = data; gs_unpack_zbuf(gs, 0); return;\n//         case 0x4F: /* printf(\"gs: ZBUF_2 <- %016lx\\n\", data); */ gs->context[1].zbuf = data; gs_unpack_zbuf(gs, 1); return;\n//         case 0x50: /* printf(\"gs: BITBLTBUF <- %016lx\\n\", data); */ gs->bitbltbuf = data; return;\n//         case 0x51: /* printf(\"gs: TRXPOS <- %016lx\\n\", data); */ gs->trxpos = data; return;\n//         case 0x52: /* printf(\"gs: TRXREG <- %016lx\\n\", data); */ gs->trxreg = data; return;\n//         case 0x53: /* printf(\"gs: TRXDIR <- %016lx\\n\", data); */ gs->trxdir = data; gs->backend.transfer_start(gs, gs->backend.udata); return;\n//         case 0x54: gs->hwreg = data; gs->backend.transfer_write(gs, gs->backend.udata); return;\n//         case 0x60: /* printf(\"gs: SIGNAL <- %016lx\\n\", data); */ {\n//             uint64_t mask = data >> 32;\n//             uint64_t value = data & mask;\n\n//             if (gs->csr & 1) {\n//                 gs->signal_pending++;\n\n//                 gs->stall_sigid = gs->siglblid & 0xffffffff;\n//                 gs->stall_sigid &= ~mask;\n//                 gs->stall_sigid |= value;\n\n//                 return;\n//             }\n\n//             gs->signal_pending++;\n//             gs->signal = data;\n\n//             gs->csr |= 1;\n//             gs->siglblid &= ~mask;\n//             gs->siglblid |= value;\n\n//             gs_test_gs_irq(gs);\n//         } return;\n//         case 0x61: /* printf(\"gs: FINISH <- %016lx\\n\", data); */ {\n//             // Trigger FINISH event\n//             gs->csr |= 2;\n\n//             gs_test_gs_irq(gs);\n//         } return;\n//         case 0x62: /* printf(\"gs: LABEL <- %016lx\\n\", data); */ {\n//             gs->label = data;\n\n//             uint64_t mask = data >> 32;\n\n//             gs->siglblid &= (~mask) << 32;\n//             gs->siglblid |= (data & mask) << 32;\n//         } break;\n//         default: {\n//             // printf(\"gs: Invalid privileged register %02x write %016lx\\n\", reg, data);\n\n//             return;\n//         }\n//     }\n// }\n\n// uint64_t ps2_gs_read_internal(struct ps2_gs* gs, int reg) {\n//     switch (reg) {\n//         case 0x00: return gs->prim;\n//         case 0x01: return gs->rgbaq;\n//         case 0x02: return gs->st;\n//         case 0x03: return gs->uv;\n//         case 0x04: return gs->xyzf2;\n//         case 0x05: return gs->xyz2;\n//         case 0x06: return gs->context[0].tex0;\n//         case 0x07: return gs->context[1].tex0;\n//         case 0x08: return gs->context[0].clamp;\n//         case 0x09: return gs->context[1].clamp;\n//         case 0x0A: return gs->fog;\n//         case 0x0C: return gs->xyzf3;\n//         case 0x0D: return gs->xyz3;\n//         case 0x14: return gs->context[0].tex1;\n//         case 0x15: return gs->context[1].tex1;\n//         case 0x16: return gs->context[0].tex2;\n//         case 0x17: return gs->context[1].tex2;\n//         case 0x18: return gs->context[0].xyoffset;\n//         case 0x19: return gs->context[1].xyoffset;\n//         case 0x1A: return gs->prmodecont;\n//         case 0x1B: return gs->prmode;\n//         case 0x1C: return gs->texclut;\n//         case 0x22: return gs->scanmsk;\n//         case 0x34: return gs->context[0].miptbp1;\n//         case 0x35: return gs->context[1].miptbp1;\n//         case 0x36: return gs->context[0].miptbp2;\n//         case 0x37: return gs->context[1].miptbp2;\n//         case 0x3B: return gs->texa;\n//         case 0x3D: return gs->fogcol;\n//         case 0x3F: return gs->texflush;\n//         case 0x40: return gs->context[0].scissor;\n//         case 0x41: return gs->context[1].scissor;\n//         case 0x42: return gs->context[0].alpha;\n//         case 0x43: return gs->context[1].alpha;\n//         case 0x44: return gs->dimx;\n//         case 0x45: return gs->dthe;\n//         case 0x46: return gs->colclamp;\n//         case 0x47: return gs->context[0].test;\n//         case 0x48: return gs->context[1].test;\n//         case 0x49: return gs->pabe;\n//         case 0x4A: return gs->context[0].fba;\n//         case 0x4B: return gs->context[1].fba;\n//         case 0x4C: return gs->context[0].frame;\n//         case 0x4D: return gs->context[1].frame;\n//         case 0x4E: return gs->context[0].zbuf;\n//         case 0x4F: return gs->context[1].zbuf;\n//         case 0x50: return gs->bitbltbuf;\n//         case 0x51: return gs->trxpos;\n//         case 0x52: return gs->trxreg;\n//         case 0x53: return gs->trxdir;\n//         case 0x54: gs->backend.transfer_read(gs, gs->backend.udata); return gs->hwreg;\n//         case 0x60: return gs->signal;\n//         case 0x61: return gs->finish;\n//         case 0x62: return gs->label;\n//         default: {\n//             fprintf(stderr, \"gs: Invalid privileged register %02x read\\n\", reg);\n\n//             exit(1);\n//         }\n//     }\n\n//     return 0;\n// }\n\nvoid gs_get_privileged_state(struct ps2_gs* gs, struct gs_privileged_state* state) {\n    state->pmode = gs->pmode;\n    state->smode1 = gs->smode1;\n    state->smode2 = gs->smode2;\n    state->srfsh = gs->srfsh;\n    state->synch1 = gs->synch1;\n    state->synch2 = gs->synch2;\n    state->syncv = gs->syncv;\n    state->dispfb1 = gs->dispfb1;\n    state->display1 = gs->display1;\n    state->dispfb2 = gs->dispfb2;\n    state->display2 = gs->display2;\n    state->extbuf = gs->extbuf;\n    state->extdata = gs->extdata;\n    state->extwrite = gs->extwrite;\n    state->bgcolor = gs->bgcolor;\n    state->csr = gs->csr;\n    state->imr = gs->imr;\n    state->busdir = gs->busdir;\n    state->siglblid = gs->siglblid;\n}\n\nint ps2_gs_is_vblank(struct ps2_gs* gs) {\n    return gs->vblank;\n}\n\nint ps2_gs_write_signal(struct ps2_gs* gs, uint64_t data) {\n    uint64_t mask = data >> 32;\n    uint64_t value = data & mask;\n\n    if (gs->csr & 1) {\n        gs->signal_pending++;\n\n        gs->stall_sigid = gs->siglblid & 0xffffffff;\n        gs->stall_sigid &= ~mask;\n        gs->stall_sigid |= value;\n\n        return 1;\n    }\n\n    gs->signal_pending++;\n    gs->signal = data;\n\n    gs->csr |= 1;\n    gs->siglblid &= ~mask;\n    gs->siglblid |= value;\n\n    gs_test_gs_irq(gs);\n\n    return 0;\n}\n\nint ps2_gs_write_finish(struct ps2_gs* gs, uint64_t data) {\n    // Trigger FINISH event\n    gs->csr |= 2;\n\n    gs_test_gs_irq(gs);\n\n    return 1;\n}\n\nint ps2_gs_write_label(struct ps2_gs* gs, uint64_t data) {\n    gs->label = data;\n\n    uint64_t mask = data >> 32;\n\n    gs->siglblid &= (~mask) << 32;\n    gs->siglblid |= (data & mask) << 32;\n\n    return 0;\n}"
  },
  {
    "path": "src/gs/gs.h",
    "content": "#ifndef GS_H\n#define GS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"u128.h\"\n#include \"scheduler.h\"\n#include \"ee/timers.h\"\n#include \"iop/timers.h\"\n\n#define GS_PRIM       0x00\n#define GS_RGBAQ      0x01\n#define GS_ST         0x02\n#define GS_UV         0x03\n#define GS_XYZF2      0x04\n#define GS_XYZ2       0x05\n#define GS_TEX0_1     0x06\n#define GS_TEX0_2     0x07\n#define GS_CLAMP_1    0x08\n#define GS_CLAMP_2    0x09\n#define GS_FOG        0x0A\n#define GS_XYZF3      0x0C\n#define GS_XYZ3       0x0D\n#define GS_TEX1_1     0x14\n#define GS_TEX1_2     0x15\n#define GS_TEX2_1     0x16\n#define GS_TEX2_2     0x17\n#define GS_XYOFFSET_1 0x18\n#define GS_XYOFFSET_2 0x19\n#define GS_PRMODECONT 0x1A\n#define GS_PRMODE     0x1B\n#define GS_TEXCLUT    0x1C\n#define GS_SCANMSK    0x22\n#define GS_MIPTBP1_1  0x34\n#define GS_MIPTBP1_2  0x35\n#define GS_MIPTBP2_1  0x36\n#define GS_MIPTBP2_2  0x37\n#define GS_TEXA       0x3B\n#define GS_FOGCOL     0x3D\n#define GS_TEXFLUSH   0x3F\n#define GS_SCISSOR_1  0x40\n#define GS_SCISSOR_2  0x41\n#define GS_ALPHA_1    0x42\n#define GS_ALPHA_2    0x43\n#define GS_DIMX       0x44\n#define GS_DTHE       0x45\n#define GS_COLCLAMP   0x46\n#define GS_TEST_1     0x47\n#define GS_TEST_2     0x48\n#define GS_PABE       0x49\n#define GS_FBA_1      0x4A\n#define GS_FBA_2      0x4B\n#define GS_FRAME_1    0x4C\n#define GS_FRAME_2    0x4D\n#define GS_ZBUF_1     0x4E\n#define GS_ZBUF_2     0x4F\n#define GS_BITBLTBUF  0x50\n#define GS_TRXPOS     0x51\n#define GS_TRXREG     0x52\n#define GS_TRXDIR     0x53\n#define GS_HWREG      0x54\n#define GS_SIGNAL     0x60\n#define GS_FINISH     0x61\n#define GS_LABEL      0x62\n\n// TCC\n#define GS_RGB 0\n#define GS_RGBA 1\n\n#define GS_IIP (1 << 3) // int0:1:0 Shading Method\n#define GS_TME (1 << 4) // int0:1:0 Texture Mapping\n#define GS_FGE (1 << 5) // int0:1:0 Fogging\n#define GS_ABE (1 << 6) // int0:1:0 Alpha Blending\n#define GS_AA1 (1 << 7) // int0:1:0 1 Pass Antialiasing (*1)\n#define GS_FST (1 << 8) // int0:1:0 Method of Specifying Texture Coordinates (*2)\n#define GS_CTXT (1 << 9) // int0:1:0 Context\n#define GS_FIX (1 << 10) // int0:1:0 Fragment Value Control (RGBAFSTQ Change by DDA)\n\n// Framebuffer/Pixel formats\n#define GS_PSMCT32 0x00\n#define GS_PSMCT24 0x01\n#define GS_PSMCT16 0x02\n#define GS_PSMCT16S 0x0a\n#define GS_PSMZ32 0x30\n#define GS_PSMZ24 0x31\n#define GS_PSMZ16 0x32\n#define GS_PSMZ16S 0x3a\n#define GS_PSMT8 0x13\n#define GS_PSMT4 0x14\n#define GS_PSMT8H 0x1b\n#define GS_PSMT4HL 0x24\n#define GS_PSMT4HH 0x2c\n\n// Z buffer formats\n#define GS_ZSMZ32 0x00\n#define GS_ZSMZ24 0x01\n#define GS_ZSMZ16 0x02\n#define GS_ZSMZ16S 0x0a\n\n// Texture function\n#define GS_MODULATE 0\n#define GS_DECAL 1\n#define GS_HIGHLIGHT 2\n#define GS_HIGHLIGHT2 3\n\n// Timings\n#define GS_FRAME_SCANS_NTSC 240\n#define GS_VBLANK_SCANS_NTSC 22\n#define GS_SCANLINE_NTSC 9370\n#define GS_FRAME_SCANS_PAL 286\n#define GS_VBLANK_SCANS_PAL 26\n#define GS_SCANLINE_PAL 9476\n\n// EE clock: 294.912 MHz, 294912000 clocks/s\n// 294912000/60=4915200 clocks/frame\n\n// #define GS_FRAME_NTSC (4497600) // (240 * 9370)\n// #define GS_VBLANK_NTSC (417600) // (22 * 9370)\n#define GS_FRAME_NTSC 4489019 // (240 * 9370)\n#define GS_VBLANK_NTSC 431096 // (22 * 9370)\n#define GS_FRAME_PAL (286 * 9476)\n#define GS_VBLANK_PAL (26 * 9476)\n\nstruct ps2_gs;\n\nstruct gs_vertex {\n    uint64_t rgbaq;\n    uint64_t xyz;\n    uint64_t st;\n    uint64_t uv;\n    uint64_t fog;\n\n    // Cached fields\n    uint32_t r;\n    uint32_t g;\n    uint32_t b;\n    uint32_t a;\n    int32_t x;\n    int32_t y;\n    uint32_t z;\n    uint32_t u;\n    uint32_t v;\n    float s;\n    float t;\n    float q;\n};\n\nstruct gs_callback {\n    void (*func)(void*);\n    void* udata;\n};\n\nstruct gs_context {\n    uint64_t frame; // (FRAME_1, FRAME_2)\n    uint64_t zbuf; // (ZBUF_1, ZBUF_2)\n    uint64_t tex0; // (TEX0_1, TEX0_2)\n    uint64_t tex1; // (TEX1_1, TEX1_2)\n    uint64_t tex2; // (TEX2_1, TEX2_2)\n    uint64_t miptbp1; // (MIPTBP1_1, MIPTBP1_2)\n    uint64_t miptbp2; // (MIPTBP2_1, MIPTBP2_2)\n    uint64_t clamp; // (CLAMP_1, CLAMP_2)\n    uint64_t test; // (TEST_1, TEST_2)\n    uint64_t alpha; // (ALPHA_1, ALPHA_2)\n    uint64_t xyoffset; // (XYOFFSET_1, XYOFFSET_2)\n    uint64_t scissor; // (SCISSOR_1, SCISSOR_2)\n    uint64_t fba; // (FBA_1, FBA_2)\n\n    // Cached fields\n    // FRAME\n    uint32_t fbp;\n    uint32_t fbw;\n    uint32_t fbpsm;\n    uint32_t fbmsk;\n\n    // ZBUF\n    uint32_t zbp;\n    uint32_t zbpsm;\n    uint32_t zbmsk;\n\n    // TEX0\n    uint32_t tbp0;\n    uint32_t tbw;\n    uint32_t tbpsm;\n    uint32_t usize;\n    uint32_t vsize;\n    uint32_t tcc;\n    uint32_t tfx;\n    uint32_t cbp;\n    uint32_t cbpsm;\n    uint32_t csm;\n    uint32_t csa;\n    uint32_t cld;\n\n    // TEX1\n    uint32_t lcm;\n    uint32_t mxl;\n    uint32_t mmag;\n    uint32_t mmin;\n    uint32_t mtba;\n    uint32_t l;\n    uint32_t k;\n\n    // MIPTBP1/2\n    uint32_t mmtbp[6];\n    uint32_t mmtbw[6];\n\n    // CLAMP\n    uint32_t wms;\n    uint32_t wmt;\n    uint32_t minu;\n    uint32_t maxu;\n    uint32_t minv;\n    uint32_t maxv;\n\n    // TEST\n    uint32_t ate;\n    uint32_t atst;\n    uint32_t aref;\n    uint32_t afail;\n    uint32_t date;\n    uint32_t datm;\n    uint32_t zte;\n    uint32_t ztst;\n\n    // ALPHA\n    uint32_t a;\n    uint32_t b;\n    uint32_t c;\n    uint32_t d;\n    uint32_t fix;\n\n    // XYOFFSET\n    uint32_t ofx;\n    uint32_t ofy;\n\n    // SCISSOR\n    uint32_t scax0;\n    uint32_t scax1;\n    uint32_t scay0;\n    uint32_t scay1;\n};\n\n#define GS_EVENT_VBLANK 0\n#define GS_EVENT_SCISSOR 1\n\nstruct ps2_gs {\n    uint32_t* vram;\n\n    int vblank;\n\n    // SIGNAL stuff\n    int signal_pending;\n    int signal_stall;\n    uint32_t stall_sigid;\n\n    // 1KB CLUT cache\n    uint32_t clut_cache[0x100];\n    uint32_t cbp0;\n    uint32_t cbp1;\n\n    uint32_t attr;\n    struct gs_context context[2];\n    struct gs_context* ctx;\n\n    // Privileged registers\n    uint64_t pmode;\n    uint64_t smode1;\n    uint64_t smode2;\n    uint64_t srfsh;\n    uint64_t synch1;\n    uint64_t synch2;\n    uint64_t syncv;\n    uint64_t dispfb1;\n    uint64_t display1;\n    uint64_t dispfb2;\n    uint64_t display2;\n    uint64_t extbuf;\n    uint64_t extdata;\n    uint64_t extwrite;\n    uint64_t bgcolor;\n    uint64_t csr;\n    uint64_t imr;\n    uint64_t busdir;\n    uint64_t siglblid;\n    uint64_t csr_enable;\n\n    // Internal registers\n    uint64_t prim;\n    uint64_t rgbaq;\n    uint64_t st;\n    uint64_t uv;\n    uint64_t xyzf2;\n    uint64_t xyz2;\n    uint64_t fog;\n    uint64_t xyzf3;\n    uint64_t xyz3;\n    uint64_t prmodecont;\n    uint64_t prmode;\n    uint64_t texclut;\n    uint64_t scanmsk;\n    uint64_t texa;\n    uint64_t fogcol;\n    uint64_t texflush;\n    uint64_t dimx;\n    uint64_t dthe;\n    uint64_t colclamp;\n    uint64_t pabe;\n    uint64_t bitbltbuf;\n    uint64_t trxpos;\n    uint64_t trxreg;\n    uint64_t trxdir;\n    uint64_t hwreg;\n    uint64_t signal;\n    uint64_t finish;\n    uint64_t label;\n\n    // Drawing data\n    struct gs_vertex vq[4];\n    unsigned int vqi;\n\n    // Cached fields\n    int iip;\n    int tme;\n    int fge;\n    int abe;\n    int aa1;\n    int fst;\n    int ctxt;\n    int fix;\n\n    // TEXCLUT\n    uint32_t cbw;\n    uint32_t cou;\n    uint32_t cov;\n\n    // TEXA\n    int aem;\n    uint32_t ta0;\n    uint32_t ta1;\n\n    // DISPFB1/2\n    uint32_t dfbp1;\n    uint32_t dfbw1;\n    uint32_t dfbpsm1;\n    uint32_t dfbp2;\n    uint32_t dfbw2;\n    uint32_t dfbpsm2;\n\n    // DIMX\n    int dither[4][4];\n\n    struct sched_state* sched;\n    struct ps2_intc* ee_intc;\n    struct ps2_iop_intc* iop_intc;\n    struct ps2_ee_timers* ee_timers;\n    struct ps2_iop_timers* iop_timers;\n};\n\nstruct ps2_gs* ps2_gs_create(void);\nvoid ps2_gs_init(struct ps2_gs* gs, struct ps2_intc* ee_intc, struct ps2_iop_intc* iop_intc, struct ps2_ee_timers* ee_timers, struct ps2_iop_timers* iop_timers, struct sched_state* sched);\nvoid ps2_gs_reset(struct ps2_gs* gs);\nvoid ps2_gs_destroy(struct ps2_gs* gs);\nuint64_t ps2_gs_read64(struct ps2_gs* gs, uint32_t addr);\nvoid ps2_gs_write64(struct ps2_gs* gs, uint32_t addr, uint64_t data);\nint ps2_gs_is_vblank(struct ps2_gs* gs);\n\nstruct gs_privileged_state {\n    uint64_t pmode;\n    uint64_t smode1;\n    uint64_t smode2;\n    uint64_t srfsh;\n    uint64_t synch1;\n    uint64_t synch2;\n    uint64_t syncv;\n    uint64_t dispfb1;\n    uint64_t display1;\n    uint64_t dispfb2;\n    uint64_t display2;\n    uint64_t extbuf;\n    uint64_t extdata;\n    uint64_t extwrite;\n    uint64_t bgcolor;\n    uint64_t csr;\n    uint64_t imr;\n    uint64_t busdir;\n    uint64_t siglblid;\n};\n\nvoid gs_get_privileged_state(struct ps2_gs* gs, struct gs_privileged_state* state);\n\nint ps2_gs_write_signal(struct ps2_gs* gs, uint64_t data);\nint ps2_gs_write_finish(struct ps2_gs* gs, uint64_t data);\nint ps2_gs_write_label(struct ps2_gs* gs, uint64_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/gs/renderer/config.hpp",
    "content": "#pragma once\n\nstruct hardware_config {\n    int super_sampling = 0;\n    bool force_progressive = false;\n    bool overscan = false;\n    bool crtc_offsets = false;\n    bool disable_mipmaps = false;\n    bool unsynced_readbacks = false;\n    bool backbuffer_promotion = false;\n    bool allow_blend_demote = false;\n};"
  },
  {
    "path": "src/gs/renderer/hardware.cpp",
    "content": "#include \"hardware.hpp\"\n\nvoid* hardware_create() {\n    return new hardware_state();\n}\n\nbool hardware_init(void* udata, const renderer_create_info& info) {\n    hardware_state* ctx = static_cast<hardware_state*>(udata);\n\n    if (!Context::init_loader(nullptr))\n        return false;\n\n    ctx->gs = info.gs;\n    ctx->gif = info.gif;\n\n    ctx->instance = new ExternallyManagedInstance(info.instance, info.instance_create_info);\n    ctx->device = new ExternallyManagedDevice(info.device, info.device_create_info);\n    ctx->signal_handler = new RendererSignalHandler(ctx->gs);\n\n    ctx->granite_ctx.set_instance_factory(ctx->instance);\n    ctx->granite_ctx.set_device_factory(ctx->device);\n    ctx->granite_ctx.set_num_thread_indices(1);\n\n    // We don't need to pass any extensions or layers because the\n    // VkInstance is managed/created externally.\n    if (!ctx->granite_ctx.init_instance(nullptr, 0)) {\n        fprintf(stderr, \"renderer: Failed to initialize Granite instance\\n\");\n\n        return false;\n    }\n\n    if (!ctx->granite_ctx.init_device(info.physical_device, VK_NULL_HANDLE, nullptr, 0)) {\n        fprintf(stderr, \"renderer: Failed to initialize Granite device\\n\");\n\n        return false;\n    }\n\n    ctx->granite_device.set_context(ctx->granite_ctx);\n    ctx->granite_device.init_frame_contexts(4);\n\n    GSOptions opts = {};\n    opts.super_sampling = (SuperSampling)ctx->config.super_sampling;\n    opts.ordered_super_sampling = true;\n    opts.super_sampled_textures = true;\n    opts.dynamic_super_sampling = true;\n\n    if (!ctx->interface.init(&ctx->granite_device, opts)) {\n        return false;\n    }\n\n    ctx->interface.reset_context_state();\n    ctx->interface.set_signal_interface(ctx->signal_handler);\n\n    ctx->config = *(hardware_config*)info.config;\n\n    Hacks hacks = {};\n    hacks.allow_blend_demote = ctx->config.allow_blend_demote;\n    hacks.backbuffer_promotion = ctx->config.backbuffer_promotion;\n    hacks.disable_mipmaps = ctx->config.disable_mipmaps;\n    hacks.unsynced_readbacks = ctx->config.unsynced_readbacks;\n\n    ctx->interface.set_hacks(hacks);\n\n    if (ctx->config.super_sampling == 0) {\n        ctx->interface.set_super_sampling_rate((SuperSampling)0, false, false);\n    } else {\n        SuperSampling super_sampling;\n\n        switch (ctx->config.super_sampling) {\n            case 1: super_sampling = SuperSampling::X2; break;\n            case 2: super_sampling = SuperSampling::X4; break;\n            case 3: super_sampling = SuperSampling::X8; break;\n            case 4: super_sampling = SuperSampling::X16; break;\n            default: super_sampling = (SuperSampling)0; break;\n        }\n\n        ctx->interface.set_super_sampling_rate(super_sampling, true, true);\n    }\n\n    return true;\n}\n\nvoid hardware_reset(void* udata) {\n    hardware_state* ctx = static_cast<hardware_state*>(udata);\n\n    // ctx->interface.flush();\n    ctx->interface.reset_context_state();\n\n    // Clear VRAM\n    void* ptr = ctx->interface.map_vram_write(0, 0x400000);\n\n    memset(ptr, 0, 0x400000);\n\n    ctx->interface.end_vram_write(0, 0x400000);\n}\n\nvoid hardware_destroy(void* udata) {\n    hardware_state* ctx = static_cast<hardware_state*>(udata);\n\n    delete ctx->instance;\n    delete ctx->device;\n    delete ctx->signal_handler;\n\n    delete ctx;\n}\n\nrenderer_image hardware_get_frame(void* udata) {\n    hardware_state* ctx = static_cast<hardware_state*>(udata);\n\n    struct gs_privileged_state state;\n\n    gs_get_privileged_state(ctx->gs, &state);\n\n    if (!state.pmode) {\n        // No display enabled.\n        renderer_image image = {};\n        image.image = VK_NULL_HANDLE;\n        image.view = VK_NULL_HANDLE;\n\n        return image;\n    }\n\n    auto& priv = ctx->interface.get_priv_register_state();\n\n    *((uint64_t*)&priv.pmode) = state.pmode;\n    *((uint64_t*)&priv.smode1) = state.smode1;\n    *((uint64_t*)&priv.smode2) = state.smode2;\n    *((uint64_t*)&priv.srfsh) = state.srfsh;\n    *((uint64_t*)&priv.synch1) = state.synch1;\n    *((uint64_t*)&priv.synch2) = state.synch2;\n    *((uint64_t*)&priv.syncv) = state.syncv;\n    *((uint64_t*)&priv.dispfb1) = state.dispfb1;\n    *((uint64_t*)&priv.display1) = state.display1;\n    *((uint64_t*)&priv.dispfb2) = state.dispfb2;\n    *((uint64_t*)&priv.display2) = state.display2;\n    *((uint64_t*)&priv.extbuf) = state.extbuf;\n    *((uint64_t*)&priv.extdata) = state.extdata;\n    *((uint64_t*)&priv.extwrite) = state.extwrite;\n    *((uint64_t*)&priv.bgcolor) = state.bgcolor;\n    *((uint64_t*)&priv.csr) = state.csr;\n    *((uint64_t*)&priv.imr) = state.imr;\n    *((uint64_t*)&priv.busdir) = state.busdir;\n    *((uint64_t*)&priv.siglblid) = state.siglblid;\n\n    ctx->interface.flush();\n\n    VSyncInfo info = {};\n\n    info.phase = ctx->gs->csr & (1 << 13) ? 0 : 1;\n\n    if (ctx->config.super_sampling) {\n        info.raw_circuit_scanout = true;\n        info.high_resolution_scanout = true;\n        info.anti_blur = true;\n    } else {\n        info.raw_circuit_scanout = false;\n        info.high_resolution_scanout = false;\n        info.anti_blur = false;\n    }\n\n    info.force_progressive = ctx->config.force_progressive;\n    info.overscan = ctx->config.overscan;\n    info.crtc_offsets = ctx->config.crtc_offsets;\n    info.dst_access = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT;\n    info.dst_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT;\n    info.dst_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL;\n    info.adapt_to_internal_horizontal_resolution = false;\n\n    ScanoutResult scanout = ctx->interface.vsync(info);\n\n    Image* granite_image = scanout.image.get();\n\n    renderer_image image;\n\n    image.image = granite_image->get_image();\n    image.width = granite_image->get_width();\n    image.height = granite_image->get_height();\n    image.format = granite_image->get_format();\n    image.view = granite_image->get_view().get_view().view;\n\n    return image;\n}\n\nextern \"C\" void hardware_transfer(void* udata, int path, const void* data, size_t size) {\n    hardware_state* ctx = static_cast<hardware_state*>(udata);\n\n    ctx->interface.gif_transfer(path, data, size);\n}\n\nvoid hardware_set_config(void* udata, void* config) {\n    hardware_state* ctx = (hardware_state*)udata;\n\n    ctx->config = *(hardware_config*)config;\n\n    Hacks hacks = {};\n    hacks.allow_blend_demote = ctx->config.allow_blend_demote;\n    hacks.backbuffer_promotion = ctx->config.backbuffer_promotion;\n    hacks.disable_mipmaps = ctx->config.disable_mipmaps;\n    hacks.unsynced_readbacks = ctx->config.unsynced_readbacks;\n\n    ctx->interface.set_hacks(hacks);\n\n    if (ctx->config.super_sampling == 0) {\n        ctx->interface.set_super_sampling_rate((SuperSampling)0, false, false);\n    } else {\n        SuperSampling super_sampling;\n\n        switch (ctx->config.super_sampling) {\n            case 1: super_sampling = SuperSampling::X2; break;\n            case 2: super_sampling = SuperSampling::X4; break;\n            case 3: super_sampling = SuperSampling::X8; break;\n            case 4: super_sampling = SuperSampling::X16; break;\n            default: super_sampling = (SuperSampling)0; break;\n        }\n\n        ctx->interface.set_super_sampling_rate(super_sampling, true, true);\n    }\n}"
  },
  {
    "path": "src/gs/renderer/hardware.hpp",
    "content": "#pragma once\n\n#include <vector>\n#include <cstdio>\n\n#include <SDL3/SDL.h>\n\n#include \"renderer.hpp\"\n\n#include \"Granite/vulkan/device.hpp\"\n#include \"Granite/vulkan/context.hpp\"\n#include \"gs_renderer.hpp\"\n#include \"gs_interface.hpp\"\n\nusing namespace Vulkan;\nusing namespace ParallelGS;\n\nclass ExternallyManagedDevice : public DeviceFactory {\n    VkDevice m_device = nullptr;\n    VkDeviceCreateInfo m_create_info;\n\npublic:\n    // Device is externally managed\n    ExternallyManagedDevice(VkDevice device, VkDeviceCreateInfo create_info) :\n        m_device(device),\n        m_create_info(create_info) {}; \n\n    // Create a device ourselves (unused)\n    ExternallyManagedDevice() = delete; \n\n    virtual ~ExternallyManagedDevice() override = default;\n\n    VkDevice create_device(VkPhysicalDevice gpu, const VkDeviceCreateInfo *info) override {\n        return m_device;\n    }\n\n    const VkDeviceCreateInfo *get_existing_create_info() override {\n        return &m_create_info;\n    }\n\n    bool factory_owns_created_device() override {\n        return true;\n    }\n};\n\nclass ExternallyManagedInstance : public InstanceFactory {\n    VkInstanceCreateInfo m_create_info = {};\n    VkInstance m_instance = nullptr;\n\npublic:\n    // Instance is externally managed\n    ExternallyManagedInstance(VkInstance instance, VkInstanceCreateInfo create_info) :\n        m_instance(instance),\n        m_create_info(create_info) {}; \n\n    // Create an instance ourselves (unused)\n    ExternallyManagedInstance() = delete; \n\n    ~ExternallyManagedInstance() override = default;\n\n    VkInstance create_instance(const VkInstanceCreateInfo *info) override {\n        if (m_instance) return m_instance;\n\n        // Create a new instance, I won't implement this\n        return m_instance;\n    }\n\n    const VkInstanceCreateInfo* get_existing_create_info() override {\n        return &m_create_info;\n    }\n\n    bool factory_owns_created_instance() override {\n        return true;\n    }\n};\n\nclass RendererSignalHandler : public SignalInterface {\n    struct ps2_gs* m_gs;\n\npublic:\n    virtual ~RendererSignalHandler() override = default;\n    RendererSignalHandler(struct ps2_gs* gs) : m_gs(gs) {}\n\n    virtual bool on_signal(uint64_t payload) override {\n        return ps2_gs_write_signal(m_gs, payload);\n    }\n\n    virtual bool on_finish(uint64_t payload) override {\n        return ps2_gs_write_finish(m_gs, payload);\n    }\n\n    virtual bool on_label(uint64_t payload) override {\n        return ps2_gs_write_label(m_gs, payload);;\n    }\n};\n\nstruct hardware_state {\n    Vulkan::Context granite_ctx;\n    Vulkan::Device granite_device;\n    GSInterface interface;\n    ExternallyManagedDevice* device;\n    ExternallyManagedInstance* instance;\n    RendererSignalHandler* signal_handler;\n    hardware_config config;\n\n    struct ps2_gs* gs;\n    struct ps2_gif* gif;\n};\n\nvoid* hardware_create();\nbool hardware_init(void* udata, const renderer_create_info& info);\nvoid hardware_reset(void* udata);\nvoid hardware_destroy(void* udata);\nvoid hardware_set_config(void* udata, void* config);\nrenderer_image hardware_get_frame(void* udata);\n\nextern \"C\" {\nvoid hardware_transfer(void* udata, int path, const void* data, size_t size);\n}"
  },
  {
    "path": "src/gs/renderer/null.cpp",
    "content": "#include \"renderer.hpp\"\n\n#include \"null.hpp\"\n\nvoid* null_create() {\n    return nullptr;\n}\n\nbool null_init(void* udata, const renderer_create_info& info) {\n    return true;\n}\n\nvoid null_reset(void* udata) {\n    // Nothing\n}\n\nvoid null_destroy(void* udata) {\n    // Nothing\n}\n\nrenderer_image null_get_frame(void* udata) {\n    renderer_image image = {};\n\n    image.image = VK_NULL_HANDLE;\n    image.view = VK_NULL_HANDLE;\n\n    return image;\n}\n\nvoid null_set_config(void* udata, void* config) {\n    // Nothing\n}\n\nextern \"C\" {\n\nvoid null_transfer(void* udata, int path, const void* data, size_t size) {\n    // Do nothing\n}\n\n}"
  },
  {
    "path": "src/gs/renderer/null.hpp",
    "content": "#pragma once\n\n#include <vector>\n#include <cstdio>\n\n#include <SDL3/SDL.h>\n\n#include \"renderer.hpp\"\n\nvoid* null_create();\nbool null_init(void* udata, const renderer_create_info& info);\nvoid null_reset(void* udata);\nvoid null_destroy(void* udata);\nvoid null_set_config(void* udata, void* config);\nrenderer_image null_get_frame(void* udata);\n\nextern \"C\" {\nvoid null_transfer(void* udata, int path, const void* data, size_t size);\n}"
  },
  {
    "path": "src/gs/renderer/renderer.cpp",
    "content": "#include \"renderer.hpp\"\n\n#include \"null.hpp\"\n#include \"hardware.hpp\"\n\nrenderer_state* renderer_create(void) {\n    return new renderer_state;\n}\n\nbool renderer_init(renderer_state* renderer, const renderer_create_info& info) {\n    renderer->info = info;\n\n    switch (info.backend) {\n        case RENDERER_BACKEND_NULL: {\n            renderer->create = null_create;\n            renderer->init = null_init;\n            renderer->reset = null_reset;\n            renderer->destroy = null_destroy;\n            renderer->get_frame = null_get_frame;\n            renderer->set_config = null_set_config;\n            renderer->transfer = null_transfer;\n        } break;\n\n        case RENDERER_BACKEND_SOFTWARE: {\n            // To-do: Software renderer\n            renderer->create = null_create;\n            renderer->init = null_init;\n            renderer->reset = null_reset;\n            renderer->destroy = null_destroy;\n            renderer->get_frame = null_get_frame;\n            renderer->set_config = null_set_config;\n            renderer->transfer = null_transfer;\n        } break;\n\n        case RENDERER_BACKEND_HARDWARE: {\n            renderer->create = hardware_create;\n            renderer->init = hardware_init;\n            renderer->reset = hardware_reset;\n            renderer->destroy = hardware_destroy;\n            renderer->get_frame = hardware_get_frame;\n            renderer->set_config = hardware_set_config;\n            renderer->transfer = hardware_transfer;\n        } break;\n    }\n\n    renderer->udata = renderer->create();\n\n    ps2_gif_set_backend(info.gif, renderer->udata, renderer->transfer);\n\n    return renderer->init(renderer->udata, info);\n}\n\nbool renderer_switch(renderer_state* renderer, int backend, void* config) {\n    if (backend == renderer->info.backend)\n        return true;\n\n    renderer->destroy(renderer->udata);\n\n    renderer_create_info info = renderer->info;\n    info.backend = backend;\n    info.config = config;\n\n    return renderer_init(renderer, info);\n}\n\nvoid renderer_destroy(renderer_state* renderer) {\n    renderer->destroy(renderer->udata);\n\n    delete renderer;\n}\n\nvoid renderer_reset(renderer_state* renderer) {\n    renderer->reset(renderer->udata);\n}\n\nrenderer_image renderer_get_frame(renderer_state* renderer) {\n    return renderer->get_frame(renderer->udata);\n}\n\nvoid renderer_set_config(renderer_state* renderer, void* config) {\n    renderer->set_config(renderer->udata, config);\n}"
  },
  {
    "path": "src/gs/renderer/renderer.hpp",
    "content": "#pragma once\n\n#include \"Granite/vulkan/vulkan_headers.hpp\"\n\n#include <volk.h>\n\n#include \"gs/gs.h\"\n\n#include \"config.hpp\"\n\n// enum : int {\n//     // Keeps aspect ratio by native resolution\n//     RENDERER_ASPECT_NATIVE,\n//     // Stretch to window (disregard aspect, disregard scale)\n//     RENDERER_ASPECT_STRETCH,\n//     // Stretch to window (keep aspect, disregard scale)\n//     RENDERER_ASPECT_STRETCH_KEEP,\n//     // Force 4:3\n//     RENDERER_ASPECT_4_3,\n//     // Force 16:9\n//     RENDERER_ASPECT_16_9,\n//     // Force 5:4 (PAL)\n//     RENDERER_ASPECT_5_4,\n//     // Use NVRAM settings (same as SOFTWARE_ASPECT_STRETCH_KEEP for now)\n//     RENDERER_ASPECT_AUTO\n// };\n\nenum : int {\n    RENDERER_BACKEND_NULL = 0,\n    RENDERER_BACKEND_SOFTWARE,\n    RENDERER_BACKEND_HARDWARE\n};\n\nstruct renderer_stats {\n    unsigned int primitives = 0;\n    unsigned int triangles = 0;\n    unsigned int lines = 0;\n    unsigned int points = 0;\n    unsigned int sprites = 0;\n    unsigned int texture_uploads = 0;\n    unsigned int texture_blits = 0;\n    unsigned int frames_rendered = 0;\n};\n/*\n    An Iris renderer consists of two APIs, a backend API that receives\n    GIF transfers from the emulation core, and a frontend API that serves\n    frames to our frontend\n\n    Backend API\n      - render_back_transfer()\n\n    Frontend API\n      - render_front_get_frame()\n*/\n\nstruct renderer_create_info {\n    struct ps2_gif* gif;\n    struct ps2_gs* gs;\n\n    VkInstance instance;\n    VkInstanceCreateInfo instance_create_info;\n    VkDevice device;\n    VkDeviceCreateInfo device_create_info;\n    VkPhysicalDevice physical_device;\n    void* config;\n\n    int backend;\n};\n\nstruct renderer_image {\n    VkImage image;\n    VkImageView view;\n    VkFormat format;\n\n    unsigned int width;\n    unsigned int height;\n};\n\nstruct renderer_state {\n    struct ps2_gif* gif = nullptr;\n    void* udata = nullptr;\n\n    renderer_create_info info = {};\n\n    void* (*create)();\n    bool (*init)(void* udata, const renderer_create_info& info);\n    void (*reset)(void* udata);\n    void (*destroy)(void* udata);\n    renderer_image (*get_frame)(void* udata);\n    void (*transfer)(void* udata, int path, const void* data, size_t size);\n    void (*set_config)(void* udata, void* config);\n};\n\nrenderer_state* renderer_create(void);\nbool renderer_init(renderer_state* renderer, const renderer_create_info& info);\nbool renderer_switch(renderer_state* renderer, int backend, void* config);\nvoid renderer_reset(renderer_state* renderer);\nvoid renderer_destroy(renderer_state* renderer);\nvoid renderer_set_config(renderer_state* renderer, void* config);\n\nrenderer_image renderer_get_frame(renderer_state* renderer);"
  },
  {
    "path": "src/gs/renderer/software_thread.cpp",
    "content": "#include <cstdlib>\n#include <cstring>\n#include <cstdio>\n#include <cmath>\n\n#include \"gs/gs.h\"\n#include \"software_thread.hpp\"\n\n// INCBIN stuff\n#define INCBIN_PREFIX g_\n#define INCBIN_STYLE INCBIN_STYLE_SNAKE\n\n#include \"incbin.h\"\n\n#ifdef __APPLE__\n#define SHADER_FORMAT SDL_GPU_SHADERFORMAT_MSL\n#define SHADER_ENTRYPOINT \"main0\"\nINCBIN(vertex_shader, \"../shaders/vertex.msl\");\nINCBIN(fragment_shader, \"../shaders/fragment.msl\");\nINCBIN_EXTERN(vertex_shader);\nINCBIN_EXTERN(fragment_shader);\n#else\n#define SHADER_FORMAT SDL_GPU_SHADERFORMAT_SPIRV\n#define SHADER_ENTRYPOINT \"main\"\nINCBIN(vertex_shader, \"../shaders/vertex.spv\");\nINCBIN(fragment_shader, \"../shaders/fragment.spv\");\nINCBIN_EXTERN(vertex_shader);\nINCBIN_EXTERN(fragment_shader);\n#endif\n\n#include <SDL3/SDL.h>\n\nint psmct32_block[] = {\n    0 , 1 , 4 , 5 , 16, 17, 20, 21,\n    2 , 3 , 6 , 7 , 18, 19, 22, 23,\n    8 , 9 , 12, 13, 24, 25, 28, 29,\n    10, 11, 14, 15, 26, 27, 30, 31\n};\n\nint psmct32_column[] = {\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15\n};\n\nstatic inline int psmct32_addr(int base, int width, int x, int y) {\n    // base expressed in blocks\n    // 1 page = 64x32 = 8 KiB = 2048 words\n    // 1 block = 8x8 = 256 B = 64 words\n    // 1 column = 8x2 = 64 B = 16 words\n\n    int page = (x >> 6) + ((y >> 5) * width);\n    int blkx = (x >> 3) & 7;\n    int blky = (y >> 3) & 3;\n    int blk = blkx + (blky * 8);\n    int col = (y >> 1) & 3;\n    int idx = (x & 7) + ((y & 1) * 8);\n\n    // Unswizzle block\n    blk = psmct32_block[blk];\n\n    // Unswizzle column\n    idx = psmct32_column[idx];\n\n    // printf(\"(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\\n\",\n    //     base, width, x, y,\n    //     page, blk, cl,\n    //     (page * 2048) + (base * 64) + (blk * 64) + cl\n    // );\n\n    return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx;\n}\n\nint psmz32_block[] = {\n    24, 25, 28, 29, 8 , 9 , 12, 13,\n    26, 27, 30, 31, 10, 11, 14, 15,\n    16, 17, 20, 21, 0 , 1 , 4 , 5 ,\n    18, 19, 22, 23, 2 , 3 , 6 , 7\n};\n\nstatic inline int psmz32_addr(int base, int width, int x, int y) {\n    // base expressed in blocks\n    // 1 page = 64x32 = 8 KiB = 2048 words\n    // 1 block = 8x8 = 256 B = 64 words\n    // 1 column = 8x2 = 64 B = 16 words\n\n    int page = (x >> 6) + ((y >> 5) * width);\n    int blkx = (x >> 3) & 7;\n    int blky = (y >> 3) & 3;\n    int blk = blkx + (blky * 8);\n    int col = (y >> 1) & 3;\n    int idx = (x & 7) + ((y & 1) * 8);\n\n    // Unswizzle block\n    blk = psmz32_block[blk];\n\n    // Unswizzle column\n    idx = psmct32_column[idx];\n\n    // printf(\"(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\\n\",\n    //     base, width, x, y,\n    //     page, blk, cl,\n    //     (page * 2048) + (base * 64) + (blk * 64) + cl\n    // );\n\n    return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx;\n}\n\nint psmct16_block[] = {\n    0 , 2 , 8 , 10,\n    1 , 3 , 9 , 11,\n    4 , 6 , 12, 14,\n    5 , 7 , 13, 15,\n    16, 18, 24, 26,\n    17, 19, 25, 27,\n    20, 22, 28, 30,\n    21, 23, 29, 31\n};\n\nint psmz16_block[] = {\n    24, 26, 16, 18,\n    25, 27, 17, 19,\n    28, 30, 20, 22,\n    29, 31, 21, 23,\n    8 , 10, 0 , 2 ,\n    9 , 11, 1 , 3 ,\n    12, 14, 4 , 6 ,\n    13, 15, 5 , 7\n};\n\nint psmct16_column[] = {\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15\n};\n\nint psmct16_shift[] = {\n    0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,\n    1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,\n    0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,\n    1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 \n};\n\nstatic inline int psmct16_addr(int base, int width, int x, int y) {\n    // page            block          column\n    // 64 x 64 pixels  16 x 8 pixels  16 x 2 pixels\n    // base expressed in blocks\n    // 1 page = 64x32 = 8 KiB = 2048 words\n    // 1 block = 8x8 = 256 B = 64 words\n    // 1 column = 8x2 = 64 B = 16 words\n\n    int page = (x >> 6) + ((y >> 6) * width);\n    int blkx = (x >> 4) & 3;\n    int blky = (y >> 3) & 7;\n    int blk = blkx + (blky * 4);\n    int col = (y >> 1) & 3;\n    int idx = (x & 15) + ((y & 1) * 16);\n\n    // Unswizzle block\n    blk = psmct16_block[blk];\n\n    // Unswizzle column\n    idx = psmct16_column[idx];\n\n    // printf(\"(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\\n\",\n    //     base, width, x, y,\n    //     page, blk, cl,\n    //     (page * 2048) + (base * 64) + (blk * 64) + cl\n    // );\n\n    return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx;\n}\n\nstatic inline int psmz16_addr(int base, int width, int x, int y) {\n    // page            block          column\n    // 64 x 64 pixels  16 x 8 pixels  16 x 2 pixels\n    // base expressed in blocks\n    // 1 page = 64x32 = 8 KiB = 2048 words\n    // 1 block = 8x8 = 256 B = 64 words\n    // 1 column = 8x2 = 64 B = 16 words\n\n    int page = (x >> 6) + ((y >> 6) * width);\n    int blkx = (x >> 4) & 3;\n    int blky = (y >> 3) & 7;\n    int blk = blkx + (blky * 4);\n    int col = (y >> 1) & 3;\n    int idx = (x & 15) + ((y & 1) * 16);\n\n    // Unswizzle block\n    blk = psmz16_block[blk];\n\n    // Unswizzle column\n    idx = psmct16_column[idx];\n\n    // printf(\"(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\\n\",\n    //     base, width, x, y,\n    //     page, blk, cl,\n    //     (page * 2048) + (base * 64) + (blk * 64) + cl\n    // );\n\n    return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx;\n}\n\nint psmct16s_block[] = {\n    0 , 2 , 16, 18,\n    1 , 3 , 17, 19,\n    8 , 10, 24, 26,\n    9 , 11, 25, 27,\n    4 , 6 , 20, 22,\n    5 , 7 , 21, 23,\n    12, 14, 28, 30,\n    13, 15, 29, 31\n};\n\nint psmz16s_block[] = {\n    24, 26, 8 , 10,\n    25, 27, 9 , 11,\n    16, 18, 0 , 2 ,\n    17, 19, 1 , 3 ,\n    28, 30, 12, 14,\n    29, 31, 13, 15,\n    20, 22, 4 , 6 ,\n    21, 23, 5 , 7\n};\n\nstatic inline int psmct16s_addr(int base, int width, int x, int y) {\n    // page            block          column\n    // 64 x 64 pixels  16 x 8 pixels  16 x 2 pixels\n    // base expressed in blocks\n    // 1 page = 64x32 = 8 KiB = 2048 words\n    // 1 block = 8x8 = 256 B = 64 words\n    // 1 column = 8x2 = 64 B = 16 words\n\n    int page = (x >> 6) + ((y >> 6) * width);\n    int blkx = (x >> 4) & 3;\n    int blky = (y >> 3) & 7;\n    int blk = blkx + (blky * 4);\n    int col = (y >> 1) & 3;\n    int idx = (x & 15) + ((y & 1) * 16);\n\n    // Unswizzle block\n    blk = psmct16s_block[blk];\n\n    // Unswizzle column\n    idx = psmct16_column[idx];\n\n    // printf(\"(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\\n\",\n    //     base, width, x, y,\n    //     page, blk, cl,\n    //     (page * 2048) + (base * 64) + (blk * 64) + cl\n    // );\n\n    return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx;\n}\n\nstatic inline int psmz16s_addr(int base, int width, int x, int y) {\n    // page            block          column\n    // 64 x 64 pixels  16 x 8 pixels  16 x 2 pixels\n    // base expressed in blocks\n    // 1 page = 64x32 = 8 KiB = 2048 words\n    // 1 block = 8x8 = 256 B = 64 words\n    // 1 column = 8x2 = 64 B = 16 words\n\n    int page = (x >> 6) + ((y >> 6) * width);\n    int blkx = (x >> 4) & 3;\n    int blky = (y >> 3) & 7;\n    int blk = blkx + (blky * 4);\n    int col = (y >> 1) & 3;\n    int idx = (x & 15) + ((y & 1) * 16);\n\n    // Unswizzle block\n    blk = psmct16s_block[blk];\n\n    // Unswizzle column\n    idx = psmct16_column[idx];\n\n    // printf(\"(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\\n\",\n    //     base, width, x, y,\n    //     page, blk, cl,\n    //     (page * 2048) + (base * 64) + (blk * 64) + cl\n    // );\n\n    return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx;\n}\n\nint psmt8_column_02[] = {\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15,\n    8 , 9 , 12, 13, 0 , 1 , 4 , 5 ,\n    8 , 9 , 12, 13, 0 , 1 , 4 , 5 ,\n    10, 11, 14, 15, 2 , 3 , 6 , 7 ,\n    10, 11, 14, 15, 2 , 3 , 6 , 7\n};\n\nint psmt8_column_13[] = {\n    8 , 9 , 12, 13, 0 , 1 , 4 , 5 ,\n    8 , 9 , 12, 13, 0 , 1 , 4 , 5 ,\n    10, 11, 14, 15, 2 , 3 , 6 , 7 ,\n    10, 11, 14, 15, 2 , 3 , 6 , 7 ,\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15\n};\n\nint psmt8_shift[] = {\n    0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,\n    2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 ,\n    0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,\n    2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 ,\n    1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,\n    3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 ,\n    1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 ,\n    3 , 3 , 3 , 3 , 3 , 3 , 3 , 3\n};\n\nstatic inline int psmt8_addr(int base, int width, int x, int y) {\n    // page            block          column\n    // 128 x 64 pixels 16 x 16 pixels 16 x 4 pixels\n    // base expressed in blocks\n    // 1 page = 128x64 = 8 KiB = 2048 words\n    // 1 block = 16x16 = 256 B = 64 words\n    // 1 column = 16x4 = 64 B = 16 words\n    // 1 page = 128/4x64 = 32x64 words\n    // 1 block = 16/4x16 = 4x16 words\n    // 1 column = 16/4x4 = 4x4 words\n\n    int page = (x >> 7) + ((y >> 6) * (width >> 1));\n    int blkx = (x >> 4) & 7;\n    int blky = (y >> 4) & 3;\n    int blk = blkx + (blky * 8);\n    int col = (y >> 2) & 3;\n    int idx = (x & 15) + ((y & 3) * 16);\n    // int shift = (x & 3) * 16;\n\n    // Unswizzle block\n    blk = psmct32_block[blk];\n\n    // Unswizzle column\n    idx = (col & 1) ? psmt8_column_13[idx] : psmt8_column_02[idx];\n\n    // printf(\"(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\\n\",\n    //     base, width, x, y,\n    //     page, blk, cl,\n    //     (page * 2048) + (base * 64) + (blk * 64) + cl\n    // );\n\n    return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx;\n}\n\nint psmt4_block[] = {\n    0 , 2 , 8 , 10, 1 , 3 , 9 , 11,\n    4 , 6 , 12, 14, 5 , 7 , 13, 15,\n    16, 18, 24, 26, 17, 19, 25, 27,\n    20, 22, 28, 30, 21, 23, 29, 31\n};\n\nint psmt4_column_02[] = {\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15,\n    8 , 9 , 12, 13, 0 , 1 , 4 , 5 ,\n    8 , 9 , 12, 13, 0 , 1 , 4 , 5 ,\n    8 , 9 , 12, 13, 0 , 1 , 4 , 5 ,\n    8 , 9 , 12, 13, 0 , 1 , 4 , 5 ,\n    10, 11, 14, 15, 2 , 3 , 6 , 7 ,\n    10, 11, 14, 15, 2 , 3 , 6 , 7 ,\n    10, 11, 14, 15, 2 , 3 , 6 , 7 ,\n    10, 11, 14, 15, 2 , 3 , 6 , 7\n};\n\nint psmt4_column_13[] = {\n    8 , 9 , 12, 13, 0 , 1 , 4 , 5 ,\n    8 , 9 , 12, 13, 0 , 1 , 4 , 5 ,\n    8 , 9 , 12, 13, 0 , 1 , 4 , 5 ,\n    8 , 9 , 12, 13, 0 , 1 , 4 , 5 ,\n    10, 11, 14, 15, 2 , 3 , 6 , 7 ,\n    10, 11, 14, 15, 2 , 3 , 6 , 7 ,\n    10, 11, 14, 15, 2 , 3 , 6 , 7 ,\n    10, 11, 14, 15, 2 , 3 , 6 , 7 ,\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    0 , 1 , 4 , 5 , 8 , 9 , 12, 13,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15,\n    2 , 3 , 6 , 7 , 10, 11, 14, 15\n};\n\nint psmt4_shift[] = {\n    0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,\n    8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 ,\n    16, 16, 16, 16, 16, 16, 16, 16,\n    24, 24, 24, 24, 24, 24, 24, 24,\n    0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 ,\n    8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 ,\n    16, 16, 16, 16, 16, 16, 16, 16,\n    24, 24, 24, 24, 24, 24, 24, 24,\n    4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 ,\n    12, 12, 12, 12, 12, 12, 12, 12,\n    20, 20, 20, 20, 20, 20, 20, 20,\n    28, 28, 28, 28, 28, 28, 28, 28,\n    4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 ,\n    12, 12, 12, 12, 12, 12, 12, 12,\n    20, 20, 20, 20, 20, 20, 20, 20,\n    28, 28, 28, 28, 28, 28, 28, 28\n};\n\nstatic inline int psmt4_addr(int base, int width, int x, int y) {\n    // page             block          column\n    // 128 x 128 pixels 32 x 16 pixels 32 x 4 pixels\n    int page = (x >> 7) + ((y >> 7) * (width >> 1));\n    int blkx = (x >> 5) & 3;\n    int blky = (y >> 4) & 7;\n    int blk = blkx + (blky * 4);\n    int col = (y >> 2) & 3;\n    int idx = (x & 31) + ((y & 3) * 32);\n    // int shift = (x & 3) * 16;\n\n    // Unswizzle block\n    blk = psmt4_block[blk];\n\n    // Unswizzle column\n    idx = (col & 1) ? psmt4_column_13[idx] : psmt4_column_02[idx];\n\n    // printf(\"(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\\n\",\n    //     base, width, x, y,\n    //     page, blk, cl,\n    //     (page * 2048) + (base * 64) + (blk * 64) + cl\n    // );\n\n    return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx;\n}\n\nstatic const int psmt8_clut_block[] = {\n    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\n    0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17,\n    0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f,\n    0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f,\n    0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27,\n    0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37,\n    0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f,\n    0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f,\n    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\n    0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57,\n    0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f,\n    0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f,\n    0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67,\n    0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77,\n    0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f,\n    0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f,\n    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\n    0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97,\n    0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f,\n    0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f,\n    0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7,\n    0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7,\n    0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf,\n    0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf,\n    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,\n    0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7,\n    0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf,\n    0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf,\n    0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7,\n    0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7,\n    0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef,\n    0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff\n};\n\nvoid render_point(struct ps2_gs* gs, void* udata);\nvoid render_line(struct ps2_gs* gs, void* udata);\nvoid render_triangle(struct ps2_gs* gs, void* udata);\nvoid render_sprite(struct ps2_gs* gs, void* udata);\nvoid transfer_start(struct ps2_gs* gs, void* udata);\nvoid transfer_write(struct ps2_gs* gs, void* udata);\nvoid transfer_read(struct ps2_gs* gs, void* udata);\n\nvoid software_thread_render_thread(software_thread_state* ctx) {\n    while (!ctx->end_signal) {\n        while (true) {\n            ctx->queue_mtx.lock();\n\n            if (ctx->render_queue.empty()) {\n                ctx->queue_mtx.unlock();\n\n                break;\n            }\n\n            // Explicitly copy data from the queue\n            render_data rdata = render_data(ctx->render_queue.front());\n\n            // Assign context pointer to the copied context\n            rdata.gs.ctx = &rdata.gs.context[(rdata.gs.attr & GS_CTXT) ? 1 : 0];\n\n            ctx->render_queue.pop();\n            ctx->queue_mtx.unlock();\n\n            ctx->render_mtx.lock();\n\n            switch (rdata.prim) {\n                case 0: render_point(&rdata.gs, nullptr); break;\n                case 1: render_line(&rdata.gs, nullptr); break;\n                case 2: render_triangle(&rdata.gs, nullptr); break;\n                case 3: render_sprite(&rdata.gs, nullptr); break;\n            }\n\n            ctx->render_mtx.unlock();\n        }\n\n        // std::this_thread::yield();\n    }\n}\n\nvoid software_thread_destroy(void* udata) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    // Send end signal to rendering thread\n    ctx->end_signal = true;\n\n    // Clear rendering queue\n    ctx->queue_mtx.lock();\n\n    while (!ctx->render_queue.empty())\n        ctx->render_queue.pop();\n\n    ctx->queue_mtx.unlock();\n\n    if (ctx->buf)\n        free(ctx->buf);\n\n    // release buffers\n    SDL_ReleaseGPUBuffer(ctx->device, ctx->vertex_buffer);\n    SDL_ReleaseGPUBuffer(ctx->device, ctx->index_buffer);\n    SDL_ReleaseGPUSampler(ctx->device, ctx->sampler[0]);\n    SDL_ReleaseGPUSampler(ctx->device, ctx->sampler[1]);\n\n    if (ctx->texture) SDL_ReleaseGPUTexture(ctx->device, ctx->texture);\n    \n    // release the pipeline\n    SDL_ReleaseGPUGraphicsPipeline(ctx->device, ctx->pipeline);\n\n    // Should call destructors for our mutex, thread and queue\n    delete ctx;\n}\n\nvoid software_thread_set_size(void* udata, int width, int height) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    int en1 = ctx->gs->pmode & 1;\n    int en2 = (ctx->gs->pmode >> 1) & 1;\n\n    uint64_t display = 0, dispfb = 0;\n\n    if (en1 && en2) {\n        display = ctx->gs->display1 > ctx->gs->display2 ? ctx->gs->display1 : ctx->gs->display2;\n        dispfb = ctx->gs->dispfb1 > ctx->gs->dispfb2 ? ctx->gs->dispfb1 : ctx->gs->dispfb2;\n    } else if (en1) {\n        display = ctx->gs->display1;\n        dispfb = ctx->gs->dispfb1;\n    } else if (en2) {\n        display = ctx->gs->display2;\n        dispfb = ctx->gs->dispfb2;\n    }\n\n    uint32_t tex_fmt = (dispfb >> 15) & 0x1f;\n\n    int magh = ((display >> 23) & 0xf) + 1;\n    int magv = ((display >> 27) & 3) + 1;\n\n    if (((display >> 32) & 0xfff) == 0) {\n        ctx->tex_w = 0;\n        ctx->tex_h = 0;\n        \n        return;\n    }\n\n    if (((display >> 44) & 0x7ff) == 0) {\n        ctx->tex_w = 0;\n        ctx->tex_h = 0;\n        \n        return;\n    }\n\n    uint32_t tex_w = (((display >> 32) & 0xfff) / magh) + 1;\n    uint32_t tex_h = (((display >> 44) & 0x7ff) / magv) + 1;\n\n    // SMODE2 INT=1 FFMD=1\n    // if ((ctx->gs->smode2 & 3) == 3) {\n    //     tex_h /= 2;\n    // }\n\n    // Weird dobiestation hack, should fix Silent Hill 2, Choro Q HG, etc.\n    if (tex_h >= (tex_w * 1.3))\n        tex_h = tex_h / 2;\n\n    // printf(\"gsr: Setting framebuffer size to %dx%d fmt=%02x magh=%d magv=%d en=(%d,%d) dwh=(%d,%d) dxy=(%d,%d)\\n\", tex_w, tex_h, tex_fmt, magh, magv, en1, en2,\n    //     (int)((display >> 32) & 0xfff) + 1,\n    //     (int)((display >> 44) & 0x7ff) + 1,\n    //     (int)(display & 0xfff),\n    //     (int)((display >> 12) & 0xfff)\n    // );\n\n    // Do nothing if the size hasn't changed\n    if (tex_w == ctx->tex_w && tex_h == ctx->tex_h && tex_fmt == ctx->disp_fmt) {\n        return;\n    }\n\n    ctx->tex_w = tex_w;\n    ctx->tex_h = tex_h;\n    ctx->disp_fmt = tex_fmt;\n\n    if (ctx->buf) free(ctx->buf);\n\n    ctx->buf = (uint32_t*)malloc((ctx->tex_w * sizeof(uint32_t)) * ctx->tex_h);\n\n    SDL_GPUTextureFormat fmt;\n\n    switch (ctx->disp_fmt) {\n        case GS_PSMCT32:\n        case GS_PSMCT24: {\n            fmt = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM;\n        } break;\n\n        case GS_PSMCT16:\n        case GS_PSMCT16S: {\n            fmt = SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM;\n        } break;\n\n        default: {\n            // printf(\"gsr: Unknown framebuffer format %02x\\n\", ctx->disp_fmt);\n        } break;\n    }\n\n    if (ctx->texture) {\n        SDL_ReleaseGPUTexture(ctx->device, ctx->texture);\n    }\n\n    // Create a texture to send in our frame data\n    SDL_GPUTextureCreateInfo tci = {\n        .type = SDL_GPU_TEXTURETYPE_2D,\n        .format = fmt,\n        .usage = SDL_GPU_TEXTUREUSAGE_SAMPLER,\n        .width = tex_w,\n        .height = tex_h,\n        .layer_count_or_depth = 1,\n        .num_levels = 1,\n        .sample_count = SDL_GPU_SAMPLECOUNT_1,\n    };\n\n    ctx->texture = SDL_CreateGPUTexture(ctx->device, &tci);\n}\n\nvoid software_thread_set_scale(void* udata, float scale) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    ctx->scale = scale;\n}\n\nvoid software_thread_set_aspect_mode(void* udata, int aspect_mode) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    ctx->aspect_mode = aspect_mode;\n}\n\nvoid software_thread_set_integer_scaling(void* udata, bool integer_scaling) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    ctx->integer_scaling = integer_scaling;\n}\n\nvoid software_thread_set_bilinear(void* udata, bool bilinear) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    ctx->bilinear = bilinear;\n}\n\nvoid software_thread_get_viewport_size(void* udata, int* w, int* h) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    *w = ctx->tex_w;\n    *h = ctx->tex_h;\n}\n\nvoid software_thread_get_display_size(void* udata, int* w, int* h) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    *w = ctx->disp_w;\n    *h = ctx->disp_h;\n}\n\nvoid software_thread_get_display_format(void* udata, int* fmt) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    *fmt = ctx->disp_fmt;\n}\n\nvoid software_thread_set_window_rect(void* udata, int x, int y, int w, int h) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    ctx->window_x = x;\n    ctx->window_y = y;\n    ctx->window_w = w;\n    ctx->window_h = h;\n}\n\nvoid* software_thread_get_buffer_data(void* udata, int* w, int* h, int* bpp) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    if (!ctx->tex_w)\n        return nullptr;\n\n    if (!ctx->buf)\n        return nullptr;\n\n    *w = ctx->tex_w;\n    *h = ctx->tex_h;\n\n    switch (ctx->disp_fmt) {\n        case GS_PSMCT32:\n        case GS_PSMCT24: {\n            *bpp = 4;\n        } break;\n        case GS_PSMCT16:\n        case GS_PSMCT16S: {\n            *bpp = 2;\n        } break;\n    }\n\n    return ctx->buf;\n}\n\nvoid software_thread_get_interlace_mode(void* udata, int* interlace) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    *interlace = ctx->gs->smode2 & 3;\n}\n\nconst char* software_thread_get_name(void* udata) {\n    return \"Software (Threaded)\";\n}\n\n#define CLAMP(v, l, u) (((v) > (u)) ? (u) : (((v) < (l)) ? (l) : (v)))\n\nstatic inline uint32_t gs_apply_function(struct ps2_gs* gs, uint32_t t, uint32_t f) {\n    int pr, pg, pb, pa;\n    int tr = t & 0xff;\n    int tg = (t >> 8) & 0xff;\n    int tb = (t >> 16) & 0xff;\n    int ta = (t >> 24) & 0xff;\n    int fr = f & 0xff;\n    int fg = (f >> 8) & 0xff;\n    int fb = (f >> 16) & 0xff;\n    int fa = (f >> 24) & 0xff;\n\n    switch (gs->ctx->tfx) {\n        case GS_MODULATE: {\n            pr = CLAMP((tr * fr) >> 7, 0, 255);\n            pg = CLAMP((tg * fg) >> 7, 0, 255);\n            pb = CLAMP((tb * fb) >> 7, 0, 255);\n\n            if (gs->ctx->tcc) {\n                pa = CLAMP((ta * fa) >> 7, 0, 255);\n            } else {\n                pa = fa;\n            }\n        } break;\n        case GS_DECAL: {\n            pr = tr;\n            pg = tg;\n            pb = tb;\n\n            if (gs->ctx->tcc) {\n                pa = ta;\n            } else {\n                pa = fa;\n            }\n        } break;\n        case GS_HIGHLIGHT: {\n            pr = CLAMP(((tr * fr) >> 7) + fa, 0, 255);\n            pg = CLAMP(((tg * fg) >> 7) + fa, 0, 255);\n            pb = CLAMP(((tb * fb) >> 7) + fa, 0, 255);\n\n            if (gs->ctx->tcc) {\n                pa = CLAMP(fa + ta, 0, 255);\n            } else {\n                pa = fa;\n            }\n        } break;\n        case GS_HIGHLIGHT2: {\n            pr = CLAMP(((tr * fr) >> 7) + fa, 0, 255);\n            pg = CLAMP(((tg * fg) >> 7) + fa, 0, 255);\n            pb = CLAMP(((tb * fb) >> 7) + fa, 0, 255);\n\n            if (gs->ctx->tcc) {\n                pa = ta;\n            } else {\n                pa = fa;\n            }\n        } break;\n    }\n\n    return (pr & 0xff) | ((pg & 0xff) << 8) | ((pb & 0xff) << 16) | ((pa & 0xff) << 24);\n}\n\nstatic inline int gs_clamp_u(struct ps2_gs* gs, int u) {\n    switch (gs->ctx->wms) {\n        case 0: {\n            u %= gs->ctx->usize;\n        } break;\n\n        case 1: {\n            u = (u < 0) ? 0 : ((u > (int)gs->ctx->usize) ? gs->ctx->usize : u);\n        } break;\n\n        case 2: {\n            u = (u < (int)gs->ctx->minu) ? gs->ctx->minu : ((u > (int)gs->ctx->maxu) ? gs->ctx->maxu : u);\n        } break;\n\n        case 3: {\n            int umsk = gs->ctx->minu;\n            int ufix = gs->ctx->maxu;\n\n            u = (u & umsk) | ufix;\n        } break;\n    }\n\n    return u;\n}\n\nstatic inline int gs_clamp_v(struct ps2_gs* gs, int v) {\n    switch (gs->ctx->wmt) {\n        case 0: {\n            v %= gs->ctx->vsize;\n        } break;\n\n        case 1: {\n            v = (v < 0) ? 0 : ((v > (int)gs->ctx->vsize) ? gs->ctx->vsize : v);\n        } break;\n\n        case 2: {\n            v = (v < (int)gs->ctx->minv) ? gs->ctx->minv : ((v > (int)gs->ctx->maxv) ? gs->ctx->maxv : v);\n        } break;\n\n        case 3: {\n            int vmsk = gs->ctx->minv;\n            int vfix = gs->ctx->maxv;\n\n            v = (v & vmsk) | vfix;\n        } break;\n    }\n\n    return v;\n}\n\nenum : int {\n    TR_FAIL,\n    TR_PASS,\n    TR_FB_ONLY,\n    TR_ZB_ONLY,\n    TR_RGB_ONLY\n};\n\nstatic inline uint32_t gs_read_fb(struct ps2_gs* gs, int x, int y) {\n    switch (gs->ctx->fbpsm) {\n        case GS_PSMCT32: {\n            uint32_t addr = psmct32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n\n            return gs->vram[addr & 0xfffff];\n        }\n        case GS_PSMCT24: {\n            uint32_t addr = psmct32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n\n            return (gs->vram[addr & 0xfffff] & 0xffffff) | 0x80000000;\n        }\n        case GS_PSMCT16: {\n            uint32_t addr = psmct16_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            return vram[psmct16_shift[idx] & 0xfffff];\n        } break;\n        case GS_PSMCT16S: {\n            uint32_t addr = psmct16s_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            return vram[psmct16_shift[idx] & 0xfffff];\n        } break;\n        case GS_PSMZ32: {\n            uint32_t addr = psmz32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n\n            return gs->vram[addr & 0xfffff];\n        }\n        case GS_PSMZ24: {\n            uint32_t addr = psmz32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n\n            return (gs->vram[addr & 0xfffff] & 0xffffff) | 0x80000000;\n        }\n        case GS_PSMZ16: {\n            uint32_t addr = psmz16_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            return vram[psmct16_shift[idx] & 0xfffff];\n        } break;\n        case GS_PSMZ16S: {\n            uint32_t addr = psmz16s_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            return vram[psmct16_shift[idx] & 0xfffff];\n        } break;\n        default: {\n            // printf(\"Unsupported PSMT %02x for fb read\\n\", gs->ctx->fbpsm);\n            // exit(1);\n        } break;\n    }\n\n    return 0;\n}\n\nstatic inline uint32_t gs_read_dispfb(struct ps2_gs* gs, int x, int y, int dfb) {\n    uint32_t dfbp = dfb ? gs->dfbp2 : gs->dfbp1;\n    uint32_t dfbw = dfb ? gs->dfbw2 : gs->dfbw1;\n    uint32_t dfbpsm = dfb ? gs->dfbpsm2 : gs->dfbpsm1;\n\n    switch (dfbpsm) {\n        case GS_PSMCT32: {\n            uint32_t addr = psmct32_addr(dfbp, dfbw, x, y);\n\n            return gs->vram[addr & 0xfffff];\n        }\n        case GS_PSMCT24: {\n            uint32_t addr = psmct32_addr(dfbp, dfbw, x, y);\n\n            return gs->vram[addr & 0xfffff] & 0xffffff;\n        }\n        case GS_PSMCT16: {\n            uint32_t addr = psmct16_addr(dfbp, dfbw, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            return vram[psmct16_shift[idx] & 0xfffff];\n        } break;\n        case GS_PSMCT16S: {\n            uint32_t addr = psmct16s_addr(dfbp, dfbw, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            return vram[psmct16_shift[idx] & 0xfffff];\n        } break;\n        default: {\n            fprintf(stderr, \"Unsupported PSMT %02x for dispfb read\\n\", dfbpsm);\n\n            exit(1);\n        } break;\n    }\n\n    return 0;\n}\n\nstatic inline uint32_t gs_read_zb(struct ps2_gs* gs, int x, int y) {\n    switch (gs->ctx->zbpsm) {\n        case GS_PSMCT32: {\n            uint32_t addr = psmct32_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y);\n\n            return gs->vram[addr & 0xfffff];\n        }\n        case GS_PSMCT24: {\n            uint32_t addr = psmct32_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y);\n\n            return gs->vram[addr & 0xfffff] & 0xffffff;\n        }\n        case GS_PSMCT16: {\n            uint32_t addr = psmct16_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            return vram[psmct16_shift[idx]];\n        } break;\n        case GS_PSMCT16S: {\n            uint32_t addr = psmct16s_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            return vram[psmct16_shift[idx]];\n        } break;\n        case GS_PSMZ32: {\n            uint32_t addr = psmz32_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y);\n\n            return gs->vram[addr & 0xfffff];\n        }\n        case GS_PSMZ24: {\n            uint32_t addr = psmz32_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y);\n\n            return gs->vram[addr & 0xfffff] & 0xffffff;\n        }\n        case GS_PSMZ16: {\n            uint32_t addr = psmz16_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            return vram[psmct16_shift[idx]];\n        } break;\n        case GS_PSMZ16S: {\n            uint32_t addr = psmz16s_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            return vram[psmct16_shift[idx]];\n        } break;\n        default: {\n            // printf(\"Unsupported PSMT %02x for zb read\\n\", gs->ctx->zbpsm);\n            // exit(1);\n        } break;\n    }\n\n    return 0;\n}\n\nstatic inline uint32_t gs_read_cb_csm2(struct ps2_gs* gs, int i) {\n    int x = i + gs->cou;\n\n    uint32_t addr = psmct16_addr(gs->ctx->cbp, gs->cbw, x, gs->cov);\n    uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n    int idx = (x & 15) + ((gs->cov & 1) * 16);\n\n    return vram[psmct16_shift[idx] & 0xfffff];\n\n    // switch (gs->ctx->tbpsm) {\n    //     case GS_PSMT8H:\n    //     case GS_PSMT8: {\n    //         uint32_t addr = psmct16_addr(gs->ctx->cbp, gs->cbw, x, gs->cov);\n    //         uint16_t* vram = (uint16_t*)(&gs->vram[addr]);\n\n    //         int idx = (x & 15) + ((gs->cov & 1) * 16);\n\n    //         return gs->vram[psmct16_shift[idx]];\n    //     } break;\n\n    //     case GS_PSMT4HL:\n    //     case GS_PSMT4HH:\n    //     case GS_PSMT4: {\n    //         uint32_t addr = psmct16_addr(gs->ctx->cbp, gs->ctx->tbw, i, 0);\n    //         uint16_t* vram = (uint16_t*)(&gs->vram[addr]);\n\n    //         return gs->vram[psmct16_shift[i & 15]];\n    //     } break;\n    // }\n}\n\nstatic inline uint32_t gs_read_cb(struct ps2_gs* gs, int i) {\n    // Note: CSM2 should be the same?\n    // not sure if the different arrangement actually\n    // changes how address calculation is performed\n    // maybe it only applies to the CLUT cache\n    // - Edit: CSM2 uses the TEXCLUT reg, and it's\n    // basically just like a normal buffer\n\n    // Note: CLUT buffers are always 64 pixels wide\n\n    if (gs->ctx->csm == 1)\n        return gs_read_cb_csm2(gs, i);\n\n    switch (gs->ctx->tbpsm) {\n        case GS_PSMT8H:\n        case GS_PSMT8: {\n            int p = psmt8_clut_block[i];\n            int x = p & 0xf;\n            int y = p >> 4;\n\n            switch (gs->ctx->cbpsm) {\n                case GS_PSMCT32: {\n                    return gs->vram[psmct32_addr(gs->ctx->cbp, 1, x, y) & 0xfffff];\n                } break;\n\n                case GS_PSMCT16:\n                case GS_PSMCT16S: {\n                    uint32_t addr = psmct16_addr(gs->ctx->cbp, 2, x, y);\n                    uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n                    int idx = (x & 15) + ((y & 1) * 16);\n\n                    return vram[psmct16_shift[idx] & 0xfffff];\n                } break;\n                default: {\n                    // printf(\"Unsupported PSMT %02x for 8-bit cb read\\n\", gs->ctx->cbpsm);\n                    // exit(1);\n                } break;\n            }\n        } break;\n\n        case GS_PSMT4HH:\n        case GS_PSMT4HL:\n        case GS_PSMT4: {\n            int x = i & 0x7;\n            int y = i >> 3;\n\n            switch (gs->ctx->cbpsm) {\n                case GS_PSMCT32: {\n                    // return gs->clut_cache[gs->ctx->csa + x + (y * 8)];\n                    return gs->vram[psmct32_addr(gs->ctx->cbp, 1, x, y) & 0xfffff];\n                } break;\n\n                case GS_PSMCT16:\n                case GS_PSMCT16S: {\n                    uint32_t addr = psmct16_addr(gs->ctx->cbp, 2, x, y);\n                    uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n                    int idx = (x & 15) + ((y & 1) * 16);\n\n                    return vram[psmct16_shift[idx] & 0xfffff];\n                } break;\n                default: {\n                    // printf(\"Unsupported PSMT %02x for 4-bit cb read\\n\", gs->ctx->cbpsm);\n                    // exit(1);\n                } break;\n            }\n        } break;\n    }\n\n    return 0;\n}\n\nstatic inline uint32_t gs_to_rgba32(struct ps2_gs* gs, uint32_t c, int fmt) {\n    switch (fmt) {\n        case GS_PSMCT32:\n        case GS_PSMZ32: {\n            return c;\n        }\n\n        case GS_PSMCT24:\n        case GS_PSMZ24: {\n            uint32_t a = 0;\n\n            if (gs->aem) {\n                if (c & 0xffffff) {\n                    a = gs->ta0;\n                } else {\n                    a = 0;\n                }\n            } else {\n                a = gs->ta0;\n            }\n\n            return c | (a << 24);\n        }\n\n        case GS_PSMCT16:\n        case GS_PSMCT16S:\n        case GS_PSMZ16:\n        case GS_PSMZ16S: {\n            int ia = c & 0x8000;\n            uint32_t oa = 0;\n\n            if (ia) {\n                oa = gs->ta1;\n            } else {\n                if (gs->aem) {\n                    if (c & 0x7fff) {\n                        oa = gs->ta0;\n                    } else {\n                        oa = 0;\n                    }\n                } else {\n                    oa = gs->ta0;\n                }\n            }\n\n            return ((c & 0x001f) << 3) |\n                   ((c & 0x03e0) << 6) |\n                   ((c & 0x7c00) << 9) |\n                   (oa << 24);\n        } break;\n\n        case GS_PSMT8:\n        case GS_PSMT8H:\n        case GS_PSMT4:\n        case GS_PSMT4HL:\n        case GS_PSMT4HH: {\n            return gs_to_rgba32(gs, c, gs->ctx->cbpsm);\n        } break;\n        default: {\n            // printf(\"Unsupported PSMT %02x for to_rgba32\\n\", fmt);\n            // exit(1);\n        } break;\n    }\n\n    return 0;\n}\n\nstatic inline uint32_t gs_from_rgba32(struct ps2_gs* gs, uint32_t c, int fmt, int x = 0, int y = 0, int dither = 0) {\n    switch (fmt) {\n        case GS_PSMCT32:\n        case GS_PSMZ32: {\n            return c;\n        }\n\n        case GS_PSMCT24:\n        case GS_PSMZ24: {\n            // To-do: Use TEXA\n            return c & 0xffffff;\n        }\n\n        case GS_PSMCT16:\n        case GS_PSMCT16S: {\n            if (dither) {\n                int dv = gs->dither[y & 3][x & 3];\n\n                int r = c & 0xff;\n                int g = (c >> 8) & 0xff;\n                int b = (c >> 16) & 0xff;\n\n                r += dv;\n                g += dv;\n                b += dv;\n\n                r = CLAMP(r, 0, 255);\n                g = CLAMP(g, 0, 255);\n                b = CLAMP(b, 0, 255);\n\n                r = (r >> 3) & 0x1f;\n                g = (g >> 3) & 0x1f;\n                b = (b >> 3) & 0x1f;\n\n                return r | (g << 5) | (b << 10) | ((c & 0x80000000) ? 0x8000 : 0);\n            }\n\n            // To-do: Use TEXA\n            return ((c & 0x0000f8) >> 3) |\n                   ((c & 0x00f800) >> 6) |\n                   ((c & 0xf80000) >> 9) |\n                   ((c & 0x80000000) ? 0x8000 : 0);\n        } break;\n        case GS_PSMZ16:\n        case GS_PSMZ16S: {\n            return c & 0xffff;\n        } break;\n        case GS_PSMT8:\n        case GS_PSMT8H:\n        case GS_PSMT4:\n        case GS_PSMT4HL:\n        case GS_PSMT4HH: {\n            return gs_from_rgba32(gs, c, gs->ctx->cbpsm);\n        } break;\n        default: {\n            // printf(\"Unsupported PSMT %02x for from_rgba32\\n\", fmt);\n            // exit(1);\n        } break;\n    }\n\n    return 0;\n}\n\nstatic inline uint32_t gs_read_tb_impl(struct ps2_gs* gs, int u, int v) {\n    u = gs_clamp_u(gs, u);\n    v = gs_clamp_v(gs, v);\n\n    switch (gs->ctx->tbpsm) {\n        case GS_PSMCT32:\n            return gs->vram[psmct32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v) & 0xfffff];\n        case GS_PSMCT24:\n            return gs->vram[psmct32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v) & 0xfffff] & 0xffffff;\n        case GS_PSMCT16: {\n            uint32_t addr = psmct16_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 1) * 16);\n\n            return vram[psmct16_shift[idx]];\n        } break;\n        case GS_PSMCT16S: {\n            uint32_t addr = psmct16s_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 1) * 16);\n\n            return vram[psmct16_shift[idx]];\n        } break;\n        case GS_PSMT8: {\n            uint32_t addr = psmt8_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v);\n            uint8_t* vram = (uint8_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 3) * 16);\n\n            return gs_read_cb(gs, vram[psmt8_shift[idx]]);\n        } break;\n        case GS_PSMT8H: {\n            uint32_t data = gs->vram[psmct32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v) & 0xfffff];\n\n            return gs_read_cb(gs, data >> 24);\n        } break;\n        case GS_PSMT4: {\n            uint32_t addr = psmt4_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v);\n\n            int idx = (u & 31) + ((v & 3) * 32);\n            int shift = psmt4_shift[idx];\n\n            uint32_t mask = 0xful << shift;\n\n            return gs_read_cb(gs, (gs->vram[addr & 0xfffff] & mask) >> shift);\n        } break;\n        case GS_PSMT4HL: {\n            uint32_t data = gs->vram[psmct32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v) & 0xfffff];\n\n            return gs_read_cb(gs, (data >> 24) & 0xf);\n        } break;\n        case GS_PSMT4HH: {\n            uint32_t data = gs->vram[psmct32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v) & 0xfffff];\n\n            return gs_read_cb(gs, data >> 28);\n        } break;\n        case GS_PSMZ32: {\n            uint32_t addr = psmz32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v);\n\n            return gs->vram[addr & 0xfffff];\n        } break;\n        case GS_PSMZ24: {\n            uint32_t addr = psmz32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v);\n\n            return gs->vram[addr & 0xfffff] & 0xffffff;\n        } break;\n        case GS_PSMZ16: {\n            uint32_t addr = psmz16_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 1) * 16);\n\n            return vram[psmct16_shift[idx] & 0xfffff];\n        } break;\n        case GS_PSMZ16S: {\n            uint32_t addr = psmz16s_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 1) * 16);\n\n            return vram[psmct16_shift[idx] & 0xfffff];\n        } break;\n        default: {\n            // printf(\"Unsupported PSMT %02x for tb read\\n\", gs->ctx->tbpsm);\n            // exit(1);\n        } break;\n    }\n\n    return 0;\n}\n\nstatic inline uint32_t gs_read_tb(struct ps2_gs* gs, int u, int v) {\n    if (gs->ctx->mmag) {\n        float a = ((u - 8) & 0xf) * 0.0625;\n        float b = ((v - 8) & 0xf) * 0.0625;\n\n        int iu0 = (u - 8) >> 4;\n        int iv0 = (v - 8) >> 4;\n        int iu1 = iu0 + 1;\n        int iv1 = iv0 + 1;\n\n        uint32_t s0 = gs_read_tb_impl(gs, iu0, iv0);\n        uint32_t s1 = gs_read_tb_impl(gs, iu1, iv0);\n        uint32_t s2 = gs_read_tb_impl(gs, iu0, iv1);\n        uint32_t s3 = gs_read_tb_impl(gs, iu1, iv1);\n\n        s0 = gs_to_rgba32(gs, s0, gs->ctx->tbpsm);\n        s1 = gs_to_rgba32(gs, s1, gs->ctx->tbpsm);\n        s2 = gs_to_rgba32(gs, s2, gs->ctx->tbpsm);\n        s3 = gs_to_rgba32(gs, s3, gs->ctx->tbpsm);\n\n        int r0 = s0 & 0xff;\n        int g0 = (s0 >> 8) & 0xff;\n        int b0 = (s0 >> 16) & 0xff;\n        int a0 = (s0 >> 24) & 0xff;\n        int r1 = s1 & 0xff;\n        int g1 = (s1 >> 8) & 0xff;\n        int b1 = (s1 >> 16) & 0xff;\n        int a1 = (s1 >> 24) & 0xff;\n        int r2 = s2 & 0xff;\n        int g2 = (s2 >> 8) & 0xff;\n        int b2 = (s2 >> 16) & 0xff;\n        int a2 = (s2 >> 24) & 0xff;\n        int r3 = s3 & 0xff;\n        int g3 = (s3 >> 8) & 0xff;\n        int b3 = (s3 >> 16) & 0xff;\n        int a3 = (s3 >> 24) & 0xff;\n\n        uint32_t rr = ((1.0-a) * (1.0-b) * r0) + (a * (1.0-b) * r1) + ((1.0-a) * b * r2) + (a * b * r3);\n        uint32_t gg = ((1.0-a) * (1.0-b) * g0) + (a * (1.0-b) * g1) + ((1.0-a) * b * g2) + (a * b * g3);\n        uint32_t bb = ((1.0-a) * (1.0-b) * b0) + (a * (1.0-b) * b1) + ((1.0-a) * b * b2) + (a * b * b3);\n        uint32_t aa = ((1.0-a) * (1.0-b) * a0) + (a * (1.0-b) * a1) + ((1.0-a) * b * a2) + (a * b * a3);\n\n        rr = CLAMP(rr, 0, 255);\n        gg = CLAMP(gg, 0, 255);\n        bb = CLAMP(bb, 0, 255);\n        aa = CLAMP(aa, 0, 255);\n\n        return gs_from_rgba32(gs, rr | (gg << 8) | (bb << 16) | (aa << 24), gs->ctx->tbpsm);\n    }\n\n    return gs_read_tb_impl(gs, u >> 4, v >> 4);\n}\n\nstatic inline void gs_write_fb(struct ps2_gs* gs, int x, int y, uint32_t c) {\n    uint32_t f = gs_from_rgba32(gs, c, gs->ctx->fbpsm, x, y, gs->dthe);\n\n    // To-do: Implement FBMSK\n    switch (gs->ctx->fbpsm) {\n        case GS_PSMCT32: {\n            uint32_t addr = psmct32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n\n            gs->vram[addr & 0xfffff] = f;\n            // gs->vram[(gs->ctx->fbp + x + (y * gs->ctx->fbw)) & 0xfffff] = f;\n        } break;\n\n        case GS_PSMCT24: {\n            uint32_t addr = psmct32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint32_t v = gs->vram[addr];\n\n            gs->vram[addr] = (v & 0xff000000) | (f & 0xffffff);\n            // gs->vram[(gs->ctx->fbp + x + (y * gs->ctx->fbw)) & 0xfffff] = f;\n        } break;\n        case GS_PSMCT16: {\n            uint32_t addr = psmct16_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            vram[psmct16_shift[idx]] = f;\n        } break;\n        case GS_PSMCT16S: {\n            uint32_t addr = psmct16s_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            vram[psmct16_shift[idx]] = f;\n        } break;\n        case GS_PSMZ32: {\n            uint32_t addr = psmz32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n\n            gs->vram[addr & 0xfffff] = f;\n            // gs->vram[(gs->ctx->fbp + x + (y * gs->ctx->fbw)) & 0xfffff] = f;\n        } break;\n\n        case GS_PSMZ24: {\n            uint32_t addr = psmz32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint32_t v = gs->vram[addr];\n\n            gs->vram[addr] = (v & 0xff000000) | (f & 0xffffff);\n            // gs->vram[(gs->ctx->fbp + x + (y * gs->ctx->fbw)) & 0xfffff] = f;\n        } break;\n        case GS_PSMZ16: {\n            uint32_t addr = psmz16_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            vram[psmct16_shift[idx]] = f;\n        } break;\n        case GS_PSMZ16S: {\n            uint32_t addr = psmz16s_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            vram[psmct16_shift[idx]] = f;\n        } break;\n        default: {\n            // printf(\"Unsupported PSMT %02x for fb write\\n\", gs->ctx->fbpsm);\n            // exit(1);\n        } break;\n    }\n}\n\nstatic inline void gs_write_fb_no_alpha(struct ps2_gs* gs, int x, int y, uint32_t c) {\n    uint32_t f = gs_from_rgba32(gs, c, gs->ctx->fbpsm);\n\n    // To-do: Implement FBMSK\n    switch (gs->ctx->fbpsm) {\n        case GS_PSMCT32: {\n            uint32_t addr = psmct32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint32_t p = gs->vram[addr & 0xfffff];\n\n            gs->vram[addr & 0xfffff] = (f & 0xffffff) | (p & 0xff000000);\n        } break;\n\n        case GS_PSMCT24: {\n            uint32_t addr = psmct32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint32_t p = gs->vram[addr];\n\n            gs->vram[addr] = (f & 0xffffff) | (p & 0xff000000);\n        } break;\n        case GS_PSMCT16: {\n            uint32_t addr = psmct16_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr]);\n            uint16_t p = gs->vram[addr];\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            vram[psmct16_shift[idx]] = (f & 0x7fff) | (p & 0x8000);\n        } break;\n        case GS_PSMCT16S: {\n            uint32_t addr = psmct16s_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr]);\n            uint16_t p = gs->vram[addr];\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            vram[psmct16_shift[idx]] = (f & 0x7fff) | (p & 0x8000);\n        } break;\n        case GS_PSMZ32: {\n            uint32_t addr = psmz32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint32_t p = gs->vram[addr & 0xfffff];\n\n            gs->vram[addr & 0xfffff] = (f & 0xffffff) | (p & 0xff000000);\n        } break;\n\n        case GS_PSMZ24: {\n            uint32_t addr = psmz32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint32_t p = gs->vram[addr];\n\n            gs->vram[addr] = (f & 0xffffff) | (p & 0xff000000);\n        } break;\n        case GS_PSMZ16: {\n            uint32_t addr = psmz16_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr]);\n            uint16_t p = gs->vram[addr];\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            vram[psmct16_shift[idx]] = (f & 0x7fff) | (p & 0x8000);\n        } break;\n        case GS_PSMZ16S: {\n            uint32_t addr = psmz16s_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr]);\n            uint16_t p = gs->vram[addr];\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            vram[psmct16_shift[idx]] = (f & 0x7fff) | (p & 0x8000);\n        } break;\n        default: {\n            // printf(\"Unsupported PSMT %02x for fb write\\n\", gs->ctx->fbpsm);\n            // exit(1);\n        } break;\n    }\n}\n\nstatic inline void gs_write_zb(struct ps2_gs* gs, int x, int y, uint32_t z) {\n    if (gs->ctx->zbmsk || !gs->ctx->zte)\n        return;\n\n    switch (gs->ctx->zbpsm) {\n        case 0x01: z = std::min(z, 0xffffffu); break;\n        case 0x02: z = std::min(z, 0xffffu); break;\n        case 0x0A: z = std::min(z, 0xffffu); break;\n        case 0x31: z = std::min(z, 0xffffffu); break;\n        case 0x32: z = std::min(z, 0xffffu); break;\n        case 0x3A: z = std::min(z, 0xffffu); break;\n    }\n\n    switch (gs->ctx->zbpsm) {\n        case GS_PSMCT32:\n        case GS_PSMCT24: {\n            gs->vram[psmct32_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y) & 0xfffff] = z;\n        } break;\n        case GS_PSMCT16: {\n            uint32_t addr = psmct16_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            vram[psmct16_shift[idx]] = z;\n        } break;\n        case GS_PSMCT16S: {\n            uint32_t addr = psmct16s_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            vram[psmct16_shift[idx]] = z;\n        } break;\n        case GS_PSMZ32:\n        case GS_PSMZ24: {\n            gs->vram[psmz32_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y) & 0xfffff] = z;\n        } break;\n        case GS_PSMZ16: {\n            uint32_t addr = psmz16_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            vram[psmct16_shift[idx]] = z;\n        } break;\n        case GS_PSMZ16S: {\n            uint32_t addr = psmz16s_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (x & 15) + ((y & 1) * 16);\n\n            vram[psmct16_shift[idx]] = z;\n        } break;\n        default: {\n            // printf(\"Unsupported PSMT %02x for zb write\\n\", gs->ctx->zbpsm);\n            // exit(1);\n        } break;\n    }\n}\n\nstatic inline int gs_test_scissor(struct ps2_gs* gs, int x, int y) {\n    if (x < (int)gs->ctx->scax0 || y < (int)gs->ctx->scay0 ||\n        x > (int)gs->ctx->scax1 || y > (int)gs->ctx->scay1) {\n        return TR_FAIL;\n    }\n\n    return TR_PASS;\n}\n\nstatic inline int gs_test_pixel(struct ps2_gs* gs, int x, int y, uint32_t z, uint8_t a) {\n    int tr = TR_PASS;\n\n    // Alpha test\n    if (gs->ctx->ate) {\n        switch (gs->ctx->atst) {\n            case 0: tr = TR_FAIL; break;\n            case 2: tr = a < gs->ctx->aref; break;\n            case 3: tr = a <= gs->ctx->aref; break;\n            case 4: tr = a == gs->ctx->aref; break;\n            case 5: tr = a >= gs->ctx->aref; break;\n            case 6: tr = a > gs->ctx->aref; break;\n            case 7: tr = a != gs->ctx->aref; break;\n        }\n\n        if (tr == TR_FAIL) {\n            switch (gs->ctx->afail) {\n                case 0: return TR_FAIL;\n                case 1: tr = TR_FB_ONLY; break;\n                case 2: tr = TR_ZB_ONLY; break;\n                case 3: tr = TR_RGB_ONLY; break;\n            }\n        }\n    }\n\n    // Destination alpha test\n    if (gs->ctx->date) {\n        // Thanks to @refraction\n        // Quote: \"When the format is 24bit (Z or C), DATE ceases to function.\n        //        It was believed that in 24bit mode all pixels pass because alpha\n        //        doesn't exist however after testing this on a PS2 it turns out\n        //        nothing passes, it ignores the draw.\"\n        if ((gs->ctx->fbpsm & 0xf) == GS_PSMCT24) return TR_FAIL;\n\n        uint32_t s = gs_read_fb(gs, x, y);\n\n        switch (gs->ctx->fbpsm) {\n            case GS_PSMCT16:\n            case GS_PSMCT16S:\n                if (((s >> 15) & 1) != gs->ctx->datm) return TR_FAIL;\n            break;\n            case GS_PSMCT32:\n                if (((s >> 31) & 1) != gs->ctx->datm) return TR_FAIL;\n            break;\n            default: {\n                fprintf(stderr, \"Unsupported PSMT %02x for destination alpha test\\n\", gs->ctx->fbpsm);\n                exit(1);\n            } break;\n        }\n    }\n\n    // Depth test\n    if (gs->ctx->zte) {\n        uint32_t zb = gs_read_zb(gs, x, y);\n\n        switch (gs->ctx->zbpsm) {\n            case 0x01: z = std::min(z, 0xffffffu); break;\n            case 0x02: z = std::min(z, 0xffffu); break;\n            case 0x0A: z = std::min(z, 0xffffu); break;\n            case 0x31: z = std::min(z, 0xffffffu); break;\n            case 0x32: z = std::min(z, 0xffffu); break;\n            case 0x3A: z = std::min(z, 0xffffu); break;\n        }\n\n        switch (gs->ctx->ztst) {\n            case 0: return TR_FAIL;\n            case 2: {\n                if (z < zb) return TR_FAIL;\n            } break;\n            case 3: {\n                if (z <= zb) return TR_FAIL;\n            } break;\n        }\n    }\n\n    return tr;\n}\n\nstatic inline uint32_t gs_alpha_blend(struct ps2_gs* gs, int x, int y, uint32_t s) {\n    uint32_t d = gs_read_fb(gs, x, y);\n\n    switch (gs->ctx->fbpsm) {\n        case GS_PSMCT32: break;\n        case GS_PSMCT24: d |= 0x80000000; break;\n        case GS_PSMCT16:\n        case GS_PSMCT16S: {\n            int a = d & 0x8000;\n\n            d = ((d & 0x001f) << 3) |\n                ((d & 0x03e0) << 6) |\n                ((d & 0x7c00) << 9);\n\n            d |= a ? 0x80000000 : 0;\n        } break;\n        default: {\n            fprintf(stderr, \"Unsupported PSMT %02x for alpha blend\\n\", gs->ctx->fbpsm);\n\n            exit(1);\n        } break;\n    }\n\n    uint32_t av = (gs->ctx->a == 0) ? s : ((gs->ctx->a == 1) ? d : 0);\n    uint32_t bv = (gs->ctx->b == 0) ? s : ((gs->ctx->b == 1) ? d : 0);\n    uint32_t cv = (gs->ctx->c == 0) ? (s >> 24) : ((gs->ctx->c == 1) ? (d >> 24) : gs->ctx->fix);\n    uint32_t dv = (gs->ctx->d == 0) ? s : ((gs->ctx->d == 1) ? d : 0);\n    \n    // cv = CLAMP(cv, 0, 127);\n\n    // if (!gs->tme)\n    //     printf(\"Cd=%08x Cs=%08x a=%08x (%d) b=%08x (%d) c=%08x (%d) d=%08x (%d)\\n\", d, s,\n    //     av, gs->ctx->a,\n    //     bv, gs->ctx->b,\n    //     cv, gs->ctx->c,\n    //     dv, gs->ctx->d\n    // );\n\n    int ar = (av >> 0 ) & 0xff;\n    int ag = (av >> 8 ) & 0xff;\n    int ab = (av >> 16) & 0xff;\n    // uint32_t aa = (av >> 24) & 0xff;\n    int br = (bv >> 0 ) & 0xff;\n    int bg = (bv >> 8 ) & 0xff;\n    int bb = (bv >> 16) & 0xff;\n    // uint32_t ba = (bv >> 24) & 0xff;\n    int dr = (dv >> 0 ) & 0xff;\n    int dg = (dv >> 8 ) & 0xff;\n    int db = (dv >> 16) & 0xff;\n    // uint32_t da = (dv >> 24) & 0xff;\n\n    int rr = ar - br;\n    int rg = ag - bg;\n    int rb = ab - bb;\n\n    rr = ((rr * (int)cv) >> 7) + dr;\n    rg = ((rg * (int)cv) >> 7) + dg;\n    rb = ((rb * (int)cv) >> 7) + db;\n    // uint32_t ra = (((aa - ba) * cv) >> 7) + da;\n\n    rr = CLAMP(rr, 0, 255);\n    rg = CLAMP(rg, 0, 255);\n    rb = CLAMP(rb, 0, 255);\n\n    return (rr & 0xff) | ((rg & 0xff) << 8) | ((rb & 0xff) << 16) | (d & 0xff000000);\n}\n\nstatic inline float lerpf(int32_t x, float u1, int32_t x1, float u2, int32_t x2) {\n    float b = u1 * (x2 - x);\n\n    b += u2 * (x - x1);\n\n    if (!(x2 - x1))\n        return u1;\n\n    return b / (x2 - x1);\n}\n\nstatic inline int32_t lerp(int32_t x, int32_t u1, int32_t x1, int32_t u2, int32_t x2) {\n    int64_t b = (int64_t)u1 * (x2 - x);\n\n    b += (int64_t)u2 * (x - x1);\n\n    if (!(x2 - x1))\n        return u1;\n\n    return b / (x2 - x1);\n}\n\nint32_t stepsize(int32_t u1, int32_t x1, int32_t u2, int32_t x2, int64_t mult) {\n    if (!(x2 - x1))\n        return ((u2 - u1) * mult);\n\n    return ((u2 - u1) * mult)/(x2 - x1);\n}\n\nstatic inline void gs_draw_pixel(struct ps2_gs* gs, int x, int y, uint32_t z, uint32_t c) {\n    int a = c >> 24;\n\n    int tr = gs_test_pixel(gs, x, y, z, a);\n\n    if (tr == TR_FAIL)\n        return;\n\n    if (gs->abe) c = gs_alpha_blend(gs, x, y, c);\n\n    switch (tr) {\n        case TR_FB_ONLY: gs_write_fb(gs, x, y, c); break;\n        case TR_ZB_ONLY: gs_write_zb(gs, x, y, z); break;\n        case TR_RGB_ONLY: gs_write_fb_no_alpha(gs, x, y, c); break;\n        case TR_PASS: {\n            gs_write_zb(gs, x, y, z);\n            gs_write_fb(gs, x, y, c);\n        } break;\n    }\n}\n\nvoid software_thread_init(void* udata, struct ps2_gs* gs, SDL_Window* window, SDL_GPUDevice* device) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    ctx->window = window;\n    ctx->device = device;\n    ctx->gs = gs;\n    ctx->scale = 1.5f;\n\n    ctx->end_signal = false;\n    ctx->render_thr = std::thread(software_thread_render_thread, ctx);\n    ctx->render_thr.detach();\n\n    // create the vertex shader\n    SDL_GPUShaderCreateInfo vsci = {\n        .code_size = g_vertex_shader_size,\n        .code = g_vertex_shader_data,\n        .entrypoint = SHADER_ENTRYPOINT,\n        .format = SHADER_FORMAT,\n        .stage = SDL_GPU_SHADERSTAGE_VERTEX,\n        .num_samplers = 0,\n        .num_storage_textures = 0,\n        .num_storage_buffers = 0,\n        .num_uniform_buffers = 0,\n    };\n\n    SDL_GPUShader* vertex_shader = SDL_CreateGPUShader(device, &vsci);\n\n    // create the fragment shader\n    SDL_GPUShaderCreateInfo fsci = {\n        .code_size = g_fragment_shader_size,\n        .code = g_fragment_shader_data,\n        .entrypoint = SHADER_ENTRYPOINT,\n        .format = SHADER_FORMAT,\n        .stage = SDL_GPU_SHADERSTAGE_FRAGMENT,\n        .num_samplers = 1,\n        .num_storage_textures = 0,\n        .num_storage_buffers = 0,\n        .num_uniform_buffers = 0,\n    };\n\n    SDL_GPUShader* fragment_shader = SDL_CreateGPUShader(device, &fsci);\n\n    // Create vertex buffer description and attributes\n    SDL_GPUVertexBufferDescription vbd[1] = {{\n        .slot = 0,\n        .pitch = sizeof(gpu_vertex),\n        .input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX,\n        .instance_step_rate = 0,\n    }};\n\n    SDL_GPUVertexAttribute va[2] = {{\n        .location = 0,\n        .buffer_slot = 0,\n        .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,\n        .offset = 0,\n    }, {\n        .location = 1,\n        .buffer_slot = 0,\n        .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2,\n        .offset = sizeof(float) * 2,\n    }};\n\n    SDL_GPUColorTargetDescription ctd[1] = {{\n        .format = SDL_GetGPUSwapchainTextureFormat(device, window),\n        .blend_state = {\n            .src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA,\n            .dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,\n            .color_blend_op = SDL_GPU_BLENDOP_ADD,\n            .src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA,\n            .dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA,\n            .alpha_blend_op = SDL_GPU_BLENDOP_ADD,\n            .color_write_mask = SDL_GPU_COLORCOMPONENT_A,\n            .enable_blend = false,\n            .enable_color_write_mask = false,\n        },\n    }};\n\n    // Create graphics pipeline\n    SDL_GPUGraphicsPipelineCreateInfo gpci = {\n        .vertex_shader = vertex_shader,\n        .fragment_shader = fragment_shader,\n        .vertex_input_state = {\n            .vertex_buffer_descriptions = vbd,\n            .num_vertex_buffers = 1,\n            .vertex_attributes = va,\n            .num_vertex_attributes = 2,\n        },\n        .primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST,\n        .target_info = {\n            .color_target_descriptions = ctd,\n            .num_color_targets = 1,\n        },\n    };\n\n    ctx->pipeline = SDL_CreateGPUGraphicsPipeline(device, &gpci);\n\n    SDL_assert(ctx->pipeline);\n\n    // we don't need to store the shaders after creating the pipeline\n    SDL_ReleaseGPUShader(device, vertex_shader);\n    SDL_ReleaseGPUShader(device, fragment_shader);\n\n    // create the vertex and index buffers\n    SDL_GPUBufferCreateInfo vbci = {\n        .usage = SDL_GPU_BUFFERUSAGE_VERTEX,\n        .size = sizeof(gpu_vertex) * 4,\n    };\n\n    ctx->vertex_buffer = SDL_CreateGPUBuffer(device, &vbci);\n\n    // We're sending two triangles to make a single quad so we need 6 indices\n    SDL_GPUBufferCreateInfo ibci = {\n        .usage = SDL_GPU_BUFFERUSAGE_INDEX,\n        .size = sizeof(Uint16) * 6,\n    };\n\n    ctx->index_buffer = SDL_CreateGPUBuffer(device, &ibci);\n\n    SDL_GPUSamplerCreateInfo nearest_sci = {\n        .min_filter = SDL_GPU_FILTER_NEAREST,\n        .mag_filter = SDL_GPU_FILTER_NEAREST,\n        .mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST,\n        .address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,\n        .address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,\n        .address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,\n    };\n\n    SDL_GPUSamplerCreateInfo linear_sci = {\n        .min_filter = SDL_GPU_FILTER_LINEAR,\n        .mag_filter = SDL_GPU_FILTER_LINEAR,\n        .mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST,\n        .address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,\n        .address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,\n        .address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE,\n    };\n\n    ctx->sampler[0] = SDL_CreateGPUSampler(ctx->device, &nearest_sci);\n    ctx->sampler[1] = SDL_CreateGPUSampler(ctx->device, &linear_sci);\n}\n\nstatic inline uint32_t gs_generic_read(struct ps2_gs* gs, uint32_t bp, uint32_t bw, uint32_t bpsm, uint32_t u, uint32_t v) {\n    switch (bpsm) {\n        case GS_PSMCT32:\n            return gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff];\n        case GS_PSMCT24:\n            return gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff];\n        case GS_PSMCT16: {\n            uint32_t addr = psmct16_addr(bp, bw, u, v);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 1) * 16);\n\n            return vram[psmct16_shift[idx] & 0xfffff];\n        } break;\n        case GS_PSMCT16S: {\n            uint32_t addr = psmct16s_addr(bp, bw, u, v);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 1) * 16);\n\n            return vram[psmct16_shift[idx] & 0xfffff];\n        } break;\n        case GS_PSMT8: {\n            uint32_t addr = psmt8_addr(bp, bw, u, v);\n            uint8_t* vram = (uint8_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 3) * 16);\n\n            return vram[psmt8_shift[idx] & 0xfffff];\n        } break;\n        case GS_PSMT8H: {\n            uint32_t data = gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff];\n\n            return data >> 24;\n        } break;\n        case GS_PSMT4: {\n            uint32_t addr = psmt4_addr(bp, bw, u, v);\n\n            int idx = (u & 31) + ((v & 3) * 32);\n            int shift = psmt4_shift[idx];\n\n            uint32_t mask = 0xful << shift;\n\n            return (gs->vram[addr & 0xfffff] & mask) >> shift;\n        } break;\n        case GS_PSMT4HL: {\n            uint32_t data = gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff];\n\n            return (data >> 24) & 0xf;\n        } break;\n        case GS_PSMT4HH: {\n            uint32_t data = gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff];\n\n            return data >> 28;\n        } break;\n        case GS_PSMZ32: {\n            return gs->vram[psmz32_addr(bp, bw, u, v) & 0xfffff];\n        } break;\n        case GS_PSMZ24: {\n            return gs->vram[psmz32_addr(bp, bw, u, v) & 0xfffff] & 0xffffff;\n        } break;\n        case GS_PSMZ16: {\n            uint32_t addr = psmz16_addr(bp, bw, u, v);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 1) * 16);\n\n            return vram[psmct16_shift[idx] & 0xfffff];\n        } break;\n        case GS_PSMZ16S: {\n            uint32_t addr = psmz16s_addr(bp, bw, u, v);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 1) * 16);\n\n            return vram[psmct16_shift[idx] & 0xfffff];\n        } break;\n        default: {\n            // printf(\"Unsupported PSMT %02x for generic read\\n\", gs->ctx->tbpsm);\n            // exit(1);\n        } break;\n    }\n}\n\nstatic inline void gs_generic_write(struct ps2_gs* gs, uint32_t bp, uint32_t bw, uint32_t bpsm, uint32_t u, uint32_t v, uint32_t data) {\n    switch (bpsm) {\n        case GS_PSMCT32:\n            gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff] = data;\n        break;\n        case GS_PSMCT24: {\n            uint32_t addr = psmct32_addr(bp, bw, u, v) & 0xfffff;\n\n            gs->vram[addr] = (gs->vram[addr] & 0xff000000) | (data & 0xffffff);\n        } break;\n        break;\n        case GS_PSMCT16: {\n            uint32_t addr = psmct16_addr(bp, bw, u, v);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 1) * 16);\n\n            vram[psmct16_shift[idx] & 0xfffff] = data;\n        } break;\n        case GS_PSMCT16S: {\n            uint32_t addr = psmct16s_addr(bp, bw, u, v);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 1) * 16);\n\n            vram[psmct16_shift[idx] & 0xfffff] = data;\n        } break;\n        case GS_PSMT8: {\n            uint32_t addr = psmt8_addr(bp, bw, u, v);\n            uint8_t* vram = (uint8_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 3) * 16);\n\n            vram[psmt8_shift[idx] & 0xfffff] = data;\n        } break;\n        case GS_PSMT8H: {\n            uint32_t addr = psmct32_addr(bp, bw, u, v) & 0xfffff;\n\n            gs->vram[addr] = (gs->vram[addr] & 0x00ffffff) | (data << 24);\n        } break;\n        case GS_PSMT4: {\n            uint32_t addr = psmt4_addr(bp, bw, u, v) & 0xfffff;\n\n            int idx = (u & 31) + ((v & 3) * 32);\n            int shift = psmt4_shift[idx];\n\n            uint32_t mask = 0xful << shift;\n\n            gs->vram[addr] = (gs->vram[addr] & ~mask) | (data << shift);\n        } break;\n        case GS_PSMT4HL: {\n            uint32_t addr = psmct32_addr(bp, bw, u, v) & 0xfffff;\n\n            gs->vram[addr] = (gs->vram[addr] & 0xf0ffffff) | ((data & 0xf) << 24);\n        } break;\n        case GS_PSMT4HH: {\n            uint32_t addr = psmct32_addr(bp, bw, u, v) & 0xfffff;\n\n            gs->vram[addr] = (gs->vram[addr] & 0x0fffffff) | ((data & 0xf) << 28);\n        } break;\n        case GS_PSMZ32:\n            gs->vram[psmz32_addr(bp, bw, u, v) & 0xfffff] = data;\n        break;\n        case GS_PSMZ24: {\n            uint32_t addr = psmz32_addr(bp, bw, u, v) & 0xfffff;\n\n            gs->vram[addr] = (gs->vram[addr] & 0xff000000) | (data & 0xffffff);\n        } break;\n        break;\n        case GS_PSMZ16: {\n            uint32_t addr = psmz16_addr(bp, bw, u, v);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 1) * 16);\n\n            vram[psmct16_shift[idx] & 0xfffff] = data;\n        } break;\n        case GS_PSMZ16S: {\n            uint32_t addr = psmz16s_addr(bp, bw, u, v);\n            uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]);\n\n            int idx = (u & 15) + ((v & 1) * 16);\n\n            vram[psmct16_shift[idx] & 0xfffff] = data;\n        } break;\n        default: {\n            // printf(\"Unsupported PSMT %02x for write\\n\", gs->ctx->tbpsm);\n            // exit(1);\n        } break;\n    }\n}\n\nstatic inline void software_thread_vram_blit(struct ps2_gs* gs, software_thread_state* ctx) {\n    // printf(\"dbp=%x (%x) dbw=%d (%d) dpsm=%02x dsa=(%d,%d) sbp=%x (%x) sbw=%d (%d) spsm=%02x ssa=(%d,%d) rr=(%d,%d) xdir=%d\\n\",\n    //     ctx->dbp, ctx->dbp,\n    //     ctx->dbw, ctx->dbw,\n    //     ctx->dpsm,\n    //     ctx->dsax,\n    //     ctx->dsay,\n    //     ctx->sbp, ctx->sbp,\n    //     ctx->sbw, ctx->sbw,\n    //     ctx->spsm,\n    //     ctx->ssax,\n    //     ctx->ssay,\n    //     ctx->rrw,\n    //     ctx->rrh,\n    //     ctx->xdir\n    // );\n\n    for (int y = 0; y < ctx->rrh; y++) {\n        for (int x = 0; x < ctx->rrw; x++) {\n            uint32_t s = gs_generic_read(gs, ctx->sbp, ctx->sbw, ctx->spsm, ctx->ssax + x, ctx->ssay + y);\n\n            gs_generic_write(gs, ctx->dbp, ctx->dbw, ctx->dpsm, ctx->dsax + x, ctx->dsay + y, s);\n        }\n    }\n}\n\nvoid render_point(struct ps2_gs* gs, void* udata) {\n    struct gs_vertex vert = gs->vq[0];\n\n    vert.x -= gs->ctx->ofx;\n    vert.y -= gs->ctx->ofy;\n\n    if (!gs_test_scissor(gs, vert.x >> 4, vert.y >> 4))\n        return;\n\n    gs_draw_pixel(gs, vert.x >> 4, vert.y >> 4, vert.z, vert.rgbaq & 0xffffffff);\n}\n\nvoid render_line(struct ps2_gs* gs, void* udata) {\n    struct gs_vertex v0 = gs->vq[0];\n    struct gs_vertex v1 = gs->vq[1];\n\n    // printf(\"v0=(%d,%d) v1=(%d,%d)\\n\", v0.x, v0.y, v1.x, v1.y);\n\n    v0.x -= gs->ctx->ofx;\n    v0.y -= gs->ctx->ofy;\n    v1.x -= gs->ctx->ofx;\n    v1.y -= gs->ctx->ofy;\n\n    int dx = abs(v1.x - v0.x);\n    int sx = v0.x < v1.x ? 1 : -1;\n    int dy = -abs(v1.y - v0.y);\n    int sy = v0.y < v1.y ? 1 : -1;\n    int error = dx + dy;\n\n    while (1) {\n        if (gs_test_scissor(gs, v0.x >> 4, v0.y >> 4))\n            gs_draw_pixel(gs, v0.x >> 4, v0.y >> 4, v0.z, v1.rgbaq & 0xffffffff);\n\n        int e2 = error << 1;\n    \n        if (e2 >= dy) {\n            if (v0.x == v1.x) break;\n\n            error = error + dy;\n            v0.x = v0.x + sx;\n        }\n\n        if (e2 <= dx) {\n            if (v0.y == v1.y) break;\n\n            error = error + dx;\n            v0.y = v0.y + sy;\n        }\n    }\n}\n\nvoid gs_draw_wireframe(struct ps2_gs* gs, struct gs_vertex v0, struct gs_vertex v1) {\n    v0.x -= gs->ctx->ofx;\n    v0.y -= gs->ctx->ofy;\n    v1.x -= gs->ctx->ofx;\n    v1.y -= gs->ctx->ofy;\n\n    int dx = abs(v1.x - v0.x);\n    int sx = v0.x < v1.x ? 1 : -1;\n    int dy = -abs(v1.y - v0.y);\n    int sy = v0.y < v1.y ? 1 : -1;\n    int error = dx + dy;\n\n    v0.x >>= 4;\n    v0.y >>= 4;\n    v1.x >>= 4;\n    v1.y >>= 4;\n\n    while (1) {\n        if (gs_test_scissor(gs, v0.x, v0.y))\n            gs_write_fb(gs, v0.x, v0.y, 0xff0000ff);\n\n        int e2 = error << 1;\n    \n        if (e2 >= dy) {\n            if (v0.x == v1.x) break;\n\n            error = error + dy;\n            v0.x = v0.x + sx;\n        }\n\n        if (e2 <= dx) {\n            if (v0.y == v1.y) break;\n\n            error = error + dx;\n            v0.y = v0.y + sy;\n        }\n    }\n}\n\n#define EDGE(a, b, c) ((b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x))\n#define MIN2(a, b) ((((int)a) < ((int)b)) ? ((int)a) : ((int)b))\n#define MIN3(a, b, c) (MIN2(MIN2(a, b), c))\n#define MAX2(a, b) ((((int)a) > ((int)b)) ? ((int)a) : ((int)b))\n#define MAX3(a, b, c) (MAX2(MAX2(a, b), c))\n\n#define IS_TOPLEFT(a, b) ((b.y > a.y) || ((a.y == b.y) && (b.x < a.x)))\n\nvoid render_triangle(struct ps2_gs* gs, void* udata) {\n    struct gs_vertex v0 = gs->vq[0];\n    struct gs_vertex v1 = gs->vq[1];\n    struct gs_vertex v2 = gs->vq[2];\n\n    int area = EDGE(v0, v1, v2);\n\n    if (area < 0) {\n        v1 = gs->vq[2];\n        v2 = gs->vq[1];\n\n        area = EDGE(v0, v1, v2);\n    }\n\n    // printf(\"triangle: v0=(%04x,%04x) v1=(%04x,%04x) v2=(%04x,%04x) ate=%d date=%d zte=%d ztst=%d zbpsm=%02x zbp=%x fbp=%x\\n\",\n    //     v0.x, v0.y,\n    //     v1.x, v1.y,\n    //     v2.x, v2.y,\n    //     gs->ctx->ate, gs->ctx->date, gs->ctx->zte, gs->ctx->ztst,\n    //     gs->ctx->zbpsm, gs->ctx->zbp, gs->ctx->fbp\n    // );\n\n    v0.x -= gs->ctx->ofx;\n    v1.x -= gs->ctx->ofx;\n    v2.x -= gs->ctx->ofx;\n    v0.y -= gs->ctx->ofy;\n    v1.y -= gs->ctx->ofy;\n    v2.y -= gs->ctx->ofy;\n\n    int32_t scax0 = gs->ctx->scax0 << 4;\n    int32_t scay0 = gs->ctx->scay0 << 4;\n    int32_t scax1 = gs->ctx->scax1 << 4;\n    int32_t scay1 = gs->ctx->scay1 << 4;\n\n    int32_t xmin = ((MAX2(MIN3(v0.x, v1.x, v2.x), scax0) + 0) >> 4) << 4;\n    int32_t ymin = ((MAX2(MIN3(v0.y, v1.y, v2.y), scay0) + 0) >> 4) << 4;\n    int32_t xmax = ((MIN2(MAX3(v0.x, v1.x, v2.x), scax1) + 16) >> 4) << 4;\n    int32_t ymax = ((MIN2(MAX3(v0.y, v1.y, v2.y), scay1) + 16) >> 4) << 4;\n\n    int a01 = (v0.y - v1.y) * 16, b01 = (v1.x - v0.x) * 16;\n    int a12 = (v1.y - v2.y) * 16, b12 = (v2.x - v1.x) * 16;\n    int a20 = (v2.y - v0.y) * 16, b20 = (v0.x - v2.x) * 16;\n\n    int fogr = gs->fogcol & 0xff;\n    int fogg = (gs->fogcol >> 8) & 0xff;\n    int fogb = (gs->fogcol >> 16) & 0xff;\n\n    struct {\n        int x, y;\n    } p;\n\n    p.x = xmin;\n    p.y = ymin;\n\n    int bias0 = IS_TOPLEFT(v1, v2) ? -1 : 0;\n    int bias1 = IS_TOPLEFT(v2, v0) ? -1 : 0;\n    int bias2 = IS_TOPLEFT(v0, v1) ? -1 : 0;\n    int w0_row = EDGE(v1, v2, p);\n    int w1_row = EDGE(v2, v0, p);\n    int w2_row = EDGE(v0, v1, p);\n\n    // printf(\"triangle: v0=(%d.%d,%d.%d) v1=(%d.%d,%d.%d) v2=(%d.%d,%d.%d) v3=(%d.%d,%d.%d)\\n\",\n    //     v0.x >> 4, (v0.x & 0xf) * 625, v0.y >> 4, (v0.y & 0xf) * 625,\n    //     v1.x >> 4, (v1.x & 0xf) * 625, v1.y >> 4, (v1.y & 0xf) * 625,\n    //     v2.x >> 4, (v2.x & 0xf) * 625, v2.y >> 4, (v2.y & 0xf) * 625,\n    //     gs->vq[3].x >> 4, (gs->vq[3].x & 0xf) * 625, gs->vq[3].y >> 4, (gs->vq[3].y & 0xf) * 625\n    // );\n\n    for (p.y = ymin; p.y < ymax; p.y += 16) {\n        // Barycentric coordinates at start of row\n        int w0 = w0_row;\n        int w1 = w1_row;\n        int w2 = w2_row;\n\n        for (p.x = xmin; p.x < xmax; p.x += 16) {\n            // If p is on or inside all edges, render pixel\n            if (((w0 + bias0) | (w1 + bias1) | (w2 + bias2)) < 0) {\n                w0 += a12;\n                w1 += a20;\n                w2 += a01;\n\n                continue;\n            }\n\n            // Calculate interpolation weights\n            double iw0 = (double)w0 / (double)area;\n            double iw1 = (double)w1 / (double)area;\n            double iw2 = (double)w2 / (double)area;\n\n            uint32_t fr, fg, fb, fa;\n\n            if (gs->iip) {\n                fr = roundf(v0.r * iw0 + v1.r * iw1 + v2.r * iw2);\n                fg = roundf(v0.g * iw0 + v1.g * iw1 + v2.g * iw2);\n                fb = roundf(v0.b * iw0 + v1.b * iw1 + v2.b * iw2);\n                fa = roundf(v0.a * iw0 + v1.a * iw1 + v2.a * iw2);\n            } else {\n                fr = v2.r;\n                fg = v2.g;\n                fb = v2.b;\n                fa = v2.a;\n            }\n\n            if (gs->tme) {\n                // Fixed-point 12:4\n                int u, v;\n\n                if (gs->fst) {\n                    u = v0.u * iw0 + v1.u * iw1 + v2.u * iw2;\n                    v = v0.v * iw0 + v1.v * iw1 + v2.v * iw2;\n                } else {\n                    float s = v0.s * iw0 + v1.s * iw1 + v2.s * iw2;\n                    float t = v0.t * iw0 + v1.t * iw1 + v2.t * iw2;\n                    float q = v0.q * iw0 + v1.q * iw1 + v2.q * iw2;\n\n                    float uf = (s / q) * gs->ctx->usize;\n                    float vf = (t / q) * gs->ctx->vsize;\n\n                    // Convert to 12:4 fixed-point\n                    u = uf * (1 << 4);\n                    v = vf * (1 << 4);\n                }\n\n                uint32_t f = fr | (fg << 8) | (fb << 16) | (fa << 24);\n\n                uint32_t t = gs_read_tb(gs, u, v);\n                t = gs_to_rgba32(gs, t, gs->ctx->tbpsm);\n                t = gs_apply_function(gs, t, f);\n\n                fr = t & 0xff;\n                fg = (t >> 8) & 0xff;\n                fb = (t >> 16) & 0xff;\n                fa = t >> 24;\n            }\n\n            if (gs->fge) {\n                int f = roundf(v0.fog * iw0 + v1.fog * iw1 + v2.fog * iw2);\n\n                fr = ((f * fr) >> 8) + (((255 - f) * fogr) >> 8);\n                fg = ((f * fg) >> 8) + (((255 - f) * fogg) >> 8);\n                fb = ((f * fb) >> 8) + (((255 - f) * fogb) >> 8);\n            }\n\n            uint32_t fz = roundf(v0.z * iw0 + v1.z * iw1 + v2.z * iw2);\n            uint32_t fc = fr | (fg << 8) | (fb << 16) | (fa << 24);\n\n            gs_draw_pixel(gs, p.x >> 4, p.y >> 4, fz, fc);\n\n            // One step to the right\n            w0 += a12;\n            w1 += a20;\n            w2 += a01;\n        }\n\n        // One row step\n        w0_row += b12;\n        w1_row += b20;\n        w2_row += b01;\n    }\n\n    // gs_draw_wireframe(gs, gs->vq[0], gs->vq[1]);\n    // gs_draw_wireframe(gs, gs->vq[1], gs->vq[2]);\n    // gs_draw_wireframe(gs, gs->vq[2], gs->vq[0]);\n}\n\nint32_t gs_lerp(int32_t x, int32_t u1, int32_t x1, int32_t u2, int32_t x2) {\n    if (!(x2 - x1))\n        return u1;\n\n    int64_t b = (int64_t)u1 * (x2 - x);\n    b += (int64_t)u2 * (x - x1);\n    return b / (x2 - x1);\n}\n\nint32_t gs_step(int32_t u1, int32_t x1, int32_t u2, int32_t x2, int64_t mult) {\n    if (!(x2 - x1))\n        return ((u2 - u1) * mult);\n    return ((u2 - u1) * mult)/(x2 - x1);\n}\n\nfloat gs_lerpf(int32_t x, float u1, int32_t x1, float u2, int32_t x2) {\n    if (x2 < x1) {\n        int temp = x1;\n\n        x1 = x2;\n        x2 = temp;\n    }\n\n    float b = u1 * (x2 - x);\n    b += u2 * (x - x1);\n    if (!(x2 - x1))\n        return u1;\n    return b / (x2 - x1);\n}\n\nfloat gs_stepf(float u1, int32_t x1, float u2, int32_t x2, int64_t mult) {\n    if (!(x2 - x1))\n        return ((u2 - u1) * mult);\n    return ((u2 - u1) * mult)/(x2 - x1);\n}\n\nvoid render_sprite(struct ps2_gs* gs, void* udata) {\n    struct gs_vertex v0 = gs->vq[0];\n    struct gs_vertex v1 = gs->vq[1];\n\n    v0.x -= gs->ctx->ofx;\n    v1.x -= gs->ctx->ofx;\n    v0.y -= gs->ctx->ofy;\n    v1.y -= gs->ctx->ofy;\n\n    int32_t scax0 = gs->ctx->scax0 << 4;\n    int32_t scay0 = gs->ctx->scay0 << 4;\n    int32_t scax1 = (gs->ctx->scax1 + 1) << 4;\n    int32_t scay1 = (gs->ctx->scay1 + 1) << 4;\n\n    int32_t xmin = ((std::max(std::min(v0.x, v1.x), scax0) + 8) >> 4) << 4;\n    int32_t ymin = ((std::max(std::min(v0.y, v1.y), scay0) + 8) >> 4) << 4;\n    int32_t xmax = ((std::min(std::max(v0.x, v1.x), scax1) + 8) >> 4) << 4;\n    int32_t ymax = ((std::min(std::max(v0.y, v1.y), scay1) + 8) >> 4) << 4;\n\n    // printf(\"sprite: v0=(%d,%d) v1=(%d,%d) sca1=(%04x,%04x) rsca1=(%04x,%04x)\\n\",\n    //     v0.x >> 4, v0.y >> 4,\n    //     v1.x >> 4, v1.y >> 4,\n    //     scax1, scay1,\n    //     gs->ctx->scax1 << 4,\n    //     gs->ctx->scay1 << 4\n    // );\n\n    // if (gs->tme)\n    // printf(\"sprite: v0=(%04x,%04x) v1=(%04x,%04x) tbpsm=%02x tbw=%x tbp=%x fbp=%x fbpsm=%02x zbp=%x zbpsm=%02x zte=%d zmsk=%d cbp=%x cbpsm=%02x\\n\",\n    //     v0.x, v0.y,\n    //     v1.x, v1.y,\n    //     gs->ctx->tbpsm,\n    //     gs->ctx->tbw,\n    //     gs->ctx->tbp0,\n    //     gs->ctx->fbp >> 6,\n    //     gs->ctx->fbpsm,\n    //     gs->ctx->zbp >> 6,\n    //     gs->ctx->zbpsm,\n    //     gs->ctx->zte,\n    //     gs->ctx->zbmsk,\n    //     gs->ctx->cbp >> 6,\n    //     gs->ctx->cbpsm\n    // );\n\n    // U and S values at the start of a row\n    float row_u = gs_lerpf(xmin, v0.u, v0.x, v1.u, v1.x);\n    float row_s = gs_lerpf(xmin, v0.s, v0.x, v1.s, v1.x);\n\n    // V and T values at the start of the entire quad\n    float v = gs_lerpf(ymin, v0.v, v0.y, v1.v, v1.y);\n    float t = gs_lerpf(ymin, v0.t, v0.y, v1.t, v1.y);\n\n    // Step values for U and V\n    float u_step = gs_stepf(v0.u, v0.x, v1.u, v1.x, 16);\n    float v_step = gs_stepf(v0.v, v0.y, v1.v, v1.y, 16);\n    float s_step = gs_stepf(v0.s, v0.x, v1.s, v1.x, 16);\n    float t_step = gs_stepf(v0.t, v0.y, v1.t, v1.y, 16);\n\n    // Static values\n    const float q = v1.q;\n    const int z = v1.z;\n    int a = v1.a;\n\n    for (int y = ymin; y < ymax; y += 16) {\n        float u = row_u;\n        float s = row_s;\n\n        for (int x = xmin; x < xmax; x += 16) {\n            uint32_t c = v1.rgbaq & 0xffffffff;\n\n            if (gs->tme) {\n                if (!gs->fst) {\n                    u = ((s / q) * gs->ctx->usize) * 16.0;\n                    v = ((t / q) * gs->ctx->vsize) * 16.0;\n                }\n\n                c = gs_read_tb(gs, u, v);\n                c = gs_to_rgba32(gs, c, gs->ctx->tbpsm);\n                c = gs_apply_function(gs, c, v1.rgbaq & 0xffffffff);\n\n                a = c >> 24;\n            }\n\n            gs_draw_pixel(gs, x >> 4, y >> 4, z, c);\n\n            u += u_step;\n            s += s_step;\n        }\n\n        v += v_step;\n        t += t_step;\n    }\n\n    // struct gs_vertex dv0 = gs->vq[0];\n    // struct gs_vertex dv1 = gs->vq[0];\n    // struct gs_vertex dv2 = gs->vq[1];\n    // struct gs_vertex dv3 = gs->vq[1];\n\n    // dv1.x = gs->vq[1].x;\n    // dv1.y = gs->vq[0].y;\n    // dv3.x = gs->vq[0].x;\n    // dv3.y = gs->vq[1].y;\n\n    // gs_draw_wireframe(gs, dv0, dv1);\n    // gs_draw_wireframe(gs, dv1, dv2);\n    // gs_draw_wireframe(gs, dv2, dv3);\n    // gs_draw_wireframe(gs, dv3, dv0);\n}\n\nstatic inline int gs_pixels_to_size(int fmt, int px) {\n    switch (fmt) {\n        case GS_PSMCT32:\n        case GS_PSMCT24:\n        case GS_PSMT8H:\n        case GS_PSMT4HL:\n        case GS_PSMT4HH:\n            return px;\n        case GS_PSMCT16:\n        case GS_PSMCT16S:\n            return px >> 1;\n        case GS_PSMT8:\n            return px >> 2;\n        case GS_PSMT4:\n            return px >> 3;\n    }\n    return px;\n}\n\nstatic inline void gs_write_psmct32(struct ps2_gs* gs, software_thread_state* ctx, uint32_t data) {\n    uint32_t addr = psmct32_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy);\n\n    ctx->dx++;\n\n    gs->vram[addr & 0xfffff] = data;\n\n    if (ctx->dx == (ctx->rrw + ctx->dsax)) {\n        ctx->dx = ctx->dsax;\n        ctx->dy++;\n    }\n}\n\nstatic inline void gs_write_psmt4hh(struct ps2_gs* gs, software_thread_state* ctx, uint32_t data) {\n    uint32_t addr = psmct32_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy);\n\n    ctx->dx++;\n\n    gs->vram[addr] = (gs->vram[addr] & 0x0fffffff) | (data << 28);\n\n    if (ctx->dx == (ctx->rrw + ctx->dsax)) {\n        ctx->dx = ctx->dsax;\n        ctx->dy++;\n    }\n}\n\nstatic inline void gs_write_psmt4hl(struct ps2_gs* gs, software_thread_state* ctx, uint32_t data) {\n    uint32_t addr = psmct32_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy);\n\n    ctx->dx++;\n\n    gs->vram[addr] = (gs->vram[addr] & 0xf0ffffff) | (data << 24);\n\n    if (ctx->dx == (ctx->rrw + ctx->dsax)) {\n        ctx->dx = ctx->dsax;\n        ctx->dy++;\n    }\n}\n\nstatic inline void gs_write_psmt4(struct ps2_gs* gs, software_thread_state* ctx, uint32_t index) {\n    uint32_t addr = psmt4_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy) & 0xfffff;\n\n    int idx = (ctx->dx & 31) + ((ctx->dy & 3) * 32);\n    int shift = psmt4_shift[idx];\n\n    uint32_t mask = 0xful << shift;\n\n    gs->vram[addr] = (gs->vram[addr] & ~mask) | (index << shift);\n\n    ctx->dx++;\n\n    if (ctx->dx == (ctx->rrw + ctx->dsax)) {\n        ctx->dx = ctx->dsax;\n        ctx->dy++;\n    }\n}\n\nstatic inline void gs_write_psmt8(struct ps2_gs* gs, software_thread_state* ctx, uint32_t index) {\n    uint32_t addr = psmt8_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy) & 0xfffff;\n    uint8_t* vram = (uint8_t*)(&gs->vram[addr]);\n\n    int idx = (ctx->dx & 15) + ((ctx->dy & 3) * 16);\n\n    vram[psmt8_shift[idx]] = index;\n\n    ctx->dx++;\n\n    if (ctx->dx == (ctx->rrw + ctx->dsax)) {\n        ctx->dx = ctx->dsax;\n        ctx->dy++;\n    }\n}\n\nstatic inline void gs_write_psmt8h(struct ps2_gs* gs, software_thread_state* ctx, uint32_t data) {\n    uint32_t addr = psmct32_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy);\n\n    ctx->dx++;\n\n    gs->vram[addr] = (gs->vram[addr] & 0x00ffffff) | (data << 24);\n\n    if (ctx->dx == (ctx->rrw + ctx->dsax)) {\n        ctx->dx = ctx->dsax;\n        ctx->dy++;\n    }\n}\n\nstatic inline void gs_store_hwreg_psmt4(struct ps2_gs* gs, software_thread_state* ctx) {\n    for (int i = 0; i < 16; i++) {\n        uint64_t index = (gs->hwreg >> (i * 4)) & 0xf;\n\n        gs_write_psmt4(gs, ctx, index);\n    }\n}\n\nstatic inline void gs_store_hwreg_psmt4hh(struct ps2_gs* gs, software_thread_state* ctx) {\n    for (int i = 0; i < 16; i++) {\n        uint64_t index = (gs->hwreg >> (i * 4)) & 0xf;\n\n        gs_write_psmt4hh(gs, ctx, index);\n    }\n}\n\nstatic inline void gs_store_hwreg_psmt4hl(struct ps2_gs* gs, software_thread_state* ctx) {\n    for (int i = 0; i < 16; i++) {\n        uint64_t index = (gs->hwreg >> (i * 4)) & 0xf;\n\n        gs_write_psmt4hl(gs, ctx, index);\n    }\n}\n\nstatic inline void gs_store_hwreg_psmt8(struct ps2_gs* gs, software_thread_state* ctx) {\n    for (int i = 0; i < 8; i++) {\n        uint64_t index = (gs->hwreg >> (i * 8)) & 0xff;\n\n        gs_write_psmt8(gs, ctx, index);\n    }\n}\n\nstatic inline void gs_store_hwreg_psmt8h(struct ps2_gs* gs, software_thread_state* ctx) {\n    for (int i = 0; i < 8; i++) {\n        uint64_t index = (gs->hwreg >> (i * 8)) & 0xff;\n\n        gs_write_psmt8h(gs, ctx, index);\n    }\n}\n\nstatic inline void gs_store_hwreg_psmct32(struct ps2_gs* gs, software_thread_state* ctx) {\n    uint64_t data[2] = {\n        gs->hwreg & 0xffffffff,\n        gs->hwreg >> 32\n    };\n\n    for (int i = 0; i < 2; i++) {\n        gs_write_psmct32(gs, ctx, data[i]);\n    }\n}\n\nstatic inline void gs_write_psmct24(struct ps2_gs* gs, software_thread_state* ctx, uint32_t data) {\n    uint32_t addr = psmct32_addr(ctx->dbp, ctx->dbw, ctx->dx++, ctx->dy) & 0xfffff;\n\n    gs->vram[addr] = (gs->vram[addr] & 0xff000000) | (data & 0xffffff);\n\n    if (ctx->dx == (ctx->rrw + ctx->dsax)) {\n        ctx->dx = ctx->dsax;\n        ctx->dy++;\n    }\n}\n\nstatic inline void gs_store_hwreg_psmct24(struct ps2_gs* gs, software_thread_state* ctx) {\n    switch (ctx->psmct24_shift++) {\n        case 0: {\n            gs_write_psmct24(gs, ctx, gs->hwreg);\n            gs_write_psmct24(gs, ctx, gs->hwreg >> 24);\n            \n            ctx->psmct24_data = gs->hwreg >> 48;\n        } break;\n\n        case 1: {\n            gs_write_psmct24(gs, ctx, ctx->psmct24_data | ((gs->hwreg & 0xff) << 16));\n            gs_write_psmct24(gs, ctx, gs->hwreg >> 8);\n            gs_write_psmct24(gs, ctx, gs->hwreg >> 32);\n\n            ctx->psmct24_data = gs->hwreg >> 56;\n        } break;\n\n        case 2: {\n            gs_write_psmct24(gs, ctx, ctx->psmct24_data | ((gs->hwreg & 0xffff) << 8));\n            gs_write_psmct24(gs, ctx, gs->hwreg >> 16);\n            gs_write_psmct24(gs, ctx, gs->hwreg >> 40);\n\n            ctx->psmct24_shift = 0;\n        } break;\n    }\n}\n\nstatic inline void gs_write_psmct16(struct ps2_gs* gs, software_thread_state* ctx, uint32_t index) {\n    uint32_t addr = psmct16_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy) & 0xfffff;\n    uint16_t* vram = (uint16_t*)(&gs->vram[addr]);\n\n    int idx = (ctx->dx & 15) + ((ctx->dy & 1) * 16);\n\n    vram[psmct16_shift[idx]] = index;\n\n    ctx->dx++;\n\n    if (ctx->dx == (ctx->rrw + ctx->dsax)) {\n        ctx->dx = ctx->dsax;\n        ctx->dy++;\n    }\n}\n\nstatic inline void gs_write_psmct16s(struct ps2_gs* gs, software_thread_state* ctx, uint32_t index) {\n    uint32_t addr = psmct16s_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy) & 0xfffff;\n    uint16_t* vram = (uint16_t*)(&gs->vram[addr]);\n\n    int idx = (ctx->dx & 15) + ((ctx->dy & 1) * 16);\n\n    vram[psmct16_shift[idx]] = index;\n\n    ctx->dx++;\n\n    if (ctx->dx == (ctx->rrw + ctx->dsax)) {\n        ctx->dx = ctx->dsax;\n        ctx->dy++;\n    }\n}\n\nstatic inline void gs_store_hwreg_psmct16(struct ps2_gs* gs, software_thread_state* ctx) {\n    for (int i = 0; i < 4; i++) {\n        uint64_t p = (gs->hwreg >> (i * 16)) & 0xffff;\n\n        gs_write_psmct16(gs, ctx, p);\n    }\n}\n\nstatic inline void gs_store_hwreg_psmct16s(struct ps2_gs* gs, software_thread_state* ctx) {\n    for (int i = 0; i < 4; i++) {\n        uint64_t p = (gs->hwreg >> (i * 16)) & 0xffff;\n\n        gs_write_psmct16s(gs, ctx, p);\n    }\n}\n\nvoid transfer_flush_buffer(software_thread_state* ctx) {\n    // printf(\"gs: Flushing transfer... %d (%d)\\n\", ctx->transfer_buffer.size(), ctx->transfer_size);\n\n    ctx->render_mtx.lock();\n\n    for (int i = 0; i < ctx->transfer_buffer.size(); i++) {\n        ctx->gs->hwreg = ctx->transfer_buffer[i];\n\n        switch (ctx->dpsm) {\n            case GS_PSMCT32: {\n                gs_store_hwreg_psmct32(ctx->gs, ctx);\n            } break;\n\n            case GS_PSMCT24: {\n                gs_store_hwreg_psmct24(ctx->gs, ctx);\n            } break;\n\n            case GS_PSMCT16: {\n                gs_store_hwreg_psmct16(ctx->gs, ctx);\n            } break;\n\n            case GS_PSMCT16S: {\n                gs_store_hwreg_psmct16s(ctx->gs, ctx);\n            } break;\n\n            case GS_PSMT8: {\n                gs_store_hwreg_psmt8(ctx->gs, ctx);\n            } break;\n\n            case GS_PSMT8H: {\n                gs_store_hwreg_psmt8h(ctx->gs, ctx);\n            } break;\n\n            case GS_PSMT4: {\n                gs_store_hwreg_psmt4(ctx->gs, ctx);\n            } break;\n\n            case GS_PSMT4HH: {\n                gs_store_hwreg_psmt4hh(ctx->gs, ctx);\n            } break;\n\n            case GS_PSMT4HL: {\n                gs_store_hwreg_psmt4hl(ctx->gs, ctx);\n            } break;\n\n            default: {\n                gs_store_hwreg_psmct32(ctx->gs, ctx);\n            } break;\n        }\n    }\n\n    ctx->render_mtx.unlock();\n}\n\nvoid transfer_start(struct ps2_gs* gs, void* udata) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    if (ctx->transfer_size != 0) {\n        transfer_flush_buffer(ctx);\n    }\n\n    ctx->dbp = (gs->bitbltbuf >> 32) & 0x3fff;\n    ctx->dbw = (gs->bitbltbuf >> 48) & 0x3f;\n    ctx->dpsm = (gs->bitbltbuf >> 56) & 0x3f;\n    ctx->sbp = (gs->bitbltbuf >> 0) & 0x3fff;\n    ctx->sbw = (gs->bitbltbuf >> 16) & 0x3f;\n    ctx->spsm = (gs->bitbltbuf >> 24) & 0x3f;\n    ctx->dsax = (gs->trxpos >> 32) & 0x7ff;\n    ctx->dsay = (gs->trxpos >> 48) & 0x7ff;\n    ctx->ssax = (gs->trxpos >> 0) & 0x7ff;\n    ctx->ssay = (gs->trxpos >> 16) & 0x7ff;\n    ctx->dir = (gs->trxpos >> 59) & 3;\n    ctx->rrw = (gs->trxreg >> 0) & 0xfff;\n    ctx->rrh = (gs->trxreg >> 32) & 0xfff;\n    ctx->xdir = gs->trxdir & 3;\n    ctx->dx = ctx->dsax;\n    ctx->dy = ctx->dsay;\n    ctx->sx = ctx->ssax;\n    ctx->sy = ctx->ssay;\n    ctx->px = 0;\n\n    unsigned int pixels = ctx->rrw * ctx->rrh;\n\n    // size in pixels = rrw*rrh\n    // size in dwords (psmct32): pixels / 2\n    // size in dwords (psmct24): floor(pixels / 3) + 1\n    // size in dwords (psmct16/s): pixels / 4\n    // size in dwords (psmt8/h): pixels / 8\n    // size in dwords (psmt4/h/hl/hh): pixels / 16\n\n    switch (ctx->dpsm) {\n        case GS_PSMZ32:\n        case GS_PSMCT32: {\n            ctx->transfer_size = pixels >> 1;\n        } break;\n        case GS_PSMCT24: {\n            ctx->transfer_size = (pixels / 3) + 1;\n        } break;\n        case GS_PSMCT16:\n        case GS_PSMCT16S: {\n            ctx->transfer_size = pixels >> 2;\n        } break;\n        case GS_PSMT8:\n        case GS_PSMT8H: {\n            ctx->transfer_size = pixels >> 3;\n        } break;\n        case GS_PSMT4:\n        case GS_PSMT4HL:\n        case GS_PSMT4HH: {\n            ctx->transfer_size = pixels >> 4;\n        } break;\n\n        // Assume 32-bit (Viewtiful Joe, Viewtiful Joe 2, etc.)\n        default: {\n            ctx->transfer_size = pixels >> 1;\n\n            // printf(\"gs: Unknown transfer format %02x\\n\", ctx->dpsm);\n\n            // exit(1);\n        } break;\n    }\n\n    ctx->transfer_buffer.clear();\n    ctx->transfer_buffer.reserve(ctx->transfer_size);\n\n    ctx->psmct24_data = 0;\n    ctx->psmct24_shift = 0;\n\n    // printf(\"dbp=%x (%x) dbw=%d (%d) dpsm=%02x dsa=(%d,%d) rr=(%d,%d) xdir=%d transfer_size=%d\\n\",\n    //     ctx->dbp, ctx->dbp,\n    //     ctx->dbw, ctx->dbw,\n    //     ctx->dpsm,\n    //     ctx->dsax,\n    //     ctx->dsay,\n    //     ctx->rrw,\n    //     ctx->rrh,\n    //     ctx->xdir,\n    //     ctx->transfer_size\n    // );\n\n    if (ctx->xdir == 2) {\n        ctx->render_mtx.lock();\n\n        ctx->stats.texture_blits++;\n\n        software_thread_vram_blit(gs, ctx);\n\n        ctx->render_mtx.unlock();\n    } else if (ctx->xdir == 1) {\n        printf(\"gs: Read transfer requested\\n\");\n\n        // exit(1);\n    } else {\n        ctx->stats.texture_uploads++;\n    }\n}\n\nvoid transfer_write(struct ps2_gs* gs, void* udata) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    ctx->transfer_buffer.push_back(gs->hwreg);\n    ctx->transfer_size--;\n\n    if (ctx->transfer_size == 0) {\n        transfer_flush_buffer(ctx);\n    }\n}\n\nvoid transfer_read(struct ps2_gs* gs, void* udata) {\n    gs->hwreg = 0;\n}\n\nextern \"C\" void software_thread_render_point(struct ps2_gs* gs, void* udata) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    ctx->stats.points++;\n    ctx->stats.primitives++;\n\n    ctx->queue_mtx.lock();\n    ctx->render_queue.push(render_data());\n\n    render_data& rdata = ctx->render_queue.back();\n\n    rdata.gs = *gs;\n    rdata.prim = 0;\n    ctx->queue_mtx.unlock();\n}\n\nextern \"C\" void software_thread_render_line(struct ps2_gs* gs, void* udata) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    ctx->stats.lines++;\n    ctx->stats.primitives++;\n\n    ctx->queue_mtx.lock();\n    ctx->render_queue.push(render_data());\n\n    render_data& rdata = ctx->render_queue.back();\n\n    rdata.gs = *gs;\n    rdata.prim = 1;\n    ctx->queue_mtx.unlock();\n}\n\nextern \"C\" void software_thread_render_triangle(struct ps2_gs* gs, void* udata) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    ctx->stats.triangles++;\n    ctx->stats.primitives++;\n\n    ctx->queue_mtx.lock();\n    ctx->render_queue.push(render_data());\n\n    render_data& rdata = ctx->render_queue.back();\n\n    rdata.gs = *gs;\n    rdata.prim = 2;\n    ctx->queue_mtx.unlock();\n}\n\nextern \"C\" void software_thread_render_sprite(struct ps2_gs* gs, void* udata) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    ctx->stats.sprites++;\n    ctx->stats.primitives++;\n\n    ctx->queue_mtx.lock();\n    ctx->render_queue.push(render_data());\n\n    render_data& rdata = ctx->render_queue.back();\n\n    rdata.gs = *gs;\n    rdata.prim = 3;\n    ctx->queue_mtx.unlock();\n}\n\nextern \"C\" void software_thread_transfer_start(struct ps2_gs* gs, void* udata) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    // Block until queue is empty\n    while (true) {\n        ctx->queue_mtx.lock();\n\n        if (ctx->render_queue.empty()) {\n            ctx->queue_mtx.unlock();\n\n            break;\n        }\n\n        ctx->queue_mtx.unlock();\n    }\n\n    transfer_start(gs, ctx);\n}\n\nextern \"C\" void software_thread_transfer_write(struct ps2_gs* gs, void* udata) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    transfer_write(gs, ctx);\n}\n\nextern \"C\" void software_thread_transfer_read(struct ps2_gs* gs, void* udata) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    // Block until queue is empty\n    while (true) {\n        ctx->queue_mtx.lock();\n\n        if (ctx->render_queue.empty()) {\n            ctx->queue_mtx.unlock();\n\n            break;\n        }\n\n        ctx->queue_mtx.unlock();\n    }\n\n    // ctx->render_mtx.lock();\n\n    transfer_read(gs, ctx);\n\n    // ctx->render_mtx.unlock();\n}\n\nvoid gs_blit_dispfb_deinterlace_frame(software_thread_state* ctx, int dfb) {\n    // Get current field\n    int odd = ((ctx->gs->csr >> 13) & 1) == 0;\n\n    switch (ctx->disp_fmt) {\n        case GS_PSMCT32:\n        case GS_PSMCT24: {\n            for (int y = 0; y < ctx->tex_h / 2; y++) {\n                for (int x = 0; x < ctx->tex_w; x++) {\n                    uint32_t* dst = ctx->buf + x + (((y * 2) + odd) * ctx->tex_w);\n\n                    *dst = gs_read_dispfb(ctx->gs, x, y, dfb);\n                }\n            }\n        } break;\n        case GS_PSMCT16:\n        case GS_PSMCT16S: {\n            for (int y = 0; y < ctx->tex_h / 2; y++) {\n                for (int x = 0; x < ctx->tex_w; x++) {\n                    uint16_t* dst = ((uint16_t*)ctx->buf) + x + (((y * 2) + odd) * ctx->tex_w);\n\n                    *dst = gs_read_dispfb(ctx->gs, x, y, dfb);\n                }\n            }\n        } break;\n    }\n}\n\nvoid gs_blit_dispfb_deinterlace_field(software_thread_state* ctx, int dfb) {\n    // Get current field\n    int odd = ((ctx->gs->csr >> 13) & 1) == 0;\n\n    switch (ctx->disp_fmt) {\n        case GS_PSMCT32:\n        case GS_PSMCT24: {\n            for (int y = 0; y < ctx->tex_h / 2; y++) {\n                for (int x = 0; x < ctx->tex_w; x++) {\n                    uint32_t* dst = ctx->buf + x + (((y * 2) + odd) * ctx->tex_w);\n\n                    *dst = gs_read_dispfb(ctx->gs, x, (y * 2) + odd, dfb);\n                }\n            }\n        } break;\n        case GS_PSMCT16:\n        case GS_PSMCT16S: {\n            for (int y = 0; y < ctx->tex_h / 2; y++) {\n                for (int x = 0; x < ctx->tex_w; x++) {\n                    uint16_t* dst = ((uint16_t*)ctx->buf) + x + (((y * 2) + odd) * ctx->tex_w);\n\n                    *dst = gs_read_dispfb(ctx->gs, x, (y * 2) + odd, dfb);\n                }\n            }\n        } break;\n    }\n}\n\nvoid gs_blit_dispfb_no_deinterlace(software_thread_state* ctx, int dfb) {\n    switch (ctx->disp_fmt) {\n        case GS_PSMCT32:\n        case GS_PSMCT24: {\n            for (int y = 0; y < ctx->tex_h; y++) {\n                for (int x = 0; x < ctx->tex_w; x++) {\n                    uint32_t* dst = ctx->buf + x + (y * ctx->tex_w);\n\n                    *dst = gs_read_dispfb(ctx->gs, x, y, dfb);\n                }\n            }\n        } break;\n        case GS_PSMCT16:\n        case GS_PSMCT16S: {\n            for (int y = 0; y < ctx->tex_h; y++) {\n                for (int x = 0; x < ctx->tex_w; x++) {\n                    uint16_t* dst = ((uint16_t*)ctx->buf) + x + (y * ctx->tex_w);\n\n                    *dst = gs_read_dispfb(ctx->gs, x, y, dfb);\n                }\n            }\n        } break;\n    }\n}\n\n// This starts a copy pass and transfers our vertex/index buffers and our\n// display frame\nvoid software_thread_begin_render(void* udata, SDL_GPUCommandBuffer* command_buffer) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    // Block until queue is empty\n    while (true) {\n        ctx->queue_mtx.lock();\n\n        if (ctx->render_queue.empty()) {\n            ctx->queue_mtx.unlock();\n\n            break;\n        }\n\n        ctx->queue_mtx.unlock();\n    }\n\n    int en1 = ctx->gs->pmode & 1;\n    int en2 = (ctx->gs->pmode >> 1) & 1;\n\n    uint32_t dfbp = 0;\n    int dfb = 0;\n\n    if (en1) {\n        dfb = 0;\n        dfbp = ctx->gs->dispfb1;\n    } else if (en2) {\n        dfb = 1;\n        dfbp = ctx->gs->dispfb2;\n    }\n\n    if (!ctx->tex_w) {\n        return;\n    }\n\n    // printf(\"dfbp2=%08x dfbp1=%08x dfbpsm2=%02x dfbpsm1=%02x dfb=%d fbp=%x\\n\", ctx->gs->dfbp2, ctx->gs->dfbp1, ctx->gs->dfbpsm2, ctx->gs->dfbpsm1, dfb, ctx->gs->ctx->fbp >> 6);\n\n    if (ctx->gs->smode2 & 1) {\n        // Need to deinterlace\n        if (ctx->gs->smode2 & 2) {\n            gs_blit_dispfb_deinterlace_frame(ctx, dfb);\n        } else {\n            gs_blit_dispfb_no_deinterlace(ctx, dfb);\n        }\n    } else {\n        gs_blit_dispfb_no_deinterlace(ctx, dfb);\n    }\n\n    uint32_t* ptr = ctx->buf;\n\n    SDL_Rect size, rect;\n\n    rect.w = ctx->tex_w;\n    rect.h = ctx->tex_h;\n\n    SDL_GetWindowSize(ctx->window, &size.w, &size.h);\n\n    float scale = ctx->integer_scaling ? floorf(ctx->scale) : ctx->scale;\n\n    switch (ctx->aspect_mode) {\n        case RENDERER_ASPECT_NATIVE: {\n            rect.w *= scale;\n            rect.h *= scale;\n        } break;\n\n        case RENDERER_ASPECT_4_3: {\n            rect.w *= scale;\n            rect.h = (float)rect.w * (3.0f / 4.0f);\n        } break;\n\n        case RENDERER_ASPECT_16_9: {\n            rect.w *= scale;\n            rect.h = (float)rect.w * (9.0f / 16.0f);\n        } break;\n\n        case RENDERER_ASPECT_5_4: {\n            rect.w *= scale;\n            rect.h = (float)rect.w * (4.0f / 5.0f);\n        } break;\n\n        case RENDERER_ASPECT_STRETCH: {\n            rect.w = size.w;\n            rect.h = size.h;\n        } break;\n\n        case RENDERER_ASPECT_AUTO:\n        case RENDERER_ASPECT_STRETCH_KEEP: {\n            rect.h = size.h;\n            rect.w = (float)rect.h * (4.0f / 3.0f);\n        } break;\n    }\n\n    ctx->disp_w = rect.w;\n    ctx->disp_h = rect.h;\n\n    rect.x = (size.w / 2) - (rect.w / 2);\n    rect.y = (size.h / 2) - (rect.h / 2);\n\n    float x0 = (rect.x / ((float)size.w / 2.0f)) - 1.0f;\n    float y0 = (rect.y / ((float)size.h / 2.0f)) - 1.0f;\n    float x1 = ((rect.x + rect.w) / ((float)size.w / 2.0f)) - 1.0f;\n    float y1 = ((rect.y + rect.h) / ((float)size.h / 2.0f)) - 1.0f;\n\n    // Create a transfer buffer to upload our vertex buffer and index buffer\n    SDL_GPUTransferBufferCreateInfo btbci = {\n        .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,\n        .size = sizeof(gpu_vertex) * 4 + sizeof(Uint16) * 6,\n    };\n\n    SDL_GPUTransferBuffer* buffer_tb = SDL_CreateGPUTransferBuffer(ctx->device, &btbci);\n\n    // fill the transfer buffer\n    gpu_vertex* vertices = (gpu_vertex*)SDL_MapGPUTransferBuffer(ctx->device, buffer_tb, false);\n\n    vertices[0] = { x0, y1, 0.0, 1.0 }; // Top left\n    vertices[1] = { x1, y1, 1.0, 1.0 }; // Top right\n    vertices[2] = { x1, y0, 1.0, 0.0 }; // Bottom right\n    vertices[3] = { x0, y0, 0.0, 0.0 }; // Bottom left\n\n    Uint16* indices = (Uint16*)&vertices[4];\n\n    indices[0] = 0;\n    indices[1] = 1;\n    indices[2] = 2;\n    indices[3] = 0;\n    indices[4] = 2;\n    indices[5] = 3;\n\n    SDL_UnmapGPUTransferBuffer(ctx->device, buffer_tb);\n\n    // start a copy pass\n    SDL_GPUCopyPass* cp = SDL_BeginGPUCopyPass(command_buffer);\n\n    // Upload vertex buffer (at offset 0)\n    SDL_GPUTransferBufferLocation tbl = {\n        .transfer_buffer = buffer_tb,\n        .offset = 0,\n    };\n\n    SDL_GPUBufferRegion br = {\n        .buffer = ctx->vertex_buffer,\n        .offset = 0,\n        .size = sizeof(gpu_vertex) * 4,\n    };\n\n    SDL_UploadToGPUBuffer(cp, &tbl, &br, false);\n\n    tbl = {\n        .transfer_buffer = buffer_tb,\n        .offset = sizeof(gpu_vertex) * 4,\n    };\n\n    br = {\n        .buffer = ctx->index_buffer,\n        .offset = 0,\n        .size = sizeof(Uint16) * 6,\n    };\n\n    // Upload index buffer (right after our vertex data)\n    SDL_UploadToGPUBuffer(cp, &tbl, &br, false);\n\n    int stride = 4;\n\n    switch (ctx->disp_fmt) {\n        case GS_PSMCT32:\n        case GS_PSMCT24: {\n            stride = 4;\n        } break;\n\n        case GS_PSMCT16:\n        case GS_PSMCT16S: {\n            stride = 2;\n        } break;\n\n        // Ignore unknown formats\n        // default: {\n        //     printf(\"Unsupported display format %d\\n\", ctx->disp_fmt);\n            \n        //     exit(1);\n        // } break;\n    }\n\n    SDL_GPUTransferBufferCreateInfo ttbci = {\n        .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD,\n        .size = static_cast<Uint32>(ctx->tex_w) * static_cast<Uint32>(ctx->tex_h) * stride\n    };\n\n    SDL_GPUTransferBuffer* texture_tb = SDL_CreateGPUTransferBuffer(ctx->device, &ttbci);\n\n    // fill the transfer buffer\n    void* data = SDL_MapGPUTransferBuffer(ctx->device, texture_tb, false);\n\n    SDL_memcpy(data, ptr, (ctx->tex_w * stride) * ctx->tex_h);\n\n    SDL_UnmapGPUTransferBuffer(ctx->device, texture_tb);\n\n    SDL_GPUTextureTransferInfo tti = {\n        .transfer_buffer = texture_tb,\n        .offset = 0,\n    };\n\n    SDL_GPUTextureRegion tr = {\n        .texture = ctx->texture,\n        .w = static_cast<Uint32>(ctx->tex_w),\n        .h = static_cast<Uint32>(ctx->tex_h),\n        .d = 1,\n    };\n\n    SDL_UploadToGPUTexture(cp, &tti, &tr, false);\n\n    // end the copy pass\n    SDL_EndGPUCopyPass(cp);\n\n    SDL_ReleaseGPUTransferBuffer(ctx->device, buffer_tb);\n    SDL_ReleaseGPUTransferBuffer(ctx->device, texture_tb);\n}\n\nvoid software_thread_render(void* udata, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    if (!ctx->texture)\n        return;\n\n    ctx->stats.frames_rendered++;\n    ctx->last_frame_stats = ctx->stats;\n    ctx->stats.lines = 0;\n    ctx->stats.points = 0;\n    ctx->stats.triangles = 0;\n    ctx->stats.sprites = 0;\n    ctx->stats.primitives = 0;\n    ctx->stats.texture_uploads = 0;\n    ctx->stats.texture_blits = 0;\n\n    SDL_BindGPUGraphicsPipeline(render_pass, ctx->pipeline);\n\n    // bind the vertex buffer\n    SDL_GPUBufferBinding bb[1]{};\n    bb[0].buffer = ctx->vertex_buffer;\n    bb[0].offset = 0;\n\n    SDL_BindGPUVertexBuffers(render_pass, 0, bb, 1);\n\n    bb[0].buffer = ctx->index_buffer;\n    bb[0].offset = 0;\n\n    SDL_BindGPUIndexBuffer(render_pass, bb, SDL_GPU_INDEXELEMENTSIZE_16BIT);\n\n    SDL_GPUTextureSamplerBinding tsb = {\n        .texture = ctx->texture,\n        .sampler = ctx->bilinear ? ctx->sampler[1] : ctx->sampler[0],\n    };\n\n    SDL_BindGPUFragmentSamplers(render_pass, 0, &tsb, 1);\n\n    // issue a draw call\n    SDL_DrawGPUIndexedPrimitives(render_pass, 6, 1, 0, 0, 0);\n}\n\nvoid software_thread_end_render(void* udata, SDL_GPUCommandBuffer* command_buffer) {\n    // Nothing for now\n}\n\nrenderer_stats* software_thread_get_debug_stats(void* udata) {\n    software_thread_state* ctx = (software_thread_state*)udata;\n\n    return &ctx->last_frame_stats;\n}"
  },
  {
    "path": "src/gs/renderer/software_thread.hpp",
    "content": "#pragma once\n\n#include <vector>\n#include <cstdio>\n#include <thread>\n#include <chrono>\n#include <atomic>\n#include <queue>\n#include <mutex>\n\n#include <SDL3/SDL.h>\n\n#include \"gs/gs.h\"\n\n#include \"renderer.hpp\"\n\nstruct render_data {\n    int prim;\n    struct ps2_gs gs;\n};\n\nstruct gpu_vertex {\n    float x, y, u, v;\n};\n\nstruct software_thread_state {\n    std::queue <render_data> render_queue;\n    std::thread render_thr;\n    std::mutex queue_mtx;\n    std::mutex render_mtx;\n\n    std::vector <uint64_t> transfer_buffer;\n    int transfer_index = 0;\n    int transfer_size = 0;\n\n    std::atomic <bool> end_signal;\n\n    unsigned int sbp = 0, dbp = 0;\n    unsigned int sbw = 0, dbw = 0;\n    unsigned int spsm = 0, dpsm = 0;\n    unsigned int ssax = 0, ssay = 0, dsax = 0, dsay = 0;\n    unsigned int rrh = 0, rrw = 0;\n    unsigned int dir = 0, xdir = 0;\n    unsigned int sx = 0, sy = 0;\n    unsigned int dx = 0, dy = 0, px = 0;\n\n    uint32_t psmct24_data = 0;\n    uint32_t psmct24_shift = 0;\n\n    // SDL stuff\n    SDL_Window* window = nullptr;\n    SDL_GPUDevice* device = nullptr;\n    SDL_GPUBuffer* vertex_buffer = nullptr;\n    SDL_GPUBuffer* index_buffer = nullptr;\n    SDL_GPUTexture* texture = nullptr;\n    SDL_GPUSampler* sampler[2] = { nullptr };\n    SDL_GPUGraphicsPipeline* pipeline = nullptr;\n\n    // Context\n    SDL_GPUCommandBuffer* cb = nullptr;\n    SDL_GPURenderPass* rp = nullptr;\n\n    struct ps2_gs* gs = nullptr;\n\n    unsigned int window_x = 0, window_y = 0;\n    unsigned int window_w = 0, window_h = 0;\n\n    int tex_w = 0;\n    int tex_h = 0;\n    int disp_w = 0;\n    int disp_h = 0;\n    int disp_fmt = 0;\n    int aspect_mode = 0; // Native\n    bool integer_scaling = false;\n    float scale = 1.5;\n    bool bilinear = true;\n\n    unsigned int frame = 0;\n\n    uint32_t* buf = nullptr;\n\n    renderer_stats stats = { 0 };\n    renderer_stats last_frame_stats = { 0 };\n};\n\nvoid software_thread_init(void* udata, struct ps2_gs* gs, SDL_Window* window, SDL_GPUDevice* device);\nvoid software_thread_destroy(void* udata);\nvoid software_thread_set_size(void* udata, int width, int height);\nvoid software_thread_set_scale(void* udata, float scale);\nvoid software_thread_set_aspect_mode(void* udata, int aspect_mode);\nvoid software_thread_set_integer_scaling(void* udata, bool integer_scaling);\nvoid software_thread_set_bilinear(void* udata, bool bilinear);\nvoid software_thread_get_viewport_size(void* udata, int* w, int* h);\nvoid software_thread_get_display_size(void* udata, int* w, int* h);\nvoid software_thread_get_display_format(void* udata, int* fmt);\nvoid software_thread_get_interlace_mode(void* udata, int* mode);\nvoid software_thread_set_window_rect(void* udata, int x, int y, int w, int h);\nvoid* software_thread_get_buffer_data(void* udata, int* w, int* h, int* bpp);\nrenderer_stats* software_thread_get_debug_stats(void* udata);\nconst char* software_thread_get_name(void* udata);\nvoid software_thread_begin_render(void* udata, SDL_GPUCommandBuffer* command_buffer);\nvoid software_thread_render(void* udata, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass);\nvoid software_thread_end_render(void* udata, SDL_GPUCommandBuffer* command_buffer);\n\nextern \"C\" {\nvoid software_thread_render_point(struct ps2_gs* gs, void* udata);\nvoid software_thread_render_line(struct ps2_gs* gs, void* udata);\nvoid software_thread_render_triangle(struct ps2_gs* gs, void* udata);\nvoid software_thread_render_sprite(struct ps2_gs* gs, void* udata);\nvoid software_thread_transfer_start(struct ps2_gs* gs, void* udata);\nvoid software_thread_transfer_write(struct ps2_gs* gs, void* udata);\nvoid software_thread_transfer_read(struct ps2_gs* gs, void* udata);\n}"
  },
  {
    "path": "src/iop/bus.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"bus.h\"\n#include \"bus_decl.h\"\n\n#define printf(fmt, ...)(0)\n\nstruct iop_bus* iop_bus_create(void) {\n    return malloc(sizeof(struct iop_bus));\n}\n\nvoid iop_bus_init(struct iop_bus* bus, const char* bios_path) {\n    memset(bus, 0, sizeof(struct iop_bus));\n\n    for (int i = 0; i < 0x10000; i++) {\n        bus->fastmem_r_table[i] = NULL;\n        bus->fastmem_w_table[i] = NULL;\n    }\n}\n\n#define RAM_MAX_SIZE 0x1000000\n\nvoid iop_bus_init_fastmem(struct iop_bus* bus, int ram_size) {\n    memset(bus->fastmem_r_table, 0, sizeof(bus->fastmem_r_table));\n    memset(bus->fastmem_w_table, 0, sizeof(bus->fastmem_w_table));\n\n    // BIOS\n    for (int i = 0; i < 0x200; i++) {\n        bus->fastmem_r_table[i+0xfe00] = bus->bios->buf + (i * 0x2000);\n    }\n\n    // IOP RAM\n    int mask = ram_size - 1;\n\n    for (int i = 0; i < (RAM_MAX_SIZE / 0x2000); i++) {\n        bus->fastmem_r_table[i+0x0000] = bus->iop_ram->buf + ((i * 0x2000) & mask);\n        bus->fastmem_w_table[i+0x0000] = bus->iop_ram->buf + ((i * 0x2000) & mask);\n    }\n}\n\nvoid iop_bus_init_bios(struct iop_bus* bus, struct ps2_bios* bios) {\n    bus->bios = bios;\n}\n\nvoid iop_bus_init_rom1(struct iop_bus* bus, struct ps2_bios* rom1) {\n    bus->rom1 = rom1;\n}\n\nvoid iop_bus_init_rom2(struct iop_bus* bus, struct ps2_bios* rom2) {\n    bus->rom2 = rom2;\n}\n\nvoid iop_bus_init_iop_ram(struct iop_bus* bus, struct ps2_ram* iop_ram) {\n    bus->iop_ram = iop_ram;\n}\n\nvoid iop_bus_init_iop_spr(struct iop_bus* bus, struct ps2_ram* iop_spr) {\n    bus->iop_spr = iop_spr;\n}\n\nvoid iop_bus_init_sif(struct iop_bus* bus, struct ps2_sif* sif) {\n    bus->sif = sif;\n}\n\nvoid iop_bus_init_dma(struct iop_bus* bus, struct ps2_iop_dma* dma) {\n    bus->dma = dma;\n}\n\nvoid iop_bus_init_intc(struct iop_bus* bus, struct ps2_iop_intc* intc) {\n    bus->intc = intc;\n}\n\nvoid iop_bus_init_timers(struct iop_bus* bus, struct ps2_iop_timers* timers) {\n    bus->timers = timers;\n}\n\nvoid iop_bus_init_cdvd(struct iop_bus* bus, struct ps2_cdvd* cdvd) {\n    bus->cdvd = cdvd;\n}\n\nvoid iop_bus_init_sio2(struct iop_bus* bus, struct ps2_sio2* sio2) {\n    bus->sio2 = sio2;\n}\n\nvoid iop_bus_init_spu2(struct iop_bus* bus, struct ps2_spu2* spu2) {\n    bus->spu2 = spu2;\n}\n\nvoid iop_bus_init_usb(struct iop_bus* bus, struct ps2_usb* usb) {\n    bus->usb = usb;\n}\n\nvoid iop_bus_init_fw(struct iop_bus* bus, struct ps2_fw* fw) {\n    bus->fw = fw;\n}\n\nvoid iop_bus_init_sbus(struct iop_bus* bus, struct ps2_sbus* sbus) {\n    bus->sbus = sbus;\n}\n\nvoid iop_bus_init_dev9(struct iop_bus* bus, struct ps2_dev9* dev9) {\n    bus->dev9 = dev9;\n}\n\nvoid iop_bus_init_speed(struct iop_bus* bus, struct ps2_speed* speed) {\n    bus->speed = speed;\n}\n\nvoid iop_bus_init_s14x_nand(struct iop_bus* bus, struct s14x_nand* nand) {\n    bus->s14x_nand = nand;\n}\n\nvoid iop_bus_init_s14x_syscon(struct iop_bus* bus, struct s14x_syscon* syscon) {\n    bus->s14x_syscon = syscon;\n}\n\nvoid iop_bus_init_s14x_sram(struct iop_bus* bus, struct s14x_sram* sram) {\n    bus->s14x_sram = sram;\n}\n\nvoid iop_bus_init_s14x_link(struct iop_bus* bus, struct s14x_link* link) {\n    bus->s14x_link = link;\n}\n\nvoid iop_bus_destroy(struct iop_bus* bus) {\n    free(bus);\n}\n\n#define MAP_MEM_READ(b, l, u, d, n) \\\n    if ((addr >= l) && (addr <= u)) return ps2_ ## d ## _read ## b (bus->n, addr - l);\n\n#define MAP_REG_READ(b, l, u, d, n) \\\n    if ((addr >= l) && (addr <= u)) return ps2_ ## d ## _read ## b (bus->n, addr);\n\n#define MAP_MEM_WRITE(b, l, u, d, n) \\\n    if ((addr >= l) && (addr <= u)) { ps2_ ## d ## _write ## b (bus->n, addr - l, data); return; }\n\n#define MAP_REG_WRITE(b, l, u, d, n) \\\n    if ((addr >= l) && (addr <= u)) { ps2_ ## d ## _write ## b (bus->n, addr, data); return; }\n\n#define MAP_REG_READ_S14X(b, l, u, d, n) \\\n    if (bus->n && (addr >= l) && (addr <= u)) return d ## _read (bus->n, addr - l);\n\n#define MAP_REG_WRITE_S14X(b, l, u, d, n) \\\n    if (bus->n && (addr >= l) && (addr <= u)) { d ## _write (bus->n, addr - l, data); return; }\n\n#define MAP_MEM_READ_S14X(b, l, u, d, n) \\\n    if (bus->n && (addr >= l) && (addr <= u)) return d ## _read ## b (bus->n, addr - l);\n\n#define MAP_MEM_WRITE_S14X(b, l, u, d, n) \\\n    if (bus->n && (addr >= l) && (addr <= u)) { d ## _write ## b (bus->n, addr - l, data); return; }\n\nuint32_t iop_bus_read8(void* udata, uint32_t addr) {\n    struct iop_bus* bus = (struct iop_bus*)udata;\n\n    void* ptr = bus->fastmem_r_table[(addr & 0x1fffffff) >> 13];\n\n    if (ptr) return *((uint8_t*)(((uint8_t*)ptr) + (addr & 0x1fff)));\n\n    // MAP_MEM_READ(8, 0x00000000, 0x001FFFFF, ram, iop_ram);\n    // MAP_MEM_READ(8, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_MEM_READ(8, 0x1F800000, 0x1F8003FF, ram, iop_spr);\n    MAP_REG_READ(8, 0x1F801070, 0x1F80107B, iop_intc, intc);\n    MAP_REG_READ(8, 0x1F402004, 0x1F4020FF, cdvd, cdvd);\n    MAP_REG_READ(8, 0x1F808200, 0x1F808280, sio2, sio2);\n    MAP_MEM_READ(8, 0x1E000000, 0x1E3FFFFF, bios, rom1);\n    MAP_MEM_READ(8, 0x1E400000, 0x1E7FFFFF, bios, rom2);\n    MAP_REG_READ(8, 0x1F801460, 0x1F80147F, dev9, dev9);\n\n    // System 147/148 mappings\n    MAP_REG_READ_S14X(8, 0x10000000, 0x1000000F, s14x_syscon, s14x_syscon);\n    MAP_REG_READ_S14X(8, 0x10800000, 0x108000FF, s14x_link, s14x_link);\n    MAP_MEM_READ_S14X(8, 0x10C00000, 0x10C07FFF, s14x_sram, s14x_sram);\n    MAP_REG_READ_S14X(8, 0x14000000, 0x1400000F, s14x_nand, s14x_nand);\n\n    // System 147/148 syscon overlays retail SPEED\n    MAP_REG_READ(8, 0x10000000, 0x1000FFFF, speed, speed);\n\n    switch (addr) {\n        // Required for T10000 TOOL BIOS\n        // Otherwise the IOP hangs during initialization if the RAM size\n        // is 8 MB or hangs with a stack overflow after init if the RAM \n        // size is 16 MB\n        case 0x1f803204: return 0x7c;\n    }\n\n    printf(\"iop_bus: Unhandled 8-bit read from physical address 0x%08x\\n\", addr);\n\n    return 0;\n}\n\nuint32_t iop_bus_read16(void* udata, uint32_t addr) {\n    struct iop_bus* bus = (struct iop_bus*)udata;\n\n    void* ptr = bus->fastmem_r_table[(addr & 0x1fffffff) >> 13];\n\n    if (ptr) return *((uint16_t*)(((uint8_t*)ptr) + (addr & 0x1fff)));\n\n    // MAP_MEM_READ(16, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    // MAP_MEM_READ(16, 0x00000000, 0x001FFFFF, ram, iop_ram);\n    MAP_MEM_READ(16, 0x1F800000, 0x1F8003FF, ram, iop_spr);\n    MAP_REG_READ(16, 0x1F801070, 0x1F80107B, iop_intc, intc);\n    MAP_REG_READ(32, 0x1F801100, 0x1F80112F, iop_timers, timers);\n    MAP_REG_READ(32, 0x1F801480, 0x1F8014AF, iop_timers, timers);\n    MAP_REG_READ(16, 0x1F801080, 0x1F8010EF, iop_dma, dma);\n    MAP_REG_READ(16, 0x1F801500, 0x1F80155F, iop_dma, dma);\n    MAP_REG_READ(16, 0x1F801570, 0x1F80157F, iop_dma, dma);\n    MAP_REG_READ(16, 0x1F8010F0, 0x1F8010F8, iop_dma, dma);\n    MAP_REG_READ(16, 0x1F900000, 0x1F9007FF, spu2, spu2);\n    MAP_MEM_READ(16, 0x1E000000, 0x1E3FFFFF, bios, rom1);\n    MAP_MEM_READ(16, 0x1E400000, 0x1E7FFFFF, bios, rom2);\n    MAP_REG_READ(16, 0x1F801460, 0x1F80147F, dev9, dev9);\n    MAP_REG_READ(16, 0x10000000, 0x1000FFFF, speed, speed);\n\n    // System 147/148 mappings\n    MAP_MEM_READ_S14X(16, 0x10C00000, 0x10C07FFF, s14x_sram, s14x_sram);\n\n    // PSX DESR\n    if (addr == 0x1000480c) return 0xffff;\n\n    // 0x20 - PCMCIA (CXD9566)\n    // 0x30 - Expansion bay\n    if (addr == 0x1f80146e) { return 0x30; }\n\n    // SPEED rev3 (Capabilities)\n    // bit 0 - SMAP\n    // bit 1 - ATA\n    // bit 3 - UART\n    // bit 4 - DVR\n    // bit 5 - FLASH\n    // if (addr == 0x10000004) { return 0x03; }\n    // if (addr == 0x1000205c) { return 0xffff; }\n    // if (addr == 0x1000205e) { return 0xffff; }\n\n    if (addr == 0x1241c000) return 0xffff;\n\n    printf(\"iop_bus: Unhandled 16-bit read from physical address 0x%08x\\n\", addr);\n\n    return 0;\n}\n\nuint32_t iop_bus_read32(void* udata, uint32_t addr) {\n    struct iop_bus* bus = (struct iop_bus*)udata;\n\n    if (addr == 0xfffe0130) return 0xffffffff;\n\n    void* ptr = bus->fastmem_r_table[(addr & 0x1fffffff) >> 13];\n\n    if (ptr) return *((uint32_t*)(((uint8_t*)ptr) + (addr & 0x1fff)));\n\n    // MAP_MEM_READ(32, 0x00000000, 0x001FFFFF, ram, iop_ram);\n    // MAP_MEM_READ(32, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_MEM_READ(32, 0x1F800000, 0x1F8003FF, ram, iop_spr);\n    MAP_REG_READ(32, 0x1D000000, 0x1D00006F, sif, sif);\n    MAP_REG_READ(32, 0x1F801070, 0x1F80107B, iop_intc, intc);\n    MAP_REG_READ(32, 0x1F801080, 0x1F8010EF, iop_dma, dma);\n    MAP_REG_READ(32, 0x1F801500, 0x1F80155F, iop_dma, dma);\n    MAP_REG_READ(32, 0x1F801570, 0x1F80157F, iop_dma, dma);\n    MAP_REG_READ(32, 0x1F8010F0, 0x1F8010F8, iop_dma, dma);\n    MAP_REG_READ(32, 0x1F801100, 0x1F80112F, iop_timers, timers);\n    MAP_REG_READ(32, 0x1F801480, 0x1F8014AF, iop_timers, timers);\n    MAP_REG_READ(32, 0x1F808200, 0x1F808280, sio2, sio2);\n    MAP_REG_READ(32, 0x1F801600, 0x1F8016FF, usb, usb);\n    MAP_REG_READ(32, 0x1F808400, 0x1F80854F, fw, fw);\n    MAP_MEM_READ(32, 0x1E000000, 0x1E3FFFFF, bios, rom1);\n    MAP_MEM_READ(32, 0x1E400000, 0x1E7FFFFF, bios, rom2);\n    MAP_REG_READ(32, 0x1F801460, 0x1F80147F, dev9, dev9);\n    MAP_REG_READ(32, 0x10000000, 0x1000FFFF, speed, speed);\n\n    // System 147/148 mappings\n    MAP_MEM_READ_S14X(32, 0x10C00000, 0x10C07FFF, s14x_sram, s14x_sram);\n\n    if (addr == 0x1f801450) return 0;\n    if (addr == 0x1f801414) return 1;\n    if (addr == 0x1f801560) return 0;\n\n    if ((addr & 0xff000000) == 0x1e000000) return 0;\n    if (addr == 0xfffe0130) return 0xffffffff;\n\n    // Bloody Roar 4 Wrong IOP CDVD DMA\n    // if ((addr & 0xff000000) == 0x0c000000) { *(uint8_t*)0 = 0; }\n\n    printf(\"iop_bus: Unhandled 32-bit read from physical address 0x%08x\\n\", addr);\n\n    return 0;\n}\n\nvoid iop_bus_write8(void* udata, uint32_t addr, uint32_t data) {\n    struct iop_bus* bus = (struct iop_bus*)udata;\n\n    void* ptr = bus->fastmem_w_table[(addr & 0x1fffffff) >> 13];\n\n    if (ptr) {\n        *((uint8_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data;\n\n        return;\n    }\n\n    // MAP_MEM_WRITE(8, 0x00000000, 0x001FFFFF, ram, iop_ram);\n    // MAP_MEM_WRITE(8, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_MEM_WRITE(8, 0x1F800000, 0x1F8003FF, ram, iop_spr);\n    MAP_REG_WRITE(8, 0x1F402004, 0x1F4020FF, cdvd, cdvd);\n    MAP_REG_WRITE(8, 0x1F801070, 0x1F80107B, iop_intc, intc);\n    MAP_REG_WRITE(32, 0x1F801080, 0x1F8010EF, iop_dma, dma);\n    MAP_REG_WRITE(32, 0x1F801500, 0x1F80155F, iop_dma, dma);\n    MAP_REG_WRITE(32, 0x1F801570, 0x1F80157F, iop_dma, dma);\n    MAP_REG_WRITE(32, 0x1F8010F0, 0x1F8010F8, iop_dma, dma);\n    MAP_REG_WRITE(8, 0x1F808200, 0x1F808280, sio2, sio2);\n    MAP_REG_WRITE(8, 0x1F801460, 0x1F80147F, dev9, dev9);\n\n    // System 147/148 mappings\n    MAP_REG_WRITE_S14X(8, 0x10000000, 0x1000000F, s14x_syscon, s14x_syscon);\n    MAP_REG_WRITE_S14X(8, 0x10800000, 0x108000FF, s14x_link, s14x_link);\n    MAP_MEM_WRITE_S14X(8, 0x10C00000, 0x10C07FFF, s14x_sram, s14x_sram);\n    MAP_REG_WRITE_S14X(8, 0x14000000, 0x1400000F, s14x_nand, s14x_nand);\n\n    // System 147/148 syscon overlays retail SPEED\n    MAP_REG_WRITE(8, 0x10000000, 0x1000FFFF, speed, speed);\n\n    printf(\"iop_bus: Unhandled 8-bit write to physical address 0x%08x (0x%02x)\\n\", addr, data);\n}\n\nvoid iop_bus_write16(void* udata, uint32_t addr, uint32_t data) {\n    struct iop_bus* bus = (struct iop_bus*)udata;\n\n    void* ptr = bus->fastmem_w_table[(addr & 0x1fffffff) >> 13];\n\n    if (ptr) {\n        *((uint16_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data;\n\n        return;\n    }\n\n    // MAP_MEM_WRITE(16, 0x00000000, 0x001FFFFF, ram, iop_ram);\n    // MAP_MEM_WRITE(16, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_MEM_WRITE(16, 0x1F800000, 0x1F8003FF, ram, iop_spr);\n    MAP_REG_WRITE(32, 0x1F801100, 0x1F80112F, iop_timers, timers);\n    MAP_REG_WRITE(32, 0x1F801480, 0x1F8014AF, iop_timers, timers);\n    MAP_REG_WRITE(16, 0x1F801070, 0x1F80107B, iop_intc, intc);\n    MAP_REG_WRITE(16, 0x1F801080, 0x1F8010EF, iop_dma, dma);\n    MAP_REG_WRITE(16, 0x1F801500, 0x1F80155F, iop_dma, dma);\n    MAP_REG_WRITE(16, 0x1F801570, 0x1F80157F, iop_dma, dma);\n    MAP_REG_WRITE(16, 0x1F8010F0, 0x1F8010F8, iop_dma, dma);\n    MAP_REG_WRITE(16, 0x1F900000, 0x1F9007FF, spu2, spu2);\n    MAP_REG_WRITE(16, 0x1F801460, 0x1F80147F, dev9, dev9);\n    MAP_REG_WRITE(16, 0x10000000, 0x1000FFFF, speed, speed);\n\n    // System 147/148 mappings\n    MAP_MEM_WRITE_S14X(16, 0x10C00000, 0x10C07FFF, s14x_sram, s14x_sram);\n\n    // printf(\"iop_bus: Unhandled 16-bit write to physical address 0x%08x (0x%04x)\\n\", addr, data);\n}\n\nvoid iop_bus_write32(void* udata, uint32_t addr, uint32_t data) {\n    struct iop_bus* bus = (struct iop_bus*)udata;\n\n    void* ptr = bus->fastmem_w_table[(addr & 0x1fffffff) >> 13];\n\n    if (ptr) {\n        *((uint32_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data;\n\n        return;\n    }\n\n    // BIU config\n    if (addr == 0xfffe0130) return;\n\n    // MAP_MEM_WRITE(32, 0x00000000, 0x001FFFFF, ram, iop_ram);\n    // MAP_MEM_WRITE(32, 0x1FC00000, 0x1FFFFFFF, bios, bios);\n    MAP_MEM_WRITE(32, 0x1F800000, 0x1F8003FF, ram, iop_spr);\n    MAP_REG_WRITE(32, 0x1D000000, 0x1D00006F, sif, sif);\n    MAP_REG_WRITE(32, 0x1F801450, 0x1F801453, sbus, sbus);\n    MAP_REG_WRITE(32, 0x1F801070, 0x1F80107B, iop_intc, intc);\n    MAP_REG_WRITE(32, 0x1F801080, 0x1F8010EF, iop_dma, dma);\n    MAP_REG_WRITE(32, 0x1F801500, 0x1F80155F, iop_dma, dma);\n    MAP_REG_WRITE(32, 0x1F801570, 0x1F80157F, iop_dma, dma);\n    MAP_REG_WRITE(32, 0x1F8010F0, 0x1F8010F8, iop_dma, dma);\n    MAP_REG_WRITE(32, 0x1F801100, 0x1F80112F, iop_timers, timers);\n    MAP_REG_WRITE(32, 0x1F801480, 0x1F8014AF, iop_timers, timers);\n    MAP_REG_WRITE(32, 0x1F808200, 0x1F808280, sio2, sio2);\n    MAP_REG_WRITE(32, 0x1F801600, 0x1F8016FF, usb, usb);\n    MAP_REG_WRITE(32, 0x1F808400, 0x1F80854F, fw, fw);\n    MAP_REG_WRITE(32, 0x1F801460, 0x1F80147F, dev9, dev9);\n    MAP_REG_WRITE(32, 0x10000000, 0x1000FFFF, speed, speed);\n\n    // System 147/148 mappings\n    MAP_MEM_WRITE_S14X(32, 0x10C00000, 0x10C07FFF, s14x_sram, s14x_sram);\n\n    // printf(\"iop_bus: Unhandled 32-bit write to physical address 0x%08x (0x%08x)\\n\", addr, data);\n}\n\n#undef MAP_READ\n#undef MAP_WRITE"
  },
  {
    "path": "src/iop/bus.h",
    "content": "#ifndef IOP_BUS_H\n#define IOP_BUS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"u128.h\"\n\n// Shared IOP/EE hardware\n#include \"shared/ram.h\"\n#include \"shared/sif.h\"\n#include \"shared/bios.h\"\n#include \"shared/sbus.h\"\n#include \"shared/dev9.h\"\n#include \"shared/speed.h\"\n\n// IOP-only hardware\n#include \"dma.h\"\n#include \"intc.h\"\n#include \"timers.h\"\n#include \"cdvd.h\"\n#include \"sio2.h\"\n#include \"spu2.h\"\n#include \"usb.h\"\n#include \"fw.h\"\n\n// Arcade hardware\n#include \"s14x/nand.h\"\n#include \"s14x/syscon.h\"\n#include \"s14x/sram.h\"\n#include \"s14x/link.h\"\n\n#define FASTMEM_BLKSIZE 0x2000\n#define FASTMEM_TBLSIZE (0x20000000 / FASTMEM_BLKSIZE)\n\nstruct iop_bus {\n    struct ps2_bios* bios;\n    struct ps2_bios* rom1;\n    struct ps2_bios* rom2;\n    struct ps2_ram* iop_ram;\n    struct ps2_ram* iop_spr;\n    struct ps2_sif* sif;\n    struct ps2_iop_dma* dma;\n    struct ps2_iop_intc* intc;\n    struct ps2_iop_timers* timers;\n    struct ps2_cdvd* cdvd;\n    struct ps2_sio2* sio2;\n    struct ps2_spu2* spu2;\n    struct ps2_usb* usb;\n    struct ps2_fw* fw;\n    struct ps2_sbus* sbus;\n    struct ps2_dev9* dev9;\n    struct ps2_speed* speed;\n\n    // Arcade hardware\n    struct s14x_nand* s14x_nand;\n    struct s14x_syscon* s14x_syscon;\n    struct s14x_sram* s14x_sram;\n    struct s14x_link* s14x_link;\n\n    void* fastmem_r_table[0x10000];\n    void* fastmem_w_table[0x10000];\n};\n\nvoid iop_bus_init_bios(struct iop_bus* bus, struct ps2_bios* bios);\nvoid iop_bus_init_rom1(struct iop_bus* bus, struct ps2_bios* rom1);\nvoid iop_bus_init_rom2(struct iop_bus* bus, struct ps2_bios* rom2);\nvoid iop_bus_init_iop_ram(struct iop_bus* bus, struct ps2_ram* iop_ram);\nvoid iop_bus_init_iop_spr(struct iop_bus* bus, struct ps2_ram* iop_spr);\nvoid iop_bus_init_sif(struct iop_bus* bus, struct ps2_sif* sif);\nvoid iop_bus_init_dma(struct iop_bus* bus, struct ps2_iop_dma* dma);\nvoid iop_bus_init_intc(struct iop_bus* bus, struct ps2_iop_intc* intc);\nvoid iop_bus_init_timers(struct iop_bus* bus, struct ps2_iop_timers* timers);\nvoid iop_bus_init_cdvd(struct iop_bus* bus, struct ps2_cdvd* cdvd);\nvoid iop_bus_init_sio2(struct iop_bus* bus, struct ps2_sio2* sio2);\nvoid iop_bus_init_spu2(struct iop_bus* bus, struct ps2_spu2* spu2);\nvoid iop_bus_init_usb(struct iop_bus* bus, struct ps2_usb* usb);\nvoid iop_bus_init_fw(struct iop_bus* bus, struct ps2_fw* fw);\nvoid iop_bus_init_sbus(struct iop_bus* bus, struct ps2_sbus* sbus);\nvoid iop_bus_init_dev9(struct iop_bus* bus, struct ps2_dev9* dev9);\nvoid iop_bus_init_speed(struct iop_bus* bus, struct ps2_speed* speed);\nvoid iop_bus_init_s14x_nand(struct iop_bus* bus, struct s14x_nand* nand);\nvoid iop_bus_init_s14x_syscon(struct iop_bus* bus, struct s14x_syscon* syscon);\nvoid iop_bus_init_s14x_sram(struct iop_bus* bus, struct s14x_sram* sram);\nvoid iop_bus_init_s14x_link(struct iop_bus* bus, struct s14x_link* link);\n\nvoid iop_bus_init_fastmem(struct iop_bus* bus, int ram_size);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/bus_decl.h",
    "content": "#ifndef IOP_BUS_DECL_H\n#define IOP_BUS_DECL_H\n\n#include <stdint.h>\n\nstruct iop_bus;\n\nstruct iop_bus* iop_bus_create(void);\nvoid iop_bus_init(struct iop_bus* bus, const char* bios_path);\nvoid iop_bus_destroy(struct iop_bus* bus);\n\nuint32_t iop_bus_read8(void* udata, uint32_t addr);\nuint32_t iop_bus_read16(void* udata, uint32_t addr);\nuint32_t iop_bus_read32(void* udata, uint32_t addr);\nvoid iop_bus_write8(void* udata, uint32_t addr, uint32_t data);\nvoid iop_bus_write16(void* udata, uint32_t addr, uint32_t data);\nvoid iop_bus_write32(void* udata, uint32_t addr, uint32_t data);\n\n#endif"
  },
  {
    "path": "src/iop/cdvd.c",
    "content": "#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n#include <stdio.h>\n#include <ctype.h>\n#include <time.h>\n\n#include \"cdvd.h\"\n\n#define printf(fmt,...)(0)\n\nstruct nvram_layout g_spc970_layout = {\n    .bios_version = 0x00000000,\n    .config0_offset = 0x00000280,\n    .config1_offset = 0x00000300,\n    .config2_offset = 0x00000200,\n    .console_id_offset = 0x000001C8,\n    .ilink_id_offset = 0x000001C0,\n    .modelnum_offset = 0x000001A0,\n    .regparams_offset = 0x00000180,\n    .mac_offset = 0x00000198\n};\n\nstruct nvram_layout g_dragon_layout = {\n    .bios_version = 0x00000146,\n    .config0_offset = 0x00000270,\n    .config1_offset = 0x000002B0,\n    .config2_offset = 0x00000200,\n    .console_id_offset = 0x000001F0,\n    .ilink_id_offset = 0x000001E0,\n    .modelnum_offset = 0x000001B0,\n    .regparams_offset = 0x00000180,\n    .mac_offset = 0x00000198\n};\n\nstatic const uint8_t itob_table[] = {\n    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\n    0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,\n    0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23,\n    0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31,\n    0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,\n    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\n    0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,\n    0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63,\n    0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71,\n    0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,\n    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\n    0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,\n    0x96, 0x97, 0x98, 0x99, 0xa0, 0xa1, 0xa2, 0xa3,\n    0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xb0, 0xb1,\n    0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9,\n    0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7,\n    0xc8, 0xc9, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5,\n    0xd6, 0xd7, 0xd8, 0xd9, 0xe0, 0xe1, 0xe2, 0xe3,\n    0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xf0, 0xf1,\n    0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9,\n    0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07,\n    0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15,\n    0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23,\n    0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31,\n    0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39,\n    0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47,\n    0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55,\n    0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63,\n    0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71,\n    0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79,\n    0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87,\n    0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95,\n};\n\nstatic inline const char* cdvd_get_n_command_name(uint8_t cmd) {\n    switch (cmd) {\n        case 0x00: return \"nop\";\n        case 0x01: return \"nop_sync\";\n        case 0x02: return \"standby\";\n        case 0x03: return \"stop\";\n        case 0x04: return \"pause\";\n        case 0x05: return \"seek\";\n        case 0x06: return \"read_cd\";\n        case 0x07: return \"read_cdda\";\n        case 0x08: return \"read_dvd\";\n        case 0x09: return \"get_toc\";\n        case 0x0c: return \"read_key\";\n    }\n\n    return \"<unknown>\";\n}\n\nstatic inline const char* cdvd_get_type_name(int type) {\n    switch (type) {\n        case CDVD_DISC_NO_DISC: return \"No disc\";\n        case CDVD_DISC_DETECTING: return \"Detecting\";\n        case CDVD_DISC_DETECTING_CD: return \"Detecting CD\";\n        case CDVD_DISC_DETECTING_DVD: return \"Detecting DVD\";\n        case CDVD_DISC_DETECTING_DL_DVD: return \"Detecting Dual-layer DVD\";\n        case CDVD_DISC_PSX_CD: return \"PlayStation CD\";\n        case CDVD_DISC_PSX_CDDA: return \"PlayStation CDDA\";\n        case CDVD_DISC_PS2_CD: return \"PlayStation 2 CD\";\n        case CDVD_DISC_PS2_CDDA: return \"PlayStation 2 CDDA\";\n        case CDVD_DISC_PS2_DVD: return \"PlayStation 2 DVD\";\n        case CDVD_DISC_CDDA: return \"CD Audio\";\n        case CDVD_DISC_DVD_VIDEO: return \"DVD Video\";\n        case CDVD_DISC_INVALID: return \"Invalid\";\n    }\n\n    return \"Unknown\";\n}\n\nstatic inline int cdvd_is_dual_layer(struct ps2_cdvd* cdvd) {\n    return disc_get_volume_lba(cdvd->disc, 1);\n}\n\nstatic inline void cdvd_set_busy(struct ps2_cdvd* cdvd) {\n    cdvd->n_stat |= CDVD_N_STATUS_BUSY;\n    cdvd->n_stat &= ~CDVD_N_STATUS_READY;\n}\n\nstatic inline void cdvd_set_ready(struct ps2_cdvd* cdvd) {\n    cdvd->n_stat &= ~CDVD_N_STATUS_BUSY;\n    cdvd->n_stat |= CDVD_N_STATUS_READY;\n}\n\nstatic inline void cdvd_init_s_fifo(struct ps2_cdvd* cdvd, int size) {\n    if (cdvd->s_fifo)\n        free(cdvd->s_fifo);\n\n    cdvd->s_fifo_size = size;\n    cdvd->s_fifo_index = 0;\n    cdvd->s_fifo = malloc(cdvd->s_fifo_size);\n    cdvd->s_stat &= ~0x40;\n}\n\nstatic inline void cdvd_s_read_subq(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 11);\n\n    int track = disc_get_track_number(cdvd->disc, cdvd->read_lba);\n\n    struct track_info info;\n    disc_get_track_info(cdvd->disc, track, &info);\n\n    int track_lba = (cdvd->read_lba + 150) - info.lba;\n\n    int track_mm = track_lba / (60 * 75);\n    int track_ss = (track_lba % (60 * 75)) / 75;\n    int track_ff = (track_lba % (60 * 75)) % 75;\n    int abs_mm = (cdvd->read_lba + 150) / (60 * 75);\n    int abs_ss = ((cdvd->read_lba + 150) % (60 * 75)) / 75;\n    int abs_ff = ((cdvd->read_lba + 150) % (60 * 75)) % 75;\n\n    fprintf(stdout, \"cdvd: S subq read: track=%02d trk %02d:%02d:%02d abs %02d:%02d:%02d\\n\",\n        track,\n        track_mm, track_ss, track_ff,\n        abs_mm, abs_ss, abs_ff\n    );\n\n    // Note: From PCSX2\n    //       the formatted subq command returns: \n    //       control/adr, track, index, trk min, trk sec, trk frm, 0x00, abs min, abs sec, abs frm\n    memset(&cdvd->s_fifo[0], 0, 11);\n\n    cdvd->s_fifo[0] = 0x41; // control/adr\n    cdvd->s_fifo[1] = track; // track\n    cdvd->s_fifo[2] = 0x01; // index\n    cdvd->s_fifo[3] = itob_table[track_mm]; // trk min\n    cdvd->s_fifo[4] = itob_table[track_ss]; // trk sec\n    cdvd->s_fifo[5] = itob_table[track_ff]; // trk frm\n    cdvd->s_fifo[6] = 0x00;\n    cdvd->s_fifo[7] = itob_table[abs_mm]; // abs min\n    cdvd->s_fifo[8] = itob_table[abs_ss]; // abs sec\n    cdvd->s_fifo[9] = itob_table[abs_ff]; // abs frm\n\n    // To-do: This doesn't work for whatever reason, even though\n    //        we're returning correct data, the CD player just\n    //        won't actually display the current time.\n}\nstatic inline void cdvd_s_mechacon_cmd(struct ps2_cdvd* cdvd) {\n    switch (cdvd->s_params[0]) {\n        case 0x00: {\n            cdvd_init_s_fifo(cdvd, 4);\n\n            cdvd->s_fifo[0] = 0x03;\n            cdvd->s_fifo[1] = 0x06;\n            cdvd->s_fifo[2] = 0x02;\n            cdvd->s_fifo[3] = 0x00;\n        } break;\n\n        case 0x90: {\n            cdvd_init_s_fifo(cdvd, 1);\n\n            cdvd->s_fifo[0] = 0x00;\n        } break;\n\n        case 0xef: {\n            cdvd_init_s_fifo(cdvd, 3);\n\n            cdvd->s_fifo[0] = 0x00;\n            cdvd->s_fifo[1] = 0x0f;\n            cdvd->s_fifo[2] = 0x05;\n        } break;\n\n        // sceCdReadRenewalDate (sent by PSX DESR BIOS)\n        case 0xfd: {\n            cdvd_init_s_fifo(cdvd, 6);\n\n            cdvd->s_fifo[0] = 0;\n            cdvd->s_fifo[1] = 0x04; //year\n            cdvd->s_fifo[2] = 0x12; //month\n            cdvd->s_fifo[3] = 0x10; //day\n            cdvd->s_fifo[4] = 0x01; //hour\n            cdvd->s_fifo[5] = 0x30; //min\n        } break;\n\n        default: {\n            fprintf(stderr, \"cdvd: Unknown S subcommand %02x\\n\", cdvd->s_params[0]);\n\n            exit(1);\n        } break;\n    }\n}\nstatic inline void cdvd_s_update_sticky_flags(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n\n    cdvd->sticky_status = cdvd->status;\n}\nstatic inline void cdvd_s_read_rtc(struct ps2_cdvd* cdvd) {\n    time_t t = time(NULL);\n\n    struct tm tm = *localtime(&t);\n\n    cdvd_init_s_fifo(cdvd, 8);\n\n    cdvd->s_fifo[0] = 0;\n    cdvd->s_fifo[1] = itob_table[tm.tm_sec];\n    cdvd->s_fifo[2] = itob_table[tm.tm_min];\n    cdvd->s_fifo[3] = itob_table[tm.tm_hour];\n    cdvd->s_fifo[4] = 0;\n    cdvd->s_fifo[5] = itob_table[tm.tm_mday];\n    cdvd->s_fifo[6] = itob_table[tm.tm_mon + 1];\n    cdvd->s_fifo[7] = itob_table[tm.tm_year - 100];\n}\nstatic inline void cdvd_s_write_rtc(struct ps2_cdvd* cdvd) {\n    fprintf(stderr, \"cdvd: write_rtc\\n\");\n\n    exit(1);\n}\nstatic inline void cdvd_s_read_nvram(struct ps2_cdvd* cdvd) {\n    uint16_t addr = *(uint16_t*)&cdvd->s_params[0];\n\n    cdvd_init_s_fifo(cdvd, 2);\n\n    cdvd->s_fifo[0] = cdvd->nvram[(addr << 1) + 0];\n    cdvd->s_fifo[1] = cdvd->nvram[(addr << 1) + 1];\n}\nstatic inline void cdvd_s_write_nvram(struct ps2_cdvd* cdvd) {\n    fprintf(stderr, \"cdvd: write_nvram\\n\");\n\n    exit(1);\n}\nstatic inline void cdvd_s_read_ilink_id(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 9);\n\n    // uint8_t id[9] = {\n    //     0x00, 0xac, 0xff, 0xff,\n    //     0xff, 0xff, 0xb9, 0x86,\n    //     0x00\n    // };\n\n    // for (int i = 0; i < 9; i++) {\n    //     cdvd->s_fifo[i] = id[i];\n    // }\n\n    int offset = cdvd->layout.ilink_id_offset;\n\n    memcpy(&cdvd->s_fifo[1], &cdvd->nvram[offset], 8);\n}\nstatic inline void cdvd_s_ctrl_audio_digital_out(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_forbid_dvd(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 5;\n}\nstatic inline void cdvd_s_read_model(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 9);\n\n    int offset = cdvd->layout.modelnum_offset + cdvd->s_params[0];\n\n    memcpy(&cdvd->s_fifo[1], &cdvd->nvram[offset], 8);\n}\nstatic inline void cdvd_s_certify_boot(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 1;\n}\nstatic inline void cdvd_s_cancel_pwoff_ready(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_blue_led_ctl(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_read_wakeup_time(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 10);\n\n    for (int i = 0; i < 10; i++)\n        cdvd->s_fifo[i] = 0;\n}\nstatic inline void cdvd_s_rc_bypass_ctrl(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_open_config(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->config_rw = cdvd->s_params[0];\n    cdvd->config_offset = cdvd->s_params[1];\n    cdvd->config_numblocks = cdvd->s_params[2];\n    cdvd->config_block_index = 0;\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_read_config(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 16);\n\n    int offset = 0;\n\n    switch (cdvd->config_offset) {\n        case 0: offset = cdvd->layout.config0_offset; break;\n        case 1: offset = cdvd->layout.config1_offset; break;\n        case 2: offset = cdvd->layout.config2_offset; break;\n\n        default: offset = cdvd->layout.config1_offset; break;\n    }\n\n    offset += (cdvd->config_block_index++) * 16;\n\n    memcpy(cdvd->s_fifo, &cdvd->nvram[offset], 16);\n}\nstatic inline void cdvd_s_write_config(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_close_config(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_mechacon_auth_80(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_mechacon_auth_81(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_mechacon_auth_82(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_mechacon_auth_83(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_mechacon_auth_84(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1+8+4);\n\n    cdvd->s_fifo[0] = 0;\n    cdvd->s_fifo[1] = 0x21;\n    cdvd->s_fifo[2] = 0xdc;\n    cdvd->s_fifo[3] = 0x31;\n    cdvd->s_fifo[4] = 0x96;\n    cdvd->s_fifo[5] = 0xce;\n    cdvd->s_fifo[6] = 0x72;\n    cdvd->s_fifo[7] = 0xe0;\n    cdvd->s_fifo[8] = 0xc8;\n    cdvd->s_fifo[9]  = 0x69;\n    cdvd->s_fifo[10] = 0xda;\n    cdvd->s_fifo[11] = 0x34;\n    cdvd->s_fifo[12] = 0x9b;\n}\nstatic inline void cdvd_s_mechacon_auth_85(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1+4+8);\n\n    cdvd->s_fifo[0] = 0;\n    cdvd->s_fifo[1] = 0xeb;\n    cdvd->s_fifo[2] = 0x01;\n    cdvd->s_fifo[3] = 0xc7;\n    cdvd->s_fifo[4] = 0xa9;\n    cdvd->s_fifo[5] = 0x3f;\n    cdvd->s_fifo[6] = 0x9c;\n    cdvd->s_fifo[7] = 0x5b;\n    cdvd->s_fifo[8] = 0x19;\n    cdvd->s_fifo[9] = 0x31;\n    cdvd->s_fifo[10] = 0xa0;\n    cdvd->s_fifo[11] = 0xb3;\n    cdvd->s_fifo[12] = 0xa3;\n}\nstatic inline void cdvd_s_mechacon_auth_86(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_mechacon_auth_87(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_mechacon_auth_88(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_mg_write_data(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n\n    printf(\"mg: Write KELF data params=\");\n\n    for (int i = 0; i < cdvd->s_param_index; i++)\n        printf(\"%02x \", cdvd->s_params[i]);\n\n    printf(\"\\n\");\n}\nstatic inline void cdvd_s_mechacon_auth_8f(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_mg_write_hdr_start(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    printf(\"mg: Write KELF header params=\");\n\n    for (int i = 0; i < cdvd->s_param_index; i++)\n        printf(\"%02x \", cdvd->s_params[i]);\n\n    printf(\"\\n\");\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_mg_read_bit_length(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 3);\n\n    for (int i = 0; i < 3; i++)\n        cdvd->s_fifo[i] = 0;\n}\nstatic inline void cdvd_s_get_region_params(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 15);\n\n    for (int i = 5; i < 15; i++)\n        cdvd->s_fifo[i] = 0;\n\n    int offset = cdvd->layout.regparams_offset;\n\n    cdvd->s_fifo[1] = 1 << 0; // MechaCon encryption zone\n    cdvd->s_fifo[2] = 0;\n\n    memcpy(&cdvd->s_fifo[3], &cdvd->nvram[offset], 8);\n\n    fprintf(stdout, \"cdvd: Region: \\'%c\\' Language set: %c%c%c%c\\n\",\n        cdvd->s_fifo[3],\n        cdvd->s_fifo[4],\n        cdvd->s_fifo[5],\n        cdvd->s_fifo[6],\n        cdvd->s_fifo[7]\n    );\n\n    //This is basically what PCSX2 returns on a blank NVM/MEC file\n    // cdvd->s_fifo[0] = 0;\n    // cdvd->s_fifo[1] = 1 << 0x3; //MEC encryption zone\n    // cdvd->s_fifo[2] = 0;\n    // cdvd->s_fifo[3] = 0x80; //Region Params\n    // cdvd->s_fifo[4] = 0x1;\n}\nstatic inline void cdvd_s_remote2_read(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 5);\n\n    cdvd->s_fifo[0] = 0x00;\n    cdvd->s_fifo[1] = 0x14;\n    cdvd->s_fifo[2] = 0x00;\n    cdvd->s_fifo[3] = 0x00;\n    cdvd->s_fifo[4] = 0x00;\n}\nstatic inline void cdvd_s_remote2_6(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 3);\n\n    cdvd->s_fifo[0] = 0;\n    cdvd->s_fifo[1] = 1;\n    cdvd->s_fifo[2] = 0;\n}\nstatic inline void cdvd_s_auto_adjust_ctrl(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_notice_game_start(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 1);\n\n    cdvd->s_fifo[0] = 0;\n}\nstatic inline void cdvd_s_get_medium_removal(struct ps2_cdvd* cdvd) {\n    cdvd_init_s_fifo(cdvd, 2);\n\n    cdvd->s_fifo[0] = 0;\n}\n\nvoid cdvd_handle_s_command(struct ps2_cdvd* cdvd, uint8_t cmd) {\n    cdvd->s_cmd = cmd;\n\n    switch (cmd) {\n        // Note: Used by CD player to get current playing position\n        case 0x02: printf(\"cdvd: read_subq\\n\"); cdvd_s_read_subq(cdvd); break;\n        case 0x03: printf(\"cdvd: mechacon_cmd(%02x)\\n\", cdvd->s_params[0]); cdvd_s_mechacon_cmd(cdvd); break;\n        case 0x05: printf(\"cdvd: update_sticky_flags\\n\"); cdvd_s_update_sticky_flags(cdvd); break;\n        // case 0x06: printf(\"cdvd: tray_ctrl\\n\"); cdvd_s_tray_ctrl(cdvd); break;\n        case 0x08: printf(\"cdvd: read_rtc\\n\"); cdvd_s_read_rtc(cdvd); break;\n        case 0x09: printf(\"cdvd: write_rtc\\n\"); cdvd_s_write_rtc(cdvd); break;\n        case 0x0a: printf(\"cdvd: read_nvram\\n\"); cdvd_s_read_nvram(cdvd); break;\n        case 0x0b: printf(\"cdvd: write_nvram\\n\"); cdvd_s_write_nvram(cdvd); break;\n        // case 0x0f: printf(\"cdvd: power_off\\n\"); cdvd_s_power_off(cdvd); break;\n        case 0x12: printf(\"cdvd: read_ilink_id\\n\"); cdvd_s_read_ilink_id(cdvd); break;\n\n        // Note: Used by CD player\n        case 0x14: printf(\"cdvd: ctrl_audio_digital_out\\n\"); cdvd_s_ctrl_audio_digital_out(cdvd); break;\n        case 0x15: printf(\"cdvd: forbid_dvd\\n\"); cdvd_s_forbid_dvd(cdvd); break;\n        case 0x16: printf(\"cdvd: auto_adjust_ctrl\\n\"); cdvd_s_auto_adjust_ctrl(cdvd); break;\n        case 0x17: printf(\"cdvd: read_model\\n\"); cdvd_s_read_model(cdvd); break;\n        case 0x1a: printf(\"cdvd: certify_boot\\n\"); cdvd_s_certify_boot(cdvd); break;\n        case 0x1b: printf(\"cdvd: cancel_pwoff_ready\\n\"); cdvd_s_cancel_pwoff_ready(cdvd); break;\n\n        // Used by Namco System 246 at boot\n        case 0x1c: printf(\"cdvd: blue_led_ctl\\n\"); cdvd_s_blue_led_ctl(cdvd); break;\n        case 0x1e: printf(\"cdvd: remote2_read\\n\"); cdvd_s_remote2_read(cdvd); break;\n\n        // Used by the PS2 DVD player\n        case 0x20: printf(\"cdvd: remote2_6\\n\"); cdvd_s_remote2_6(cdvd); break;\n        case 0x22: printf(\"cdvd: read_wakeup_time\\n\"); cdvd_s_read_wakeup_time(cdvd); break;\n        case 0x24: printf(\"cdvd: rc_bypass_ctrl\\n\"); cdvd_s_rc_bypass_ctrl(cdvd); break;\n        case 0x29: printf(\"cdvd: notice_game_start\\n\"); cdvd_s_notice_game_start(cdvd); break;\n        case 0x32: printf(\"cdvd: get_medium_removal\\n\"); cdvd_s_get_medium_removal(cdvd); break; \n        case 0x36: printf(\"cdvd: get_region_params\\n\"); cdvd_s_get_region_params(cdvd); break;\n        case 0x40: printf(\"cdvd: open_config\\n\"); cdvd_s_open_config(cdvd); break;\n        case 0x41: printf(\"cdvd: read_config\\n\"); cdvd_s_read_config(cdvd); break;\n        case 0x42: printf(\"cdvd: write_config\\n\"); cdvd_s_write_config(cdvd); break;\n        case 0x43: printf(\"cdvd: close_config\\n\"); cdvd_s_close_config(cdvd); break;\n        case 0x80: printf(\"cdvd: mechacon_auth_80\\n\"); cdvd_s_mechacon_auth_80(cdvd); break;\n        case 0x81: printf(\"cdvd: mechacon_auth_81\\n\"); cdvd_s_mechacon_auth_81(cdvd); break;\n        case 0x82: printf(\"cdvd: mechacon_auth_82\\n\"); cdvd_s_mechacon_auth_82(cdvd); break;\n        case 0x83: printf(\"cdvd: mechacon_auth_83\\n\"); cdvd_s_mechacon_auth_83(cdvd); break;\n        case 0x84: printf(\"cdvd: mechacon_auth_84\\n\"); cdvd_s_mechacon_auth_84(cdvd); break;\n        case 0x85: printf(\"cdvd: mechacon_auth_85\\n\"); cdvd_s_mechacon_auth_85(cdvd); break;\n        case 0x86: printf(\"cdvd: mechacon_auth_86\\n\"); cdvd_s_mechacon_auth_86(cdvd); break;\n        case 0x87: printf(\"cdvd: mechacon_auth_87\\n\"); cdvd_s_mechacon_auth_87(cdvd); break;\n        case 0x88: printf(\"cdvd: mechacon_auth_88\\n\"); cdvd_s_mechacon_auth_88(cdvd); break;\n        case 0x8d: printf(\"cdvd: mg_write_data\\n\"); cdvd_s_mg_write_data(cdvd); break;\n        case 0x8f: printf(\"cdvd: mechacon_auth_8f\\n\"); cdvd_s_mechacon_auth_8f(cdvd); break;\n        case 0x90: printf(\"cdvd: mg_write_hdr_start\\n\"); cdvd_s_mg_write_hdr_start(cdvd); break;\n        case 0x91: printf(\"cdvd: mg_read_bit_length\\n\"); cdvd_s_mg_read_bit_length(cdvd); break;\n\n        default: {\n            fprintf(stderr, \"cdvd: Unknown S command %02xh\\n\", cmd);\n\n            exit(1);\n        } break;\n    }\n\n    cdvd->s_param_index = 0;\n}\n\nstatic inline void cdvd_handle_s_param(struct ps2_cdvd* cdvd, uint8_t param) {\n    if (cdvd->s_param_index == 16) {\n        printf(\"cdvd: S parameter FIFO overflow\\n\");\n    }\n\n    cdvd->s_params[cdvd->s_param_index++] = param;\n}\n\nstatic inline uint8_t cdvd_read_s_response(struct ps2_cdvd* cdvd) {\n    if (cdvd->s_fifo_index == cdvd->s_fifo_size) {\n        return 0;\n    }\n\n    uint8_t data = cdvd->s_fifo[cdvd->s_fifo_index++];\n\n    if (cdvd->s_fifo_index == cdvd->s_fifo_size)\n        cdvd->s_stat |= 0x40;\n\n    return data;\n}\n\nstatic inline long cdvd_get_cd_read_timing(struct ps2_cdvd* cdvd, int from) {\n    long block_timing = (36864000L * cdvd->read_size) / (24 * 153600);\n    long delta = cdvd->read_lba - from;\n    long contiguous_cycles = block_timing * cdvd->read_count;\n\n    if (!delta)\n        return 1000;\n\n    if (delta < 0) {\n        delta = -delta;\n    }\n\n    // Small delta\n    if (delta < 8)\n        return ((block_timing * delta) + contiguous_cycles) / 4;\n\n    // Fast seek: ~30ms\n    if (delta < 4371)\n        return (((36864000 / 1000) * 30) + contiguous_cycles) / 4;\n\n    // Full seek: ~100ms\n    return (((36864000 / 1000) * 100) + contiguous_cycles) / 4;\n}\n\nstatic inline void cdvd_set_status(struct ps2_cdvd* cdvd, uint8_t data) {\n    cdvd->status = data;\n    cdvd->sticky_status |= data;\n}\n\nstatic inline void cdvd_send_irq(struct ps2_cdvd* cdvd) {\n    ps2_iop_intc_irq(cdvd->intc, IOP_INTC_CDVD);\n\n    cdvd->i_stat |= 2;\n}\n\nvoid cdvd_fetch_sector(struct ps2_cdvd* cdvd) {\n    memset(cdvd->buf, 0, 2352);\n\n    switch (cdvd->read_size) {\n        case CDVD_CD_SS_2048:\n        case CDVD_CD_SS_2328: {\n            disc_read_sector(cdvd->disc, cdvd->buf, cdvd->read_lba++, DISC_SS_DATA);\n        } break;\n        case CDVD_CD_SS_2352: {\n            disc_read_sector(cdvd->disc, cdvd->buf, cdvd->read_lba++, DISC_SS_RAW);\n        } break;\n        case CDVD_CD_SS_2340: {\n            // LBA -> MSF\n            uint64_t a = cdvd->read_lba + 150;\n            uint32_t m = a / 4500;\n\n            a -= m * 4500;\n\n            uint32_t s = a / 75;\n            uint32_t f = a - (s * 75);\n\n            // Fill in header\n            cdvd->buf[0] = itob_table[m];\n            cdvd->buf[1] = itob_table[s];\n            cdvd->buf[2] = itob_table[f];\n            cdvd->buf[3] = 1;\n\n            // Write raw data at offset 12\n            disc_read_sector(cdvd->disc, cdvd->buf + 12, cdvd->read_lba++, DISC_SS_DATA);\n        } break;\n        case CDVD_DVD_SS: {\n            memset(cdvd->buf, 0, 2340);\n\n            uint32_t lba, layer;\n\n            if (cdvd->layer2_lba && (cdvd->read_lba >= cdvd->layer2_lba)) {\n                layer = cdvd->read_lba >= cdvd->layer2_lba;\n                lba = cdvd->read_lba - cdvd->layer2_lba + 0x30000;\n            } else {\n                layer = 0;\n                lba = cdvd->read_lba + 0x30000;\n            }\n\n            cdvd->buf[0] = 0x20 | layer;\n            cdvd->buf[1] = (lba >> 16) & 0xFF;\n            cdvd->buf[2] = (lba >> 8) & 0xFF;\n            cdvd->buf[3] = lba & 0xff;\n\n            disc_read_sector(cdvd->disc, cdvd->buf + 12, cdvd->read_lba++, DISC_SS_DATA);\n\n            // for (int i = 0; i < 2064;) {\n            //     for (int x = 0; x < 16; x++) {\n            //         printf(\"%02x \", cdvd->buf[i+x]);\n            //     }\n    \n            //     putchar('|');\n    \n            //     for (int x = 0; x < 16; x++) {\n            //         printf(\"%c\", isprint(cdvd->buf[i+x]) ? cdvd->buf[i+x] : '.');\n            //     }\n    \n            //     puts(\"|\");\n    \n            //     i += 16;\n            // }\n        } break;\n    }\n\n    if (!cdvd->mecha_decode)\n        return;\n\n    uint8_t shift_amount = (cdvd->mecha_decode >> 4) & 7;\n    int do_xor = (cdvd->mecha_decode) & 1;\n    int do_shift = (cdvd->mecha_decode) & 2;\n\n    for (int i = 0; i < cdvd->read_size; ++i) {\n        if (do_xor) cdvd->buf[i] ^= cdvd->cdkey[4];\n        if (do_shift) cdvd->buf[i] = (cdvd->buf[i] >> shift_amount) | (cdvd->buf[i] << (8 - shift_amount));\n    }\n}\n\nvoid cdvd_do_read(void* udata, int overshoot) {\n    struct ps2_cdvd* cdvd = (struct ps2_cdvd*)udata;\n\n    // Ugly hack!!\n    // Some games will send\n    if (!(cdvd->dma->cdvd.chcr & 0x1000000)) {\n        // printf(\"cdvd: CDVD DMA not yet ready\\n\");\n\n        struct sched_event event;\n\n        event.name = \"CDVD Read\";\n        event.udata = cdvd;\n        event.callback = cdvd_do_read;\n        event.cycles = 1000;\n\n        sched_schedule(cdvd->sched, event);\n\n        cdvd_set_status(cdvd, CDVD_STATUS_READING);\n\n        return;\n    }\n\n    // Fetch a sector\n    cdvd_fetch_sector(cdvd);\n\n    // Send sector to DMA\n    cdvd->buf_size = cdvd->read_size;\n    cdvd->read_count--;\n\n    // printf(\"cdvd: Sending a sector to DMA (left=%d)\\n\", cdvd->read_count);\n\n    iop_dma_handle_cdvd_transfer(cdvd->dma);\n\n    if (cdvd->read_count) {\n        struct sched_event event;\n\n        event.name = \"CDVD Read\";\n        event.udata = cdvd;\n        event.callback = cdvd_do_read;\n        event.cycles = 1000;\n\n        sched_schedule(cdvd->sched, event);\n\n        cdvd_set_status(cdvd, CDVD_STATUS_READING);\n\n        return;\n    }\n\n    cdvd->n_stat = 0x4e;\n    cdvd->n_cmd = 0;\n\n    cdvd_set_ready(cdvd);\n    cdvd_set_status(cdvd, CDVD_STATUS_PAUSED);\n\n    cdvd_send_irq(cdvd);\n\n    // I_STAT needs to be set to 3?\n    cdvd->i_stat |= 2;\n}\n\nstatic inline void cdvd_n_nop(struct ps2_cdvd* cdvd) {\n    printf(\"cdvd: nop\\n\");\n\n    cdvd_set_ready(cdvd);\n    cdvd_send_irq(cdvd);\n}\nstatic inline void cdvd_n_nop_sync(struct ps2_cdvd* cdvd) {\n    fprintf(stderr, \"cdvd: nop_sync\\n\"); exit(1);\n}\nstatic inline void cdvd_n_standby(struct ps2_cdvd* cdvd) {\n    printf(\"cdvd: standby\\n\");\n\n    cdvd_set_ready(cdvd);\n    cdvd_set_status(cdvd, CDVD_STATUS_PAUSED);\n\n    cdvd_send_irq(cdvd);\n}\nstatic inline void cdvd_n_stop(struct ps2_cdvd* cdvd) {\n    printf(\"cdvd: stop\\n\");\n\n    cdvd_set_ready(cdvd);\n    cdvd_set_status(cdvd, CDVD_STATUS_STOPPED);\n\n    cdvd_send_irq(cdvd);\n}\nstatic inline void cdvd_n_pause(struct ps2_cdvd* cdvd) {\n    printf(\"cdvd: pause\\n\");\n\n    cdvd_set_ready(cdvd);\n    cdvd_set_status(cdvd, CDVD_STATUS_PAUSED);\n\n    cdvd_send_irq(cdvd);\n\n    cdvd->n_cmd = 0;\n}\nstatic inline void cdvd_n_seek(struct ps2_cdvd* cdvd) {\n    printf(\"cdvd: seek\\n\");\n\n    cdvd->read_lba = *(uint32_t*)(cdvd->n_params);\n\n    cdvd_set_ready(cdvd);\n    cdvd_set_status(cdvd, CDVD_STATUS_SEEKING);\n\n    cdvd_send_irq(cdvd);\n}\nstatic inline void cdvd_n_read_cd(struct ps2_cdvd* cdvd) {\n    /*  Params:\n        0-3   Sector position\n        4-7   Sectors to read\n        10    Block size (1=2328 bytes, 2=2340 bytes, all others=2048 bytes)\n\n            Performs a CD-style read. Seems to raise bit 0 of CDVD I_STAT upon completion?\n    */\n\n    int prev_lba = cdvd->read_lba;\n\n    cdvd->read_lba = *(uint32_t*)(cdvd->n_params);\n    cdvd->read_count = *(uint32_t*)(cdvd->n_params + 4);\n    cdvd->read_speed = cdvd->n_params[9];\n    cdvd->read_size = CDVD_CD_SS_2048;\n\n    switch (cdvd->n_params[10]) {\n        case 1: cdvd->read_size = CDVD_CD_SS_2328; break;\n        case 2: cdvd->read_size = CDVD_CD_SS_2340; break;\n    }\n\n    struct sched_event event;\n\n    event.name = \"CDVD ReadCd\";\n    event.udata = cdvd;\n    event.callback = cdvd_do_read;\n    event.cycles = cdvd_get_cd_read_timing(cdvd, prev_lba);\n\n    sched_schedule(cdvd->sched, event);\n\n    cdvd_set_status(cdvd, CDVD_STATUS_READING);\n\n    // printf(\"cdvd: ReadCd lba=%08x count=%08x size=%d cycles=%ld speed=%02x (%p)\\n\",\n    //     cdvd->read_lba,\n    //     cdvd->read_count,\n    //     cdvd->read_size,\n    //     event.cycles,\n    //     cdvd->n_params[9],\n    //     cdvd->disc->read_sector\n    // );\n}\nstatic inline void cdvd_n_read_cdda(struct ps2_cdvd* cdvd) {\n    int prev_lba = cdvd->read_lba;\n\n    cdvd->read_lba = *(uint32_t*)(cdvd->n_params);\n    cdvd->read_count = *(uint32_t*)(cdvd->n_params + 4);\n    cdvd->read_speed = cdvd->n_params[9];\n    cdvd->read_size = CDVD_CD_SS_2352;\n\n    // fprintf(stderr, \"cdvd: ReadCdda lba=%d count=%d decode=%d\\n\", cdvd->read_lba, cdvd->read_count, cdvd->mecha_decode);\n\n    struct sched_event event;\n\n    event.name = \"CDVD ReadCdda\";\n    event.udata = cdvd;\n    event.callback = cdvd_do_read;\n    event.cycles = ((2352.f / 2.f) / 44100.f) * (36864000.f * 8);\n\n    cdvd_set_status(cdvd, CDVD_STATUS_READING);\n\n    sched_schedule(cdvd->sched, event);\n}\nstatic inline void cdvd_n_read_dvd(struct ps2_cdvd* cdvd) {\n    /*  Params:\n        0-3   Sector position\n        4-7   Sectors to read\n\n        Performs a DVD-style read, with a block size of 2064 bytes. The format of the data is as follows:\n        0    1    Volume number + 0x20\n        1    3    Sector number - volume start + 0x30000, in big-endian.\n        4    8    ? (all zeroes)\n        12   2048 Raw sector data\n        2060 4    ? (all zeroes)\n    */\n\n    int prev_lba = cdvd->read_lba;\n\n    cdvd->read_lba = *(uint32_t*)(cdvd->n_params);\n    cdvd->read_count = *(uint32_t*)(cdvd->n_params + 4);\n    cdvd->read_speed = cdvd->n_params[9];\n    cdvd->read_size = CDVD_DVD_SS;\n\n    struct sched_event event;\n\n    event.name = \"CDVD ReadDvd\";\n    event.udata = cdvd;\n    event.callback = cdvd_do_read;\n    event.cycles = cdvd_get_cd_read_timing(cdvd, prev_lba);\n\n    sched_schedule(cdvd->sched, event);\n\n    cdvd_set_status(cdvd, CDVD_STATUS_READING);\n}\nstatic inline void cdvd_n_get_toc(struct ps2_cdvd* cdvd) {\n    fprintf(stderr, \"cdvd: get_toc\\n\");\n\n    memset(cdvd->buf, 0, 2064);\n\n    if (cdvd->disc_type == CDVD_DISC_CDDA) {\n        int track_count = disc_get_track_count(cdvd->disc);\n        int disc_size = disc_get_size(cdvd->disc) / 2352;\n\n        int size_mm = disc_size / (60 * 75);\n        int size_ss = (disc_size % (60 * 75)) / 75;\n        int size_ff = (disc_size % (60 * 75)) % 75;\n\n        cdvd->buf[0] = 0x41;\n        cdvd->buf[1] = 0x00;\n\n        //Number of FirstTrack\n        cdvd->buf[2] = 0xa0;\n        cdvd->buf[7] = 0x01;\n\n        //Number of LastTrack\n        cdvd->buf[12] = 0xa1;\n        cdvd->buf[17] = itob_table[track_count];\n\n        //DiskLength\n        cdvd->buf[22] = 0xa2;\n        cdvd->buf[27] = itob_table[size_mm]; // mm\n        cdvd->buf[28] = itob_table[size_ss]; // ss\n        cdvd->buf[29] = itob_table[size_ff]; // ff\n\n        for (int i = 0; i < track_count; i++) {\n            int num = i + 1;\n\n            struct track_info info;\n\n            disc_get_track_info(cdvd->disc, num, &info);\n\n            int track_mm = info.lba / (60 * 75);\n            int track_ss = (info.lba % (60 * 75)) / 75;\n            int track_ff = (info.lba % (60 * 75)) % 75;\n\n            // fprintf(stderr, \"cdvd: track %d %02d:%02d:%02d\\n\", i + 1, track_mm, track_ss, track_ff);\n\n            cdvd->buf[30 + (i * 10)] = info.type;\n            cdvd->buf[32 + (i * 10)] = itob_table[num]; // Track number\n            cdvd->buf[37 + (i * 10)] = itob_table[track_mm]; // mm\n            cdvd->buf[38 + (i * 10)] = itob_table[track_ss]; // ss\n            cdvd->buf[39 + (i * 10)] = itob_table[track_ff]; // ff\n        }\n    } else if (!cdvd_is_dual_layer(cdvd)) {\n        cdvd->buf[0] = 0x04;\n        cdvd->buf[1] = 0x02;\n        cdvd->buf[2] = 0xF2;\n        cdvd->buf[3] = 0x00;\n        cdvd->buf[4] = 0x86;\n        cdvd->buf[5] = 0x72;\n        cdvd->buf[17] = 0x03;\n    } else {\n        cdvd->buf[0] = 0x24;\n        cdvd->buf[1] = 0x02;\n        cdvd->buf[2] = 0xF2;\n        cdvd->buf[3] = 0x00;\n        cdvd->buf[4] = 0x41;\n        cdvd->buf[5] = 0x95;\n\n        cdvd->buf[14] = 0x60;\n\n        cdvd->buf[16] = 0x00;\n        cdvd->buf[17] = 0x03;\n        cdvd->buf[18] = 0x00;\n        cdvd->buf[19] = 0x00;\n\n        int32_t start = cdvd->layer2_lba + 0x30000 - 1;\n\n        cdvd->buf[20] = start >> 24;\n        cdvd->buf[21] = (start >> 16) & 0xff;\n        cdvd->buf[22] = (start >> 8) & 0xff;\n        cdvd->buf[23] = start & 0xFF;\n    }\n\n    cdvd->buf_size = 2064;\n    cdvd->n_stat = 0x40;\n\n    iop_dma_handle_cdvd_transfer(cdvd->dma);\n\n    cdvd_set_ready(cdvd);\n    cdvd_set_status(cdvd, CDVD_STATUS_READING);\n\n    cdvd_send_irq(cdvd);\n\n    cdvd->n_cmd = 0;\n}\nstatic inline void cdvd_n_read_key(struct ps2_cdvd* cdvd) {\n    uint32_t b0 = cdvd->n_params[3];\n    uint32_t b1 = cdvd->n_params[4];\n    uint32_t b2 = cdvd->n_params[5];\n    uint32_t b3 = cdvd->n_params[6];\n    uint32_t arg = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24);\n\n    // Code referenced/taken from PCSX2\n    // This performs some kind of encryption/checksum with the game's serial?\n    memset(cdvd->cdkey, 0, 16);\n\n    char serial[16];\n\n    if (!disc_get_serial(cdvd->disc, serial)) {\n        printf(\"cdvd: Couldn't find game serial, can't get cdkey\\n\");\n    } else {\n        printf(\"cdvd: \\'%s\\'\\n\", serial);\n    }\n\n    int32_t letters = (int32_t)((serial[3] & 0x7F) << 0) |\n                (int32_t)((serial[2] & 0x7F) << 7) |\n                (int32_t)((serial[1] & 0x7F) << 14) |\n                (int32_t)((serial[0] & 0x7F) << 21);\n\n    char m[6];\n\n    for (int i = 0; i < 3; i++)\n        m[i] = serial[i+5];\n\n    for (int i = 0; i < 2; i++)\n        m[i+3] = serial[i+9];\n\n    m[5] = '\\0';\n    \n    int32_t code = strtoul(m, NULL, 10);\n\n    uint32_t key_0_3 = ((code & 0x1FC00) >> 10) | ((0x01FFFFFF & letters) << 7);\n    uint32_t key_4 = ((code & 0x0001F) << 3) | ((0x0E000000 & letters) >> 25);\n    uint32_t key_14 = ((code & 0x003E0) >> 2) | 0x04;\n\n    cdvd->cdkey[0] = (key_0_3 & 0x000000FF) >> 0;\n    cdvd->cdkey[1] = (key_0_3 & 0x0000FF00) >> 8;\n    cdvd->cdkey[2] = (key_0_3 & 0x00FF0000) >> 16;\n    cdvd->cdkey[3] = (key_0_3 & 0xFF000000) >> 24;\n    cdvd->cdkey[4] = key_4;\n\n    switch (arg) {\n        case 75: {\n            cdvd->cdkey[14] = key_14;\n            cdvd->cdkey[15] = 0x05;\n        } break;\n        case 4246: {\n            cdvd->cdkey[0] = 0x07;\n            cdvd->cdkey[1] = 0xF7;\n            cdvd->cdkey[2] = 0xF2;\n            cdvd->cdkey[3] = 0x01;\n            cdvd->cdkey[4] = 0x00;\n            cdvd->cdkey[15] = 0x01;\n        } break;\n        default: {\n            cdvd->cdkey[15] = 0x01;\n        } break;\n    }\n\n    cdvd_set_ready(cdvd);\n    cdvd_send_irq(cdvd);\n}\n\nstatic inline void cdvd_handle_n_command(struct ps2_cdvd* cdvd, uint8_t cmd) {\n    cdvd->n_cmd = cmd;\n\n    // fprintf(stdout, \"cdvd: N command %s (%02x)\\n\", cdvd_get_n_command_name(cmd), cmd);\n\n    cdvd_set_busy(cdvd);\n\n    switch (cdvd->n_cmd) {\n        case 0x00: cdvd_n_nop(cdvd); break;\n        case 0x01: cdvd_n_nop_sync(cdvd); break;\n        case 0x02: cdvd_n_standby(cdvd); break;\n        case 0x03: cdvd_n_stop(cdvd); break;\n        case 0x04: cdvd_n_pause(cdvd); break;\n        case 0x05: cdvd_n_seek(cdvd); break;\n        case 0x06: cdvd_n_read_cd(cdvd); break;\n        case 0x07: cdvd_n_read_cdda(cdvd); break;\n        case 0x08: cdvd_n_read_dvd(cdvd); break;\n        case 0x09: cdvd_n_get_toc(cdvd); break;\n        case 0x0c: cdvd_n_read_key(cdvd); break;\n        default: {\n            fprintf(stderr, \"cdvd: Unhandled N command %02x\\n\", cdvd->n_cmd);\n\n            exit(1);\n        } break;\n    }\n\n    // Reset N param FIFO\n    cdvd->n_param_index = 0;\n}\n\nstatic inline void cdvd_handle_n_param(struct ps2_cdvd* cdvd, uint8_t param) {\n    cdvd->n_params[cdvd->n_param_index++] = param;\n\n    if (cdvd->n_param_index > 15) {\n        fprintf(stderr, \"cdvd: N parameter FIFO overflow\\n\");\n\n        exit(1);\n    }\n}\n\nstruct ps2_cdvd* ps2_cdvd_create(void) {\n    return malloc(sizeof(struct ps2_cdvd));\n}\n\nvoid ps2_cdvd_init(struct ps2_cdvd* cdvd, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched) {\n    memset(cdvd, 0, sizeof(struct ps2_cdvd));\n\n    // 00:02:00\n    cdvd->read_lba = 0x150;\n\n    cdvd->n_stat = 0x4e;\n    cdvd->s_stat = CDVD_S_STATUS_NO_DATA;\n    cdvd->sticky_status = 0x1e;\n    cdvd->sched = sched;\n    cdvd->dma = dma;\n    cdvd->intc = intc;\n}\n\nvoid ps2_cdvd_destroy(struct ps2_cdvd* cdvd) {\n    ps2_cdvd_close(cdvd);\n\n    free(cdvd->s_fifo);\n    free(cdvd);\n}\n\nvoid cdvd_set_detected_type(void* udata, int overshoot) {\n    struct ps2_cdvd* cdvd = (struct ps2_cdvd*)udata;\n\n    cdvd_set_status(cdvd, CDVD_STATUS_PAUSED);\n\n    cdvd->disc_type = cdvd->detected_disc_type;\n}\n\nint ps2_cdvd_open(struct ps2_cdvd* cdvd, const char* path, int delay) {\n    ps2_cdvd_close(cdvd);\n\n    cdvd->layer2_lba = 0;\n\n    cdvd->disc = disc_open(path);\n\n    if (!cdvd->disc) {\n        printf(\"cdvd: Couldn't open disc \\'%s\\'\\n\", path);\n\n        return 1;\n    }\n\n    cdvd->detected_disc_type = disc_get_type(cdvd->disc);\n    cdvd->layer2_lba = disc_get_volume_lba(cdvd->disc, 1);\n\n    fprintf(stdout, \"cdvd: Opened \\'%s\\' (%s)\\n\", path, cdvd_get_type_name(cdvd->detected_disc_type));\n\n    if (!delay) {\n        cdvd_set_status(cdvd, CDVD_STATUS_SPINNING);\n\n        cdvd->disc_type = cdvd->detected_disc_type;\n\n        return 0;\n    }\n\n    switch (cdvd->detected_disc_type) {\n        case CDVD_DISC_PS2_CD:\n        case CDVD_DISC_PS2_CDDA:\n        case CDVD_DISC_CDDA:\n        case CDVD_DISC_PSX_CD:\n        case CDVD_DISC_PSX_CDDA: {\n            cdvd->disc_type = CDVD_DISC_DETECTING_CD;\n        } break;\n\n        case CDVD_DISC_PS2_DVD:\n        case CDVD_DISC_DVD_VIDEO: {\n            cdvd->disc_type = cdvd->layer2_lba ? CDVD_DISC_DETECTING_DL_DVD : CDVD_DISC_DETECTING_DVD;\n        } break;\n    }\n\n    cdvd_set_status(cdvd, CDVD_STATUS_TRAY_OPEN_BIT);\n\n    struct sched_event event;\n\n    event.cycles = delay; // IOP clock * 2 = 2s\n    event.udata = cdvd;\n    event.name = \"CDVD disc detect\";\n    event.callback = cdvd_set_detected_type;\n\n    sched_schedule(cdvd->sched, event);\n\n    return 0;\n}\n\nvoid ps2_cdvd_close(struct ps2_cdvd* cdvd) {\n    if (cdvd->disc) {\n        disc_close(cdvd->disc);\n\n        cdvd->disc = NULL;\n    }\n\n    cdvd->disc_type = CDVD_DISC_NO_DISC;\n\n    cdvd_set_status(cdvd, CDVD_STATUS_TRAY_OPEN_BIT);\n\n    // Send disc ejected IRQ\n    cdvd->i_stat = CDVD_IRQ_DISC_EJECTED;\n\n    ps2_iop_intc_irq(cdvd->intc, IOP_INTC_CDVD);\n}\n\nvoid ps2_cdvd_power_off(struct ps2_cdvd* cdvd) {\n    // Send poweroff IRQ\n    cdvd->i_stat = CDVD_IRQ_POWER_OFF;\n\n    ps2_iop_intc_irq(cdvd->intc, IOP_INTC_CDVD);\n}\n\nuint8_t cdvd_read_speed(struct ps2_cdvd* cdvd) {\n    uint8_t speed = cdvd->read_speed & 0x3F;\n\n    if (!speed)\n        speed = (cdvd->disc_type == CDVD_DISC_PS2_DVD) ? 3 : 5;\n\n    if (cdvd->disc_type == CDVD_DISC_PS2_DVD)\n        speed += 0xF;\n    else\n        speed--;\n\n    return speed;\n}\n\nuint64_t ps2_cdvd_read8(struct ps2_cdvd* cdvd, uint32_t addr) {\n    // printf(\"cdvd: read %08x\\n\", addr);\n\n    switch (addr) {\n        case 0x1F402004: printf(\"cdvd: read n_cmd %x\\n\", cdvd->n_cmd); return cdvd->n_cmd;\n        case 0x1F402005: printf(\"cdvd: read n_stat %x\\n\", cdvd->n_stat); return cdvd->n_stat;\n        // case 0x1F402005: (W)\n        case 0x1F402006: printf(\"cdvd: read error %x\\n\", 0); return 0; //cdvd->error;\n        // case 0x1F402007: (W)\n        case 0x1F402008: printf(\"cdvd: read i_stat %x\\n\", cdvd->i_stat); return cdvd->i_stat;\n        case 0x1F40200A: printf(\"cdvd: read status %x\\n\", cdvd->status); return cdvd->status;\n        case 0x1F40200B: printf(\"cdvd: read sticky_status %x\\n\", cdvd->sticky_status); return cdvd->sticky_status;\n        case 0x1F40200F: printf(\"cdvd: read disc_type %x\\n\", cdvd->disc_type); return cdvd->disc_type;\n        case 0x1F402013: printf(\"cdvd: read speed %x\\n\", cdvd_read_speed(cdvd)); return cdvd_read_speed(cdvd);\n        case 0x1F402015: return 0xff;\n        case 0x1F402016: printf(\"cdvd: read s_cmd %x\\n\", cdvd->s_cmd); return cdvd->s_cmd;\n        case 0x1F402017: printf(\"cdvd: read s_stat %x\\n\", cdvd->s_stat); return cdvd->s_stat;\n        // case 0x1F402017: (W);\n        case 0x1F402018: { int r = cdvd_read_s_response(cdvd); printf(\"cdvd: read s_response %x\\n\", r); return r; }\n\n        case 0x1F402020:\n        case 0x1F402021:\n        case 0x1F402022:\n        case 0x1F402023:\n        case 0x1F402024:\n            printf(\"cdvd: ReadKey %08x (%d) -> %02x\\n\", addr, addr - 0x1f402020, cdvd->cdkey[addr - 0x1F402020]);\n            return cdvd->cdkey[addr - 0x1F402020];\n        case 0x1F402028:\n        case 0x1F402029:\n        case 0x1F40202A:\n        case 0x1F40202B:\n        case 0x1F40202C:\n            printf(\"cdvd: ReadKey %08x (%d) -> %02x\\n\", addr, addr - 0x1f402023, cdvd->cdkey[addr - 0x1F402023]);\n            return cdvd->cdkey[addr - 0x1F402023];\n        case 0x1F402030:\n        case 0x1F402031:\n        case 0x1F402032:\n        case 0x1F402033:\n        case 0x1F402034:\n            printf(\"cdvd: ReadKey %08x (%d) -> %02x\\n\", addr, addr - 0x1f402026, cdvd->cdkey[addr - 0x1F402026]);\n            return cdvd->cdkey[addr - 0x1F402026];\n        case 0x1F402038:\n            printf(\"cdvd: ReadKey %08x (%d) -> %02x\\n\", addr, addr - 0x1f402038, cdvd->cdkey[15]);\n            return cdvd->cdkey[15];\n    }\n\n    printf(\"cdvd: unknown read %08x\\n\", addr);\n    \n    return 0;\n}\n\nvoid ps2_cdvd_write8(struct ps2_cdvd* cdvd, uint32_t addr, uint64_t data) {\n    // printf(\"cdvd: write %08x %02x\\n\", addr, data);\n\n    switch (addr) {\n        case 0x1F402004: cdvd_handle_n_command(cdvd, data); return;\n        case 0x1F402005: cdvd_handle_n_param(cdvd, data); return;\n        case 0x1F402006: /* Read-only */ return;\n        case 0x1F402007: printf(\"cdvd: break\\n\"); /* To-do: BREAK */ return;\n        case 0x1F402008: cdvd->i_stat &= ~data; return;\n        case 0x1F40200A: return;\n        case 0x1F40200B: return;\n        case 0x1F40200F: return;\n        case 0x1F402016: cdvd_handle_s_command(cdvd, data); return;\n        case 0x1F402017: cdvd_handle_s_param(cdvd, data); return;\n        // case 0x1F402017: (W);\n        case 0x1F40203A: cdvd->mecha_decode = data; return;\n        case 0x1F402018: return;\n    }\n\n    return;\n}\n\nvoid ps2_cdvd_reset(struct ps2_cdvd* cdvd) {\n    cdvd->n_stat = 0x4c;\n    cdvd->read_lba = 0x150;\n    cdvd->read_count = 0;\n    cdvd->read_size = 0;\n    cdvd->read_speed = 0;\n\n    cdvd->config_rw = 0;\n    cdvd->config_offset = 0;\n    cdvd->config_numblocks = 0;\n    cdvd->config_block_index = 0;\n    cdvd->s_cmd = 0;\n    cdvd->buf_size = 0;\n}\n\nvoid ps2_cdvd_set_mechacon_model(struct ps2_cdvd* cdvd, int model) {\n    cdvd->mechacon_model = model;\n\n    switch (model) {\n        case CDVD_MECHACON_SPC970: {\n            cdvd->layout = g_spc970_layout;\n        } break;\n\n        case CDVD_MECHACON_DRAGON: {\n            cdvd->layout = g_dragon_layout;\n        } break;\n    }\n}\n\nint ps2_cdvd_load_nvram(struct ps2_cdvd* cdvd, const char* path) {\n    FILE* file = fopen(path, \"rb\");\n\n    if (!file) {\n        printf(\"cdvd: Couldn't open NVRAM file \\'%s\\'\\n\", path);\n\n        return 0;\n    }\n\n    fread(cdvd->nvram, 1, 1024, file);\n    fclose(file);\n\n    return 1;\n}"
  },
  {
    "path": "src/iop/cdvd.h",
    "content": "#ifndef CDVD_H\n#define CDVD_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"scheduler.h\"\n#include \"dma.h\"\n#include \"disc.h\"\n\n/*\n    0     Tray status (1=open)\n    1     Spindle spinning (1=spinning)\n    2     Read status (1=reading data sectors)\n    3     Paused\n    4     Seek status (1=seeking)\n    5     Error (1=error occurred)\n    6-7   Unknown\n*/\n#define CDVD_STATUS_TRAY_OPEN_BIT 1\n#define CDVD_STATUS_SPINNING_BIT  2\n#define CDVD_STATUS_READING_BIT   4\n#define CDVD_STATUS_PAUSED_BIT    8\n#define CDVD_STATUS_SEEKING_BIT   16\n#define CDVD_STATUS_ERROR_BIT     32\n#define CDVD_STATUS_STOPPED       0x00\n#define CDVD_STATUS_SPINNING      0x02\n#define CDVD_STATUS_READING       0x06\n#define CDVD_STATUS_PAUSED        0x0A\n#define CDVD_STATUS_SEEKING       0x12\n\n/*\n    0     Error (1=error occurred)\n    1     Unknown/unused\n    2     DEV9 device connected (1=HDD/network adapter connected)\n    3     Unknown/unused\n    4     Test mode\n    5     Power off ready\n    6     Drive status (1=ready)\n    7     Busy executing NCMD\n*/\n#define CDVD_N_STATUS_ERROR          1\n#define CDVD_N_STATUS_DEV9_CONNECTED 4\n#define CDVD_N_STATUS_TEST_MODE      16\n#define CDVD_N_STATUS_POWER_OFF      32\n#define CDVD_N_STATUS_READY          64\n#define CDVD_N_STATUS_BUSY           128\n\n/*\n    0     Data ready?\n    1     (N?) Command complete\n    2     Power off pressed\n    3     Disk ejected\n    4     BS_Power DET?\n    5-7   Unused\n*/\n#define CDVD_IRQ_DATA_READY   1\n#define CDVD_IRQ_NCMD_DONE    2\n#define CDVD_IRQ_POWER_OFF    4\n#define CDVD_IRQ_DISC_EJECTED 8\n#define CDVD_IRQ_BS_POWER     16\n\n/*\n    0-5   Unknown\n    6     Result data available (0=available, 1=no data)\n    7     Busy\n*/\n#define CDVD_S_STATUS_NO_DATA 64\n#define CDVD_S_STATUS_BUSY    128\n\n#define CDVD_CD_SS_2328 2328\n#define CDVD_CD_SS_2340 2340\n#define CDVD_CD_SS_2048 2048\n#define CDVD_CD_SS_2352 2352\n#define CDVD_DVD_SS 2064\n\nstruct nvram_layout {\n    uint32_t bios_version;   // bios version that this eeprom layout is for\n    int32_t config0_offset;   // offset of 1st config block\n    int32_t config1_offset;   // offset of 2nd config block\n    int32_t config2_offset;   // offset of 3rd config block\n    int32_t console_id_offset; // offset of console id (?)\n    int32_t ilink_id_offset;   // offset of ilink id (ilink mac address)\n    int32_t modelnum_offset;  // offset of ps2 model number (eg \"SCPH-70002\")\n    int32_t regparams_offset; // offset of RegionParams for PStwo\n    int32_t mac_offset;       // offset of MAC address on PStwo\n};\n\nenum {\n    CDVD_MECHACON_SPC970,\n    CDVD_MECHACON_DRAGON\n};\n\nstruct ps2_cdvd {\n    uint8_t n_cmd;\n    uint8_t n_stat;\n    uint8_t error;\n    uint8_t i_stat;\n    uint8_t status;\n    uint8_t sticky_status;\n    uint8_t disc_type;\n    uint8_t s_cmd;\n    uint8_t s_stat;\n\n#ifdef _MSC_VER\n    __declspec(align(4)) uint8_t n_params[16];\n    __declspec(align(4)) uint8_t s_params[16];\n#else\n    uint8_t n_params[16] __attribute__((aligned(4)));\n    uint8_t s_params[16] __attribute__((aligned(4)));\n#endif\n\n    uint8_t* s_fifo;\n    int n_param_index;\n    int s_param_index;\n    int s_fifo_index;\n    int s_fifo_size;\n\n    uint8_t detected_disc_type;\n\n    uint8_t mecha_decode;\n    uint8_t cdkey[16];\n\n    struct disc_state* disc;\n    uint8_t buf[2352];\n    int buf_size;\n\n    // Pending read\n    uint32_t read_lba;\n    uint32_t read_count;\n    uint32_t read_size;\n    uint8_t read_speed;\n\n    uint8_t nvram[1024];\n\n    struct ps2_iop_dma* dma;\n    struct ps2_iop_intc* intc;\n    struct sched_state* sched;\n    uint64_t layer2_lba;\n\n    uint32_t config_rw;\n    uint32_t config_offset;\n    uint32_t config_numblocks;\n    uint32_t config_block_index;\n\n    int mechacon_model;\n    struct nvram_layout layout;\n\n    // To-do:\n    // void (*poweroff_handler)(void* udata)\n    // void (*trayctrl_handler)(void* udata, uint8_t ctrl)\n};\n\nstruct ps2_cdvd* ps2_cdvd_create(void);\nvoid ps2_cdvd_init(struct ps2_cdvd* cdvd, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched);\nvoid ps2_cdvd_destroy(struct ps2_cdvd* cdvd);\nint ps2_cdvd_open(struct ps2_cdvd* cdvd, const char* path, int delay);\nvoid ps2_cdvd_close(struct ps2_cdvd* cdvd);\nvoid ps2_cdvd_power_off(struct ps2_cdvd* cdvd);\nint ps2_cdvd_load_nvram(struct ps2_cdvd* cdvd, const char* path);\nvoid ps2_cdvd_set_mechacon_model(struct ps2_cdvd* cdvd, int model);\nuint64_t ps2_cdvd_read8(struct ps2_cdvd* cdvd, uint32_t addr);\nvoid ps2_cdvd_write8(struct ps2_cdvd* cdvd, uint32_t addr, uint64_t data);\nvoid ps2_cdvd_reset(struct ps2_cdvd* cdvd);\n\n#undef ALIGNED_U32\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/disc/bin.c",
    "content": "#include <stdlib.h>\n\n#include \"../disc.h\"\n#include \"bin.h\"\n\nstruct disc_bin* bin_create(void) {\n    return malloc(sizeof(struct disc_bin));\n}\n\nint bin_init(struct disc_bin* bin, const char* path) {\n    bin->file = fopen(path, \"rb\");\n\n    if (!bin->file) {\n        free(bin);\n\n        return 0;\n    }\n\n    return 1;\n}\n\nvoid bin_destroy(struct disc_bin* bin) {\n    fclose(bin->file);\n    free(bin);\n}\n\n// Disc IF\nint bin_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size) {\n    struct disc_bin* bin = (struct disc_bin*)udata;\n\n    int s, r;\n\n    if (size == DISC_SS_DATA) {\n        s = fseek64(bin->file, (lba * 2352) + 0x18, SEEK_SET);\n        r = fread(buf, 1, 2048, bin->file);\n    } else {\n        s = fseek64(bin->file, lba * 2352, SEEK_SET);\n        r = fread(buf, 1, 2352, bin->file);\n    }\n\n    return r && !s;\n}\n\nuint64_t bin_get_size(void* udata) {\n    struct disc_bin* bin = (struct disc_bin*)udata;\n\n    fseek64(bin->file, 0, SEEK_END);\n\n    return ftell64(bin->file);\n}\n\nint bin_get_sector_size(void* udata) {\n    return 2352;\n}\n\nint bin_get_track_count(void* udata) {\n    return 1;\n}\n\nint bin_get_track_info(void* udata, int track, struct track_info* info) {\n    return 0;\n}\n\nint bin_get_track_number(void* udata, uint64_t lba) {\n    return 1;\n}\n\n#undef fseek64\n#undef ftell64"
  },
  {
    "path": "src/iop/disc/bin.h",
    "content": "#ifndef BIN_H\n#define BIN_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdio.h>\n#include <stdint.h>\n\n#include \"../disc.h\"\n\nstruct disc_bin {\n    FILE* file;\n};\n\nstruct disc_bin* bin_create(void);\nint bin_init(struct disc_bin* bin, const char* path);\nvoid bin_destroy(struct disc_bin* bin);\n\n// Disc IF\nint bin_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size);\nuint64_t bin_get_size(void* udata);\nint bin_get_sector_size(void* udata);\nint bin_get_track_count(void* udata);\nint bin_get_track_info(void* udata, int track, struct track_info* info);\nint bin_get_track_number(void* udata, uint64_t lba);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/disc/chd.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n\n#include \"chd.h\"\n\nint get_sector_type_size(const char* type) {\n    if (strncmp(type, \"MODE1\", 64) == 0) {\n        return 2048;\n    } else if (strncmp(type, \"MODE1_RAW\", 64) == 0) {\n        return 2352;\n    } else if (strncmp(type, \"MODE2\", 64) == 0) {\n        return 2336;\n    } else if (strncmp(type, \"MODE2_FORM1\", 64) == 0) {\n        return 2048;\n    } else if (strncmp(type, \"MODE2_FORM2\", 64) == 0) {\n        return 2324;\n    } else if (strncmp(type, \"MODE2_FORM_MIX\", 64) == 0) {\n        return 2336;\n    } else if (strncmp(type, \"MODE2_RAW\", 64) == 0) {\n        return 2352;\n    }\n\n    return 0;\n}\n\nstruct disc_chd* chd_create(void) {\n    return malloc(sizeof(struct disc_chd));\n}\n\nint chd_init(struct disc_chd* chd, const char* path) {\n    if (chd_open(path, CHD_OPEN_READ, NULL, &chd->file)) {\n        chd_close(chd->file);\n\n        return 0;\n    }\n\n    chd->header = chd_get_header(chd->file);\n    chd->buffer = malloc(chd->header->hunkbytes);\n\n    // Get sector size from metadata\n    char buf[512], type[64];\n    uint32_t len, tag;\n    uint8_t flags;\n\n    if (chd_get_metadata(chd->file, CDROM_TRACK_METADATA2_TAG, 0, buf, 512, &len, &tag, &flags)) {\n        printf(\"chd: Failed to get metadata\\n\");\n\n        return 0;\n    }\n\n    if (tag != CDROM_TRACK_METADATA2_TAG) {\n        printf(\"chd: Failed to get track metadata\\n\");\n\n        return 0;\n    }\n\n    // Parse track type\n    char* c = buf;\n    char* t = type;\n\n    while (*c != ':') c++;\n\n    c++;\n\n    while (*c != ':') c++;\n\n    c++;\n\n    while (((t - type) < 64) && !isspace(*c)) {\n        *t++ = *c++;\n    }\n\n    *t = '\\0';\n\n    chd->sector_size = get_sector_type_size(type);\n\n    if (!chd->sector_size) {\n        printf(\"chd: Unsupported sector type \\'%s\\'\\n\", type);\n\n        return 0;\n    }\n\n    return 1;\n}\n\nvoid chd_destroy(struct disc_chd* chd) {\n    chd_close(chd->file);\n    \n    free(chd->buffer);\n    free(chd);\n}\n\nstatic inline size_t find_hunk_number(struct disc_chd* chd, uint64_t offset) {\n    return offset / chd->header->hunkbytes;\n}\n\n// Disc IF\nint chd_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size) {\n    struct disc_chd* chd = (struct disc_chd*)udata;\n\n    uint64_t offset = lba * chd->header->unitbytes;\n\n    size_t hunknum = find_hunk_number(chd, offset);\n\n    if (chd->cached_hunknum != hunknum) {\n        chd->cached_hunknum = hunknum;\n\n        // Cache hunk\n        chd_read(chd->file, hunknum, chd->buffer);\n    } else {\n        uint8_t* base = chd->buffer + (offset % chd->header->hunkbytes);\n\n        // Hunk is cached\n        if (chd->sector_size == 2048) {\n            memcpy(buf, base, 2048);\n        } else {\n            if (size == DISC_SS_DATA) {\n                memcpy(buf, base + 0x18, 2048);\n            } else {\n                memcpy(buf, base, 2352);\n            }\n        }\n\n        return 1;\n    }\n\n    // Read cached hunk\n    return chd_read_sector(udata, buf, lba, size);\n}\n\nuint64_t chd_get_size(void* udata) {\n    struct disc_chd* chd = (struct disc_chd*)udata;\n\n    return chd->sector_size * chd->header->hunkcount;\n}\n\nint chd_get_sector_size(void* udata) {\n    struct disc_chd* chd = (struct disc_chd*)udata;\n\n    return chd->sector_size;\n}\n\nint chd_get_track_count(void* udata) {\n    return 1;\n}\n\nint chd_get_track_info(void* udata, int track, struct track_info* info) {\n    return 0;\n}\n\nint chd_get_track_number(void* udata, uint64_t lba) {\n    return 1;\n}\n\n#undef fseek64\n#undef ftell64"
  },
  {
    "path": "src/iop/disc/chd.h",
    "content": "#ifndef CHD_H\n#define CHD_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"../disc.h\"\n\n#include <libchdr/chd.h>\n#include <stdio.h>\n#include <stdint.h>\n\nstruct disc_chd {\n    const chd_header* header;\n    chd_file* file;\n    uint8_t* buffer;\n    size_t cached_hunknum;\n    int sector_size;\n};\n\nstruct disc_chd* chd_create(void);\nint chd_init(struct disc_chd* chd, const char* path);\nvoid chd_destroy(struct disc_chd* chd);\n\n// Disc IF\nint chd_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size);\nuint64_t chd_get_size(void* udata);\nint chd_get_sector_size(void* udata);\nint chd_get_track_count(void* udata);\nint chd_get_track_info(void* udata, int track, struct track_info* info);\nint chd_get_track_number(void* udata, uint64_t lba);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/disc/ciso.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <ctype.h>\n\n#include \"ciso.h\"\n\n#define CISO_DEFLATE 0\n#define CISO_LZ4 1\n\nint get_compression_type(const char* path) {\n    char* ext = strrchr(path, '.');\n\n    if (!ext) {\n        return CISO_DEFLATE;\n    }\n\n    if (tolower(ext[1]) == 'c') {\n        return CISO_DEFLATE;\n    } else if (tolower(ext[1]) == 'z') {\n        return CISO_LZ4;\n    }\n\n    return CISO_DEFLATE;\n}\n\nint realloc_comp_buf(struct disc_ciso* ciso, size_t size) {\n    if (size > ciso->comp_buf_cap) {\n        uint8_t* new_buf = realloc(ciso->comp_buf, size);\n\n        if (!new_buf) {\n            return 0;\n        }\n\n        ciso->comp_buf = new_buf;\n        ciso->comp_buf_cap = size;\n    }\n\n    return 1;\n}\n\nstruct disc_ciso* ciso_create(void) {\n    return malloc(sizeof(struct disc_ciso));\n}\n\nint ciso_init(struct disc_ciso* ciso, const char* path) {\n    memset(ciso, 0, sizeof(struct disc_ciso));\n\n    ciso->file = fopen(path, \"rb\");\n\n    if (!ciso->file) {\n        free(ciso);\n\n        return 0;\n    }\n\n    fread(&ciso->header, sizeof(struct ciso_header), 1, ciso->file);\n\n    if (ciso->header.magic != 0x4f534943 && ciso->header.magic != 0x4f53495a) {\n        fprintf(stderr, \"ciso: Invalid CISO/ZISO magic\\n\");\n\n        ciso_destroy(ciso);\n\n        return 0;\n    }\n\n    if (ciso->header.version > 1) {\n        fprintf(stderr, \"ciso: Unsupported CISO version %u\\n\", ciso->header.version);\n\n        ciso_destroy(ciso);\n\n        return 0;\n    }\n\n    if (ciso->header.block_size != 2048) {\n        fprintf(stderr, \"ciso: Unsupported CISO header size %u\\n\", ciso->header.header_size);\n\n        ciso_destroy(ciso);\n\n        return 0;\n    }\n\n    ciso->compression_type = get_compression_type(path);\n\n    ciso->decompressor = libdeflate_alloc_decompressor();\n\n    if (!ciso->decompressor) {\n        fprintf(stderr, \"ciso: Failed to allocate decompressor\\n\");\n\n        ciso_destroy(ciso);\n\n        return 0;\n    }\n\n    ciso->index_count = (ciso->header.uncompressed_size / (uint64_t)ciso->header.block_size) + 1;\n    ciso->index_table = malloc(ciso->index_count * sizeof(uint32_t));\n\n    if (!ciso->index_table) {\n        fprintf(stderr, \"ciso: Failed to allocate index table\\n\");\n\n        ciso_destroy(ciso);\n\n        return 0;\n    }\n\n    fread(ciso->index_table, ciso->index_count * sizeof(uint32_t), 1, ciso->file);\n\n    return 1;\n}\n\nvoid ciso_destroy(struct disc_ciso* ciso) {\n    if (ciso->file) fclose(ciso->file);\n    if (ciso->decompressor) libdeflate_free_decompressor(ciso->decompressor);\n    if (ciso->index_table) free(ciso->index_table);\n    if (ciso->comp_buf) free(ciso->comp_buf);\n\n    free(ciso);\n}\n\n// Disc IF\nint ciso_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size) {\n    struct disc_ciso* ciso = (struct disc_ciso*)udata;\n\n    if (lba * 2048 >= ciso->header.uncompressed_size)\n        return 0;\n\n    size = 2048;\n\n    uint32_t index_entry = ciso->index_table[lba];\n    uint32_t next_index_entry = ciso->index_table[lba + 1];\n\n    uint32_t offset = index_entry & 0x7fffffff;\n    uint32_t next_offset = next_index_entry & 0x7fffffff;\n\n    uint32_t buf_size = next_offset - offset;\n\n    int is_compressed = !(index_entry & 0x80000000);\n\n    fseek(ciso->file, offset, SEEK_SET);\n\n    if (is_compressed) {\n        realloc_comp_buf(ciso, buf_size);\n\n        fseek(ciso->file, offset, SEEK_SET);\n        fread(ciso->comp_buf, 1, buf_size, ciso->file);\n\n        size_t actual_decomp_size = 0;\n\n        if (ciso->compression_type == CISO_LZ4) {\n            actual_decomp_size = LZ4_decompress_safe(\n                (const char*)ciso->comp_buf, (char*)buf,\n                buf_size, size\n            );\n        } else {\n            libdeflate_deflate_decompress(\n                ciso->decompressor,\n                ciso->comp_buf, buf_size,\n                buf, size,\n                &actual_decomp_size\n            );\n        }\n\n        if (actual_decomp_size != 2048) {\n            fprintf(stderr, \"ciso: Decompression failed for sector %llu\\n\", lba);\n\n            return 0;\n        }\n\n        return 1;\n    }\n\n    if (buf_size != 2048) {\n        fprintf(stderr, \"ciso: Invalid uncompressed sector size %u for sector %llu\\n\", buf_size, lba);\n\n        return 0;\n    }\n\n    fread(buf, 1, buf_size, ciso->file);\n\n    return 1;\n}\n\nuint64_t ciso_get_size(void* udata) {\n    struct disc_ciso* ciso = (struct disc_ciso*)udata;\n\n    return ciso->header.uncompressed_size;\n}\n\nint ciso_get_sector_size(void* udata) {\n    return 2048;\n}\n\nint ciso_get_track_count(void* udata) {\n    return 1;\n}\n\nint ciso_get_track_info(void* udata, int track, struct track_info* info) {\n    return 0;\n}\n\nint ciso_get_track_number(void* udata, uint64_t lba) {\n    return 1;\n}"
  },
  {
    "path": "src/iop/disc/ciso.h",
    "content": "#ifndef CISO_H\n#define CISO_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"../disc.h\"\n\n#include <stdio.h>\n#include <stdint.h>\n\n#include <libdeflate.h>\n#include <lz4.h>\n\nstruct ciso_header {\n    uint32_t magic;\n    uint32_t header_size;\n    uint64_t uncompressed_size;\n    uint32_t block_size;\n    uint8_t version;\n    uint8_t alignment;\n    uint8_t reserved[2];\n};\n\nstruct disc_ciso {\n    struct ciso_header header;\n    struct libdeflate_decompressor* decompressor;\n    FILE* file;\n    uint32_t* index_table;\n    size_t index_count;\n    int compression_type;\n    uint8_t* comp_buf;\n    size_t comp_buf_cap;\n};\n\nstruct disc_ciso* ciso_create(void);\nint ciso_init(struct disc_ciso* ciso, const char* path);\nvoid ciso_destroy(struct disc_ciso* ciso);\n\n// Disc IF\nint ciso_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size);\nuint64_t ciso_get_size(void* udata);\nint ciso_get_sector_size(void* udata);\nint ciso_get_track_count(void* udata);\nint ciso_get_track_info(void* udata, int track, struct track_info* info);\nint ciso_get_track_number(void* udata, uint64_t lba);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/disc/cue.c",
    "content": "#include <stdlib.h>\n#include <stdint.h>\n#include <string.h>\n#include <stdio.h>\n#include <ctype.h>\n\n#include \"cue.h\"\n#include \"../disc.h\"\n\nstatic const char* cue_keywords[] = {\n    \"4CH\",\n    \"AIFF\",\n    \"AUDIO\",\n    \"BINARY\",\n    \"CATALOG\",\n    \"CDG\",\n    \"CDI/2336\",\n    \"CDI/2352\",\n    \"CDTEXTFILE\",\n    \"DCP\",\n    \"FILE\",\n    \"FLAGS\",\n    \"INDEX\",\n    \"ISRC\",\n    \"MODE1/2048\",\n    \"MODE1/2352\",\n    \"MODE2/2336\",\n    \"MODE2/2352\",\n    \"MOTOROLA\",\n    \"MP3\",\n    \"PERFORMER\",\n    \"POSTGAP\",\n    \"PRE\",\n    \"PREGAP\",\n    \"REM\",\n    \"SCMS\",\n    \"SONGWRITER\",\n    \"TITLE\",\n    \"TRACK\",\n    \"WAVE\",\n    0\n};\n\nchar* strapp(char* dst, const char* a, const char* b) {\n    char* d = dst;\n\n    while (*a)\n        *dst++ = *a++;\n\n    while (*b)\n        *dst++ = *b++;\n\n    *dst = '\\0';\n\n    return d;\n}\n\nconst char* find_last_slash(const char* a) {\n    if (!a)\n        return NULL;\n\n    const char* b = a;\n\n    while (*a) {\n        if (*a == '/' || *a == '\\\\')\n            b = a + 1;\n\n        ++a;\n    }\n\n    return b;\n}\n\nchar* get_root_path(char* dst, const char* a) {\n    if (!a) {\n        *dst = '\\0';\n\n        return dst;\n    }\n\n    const char* b = a;\n    const char* c = a;\n    char* d = dst;\n\n    while (*a) {\n        if (*a == '/' || *a == '\\\\')\n            b = a + 1;\n\n        ++a;\n    }\n\n    while (c != b)\n        *dst++ = *c++;\n\n    *dst = '\\0';\n\n    return d;\n}\n\nint cue_parse_keyword(struct disc_cue* cue) {\n    char buf[256];\n    char* ptr = buf;\n\n    while (isalpha(cue->c) || isdigit(cue->c) || cue->c == '/') {\n        *ptr++ = cue->c;\n\n        cue->c = fgetc(cue->file);\n    }\n\n    *ptr = '\\0';\n\n    int i = 0;\n\n    const char* keyword = cue_keywords[i];\n\n    while (keyword) {\n        if (!strcmp(keyword, buf)) {\n            return i;\n        } else {\n            keyword = cue_keywords[++i];\n        }\n    }\n\n    return -1;\n}\n\nint cue_parse_number(struct disc_cue* cue) {\n    if (!isdigit(cue->c))\n        return 0;\n\n    char buf[4];\n\n    char* ptr = buf;\n\n    while (isdigit(cue->c)) {\n        *ptr++ = cue->c;\n\n        cue->c = fgetc(cue->file);\n    }\n\n    *ptr = '\\0';\n\n    return atoi(buf);\n}\n\nuint64_t cue_parse_msf(struct disc_cue* cue) {\n    int m = 0;\n    int s = 0;\n    int f = 0;\n\n    if (!isdigit(cue->c))\n        return 0;\n\n    m = cue_parse_number(cue);\n\n    if (cue->c != ':')\n        return 0;\n\n    cue->c = fgetc(cue->file);\n\n    s = cue_parse_number(cue);\n\n    if (cue->c != ':')\n        return 0;\n\n    cue->c = fgetc(cue->file);\n\n    f = cue_parse_number(cue);\n\n    // 1 second = 75 frames (sectors)\n    // 1 minute = 60 seconds = 4500 frames\n    return f + (s * 75) + (m * 4500);\n}\n\nvoid cue_parse_index(struct disc_cue* cue) {\n    cue_track_t* track = list_back(cue->tracks)->data;\n\n    while (isspace(cue->c))\n        cue->c = fgetc(cue->file);\n\n    if (!isdigit(cue->c))\n        return;\n\n    int i = cue_parse_number(cue);\n\n    while (isspace(cue->c))\n        cue->c = fgetc(cue->file);\n\n    if (i > 1)\n        return;\n\n    track->index[i] = cue_parse_msf(cue);\n}\n\ncue_track_t* cue_parse_track(struct disc_cue* cue) {\n    while (isspace(cue->c))\n        cue->c = fgetc(cue->file);\n\n    if (!isdigit(cue->c))\n        return NULL;\n\n    cue_track_t* track = malloc(sizeof(cue_track_t));\n\n    track->end = 0;\n    track->start = 0;\n    track->pregap = 0;\n    track->index[0] = -1;\n    track->index[1] = -1;\n    track->file = list_back(cue->files)->data;\n    track->number = cue_parse_number(cue);\n\n    while (isspace(cue->c))\n        cue->c = fgetc(cue->file);\n\n    track->mode = cue_parse_keyword(cue);\n\n    return track;\n}\n\ncue_file_t* cue_parse_file(struct disc_cue* cue, const char* p, const char* s) {\n    while (isspace(cue->c))\n        cue->c = fgetc(cue->file);\n\n    if (cue->c != '\\\"')\n        return NULL;\n\n    cue_file_t* file = malloc(sizeof(cue_file_t));\n\n    file->tracks = list_create();\n    file->name = malloc(512);\n\n    // Append root path to track file path\n    char* ptr = file->name;\n\n    while (p != s)\n        *ptr++ = *p++;\n\n    cue->c = fgetc(cue->file);\n\n    while (cue->c != '\\\"') {\n        *ptr++ = cue->c;\n\n        cue->c = fgetc(cue->file);\n    }\n\n    *ptr = '\\0';\n\n    cue->c = fgetc(cue->file);\n\n    // Ignore file type\n    while (isspace(cue->c))\n        cue->c = fgetc(cue->file);\n\n    while (isalpha(cue->c))\n        cue->c = fgetc(cue->file);\n\n    return file;\n}\n\nstruct disc_cue* cue_create(void) {\n    return malloc(sizeof(struct disc_cue));\n}\n\nint cue_parse(struct disc_cue* cue, const char* path) {\n    cue->file = fopen(path, \"rb\");\n\n    if (!cue->file)\n        return CUE_FILE_NOT_FOUND;\n\n    const char* s = find_last_slash(path);\n\n    cue->c = fgetc(cue->file);\n\n    while (isspace(cue->c))\n        cue->c = fgetc(cue->file);\n\n    while (!feof(cue->file)) {\n        int kw = cue_parse_keyword(cue);\n\n        switch (kw) {\n            case CUE_FILE: {\n                list_push_back(cue->files, cue_parse_file(cue, path, s));\n            } break;\n\n            case CUE_TRACK: {\n                cue_track_t* track = cue_parse_track(cue);\n                cue_file_t* file = list_back(cue->files)->data;\n\n                list_push_back(cue->tracks, track);\n                list_push_back(file->tracks, track);\n            } break;\n\n            case CUE_INDEX: {\n                cue_parse_index(cue);\n            } break;\n\n            case CUE_REM: case CUE_PREGAP: case CUE_FLAGS: case CUE_POSTGAP: {\n                // Ignore everything until a newline (handle CRLF and LF)\n                while ((cue->c != '\\n') && (cue->c != '\\r'))\n                    cue->c = fgetc(cue->file);\n\n                while ((cue->c == '\\n') && (cue->c == '\\r'))\n                    cue->c = fgetc(cue->file);\n            } break;\n\n            default: {\n                printf(\"Unknown keyword: %s (%u)\\n\", cue_keywords[kw], kw);\n\n                return 1;\n            } break;\n        }\n\n        while (isspace(cue->c))\n            cue->c = fgetc(cue->file);\n    }\n\n    return 0;\n}\n\nsize_t get_file_size(FILE* file) {\n    fseek64(file, 0, SEEK_END);\n\n    size_t size = ftell64(file);\n\n    fseek64(file, 0, SEEK_SET);\n\n    return size;\n}\n\n/*\n(0   - 0  )                   = 150\n(0   - 0  ) + 150    + 315000 = 315150\n(0   - 0  ) + 315150 + 375    = 315525\n(150 - 0  ) + 315525 + 390    = 316065\n(195 - 150) + 316065 + 390    = 316500\n(155 - 190) + 316500 + 390    = \n*/\n\nint prev_pregap = 0;\n\nint init_tracks(cue_file_t* file, uint64_t* lba) {\n    node_t* node = list_front(file->tracks);\n\n    // 1 track per file case\n    if (file->tracks->size == 1) {\n        cue_track_t* data = node->data;\n\n        data->pregap = 0;\n\n        if ((data->index[0] != -1) && (data->index[1] != -1))\n            data->pregap = data->index[1];\n\n        data->start = *lba + data->pregap;\n        data->end = data->start + (file->size / 0x930);\n\n        *lba = data->end;\n\n        return 0;\n    }\n\n    // Multiple tracks per file\n    while (node) {\n        cue_track_t* data = node->data;\n\n        // If this is the last track\n        if (!node->next) {\n            data->pregap = 0;\n            data->start = data->index[1] + 150;\n            data->end = file->size / 0x930;\n\n            return 0;\n        }\n\n        cue_track_t* next = node->next->data;\n\n        data->start = data->index[1] + 150;\n        data->end = (next->index[1] + 150) - 1;\n        data->pregap = 0;\n\n        node = node->next;\n    }\n\n    return 0;\n}\n\nint cue_load(struct disc_cue* cue, int mode) {\n    node_t* node = list_front(cue->files);\n\n    // 00:02:00\n    uint64_t lba = 2 * 75;\n\n    while (node) {\n        cue_file_t* data = node->data;\n\n        FILE* file = fopen(data->name, \"rb\");\n\n        if (!file)\n            return CUE_TRACK_FILE_NOT_FOUND;\n\n        data->buf_mode = mode;\n        data->size = get_file_size(file);\n\n        // printf(\"Loaded \\'%s\\': size=%llx, sectors=%llu\\n\",\n        //     data->name,\n        //     data->size,\n        //     data->size / 0x930\n        // );\n\n        if (data->buf_mode == LD_BUFFERED) {\n            data->buf = malloc(data->size);\n\n            fseek64(file, 0, SEEK_SET);\n\n            if (!fread(data->buf, 1, data->size, file))\n                return CUE_TRACK_READ_ERROR;\n\n            fclose(file);\n        } else {\n            data->buf = file;\n        }\n\n        data->start = lba;\n\n        init_tracks(data, &lba);\n\n        node = node->next;\n    }\n\n    return CUE_OK;\n}\n\nvoid cue_destroy(struct disc_cue* cue) {\n    node_t* node = list_front(cue->files);\n\n    while (node) {\n        cue_file_t* file = node->data;\n\n        if (file->buf_mode == LD_BUFFERED) {\n            free(file->buf);\n        } else {\n            fclose((FILE*)file->buf);\n        }\n\n        list_destroy(file->tracks);\n\n        free(file->name);\n        free(file);\n\n        node = node->next;\n    }\n\n    list_destroy(cue->files);\n\n    node = list_front(cue->tracks);\n\n    while (node) {\n        free(node->data);\n\n        node = node->next;\n    }\n\n    list_destroy(cue->tracks);\n\n    free(cue);\n}\n\ncue_track_t* get_sector_track(struct disc_cue* cue, uint64_t lba) {\n    node_t* node = list_front(cue->tracks);\n\n    while (node) {\n        cue_track_t* track = node->data;\n\n        if ((lba >= track->start) && (lba < track->end))\n            return track;\n\n        node = node->next;\n    }\n\n    return NULL;\n}\n\ncue_track_t* get_sector_track_in_pregap(struct disc_cue* cue, uint64_t lba) {\n    node_t* node = list_front(cue->tracks);\n\n    while (node) {\n        cue_track_t* track = node->data;\n\n        if (!node->next)\n            return track;\n\n        cue_track_t* next = node->next->data;\n\n        // Ignore sector number\n        int curr_start = track->start - (track->start % 75);\n        int next_start = next->start - (next->start % 75);\n\n        if ((lba >= curr_start) && (lba < next_start))\n            return track;\n\n        node = node->next;\n    }\n\n    return NULL;\n}\n\nint cue_query(struct disc_cue* cue, uint64_t lba) {\n    if (lba >= ((cue_track_t*)list_back(cue->tracks)->data)->end)\n        return TS_FAR;\n\n    cue_track_t* track = get_sector_track(cue, lba);\n\n    // If the LBA isn't too far but the track wasn't found\n    // then we are being requested a pregap sector. Clear buffer\n    // and initialize sync data (not actually needed)\n    if (!track)\n        return TS_PREGAP;\n\n    return (track->mode != CUE_AUDIO) ? TS_DATA : TS_AUDIO;\n}\n\nint cue_read(struct disc_cue* cue, uint64_t lba, void* buf, int* sector_size) {\n    if (lba >= ((cue_track_t*)list_back(cue->tracks)->data)->end)\n        return TS_FAR;\n\n    cue_track_t* track = get_sector_track(cue, lba);\n\n    *sector_size = 2352;\n\n    switch (track->mode) {\n        case CUE_MODE1_2048: *sector_size = 2048; break;\n        case CUE_MODE2_2336: *sector_size = 2336; break;\n        case CUE_MODE2_2352: *sector_size = 2352; break;\n    }\n\n    // If the LBA isn't too far but the track wasn't found\n    // then we are being requested a pregap sector. Clear buffer\n    // and initialize sync data (not actually needed)\n    if (!track) {\n        memset((uint8_t*)buf, 0, *sector_size);\n        memset((uint8_t*)buf + 1, 255, 10);\n\n        return TS_PREGAP;\n    }\n\n    cue_file_t* file = track->file;\n\n    // printf(\"Reading sector %u at track %u, file=%s (%u), offset=%u (%08x)\\n\",\n    //     lba,\n    //     track->number,\n    //     track->file->name,\n    //     file->start,\n    //     lba - file->start,\n    //     (lba - file->start) * 2352\n    // );\n\n    if (file->buf_mode == LD_BUFFERED) {\n        uint8_t* ptr = (uint8_t*)file->buf + ((lba - file->start) * (*sector_size));\n\n        memcpy(buf, ptr, *sector_size);\n    } else {\n        fseek64(file->buf, (lba - file->start) * (*sector_size), SEEK_SET);\n\n        // Should always succeed, ignore result for speed\n        (void)fread(buf, 1, *sector_size, file->buf);\n    }\n\n    return (track->mode != CUE_AUDIO) ? TS_DATA : TS_AUDIO;\n}\n\nint cue_get_track_number_impl(struct disc_cue* cue, uint64_t lba) {\n    cue_track_t* track = get_sector_track_in_pregap(cue, lba);\n\n    return track->number;\n}\n\nint cue_get_track_count_impl(struct disc_cue* cue) {\n    return cue->tracks->size;\n}\n\nint cue_get_track_lba(struct disc_cue* cue, int track) {\n    if (!track)\n        return ((cue_track_t*)list_back(cue->tracks)->data)->end;\n\n    if (track > cue->tracks->size)\n        return TS_FAR;\n\n    cue_track_t* data = list_at(cue->tracks, track - 1)->data;\n\n    return data->start;\n}\n\nint cue_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size) {\n    struct disc_cue* cue = (struct disc_cue*)udata;\n\n    // Adjust for lead-in\n    lba += 150;\n\n    char temp[2352];\n\n    int sector_size;\n\n    if (cue_read(cue, lba, temp, &sector_size) == TS_FAR) {\n        if (size == DISC_SS_DATA) {\n            memset(buf, 0, 2048);\n        } else {\n            memset(buf, 0, 2352);\n        }\n\n        return 0;\n    }\n\n    if (size == DISC_SS_DATA) {\n        if (sector_size == 2048) {\n            memcpy(buf, temp, 2048);\n        } else {\n            memcpy(buf, temp + 0x18, 2048);\n        }\n    } else {\n        memcpy(buf, temp, sector_size);\n    }\n\n    return 1;\n}\n\nuint64_t cue_get_size(void* udata) {\n    struct disc_cue* cue = (struct disc_cue*)udata;\n\n    unsigned int size = 0;\n\n    for (int i = 0; i < cue->files->size; i++) {\n        cue_file_t* file = list_at(cue->files, i)->data;\n\n        size += file->size;\n    }\n\n    return size;\n}\n\nint cue_get_sector_size(void* udata) {\n    return 2352;\n}\n\nint cue_init(struct disc_cue* cue, const char* path) {\n    cue->files = list_create();\n    cue->tracks = list_create();\n\n    if (cue_parse(cue, path) != CUE_OK) {\n        printf(\"cue: Failed to parse CUE file '%s'\\n\", path);\n\n        return 0;\n    }\n\n    if (cue_load(cue, LD_FILE) != CUE_OK) {\n        printf(\"cue: Failed to load CUE file '%s'\\n\", path);\n\n        return 0;\n    }\n\n    return 1;\n}\n\nint cue_get_track_count(void* udata) {\n    struct disc_cue* cue = (struct disc_cue*)udata;\n\n    return cue_get_track_count_impl(cue);\n}\n\nint cue_get_track_info(void* udata, int track, struct track_info* info) {\n    struct disc_cue* cue = (struct disc_cue*)udata;\n\n    if (track > cue->tracks->size)\n        return 0;\n\n    cue_track_t* data = list_at(cue->tracks, track - 1)->data;\n\n    info->number = data->number;\n    info->type = (data->mode != CUE_AUDIO) ? TS_DATA : TS_AUDIO;\n    info->lba = data->start;\n\n    return 1;\n}\n\nint cue_get_track_number(void* udata, uint64_t lba) {\n    struct disc_cue* cue = (struct disc_cue*)udata;\n\n    return cue_get_track_number_impl(cue, lba + 150);\n}"
  },
  {
    "path": "src/iop/disc/cue.h",
    "content": "#ifndef CUE_H\n#define CUE_H\n\n#include \"list.h\"\n#include \"../disc.h\"\n\n#include <stdint.h>\n#include <stddef.h>\n#include <stdio.h>\n\nenum {\n    TS_FAR = 0,\n    TS_DATA,\n    TS_AUDIO,\n    TS_PREGAP\n};\n\nenum {\n    CUE_OK = 0,\n    CUE_FILE_NOT_FOUND,\n    CUE_TRACK_FILE_NOT_FOUND,\n    CUE_TRACK_READ_ERROR\n};\n\nenum {\n    CUE_4CH = 0,\n    CUE_AIFF,\n    CUE_AUDIO,\n    CUE_BINARY,\n    CUE_CATALOG,\n    CUE_CDG,\n    CUE_CDI_2336,\n    CUE_CDI_2352,\n    CUE_CDTEXTFILE,\n    CUE_DCP,\n    CUE_FILE,\n    CUE_FLAGS,\n    CUE_INDEX,\n    CUE_ISRC,\n    CUE_MODE1_2048,\n    CUE_MODE1_2352,\n    CUE_MODE2_2336,\n    CUE_MODE2_2352,\n    CUE_MOTOROLA,\n    CUE_MP3,\n    CUE_PERFORMER,\n    CUE_POSTGAP,\n    CUE_PRE,\n    CUE_PREGAP,\n    CUE_REM,\n    CUE_SCMS,\n    CUE_SONGWRITER,\n    CUE_TITLE,\n    CUE_TRACK,\n    CUE_WAVE,\n    CUE_NONE = 255\n};\n\nenum {\n    LD_BUFFERED,\n    LD_FILE\n};\n\ntypedef struct {\n    char* name;\n    int buf_mode;\n    void* buf;\n    size_t size;\n    uint64_t start;\n    list_t* tracks;\n} cue_file_t;\n\ntypedef struct {\n    int number;\n    int mode;\n\n    int32_t index[2];\n    uint64_t pregap;\n    uint64_t start;\n    uint64_t end;\n\n    cue_file_t* file;\n} cue_track_t;\n\nstruct disc_cue {\n    list_t* files;\n    list_t* tracks;\n\n    char c;\n    FILE* file;\n};\n\nstruct disc_cue* cue_create(void);\nint cue_init(struct disc_cue* cue, const char* path);\nvoid cue_destroy(struct disc_cue* cue);\n\n// Disc interface\nint cue_read(struct disc_cue* cue, uint64_t lba, void* buf, int* sector_size);\nint cue_query(struct disc_cue* cue, uint64_t lba);\nint cue_get_track_number_impl(struct disc_cue* cue, uint64_t lba);\nint cue_get_track_count_impl(struct disc_cue* cue);\nint cue_get_track_lba(struct disc_cue* cue, int track);\n\nint cue_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size);\nuint64_t cue_get_size(void* udata);\nint cue_get_sector_size(void* udata);\nint cue_get_track_count(void* udata);\nint cue_get_track_info(void* udata, int track, struct track_info* info);\nint cue_get_track_number(void* udata, uint64_t lba);\n\n#endif"
  },
  {
    "path": "src/iop/disc/iso.c",
    "content": "#include <stdlib.h>\n\n#include \"iso.h\"\n\nstruct disc_iso* iso_create(void) {\n    return malloc(sizeof(struct disc_iso));\n}\n\nint iso_init(struct disc_iso* iso, const char* path) {\n    iso->file = fopen(path, \"rb\");\n\n    if (!iso->file) {\n        free(iso);\n\n        return 0;\n    }\n\n    return 1;\n}\n\nvoid iso_destroy(struct disc_iso* iso) {\n    fclose(iso->file);\n    free(iso);\n}\n\n// Disc IF\nint iso_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size) {\n    struct disc_iso* iso = (struct disc_iso*)udata;\n\n    int s, r;\n\n    s = fseek64(iso->file, lba * 0x800, SEEK_SET);\n    r = fread(buf, 1, 0x800, iso->file);\n\n    return r && !s;\n}\n\nuint64_t iso_get_size(void* udata) {\n    struct disc_iso* iso = (struct disc_iso*)udata;\n\n    fseek64(iso->file, 0, SEEK_END);\n\n    return ftell64(iso->file);\n}\n\nint iso_get_sector_size(void* udata) {\n    return 2048;\n}\n\nint iso_get_track_count(void* udata) {\n    return 1;\n}\n\nint iso_get_track_info(void* udata, int track, struct track_info* info) {\n    return 0;\n}\n\nint iso_get_track_number(void* udata, uint64_t lba) {\n    return 1;\n}\n\n#undef fseek64\n#undef ftell64"
  },
  {
    "path": "src/iop/disc/iso.h",
    "content": "#ifndef ISO_H\n#define ISO_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"../disc.h\"\n\n#include <stdio.h>\n#include <stdint.h>\n\nstruct disc_iso {\n    FILE* file;\n};\n\nstruct disc_iso* iso_create(void);\nint iso_init(struct disc_iso* iso, const char* path);\nvoid iso_destroy(struct disc_iso* iso);\n\n// Disc IF\nint iso_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size);\nuint64_t iso_get_size(void* udata);\nint iso_get_sector_size(void* udata);\nint iso_get_track_count(void* udata);\nint iso_get_track_info(void* udata, int track, struct track_info* info);\nint iso_get_track_number(void* udata, uint64_t lba);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/disc.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <ctype.h>\n\n#include \"disc.h\"\n#include \"disc/iso.h\"\n#include \"disc/cue.h\"\n#include \"disc/chd.h\"\n#include \"disc/ciso.h\"\n#include \"disc/bin.h\"\n\n#ifdef _MSC_VER\n__pragma(pack(push, 1))\n\n#define PACKED\n#else\n#define PACKED __attribute__((packed))\n#endif\n\nstruct PACKED iso9660_pvd {\n    char id[8];\n    char system_id[32];\n    char volume_id[32];\n    char zero[8];\n    uint32_t total_sector_le, total_sect_be;\n    char zero2[32];\n    uint16_t volume_set_size_le, volume_set_size_be;\n    uint16_t volume_seq_nr_le, volume_seq_nr_be;\n    uint16_t sector_size_le, sector_size_be;\n    uint32_t path_table_len_le, path_table_len_be;\n    uint32_t path_table_le, path_table_2nd_le;\n    uint32_t path_table_be, path_table_2nd_be;\n    uint8_t root[34];\n    char volume_set_id[128], publisher_id[128], data_preparer_id[128], application_id[128];\n    char copyright_file_id[37], abstract_file_id[37], bibliographical_file_id[37];\n};\n\nstruct PACKED iso9660_dirent {\n    uint8_t dr_len;\n    uint8_t ext_dr_len;\n    uint32_t lba_le, lba_be;\n    uint32_t size_le, size_be;\n    uint8_t date[7];\n    uint8_t flags;\n    uint8_t file_unit_size;\n    uint8_t interleave_gap_size;\n    uint16_t volume_seq_nr_le, volume_seq_nr_be;\n    uint8_t id_len;\n    uint8_t id;\n};\n\n#ifdef _MSC_VER\n__pragma(pack(pop))\n#endif\n\nstatic const char* disc_extensions[] = {\n    \"iso\",\n    \"bin\",\n    \"cue\",\n    \"chd\",\n    \"cso\",\n    \"zso\",\n    NULL\n};\n\nstatic inline int disc_fetch_pvd(struct disc_state* disc) {\n    if (disc->pvd_cached)\n        return 1;\n\n    if (!disc_read_sector(disc, disc->pvd, 16, DISC_SS_DATA))\n        return 0;\n\n    disc->pvd_cached = 1;\n\n    return 1;\n}\n\nstatic inline int disc_fetch_root(struct disc_state* disc) {\n    if (disc->root_cached)\n        return 1;\n\n    if (!disc_fetch_pvd(disc))\n        return 0;\n\n    struct iso9660_pvd pvd = *(struct iso9660_pvd*)disc->pvd;\n    struct iso9660_dirent* root = (struct iso9660_dirent*)pvd.root;\n\n    // Root points to unreadable sector\n    if (!disc_read_sector(disc, disc->root, root->lba_le, DISC_SS_DATA))\n        return 0;\n\n    disc->root_cached = 1;\n\n    return 1;\n}\n\nstatic inline int disc_fetch_system_cnf(struct disc_state* disc) {\n    if (disc->system_cnf_cached)\n        return 1;\n\n    if (!disc_fetch_root(disc))\n        return 0;\n\n    struct iso9660_dirent* dir = (struct iso9660_dirent*)disc->root;\n\n    while (dir->dr_len) {\n        if (dir->id_len == 12) {\n            if (!strcmp((char*)&dir->id, \"SYSTEM.CNF;1\")) {\n                break;\n            }\n        }\n\n        uint8_t* ptr = (uint8_t*)dir;\n\n        dir = (struct iso9660_dirent*)(ptr + dir->dr_len);\n    }\n\n    // Couldn't find SYSTEM.CNF file, non-playstation disc\n    if (!dir->dr_len)\n        return 0;\n\n    // SYSTEM.CNF points to unreadable sector, invalid\n    if (!disc_read_sector(disc, disc->system_cnf, dir->lba_le, DISC_SS_DATA))\n        return 0;\n\n    disc->system_cnf_cached = 1;\n\n    return 1;\n}\n\nint disc_get_extension(const char* path) {\n    if (!path)\n        return DISC_EXT_UNSUPPORTED;\n\n    const char* ptr = strrchr(path, '.');\n\n    if (!ptr) {\n        return DISC_EXT_NONE;\n    }\n\n    char* lc = strdup(ptr + 1);\n\n    for (char* p = lc; *p; p++) {\n        *p = tolower(*p);\n    }\n\n    for (int i = 0; disc_extensions[i]; i++) {\n        if (!strcmp(lc, disc_extensions[i])) {\n            free(lc);\n\n            return i;\n        }\n    }\n\n    free(lc);\n\n    return DISC_EXT_UNSUPPORTED;\n}\n\nstruct disc_state* disc_open(const char* path) {\n    int ext = disc_get_extension(path);\n\n    if (ext == DISC_EXT_UNSUPPORTED)\n        return NULL;\n\n    struct disc_state* s = malloc(sizeof(struct disc_state));\n\n    memset(s, 0, sizeof(struct disc_state));\n\n    s->layer2_lba = 0;\n    s->ext = ext;\n\n    int r;\n\n    switch (ext) {\n        // Standard raw 2048-byte sector ISO 9660 image\n        // usually used for DVDs\n        case DISC_EXT_ISO: {\n            struct disc_iso* iso = iso_create();\n\n            s->udata = iso;\n            s->read_sector = iso_read_sector;\n            s->get_size = iso_get_size;\n            s->get_sector_size = iso_get_sector_size;\n            s->get_track_count = iso_get_track_count;\n            s->get_track_info = iso_get_track_info;\n            s->get_track_number = iso_get_track_number;\n\n            // To-do: Check if path exists\n            r = iso_init(iso, path);\n        } break;\n\n        // Raw 2352-byte sector disc image (CD)\n        case DISC_EXT_NONE:\n        case DISC_EXT_BIN: {\n            struct disc_bin* bin = bin_create();\n\n            s->udata = bin;\n            s->read_sector = bin_read_sector;\n            s->get_size = bin_get_size;\n            s->get_sector_size = bin_get_sector_size;\n            s->get_track_count = bin_get_track_count;\n            s->get_track_info = bin_get_track_info;\n            s->get_track_number = bin_get_track_number;\n\n            r = bin_init(bin, path);\n        } break;\n\n        // CUE+BIN disc image (contains track information)\n        case DISC_EXT_CUE: {\n            struct disc_cue* cue = cue_create();\n\n            s->udata = cue;\n            s->read_sector = cue_read_sector;\n            s->get_size = cue_get_size;\n            s->get_sector_size = cue_get_sector_size;\n            s->get_track_count = cue_get_track_count;\n            s->get_track_info = cue_get_track_info;\n            s->get_track_number = cue_get_track_number;\n\n            r = cue_init(cue, path);\n        } break;\n\n        // MAME CHD disc image (Compressed Hunks of Data)\n        case DISC_EXT_CHD: {\n            struct disc_chd* chd = chd_create();\n\n            s->udata = chd;\n            s->read_sector = chd_read_sector;\n            s->get_size = chd_get_size;\n            s->get_sector_size = chd_get_sector_size;\n            s->get_track_count = chd_get_track_count;\n            s->get_track_info = chd_get_track_info;\n            s->get_track_number = chd_get_track_number;\n\n            r = chd_init(chd, path);\n        } break;\n\n        case DISC_EXT_CSO:\n        case DISC_EXT_ZSO: {\n            struct disc_ciso* ciso = ciso_create();\n\n            s->udata = ciso;\n            s->read_sector = ciso_read_sector;\n            s->get_size = ciso_get_size;\n            s->get_sector_size = ciso_get_sector_size;\n            s->get_track_count = ciso_get_track_count;\n            s->get_track_info = ciso_get_track_info;\n            s->get_track_number = ciso_get_track_number;\n\n            r = ciso_init(ciso, path);\n        } break;\n\n        default: {\n            free(s);\n\n            return NULL;\n        } break;\n    }\n\n    if (!r) {\n        free(s);\n\n        return NULL;\n    }\n\n    return s;\n}\n\n#define CD_EXTRA_SIZE 800000000\n#define CDX_MAX_SIZE 734003200\n#define CD_MAX_SIZE 681574400\n\nint disc_detect_media(struct disc_state* disc) {\n    uint64_t size = disc_get_size(disc);\n\n    disc_fetch_pvd(disc);\n\n    uint64_t sector_size = disc_get_sector_size(disc);\n    uint64_t volume_size = *(uint32_t*)&disc->pvd[0x50];\n    uint64_t path_table_lba = *(uint32_t*)&disc->pvd[0x8c];\n\n    printf(\"sector_size=%lx volume_size=%lx (%ld) in bytes=%lx (%ld) path_table_lba=%lx (%ld) size=%lx (%ld)\\n\",\n        sector_size,\n        volume_size, volume_size,\n        volume_size * sector_size, volume_size * sector_size,\n        path_table_lba, path_table_lba,\n        size, size\n    );\n\n    // DVD is dual-layer\n    if ((volume_size * sector_size) < size) {\n        disc->layer2_lba = volume_size;\n    \n        return DISC_MEDIA_DVD;\n    }\n\n    if (((volume_size * sector_size) <= CD_EXTRA_SIZE) && (path_table_lba != 257)) {\n        return DISC_MEDIA_CD;\n    }\n\n    return DISC_MEDIA_DVD;\n}\n\nstatic inline int disc_detect_type(struct disc_state* disc) {\n    if (!disc_fetch_pvd(disc))\n        return DISC_TYPE_INVALID;\n\n    struct iso9660_pvd pvd = *(struct iso9660_pvd*)disc->pvd;\n\n    // Not ISO 9660 format disc\n    if (strncmp(pvd.id, \"\\1CD001\\1\", 8)) {\n        // If the disc doesn't contain an ISO filesystem\n        // and it's a CUE image, it's probably a CD audio image\n        if (disc->ext == DISC_EXT_CUE) {\n            return DISC_TYPE_CDDA;\n        }\n\n        // Otherwise it's invalid\n        return DISC_TYPE_INVALID;\n    }\n\n    // Check for the \"PLAYSTATION\" string at PVD offset 08h\n    // Patch 20 byte so comparison is done correctly\n    disc->pvd[0x13] = 0;\n\n    // Disc contains a \"valid\" ISO filesystem, but it's not a\n    // PlayStation disc, it might be a DVD video disc or something\n    // else entirely, either way don't outright reject it just yet\n    if (strncmp(&disc->pvd[0x8], \"PLAYSTATION\", 12))\n        return DISC_TYPE_UNKNOWN;\n\n    // Disc contains a valid ISO filesystem and the PlayStation string\n    // is present, so this is most likely a game disc.\n    return DISC_TYPE_GAME;\n}\n\nint disc_get_type(struct disc_state* disc) {\n    int media = disc_detect_media(disc);\n    int type = disc_detect_type(disc);\n\n    if (type == DISC_TYPE_INVALID) {\n        return CDVD_DISC_INVALID;\n    }\n\n    if (type == DISC_TYPE_CDDA) {\n        return CDVD_DISC_CDDA;\n    }\n\n    // Start final detection\n    char buf[2048];\n\n    if (!disc_read_sector(disc, buf, 16, DISC_SS_DATA)) {\n        return CDVD_DISC_INVALID;\n    }\n\n    struct iso9660_pvd pvd = *(struct iso9660_pvd*)buf;\n    struct iso9660_dirent* root = (struct iso9660_dirent*)pvd.root;\n\n    // Root points to unreadable sector\n    if (!disc_read_sector(disc, buf, root->lba_le, DISC_SS_DATA)) {\n        return CDVD_DISC_INVALID;\n    }\n\n    struct iso9660_dirent* dir = (struct iso9660_dirent*)buf;\n\n    while (dir->dr_len) {\n        if (dir->id_len == 12) {\n            if (!strcmp((char*)&dir->id, \"SYSTEM.CNF;1\")) {\n                break;\n            }\n        }\n\n        // printf(\"dir=\\'%s\\'\\n\", &dir->id);\n\n        uint8_t* ptr = (uint8_t*)dir;\n\n        dir = (struct iso9660_dirent*)(ptr + dir->dr_len);\n    }\n\n    // Couldn't find SYSTEM.CNF file, non-playstation disc\n    if (!dir->dr_len) {\n        // Might be a DVD Video disc\n        // Look for VIDEO_TS in the root dir\n        dir = (struct iso9660_dirent*)buf;\n\n        while (dir->dr_len) {\n            // Search directories\n            if ((dir->flags & 2) == 0) goto next;\n            if (dir->id_len != 8) goto next;\n\n            if (!strncmp((char*)&dir->id, \"VIDEO_TS\", dir->id_len)) {\n                return CDVD_DISC_DVD_VIDEO;\n            }\n\n            // printf(\"dir=\\'%s\\' (%d)\\n\", (char*)&dir->id, dir->id_len);\n\n            next:;\n\n            uint8_t* ptr = (uint8_t*)dir;\n\n            dir = (struct iso9660_dirent*)(ptr + dir->dr_len);\n        }\n\n        // SYSTEM.CNF not found and VIDEO_TS not found\n        // If the PLAYSTATION string is in the PVD, then this might actually\n        // be part of a multi-disc set, return as a PlayStation disc\n        // Otherwise it's probably something else entirely (like an Xbox disc)\n        // The PS2 wouldn't handle it anyways, return as invalid.\n\n        // The Linux for PlayStation 2 install disc is an example of this.\n        // Disc 2 contains a valid ISO filesystem and the PLAYSTATION string,\n        // but no SYSTEM.CNF file.\n        if (media == DISC_MEDIA_DVD) {\n            return type == DISC_TYPE_GAME ? CDVD_DISC_PS2_DVD : CDVD_DISC_INVALID;\n        }\n\n        return type == DISC_TYPE_GAME ? CDVD_DISC_PS2_CD : CDVD_DISC_INVALID;\n    }\n\n    // SYSTEM.CNF points to unreadable sector, invalid\n    if (!disc_read_sector(disc, buf, dir->lba_le, DISC_SS_DATA)) {\n        return CDVD_DISC_INVALID;\n    }\n\n    // Parse SYSTEM.CNF\n    char* p = buf;\n    char key[64];\n    \n    while (*p) {\n        char* kptr = key;\n\n        while (isspace(*p))\n            ++p;\n\n        while (isalnum(*p))\n            *kptr++ = *p++;\n\n        *kptr = '\\0';\n\n        // printf(\"key: %s\\n\", key);\n\n        // BOOT entry found, PlayStation CD\n        if (!strncmp(key, \"BOOT\", 64))\n            return CDVD_DISC_PSX_CD;\n\n        // BOOT2 entry found, PlayStation 2 disc\n        if (!strncmp(key, \"BOOT2\", 64)) {\n            return media == DISC_MEDIA_CD ? CDVD_DISC_PS2_CD : CDVD_DISC_PS2_DVD;\n        }\n    }\n\n    // Couldn't find BOOT or BOOT2 entry, invalid/bootleg PlayStation disc?\n    return DISC_TYPE_INVALID;\n}\n\nint disc_read_sector(struct disc_state* disc, unsigned char* buf, uint64_t lba, int size) {\n    if (!disc)\n        return 0;\n\n    if (!disc->read_sector)\n        return 0;\n\n    return disc->read_sector(disc->udata, buf, lba, size);\n}\n\nuint64_t disc_get_size(struct disc_state* disc) {\n    if (!disc)\n        return 0;\n\n    if (!disc->read_sector)\n        return 0;\n\n    return disc->get_size(disc->udata);\n}\n\nuint64_t disc_get_volume_lba(struct disc_state* disc, int vol) {\n    if (!disc)\n        return 0;\n\n    if (!disc->read_sector)\n        return 0;\n\n    if (!vol)\n        return 0;\n\n    if (!disc->layer2_lba) {\n        disc_detect_media(disc);\n    }\n\n    return disc->layer2_lba;\n}\n\nint disc_get_sector_size(struct disc_state* disc) {\n    if (!disc)\n        return 0;\n\n    if (!disc->get_sector_size)\n        return 0;\n\n    return disc->get_sector_size(disc->udata);\n}\n\nint disc_get_track_count(struct disc_state* disc) {\n    if (!disc)\n        return 0;\n\n    if (!disc->get_track_count)\n        return 0;\n\n    return disc->get_track_count(disc->udata);\n}\n\nint disc_get_track_info(struct disc_state* disc, int track, struct track_info* info) {\n    if (!disc)\n        return 0;\n\n    if (!disc->get_track_info)\n        return 0;\n\n    return disc->get_track_info(disc->udata, track, info);\n}\n\nint disc_get_track_number(struct disc_state* disc, uint64_t lba) {\n    if (!disc)\n        return 0;\n\n    if (!disc->get_track_number)\n        return 0;\n\n    return disc->get_track_number(disc->udata, lba);\n}\n\nvoid disc_close(struct disc_state* disc) {\n    switch (disc->ext) {\n        // Standard raw 2048-byte sector ISO 9660 image\n        // usually used for DVDs\n        case DISC_EXT_ISO: {\n            iso_destroy(disc->udata);\n        } break;\n\n        case DISC_EXT_CUE: {\n            cue_destroy(disc->udata);\n        } break;\n\n        // Raw 2352-byte sector disc image (CD)\n        case DISC_EXT_BIN: {\n            bin_destroy(disc->udata);\n        } break;\n    }\n\n    free(disc);\n}\n\nchar* disc_get_serial(struct disc_state* disc, char* serial) {\n    if (!disc)\n        return NULL;\n\n    // No game serial\n    if (!disc_fetch_system_cnf(disc))\n        return NULL;\n\n    // Parse SYSTEM.CNF\n    char* p = disc->system_cnf;\n    char key[64];\n    \n    while (*p) {\n        char* kptr = key;\n\n        while (isspace(*p))\n            ++p;\n\n        while (isalnum(*p))\n            *kptr++ = *p++;\n\n        *kptr = '\\0';\n\n        if (!strncmp(key, \"BOOT2\", 64)) {\n            while (isspace(*p)) ++p;\n\n            if (*p != '=') {\n                printf(\"iso: Expected =\\n\");\n\n                return NULL;\n            }\n\n            ++p;\n\n            while (isspace(*p)) ++p;\n\n            while (*p != ':') ++p;\n\n            ++p;\n\n            if (*p == '\\\\' || *p == '/')\n                ++p;\n\n            int i;\n\n            for (i = 0; i < 16; i++) {\n                if (*p == ';' || *p == '\\n' || *p == '\\r')\n                    break;\n\n                serial[i] = *p++;\n            }\n\n            serial[i] = '\\0';\n\n            return serial;\n        } else {\n            while ((*p != '\\n') && (*p != '\\0') && (*p != '\\r')) ++p;\n            while ((*p == '\\n') || (*p == '\\r')) ++p;\n        }\n    }\n\n    printf(\"iso: Couldn't find BOOT2 entry in SYSTEM.CNF (PlayStation disc?)\\n\");\n\n    return NULL;\n}\n\nchar* disc_get_boot_path(struct disc_state* disc) {\n    if (disc->boot_path_cached)\n        return disc->boot_path;\n\n    // No boot path\n    if (!disc_fetch_system_cnf(disc))\n        return NULL;\n\n    // Parse SYSTEM.CNF\n    char* p = disc->system_cnf;\n    char key[64];\n    \n    while (*p) {\n        char* kptr = key;\n\n        while (isspace(*p))\n            ++p;\n\n        while (isalnum(*p))\n            *kptr++ = *p++;\n\n        *kptr = '\\0';\n\n        // printf(\"key: %s\\n\", key);\n\n        if (!strncmp(key, \"BOOT2\", 64)) {\n            while (isspace(*p)) ++p;\n\n            if (*p != '=') {\n                printf(\"iso: Expected =\\n\");\n\n                return NULL;\n            }\n\n            ++p;\n\n            while (isspace(*p)) ++p;\n\n            int i;\n\n            for (i = 0; i < 255; i++) {\n                if (*p == '\\n' || *p == '\\r')\n                    break;\n\n                disc->boot_path[i] = *p++;\n            }\n\n            disc->boot_path[i] = '\\0';\n\n            return disc->boot_path;\n        } else {\n            while ((*p != '\\n') && (*p != '\\0') && (*p != '\\r')) ++p;\n            while ((*p == '\\n') || (*p == '\\r')) ++p;\n        }\n    }\n\n    printf(\"iso: Couldn't find BOOT2 entry in SYSTEM.CNF (PlayStation disc?)\\n\");\n\n    return NULL;\n}\n\nchar* disc_read_boot_elf(struct disc_state* disc, int s) {\n    char* boot_path = disc_get_boot_path(disc);\n\n    printf(\"iso: Reading boot ELF from disc at path \\'%s\\'...\\n\", boot_path);\n\n    if (!boot_path) {\n        printf(\"iso: No boot path found in SYSTEM.CNF\\n\");\n\n        return NULL;\n    }\n\n    if (!disc_fetch_root(disc)) {\n        printf(\"iso: Couldn't fetch root directory\\n\");\n\n        return NULL;\n    }\n\n    char path[256];\n\n    path[0] = '\\0';\n\n    char* ptr = boot_path;\n\n    // Go to end of boot path\n    while (*ptr++);\n\n    // Reverse search for a path separator\n    while (*ptr != '/' && *ptr != '\\\\' && *ptr != ':') {\n        if (ptr == boot_path)\n            break;\n\n        --ptr;\n    }\n\n    // Skip the path separator\n    ptr += 1;\n\n    // Copy the path to our buffer\n    int i;\n\n    for (i = 0; *ptr; i++) {\n        path[i] = *ptr++;\n    }\n\n    path[i] = '\\0';\n\n    struct iso9660_dirent* dir = (struct iso9660_dirent*)disc->root;\n\n    while (dir->dr_len) {\n        if (!strncmp((char*)&dir->id, path, dir->id_len)) {\n            int32_t size = dir->size_le;\n            uint32_t lba = dir->lba_le;\n\n            char* buf = malloc(((size >> 11) + 2) << 11);\n            char* ptr = buf;\n\n            printf(\"iso: Boot ELF found at lba=%08x size=%08x\\n\", lba, dir->size_le, dir->size_le);\n\n            while (size > 0) {\n                if (!disc_read_sector(disc, ptr, lba++, DISC_SS_DATA)) {\n                    printf(\"iso: Couldn't read boot ELF sector %d\\n\", lba - 1);\n\n                    return NULL;\n                }\n\n                ptr += 2048;\n                size -= 2048;\n            }\n\n            if (size != 0) {\n                // Read the last sector, which might be smaller than 2048 bytes\n                if (!disc_read_sector(disc, ptr, lba, DISC_SS_DATA)) {\n                    printf(\"iso: Couldn't read boot ELF sector %d\\n\", lba);\n\n                    return NULL;\n                }\n            }\n\n            return buf;\n        }\n\n        uint8_t* ptr = (uint8_t*)dir;\n\n        dir = (struct iso9660_dirent*)(ptr + dir->dr_len);\n    }\n\n    // Couldn't find the boot ELF file in the root directory\n    return NULL;\n}\n\n#undef PACKED"
  },
  {
    "path": "src/iop/disc.h",
    "content": "#ifndef DISC_H\n#define DISC_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#ifdef _MSC_VER\n#define fseek64 _fseeki64\n#define ftell64 _ftelli64\n#elif defined(_WIN32)\n#define fseek64 fseeko64\n#define ftell64 ftello64\n#else\n#define fseek64 fseek\n#define ftell64 ftell\n#endif\n\n#define DISC_ERR_CANT_OPEN 0\n#define DISC_ERR_ISO_INVALID 1\n#define DISC_ERR_UNSUPPORTED_SS 2\n\n#define DISC_EXT_ISO 0\n#define DISC_EXT_BIN 1\n#define DISC_EXT_CUE 2\n#define DISC_EXT_CHD 3\n#define DISC_EXT_CSO 4\n#define DISC_EXT_ZSO 5\n#define DISC_EXT_NONE 6\n#define DISC_EXT_UNSUPPORTED 7\n\n#define DISC_MEDIA_CD 0\n#define DISC_MEDIA_DVD 1\n\n#define DISC_TYPE_INVALID 0\n#define DISC_TYPE_CDDA 1\n#define DISC_TYPE_GAME 2\n#define DISC_TYPE_UNKNOWN 3\n\n/*\n    00h  No disc\n    01h  Detecting\n    02h  Detecting CD\n    03h  Detecting DVD\n    04h  Detecting dual-layer DVD\n    05h  Unknown\n    10h  PSX CD\n    11h  PSX CDDA\n    12h  PS2 CD\n    13h  PS2 CDDA\n    14h  PS2 DVD\n    FDh  CDDA (Music)\n    FEh  DVDV (Movie disc)\n    FFh  Illegal\n*/\n#define CDVD_DISC_NO_DISC          0\n#define CDVD_DISC_DETECTING        1\n#define CDVD_DISC_DETECTING_CD     2\n#define CDVD_DISC_DETECTING_DVD    3\n#define CDVD_DISC_DETECTING_DL_DVD 4\n#define CDVD_DISC_PSX_CD           16\n#define CDVD_DISC_PSX_CDDA         17\n#define CDVD_DISC_PS2_CD           18\n#define CDVD_DISC_PS2_CDDA         19\n#define CDVD_DISC_PS2_DVD          20\n#define CDVD_DISC_CDDA             253\n#define CDVD_DISC_DVD_VIDEO        254\n#define CDVD_DISC_INVALID          255\n\n#define DISC_SS_DATA 0\n#define DISC_SS_RAW 1\n\n#define CDVD_TRACK_AUDIO 0x01\n#define CDVD_TRACK_MODE1 0x41\n#define CDVD_TRACK_MODE2 0x61\n\nstruct track_info {\n    int number;\n    int type;\n    uint32_t lba;\n};\n\nstruct disc_state {\n    int (*read_sector)(void* udata, unsigned char* buf, uint64_t lba, int size);\n    uint64_t (*get_size)(void* udata);\n    int (*get_sector_size)(void* udata);\n    int (*get_track_count)(void* udata);\n    int (*get_track_info)(void* udata, int track, struct track_info* info);\n    int (*get_track_number)(void* udata, uint64_t lba);\n\n    void* udata;\n\n    uint64_t layer2_lba;\n    int ext;\n    int pvd_cached, system_cnf_cached, root_cached, boot_path_cached;\n    char pvd[2048];\n    char root[2048];\n    char system_cnf[2048];\n    char boot_path[256];\n};\n\nstruct disc_state* disc_open(const char* path);\nint disc_read_sector(struct disc_state* disc, unsigned char* buf, uint64_t lba, int size);\nint disc_get_type(struct disc_state* disc);\nuint64_t disc_get_size(struct disc_state* disc);\nuint64_t disc_get_volume_lba(struct disc_state* disc, int vol);\nint disc_get_sector_size(struct disc_state* disc);\nint disc_get_track_count(struct disc_state* disc);\nint disc_get_track_info(struct disc_state* disc, int track, struct track_info* info);\nint disc_get_track_number(struct disc_state* disc, uint64_t lba);\nchar* disc_get_serial(struct disc_state* disc, char* buf);\nchar* disc_get_boot_path(struct disc_state* disc);\nchar* disc_read_boot_elf(struct disc_state* disc, int size);\nvoid disc_close(struct disc_state* disc);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/dma.c",
    "content": "#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <assert.h>\n#include <stdio.h>\n#include <ctype.h>\n#include <signal.h>\n\n#include \"dma.h\"\n\nstatic inline void iop_dma_set_dicr(struct ps2_iop_dma* dma, uint32_t v) {\n    dma->dicr &= ~0xffffff;\n    dma->dicr |= v & 0xffffff;\n    dma->dicr &= ~(v & 0x7f000000);\n}\n\nstatic inline void iop_dma_set_dicr2(struct ps2_iop_dma* dma, uint32_t v) {\n    dma->dicr2 &= ~0xffffff;\n    dma->dicr2 |= v & 0xffffff;\n    dma->dicr2 &= ~(v & 0x3f000000);\n}\n\ninline static void iop_dma_set_dicr_flag(struct ps2_iop_dma* dma, uint32_t ch) {\n    if (ch < 7) {\n        uint32_t m = 0x10000 << ch;\n\n        if (dma->dicr & m)\n            dma->dicr |= 0x1000000 << ch;\n\n        return;\n    }\n\n    uint32_t m = 0x10000 << (ch - 7);\n\n    if (dma->dicr2 & m)\n        dma->dicr2 |= 0x1000000 << (ch - 7);\n}\n\ninline static void iop_dma_check_irq(struct ps2_iop_dma* dma) {\n    int be = dma->dicr & 0x8000;\n    int mcien = dma->dicr & 0x800000;\n    uint32_t dicr_flags = dma->dicr & 0x7f000000;\n    uint32_t dicr2_flags = dma->dicr2 & 0x3f000000;\n    int cinten = !!(dma->dmacinten & 1);\n    int minten = !(dma->dmacinten & 2);\n\n    int mif = be || (mcien && (dicr_flags || dicr2_flags));\n\n    mif = mif && cinten;\n\n    dma->dicr &= 0x7fffffff;\n    dma->dicr |= mif ? 0x80000000 : 0;\n\n    if (mif && minten) {\n        ps2_iop_intc_irq(dma->intc, IOP_INTC_DMA);\n    }\n}\n\nstruct ps2_iop_dma* ps2_iop_dma_create(void) {\n    return malloc(sizeof(struct ps2_iop_dma));\n}\n\nvoid ps2_iop_dma_init(struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct ps2_sif* sif, struct ps2_cdvd* cdvd, struct ps2_dmac* ee_dma, struct ps2_sio2* sio2, struct ps2_spu2* spu, struct sched_state* sched, struct iop_bus* bus) {\n    memset(dma, 0, sizeof(struct ps2_iop_dma));\n\n    dma->intc = intc;\n    dma->bus = bus;\n    dma->sif = sif;\n    dma->drive = cdvd;\n    dma->sched = sched;\n    dma->ee_dma = ee_dma;\n    dma->sio2 = sio2;\n    dma->spu = spu;\n\n    dma->dmacinten = 0x01;\n}\n\nvoid ps2_iop_dma_destroy(struct ps2_iop_dma* dma) {\n    free(dma);\n}\n\nstatic inline struct iop_dma_channel* iop_dma_get_channel(struct ps2_iop_dma* dma, uint32_t addr) {\n    switch (addr & 0xff0) {\n        case 0x080: return &dma->mdec_in;\n        case 0x090: return &dma->mdec_out;\n        case 0x0a0: return &dma->sif2;\n        case 0x0b0: return &dma->cdvd;\n        case 0x0c0: return &dma->spu1;\n        case 0x0d0: return &dma->pio;\n        case 0x0e0: return &dma->otc;\n        case 0x500: return &dma->spu2;\n        case 0x510: return &dma->dev9;\n        case 0x520: return &dma->sif0;\n        case 0x530: return &dma->sif1;\n        case 0x540: return &dma->sio2_in;\n        case 0x550: return &dma->sio2_out;\n    }\n\n    return NULL;\n}\n\nstatic inline const char* iop_dma_get_channel_name(uint32_t addr) {\n    switch (addr & 0xff0) {\n        case 0x080: return \"mdec_in\";\n        case 0x090: return \"mdec_out\";\n        case 0x0a0: return \"sif2\";\n        case 0x0b0: return \"cdvd\";\n        case 0x0c0: return \"spu1\";\n        case 0x0d0: return \"pio\";\n        case 0x0e0: return \"otc\";\n        case 0x500: return \"spu2\";\n        case 0x510: return \"dev9\";\n        case 0x520: return \"sif0\";\n        case 0x530: return \"sif1\";\n        case 0x540: return \"sio2_in\";\n        case 0x550: return \"sio2_out\";\n    }\n\n    return NULL;\n}\n\nstatic inline void dma_fetch_tag(struct ps2_iop_dma* dma, struct iop_dma_channel* c) {\n    c->tag = iop_bus_read32(dma->bus, c->tadr);\n    c->tag |= (uint64_t)iop_bus_read32(dma->bus, c->tadr + 4) << 32;\n\n    c->addr = c->tag & 0x7fffff;\n    c->size = (c->tag >> 32) & 0xffffff;\n    c->irq = !!(c->tag & 0x40000000);\n    c->eot = !!(c->tag & 0x80000000);\n    c->extra = !!(c->chcr & 0x100);\n\n    // Round to 8 words\n    c->size = (c->size + 3) & ~3;\n}\n\nvoid iop_dma_handle_mdec_in_transfer(struct ps2_iop_dma* dma) {\n    fprintf(stderr, \"iop: MDEC in channel unimplemented\\n\"); exit(1);\n}\nvoid iop_dma_handle_mdec_out_transfer(struct ps2_iop_dma* dma) {\n    fprintf(stderr, \"iop: MDEC out channel unimplemented\\n\"); exit(1);\n}\nvoid iop_dma_handle_sif2_transfer(struct ps2_iop_dma* dma) {\n    fprintf(stderr, \"iop: SIF2 channel unimplemented\\n\"); exit(1);\n}\nvoid iop_dma_handle_cdvd_transfer(struct ps2_iop_dma* dma) {\n    // No data in CDVD buffer yet\n    if (!dma->drive->buf_size)\n        return;\n\n    // Channel not yet started\n    if (!(dma->cdvd.chcr & 0x1000000)) {\n        printf(\"iop: CDVD transfer incoming, channel not yet started (%08x)\\n\", dma->cdvd.chcr);\n\n        // exit(1);\n\n        return;\n    }\n\n    // printf(\"iop: Writing %d bytes of sector data to %08x (%08x)\\n\", dma->drive->buf_size, dma->cdvd.madr, dma->cdvd.bcr);\n\n    // uint32_t addr = dma->cdvd.madr;\n\n    int i = 0;\n\n    while (dma->cdvd.transfer_size && dma->drive->buf_size) {\n        iop_bus_write8(dma->bus, dma->cdvd.madr++, dma->drive->buf[i++]);\n\n        dma->drive->buf_size--;\n        dma->cdvd.transfer_size--;\n    }\n\n    // printf(\"dma: buf_size=%d transfer_size=%d\\n\",\n    //     dma->drive->buf_size,\n    //     dma->cdvd.transfer_size\n    // );\n\n    // int size = dma->drive->buf_size;\n\n    // while (size > 0) {\n    //     printf(\"%08x: \", addr);\n\n    //     for (int i = 0; i < 16; i++) {\n    //         printf(\"%02x \", iop_bus_read8(dma->bus, addr + i));\n    //     }\n\n    //     putchar('|');\n\n    //     for (int i = 0; i < 16; i++) {\n    //         uint8_t b = iop_bus_read8(dma->bus, addr + i);\n\n    //         printf(\"%c\", isprint(b) ? b : '.');\n    //     }\n\n    //     puts(\"|\");\n\n    //     addr += 16;\n    //     size -= 16;\n    // }\n\n    // Only end the transfer when there aren't any\n    // blocks left to copy\n    if (dma->cdvd.transfer_size)\n        return;\n\n    // printf(\"dma: Sending IRQ to IOP\\n\");\n    iop_dma_set_dicr_flag(dma, IOP_DMA_CDVD);\n    iop_dma_check_irq(dma);\n\n    // printf(\"cdvd: Ending transfer\\n\");\n    dma->cdvd.chcr &= ~0x1000000;\n    dma->cdvd.bcr = 0;\n}\n\nvoid spu1_dma_irq_event_handler(void* udata, int overshoot) {\n    struct ps2_iop_dma* dma = (struct ps2_iop_dma*)udata;\n\n    iop_dma_set_dicr_flag(dma, IOP_DMA_SPU1);\n    iop_dma_check_irq(dma);\n\n    dma->spu1.chcr &= ~0x1000000;\n}\n\nvoid iop_dma_handle_spu1_transfer(struct ps2_iop_dma* dma) {\n    // printf(\"spu2 core0: chcr=%08x madr=%08x bcr=%08x bytes=%d (%08x) adma=%d\\n\", dma->spu2.chcr, dma->spu2.madr, dma->spu2.bcr,\n    //     (dma->spu2.bcr & 0xffff) * (dma->spu2.bcr >> 16) * 4, (dma->spu2.bcr & 0xffff) * (dma->spu2.bcr >> 16) * 4, dma->spu->c[1].admas\n    // );\n\n    // If ADMA is off, then transfer all the data at once and trigger\n    // an IRQ event to signal the end of the transfer\n    if (!(dma->spu->c[0].admas & 1)) {\n        unsigned int size = (dma->spu1.bcr & 0xffff) * (dma->spu1.bcr >> 16);\n\n        for (int i = 0; i < size; i++) {\n            uint32_t d = iop_bus_read32(dma->bus, dma->spu1.madr);\n\n            iop_bus_write16(dma->bus, 0x1f9001ac, d & 0xffff);\n            iop_bus_write16(dma->bus, 0x1f9001ac, d >> 16);\n\n            dma->spu1.madr += 4;\n        }\n\n        struct sched_event spu1_dma_irq_event;\n\n        spu1_dma_irq_event.callback = spu1_dma_irq_event_handler;\n        spu1_dma_irq_event.cycles = 1000000;\n        spu1_dma_irq_event.name = \"SPU1 DMA IRQ event\";\n        spu1_dma_irq_event.udata = dma;\n\n        sched_schedule(dma->sched, spu1_dma_irq_event);\n\n        return;\n    }\n\n    if ((dma->spu1.chcr & 0x1000000) == 0)\n        return;\n\n    // else we need to do an ADMA transfer\n    spu2_start_adma(dma->spu, 0);\n\n    // Transfer data as long as the SPU2 isn't streaming ADMA\n    // samples and we still have data to transfer\n    while (dma->spu1.transfer_size && !spu2_is_adma_active(dma->spu, 0)) {\n        uint32_t d = iop_bus_read32(dma->bus, dma->spu1.madr);\n\n        iop_bus_write16(dma->bus, 0x1f9001ac, d & 0xffff);\n        iop_bus_write16(dma->bus, 0x1f9001ac, d >> 16);\n\n        dma->spu1.madr += 4;\n        dma->spu1.transfer_size -= 4;\n    }\n\n    // if (!dma->spu1.transfer_size) {\n    //     // If we have no more data to transfer, then we can end the transfer\n    //     // and trigger an IRQ event\n    //     iop_dma_set_dicr_flag(dma, IOP_DMA_SPU1);\n    //     iop_dma_check_irq(dma);\n\n    //     dma->spu1.chcr &= ~0x1000000;\n    // }\n}\nvoid iop_dma_handle_pio_transfer(struct ps2_iop_dma* dma) {\n    fprintf(stderr, \"iop: PIO channel unimplemented\\n\"); exit(1);\n}\nvoid iop_dma_handle_otc_transfer(struct ps2_iop_dma* dma) {\n    fprintf(stderr, \"iop: OTC channel unimplemented\\n\"); exit(1);\n}\n\nvoid spu2_dma_irq_event_handler(void* udata, int overshoot) {\n    struct ps2_iop_dma* dma = (struct ps2_iop_dma*)udata;\n\n    iop_dma_set_dicr_flag(dma, IOP_DMA_SPU2);\n    iop_dma_check_irq(dma);\n\n    dma->spu2.chcr &= ~0x1000000;\n}\n\nvoid iop_dma_handle_spu2_transfer(struct ps2_iop_dma* dma) {\n    if ((dma->spu2.chcr & 0x1000000) == 0)\n        return;\n\n    // printf(\"spu2 core1: chcr=%08x madr=%08x bcr=%08x bytes=%d (%08x) adma=%d\\n\", dma->spu2.chcr, dma->spu2.madr, dma->spu2.bcr,\n    //     (dma->spu2.bcr & 0xffff) * (dma->spu2.bcr >> 16) * 4, (dma->spu2.bcr & 0xffff) * (dma->spu2.bcr >> 16) * 4, dma->spu->c[1].admas\n    // );\n\n    if (!dma->spu2.transfer_size)\n        return;\n\n    // If ADMA is off, then transfer all the data at once and trigger\n    // an IRQ event to signal the end of the transfer\n    if (!(dma->spu->c[1].admas & 2)) {\n        unsigned int size = (dma->spu2.bcr & 0xffff) * (dma->spu2.bcr >> 16);\n\n        for (int i = 0; i < size; i++) {\n            uint32_t d = iop_bus_read32(dma->bus, dma->spu2.madr);\n\n            iop_bus_write16(dma->bus, 0x1f9005ac, d & 0xffff);\n            iop_bus_write16(dma->bus, 0x1f9005ac, d >> 16);\n\n            dma->spu2.madr += 4;\n        }\n\n        struct sched_event spu2_dma_irq_event;\n\n        spu2_dma_irq_event.callback = spu2_dma_irq_event_handler;\n        spu2_dma_irq_event.cycles = 1000000;\n        spu2_dma_irq_event.name = \"SPU2 DMA IRQ event\";\n        spu2_dma_irq_event.udata = dma;\n\n        sched_schedule(dma->sched, spu2_dma_irq_event);\n\n        return;\n    }\n\n    if ((dma->spu2.chcr & 0x01000000) == 0)\n        return;\n\n    // printf(\"spu2 core1: transfer start (%d bytes pending)\\n\", dma->spu2.transfer_size);\n\n    // else we need to do an ADMA transfer\n    spu2_start_adma(dma->spu, 1);\n\n    // Transfer data as long as the SPU2 isn't streaming ADMA\n    // samples and we still have data to transfer\n    while (dma->spu2.transfer_size && !spu2_is_adma_active(dma->spu, 1)) {\n        uint32_t d = iop_bus_read32(dma->bus, dma->spu2.madr);\n\n        // int transfer_size = dma->spu2.transfer_size;\n\n        iop_bus_write16(dma->bus, 0x1f9005ac, d & 0xffff);\n        iop_bus_write16(dma->bus, 0x1f9005ac, d >> 16);\n\n        // if (dma->spu2.transfer_size != transfer_size) {\n        //     printf(\"iop: SPU2 transfer size changed from %d to %d (loop=%d)\\n\", transfer_size, dma->spu2.transfer_size, loop);\n        //     *(int*)0 = 0;\n        //     dma->spu2.transfer_size = transfer_size;\n        // }\n\n        dma->spu2.madr += 4;\n        dma->spu2.transfer_size -= 4;\n    }\n\n    // if (!dma->spu2.transfer_size) {\n    //     // If we have no more data to transfer, then we can end the transfer\n    //     // and trigger an IRQ event\n    //     printf(\"spu2 core1: ending transfer\\n\");\n\n    //     iop_dma_set_dicr_flag(dma, IOP_DMA_SPU2);\n    //     iop_dma_check_irq(dma);\n\n    //     dma->spu2.chcr &= ~0x1000000;\n    // }\n}\nvoid iop_dma_handle_dev9_transfer(struct ps2_iop_dma* dma) {\n    // Note: DEV9 DMA serves different purposes based on the system.\n\n    // On retail hardware, DEV9 DMA is used to transfer data in and out\n    // of the HDD. On the Namco Syste 147/148 arcade hardware, DEV9 DMA\n    // is used to transfer data to and from the Samsung NAND flash\n    // storage chip.\n\n    // We'll have to account for this once we implement HDD support, for\n    // now we're defaulting to the System 147/148 behavior, since it\n    // won't be used by any retail games anyways unless the HDD is present.\n\n    while (dma->dev9.transfer_size) {\n        uint32_t d = iop_bus_read8(dma->bus, 0x14000008);\n\n        iop_bus_write8(dma->bus, dma->dev9.madr++, d);\n\n        dma->dev9.transfer_size--;\n    }\n\n    iop_dma_set_dicr_flag(dma, IOP_DMA_DEV9);\n    iop_dma_check_irq(dma);\n\n    dma->dev9.chcr &= ~0x1000000;\n}\nvoid iop_dma_handle_sif0_transfer(struct ps2_iop_dma* dma) {\n    // if (!ps2_sif0_is_empty(dma->sif)) {\n    //     printf(\"iopdma: SIF FIFO not empty\\n\");\n\n    //     exit(1);\n    // }\n\n    // ps2_sif0_reset(dma->sif);\n\n    dma->sif0.eot = 0;\n\n    do {\n        dma_fetch_tag(dma, &dma->sif0);\n\n        // printf(\"iop: SIF0 tag at %08x extra=%u addr=%08x size=%08x irq=%d eot=%d\\n\",\n        //     dma->sif0.tadr, dma->sif0.extra, dma->sif0.addr, dma->sif0.size, dma->sif0.irq, dma->sif0.eot\n        // );\n\n        uint128_t q;\n\n        if (dma->sif0.extra) {\n            q.u32[0] = iop_bus_read32(dma->bus, dma->sif0.tadr + 8);\n            q.u32[1] = iop_bus_read32(dma->bus, dma->sif0.tadr + 12);\n            q.u32[2] = iop_bus_read32(dma->bus, dma->sif0.tadr + 0);\n            q.u32[3] = iop_bus_read32(dma->bus, dma->sif0.tadr + 4);\n\n            ps2_sif0_write(dma->sif, q);\n        }\n\n        while (dma->sif0.size) {\n            q.u32[0] = iop_bus_read32(dma->bus, dma->sif0.addr);\n            q.u32[1] = iop_bus_read32(dma->bus, dma->sif0.addr + 4);\n            q.u32[2] = iop_bus_read32(dma->bus, dma->sif0.addr + 8);\n            q.u32[3] = iop_bus_read32(dma->bus, dma->sif0.addr + 12);\n\n            ps2_sif0_write(dma->sif, q);\n\n            dma->sif0.addr += 16;\n            dma->sif0.size -= 4;\n        }\n\n        dma->sif0.tadr += (dma->sif0.extra ? 4 : 2) * 4;\n    } while (!dma->sif0.eot);\n\n    iop_dma_set_dicr_flag(dma, IOP_DMA_SIF0);\n    iop_dma_check_irq(dma);\n\n    dmac_handle_sif0_transfer(dma->ee_dma);\n\n    dma->sif0.tadr += dma->sif0.extra ? 4 : 2;\n    dma->sif0.chcr &= ~0x1000000;\n}\n\n#include \"rpc.h\"\n\nvoid iop_dma_handle_sif1_transfer(struct ps2_iop_dma* dma) {\n    int madr_increment = ((dma->sif1.chcr >> 1) & 1) ? -4 : 4;\n\n    // No data in the SIF FIFO yet\n    if (ps2_sif1_is_empty(dma->sif))\n        return;\n\n    // Data ready but channel isn't ready yet, keep waiting\n    if (!(dma->sif1.chcr & 0x1000000)) {\n        // printf(\"iop: EE sent SIF1 but channel isn't ready\\n\");\n\n        return;\n    }\n\n    // Data ready and channel is started, do transfer\n    int eot;\n\n    do {\n        uint128_t q = ps2_sif1_read(dma->sif);\n\n        uint64_t tag = q.u64[0];\n\n        uint32_t addr = tag & 0x7fffff;\n        int size = (tag >> 32) & 0xffffff;\n        int irq = !!(tag & 0x40000000);\n        eot = !!(tag & 0x80000000);\n\n        // printf(\"iop: SIF1 tag read_index=%\\n\", dma->sif->sif1.read_index);\n\n        char buf[128];\n\n        if (rpc_decode_packet(dma->intc->iop, buf, ((void*)dma->sif->sif1.data) + (dma->sif->sif1.read_index * 16))) {\n            // printf(\"%s\\n\", buf);\n        }\n\n        while (size) {\n            uint128_t q = ps2_sif1_read(dma->sif);\n\n            for (int i = 0; i < 4; i++) {\n                iop_bus_write32(dma->bus, addr, q.u32[i]);\n\n                addr += madr_increment;\n                --size;\n            }\n        }\n\n        // Send interrupt on tag IRQ (regardless of channel IRQ enable)\n        if ((dma->dicr2 & 0x400) && irq) {\n            ps2_iop_intc_irq(dma->intc, IOP_INTC_DMA);\n        }\n\n        if (ps2_sif1_is_empty(dma->sif))\n            break;\n    } while (!eot);\n\n    iop_dma_set_dicr_flag(dma, IOP_DMA_SIF1);\n    iop_dma_check_irq(dma);\n\n    // ps2_sif1_reset(dma->sif);\n\n    dma->sif1.chcr &= ~0x1000000;\n}\nvoid iop_dma_handle_sio2_in_transfer(struct ps2_iop_dma* dma) {\n    uint32_t size = (dma->sio2_in.bcr & 0xffff) * (dma->sio2_in.bcr >> 16);\n\n    // printf(\"dma: SIO2 in transfer size=%d\\n\", size);\n\n    sio2_dma_reset(dma->sio2);\n\n    for (int i = 0; i < size; i++) {\n        uint32_t w = iop_bus_read32(dma->bus, dma->sio2_in.madr);\n\n        iop_bus_write8(dma->bus, 0x1F808260, (w >> 0) & 0xff);\n        iop_bus_write8(dma->bus, 0x1F808260, (w >> 8) & 0xff);\n        iop_bus_write8(dma->bus, 0x1F808260, (w >> 16) & 0xff);\n        iop_bus_write8(dma->bus, 0x1F808260, (w >> 24) & 0xff);\n\n        // printf(\"%02x %02x %02x %02x\\n\",\n        //     (w >> 0) & 0xff,\n        //     (w >> 8) & 0xff,\n        //     (w >> 16) & 0xff,\n        //     (w >> 24) & 0xff\n        // );\n\n        dma->sio2_in.madr += 4;\n    }\n\n    iop_dma_set_dicr_flag(dma, IOP_DMA_SIO2_IN);\n    iop_dma_check_irq(dma);\n\n    dma->sio2_in.chcr &= ~0x1000000;\n}\n\nvoid dma_handle_sio2_out_irq_event(void* udata, int overshoot) {\n    struct ps2_iop_dma* dma = (struct ps2_iop_dma*)udata;\n\n    iop_dma_set_dicr_flag(dma, IOP_DMA_SIO2_OUT);\n    iop_dma_check_irq(dma);\n\n    dma->sio2_out.chcr &= ~0x1000000;\n}\nvoid iop_dma_handle_sio2_out_transfer(struct ps2_iop_dma* dma) {\n    if ((dma->sio2_out.chcr & 0x1000000) == 0) {\n        fprintf(stderr, \"dma: SIO2_out not requested\\n\");\n\n        exit(1);\n\n        return;\n    }\n    \n    if (queue_is_empty(dma->sio2->out)) {\n        // printf(\"dma: SIO2_out waiting size=%d bcr=%08x madr=%08x\\n\", queue_size(dma->sio2->out), dma->sio2_out.bcr, dma->sio2_out.madr);\n        \n        return;\n    }\n\n    fprintf(stderr, \"dma: WHAT? Doing SIO2 out transfer size=%d bcr=%08x madr=%08x\\n\", queue_size(dma->sio2->out), dma->sio2_out.bcr, dma->sio2_out.madr);\n\n    exit(1);\n\n    for (int b = 0; b < (dma->sio2_out.bcr >> 16); b++) {\n        for (int i = 0; i < (dma->sio2_out.bcr & 0xffff); i++) {\n            for (int j = 0; j < 4; j++) {\n                uint8_t b = iop_bus_read8(dma->bus, 0x1F808264);\n\n                iop_bus_write8(dma->bus, dma->sio2_out.madr++, b);    \n            } \n\n            // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out));\n            // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out));\n            // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out));\n            // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out));\n        }\n    }\n    // for (int b = 0; b < (dma->sio2_out.bcr >> 16); b++) {\n    //     for (int i = 0; i < (dma->sio2_out.bcr & 0xffff); i++) {\n    //         iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0);\n    //         iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0);\n    //         iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0);\n    //         iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0);\n    //     }\n    // }\n\n    // struct sched_event event;\n\n    // event.callback = dma_handle_sio2_out_irq_event;\n    // event.cycles = 10000;\n    // event.name = \"SIO2 out DMA IRQ\";\n    // event.udata = dma;\n\n    // sched_schedule(dma->sched, event);\n\n    iop_dma_set_dicr_flag(dma, IOP_DMA_SIO2_OUT);\n    iop_dma_check_irq(dma);\n\n    dma->sio2_out.chcr &= ~0x1000000;\n}\n\nvoid iop_dma_end_sio2_out_transfer(struct ps2_iop_dma* dma) {\n    if ((dma->sio2_out.chcr & 0x1000000) == 0) {\n        // printf(\"dma: SIO2_out not requested\\n\");\n\n        return;\n    }\n    \n    if (queue_is_empty(dma->sio2->out)) {\n        // printf(\"dma: SIO2 command put nothing in the fifo, ending transfer\\n\");\n\n        iop_dma_set_dicr_flag(dma, IOP_DMA_SIO2_OUT);\n        iop_dma_check_irq(dma);\n\n        dma->sio2_out.chcr &= ~0x1000000;\n        // printf(\"dma: SIO2_out waiting size=%d bcr=%08x madr=%08x\\n\", queue_size(dma->sio2->out), dma->sio2_out.bcr, dma->sio2_out.madr);\n        \n        return;\n    }\n\n    // printf(\"dma: Doing SIO2 out transfer size=%d bcr=%08x madr=%08x\\n\", queue_size(dma->sio2->out), dma->sio2_out.bcr, dma->sio2_out.madr);\n\n    for (int b = 0; b < (dma->sio2_out.bcr >> 16); b++) {\n        for (int i = 0; i < (dma->sio2_out.bcr & 0xffff); i++) {\n            for (int j = 0; j < 4; j++) {\n                uint8_t b = iop_bus_read8(dma->bus, 0x1F808264);\n\n                iop_bus_write8(dma->bus, dma->sio2_out.madr++, b);    \n            } \n\n            // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out));\n            // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out));\n            // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out));\n            // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out));\n        }\n    }\n    // for (int b = 0; b < (dma->sio2_out.bcr >> 16); b++) {\n    //     for (int i = 0; i < (dma->sio2_out.bcr & 0xffff); i++) {\n    //         iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0);\n    //         iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0);\n    //         iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0);\n    //         iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0);\n    //     }\n    // }\n\n    // struct sched_event event;\n\n    // event.callback = dma_handle_sio2_out_irq_event;\n    // event.cycles = 10000;\n    // event.name = \"SIO2 out DMA IRQ\";\n    // event.udata = dma;\n\n    // sched_schedule(dma->sched, event);\n\n    iop_dma_set_dicr_flag(dma, IOP_DMA_SIO2_OUT);\n    iop_dma_check_irq(dma);\n\n    dma->sio2_out.chcr &= ~0x1000000;\n}\n\nuint64_t ps2_iop_dma_read32(struct ps2_iop_dma* dma, uint32_t addr) {\n    struct iop_dma_channel* c = iop_dma_get_channel(dma, addr);\n\n    if (c) {\n        switch (addr & 0xf) {\n            case 0x0: return c->madr;\n            case 0x4: return c->bcr;\n            case 0x8: return c->chcr;\n            case 0xc: return c->tadr;\n        }\n\n        const char* name = iop_dma_get_channel_name(addr);\n\n        printf(\"iop_dma: Unknown %s register read %08x\\n\", name, addr);\n\n        return 0;\n    }\n\n    switch (addr) {\n        case 0x1f8010f0: return dma->dpcr;\n        case 0x1f801570: return dma->dpcr2;\n        case 0x1f8010f4: return dma->dicr;\n        case 0x1f801574: return dma->dicr2;\n        case 0x1f801578: return dma->dmacen;\n        case 0x1f80157c: return dma->dmacinten;\n    }\n\n    printf(\"iop_dma: Unknown DMA register read %08x\\n\", addr);\n\n    return 0;\n}\n\nvoid ps2_iop_dma_write32(struct ps2_iop_dma* dma, uint32_t addr, uint64_t data) {\n    struct iop_dma_channel* c = iop_dma_get_channel(dma, addr);\n\n    if (c) {\n        switch (addr & 0xf) {\n            case 0x0: c->madr = data; return;\n            case 0x4: c->bcr = data; return;\n            case 0x8: {\n                if ((c->chcr & 0x1000000) && (data & 0x1000000)) {\n                    printf(\"iop: SPU2 channel already started, ignoring\\n\");\n\n                    return;\n                }\n\n                c->chcr = data;\n\n                if (!(c->chcr & 0x1000000)) {\n                    return;\n                }\n\n                c->transfer_size = (c->bcr >> 16) * ((c->bcr & 0xffff) * 4);\n\n                // if ((addr & 0xff0) == 0x0b0) {\n                //     FILE* file = fopen(\"cdvd.dump\", \"a\");\n                //     fprintf(file, \"iop: Starting %s channel with chcr=%08x madr=%08x bcr=%08x tadr=%08x\\n\", iop_dma_get_channel_name(addr), data, c->madr, c->bcr, c->tadr);\n                //     fclose(file);\n                // }\n\n                // if ((addr & 0xff0) == 0x0b0)\n                // printf(\"iop: Starting %s channel with chcr=%08x madr=%08x bcr=%08x tadr=%08x\\n\", iop_dma_get_channel_name(addr), data, c->madr, c->bcr, c->tadr);\n\n                // printf(\"iop: Starting %s channel with chcr=%08x madr=%08x bcr=%08x tadr=%08x\\n\", iop_dma_get_channel_name(addr), data, c->madr, c->bcr, c->tadr);\n\n                // Check negative MADR increments\n                // if ((c->chcr & 2) == 0) {\n                //     fprintf(stderr, \"iop: Negative MADR increments not supported on IOP DMA channels\\n\");\n\n                //     // exit(1);\n                // }\n\n                // // Check for burst transfers\n                // if (((c->chcr >> 9) & 3) != 0) {\n                //     fprintf(stderr, \"iop: Burst transfers not supported on IOP DMA channels\\n\");\n\n                //     // exit(1);\n                // }\n\n                // // Check for 0-sized blocks\n                // if ((c->bcr & 0xffff) == 0) {\n                //     fprintf(stderr, \"iop: 0-sized blocks not supported on IOP DMA channels\\n\");\n\n                //     exit(1);\n                // }\n\n                switch (addr & 0xff0) {\n                    case 0x080: iop_dma_handle_mdec_in_transfer(dma); break;\n                    case 0x090: iop_dma_handle_mdec_out_transfer(dma); break;\n                    case 0x0a0: iop_dma_handle_sif2_transfer(dma); break;\n                    case 0x0b0: iop_dma_handle_cdvd_transfer(dma); break;\n                    case 0x0c0: iop_dma_handle_spu1_transfer(dma); break;\n                    case 0x0d0: iop_dma_handle_pio_transfer(dma); break;\n                    case 0x0e0: iop_dma_handle_otc_transfer(dma); break;\n                    case 0x500: iop_dma_handle_spu2_transfer(dma); break;\n                    case 0x510: iop_dma_handle_dev9_transfer(dma); break;\n                    case 0x520: iop_dma_handle_sif0_transfer(dma); break;\n                    case 0x530: iop_dma_handle_sif1_transfer(dma); break;\n                    case 0x540: iop_dma_handle_sio2_in_transfer(dma); break;\n                    case 0x550: iop_dma_handle_sio2_out_transfer(dma); break;\n                }\n\n                return;\n            }\n            case 0xc: c->tadr = data; return;\n        }\n\n        const char* name = iop_dma_get_channel_name(addr);\n\n        printf(\"iop_dma: Unknown %s register write %08x %08lx\\n\", name, addr, data);\n\n        return;\n    }\n\n    switch (addr) {\n        case 0x1f8010f0: dma->dpcr = data; return;\n        case 0x1f801570: dma->dpcr2 = data; return;\n        case 0x1f8010f4: iop_dma_set_dicr(dma, data); iop_dma_check_irq(dma); return;\n        case 0x1f801574: iop_dma_set_dicr2(dma, data); iop_dma_check_irq(dma); return;\n        case 0x1f801578: dma->dmacen = data; return;\n        case 0x1f80157c: dma->dmacinten = data; iop_dma_check_irq(dma); return;\n    }\n\n    printf(\"iop_dma: Unknown DMA register write %08x %08lx\\n\", addr, data);\n}\n\nuint64_t ps2_iop_dma_read16(struct ps2_iop_dma* dma, uint32_t addr) {\n    struct iop_dma_channel* c = iop_dma_get_channel(dma, addr);\n\n    if (c) {\n        switch (addr & 0xf) {\n            case 0x0: return c->madr;\n            case 0x4: return c->bcr;\n            case 0x8: return c->chcr;\n            case 0xc: return c->tadr;\n        }\n\n        const char* name = iop_dma_get_channel_name(addr);\n\n        printf(\"iop_dma: Unknown %s register read %08x\\n\", name, addr);\n\n        return 0;\n    }\n\n    switch (addr) {\n        case 0x1f8010f0: return dma->dpcr;\n        case 0x1f801570: return dma->dpcr2;\n        case 0x1f8010f4: return dma->dicr;\n        case 0x1f801574: return dma->dicr2;\n        case 0x1f801578: return dma->dmacen;\n        case 0x1f80157c: return dma->dmacinten;\n    }\n\n    printf(\"iop_dma: Unknown DMA register read %08x\\n\", addr);\n\n    return 0;\n}\n\nvoid ps2_iop_dma_write16(struct ps2_iop_dma* dma, uint32_t addr, uint64_t data) {\n    struct iop_dma_channel* c = iop_dma_get_channel(dma, addr);\n\n    if (c) {\n        switch (addr & 0xf) {\n            case 0x4: if ((addr & 0xff0) == 0x0b0) printf(\"iop: bcrlo write16 %08x\\n\", data); c->bcr &= ~0xffff; c->bcr |= data; return;\n            case 0x6: if ((addr & 0xff0) == 0x0b0) printf(\"iop: bcrhi write16 %08x\\n\", data); c->bcr &= 0xffff; c->bcr |= data << 16; return;\n        }\n\n        const char* name = iop_dma_get_channel_name(addr);\n\n        fprintf(stderr, \"iop_dma: Unknown 16-bit %s register write %08x %08lx\\n\", name, addr, data);\n\n        exit(1);\n\n        return;\n    }\n\n    fprintf(stderr, \"iop_dma: Unknown DMA register write %08x %08lx\\n\", addr, data);\n\n    exit(1);\n}\n\nvoid iop_dma_end_spu1_transfer(struct ps2_iop_dma* dma) {\n    iop_dma_set_dicr_flag(dma, IOP_DMA_SPU1);\n    iop_dma_check_irq(dma);\n\n    dma->spu1.chcr &= ~0x1000000;\n}\n\nvoid iop_dma_end_spu2_transfer(struct ps2_iop_dma* dma) {\n    iop_dma_set_dicr_flag(dma, IOP_DMA_SPU2);\n    iop_dma_check_irq(dma);\n\n    dma->spu2.chcr &= ~0x1000000;\n}\n\nvoid iop_dma_handle_spu1_adma(struct ps2_iop_dma* dma) {\n    if (!dma->spu1.transfer_size) {\n        // If we have no more data to transfer, then we can end the transfer\n        // and trigger an IRQ event\n        iop_dma_set_dicr_flag(dma, IOP_DMA_SPU1);\n        iop_dma_check_irq(dma);\n\n        dma->spu1.chcr &= ~0x1000000;\n\n        // printf(\"spu2 core0: transfer done (chcr=%08x)\\n\", dma->spu1.chcr);\n\n        return;\n    }\n\n    // printf(\"spu2 core0: transfer update (%d bytes pending)\\n\", dma->spu1.transfer_size);\n\n    // Transfer data as long as the spu1 isn't streaming ADMA\n    // samples and we still have data to transfer\n    while (dma->spu1.transfer_size && !spu2_is_adma_active(dma->spu, 0)) {\n        uint32_t d = iop_bus_read32(dma->bus, dma->spu1.madr);\n\n        iop_bus_write16(dma->bus, 0x1f9001ac, d & 0xffff);\n        iop_bus_write16(dma->bus, 0x1f9001ac, d >> 16);\n\n        dma->spu1.madr += 4;\n        dma->spu1.transfer_size -= 4;\n    }\n}\nvoid iop_dma_handle_spu2_adma(struct ps2_iop_dma* dma) {\n    if (!dma->spu2.transfer_size) {\n        // If we have no more data to transfer, then we can end the transfer\n        // and trigger an IRQ event\n        iop_dma_set_dicr_flag(dma, IOP_DMA_SPU2);\n        iop_dma_check_irq(dma);\n\n        dma->spu2.chcr &= ~0x1000000;\n\n        // printf(\"spu2 core1: transfer done (chcr=%08x)\\n\", dma->spu2.chcr);\n\n        return;\n    }\n    \n    // printf(\"spu2 core1: transfer update (%d bytes pending)\\n\", dma->spu2.transfer_size);\n\n    // Transfer data as long as the SPU2 isn't streaming ADMA\n    // samples and we still have data to transfer\n    while (dma->spu2.transfer_size && !spu2_is_adma_active(dma->spu, 1)) {\n        uint32_t d = iop_bus_read32(dma->bus, dma->spu2.madr);\n\n        iop_bus_write16(dma->bus, 0x1f9005ac, d & 0xffff);\n        iop_bus_write16(dma->bus, 0x1f9005ac, d >> 16);\n\n        dma->spu2.madr += 4;\n        dma->spu2.transfer_size -= 4;\n    }\n}"
  },
  {
    "path": "src/iop/dma.h",
    "content": "#ifndef IOP_DMA_H\n#define IOP_DMA_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"u128.h\"\n\n#include \"shared/sif.h\"\n\n#include \"intc.h\"\n#include \"cdvd.h\"\n#include \"scheduler.h\"\n#include \"sio2.h\"\n#include \"spu2.h\"\n\n#include \"bus_decl.h\"\n\n#include \"ee/dmac.h\"\n\n#define IOP_DMA_MDEC_IN  0\n#define IOP_DMA_MDEC_OUT 1\n#define IOP_DMA_SIF2     2\n#define IOP_DMA_CDVD     3\n#define IOP_DMA_SPU1     4\n#define IOP_DMA_PIO      5\n#define IOP_DMA_OTC      6\n#define IOP_DMA_SPU2     7\n#define IOP_DMA_DEV9     8\n#define IOP_DMA_SIF0     9\n#define IOP_DMA_SIF1     10\n#define IOP_DMA_SIO2_IN  11\n#define IOP_DMA_SIO2_OUT 12\n\nstruct iop_dma_channel {\n    uint32_t madr;\n    uint32_t bcr;\n    uint32_t chcr;\n    uint32_t tadr;\n    int transfer_pending;\n\n    // Tag\n    uint64_t tag;\n    uint32_t addr;\n    uint32_t size;\n    int irq;\n    int eot;\n    int extra;\n    int32_t transfer_size;\n};\n\nstruct ps2_iop_dma {\n    struct iop_bus* bus;\n\n    struct iop_dma_channel mdec_in;\n    struct iop_dma_channel mdec_out;\n    struct iop_dma_channel sif2;\n    struct iop_dma_channel cdvd;\n    struct iop_dma_channel spu1;\n    struct iop_dma_channel pio;\n    struct iop_dma_channel otc;\n    struct iop_dma_channel spu2;\n    struct iop_dma_channel dev9;\n    struct iop_dma_channel sif0;\n    struct iop_dma_channel sif1;\n    struct iop_dma_channel sio2_in;\n    struct iop_dma_channel sio2_out;\n\n    uint32_t dpcr;\n    uint32_t dpcr2;\n    uint32_t dicr;\n    uint32_t dicr2;\n    uint32_t dmacen;\n    uint32_t dmacinten;\n\n    struct ps2_iop_intc* intc;\n    struct ps2_sif* sif;\n    struct ps2_cdvd* drive;\n    struct ps2_dmac* ee_dma;\n    struct ps2_sio2* sio2;\n    struct ps2_spu2* spu;\n    struct sched_state* sched;\n};\n\nstruct ps2_iop_dma* ps2_iop_dma_create(void);\nvoid ps2_iop_dma_init(struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct ps2_sif* sif, struct ps2_cdvd* cdvd, struct ps2_dmac* ee_dma, struct ps2_sio2* sio2, struct ps2_spu2* spu, struct sched_state* sched, struct iop_bus* bus);\nvoid ps2_iop_dma_destroy(struct ps2_iop_dma* dma);\nuint64_t ps2_iop_dma_read16(struct ps2_iop_dma* dma, uint32_t addr);\nvoid ps2_iop_dma_write16(struct ps2_iop_dma* dma, uint32_t addr, uint64_t data);\nuint64_t ps2_iop_dma_read32(struct ps2_iop_dma* dma, uint32_t addr);\nvoid ps2_iop_dma_write32(struct ps2_iop_dma* dma, uint32_t addr, uint64_t data);\nvoid iop_dma_handle_mdec_in_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_mdec_out_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_sif2_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_cdvd_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_spu1_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_pio_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_otc_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_spu2_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_dev9_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_sif0_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_sif1_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_sio2_in_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_sio2_out_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_end_sio2_out_transfer(struct ps2_iop_dma* dma);\n\nvoid iop_dma_handle_spu1_adma(struct ps2_iop_dma* dma);\nvoid iop_dma_handle_spu2_adma(struct ps2_iop_dma* dma);\n\nvoid iop_dma_end_spu1_transfer(struct ps2_iop_dma* dma);\nvoid iop_dma_end_spu2_transfer(struct ps2_iop_dma* dma);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/fw.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"fw.h\"\n\nstruct ps2_fw* ps2_fw_create(void) {\n    return malloc(sizeof(struct ps2_fw));\n}\n\nvoid ps2_fw_init(struct ps2_fw* fw, struct ps2_iop_intc* intc) {\n    memset(fw, 0, sizeof(struct ps2_fw));\n\n    fw->intc = intc;\n}\n\nvoid ps2_fw_destroy(struct ps2_fw* fw) {\n    free(fw);\n}\n\nvoid fw_read_phy(struct ps2_fw* fw) {\n    uint8_t reg = (fw->phy_access >> 24) & 0xF;\n\n    fw->phy_access &= ~0x80000000; //Cancel read request\n    fw->phy_access |= fw->phy_r[reg] | ((uint16_t)reg << 8);\n\n    if (fw->intr0mask & 0x40000000) {\n        fw->intr0 |= 0x40000000;\n\n        ps2_iop_intc_irq(fw->intc, IOP_INTC_FWRE);\n    }\n\n    // printf(\"fw: PHY read from reg %d (%08x)\\n\", reg, fw->phy_access & 0xff);\n}\n\nvoid fw_write_phy(struct ps2_fw* fw) {\n    uint8_t reg = (fw->phy_access >> 8) & 0xF;\n    uint8_t value = fw->phy_access & 0xFF;\n\n    fw->phy_r[reg] = value;\n\n    fw->phy_access &= ~0x4000ffff;\n\n    // printf(\"fw: PHY write to reg %d (%08x)\\n\", reg, value);\n}\n\nuint64_t ps2_fw_read32(struct ps2_fw* fw, uint32_t addr) {\n    uint32_t reg = addr & 0x1ff;\n\n    switch (reg) {\n        case 0x0: return 0xffc00001;\n        case 0x8: return fw->ctrl0;\n        case 0x10: return fw->ctrl2;\n        case 0x14: return fw->phy_access;\n        case 0x20: return fw->intr0;\n        case 0x24: return fw->intr0mask;\n        case 0x28: return fw->intr1;\n        case 0x2C: return fw->intr1mask;\n        case 0x30: return fw->intr2;\n        case 0x34: return fw->intr2mask;\n        case 0x7C: return 0x10000001; //Value related to NodeID somehow\n    }\n\n    printf(\"fw: Unhandled 32-bit read from %08x\\n\", addr);\n\n    return 0;\n}\n\nvoid ps2_fw_write32(struct ps2_fw* fw, uint32_t addr, uint64_t data) {\n    uint32_t reg = addr & 0x1ff;\n    \n    switch (reg) {\n        case 0x8: {\n            fw->ctrl0 = data;\n            fw->ctrl0 &= ~0x3800000;\n        } return;\n        case 0x10: {\n            if (data & 0x2) //Power On\n                fw->ctrl2 |= 0x8; //SCLK OK\n        } return;\n        case 0x14: {\n            fw->phy_access = data;\n\n            if (fw->phy_access & 0x40000000) {\n                fw_write_phy(fw);\n            } else if (fw->phy_access & 0x80000000) {\n                fw_read_phy(fw);\n            }\n        } return;\n        case 0x20: {\n            fw->intr0 &= ~data;\n        } return;\n        case 0x24: {\n            fw->intr0mask = data;\n        } return;\n        case 0x28: {\n            fw->intr1 &= ~data;\n        } return;\n        case 0x2C: {\n            fw->intr1mask = data;\n        } return;\n        case 0x30: {\n            fw->intr2 &= ~data;\n        } return;\n        case 0x34: {\n            fw->intr2mask = data;\n        } return;\n        case 0xB8: {\n            fw->dma_ctrl_sr0 = data;\n        } return;\n        case 0x138: {\n            fw->dma_ctrl_sr1 = data;\n        } return;\n    }\n\n    printf(\"fw: Unhandled 32-bit write to %08x (%08lx)\\n\", addr, data);\n}"
  },
  {
    "path": "src/iop/fw.h",
    "content": "#ifndef FW_H\n#define FW_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"intc.h\"\n\nstruct ps2_fw {\n    uint32_t intr0;\n    uint32_t intr1;\n    uint32_t intr2;\n    uint32_t intr0mask;\n    uint32_t intr1mask;\n    uint32_t intr2mask;\n    uint32_t ctrl0;\n    uint32_t ctrl1;\n    uint32_t ctrl2;\n    uint32_t dma_ctrl_sr0;\n    uint32_t dma_ctrl_sr1;\n    uint32_t phy_access;\n    uint8_t phy_r[16];\n\n    struct ps2_iop_intc* intc;\n};\n\nstruct ps2_fw* ps2_fw_create(void);\nvoid ps2_fw_init(struct ps2_fw* fw, struct ps2_iop_intc* intc);\nvoid ps2_fw_destroy(struct ps2_fw* fw);\nuint64_t ps2_fw_read32(struct ps2_fw* fw, uint32_t addr);\nvoid ps2_fw_write32(struct ps2_fw* fw, uint32_t addr, uint64_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/hle/ioman.cpp",
    "content": "#include <filesystem>\n#include <cstdlib>\n#include <cstdio>\n#include <string>\n\n#include \"ioman.h\"\n\n#include \"../iop.h\"\n#include \"../bus.h\"\n#include \"../iop_export.h\"\n\n#define IOMAN_MAX_OPEN_FILES 128\n\n/** Format mask */\n#define FIO_SO_IFMT  0x0038\n/** Symbolic link */\n#define FIO_SO_IFLNK 0x0008\n/** Regular file */\n#define FIO_SO_IFREG 0x0010\n/** Directory */\n#define FIO_SO_IFDIR 0x0020\n\n/** read */\n#define FIO_SO_IROTH 0x0004\n/** write */\n#define FIO_SO_IWOTH 0x0002\n/** execute */\n#define FIO_SO_IXOTH 0x0001\n\n#define FIO_O_RDONLY       0x0001\n#define FIO_O_WRONLY       0x0002\n#define FIO_O_RDWR         0x0003\n#define FIO_O_DIROPEN      0x0008  // Internal use for dopen\n#define FIO_O_NBLOCK       0x0010\n#define FIO_O_APPEND       0x0100\n#define FIO_O_CREAT        0x0200\n#define FIO_O_TRUNC        0x0400\n#define FIO_O_EXCL         0x0800\n#define FIO_O_NOWAIT       0x8000\n\n#define FIO_SEEK_SET       0\n#define FIO_SEEK_CUR       1\n#define FIO_SEEK_END       2\n\nstd::string ioman_read_string(struct iop_state* iop, uint32_t addr) {\n    std::string str;\n\n    for (int i = 0; i < 256; i++) {\n        uint8_t d = iop_read8(iop, addr + i);\n\n        if (!d)\n            break;\n\n        str += d;\n    }\n\n    return str;\n}\n\nvoid ioman_read_ptr(struct iop_state* iop, uint32_t addr, void* buf, int size) {\n    unsigned char* ptr = (unsigned char*)buf;\n\n    for (int i = 0; i < size; i++) {\n        ptr[i] = iop_read8(iop, addr + i);\n    }\n}\n\nstruct iomanx_stat {\n    unsigned int mode;\n    unsigned int attr;\n    unsigned int size;\n    unsigned char ctime[8];\n    unsigned char atime[8];\n    unsigned char mtime[8];\n    unsigned int hisize;\n    /** Number of subs (main) / subpart number (sub) */\n    unsigned int private_0;\n    unsigned int private_1;\n    unsigned int private_2;\n    unsigned int private_3;\n    unsigned int private_4;\n    /** Sector start.  */\n    unsigned int private_5;\n};\n\nstruct iomanx_dirent {\n    iomanx_stat stat;\n    char name[256];\n    uint32_t privdata;\n};\n\nstruct ioman_dirent {\n    std::filesystem::path* path;\n    int index;\n};\n\nstruct ioman_hle_state {\n    FILE* files[IOMAN_MAX_OPEN_FILES] = { nullptr };\n    ioman_dirent directories[IOMAN_MAX_OPEN_FILES] = { nullptr };\n} state;\n\nstatic inline int ioman_allocate_file(FILE* file) {\n    for (int i = 0; i < IOMAN_MAX_OPEN_FILES; i++) {\n        if (!state.files[i]) {\n            state.files[i] = file;\n\n            return i;\n        }\n    }\n\n    // No free file slots\n    return -1;\n}\n\nstatic inline int ioman_allocate_directory(std::filesystem::path path) {\n    for (int i = 0; i < IOMAN_MAX_OPEN_FILES; i++) {\n        if (!state.directories[i].path) {\n            state.directories[i].path = new std::filesystem::path(path);\n\n            return i;\n        }\n    }\n\n    // No free directory slots\n    return -1;\n}\n\nstatic inline int ioman_get_device(std::string path) {\n    auto p = path.find_first_of(':');\n\n    if (p == std::string::npos)\n        return 0;\n\n    std::string device = path.substr(0, p);\n\n    if (device == \"rom0\") {\n        return IOMAN_DEV_ROM0;\n    } else if (device == \"rom1\") {\n        return IOMAN_DEV_ROM1;\n    } else if (device == \"cdrom0\") {\n        return IOMAN_DEV_CDROM0;\n    } else if (device == \"host\") {\n        return IOMAN_DEV_HOST;\n    } else if (device == \"host0\") {\n        return IOMAN_DEV_HOST;        \n    } else if (device == \"mc0\") {\n        return IOMAN_DEV_MC0;\n    } else if (device == \"mc1\") {\n        return IOMAN_DEV_MC1;\n    } else if (device == \"mass\") {\n        return IOMAN_DEV_MASS;\n    }\n\n    // To-do: There's probably some ATA/DEV9/HDD device\n    // but I have no idea how it's used\n\n    return IOMAN_DEV_UNKNOWN;\n}\n\nconst char* ioman_get_mode_string(int mode) {\n    if ((mode & FIO_O_RDWR) == FIO_O_RDWR) {\n        return \"r+b\";\n    } else if ((mode & FIO_O_RDWR) == FIO_O_WRONLY) {\n        return \"wb\";\n    } else {\n        return \"rb\";\n    }\n}\n\nstd::string ioman_get_host_path(std::string path) {\n    auto p = path.find_first_of(':');\n\n    if (p == std::string::npos)\n        return \"\";\n\n    std::string str = path.substr(p + 1);\n\n    if (!str.size())\n        return \"\";\n\n    if (str[0] == '/' || str[0] == '\\\\')\n        str = str.substr(1);\n\n    p = str.find_first_not_of(' ');\n\n    if (p != std::string::npos)\n        str = str.substr(p);\n\n    return str;\n}\n\nextern \"C\" int ioman_open(struct iop_state* iop, int iomanx) {\n    std::string path = ioman_read_string(iop, iop->r[4]);\n    int mode = iop->r[5];\n\n    int device = ioman_get_device(path);\n\n    // printf(\"%s: path=%s mode=%d\\n\", iomanx ? \"iomanx\" : \"ioman\", path.c_str(), mode);\n\n    // Only hook host files\n    if (device != IOMAN_DEV_HOST && device != IOMAN_DEV_MASS)\n        return 0;\n\n    // Get access path\n    std::string str = ioman_get_host_path(path);\n\n    if (!str.size()) return 0;\n\n    std::filesystem::path absolute = std::filesystem::absolute(str);\n\n    FILE* file = NULL;\n\n    if (mode & FIO_O_TRUNC && mode & FIO_O_CREAT) {\n        // Truncate file if it exists, otherwise create new file\n        if (!(file = fopen(absolute.string().c_str(), \"w+b\"))) {\n            return 0;\n        }\n    } else if (mode & FIO_O_CREAT) {\n        // Only create file\n        if (!(file = fopen(absolute.string().c_str(), \"a+b\"))) {\n            return 0;\n        }\n    } else if (mode & FIO_O_TRUNC) {\n        // Check if file exists before truncating\n        if (!(file = fopen(absolute.string().c_str(), \"r+b\"))) {\n            return 0;\n        }\n\n        if (!(file = fopen(absolute.string().c_str(), \"w+b\"))) {\n            return 0;\n        }\n    }\n\n    fclose(file);\n\n    file = fopen(absolute.string().c_str(), ioman_get_mode_string(mode));\n\n    if (!file)\n        return 0;\n\n    printf(\"%s: Opened \\'%s\\'\\n\", iomanx ? \"iomanx\" : \"ioman\", absolute.string().c_str());\n\n    int slot = ioman_allocate_file(file);\n\n    // Return file handle\n    iop_return(iop, 0x100 + slot);\n\n    return 1;\n}\nextern \"C\" int ioman_close(struct iop_state* iop, int iomanx) {\n    uint32_t fd = iop->r[4];\n\n    if (!(fd >= 0x100 && fd < 0x140))\n        return 0;\n\n    fd -= 0x100;\n\n    if (state.files[fd])\n        fclose(state.files[fd]);\n\n    state.files[fd] = nullptr;\n\n    iop_return(iop, 0);\n\n    return 1;\n}\nextern \"C\" int ioman_read(struct iop_state* iop, int iomanx) {\n    uint32_t fd = iop->r[4];\n\n    if (!(fd >= 0x100 && fd < 0x140))\n        return 0;\n\n    fd -= 0x100;\n\n    if (!state.files[fd])\n        return 0;\n    \n    uint32_t ptr = iop->r[5];\n    uint32_t size = iop->r[6];\n\n    uint8_t* buf = (uint8_t*)malloc(size);\n\n    int ret = fread(buf, 1, size, state.files[fd]);\n\n    for (int i = 0; i < size; i++) {\n        iop_write8(iop, ptr + i, buf[i]);\n    }\n\n    free(buf);\n\n    iop_return(iop, ret);\n\n    return 1;\n}\nextern \"C\" int ioman_write(struct iop_state* iop, int iomanx) {\n    uint32_t fd = iop->r[4];\n\n    // We only use this to HLE IOMAN stdout writes\n    // if (fd != 1)\n    //     return 0;\n\n    // printf(\"%s: write fd=%d\\n\", iomanx ? \"iomanx\" : \"ioman\", fd);\n\n    if (fd >= 0x100 && fd < 0x140) {\n        fd -= 0x100;\n\n        if (!state.files[fd])\n            return 0;\n\n        uint32_t ptr = iop->r[5];\n        uint32_t size = iop->r[6];\n\n        uint8_t* buf = (uint8_t*)malloc(size);\n\n        for (int i = 0; i < size; i++) {\n            buf[i] = iop_read8(iop, ptr + i);\n        }\n\n        int ret = fwrite(buf, 1, size, state.files[fd]);\n\n        free(buf);\n\n        iop_return(iop, ret);\n\n        return 1;\n    } else if (fd == 1) {\n        // HLE IOMAN stdout writes\n        uint32_t ptr = iop->r[5];\n        uint32_t size = iop->r[6] & 0xfff;\n\n        char c = iop_read8(iop, ptr++);\n        int cnt = 0;\n\n        while (c && ((cnt++) != size)) {\n            iop->kputchar(iop->kputchar_udata, c);\n\n            c = iop_read8(iop, ptr++);\n        }\n\n        fflush(stdout);\n\n        iop_return(iop, size);\n\n        return 1;\n    }\n\n    return 0;\n}\nextern \"C\" int ioman_lseek(struct iop_state* iop, int iomanx) {\n    uint32_t fd = iop->r[4];\n\n    if (!(fd >= 0x100 && fd < 0x140))\n        return 0;\n\n    fd -= 0x100;\n\n    if (!state.files[fd])\n        return 0;\n\n    int32_t off = iop->r[5];\n    uint32_t whence = iop->r[6];\n\n    switch (whence) {\n        case 0: fseek(state.files[fd], off, SEEK_SET); break;\n        case 1: fseek(state.files[fd], off, SEEK_CUR); break;\n        case 2: fseek(state.files[fd], off, SEEK_END); break;\n    }\n\n    int ret = ftell(state.files[fd]);\n\n    iop_return(iop, ret);\n\n    return 1;\n}\nextern \"C\" int ioman_ioctl(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_remove(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_mkdir(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_rmdir(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_dopen(struct iop_state* iop, int iomanx) {\n    std::string path = ioman_read_string(iop, iop->r[4]);\n    int mode = iop->r[5];\n\n    int device = ioman_get_device(path);\n\n    if (device != IOMAN_DEV_HOST && device != IOMAN_DEV_MASS)\n        return 0;\n\n    // printf(\"%s: dopen path=%s mode=%d\\n\", iomanx ? \"iomanx\" : \"ioman\", path.c_str(), mode);\n\n    // Get access path\n    std::string str = ioman_get_host_path(path);\n\n    if (!str.size()) return 0;\n\n    std::filesystem::path absolute = std::filesystem::absolute(str);\n\n    if (!std::filesystem::exists(absolute) || !std::filesystem::is_directory(absolute)) {\n        fprintf(stderr, \"ioman: Directory \\'%s\\' does not exist!\\n\", absolute.string().c_str());\n\n        return 0;\n    }\n\n    int slot = ioman_allocate_directory(absolute);\n\n    if (slot == -1)\n        return 0;\n\n    // printf(\"%s: Opened directory \\'%s\\' (fd=%x)\\n\", iomanx ? \"iomanx\" : \"ioman\", absolute.string().c_str(), 0x140 + slot);\n\n    iop_return(iop, 0x140 + slot);\n\n    return 1;\n}\nextern \"C\" int ioman_dclose(struct iop_state* iop, int iomanx) {\n    uint32_t fd = iop->r[4];\n\n    if (!(fd >= 0x140 && fd < 0x180))\n        return 0;\n\n    fd -= 0x140;\n\n    if (state.directories[fd].path)\n        delete state.directories[fd].path;\n\n    state.directories[fd].path = nullptr;\n    state.directories[fd].index = 0;\n\n    iop_return(iop, 0);\n\n    return 1;\n}\nextern \"C\" int ioman_dread(struct iop_state* iop, int iomanx) {\n    uint32_t fd = iop->r[4];\n    uint32_t ptr = iop->r[5];\n\n    if (!(fd >= 0x140 && fd < 0x180))\n        return 0;\n\n    fd -= 0x140;\n\n    if (!state.directories[fd].path)\n        return 0;\n\n    ioman_dirent* dir = &state.directories[fd];\n\n    std::filesystem::directory_entry entry;\n\n    bool found = false;\n    int i = 0;\n\n    for (const auto& e : std::filesystem::directory_iterator(*dir->path)) {\n        if (i == dir->index) {\n            entry = e;\n            found = true;\n\n            break;\n        }\n\n        i++;\n    }\n\n    if (!found) {\n        iop_return(iop, 0);\n\n        return 1;\n    }\n\n    iomanx_dirent dirent;\n\n    dirent.stat.mode = entry.is_directory() ? FIO_SO_IFDIR : FIO_SO_IFREG;\n\n    strncpy(dirent.name, entry.path().filename().string().c_str(), 256);\n\n    dirent.name[255] = '\\0';\n\n    for (int i = 0; i < sizeof(dirent); i++) {\n        iop_write8(iop, ptr + i, ((uint8_t*)&dirent)[i]);\n    }\n\n    printf(\"%s: dread index=%d name=%s\\n\", iomanx ? \"iomanx\" : \"ioman\", dir->index, dirent.name);\n\n    dir->index++;\n\n    iop_return(iop, fd + 0x140);\n\n    return 1;\n}\nextern \"C\" int ioman_getstat(struct iop_state* iop, int iomanx) {\n    char buf[256];\n\n    for (int i = 0; i < 256; i++) {\n        uint8_t d = iop_read8(iop, iop->r[4] + i);\n\n        buf[i] = d;\n\n        if (!d)\n            break;\n    }\n\n    // fprintf(stderr, \"%s: getstat(%s)\\n\", iomanx ? \"iomanx\" : \"ioman\", buf);\n\n    iop_return(iop, 0);\n\n    return 1;\n}\nextern \"C\" int ioman_chstat(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_format(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_adddrv(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_deldrv(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_stdioinit(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_rename(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_chdir(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_sync(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_mount(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_umount(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_lseek64(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_devctl(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_symlink(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_readlink(struct iop_state* iop, int iomanx) { return 0; }\nextern \"C\" int ioman_ioctl2(struct iop_state* iop, int iomanx) { return 0; }"
  },
  {
    "path": "src/iop/hle/ioman.h",
    "content": "#ifndef IOMAN_H\n#define IOMAN_H\n\n#include \"../iop.h\"\n#include \"../iop_export.h\"\n\n#define IOMAN_DEV_UNKNOWN 0\n\n// BIOS ROM\n// DVD ROM\n// CDVD drive\n// Host machine\n// Memory card slot 1\n// Memory card slot 2\n// USB drive\n#define IOMAN_DEV_ROM0      1\n#define IOMAN_DEV_ROM1      2\n#define IOMAN_DEV_CDROM0    3\n#define IOMAN_DEV_HOST      4\n#define IOMAN_DEV_MC0       5\n#define IOMAN_DEV_MC1       6\n#define IOMAN_DEV_MASS      7\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nint ioman_open(struct iop_state* iop, int iomanx);\nint ioman_close(struct iop_state* iop, int iomanx);\nint ioman_read(struct iop_state* iop, int iomanx);\nint ioman_write(struct iop_state* iop, int iomanx);\nint ioman_lseek(struct iop_state* iop, int iomanx);\nint ioman_ioctl(struct iop_state* iop, int iomanx);\nint ioman_remove(struct iop_state* iop, int iomanx);\nint ioman_mkdir(struct iop_state* iop, int iomanx);\nint ioman_rmdir(struct iop_state* iop, int iomanx);\nint ioman_dopen(struct iop_state* iop, int iomanx);\nint ioman_dclose(struct iop_state* iop, int iomanx);\nint ioman_dread(struct iop_state* iop, int iomanx);\nint ioman_getstat(struct iop_state* iop, int iomanx);\nint ioman_chstat(struct iop_state* iop, int iomanx);\nint ioman_format(struct iop_state* iop, int iomanx);\nint ioman_adddrv(struct iop_state* iop, int iomanx);\nint ioman_deldrv(struct iop_state* iop, int iomanx);\nint ioman_stdioinit(struct iop_state* iop, int iomanx);\nint ioman_rename(struct iop_state* iop, int iomanx);\nint ioman_chdir(struct iop_state* iop, int iomanx);\nint ioman_sync(struct iop_state* iop, int iomanx);\nint ioman_mount(struct iop_state* iop, int iomanx);\nint ioman_umount(struct iop_state* iop, int iomanx);\nint ioman_lseek64(struct iop_state* iop, int iomanx);\nint ioman_devctl(struct iop_state* iop, int iomanx);\nint ioman_symlink(struct iop_state* iop, int iomanx);\nint ioman_readlink(struct iop_state* iop, int iomanx);\nint ioman_ioctl2(struct iop_state* iop, int iomanx);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/hle/loadcore.c",
    "content": "#include \"loadcore.h\"\n\n#include \"../iop.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\nstatic unsigned get_module_list(struct iop_state* iop)\n{\n    /* Loadcore puts a pointer at 0x3f0 to an array in its data section */\n    unsigned bootmodes_ptr = iop_read32(iop, 0x3f0);\n    unsigned p = bootmodes_ptr - 0x60;\n    unsigned found = 0;\n\n    /* see if the string starting with PsIIload is there*/\n    while (p < bootmodes_ptr) {\n        if (iop_read32(iop, p) == 0x49497350\n            && iop_read32(iop, p + 4) == 0x64616F6C) {\n            found = p;\n            break;\n        }\n\n        p += 4;\n    }\n\n    /* This seems to have held true for all the versions i've seen */\n    unsigned lc_struct;\n    if (!found) {\n        lc_struct = bootmodes_ptr - 0x20;\n    } else {\n        lc_struct = p + 0x18;\n    }\n\n    return lc_struct + 0x10;\n}\n\nstatic void iop_strncpy(struct iop_state* iop, char* dest, unsigned src, int n)\n{\n    char c;\n\n    while ((c = iop_read8(iop, src)) && n) {\n        *dest = c;\n        dest++;\n        src++;\n        n--;\n    }\n}\n\nstatic void cache_loaded_modules(struct iop_state* iop, unsigned list)\n{\n    unsigned ent = iop_read32(iop, list);\n    struct iop_module* mod;\n    int count = 0;\n\n    while (ent) {\n        ent = iop_read32(iop, ent);\n        count++;\n    }\n\n    mod = calloc(count, sizeof(*mod));\n\n    ent = iop_read32(iop, list);\n    int i = 0;\n    while (ent != 0) {\n        if (iop_read32(iop, ent + 4)) {\n            iop_strncpy(iop, mod[i].name, iop_read32(iop, ent + 4), sizeof(mod[i].name));\n        } else {\n            strcpy(mod[i].name, \"-- MISSING --\");\n        }\n        mod[i].version = iop_read16(iop, ent + 8);\n        mod[i].entry = iop_read32(iop, ent + 0x10);\n        mod[i].gp = iop_read32(iop, ent + 0x14);\n        mod[i].text_addr = iop_read32(iop, ent + 0x18);\n        mod[i].text_size = iop_read32(iop, ent + 0x1c);\n        mod[i].data_size = iop_read32(iop, ent + 0x20);\n        mod[i].bss_size = iop_read32(iop, ent + 0x24);\n\n        ent = iop_read32(iop, ent);\n        i++;\n    }\n\n    iop->module_count = count;\n    iop->module_list = mod;\n}\n\nvoid refresh_module_list(struct iop_state* iop)\n{\n    struct iop_module* mod = iop->module_list;\n\n    iop->module_count = 0;\n    iop->module_list = NULL;\n    free(mod);\n\n    cache_loaded_modules(iop, iop->module_list_addr);\n}\n\nint loadcore_reg_lib_ent(struct iop_state* iop)\n{\n    unsigned list = get_module_list(iop);\n\n    iop->module_list_addr = list;\n\n    refresh_module_list(iop);\n\n    return 0;\n}\n"
  },
  {
    "path": "src/iop/hle/loadcore.h",
    "content": "#ifndef LOADCORE_H_\n#define LOADCORE_H_\n\n#include \"../iop.h\"\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\nstruct iop_module {\n    char name[64];\n    uint16_t version;\n    uint32_t text_addr;\n    uint32_t entry;\n    uint32_t gp;\n    uint32_t text_size;\n    uint32_t data_size;\n    uint32_t bss_size;\n};\n\nint loadcore_reg_lib_ent(struct iop_state* iop);\nvoid refresh_module_list(struct iop_state* iop);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif // LOADCORE_H_\n"
  },
  {
    "path": "src/iop/hle/sysmem.c",
    "content": "#include \"sysmem.h\"\n\n#define SM_PUTCHAR(c) \\\n    iop->sm_putchar(iop->sm_putchar_udata, c);\n\nint reg_index = 0;\n\nuint32_t fetch_next_param(struct iop_state* iop) {\n    return iop->r[5 + reg_index++];\n}\n\nint sysmem_kprintf(struct iop_state* iop) {\n    if (!iop->sm_putchar)\n        return 0;\n\n    int ptr = iop->r[4];\n\n    reg_index = 5;\n\n    char c = iop_read8(iop, ptr++);\n\n    while (c != 0) {\n        switch (c) {\n            case '%': {\n                int zero_pad = 0;\n                int digits = 0;\n\n                parse:\n\n                c = iop_read8(iop, ptr++);\n\n                switch (c) {\n                    case 'c': {\n                        char ch = fetch_next_param(iop) & 0xff;\n\n                        SM_PUTCHAR(ch);\n                    } break;\n\n                    case 's': {\n                        uint32_t str_addr = fetch_next_param(iop);\n                        char ch = iop_read8(iop, str_addr++);\n\n                        while (ch != 0) {\n                            SM_PUTCHAR(ch);\n\n                            ch = iop_read8(iop, str_addr++);\n                        }\n                    } break;\n\n                    case '0': {\n                        zero_pad = 1;\n\n                        goto parse;\n                    } break;\n\n                    case '1': case '2': case '3': case '4': \n                    case '5': case '6': case '7': case '8':\n                    case '9': {\n                        digits = (digits * 10) + (c - '0');\n\n                        goto parse;\n                    } break;\n\n                    case 'd': case 'u': case 'i': case 'x': case 'X':{\n                        uint32_t val = fetch_next_param(iop);\n\n                        char fmt_buf[8];\n                        char* fmt = fmt_buf;\n\n                        *fmt++ = '%';\n\n                        if (zero_pad) {\n                            *fmt++ = '0';\n                        }\n\n                        if (digits) {\n                            fmt += sprintf(fmt, \"%d\", digits);\n                        }\n\n                        *fmt++ = c;\n                        *fmt = '\\0';\n\n                        char buf[16];\n                        sprintf(buf, fmt_buf, val);\n\n                        for (char* p = buf; *p != 0; p++) {\n                            SM_PUTCHAR(*p);\n                        }\n                    } break;\n\n                    case '%': {\n                        SM_PUTCHAR('%');\n                    } break;\n\n                    default: {\n                        printf(\"sysmem_kprintf: Unknown format specifier %c\\n\", c);\n                        // Unknown format specifier, just print it as is\n                        SM_PUTCHAR('%');\n                        SM_PUTCHAR(c);\n                    } break;\n                }\n            } break;\n\n            default:\n                SM_PUTCHAR(c);\n\n                break;\n        }\n\n        c = iop_read8(iop, ptr++);\n    }\n\n    return 0;\n}"
  },
  {
    "path": "src/iop/hle/sysmem.h",
    "content": "#ifndef SYSMEM_H\n#define SYSMEM_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"../iop.h\"\n#include \"../iop_export.h\"\n\nint sysmem_kprintf(struct iop_state* iop);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/intc.c",
    "content": "#include <stdint.h>\n#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"intc.h\"\n\nstruct ps2_iop_intc* ps2_iop_intc_create(void) {\n    return malloc(sizeof(struct ps2_iop_intc));\n}\n\nvoid ps2_iop_intc_init(struct ps2_iop_intc* intc, struct iop_state* iop) {\n    memset(intc, 0, sizeof(struct ps2_iop_intc));\n\n    intc->iop = iop;\n    intc->ctrl = 1;\n}\n\nvoid ps2_iop_intc_irq(struct ps2_iop_intc* intc, int dev) {\n    intc->stat |= dev;\n\n    if (intc->ctrl && (intc->stat & intc->mask)) {\n        iop_set_irq_pending(intc->iop);\n    } else {\n        intc->iop->cop0_r[COP0_CAUSE] &= ~SR_IM2;\n    }\n}\n\nvoid ps2_iop_intc_destroy(struct ps2_iop_intc* intc) {\n    free(intc);\n}\n\nuint64_t ps2_iop_intc_read8(struct ps2_iop_intc* intc, uint32_t addr) {\n    printf(\"intc: IOP intc 8-bit read from address %08x\\n\", addr); exit(1);\n}\n\nuint64_t ps2_iop_intc_read16(struct ps2_iop_intc* intc, uint32_t addr) {\n    printf(\"intc: IOP intc 16-bit read from address %08x\\n\", addr); exit(1);\n}\n\nuint64_t ps2_iop_intc_read32(struct ps2_iop_intc* intc, uint32_t addr) {\n    uint32_t ctrl = intc->ctrl;\n\n    switch (addr) {\n        case 0x1f801070: return intc->stat;\n        case 0x1f801074: return intc->mask;\n        case 0x1f801078: intc->ctrl = 0; break;\n    }\n\n    int n = intc->ctrl && (intc->stat & intc->mask);\n\n    if (!n) {\n        intc->iop->cop0_r[COP0_CAUSE] &= ~SR_IM2;\n    } else {\n        iop_set_irq_pending(intc->iop);\n    }\n\n    return ctrl;\n}\n\nvoid ps2_iop_intc_write8(struct ps2_iop_intc* intc, uint32_t addr, uint64_t data) {\n    printf(\"iop: IOP INTC 8-bit write to address %08x (%02lx)\\n\", addr, data); exit(1);\n}\n\nvoid ps2_iop_intc_write16(struct ps2_iop_intc* intc, uint32_t addr, uint64_t data) {\n    printf(\"iop: IOP INTC 8-bit write to address %08x (%04lx)\\n\", addr, data); exit(1);\n}\n\nvoid ps2_iop_intc_write32(struct ps2_iop_intc* intc, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        case 0x1f801070: intc->stat &= data; break;\n        case 0x1f801074: intc->mask = data; break;\n        case 0x1f801078: intc->ctrl = data; break;\n    }\n\n    int n = intc->ctrl && (intc->stat & intc->mask);\n\n    if (!n) {\n        intc->iop->cop0_r[COP0_CAUSE] &= ~SR_IM2;\n    } else {\n        iop_set_irq_pending(intc->iop);\n    }\n}"
  },
  {
    "path": "src/iop/intc.h",
    "content": "#ifndef IOP_INTC_H\n#define IOP_INTC_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"iop.h\"\n\n/*\n  0     IRQ0   VBLANK start\n  1     IRQ1   GPU (used in PSX mode)\n  2     IRQ2   CDVD Drive\n  3     IRQ3   DMA\n  4     IRQ4   Timer 0\n  5     IRQ5   Timer 1\n  6     IRQ6   Timer 2\n  7     IRQ7   SIO0\n  8     IRQ8   SIO1\n  9     IRQ9   SPU2\n  10    IRQ10  PIO\n  11    IRQ11  VBLANK end\n  12    IRQ12  DVD? (unknown purpose)\n  13    IRQ13  DEV9\n  14    IRQ14  Timer 3\n  15    IRQ15  Timer 4\n  16    IRQ16  Timer 5\n  17    IRQ17  SIO2\n  18    IRQ18  HTR0? (unknown purpose)\n  19    IRQ19  HTR1?\n  20    IRQ20  HTR2?\n  21    IRQ21  HTR3?\n  22    IRQ22  USB\n  23    IRQ23  EXTR? (unknown purpose)\n  24    IRQ24  FWRE (related to FireWire)\n  25    IRQ25  FDMA? (FireWire DMA?)\n*/\n\n#define IOP_INTC_VBLANK_IN  0x00000001\n\n// Bit 1 in PS2 mode is mapped to the SBUS IRQ, in PS1 mode\n// it's mapped to the PS1 GPU\n#define IOP_INTC_SBUS       0x00000002\n#define IOP_INTC_GPU        0x00000002\n#define IOP_INTC_CDVD       0x00000004\n#define IOP_INTC_DMA        0x00000008\n#define IOP_INTC_TIMER0     0x00000010\n#define IOP_INTC_TIMER1     0x00000020\n#define IOP_INTC_TIMER2     0x00000040\n#define IOP_INTC_SIO0       0x00000080\n#define IOP_INTC_SIO1       0x00000100\n#define IOP_INTC_SPU2       0x00000200\n#define IOP_INTC_PIO        0x00000400\n#define IOP_INTC_VBLANK_OUT 0x00000800\n#define IOP_INTC_DVD        0x00001000\n#define IOP_INTC_DEV9       0x00002000\n#define IOP_INTC_TIMER3     0x00004000\n#define IOP_INTC_TIMER4     0x00008000\n#define IOP_INTC_TIMER5     0x00010000\n#define IOP_INTC_SIO2       0x00020000\n#define IOP_INTC_HTR0       0x00040000\n#define IOP_INTC_HTR1       0x00080000\n#define IOP_INTC_HTR2       0x00100000\n#define IOP_INTC_HTR3       0x00200000\n#define IOP_INTC_USB        0x00400000\n#define IOP_INTC_EXTR       0x00800000\n#define IOP_INTC_FWRE       0x01000000\n#define IOP_INTC_FDMA       0x02000000\n\nstruct ps2_iop_intc {\n    uint32_t stat;\n    uint32_t mask;\n    uint32_t ctrl;\n\n    struct iop_state* iop;\n};\n\nstruct ps2_iop_intc* ps2_iop_intc_create(void);\nvoid ps2_iop_intc_init(struct ps2_iop_intc* intc, struct iop_state* iop);\nvoid ps2_iop_intc_irq(struct ps2_iop_intc* intc, int dev);\nvoid ps2_iop_intc_destroy(struct ps2_iop_intc* intc);\nuint64_t ps2_iop_intc_read8(struct ps2_iop_intc* intc, uint32_t addr);\nuint64_t ps2_iop_intc_read16(struct ps2_iop_intc* intc, uint32_t addr);\nuint64_t ps2_iop_intc_read32(struct ps2_iop_intc* intc, uint32_t addr);\nvoid ps2_iop_intc_write8(struct ps2_iop_intc* intc, uint32_t addr, uint64_t data);\nvoid ps2_iop_intc_write16(struct ps2_iop_intc* intc, uint32_t addr, uint64_t data);\nvoid ps2_iop_intc_write32(struct ps2_iop_intc* intc, uint32_t addr, uint64_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/iop.c",
    "content": "#include <stdlib.h>\r\n#include <string.h>\r\n\r\n#include \"iop.h\"\r\n#include \"iop_dis.h\"\r\n\r\n#include \"iop_export.h\"\r\n\r\n// static int p = 0;\r\n\r\nconst uint32_t iop_bus_region_mask_table[] = {\r\n    0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff,\r\n    0x7fffffff, 0x1fffffff, 0xffffffff, 0xffffffff\r\n};\r\n\r\nstatic inline uint32_t iop_translate_addr(uint32_t addr) {\r\n    //KSEG0\r\n    if (addr >= 0x80000000 && addr < 0xA0000000)\r\n        return addr - 0x80000000;\r\n\r\n    //KSEG1\r\n    if (addr >= 0xA0000000 && addr < 0xC0000000)\r\n        return addr - 0xA0000000;\r\n\r\n    //KUSEG, KSEG2\r\n    return addr;\r\n}\r\n\r\nstatic inline uint32_t iop_bus_read8(struct iop_state* iop, uint32_t addr) {\r\n    return iop->bus.read8(iop->bus.udata, iop_translate_addr(addr));\r\n}\r\n\r\nstatic inline uint32_t iop_bus_read16(struct iop_state* iop, uint32_t addr) {\r\n    return iop->bus.read16(iop->bus.udata, iop_translate_addr(addr));\r\n}\r\n\r\nstatic inline uint32_t iop_bus_read32(struct iop_state* iop, uint32_t addr) {\r\n    return iop->bus.read32(iop->bus.udata, iop_translate_addr(addr));\r\n}\r\n\r\nstatic inline void iop_bus_write8(struct iop_state* iop, uint32_t addr, uint32_t data) {\r\n    iop->bus.write8(iop->bus.udata, iop_translate_addr(addr), data);\r\n}\r\n\r\nstatic inline void iop_bus_write16(struct iop_state* iop, uint32_t addr, uint32_t data) {\r\n    iop->bus.write16(iop->bus.udata, iop_translate_addr(addr), data);\r\n}\r\n\r\nstatic inline void iop_bus_write32(struct iop_state* iop, uint32_t addr, uint32_t data) {\r\n    iop->bus.write32(iop->bus.udata, iop_translate_addr(addr), data);\r\n}\r\n\r\n// External functions\r\nuint32_t iop_read8(struct iop_state* iop, uint32_t addr) {\r\n    return iop->bus.read8(iop->bus.udata, iop_translate_addr(addr));\r\n}\r\n\r\nuint32_t iop_read16(struct iop_state* iop, uint32_t addr) {\r\n    return iop->bus.read16(iop->bus.udata, iop_translate_addr(addr));\r\n}\r\n\r\nuint32_t iop_read32(struct iop_state* iop, uint32_t addr) {\r\n    return iop->bus.read32(iop->bus.udata, iop_translate_addr(addr));\r\n}\r\n\r\nvoid iop_write8(struct iop_state* iop, uint32_t addr, uint32_t data) {\r\n    iop->bus.write8(iop->bus.udata, iop_translate_addr(addr), data);\r\n}\r\n\r\nvoid iop_write16(struct iop_state* iop, uint32_t addr, uint32_t data) {\r\n    iop->bus.write16(iop->bus.udata, iop_translate_addr(addr), data);\r\n}\r\n\r\nvoid iop_write32(struct iop_state* iop, uint32_t addr, uint32_t data) {\r\n    iop->bus.write32(iop->bus.udata, iop_translate_addr(addr), data);\r\n}\r\n\r\nstatic const uint32_t g_iop_cop0_write_mask_table[] = {\r\n    0x00000000, // cop0r0   - N/A\r\n    0x00000000, // cop0r1   - N/A\r\n    0x00000000, // cop0r2   - N/A\r\n    0xffffffff, // BPC      - Breakpoint on execute (R/W)\r\n    0x00000000, // cop0r4   - N/A\r\n    0xffffffff, // BDA      - Breakpoint on data access (R/W)\r\n    0x00000000, // JUMPDEST - Randomly memorized jump address (R)\r\n    0xffc0f03f, // DCIC     - Breakpoint control (R/W)\r\n    0x00000000, // BadVaddr - Bad Virtual Address (R)\r\n    0xffffffff, // BDAM     - Data Access breakpoint mask (R/W)\r\n    0x00000000, // cop0r10  - N/A\r\n    0xffffffff, // BPCM     - Execute breakpoint mask (R/W)\r\n    0xffffffff, // SR       - System status register (R/W)\r\n    0x00000300, // CAUSE    - Describes the most recently recognised exception (R)\r\n    0x00000000, // EPC      - Return Address from Trap (R)\r\n    0x00000000  // PRID     - Processor ID (R)\r\n};\r\n\r\n#define OP ((iop->opcode >> 26) & 0x3f)\r\n#define S ((iop->opcode >> 21) & 0x1f)\r\n#define T ((iop->opcode >> 16) & 0x1f)\r\n#define D ((iop->opcode >> 11) & 0x1f)\r\n#define IMM5 ((iop->opcode >> 6) & 0x1f)\r\n#define CMT ((iop->opcode >> 6) & 0xfffff)\r\n#define SOP (iop->opcode & 0x3f)\r\n#define IMM26 (iop->opcode & 0x3ffffff)\r\n#define IMM16 (iop->opcode & 0xffff)\r\n#define IMM16S ((int32_t)((int16_t)IMM16))\r\n\r\n#define R_R0 (iop->r[0])\r\n#define R_A0 (iop->r[4])\r\n#define R_RA (iop->r[31])\r\n\r\n#define DO_PENDING_LOAD { \\\r\n    iop->r[iop->load_d] = iop->load_v; \\\r\n    R_R0 = 0; \\\r\n    iop->load_v = 0xffffffff; \\\r\n    iop->load_d = 0; }\r\n\r\n#define SE8(v) ((int32_t)((int8_t)v))\r\n#define SE16(v) ((int32_t)((int16_t)v))\r\n\r\n#define BRANCH(offset) { \\\r\n    iop->next_pc = iop->next_pc + (offset); \\\r\n    iop->next_pc = iop->next_pc - 4; \\\r\n    iop->branch = 1; \\\r\n    iop->branch_taken = 1; }\r\n\r\nstruct iop_state* iop_create(void) {\r\n    return (struct iop_state*)malloc(sizeof(struct iop_state));\r\n}\r\n\r\nvoid iop_destroy(struct iop_state* iop) {\r\n    free(iop);\r\n}\r\n\r\nvoid iop_init(struct iop_state* iop, struct iop_bus_s bus) {\r\n    memset(iop, 0, sizeof(struct iop_state));\r\n\r\n    iop->bus = bus;\r\n    iop->pc = 0xbfc00000;\r\n    iop->next_pc = iop->pc + 4;\r\n\r\n    iop->cop0_r[COP0_SR] = 0x10900000;\r\n    iop->cop0_r[COP0_PRID] = 0x0000001f;\r\n}\r\n\r\nvoid iop_init_kputchar(struct iop_state* iop, void (*kputchar)(void*, char), void* udata) {\r\n    iop->kputchar = kputchar;\r\n    iop->kputchar_udata = udata;\r\n}\r\n\r\nvoid iop_init_sm_putchar(struct iop_state* iop, void (*sm_putchar)(void*, char), void* udata) {\r\n    iop->sm_putchar = sm_putchar;\r\n    iop->sm_putchar_udata = udata;\r\n}\r\n\r\nstatic inline int iop_check_irq(struct iop_state* iop) {\r\n    return (iop->cop0_r[COP0_SR] & SR_IEC) &&\r\n           (iop->cop0_r[COP0_SR] & iop->cop0_r[COP0_CAUSE] & 0x00000400);\r\n}\r\n\r\nstatic inline void iop_print_disassembly(struct iop_state* iop) {\r\n    char buf[128];\r\n    struct iop_dis_state state;\r\n\r\n    state.print_address = 1;\r\n    state.print_opcode = 1;\r\n    state.addr = iop->pc;\r\n\r\n    puts(iop_disassemble(buf, iop->opcode, &state));\r\n}\r\n\r\nstatic inline void iop_exception(struct iop_state* iop, uint32_t cause) {\r\n    if ((cause != CAUSE_SYSCALL) && (cause != CAUSE_INT))\r\n        printf(\"iop: Crashed with cause %02x at pc=%08x next=%08x saved=%08x\\n\", cause >> 2, iop->pc, iop->saved_pc, iop->saved_pc);\r\n\r\n    // Set excode and clear 3 LSBs\r\n    iop->cop0_r[COP0_CAUSE] &= 0xffffff80;\r\n    iop->cop0_r[COP0_CAUSE] |= cause;\r\n\r\n    iop->cop0_r[COP0_EPC] = iop->saved_pc;\r\n\r\n    if (iop->delay_slot) {\r\n        iop->cop0_r[COP0_EPC] -= 4;\r\n        iop->cop0_r[COP0_CAUSE] |= 0x80000000;\r\n    }\r\n\r\n    // Do exception stack push\r\n    uint32_t mode = iop->cop0_r[COP0_SR] & 0x3f;\r\n\r\n    iop->cop0_r[COP0_SR] &= 0xffffffc0;\r\n    iop->cop0_r[COP0_SR] |= (mode << 2) & 0x3f;\r\n\r\n    // Set PC to the vector selected on BEV\r\n    iop->pc = (iop->cop0_r[COP0_SR] & SR_BEV) ? 0xbfc00180 : 0x80000080;\r\n\r\n    iop->next_pc = iop->pc + 4;\r\n}\r\n\r\nvoid iop_cycle(struct iop_state* iop) {\r\n    iop->last_cycles = 0;\r\n\r\n    iop->saved_pc = iop->pc;\r\n    iop->delay_slot = iop->branch;\r\n    iop->branch = 0;\r\n    iop->branch_taken = 0;\r\n\r\n    if (iop->saved_pc & 3)\r\n        iop_exception(iop, CAUSE_ADEL);\r\n\r\n    iop->opcode = iop_bus_read32(iop, iop->pc);\r\n    iop->last_cycles = 0;\r\n\r\n    if (iop->p) {\r\n        iop_print_disassembly(iop);\r\n\r\n        --iop->p;\r\n    }\r\n\r\n    iop->pc = iop->next_pc;\r\n    iop->next_pc += 4;\r\n\r\n    if (iop_check_irq(iop)) {\r\n        iop->r[0] = 0;\r\n\r\n        // printf(\"iop: irq pc=%08x next_pc=%08x saved_pc=%08x\\n\", iop->pc, iop->next_pc, iop->saved_pc);\r\n\r\n        iop_exception(iop, CAUSE_INT);\r\n\r\n        return;\r\n    }\r\n\r\n    int cyc = iop_execute(iop);\r\n\r\n    if (!cyc) {\r\n        printf(\"iop: Illegal instruction %08x at %08x (next=%08x, saved=%08x)\\n\", iop->opcode, iop->pc, iop->next_pc, iop->saved_pc);\r\n\r\n        iop_exception(iop, CAUSE_RI);\r\n    }\r\n\r\n    iop->last_cycles += cyc;\r\n    iop->total_cycles += iop->last_cycles;\r\n\r\n    iop->r[0] = 0;\r\n}\r\n\r\nvoid iop_reset(struct iop_state* iop) {\r\n    for (int i = 0; i < 32; i++)\r\n        iop->r[i] = 0;\r\n\r\n    for (int i = 0; i < 16; i++)\r\n        iop->cop0_r[i] = 0;\r\n\r\n    iop->pc = 0xbfc00000;\r\n    iop->next_pc = iop->pc + 4;\r\n\r\n    iop->cop0_r[COP0_SR] = 0x10900000;\r\n    iop->cop0_r[COP0_PRID] = 0x0000001f;\r\n\r\n    iop->opcode = 0;\r\n    iop->hi = 0;\r\n    iop->lo = 0;\r\n    iop->load_d = 0;\r\n    iop->load_v = 0;\r\n    iop->last_cycles = 0;\r\n    iop->total_cycles = 0;\r\n    iop->biu_config = 0;\r\n    iop->branch = 0;\r\n    iop->delay_slot = 0;\r\n    iop->branch_taken = 0;\r\n}\r\n\r\nvoid iop_set_irq_pending(struct iop_state* iop) {\r\n    iop->cop0_r[COP0_CAUSE] |= SR_IM2;\r\n}\r\n\r\nstatic inline void iop_i_invalid(struct iop_state* iop) {\r\n    printf(\"%08x: Illegal instruction %08x\", iop->pc - 8, iop->opcode);\r\n\r\n    iop_exception(iop, CAUSE_RI);\r\n}\r\n\r\nstatic inline void iop_i_bltz(struct iop_state* iop) {\r\n    int32_t s = (int32_t)iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    if ((int32_t)s < (int32_t)0)\r\n        BRANCH(IMM16S << 2);\r\n}\r\n\r\nstatic inline void iop_i_bgez(struct iop_state* iop) {\r\n    int32_t s = (int32_t)iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    if ((int32_t)s >= (int32_t)0)\r\n        BRANCH(IMM16S << 2);\r\n}\r\n\r\nstatic inline void iop_i_bltzal(struct iop_state* iop) {\r\n    int32_t s = (int32_t)iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    R_RA = iop->next_pc;\r\n\r\n    if ((int32_t)s < (int32_t)0)\r\n        BRANCH(IMM16S << 2);\r\n}\r\n\r\nstatic inline void iop_i_bgezal(struct iop_state* iop) {\r\n    int32_t s = (int32_t)iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    R_RA = iop->next_pc;\r\n\r\n    if ((int32_t)s >= (int32_t)0)\r\n        BRANCH(IMM16S << 2);\r\n}\r\n\r\nstatic inline void iop_i_j(struct iop_state* iop) {\r\n    iop->branch = 1;\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    // If we get a 1 that means the call has been HLE'd\r\n    if (iop_test_module_hooks(iop))\r\n        return;\r\n\r\n    iop->next_pc = (iop->next_pc & 0xf0000000) | (IMM26 << 2);\r\n}\r\n\r\nstatic inline void iop_i_jal(struct iop_state* iop) {\r\n    iop->branch = 1;\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    R_RA = iop->next_pc;\r\n\r\n    iop->next_pc = (iop->next_pc & 0xf0000000) | (IMM26 << 2);\r\n}\r\n\r\nstatic inline void iop_i_beq(struct iop_state* iop) {\r\n    iop->branch = 1;\r\n    iop->branch_taken = 0;\r\n\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    if (s == t)\r\n        BRANCH(IMM16S << 2);\r\n}\r\n\r\nstatic inline void iop_i_bne(struct iop_state* iop) {\r\n    iop->branch = 1;\r\n    iop->branch_taken = 0;\r\n\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    if (s != t)\r\n        BRANCH(IMM16S << 2);\r\n}\r\n\r\nstatic inline void iop_i_blez(struct iop_state* iop) {\r\n    iop->branch = 1;\r\n    iop->branch_taken = 0;\r\n\r\n    int32_t s = (int32_t)iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    if ((int32_t)s <= (int32_t)0)\r\n        BRANCH(IMM16S << 2);\r\n}\r\n\r\nstatic inline void iop_i_bgtz(struct iop_state* iop) {\r\n    iop->branch = 1;\r\n    iop->branch_taken = 0;\r\n\r\n    int32_t s = (int32_t)iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    if ((int32_t)s > (int32_t)0)\r\n        BRANCH(IMM16S << 2);\r\n}\r\n\r\nstatic inline void iop_i_addi(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    uint32_t i = IMM16S;\r\n    uint32_t r = s + i;\r\n    uint32_t o = (s ^ r) & (i ^ r);\r\n\r\n    if (o & 0x80000000) {\r\n        iop_exception(iop, CAUSE_OV);\r\n    } else {\r\n        iop->r[T] = r;\r\n    }\r\n}\r\n\r\nstatic inline void iop_i_addiu(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[T] = s + IMM16S;\r\n}\r\n\r\nstatic inline void iop_i_slti(struct iop_state* iop) {\r\n    int32_t s = (int32_t)iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[T] = s < IMM16S;\r\n}\r\n\r\nstatic inline void iop_i_sltiu(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[T] = s < IMM16S;\r\n}\r\n\r\nstatic inline void iop_i_andi(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[T] = s & IMM16;\r\n}\r\n\r\nstatic inline void iop_i_ori(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[T] = s | IMM16;\r\n}\r\n\r\nstatic inline void iop_i_xori(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[T] = s ^ IMM16;\r\n}\r\n\r\nstatic inline void iop_i_lui(struct iop_state* iop) {\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[T] = IMM16 << 16;\r\n}\r\n\r\nstatic inline void iop_i_lb(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n\r\n    if (iop->load_d != T)\r\n        DO_PENDING_LOAD;\r\n\r\n    iop->load_d = T;\r\n    iop->load_v = SE8(iop_bus_read8(iop, s + IMM16S));\r\n}\r\n\r\nstatic inline void iop_i_lh(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n\r\n    if (iop->load_d != T)\r\n        DO_PENDING_LOAD;\r\n\r\n    uint32_t addr = s + IMM16S;\r\n\r\n    if (addr & 0x1) {\r\n        iop_exception(iop, CAUSE_ADEL);\r\n    } else {\r\n        iop->load_d = T;\r\n        iop->load_v = SE16(iop_bus_read16(iop, addr));\r\n    }\r\n}\r\n\r\nstatic inline void iop_i_lwl(struct iop_state* iop) {\r\n    uint32_t rt = T;\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[rt];\r\n\r\n    uint32_t addr = s + IMM16S;\r\n    uint32_t load = iop_bus_read32(iop, addr & 0xfffffffc);\r\n\r\n    if (rt == iop->load_d) {\r\n        t = iop->load_v;\r\n    } else {\r\n        DO_PENDING_LOAD;\r\n    }\r\n\r\n    int shift = (int)((addr & 0x3) << 3);\r\n    uint32_t mask = (uint32_t)0x00FFFFFF >> shift;\r\n    uint32_t value = (t & mask) | (load << (24 - shift)); \r\n\r\n    iop->load_d = rt;\r\n    iop->load_v = value;\r\n\r\n    // printf(\"lwl rt=%u s=%08x t=%08x addr=%08x load=%08x (%08x) shift=%u mask=%08x value=%08x\\n\",\r\n    //     rt, s, t, addr, load, addr & 0xfffffffc, shift, mask, value\r\n    // );\r\n}\r\n\r\nstatic inline void iop_i_lw(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t addr = s + IMM16S;\r\n\r\n    if (iop->load_d != T)\r\n        DO_PENDING_LOAD;\r\n\r\n    if (addr & 0x3) {\r\n        iop_exception(iop, CAUSE_ADEL);\r\n    } else {\r\n        iop->load_d = T;\r\n        iop->load_v = iop_bus_read32(iop, addr);\r\n    }\r\n}\r\n\r\nstatic inline void iop_i_lbu(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n\r\n    if (iop->load_d != T)\r\n        DO_PENDING_LOAD;\r\n\r\n    iop->load_d = T;\r\n    iop->load_v = iop_bus_read8(iop, s + IMM16S);\r\n}\r\n\r\nstatic inline void iop_i_lhu(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t addr = s + IMM16S;\r\n\r\n    if (iop->load_d != T)\r\n        DO_PENDING_LOAD;\r\n\r\n    if (addr & 0x1) {\r\n        iop_exception(iop, CAUSE_ADEL);\r\n    } else {\r\n        iop->load_d = T;\r\n        iop->load_v = iop_bus_read16(iop, addr);\r\n    }\r\n}\r\n\r\nstatic inline void iop_i_lwr(struct iop_state* iop) {\r\n    uint32_t rt = T;\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[rt];\r\n\r\n    uint32_t addr = s + IMM16S;\r\n    uint32_t load = iop_bus_read32(iop, addr & 0xfffffffc);\r\n\r\n    if (rt == iop->load_d) {\r\n        t = iop->load_v;\r\n    } else {\r\n        DO_PENDING_LOAD;\r\n    }\r\n\r\n    int shift = (int)((addr & 0x3) << 3);\r\n    uint32_t mask = 0xFFFFFF00 << (24 - shift);\r\n    uint32_t value = (t & mask) | (load >> shift); \r\n\r\n    iop->load_d = rt;\r\n    iop->load_v = value;\r\n\r\n    // printf(\"lwr rt=%u s=%08x t=%08x addr=%08x load=%08x (%08x) shift=%u mask=%08x value=%08x\\n\",\r\n    //     rt, s, t, addr, load, addr & 0xfffffffc, shift, mask, value\r\n    // );\r\n}\r\n\r\nstatic inline void iop_i_sb(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    // Cache isolated\r\n    if (iop->cop0_r[COP0_SR] & SR_ISC) {\r\n        return;\r\n    }\r\n\r\n    iop_bus_write8(iop, s + IMM16S, t);\r\n}\r\n\r\nstatic inline void iop_i_sh(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n    uint32_t addr = s + IMM16S;\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    // Cache isolated\r\n    if (iop->cop0_r[COP0_SR] & SR_ISC) {\r\n        return;\r\n    }\r\n\r\n    if (addr & 0x1) {\r\n        iop_exception(iop, CAUSE_ADES);\r\n    } else {\r\n        iop_bus_write16(iop, addr, t);\r\n    }\r\n}\r\n\r\nstatic inline void iop_i_swl(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    uint32_t addr = s + IMM16S;\r\n    uint32_t aligned = addr & 0xfffffffc;\r\n    uint32_t v = iop_bus_read32(iop, aligned);\r\n\r\n    switch (addr & 0x3) {\r\n        case 0: v = (v & 0xffffff00) | (iop->r[T] >> 24); break;\r\n        case 1: v = (v & 0xffff0000) | (iop->r[T] >> 16); break;\r\n        case 2: v = (v & 0xff000000) | (iop->r[T] >> 8 ); break;\r\n        case 3: v =                     iop->r[T]       ; break;\r\n    }\r\n\r\n    iop_bus_write32(iop, aligned, v);\r\n}\r\n\r\nstatic inline void iop_i_sw(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n    uint32_t addr = s + IMM16S;\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    // Cache isolated\r\n    if (iop->cop0_r[COP0_SR] & SR_ISC) {\r\n        return;\r\n    }\r\n\r\n    if (addr & 0x3) {\r\n        iop_exception(iop, CAUSE_ADES);\r\n    } else {\r\n        if (addr == 0xfffe0130) {\r\n            iop->biu_config = t;\r\n\r\n            return;\r\n        }\r\n\r\n        iop_bus_write32(iop, addr, t);\r\n    }\r\n}\r\n\r\nstatic inline void iop_i_swr(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    uint32_t addr = s + IMM16S;\r\n    uint32_t aligned = addr & 0xfffffffc;\r\n    uint32_t v = iop_bus_read32(iop, aligned);\r\n\r\n    switch (addr & 0x3) {\r\n        case 0: v =                     iop->r[T]       ; break;\r\n        case 1: v = (v & 0x000000ff) | (iop->r[T] << 8 ); break;\r\n        case 2: v = (v & 0x0000ffff) | (iop->r[T] << 16); break;\r\n        case 3: v = (v & 0x00ffffff) | (iop->r[T] << 24); break;\r\n    }\r\n\r\n    iop_bus_write32(iop, aligned, v);\r\n}\r\n\r\nstatic inline void iop_i_lwc0(struct iop_state* iop) {\r\n    iop_exception(iop, CAUSE_CPU);\r\n}\r\n\r\nstatic inline void iop_i_lwc1(struct iop_state* iop) {\r\n    iop_exception(iop, CAUSE_CPU);\r\n}\r\n\r\nstatic inline void iop_i_lwc2(struct iop_state* iop) {\r\n    iop_exception(iop, CAUSE_CPU);\r\n}\r\n\r\nstatic inline void iop_i_lwc3(struct iop_state* iop) {\r\n    iop_exception(iop, CAUSE_CPU);\r\n}\r\n\r\nstatic inline void iop_i_swc0(struct iop_state* iop) {\r\n    iop_exception(iop, CAUSE_CPU);\r\n}\r\n\r\nstatic inline void iop_i_swc1(struct iop_state* iop) {\r\n    iop_exception(iop, CAUSE_CPU);\r\n}\r\n\r\nstatic inline void iop_i_swc2(struct iop_state* iop) {\r\n    iop_exception(iop, CAUSE_CPU);\r\n}\r\n\r\nstatic inline void iop_i_swc3(struct iop_state* iop) {\r\n    iop_exception(iop, CAUSE_CPU);\r\n}\r\n\r\n// Secondary\r\nstatic inline void iop_i_sll(struct iop_state* iop) {\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = t << IMM5;\r\n}\r\n\r\nstatic inline void iop_i_srl(struct iop_state* iop) {\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = t >> IMM5;\r\n}\r\n\r\nstatic inline void iop_i_sra(struct iop_state* iop) {\r\n    int32_t t = (int32_t)iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = t >> IMM5;\r\n}\r\n\r\nstatic inline void iop_i_sllv(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = t << (s & 0x1f);\r\n}\r\n\r\nstatic inline void iop_i_srlv(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = t >> (s & 0x1f);\r\n}\r\n\r\nstatic inline void iop_i_srav(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    int32_t t = (int32_t)iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = t >> (s & 0x1f);\r\n}\r\n\r\nstatic inline void iop_i_jr(struct iop_state* iop) {\r\n    iop->branch = 1;\r\n\r\n    uint32_t s = iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->next_pc = s;\r\n}\r\n\r\nstatic inline void iop_i_jalr(struct iop_state* iop) {\r\n    iop->branch = 1;\r\n\r\n    uint32_t s = iop->r[S];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = iop->next_pc;\r\n\r\n    iop->next_pc = s;\r\n}\r\n\r\nstatic inline void iop_i_syscall(struct iop_state* iop) {\r\n    DO_PENDING_LOAD;\r\n\r\n    iop_exception(iop, CAUSE_SYSCALL);\r\n}\r\n\r\nstatic inline void iop_i_break(struct iop_state* iop) {\r\n    DO_PENDING_LOAD;\r\n\r\n    // iop_exception(iop, CAUSE_BP);\r\n}\r\n\r\nstatic inline void iop_i_mfhi(struct iop_state* iop) {\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = iop->hi;\r\n}\r\n\r\nstatic inline void iop_i_mthi(struct iop_state* iop) {\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->hi = iop->r[S];\r\n}\r\n\r\nstatic inline void iop_i_mflo(struct iop_state* iop) {\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = iop->lo;\r\n}\r\n\r\nstatic inline void iop_i_mtlo(struct iop_state* iop) {\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->lo = iop->r[S];\r\n}\r\n\r\nstatic inline void iop_i_mult(struct iop_state* iop) {\r\n    int64_t s = (int64_t)((int32_t)iop->r[S]);\r\n    int64_t t = (int64_t)((int32_t)iop->r[T]);\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    uint64_t r = s * t;\r\n\r\n    iop->hi = r >> 32;\r\n    iop->lo = r & 0xffffffff;\r\n}\r\n\r\nstatic inline void iop_i_multu(struct iop_state* iop) {\r\n    uint64_t s = (uint64_t)iop->r[S];\r\n    uint64_t t = (uint64_t)iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    uint64_t r = s * t;\r\n\r\n    iop->hi = r >> 32;\r\n    iop->lo = r & 0xffffffff;\r\n}\r\n\r\nstatic inline void iop_i_div(struct iop_state* iop) {\r\n    int32_t s = (int32_t)iop->r[S];\r\n    int32_t t = (int32_t)iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    if (!t) {\r\n        iop->hi = s;\r\n        iop->lo = (s >= 0) ? 0xffffffff : 1;\r\n    } else if ((((uint32_t)s) == 0x80000000) && (t == -1)) {\r\n        iop->hi = 0;\r\n        iop->lo = 0x80000000;\r\n    } else {\r\n        iop->hi = (uint32_t)(s % t);\r\n        iop->lo = (uint32_t)(s / t);\r\n    }\r\n}\r\n\r\nstatic inline void iop_i_divu(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    if (!t) {\r\n        iop->hi = s;\r\n        iop->lo = 0xffffffff;\r\n    } else {\r\n        iop->hi = s % t;\r\n        iop->lo = s / t;\r\n    }\r\n}\r\n\r\nstatic inline void iop_i_add(struct iop_state* iop) {\r\n    int32_t s = iop->r[S];\r\n    int32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    int32_t r = s + t;\r\n    uint32_t o = (s ^ r) & (t ^ r);\r\n\r\n    if (o & 0x80000000) {\r\n        iop_exception(iop, CAUSE_OV);\r\n    } else {\r\n        iop->r[D] = (uint32_t)r;\r\n    }\r\n}\r\n\r\nstatic inline void iop_i_addu(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = s + t;\r\n}\r\n\r\nstatic inline void iop_i_sub(struct iop_state* iop) {\r\n    int32_t s = (int32_t)iop->r[S];\r\n    int32_t t = (int32_t)iop->r[T];\r\n    int32_t r;\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    int o = __builtin_ssub_overflow(s, t, &r);\r\n\r\n    if (o) {\r\n        iop_exception(iop, CAUSE_OV);\r\n    } else {\r\n        iop->r[D] = r;\r\n    }\r\n}\r\n\r\nstatic inline void iop_i_subu(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = s - t;\r\n}\r\n\r\nstatic inline void iop_i_and(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = s & t;\r\n}\r\n\r\nstatic inline void iop_i_or(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = s | t;\r\n}\r\n\r\nstatic inline void iop_i_xor(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = (s ^ t);\r\n}\r\n\r\nstatic inline void iop_i_nor(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = ~(s | t);\r\n}\r\n\r\nstatic inline void iop_i_slt(struct iop_state* iop) {\r\n    int32_t s = (int32_t)iop->r[S];\r\n    int32_t t = (int32_t)iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = s < t;\r\n}\r\n\r\nstatic inline void iop_i_sltu(struct iop_state* iop) {\r\n    uint32_t s = iop->r[S];\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->r[D] = s < t;\r\n}\r\n\r\n// COP0\r\nstatic inline void iop_i_mfc0(struct iop_state* iop) {\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->load_v = iop->cop0_r[D];\r\n    iop->load_d = T;\r\n}\r\n\r\nstatic inline void iop_i_mtc0(struct iop_state* iop) {\r\n    uint32_t t = iop->r[T];\r\n\r\n    DO_PENDING_LOAD;\r\n\r\n    iop->cop0_r[D] = t & g_iop_cop0_write_mask_table[D];\r\n}\r\n\r\nstatic inline void iop_i_rfe(struct iop_state* iop) {\r\n    DO_PENDING_LOAD;\r\n\r\n    uint32_t mode = iop->cop0_r[COP0_SR] & 0x3f;\r\n\r\n    iop->cop0_r[COP0_SR] &= 0xfffffff0;\r\n    iop->cop0_r[COP0_SR] |= mode >> 2;\r\n}\r\n\r\nint iop_execute(struct iop_state* iop) {\r\n    switch ((iop->opcode & 0xfc000000) >> 26) {\r\n        case 0x00000000 >> 26: {\r\n            switch (iop->opcode & 0x0000003f) {\r\n                case 0x00000000: iop_i_sll(iop); return 2;\r\n                case 0x00000002: iop_i_srl(iop); return 2;\r\n                case 0x00000003: iop_i_sra(iop); return 2;\r\n                case 0x00000004: iop_i_sllv(iop); return 2;\r\n                case 0x00000006: iop_i_srlv(iop); return 2;\r\n                case 0x00000007: iop_i_srav(iop); return 2;\r\n                case 0x00000008: iop_i_jr(iop); return 2;\r\n                case 0x00000009: iop_i_jalr(iop); return 2;\r\n                case 0x0000000c: iop_i_syscall(iop); return 2;\r\n                case 0x0000000d: iop_i_break(iop); return 2;\r\n                case 0x00000010: iop_i_mfhi(iop); return 2;\r\n                case 0x00000011: iop_i_mthi(iop); return 2;\r\n                case 0x00000012: iop_i_mflo(iop); return 2;\r\n                case 0x00000013: iop_i_mtlo(iop); return 2;\r\n                case 0x00000018: iop_i_mult(iop); return 2;\r\n                case 0x00000019: iop_i_multu(iop); return 2;\r\n                case 0x0000001a: iop_i_div(iop); return 2;\r\n                case 0x0000001b: iop_i_divu(iop); return 2;\r\n                case 0x00000020: iop_i_add(iop); return 2;\r\n                case 0x00000021: iop_i_addu(iop); return 2;\r\n                case 0x00000022: iop_i_sub(iop); return 2;\r\n                case 0x00000023: iop_i_subu(iop); return 2;\r\n                case 0x00000024: iop_i_and(iop); return 2;\r\n                case 0x00000025: iop_i_or(iop); return 2;\r\n                case 0x00000026: iop_i_xor(iop); return 2;\r\n                case 0x00000027: iop_i_nor(iop); return 2;\r\n                case 0x0000002a: iop_i_slt(iop); return 2;\r\n                case 0x0000002b: iop_i_sltu(iop); return 2;\r\n            } break;\r\n        } break;\r\n        case 0x04000000 >> 26: {\r\n            iop->branch = 1;\r\n            iop->branch_taken = 0;\r\n\r\n            switch ((iop->opcode & 0x001f0000) >> 16) {\r\n                case 0x00000000 >> 16: iop_i_bltz(iop); return 2;\r\n                case 0x00010000 >> 16: iop_i_bgez(iop); return 2;\r\n                case 0x00100000 >> 16: iop_i_bltzal(iop); return 2;\r\n                case 0x00110000 >> 16: iop_i_bgezal(iop); return 2;\r\n                // bltz/bgez dupes\r\n                default: {\r\n                    if (iop->opcode & 0x00010000) {\r\n                        iop_i_bgez(iop);\r\n                    } else {\r\n                        iop_i_bltz(iop);\r\n                    }\r\n                } return 2;\r\n            } break;\r\n        } break;\r\n        case 0x08000000 >> 26: iop_i_j(iop); return 2;\r\n        case 0x0c000000 >> 26: iop_i_jal(iop); return 2;\r\n        case 0x10000000 >> 26: iop_i_beq(iop); return 2;\r\n        case 0x14000000 >> 26: iop_i_bne(iop); return 2;\r\n        case 0x18000000 >> 26: iop_i_blez(iop); return 2;\r\n        case 0x1c000000 >> 26: iop_i_bgtz(iop); return 2;\r\n        case 0x20000000 >> 26: iop_i_addi(iop); return 2;\r\n        case 0x24000000 >> 26: iop_i_addiu(iop); return 2;\r\n        case 0x28000000 >> 26: iop_i_slti(iop); return 2;\r\n        case 0x2c000000 >> 26: iop_i_sltiu(iop); return 2;\r\n        case 0x30000000 >> 26: iop_i_andi(iop); return 2;\r\n        case 0x34000000 >> 26: iop_i_ori(iop); return 2;\r\n        case 0x38000000 >> 26: iop_i_xori(iop); return 2;\r\n        case 0x3c000000 >> 26: iop_i_lui(iop); return 2;\r\n        case 0x40000000 >> 26: {\r\n            switch ((iop->opcode & 0x03e00000) >> 21) {\r\n                case 0x00000000 >> 21: iop_i_mfc0(iop); return 2;\r\n                case 0x00800000 >> 21: iop_i_mtc0(iop); return 2;\r\n                case 0x02000000 >> 21: iop_i_rfe(iop); return 2;\r\n            }\r\n        } break;\r\n        case 0x48000000 >> 26: iop_i_invalid(iop); return 2;\r\n        case 0x80000000 >> 26: iop_i_lb(iop); return 2;\r\n        case 0x84000000 >> 26: iop_i_lh(iop); return 2;\r\n        case 0x88000000 >> 26: iop_i_lwl(iop); return 2;\r\n        case 0x8c000000 >> 26: iop_i_lw(iop); return 2;\r\n        case 0x90000000 >> 26: iop_i_lbu(iop); return 2;\r\n        case 0x94000000 >> 26: iop_i_lhu(iop); return 2;\r\n        case 0x98000000 >> 26: iop_i_lwr(iop); return 2;\r\n        case 0xa0000000 >> 26: iop_i_sb(iop); return 2;\r\n        case 0xa4000000 >> 26: iop_i_sh(iop); return 2;\r\n        case 0xa8000000 >> 26: iop_i_swl(iop); return 2;\r\n        case 0xac000000 >> 26: iop_i_sw(iop); return 2;\r\n        case 0xb8000000 >> 26: iop_i_swr(iop); return 2;\r\n        case 0xc0000000 >> 26: iop_i_lwc0(iop); return 2;\r\n        case 0xc4000000 >> 26: iop_i_lwc1(iop); return 2;\r\n        case 0xc8000000 >> 26: iop_i_lwc2(iop); return 2;\r\n        case 0xcc000000 >> 26: iop_i_lwc3(iop); return 2;\r\n        case 0xe0000000 >> 26: iop_i_swc0(iop); return 2;\r\n        case 0xe4000000 >> 26: iop_i_swc1(iop); return 2;\r\n        case 0xe8000000 >> 26: iop_i_swc2(iop); return 2;\r\n        case 0xec000000 >> 26: iop_i_swc3(iop); return 2;\r\n    }\r\n\r\n    return 0;\r\n}\r\n\r\n#undef R_R0\r\n#undef R_A0\r\n#undef R_RA\r\n\r\n#undef OP\r\n#undef S\r\n#undef T\r\n#undef D\r\n#undef IMM5\r\n#undef CMT\r\n#undef SOP\r\n#undef IMM26\r\n#undef IMM16\r\n#undef IMM16S\r\n\r\n#undef DO_PENDING_LOAD\r\n\r\n#undef DEBUG_ALL\r\n\r\n#undef SE8\r\n#undef SE16"
  },
  {
    "path": "src/iop/iop.h",
    "content": "#ifndef IOP_H\r\n#define IOP_H\r\n\r\n#ifdef __cplusplus\r\nextern \"C\" {\r\n#endif\r\n\r\n#include <stdint.h>\r\n#include <stdio.h>\r\n\r\n#define COP0_BPC      3\r\n#define COP0_BDA      5\r\n#define COP0_JUMPDEST 6\r\n#define COP0_DCIC     7\r\n#define COP0_BADVADDR 8\r\n#define COP0_BDAM     9\r\n#define COP0_BPCM     11\r\n#define COP0_SR       12\r\n#define COP0_CAUSE    13\r\n#define COP0_EPC      14\r\n#define COP0_PRID     15\r\n\r\n/*\r\n  Name       Alias    Common Usage\r\n  R0         zero     Constant (always 0)\r\n  R1         at       Assembler temporary (destroyed by some assembler pseudoinstructions!)\r\n  R2-R3      v0-v1    Subroutine return values, may be changed by subroutines\r\n  R4-R7      a0-a3    Subroutine arguments, may be changed by subroutines\r\n  R8-R15     t0-t7    Temporaries, may be changed by subroutines\r\n  R16-R23    s0-s7    Static variables, must be saved by subs\r\n  R24-R25    t8-t9    Temporaries, may be changed by subroutines\r\n  R26-R27    k0-k1    Reserved for kernel (destroyed by some IRQ handlers!)\r\n  R28        gp       Global pointer (rarely used)\r\n  R29        sp       Stack pointer\r\n  R30        fp(s8)   Frame Pointer, or 9th Static variable, must be saved\r\n  R31        ra       Return address (used so by JAL,BLTZAL,BGEZAL opcodes)\r\n  -          pc       Program counter\r\n  -          hi,lo    Multiply/divide results, may be changed by subroutines\r\n*/\r\n\r\nstruct iop_bus_s {\r\n    void* udata;\r\n    uint32_t (*read8)(void* udata, uint32_t addr);\r\n    uint32_t (*read16)(void* udata, uint32_t addr);\r\n    uint32_t (*read32)(void* udata, uint32_t addr);\r\n    void (*write8)(void* udata, uint32_t addr, uint32_t data);\r\n    void (*write16)(void* udata, uint32_t addr, uint32_t data);\r\n    void (*write32)(void* udata, uint32_t addr, uint32_t data);\r\n};\r\n\r\nstruct iop_state {\r\n    struct iop_bus_s bus;\r\n\r\n    uint32_t r[32];\r\n    uint32_t opcode;\r\n    uint32_t pc, next_pc, saved_pc;\r\n    uint32_t hi, lo;\r\n    uint32_t load_d, load_v;\r\n    uint32_t last_cycles;\r\n    uint32_t total_cycles;\r\n    uint32_t biu_config;\r\n    int branch, delay_slot, branch_taken;\r\n\r\n    void (*kputchar)(void*, char);\r\n    void* kputchar_udata;\r\n    void (*sm_putchar)(void*, char);\r\n    void* sm_putchar_udata;\r\n\r\n    uint32_t cop0_r[16];\r\n\r\n    int p;\r\n\r\n    uint32_t module_list_addr;\r\n\r\n    /* cache module list */\r\n    int module_count;\r\n    struct iop_module *module_list;\r\n};\r\n\r\n/*\r\n  0     IEc Current Interrupt Enable  (0=Disable, 1=Enable) ;rfe pops IUp here\r\n  1     KUc Current Kernel/User Mode  (0=Kernel, 1=User)    ;rfe pops KUp here\r\n  2     IEp Previous Interrupt Disable                      ;rfe pops IUo here\r\n  3     KUp Previous Kernel/User Mode                       ;rfe pops KUo here\r\n  4     IEo Old Interrupt Disable                       ;left unchanged by rfe\r\n  5     KUo Old Kernel/User Mode                        ;left unchanged by rfe\r\n  6-7   -   Not used (zero)\r\n  8-15  Im  8 bit interrupt mask fields. When set the corresponding\r\n            interrupts are allowed to cause an exception.\r\n  16    Isc Isolate Cache (0=No, 1=Isolate)\r\n              When isolated, all load and store operations are targetted\r\n              to the Data cache, and never the main memory.\r\n              (Used by PSX Kernel, in combination with Port FFFE0130h)\r\n  17    Swc Swapped cache mode (0=Normal, 1=Swapped)\r\n              Instruction cache will act as Data cache and vice versa.\r\n              Use only with Isc to access & invalidate Instr. cache entries.\r\n              (Not used by PSX Kernel)\r\n  18    PZ  When set cache parity bits are written as 0.\r\n  19    CM  Shows the result of the last load operation with the D-cache\r\n            isolated. It gets set if the cache really contained data\r\n            for the addressed memory location.\r\n  20    PE  Cache parity error (Does not cause exception)\r\n  21    TS  TLB shutdown. Gets set if a programm address simultaneously\r\n            matches 2 TLB entries.\r\n            (initial value on reset allows to detect extended CPU version?)\r\n  22    BEV Boot exception vectors in RAM/ROM (0=RAM/KSEG0, 1=ROM/KSEG1)\r\n  23-24 -   Not used (zero)\r\n  25    RE  Reverse endianness   (0=Normal endianness, 1=Reverse endianness)\r\n              Reverses the byte order in which data is stored in\r\n              memory. (lo-hi -> hi-lo)\r\n              (Affects only user mode, not kernel mode) (?)\r\n              (The bit doesn't exist in PSX ?)\r\n  26-27 -   Not used (zero)\r\n  28    CU0 COP0 Enable (0=Enable only in Kernel Mode, 1=Kernel and User Mode)\r\n  29    CU1 COP1 Enable (0=Disable, 1=Enable) (none in PSX)\r\n  30    CU2 COP2 Enable (0=Disable, 1=Enable) (GTE in PSX)\r\n  31    CU3 COP3 Enable (0=Disable, 1=Enable) (none in PSX)\r\n*/\r\n\r\n#define SR_IEC 0x00000001\r\n#define SR_KUC 0x00000002\r\n#define SR_IEP 0x00000004\r\n#define SR_KUP 0x00000008\r\n#define SR_IEO 0x00000010\r\n#define SR_KUO 0x00000020\r\n#define SR_IM  0x0000ff00\r\n#define SR_IM0 0x00000100\r\n#define SR_IM1 0x00000200\r\n#define SR_IM2 0x00000400\r\n#define SR_IM3 0x00000800\r\n#define SR_IM4 0x00001000\r\n#define SR_IM5 0x00002000\r\n#define SR_IM6 0x00004000\r\n#define SR_IM7 0x00008000\r\n#define SR_ISC 0x00010000\r\n#define SR_SWC 0x00020000\r\n#define SR_PZ  0x00040000\r\n#define SR_CM  0x00080000\r\n#define SR_PE  0x00100000\r\n#define SR_TS  0x00200000\r\n#define SR_BEV 0x00400000\r\n#define SR_RE  0x02000000\r\n#define SR_CU0 0x10000000\r\n#define SR_CU1 0x20000000\r\n#define SR_CU2 0x40000000\r\n#define SR_CU3 0x80000000\r\n\r\nstruct iop_state* iop_create(void);\r\nvoid iop_init(struct iop_state* iop, struct iop_bus_s bus);\r\nvoid iop_init_kputchar(struct iop_state* iop, void (*kputchar)(void*, char), void* udata);\r\nvoid iop_init_sm_putchar(struct iop_state* iop, void (*sm_putchar)(void*, char), void* udata);\r\nvoid iop_destroy(struct iop_state* iop);\r\nvoid iop_cycle(struct iop_state* iop);\r\nvoid iop_reset(struct iop_state* iop);\r\nvoid iop_set_irq_pending(struct iop_state* iop);\r\nvoid iop_fetch(struct iop_state* iop);\r\nint iop_execute(struct iop_state* iop);\r\n\r\n// External bus access functions\r\nuint32_t iop_read8(struct iop_state* iop, uint32_t addr);\r\nuint32_t iop_read16(struct iop_state* iop, uint32_t addr);\r\nuint32_t iop_read32(struct iop_state* iop, uint32_t addr);\r\nvoid iop_write8(struct iop_state* iop, uint32_t addr, uint32_t data);\r\nvoid iop_write16(struct iop_state* iop, uint32_t addr, uint32_t data);\r\nvoid iop_write32(struct iop_state* iop, uint32_t addr, uint32_t data);\r\n\r\n/*\r\n    00h INT     Interrupt\r\n    01h MOD     TLB modification (none such in PSX)\r\n    02h TLBL    TLB load         (none such in PSX)\r\n    03h TLBS    TLB store        (none such in PSX)\r\n    04h AdEL    Address error, Data load or Instruction fetch\r\n    05h AdES    Address error, Data store\r\n                The address errors occur when attempting to read\r\n                outside of KUseg in user mode and when the address\r\n                is misaligned. (See also: BadVaddr register)\r\n    06h IBE     Bus error on Instruction fetch\r\n    07h DBE     Bus error on Data load/store\r\n    08h Syscall Generated unconditionally by syscall instruction\r\n    09h BP      Breakpoint - break instruction\r\n    0Ah RI      Reserved instruction\r\n    0Bh CpU     Coprocessor unusable\r\n    0Ch Ov      Arithmetic overflow\r\n*/\r\n\r\n#define CAUSE_INT       (0x00 << 2)\r\n#define CAUSE_MOD       (0x01 << 2)\r\n#define CAUSE_TLBL      (0x02 << 2)\r\n#define CAUSE_TLBS      (0x03 << 2)\r\n#define CAUSE_ADEL      (0x04 << 2)\r\n#define CAUSE_ADES      (0x05 << 2)\r\n#define CAUSE_IBE       (0x06 << 2)\r\n#define CAUSE_DBE       (0x07 << 2)\r\n#define CAUSE_SYSCALL   (0x08 << 2)\r\n#define CAUSE_BP        (0x09 << 2)\r\n#define CAUSE_RI        (0x0a << 2)\r\n#define CAUSE_CPU       (0x0b << 2)\r\n#define CAUSE_OV        (0x0c << 2)\r\n\r\n#ifdef __cplusplus\r\n}\r\n#endif\r\n\r\n#endif\r\n"
  },
  {
    "path": "src/iop/iop_dis.c",
    "content": "// license:MIT\n// copyright-holders:Lisandro Alarcon (Allkern)\n\n/**\n * @file r3000d.c\n * @brief Disassembler for MIPS R3000A compatible code\n * @author Allkern (https://github.com/allkern)\n */\n\n#include <stdint.h>\n#include <stdio.h>\n\n#include \"iop_dis.h\"\n\n#define OP ((opcode >> 26) & 0x3f)\n#define S ((opcode >> 21) & 0x1f)\n#define T ((opcode >> 16) & 0x1f)\n#define D ((opcode >> 11) & 0x1f)\n#define IMM5 ((opcode >> 6) & 0x1f)\n#define CMT ((opcode >> 6) & 0xfffff)\n#define SOP (opcode & 0x3f)\n#define IMM26 (opcode & 0x3ffffff)\n#define IMM16 (opcode & 0xffff)\n#define IMM16S ((int32_t)((int16_t)IMM16))\n\nconst char* invalid = \"\";\n\nstatic const char* r3000_secondary_table[] = {\n    \"sll\"    , \"invalid\", \"srl\"    , \"sra\"    ,\n    \"sllv\"   , \"invalid\", \"srlv\"   , \"srav\"   ,\n    \"jr\"     , \"jalr\"   , \"invalid\", \"invalid\",\n    \"syscall\", \"break\"  , \"invalid\", \"invalid\",\n    \"mfhi\"   , \"mthi\"   , \"mflo\"   , \"mtlo\"   ,\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"mult\"   , \"multu\"  , \"div\"    , \"divu\"   ,\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"add\"    , \"addu\"   , \"sub\"    , \"subu\"   ,\n    \"and\"    , \"or\"     , \"xor\"    , \"nor\"    ,\n    \"invalid\", \"invalid\", \"slt\"    , \"sltu\"   ,\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\"\n};\n\nstatic const char* r3000_primary_table[] = {\n    \"special\", \"bxx\"    , \"j\"      , \"jal\"    ,\n    \"beq\"    , \"bne\"    , \"blez\"   , \"bgtz\"   ,\n    \"addi\"   , \"addiu\"  , \"slti\"   , \"sltiu\"  ,\n    \"andi\"   , \"ori\"    , \"xori\"   , \"lui\"    ,\n    \"cop0\"   , \"cop1\"   , \"cop2\"   , \"cop3\"   ,\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"lb\"     , \"lh\"     , \"lwl\"    , \"lw\"     ,\n    \"lbu\"    , \"lhu\"    , \"lwr\"    , \"invalid\",\n    \"sb\"     , \"sh\"     , \"swl\"    , \"sw\"     ,\n    \"invalid\", \"invalid\", \"swr\"    , \"invalid\",\n    \"lwc0\"   , \"lwc1\"   , \"lwc2\"   , \"lwc3\"   ,\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"swc0\"   , \"swc1\"   , \"swc2\"   , \"swc3\"   ,\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\"\n};\n\nstatic const char* r3000_cop0_table[] = {\n    \"mfc0\"   , \"invalid\", \"invalid\", \"invalid\",\n    \"mtc0\"   , \"invalid\", \"invalid\", \"invalid\",\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"rfe\"    , \"invalid\", \"invalid\", \"invalid\",\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n    \"invalid\", \"invalid\", \"invalid\", \"invalid\"\n};\n\n// static const char* r3000_cop2_table[] = {\n//     \"mfc2\"   , \"invalid\", \"cfc2\"   , \"invalid\",\n//     \"mtc2\"   , \"invalid\", \"ctc2\"   , \"invalid\",\n//     \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n//     \"invalid\", \"invalid\", \"invalid\", \"invalid\",\n//     \"gte\"    , \"gte\"    , \"gte\"    , \"gte\"    ,\n//     \"gte\"    , \"gte\"    , \"gte\"    , \"gte\"    ,\n//     \"gte\"    , \"gte\"    , \"gte\"    , \"gte\"    ,\n//     \"gte\"    , \"gte\"    , \"gte\"    , \"gte\"\n// };\n\nstatic const char* r3000_bxx_table[] = {\n    \"bltz\"   , \"bgez\"   , \"bltz\"   , \"bgez\"   ,\n    \"bltz\"   , \"bgez\"   , \"bltz\"   , \"bgez\"   ,\n    \"bltz\"   , \"bgez\"   , \"bltz\"   , \"bgez\"   ,\n    \"bltz\"   , \"bgez\"   , \"bltz\"   , \"bgez\"   ,\n    \"bltzal\" , \"bgezal\" , \"bltz\"   , \"bgez\"   ,\n    \"bltz\"   , \"bgez\"   , \"bltz\"   , \"bgez\"   ,\n    \"bltz\"   , \"bgez\"   , \"bltz\"   , \"bgez\"   ,\n    \"bltz\"   , \"bgez\"   , \"bltz\"   , \"bgez\"\n};\n\nstatic const char* r3000_register_names[] = {\n    \"r0\", \"at\", \"v0\", \"v1\", \"a0\", \"a1\", \"a2\", \"a3\",\n    \"t0\", \"t1\", \"t2\", \"t3\", \"t4\", \"t5\", \"t6\", \"t7\",\n    \"s0\", \"s1\", \"s2\", \"s3\", \"s4\", \"s5\", \"s6\", \"s7\",\n    \"t8\", \"t9\", \"k0\", \"k1\", \"gp\", \"sp\", \"fp\", \"ra\"\n};\n\nstatic const char* r3000_cop0_register_names[] = {\n    \"Index\",\n    \"Random\",\n    \"EntryLo\",\n    \"BPC\",\n    \"Context\",\n    \"BDA\",\n    \"JUMPDEST\",\n    \"DCIC\",\n    \"BadVAddr\",\n    \"BDAM\",\n    \"EntryHi\",\n    \"BPCM\",\n    \"SR\",\n    \"Cause\",\n    \"EPC\",\n    \"PRId\",\n    \"r16\",\n    \"r17\",\n    \"r18\",\n    \"r19\",\n    \"r20\",\n    \"r21\",\n    \"r22\",\n    \"r23\",\n    \"r24\",\n    \"r25\",\n    \"r26\",\n    \"r27\",\n    \"r28\",\n    \"r29\",\n    \"r30\",\n    \"r31\",\n    \"r32\",\n    \"r33\",\n    \"r34\",\n    \"r35\",\n    \"r36\",\n    \"r37\",\n    \"r38\",\n    \"r39\",\n    \"r40\",\n    \"r41\",\n    \"r42\",\n    \"r43\",\n    \"r44\",\n    \"r45\",\n    \"r46\",\n    \"r47\"\n};\n\nvoid disassemble_secondary(char* buf, uint32_t opcode, struct iop_dis_state* state) {\n    char* ptr = buf;\n\n    int sop = SOP;\n\n    switch (sop) {\n        case 0:\n        case 2:\n        case 3:\n            sprintf(ptr,\n                \"%-8s $%s, $%s, %u\",\n                r3000_secondary_table[sop],\n                r3000_register_names[D],\n                r3000_register_names[T],\n                IMM5\n            );\n        break;\n        case 4:\n        case 6:\n        case 7:\n            sprintf(ptr,\n                \"%-8s $%s, $%s, $%s\",\n                r3000_secondary_table[sop],\n                r3000_register_names[D],\n                r3000_register_names[T],\n                r3000_register_names[S]\n            );\n        break;\n        case 9:\n            sprintf(ptr,\n                \"%-8s $%s, $%s\",\n                r3000_secondary_table[sop],\n                r3000_register_names[S],\n                r3000_register_names[D]\n            );\n        break;\n        case 12:\n        case 13:\n            sprintf(ptr,\n                \"%-8s 0x%x\",\n                r3000_secondary_table[sop],\n                CMT\n            );\n        break;\n        case 16:\n            sprintf(ptr,\n                \"%-8s 0x%x\",\n                r3000_cop0_table[sop],\n                CMT\n            );\n        break;\n        case 18:\n            sprintf(ptr,\n                \"%-8s $%s\",\n                r3000_secondary_table[sop],\n                r3000_register_names[D]\n            );\n        break;\n        case 8:\n        case 17:\n        case 19:\n            sprintf(ptr,\n                \"%-8s $%s\",\n                r3000_secondary_table[sop],\n                r3000_register_names[S]\n            );\n        break;\n        case 24:\n        case 25:\n        case 26:\n        case 27:\n            sprintf(ptr,\n                \"%-8s $%s, $%s\",\n                r3000_secondary_table[sop],\n                r3000_register_names[S],\n                r3000_register_names[T]\n            );\n        break;\n        case 32:\n        case 33:\n        case 34:\n        case 35:\n        case 36:\n        case 37:\n        case 38:\n        case 39:\n        case 42:\n        case 43:\n            sprintf(ptr,\n                \"%-8s $%s, $%s, $%s\",\n                r3000_secondary_table[sop],\n                r3000_register_names[D],\n                r3000_register_names[S],\n                r3000_register_names[T]\n            );\n        break;\n        default:\n            sprintf(ptr, \"<invalid>\");\n        break;\n    }\n}\n\nchar* iop_disassemble(char* buf, uint32_t opcode, struct iop_dis_state* state) {\n    char* ptr = buf;\n\n    if (state) if (state->print_address)\n        ptr += sprintf(ptr, \"%08x: \", state->addr);\n\n    if (state) if (state->print_opcode)\n        ptr += sprintf(ptr, \"%08x \", opcode);\n\n    int op = OP;\n\n    switch (op) {\n        case 0:\n            disassemble_secondary(ptr, opcode, state);\n        break;\n        case 1:\n            sprintf(ptr,\n                (IMM16S >= 0) ? \"%-8s $%s, 0x%04x\" : \"%-8s $%s, -0x%04x\",\n                r3000_bxx_table[T],\n                r3000_register_names[S],\n                (IMM16S >= 0) ? IMM16S : ((~IMM16S) + 1)\n            );\n        break;\n        case 2:\n        case 3:\n            sprintf(ptr, \"%-8s 0x%08x\",\n                r3000_primary_table[op],\n                (state ? (state->addr & 0xf0000000) : 0) |\n                (IMM26 << 2)\n            );\n        break;\n        case 4:\n        case 5:\n            sprintf(ptr,\n                (IMM16S >= 0) ? \"%-8s $%s, $%s, 0x%04x\" : \"%-8s $%s, $%s, -0x%04x\",\n                r3000_primary_table[op],\n                r3000_register_names[S],\n                r3000_register_names[T],\n                (IMM16S >= 0 ? IMM16S : ~IMM16S) << 2\n            );\n        break;\n        case 6:\n        case 7:\n            sprintf(ptr,\n                (IMM16S >= 0) ? \"%-8s $%s, 0x%04x\" : \"%-8s $%s, -0x%04x\",\n                r3000_primary_table[op],\n                r3000_register_names[S],\n                (IMM16S >= 0) ? IMM16S : ((~IMM16S) + 1)\n            );\n        break;\n        case 8:\n        case 9:\n        case 10:\n        case 11:\n        case 12:\n        case 13:\n        case 14:\n            sprintf(ptr, \"%-8s $%s, $%s, 0x%x\",\n                r3000_primary_table[op],\n                r3000_register_names[T],\n                r3000_register_names[S],\n                IMM16S\n            );\n        break;\n        case 15:\n            sprintf(ptr, \"%-8s $%s, 0x%04x\",\n                r3000_primary_table[op],\n                r3000_register_names[T],\n                IMM16\n            );\n        break;\n        case 16: {\n            int s = S;\n\n            sprintf(ptr,\n                s == 16 ? \"%-8s\" : \"%-8s $%s, $Cop0_%s\",\n                r3000_cop0_table[s],\n                r3000_register_names[T],\n                r3000_cop0_register_names[D]\n            );\n        } break;\n\n        /* To-do: case 18 (COP2, GTE) */\n        case 18:\n            sprintf(ptr, \"<gte-unimplemented>\");\n        break;\n\n        case 32:\n        case 33:\n        case 34:\n        case 35:\n        case 36:\n        case 37:\n        case 38:\n        case 40:\n        case 41:\n        case 42:\n        case 43:\n        case 46:\n        case 48:\n        case 49:\n        case 50:\n        case 51:\n        case 56:\n        case 57:\n        case 58:\n        case 59: {\n            const char* fmt = \"%-8s $%s, %i($%s)\";\n\n            if (state) if (state->hex_memory_offset)\n                fmt = \"%-8s $%s, 0x%04x($%s)\";\n\n            sprintf(ptr, fmt,\n                r3000_primary_table[op],\n                r3000_register_names[T],\n                IMM16S,\n                r3000_register_names[S]\n            );\n        } break;\n        default:\n            sprintf(ptr, \"<invalid>\");\n        break;\n    }\n\n    return buf;\n}"
  },
  {
    "path": "src/iop/iop_dis.h",
    "content": "// license:MIT\n// copyright-holders:Lisandro Alarcon (Allkern)\n\n/**\n * @file iop_dis.h\n * @brief Disassembler for MIPS R3000A (IOP) compatible code\n * @author Allkern (https://github.com/allkern)\n */\n\n#ifndef IOP_DIS_H\n#define IOP_DIS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\nstruct iop_dis_state {\n    uint32_t addr;\n    int print_address;\n    int print_opcode;\n    int hex_memory_offset;\n};\n\n/** @brief Disassemble a single opcode, printing to a buffer and\n *         optionally taking in a pointer to a disassembler state\n *         struct\n *\n *  @param buf pointer to a char buffer\n *  @param opcode opcode to disassemble\n *  @param state optional pointer to disassembler state struct\n *         (pass NULL if not required)\n *  @returns `buf`\n */\nchar* iop_disassemble(char* buf, uint32_t opcode, struct iop_dis_state* state);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/iop_export.c",
    "content": "#include \"iop_export.h\"\n\nstatic inline uint32_t irx_import_table_addr(struct iop_state* iop, int entry) {\n    uint32_t i = entry - 0x18;\n\n    while ((entry - i) < 0x2000) {\n        if (iop_read32(iop, i) == 0x41e00000)\n            return i;\n\n        i -= 4;\n    }\n\n    return 0;\n}\n\nstatic inline int iop_get_module(struct iop_state* iop, int itable) {\n    char buf[9];\n\n    for (int i = 0; i < 8; i++)\n        buf[i] = iop_read8(iop, itable + 12 + i);\n\n    if (!strncmp(buf, \"ioman\", 8)) return MODULE_IOMAN;\n    if (!strncmp(buf, \"iomanx\", 8)) return MODULE_IOMANX;\n    if (!strncmp(buf, \"loadcore\", 8)) return MODULE_LOADCORE;\n    if (!strncmp(buf, \"sysmem\", 8)) return MODULE_SYSMEM;\n\n    return MODULE_UNKNOWN;\n}\n\nstatic inline int iop_delegate_ioman(struct iop_state* iop, int slot, int iomanx) {\n    switch (slot & 0xffff) {\n        case IOMAN_OPEN: return ioman_open(iop, iomanx);\n        case IOMAN_CLOSE: return ioman_close(iop, iomanx);\n        case IOMAN_READ: return ioman_read(iop, iomanx);\n        case IOMAN_WRITE: return ioman_write(iop, iomanx);\n        case IOMAN_LSEEK: return ioman_lseek(iop, iomanx);\n        case IOMAN_IOCTL: return ioman_ioctl(iop, iomanx);\n        case IOMAN_REMOVE: return ioman_remove(iop, iomanx);\n        case IOMAN_MKDIR: return ioman_mkdir(iop, iomanx);\n        case IOMAN_RMDIR: return ioman_rmdir(iop, iomanx);\n        case IOMAN_DOPEN: return ioman_dopen(iop, iomanx);\n        case IOMAN_DCLOSE: return ioman_dclose(iop, iomanx);\n        case IOMAN_DREAD: return ioman_dread(iop, iomanx);\n        case IOMAN_GETSTAT: return ioman_getstat(iop, iomanx);\n        case IOMAN_CHSTAT: return ioman_chstat(iop, iomanx);\n        case IOMAN_FORMAT: return ioman_format(iop, iomanx);\n        case IOMAN_ADDDRV: return ioman_adddrv(iop, iomanx);\n        case IOMAN_DELDRV: return ioman_deldrv(iop, iomanx);\n        case IOMAN_STDIOINIT: return ioman_stdioinit(iop, iomanx);\n        case IOMAN_RENAME: return ioman_rename(iop, iomanx);\n        case IOMAN_CHDIR: return ioman_chdir(iop, iomanx);\n        case IOMAN_SYNC: return ioman_sync(iop, iomanx);\n        case IOMAN_MOUNT: return ioman_mount(iop, iomanx);\n        case IOMAN_UMOUNT: return ioman_umount(iop, iomanx);\n        case IOMAN_LSEEK64: return ioman_lseek64(iop, iomanx);\n        case IOMAN_DEVCTL: return ioman_devctl(iop, iomanx);\n        case IOMAN_SYMLINK: return ioman_symlink(iop, iomanx);\n        case IOMAN_READLINK: return ioman_readlink(iop, iomanx);\n        case IOMAN_IOCTL2: return ioman_ioctl2(iop, iomanx);\n    }\n\n    return 0;\n}\n\nstatic inline int iop_delegate_loadcore(struct iop_state* iop, int slot) {\n    switch (slot & 0xffff) {\n        case LOADCORE_REG_LIB_ENT: return loadcore_reg_lib_ent(iop);\n    }\n\n    return 0;\n}\n\nstatic inline int iop_delegate_sysmem(struct iop_state* iop, int slot) {\n    switch (slot & 0xffff) {\n        // SYSMEM kprintf\n        case 14: return sysmem_kprintf(iop);\n    }\n\n    return 0;\n}\n\nint iop_test_module_hooks(struct iop_state* iop) {\n    uint32_t slot = iop_read32(iop, iop->pc);\n\n    if ((slot >> 16) != 0x2400)\n        return 0;\n\n    uint32_t itable = irx_import_table_addr(iop, iop->pc);\n\n    if (!itable)\n        return 0;\n\n    int module = iop_get_module(iop, itable);\n\n    if (!module)\n        return 0;\n\n    switch (module) {\n        case MODULE_IOMAN: return iop_delegate_ioman(iop, slot, 0);\n        case MODULE_IOMANX: return iop_delegate_ioman(iop, slot, 1);\n        case MODULE_LOADCORE: return iop_delegate_loadcore(iop, slot);\n        case MODULE_SYSMEM: return iop_delegate_sysmem(iop, slot);\n    }\n\n    return 0;\n}\n\nvoid iop_return(struct iop_state* iop, int ret) {\n    // Set v0 (return register) to ret\n    iop->r[2] = ret;\n\n    // Emulate jal ra\n    iop->pc = iop->r[31];\n    iop->next_pc = iop->pc + 4;\n}\n"
  },
  {
    "path": "src/iop/iop_export.h",
    "content": "#ifndef IOP_EXPORT_H\n#define IOP_EXPORT_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <string.h>\n\n#include \"iop.h\"\n\n#include \"hle/ioman.h\"\n#include \"hle/loadcore.h\"\n#include \"hle/sysmem.h\"\n\n#define MODULE_UNKNOWN  0\n#define MODULE_IOMAN    1\n#define MODULE_IOMANX   2\n#define MODULE_LOADCORE 3\n#define MODULE_SYSMEM   4\n\n#define IOMAN_OPEN      4\n#define IOMAN_CLOSE     5\n#define IOMAN_READ      6\n#define IOMAN_WRITE     7\n#define IOMAN_LSEEK     8\n#define IOMAN_IOCTL     9\n#define IOMAN_REMOVE    10\n#define IOMAN_MKDIR     11\n#define IOMAN_RMDIR     12\n#define IOMAN_DOPEN     13\n#define IOMAN_DCLOSE    14\n#define IOMAN_DREAD     15\n#define IOMAN_GETSTAT   16\n#define IOMAN_CHSTAT    17\n#define IOMAN_FORMAT    18\n#define IOMAN_ADDDRV    20\n#define IOMAN_DELDRV    21\n#define IOMAN_STDIOINIT 23\n#define IOMAN_RENAME    25\n#define IOMAN_CHDIR     26\n#define IOMAN_SYNC      27\n#define IOMAN_MOUNT     28\n#define IOMAN_UMOUNT    29\n#define IOMAN_LSEEK64   30\n#define IOMAN_DEVCTL    31\n#define IOMAN_SYMLINK   32\n#define IOMAN_READLINK  33\n#define IOMAN_IOCTL2    34\n\n#define LOADCORE_REG_LIB_ENT 6\n\nint iop_test_module_hooks(struct iop_state* iop);\nvoid iop_return(struct iop_state* iop, int ret);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n"
  },
  {
    "path": "src/iop/rpc.c",
    "content": "#include <stdlib.h>\n#include <stdint.h>\n#include <stdio.h>\n\n#include \"rpc.h\"\n\n#define printf(fmt, ...)(0)\n\nconst char* rpc_get_server(uint32_t id) {\n    switch (id) {\n        case 0x80000001: return \"FILEIO\";\n        case 0x80000003: return \"IOP heap alloc (FILEIO)\";\n        case 0x80000006: return \"LOADFILE\";\n        case 0x80000100: return \"PADMAN\";\n        case 0x80000101: return \"PADMAN ext\";\n        case 0x80000400: return \"MCSERV\";\n        case 0x80000592: return \"CDVD Init (CDVDFSV)\";\n        case 0x80000593: return \"CDVD S commands (CDVDFSV)\";\n        case 0x80000595: return \"CDVD N commands (CDVDFSV)\";\n        case 0x80000597: return \"CDVD SearchFile (CDVDFSV)\";\n        case 0x8000059A: return \"CDVD Disk Ready (CDVDFSV)\";\n        case 0x80000701: return \"LIBSD Remote (SDRDRV)\";\n        case 0x80000901: return \"MTAP Port Open (MTAPMAN)\";\n        case 0x80000902: return \"MTAP Port Close (MTAPMAN)\";\n        case 0x80000903: return \"MTAP Get Connection (MTAPMAN)\";\n        case 0x80000904: return \"MTAP Unknown (MTAPMAN)\";\n        case 0x80000905: return \"MTAP Unknown (MTAPMAN)\";\n        case 0x80001400: return \"EYETOY\";\n    }\n\n    return \"<unknown>\";\n}\n\nchar* rpc_decode_packet(struct iop_state* iop, char* buf, uint32_t* data) {\n    char* ptr = buf;\n\n    struct sif_cmd_header* hdr = (struct sif_cmd_header*)data;\n\n    // ptr += sprintf(ptr, \"rpc: \");\n\n    switch (hdr->cid) {\n        case 0x80000000: ptr += sprintf(ptr, \"rpc: ChangeSaddr: \"); break;\n        case 0x80000001: ptr += sprintf(ptr, \"rpc: SetSreg: \"); break;\n        case 0x80000002: {\n            struct sif_init_pkt* init = (struct sif_init_pkt*)data;\n\n            ptr += sprintf(ptr, \"rpc: Init (opt=%d)\", init->header.opt);\n        } break;\n        case 0x80000003: ptr += sprintf(ptr, \"rpc: Reboot: \"); break;\n        case 0x80000008: ptr += sprintf(ptr, \"rpc: RequestEnd: \"); break;\n        case 0x80000009: {\n            struct sif_rpc_bind_pkt* bind = (struct sif_rpc_bind_pkt*)data;\n\n            const char* server = rpc_get_server(bind->sid);\n\n            ptr += sprintf(ptr, \"rpc: Bind (%s)\", server); break;\n        } break;\n        case 0x8000000A: {\n            struct sif_rpc_call_pkt* call = (struct sif_rpc_call_pkt*)data;\n\n            uint32_t sid = iop_read32(iop, call->server);\n\n            ptr += sprintf(ptr, \"rpc: Call Server=%08x Func=%08x\", sid, call->rpc_number);\n\n            if (sid == 0x01470201) {\n                printf(ptr, \"rpc: ReadBackupRam Func=%08x SendSize=%d PktAddr=%08x\\n\",\n                    call->rpc_number,\n                    call->send_size,\n                    call->pkt_addr\n                );\n            }\n\n            if (sid == 0x01470200) {\n                printf(ptr, \"rpc: WriteBackupRam Func=%08x SendSize=%d PktAddr=%08x\\n\",\n                    call->rpc_number,\n                    call->send_size,\n                    call->pkt_addr\n                );\n            }\n\n            if (sid == 0x14799) {\n                struct MODULE_99_PACKET {\n                    uint8_t type;\n                    uint8_t unknown[4];\n                    uint8_t command;\n                    uint8_t data[0x39];\n                    uint8_t checksum;\n                };\n\n                switch (call->rpc_number) {\n                    case 0x02000000: printf(\"s14x_link: RPC ReceiveData\\n\"); break;\n                    case 0x03004002: {\n                        uint8_t* buf = malloc(call->send_size);\n\n                        for (int i = 0; i < call->send_size; i++)\n                            buf[i] = iop_read8(iop, call->pkt_addr + i);\n\n                        struct MODULE_99_PACKET* packet = (struct MODULE_99_PACKET*)buf;\n\n                        printf(\"s14x_link: RPC SendData(type=%d, command=%02x)\\n\",\n                            packet->type,\n                            packet->command\n                        );\n                    } break;\n                    case 0x08000000: printf(\"s14x_link: RPC CheckOnline\\n\"); break;\n                    default: printf(\"s14x_link: RPC LINK_%08X\\n\", call->rpc_number); break;\n                }\n            }\n        } break;\n        case 0x8000000C: ptr += sprintf(ptr, \"rpc: GetOtherData\"); break;\n        // default: ptr += sprintf(ptr, \"Unknown CID %08x\", hdr->cid); break;\n        default: return NULL;\n    }\n\n    *ptr++ = '\\0';\n\n    return buf;\n}\n\n// 80000000h Change SADDR\n// 80000001h Set SREG\n// 80000002h SIFCMD Init\n// 80000003h Reboot IOP\n// 80000008h Request End\n// 80000009h Bind\n// 8000000Ah Call\n// 8000000Ch Get other data\n\n// 0x80000001 \"FILEIO\"\n// 0x80000003 \"IOP heap alloc (FILEIO)\"\n// 0x80000006 \"LOADFILE\"\n// 0x80000100 \"PADMAN\"\n// 0x80000101 \"PADMAN ext\"\n// 0x80000400 \"MCSERV\"\n// 0x80000592 \"CDVD Init (CDVDFSV)\"\n// 0x80000593 \"CDVD S commands (CDVDFSV)\"\n// 0x80000595 \"CDVD N commands (CDVDFSV)\"\n// 0x80000597 \"CDVD SearchFile (CDVDFSV)\"\n// 0x8000059A \"CDVD Disk Ready (CDVDFSV)\"\n// 0x80000701 \"LIBSD Remote (SDRDRV)\"\n// 0x80000901 \"MTAP Port Open (MTAPMAN)\"\n// 0x80000902 \"MTAP Port Close (MTAPMAN)\"\n// 0x80000903 \"MTAP Get Connection (MTAPMAN)\"\n// 0x80000904 \"MTAP Unknown (MTAPMAN)\"\n// 0x80000905 \"MTAP Unknown (MTAPMAN)\"\n// 0x80001400 \"EYETOY\""
  },
  {
    "path": "src/iop/rpc.h",
    "content": "#ifndef RPC_H\n#define RPC_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"iop.h\"\n\nstruct __attribute__((packed)) sif_cmd_header {\n    unsigned int psize : 8;\n    unsigned int dsize : 24;\n    unsigned int dest;\n    unsigned int cid;\n    unsigned int opt;\n};\n\nstruct sif_rpc_packet_header {\n    struct sif_cmd_header sif_cmd;\n    unsigned int rec_id;\n    unsigned int pkt_addr;\n    unsigned int rpc_id;\n};\n\nstruct sif_rpc_client_data {\n    struct sif_rpc_packet_header hdr;\n    unsigned int command;\n    unsigned int buff, *cbuff;\n    unsigned int end_function;\n    unsigned int end_param;\n    unsigned int server;\n};\n\nstruct sif_rpc_server_data {\n    unsigned int sid;\n    unsigned int func;\n    unsigned int buff;\n    unsigned int size;\n    unsigned int cfunc;\n    unsigned int cbuff;\n    unsigned int size2;\n    unsigned int client;\n    unsigned int pkt_addr;\n    unsigned int rpc_number;\n    unsigned int recv;\n    unsigned int rsize;\n    unsigned int rmode;\n    unsigned int rid;\n    unsigned int link;\n    unsigned int next;\n    unsigned int base;\n};\n\nstruct sif_rpc_data_queue {\n    unsigned int thread_id, active;\n    unsigned int link, start, end;\n    unsigned int next;\n};\n\nstruct sif_dma_transfer {\n    unsigned int src, dest;\n    unsigned int size;\n    unsigned int attr;\n};\n\nstruct sif_saddr_pkt {\n    struct sif_cmd_header header;\n    unsigned int buf;\n};\n\nstruct sif_cmd_set_sreg_pkt {\n    struct sif_cmd_header header;\n    unsigned int index;\n    unsigned int value;\n};\n\nstruct sif_init_pkt {\n    struct sif_cmd_header header;\n    unsigned int buff;\n};\n\nstruct sif_iop_reset_pkt {\n    struct sif_cmd_header header;\n    unsigned int arglen;\n    unsigned int mode;\n    char arg[80];\n};\n\nstruct sif_rpc_rend_pkt {\n    struct sif_cmd_header sifcmd;\n    unsigned int rec_id;\n    unsigned int pkt_addr;\n    unsigned int rpc_id;\n    unsigned int client;\n    unsigned int cid;\n    unsigned int server;\n    unsigned int buff;\n    unsigned int cbuff;\n};\n\nstruct sif_rpc_bind_pkt {\n    struct sif_cmd_header sifcmd;\n    unsigned int rec_id;\n    unsigned int pkt_addr;\n    unsigned int rpc_id;\n    unsigned int client;\n    unsigned int sid;\n};\n\nstruct sif_rpc_call_pkt {\n    struct sif_cmd_header sifcmd;\n    unsigned int rec_id; \n    unsigned int pkt_addr;\n    unsigned int rpc_id;\n    unsigned int client;\n    unsigned int rpc_number;\n    unsigned int send_size;\n    unsigned int receive;\n    unsigned int recv_size;\n    unsigned int rmode;\n    unsigned int server;\n};\n\nstruct sif_rpc_other_data_pkt {\n    struct sif_cmd_header sifcmd;\n    unsigned int rec_id;\n    unsigned int pkt_addr;\n    unsigned int rpc_id;\n    unsigned int receive;\n    unsigned int src;\n    unsigned int dest;\n    unsigned int size;\n};\n\nchar* rpc_decode_packet(struct iop_state* iop, char* buf, uint32_t* data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/sio2.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"sio2.h\"\n\nstatic inline void sio2_reset(struct ps2_sio2* sio2) {\n    queue_clear(sio2->in);\n\n    sio2->send3_index = 0;\n}\n\nstruct ps2_sio2* ps2_sio2_create(void) {\n    return malloc(sizeof(struct ps2_sio2));\n}\n\nvoid sio2_send_irq(void* udata, int overshoot) {\n    struct ps2_sio2* sio2 = (struct ps2_sio2*)udata;\n\n    sio2->istat |= 1;\n\n    ps2_iop_intc_irq(sio2->intc, IOP_INTC_SIO2);\n}\n\nvoid sio2_dma_reset(struct ps2_sio2* sio2) {\n    queue_clear(sio2->in);\n}\n\nvoid ps2_sio2_init(struct ps2_sio2* sio2, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched) {\n    memset(sio2, 0, sizeof(struct ps2_sio2));\n\n    sio2->intc = intc;\n    sio2->dma = dma;\n\n    sio2->in = queue_create();\n    sio2->out = queue_create();\n\n    queue_init(sio2->in);\n    queue_init(sio2->out);\n\n    sio2->sched = sched;\n    sio2->recv1 = 0x1d100;\n}\n\nvoid ps2_sio2_destroy(struct ps2_sio2* sio2) {\n    // Detach all devices\n    for (int i = 0; i < 4; i++)\n        ps2_sio2_detach_device(sio2, i);\n\n    queue_destroy(sio2->in);\n    queue_destroy(sio2->out);\n\n    free(sio2);\n}\n\nstatic inline int sio2_handle_command(struct ps2_sio2* sio2, int idx) {\n    int devid = queue_at(sio2->in, 0);\n    int cmd = queue_at(sio2->in, 1);\n    int p = sio2->send3[idx] & 3;\n    int len = (sio2->send3[idx] >> 18) & 0xff;\n\n    // printf(\"sio2: Trying command %02x in SEND3[%d] port %d devid %02x (in: %d (%d), len: %d)\\n\", cmd,\n    //     idx, p, devid, queue_size(sio2->in), queue_size(sio2->in) + 2, len\n    // );\n\n    // If we're sending a pad command, make sure it's only for ports\n    // 0-1, and if it's a memory card command, make sure it's only\n    // for ports 2-3\n    int pad = devid == SIO2_DEV_PAD;\n    int mcd = devid == SIO2_DEV_MCD;\n    int mtap = devid == SIO2_DEV_MTAP;\n\n    if (!(pad || mcd))\n        return 0;\n\n    // Check if the port is actually connected\n    if (!sio2->port[p].handle_command)\n        return 0;\n\n    // printf(\"sio2: Sending command %02x to port %d with devid %02x (in: %d (%d), len: %d)\\n\",\n    //     cmd, p, devid, queue_size(sio2->in), queue_size(sio2->in) + 2, len\n    // );\n\n    // Send command\n    sio2->port[p].handle_command(sio2, sio2->port[p].udata, cmd);\n\n    // Clear current command parameters\n    for (int i = 0; i < len; i++)\n        queue_pop(sio2->in);\n\n    // Weird behavior, DMA MCD commands are aligned to a 36-word boundary\n    if (mcd) {\n        while (sio2->out->size % 0x90)\n            queue_push(sio2->out, 0);\n\n        if (!queue_is_empty(sio2->in)) {\n            // Remove input padding\n            while (sio2->in->index % 0x90)\n                queue_pop(sio2->in);\n        }\n    }\n\n    // printf(\"sio2: FIFOOUT size: %d (%x)\\n\", sio2->out->size, sio2->out->size);\n\n    // if (cmd == 0x81) exit(1);\n\n    return 1;\n}\n\nstatic inline void sio2_write_ctrl(struct ps2_sio2* sio2, uint32_t data) {\n    sio2->ctrl = data & ~1;\n\n    if (data & 0xc) {\n        // printf(\"sio2: Controller reset\\n\");\n\n        queue_clear(sio2->out);\n    }\n\n    // Send command bit\n    if ((data & 1) == 0)\n        return;\n\n    // Disconnected by default\n    sio2->recv1 = 0x1d100;\n\n    // Send IRQ no matter what\n    sio2->istat |= 1;\n\n    ps2_iop_intc_irq(sio2->intc, IOP_INTC_SIO2);\n\n    // printf(\"sio2: Sending command %02x to port %d with devid %02x (in: %d, len: %d)\\n\",\n    //     cmd, p, devid, queue_size(sio2->in), len\n    // );\n\n    for (int i = 0; i < 16; i++) {\n        // Break when we find a null command\n        if (!sio2->send3[i])\n            break;\n\n        // If any of the commands were handled, set RECV1 to 0x1100\n        if (sio2_handle_command(sio2, i)) {\n            if (sio2->recv1 & 0x2000) {\n                sio2->recv1 = 0x1d100;\n            } else {\n                sio2->recv1 = 0x1100;\n            }\n        }\n    }\n\n    // Complete SIO2_out transfer after all commands have executed\n    // For commands that use DMA (mostly MCD commands), this will handle\n    // reading from the output FIFO. Will do nothing on commands\n    // that don't use DMA, because the IOP needs to start a transfer\n    // before sending a DMA command, and if a command executed, but\n    // didn't actually put anything in the FIFO, the DMA request\n    // will be cleared. (e.g. when a memory card isn't connected)\n    iop_dma_end_sio2_out_transfer(sio2->dma);\n}\n\nuint64_t ps2_sio2_read8(struct ps2_sio2* sio2, uint32_t addr) {\n    switch (addr) {\n        // case 0x1F808260: /* printf(\"8-bit SIO2_FIFOIN read\\n\"); */ return 0;\n        case 0x1F808264: {\n            if (queue_is_empty(sio2->out)) {\n                // printf(\"sio2: SIO2_FIFOOUT read %02x\\n\", 0x00);\n                return 0x00;\n            }\n\n            uint8_t b = queue_pop(sio2->out);\n\n            // printf(\"sio2: SIO2_FIFOOUT read %02x\\n\", b);\n\n            return b;\n        } break;\n        // case 0x1F808268: /* printf(\"8-bit SIO2_CTRL read\\n\"); */ return 0; \n        // case 0x1F80826C: /* printf(\"8-bit SIO2_RECV1 read\\n\"); */ return 0;\n        // case 0x1F808270: /* printf(\"8-bit SIO2_RECV2 read\\n\"); */ return 0;\n        // case 0x1F808274: /* printf(\"8-bit SIO2_RECV3 read\\n\"); */ return 0;\n        // case 0x1F808280: /* printf(\"8-bit SIO2_ISTAT read\\n\"); */ return 0;\n        // default: {\n        //     if (addr >= 0x1F808200 && addr <= 0x1F80823F) {\n        // Memcard or non-controller access\n        //         // printf(\"8-bit SIO2_SEND3 read\\n\");\n        //     }\n        //     if (addr >= 0x1F808240 && addr <= 0x1F80825F) {\n        //         // printf(\"8-bit SIO2_SEND%d read\\n\", (addr & 4) ? 2 : 1);\n        //     }\n        // } break;\n    }\n\n    printf(\"sio2: Unhandled 8-bit read from address %08x\\n\", addr); exit(1);\n\n    return 0;\n}\n\nuint64_t ps2_sio2_read32(struct ps2_sio2* sio2, uint32_t addr) {\n    switch (addr) {\n        // case 0x1F808260: /* printf(\"32-bit SIO2_FIFOIN read\\n\"); */ return 0;\n        // case 0x1F808264: /* printf(\"32-bit SIO2_FIFOOUT read\\n\"); */ return 0;\n        case 0x1F808268: /* printf(\"32-bit SIO2_CTRL read\\n\"); */ return 0;\n        case 0x1F80826C: /* printf(\"32-bit SIO2_RECV1 read\\n\"); */ return sio2->recv1;\n        case 0x1F808270: /* printf(\"32-bit SIO2_RECV2 read\\n\"); */ return 0xf;\n        case 0x1F808274: /* printf(\"32-bit SIO2_RECV3 read\\n\"); */ return 0;\n        case 0x1F808280: /* printf(\"32-bit SIO2_ISTAT read\\n\"); */ return sio2->istat;\n        default: {\n            if (addr >= 0x1F808200 && addr <= 0x1F80823F) {\n                // printf(\"32-bit SIO2_SEND3 read\\n\");\n\n                return sio2->send3[(addr & 0x3f) >> 2];\n            }\n            // if (addr >= 0x1F808240 && addr <= 0x1F80825F) {\n            //     // printf(\"32-bit SIO2_SEND%d read\\n\", (addr & 4) ? 2 : 1);\n            // }\n        } break;\n    }\n\n    printf(\"sio2: Unhandled 32-bit read from address %08x\\n\", addr); exit(1);\n\n    return 0;\n}\n\nvoid ps2_sio2_write8(struct ps2_sio2* sio2, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        case 0x1F808260: /* printf(\"sio2: FIFOIN write %02x\\n\", data); */ queue_push(sio2->in, data); return;\n        // case 0x1F808264: /* printf(\"8-bit SIO2_FIFOOUT write %02x\\n\", data); */ return;\n        case 0x1F808268: sio2_write_ctrl(sio2, data); return;\n        // case 0x1F80826C: /* printf(\"8-bit SIO2_RECV1 write %02x\\n\", data); */ return;\n        // case 0x1F808270: /* printf(\"8-bit SIO2_RECV2 write %02x\\n\", data); */ return;\n        // case 0x1F808274: /* printf(\"8-bit SIO2_RECV3 write %02x\\n\", data); */ return;\n        // case 0x1F808280: /* printf(\"8-bit SIO2_ISTAT write %02x\\n\", data); */ return;\n        // default: {\n        //     if (addr >= 0x1F808200 && addr <= 0x1F80823F) {\n        //         // printf(\"8-bit SIO2_SEND3 write %02x\\n\", data);\n        //     }\n        //     if (addr >= 0x1F808240 && addr <= 0x1F80825F) {\n        //         // printf(\"8-bit SIO2_SEND%d write %02x\\n\", (addr & 4) ? 2 : 1, data);\n        //     }\n        // } break;\n    }\n\n    printf(\"sio2: Unhandled 8-bit write to address %08x\\n\", addr); exit(1);\n}\n\nvoid ps2_sio2_write32(struct ps2_sio2* sio2, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        // case 0x1F808260: /* printf(\"32-bit SIO2_FIFOIN write %08x\\n\", data); */ return;\n        // case 0x1F808264: /* printf(\"32-bit SIO2_FIFOOUT write %08x\\n\", data); */ return;\n        case 0x1F808268: sio2_write_ctrl(sio2, data); return;\n        // case 0x1F80826C: /* printf(\"32-bit SIO2_RECV1 write %08x\\n\", data); */ return;\n        // case 0x1F808270: /* printf(\"32-bit SIO2_RECV2 write %08x\\n\", data); */ return;\n        // case 0x1F808274: /* printf(\"32-bit SIO2_RECV3 write %08x\\n\", data); */ return;\n        case 0x1F808280: /* printf(\"32-bit SIO2_ISTAT write %08x\\n\", data); */ sio2->istat &= ~(data & 3); return;\n        default: {\n            if (addr >= 0x1F808200 && addr <= 0x1F80823F) {\n                int index = (addr & 0x3f) >> 2;\n\n                sio2->send3[index] = data;\n\n                if (!index) sio2_reset(sio2);\n\n                // printf(\"sio2: 32-bit SEND3 write %08x\\n\", data);\n\n                return;\n            }\n            if (addr >= 0x1F808240 && addr <= 0x1F80825F) {\n                // printf(\"32-bit SIO2_SEND%d write %08x\\n\", (addr & 4) ? 2 : 1, data);\n\n                return;\n            }\n        } break;\n    }\n\n    printf(\"sio2: Unhandled 32-bit write to address %08x (%08x)\\n\", addr, data); // exit(1);\n}\n\nvoid ps2_sio2_attach_device(struct ps2_sio2* sio2, struct sio2_device dev, int port) {\n    sio2->port[port] = dev;\n}\n\nvoid ps2_sio2_detach_device(struct ps2_sio2* sio2, int port) {\n    struct sio2_device* dev = &sio2->port[port];\n\n    if (dev->detach)\n        dev->detach(dev->udata);\n\n    dev->handle_command = 0;\n    dev->detach = 0;\n    dev->udata = NULL;\n}"
  },
  {
    "path": "src/iop/sio2.h",
    "content": "#ifndef SIO2_H\n#define SIO2_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"queue.h\"\n#include \"intc.h\"\n#include \"scheduler.h\"\n#include \"dma.h\"\n\n#define SIO2_DEV_PAD  0x01\n#define SIO2_DEV_PS1PAD 0x42 // ?\n#define SIO2_DEV_MTAP 0x21\n#define SIO2_DEV_IR   0x61\n#define SIO2_DEV_MCD  0x81\n\nstruct ps2_sio2;\n\nstruct sio2_device {\n    void (*handle_command)(struct ps2_sio2*, void*, int);\n    void (*detach)(void*);\n    void* udata;\n};\n\nstruct ps2_sio2 {\n    struct sio2_device port[4];\n\n    uint32_t ctrl;\n    uint32_t send3[16];\n    uint32_t send1[8];\n    uint32_t send2[8];\n    struct queue_state* in;\n    struct queue_state* out;\n    uint32_t recv1;\n    uint32_t recv2;\n    uint32_t recv3;\n    uint32_t istat;\n\n    int send3_index;\n\n    struct ps2_iop_dma* dma;\n    struct ps2_iop_intc* intc;\n    struct sched_state* sched;\n};\n\nstruct ps2_sio2* ps2_sio2_create(void);\nvoid ps2_sio2_init(struct ps2_sio2* sio2, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched);\nvoid ps2_sio2_destroy(struct ps2_sio2* sio2);\nuint64_t ps2_sio2_read8(struct ps2_sio2* sio2, uint32_t addr);\nuint64_t ps2_sio2_read32(struct ps2_sio2* sio2, uint32_t addr);\nvoid ps2_sio2_write8(struct ps2_sio2* sio2, uint32_t addr, uint64_t data);\nvoid ps2_sio2_write32(struct ps2_sio2* sio2, uint32_t addr, uint64_t data);\nvoid ps2_sio2_attach_device(struct ps2_sio2* sio2, struct sio2_device dev, int port);\nvoid ps2_sio2_detach_device(struct ps2_sio2* sio2, int port);\nvoid sio2_dma_reset(struct ps2_sio2* sio2);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/spu2.c",
    "content": "#include <stdint.h>\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"spu2.h\"\n\nFILE* output = NULL;\nuint32_t chunk_size = 0;\n\nstatic const int16_t g_spu_gauss_table[] = {\n    -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001,\n    -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001,\n    0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001,\n    0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003,\n    0x0003, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006, 0x0007, 0x0007,\n    0x0008, 0x0009, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E,\n    0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0015, 0x0016, 0x0018,\n    0x0019, 0x001B, 0x001C, 0x001E, 0x0020, 0x0021, 0x0023, 0x0025,\n    0x0027, 0x0029, 0x002C, 0x002E, 0x0030, 0x0033, 0x0035, 0x0038,\n    0x003A, 0x003D, 0x0040, 0x0043, 0x0046, 0x0049, 0x004D, 0x0050,\n    0x0054, 0x0057, 0x005B, 0x005F, 0x0063, 0x0067, 0x006B, 0x006F,\n    0x0074, 0x0078, 0x007D, 0x0082, 0x0087, 0x008C, 0x0091, 0x0096,\n    0x009C, 0x00A1, 0x00A7, 0x00AD, 0x00B3, 0x00BA, 0x00C0, 0x00C7,\n    0x00CD, 0x00D4, 0x00DB, 0x00E3, 0x00EA, 0x00F2, 0x00FA, 0x0101,\n    0x010A, 0x0112, 0x011B, 0x0123, 0x012C, 0x0135, 0x013F, 0x0148,\n    0x0152, 0x015C, 0x0166, 0x0171, 0x017B, 0x0186, 0x0191, 0x019C,\n    0x01A8, 0x01B4, 0x01C0, 0x01CC, 0x01D9, 0x01E5, 0x01F2, 0x0200,\n    0x020D, 0x021B, 0x0229, 0x0237, 0x0246, 0x0255, 0x0264, 0x0273,\n    0x0283, 0x0293, 0x02A3, 0x02B4, 0x02C4, 0x02D6, 0x02E7, 0x02F9,\n    0x030B, 0x031D, 0x0330, 0x0343, 0x0356, 0x036A, 0x037E, 0x0392,\n    0x03A7, 0x03BC, 0x03D1, 0x03E7, 0x03FC, 0x0413, 0x042A, 0x0441,\n    0x0458, 0x0470, 0x0488, 0x04A0, 0x04B9, 0x04D2, 0x04EC, 0x0506,\n    0x0520, 0x053B, 0x0556, 0x0572, 0x058E, 0x05AA, 0x05C7, 0x05E4,\n    0x0601, 0x061F, 0x063E, 0x065C, 0x067C, 0x069B, 0x06BB, 0x06DC,\n    0x06FD, 0x071E, 0x0740, 0x0762, 0x0784, 0x07A7, 0x07CB, 0x07EF,\n    0x0813, 0x0838, 0x085D, 0x0883, 0x08A9, 0x08D0, 0x08F7, 0x091E,\n    0x0946, 0x096F, 0x0998, 0x09C1, 0x09EB, 0x0A16, 0x0A40, 0x0A6C,\n    0x0A98, 0x0AC4, 0x0AF1, 0x0B1E, 0x0B4C, 0x0B7A, 0x0BA9, 0x0BD8,\n    0x0C07, 0x0C38, 0x0C68, 0x0C99, 0x0CCB, 0x0CFD, 0x0D30, 0x0D63,\n    0x0D97, 0x0DCB, 0x0E00, 0x0E35, 0x0E6B, 0x0EA1, 0x0ED7, 0x0F0F,\n    0x0F46, 0x0F7F, 0x0FB7, 0x0FF1, 0x102A, 0x1065, 0x109F, 0x10DB,\n    0x1116, 0x1153, 0x118F, 0x11CD, 0x120B, 0x1249, 0x1288, 0x12C7,\n    0x1307, 0x1347, 0x1388, 0x13C9, 0x140B, 0x144D, 0x1490, 0x14D4,\n    0x1517, 0x155C, 0x15A0, 0x15E6, 0x162C, 0x1672, 0x16B9, 0x1700,\n    0x1747, 0x1790, 0x17D8, 0x1821, 0x186B, 0x18B5, 0x1900, 0x194B,\n    0x1996, 0x19E2, 0x1A2E, 0x1A7B, 0x1AC8, 0x1B16, 0x1B64, 0x1BB3,\n    0x1C02, 0x1C51, 0x1CA1, 0x1CF1, 0x1D42, 0x1D93, 0x1DE5, 0x1E37,\n    0x1E89, 0x1EDC, 0x1F2F, 0x1F82, 0x1FD6, 0x202A, 0x207F, 0x20D4,\n    0x2129, 0x217F, 0x21D5, 0x222C, 0x2282, 0x22DA, 0x2331, 0x2389,\n    0x23E1, 0x2439, 0x2492, 0x24EB, 0x2545, 0x259E, 0x25F8, 0x2653,\n    0x26AD, 0x2708, 0x2763, 0x27BE, 0x281A, 0x2876, 0x28D2, 0x292E,\n    0x298B, 0x29E7, 0x2A44, 0x2AA1, 0x2AFF, 0x2B5C, 0x2BBA, 0x2C18,\n    0x2C76, 0x2CD4, 0x2D33, 0x2D91, 0x2DF0, 0x2E4F, 0x2EAE, 0x2F0D,\n    0x2F6C, 0x2FCC, 0x302B, 0x308B, 0x30EA, 0x314A, 0x31AA, 0x3209,\n    0x3269, 0x32C9, 0x3329, 0x3389, 0x33E9, 0x3449, 0x34A9, 0x3509,\n    0x3569, 0x35C9, 0x3629, 0x3689, 0x36E8, 0x3748, 0x37A8, 0x3807,\n    0x3867, 0x38C6, 0x3926, 0x3985, 0x39E4, 0x3A43, 0x3AA2, 0x3B00,\n    0x3B5F, 0x3BBD, 0x3C1B, 0x3C79, 0x3CD7, 0x3D35, 0x3D92, 0x3DEF,\n    0x3E4C, 0x3EA9, 0x3F05, 0x3F62, 0x3FBD, 0x4019, 0x4074, 0x40D0,\n    0x412A, 0x4185, 0x41DF, 0x4239, 0x4292, 0x42EB, 0x4344, 0x439C,\n    0x43F4, 0x444C, 0x44A3, 0x44FA, 0x4550, 0x45A6, 0x45FC, 0x4651,\n    0x46A6, 0x46FA, 0x474E, 0x47A1, 0x47F4, 0x4846, 0x4898, 0x48E9,\n    0x493A, 0x498A, 0x49D9, 0x4A29, 0x4A77, 0x4AC5, 0x4B13, 0x4B5F,\n    0x4BAC, 0x4BF7, 0x4C42, 0x4C8D, 0x4CD7, 0x4D20, 0x4D68, 0x4DB0,\n    0x4DF7, 0x4E3E, 0x4E84, 0x4EC9, 0x4F0E, 0x4F52, 0x4F95, 0x4FD7,\n    0x5019, 0x505A, 0x509A, 0x50DA, 0x5118, 0x5156, 0x5194, 0x51D0,\n    0x520C, 0x5247, 0x5281, 0x52BA, 0x52F3, 0x532A, 0x5361, 0x5397,\n    0x53CC, 0x5401, 0x5434, 0x5467, 0x5499, 0x54CA, 0x54FA, 0x5529,\n    0x5558, 0x5585, 0x55B2, 0x55DE, 0x5609, 0x5632, 0x565B, 0x5684,\n    0x56AB, 0x56D1, 0x56F6, 0x571B, 0x573E, 0x5761, 0x5782, 0x57A3,\n    0x57C3, 0x57E2, 0x57FF, 0x581C, 0x5838, 0x5853, 0x586D, 0x5886,\n    0x589E, 0x58B5, 0x58CB, 0x58E0, 0x58F4, 0x5907, 0x5919, 0x592A,\n    0x593A, 0x5949, 0x5958, 0x5965, 0x5971, 0x597C, 0x5986, 0x598F,\n    0x5997, 0x599E, 0x59A4, 0x59A9, 0x59AD, 0x59B0, 0x59B2, 0x59B3\n};\n\nstruct ps2_spu2* ps2_spu2_create(void) {\n    return (struct ps2_spu2*)malloc(sizeof(struct ps2_spu2));\n}\n\nstruct wav_hdr {\n    char riff[4];\n    uint32_t size;\n    char wave[4];\n    char fmt[4];\n    uint32_t block_size;\n    uint16_t audio_format;\n    uint16_t num_channels;\n    uint32_t samplerate;\n    uint32_t bytes_per_sec;\n    uint16_t bytes_per_block;\n    uint16_t bits_per_sample;\n};\n\nstruct wav_chunk {\n    char id[4]; // \"data\"\n    uint32_t size;\n};\n\n//    FormatBlocID    (4 bytes) : Identifier « fmt␣ »  (0x66, 0x6D, 0x74, 0x20)\n//    BlocSize        (4 bytes) : Chunk size minus 8 bytes, which is 16 bytes here  (0x10)\n//    AudioFormat     (2 bytes) : Audio format (1: PCM integer, 3: IEEE 754 float)\n//    NbrChannels     (2 bytes) : Number of channels\n//    Frequency       (4 bytes) : Sample rate (in hertz)\n//    BytePerSec      (4 bytes) : Number of bytes to read per second (Frequency * BytePerBloc).\n//    BytePerBloc     (2 bytes) : Number of bytes per block (NbrChannels * BitsPerSample / 8).\n//    BitsPerSample   (2 bytes) : Number of bits per sample\n\n// void spu2_adma_cb(void* udata, int overshoot) {\n//     struct ps2_spu2* spu2 = (struct ps2_spu2*)udata;\n\n//     // Request more data\n//     if (spu2->dma->spu1.transfer_size)\n//         iop_dma_handle_spu1_adma(spu2->dma);\n\n//     struct sched_event event;\n\n//     event.name = \"spu2 adma callback\";\n//     event.callback = spu2_adma_cb;\n//     event.cycles = 49152;\n//     event.udata = spu2;\n\n//     sched_schedule(spu2->sched, event);\n// }\n\nvoid ps2_spu2_init(struct ps2_spu2* spu2, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched) {\n    memset(spu2, 0, sizeof(struct ps2_spu2));\n\n    spu2->dma = dma;\n    spu2->intc = intc;\n    spu2->sched = sched;\n\n    // CORE0/1 DMA status (ready)\n    spu2->c[0].stat = 0x80;\n    spu2->c[1].stat = 0x80;\n    spu2->c[0].endx = 0x00ffffff;\n    spu2->c[1].endx = 0x00ffffff;\n\n    // output = fopen(\"adma.wav\", \"wb\");\n\n    // fseek(output, sizeof(struct wav_hdr) + sizeof(struct wav_chunk), SEEK_SET);\n\n    // struct sched_event event;\n\n    // event.name = \"spu2 adma callback\";\n    // event.callback = spu2_adma_cb;\n    // event.cycles = 49152;\n    // event.udata = spu2;\n\n    // sched_schedule(spu2->sched, event);\n}\n\nvoid spu2_irq(struct ps2_spu2* spu2, int c) {\n    if (spu2->spdif_irq & (4 << c))\n        return;\n\n    spu2->spdif_irq |= 4 << c;\n\n    // printf(\"spu2: IRQ fired\\n\");\n\n    ps2_iop_intc_irq(spu2->intc, IOP_INTC_SPU2);\n}\n\nvoid spu2_check_irq(struct ps2_spu2* spu2, uint32_t addr) {\n    for (int i = 0; i < 2; i++) {\n        if ((addr == spu2->c[i].irqa) && (spu2->c[i].attr & (1 << 6))) {\n            spu2_irq(spu2, i);\n        }\n    }\n}\n\nvoid spu2_decode_adpcm_block(struct ps2_spu2* spu2, struct spu2_voice* v);\nvoid adsr_load_attack(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v);\nvoid adsr_load_release(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v, int i);\n\nvoid spu2_write_kon(struct ps2_spu2* spu2, int c, int h, uint64_t data) {\n    // printf(\"spu2: core%d kon%c %04x\\n\", c, h ? 'h' : 'l', data);\n\n    for (int i = 0; i < 16; i++) {\n        if (!(data & (1 << i)))\n            continue;\n\n        int idx = i+h*16;\n\n        if (idx >= 24)\n            break;\n\n        struct spu2_core* cr = &spu2->c[c];\n        struct spu2_voice* v = &cr->v[idx];\n\n        // if (v->playing)\n        //     continue;\n\n        // Make sure to clear the internal state of a voice\n        // before playing\n        v->playing = 1;\n        v->counter = 0;\n        v->h[0] = 0;\n        v->h[1] = 0;\n\n        for (int i = 0; i < 28; i++)\n            v->buf[i] = 0;\n\n        v->loop_start = 0;\n        v->loop = 0;\n        v->loop_end = 0;\n        v->prev_sample_index = 0;\n\n        for (int i = 0; i < 4; i++)\n            v->s[i] = 0;\n\n        v->adsr_cycles_left = 0;\n        v->adsr_phase = 0;\n        v->adsr_cycles_reload = 0;\n        v->adsr_cycles = 0;\n        v->adsr_mode = 0;\n        v->adsr_dir = 0;\n        v->adsr_shift = 0;\n        v->adsr_step = 0;\n        v->adsr_pending_step = 0;\n        v->adsr_sustain_level = 0;\n\n        v->nax = v->ssa;\n        v->adsr_sustain_level = ((v->adsr1 & 0xf) + 1) * 0x800;\n\n        cr->endx &= ~(1u << idx);\n\n        // printf(\"spu2: CORE%d Voice %d playing, ssa=%08x lsax=%08x nax=%08x voll=%04x volr=%04x\\n\", c, i+h*16, v->ssa, v->lsax, v->nax, v->voll, v->volr);\n\n        adsr_load_attack(spu2, cr, v);\n        spu2_decode_adpcm_block(spu2, v);\n\n        v->envx = 0x7fff;\n    }\n}\n\nvoid spu2_write_koff(struct ps2_spu2* spu2, int c, int h, uint64_t data) {\n    // printf(\"spu2: core%d koff%c %04x\\n\", c, h ? 'h' : 'l', data);\n\n    for (int i = 0; i < 16; i++) {\n        if (!(data & (1 << i)))\n            continue;\n\n        int v = i+h*16;\n\n        if (v >= 24)\n            break;\n\n        // spu2->c[c].v[i+h*16].playing = 0;\n\n        // printf(\"spu2: core %d voice %d koff\\n\", c, v);\n        if (!spu2->c[c].v[v].playing)\n            continue;\n\n        adsr_load_release(spu2, &spu2->c[c], &spu2->c[c].v[v], v);\n    }\n}\n\nvoid adma_write_data(struct ps2_spu2* spu2, int c, uint64_t data) {\n    spu2->ram[(c ? 0x2400 : 0x2000) + ((spu2->c[c].memin_write_addr++) & 0x3ff)] = data;\n}\n\nvoid spu2_write_data(struct ps2_spu2* spu2, int c, uint64_t data) {\n    // printf(\"spu2: core%d data=%04x tsa=%08x\\n\", c, data, spu2->c[c].tsa);\n    if (spu2->c[c].admas & (1 << c)) {\n        adma_write_data(spu2, c, data);\n\n        return;\n    }\n\n    spu2_check_irq(spu2, spu2->c[c].tsa);\n\n    spu2->ram[spu2->c[c].tsa++] = data;\n\n    spu2->c[c].tsa &= 0xfffff;\n}\n\nvoid spu2_core0_reset_handler(void* udata, int overshoot) {\n    struct ps2_spu2* spu2 = (struct ps2_spu2*)udata;\n\n    printf(\"spu2: core0 Reset\\n\");\n\n    spu2->c[0].stat |= 0x80;\n}\n\nvoid spu2_core1_reset_handler(void* udata, int overshoot) {\n    struct ps2_spu2* spu2 = (struct ps2_spu2*)udata;\n\n    printf(\"spu2: core1 Reset\\n\");\n\n    spu2->c[1].stat |= 0x80;\n}\n\nvoid spu2_write_attr(struct ps2_spu2* spu2, int c, uint64_t data) {\n    if (spu2->c[c].attr & (1 << 6)) {\n        if (!(data & (1 << 6))) {\n            spu2->spdif_irq &= ~(4 << c);\n        }\n    }\n\n    spu2->c[c].attr = data & 0x7fff;\n\n    if (data & 0x8000) {\n        struct sched_event event;\n\n        printf(\"spu2: Resetting core%d\\n\", c);\n\n        event.callback = c ? spu2_core1_reset_handler : spu2_core0_reset_handler;\n        event.cycles = 10000;\n        event.name = \"SPU2 Reset\";\n        event.udata = spu2;\n\n        sched_schedule(spu2->sched, event);\n\n        spu2->c[c].stat = 0;\n    }\n}\n\nuint64_t ps2_spu2_read16(struct ps2_spu2* spu2, uint32_t addr) {\n    addr &= 0x7ff;\n\n    if ((addr >= 0x000 && addr <= 0x17f) || (addr >= 0x400 && addr <= 0x57f)) {\n        int core = (addr >> 10) & 1;\n        int voice = (addr >> 4) & 0x1f;\n\n        switch (addr & 0xf) {\n            case 0x0: return spu2->c[core].v[voice].voll;\n            case 0x2: return spu2->c[core].v[voice].volr;\n            case 0x4: return spu2->c[core].v[voice].pitch;\n            case 0x6: return spu2->c[core].v[voice].adsr1;\n            case 0x8: return spu2->c[core].v[voice].adsr2;\n            case 0xA: return spu2->c[core].v[voice].envx;\n            case 0xC: return spu2->c[core].v[voice].volxl;\n            case 0xE: return spu2->c[core].v[voice].volxr;\n        }\n    }\n\n    if ((addr >= 0x180 && addr <= 0x1bf) || (addr >= 0x580 && addr <= 0x5bf)) {\n        int core = (addr >> 10) & 1;\n\n        switch (addr & 0x3ff) {\n            case 0x180: return spu2->c[core].pmon & 0xffff;\n            case 0x182: return spu2->c[core].pmon >> 16;\n            case 0x184: return spu2->c[core].non & 0xffff;\n            case 0x186: return spu2->c[core].non >> 16;\n            case 0x188: return spu2->c[core].vmixl & 0xffff;\n            case 0x18A: return spu2->c[core].vmixl >> 16;\n            case 0x18C: return spu2->c[core].vmixel & 0xffff;\n            case 0x18E: return spu2->c[core].vmixel >> 16;\n            case 0x190: return spu2->c[core].vmixr & 0xffff;\n            case 0x192: return spu2->c[core].vmixr >> 16;\n            case 0x194: return spu2->c[core].vmixer & 0xffff;\n            case 0x196: return spu2->c[core].vmixer >> 16;\n            case 0x198: return spu2->c[core].mmix;\n            case 0x19A: return spu2->c[core].attr;\n            case 0x19C: return spu2->c[core].irqa >> 16;\n            case 0x19E: return spu2->c[core].irqa & 0xffff;\n            case 0x1A0: return spu2->c[core].kon & 0xffff;\n            case 0x1A2: return spu2->c[core].kon >> 16;\n            case 0x1A4: return spu2->c[core].koff & 0xffff;\n            case 0x1A6: return spu2->c[core].koff >> 16;\n            case 0x1A8: return spu2->c[core].tsa >> 16;\n            case 0x1AA: return spu2->c[core].tsa & 0xffff;\n            case 0x1AC: return spu2->c[core].data;\n            case 0x1AE: return spu2->c[core].ctrl;\n            case 0x1B0: return spu2->c[core].admas;\n            case 0x2E0: return spu2->c[core].esa >> 16;\n            case 0x2E2: return spu2->c[core].esa & 0xffff;\n        }\n    }\n\n    if ((addr >= 0x1c0 && addr <= 0x2df) || (addr >= 0x5c0 && addr <= 0x6df)) {\n        int core = (addr >> 10) & 1;\n\n        addr -= 0x1c0 + 0x400 * core;\n\n        int voice = addr / 12;\n        int reg = addr % 12;\n\n        switch (reg) {\n            case 0x0: return spu2->c[core].v[voice].ssa >> 16;\n            case 0x2: return spu2->c[core].v[voice].ssa & 0xffff;\n            case 0x4: return spu2->c[core].v[voice].lsax >> 16;\n            case 0x6: return spu2->c[core].v[voice].lsax & 0xffff;\n            case 0x8: return spu2->c[core].v[voice].nax >> 16;\n            case 0xA: return spu2->c[core].v[voice].nax & 0xffff;\n        }\n    }\n\n    if ((addr >= 0x2e0 && addr <= 0x347) || (addr >= 0x6e0 && addr <= 0x747)) {\n        int core = (addr >> 10) & 1;\n\n        switch (addr & 0x3ff) {\n            case 0x2E0: return spu2->c[core].esa >> 16;\n            case 0x2E2: return spu2->c[core].esa & 0xffff;\n            case 0x2E4: return spu2->c[core].fb_src_a >> 16;\n            case 0x2E6: return spu2->c[core].fb_src_a & 0xffff;\n            case 0x2E8: return spu2->c[core].fb_src_b >> 16;\n            case 0x2EA: return spu2->c[core].fb_src_b & 0xffff;\n            case 0x2EC: return spu2->c[core].iir_dest_a0 >> 16;\n            case 0x2EE: return spu2->c[core].iir_dest_a0 & 0xffff;\n            case 0x2F0: return spu2->c[core].iir_dest_a1 >> 16;\n            case 0x2F2: return spu2->c[core].iir_dest_a1 & 0xffff;\n            case 0x2F4: return spu2->c[core].acc_src_a0 >> 16;\n            case 0x2F6: return spu2->c[core].acc_src_a0 & 0xffff;\n            case 0x2F8: return spu2->c[core].acc_src_a1 >> 16;\n            case 0x2FA: return spu2->c[core].acc_src_a1 & 0xffff;\n            case 0x2FC: return spu2->c[core].acc_src_b0 >> 16;\n            case 0x2FE: return spu2->c[core].acc_src_b0 & 0xffff;\n            case 0x300: return spu2->c[core].acc_src_b1 >> 16;\n            case 0x302: return spu2->c[core].acc_src_b1 & 0xffff;\n            case 0x304: return spu2->c[core].iir_src_a0 >> 16;\n            case 0x306: return spu2->c[core].iir_src_a0 & 0xffff;\n            case 0x308: return spu2->c[core].iir_src_a1 >> 16;\n            case 0x30A: return spu2->c[core].iir_src_a1 & 0xffff;\n            case 0x30C: return spu2->c[core].iir_dest_b0 >> 16;\n            case 0x30E: return spu2->c[core].iir_dest_b0 & 0xffff;\n            case 0x310: return spu2->c[core].iir_dest_b1 >> 16;\n            case 0x312: return spu2->c[core].iir_dest_b1 & 0xffff;\n            case 0x314: return spu2->c[core].acc_src_c0 >> 16;\n            case 0x316: return spu2->c[core].acc_src_c0 & 0xffff;\n            case 0x318: return spu2->c[core].acc_src_c1 >> 16;\n            case 0x31A: return spu2->c[core].acc_src_c1 & 0xffff;\n            case 0x31C: return spu2->c[core].acc_src_d0 >> 16;\n            case 0x31E: return spu2->c[core].acc_src_d0 & 0xffff;\n            case 0x320: return spu2->c[core].acc_src_d1 >> 16;\n            case 0x322: return spu2->c[core].acc_src_d1 & 0xffff;\n            case 0x324: return spu2->c[core].iir_src_b1 >> 16;\n            case 0x326: return spu2->c[core].iir_src_b1 & 0xffff;\n            case 0x328: return spu2->c[core].iir_src_b0 >> 16;\n            case 0x32A: return spu2->c[core].iir_src_b0 & 0xffff;\n            case 0x32C: return spu2->c[core].mix_dest_a0 >> 16;\n            case 0x32E: return spu2->c[core].mix_dest_a0 & 0xffff;\n            case 0x330: return spu2->c[core].mix_dest_a1 >> 16;\n            case 0x332: return spu2->c[core].mix_dest_a1 & 0xffff;\n            case 0x334: return spu2->c[core].mix_dest_b0 >> 16;\n            case 0x336: return spu2->c[core].mix_dest_b0 & 0xffff;\n            case 0x338: return spu2->c[core].mix_dest_b1 >> 16;\n            case 0x33A: return spu2->c[core].mix_dest_b1 & 0xffff;\n            case 0x33C: return spu2->c[core].eea >> 16;\n            case 0x33E: return spu2->c[core].eea & 0xffff;\n            case 0x340: return spu2->c[core].endx >> 16;\n            case 0x342: return spu2->c[core].endx & 0xffff;\n            case 0x344: return spu2->c[core].stat;\n            case 0x346: return spu2->c[core].ends;\n        }\n    }\n\n    // Misc.\n    switch (addr & 0x7ff) {\n        case 0x760: return spu2->c[0].mvoll;\n        case 0x762: return spu2->c[0].mvolr;\n        case 0x764: return spu2->c[0].evoll;\n        case 0x766: return spu2->c[0].evolr;\n        case 0x768: return spu2->c[0].avoll;\n        case 0x76A: return spu2->c[0].avolr;\n        case 0x76C: return spu2->c[0].bvoll;\n        case 0x76E: return spu2->c[0].bvolr;\n        case 0x770: return spu2->c[0].mvolxl;\n        case 0x772: return spu2->c[0].mvolxr;\n        case 0x774: return spu2->c[0].iir_alpha;\n        case 0x776: return spu2->c[0].acc_coef_a;\n        case 0x778: return spu2->c[0].acc_coef_b;\n        case 0x77A: return spu2->c[0].acc_coef_c;\n        case 0x77C: return spu2->c[0].acc_coef_d;\n        case 0x77E: return spu2->c[0].iir_coef;\n        case 0x780: return spu2->c[0].fb_alpha;\n        case 0x782: return spu2->c[0].fb_x;\n        case 0x784: return spu2->c[0].in_coef_l;\n        case 0x786: return spu2->c[0].in_coef_r;\n        case 0x788: return spu2->c[1].mvoll;\n        case 0x78a: return spu2->c[1].mvolr;\n        case 0x78c: return spu2->c[1].evoll;\n        case 0x78e: return spu2->c[1].evolr;\n        case 0x790: return spu2->c[1].avoll;\n        case 0x792: return spu2->c[1].avolr;\n        case 0x794: return spu2->c[1].bvoll;\n        case 0x796: return spu2->c[1].bvolr;\n        case 0x798: return spu2->c[1].mvolxl;\n        case 0x79a: return spu2->c[1].mvolxr;\n        case 0x79c: return spu2->c[1].iir_alpha;\n        case 0x79e: return spu2->c[1].acc_coef_a;\n        case 0x7a0: return spu2->c[1].acc_coef_b;\n        case 0x7a2: return spu2->c[1].acc_coef_c;\n        case 0x7a4: return spu2->c[1].acc_coef_d;\n        case 0x7a6: return spu2->c[1].iir_coef;\n        case 0x7a8: return spu2->c[1].fb_alpha;\n        case 0x7aa: return spu2->c[1].fb_x;\n        case 0x7ac: return spu2->c[1].in_coef_l;\n        case 0x7ae: return spu2->c[1].in_coef_r;\n        case 0x7C0: return spu2->spdif_out;\n        case 0x7C2: return spu2->spdif_irq;\n        case 0x7C6: return spu2->spdif_mode;\n        case 0x7C8: return spu2->spdif_media;\n        case 0x7CA: return spu2->spdif_copy;\n    }\n\n    printf(\"spu2: Unhandled register %x read\\n\", addr & 0x7ff);\n\n    return 0;\n}\n\n#define SPU2_WRITEL(cr, r) { spu2->c[cr].r &= 0xffff0000; spu2->c[cr].r |= data; }\n#define SPU2_WRITEH(cr, r) { spu2->c[cr].r &= 0x0000ffff; spu2->c[cr].r |= data << 16; }\n#define SPU2_WRITEL_V(cr, vc, r) { spu2->c[cr].v[vc].r &= 0xffff0000; spu2->c[cr].v[vc].r |= data; }\n#define SPU2_WRITEH_V(cr, vc, r) { spu2->c[cr].v[vc].r &= 0x0000ffff; spu2->c[cr].v[vc].r |= data << 16; }\n\nvoid ps2_spu2_write16(struct ps2_spu2* spu2, uint32_t addr, uint64_t data) {\n    addr &= 0x7ff;\n\n    if ((addr >= 0x000 && addr <= 0x17f) || (addr >= 0x400 && addr <= 0x57f)) {\n        int core = (addr >> 10) & 1;\n        int voice = (addr >> 4) & 0x1f;\n\n        switch (addr & 0xf) {\n            case 0x0: spu2->c[core].v[voice].voll = data; return;\n            case 0x2: spu2->c[core].v[voice].volr = data; return;\n            case 0x4: spu2->c[core].v[voice].pitch = data; return;\n            case 0x6: spu2->c[core].v[voice].adsr1 = data; return;\n            case 0x8: spu2->c[core].v[voice].adsr2 = data; return;\n            case 0xA: spu2->c[core].v[voice].envx = data; return;\n            case 0xC: spu2->c[core].v[voice].volxl = data; return;\n            case 0xE: spu2->c[core].v[voice].volxr = data; return;\n        }\n    }\n\n    if ((addr >= 0x180 && addr <= 0x1bf) || (addr >= 0x580 && addr <= 0x5bf)) {\n        int core = (addr >> 10) & 1;\n\n        switch (addr & 0x3ff) {\n            case 0x180: SPU2_WRITEL(core, pmon); return;\n            case 0x182: SPU2_WRITEH(core, pmon); return;\n            case 0x184: SPU2_WRITEL(core, non); return;\n            case 0x186: SPU2_WRITEH(core, non); return;\n            case 0x188: SPU2_WRITEL(core, vmixl); return;\n            case 0x18A: SPU2_WRITEH(core, vmixl); return;\n            case 0x18C: SPU2_WRITEL(core, vmixel); return;\n            case 0x18E: SPU2_WRITEH(core, vmixel); return;\n            case 0x190: SPU2_WRITEL(core, vmixr); return;\n            case 0x192: SPU2_WRITEH(core, vmixr); return;\n            case 0x194: SPU2_WRITEL(core, vmixer); return;\n            case 0x196: SPU2_WRITEH(core, vmixer); return;\n            case 0x198: spu2->c[core].mmix = data; return;\n            case 0x19A: spu2_write_attr(spu2, core, data); return;\n            case 0x19C: SPU2_WRITEH(core, irqa); return;\n            case 0x19E: SPU2_WRITEL(core, irqa); return;\n            case 0x1A0: spu2_write_kon(spu2, core, 0, data); return;\n            case 0x1A2: spu2_write_kon(spu2, core, 1, data); return;\n            case 0x1A4: spu2_write_koff(spu2, core, 0, data); return;\n            case 0x1A6: spu2_write_koff(spu2, core, 1, data); return;\n            case 0x1A8: SPU2_WRITEH(core, tsa); return;\n            case 0x1AA: SPU2_WRITEL(core, tsa); return;\n            case 0x1AC: spu2_write_data(spu2, core, data); return;\n            case 0x1AE: spu2->c[core].ctrl = data; return;\n            case 0x1B0: spu2->c[core].admas = data; return;\n        }\n    }\n\n    if ((addr >= 0x1c0 && addr <= 0x2df) || (addr >= 0x5c0 && addr <= 0x6df)) {\n        int core = (addr >> 10) & 1;\n\n        addr -= (core ? 0x5c0 : 0x1c0);\n\n        int voice = addr / 12;\n\n        switch (addr % 12) {\n            case 0x0: SPU2_WRITEH_V(core, voice, ssa); spu2->c[core].v[voice].ssa &= 0xfffff; return;\n            case 0x2: SPU2_WRITEL_V(core, voice, ssa); spu2->c[core].v[voice].ssa &= 0xfffff; return;\n            case 0x4: SPU2_WRITEH_V(core, voice, lsax); spu2->c[core].v[voice].lsax &= 0xfffff; return;\n            case 0x6: SPU2_WRITEL_V(core, voice, lsax); spu2->c[core].v[voice].lsax &= 0xfffff; return;\n            case 0x8: SPU2_WRITEH_V(core, voice, nax); spu2->c[core].v[voice].nax &= 0xfffff; return;\n            case 0xA: SPU2_WRITEL_V(core, voice, nax); spu2->c[core].v[voice].nax &= 0xfffff; return;\n        }\n    }\n\n    if ((addr >= 0x2e0 && addr <= 0x347) || (addr >= 0x6e0 && addr <= 0x747)) {\n        int core = (addr >> 10) & 1;\n\n        switch (addr & 0x3ff) {\n            case 0x2E0: SPU2_WRITEH(core, esa); /* To-do: Reverb spu2->c[core].eaddr = 0; */ return;\n            case 0x2E2: SPU2_WRITEL(core, esa); /* To-do: Reverb spu2->c[core].eaddr = 0; */ return;\n            case 0x2E4: SPU2_WRITEH(core, fb_src_a); return;\n            case 0x2E6: SPU2_WRITEL(core, fb_src_a); return;\n            case 0x2E8: SPU2_WRITEH(core, fb_src_b); return;\n            case 0x2EA: SPU2_WRITEL(core, fb_src_b); return;\n            case 0x2EC: SPU2_WRITEH(core, iir_dest_a0); return;\n            case 0x2EE: SPU2_WRITEL(core, iir_dest_a0); return;\n            case 0x2F0: SPU2_WRITEH(core, iir_dest_a1); return;\n            case 0x2F2: SPU2_WRITEL(core, iir_dest_a1); return;\n            case 0x2F4: SPU2_WRITEH(core, acc_src_a0); return;\n            case 0x2F6: SPU2_WRITEL(core, acc_src_a0); return;\n            case 0x2F8: SPU2_WRITEH(core, acc_src_a1); return;\n            case 0x2FA: SPU2_WRITEL(core, acc_src_a1); return;\n            case 0x2FC: SPU2_WRITEH(core, acc_src_b0); return;\n            case 0x2FE: SPU2_WRITEL(core, acc_src_b0); return;\n            case 0x300: SPU2_WRITEH(core, acc_src_b1); return;\n            case 0x302: SPU2_WRITEL(core, acc_src_b1); return;\n            case 0x304: SPU2_WRITEH(core, iir_src_a0); return;\n            case 0x306: SPU2_WRITEL(core, iir_src_a0); return;\n            case 0x308: SPU2_WRITEH(core, iir_src_a1); return;\n            case 0x30A: SPU2_WRITEL(core, iir_src_a1); return;\n            case 0x30C: SPU2_WRITEH(core, iir_dest_b0); return;\n            case 0x30E: SPU2_WRITEL(core, iir_dest_b0); return;\n            case 0x310: SPU2_WRITEH(core, iir_dest_b1); return;\n            case 0x312: SPU2_WRITEL(core, iir_dest_b1); return;\n            case 0x314: SPU2_WRITEH(core, acc_src_c0); return;\n            case 0x316: SPU2_WRITEL(core, acc_src_c0); return;\n            case 0x318: SPU2_WRITEH(core, acc_src_c1); return;\n            case 0x31A: SPU2_WRITEL(core, acc_src_c1); return;\n            case 0x31C: SPU2_WRITEH(core, acc_src_d0); return;\n            case 0x31E: SPU2_WRITEL(core, acc_src_d0); return;\n            case 0x320: SPU2_WRITEH(core, acc_src_d1); return;\n            case 0x322: SPU2_WRITEL(core, acc_src_d1); return;\n            case 0x324: SPU2_WRITEH(core, iir_src_b1); return;\n            case 0x326: SPU2_WRITEL(core, iir_src_b1); return;\n            case 0x328: SPU2_WRITEH(core, iir_src_b0); return;\n            case 0x32A: SPU2_WRITEL(core, iir_src_b0); return;\n            case 0x32C: SPU2_WRITEH(core, mix_dest_a0); return;\n            case 0x32E: SPU2_WRITEL(core, mix_dest_a0); return;\n            case 0x330: SPU2_WRITEH(core, mix_dest_a1); return;\n            case 0x332: SPU2_WRITEL(core, mix_dest_a1); return;\n            case 0x334: SPU2_WRITEH(core, mix_dest_b0); return;\n            case 0x336: SPU2_WRITEL(core, mix_dest_b0); return;\n            case 0x338: SPU2_WRITEH(core, mix_dest_b1); return;\n            case 0x33A: SPU2_WRITEL(core, mix_dest_b1); return;\n            case 0x33C: SPU2_WRITEH(core, eea); /* To-do: Reverb spu2->c[core].eaddr = 0; */ return;\n            case 0x33E: SPU2_WRITEL(core, eea); /* To-do: Reverb spu2->c[core].eaddr = 0; */ return;\n            case 0x340: SPU2_WRITEH(core, endx); return;\n            case 0x342: SPU2_WRITEL(core, endx); return;\n            case 0x344: spu2->c[core].stat = data; return;\n            case 0x346: spu2->c[core].ends = data; return;\n        }\n    }\n\n    // Misc.\n    switch (addr & 0x7ff) {\n        case 0x760: spu2->c[0].mvoll = data; return;\n        case 0x762: spu2->c[0].mvolr = data; return;\n        case 0x764: spu2->c[0].evoll = data; return;\n        case 0x766: spu2->c[0].evolr = data; return;\n        case 0x768: spu2->c[0].avoll = data; return;\n        case 0x76A: spu2->c[0].avolr = data; return;\n        case 0x76C: spu2->c[0].bvoll = data; return;\n        case 0x76E: spu2->c[0].bvolr = data; return;\n        case 0x770: spu2->c[0].mvolxl = data; return;\n        case 0x772: spu2->c[0].mvolxr = data; return;\n        case 0x774: spu2->c[0].iir_alpha = data; return;\n        case 0x776: spu2->c[0].acc_coef_a = data; return;\n        case 0x778: spu2->c[0].acc_coef_b = data; return;\n        case 0x77A: spu2->c[0].acc_coef_c = data; return;\n        case 0x77C: spu2->c[0].acc_coef_d = data; return;\n        case 0x77E: spu2->c[0].iir_coef = data; return;\n        case 0x780: spu2->c[0].fb_alpha = data; return;\n        case 0x782: spu2->c[0].fb_x = data; return;\n        case 0x784: spu2->c[0].in_coef_l = data; return;\n        case 0x786: spu2->c[0].in_coef_r = data; return;\n        case 0x788: spu2->c[1].mvoll = data; return;\n        case 0x78a: spu2->c[1].mvolr = data; return;\n        case 0x78c: spu2->c[1].evoll = data; return;\n        case 0x78e: spu2->c[1].evolr = data; return;\n        case 0x790: spu2->c[1].avoll = data; return;\n        case 0x792: spu2->c[1].avolr = data; return;\n        case 0x794: spu2->c[1].bvoll = data; return;\n        case 0x796: spu2->c[1].bvolr = data; return;\n        case 0x798: spu2->c[1].mvolxl = data; return;\n        case 0x79a: spu2->c[1].mvolxr = data; return;\n        case 0x79c: spu2->c[1].iir_alpha = data; return;\n        case 0x79e: spu2->c[1].acc_coef_a = data; return;\n        case 0x7a0: spu2->c[1].acc_coef_b = data; return;\n        case 0x7a2: spu2->c[1].acc_coef_c = data; return;\n        case 0x7a4: spu2->c[1].acc_coef_d = data; return;\n        case 0x7a6: spu2->c[1].iir_coef = data; return;\n        case 0x7a8: spu2->c[1].fb_alpha = data; return;\n        case 0x7aa: spu2->c[1].fb_x = data; return;\n        case 0x7ac: spu2->c[1].in_coef_l = data; return;\n        case 0x7ae: spu2->c[1].in_coef_r = data; return;\n        case 0x7C0: spu2->spdif_out = data; return;\n        case 0x7C2: spu2->spdif_irq = data; return;\n        case 0x7C6: spu2->spdif_mode = data; return;\n        case 0x7C8: spu2->spdif_media = data; return;\n        case 0x7CA: spu2->spdif_copy = data; return;\n    }\n\n    printf(\"spu2: Unhandled register %x write (%04lx)\\n\", addr & 0x7ff, data);\n}\n\nvoid ps2_spu2_destroy(struct ps2_spu2* spu2) {\n    // uint32_t size = ftell(output) - 8;\n\n    // struct wav_hdr hdr;\n\n    // hdr.riff[0] = 'R';\n    // hdr.riff[1] = 'I';\n    // hdr.riff[2] = 'F';\n    // hdr.riff[3] = 'F';\n    // hdr.size = size;\n    // hdr.wave[0] = 'W';\n    // hdr.wave[1] = 'A';\n    // hdr.wave[2] = 'V';\n    // hdr.wave[3] = 'E';\n    // hdr.fmt[0] = 'f';\n    // hdr.fmt[1] = 'm';\n    // hdr.fmt[2] = 't';\n    // hdr.fmt[3] = ' ';\n    // hdr.block_size = 16;\n    // hdr.audio_format = 1;\n    // hdr.num_channels = 2;\n    // hdr.samplerate = 48000;\n    // hdr.bits_per_sample = 16;\n    // hdr.bytes_per_block = 4;\n    // hdr.bytes_per_sec = 48000 * 4;\n\n    // struct wav_chunk chunk;\n\n    // chunk.id[0] = 'd';\n    // chunk.id[1] = 'a';\n    // chunk.id[2] = 't';\n    // chunk.id[3] = 'a';\n    // chunk.size = chunk_size;\n\n    // fseek(output, 0, SEEK_SET);\n    // fwrite(&hdr, sizeof(struct wav_hdr), 1, output);\n    // fwrite(&chunk, sizeof(struct wav_chunk), 1, output);\n\n    // fflush(output);\n    // fclose(output);\n\n    free(spu2);\n}\n\nstatic const struct spu2_sample silence = {\n    .u32 = 0\n};\n\nstatic const int ps_adpcm_coefs_i[5][2] = {\n    {   0 ,   0 },\n    {  60 ,   0 },\n    { 115 , -52 },\n    {  98 , -55 },\n    { 122 , -60 },\n};\n\nvoid spu2_decode_adpcm_block(struct ps2_spu2* spu2, struct spu2_voice* v) {\n    uint16_t hdr = spu2->ram[v->nax];\n\n    // if (v->nax == spu2->c[0].irqa || v->nax == spu2->c[1].irqa)\n    //     ps2_iop_intc_irq(spu2->intc, IOP_INTC_SPU2);\n\n    v->loop_end = (hdr >> 8) & 1;\n    v->loop = (hdr >> 9) & 1;\n    v->loop_start = (hdr >> 10) & 1;\n\n    // printf(\"spu2: start=%d loop=%d end=%d\\n\", v->loop_start, v->loop, v->loop_end);\n\n    int shift_factor = (hdr & 0xf);\n    int coef_index = ((hdr >> 4) & 0x7);\n\n    if (coef_index > 4) {\n        // printf(\"spu2: Invalid ADPCM coefficient index %d\\n\", coef_index);\n\n        coef_index = 4;\n    }\n\n    int32_t f0 = ps_adpcm_coefs_i[coef_index][0];\n    int32_t f1 = ps_adpcm_coefs_i[coef_index][1];\n\n    for (int i = 0; i < 28; i++) {\n        int sh = (i & 3) * 4;\n\n        uint32_t addr = v->nax + 1 + (i >> 2);\n        uint16_t n = (spu2->ram[addr] & (0xf << sh)) >> sh;\n\n        // Don't send SPU2 IRQs\n        // if (addr == spu2->c[0].irqa || addr == spu2->c[1].irqa)\n        //     ps2_iop_intc_irq(spu2->intc, IOP_INTC_SPU2);\n\n        // Sign extend t\n        int32_t t = (int16_t)((n << 12) & 0xf000) >> shift_factor;\n\n        t += (f0 * v->h[0] + f1 * v->h[1]) >> 6;\n        t = (t < INT16_MIN) ? INT16_MIN : ((t > INT16_MAX) ? INT16_MAX : t);\n\n        v->h[1] = v->h[0];\n        v->h[0] = t;\n        v->buf[i] = t;\n\n        // printf(\"spu2: buf[%d]=%04x n=%04x nax=%08x\\n\", i, s, n, v->nax);\n    }\n}\n\n#define PHASE v->adsr_phase\n#define CYCLES v->adsr_cycles\n#define EXPONENTIAL v->adsr_mode\n#define DECREASE v->adsr_dir\n#define SHIFT v->adsr_shift\n#define STEP v->adsr_step\n#define LEVEL_STEP v->adsr_pending_step\n#define LEVEL v->envx\n#define CLAMP(v, l, h) (((v) <= (l)) ? (l) : (((v) >= (h)) ? (h) : (v)))\n#define MAX(a, b) ((a) > (b) ? (a) : (b))\n\nenum {\n    ADSR_ATTACK,\n    ADSR_DECAY,\n    ADSR_SUSTAIN,\n    ADSR_RELEASE,\n    ADSR_END\n};\n\nvoid adsr_calculate_values(struct ps2_spu2* spu2, struct spu2_voice* v) {\n    CYCLES = 1 << MAX(0, SHIFT - 11);\n    LEVEL_STEP = STEP << MAX(0, 11 - SHIFT);\n\n    if (EXPONENTIAL && (LEVEL > 0x6000) && !DECREASE)\n        CYCLES *= 4;\n\n    if (EXPONENTIAL && DECREASE)\n        LEVEL_STEP = (LEVEL_STEP * LEVEL) >> 15;\n\n    v->adsr_cycles_reload = CYCLES;\n}\n\nvoid adsr_load_attack(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v) {\n    v->adsr_phase = ADSR_ATTACK;\n    v->adsr_mode  = (v->adsr1 >> 15) & 1;\n    v->adsr_shift = (v->adsr1 >> 10) & 0x1f;\n    v->adsr_step  = 7 - ((v->adsr1 >> 8) & 3);\n    v->adsr_dir   = 0;\n    v->envx       = 0;\n\n    adsr_calculate_values(spu2, v);\n\n    // printf(\"adsr: attack mode=%d shift=%d step=%d dir=%d envx=%d cycles=%d level_step=%d\\n\",\n    //     v->adsr_mode,\n    //     v->adsr_shift,\n    //     v->adsr_step,\n    //     v->adsr_dir,\n    //     v->envx,\n    //     CYCLES,\n    //     LEVEL_STEP\n    // );\n}\n\nvoid adsr_load_decay(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v) {\n    v->adsr_phase = ADSR_DECAY;\n    v->adsr_mode  = 1;\n    v->adsr_dir   = 1;\n    v->adsr_shift = (v->adsr1 >> 4) & 0xf;\n    v->adsr_step  = -8;\n    v->envx       = 0x7fff;\n\n    adsr_calculate_values(spu2, v);\n}\n\nvoid adsr_load_sustain(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v) {\n    v->adsr_phase = ADSR_SUSTAIN;\n    v->adsr_mode  = (v->adsr2 >> 15) & 1;\n    v->adsr_dir   = (v->adsr2 >> 14) & 1;\n    v->adsr_shift = (v->adsr2 >> 8) & 0x1f;\n    v->adsr_step  = (v->adsr2 >> 6) & 3;\n    v->adsr_step  = v->adsr_dir ? (-8 + v->adsr_step) : (7 - v->adsr_step);\n    v->envx       = v->adsr_sustain_level;\n\n    adsr_calculate_values(spu2, v);\n}\n\nvoid adsr_load_release(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v, int i) {\n    v->adsr_phase = ADSR_RELEASE;\n    v->adsr_mode  = (v->adsr2 >> 5) & 1;\n    v->adsr_dir   = 1;\n    v->adsr_shift = v->adsr2 & 0x1f;\n    v->adsr_step  = -8;\n\n    c->endx |= 1u << i;\n\n    adsr_calculate_values(spu2, v);\n}\n\nvoid spu2_handle_adsr(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v) {\n    if (CYCLES) {\n        CYCLES -= 1;\n\n        return;\n    }\n\n    int level = v->envx;\n\n    level += LEVEL_STEP;\n\n    switch (v->adsr_phase) {\n        case ADSR_ATTACK: {\n            level = CLAMP(level, 0x0000, 0x7fff);\n\n            if (level == 0x7fff)\n                adsr_load_decay(spu2, c, v);\n        } break;\n\n        case ADSR_DECAY: {\n            level = CLAMP(level, 0x0000, 0x7fff);\n\n            if (level <= v->adsr_sustain_level)\n                adsr_load_sustain(spu2, c, v);\n        } break;\n\n        case ADSR_SUSTAIN: {\n            level = CLAMP(level, 0x0000, 0x7fff);\n\n            /* Not stopped automatically, need to KOFF */\n        } break;\n\n        case ADSR_RELEASE: {\n            level = CLAMP(level, 0x0000, 0x7fff);\n\n            if (!level) {\n                PHASE = ADSR_END;\n                CYCLES = 0;\n\n                // v->nax = 0;\n                // v->ssa = 0;\n                // v->lsax = 0;\n\n                v->envx = 0;\n                v->playing = 0;\n            }\n        } break;\n\n        case ADSR_END: {\n            level = 0;\n            v->envx = 0;\n            v->playing = 0;\n        } break;\n    }\n\n    v->envx = level;\n\n    adsr_calculate_values(spu2, v);\n\n    CYCLES = v->adsr_cycles_reload;\n}\n\n#undef PHASE\n#undef CYCLES\n#undef MODE\n#undef DIR\n#undef SHIFT\n#undef STEP\n#undef PENDING_STEP\n#undef CLAMP\n#undef MAX\n\nstruct spu2_sample spu2_get_voice_sample(struct ps2_spu2* spu2, int cr, int vc) {\n    // if (!spu2->c[cr].v[vc].playing)\n    //     return silence;\n\n    struct spu2_core* c = &spu2->c[cr];\n    struct spu2_voice* v = &c->v[vc];\n    struct spu2_sample s;\n\n    int sample_index = v->counter >> 12;\n\n    spu2_handle_adsr(spu2, c, v);\n\n    if (sample_index > 27) {\n        sample_index -= 28;\n\n        v->counter &= 0xfff;\n        v->counter |= sample_index << 12;\n\n        if (v->loop_start)\n            v->lsax = v->nax;\n\n        v->nax += 8;\n        v->nax &= 0xfffff;\n\n        spu2_check_irq(spu2, v->nax);\n\n        if (v->loop_end) {\n            if (!v->loop) {\n                v->envx = 0;\n\n                adsr_load_release(spu2, c, v, vc);\n            } else {\n                v->nax = v->lsax;\n\n                spu2_check_irq(spu2, v->nax);\n            }\n        }\n\n        spu2_decode_adpcm_block(spu2, v);\n    }\n\n    if (v->prev_sample_index != sample_index) {\n        v->s[3] = v->s[2];\n        v->s[2] = v->s[1];\n        v->s[1] = v->s[0];\n    }\n\n    v->s[0] = v->buf[sample_index];\n\n    // Apply 4-point Gaussian interpolation\n    uint8_t gauss_index = (v->counter >> 4) & 0xff;\n    int32_t g0 = g_spu_gauss_table[0x0ff - gauss_index];\n    int32_t g1 = g_spu_gauss_table[0x1ff - gauss_index];\n    int32_t g2 = g_spu_gauss_table[0x100 + gauss_index];\n    int32_t g3 = g_spu_gauss_table[0x000 + gauss_index];\n    int32_t out;\n\n    out  = (g0 * v->s[3]) >> 15;\n    out += (g1 * v->s[2]) >> 15;\n    out += (g2 * v->s[1]) >> 15;\n    out += (g3 * v->s[0]) >> 15;\n\n    // Output voice 1 and 3 to the capture buffers\n    if (vc == 1) {\n        uint16_t addr = c->cb_out1_addr + (cr ? 0xc00 : 0x400);\n\n        c->cb_out1_addr = (c->cb_out1_addr + 1) & 0x1ff;\n\n        spu2->ram[addr] = out;\n\n        spu2_check_irq(spu2, addr);\n    }\n\n    if (vc == 3) {\n        uint16_t addr = c->cb_out3_addr + (cr ? 0xe00 : 0x600);\n\n        c->cb_out3_addr = (c->cb_out3_addr + 1) & 0x1ff;\n\n        spu2->ram[addr] = out;\n\n        spu2_check_irq(spu2, addr);\n    }\n\n    s.s16[0] = (out * v->voll) >> 15;\n    s.s16[1] = (out * v->volr) >> 15;\n    s.s16[0] = ((int32_t)s.s16[0] * v->envx) >> 15;\n    s.s16[1] = ((int32_t)s.s16[1] * v->envx) >> 15;\n\n    v->counter += v->pitch;\n\n    v->prev_sample_index = sample_index;\n\n    return s;\n}\n\nstatic inline struct spu2_sample spu2_get_adma_sample(struct ps2_spu2* spu2, int c) {\n    if (spu2->c[c].memin_write_addr < 0x200) {\n        return silence;\n    }\n\n    spu2->c[c].adma_playing = 1;\n\n    struct spu2_sample s = silence;\n\n    // 32-bit HIFI PCM mode\n    if (spu2->spdif_out & 4) {\n        int32_t left = *(int32_t*)(&spu2->ram[0x2400 + spu2->c[c].memin_read_addr]);\n        int32_t right = *(int32_t*)(&spu2->ram[0x2600 + spu2->c[c].memin_read_addr]);\n\n        s.s16[0] = (int16_t)((left >> 16) & 0xffff);\n        s.s16[1] = (int16_t)((right >> 16) & 0xffff);\n\n        spu2->c[c].memin_read_addr++;\n        spu2->c[c].memin_read_addr++;\n    } else {\n        s.s16[0] = spu2->ram[(c ? 0x2400 : 0x2000) + spu2->c[c].memin_read_addr];\n        s.s16[1] = spu2->ram[(c ? 0x2600 : 0x2200) + spu2->c[c].memin_read_addr];\n\n        spu2->c[c].memin_read_addr++;\n    }\n\n    if (spu2->c[c].memin_read_addr == 0x100) {\n        spu2->c[c].memin_read_addr = 0;\n        spu2->c[c].memin_write_addr = 0;\n        spu2->c[c].adma_playing = 0;\n\n        // Request more data\n        if (c == 0) {\n            iop_dma_handle_spu1_adma(spu2->dma);\n        } else {\n            iop_dma_handle_spu2_adma(spu2->dma);\n        }\n    }\n\n    return s;\n}\n\nstruct spu2_sample ps2_spu2_get_adma_sample(struct ps2_spu2* spu2, int c) {\n    return spu2_get_adma_sample(spu2, c);\n}\n\nstruct spu2_sample ps2_spu2_get_sample(struct ps2_spu2* spu2, int adma_enable) {\n    struct spu2_sample s = silence;\n\n    s.u16[0] = 0;\n    s.u16[1] = 0;\n\n    // ADMA\n    struct spu2_sample c0_adma = spu2_get_adma_sample(spu2, 0);\n    struct spu2_sample c1_adma = spu2_get_adma_sample(spu2, 1);\n\n    // if (output) {\n    //     if (spu2->c[0].adma_playing) {\n    //         chunk_size += sizeof(int16_t) * 2;\n    //         fwrite(&c0_adma.s16, sizeof(int16_t), 2, output);\n    //     }\n\n    //     if (spu2->c[1].adma_playing) {\n    //         chunk_size += sizeof(int16_t) * 2;\n    //         fwrite(&c1_adma.s16, sizeof(int16_t), 2, output);\n    //     }\n    // }\n\n    if (adma_enable) {\n        s.s16[0] += c0_adma.s16[0];\n        s.s16[1] += c0_adma.s16[1];\n        s.s16[0] += c1_adma.s16[0];\n        s.s16[1] += c1_adma.s16[1];\n    }\n\n    for (int i = 0; i < 24; i++) {\n        struct spu2_sample c0 = spu2_get_voice_sample(spu2, 0, i);\n        struct spu2_sample c1 = spu2_get_voice_sample(spu2, 1, i);\n\n        s.s16[0] += c0.s16[0] << 1;\n        s.s16[1] += c0.s16[1] << 1;\n        s.s16[0] += c1.s16[0] << 1;\n        s.s16[1] += c1.s16[1] << 1;\n    }\n\n    return s;\n}\n\nstruct spu2_sample ps2_spu2_get_voice_sample(struct ps2_spu2* spu2, int c, int v) {\n    return spu2_get_voice_sample(spu2, c, v);\n}\n\nint spu2_is_adma_active(struct ps2_spu2* spu2, int c) {\n    return spu2->c[c].memin_write_addr >= 0x400;\n}\n\nvoid spu2_start_adma(struct ps2_spu2* spu2, int c) {\n    spu2->c[c].adma_playing = 0;\n    spu2->c[c].memin_read_addr = 0;\n    spu2->c[c].memin_write_addr = 0;\n}"
  },
  {
    "path": "src/iop/spu2.h",
    "content": "#ifndef SPU2_H\n#define SPU2_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"scheduler.h\"\n#include \"intc.h\"\n#include \"dma.h\"\n\n#define SPU2_RAM_SIZE 0x100000 // 2 MB\n\n/* Memory ranges:\n    1f900000-1f90017f CORE0 Voice settings\n    1f900180-1f9001b1 CORE0 Common settings\n    1f9001c0-1f9002df CORE0 Voice address settings\n    1f9002e0-1f900347 CORE0 Misc. settings\n    1f900400-1f90057f CORE1 Voice settings\n    1f900580-1f9005b1 CORE1 Common settings\n    1f9005c0-1f9006df CORE1 Voice address settings\n    1f9006e0-1f900747 CORE1 Reverb/Misc. settings\n    1f900760-1f900787 CORE0 Volume/Reverb settings\n    1f900788-1f9007af CORE1 Volume/Reverb settings\n    1f9007c0-1f9007cf S/PDIF settings\n\n   Breakdown:\n    1f900000-1f90017f CORE0 Voice settings:\n        1f900XX0 Voice X (0-23) VOLL                  (S) voice volume (left)\n        1f900XX2 Voice X (0-23) VOLR                  (S) voice volume (right)\n        1f900XX4 Voice X (0-23) PITCH                 (S) voice pitch\n        1f900XX6 Voice X (0-23) ADSR1                 (S) voice envelope (AR, DR, SL)\n        1f900XX8 Voice X (0-23) ADSR2                 (S) voice envelope (SR, RR)\n        1f900XXa Voice X (0-23) ENVX                  (S) voice envelope (current value)\n        1f900XXc Voice X (0-23) VOLXL                 (S) voice volume (current value left)\n        1f900XXe Voice X (0-23) VOLXR                 (S) voice volume (current value right)\n    1f900180-1f9001b1 CORE0 Common settings\n        1f900180 PMON                                 (P) pitch modulation on\n        1f900184 NON                                  (P) noise generator on\n        1f900188 VMIXL                                (P) voice output mixing (dry left)\n        1f90018C VMIXEL                               (P) voice output mixing (wet left)\n        1f900190 VMIXR                                (P) voice output mixing (dry right)\n        1f900194 VMIXER                               (P) voice output mixing (wet right)\n        1f900198 MMIX                                 (P) output type after voice mixing\n        1f90019A ATTR                                 (P) core attributes\n        1f90019C IRQA                                 (P) IRQ address \n        1f9001A0 KON                                  (P) key on (start voice sound generation)\n        1f9001A4 KOFF                                 (P) key off (end voice sound generation)\n        1f9001A8 TSA                                  (P) DMA transfer start address\n        1f9001AC DATA                                 (S) DMA data register\n        1f9001AE CTRL                                 (S) DMA control register\n        1f9001B0 ADMAS                                (S) AutoDMA (ADMA) status\n    1f9001c0-1f9002df CORE0 Voice address settings\n    1f9002e0-1f900347 CORE0 Misc. settings\n    1f900400-1f90057f CORE1 Voice settings\n    1f900580-1f9005b1 CORE1 Common settings\n    1f9005c0-1f9006df CORE1 Voice address settings\n    1f9006e0-1f900747 CORE1 Reverb/Misc. settings\n    1f900760-1f900787 CORE0 Volume/Reverb settings\n    1f900788-1f9007af CORE1 Volume/Reverb settings\n    1f9007c0-1f9007cf S/PDIF settings\n*/\n\nstruct spu2_voice {\n    uint16_t voll;\n    uint16_t volr;\n    uint16_t pitch;\n    uint16_t adsr1;\n    uint16_t adsr2;\n    uint16_t envx;\n    uint16_t volxl;\n    uint16_t volxr;\n    uint32_t ssa;\n    uint32_t lsax;\n    uint32_t nax;\n\n    // Internal stuff\n    int playing;\n    unsigned int counter;\n    int32_t h[2];\n    int16_t buf[28];\n    int loop_start;\n    int loop;\n    int loop_end;\n    int prev_sample_index;\n    int loop_addr_specified;\n    int16_t s[4];\n\n    // Envelope\n    int adsr_cycles_left;\n    int adsr_phase;\n    int adsr_cycles_reload;\n    int adsr_cycles;\n    int adsr_mode;\n    int adsr_dir;\n    int adsr_shift;\n    int adsr_step;\n    int adsr_pending_step;\n    int adsr_sustain_level;\n};\n\nstruct spu2_core {\n    struct spu2_voice v[24];\n\n    int words_written;\n    uint32_t pmon;\n    uint32_t non;\n    uint32_t vmixl;\n    uint32_t vmixel;\n    uint32_t vmixr;\n    uint32_t vmixer;\n    uint16_t mmix;\n    uint16_t attr;\n    uint32_t irqa;\n    uint32_t kon;\n    uint32_t koff;\n    uint32_t tsa;\n    uint16_t data;\n    uint16_t ctrl;\n    uint16_t admas;\n    uint32_t esa;\n    uint32_t fb_src_a;\n    uint32_t fb_src_b;\n    uint32_t iir_dest_a0;\n    uint32_t iir_dest_a1;\n    uint32_t acc_src_a0;\n    uint32_t acc_src_a1;\n    uint32_t acc_src_b0;\n    uint32_t acc_src_b1;\n    uint32_t iir_src_a0;\n    uint32_t iir_src_a1;\n    uint32_t iir_dest_b0;\n    uint32_t iir_dest_b1;\n    uint32_t acc_src_c0;\n    uint32_t acc_src_c1;\n    uint32_t acc_src_d0;\n    uint32_t acc_src_d1;\n    uint32_t iir_src_b1;\n    uint32_t iir_src_b0;\n    uint32_t mix_dest_a0;\n    uint32_t mix_dest_a1;\n    uint32_t mix_dest_b0;\n    uint32_t mix_dest_b1;\n    uint32_t eea;\n    uint32_t endx;\n    uint16_t stat;\n    uint16_t ends;\n    uint16_t mvoll;\n    uint16_t mvolr;\n    uint16_t evoll;\n    uint16_t evolr;\n    uint16_t avoll;\n    uint16_t avolr;\n    uint16_t bvoll;\n    uint16_t bvolr;\n    uint16_t mvolxl;\n    uint16_t mvolxr;\n    uint16_t iir_alpha;\n    uint16_t acc_coef_a;\n    uint16_t acc_coef_b;\n    uint16_t acc_coef_c;\n    uint16_t acc_coef_d;\n    uint16_t iir_coef;\n    uint16_t fb_alpha;\n    uint16_t fb_x;\n    uint16_t in_coef_l;\n    uint16_t in_coef_r;\n\n    // ADMA\n    uint32_t memin_write_addr;\n    uint32_t memin_read_addr;\n\n    int adma_playing;\n    int16_t adma_ringbuf[4096];\n    uint32_t adma_ringbuf_write_idx;\n    uint32_t adma_ringbuf_read_idx;\n    int adma_ringbuf_full;\n\n    // Capture buffers\n    uint16_t cb_out1_addr;\n    uint16_t cb_out3_addr;\n};\n\nstruct ps2_spu2 {\n    // 2 MB\n    uint16_t ram[0x100000];\n\n    struct spu2_core c[2];\n\n    // CORE1 S/PDIF settings\n    uint32_t spdif_out;\n    uint32_t spdif_mode;\n    uint32_t spdif_media;\n    uint32_t spdif_copy;\n    int spdif_irq;\n\n    struct ps2_iop_dma* dma;\n    struct ps2_iop_intc* intc;\n    struct sched_state* sched;\n};\n\nstruct spu2_sample {\n    union {\n        uint32_t u32;\n        uint16_t u16[2];\n        int16_t s16[2];\n    };\n};\n\nstruct ps2_spu2* ps2_spu2_create(void);\nvoid ps2_spu2_init(struct ps2_spu2* spu2, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched);\nuint64_t ps2_spu2_read16(struct ps2_spu2* spu2, uint32_t addr);\nvoid ps2_spu2_write16(struct ps2_spu2* spu2, uint32_t addr, uint64_t data);\nvoid ps2_spu2_destroy(struct ps2_spu2* spu2);\nstruct spu2_sample ps2_spu2_get_sample(struct ps2_spu2* spu, int adma_enable);\nstruct spu2_sample ps2_spu2_get_voice_sample(struct ps2_spu2* spu2, int c, int v);\nstruct spu2_sample ps2_spu2_get_adma_sample(struct ps2_spu2* spu2, int c);\nvoid spu2_start_adma(struct ps2_spu2* spu2, int c);\nint spu2_is_adma_active(struct ps2_spu2* spu2, int c);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/timers.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"timers.h\"\n#include \"intc.h\"\n#include \"scheduler.h\"\n\nstruct ps2_iop_timers* ps2_iop_timers_create(void) {\n    return malloc(sizeof(struct ps2_iop_timers));\n}\n\nvoid ps2_iop_timers_init(struct ps2_iop_timers* timers, struct ps2_iop_intc* intc, struct sched_state* sched) {\n    memset(timers, 0, sizeof(struct ps2_iop_timers));\n\n    timers->intc = intc;\n    timers->sched = sched;\n}\n\nvoid ps2_iop_timers_destroy(struct ps2_iop_timers* timers) {\n    free(timers);\n}\n\nstatic inline uint32_t timer_get_irq_mask(int t) {\n    switch (t) {\n        case 0: return IOP_INTC_TIMER0;\n        case 1: return IOP_INTC_TIMER1;\n        case 2: return IOP_INTC_TIMER2;\n        case 3: return IOP_INTC_TIMER3;\n        case 4: return IOP_INTC_TIMER4;\n        case 5: return IOP_INTC_TIMER5;\n    }\n\n    return 0;\n}\n\nvoid iop_timer_tick(struct ps2_iop_timers* timers, int i) {\n    struct iop_timer* t = &timers->timer[i];\n\n    uint32_t prev = t->counter;\n\n    // To-do: Breaks Crazy Taxi (USA)\n    if (i == 1) {\n        if (t->use_ext) {\n            if (t->internal != 90) {\n                ++t->internal;\n            } else {\n                ++t->counter;\n                t->internal = 0;\n            }\n        } else {\n            t->counter += 2;\n        }\n    } else {\n        if (i == 4) {\n            switch (t->t4_prescaler) {\n                case 0: t->counter += 2; break;\n                case 1: {\n                    if (t->internal != 128) {\n                        ++t->internal;\n                    } else {\n                        t->counter += 1;\n                        t->internal = 0;\n                    }\n                } break;\n            }\n        } else {\n            t->counter += 2;\n        }\n    }\n\n    if (t->counter >= t->target && prev < t->target) {\n        // printf(\"iop: Timer 5 reached target %08x <= %08x\\n\", t->counter, t->target);\n\n        t->cmp_irq_set = 1;\n\n        if (t->cmp_irq && t->irq_en) {\n            // printf(\" timer %d irq\\n\", i);\n\n            ps2_iop_intc_irq(timers->intc, timer_get_irq_mask(i));\n\n            if (!t->rep_irq) {\n                t->irq_en = 0;\n            } else {\n                if (t->levl) {\n                    t->irq_en = !t->irq_en;\n                }\n            }\n\n            if (t->irq_reset) {\n                t->counter = 0;\n            }\n\n            // printf(\"iop: Timer %d compare IRQ cnt=%d tgt=%d rep=%d levl=%d irq_en=%d irq_reset=%d\\n\", i,\n            //     t->counter,\n            //     t->target,\n            //     t->rep_irq,\n            //     t->levl,\n            //     t->irq_en,\n            //     t->irq_reset\n            // );\n        }\n    }\n\n    uint64_t ovf = (i < 3) ? 0xffff : 0xffffffff;\n\n    if (t->counter > ovf) {\n        t->ovf_irq_set = 1;\n\n        if (t->ovf_irq && t->irq_en) {\n            // printf(\"iop: Timer %d overflow IRQ rep=%d levl=%d irq_en=%d irq_reset=%d\\n\", i,\n            //     t->rep_irq,\n            //     t->levl,\n            //     t->irq_en,\n            //     t->irq_reset\n            // );\n\n            ps2_iop_intc_irq(timers->intc, timer_get_irq_mask(i));\n\n            if (!t->rep_irq) {\n                t->irq_en = 0;\n            } else {\n                if (t->levl) {\n                    t->irq_en = !t->irq_en;\n                }\n            }\n\n            if (t->irq_reset) {\n                t->counter = 0;\n            }\n        }\n\n        t->counter -= ovf;\n    }\n}\n\nvoid ps2_iop_timers_tick(struct ps2_iop_timers* timers) {\n    for (int i = 0; i < 6; i++) {\n        iop_timer_tick(timers, i);\n    }\n}\n\nuint32_t iop_timer_handle_mode_read(struct ps2_iop_timers* timers, int i) {\n    uint32_t r = timers->timer[i].mode;\n\n    timers->timer[i].cmp_irq_set = 0;\n    timers->timer[i].ovf_irq_set = 0;\n    // timers->timer[i].irq_en = 1;\n\n    // if (i == 5)\n    // printf(\"iop: Timer %d mode read %08x -> %08x\\n\", i, r, timers->timer[i].mode);\n\n    return r;\n}\n\nuint64_t ps2_iop_timers_read32(struct ps2_iop_timers* timers, uint32_t addr) {\n    switch (addr & 0xfff) {\n        case 0x100: return timers->timer[0].counter;\n        case 0x110: return timers->timer[1].counter;\n        case 0x120: return timers->timer[2].counter;\n        case 0x480: return timers->timer[3].counter;\n        case 0x490: return timers->timer[4].counter;\n        case 0x4a0: return timers->timer[5].counter;\n        case 0x108: return timers->timer[0].target;\n        case 0x118: return timers->timer[1].target;\n        case 0x128: return timers->timer[2].target;\n        case 0x488: return timers->timer[3].target;\n        case 0x498: return timers->timer[4].target;\n        case 0x4a8: return timers->timer[5].target;\n        case 0x104: return iop_timer_handle_mode_read(timers, 0);\n        case 0x114: return iop_timer_handle_mode_read(timers, 1);\n        case 0x124: return iop_timer_handle_mode_read(timers, 2);\n        case 0x484: return iop_timer_handle_mode_read(timers, 3);\n        case 0x494: return iop_timer_handle_mode_read(timers, 4);\n        case 0x4a4: return iop_timer_handle_mode_read(timers, 5);\n    }\n\n    return 0;\n}\n\nvoid iop_timer_handle_mode_write(struct ps2_iop_timers* timers, int t, uint64_t data) {\n    struct iop_timer* timer = &timers->timer[t];\n\n    timer->counter = 0;\n    timer->mode |= 0x400;\n    timer->mode &= 0x1c00;\n    timer->mode |= data & 0xe3ff;\n\n    if (timer->counter >= timer->target) {\n        timer->cmp_irq_set = 1;\n\n        // ps2_iop_intc_irq(timers->intc, timer_get_irq_mask(t));\n    }\n\n    // printf(\"iop: Timer %d mode write %08x -> %08x gate_en=%d gate_mode=%d irq_reset=%d cmp_irq=%d ovf_irq=%d rep_irq=%d levl=%d use_ext=%d irq_en=%d t4_prescaler=%d\\n\",\n    //     t, timers->timer[t].mode,\n    //     data,\n    //     timers->timer[t].gate_en,\n    //     timers->timer[t].gate_mode,\n    //     timers->timer[t].irq_reset,\n    //     timers->timer[t].cmp_irq,\n    //     timers->timer[t].ovf_irq,\n    //     timers->timer[t].rep_irq,\n    //     timers->timer[t].levl,\n    //     timers->timer[t].use_ext,\n    //     timers->timer[t].irq_en,\n    //     timers->timer[t].t4_prescaler\n    // );\n\n    /* To-do: Schedule timer interrupts for better performance */\n}\n\nvoid iop_timer_handle_target_write(struct ps2_iop_timers* timers, int t, uint64_t data) {\n    struct iop_timer* timer = &timers->timer[t];\n\n    timer->target = data;\n\n    if (!timer->levl) {\n        timer->irq_en = 1;\n    }\n\n    if (t != 5)\n        return;\n\n    // printf(\"iop: Timer %d target write %08x levl=%d mode=%08x counter=%08x\\n\", t, data, timer->levl, timer->mode, timer->counter);\n}\n\nvoid ps2_iop_timers_write32(struct ps2_iop_timers* timers, uint32_t addr, uint64_t data) {\n    switch (addr & 0xfff) {\n        case 0x100: /* printf(\"iop: Timer 0 counter write %08x prev=%08x\\n\", data, timers->timer[0].counter); */ timers->timer[0].counter = data; break;\n        case 0x110: /* printf(\"iop: Timer 1 counter write %08x prev=%08x\\n\", data, timers->timer[1].counter); */ timers->timer[1].counter = data; break;\n        case 0x120: /* printf(\"iop: Timer 2 counter write %08x prev=%08x\\n\", data, timers->timer[2].counter); */ timers->timer[2].counter = data; break;\n        case 0x480: /* printf(\"iop: Timer 3 counter write %08x prev=%08x\\n\", data, timers->timer[3].counter); */ timers->timer[3].counter = data; break;\n        case 0x490: /* printf(\"iop: Timer 4 counter write %08x prev=%08x\\n\", data, timers->timer[4].counter); */ timers->timer[4].counter = data; break;\n        case 0x4a0: /* printf(\"iop: Timer 5 counter write %08x prev=%08x\\n\", data, timers->timer[5].counter); */ timers->timer[5].counter = data; break;\n        case 0x108: iop_timer_handle_target_write(timers, 0, data); break;\n        case 0x118: iop_timer_handle_target_write(timers, 1, data); break;\n        case 0x128: iop_timer_handle_target_write(timers, 2, data); break;\n        case 0x488: iop_timer_handle_target_write(timers, 3, data); break;\n        case 0x498: iop_timer_handle_target_write(timers, 4, data); break;\n        case 0x4a8: iop_timer_handle_target_write(timers, 5, data); break;\n        case 0x104: iop_timer_handle_mode_write(timers, 0, data); break;\n        case 0x114: iop_timer_handle_mode_write(timers, 1, data); break;\n        case 0x124: iop_timer_handle_mode_write(timers, 2, data); break;\n        case 0x484: iop_timer_handle_mode_write(timers, 3, data); break;\n        case 0x494: iop_timer_handle_mode_write(timers, 4, data); break;\n        case 0x4a4: iop_timer_handle_mode_write(timers, 5, data); break;\n    }\n}"
  },
  {
    "path": "src/iop/timers.h",
    "content": "#ifndef IOP_TIMER_H\n#define IOP_TIMER_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\nstruct iop_timer {\n    int64_t counter;\n\n    union {\n        uint32_t mode;\n\n        struct {\n            unsigned int gate_en : 1;\n            unsigned int gate_mode : 2;\n            unsigned int irq_reset : 1;\n            unsigned int cmp_irq : 1;\n            unsigned int ovf_irq : 1;\n            unsigned int rep_irq : 1;\n            unsigned int levl : 1;\n            unsigned int use_ext : 1;\n            unsigned int t2_prescaler : 1;\n            unsigned int irq_en : 1;\n            unsigned int cmp_irq_set : 1;\n            unsigned int ovf_irq_set : 1;\n            unsigned int t4_prescaler : 1;\n            unsigned int t5_prescaler : 1;\n            unsigned int unused : 17;\n        };\n    };\n\n    uint32_t target;\n    int64_t internal;\n};\n\nstruct ps2_iop_timers {\n    struct iop_timer timer[6];\n\n    struct ps2_iop_intc* intc;\n    struct sched_state* sched;\n};\n\nstruct ps2_iop_timers* ps2_iop_timers_create(void);\nvoid ps2_iop_timers_init(struct ps2_iop_timers* timers, struct ps2_iop_intc* intc, struct sched_state* sched);\nvoid ps2_iop_timers_destroy(struct ps2_iop_timers* timers);\nvoid ps2_iop_timers_tick(struct ps2_iop_timers* timers);\nuint64_t ps2_iop_timers_read32(struct ps2_iop_timers* timers, uint32_t addr);\nvoid ps2_iop_timers_write32(struct ps2_iop_timers* timers, uint32_t addr, uint64_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/iop/usb.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"usb.h\"\n\nstruct ps2_usb* ps2_usb_create(void) {\n    return malloc(sizeof(struct ps2_usb));\n}\n\nvoid ps2_usb_init(struct ps2_usb* usb) {\n    memset(usb, 0, sizeof(struct ps2_usb));\n}\n\nvoid ps2_usb_destroy(struct ps2_usb* usb) {\n    free(usb);\n}\n\nuint64_t ps2_usb_read32(struct ps2_usb* usb, uint32_t addr) {\n    addr &= 0xff;\n\n    switch (addr) {\n        case 0x00: printf(\"usb: read USB_HC_REVISION\\n\"); return 0;\n        case 0x04: printf(\"usb: read USB_HC_CONTROL\\n\"); return 0;\n        case 0x08: printf(\"usb: read USB_HC_COMMANDSTATUS\\n\"); return 0;\n        case 0x0c: printf(\"usb: read USB_HC_INTERRUPTSTATUS\\n\"); return 0xffffffff;\n        case 0x10: printf(\"usb: read USB_HC_INTERRUPTENABLE\\n\"); return 0xffffffff;\n        case 0x14: printf(\"usb: read USB_HC_INTERRUPTDISABLE\\n\"); return 0;\n        case 0x18: printf(\"usb: read USB_HC_HCCA\\n\"); return 0;\n        case 0x1c: printf(\"usb: read USB_HC_PERIODCURRENTED\\n\"); return 0;\n        case 0x20: printf(\"usb: read USB_HC_CONTROLHEADED\\n\"); return 0;\n        case 0x24: printf(\"usb: read USB_HC_CONTROLCURRENTED\\n\"); return 0;\n        case 0x28: printf(\"usb: read USB_HC_BULKHEADED\\n\"); return 0;\n        case 0x2c: printf(\"usb: read USB_HC_BULKCURRENTED\\n\"); return 0;\n        case 0x30: printf(\"usb: read USB_HC_DONEHEAD\\n\"); return 0;\n        case 0x34: printf(\"usb: read USB_HC_FMINTERVAL\\n\"); return 0;\n        case 0x38: printf(\"usb: read USB_HC_FMREMAINING\\n\"); return 0;\n        case 0x3c: printf(\"usb: read USB_HC_FMNUMBER\\n\"); return 0;\n        case 0x40: printf(\"usb: read USB_HC_PERIODICSTART\\n\"); return 0;\n        case 0x44: printf(\"usb: read USB_HC_LSTHRESHOLD\\n\"); return 0;\n        case 0x48: printf(\"usb: read USB_HC_RHDESCRIPTORA\\n\"); return 0x3F000202;\n        case 0x4c: printf(\"usb: read USB_HC_RHDESCRIPTORB\\n\"); return 0xffff;\n        case 0x50: printf(\"usb: read USB_HC_RHSTATUS\\n\"); return 0xffffffff;\n    }\n\n    if (addr >= 0x54 && addr <= 0x8c) {\n        printf(\"usb: Read USB_HC_RHPORT%dSTATUS\\n\", (addr - 0x54) >> 2);\n        return 0x150303;\n    }\n\n    printf(\"usb: Unhandled read at %08x\\n\", addr);\n\n    return 0;\n}\n\nvoid ps2_usb_write32(struct ps2_usb* usb, uint32_t addr, uint64_t data) {\n    addr &= 0xff;\n\n    switch (addr) {\n        case 0x00: printf(\"usb: write %08x to USB_HC_REVISION\\n\", data); return;\n        case 0x04: printf(\"usb: write %08x to USB_HC_CONTROL\\n\", data); return;\n        case 0x08: printf(\"usb: write %08x to USB_HC_COMMANDSTATUS\\n\", data); return;\n        case 0x0c: printf(\"usb: write %08x to USB_HC_INTERRUPTSTATUS\\n\", data); return;\n        case 0x10: printf(\"usb: write %08x to USB_HC_INTERRUPTENABLE\\n\", data); return;\n        case 0x14: printf(\"usb: write %08x to USB_HC_INTERRUPTDISABLE\\n\", data); return;\n        case 0x18: printf(\"usb: write %08x to USB_HC_HCCA\\n\", data); return;\n        case 0x1c: printf(\"usb: write %08x to USB_HC_PERIODCURRENTED\\n\", data); return;\n        case 0x20: printf(\"usb: write %08x to USB_HC_CONTROLHEADED\\n\", data); return;\n        case 0x24: printf(\"usb: write %08x to USB_HC_CONTROLCURRENTED\\n\", data); return;\n        case 0x28: printf(\"usb: write %08x to USB_HC_BULKHEADED\\n\", data); return;\n        case 0x2c: printf(\"usb: write %08x to USB_HC_BULKCURRENTED\\n\", data); return;\n        case 0x30: printf(\"usb: write %08x to USB_HC_DONEHEAD\\n\", data); return;\n        case 0x34: printf(\"usb: write %08x to USB_HC_FMINTERVAL\\n\", data); return;\n        case 0x38: printf(\"usb: write %08x to USB_HC_FMREMAINING\\n\", data); return;\n        case 0x3c: printf(\"usb: write %08x to USB_HC_FMNUMBER\\n\", data); return;\n        case 0x40: printf(\"usb: write %08x to USB_HC_PERIODICSTART\\n\", data); return;\n        case 0x44: printf(\"usb: write %08x to USB_HC_LSTHRESHOLD\\n\", data); return;\n        case 0x48: printf(\"usb: write %08x to USB_HC_RHDESCRIPTORA\\n\", data); return;\n        case 0x4c: printf(\"usb: write %08x to USB_HC_RHDESCRIPTORB\\n\", data); return;\n        case 0x50: printf(\"usb: write %08x to USB_HC_RHSTATUS\\n\", data); return;\n    }\n\n    if (addr >= 0x54 && addr <= 0x8c) {\n        printf(\"usb: write %08x to USB_HC_RHPORT%dSTATUS\\n\", data, (addr - 0x54) >> 2);\n\n        return;\n    }\n\n    printf(\"usb: Unhandled write at %08x (%08x)\\n\", addr, data);\n\n    return;\n}"
  },
  {
    "path": "src/iop/usb.h",
    "content": "#ifndef USB_H\n#define USB_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#define OHCI_BASE 0x1f801600\n#define OHCI_MAX_PORTS 15\n\n#define USB_HC_REVISION         0x00\n#define USB_HC_CONTROL          0x04\n#define USB_HC_COMMANDSTATUS    0x08\n#define USB_HC_INTERRUPTSTATUS  0x0c\n#define USB_HC_INTERRUPTENABLE  0x10\n#define USB_HC_INTERRUPTDISABLE 0x14\n#define USB_HC_HCCA             0x18\n#define USB_HC_PERIODCURRENTED  0x1c\n#define USB_HC_CONTROLHEADED    0x20\n#define USB_HC_CONTROLCURRENTED 0x24\n#define USB_HC_BULKHEADED       0x28\n#define USB_HC_BULKCURRENTED    0x2c\n#define USB_HC_DONEHEAD         0x30\n#define USB_HC_FMINTERVAL       0x34\n#define USB_HC_FMREMAINING      0x38\n#define USB_HC_FMNUMBER         0x3c\n#define USB_HC_PERIODICSTART    0x40\n#define USB_HC_LSTHRESHOLD      0x44\n#define USB_HC_RHDESCRIPTORA    0x48\n#define USB_HC_RHDESCRIPTORB    0x4c\n#define USB_HC_RHSTATUS         0x50\n\nstruct ps2_usb {\n    int dummy;\n};\n\nstruct ps2_usb* ps2_usb_create(void);\nvoid ps2_usb_init(struct ps2_usb* usb);\nvoid ps2_usb_destroy(struct ps2_usb* usb);\nuint64_t ps2_usb_read32(struct ps2_usb* usb, uint32_t addr);\nvoid ps2_usb_write32(struct ps2_usb* usb, uint32_t addr, uint64_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ipu/chromtable.cpp",
    "content": "#include \"chromtable.hpp\"\n\nVLC_Entry ChromTable::table[] =\n{\n    {0x0, 0, 2},\n    {0x1, 1, 2},\n    {0x2, 2, 2},\n    {0x6, 3, 3},\n    {0xE, 4, 4},\n    {0x1E, 5, 5},\n    {0x3E, 6, 6},\n    {0x7E, 7, 7},\n    {0xFE, 8, 8},\n    {0x1FE, 9, 9},\n    {0x3FE, 10, 10},\n    {0x3FF, 11, 10}\n};\n\nunsigned int ChromTable::index_table[10] =\n{\n    0,\n    0,\n    3,\n    4,\n    5,\n    6,\n    7,\n    8,\n    9,\n    10,\n};\n\nChromTable::ChromTable() : VLC_Table(table, SIZE, 10, index_table)\n{\n\n}\n"
  },
  {
    "path": "src/ipu/chromtable.hpp",
    "content": "#ifndef CHROMTABLE_HPP\n#define CHROMTABLE_HPP\n#include \"vlc_table.hpp\"\n\nclass ChromTable : public VLC_Table\n{\n    private:\n        static VLC_Entry table[];\n        static unsigned int index_table[];\n\n        constexpr static int SIZE = 12;\n    public:\n        ChromTable();\n};\n\n#endif // CHROMTABLE_HPP\n"
  },
  {
    "path": "src/ipu/codedblockpattern.cpp",
    "content": "#include \"codedblockpattern.hpp\"\n\nVLC_Entry CodedBlockPattern::table[] =\n{\n    {0x7, 60, 3},\n    {0xD, 4, 4},\n    {0xC, 8, 4},\n    {0xB, 16, 4},\n\n    {0xA, 32, 4},\n    {0x13, 12, 5},\n    {0x12, 48, 5},\n    {0x11, 20, 5},\n\n    {0x10, 40, 5},\n    {0xF, 28, 5},\n    {0xE, 44, 5},\n    {0xD, 52, 5},\n\n    //16\n    {0xC, 56, 5},\n    {0xB, 1, 5},\n    {0xA, 61, 5},\n    {0x9, 2, 5},\n\n    //20\n    {0x8, 62, 5},\n    {0xF, 24, 6},\n    {0xE, 36, 6},\n    {0xD, 3, 6},\n\n    //24\n    {0xC, 63, 6},\n    {0x17, 5, 7},\n    {0x16, 9, 7},\n    {0x15, 17, 7},\n\n    //28\n    {0x14, 33, 7},\n    {0x13, 6, 7},\n    {0x12, 10, 7},\n    {0x11, 18, 7},\n\n    //32\n    {0x10, 34, 7},\n    {0x1F, 7, 8},\n    {0x1E, 11, 8},\n    {0x1D, 19, 8},\n\n    //36\n    {0x1C, 35, 8},\n    {0x1B, 13, 8},\n    {0x1A, 49, 8},\n    {0x19, 21, 8},\n\n    //40\n    {0x18, 41, 8},\n    {0x17, 14, 8},\n    {0x16, 50, 8},\n    {0x15, 22, 8},\n\n    //44\n    {0x14, 42, 8},\n    {0x13, 15, 8},\n    {0x12, 51, 8},\n    {0x11, 23, 8},\n\n    //48\n    {0x10, 43, 8},\n    {0xF, 25, 8},\n    {0xE, 37, 8},\n    {0xD, 26, 8},\n\n    //52\n    {0xC, 38, 8},\n    {0xB, 29, 8},\n    {0xA, 45, 8},\n    {0x9, 53, 8},\n\n    //56\n    {0x8, 57, 8},\n    {0x7, 30, 8},\n    {0x6, 46, 8},\n    {0x5, 54, 8},\n\n    //60\n    {0x4, 58, 8},\n    {0x7, 31, 9},\n    {0x6, 47, 9},\n    {0x5, 55, 9},\n\n    //64\n    {0x4, 59, 9},\n    {0x3, 27, 9},\n    {0x2, 39, 9},\n    {0x1, 0, 9}\n};\n\nunsigned int CodedBlockPattern::index_table[9] =\n{\n    0,\n    0,\n    0,\n    1,\n    5,\n    17,\n    21,\n    29,\n    57,\n};\n\nCodedBlockPattern::CodedBlockPattern() : VLC_Table(table, SIZE, 9, index_table)\n{\n\n}\n"
  },
  {
    "path": "src/ipu/codedblockpattern.hpp",
    "content": "#ifndef CODEDBLOCKPATTERN_HPP\n#define CODEDBLOCKPATTERN_HPP\n#include \"vlc_table.hpp\"\n\nclass CodedBlockPattern : public VLC_Table\n{\n    private:\n        static VLC_Entry table[];\n        static unsigned int index_table[];\n\n        constexpr static int SIZE = 64;\n    public:\n        CodedBlockPattern();\n};\n\n#endif // CODEDBLOCKPATTERN_HPP\n"
  },
  {
    "path": "src/ipu/dct_coeff.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include \"dct_coeff.hpp\"\n\nDCT_Coeff::DCT_Coeff(VLC_Entry* table, int table_size, int max_bits, unsigned int* index_table) :\n    VLC_Table(table, table_size, max_bits, index_table)\n{\n\n}\n\nbool DCT_Coeff::peek_value(IPU_FIFO &FIFO, int bits, int &bit_count, uint32_t &result)\n{\n    if (bits + bit_count > 32)\n    {\n    }\n    if (!FIFO.get_bits(result, bits + bit_count))\n        return false;\n\n    result &= ~(0xFFFFFFFF << bits);\n    bit_count += bits;\n    return true;\n}\n"
  },
  {
    "path": "src/ipu/dct_coeff.hpp",
    "content": "#ifndef DCT_COEFF_HPP\n#define DCT_COEFF_HPP\n#include \"vlc_table.hpp\"\n\nstruct RunLevelPair\n{\n    int run, level;\n};\n\nclass DCT_Coeff : public VLC_Table\n{\n    protected:\n        constexpr static int RUN_ESCAPE = 102;\n    public:\n        DCT_Coeff(VLC_Entry* table, int table_size, int max_bits, unsigned int* index_table);\n\n        virtual bool get_end_of_block(IPU_FIFO& FIFO, uint32_t& result) = 0;\n        virtual bool get_skip_block(IPU_FIFO& FIFO) = 0;\n        virtual bool get_runlevel_pair(IPU_FIFO& FIFO, RunLevelPair& pair, bool MPEG1) = 0;\n        virtual bool get_runlevel_pair_dc(IPU_FIFO& FIFO, RunLevelPair& pair, bool MPEG1) = 0;\n\n        bool peek_value(IPU_FIFO& FIFO, int bits, int& bit_count, uint32_t& result);\n};\n\n#endif // DCT_COEFF_HPP\n"
  },
  {
    "path": "src/ipu/dct_coeff_table0.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include \"dct_coeff_table0.hpp\"\n\n#define printf(fmt, ...)(0)\n\nVLC_Entry DCT_Coeff_Table0::table[] =\n{\n    {0x3, 0, 2},\n    {0x3, 1, 3},\n    {0x4, 2, 4},\n    {0x5, 3, 4},\n\n    {0x5, 4, 5},\n    {0x7, 5, 5},\n    {0x6, 6, 5},\n    {0x6, 7, 6},\n\n    {0x7, 8, 6},\n    {0x5, 9, 6},\n    {0x4, 10, 6},\n    {0x1, 15, 6},\n\n    {0x6, 11, 7},\n    {0x4, 12, 7},\n    {0x7, 13, 7},\n    {0x5, 14, 7},\n\n    //20\n    {0x26, 16, 8},\n    {0x21, 17, 8},\n    {0x25, 18, 8},\n    {0x24, 19, 8},\n\n    //24\n    {0x27, 20, 8},\n    {0x23, 21, 8},\n    {0x22, 22, 8},\n    {0x20, 23, 8},\n\n    //28\n    {0xA, 24, 10},\n    {0xC, 25, 10},\n    {0xB, 26, 10},\n    {0xF, 27, 10},\n\n    //32\n    {0x9, 28, 10},\n    {0xE, 29, 10},\n    {0xD, 30, 10},\n    {0x8, 31, 10},\n\n    //36\n    {0x1D, 32, 12},\n    {0x18, 33, 12},\n    {0x13, 34, 12},\n    {0x10, 35, 12},\n\n    //40\n    {0x1B, 36, 12},\n    {0x14, 37, 12},\n    {0x1C, 38, 12},\n    {0x12, 39, 12},\n\n    //44\n    {0x1E, 40, 12},\n    {0x15, 41, 12},\n    {0x11, 42, 12},\n    {0x1F, 43, 12},\n\n    //48\n    {0x1A, 44, 12},\n    {0x19, 45, 12},\n    {0x17, 46, 12},\n    {0x16, 47, 12},\n\n    //52\n    {0x1A, 48, 13},\n    {0x19, 49, 13},\n    {0x18, 50, 13},\n    {0x17, 51, 13},\n\n    //56\n    {0x16, 52, 13},\n    {0x15, 53, 13},\n    {0x14, 54, 13},\n    {0x13, 55, 13},\n\n    //60\n    {0x12, 56, 13},\n    {0x11, 57, 13},\n    {0x10, 58, 13},\n    {0x1F, 59, 13},\n\n    //64\n    {0x1E, 60, 13},\n    {0x1D, 61, 13},\n    {0x1C, 62, 13},\n    {0x1B, 63, 13},\n\n    //68\n    {0x1F, 64, 14},\n    {0x1E, 65, 14},\n    {0x1D, 66, 14},\n    {0x1C, 67, 14},\n\n    //72\n    {0x1B, 68, 14},\n    {0x1A, 69, 14},\n    {0x19, 70, 14},\n    {0x18, 71, 14},\n\n    //76\n    {0x17, 72, 14},\n    {0x16, 73, 14},\n    {0x15, 74, 14},\n    {0x14, 75, 14},\n\n    //80\n    {0x13, 76, 14},\n    {0x12, 77, 14},\n    {0x11, 78, 14},\n    {0x10, 79, 14},\n\n    //84\n    {0x18, 80, 15},\n    {0x17, 81, 15},\n    {0x16, 82, 15},\n    {0x15, 83, 15},\n\n    //88\n    {0x14, 84, 15},\n    {0x13, 85, 15},\n    {0x12, 86, 15},\n    {0x11, 87, 15},\n\n    //92\n    {0x10, 88, 15},\n    {0x1F, 89, 15},\n    {0x1E, 90, 15},\n    {0x1D, 91, 15},\n\n    //96\n    {0x1C, 92, 15},\n    {0x1B, 93, 15},\n    {0x1A, 94, 15},\n    {0x19, 95, 15},\n\n    //100\n    {0x13, 96, 16},\n    {0x12, 97, 16},\n    {0x11, 98, 16},\n    {0x10, 99, 16},\n\n    //104\n    {0x14, 100, 16},\n    {0x1A, 101, 16},\n    {0x19, 102, 16},\n    {0x18, 103, 16},\n\n    //108\n    {0x17, 104, 16},\n    {0x16, 105, 16},\n    {0x15, 106, 16},\n    {0x1F, 107, 16},\n\n    //112\n    {0x1E, 108, 16},\n    {0x1D, 109, 16},\n    {0x1C, 110, 16},\n    {0x1B, 111, 16},\n};\n\nRunLevelPair DCT_Coeff_Table0::runlevel_table[] =\n{\n    { 0,\t\t\t1\t},\n    { 1,\t\t\t1\t},\n    { 0,\t\t\t2\t},\n    { 2,\t\t\t1\t},\n    { 0,\t\t\t3\t},\n    { 3,\t\t\t1\t},\n    { 4,\t\t\t1\t},\n    { 1,\t\t\t2\t},\n    { 5,\t\t\t1\t},\n    { 6,\t\t\t1\t},\n    { 7,\t\t\t1\t},\n    { 0,\t\t\t4\t},\n    { 2,\t\t\t2\t},\n    { 8,\t\t\t1\t},\n    { 9,\t\t\t1\t},\n    { RUN_ESCAPE,\t0\t},\n    { 0,\t\t\t5\t},\n    { 0,\t\t\t6\t},\n    { 1,\t\t\t3\t},\n    { 3,\t\t\t2\t},\n    { 10,\t\t\t1\t},\n    { 11,\t\t\t1\t},\n    { 12,\t\t\t1\t},\n    { 13,\t\t\t1\t},\n    { 0,\t\t\t7\t},\n    { 1,\t\t\t4\t},\n    { 2,\t\t\t3\t},\n    { 4,\t\t\t2\t},\n    { 5,\t\t\t2\t},\n    { 14,\t\t\t1\t},\n    { 15,\t\t\t1\t},\n    { 16,\t\t\t1\t},\n\n    { 0,\t\t\t8\t},\n    { 0,\t\t\t9\t},\n    { 0,\t\t\t10\t},\n    { 0,\t\t\t11\t},\n    { 1,\t\t\t5\t},\n    { 2,\t\t\t4\t},\n    { 3,\t\t\t3\t},\n    { 4,\t\t\t3\t},\n    { 6,\t\t\t2\t},\n    { 7,\t\t\t2\t},\n    { 8,\t\t\t2\t},\n    { 17,\t\t\t1\t},\n    { 18,\t\t\t1\t},\n    { 19,\t\t\t1\t},\n    { 20,\t\t\t1\t},\n    { 21,\t\t\t1\t},\n    { 0,\t\t\t12\t},\n    { 0,\t\t\t13\t},\n    { 0,\t\t\t14\t},\n    { 0,\t\t\t15\t},\n    { 1,\t\t\t6\t},\n    { 1,\t\t\t7\t},\n    { 2,\t\t\t5\t},\n    { 3,\t\t\t4\t},\n    { 5,\t\t\t3\t},\n    { 9,\t\t\t2\t},\n    { 10,\t\t\t2\t},\n    { 22,\t\t\t1\t},\n    { 23,\t\t\t1\t},\n    { 24,\t\t\t1\t},\n    { 25,\t\t\t1\t},\n    { 26,\t\t\t1\t},\n\n    { 0,\t\t\t16\t},\n    { 0,\t\t\t17\t},\n    { 0,\t\t\t18\t},\n    { 0,\t\t\t19\t},\n    { 0,\t\t\t20\t},\n    { 0,\t\t\t21\t},\n    { 0,\t\t\t22\t},\n    { 0,\t\t\t23\t},\n    { 0,\t\t\t24\t},\n    { 0,\t\t\t25\t},\n    { 0,\t\t\t26\t},\n    { 0,\t\t\t27\t},\n    { 0,\t\t\t28\t},\n    { 0,\t\t\t29\t},\n    { 0,\t\t\t30\t},\n    { 0,\t\t\t31\t},\n    { 0,\t\t\t32\t},\n    { 0,\t\t\t33\t},\n    { 0,\t\t\t34\t},\n    { 0,\t\t\t35\t},\n    { 0,\t\t\t36\t},\n    { 0,\t\t\t37\t},\n    { 0,\t\t\t38\t},\n    { 0,\t\t\t39\t},\n    { 0,\t\t\t40\t},\n    { 1,\t\t\t8\t},\n    { 1,\t\t\t9\t},\n    { 1,\t\t\t10\t},\n    { 1,\t\t\t11\t},\n    { 1,\t\t\t12\t},\n    { 1,\t\t\t13\t},\n    { 1,\t\t\t14\t},\n\n    { 1,\t\t\t15\t},\n    { 1,\t\t\t16\t},\n    { 1,\t\t\t17\t},\n    { 1,\t\t\t18\t},\n    { 6,\t\t\t3\t},\n    { 11,\t\t\t2\t},\n    { 12,\t\t\t2\t},\n    { 13,\t\t\t2\t},\n    { 14,\t\t\t2\t},\n    { 15,\t\t\t2\t},\n    { 16,\t\t\t2\t},\n    { 27,\t\t\t1\t},\n    { 28,\t\t\t1\t},\n    { 29,\t\t\t1\t},\n    { 30,\t\t\t1\t},\n    { 31,\t\t\t1\t},\n};\n\nunsigned int DCT_Coeff_Table0::index_table[16] =\n{\n    0,\n    0,\n    1,\n    2,\n    4,\n    7,\n    12,\n    16,\n    24,\n    24,\n    32,\n    32,\n    48,\n    64,\n    80,\n    96,\n};\n\nDCT_Coeff_Table0::DCT_Coeff_Table0() :\n    DCT_Coeff(table, SIZE, 16, index_table)\n{\n\n}\n\nbool DCT_Coeff_Table0::get_end_of_block(IPU_FIFO &FIFO, uint32_t &result)\n{\n    if (!FIFO.get_bits(result, 2))\n        return false;\n\n    printf(\"[DCT_Coeff_Table0] EOB: $%08X\\n\", result);\n    result = (result == 2);\n    return true;\n}\n\nbool DCT_Coeff_Table0::get_skip_block(IPU_FIFO &FIFO)\n{\n    uint32_t blorp;\n    if (!FIFO.get_bits(blorp, 2))\n        return false;\n    FIFO.advance_stream(2);\n    return true;\n}\n\nbool DCT_Coeff_Table0::get_runlevel_pair(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1)\n{\n    VLC_Entry entry;\n    if (!peek_symbol(FIFO, entry))\n        return false;\n\n    int bit_count = entry.bits;\n    RunLevelPair cur_pair = runlevel_table[entry.value];\n    printf(\"Run level pair index: %d (Key: $%02X)\\n\", entry.value, entry.key);\n    if (cur_pair.run == RUN_ESCAPE)\n    {\n        uint32_t run;\n        if (!peek_value(FIFO, 6, bit_count, run))\n            return false;\n\n        pair.run = run;\n\n        uint32_t level;\n        if (MPEG1)\n        {\n            if (!peek_value(FIFO, 8, bit_count, level))\n                return false;\n\n            if (!level)\n            {\n                if (!peek_value(FIFO, 8, bit_count, level))\n                    return false;\n            }\n            else if ((uint8_t)level == 128)\n            {\n                if (!peek_value(FIFO, 8, bit_count, level))\n                    return false;\n                level -= 256;\n            }\n            else if (level > 128)\n                level -= 256;\n        }\n        else\n        {\n            if (!peek_value(FIFO, 12, bit_count, level))\n                return false;\n\n            if (level & 0x800)\n            {\n                level |= 0xF000;\n                level = (int16_t)level;\n            }\n        }\n        pair.level = level;\n    }\n    else\n    {\n        uint32_t sign = 0;\n        if (!peek_value(FIFO, 1, bit_count, sign))\n            return false;\n\n        pair.run = cur_pair.run;\n        if (sign)\n            pair.level = 0 - cur_pair.level;\n        else\n            pair.level = cur_pair.level;\n    }\n\n    FIFO.advance_stream(bit_count);\n    return true;\n}\n\nbool DCT_Coeff_Table0::get_runlevel_pair_dc(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1)\n{\n    uint32_t special;\n    if (!FIFO.get_bits(special, 1))\n        return false;\n\n    if (special)\n    {\n        uint32_t sign;\n        if (!FIFO.get_bits(sign, 2))\n            return false;\n        FIFO.advance_stream(2);\n        pair.run = 0;\n\n        if (sign & 0x1)\n            pair.level = 0 - 1;\n        else\n            pair.level = 1;\n        return true;\n    }\n    else\n        return get_runlevel_pair(FIFO, pair, MPEG1);\n}\n"
  },
  {
    "path": "src/ipu/dct_coeff_table0.hpp",
    "content": "#ifndef DCT_COEFF_TABLE0_HPP\n#define DCT_COEFF_TABLE0_HPP\n#include \"dct_coeff.hpp\"\n\nclass DCT_Coeff_Table0 : public DCT_Coeff\n{\n    private:\n        static VLC_Entry table[];\n        static RunLevelPair runlevel_table[];\n        static unsigned int index_table[];\n\n        constexpr static int SIZE = 112;\n    public:\n        DCT_Coeff_Table0();\n\n        bool get_end_of_block(IPU_FIFO &FIFO, uint32_t &result);\n        bool get_skip_block(IPU_FIFO &FIFO);\n        bool get_runlevel_pair(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1);\n        bool get_runlevel_pair_dc(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1);\n};\n\n#endif // DCT_COEFF_TABLE0_HPP\n"
  },
  {
    "path": "src/ipu/dct_coeff_table1.cpp",
    "content": "#include <cstdio>\n#include <cstdlib>\n#include \"dct_coeff_table1.hpp\"\n\n#define printf(fmt, ...)(0)\n\nVLC_Entry DCT_Coeff_Table1::table[] =\n{\n    {0x2, 0, 2},\n    {0x2, 1, 3},\n    {0x6, 2, 3},\n    {0x7, 4, 4},\n\n    {0x5, 3, 5},\n    {0x7, 5, 5},\n    {0x6, 7, 5},\n    {0x1C, 11, 5},\n\n    {0x1D, 16, 5},\n    {0x6, 6, 6},\n    {0x7, 8, 6},\n    {0x5, 17, 6},\n\n    {0x4, 24, 6},\n    {0x1, 15, 6},\n    {0x79, 18, 7},\n    {0x7A, 20, 7},\n\n    {0x7, 12, 7},\n    {0x5, 13, 7},\n    {0x78, 14, 7},\n    {0x6, 9, 7},\n\n    {0x4, 10, 7},\n    {0x7B, 32, 7},\n    {0x7C, 33, 7},\n    {0x26, 19, 8},\n\n    {0x21, 21, 8},\n    {0x25, 22, 8},\n    {0x24, 23, 8},\n    {0x27, 25, 8},\n\n    {0xFC, 26, 8},\n    {0xFD, 27, 8},\n    {0xFA, 48, 8},\n    {0xFB, 49, 8},\n\n    {0xFE, 50, 8},\n    {0xFF, 51, 8},\n    {0x23, 34, 8},\n    {0x22, 35, 8},\n\n    {0x20, 36, 8},\n    {0x4, 28, 9},\n    {0x5, 29, 9},\n    {0x7, 30, 9}, //40\n\n    {0xD, 31, 10},\n    {0xC, 37, 10},\n    {0x1C, 38, 12},\n    {0x12, 39, 12}, //44\n\n    {0x1E, 40, 12},\n    {0x15, 41, 12},\n    {0x11, 42, 12},\n    {0x1F, 43, 12}, //48\n\n    {0x1A, 44, 12},\n    {0x19, 45, 12},\n    {0x17, 46, 12},\n    {0x16, 47, 12}, //52\n\n    {0x16, 52, 13},\n    {0x15, 53, 13},\n    {0x14, 54, 13},\n    {0x13, 55, 13}, //56\n\n    {0x12, 56, 13},\n    {0x11, 57, 13},\n    {0x10, 58, 13},\n    {0x1F, 59, 13}, //60\n\n    {0x1E, 60, 13},\n    {0x1D, 61, 13},\n    {0x1C, 62, 13},\n    {0x1B, 63, 13}, //64\n\n    {0x1F, 64, 14},\n    {0x1E, 65, 14},\n    {0x1D, 66, 14},\n    {0x1C, 67, 14}, //68\n\n    {0x1B, 68, 14},\n    {0x1A, 69, 14},\n    {0x19, 70, 14},\n    {0x18, 71, 14}, //72\n\n    {0x17, 72, 14},\n    {0x16, 73, 14},\n    {0x15, 74, 14},\n    {0x14, 75, 14}, //76\n\n    {0x13, 76, 14},\n    {0x12, 77, 14},\n    {0x11, 78, 14},\n    {0x10, 79, 14}, //80\n\n    {0x18, 80, 15},\n    {0x17, 81, 15},\n    {0x16, 82, 15},\n    {0x15, 83, 15}, //84\n\n    {0x14, 84, 15},\n    {0x13, 85, 15},\n    {0x12, 86, 15},\n    {0x11, 87, 15}, //88\n\n    {0x10, 88, 15},\n    {0x1F, 89, 15},\n    {0x1E, 90, 15},\n    {0x1D, 91, 15}, //92\n\n    {0x1C, 92, 15},\n    {0x1B, 93, 15},\n    {0x1A, 94, 15},\n    {0x19, 95, 15}, //96\n\n    {0x13, 96, 16},\n    {0x12, 97, 16},\n    {0x11, 98, 16},\n    {0x10, 99, 16}, //100\n\n    {0x14, 100, 16},\n    {0x1A, 101, 16},\n    {0x19, 102, 16},\n    {0x18, 103, 16}, //104\n\n    {0x17, 104, 16},\n    {0x16, 105, 16},\n    {0x15, 106, 16},\n    {0x1F, 107, 16}, //108\n\n    {0x1E, 108, 16},\n    {0x1D, 109, 16},\n    {0x1C, 110, 16},\n    {0x1B, 111, 16}  //112\n};\n\nRunLevelPair DCT_Coeff_Table1::runlevel_table[] =\n{\n    //Page 134\n    { 0,\t\t\t1\t},\n    { 1,\t\t\t1\t},\n    { 0,\t\t\t2\t},\n    { 2,\t\t\t1\t},\n    { 0,\t\t\t3\t},\n    { 3,\t\t\t1\t},\n    { 4,\t\t\t1\t},\n    { 1,\t\t\t2\t},\n    { 5,\t\t\t1\t},\n    { 6,\t\t\t1\t},\n    { 7,\t\t\t1\t},\n    { 0,\t\t\t4\t},\n    { 2,\t\t\t2\t},\n    { 8,\t\t\t1\t},\n    { 9,\t\t\t1\t},\n    { RUN_ESCAPE,\t0\t},\n    { 0,\t\t\t5\t},\n    { 0,\t\t\t6\t},\n    { 1,\t\t\t3\t},\n    { 3,\t\t\t2\t},\n    { 10,\t\t\t1\t},\n    { 11,\t\t\t1\t},\n    { 12,\t\t\t1\t},\n    { 13,\t\t\t1\t},\n    { 0,\t\t\t7\t},\n    { 1,\t\t\t4\t},\n    { 2,\t\t\t3\t},\n    { 4,\t\t\t2\t},\n    { 5,\t\t\t2\t},\n    { 14,\t\t\t1\t},\n    { 15,\t\t\t1\t},\n    { 16,\t\t\t1\t},\n\n    //Page 135\n    { 0,\t\t\t8\t},\n    { 0,\t\t\t9\t},\n    { 0,\t\t\t10\t},\n    { 0,\t\t\t11\t},\n    { 1,\t\t\t5\t},\n    { 2,\t\t\t4\t},\n    { 3,\t\t\t3\t},\n    { 4,\t\t\t3\t},\n    { 6,\t\t\t2\t},\n    { 7,\t\t\t2\t},\n    { 8,\t\t\t2\t},\n    { 17,\t\t\t1\t},\n    { 18,\t\t\t1\t},\n    { 19,\t\t\t1\t},\n    { 20,\t\t\t1\t},\n    { 21,\t\t\t1\t},\n    { 0,\t\t\t12\t},\n    { 0,\t\t\t13\t},\n    { 0,\t\t\t14\t},\n    { 0,\t\t\t15\t},\n    { 1,\t\t\t6\t},\n    { 1,\t\t\t7\t},\n    { 2,\t\t\t5\t},\n    { 3,\t\t\t4\t},\n    { 5,\t\t\t3\t},\n    { 9,\t\t\t2\t},\n    { 10,\t\t\t2\t},\n    { 22,\t\t\t1\t},\n    { 23,\t\t\t1\t},\n    { 24,\t\t\t1\t},\n    { 25,\t\t\t1\t},\n    { 26,\t\t\t1\t},\n\n    //Page 132\n    { 0,\t\t\t16\t},\n    { 0,\t\t\t17\t},\n    { 0,\t\t\t18\t},\n    { 0,\t\t\t19\t},\n    { 0,\t\t\t20\t},\n    { 0,\t\t\t21\t},\n    { 0,\t\t\t22\t},\n    { 0,\t\t\t23\t},\n    { 0,\t\t\t24\t},\n    { 0,\t\t\t25\t},\n    { 0,\t\t\t26\t},\n    { 0,\t\t\t27\t},\n    { 0,\t\t\t28\t},\n    { 0,\t\t\t29\t},\n    { 0,\t\t\t30\t},\n    { 0,\t\t\t31\t},\n    { 0,\t\t\t32\t},\n    { 0,\t\t\t33\t},\n    { 0,\t\t\t34\t},\n    { 0,\t\t\t35\t},\n    { 0,\t\t\t36\t},\n    { 0,\t\t\t37\t},\n    { 0,\t\t\t38\t},\n    { 0,\t\t\t39\t},\n    { 0,\t\t\t40\t},\n    { 1,\t\t\t8\t},\n    { 1,\t\t\t9\t},\n    { 1,\t\t\t10\t},\n    { 1,\t\t\t11\t},\n    { 1,\t\t\t12\t},\n    { 1,\t\t\t13\t},\n    { 1,\t\t\t14\t},\n\n\n    { 1,\t\t\t15\t},\n    { 1,\t\t\t16\t},\n    { 1,\t\t\t17\t},\n    { 1,\t\t\t18\t},\n    { 6,\t\t\t3\t},\n    { 11,\t\t\t2\t},\n    { 12,\t\t\t2\t},\n    { 13,\t\t\t2\t},\n    { 14,\t\t\t2\t},\n    { 15,\t\t\t2\t},\n    { 16,\t\t\t2\t},\n    { 27,\t\t\t1\t},\n    { 28,\t\t\t1\t},\n    { 29,\t\t\t1\t},\n    { 30,\t\t\t1\t},\n    { 31,\t\t\t1\t},\n};\n\nunsigned int DCT_Coeff_Table1::index_table[16] =\n{\n    0,\n    0,\n    1,\n    3,\t\t//4\n    4,\t\t//5\n    9,\t\t//6\n    14,\t\t//7\n    23,\t\t//8\n    37,\t\t//9\n    40,\t\t//10\n    40,\t\t//11\n    42,\t\t//12\n    52,\t\t//13\n    64,\t\t//14\n    80,\t\t//15\n    96,\t\t//16\n};\n\nDCT_Coeff_Table1::DCT_Coeff_Table1() :\n    DCT_Coeff(table, SIZE, 16, index_table)\n{\n\n}\n\nbool DCT_Coeff_Table1::get_end_of_block(IPU_FIFO &FIFO, uint32_t &result)\n{\n    if (!FIFO.get_bits(result, 4))\n        return false;\n\n    printf(\"[DCT_Coeff_Table1] EOB: $%08X\\n\", result);\n    result = (result == 6);\n    return true;\n}\n\nbool DCT_Coeff_Table1::get_skip_block(IPU_FIFO &FIFO)\n{\n    uint32_t blorp;\n    if (!FIFO.get_bits(blorp, 4))\n        return false;\n    FIFO.advance_stream(4);\n    return true;\n}\n\nbool DCT_Coeff_Table1::get_runlevel_pair(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1)\n{\n    VLC_Entry entry;\n    if (!peek_symbol(FIFO, entry))\n        return false;\n\n    printf(\"Key: $%08X Value: $%08X Bits: %d\\n\", entry.key, entry.value, entry.bits);\n    int bit_count = entry.bits;\n    RunLevelPair cur_pair = runlevel_table[entry.value];\n    if (cur_pair.run == RUN_ESCAPE)\n    {\n        printf(\"[DCT_Coeff_Table1] RUN_ESCAPE\\n\");\n        uint32_t run = 0;\n        if (!peek_value(FIFO, 6, bit_count, run))\n            return false;\n\n        pair.run = run;\n\n        uint32_t level = 0;\n        if (MPEG1)\n        {\n            if (!peek_value(FIFO, 8, bit_count, level))\n                return false;\n\n            if (!level)\n            {\n                if (!peek_value(FIFO, 8, bit_count, level))\n                    return false;\n            }\n            else if ((uint8_t)level == 128)\n            {\n                if (!peek_value(FIFO, 8, bit_count, level))\n                    return false;\n                level -= 256;\n            }\n            else if (level > 128)\n            {\n                level -= 256;\n            }\n        }\n        else\n        {\n            if (!peek_value(FIFO, 12, bit_count, level))\n                return false;\n\n            if (level & 0x800)\n            {\n                level |= 0xF000;\n                level = (int16_t)level;\n            }\n        }\n\n        pair.level = level;\n    }\n    else\n    {\n        uint32_t sign = 0;\n        if (!peek_value(FIFO, 1, bit_count, sign))\n            return false;\n\n        pair.run = cur_pair.run;\n        if (sign)\n            pair.level = -cur_pair.level;\n        else\n            pair.level = cur_pair.level;\n    }\n    FIFO.advance_stream(bit_count);\n    return true;\n}\n\nbool DCT_Coeff_Table1::get_runlevel_pair_dc(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1)\n{\n    //DCT_Coeff_Table1 only gets called by intra macroblocks, so this should never happen\n    return false;\n}\n"
  },
  {
    "path": "src/ipu/dct_coeff_table1.hpp",
    "content": "#ifndef DCT_COEFF_TABLE1_HPP\n#define DCT_COEFF_TABLE1_HPP\n#include \"dct_coeff.hpp\"\n\nclass DCT_Coeff_Table1 : public DCT_Coeff\n{\n    private:\n        static VLC_Entry table[];\n        static RunLevelPair runlevel_table[];\n        static unsigned int index_table[];\n\n        constexpr static int SIZE = 112;\n    public:\n        DCT_Coeff_Table1();\n\n        bool get_end_of_block(IPU_FIFO &FIFO, uint32_t &result);\n        bool get_skip_block(IPU_FIFO &FIFO);\n        bool get_runlevel_pair(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1);\n        bool get_runlevel_pair_dc(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1);\n};\n\n#endif // DCT_COEFF_TABLE1_HPP\n"
  },
  {
    "path": "src/ipu/ipu.cpp",
    "content": "#include <cmath>\n#include <cstdio>\n#include <cstdlib>\n#include <cstring>\n#include <limits>\n#include \"ipu.hpp\"\n#include \"ee/dmac.h\"\n#include \"ee/intc.h\"\n\n#if defined(IRIS_IPU_TRACE)\n#define printf(...) std::printf(__VA_ARGS__)\n#else\n#define printf(...) ((void)0)\n#endif\n\n/**\n  * The majority of this code is based upon Play!'s implementation of the IPU.\n  * All the relevant files are located in the following links:\n  *\n  * https://github.com/jpd002/Play-/tree/master/Source/ee (IPU base, some tables)\n  * https://github.com/jpd002/Play--Framework/tree/master/include/mpeg2 (Table includes)\n  * https://github.com/jpd002/Play--Framework/tree/master/src/mpeg2 (Tables)\n  * https://github.com/jpd002/Play--Framework/blob/master/src/idct/IEEE1180.cpp (IDCT transformation)\n  */\n\nuint32_t ImageProcessingUnit::inverse_scan_zigzag[0x40] =\n{\n    0,  1,  5,  6, 14, 15, 27, 28,\n    2,  4,  7,  13, 16, 26, 29, 42,\n    3,  8,  12, 17, 25, 30, 41, 43,\n    9,  11, 18, 24, 31, 40, 44, 53,\n    10, 19, 23, 32, 39, 45, 52, 54,\n    20, 22, 33, 38, 46, 51, 55, 60,\n    21, 34, 37, 47, 50, 56, 59, 61,\n    35, 36, 48, 49, 57, 58, 62, 63\n};\n\nuint32_t ImageProcessingUnit::inverse_scan_alternate[0x40] =\n{\n    0, 4, 6, 20, 22, 36, 38, 52,\n    1, 5, 7, 21, 23, 37, 39, 53,\n    2, 8, 19, 24, 34, 40, 50, 54,\n    3, 9, 18, 25, 35, 41, 51, 55,\n    10, 17, 26, 30, 42, 46, 56, 60,\n    11, 16, 27, 31, 43, 47, 57, 61,\n    12, 15, 28, 32, 44, 48, 58, 62,\n    13, 14, 29, 33, 45, 49, 59, 63\n};\n\nuint32_t ImageProcessingUnit::quantizer_linear[0x20] =\n{\n    0,  2,  4,  6,  8,  10,  12,  14,\n    16,  18,  20,  22,  24,  26,  28,  30,\n    32,  34,  36,  38,  40,  42,  44,  46,\n    48,  50,  52,  54,  56,  58,  60,  62\n};\n\nuint32_t ImageProcessingUnit::quantizer_nonlinear[0x20] =\n{\n    0,  1,  2,  3,  4,  5,  6,  7,\n    8,  10,  12,  14,  16,  18,  20,  22,\n    24,  28,  32,  36,  40,  44,  48,  52,\n    56,  64,  72,  80,  88,  96,  104, 112,\n};\n\nstatic const uint8_t default_intra_IQ[0x40] =\n{\n    8,  16, 19, 22, 26, 27, 29, 34,\n    16, 16, 22, 24, 27, 29, 34, 37,\n    19, 22, 26, 27, 29, 34, 34, 38,\n    22, 22, 26, 27, 29, 34, 37, 40,\n    22, 26, 27, 29, 32, 35, 40, 48,\n    26, 27, 29, 32, 35, 40, 48, 58,\n    26, 27, 29, 34, 38, 46, 56, 69,\n    27, 29, 35, 38, 46, 56, 69, 83,\n};\n\nstatic const uint8_t default_nonintra_IQ[0x40] =\n{\n    16, 17, 18, 19, 20, 21, 22, 23,\n    17, 18, 19, 20, 21, 22, 23, 24,\n    18, 19, 20, 21, 22, 23, 24, 25,\n    19, 20, 21, 22, 23, 24, 26, 27,\n    20, 21, 22, 23, 25, 26, 27, 28,\n    21, 22, 23, 24, 26, 27, 28, 30,\n    22, 23, 24, 26, 27, 28, 30, 31,\n    23, 24, 25, 27, 28, 30, 31, 33,\n};\n\nImageProcessingUnit::ImageProcessingUnit(struct ps2_intc* intc, struct ps2_dmac* dmac) : intc(intc), dmac(dmac)\n{\n    //Generate CrCb->RGB conversion map\n    for (unsigned int i = 0; i < 0x40; i += 0x8)\n    {\n        for (unsigned int j = 0; j < 0x10; j += 2)\n        {\n            int index = j + (i * 4);\n            crcb_map[index + 0x00] = (j / 2) + i;\n            crcb_map[index + 0x01] = (j / 2) + i;\n\n            crcb_map[index + 0x10] = (j / 2) + i;\n            crcb_map[index + 0x11] = (j / 2) + i;\n        }\n    }\n\n    //I'm assuming the dithering process rounds down so I've rounded the matrix values down\n    dither_mtx[0][0] = -4;\n    dither_mtx[0][1] = 0;\n    dither_mtx[0][2] = -3;\n    dither_mtx[0][3] = 1;\n    dither_mtx[1][0] = 2;\n    dither_mtx[1][1] = -2;\n    dither_mtx[1][2] = 3;\n    dither_mtx[1][3] = -1;\n    dither_mtx[2][0] = -3;\n    dither_mtx[2][1] = 1;\n    dither_mtx[2][2] = -4;\n    dither_mtx[2][3] = 0;\n    dither_mtx[3][0] = 3;\n    dither_mtx[3][1] = -1;\n    dither_mtx[3][2] = 2;\n    dither_mtx[3][3] = -2;\n}\n\nvoid ImageProcessingUnit::reset()\n{\n    dct_coeff = nullptr;\n    VDEC_table = nullptr;\n    in_FIFO.reset();\n    out_FIFO.reset();\n    prepare_IDCT();\n    memcpy(intra_IQ, default_intra_IQ, sizeof(intra_IQ));\n    memcpy(nonintra_IQ, default_nonintra_IQ, sizeof(nonintra_IQ));\n\n    ctrl.error_code = false;\n    ctrl.start_code = false;\n    ctrl.intra_DC_precision = 0;\n    ctrl.MPEG1 = false;\n    ctrl.picture_type = 0;\n    ctrl.busy = false;\n    ctrl.coded_block_pattern = 0;\n    command = 0;\n    command_option = 0;\n    bytes_left = 0;\n    command_decoding = false;\n}\n\nvoid ImageProcessingUnit::run()\n{\n    if (ctrl.busy)\n    {\n        try\n        {\n            switch (command)\n            {\n                case 0x01:\n                    if (in_FIFO.f.size())\n                    {\n                        if (process_IDEC())\n                            finish_command();\n                    }\n                    break;\n                case 0x02:\n                    if (in_FIFO.f.size())\n                    {\n                        if (process_BDEC())\n                            finish_command();\n                    }\n                    break;\n                case 0x03:\n                    if (in_FIFO.f.size())\n                        process_VDEC();\n                    break;\n                case 0x04:\n                    if (in_FIFO.f.size())\n                        process_FDEC();\n                    break;\n                case 0x05:\n                    if (setiq_state == SETIQ_STATE::ADVANCE)\n                    {\n                        if (!in_FIFO.advance_stream(command_option & 0x3F))\n                            break;\n\n                        setiq_state = SETIQ_STATE::POPULATE_TABLE;\n                    }\n                    while (bytes_left && in_FIFO.f.size())\n                    {\n                        uint32_t value;\n                        if (!in_FIFO.get_bits(value, 8))\n                            break;\n                        in_FIFO.advance_stream(8);\n                        int index = 64 - bytes_left;\n                        if (command_option & (1 << 27))\n                            nonintra_IQ[index] = value & 0xFF;\n                        else\n                            intra_IQ[index] = value & 0xFF;\n                        bytes_left--;\n                    }\n                    if (bytes_left <= 0)\n                        ctrl.busy = false;\n                    break;\n                case 0x06:\n                    while (bytes_left && in_FIFO.f.size())\n                    {\n                        uint128_t quad = in_FIFO.f.front();\n                        in_FIFO.f.pop_front();\n                        for (int i = 0; i < 8; i++)\n                        {\n                            int index = (32 - bytes_left) >> 1;\n                            VQCLUT[index] = quad.u16[i];\n                            bytes_left -= 2;\n                        }\n                    }\n                    if (bytes_left <= 0)\n                        ctrl.busy = false;\n                    break;\n                case 0x07:\n                    if (in_FIFO.f.size())\n                    {\n                        if (process_CSC())\n                            finish_command();\n                    }\n                    break;\n                case 0x08:\n                    if (in_FIFO.f.size())\n                    {\n                        if (process_PACK())\n                            finish_command();\n                    }\n                    break;\n            }\n        }\n        catch (VLC_Error& e)\n        {\n            std::fprintf(stderr, \"ipu: VLC error: %s\\n\", e.what());\n\n            ctrl.error_code = true;\n            finish_command();\n        }\n    }\n    if (can_write_FIFO()) {\n        dmac->ipu_to.dreq = 1;\n        // printf(\"ipu: set ipu_to dreq\\n\");\n        dmac_handle_ipu_to_transfer(dmac);\n    }\n    if (can_read_FIFO()) {\n        dmac->ipu_from.dreq = 1;\n        printf(\"ipu: Output FIFO ready\\n\");\n        // printf(\"ipu: set ipu_from dreq out=%x\\n\", out_FIFO.f.size());\n        dmac_handle_ipu_from_transfer(dmac);\n    }\n}\n\nvoid ImageProcessingUnit::finish_command()\n{\n    ctrl.busy = false;\n    command_decoding = false;\n\n    ps2_intc_irq(intc, EE_INTC_IPU);\n}\n\nbool ImageProcessingUnit::process_IDEC()\n{\n    while (true)\n    {\n        switch (idec.state)\n        {\n            case IDEC_STATE::DELAY:\n                //Play delays IDEC execution before consuming FIFO data.\n                idec.state = IDEC_STATE::ADVANCE;\n                return false;\n            case IDEC_STATE::ADVANCE:\n                printf(\"ipu: Advance stream\\n\");\n                if (!in_FIFO.advance_stream(command_option & 0x3F))\n                    return false;\n                idec.state = IDEC_STATE::MACRO_I_TYPE;\n                break;\n            case IDEC_STATE::MACRO_I_TYPE:\n                printf(\"ipu: Decode macroblock I type\\n\");\n                if (!macroblock_I_pic.get_symbol(in_FIFO, idec.macro_type))\n                    return false;\n                idec.state = IDEC_STATE::DCT_TYPE;\n                break;\n            case IDEC_STATE::DCT_TYPE:\n                printf(\"ipu: Decode DCT\\n\");\n                if (idec.decodes_dct)\n                {\n                    uint32_t value;\n                    if (!in_FIFO.get_bits(value, 1))\n                        return false;\n                    in_FIFO.advance_stream(1);\n\n                    //Play expects this to be zero for IDEC intra path.\n                    if (value != 0)\n                        throw VLC_Error(\"IDEC unsupported DCT type\");\n                }\n                idec.state = IDEC_STATE::QSC;\n                break;\n            case IDEC_STATE::QSC:\n                printf(\"ipu: Decode QSC\\n\");\n                if (idec.macro_type & 0x10)\n                {\n                    if (!in_FIFO.get_bits(idec.qsc, 5))\n                        return false;\n                    in_FIFO.advance_stream(5);\n                }\n                idec.state = IDEC_STATE::INIT_BDEC;\n                break;\n            case IDEC_STATE::INIT_BDEC:\n                //We don't need to advance, and the macroblock is always intra so no need to check for a CBP.\n                printf(\"ipu: Init BDEC\\n\");\n                bdec.state = BDEC_STATE::RESET_DC;\n                bdec.intra = true;\n                bdec.quantizer_step = idec.qsc;\n                bdec.out_fifo = &idec.temp_fifo;\n                ctrl.coded_block_pattern = 0x3F;\n                bdec.block_index = 0;\n                bdec.cur_channel = 0;\n                bdec.reset_dc = idec.blocks_decoded == 0;\n                bdec.check_start_code = false;\n                idec.state = IDEC_STATE::READ_BLOCK;\n                break;\n            case IDEC_STATE::READ_BLOCK:\n                printf(\"ipu: Read macroblock\\n\");\n                if (!process_BDEC())\n                    return false;\n                idec.blocks_decoded++;\n                idec.state = IDEC_STATE::INIT_CSC;\n                break;\n            case IDEC_STATE::INIT_CSC:\n                //BDEC outputs in RAW16. CSC works in RAW8, so we need to convert appropriately.\n                printf(\"ipu: Init CSC\\n\");\n                for (int i = 0; i < RAW_BLOCK_SIZE / 8; i++)\n                {\n                    uint128_t quad = idec.temp_fifo.f.front();\n                    idec.temp_fifo.f.pop_front();\n\n                    int offset = i * 8;\n\n                    for (int j = 0; j < 8; j++)\n                    {\n                        int16_t data = (int16_t)quad.u16[j];\n                        if (data < 0)\n                            data = 0;\n                        if (data > 255)\n                            data = 255;\n                        csc.block[offset + j] = (uint8_t)data;\n                    }\n                }\n                csc.state = CSC_STATE::CONVERT;\n                csc.block_index = 0;\n                csc.macroblocks = 1;\n\n                idec.state = IDEC_STATE::EXEC_CSC;\n                break;\n            case IDEC_STATE::EXEC_CSC:\n                printf(\"ipu: Exec CSC\\n\");\n                if (!process_CSC())\n                    return false;\n                idec.state = IDEC_STATE::CHECK_START_CODE;\n                break;\n            case IDEC_STATE::CHECK_START_CODE:\n            {\n                printf(\"ipu: Check start code\\n\");\n                uint32_t code;\n                if (!in_FIFO.get_bits(code, 8))\n                    return false;\n                if (!code)\n                {\n                    idec.state = IDEC_STATE::VALID_START_CODE;\n                    in_FIFO.byte_align();\n                }\n                else\n                    idec.state = IDEC_STATE::MACRO_INC;\n            }\n                break;\n            case IDEC_STATE::VALID_START_CODE:\n            {\n                printf(\"ipu: Validate start code\\n\");\n                uint32_t code;\n                if (!in_FIFO.get_bits(code, 24))\n                {\n                    //If we already detected 8 zero bits in CHECK_START_CODE but don't have\n                    //enough bits for a full 24-bit validation code, treat this as a valid\n                    //start-code boundary and finish the command.\n                    idec.state = IDEC_STATE::DONE;\n                    break;\n                }\n\n                if (code == 0)\n                {\n                    //Consume one byte of zero padding and keep searching for 0x000001.\n                    if (!in_FIFO.advance_stream(8))\n                        return false;\n                }\n                else if (code == 1)\n                {\n                    idec.state = IDEC_STATE::DONE;\n                }\n                else\n                {\n                    throw VLC_Error(\"IDEC start code invalid\");\n                }\n            }\n                break;\n            case IDEC_STATE::MACRO_INC:\n            {\n                printf(\"ipu: Macroblock increment\\n\");\n                uint32_t inc;\n                if (!macroblock_increment.get_symbol(in_FIFO, inc))\n                    return false;\n\n                if ((inc & 0xFFFF) != 1)\n                    throw VLC_Error(\"IDEC invalid macroblock increment\");\n\n                idec.state = IDEC_STATE::MACRO_I_TYPE;\n            }\n                break;\n            case IDEC_STATE::DONE:\n                printf(\"ipu: IDEC done!\\n\");\n                return true;\n        }\n    }\n}\n\nbool ImageProcessingUnit::process_BDEC()\n{\n    while (true)\n    {\n        switch (bdec.state)\n        {\n            case BDEC_STATE::ADVANCE:\n                if (!in_FIFO.advance_stream(command_option & 0x3F))\n                    return false;\n                bdec.state = BDEC_STATE::GET_CBP;\n                break;\n            case BDEC_STATE::GET_CBP:\n                printf(\"ipu: Get CBP!\\n\");\n                if (!bdec.intra)\n                {\n                    uint32_t pattern;\n                    if (!cbp.get_symbol(in_FIFO, pattern))\n                        return false;\n                    ctrl.coded_block_pattern = pattern;\n                    printf(\"CBP: %d\\n\", ctrl.coded_block_pattern);\n                }\n                else\n                    ctrl.coded_block_pattern = 0x3F;\n                bdec.state = BDEC_STATE::RESET_DC;\n                break;\n            case BDEC_STATE::RESET_DC:\n                if (bdec.reset_dc)\n                {\n                    printf(\"ipu: Reset DC!\\n\");\n\n                    int16_t value;\n                    switch (ctrl.intra_DC_precision)\n                    {\n                        case 0:\n                            value = 128;\n                            break;\n                        case 1:\n                            value = 256;\n                            break;\n                        case 2:\n                            value = 512;\n                            break;\n                    }\n\n                    for (int i = 0; i < 3; i++)\n                        bdec.dc_predictor[i] = value;\n                }\n                bdec.state = BDEC_STATE::BEGIN_DECODING;\n                break;\n            case BDEC_STATE::BEGIN_DECODING:\n                printf(\"ipu: Begin decoding block %d!\\n\", bdec.block_index);\n\n                bdec.cur_block = bdec.blocks[bdec.block_index];\n                memset(bdec.cur_block, 0, sizeof(int16_t) * 64);\n\n                if (ctrl.coded_block_pattern & (1 << (5 - bdec.block_index)))\n                {\n                    //If block index is Cb/Cr, set the channel to the relevant chromance one\n                    if (bdec.block_index > 3)\n                        bdec.cur_channel = bdec.block_index - 3;\n                    else\n                        bdec.cur_channel = 0;\n\n                    if (bdec.intra && ctrl.intra_VLC_table)\n                    {\n                        printf(\"ipu: Use DCT coefficient table 1\\n\");\n                        dct_coeff = &dct_coeff1;\n                    }\n                    else\n                    {\n                        printf(\"ipu: Use DCT coefficient table 0\\n\");\n                        dct_coeff = &dct_coeff0;\n                    }\n\n                    bdec.read_coeff_state = BDEC_Command::READ_COEFF::INIT;\n                    bdec.state = BDEC_STATE::READ_COEFFS;\n                }\n                else\n                    bdec.state = BDEC_STATE::LOAD_NEXT_BLOCK;\n                break;\n            case BDEC_STATE::READ_COEFFS:\n            {\n                printf(\"ipu: Read coeffs!\\n\");\n                if (!BDEC_read_coeffs())\n                    return false;\n                printf(\"ipu: Dequantize!\\n\");\n                dequantize(bdec.cur_block);\n                printf(\"ipu: Inverse scan!\\n\");\n                inverse_scan(bdec.cur_block);\n                printf(\"ipu: IDCT!\\n\");\n\n                int16_t temp[0x40];\n                memcpy(temp, bdec.cur_block, 0x40 * sizeof(int16_t));\n                perform_IDCT(temp, bdec.cur_block);\n                bdec.state = BDEC_STATE::LOAD_NEXT_BLOCK;\n            }\n                break;\n            case BDEC_STATE::LOAD_NEXT_BLOCK:\n                printf(\"ipu: Load next block!\\n\");\n                bdec.block_index++;\n                if (bdec.block_index == 6)\n                    bdec.state = BDEC_STATE::DONE;\n                else\n                    bdec.state = BDEC_STATE::BEGIN_DECODING;\n                break;\n            case BDEC_STATE::DONE:\n            {\n                printf(\"ipu: BDEC done!\\n\");\n                uint128_t quad;\n                for (int i = 0; i < 8; i++)\n                {\n                    memcpy(quad.u8, bdec.blocks[0] + (i * 8), sizeof(int16_t) * 8);\n                    bdec.out_fifo->f.push_back(quad);\n                    memcpy(quad.u8, bdec.blocks[1] + (i * 8), sizeof(int16_t) * 8);\n                    bdec.out_fifo->f.push_back(quad);\n                }\n\n                for (int i = 0; i < 8; i++)\n                {\n                    memcpy(quad.u8, bdec.blocks[2] + (i * 8), sizeof(int16_t) * 8);\n                    bdec.out_fifo->f.push_back(quad);\n                    memcpy(quad.u8, bdec.blocks[3] + (i * 8), sizeof(int16_t) * 8);\n                    bdec.out_fifo->f.push_back(quad);\n                }\n\n                for (int i = 0; i < 8; i++)\n                {\n                    memcpy(quad.u8, bdec.blocks[4] + (i * 8), sizeof(int16_t) * 8);\n                    bdec.out_fifo->f.push_back(quad);\n                }\n\n                for (int i = 0; i < 8; i++)\n                {\n                    memcpy(quad.u8, bdec.blocks[5] + (i * 8), sizeof(int16_t) * 8);\n                    bdec.out_fifo->f.push_back(quad);\n                }\n\n                if (bdec.check_start_code)\n                    bdec.state = BDEC_STATE::CHECK_START_CODE;\n                else\n                    return true;\n            }\n                break;\n            case BDEC_STATE::CHECK_START_CODE:\n            {\n                uint32_t bits;\n                if (in_FIFO.get_bits(bits, 8))\n                {\n                    if (!bits)\n                    {\n                        ctrl.start_code = true;\n                        printf(\"ipu: Start code detected!\\n\");\n                    }\n                    return true;\n                }\n                else\n                    return false;\n            }\n        }\n    }\n}\n\nvoid ImageProcessingUnit::inverse_scan(int16_t *block)\n{\n    int16_t temp[0x40];\n    memcpy(temp, block, 0x40 * sizeof(int16_t));\n\n    int id;\n    for (int i = 0; i < 0x40; i++)\n    {\n        if (ctrl.alternate_scan)\n            id = inverse_scan_alternate[i];\n        else\n            id = inverse_scan_zigzag[i];\n        block[i] = temp[id];\n    }\n}\n\nvoid ImageProcessingUnit::dequantize(int16_t *block)\n{\n    int q_scale;\n    if (ctrl.nonlinear_Q_step)\n        q_scale = quantizer_nonlinear[bdec.quantizer_step];\n    else\n        q_scale = quantizer_linear[bdec.quantizer_step];\n    if (bdec.intra)\n    {\n        switch (ctrl.intra_DC_precision)\n        {\n            case 0:\n                block[0] *= 8;\n                break;\n            case 1:\n                block[0] *= 4;\n                break;\n            case 2:\n                block[0] *= 2;\n                break;\n            default:\n                printf(\"ipu: Dequantize: Intra DC precision == 3!\\n\");\n                block[0] = 0;\n                break;\n        }\n\n        for (int i = 1; i < 0x40; i++)\n        {\n            int16_t sign;\n            if (!block[i])\n                sign = 0;\n            else\n            {\n                if (block[i] > 0)\n                    sign = 1;\n                else\n                    sign = (int16_t)0xFFFF;\n            }\n\n            int32_t scaled = (int32_t)block[i] * (int32_t)intra_IQ[i] * q_scale * 2;\n            block[i] = (int16_t)(scaled / 32);\n\n            if (sign)\n            {\n                if (!(block[i] & 0x1))\n                {\n                    block[i] -= sign;\n                    block[i] |= 1;\n                }\n            }\n        }\n    }\n    else\n    {\n        for (int i = 0; i < 64; i++)\n        {\n            int16_t sign;\n            if (!block[i])\n                sign = 0;\n            else\n            {\n                if (block[i] > 0)\n                    sign = 1;\n                else\n                    sign = 0xFFFF;\n            }\n\n            int32_t scaled = (((int32_t)block[i] * 2) + sign) * (int32_t)nonintra_IQ[i] * q_scale;\n            block[i] = (int16_t)(scaled / 32);\n\n            if (sign)\n            {\n                if (!(block[i] & 0x1))\n                {\n                    block[i] -= sign;\n                    block[i] |= 1;\n                }\n            }\n        }\n    }\n\n    //Saturation step\n    for (int i = 0; i < 64; i++)\n    {\n        if (block[i] > 2047)\n            block[i] = 2047;\n        if (block[i] < -2048)\n            block[i] = -2048;\n    }\n}\n\n//IDCT code here taken from mpeg2decode\n//Copyright (C) 1996, MPEG Software Simulation Group. All Rights Reserved.\n\n#ifndef PI\n# ifdef M_PI\n#  define PI M_PI\n# else\n#  define PI 3.14159265358979323846\n# endif\n#endif\nvoid ImageProcessingUnit::prepare_IDCT()\n{\n    int freq, time;\n    double scale;\n\n    for (freq=0; freq < 8; freq++)\n    {\n        scale = (freq == 0) ? sqrt(0.125) : 0.5;\n        for (time=0; time<8; time++)\n        {\n            IDCT_table[freq][time] = scale*cos((PI/8.0)*freq*(time + 0.5));\n        }\n    }\n}\n\nvoid ImageProcessingUnit::perform_IDCT(const int16_t* pUV, int16_t* pXY)\n{\n    int i, j, k, v;\n    double partial_product;\n    double tmp[64];\n\n    for (i=0; i<8; i++)\n    {\n        for (j=0; j<8; j++)\n        {\n            partial_product = 0.0;\n\n            for (k=0; k<8; k++)\n            {\n                partial_product+= IDCT_table[k][j]*pUV[8*i+k];\n            }\n\n            tmp[8*i+j] = partial_product;\n        }\n    }\n\n  /* Transpose operation is integrated into address mapping by switching\n     loop order of i and j */\n\n    for (j=0; j<8; j++)\n    {\n        for (i=0; i<8; i++)\n        {\n            partial_product = 0.0;\n\n            for (k=0; k<8; k++)\n            {\n                partial_product+= IDCT_table[k][i]*tmp[8*k+j];\n            }\n\n            v = (int) floor(partial_product+0.5);\n            pXY[8*i+j] = v;\n        }\n    }\n}\n\n//End IDCT code\n\nbool ImageProcessingUnit::BDEC_read_coeffs()\n{\n    while (true)\n    {\n        switch (bdec.read_coeff_state)\n        {\n            case BDEC_Command::READ_COEFF::INIT:\n                printf(\"ipu: READ_COEFF Init!\\n\");\n                bdec.read_diff_state = BDEC_Command::READ_DIFF::SIZE;\n                bdec.subblock_index = 0;\n                if (bdec.intra)\n                {\n                    bdec.subblock_index = 1;\n                    bdec.read_coeff_state = BDEC_Command::READ_COEFF::READ_DC_DIFF;\n                }\n                else\n                    bdec.read_coeff_state = BDEC_Command::READ_COEFF::CHECK_END;\n                break;\n            case BDEC_Command::READ_COEFF::READ_DC_DIFF:\n                printf(\"ipu: READ_COEFF Read DC diffs!\\n\");\n                if (!BDEC_read_diff())\n                    return false;\n                bdec.cur_block[0] = (int16_t)(bdec.dc_predictor[bdec.cur_channel] + bdec.dc_diff);\n                bdec.dc_predictor[bdec.cur_channel] = bdec.cur_block[0];\n                bdec.read_coeff_state = BDEC_Command::READ_COEFF::CHECK_END;\n                break;\n            case BDEC_Command::READ_COEFF::CHECK_END:\n                printf(\"ipu: READ_COEFF Check end of block!\\n\");\n            {\n                uint32_t end = 0;\n                if (!dct_coeff->get_end_of_block(in_FIFO, end))\n                    return false;\n                if (bdec.subblock_index && end)\n                    bdec.read_coeff_state = BDEC_Command::READ_COEFF::SKIP_END;\n                else\n                    bdec.read_coeff_state = BDEC_Command::READ_COEFF::COEFF;\n            }\n                break;\n            case BDEC_Command::READ_COEFF::COEFF:\n                printf(\"ipu: READ_COEFF Read coeffs!\\n\");\n            {\n                RunLevelPair pair;\n                if (!bdec.subblock_index)\n                {\n                    if (!dct_coeff->get_runlevel_pair_dc(in_FIFO, pair, ctrl.MPEG1))\n                        return false;\n                }\n                else\n                {\n                    if (!dct_coeff->get_runlevel_pair(in_FIFO, pair, ctrl.MPEG1))\n                        return false;\n                }\n                printf(\"ipu: Run: %d Level: %d\\n\", pair.run, pair.level);\n                bdec.subblock_index += pair.run;\n\n                if (bdec.subblock_index < 0x40)\n                {\n                    bdec.cur_block[bdec.subblock_index] = (int16_t)pair.level;\n                }\n                else\n                {\n                    throw VLC_Error(\"BDEC coefficient index overflow\");\n                }\n                bdec.subblock_index++;\n                bdec.read_coeff_state = BDEC_Command::READ_COEFF::CHECK_END;\n            }\n                break;\n            case BDEC_Command::READ_COEFF::SKIP_END:\n                printf(\"ipu: READ_COEFF Skip end!\\n\");\n                if (!dct_coeff->get_skip_block(in_FIFO))\n                    return false;\n                return true;\n        }\n    }\n}\n\nbool ImageProcessingUnit::BDEC_read_diff()\n{\n    while (true)\n    {\n        switch (bdec.read_diff_state)\n        {\n            case BDEC_Command::READ_DIFF::SIZE:\n                printf(\"ipu: READ_DIFF SIZE!\\n\");\n                if (bdec.cur_channel == 0)\n                {\n                    if (!lum_table.get_symbol(in_FIFO, bdec.dc_size))\n                        return false;\n                }\n                else\n                {\n                    if (!chrom_table.get_symbol(in_FIFO, bdec.dc_size))\n                        return false;\n                }\n                bdec.read_diff_state = BDEC_Command::READ_DIFF::DIFF;\n                break;\n            case BDEC_Command::READ_DIFF::DIFF:\n                printf(\"ipu: READ_DIFF DIFF!\\n\");\n                if (!bdec.dc_size)\n                    bdec.dc_diff = 0;\n                else\n                {\n                    uint32_t result = 0;\n                    if (!in_FIFO.get_bits(result, bdec.dc_size))\n                        return false;\n\n                    if (!in_FIFO.advance_stream(bdec.dc_size))\n                        return false;\n\n                    int16_t half_range = 1 << (bdec.dc_size - 1);\n                    bdec.dc_diff = (int16_t)result;\n                    if (bdec.dc_diff < half_range)\n                        bdec.dc_diff += 1 - (2 * half_range);\n                }\n                return true;\n        }\n    }\n}\n\nvoid ImageProcessingUnit::convert_RGB32_to_RGB16(const uint8_t* rgb32, uint16_t* rgb16, bool dithering)\n{\n    for (int i = 0; i < 16; ++i)\n    {\n        for (int j = 0; j < 16; ++j)\n        {\n            //It's worth noting that bit 30 is the alpha bit for RGB16, not bit 31.\n            const int index = j + (i * 16);\n            const int dither = dithering ? dither_mtx[i & 3][j & 3] : 0;\n            const int r = std::max(0, std::min(rgb32[4 * index] + dither, 255)) >> 3;\n            const int g = std::max(0, std::min(rgb32[4 * index + 1] + dither, 255)) >> 3;\n            const int b = std::max(0, std::min(rgb32[4 * index + 2] + dither, 255)) >> 3;\n            const int a = rgb32[4 * index + 3] == 0x40;\n            rgb16[index] = r | g  << 5 | b << 10 | a << 15;\n        }\n    }\n}\n\nvoid ImageProcessingUnit::process_VDEC()\n{\n    int table = command_option >> 26;\n    switch (table)\n    {\n        case 0:\n            printf(\"ipu: MBAI\\n\");\n            VDEC_table = &macroblock_increment;\n            break;\n        case 1:\n            printf(\"ipu: MBT\\n\");\n            switch (ctrl.picture_type)\n            {\n                case 0x1:\n                    printf(\"ipu: I pic\\n\");\n                    VDEC_table = &macroblock_I_pic;\n                    break;\n                case 0x2:\n                    printf(\"ipu: P pic\\n\");\n                    VDEC_table = &macroblock_P_pic;\n                    break;\n                case 0x3:\n                    printf(\"ipu: B pic\\n\");\n                    VDEC_table = &macroblock_B_pic;\n                    break;\n            }\n            break;\n        case 2:\n            printf(\"ipu: MC\\n\");\n            VDEC_table = &motioncode;\n            break;\n    }\n\n    while (true)\n    {\n        switch (vdec_state)\n        {\n            case VDEC_STATE::ADVANCE:\n                if (!in_FIFO.advance_stream(command_option & 0x3F))\n                    return;\n                vdec_state = VDEC_STATE::DECODE;\n                break;\n            case VDEC_STATE::DECODE:\n                if (!VDEC_table->get_symbol(in_FIFO, command_output))\n                    return;\n                else\n                    vdec_state = VDEC_STATE::DONE;\n                break;\n            case VDEC_STATE::DONE:\n                printf(\"ipu: VDEC done! Output: $%08X infifo=%d\\n\", command_output, in_FIFO.f.size());\n                finish_command();\n                return;\n        }\n    }\n}\n\nvoid ImageProcessingUnit::process_FDEC()\n{\n    while (true)\n    {\n        switch (fdec_state)\n        {\n            case VDEC_STATE::ADVANCE:\n                if (!in_FIFO.advance_stream(command_option & 0x3F))\n                    return;\n                fdec_state = VDEC_STATE::DECODE;\n                break;\n            case VDEC_STATE::DECODE:\n                if (!in_FIFO.get_bits(command_output, 32))\n                    return;\n                fdec_state = VDEC_STATE::DONE;\n                break;\n            case VDEC_STATE::DONE:\n                finish_command();\n                printf(\"ipu: FDEC result: $%08X\\n\", command_output);\n                return;\n        }\n    }\n}\n\nbool ImageProcessingUnit::process_CSC()\n{\n    while (true)\n    {\n        switch (csc.state)\n        {\n            case CSC_STATE::BEGIN:\n                if (csc.macroblocks)\n                {\n                    csc.state = CSC_STATE::READ;\n                    csc.block_index = 0;\n                }\n                else\n                    csc.state = CSC_STATE::DONE;\n                break;\n            case CSC_STATE::READ:\n                if (csc.block_index == RAW_BLOCK_SIZE)\n                    csc.state = CSC_STATE::CONVERT;\n                else\n                {\n                    uint32_t value;\n                    if (!in_FIFO.get_bits(value, 8))\n                        return false;\n                    in_FIFO.advance_stream(8);\n                    csc.block[csc.block_index] = value & 0xFF;\n                    csc.block_index++;\n                }\n                break;\n            case CSC_STATE::CONVERT:\n            {\n                uint8_t rgb32[4 * RGB_BLOCK_SIZE];\n\n                uint8_t* lum_block = csc.block;\n                uint8_t* cb_block = csc.block + 0x100;\n                uint8_t* cr_block = csc.block + 0x140;\n\n                uint16_t alphaTh0 = (TH0 & 0x1FF);\n                uint16_t alphaTh1 = (TH1 & 0x1FF);\n\n                for (int i = 0; i < 16; i++)\n                {\n                    for (int j = 0; j < 16; j++)\n                    {\n                        int index = j + (i * 16);\n                        float lum = lum_block[index];\n                        float cb = cb_block[crcb_map[index]];\n                        float cr = cr_block[crcb_map[index]];\n\n                        float r = lum + 1.402f * (cr - 128);\n                        float g = lum - 0.34414f * (cb - 128) - 0.71414f * (cr - 128);\n                        float b = lum + 1.772f * (cb - 128);\n\n                        if (r < 0)\n                            r = 0;\n                        if (r > 255)\n                            r = 255;\n                        if (g < 0)\n                            g = 0;\n                        if (g > 255)\n                            g = 255;\n                        if (b < 0)\n                            b = 0;\n                        if (b > 255)\n                            b = 255;\n\n                        uint8_t alpha;\n                        if (r < alphaTh0 && g < alphaTh0 && b < alphaTh0)\n                            alpha = 0;\n                        else if (r < alphaTh1 && g < alphaTh1 && b < alphaTh1)\n                            alpha = 0x40;\n                        else\n                            alpha = 0x80;\n\n                        rgb32[4 * index] = (uint8_t)r;\n                        rgb32[4 * index + 1] = (uint8_t)g;\n                        rgb32[4 * index + 2] = (uint8_t)b;\n                        rgb32[4 * index + 3] = alpha;\n                    }\n                }\n\n                uint128_t quad;\n                if (csc.use_RGB16)\n                {\n                    uint16_t rgb16[RGB_BLOCK_SIZE];\n\n                    convert_RGB32_to_RGB16(rgb32, rgb16, csc.use_dithering);\n\n                    for (int i = 0; i < RGB_BLOCK_SIZE / 8; i++)\n                    {\n                        for (int j = 0; j < 8; j++)\n                        {\n                            quad.u16[j] = rgb16[j + (i * 8)];\n                        }\n                        out_FIFO.f.push_back(quad);\n                    }\n                }\n                else\n                {\n                    for (int i = 0; i < RGB_BLOCK_SIZE / 4; i++)\n                    {\n                        for (int j = 0; j < 4; j++)\n                        {\n                            int index = 4 * (j + (i * 4));\n                            uint8_t r = rgb32[index];\n                            uint8_t g = rgb32[index + 1];\n                            uint8_t b = rgb32[index + 2];\n                            uint8_t a = rgb32[index + 3];\n                            uint32_t color = r | g << 8 | b << 16 | a << 24;\n                            quad.u32[j] = color;\n                        }\n                        out_FIFO.f.push_back(quad);\n                    }\n                }\n                csc.macroblocks--;\n                csc.state = CSC_STATE::BEGIN;\n                dmac->ipu_from.dreq = 1;\n                printf(\"ipu: set ipu_from dreq out=%x\\n\", out_FIFO.f.size());\n                dmac_handle_ipu_from_transfer(dmac);\n            }\n                break;\n            case CSC_STATE::DONE:\n                printf(\"ipu: CSC done!\\n\");\n                return true;\n        }\n    }\n}\n\nbool ImageProcessingUnit::process_PACK()\n{\n    while (true)\n    {\n        switch (pack.state)\n        {\n            case PACK_STATE::BEGIN:\n                if (pack.macroblocks)\n                {\n                    pack.state = PACK_STATE::READ;\n                    pack.block_index = 0;\n                }\n                else\n                    pack.state = PACK_STATE::DONE;\n                break;\n            case PACK_STATE::READ:\n                if (pack.block_index == 4 * RGB_BLOCK_SIZE)\n                    pack.state = PACK_STATE::CONVERT;\n                else\n                {\n                    uint32_t value;\n                    if (!in_FIFO.get_bits(value, 8))\n                        return false;\n                    in_FIFO.advance_stream(8);\n                    pack.block[pack.block_index] = value & 0xFF;\n                    pack.block_index++;\n                }\n                break;\n            case PACK_STATE::CONVERT:\n            {\n                uint16_t rgb16[RGB_BLOCK_SIZE];\n\n                convert_RGB32_to_RGB16(pack.block, rgb16, pack.use_dithering);\n\n                uint128_t quad;\n                if (pack.use_RGB16)\n                {\n                    for (int i = 0; i < RGB_BLOCK_SIZE / 8; ++i)\n                    {\n                        for (int j = 0; j < 8; ++j)\n                        {\n                            quad.u16[j] = rgb16[j + (i * 8)];\n                        }\n                        out_FIFO.f.push_back(quad);\n                    }\n                }\n                else\n                {\n                    int clut_r[16];\n                    int clut_g[16];\n                    int clut_b[16];\n                    for (int i = 0; i < 16; ++i)\n                    {\n                        clut_r[i] = VQCLUT[i] & 0x1F;\n                        clut_g[i] = (VQCLUT[i] >> 5) & 0x1F;\n                        clut_b[i] = (VQCLUT[i] >> 10) & 0x1F;\n                    }\n\n                    auto closest_index = [&](uint16_t color) {\n                        const int r = color & 0x1F;\n                        const int g = (color >> 5) & 0x1F;\n                        const int b = (color >> 10) & 0x1F;\n\n                        uint8_t index = 0;\n                        int min_distance = std::numeric_limits<int>::max();\n                        for (uint8_t i = 0; i < 16; ++i)\n                        {\n                            const int dr = r - clut_r[i];\n                            const int dg = g - clut_g[i];\n                            const int db = b - clut_b[i];\n                            const int distance = dr * dr + dg * dg + db * db;\n\n                            // TODO: If two distances are the same which index is used?\n                            if (min_distance > distance)\n                            {\n                                index = i;\n                                min_distance = distance;\n                            }\n                        }\n\n                        return index;\n                    };\n\n                    for (int i = 0; i < RGB_BLOCK_SIZE / 32; ++i)\n                    {\n                        for (int j = 0; j < 16; ++j)\n                        {\n                            int index = 2 * j + (i * 32);\n                            const uint16_t color16_low = rgb16[index];\n                            const uint16_t color16_high = rgb16[index + 1];\n                            quad.u8[j] = closest_index(color16_high) << 4 | closest_index(color16_low);\n                        }\n                        out_FIFO.f.push_back(quad);\n                    }\n                }\n                pack.macroblocks--;\n                pack.state = PACK_STATE::BEGIN;\n                dmac->ipu_from.dreq = 1;\n                printf(\"ipu: set ipu_from dreq out=%x\\n\", out_FIFO.f.size());\n                dmac_handle_ipu_from_transfer(dmac);\n            }\n                break;\n            case PACK_STATE::DONE:\n                printf(\"ipu: PACK done!\\n\");\n                return true;\n        }\n    }\n}\n\nuint64_t ImageProcessingUnit::read_command()\n{\n    uint64_t reg = 0;\n    reg |= command_output;\n    reg |= (uint64_t)command_decoding << 63UL;\n\n    printf(\"ipu: Read command: $%08X\\n\", command_output);\n\n    return reg;\n}\n\nuint32_t ImageProcessingUnit::read_control()\n{\n    uint32_t reg = 0;\n    reg |= in_FIFO.f.size();\n    reg |= (ctrl.coded_block_pattern & 0x3F) << 8;\n    reg |= ctrl.error_code << 14;\n    reg |= ctrl.start_code << 15;\n    reg |= ctrl.intra_DC_precision << 16;\n    reg |= ctrl.alternate_scan << 20;\n    reg |= ctrl.intra_VLC_table << 21;\n    reg |= ctrl.nonlinear_Q_step << 22;\n    reg |= ctrl.MPEG1 << 23;\n    reg |= ctrl.picture_type << 24;\n    reg |= ctrl.busy << 31;\n    return reg;\n}\n\nuint32_t ImageProcessingUnit::read_BP()\n{\n    uint32_t reg = 0;\n    uint8_t fifo_size = in_FIFO.f.size();\n\n    //Check for FP bit\n    if (in_FIFO.bit_pointer && fifo_size)\n    {\n        reg |= 1 << 16;\n        fifo_size--;\n    }\n    reg |= in_FIFO.bit_pointer;\n    reg |= fifo_size << 8;\n    printf(\"ipu: Read BP: $%08X\\n\", reg);\n    return reg;\n}\n\nuint64_t ImageProcessingUnit::read_top()\n{\n    uint64_t reg = 0;\n    int max_bits = (in_FIFO.f.size() * 128) - in_FIFO.bit_pointer;\n    if (max_bits > 32)\n        max_bits = 32;\n    uint32_t next_data;\n    in_FIFO.get_bits(next_data, max_bits);\n    reg |= next_data << (32 - max_bits);\n\n    /** Note on max_bits\n      * This seems to be undocumented behavior. FMV libraries use this bit to determine how much data is left\n      * in the input FIFO, for the purposes of flushing their bitstream cache.\n      * If this bit is set, this means that there are less than 32 bits left in the FIFO, and BP is then used.\n      * If this is not set, at least 32 bits are available.\n      *\n      * This is needed for rare cases where games peek in the FIFO when less than 32 bits are available.\n      */\n    reg |= ((uint64_t)command_decoding | (uint64_t)(max_bits < 32)) << 63UL;\n    return reg;\n}\n\nvoid ImageProcessingUnit::write_command(uint32_t value)\n{\n    printf(\"ipu: Write command: $%08X\\n\", value);\n    if (!ctrl.busy)\n    {\n        ctrl.busy = true;\n        command = (value >> 28);\n        command_option = value & ~0xF0000000;\n        ctrl.error_code = false;\n        ctrl.start_code = false;\n        switch (command)\n        {\n            case 0x00:\n                printf(\"ipu: BCLR\\n\");\n                in_FIFO.reset();\n                in_FIFO.bit_pointer = command_option & 0x7F;\n                finish_command();\n                break;\n            case 0x01:\n                printf(\"ipu: IDEC\\n\");\n                idec.state = IDEC_STATE::DELAY;\n                idec.macro_type = 0;\n                idec.qsc = (command_option >> 16) & 0x1F;\n                idec.decodes_dct = command_option & (1 << 24);\n                idec.blocks_decoded = 0;\n                csc.use_RGB16 = command_option & (1 << 27);\n                break;\n            case 0x02:\n                printf(\"ipu: BDEC\\n\");\n                bdec.state = BDEC_STATE::ADVANCE;\n                bdec.out_fifo = &out_FIFO;\n                ctrl.coded_block_pattern = 0x3F;\n                bdec.block_index = 0;\n                bdec.cur_channel = 0;\n                bdec.quantizer_step = (command_option >> 16) & 0x1F;\n                bdec.reset_dc = command_option & (1 << 26);\n                bdec.intra = command_option & (1 << 27);\n                bdec.check_start_code = true;\n                break;\n            case 0x03:\n                printf(\"ipu: VDEC\\n\");\n                command_decoding = true;\n                vdec_state = VDEC_STATE::ADVANCE;\n                process_VDEC();\n                break;\n            case 0x04:\n                printf(\"ipu: FDEC\\n\");\n                command_decoding = true;\n                fdec_state = VDEC_STATE::ADVANCE;\n                process_FDEC();\n                break;\n            case 0x05:\n                printf(\"ipu: SETIQ\\n\");\n                bytes_left = 64;\n                setiq_state = SETIQ_STATE::ADVANCE;\n                break;\n            case 0x06:\n                printf(\"ipu: SETVQ\\n\");\n                bytes_left = 32;\n                break;\n            case 0x07:\n                printf(\"ipu: CSC\\n\");\n                csc.state = CSC_STATE::BEGIN;\n                csc.macroblocks = command_option & 0x7FF;\n                csc.use_RGB16 = command_option & (1 << 27);\n                csc.use_dithering = command_option & (1 << 26);\n                break;\n            case 0x08:\n                printf(\"ipu: PACK\\n\");\n                pack.state = PACK_STATE::BEGIN;\n                pack.macroblocks = command_option & 0x7FF;\n                pack.use_RGB16 = command_option & (1 << 27);\n                pack.use_dithering = command_option & (1 << 26);\n                break;\n            case 0x09:\n                printf(\"ipu: SETTH\\n\");\n                TH0 = command_option & 0x1FF;\n                TH1 = (command_option >> 16) & 0x1FF;\n                finish_command();\n                break;\n        }\n    }\n}\n\nvoid ImageProcessingUnit::write_control(uint32_t value)\n{\n    printf(\"ipu: Write control: $%08X\\n\", value);\n    ctrl.intra_DC_precision = (value >> 16) & 0x3;\n    ctrl.alternate_scan = value & (1 << 20);\n    ctrl.intra_VLC_table = value & (1 << 21);\n    ctrl.nonlinear_Q_step = value & (1 << 22);\n    ctrl.MPEG1 = value & (1 << 23);\n    ctrl.picture_type = (value >> 24) & 0x7;\n    if (value & (1 << 30))\n    {\n        command = 0;\n        in_FIFO.reset();\n        out_FIFO.reset();\n        // Note: A control reset does a forced command end, meaning it will\n        //       force the procedure of a command stopping even if there is\n        //       no command currently active, causing an interrupt to the core.\n        //       Fightbox relies on this behaviour to boot and play its first\n        //       two videos.\n        finish_command();\n    }\n}\n\nbool ImageProcessingUnit::can_read_FIFO()\n{\n    return out_FIFO.f.size() > 0;\n}\n\nbool ImageProcessingUnit::can_write_FIFO()\n{\n    return in_FIFO.f.size() < 8;\n}\n\nuint128_t ImageProcessingUnit::read_FIFO()\n{\n    uint128_t quad = out_FIFO.f.front();\n    out_FIFO.f.pop_front();\n    if (!out_FIFO.f.size()) {\n        printf(\"ipu: clear ipu_from dreq\\n\");\n        dmac->ipu_from.dreq = 0;\n    }\n    return quad;\n}\n\nvoid ImageProcessingUnit::write_FIFO(uint128_t quad)\n{\n    printf(\"ipu: Write FIFO: $%08X_%08X_%08X_%08X\\n\", quad.u32[3], quad.u32[2], quad.u32[1], quad.u32[0]);\n\n    //Certain games (Theme Park, Neo Contra, etc) read command output without sending a command.\n    //They expect to read the first word of a newly started IPU_TO transfer.\n    if (in_FIFO.f.size() == 0 && !ctrl.busy)\n    {\n        command_output = quad.u32[0];\n        command_output = (command_output >> 24) | (((command_output >> 16) & 0xFF) << 8) |\n                         (((command_output >> 8) & 0xFF) << 16) | (command_output << 24);\n    }\n    if (in_FIFO.f.size() == 7)\n    {\n        dmac->ipu_to.dreq = 0;\n    }\n    if (in_FIFO.f.size() >= 8)\n    {\n    }\n    in_FIFO.f.push_back(quad);\n    in_FIFO.bit_cache_dirty = true;\n}\n\nstruct ps2_ipu {\n    ImageProcessingUnit* ipu;\n};\n\nextern \"C\" struct ps2_ipu* ps2_ipu_create(void) {\n    return (struct ps2_ipu*)malloc(sizeof(struct ps2_ipu));\n}\n\nextern \"C\" void ps2_ipu_init(struct ps2_ipu* ipu, struct ps2_dmac* dmac, struct ps2_intc* intc) {\n    ipu->ipu = new ImageProcessingUnit(intc, dmac);\n}\n\nextern \"C\" void ps2_ipu_reset(struct ps2_ipu* ipu) {\n    ipu->ipu->reset();\n}\n\nextern \"C\" uint64_t ps2_ipu_read64(struct ps2_ipu* ipu, uint32_t addr) {\n    switch (addr) {\n        case 0x10002000: return ipu->ipu->read_command();\n        case 0x10002010: return ipu->ipu->read_control();\n        case 0x10002020: return ipu->ipu->read_BP();\n        case 0x10002030: return ipu->ipu->read_top();\n    }\n\n    std::fprintf(stderr, \"ipu: Unhandled IPU read address %08x\\n\", addr);\n\n    return 0;\n}\n\nextern \"C\" void ps2_ipu_write64(struct ps2_ipu* ipu, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        case 0x10002000: ipu->ipu->write_command(data); return;\n        case 0x10002010: ipu->ipu->write_control(data); return;\n        case 0x10002020: return; // (W) ipu->ipu->write_BP(); return;\n        case 0x10002030: return; // (W) ipu->ipu->write_top(); return;\n    }\n\n    fprintf(stderr, \"ipu: Unhandled IPU write address %08x\\n\", addr);\n\n    exit(1);\n}\n\nextern \"C\" uint128_t ps2_ipu_read128(struct ps2_ipu* ipu, uint32_t addr) {\n    switch (addr) {\n        case 0x10007000: return ipu->ipu->read_FIFO();\n        case 0x10007010: break; // (W) ipu->ipu->write_FIFO();\n    }\n\n    fprintf(stderr, \"ipu: Unhandled IPU read address %08x\\n\", addr);\n\n    return { 0 }; // Return a zeroed quad if the address is unhandled\n}\n\nextern \"C\" void ps2_ipu_write128(struct ps2_ipu* ipu, uint32_t addr, uint128_t data) {\n    switch (addr) {\n        case 0x10007000: break; // (R) ipu->ipu->read_FIFO();\n        case 0x10007010: ipu->ipu->write_FIFO(data); return;\n    }\n\n    std::fprintf(stderr, \"ipu: Unhandled IPU write address %08x\\n\", addr);\n\n    exit(1);\n}\n\nint ps2_ipu_is_busy(struct ps2_ipu* ipu) {\n    return ipu->ipu->ctrl.busy;\n}\n\nvoid ps2_ipu_run(struct ps2_ipu* ipu) {\n    ipu->ipu->run();\n}\n\nextern \"C\" void ps2_ipu_destroy(struct ps2_ipu* ipu) {\n    delete ipu->ipu;\n\n    free(ipu);\n}"
  },
  {
    "path": "src/ipu/ipu.h",
    "content": "#ifndef IPU_H\n#define IPU_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"ee/dmac.h\"\n#include \"ee/intc.h\"\n#include \"u128.h\"\n\n#include <stdint.h>\n\nstruct ipu_fifo {\n    uint128_t buf[8];\n\n    int bp;\n    int wq;\n    int rq;\n};\n\n// struct ps2_ipu {\n//     uint8_t iq[64];\n//     uint8_t vq[32];\n\n//     struct ipu_fifo in;\n//     struct ipu_fifo out;\n\n//     uint64_t result;\n//     uint64_t cmd;\n//     uint32_t ctrl;\n//     uint32_t bp;\n//     uint64_t top;\n\n//     struct ps2_dmac* dmac;\n//     struct ps2_intc* intc;\n// };\n\nstruct ps2_ipu;\n\nstruct ps2_ipu* ps2_ipu_create(void);\nvoid ps2_ipu_init(struct ps2_ipu* ipu, struct ps2_dmac* dmac, struct ps2_intc* intc);\nvoid ps2_ipu_reset(struct ps2_ipu* ipu);\nint ps2_ipu_is_busy(struct ps2_ipu* ipu);\nuint64_t ps2_ipu_read64(struct ps2_ipu* ipu, uint32_t addr);\nuint128_t ps2_ipu_read128(struct ps2_ipu* ipu, uint32_t addr);\nvoid ps2_ipu_write64(struct ps2_ipu* ipu, uint32_t addr, uint64_t data);\nvoid ps2_ipu_write128(struct ps2_ipu* ipu, uint32_t addr, uint128_t data);\nvoid ps2_ipu_run(struct ps2_ipu* ipu);\nvoid ps2_ipu_destroy(struct ps2_ipu* ipu);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ipu/ipu.hpp",
    "content": "#ifndef IPU_HPP\n#define IPU_HPP\n#include <cstdint>\n#include <queue>\n\n#include \"u128.h\"\n#include \"chromtable.hpp\"\n#include \"codedblockpattern.hpp\"\n#include \"dct_coeff_table0.hpp\"\n#include \"dct_coeff_table1.hpp\"\n#include \"lumtable.hpp\"\n#include \"mac_addr_inc.hpp\"\n#include \"mac_i_pic.hpp\"\n#include \"mac_p_pic.hpp\"\n#include \"mac_b_pic.hpp\"\n#include \"motioncode.hpp\"\n\n// eegs includes\n#include \"ee/dmac.h\"\n#include \"ee/intc.h\"\n\nconstexpr int RAW_BLOCK_SIZE = 0x180;\nconstexpr int RGB_BLOCK_SIZE = 0x100;\n\nstruct IPU_CTRL\n{\n    uint8_t coded_block_pattern;\n    bool error_code;\n    bool start_code;\n    uint8_t intra_DC_precision;\n    bool alternate_scan;\n    bool intra_VLC_table;\n    bool nonlinear_Q_step;\n    bool MPEG1;\n    uint8_t picture_type;\n    bool busy;\n};\n\nenum class VDEC_STATE\n{\n    ADVANCE,\n    DECODE,\n    DONE\n};\n\nenum class BDEC_STATE\n{\n    ADVANCE,\n    GET_CBP,\n    RESET_DC,\n    BEGIN_DECODING,\n    READ_COEFFS,\n    LOAD_NEXT_BLOCK,\n    DONE,\n    CHECK_START_CODE\n};\n\nenum class IDEC_STATE\n{\n    DELAY,\n    ADVANCE,\n    MACRO_I_TYPE,\n    DCT_TYPE,\n    QSC,\n    INIT_BDEC,\n    READ_BLOCK,\n    INIT_CSC,\n    EXEC_CSC,\n    CHECK_START_CODE,\n    VALID_START_CODE,\n    MACRO_INC,\n    DONE\n};\n\nenum class SETIQ_STATE\n{\n    ADVANCE,\n    POPULATE_TABLE\n};\n\nstruct IDEC_Command\n{\n    IDEC_STATE state;\n    uint32_t macro_type;\n\n    bool decodes_dct;\n    uint32_t qsc;\n\n    IPU_FIFO temp_fifo;\n\n    int blocks_decoded;\n};\n\nstruct BDEC_Command\n{\n    BDEC_STATE state;\n    IPU_FIFO* out_fifo;\n    bool intra;\n    bool reset_dc;\n    bool check_start_code;\n    int quantizer_step;\n\n    int block_index;\n    int subblock_index;\n\n    //Four Y blocks, a Cb block, and a Cr block\n    int16_t blocks[6][64];\n    int16_t* cur_block;\n    int cur_channel; //0 = Y, 1 = Cb, 2 = Cr\n\n    enum class READ_COEFF\n    {\n        INIT,\n        READ_DC_DIFF,\n        CHECK_END,\n        COEFF,\n        SKIP_END\n    };\n\n    enum class READ_DIFF\n    {\n        SIZE,\n        DIFF,\n    };\n\n    uint32_t dc_size;\n    int16_t dc_diff;\n    int16_t dc_predictor[3];\n\n    READ_COEFF read_coeff_state;\n    READ_DIFF read_diff_state;\n};\n\nenum class CSC_STATE\n{\n    BEGIN,\n    READ,\n    CONVERT,\n    DONE\n};\n\nstruct CSC_Command\n{\n    CSC_STATE state;\n    int macroblocks;\n    bool use_RGB16;\n    bool use_dithering;\n\n    uint8_t block[RAW_BLOCK_SIZE];\n    int block_index;\n};\n\nenum class PACK_STATE\n{\n    BEGIN,\n    READ,\n    CONVERT,\n    DONE\n};\n\nstruct PACK_Command\n{\n    PACK_STATE state;\n    int macroblocks;\n    bool use_RGB16;\n    bool use_dithering;\n\n    uint8_t block[4 * RGB_BLOCK_SIZE];\n    int block_index;\n};\n\nstruct ImageProcessingUnit\n{\n    private:\n        struct ps2_intc* intc;\n        struct ps2_dmac* dmac;\n        DCT_Coeff_Table0 dct_coeff0;\n        DCT_Coeff_Table1 dct_coeff1;\n        DCT_Coeff* dct_coeff;\n        ChromTable chrom_table;\n        CodedBlockPattern cbp;\n        LumTable lum_table;\n        MacroblockAddrInc macroblock_increment;\n        Macroblock_IPic macroblock_I_pic;\n        Macroblock_PPic macroblock_P_pic;\n        Macroblock_BPic macroblock_B_pic;\n        MotionCode motioncode;\n        VLC_Table* VDEC_table;\n        IPU_FIFO in_FIFO, out_FIFO;\n\n        int8_t dither_mtx[4][4];\n\n        uint8_t intra_IQ[0x40], nonintra_IQ[0x40];\n        uint16_t VQCLUT[16];\n        uint32_t TH0, TH1;\n\n        unsigned int crcb_map[0x100];\n\n        static uint32_t inverse_scan_zigzag[0x40];\n        static uint32_t inverse_scan_alternate[0x40];\n\n        static uint32_t quantizer_linear[0x20];\n        static uint32_t quantizer_nonlinear[0x20];\n\n        bool command_decoding;\n        uint8_t command;\n        uint32_t command_option;\n        uint32_t command_output;\n        int bytes_left;\n\n        IDEC_Command idec;\n        BDEC_Command bdec;\n        VDEC_STATE vdec_state, fdec_state;\n        CSC_Command csc;\n        SETIQ_STATE setiq_state;\n        PACK_Command pack;\n\n        double IDCT_table[8][8];\n\n        void finish_command();\n\n        bool process_IDEC();\n\n        bool process_BDEC();\n        void inverse_scan(int16_t* block);\n        void dequantize(int16_t* block);\n        void prepare_IDCT();\n        void perform_IDCT(const int16_t* pUV, int16_t* pXY);\n        bool BDEC_read_coeffs();\n        bool BDEC_read_diff();\n\n        void convert_RGB32_to_RGB16(const uint8_t* rgb32, uint16_t *rgb16, bool dithering);\n        void process_VDEC();\n        void process_FDEC();\n        bool process_CSC();\n        bool process_PACK();\n    public:\n        IPU_CTRL ctrl;\n\n        ImageProcessingUnit(struct ps2_intc* intc, struct ps2_dmac* dmac);\n\n        void reset();\n        void run();\n\n        uint64_t read_command();\n        uint32_t read_control();\n        uint32_t read_BP();\n        uint64_t read_top();\n\n        void write_command(uint32_t value);\n        void write_control(uint32_t value);\n\n        bool can_read_FIFO();\n        bool can_write_FIFO();\n        uint128_t read_FIFO();\n        void write_FIFO(uint128_t quad);\n};\n\n#endif // IPU_HPP\n"
  },
  {
    "path": "src/ipu/ipu_fifo.cpp",
    "content": "#include <cstdlib>\n#include <cstdio>\n#include \"ipu_fifo.hpp\"\n\nbool IPU_FIFO::get_bits(uint32_t &data, int bits)\n{\n    int bits_available = (f.size() * 128) - bit_pointer;\n\n    if (bits_available < bits || bits_available == 0)\n    {\n        data = 0;\n        return false;\n    }\n\n    if (bit_cache_dirty)\n    {\n        uint8_t temp[32];\n        *(uint128_t*)&temp[0] = f[0];\n        if (f.size() > 1)\n            *(uint128_t*)&temp[16] = f[1];\n\n        uint8_t blorp[8];\n        int index = (bit_pointer & ~0x1F) / 8;\n\n        //MPEG is big-endian...\n        for (int i = 0; i < 8; i++)\n        {\n            int fifo_index = index + i;\n            blorp[7 - i] = temp[fifo_index];\n        }\n        bit_cache_dirty = false;\n        cached_bits = *(uint64_t*)&blorp[0];\n    }\n    int shift = 64 - (bit_pointer % 32) - bits;\n    uint64_t mask = ~0x0ULL >> (64 - bits);\n    data = (cached_bits >> shift) & mask;\n\n    return true;\n}\n\nbool IPU_FIFO::advance_stream(uint8_t amount)\n{\n    if (amount > 32)\n        amount = 32;\n    \n    //printf(\"Advance stream: %d + %d = %d\\n\", bit_pointer - amount, amount, bit_pointer);\n\n    if ((bit_pointer + amount) > (f.size() * 128))\n    {\n        return false;\n    }\n\n    uint32_t old_words = bit_pointer / 32;\n    uint32_t new_words = (bit_pointer + amount) / 32;\n    bit_cache_dirty |= (old_words != new_words);\n    bit_pointer += amount;\n\n    while (bit_pointer >= 128)\n    {\n        bit_pointer -= 128;\n        f.pop_front();\n        bit_cache_dirty = true;\n    }\n    return true;\n}\n\nvoid IPU_FIFO::reset()\n{\n    std::deque<uint128_t> empty;\n    f.swap(empty);\n    bit_pointer = 0;\n    cached_bits = 0;\n    bit_cache_dirty = true;\n}\n\nvoid IPU_FIFO::byte_align()\n{\n    int bits = bit_pointer & 0x7;\n    if (bits)\n        advance_stream(8 - bits);\n}\n"
  },
  {
    "path": "src/ipu/ipu_fifo.hpp",
    "content": "#ifndef IPU_FIFO_HPP\n#define IPU_FIFO_HPP\n#include <cstdint>\n#include <queue>\n\n#include \"u128.h\"\n\nstruct IPU_FIFO\n{\n    std::deque<uint128_t> f;\n    int bit_pointer;\n    uint64_t cached_bits;\n    bool bit_cache_dirty;\n    bool get_bits(uint32_t& data, int bits);\n    bool advance_stream(uint8_t amount);\n\n    void reset();\n    void byte_align();\n};\n\n#endif // IPU_FIFO_HPP\n"
  },
  {
    "path": "src/ipu/lumtable.cpp",
    "content": "#include \"lumtable.hpp\"\n\nVLC_Entry LumTable::table[] =\n{\n    {0x0000, 1, 2},\n    {0x0001, 2, 2},\n    {0x0004, 0, 3},\n    {0x0005, 3, 3},\n    {0x0006, 4, 3},\n    {0x000E, 5, 4},\n    {0x001E, 6, 5},\n    {0x003E, 7, 6},\n    {0x007E, 8, 7},\n    {0x00FE, 9, 8},\n    {0x01FE, 10, 9},\n    {0x01FF, 11, 9}\n};\n\nunsigned int LumTable::index_table[9] =\n{\n    0,\n    0,\n    2,\n    5,\n    6,\n    7,\n    8,\n    9,\n    10,\n};\n\nLumTable::LumTable() : VLC_Table(table, SIZE, 9, index_table)\n{\n\n}\n"
  },
  {
    "path": "src/ipu/lumtable.hpp",
    "content": "#ifndef LUMTABLE_HPP\n#define LUMTABLE_HPP\n#include \"vlc_table.hpp\"\n\nclass LumTable : public VLC_Table\n{\n    private:\n        static VLC_Entry table[];\n        static unsigned int index_table[];\n\n        constexpr static int SIZE = 12;\n    public:\n        LumTable();\n};\n\n#endif // LUMTABLE_HPP\n"
  },
  {
    "path": "src/ipu/mac_addr_inc.cpp",
    "content": "#include \"mac_addr_inc.hpp\"\n\nVLC_Entry MacroblockAddrInc::table[] =\n{\n    {0x1, 0x10001, 1},\n    {0x3, 0x30002, 3},\n    {0x2, 0x30003, 3},\n    {0x3, 0x40004, 4},\n\n    {0x2, 0x40005, 4},\n    {0x3, 0x50006, 5},\n    {0x2, 0x50007, 5},\n    {0x7, 0x70008, 7},\n\n    {0x6, 0x70009, 7},\n    {0xB, 0x8000A, 8},\n    {0xA, 0x8000B, 8},\n    {0x9, 0x8000C, 8},\n\n    {0x8, 0x8000D, 8},\n    {0x7, 0x8000E, 8},\n    {0x6, 0x8000F, 8},\n    {0x17, 0xA0010, 10},\n\n    //16\n    {0x16, 0xA0011, 10},\n    {0x15, 0xA0012, 10},\n    {0x14, 0xA0013, 10},\n    {0x13, 0xA0014, 10},\n\n    //20\n    {0x12, 0xA0015, 10},\n    {0x23, 0xB0016, 11},\n    {0x22, 0xB0017, 11},\n    {0x21, 0xB0018, 11},\n\n    //24\n    {0x20, 0xB0019, 11},\n    {0x1F, 0xB001A, 11},\n    {0x1E, 0xB001B, 11},\n    {0x1D, 0xB001C, 11},\n\n    //28\n    {0x1C, 0xB001D, 11},\n    {0x1B, 0xB001E, 11},\n    {0x1A, 0xB001F, 11},\n    {0x19, 0xB0020, 11},\n\n    {0x18, 0xB0021, 11},\n    {0xF, 0xB0022, 11},\n    {0x8, 0xB0023, 11}\n};\n\nunsigned int MacroblockAddrInc::index_table[11] =\n{\n    0,\n    1,\n    1,\n    3,\n    5,\n    5,\n    7,\n    9,\n    9,\n    15,\n    21,\n};\n\nMacroblockAddrInc::MacroblockAddrInc() : VLC_Table(table, SIZE, 11, index_table)\n{\n\n}\n"
  },
  {
    "path": "src/ipu/mac_addr_inc.hpp",
    "content": "#ifndef MAC_ADDR_INC_H\n#define MAC_ADDR_INC_H\n#include \"vlc_table.hpp\"\n\nclass MacroblockAddrInc : public VLC_Table\n{\n    private:\n        static VLC_Entry table[];\n        static unsigned int index_table[];\n\n        constexpr static int SIZE = 35;\n    public:\n        MacroblockAddrInc();\n};\n\n#endif // MAC_ADDR_INC_H\n"
  },
  {
    "path": "src/ipu/mac_b_pic.cpp",
    "content": "#include \"mac_b_pic.hpp\"\n\nVLC_Entry Macroblock_BPic::table[] =\n{\n    {0x2, 0x2000C, 2},\n    {0x3, 0x2000E, 2},\n    {0x2, 0x30004, 3},\n    {0x3, 0x30006, 3},\n\n    {0x2, 0x40008, 4},\n    {0x3, 0x4000A, 4},\n    {0x3, 0x50001, 5},\n    {0x2, 0x5001E, 5},\n\n    {0x3, 0x6001A, 6},\n    {0x2, 0x60016, 6},\n    {0x1, 0x60011, 6}\n};\n\nunsigned int Macroblock_BPic::index_table[6] =\n{\n    0,\n    0,\n    2,\n    4,\n    6,\n    8,\n};\n\nMacroblock_BPic::Macroblock_BPic() : VLC_Table(table, SIZE, 6, index_table)\n{\n\n}\n"
  },
  {
    "path": "src/ipu/mac_b_pic.hpp",
    "content": "#ifndef MAC_B_PIC_HPP\n#define MAC_B_PIC_HPP\n#include \"vlc_table.hpp\"\n\nclass Macroblock_BPic : public VLC_Table\n{\n    private:\n        static VLC_Entry table[];\n        static unsigned int index_table[];\n\n        constexpr static int SIZE = 11;\n    public:\n        Macroblock_BPic();\n};\n\n#endif // MAC_B_PIC_HPP\n"
  },
  {
    "path": "src/ipu/mac_i_pic.cpp",
    "content": "#include \"mac_i_pic.hpp\"\n\nVLC_Entry Macroblock_IPic::table[] =\n{\n    {0x1, 0x10001, 1},\n    {0x1, 0x20011, 2}\n};\n\nunsigned int Macroblock_IPic::index_table[2] =\n{\n    0,\n    1,\n};\n\nMacroblock_IPic::Macroblock_IPic() : VLC_Table(table, SIZE, 2, index_table)\n{\n\n}\n"
  },
  {
    "path": "src/ipu/mac_i_pic.hpp",
    "content": "#ifndef MAC_I_PIC_HPP\n#define MAC_I_PIC_HPP\n#include \"vlc_table.hpp\"\n\nclass Macroblock_IPic : public VLC_Table\n{\n    private:\n        static VLC_Entry table[];\n        static unsigned int index_table[];\n\n        constexpr static int SIZE = 2;\n    public:\n        Macroblock_IPic();\n};\n\n#endif // MAC_I_PIC_HPP\n"
  },
  {
    "path": "src/ipu/mac_p_pic.cpp",
    "content": "#include \"mac_p_pic.hpp\"\n\nVLC_Entry Macroblock_PPic::table[] =\n{\n    {0x1, 0x1000A, 1},\n    {0x1, 0x20002, 2},\n    {0x1, 0x30008, 3},\n    {0x3, 0x50001, 5},\n    {0x2, 0x5001A, 5},\n    {0x1, 0x50012, 5},\n    {0x1, 0x60011, 6}\n};\n\nunsigned int Macroblock_PPic::index_table[6] =\n{\n    0,\n    1,\n    2,\n    2,\n    3,\n    6,\n};\n\nMacroblock_PPic::Macroblock_PPic() :\n    VLC_Table(table, SIZE, 6, index_table)\n{\n\n}\n"
  },
  {
    "path": "src/ipu/mac_p_pic.hpp",
    "content": "#ifndef MAC_P_PIC_HPP\n#define MAC_P_PIC_HPP\n#include \"vlc_table.hpp\"\n\nclass Macroblock_PPic : public VLC_Table\n{\n    private:\n        static VLC_Entry table[];\n        static unsigned int index_table[];\n\n        constexpr static int SIZE = 7;\n    public:\n        Macroblock_PPic();\n};\n\n#endif // MAC_P_PIC_HPP\n"
  },
  {
    "path": "src/ipu/motioncode.cpp",
    "content": "#include \"motioncode.hpp\"\n\nVLC_Entry MotionCode::table[] =\n{\n    {0x1, 0x10000, 1},\n    {0x2, 0x30001, 3},\n    {0x3, 0x3FFFF, 3},\n    {0x2, 0x40002, 4},\n\n    {0x3, 0x4FFFE, 4},\n    {0x2, 0x50003, 5},\n    {0x3, 0x5FFFD, 5},\n    {0x6, 0x70004, 7},\n\n    {0x7, 0x7FFFC, 7},\n    {0xA, 0x80005, 8},\n    {0xB, 0x8FFFB, 8},\n    {0x8, 0x80006, 8},\n\n    {0x9, 0x8FFFA, 8},\n    {0x6, 0x80007, 8},\n    {0x7, 0x8FFF9, 8},\n    {0x16, 0xA0008, 10},\n\n    {0x17, 0xAFFF8, 10},\n    {0x14, 0xA0009, 10},\n    {0x15, 0xAFFF7, 10},\n    {0x12, 0xA000A, 10},\n\n    {0x13, 0xAFFF6, 10},\n    {0x22, 0xB000B, 11},\n    {0x23, 0xBFFF5, 11},\n    {0x20, 0xB000C, 11},\n\n    {0x21, 0xBFFF4, 11},\n    {0x1E, 0xB000D, 11},\n    {0x1F, 0xBFFF3, 11},\n    {0x1C, 0xB000E, 11},\n\n    {0x1D, 0xBFFF2, 11},\n    {0x1A, 0xB000F, 11},\n    {0x1B, 0xBFFF1, 11},\n    {0x18, 0xB0010, 11},\n\n    {0x19, 0xBFFF0, 11}\n};\n\nunsigned int MotionCode::index_table[11] =\n{\n    0,\n    1,\n    1,\n    3,\n    5,\n    7,\n    7,\n    9,\n    15,\n    15,\n    21,\n};\n\nMotionCode::MotionCode() :\n    VLC_Table(table, SIZE, 11, index_table)\n{\n\n}\n"
  },
  {
    "path": "src/ipu/motioncode.hpp",
    "content": "#ifndef MOTIONCODE_HPP\n#define MOTIONCODE_HPP\n#include \"vlc_table.hpp\"\n\nclass MotionCode : public VLC_Table\n{\n    private:\n        static VLC_Entry table[];\n        static unsigned int index_table[];\n\n        constexpr static int SIZE = 33;\n    public:\n        MotionCode();\n};\n\n#endif // MOTIONCODE_HPP\n"
  },
  {
    "path": "src/ipu/vlc_table.cpp",
    "content": "#include <cstdlib>\n#include <cstdio>\n#include \"vlc_table.hpp\"\n\nVLC_Table::VLC_Table(VLC_Entry* table, int table_size, int max_bits, unsigned int* index_table) :\n    table(table), table_size(table_size), max_bits(max_bits), index_table(index_table)\n{\n\n}\n\nbool VLC_Table::peek_symbol(IPU_FIFO &FIFO, VLC_Entry &entry)\n{\n    uint32_t key;\n    for (int i = 0; i < max_bits; i++)\n    {\n        int bits = i + 1;\n        if (!FIFO.get_bits(key, bits))\n            return false;\n        for (int j = index_table[i]; j < table_size; j++)\n        {\n            if (bits != table[j].bits)\n                break;\n\n            if (key == table[j].key)\n            {\n                entry = table[j];\n                return true;\n            }\n        }\n    }\n    throw VLC_Error(\"VLC symbol not found\");\n    return false;\n}\n\nbool VLC_Table::get_symbol(IPU_FIFO& FIFO, uint32_t &result)\n{\n    VLC_Entry entry;\n    if (!peek_symbol(FIFO, entry))\n        return false;\n\n    FIFO.advance_stream(entry.bits);\n    result = entry.value;\n    return true;\n}\n"
  },
  {
    "path": "src/ipu/vlc_table.hpp",
    "content": "#ifndef VLC_TABLE_HPP\n#define VLC_TABLE_HPP\n#include <stdexcept>\n#include <cstdint>\n#include <queue>\n#include \"ipu_fifo.hpp\"\n\nstruct VLC_Entry\n{\n    uint32_t key;\n    uint32_t value;\n    uint8_t bits;\n};\n\nclass VLC_Error : public std::runtime_error\n{\n    using std::runtime_error::runtime_error;\n};\n\nclass VLC_Table\n{\n    private:\n        VLC_Entry* table;\n        int table_size, max_bits;\n        unsigned int* index_table;\n    protected:\n        VLC_Table(VLC_Entry* table, int table_size, int max_bits, unsigned int* index_table);\n    public:\n        bool peek_symbol(IPU_FIFO& FIFO, VLC_Entry& entry);\n        bool get_symbol(IPU_FIFO& FIFO, uint32_t& result);\n};\n\n#endif // VLC_TABLE_HPP\n"
  },
  {
    "path": "src/list.c",
    "content": "#include \"list.h\"\n\n#include <stdlib.h>\n#include <string.h>\n\nlist_t* list_create(void) {\n    list_t* list = malloc(sizeof(list_t));\n\n    list_init(list);\n\n    return list;\n}\n\nvoid list_init(list_t* list) {\n    list->first = NULL;\n    list->last = NULL;\n    list->size = 0;\n}\n\nvoid list_push_front(list_t* list, void* data) {\n    node_t* node = malloc(sizeof(node_t));\n\n    node->data = data;\n    node->next = list->first;\n\n    list->first = node;\n    \n    if (!list->last)\n        list->last = list->first;\n\n    ++list->size;\n}\n\nvoid list_push_back(list_t* list, void* data) {\n    node_t* node = malloc(sizeof(node_t));\n\n    node->data = data;\n    node->next = NULL;\n\n    if (!list->last) {\n        list->first = node;\n        list->last = node;\n    } else {\n        list->last->next = node;\n        list->last = node;\n    }\n\n    ++list->size;\n}\n\nvoid list_pop_front(list_t* list) {\n    if (!list->first)\n        return;\n\n    node_t* next = list->first->next;\n\n    free(list->first);\n\n    list->first = next;\n\n    --list->size;\n}\n\nvoid list_pop_back(list_t* list) {\n    if (!list->last)\n        return;\n\n    node_t* node = list->first;\n\n    while (node->next != list->last)\n        node = node->next;\n\n    free(node->next);\n\n    node->next = NULL;\n\n    list->last = node;\n}\n\nnode_t* list_front(list_t* list) {\n    return list->first;\n}\n\nnode_t* list_back(list_t* list) {\n    return list->last;\n}\n\nnode_t* list_at(list_t* list, int index) {\n    if (index > list->size)\n        return NULL;\n\n    node_t* node = list->first;\n\n    for (int i = 0; i < index; i++)\n        node = node->next;\n\n    return node;\n}\n\nvoid list_iterate(list_t* list, void (*func)(void*)) {\n    node_t* node = list->first;\n\n    while (node) {\n        func(node->data);\n\n        node = node->next;\n    }\n}\n\nvoid list_destroy(list_t* list) {\n    node_t* node = list->first;\n\n    while (node) {\n        node_t* next = node->next;\n\n        free(node);\n\n        node = next;\n    }\n\n    free(list);\n}"
  },
  {
    "path": "src/list.h",
    "content": "#ifndef LIST_H\n#define LIST_H\n\n#include <stddef.h>\n\ntypedef struct node_t node_t;\n\ntypedef struct node_t {\n    node_t* next;\n    void* data;\n} node_t;\n\ntypedef struct {\n    node_t* first;\n    node_t* last;\n    size_t size;\n} list_t;\n\nlist_t* list_create(void);\nvoid list_init(list_t* list);\nvoid list_push_front(list_t* list, void* data);\nvoid list_push_back(list_t* list, void* data);\nvoid list_pop_front(list_t* list);\nvoid list_pop_back(list_t* list);\nnode_t* list_front(list_t* list);\nnode_t* list_back(list_t* list);\nnode_t* list_at(list_t* list, int index);\nvoid list_iterate(list_t* list, void (*func)(void*));\nvoid list_destroy(list_t* list);\n\n#endif"
  },
  {
    "path": "src/md5.c",
    "content": "/*\n * Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm\n * and modified slightly to be functionally identical but condensed into control structures.\n */\n\n#include \"md5.h\"\n\n/*\n * Constants defined by the MD5 algorithm\n */\n#define A 0x67452301\n#define B 0xefcdab89\n#define C 0x98badcfe\n#define D 0x10325476\n\nstatic uint32_t S[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22,\n                       5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20, 5,  9, 14, 20,\n                       4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23,\n                       6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21};\n\nstatic uint32_t K[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee,\n                       0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501,\n                       0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be,\n                       0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821,\n                       0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa,\n                       0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8,\n                       0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed,\n                       0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a,\n                       0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c,\n                       0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70,\n                       0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05,\n                       0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665,\n                       0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039,\n                       0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1,\n                       0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1,\n                       0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391};\n\n/*\n * Padding used to make the size (in bits) of the input congruent to 448 mod 512\n */\nstatic uint8_t PADDING[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,\n                            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};\n\n/*\n * Bit-manipulation functions defined by the MD5 algorithm\n */\n#define F(X, Y, Z) ((X & Y) | (~X & Z))\n#define G(X, Y, Z) ((X & Z) | (Y & ~Z))\n#define H(X, Y, Z) (X ^ Y ^ Z)\n#define I(X, Y, Z) (Y ^ (X | ~Z))\n\n/*\n * Rotates a 32-bit word left by n bits\n */\nuint32_t rotateLeft(uint32_t x, uint32_t n){\n    return (x << n) | (x >> (32 - n));\n}\n\n\n/*\n * Initialize a context\n */\nvoid md5_init(struct md5_context* ctx){\n    ctx->size = (uint64_t)0;\n\n    ctx->buffer[0] = (uint32_t)A;\n    ctx->buffer[1] = (uint32_t)B;\n    ctx->buffer[2] = (uint32_t)C;\n    ctx->buffer[3] = (uint32_t)D;\n}\n\n/*\n * Add some amount of input to the context\n *\n * If the input fills out a block of 512 bits, apply the algorithm (md5_step)\n * and save the result in the buffer. Also updates the overall size.\n */\nvoid md5_update(struct md5_context* ctx, uint8_t* input_buffer, size_t input_len){\n    uint32_t input[16];\n    unsigned int offset = ctx->size % 64;\n    ctx->size += (uint64_t)input_len;\n\n    // Copy each byte in input_buffer into the next space in our context input\n    for(unsigned int i = 0; i < input_len; ++i){\n        ctx->input[offset++] = (uint8_t)*(input_buffer + i);\n\n        // If we've filled our context input, copy it into our local array input\n        // then reset the offset to 0 and fill in a new buffer.\n        // Every time we fill out a chunk, we run it through the algorithm\n        // to enable some back and forth between cpu and i/o\n        if(offset % 64 == 0){\n            for(unsigned int j = 0; j < 16; ++j){\n                // Convert to little-endian\n                // The local variable `input` our 512-bit chunk separated into 32-bit words\n                // we can use in calculations\n                input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |\n                           (uint32_t)(ctx->input[(j * 4) + 2]) << 16 |\n                           (uint32_t)(ctx->input[(j * 4) + 1]) <<  8 |\n                           (uint32_t)(ctx->input[(j * 4)]);\n            }\n            md5_step(ctx->buffer, input);\n            offset = 0;\n        }\n    }\n}\n\n/*\n * Pad the current input to get to 448 bytes, append the size in bits to the very end,\n * and save the result of the final iteration into digest.\n */\nvoid md5_finalize(struct md5_context* ctx){\n    uint32_t input[16];\n    unsigned int offset = ctx->size % 64;\n    unsigned int padding_length = offset < 56 ? 56 - offset : (56 + 64) - offset;\n\n    // Fill in the padding and undo the changes to size that resulted from the update\n    md5_update(ctx, PADDING, padding_length);\n    ctx->size -= (uint64_t)padding_length;\n\n    // Do a final update (internal to this function)\n    // Last two 32-bit words are the two halves of the size (converted from bytes to bits)\n    for(unsigned int j = 0; j < 14; ++j){\n        input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 |\n                   (uint32_t)(ctx->input[(j * 4) + 2]) << 16 |\n                   (uint32_t)(ctx->input[(j * 4) + 1]) <<  8 |\n                   (uint32_t)(ctx->input[(j * 4)]);\n    }\n    input[14] = (uint32_t)(ctx->size * 8);\n    input[15] = (uint32_t)((ctx->size * 8) >> 32);\n\n    md5_step(ctx->buffer, input);\n\n    // Move the result into digest (convert from little-endian)\n    for(unsigned int i = 0; i < 4; ++i){\n        ctx->digest[(i * 4) + 0] = (uint8_t)((ctx->buffer[i] & 0x000000FF));\n        ctx->digest[(i * 4) + 1] = (uint8_t)((ctx->buffer[i] & 0x0000FF00) >>  8);\n        ctx->digest[(i * 4) + 2] = (uint8_t)((ctx->buffer[i] & 0x00FF0000) >> 16);\n        ctx->digest[(i * 4) + 3] = (uint8_t)((ctx->buffer[i] & 0xFF000000) >> 24);\n    }\n}\n\n/*\n * Step on 512 bits of input with the main MD5 algorithm.\n */\nvoid md5_step(uint32_t *buffer, uint32_t *input){\n    uint32_t AA = buffer[0];\n    uint32_t BB = buffer[1];\n    uint32_t CC = buffer[2];\n    uint32_t DD = buffer[3];\n\n    uint32_t E;\n\n    unsigned int j;\n\n    for(unsigned int i = 0; i < 64; ++i){\n        switch(i / 16){\n            case 0:\n                E = F(BB, CC, DD);\n                j = i;\n                break;\n            case 1:\n                E = G(BB, CC, DD);\n                j = ((i * 5) + 1) % 16;\n                break;\n            case 2:\n                E = H(BB, CC, DD);\n                j = ((i * 3) + 5) % 16;\n                break;\n            default:\n                E = I(BB, CC, DD);\n                j = (i * 7) % 16;\n                break;\n        }\n\n        uint32_t temp = DD;\n        DD = CC;\n        CC = BB;\n        BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]);\n        AA = temp;\n    }\n\n    buffer[0] += AA;\n    buffer[1] += BB;\n    buffer[2] += CC;\n    buffer[3] += DD;\n}\n\n/*\n * Functions that run the algorithm on the provided input and put the digest into result.\n * result should be able to store 16 bytes.\n */\nvoid md5_hash_string(char *input, uint8_t* result){\n    struct md5_context ctx;\n    md5_init(&ctx);\n    md5_update(&ctx, (uint8_t* )input, strlen(input));\n    md5_finalize(&ctx);\n\n    memcpy(result, ctx.digest, 16);\n}\n\nvoid md5_hash_file(FILE *file, uint8_t* result){\n    char *input_buffer = malloc(1024);\n    size_t input_size = 0;\n\n    struct md5_context ctx;\n    md5_init(&ctx);\n\n    while((input_size = fread(input_buffer, 1, 1024, file)) > 0){\n        md5_update(&ctx, (uint8_t* )input_buffer, input_size);\n    }\n\n    md5_finalize(&ctx);\n\n    free(input_buffer);\n\n    memcpy(result, ctx.digest, 16);\n}\n"
  },
  {
    "path": "src/md5.h",
    "content": "#ifndef MD5_H\n#define MD5_H\n\n#include <stdio.h>\n#include <stdint.h>\n#include <string.h>\n#include <stdlib.h>\n\nstruct md5_context {\n    uint64_t size;        // Size of input in bytes\n    uint32_t buffer[4];   // Current accumulation of hash\n    uint8_t input[64];    // Input to be used in the next step\n    uint8_t digest[16];   // Result of algorithm\n};\n\nvoid md5_init(struct md5_context* ctx);\nvoid md5_update(struct md5_context* ctx, uint8_t* input, size_t input_len);\nvoid md5_finalize(struct md5_context* ctx);\nvoid md5_step(uint32_t* buffer, uint32_t* input);\n\nvoid md5_hash_string(char* input, uint8_t* result);\nvoid md5_hash_file(FILE* file, uint8_t* result);\n\n#endif\n"
  },
  {
    "path": "src/ps2.c",
    "content": "#include <stdint.h>\n#include <stdlib.h>\n#include <stddef.h>\n#include <string.h>\n\n#include \"ps2.h\"\n#include \"rom.h\"\n\nstruct ps2_state* ps2_create(void) {\n    return malloc(sizeof(struct ps2_state));\n}\n\nvoid ps2_init(struct ps2_state* ps2) {\n    memset(ps2, 0, sizeof(struct ps2_state));\n\n    ps2->sched = sched_create();\n    sched_init(ps2->sched);\n\n    ps2->ee = ee_create();\n    ps2->vu0 = vu_create();\n    ps2->vu1 = vu_create();\n    ps2->iop = iop_create();\n    ps2->ee_bus = ee_bus_create();\n    ps2->iop_bus = iop_bus_create();\n    ps2->gif = ps2_gif_create();\n    ps2->vif0 = ps2_vif_create();\n    ps2->vif1 = ps2_vif_create();\n    ps2->gs = ps2_gs_create();\n    ps2->ipu = ps2_ipu_create();\n    ps2->ee_dma = ps2_dmac_create();\n    ps2->ee_ram = ps2_ram_create();\n    ps2->ee_intc = ps2_intc_create();\n    ps2->ee_timers = ps2_ee_timers_create();\n    ps2->iop_dma = ps2_iop_dma_create();\n    ps2->iop_spr = ps2_ram_create();\n    ps2->iop_intc = ps2_iop_intc_create();\n    ps2->iop_timers = ps2_iop_timers_create();\n    ps2->iop_ram = ps2_ram_create();\n    ps2->bios = ps2_bios_create();\n    ps2->rom1 = ps2_bios_create();\n    ps2->rom2 = ps2_bios_create();\n    ps2->sif = ps2_sif_create();\n    ps2->cdvd = ps2_cdvd_create();\n    ps2->sio2 = ps2_sio2_create();\n    ps2->spu2 = ps2_spu2_create();\n    ps2->usb = ps2_usb_create();\n    ps2->fw = ps2_fw_create();\n    ps2->sbus = ps2_sbus_create();\n    ps2->dev9 = ps2_dev9_create();\n    ps2->speed = ps2_speed_create();\n\n    // Initialize EE\n    ee_bus_init(ps2->ee_bus, NULL);\n\n    struct ee_bus_s ee_bus_data;\n    ee_bus_data.read8 = ee_bus_read8;\n    ee_bus_data.read16 = ee_bus_read16;\n    ee_bus_data.read32 = ee_bus_read32;\n    ee_bus_data.read64 = ee_bus_read64;\n    ee_bus_data.read128 = ee_bus_read128;\n    ee_bus_data.write8 = ee_bus_write8;\n    ee_bus_data.write16 = ee_bus_write16;\n    ee_bus_data.write32 = ee_bus_write32;\n    ee_bus_data.write64 = ee_bus_write64;\n    ee_bus_data.write128 = ee_bus_write128;\n    ee_bus_data.udata = ps2->ee_bus;\n\n    ee_init(ps2->ee, ps2->vu0, ps2->vu1, RAM_SIZE_32MB, ee_bus_data);\n    vu_init(ps2->vu0, 0, ps2->gif, ps2->vif0, ps2->vu1);\n    vu_init(ps2->vu1, 1, ps2->gif, ps2->vif1, ps2->vu1);\n\n    // Initialize IOP\n    iop_bus_init(ps2->iop_bus, NULL);\n\n    struct iop_bus_s iop_bus_data;\n    iop_bus_data.read8 = iop_bus_read8;\n    iop_bus_data.read16 = iop_bus_read16;\n    iop_bus_data.read32 = iop_bus_read32;\n    iop_bus_data.write8 = iop_bus_write8;\n    iop_bus_data.write16 = iop_bus_write16;\n    iop_bus_data.write32 = iop_bus_write32;\n    iop_bus_data.udata = ps2->iop_bus;\n\n    iop_init(ps2->iop, iop_bus_data);\n\n    // Initialize devices\n    ps2_dmac_init(ps2->ee_dma, ps2->sif, ps2->iop_dma, ee_get_spr(ps2->ee), ps2->ee, ps2->sched, ps2->ee_bus);\n    ps2_ram_init(ps2->ee_ram, RAM_SIZE_32MB);\n    ps2_gif_init(ps2->gif, ps2->vu1, ps2->gs);\n    ps2_vif_init(ps2->vif0, 0, ps2->vu0, ps2->gif, ps2->ee_intc, ps2->sched, ps2->ee_bus);\n    ps2_vif_init(ps2->vif1, 1, ps2->vu1, ps2->gif, ps2->ee_intc, ps2->sched, ps2->ee_bus);\n    ps2_gs_init(ps2->gs, ps2->ee_intc, ps2->iop_intc, ps2->ee_timers, ps2->iop_timers, ps2->sched);\n    ps2_ipu_init(ps2->ipu, ps2->ee_dma, ps2->ee_intc);\n    ps2_intc_init(ps2->ee_intc, ps2->ee, ps2->sched);\n    ps2_ee_timers_init(ps2->ee_timers, ps2->ee_intc, ps2->sched);\n    ps2_ram_init(ps2->iop_ram, RAM_SIZE_2MB);\n    ps2_iop_dma_init(ps2->iop_dma, ps2->iop_intc, ps2->sif, ps2->cdvd, ps2->ee_dma, ps2->sio2, ps2->spu2, ps2->sched, ps2->iop_bus);\n    ps2_ram_init(ps2->iop_spr, RAM_SIZE_1KB);\n    ps2_iop_intc_init(ps2->iop_intc, ps2->iop);\n    ps2_iop_timers_init(ps2->iop_timers, ps2->iop_intc, ps2->sched);\n    ps2_cdvd_init(ps2->cdvd, ps2->iop_dma, ps2->iop_intc, ps2->sched);\n    ps2_sio2_init(ps2->sio2, ps2->iop_dma, ps2->iop_intc, ps2->sched);\n    ps2_spu2_init(ps2->spu2, ps2->iop_dma, ps2->iop_intc, ps2->sched);\n    ps2_usb_init(ps2->usb);\n    ps2_fw_init(ps2->fw, ps2->iop_intc);\n    ps2_sbus_init(ps2->sbus, ps2->ee_intc, ps2->iop_intc, ps2->sched);\n    ps2_dev9_init(ps2->dev9, DEV9_TYPE_EXPBAY);\n    ps2_speed_init(ps2->speed, ps2->iop_intc);\n    ps2_bios_init(ps2->bios);\n    ps2_bios_init(ps2->rom1);\n    ps2_bios_init(ps2->rom2);\n    ps2_sif_init(ps2->sif, ps2->iop_intc);\n\n    // Initialize bus pointers\n    iop_bus_init_bios(ps2->iop_bus, ps2->bios);\n    iop_bus_init_rom1(ps2->iop_bus, ps2->rom1);\n    iop_bus_init_rom2(ps2->iop_bus, ps2->rom2);\n    iop_bus_init_iop_ram(ps2->iop_bus, ps2->iop_ram);\n    iop_bus_init_iop_spr(ps2->iop_bus, ps2->iop_spr);\n    iop_bus_init_sif(ps2->iop_bus, ps2->sif);\n    iop_bus_init_dma(ps2->iop_bus, ps2->iop_dma);\n    iop_bus_init_intc(ps2->iop_bus, ps2->iop_intc);\n    iop_bus_init_timers(ps2->iop_bus, ps2->iop_timers);\n    iop_bus_init_cdvd(ps2->iop_bus, ps2->cdvd);\n    iop_bus_init_sio2(ps2->iop_bus, ps2->sio2);\n    iop_bus_init_spu2(ps2->iop_bus, ps2->spu2);\n    iop_bus_init_usb(ps2->iop_bus, ps2->usb);\n    iop_bus_init_fw(ps2->iop_bus, ps2->fw);\n    iop_bus_init_sbus(ps2->iop_bus, ps2->sbus);\n    iop_bus_init_dev9(ps2->iop_bus, ps2->dev9);\n    iop_bus_init_speed(ps2->iop_bus, ps2->speed);\n    ee_bus_init_bios(ps2->ee_bus, ps2->bios);\n    ee_bus_init_rom1(ps2->ee_bus, ps2->rom1);\n    ee_bus_init_rom2(ps2->ee_bus, ps2->rom2);\n    ee_bus_init_iop_ram(ps2->ee_bus, ps2->iop_ram);\n    ee_bus_init_sif(ps2->ee_bus, ps2->sif);\n    ee_bus_init_dmac(ps2->ee_bus, ps2->ee_dma);\n    ee_bus_init_intc(ps2->ee_bus, ps2->ee_intc);\n    ee_bus_init_timers(ps2->ee_bus, ps2->ee_timers);\n    ee_bus_init_gif(ps2->ee_bus, ps2->gif);\n    ee_bus_init_vif0(ps2->ee_bus, ps2->vif0);\n    ee_bus_init_vif1(ps2->ee_bus, ps2->vif1);\n    ee_bus_init_gs(ps2->ee_bus, ps2->gs);\n    ee_bus_init_ipu(ps2->ee_bus, ps2->ipu);\n    ee_bus_init_vu0(ps2->ee_bus, ps2->vu0);\n    ee_bus_init_vu1(ps2->ee_bus, ps2->vu1);\n    ee_bus_init_cdvd(ps2->ee_bus, ps2->cdvd);\n    ee_bus_init_usb(ps2->ee_bus, ps2->usb);\n    ee_bus_init_sbus(ps2->ee_bus, ps2->sbus);\n    ee_bus_init_dev9(ps2->ee_bus, ps2->dev9);\n    ee_bus_init_speed(ps2->ee_bus, ps2->speed);\n    ee_bus_init_ram(ps2->ee_bus, ps2->ee_ram);\n\n    ps2_ipu_reset(ps2->ipu);\n\n    ps2->ee_cycles = 0;\n    ps2->timescale = 1;\n}\n\nvoid ps2_init_tty_handler(struct ps2_state* ps2, int tty, void (*handler)(void*, char), void* udata) {\n    switch (tty) {\n        case PS2_TTY_EE:  \n            ee_bus_init_kputchar(ps2->ee_bus, handler, udata);\n            break;\n        case PS2_TTY_IOP:\n            iop_init_kputchar(ps2->iop, handler, udata);\n            break;\n        case PS2_TTY_SYSMEM:\n            iop_init_sm_putchar(ps2->iop, handler, udata);\n            break;\n    }\n}\n\nvoid ps2_boot_file(struct ps2_state* ps2, const char* path) {\n    ps2_reset(ps2);\n\n    while (ee_get_pc(ps2->ee) != 0x00082000)\n        ps2_cycle(ps2);\n\n    uint32_t i;\n\n    // Find rom0:OSDSYS string\n    for (i = 0; i < RAM_SIZE_32MB; i += 0x10) {\n        char* ptr = (char*)&ps2->ee_ram->buf[i];\n\n        if (!strncmp(ptr, \"rom0:OSDSYS\", 12)) {\n            printf(\"eegs: Found OSDSYS path at 0x%08x\\n\", i);\n\n            sprintf(ptr, \"%s\", path);\n        }\n    }\n}\n\nint ps2_load_bios(struct ps2_state* ps2, const char* path) {\n    if (ps2_bios_load(ps2->bios, path)) {\n        return 0;\n    }\n\n    ee_bus_init_fastmem(ps2->ee_bus, ps2->ee_ram->size, ps2->iop_ram->size);\n    iop_bus_init_fastmem(ps2->iop_bus, ps2->iop_ram->size);\n\n    if (ps2->system == PS2_SYSTEM_AUTO) {\n        ps2->rom0_info = ps2_rom0_search(ps2->bios->buf, ps2->bios->size + 1);\n\n        ps2_set_system(ps2, ps2->rom0_info.system);\n\n        ps2->detected_system = ps2->rom0_info.system;\n    }\n\n    return 1;\n}\n\nint ps2_load_rom1(struct ps2_state* ps2, const char* path) {\n    if (ps2_bios_load(ps2->rom1, path)) {\n        return 0;\n    }\n\n    ps2->rom1_info = ps2_rom1_search(ps2->rom1->buf, ps2->rom1->size + 1);\n\n    return 1;\n}\n\nint ps2_load_rom2(struct ps2_state* ps2, const char* path) {\n    if (!ps2_bios_load(ps2->rom2, path)) {\n        return 0;\n    }\n\n    return 1;\n}\n\nvoid ps2_reset(struct ps2_state* ps2) {\n    sched_reset(ps2->sched);\n\n    ee_reset(ps2->ee);\n    iop_reset(ps2->iop);\n    vu_init(ps2->vu0, 0, ps2->gif, ps2->vif0, ps2->vu1);\n    vu_init(ps2->vu1, 1, ps2->gif, ps2->vif1, ps2->vu1);\n\n    ps2_dmac_init(ps2->ee_dma, ps2->sif, ps2->iop_dma, ee_get_spr(ps2->ee), ps2->ee, ps2->sched, ps2->ee_bus);\n    ps2_vif_init(ps2->vif0, 0, ps2->vu0, ps2->gif, ps2->ee_intc, ps2->sched, ps2->ee_bus);\n    ps2_vif_init(ps2->vif1, 1, ps2->vu1, ps2->gif, ps2->ee_intc, ps2->sched, ps2->ee_bus);\n    ps2_intc_init(ps2->ee_intc, ps2->ee, ps2->sched);\n    ps2_ee_timers_init(ps2->ee_timers, ps2->ee_intc, ps2->sched);\n    ps2_iop_dma_init(ps2->iop_dma, ps2->iop_intc, ps2->sif, ps2->cdvd, ps2->ee_dma, ps2->sio2, ps2->spu2, ps2->sched, ps2->iop_bus);\n    ps2_iop_intc_init(ps2->iop_intc, ps2->iop);\n    ps2_iop_timers_init(ps2->iop_timers, ps2->iop_intc, ps2->sched);\n    ps2_spu2_init(ps2->spu2, ps2->iop_dma, ps2->iop_intc, ps2->sched);\n    ps2_usb_init(ps2->usb);\n    ps2_fw_init(ps2->fw, ps2->iop_intc);\n    ps2_sbus_init(ps2->sbus, ps2->ee_intc, ps2->iop_intc, ps2->sched);\n    ps2_cdvd_reset(ps2->cdvd);\n\n    ps2_gif_reset(ps2->gif);\n    ps2_gs_reset(ps2->gs);\n    ps2_ram_reset(ps2->ee_ram);\n    ps2_ram_reset(ps2->iop_ram);\n\n    ps2_ipu_reset(ps2->ipu);\n\n    ps2->ee_cycles = 0;\n}\n\n// To-do: This will soon be useless, need to integrate\n// the tracer into our debugging UI\n// static int depth = 0;\n\n// static inline void ps2_trace(struct ps2_state* ps2) {\n//     for (int i = 0; i < ps2->nfuncs; i++) {\n//         if (ps2->ee->pc == ps2->func[i].addr) {\n//             printf(\"trace: \");\n\n//             for (int i = 0; i < depth; i++)\n//                 putchar(' ');\n\n//             printf(\"%s @ 0x%08x\\n\", ps2->func[i].name, ps2->func[i].addr);\n\n//             ++depth;\n\n//             break;\n//         }\n//     }\n\n//     if (ps2->ee->opcode == 0x03e00008)\n//         if (depth > 0) --depth;\n// }\n\nvoid ps2_cycle(struct ps2_state* ps2) {\n    int cycles = ee_run_block(ps2->ee, 128);\n\n    ps2->ee_cycles += cycles;\n\n    sched_tick(ps2->sched, ps2->timescale * cycles);\n\n    ps2_ipu_run(ps2->ipu);\n\n    ps2_ee_timers_tick_cycles(ps2->ee_timers, cycles);\n\n    while (ps2->ee_cycles > 8) {\n        iop_cycle(ps2->iop);\n\n        ps2_iop_timers_tick(ps2->iop_timers);\n\n        ps2->ee_cycles -= 8;\n    }\n}\n\nvoid ps2_step_ee(struct ps2_state* ps2) {\n    ee_step(ps2->ee);\n    sched_tick(ps2->sched, 1);\n    ps2_ee_timers_tick(ps2->ee_timers);\n\n    ps2_ipu_run(ps2->ipu);\n\n    ps2->ee_cycles++; \n\n    if (ps2->ee_cycles == 8) {\n        iop_cycle(ps2->iop);\n        ps2_iop_timers_tick(ps2->iop_timers);\n\n        ps2->ee_cycles = 0;\n    }\n}\n\nvoid ps2_step_iop(struct ps2_state* ps2) {\n    for (int i = 0; i < 8; i++) {\n        ps2_ee_timers_tick(ps2->ee_timers);\n        ee_step(ps2->ee);\n    }\n\n    sched_tick(ps2->sched, 8);\n    iop_cycle(ps2->iop);\n    ps2_iop_timers_tick(ps2->iop_timers);\n\n    ps2_ipu_run(ps2->ipu);\n}\n\nvoid ps2_iop_cycle(struct ps2_state* ps2) {\n    // while (ps2->ee_cycles) {\n    //     sched_tick(ps2->sched, 1);\n    //     ee_cycle(ps2->ee);\n    //     ps2_ee_timers_tick(ps2->ee_timers);\n\n    //     --ps2->ee_cycles;\n    // }\n\n    // iop_cycle(ps2->iop);\n    // ps2_iop_timers_tick(ps2->iop_timers);\n\n    // ps2->ee_cycles = 7;\n}\n\nvoid ps2_set_timescale(struct ps2_state* ps2, int timescale) {\n    ps2->timescale = timescale;\n}\n\nvoid ps2_destroy(struct ps2_state* ps2) {\n    free(ps2->strtab);\n    free(ps2->func);\n\n    ps2_cdvd_destroy(ps2->cdvd);\n\n    sched_destroy(ps2->sched);\n    ee_destroy(ps2->ee);\n    vu_destroy(ps2->vu0);\n    vu_destroy(ps2->vu1);\n    iop_destroy(ps2->iop);\n    ee_bus_destroy(ps2->ee_bus);\n    iop_bus_destroy(ps2->iop_bus);\n    ps2_gif_destroy(ps2->gif);\n    ps2_gs_destroy(ps2->gs);\n    ps2_ipu_destroy(ps2->ipu);\n    ps2_vif_destroy(ps2->vif0);\n    ps2_vif_destroy(ps2->vif1);\n    ps2_dmac_destroy(ps2->ee_dma);\n    ps2_ram_destroy(ps2->ee_ram);\n    ps2_intc_destroy(ps2->ee_intc);\n    ps2_ee_timers_destroy(ps2->ee_timers);\n    ps2_iop_dma_destroy(ps2->iop_dma);\n    ps2_ram_destroy(ps2->iop_spr);\n    ps2_iop_intc_destroy(ps2->iop_intc);\n    ps2_iop_timers_destroy(ps2->iop_timers);\n    ps2_ram_destroy(ps2->iop_ram);\n    ps2_sio2_destroy(ps2->sio2);\n    ps2_spu2_destroy(ps2->spu2);\n    ps2_usb_destroy(ps2->usb);\n    ps2_fw_destroy(ps2->fw);\n    ps2_sbus_destroy(ps2->sbus);\n    ps2_dev9_destroy(ps2->dev9);\n    ps2_speed_destroy(ps2->speed);\n    ps2_bios_destroy(ps2->bios);\n    ps2_bios_destroy(ps2->rom1);\n    ps2_bios_destroy(ps2->rom2);\n    ps2_sif_destroy(ps2->sif);\n\n    // Destroy optional hardware\n    if (ps2->s14x_nand) s14x_nand_destroy(ps2->s14x_nand);\n    if (ps2->s14x_syscon) s14x_syscon_destroy(ps2->s14x_syscon);\n    if (ps2->s14x_sram) s14x_sram_destroy(ps2->s14x_sram);\n    if (ps2->s14x_link) s14x_link_destroy(ps2->s14x_link);\n    if (ps2->s14x_ioboard) s14x_ioboard_destroy(ps2->s14x_ioboard);\n    if (ps2->s14x_aiboard) s14x_aiboard_destroy(ps2->s14x_aiboard);\n\n    free(ps2);\n}\n\nvoid ps2_set_system(struct ps2_state* ps2, int system) {\n    int ee_ram_size, iop_ram_size, mechacon_model;\n\n    // Destroy optional hardware\n    if (ps2->s14x_nand) { s14x_nand_destroy(ps2->s14x_nand); ps2->s14x_nand = NULL; }\n    if (ps2->s14x_syscon) { s14x_syscon_destroy(ps2->s14x_syscon); ps2->s14x_syscon = NULL; }\n    if (ps2->s14x_sram) { s14x_sram_destroy(ps2->s14x_sram); ps2->s14x_sram = NULL; }\n    if (ps2->s14x_link) { s14x_link_destroy(ps2->s14x_link); ps2->s14x_link = NULL; }\n    if (ps2->s14x_ioboard) { s14x_ioboard_destroy(ps2->s14x_ioboard); ps2->s14x_ioboard = NULL; }\n    if (ps2->s14x_aiboard) { s14x_aiboard_destroy(ps2->s14x_aiboard); ps2->s14x_aiboard = NULL; }\n\n    switch (system) {\n        case PS2_SYSTEM_AUTO: {\n            ps2->rom0_info = ps2_rom0_search(ps2->bios->buf, ps2->bios->size + 1);\n\n            ps2_set_system(ps2, ps2->rom0_info.system);\n\n            ps2->detected_system = ps2->rom0_info.system;\n\n            return;\n        } break;\n        case PS2_SYSTEM_RETAIL: {\n            ee_ram_size = RAM_SIZE_32MB;\n            iop_ram_size = RAM_SIZE_2MB;\n            mechacon_model = CDVD_MECHACON_SPC970;\n        } break;\n\n        case PS2_SYSTEM_RETAIL_DECKARD: {\n            ee_ram_size = RAM_SIZE_32MB;\n            iop_ram_size = RAM_SIZE_2MB;\n            mechacon_model = CDVD_MECHACON_DRAGON;\n        } break;\n\n        case PS2_SYSTEM_DESR: {\n            ee_ram_size = RAM_SIZE_64MB;\n            iop_ram_size = RAM_SIZE_8MB;\n            mechacon_model = CDVD_MECHACON_DRAGON;\n        } break;\n\n        case PS2_SYSTEM_TEST:\n        case PS2_SYSTEM_TOOL: {\n            ee_ram_size = RAM_SIZE_128MB;\n            iop_ram_size = RAM_SIZE_8MB;\n\n            // To-do: Separate mechacon model for TOOL/TEST\n            mechacon_model = CDVD_MECHACON_DRAGON;\n        } break;\n\n        case PS2_SYSTEM_KONAMI_PYTHON: {\n            ee_ram_size = RAM_SIZE_32MB;\n            iop_ram_size = RAM_SIZE_2MB;\n            mechacon_model = CDVD_MECHACON_SPC970;\n        } break;\n\n        case PS2_SYSTEM_KONAMI_PYTHON2: {\n            ee_ram_size = RAM_SIZE_32MB;\n            iop_ram_size = RAM_SIZE_2MB;\n            mechacon_model = CDVD_MECHACON_DRAGON;\n        } break;\n\n        case PS2_SYSTEM_NAMCO_S147: {\n            ee_ram_size = RAM_SIZE_32MB;\n            iop_ram_size = RAM_SIZE_2MB;\n\n            // This board actually has no MechaCon\n            mechacon_model = CDVD_MECHACON_DRAGON;\n\n            // Wire up System 147/148 hardware\n            ps2->s14x_nand = s14x_nand_create();\n            ps2->s14x_syscon = s14x_syscon_create();\n            ps2->s14x_sram = s14x_sram_create();\n            ps2->s14x_link = s14x_link_create();\n            ps2->s14x_ioboard = s14x_ioboard_create();\n            ps2->s14x_aiboard = s14x_aiboard_create();\n\n            s14x_nand_init(ps2->s14x_nand);\n            s14x_syscon_init(ps2->s14x_syscon);\n            s14x_sram_init(ps2->s14x_sram, &ps2->s14x_syscon->sram_write_flag);\n            s14x_link_init(ps2->s14x_link, ps2->iop_intc, ps2->sched);\n            s14x_ioboard_init(ps2->s14x_ioboard, 0);\n            s14x_aiboard_init(ps2->s14x_aiboard);\n\n            iop_bus_init_s14x_nand(ps2->iop_bus, ps2->s14x_nand);\n            iop_bus_init_s14x_syscon(ps2->iop_bus, ps2->s14x_syscon);\n            iop_bus_init_s14x_sram(ps2->iop_bus, ps2->s14x_sram);\n            iop_bus_init_s14x_link(ps2->iop_bus, ps2->s14x_link);\n\n            s14x_link_register_node(ps2->s14x_link, 2, s14x_ioboard_handle_packet, ps2->s14x_ioboard);\n            s14x_link_register_node(ps2->s14x_link, 3, s14x_aiboard_handle_packet, ps2->s14x_aiboard);\n        } break;\n\n        case PS2_SYSTEM_NAMCO_S148: {\n            ee_ram_size = RAM_SIZE_64MB;\n            iop_ram_size = RAM_SIZE_2MB;\n\n            // This board actually has no MechaCon\n            mechacon_model = CDVD_MECHACON_DRAGON;\n\n            // Wire up System 147/148 hardware\n            ps2->s14x_nand = s14x_nand_create();\n            ps2->s14x_syscon = s14x_syscon_create();\n            ps2->s14x_sram = s14x_sram_create();\n            ps2->s14x_link = s14x_link_create();\n            ps2->s14x_ioboard = s14x_ioboard_create();\n            ps2->s14x_aiboard = s14x_aiboard_create();\n\n            s14x_nand_init(ps2->s14x_nand);\n            s14x_syscon_init(ps2->s14x_syscon);\n            s14x_sram_init(ps2->s14x_sram, &ps2->s14x_syscon->sram_write_flag);\n            s14x_link_init(ps2->s14x_link, ps2->iop_intc, ps2->sched);\n            s14x_ioboard_init(ps2->s14x_ioboard, 0);\n            s14x_aiboard_init(ps2->s14x_aiboard);\n\n            iop_bus_init_s14x_nand(ps2->iop_bus, ps2->s14x_nand);\n            iop_bus_init_s14x_syscon(ps2->iop_bus, ps2->s14x_syscon);\n            iop_bus_init_s14x_sram(ps2->iop_bus, ps2->s14x_sram);\n            iop_bus_init_s14x_link(ps2->iop_bus, ps2->s14x_link);\n\n            s14x_link_register_node(ps2->s14x_link, 2, s14x_ioboard_handle_packet, ps2->s14x_ioboard);\n            s14x_link_register_node(ps2->s14x_link, 3, s14x_aiboard_handle_packet, ps2->s14x_aiboard);\n        } break;\n\n        case PS2_SYSTEM_NAMCO_S246: {\n            ee_ram_size = RAM_SIZE_32MB;\n            iop_ram_size = RAM_SIZE_2MB;\n            mechacon_model = CDVD_MECHACON_DRAGON;\n        } break;\n\n        case PS2_SYSTEM_NAMCO_S256: {\n            ee_ram_size = RAM_SIZE_64MB;\n            iop_ram_size = RAM_SIZE_4MB;\n            mechacon_model = CDVD_MECHACON_DRAGON;\n        } break;\n\n        default: {\n            ee_ram_size = RAM_SIZE_32MB;\n            iop_ram_size = RAM_SIZE_2MB;\n            mechacon_model = CDVD_MECHACON_DRAGON;\n        } break;\n    }\n\n    ps2->detected_system = system;\n\n    ps2_ram_destroy(ps2->ee_ram);\n    ps2_ram_destroy(ps2->iop_ram);\n\n    ps2->ee_ram = ps2_ram_create();\n    ps2->iop_ram = ps2_ram_create();\n\n    ps2_ram_init(ps2->ee_ram, ee_ram_size);\n    ps2_ram_init(ps2->iop_ram, iop_ram_size);\n\n    ps2_cdvd_set_mechacon_model(ps2->cdvd, mechacon_model);\n\n    struct ee_bus_s ee_bus_data;\n    ee_bus_data.read8 = ee_bus_read8;\n    ee_bus_data.read16 = ee_bus_read16;\n    ee_bus_data.read32 = ee_bus_read32;\n    ee_bus_data.read64 = ee_bus_read64;\n    ee_bus_data.read128 = ee_bus_read128;\n    ee_bus_data.write8 = ee_bus_write8;\n    ee_bus_data.write16 = ee_bus_write16;\n    ee_bus_data.write32 = ee_bus_write32;\n    ee_bus_data.write64 = ee_bus_write64;\n    ee_bus_data.write128 = ee_bus_write128;\n    ee_bus_data.udata = ps2->ee_bus;\n\n    ee_set_ram_size(ps2->ee, ee_ram_size);\n\n    ee_bus_init_ram(ps2->ee_bus, ps2->ee_ram);\n    ee_bus_init_iop_ram(ps2->ee_bus, ps2->iop_ram);\n    iop_bus_init_iop_ram(ps2->iop_bus, ps2->iop_ram);\n\n    ee_bus_init_fastmem(ps2->ee_bus, ps2->ee_ram->size, ps2->iop_ram->size);\n    iop_bus_init_fastmem(ps2->iop_bus, ps2->iop_ram->size);\n}\n\nvoid ps2_set_mac_address(struct ps2_state* ps2, const uint8_t* mac) {\n    ps2_speed_set_mac_address(ps2->speed, mac);\n}"
  },
  {
    "path": "src/ps2.h",
    "content": "#ifndef PS2_H\n#define PS2_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"ee/bus.h\"\n#include \"ee/ee.h\"\n#include \"ee/gif.h\"\n#include \"ee/vif.h\"\n#include \"ee/dmac.h\"\n#include \"ee/intc.h\"\n#include \"ee/timers.h\"\n#include \"ee/vu.h\"\n#include \"iop/bus.h\"\n#include \"iop/bus_decl.h\"\n#include \"iop/iop.h\"\n#include \"iop/dma.h\"\n#include \"iop/intc.h\"\n#include \"iop/timers.h\"\n#include \"iop/cdvd.h\"\n#include \"iop/sio2.h\"\n#include \"iop/spu2.h\"\n#include \"iop/usb.h\"\n#include \"iop/fw.h\"\n#include \"shared/bios.h\"\n#include \"shared/ram.h\"\n#include \"shared/sif.h\"\n#include \"shared/sbus.h\"\n#include \"shared/dev9.h\"\n#include \"shared/speed.h\"\n#include \"gs/gs.h\"\n#include \"ipu/ipu.h\"\n\n// Arcade hardware\n// Namco System 147/148\n#include \"s14x/nand.h\"\n#include \"s14x/syscon.h\"\n#include \"s14x/sram.h\"\n#include \"s14x/link.h\"\n#include \"s14x/ioboard.h\"\n#include \"s14x/aiboard.h\"\n\n// SIO2 devices (controllers, memory cards, etc.)\n#include \"dev/ds.h\"\n#include \"dev/guncon.h\"\n#include \"dev/mcd.h\"\n#include \"dev/ps1_mcd.h\"\n#include \"dev/mtap.h\"\n\n#include \"scheduler.h\"\n#include \"rom.h\"\n\n#define PS2_TTY_EE 0\n#define PS2_TTY_IOP 1\n#define PS2_TTY_SYSMEM 2\n\nenum {\n    PS2_SYSTEM_AUTO = 0,\n    PS2_SYSTEM_RETAIL,\n    PS2_SYSTEM_RETAIL_DECKARD,\n    PS2_SYSTEM_DESR,\n    PS2_SYSTEM_TEST,\n    PS2_SYSTEM_TOOL,\n    PS2_SYSTEM_KONAMI_PYTHON,\n    PS2_SYSTEM_KONAMI_PYTHON2,\n    PS2_SYSTEM_NAMCO_S147,\n    PS2_SYSTEM_NAMCO_S148,\n    PS2_SYSTEM_NAMCO_S246,\n    PS2_SYSTEM_NAMCO_S256\n};\n\nstruct ps2_elf_function {\n    char* name;\n    uint32_t addr;\n};\n\nstruct ps2_state {\n    // CPUs\n    struct ee_state* ee;\n    struct iop_state* iop;\n    struct vu_state* vu0;\n    struct vu_state* vu1;\n\n    // EE-only\n    struct ee_bus* ee_bus;\n    struct ps2_gif* gif;\n    struct ps2_vif* vif0;\n    struct ps2_vif* vif1;\n    struct ps2_gs* gs;\n    struct ps2_ipu* ipu;\n    struct ps2_dmac* ee_dma;\n    struct ps2_ram* ee_ram;\n    struct ps2_intc* ee_intc;\n    struct ps2_ee_timers* ee_timers;\n\n    // IOP-only\n    struct iop_bus* iop_bus;\n    struct ps2_ram* iop_spr;\n    struct ps2_iop_dma* iop_dma;\n    struct ps2_iop_intc* iop_intc;\n    struct ps2_iop_timers* iop_timers;\n    struct ps2_sio2* sio2;\n    struct ps2_spu2* spu2;\n    struct ps2_fw* fw;\n    \n    // Shared\n    struct ps2_ram* iop_ram;\n    struct ps2_bios* bios;\n    struct ps2_bios* rom1; // Mapped to 1E000000-1E3FFFFF (DVD firmware)\n    struct ps2_bios* rom2; // Mapped to 1E400000-1E7FFFFF (Chinese exts)\n    struct ps2_cdvd* cdvd;\n    struct ps2_sif* sif;\n    struct ps2_usb* usb;\n    struct ps2_sbus* sbus;\n    struct ps2_dev9* dev9;\n    struct ps2_speed* speed;\n\n    // Namco System 147/148\n    struct s14x_nand* s14x_nand;\n    struct s14x_syscon* s14x_syscon;\n    struct s14x_sram* s14x_sram;\n    struct s14x_link* s14x_link;\n    struct s14x_ioboard* s14x_ioboard;\n    struct s14x_aiboard* s14x_aiboard;\n\n    struct sched_state* sched;\n\n    int ee_cycles;\n    int timescale;\n    int system, detected_system;\n\n    struct ps2_rom_info rom0_info;\n    struct ps2_rom_info rom1_info;\n\n    // Debug\n    struct ps2_elf_function* func;\n    unsigned int nfuncs;\n    char* strtab;\n};\n\nstruct ps2_state* ps2_create(void);\nvoid ps2_init(struct ps2_state* ps2);\nvoid ps2_init_tty_handler(struct ps2_state* ps2, int tty, void (*handler)(void*, char), void* udata);\nvoid ps2_boot_file(struct ps2_state* ps2, const char* path);\nvoid ps2_reset(struct ps2_state* ps2);\nint ps2_load_bios(struct ps2_state* ps2, const char* path);\nint ps2_load_rom1(struct ps2_state* ps2, const char* path);\nint ps2_load_rom2(struct ps2_state* ps2, const char* path);\nvoid ps2_cycle(struct ps2_state* ps2);\nvoid ps2_step_ee(struct ps2_state* ps2);\nvoid ps2_step_iop(struct ps2_state* ps2);\nvoid ps2_set_timescale(struct ps2_state* ps2, int timescale);\nvoid ps2_iop_cycle(struct ps2_state* ps2);\nvoid ps2_destroy(struct ps2_state* ps2);\nvoid ps2_set_system(struct ps2_state* ps2, int system);\nvoid ps2_set_mac_address(struct ps2_state* ps2, const uint8_t* mac);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ps2_elf.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"ps2_elf.h\"\n\nint ps2_elf_load(struct ps2_state* ps2, const char* path) {\n    ps2_reset(ps2);\n\n    while (ee_get_pc(ps2->ee) != 0x00082000)\n        ps2_cycle(ps2);\n\n    Elf32_Ehdr ehdr;\n    FILE* file = fopen(path, \"rb\");\n\n    if (!fread(&ehdr, sizeof(Elf32_Ehdr), 1, file)) {\n        printf(\"elf: Couldn't read ELF header\\n\");\n\n        return 1;\n    }\n\n    // ps2->ee->pc = ehdr.e_entry;\n    // ps2->ee->next_pc = ps2->ee->pc + 4;\n\n    Elf32_Phdr phdr;\n\n    puts(\"  Type           Offset   VirtAddr   PhysAddr   FileSiz MemSiz  Flg Align\");\n\n    for (int i = 0; i < ehdr.e_phnum; i++) {\n        fseek(file, ehdr.e_phoff + (i * ehdr.e_phentsize), SEEK_SET);\n        \n        if (!fread(&phdr, sizeof(Elf32_Phdr), 1, file)) {\n            printf(\"elf: Couldn't read program header\\n\");\n\n            return 1;\n        }\n\n        if (phdr.p_type != PT_LOAD)\n            continue;\n\n        printf(\"  LOAD           0x%06x 0x%08x 0x%08x 0x%05x 0x%05x %c%c%c 0x%x\\n\",\n            phdr.p_offset,\n            phdr.p_vaddr,\n            phdr.p_paddr,\n            phdr.p_filesz,\n            phdr.p_memsz,\n            (phdr.p_flags & 1) ? 'R' : ' ',\n            (phdr.p_flags & 2) ? 'W' : ' ',\n            (phdr.p_flags & 4) ? 'X' : ' ',\n            phdr.p_align\n        );\n\n        // Clear p_memsz bytes of EE RAM\n        memset(ps2->ee_ram->buf + phdr.p_vaddr, 0, phdr.p_memsz);\n\n        // Read segment binary\n        fseek(file, phdr.p_offset, SEEK_SET);\n\n        if (!fread(ps2->ee_ram->buf + phdr.p_vaddr, 1, phdr.p_filesz, file)) {\n            printf(\"elf: Couldn't read segment binary\\n\");\n        }\n    }\n\n    printf(\"Entry: 0x%08x\\n\", ehdr.e_entry);\n\n    // Read symbol table header\n    Elf32_Shdr symtab;\n\n    memset(&symtab, 0, sizeof(Elf32_Shdr));\n\n    for (int i = 0; i < ehdr.e_shnum; i++) {\n        Elf32_Shdr shdr;\n\n        fseek(file, ehdr.e_shoff + (i * ehdr.e_shentsize), SEEK_SET);\n        \n        if (!fread(&shdr, sizeof(Elf32_Shdr), 1, file)) {\n            printf(\"elf: Couldn't read section header\\n\");\n\n            return 1;\n        }\n\n        if ((shdr.sh_type == SHT_STRTAB) && (i != ehdr.e_shstrndx)) {\n            printf(\"elf: Loading string table size=%x offset=%x\\n\", shdr.sh_size, shdr.sh_offset);\n\n            ps2->strtab = malloc(shdr.sh_size);\n\n            fseek(file, shdr.sh_offset, SEEK_SET);\n\n            if (!fread(ps2->strtab, 1, shdr.sh_size, file)) {\n                printf(\"elf: Couldn't read string table\\n\");\n\n                free(ps2->strtab);\n\n                return 1;\n            }\n        }\n\n        if (shdr.sh_type == SHT_SYMTAB)\n            symtab = shdr;\n    }\n\n    // No symbol table present\n    if (symtab.sh_type != SHT_SYMTAB) {\n        fclose(file);\n\n        return 0;\n    }\n\n    if (!symtab.sh_entsize) {\n        fclose(file);\n\n        return 0;\n    }\n\n    if (!symtab.sh_size) {\n        fclose(file);\n\n        return 0;\n    }\n\n    printf(\"elf: Got symbol table\\n\");\n\n    size_t nsyms = symtab.sh_size / symtab.sh_entsize;\n\n    // Read symbol table\n    Elf32_Sym sym;\n\n    for (int i = 0; i < nsyms; i++) {\n        fseek(file, symtab.sh_offset + (i * symtab.sh_entsize), SEEK_SET);\n\n        if (!fread(&sym, sizeof(Elf32_Sym), 1, file)) {\n            printf(\"elf: Couldn't read symbol table\\n\");\n\n            free(ps2->strtab);\n\n            return 1;\n        }\n\n        if (ELF32_ST_TYPE(sym.st_info) != STT_FUNC)\n            continue;\n\n        ++ps2->nfuncs;\n    }\n\n    ps2->func = malloc(ps2->nfuncs * sizeof(struct ps2_elf_function));\n\n    int index = 0;\n\n    for (int i = 0; i < nsyms; i++) {\n        fseek(file, symtab.sh_offset + (i * symtab.sh_entsize), SEEK_SET);\n\n        if (!fread(&sym, sizeof(Elf32_Sym), 1, file)) {\n            printf(\"elf: Couldn't read symbols\\n\");\n\n            free(ps2->strtab);\n            free(ps2->func);\n\n            return 1;\n        }\n\n        if (ELF32_ST_TYPE(sym.st_info) != STT_FUNC)\n            continue;\n\n        ps2->func[index].addr = sym.st_value;\n        ps2->func[index++].name = ps2->strtab + sym.st_name;\n    }\n \n    fclose(file);\n\n    return 0;\n}"
  },
  {
    "path": "src/ps2_elf.h",
    "content": "#ifndef PS2_ELF_H\n#define PS2_ELF_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#ifdef __linux__\n#include <elf.h>\n#else\n#include \"elf.h\"\n#endif\n\n#include \"ps2.h\"\n\nint ps2_elf_load(struct ps2_state* ps2, const char* path);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/ps2_iso9660.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdint.h>\n#include <stdio.h>\n#include <ctype.h>\n\n#include \"ps2_iso9660.h\"\n\n#ifdef _MSC_VER\n#define fseek64 _fseeki64\n#define ftell64 _ftelli64\n#elif defined(_WIN32)\n#define fseek64 fseeko64\n#define ftell64 ftello64\n#else\n#define fseek64 fseek\n#define ftell64 ftell\n#endif\n\n#define IGNORE_RETURN ((void)!)\n\nstruct iso9660_state* iso9660_open(const char* path) {\n    FILE* file = fopen(path, \"rb\");\n\n    if (!file)\n        return NULL;\n\n    struct iso9660_state* iso = malloc(sizeof(struct iso9660_state));\n\n    memset(iso, 0, sizeof(struct iso9660_state));\n\n    iso->file = file;\n\n    return iso;\n}\n\nchar* iso9660_get_boot_path(struct iso9660_state* iso) {\n    // Cache the PVD (Primary Volume Descriptor)\n    fseek64(iso->file, 16 * 0x800, SEEK_SET);\n\n    if (!fread(&iso->pvd, sizeof(struct iso9660_pvd), 1, iso->file)) {\n        printf(\"iso9660: Couldn't read PVD\\n\");\n\n        return NULL;\n    }\n\n    if (strncmp(iso->pvd.id, \"\\1CD001\\1\", 8)) {\n        printf(\"iso: Unknown format disc %d\\n\", iso->pvd.id[0]);\n\n        return NULL;\n    }\n\n    struct iso9660_dirent* root = (struct iso9660_dirent*)iso->pvd.root;\n\n    fseek64(iso->file, (uint64_t)root->lba_le * 0x800, SEEK_SET);\n\n    if (!fread(iso->buf, 0x800, 1, iso->file)) {\n        printf(\"iso9660: Couldn't read root sector\\n\");\n\n        return NULL;\n    }\n\n    struct iso9660_dirent* dir = (struct iso9660_dirent*)iso->buf;\n\n    while (dir->dr_len) {\n        // printf(\"Entry: lba=%d len=%d size=%d name_len=%d name=\\\"\", dir->lba_le, dir->dr_len, dir->size_le, dir->id_len);\n\n        // if (dir->id == '\\0') {\n        //     putchar('.');\n        // } else if (dir->id == '\\1') {\n        //     putchar('.');\n        //     putchar('.');\n        // } else {\n        //     for (int j = 0; j < dir->id_len; j++) {\n        //         putchar(*(((char*)&dir->id) + j));\n        //     }\n        // }\n\n        // puts(\"\\\"\");\n\n        if (dir->id_len == 12) {\n            if (!strcmp((char*)&dir->id, \"SYSTEM.CNF;1\")) {\n                break;\n            }\n        }\n\n        uint8_t* ptr = (uint8_t*)dir;\n\n        dir = (struct iso9660_dirent*)(ptr + dir->dr_len);\n    }\n\n    if (!dir->dr_len) {\n        printf(\"iso: SYSTEM.CNF not found! (non-PlayStation disc?)\\n\");\n\n        return NULL;\n    }\n\n    fseek64(iso->file, (uint64_t)dir->lba_le * 0x800, SEEK_SET);\n\n    if (!fread(iso->buf, 0x800, 1, iso->file)) {\n        printf(\"iso9660: Couldn't read SYSTEM.CNF\\n\");\n\n        return NULL;\n    }\n\n    printf(\"isobuf: %s\\n\", iso->buf);\n\n    // Parse SYSTEM.CNF\n    char* p = iso->buf;\n    char key[64];\n    \n    while (*p) {\n        char* kptr = key;\n\n        while (isspace(*p))\n            ++p;\n\n        while (isalnum(*p))\n            *kptr++ = *p++;\n\n        *kptr = '\\0';\n\n        // printf(\"key: %s\\n\", key);\n\n        if (!strncmp(key, \"BOOT2\", 64)) {\n            while (isspace(*p)) ++p;\n\n            if (*p != '=') {\n                printf(\"iso: Expected =\\n\");\n\n                return NULL;\n            }\n\n            ++p;\n\n            while (isspace(*p)) ++p;\n\n            int i;\n\n            for (i = 0; i < 255; i++) {\n                if (*p == '\\n' || *p == '\\r')\n                    break;\n\n                iso->boot_file[i] = *p++;\n            }\n\n            iso->boot_file[i] = '\\0';\n\n            return iso->boot_file;\n        } else {\n            while ((*p != '\\n') && (*p != '\\0') && (*p != '\\r')) ++p;\n            while ((*p == '\\n') || (*p == '\\r')) ++p;\n        }\n    }\n\n    printf(\"iso: Couldn't find BOOT2 entry in SYSTEM.CNF (PlayStation disc?)\\n\");\n\n    return NULL;\n}\n\n// void iso9660_load_boot_elf(struct iso9660_state* iso, char* buf);\n\nvoid iso9660_close(struct iso9660_state* iso) {\n    if (iso->file)\n        fclose(iso->file);\n\n    free(iso);\n}\n\n#undef fseek64\n#undef ftell64"
  },
  {
    "path": "src/ps2_iso9660.h",
    "content": "#ifndef PS2_ISO9660_H\n#define PS2_ISO9660_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdio.h>\n\n#include \"ps2.h\"\n\n#ifdef _MSC_VER\n#pragma pack(push, 1)\n#define PACKED\n#else\n#define PACKED __attribute__((packed))\n#endif\n\nstruct PACKED iso9660_pvd {\n    char id[8];\n    char system_id[32];\n    char volume_id[32];\n    char zero[8];\n    uint32_t total_sector_le, total_sect_be;\n    char zero2[32];\n    uint16_t volume_set_size_le, volume_set_size_be;\n    uint16_t volume_seq_nr_le, volume_seq_nr_be;\n    uint16_t sector_size_le, sector_size_be;\n    uint32_t path_table_len_le, path_table_len_be;\n    uint32_t path_table_le, path_table_2nd_le;\n    uint32_t path_table_be, path_table_2nd_be;\n    uint8_t root[34];\n    char volume_set_id[128], publisher_id[128], data_preparer_id[128], application_id[128];\n    char copyright_file_id[37], abstract_file_id[37], bibliographical_file_id[37];\n};\n\nstruct PACKED iso9660_dirent {\n    uint8_t dr_len;\n    uint8_t ext_dr_len;\n    uint32_t lba_le, lba_be;\n    uint32_t size_le, size_be;\n    uint8_t date[7];\n    uint8_t flags;\n    uint8_t file_unit_size;\n    uint8_t interleave_gap_size;\n    uint16_t volume_seq_nr_le, volume_seq_nr_be;\n    uint8_t id_len;\n    uint8_t id;\n};\n\n#ifdef _MSC_VER\n#pragma pack(pop)\n#endif\n\nstruct iso9660_state {\n    char buf[0x800];\n    char boot_file[256];\n    struct iso9660_pvd pvd;\n    FILE* file;\n};\n\nstruct iso9660_state* iso9660_open(const char* path);\nchar* iso9660_get_boot_path(struct iso9660_state* iso);\n// void iso9660_load_boot_elf(struct iso9660_state* iso, char* buf);\nvoid iso9660_close(struct iso9660_state* iso);\n\n#undef PACKED\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif\n\n"
  },
  {
    "path": "src/queue.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stddef.h>\n#include <stdio.h>\n\n#include \"queue.h\"\n\nstruct queue_state* queue_create(void) {\n    return malloc(sizeof(struct queue_state));\n}\n\nvoid queue_init(struct queue_state* queue) {\n    memset(queue, 0, sizeof(struct queue_state));\n\n    queue->cap = 4;\n    queue->buf = malloc(queue->cap * sizeof(uint32_t));\n}\n\nvoid queue_push(struct queue_state* queue, uint32_t value) {\n    if (queue->size == queue->cap) {\n        queue->cap *= 2;\n        \n        uint32_t* buf = realloc(queue->buf, queue->cap * sizeof(uint32_t));\n\n        if (!buf) {\n            printf(\"queue: Couldn't allocate memory\\n\");\n\n            exit(1);\n        }\n\n        queue->buf = buf;\n    }\n\n    queue->buf[queue->size++] = value;\n}\n\nuint32_t queue_pop(struct queue_state* queue) {\n    if (queue->index == queue->size)\n        return 0;\n\n    return queue->buf[queue->index++];\n}\n\nuint32_t queue_peek(struct queue_state* queue) {\n    if (queue->index == queue->size)\n        return 0;\n\n    return queue->buf[queue->index];\n}\n\nuint32_t queue_at(struct queue_state* queue, int idx) {\n    return queue->buf[queue->index + idx];\n}\n\nint queue_is_empty(struct queue_state* queue) {\n    return queue->index == queue->size;\n}\n\nint queue_size(struct queue_state* queue) {\n    return queue->size;\n}\n\nvoid queue_clear(struct queue_state* queue) {\n    queue->size = 0;\n    queue->index = 0;\n}\n\nvoid queue_destroy(struct queue_state* queue) {\n    free(queue->buf);\n    free(queue);\n}"
  },
  {
    "path": "src/queue.h",
    "content": "#ifndef QUEUE_H\n#define QUEUE_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\nstruct queue_state {\n    uint32_t* buf;\n\n    unsigned int cap;\n    unsigned int size;\n    unsigned int index;\n};\n\nstruct queue_state* queue_create(void);\nvoid queue_init(struct queue_state* queue);\nvoid queue_push(struct queue_state* queue, uint32_t value);\nuint32_t queue_pop(struct queue_state* queue);\nuint32_t queue_peek(struct queue_state* queue);\nuint32_t queue_at(struct queue_state* queue, int idx);\nint queue_is_empty(struct queue_state* queue);\nint queue_size(struct queue_state* queue);\nvoid queue_clear(struct queue_state* queue);\nvoid queue_destroy(struct queue_state* queue);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/rom.c",
    "content": "#include \"rom.h\"\n#include \"md5.h\"\n\nstatic const struct ps2_rom_info rom0_info_table[] = {\n    { \"32f2e4d5ff5ee11072a6bc45530f5765\", \"5.0 01/17/00 T\", \"N/A\", \"DTL-H10000\", 4 },\n    { \"acf4730ceb38ac9d8c7d8e21f2614600\", \"5.0 01/17/00 T\", \"NTSC-J\", \"SCPH-10000\", 1 },\n    { \"acf9968c8f596d2b15f42272082513d1\", \"5.0 02/17/00 T\", \"N/A\", \"DTL-H10000\", 4 },\n    { \"b1459d7446c69e3e97e6ace3ae23dd1c\", \"5.0 02/17/00 T\", \"NTSC-J\", \"SCPH-10000/SCPH-15000\", 1 },\n    { \"d3f1853a16c2ec18f3cd1ae655213308\", \"5.0 02/24/00 T\", \"N/A\", \"DTL-H10000\", 4 },\n    { \"63e6fd9b3c72e0d7b920e80cf76645cd\", \"5.0 07/27/00 A\", \"N/A\", \"DTL-H30001\", 4 },\n    { \"a20c97c02210f16678ca3010127caf36\", \"5.0 07/27/00 A\", \"NTSC-U/C\", \"SCPH-30001\", 1 },\n    { \"a925e84ac0ed2711af36e28772793be2\", \"5.0 09/01/00 ?\", \"N/A\", \"COH-H31000 (Konami Python)\", 6 },\n    { \"8db2fbbac7413bf3e7154c1e0715e565\", \"5.0 09/02/00 A\", \"NTSC-U/C\", \"SCPH-30001\", 1 },\n    { \"91c87cb2f2eb6ce529a2360f80ce2457\", \"5.0 09/02/00 E\", \"N/A\", \"DTL-H30002\", 4 },\n    { \"3016b3dd42148a67e2c048595ca4d7ce\", \"5.0 09/02/00 E\", \"N/A\", \"DTL-H30102\", 4 },\n    { \"b7fa11e87d51752a98b38e3e691cbf17\", \"5.0 09/02/00 E\", \"PAL-E\", \"SCPH-30002/SCPH-30003/SCPH-30004\", 1 },\n    { \"f63bc530bd7ad7c026fcd6f7bd0d9525\", \"5.0 10/27/00 J\", \"NTSC-J\", \"SCPH-18000 (GH-003)\", 1 },\n    { \"cee06bd68c333fc5768244eae77e4495\", \"5.0 10/27/00 J\", \"NTSC-J\", \"SCPH-18000 (GH-008)\", 1 },\n    { \"0bf988e9c7aaa4c051805b0fa6eb3387\", \"5.0 12/28/00 A\", \"N/A\", \"DTL-H30101\", 4 },\n    { \"8accc3c49ac45f5ae2c5db0adc854633\", \"5.0 12/28/00 A\", \"NTSC-U/C\", \"SCPH-30001/SCPH-35001\", 1 },\n    { \"6f9a6feb749f0533aaae2cc45090b0ed\", \"5.0 12/28/00 E\", \"N/A\", \"DTL-H30102\", 4 },\n    { \"838544f12de9b0abc90811279ee223c8\", \"5.0 12/28/00 E\", \"PAL-E\", \"SCPH-30002/SCPH-30003/SCPH-30004/SCPH-35002/SCPH-35003/SCPH-35004\", 1 },\n    { \"bb6bbc850458fff08af30e969ffd0175\", \"5.0 01/18/01 J\", \"N/A\", \"DTL-H30000\", 4 },\n    { \"815ac991d8bc3b364696bead3457de7d\", \"5.0 01/18/01 J\", \"NTSC-J\", \"SCPH-30000/SCPH-35000\", 1 },\n    { \"b107b5710042abe887c0f6175f6e94bb\", \"5.0 04/27/01 A\", \"NTSC-U/C\", \"SCPH-30001R\", 1 },\n    { \"ab55cceea548303c22c72570cfd4dd71\", \"5.0 04/27/01 J\", \"NTSC-J\", \"SCPH-30000\", 1 },\n    { \"18bcaadb9ff74ed3add26cdf709fff2e\", \"5.0 07/04/01 A\", \"NTSC-U/C\", \"SCPH-30001R\", 1 },\n    { \"491209dd815ceee9de02dbbc408c06d6\", \"5.0 07/04/01 E\", \"PAL-E\", \"SCPH-30002R/SCPH-30003R/SCPH-30004R\", 1 },\n    { \"7200a03d51cacc4c14fcdfdbc4898431\", \"5.0 10/04/01 A\", \"NTSC-U/C\", \"SCPH-30001R\", 1 },\n    { \"8359638e857c8bc18c3c18ac17d9cc3c\", \"5.0 10/04/01 E\", \"PAL-E\", \"SCPH-30002R/SCPH-30003R/SCPH-30004R\", 1 },\n    { \"28922c703cc7d2cf856f177f2985b3a9\", \"5.0 10/04/01 E\", \"PAL-E\", \"SCPH-30002R/SCPH-30003R/SCPH-30004R\", 1 },\n    { \"352d2ff9b3f68be7e6fa7e6dd8389346\", \"5.0 07/30/01 J\", \"NTSC-J\", \"SCPH-30005R/SCPH-30006R/SCPH-30007R\", 1 },\n    { \"d5ce2c7d119f563ce04bc04dbc3a323e\", \"5.0 02/07/02 A\", \"NTSC-U\", \"SCPH-39001\", 1 },\n    { \"0d2228e6fd4fb639c9c39d077a9ec10c\", \"5.0 03/19/02 E\", \"PAL-E\", \"SCPH-39002/SCPH-39003/SCPH-39004\", 1 },\n    { \"72da56fccb8fcd77bba16d1b6f479914\", \"5.0 04/26/02 J\", \"NTSC-J\", \"SCPH-37000/SCPH-39000\", 1 },\n    { \"5b1f47fbeb277c6be2fccdd6344ff2fd\", \"5.0 04/26/02 E\", \"PAL-E\", \"SCPH-39008\", 1 },\n    { \"315a4003535dfda689752cb25f24785c\", \"5.0 04/26/02 J\", \"NTSC-J\", \"SCPH-39005/SCPH-39006/SCPH-39007\", 1 },\n    { \"52cca0058626569c7a9699838baab2d8\", \"5.0 11/19/02 ?\", \"N/A\", \"Namco System 246\", 10 },\n    { \"312ad4816c232a9606e56f946bc0678a\", \"5.0 02/06/03 J\", \"NTSC-J\", \"SCPH-50000/SCPH-55000\", 2 },\n    { \"666018ffec65c5c7e04796081295c6c7\", \"5.0 02/27/03 E\", \"N/A\", \"DTL-H50002\", 4 },\n    { \"6e69920fa6eef8522a1d688a11e41bc6\", \"5.0 02/27/03 E\", \"PAL-E\", \"SCPH-50002/SCPH-50003/SCPH-50004\", 2 },\n    { \"eb960de68f0c0f7f9fa083e9f79d0360\", \"5.0 03/25/03 A\", \"N/A\", \"DTL-H50001\", 4 },\n    { \"8aa12ce243210128c5074552d3b86251\", \"5.0 03/25/03 A\", \"NTSC-U/C\", \"SCPH-50001\", 2 },\n    { \"240d4c5ddd4b54069bdc4a3cd2faf99d\", \"5.0 02/24/03 J\", \"N/A\", \"DTL-H50009\", 4 },\n    { \"1c6cd089e6c83da618fbf2a081eb4888\", \"5.0 10/28/03 J\", \"NTSC-J\", \"DESR-5000/DESR-5100/DESR-7000/DESR-7100 (PSX1)\", 3 },\n    { \"1999f40e409756522f9e70fea308a020\", \"5.0 10/31/03 T\", \"N/A\", \"DTL-T10000\", 5 },\n    { \"463d87789c555a4a7604e97d7db545d1\", \"5.0 06/23/03 J\", \"NTSC-J\", \"SCPH-55000\", 2 },\n    { \"35461cecaa51712b300b2d6798825048\", \"5.0 06/23/03 A\", \"NTSC-U/C\", \"SCPH-50001/SCPH-50010\", 2 },\n    { \"bd6415094e1ce9e05daabe85de807666\", \"5.0 06/23/03 E\", \"PAL-E\", \"SCPH-50002/SCPH-50003/SCPH-50004\", 2 },\n    { \"2e70ad008d4ec8549aada8002fdf42fb\", \"5.0 06/23/03 J\", \"NTSC-J\", \"SCPH-50005/SCPH-50006/SCPH-50007\", 2 },\n    { \"b53d51edc7fc086685e31b811dc32aad\", \"5.0 06/23/03 E\", \"PAL-E\", \"SCPH-50008\", 2 },\n    { \"1b6e631b536247756287b916f9396872\", \"5.0 06/23/03 J\", \"NTSC-J\", \"SCPH-50009\", 2 },\n    { \"00da1b177096cfd2532c8fa22b43e667\", \"5.0 08/22/03 J\", \"NTSC-J\", \"SCPH-50000/Konami Python 2\", 2 },\n    { \"afde410bd026c16be605a1ae4bd651fd\", \"5.0 08/22/03 E\", \"PAL-E\", \"SCPH-50004\", 2 },\n    { \"81f4336c1de607dd0865011c0447052e\", \"5.0 03/29/04 A\", \"NTSC-U/C\", \"SCPH-50011\", 2 },\n    { \"a58676c6bd79229bda967d07b4ec2e16\", \"5.0 05/19/04 ?\", \"N/A\", \"Namco System 256\", 11 },\n    { \"0eee5d1c779aa50e94edd168b4ebf42e\", \"5.0 06/14/04 J\", \"NTSC-J\", \"SCPH-70000\", 2 },\n    { \"d333558cc14561c1fdc334c75d5f37b7\", \"5.0 06/14/04 A\", \"NTSC-U/C\", \"SCPH-70001/SCPH-70011/SCPH-70012\", 2 },\n    { \"dc752f160044f2ed5fc1f4964db2a095\", \"5.0 06/14/04 E\", \"PAL-E\", \"SCPH-70002/SCPH-70003/SCPH-70004/SCPH-70008\", 2 },\n    { \"63ead1d74893bf7f36880af81f68a82d\", \"5.0 06/14/04 E\", \"N/A\", \"DTL-H70002\", 4 },\n    { \"3e3e030c0f600442fa05b94f87a1e238\", \"5.0 06/14/04 J\", \"NTSC-J\", \"SCPH-70005/SCPH-70006/SCPH-70007\", 2 },\n    { \"1ad977bb539fc9448a08ab276a836bbc\", \"5.0 09/17/04 J\", \"NTSC-J\", \"DESR-5500/DESR-5700/DESR-7500/DESR-7700 (PSX2)\", 3 },\n    { \"eb4f40fcf4911ede39c1bbfe91e7a89a\", \"5.0 06/20/05 J\", \"NTSC-J\", \"SCPH-75000\", 2 },\n    { \"9959ad7a8685cad66206e7752ca23f8b\", \"5.0 06/20/05 A\", \"N/A\", \"DTL-H75000A\", 4 },\n    { \"929a14baca1776b00869f983aa6e14d2\", \"5.0 06/20/05 A\", \"NTSC-U/C\", \"SCPH-75001/SCPH-75010\", 2 },\n    { \"573f7d4a430c32b3cc0fd0c41e104bbd\", \"5.0 06/20/05 E\", \"PAL-E\", \"SCPH-75002/SCPH-75003/SCPH-75004/SCPH-75008\", 2 },\n    { \"df63a604e8bff5b0599bd1a6c2721bd0\", \"5.0 06/20/05 J\", \"NTSC-J\", \"SCPH-75006\", 2 },\n    { \"5b1ba4bb914406fae75ab8e38901684d\", \"5.0 02/10/06 J\", \"NTSC-J\", \"SCPH-77000\", 2 },\n    { \"cb801b7920a7d536ba07b6534d2433ca\", \"5.0 02/10/06 A\", \"NTSC-U/C\", \"SCPH-77001/SCPH-77010\", 2 },\n    { \"af60e6d1a939019d55e5b330d24b1c25\", \"5.0 02/10/06 E\", \"PAL-E\", \"SCPH-77002/SCPH-77003/SCPH-77004/SCPH-77008\", 2 },\n    { \"549a66d0c698635ca9fa3ab012da7129\", \"5.0 02/10/06 J\", \"NTSC-J\", \"SCPH-77006/SCPH-77007\", 2 },\n    { \"5de9d0d730ff1e7ad122806335332524\", \"5.0 09/05/06 J\", \"NTSC-J\", \"SCPH-79000/SCPH-90000\", 2 },\n    { \"21fe4cad111f7dc0f9af29477057f88d\", \"5.0 09/05/06 A\", \"N/A\", \"DTL-H90000\", 4 },\n    { \"40c11c063b3b9409aa5e4058e984e30c\", \"5.0 09/05/06 A\", \"NTSC-U/C\", \"SCPH-79001/SCPH-79010/SCPH-90001\", 2 },\n    { \"80bbb237a6af9c611df43b16b930b683\", \"5.0 09/05/06 E\", \"PAL-E\", \"SCPH-79002/SCPH-79003/SCPH-79004/SCPH-79008/SCPH-90002/SCPH-90003/SCPH-90004\", 2 },\n    { \"c37bce95d32b2be480f87dd32704e664\", \"5.0 09/05/06 J\", \"NTSC-J\", \"SCPH-79006/SCPH-79007/SCPH-90006/SCPH-90007\", 2 },\n    { \"80ac46fa7e77b8ab4366e86948e54f83\", \"5.0 02/20/08 J\", \"NTSC-J\", \"SCPH-90000\", 2 },\n    { \"21038400dc633070a78ad53090c53017\", \"5.0 02/20/08 A\", \"NTSC-U/C\", \"SCPH-90001/SCPH-90010\", 2 },\n    { \"dc69f0643a3030aaa4797501b483d6c4\", \"5.0 02/20/08 E\", \"PAL-E\", \"SCPH-90002/SCPH-90003/SCPH-90004/SCPH-90008\", 2 },\n    { \"30d56e79d89fbddf10938fa67fe3f34e\", \"5.0 02/20/08 J\", \"NTSC-J\", \"SCPH-90005/SCPH-90006/SCPH-90007\", 2 },\n    { \"93ea3bcee4252627919175ff1b16a1d9\", \"5.0 04/15/10 E\", \"PAL-E\", \"KDL-22PX300 (Sony Bravia TV) (Europe)\", 2 },\n    { \"d3e81e95db25f5a86a7b7474550a2155\", \"5.0 04/15/10 J\", \"NTSC-J\", \"KDL-22PX300 (Sony Bravia TV)\", 2 },\n    { \"cc4b9cea0fdb3d2506173668a2a88305\", \"?.? \\?\\?/\\?\\?/?? ?\", \"N/A\", \"Namco System 147\", 8 },\n    { \"73d4ba0f0a6fb84a151b89fde468aa74\", \"?.? \\?\\?/\\?\\?/?? ?\", \"N/A\", \"Namco System 147B\", 8 },\n    { \"860c13259a548c7ff07b67157928b076\", \"?.? \\?\\?/\\?\\?/?? ?\", \"N/A\", \"Namco System 148\", 9 }\n};\n\nstatic const struct ps2_rom_info rom1_info_table[] = {\n    { \"31a671627b9bf2d88f5e3f6680941fa6\", \"1.10U\" },\n    { \"22080eed26576f4e2282c905dc6e0a4b\", \"1.20E\" },\n    { \"20a4e401b9e7885e25f1c31f6bfcbe0c\", \"1.20U\" },\n    { \"a1a15b62cef142575faaea17fb23dbd1\", \"1.30E\" },\n    { \"567fe068711a9e5914e836cb650600af\", \"1.30U\" },\n    { \"8afc4544e572842a6bb301ad92dc0c02\", \"2.00J\" },\n    { \"d5118e3979eb2a3814ebbfa32825e7b0\", \"2.10E\" },\n    { \"f0b2e6b7f6d06561e230a5b85e5d1bf9\", \"2.10J\" },\n    { \"d0f79251699fdeff073a7c2365d0c526\", \"2.10U\" },\n    { \"32abbe7ab7c1b72d5ffc24d4963bd6c6\", \"2.12G\" },\n    { \"4499f6303d05d4caeb289c2344ea3469\", \"2.12U\" },\n    { \"6bdb45a952f697f367cd1646cdf09235\", \"2.13E\" },\n    { \"d609f69d9e3ef236f6e2bf0a80762b6f\", \"2.15G\" },\n    { \"f40466438d83a02c1eecba3efff20b6a\", \"3.00E\" },\n    { \"e414c981647883e33f17642da827a739\", \"3.00U\" },\n    { \"6bbd2f348c585fdd645900f6ec75f2c7\", \"3.02C\" },\n    { \"503115717429b64e19fa6103e3fa5a35\", \"3.02E\" },\n    { \"2ac40eec790adecf4bc5ee1090a27676\", \"3.02U\" },\n    { \"a2b55c44a3c3eec0abe5647cfb6a6493\", \"3.10\" },\n    { \"79b4880006769a1af9b0a6c7302cc18d\", \"3.11\" }\n};\n\nstatic const struct ps2_rom_info unknown = {\n    \"00000000000000000000000000000000\",\n    \"Unknown\",\n    \"Unknown\",\n    \"Unknown\",\n    2\n};\n\nstruct ps2_rom_info ps2_rom0_search(uint8_t* rom, size_t size) {\n    struct md5_context ctx;\n    char buf[33];\n\n    md5_init(&ctx);\n    md5_update(&ctx, rom, size);\n    md5_finalize(&ctx);\n\n    for (int i = 0; i < 16; i++) {\n        sprintf(&buf[i * 2], \"%02x\", ctx.digest[i]);\n    }\n\n    for (size_t i = 0; i < sizeof(rom0_info_table) / sizeof(rom0_info_table[0]); i++) {\n        if (strncmp(buf, rom0_info_table[i].md5hash, 32) == 0) {\n            return rom0_info_table[i];\n        }\n    }\n\n    struct ps2_rom_info info;\n    \n    info = unknown;\n\n    memcpy(info.md5hash, buf, 33);\n\n    return info;\n}\n\nstruct ps2_rom_info ps2_rom1_search(uint8_t* rom, size_t size) {\n    struct md5_context ctx;\n    char buf[33];\n\n    md5_init(&ctx);\n    md5_update(&ctx, rom, size);\n    md5_finalize(&ctx);\n\n    for (int i = 0; i < 16; i++) {\n        sprintf(&buf[i * 2], \"%02x\", ctx.digest[i]);\n    }\n\n    for (size_t i = 0; i < sizeof(rom1_info_table) / sizeof(rom1_info_table[0]); i++) {\n        if (strncmp(buf, rom1_info_table[i].md5hash, 32) == 0) {\n            return rom1_info_table[i];\n        }\n    }\n\n    struct ps2_rom_info info;\n    \n    info = unknown;\n\n    memcpy(info.md5hash, buf, 33);\n\n    return info;\n}\n\nint ps2_rom0_is_valid(uint8_t* rom, size_t size) {\n    struct ps2_rom_info info = ps2_rom0_search(rom, size);\n\n    return strcmp(info.version, \"Unknown\") != 0;\n}\n\nint ps2_rom1_is_valid(uint8_t* rom, size_t size) {\n    struct ps2_rom_info info = ps2_rom1_search(rom, size);\n\n    return strcmp(info.version, \"Unknown\") != 0;\n}"
  },
  {
    "path": "src/rom.h",
    "content": "#ifndef ROM_H\n#define ROM_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stddef.h>\n\nstruct ps2_rom_info {\n    char md5hash[33];\n    const char* version;\n    const char* region;\n    const char* model;\n    int system;\n};\n\nstruct ps2_rom_info ps2_rom0_search(uint8_t* rom, size_t size);\nstruct ps2_rom_info ps2_rom1_search(uint8_t* rom, size_t size);\nint ps2_rom0_is_valid(uint8_t* rom, size_t size);\nint ps2_rom1_is_valid(uint8_t* rom, size_t size);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/s14x/aiboard.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"aiboard.h\"\n\nstruct s14x_aiboard* s14x_aiboard_create(void) {\n    return malloc(sizeof(struct s14x_aiboard));\n}\n\nint s14x_aiboard_init(struct s14x_aiboard* aiboard) {\n    memset(aiboard, 0, sizeof(struct s14x_aiboard));\n\n    aiboard->version = 0x0104;\n}\n\nvoid s14x_aiboard_destroy(struct s14x_aiboard* aiboard) {\n    free(aiboard);\n}\n\nvoid s14x_aiboard_handle_packet(void* udata, struct link_packet* in, struct link_packet* out) {\n    struct s14x_aiboard* aiboard = (struct s14x_aiboard*)udata;\n\n    // if (cp == 0x38) {\n    //     link->ram[addr+0] = node;\n    //     link->ram[addr+1] = 0; // Broadcast\n    //     link->ram[addr+2] = 0x38;\n    //     link->ram[addr+3] = 0;\n    //     link->ram[addr+0x38] = 0x20;\n    //     // link->ram[addr+0x39] = link->ram[0x79];\n    //     // link->ram[addr+0x3a] = link->ram[0x7a];\n    //     // link->ram[addr+0x3b] = link->ram[0x7b];\n    //     // link->ram[addr+0x3c] = link->ram[0x7c];\n    //     // link->ram[addr+0x3d] = link->ram[0x7d];\n    //     // link->ram[addr+0x3e] = link->ram[0x7e];\n    //     link->ram[addr+0x3f] = 0;\n\n    //     // for (int i = 0; i < 7; i++)\n    //     //     link->ram[addr+0x3f] += link->ram[addr+0x38+i];\n\n    //     ps2_iop_intc_irq(link->intc, IOP_INTC_DEV9);\n\n    //     return;\n    // }\n}"
  },
  {
    "path": "src/s14x/aiboard.h",
    "content": "#ifndef S14X_AIBOARD_H\n#define S14X_AIBOARD_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdio.h>\n\n#include \"link.h\"\n\nstruct s14x_aiboard {\n    uint16_t version;\n};\n\nstruct s14x_aiboard* s14x_aiboard_create(void);\nint s14x_aiboard_init(struct s14x_aiboard* aiboard);\nvoid s14x_aiboard_destroy(struct s14x_aiboard* aiboard);\n\nvoid s14x_aiboard_handle_packet(void* udata, struct link_packet* in, struct link_packet* out);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/s14x/ioboard.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"ioboard.h\"\n\nstruct s14x_ioboard* s14x_ioboard_create(void) {\n    return malloc(sizeof(struct s14x_ioboard));\n}\n\nint s14x_ioboard_init(struct s14x_ioboard* ioboard, int mode) {\n    memset(ioboard, 0, sizeof(struct s14x_ioboard));\n\n    ioboard->version = 0x0104;\n    ioboard->switches = 0xffff;\n    ioboard->mode = mode;\n}\n\nvoid s14x_ioboard_destroy(struct s14x_ioboard* ioboard) {\n    free(ioboard);\n}\n\nuint8_t link_calculate_checksum(struct link_packet* packet) {\n    uint8_t checksum = 0;\n\n    for (int i = 0; i < sizeof(packet->data); i++) {\n        checksum += packet->data[i];\n    }\n\n    return checksum;\n}\n\nvoid ioboard_process_ic_card(struct link_packet* in, struct link_packet* out) {\n    uint8_t channel = in->data[0];\n    uint8_t size = in->data[1];\n    uint8_t cmd = in->data[5];\n\n    const uint8_t base = 4;\n\n    out->data[base + 0] = 0x02;\n    out->data[base + 1] = cmd; // Command ID\n\n    int out_size = 0;\n\n    switch (cmd) {\n        case 0x78: {\n            out_size = 6;\n\n            out->data[base + 3] = 0x00; // Command result?\n            out->data[base + 4] = 0x00;\n            out->data[base + 5] = 0xFF;\n        } break;\n\n        case 0x7A: {\n            out_size = 4;\n\n            out->data[base + 3] = 0x00; // Command result?\n        } break;\n\n        case 0x7B: {\n            out_size = 6;\n\n            out->data[base + 3] = 0x00; // Command result? (0x03 -> No IC card)\n            out->data[base + 4] = 0x00; // ??\n            out->data[base + 5] = 0x00; // ??\n        } break;\n\n        case 0x80: {\n            out_size = 4;\n\n            out->data[base + 3] = 0x0D; // Command result?\n        } break;\n\n        case 0x9F: {\n            out_size = 4;\n\n            out->data[base + 3] = 0x00; // Command result?\n        } break;\n\n        case 0xA7: {\n            out_size = 4;\n\n            out->data[base + 3] = 0x00; // Command result?\n        } break;\n\n        case 0xAC: {\n            // Some key stuff (parity)? (game expects 8 bytes in payload)\n            out_size = 12;\n\n            out->data[base + 3] = 0x00; // Command result?\n            out->data[base + 4] = 0xAA;\n            out->data[base + 5] = 0xAA;\n            out->data[base + 6] = 0xAA;\n            out->data[base + 7] = 0xAA;\n            out->data[base + 8] = 0x55;\n            out->data[base + 9] = 0x55;\n            out->data[base + 10] = 0x55;\n            out->data[base + 11] = 0x55;\n        } break;\n\n        case 0xAF: {\n            // More key stuff (checksum)? (game expects 16 bytes in payload)\n            out_size = 20;\n\n            out->data[base + 3] = 0x00; //Command result?\n        } break;\n\n        default: {\n            printf(\"ioboard: Unknown IC card command %02x\", cmd);\n        } break;\n    }\n\n    uint8_t out_size_with_parity = out_size + 1;\n\n    out->data[0] = channel;\n    out->data[1] = out_size_with_parity; // This needs to be at least 5\n    out->data[base + 2] = out_size_with_parity - 5;\n\n    uint8_t parity = 0;\n\n    for (int i = 1; i < out_size; i++) {\n        parity ^= out->data[base + i];\n    }\n\n    out->data[base + out_size] = parity;\n}\n\nvoid s14x_ioboard_handle_packet(void* udata, struct link_packet* in, struct link_packet* out) {\n    struct s14x_ioboard* ioboard = (struct s14x_ioboard*)udata;\n\n    // Setup header (cmd, cp, unk03, seq_number)\n    *out = *in;\n\n    out->src_node = in->dst_node;\n    out->dst_node = in->src_node;\n\n    switch (in->cmd) {\n        // GetPCBInformation\n        case 0x0f: {\n            out->data[0] = 0;\n            out->data[1] = 0;\n            out->data[2] = ioboard->version >> 8;\n            out->data[3] = ioboard->version & 0xff;\n        } break;\n\n        // GetSystemSwitches\n        case 0x10: {\n            if (ioboard->mode == 0) {\n                out->data[0] = ioboard->switches & 0xff;\n                out->data[1] = ioboard->switches >> 8;\n            } else {\n                out->data[0] = in->data[0];\n            }\n        } break;\n\n        case 0x11: {\n            out->data[0x32] = 0x20;\n            out->data[0x36] = ioboard->buttons & 0xff;\n            out->data[0x37] = ioboard->buttons >> 8;\n        } break;\n\n        case 0x31: {\n            uint8_t channel = in->data[0];\n            uint8_t size = in->data[1];\n\n            if (channel == 0) {\n                // Barcode Reader\n                out->data[0] = channel;\n                out->data[1] = 0x6;\n                out->data[4 + 0] = 0x02;\n                out->data[4 + 1] = 0x02;\n                out->data[4 + 2] = 0x80;\n                out->data[4 + 3] = 0x02;\n                out->data[4 + 4] = 0x03;\n                out->data[4 + 5] = 0x03;\n            } else if (channel == 2) {\n                ioboard_process_ic_card(in, out);\n            }\n        } break;\n\n        // Used by Animal Kaiser\n        case 0x41: {\n            out->data[0] = 0;\n        } break;\n\n        // Used by Animal Kaiser\n        case 0x51: {\n            out->data[0] = 0;\n        } break;\n\n        case 0x0d: // ?\n        case 0x18: // Switch\n        case 0x38: // SCI\n        case 0x39: // GetSwitches\n        case 0x48: // CoinSensor\n        case 0x58: // MechanicalSensor\n        case 0xa5: { // Reset\n            out->data[0] = in->data[0];\n        } break;\n        default: {\n            printf(\"s14x_ioboard: Unhandled command %02x\\n\", in->cmd);\n\n            out->data[0] = in->data[0];\n        } break;\n    }\n\n    out->checksum = link_calculate_checksum(out);\n}\n\nvoid s14x_ioboard_press_switch(struct s14x_ioboard* ioboard, uint16_t mask) {\n    ioboard->switches &= ~mask;\n}\n\nvoid s14x_ioboard_release_switch(struct s14x_ioboard* ioboard, uint16_t mask) {\n    ioboard->switches |= mask;\n}\n\nvoid s14x_ioboard_press_button(struct s14x_ioboard* ioboard, uint16_t mask) {\n    ioboard->buttons |= mask;\n}\n\nvoid s14x_ioboard_release_button(struct s14x_ioboard* ioboard, uint16_t mask) {\n    ioboard->buttons &= ~mask;\n}"
  },
  {
    "path": "src/s14x/ioboard.h",
    "content": "/*  s14x/ioboard.h - Namco System 147/148 I/O board emulation\n\n    Notes: The I/O board is connected to the main board via the CircLink\n           network at node 2. The main board sends ARCNET packets with\n           commands through CircLink and the I/O board responds with data.\n\n           Packet format\n           -------------\n           Offset   Description\n           00h      Source node (e.g. 1 for the main board)\n           01h      Destination node\n           02h      Unknown (driver will only accept 04h)\n           03h      Unknown (driver will only accept 00h)\n           04h      Sequence number\n                    Note: This byte needs to contain the same sequence number\n                    that was sent by the main board.\n           05h      Command\n           06h-3Eh  Data\n           3Fh      Checksum (sum of 06h-3Eh)\n\n           I/O board commands\n           ------------------\n           Command  Description\n           0Dh      Unknown (?)\n           0Fh      Get PCB information (returns version 0000:0104h)\n           10h      Depends on the board, on pacmanap this returns the\n                    state of the switches\n           18h      Switch (?)\n           38h      SCI (?)\n           39h      Get switches (?)\n           48h      Coin sensor (?)\n           58h      Mechanical sensor (?)\n           A5h      Reset\n\n           There are actually more commands but we stub most of them by\n           returning the same data that was sent by the main board.\n*/\n\n#ifndef S14X_IOBOARD_H\n#define S14X_IOBOARD_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdio.h>\n\n#include \"link.h\"\n\n#define S14X_IOBOARD_SW_DOWN     0x0001\n#define S14X_IOBOARD_SW_UP       0x0002\n#define S14X_IOBOARD_SW_ENTER    0x0004\n#define S14X_IOBOARD_SW_TEST     0x0008\n#define S14X_IOBOARD_SW_SERVICE  0x0020\n#define S14X_IOBOARD_SW_P4_START 0x0100\n#define S14X_IOBOARD_SW_P3_START 0x0200\n#define S14X_IOBOARD_SW_P2_START 0x0400\n#define S14X_IOBOARD_SW_P1_START 0x0800\n\n#define S14X_IOBOARD_BT_P4_UP    0x0001\n#define S14X_IOBOARD_BT_P4_DOWN  0x0002\n#define S14X_IOBOARD_BT_P4_RIGHT 0x0004\n#define S14X_IOBOARD_BT_P4_LEFT  0x0008\n#define S14X_IOBOARD_BT_P2_UP    0x0010\n#define S14X_IOBOARD_BT_P2_DOWN  0x0020\n#define S14X_IOBOARD_BT_P2_RIGHT 0x0040\n#define S14X_IOBOARD_BT_P2_LEFT  0x0080\n#define S14X_IOBOARD_BT_P3_UP    0x0100\n#define S14X_IOBOARD_BT_P3_DOWN  0x0200\n#define S14X_IOBOARD_BT_P3_RIGHT 0x0400\n#define S14X_IOBOARD_BT_P3_LEFT  0x0800\n#define S14X_IOBOARD_BT_P1_UP    0x1000\n#define S14X_IOBOARD_BT_P1_DOWN  0x2000\n#define S14X_IOBOARD_BT_P1_RIGHT 0x4000\n#define S14X_IOBOARD_BT_P1_LEFT  0x8000\n\nstruct s14x_ioboard {\n    uint16_t version;\n    uint16_t switches;\n    uint16_t buttons;\n\n    int mode;\n};\n\nstruct s14x_ioboard* s14x_ioboard_create(void);\nint s14x_ioboard_init(struct s14x_ioboard* ioboard, int mode);\nvoid s14x_ioboard_destroy(struct s14x_ioboard* ioboard);\n\nvoid s14x_ioboard_press_switch(struct s14x_ioboard* ioboard, uint16_t mask);\nvoid s14x_ioboard_release_switch(struct s14x_ioboard* ioboard, uint16_t mask);\nvoid s14x_ioboard_press_button(struct s14x_ioboard* ioboard, uint16_t mask);\nvoid s14x_ioboard_release_button(struct s14x_ioboard* ioboard, uint16_t mask);\n\nvoid s14x_ioboard_handle_packet(void* udata, struct link_packet* in, struct link_packet* out);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/s14x/link.c",
    "content": "#include \"link.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <ctype.h>\n\n#define printf(fmt, ...)(0)\n\nconst char* g_reg_names[] = {\n    \"S14X_LINK_PAD00\",\n    \"S14X_LINK_COMR0\",\n    \"S14X_LINK_PAD02\",\n    \"S14X_LINK_COMR1\",\n    \"S14X_LINK_PAD04\",\n    \"S14X_LINK_COMR2\",\n    \"S14X_LINK_PAD06\",\n    \"S14X_LINK_COMR3\",\n    \"S14X_LINK_PAD08\",\n    \"S14X_LINK_COMR4\",\n    \"S14X_LINK_PAD0A\",\n    \"S14X_LINK_COMR5\",\n    \"S14X_LINK_PAD0C\",\n    \"S14X_LINK_COMR6\",\n    \"S14X_LINK_PAD0E\",\n    \"S14X_LINK_COMR7\",\n    \"S14X_LINK_NSTH\",\n    \"S14X_LINK_NSTL\",\n    \"S14X_LINK_STSH\",\n    \"S14X_LINK_STSL\",\n    \"S14X_LINK_MSKH\",\n    \"S14X_LINK_MSKL\",\n    \"S14X_LINK_PAD16\",\n    \"S14X_LINK_ECCMD\",\n    \"S14X_LINK_MRSID\",\n    \"S14X_LINK_RSID\",\n    \"S14X_LINK_PAD1A\",\n    \"S14X_LINK_SSID\",\n    \"S14X_LINK_RXFHH\",\n    \"S14X_LINK_RXFHL\",\n    \"S14X_LINK_RXFLH\",\n    \"S14X_LINK_RXFLL\",\n    \"S14X_LINK_PAD20\",\n    \"S14X_LINK_CMID\",\n    \"S14X_LINK_MODEH\",\n    \"S14X_LINK_MODEL\",\n    \"S14X_LINK_CARRYH\",\n    \"S14X_LINK_CARRYL\",\n    \"S14X_LINK_RXMHH\",\n    \"S14X_LINK_RXMHL\",\n    \"S14X_LINK_RXMLH\",\n    \"S14X_LINK_RXMLL\",\n    \"S14X_LINK_PAD2A\",\n    \"S14X_LINK_MAXID\",\n    \"S14X_LINK_PAD2C\",\n    \"S14X_LINK_NID\",\n    \"S14X_LINK_PAD2E\",\n    \"S14X_LINK_PS\",\n    \"S14X_LINK_PAD30\",\n    \"S14X_LINK_CKP\",\n    \"S14X_LINK_NSTDIFH\",\n    \"S14X_LINK_NSTDIFL\",\n    \"S14X_LINK_WATCHDOG_FLAG\"\n};\n\nstruct s14x_link* s14x_link_create(void) {\n    return malloc(sizeof(struct s14x_link));\n}\n\nvoid s14x_link_init(struct s14x_link* link, struct ps2_iop_intc* intc, struct sched_state* sched) {\n    memset(link, 0, sizeof(struct s14x_link));\n\n    // ARCNET CORE interrupt (possibly sent after INIMODE was set?)\n    // TA bit set\n    link->stsl = 1;\n\n    // RECON bit set\n    link->comr0 = 4;\n\n    link->intc = intc;\n    link->sched = sched;\n}\n\nvoid link_send_irq(struct s14x_link* link, uint16_t irq) {\n    link->stsl |= irq & 0xff;\n    link->stsh |= (irq >> 8) & 0xff;\n\n    ps2_iop_intc_irq(link->intc, IOP_INTC_DEV9);\n}\n\nuint64_t s14x_link_read(struct s14x_link* link, uint32_t addr) {\n    uint32_t r = 0;\n\n    switch (addr) {\n        case S14X_LINK_PAD00: r = link->pad00; break;\n        case S14X_LINK_COMR0: r = link->comr0; break;\n        case S14X_LINK_PAD02: r = link->pad02; break;\n        case S14X_LINK_COMR1: r = link->comr1; break;\n        case S14X_LINK_PAD04: r = link->pad04; break;\n        case S14X_LINK_COMR2: r = link->comr2; break;\n        case S14X_LINK_PAD06: r = link->pad06; break;\n        case S14X_LINK_COMR3: r = link->comr3; break;\n        case S14X_LINK_PAD08: r = link->pad08; break;\n        case S14X_LINK_COMR4: {\n            uint32_t addr = link->ramadr;\n\n            if (link->comr2 & S14X_LINK_COMR2_AUTOINC) {\n                if (link->comr2 & S14X_LINK_COMR2_WRAPAR) {\n                    link->ramadr = (link->ramadr & 0x3c0) | ((link->ramadr + 1) & 0x3f);\n                } else {\n                    link->ramadr = (link->ramadr + 1) & 0x3ff;\n                }\n            }\n\n            // printf(\"s14x_link: Read RAM[%04x] = %02x\\n\", addr, link->ram[addr]);\n\n            r = link->ram[addr];\n        } break;\n        case S14X_LINK_PAD0A: r = link->pad0a; break;\n        case S14X_LINK_COMR5: r = link->comr5; break;\n        case S14X_LINK_PAD0C: r = link->pad0c; break;\n        case S14X_LINK_COMR6: r = link->comr6; break;\n        case S14X_LINK_PAD0E: r = link->pad0e; break;\n        case S14X_LINK_COMR7: r = link->comr7; break;\n        case S14X_LINK_NSTH: r = link->nsth; break;\n        case S14X_LINK_NSTL: r = link->nstl; break;\n        case S14X_LINK_STSH: r = link->stsh; break;\n        case S14X_LINK_STSL: r = link->stsl; break;\n        case S14X_LINK_MSKH: r = link->mskh; break;\n        case S14X_LINK_MSKL: r = link->mskl; break;\n        case S14X_LINK_PAD16: r = link->pad16; break;\n        case S14X_LINK_ECCMD: r = link->eccmd; break;\n        case S14X_LINK_MRSID: r = link->mrsid; break;\n        case S14X_LINK_RSID: r = link->rsid; break;\n        case S14X_LINK_PAD1A: r = link->pad1a; break;\n        case S14X_LINK_SSID: r = link->ssid; break;\n        case S14X_LINK_RXFHH: r = link->rxfhh; break;\n        case S14X_LINK_RXFHL: r = link->rxfhl; break;\n        case S14X_LINK_RXFLH: r = link->rxflh; break;\n        case S14X_LINK_RXFLL: r = link->rxfll; break;\n        case S14X_LINK_PAD20: r = link->pad20; break;\n        case S14X_LINK_CMID: r = link->cmid; break;\n        case S14X_LINK_MODEH: r = link->modeh; break;\n        case S14X_LINK_MODEL: r = link->model; break;\n        case S14X_LINK_CARRYH: r = link->carryh; break;\n        case S14X_LINK_CARRYL: r = link->carryl; break;\n        case S14X_LINK_RXMHH: r = link->rxmhh; break;\n        case S14X_LINK_RXMHL: r = link->rxmhl; break;\n        case S14X_LINK_RXMLH: r = link->rxmlh; break;\n        case S14X_LINK_RXMLL: r = link->rxmll; break;\n        case S14X_LINK_PAD2A: r = link->pad2a; break;\n        case S14X_LINK_MAXID: r = link->maxid; break;\n        case S14X_LINK_PAD2C: r = link->pad2c; break;\n        case S14X_LINK_NID: r = link->nid; break;\n        case S14X_LINK_PAD2E: r = link->pad2e; break;\n        case S14X_LINK_PS: r = link->ps; break;\n        case S14X_LINK_PAD30: r = link->pad30; break;\n        case S14X_LINK_CKP: r = link->ckp; break;\n        case S14X_LINK_NSTDIFH: r = link->nstdifh; break;\n        case S14X_LINK_NSTDIFL: r = link->nstdifl; break;\n        case S14X_LINK_WATCHDOG_FLAG: r = link->watchdog_flag; break;\n    }\n\n    if (addr != S14X_LINK_WATCHDOG_FLAG) {\n        printf(\"s14x_link: Read %s (%08x) %08x\\n\", g_reg_names[addr], addr, r);\n    }\n\n    return r;\n}\n\nvoid s14x_link_register_node(struct s14x_link* link, int node, link_packet_handler handler, void* udata) {\n    link->nodes[node].handler = handler;\n    link->nodes[node].udata = udata;\n}\n\n// Note: pacmanap reverse engineering\n//       mynode = 1, maxnodes = 2\n//       Main board is at node 1\n//       I/O board is at node 2\n\n//       pacmanbr\n//       node = 1, maxnodes = 3\n//       Main board is at node 1\n//       I/O board is at node 2\n//       A.I./Standalone board is at node 3\n\nvoid link_recv_reply(void* udata, int overshoot) {\n    struct s14x_link* link = (struct s14x_link*)udata;\n\n    struct link_packet* tx = (struct link_packet*)&link->ram[0x40];\n    struct link_packet* rx = (struct link_packet*)&link->ram[tx->dst_node * 0x40];\n\n    memset(rx, 0, sizeof(struct link_packet));\n\n    // Set TA and TMA bits (transmitter available)\n    link->stsl |= S14X_LINK_STSL_TA;\n    link->comr0 |= S14X_LINK_COMR0_R_TA | S14X_LINK_COMR0_R_TMA;\n\n    struct link_node* node = &link->nodes[tx->dst_node];\n\n    if (!node->handler) {\n        fprintf(stdout, \"s14x_link: Packet sent to disconnected node %d (cmd=%02x cp=%02x)\\n\",\n            tx->dst_node,\n            tx->cmd,\n            tx->cp\n        );\n\n        return;\n    }\n\n    // Set packet pending flag\n    link->rxfll = 1 << tx->dst_node;\n\n    // Get response from the requested node\n    node->handler(node->udata, tx, rx);\n\n    // Send DEV9 IRQ to IOP\n    ps2_iop_intc_irq(link->intc, IOP_INTC_DEV9);\n}\n\nvoid s14x_link_write(struct s14x_link* link, uint32_t addr, uint64_t data) {\n    if (addr != S14X_LINK_WATCHDOG_FLAG) {\n        printf(stdout, \"s14x_link: Write %s (%08x) %08x\\n\", g_reg_names[addr], addr, data);\n    }\n\n    switch (addr) {\n        case S14X_LINK_PAD00: link->pad00 = data; return;\n        case S14X_LINK_COMR0: {\n            link->comr0 &= ~(S14X_LINK_COMR0_R_RECON | S14X_LINK_COMR0_W_TA);\n            link->comr0 |= data & (S14X_LINK_COMR0_R_RECON | S14X_LINK_COMR0_W_TA);\n\n            if (data & 0xf) {\n                link_send_irq(link, S14X_LINK_IRQ_COM);\n            }\n        } return;\n        case S14X_LINK_PAD02: link->pad02 = data; return;\n        case S14X_LINK_COMR1: link->comr1 = data; return;\n        case S14X_LINK_PAD04: link->pad04 = data; return;\n        case S14X_LINK_COMR2: {\n            link->comr2 = data;\n            link->ramadr = (link->ramadr & 0x3f) | ((data & 0xf) << 6);\n        } return;\n        case S14X_LINK_PAD06: link->pad06 = data; return;\n        case S14X_LINK_COMR3: {\n            link->comr3 = data;\n            link->ramadr = (link->ramadr & 0x3c0) | (data & 0x3f);\n        } return;\n        case S14X_LINK_PAD08: link->pad08 = data; return;\n        case S14X_LINK_COMR4: {\n            uint32_t addr = link->ramadr;\n\n            if (link->comr2 & S14X_LINK_COMR2_AUTOINC) {\n                if (link->comr2 & S14X_LINK_COMR2_WRAPAR) {\n                    link->ramadr = (link->ramadr + 1) & 0x3ff;\n                } else {\n                    link->ramadr = (link->ramadr & 0x3c0) | ((link->ramadr + 1) & 0x3f);\n                }\n            }\n\n            // printf(\"s14x_link: Write RAM[%04x] = %02x\\n\", addr, data);\n\n            link->ram[addr] = data;\n        } return;\n        case S14X_LINK_PAD0A: link->pad0a = data; return;\n        case S14X_LINK_COMR5: link->comr5 = data; return;\n        case S14X_LINK_PAD0C: link->pad0c = data; return;\n        case S14X_LINK_COMR6: link->comr6 = data; return;\n        case S14X_LINK_PAD0E: link->pad0e = data; return;\n        case S14X_LINK_COMR7: link->comr7 = data; return;\n        case S14X_LINK_NSTH: link->nsth = data; return;\n        case S14X_LINK_NSTL: link->nstl = data; return;\n        case S14X_LINK_STSH: {\n            link->stsh &= 0x30;\n            link->stsh |= data & 0xcf;\n        } return;\n        case S14X_LINK_STSL: {\n            link->stsl &= 0x09;\n            link->stsl |= data & 0xf6;\n        } return;\n        case S14X_LINK_MSKH: {\n            link->mskh = data;\n        } return;\n        case S14X_LINK_MSKL: {\n            link->mskl = data;\n        } return;\n        case S14X_LINK_PAD16: link->pad16 = data; return;\n        case S14X_LINK_ECCMD: { \n            link->eccmd = data;\n\n            switch (link->eccmd) {\n                case 0x03: {\n                    struct sched_event event;\n\n                    event.callback = link_recv_reply;\n                    event.udata = link;\n                    event.cycles = 10000;\n                    event.name = \"Link reply\";\n\n                    link->stsl &= ~S14X_LINK_STSL_TA;\n                    link->comr0 &= ~(S14X_LINK_COMR0_R_TA | S14X_LINK_COMR0_R_TMA);\n\n                    sched_schedule(link->sched, event);\n                } break;\n\n                case 0x16: {\n                    // Clear RECON bit\n                    link->comr0 &= ~S14X_LINK_COMR0_W_RECON;\n                } break;\n\n                default: {\n                    printf(\"s14x_link: Unhandled EC command %02x\\n\", link->eccmd);\n                } break;\n            }\n        } return;\n        case S14X_LINK_MRSID: link->mrsid = data; return;\n        case S14X_LINK_RSID: link->rsid = data; return;\n        case S14X_LINK_PAD1A: link->pad1a = data; return;\n        case S14X_LINK_SSID: link->ssid = data; return;\n\n        // Writing clears receive flags\n        case S14X_LINK_RXFHH: link->rxfhh &= ~data; return;\n        case S14X_LINK_RXFHL: link->rxfhl &= ~data; return;\n        case S14X_LINK_RXFLH: link->rxflh &= ~data; return;\n        case S14X_LINK_RXFLL: link->rxfll &= ~data; return;\n        case S14X_LINK_PAD20: link->pad20 = data; return;\n        case S14X_LINK_CMID: link->cmid = data; return;\n        case S14X_LINK_MODEH: {\n            // Software reset\n            if (link->modeh & S14X_LINK_MODEH_INIMODE != data & S14X_LINK_MODEH_INIMODE) {\n                link->stsl = 0;\n                link->stsh = 0;\n                link->mskl = 0;\n                link->mskh = 0;\n                link->comr0 = 0;\n                link->comr1 = 0;\n            }\n\n            link->modeh = data;\n        } break;\n        case S14X_LINK_MODEL: link->model = data; return;\n        case S14X_LINK_CARRYH: link->carryh = data; return;\n        case S14X_LINK_CARRYL: link->carryl = data; return;\n        case S14X_LINK_RXMHH: link->rxmhh = data; return;\n        case S14X_LINK_RXMHL: link->rxmhl = data; return;\n        case S14X_LINK_RXMLH: link->rxmlh = data; return;\n        case S14X_LINK_RXMLL: link->rxmll = data; return;\n        case S14X_LINK_PAD2A: link->pad2a = data; return;\n        case S14X_LINK_MAXID: link->maxid = data; return;\n        case S14X_LINK_PAD2C: link->pad2c = data; return;\n        case S14X_LINK_NID: link->nid = data; return;\n        case S14X_LINK_PAD2E: link->pad2e = data; return;\n        case S14X_LINK_PS: link->ps = data; return;\n        case S14X_LINK_PAD30: link->pad30 = data; return;\n        case S14X_LINK_CKP: link->ckp = data; return;\n        case S14X_LINK_NSTDIFH: link->nstdifh = data; return;\n        case S14X_LINK_NSTDIFL: link->nstdifl = data; return;\n        case S14X_LINK_WATCHDOG_FLAG: link->watchdog_flag = data; return;\n    }\n}\n\nvoid s14x_link_destroy(struct s14x_link* link) {\n    free(link);\n}"
  },
  {
    "path": "src/s14x/link.h",
    "content": "struct s14x_link;\n\n#ifndef S14X_LINK_H\n#define S14X_LINK_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdio.h>\n\n#include \"iop/intc.h\"\n#include \"scheduler.h\"\n\n#define S14X_LINK_PAD00 0x00\n#define S14X_LINK_COMR0 0x01\n#define S14X_LINK_PAD02 0x02\n#define S14X_LINK_COMR1 0x03\n#define S14X_LINK_PAD04 0x04\n#define S14X_LINK_COMR2 0x05\n#define S14X_LINK_PAD06 0x06\n#define S14X_LINK_COMR3 0x07\n#define S14X_LINK_PAD08 0x08\n#define S14X_LINK_COMR4 0x09\n#define S14X_LINK_PAD0A 0x0a\n#define S14X_LINK_COMR5 0x0b\n#define S14X_LINK_PAD0C 0x0c\n#define S14X_LINK_COMR6 0x0d\n#define S14X_LINK_PAD0E 0x0e\n#define S14X_LINK_COMR7 0x0f\n#define S14X_LINK_NSTH 0x10\n#define S14X_LINK_NSTL 0x11\n#define S14X_LINK_STSH 0x12\n#define S14X_LINK_STSL 0x13\n#define S14X_LINK_MSKH 0x14\n#define S14X_LINK_MSKL 0x15\n#define S14X_LINK_PAD16 0x16\n#define S14X_LINK_ECCMD 0x17\n#define S14X_LINK_MRSID 0x18\n#define S14X_LINK_RSID 0x19\n#define S14X_LINK_PAD1A 0x1a\n#define S14X_LINK_SSID 0x1b\n#define S14X_LINK_RXFHH 0x1c\n#define S14X_LINK_RXFHL 0x1d\n#define S14X_LINK_RXFLH 0x1e\n#define S14X_LINK_RXFLL 0x1f\n#define S14X_LINK_PAD20 0x20\n#define S14X_LINK_CMID 0x21\n#define S14X_LINK_MODEH 0x22\n#define S14X_LINK_MODEL 0x23\n#define S14X_LINK_CARRYH 0x24\n#define S14X_LINK_CARRYL 0x25\n#define S14X_LINK_RXMHH 0x26\n#define S14X_LINK_RXMHL 0x27\n#define S14X_LINK_RXMLH 0x28\n#define S14X_LINK_RXMLL 0x29\n#define S14X_LINK_PAD2A 0x2a\n#define S14X_LINK_MAXID 0x2b\n#define S14X_LINK_PAD2C 0x2c\n#define S14X_LINK_NID 0x2d\n#define S14X_LINK_PAD2E 0x2e\n#define S14X_LINK_PS 0x2f\n#define S14X_LINK_PAD30 0x30\n#define S14X_LINK_CKP 0x31\n#define S14X_LINK_NSTDIFH 0x32\n#define S14X_LINK_NSTDIFL 0x33\n#define S14X_LINK_WATCHDOG_FLAG 0x34\n\n#define S14X_LINK_COMR2_RDDATA  0x80\n#define S14X_LINK_COMR2_AUTOINC 0x40\n#define S14X_LINK_COMR2_WRAPAR  0x20\n#define S14X_LINK_COMR2_PAGE    0x1f\n\n#define S14X_LINK_COMR0_W_EXCNAK 8\n#define S14X_LINK_COMR0_W_RECON 4\n#define S14X_LINK_COMR0_W_NXTIDERR 2\n#define S14X_LINK_COMR0_W_TA 1\n\n#define S14X_LINK_COMR0_R_POR 0x10\n#define S14X_LINK_COMR0_R_RECON 4\n#define S14X_LINK_COMR0_R_TMA 2\n#define S14X_LINK_COMR0_R_TA 1\n\n#define S14X_LINK_IRQ_RXERR 0x8000\n#define S14X_LINK_IRQ_CMIECC 0x4000\n#define S14X_LINK_IRQ_NSTUNLOC 0x2000\n#define S14X_LINK_IRQ_WARTERR 0x1000\n#define S14X_LINK_IRQ_FRCV 0x0800\n#define S14X_LINK_IRQ_RRCV 0x0300\n#define S14X_LINK_IRQ_MRCV 0x0200\n#define S14X_LINK_IRQ_SIDF 0x0100\n#define S14X_LINK_IRQ_TKNRETF 0x0080\n#define S14X_LINK_IRQ_ACKNAKF 0x0040\n#define S14X_LINK_IRQ_HUBWDTO 0x0020\n#define S14X_LINK_IRQ_CPERR 0x0010\n#define S14X_LINK_IRQ_COM 0x0008\n#define S14X_LINK_IRQ_FBENR 0x0003\n#define S14X_LINK_IRQ_TXERR 0x0002\n#define S14X_LINK_IRQ_TA 0x0001\n\n#define S14X_LINK_MODEH_CMIERRMD 0x10\n#define S14X_LINK_MODEH_NSTSEND 0x08\n#define S14X_LINK_MODEH_NSTSTOP 0x04\n#define S14X_LINK_MODEH_INIMODE 0x02\n#define S14X_LINK_MODEH_TXEN 0x01\n#define S14X_LINK_MODEL_ECRI 0x80\n#define S14X_LINK_MODEL_BRE 0x40\n#define S14X_LINK_MODEL_TXM 0x20\n#define S14X_LINK_MODEL_RTO 0x10\n#define S14X_LINK_MODEL_WDMD 0x08\n#define S14X_LINK_MODEL_NTKNRTY 0x04\n#define S14X_LINK_MODEL_NACKNAK 0x02\n#define S14X_LINK_MODEL_NACLR 0x01\n\n#define S14X_LINK_MSKH_RXERR 0x80\n#define S14X_LINK_MSKH_CMIECC 0x40\n#define S14X_LINK_MSKH_NSTUNLOC 0x20\n#define S14X_LINK_MSKH_WARTERR 0x10\n#define S14X_LINK_MSKH_FRCV 0x08\n#define S14X_LINK_MSKH_RRCV 0x03\n#define S14X_LINK_MSKH_MRCV 0x02\n#define S14X_LINK_MSKH_SIDF 0x01\n#define S14X_LINK_MSKL_TKNRETF 0x80\n#define S14X_LINK_MSKL_ACKNAKF 0x40\n#define S14X_LINK_MSKL_HUBWDTO 0x20\n#define S14X_LINK_MSKL_CPERR 0x10\n#define S14X_LINK_MSKL_COM 0x08\n#define S14X_LINK_MSKL_FBENR 0x03\n#define S14X_LINK_MSKL_TXERR 0x02\n#define S14X_LINK_MSKL_TA 0x01\n\n#define S14X_LINK_STSH_RXERR 0x80\n#define S14X_LINK_STSH_CMIECC 0x40\n#define S14X_LINK_STSH_NSTUNLOC 0x20\n#define S14X_LINK_STSH_WARTERR 0x10\n#define S14X_LINK_STSH_FRCV 0x08\n#define S14X_LINK_STSH_RRCV 0x03\n#define S14X_LINK_STSH_MRCV 0x02\n#define S14X_LINK_STSH_SIDF 0x01\n#define S14X_LINK_STSL_TKNRETF 0x80\n#define S14X_LINK_STSL_ACKNAKF 0x40\n#define S14X_LINK_STSL_HUBWDTO 0x20\n#define S14X_LINK_STSL_CPERR 0x10\n#define S14X_LINK_STSL_COM 0x08\n#define S14X_LINK_STSL_FBENR 0x03\n#define S14X_LINK_STSL_TXERR 0x02\n#define S14X_LINK_STSL_TA 0x01\n\n#define S14X_LINK_RAMSIZE 1024\n\n// ARCNET packets are 64 bytes long\nstruct link_packet {\n    union {\n        uint8_t raw[64];\n\n        struct {\n            // Offset 00h - Node of the sender\n            uint8_t src_node;\n\n            // Offset 01h - Node of the receiver\n            uint8_t dst_node;\n\n            // Offset 02h - Continuation Pointer (data offset within packet)\n            uint8_t cp;\n\n            // Offset 03h - Unknown (must be 00h)\n            uint8_t unk03;\n\n            // Offset 04h - Packet number in a sequence\n            uint8_t seq_number;\n\n            // Offset 05h - I/O board command\n            uint8_t cmd;\n\n            // Offset 06h-3Eh - Command payload/data\n            uint8_t data[0x39];\n\n            // Offsset 3Fh - Checksum of all the data bytes\n            uint8_t checksum;\n        };\n    };\n};\n\ntypedef void (*link_packet_handler)(void*, struct link_packet*, struct link_packet*);\n\nstruct link_node {\n    link_packet_handler handler;\n    void* udata;\n};\n\nstatic uint8_t link_calculate_checksum(struct link_packet* packet);\n\nstruct s14x_link {\n    uint8_t pad00;\n    uint8_t comr0;\n    uint8_t pad02;\n    uint8_t comr1;\n    uint8_t pad04;\n    uint8_t comr2;\n    uint8_t pad06;\n    uint8_t comr3;\n    uint8_t pad08;\n    uint8_t comr4;\n    uint8_t pad0a;\n    uint8_t comr5;\n    uint8_t pad0c;\n    uint8_t comr6;\n    uint8_t pad0e;\n    uint8_t comr7;\n    uint8_t nsth;\n    uint8_t nstl;\n    uint8_t stsh;\n    uint8_t stsl;\n    uint8_t mskh;\n    uint8_t mskl;\n    uint8_t pad16;\n    uint8_t eccmd;\n    uint8_t mrsid;\n    uint8_t rsid;\n    uint8_t pad1a;\n    uint8_t ssid;\n    uint8_t rxfhh;\n    uint8_t rxfhl;\n    uint8_t rxflh;\n    uint8_t rxfll;\n    uint8_t pad20;\n    uint8_t cmid;\n    uint8_t modeh;\n    uint8_t model;\n    uint8_t carryh;\n    uint8_t carryl;\n    uint8_t rxmhh;\n    uint8_t rxmhl;\n    uint8_t rxmlh;\n    uint8_t rxmll;\n    uint8_t pad2a;\n    uint8_t maxid;\n    uint8_t pad2c;\n    uint8_t nid;\n    uint8_t pad2e;\n    uint8_t ps;\n    uint8_t pad30;\n    uint8_t ckp;\n    uint8_t nstdifh;\n    uint8_t nstdifl;\n    uint8_t watchdog_flag;\n\n    uint8_t ram[S14X_LINK_RAMSIZE];\n\n    uint32_t ramadr;\n\n    struct link_node nodes[32];\n\n    struct ps2_iop_intc* intc;\n    struct sched_state* sched;\n};\n\nstruct s14x_link* s14x_link_create(void);\nvoid s14x_link_init(struct s14x_link* link, struct ps2_iop_intc* intc, struct sched_state* sched);\nuint64_t s14x_link_read(struct s14x_link* link, uint32_t addr);\nvoid s14x_link_write(struct s14x_link* link, uint32_t addr, uint64_t data);\nvoid s14x_link_register_node(struct s14x_link* link, int node, link_packet_handler handler, void* udata);\nvoid s14x_link_send_packet(struct s14x_link* link, struct link_packet packet);\nvoid s14x_link_destroy(struct s14x_link* link);\n\nuint8_t link_calculate_checksum(struct link_packet* packet);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/s14x/nand.c",
    "content": "#include \"nand.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n#include <ctype.h>\n\nstruct s14x_nand* s14x_nand_create(void) {\n    return malloc(sizeof(struct s14x_nand));\n}\n\nint s14x_nand_init(struct s14x_nand* nand) {\n    memset(nand, 0, sizeof(struct s14x_nand));\n\n    nand->buf = malloc(S14X_NAND_PAGE_SIZE_ECC);\n    nand->state = S14X_NAND_STATE_READ_BYTE0;\n\n    return 1;\n}\n\nint s14x_nand_load(struct s14x_nand* nand, const char* path) {\n    nand->file = fopen(path, \"rb\");\n\n    if (!nand->file) {\n        return 0;\n    }\n\n    return 1;\n}\n\nvoid nand_handle_offset_write(struct s14x_nand* nand, uint8_t data) {\n    switch (nand->state) {\n        case S14X_NAND_STATE_READ_BYTE0: {\n            nand->byte_offset = (nand->byte_offset & 0xff00) | data;\n            nand->state++;\n        } break;\n        case S14X_NAND_STATE_READ_BYTE1: {\n            nand->byte_offset = (nand->byte_offset & 0x00ff) | (data << 8);\n            nand->state++;\n        } break;\n        case S14X_NAND_STATE_READ_PAGE0: {\n            nand->page_offset = (nand->page_offset & 0xffff00) | data;\n            nand->state++;\n        } break;\n        case S14X_NAND_STATE_READ_PAGE1: {\n            nand->page_offset = (nand->page_offset & 0xff00ff) | (data << 8);\n            nand->state++;\n        } break;\n        case S14X_NAND_STATE_READ_PAGE2: {\n            nand->page_offset = (nand->page_offset & 0x00ffff) | ((uint32_t)data << 16);\n            nand->state = S14X_NAND_STATE_READ_BYTE0;\n        } break;\n    }\n}\n\nvoid nand_handle_cmd_read(struct s14x_nand* nand) {\n    nand->size = S14X_NAND_PAGE_SIZE_ECC;\n    nand->index = nand->byte_offset;\n\n    if (!nand->file) {\n        memset(nand->buf, 0, S14X_NAND_PAGE_SIZE_ECC);\n\n        return;\n    }\n\n    fseek(nand->file, nand->page_offset * S14X_NAND_PAGE_SIZE_ECC, SEEK_SET);\n    fread(nand->buf, 1, S14X_NAND_PAGE_SIZE_ECC, nand->file);\n}\n\nuint64_t s14x_nand_read(struct s14x_nand* nand, uint32_t addr) {\n    switch (addr) {\n        case S14X_NAND_REG_OUTBYTE: {\n            int index = nand->index++ % nand->size;\n\n            return nand->buf[index];\n        } break;\n\n        default: {\n            // printf(\"s14x_nand: Unhandled register read %02x\\n\", addr);\n        } break;\n    }\n}\n\nvoid s14x_nand_write(struct s14x_nand* nand, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        case S14X_NAND_REG_CMD: {\n            nand->cmd = data;\n\n            switch (nand->cmd) {\n                case 0: {\n                    // NOP\n                } break;\n\n                case S14X_NAND_CMD_READ: {\n                    nand_handle_cmd_read(nand);\n                } break;\n\n                default: {\n                    printf(\"s14x_nand: Unhandled command %02x\\n\", nand->cmd);\n\n                    exit(1);\n                } break;\n            }\n        } break;\n\n        case S14X_NAND_REG_OFFSET: {\n            nand_handle_offset_write(nand, data);\n        } break;\n\n        default: {\n            // printf(\"s14x_nand: Unhandled register write %02x = %02x\\n\", addr, (uint8_t)data);\n        } break;\n    }\n}\n\nvoid s14x_nand_destroy(struct s14x_nand* nand) {\n    if (nand->buf) free(nand->buf);\n\n    free(nand);\n}"
  },
  {
    "path": "src/s14x/nand.h",
    "content": "#ifndef S14X_NAND_H\n#define S14X_NAND_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdio.h>\n\n#define S14X_NAND_CMD_READ   0x30\n#define S14X_NAND_CMD_ERASE  0x60\n#define S14X_NAND_CMD_WRITE  0x80\n#define S14X_NAND_CMD_READID 0x90\n\n#define S14X_NAND_PAGE_SIZE_NOECC 0x800\n#define S14X_NAND_PAGE_SIZE_ECC 0x840\n#define S14X_NAND_PAGES_PER_BLOCK 0x40\n\n#define S14X_NAND_STATE_READ_BYTE0 0\n#define S14X_NAND_STATE_READ_BYTE1 1\n#define S14X_NAND_STATE_READ_PAGE0 2\n#define S14X_NAND_STATE_READ_PAGE1 3\n#define S14X_NAND_STATE_READ_PAGE2 4\n\n#define S14X_NAND_REG_WAITFLAG 0\n#define S14X_NAND_REG_ENABLE 1\n#define S14X_NAND_REG_CMD 2\n#define S14X_NAND_REG_OFFSET 3\n#define S14X_NAND_REG_WRITE_UNLOCK 4\n#define S14X_NAND_REG_OUTBYTE 8\n\nstruct s14x_nand {\n    FILE* file;\n    int enable;\n    uint8_t cmd;\n    uint8_t* buf;\n    int index;\n    int size;\n\n    uint16_t byte_offset;\n    uint32_t page_offset;\n    int state;\n};\n\nstruct s14x_nand* s14x_nand_create(void);\nint s14x_nand_init(struct s14x_nand* nand);\nint s14x_nand_load(struct s14x_nand* nand, const char* path);\nuint64_t s14x_nand_read(struct s14x_nand* nand, uint32_t addr);\nvoid s14x_nand_write(struct s14x_nand* nand, uint32_t addr, uint64_t data);\nvoid s14x_nand_destroy(struct s14x_nand* nand);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/s14x/sram.c",
    "content": "#include \"sram.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\nstruct s14x_sram* s14x_sram_create(void) {\n    return malloc(sizeof(struct s14x_sram));\n}\n\nint s14x_sram_init(struct s14x_sram* sram, int* write_flag) {\n    memset(sram, 0, sizeof(struct s14x_sram));\n\n    sram->write_flag = write_flag;\n\n    return 0;\n}\n\nint s14x_sram_load(struct s14x_sram* sram, const char* path) {\n    FILE* file = fopen(path, \"rb\");\n\n    sram->path = path;\n\n    // If the file doesn't exist then we'll create it\n    if (!file) {\n        fclose(file);\n\n        file = fopen(path, \"wb\");\n\n        fwrite(sram->buf, 1, S14X_SRAM_SIZE, file);\n        fclose(file);\n\n        return 0;\n    }\n\n    fread(sram->buf, 1, S14X_SRAM_SIZE, file);\n    fclose(file);\n\n    return 1;\n}\n\nuint64_t s14x_sram_read8(struct s14x_sram* sram, uint32_t addr) {\n    return sram->buf[addr & 0x7fff];\n}\n\nuint64_t s14x_sram_read16(struct s14x_sram* sram, uint32_t addr) {\n    return *(uint16_t*)(sram->buf + (addr & 0x7fff));\n}\n\nuint64_t s14x_sram_read32(struct s14x_sram* sram, uint32_t addr) {\n    return *(uint32_t*)(sram->buf + (addr & 0x7fff));\n}\n\nvoid s14x_sram_write8(struct s14x_sram* sram, uint32_t addr, uint64_t data) {\n    if (sram->write_flag && !*sram->write_flag) return;\n\n    sram->buf[addr & 0x7fff] = data & 0xff;\n}\n\nvoid s14x_sram_write16(struct s14x_sram* sram, uint32_t addr, uint64_t data) {\n    if (sram->write_flag && !*sram->write_flag) return;\n\n    *(uint16_t*)(sram->buf + (addr & 0x7fff)) = data & 0xffff;\n}\n\nvoid s14x_sram_write32(struct s14x_sram* sram, uint32_t addr, uint64_t data) {\n    if (sram->write_flag && !*sram->write_flag) return;\n\n    *(uint32_t*)(sram->buf + (addr & 0x7fff)) = data & 0xffffffff;\n}\n\nvoid s14x_sram_destroy(struct s14x_sram* sram) {\n    if (sram->path) {\n        FILE* file = fopen(sram->path, \"wb\");\n\n        fwrite(sram->buf, 1, S14X_SRAM_SIZE, file);\n        fclose(file);\n    }\n\n    free(sram);\n}"
  },
  {
    "path": "src/s14x/sram.h",
    "content": "#ifndef S14X_SRAM_H\n#define S14X_SRAM_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdio.h>\n\n#define S14X_SRAM_SIZE 0x8000\n\nstruct s14x_sram {\n    const char* path;\n    int* write_flag;\n    uint8_t buf[S14X_SRAM_SIZE];\n};\n\nstruct s14x_sram* s14x_sram_create(void);\nint s14x_sram_init(struct s14x_sram* sram, int* write_flag);\nint s14x_sram_load(struct s14x_sram* sram, const char* path);\nuint64_t s14x_sram_read8(struct s14x_sram* sram, uint32_t addr);\nuint64_t s14x_sram_read16(struct s14x_sram* sram, uint32_t addr);\nuint64_t s14x_sram_read32(struct s14x_sram* sram, uint32_t addr);\nvoid s14x_sram_write8(struct s14x_sram* sram, uint32_t addr, uint64_t data);\nvoid s14x_sram_write16(struct s14x_sram* sram, uint32_t addr, uint64_t data);\nvoid s14x_sram_write32(struct s14x_sram* sram, uint32_t addr, uint64_t data);\nvoid s14x_sram_destroy(struct s14x_sram* sram);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/s14x/syscon.c",
    "content": "#include \"syscon.h\"\n\n#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\nstruct s14x_syscon* s14x_syscon_create(void) {\n    return malloc(sizeof(struct s14x_syscon));\n}\n\nvoid s14x_syscon_init(struct s14x_syscon* syscon) {\n    memset(syscon, 0, sizeof(struct s14x_syscon));\n\n    syscon->battery_level = 0x0; // 0 - OK, non-zero - NG\n}\n\nuint64_t s14x_syscon_read(struct s14x_syscon* syscon, uint32_t addr) {\n    switch (addr) {\n        case S14X_SYSCON_REG_LED: return syscon->led;\n        case S14X_SYSCON_REG_SECURITY_UNLOCK: return syscon->security_unlock;\n        case S14X_SYSCON_REG_RTC_FLAG: {\n            switch (syscon->rtc_state) {\n                case S14X_RTC_STATE_READ_YEAR: syscon->rtc_flag = 23; break;\n                case S14X_RTC_STATE_READ_MONTH: syscon->rtc_flag = 6; break;\n                case S14X_RTC_STATE_READ_DAY: syscon->rtc_flag = 15; break;\n                case S14X_RTC_STATE_READ_DOW: syscon->rtc_flag = 4; break;\n                case S14X_RTC_STATE_READ_HOURS: syscon->rtc_flag = 12; break;\n                case S14X_RTC_STATE_READ_MINUTES: syscon->rtc_flag = 34; break;\n                case S14X_RTC_STATE_READ_SECONDS: syscon->rtc_flag = 56; break;\n            }\n\n            int b = (syscon->rtc_flag >> syscon->rtc_bit++) & 1;\n            int bits = syscon->rtc_state == S14X_RTC_STATE_READ_DOW ? 4 : 8;\n\n            if (syscon->rtc_bit >= bits) {\n                syscon->rtc_bit = 0;\n                syscon->rtc_state++;\n\n                if (syscon->rtc_state > S14X_RTC_STATE_READ_SECONDS) {\n                    syscon->rtc_state = S14X_RTC_STATE_READ_YEAR;\n                }\n            }\n\n            // printf(\"s14x_rtc: read RTC_FLAG %d\\n\", syscon->rtc_flag);\n\n            return b;\n        }\n        case S14X_SYSCON_REG_WATCHDOG_FLAG2: return syscon->watchdog_flag2;\n        case S14X_SYSCON_REG_BATTERY_LEVEL: return syscon->battery_level;\n        case S14X_SYSCON_REG_SRAM_WRITE_FLAG: return syscon->sram_write_flag;\n        case S14X_SYSCON_REG_SECURITY_UNLOCK_SET1: return syscon->security_unlock_set1;\n        case S14X_SYSCON_REG_SECURITY_UNLOCK_SET2: return syscon->security_unlock_set2;\n        default: printf(\"s14x_syscon: Unknown register read %08x\\n\", addr); return 0;\n    }\n\n    return 0;\n}\n\nvoid s14x_syscon_write(struct s14x_syscon* syscon, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        case S14X_SYSCON_REG_LED: syscon->led = data; return;\n        case S14X_SYSCON_REG_SECURITY_UNLOCK: syscon->security_unlock = data; return;\n        // case S14X_SYSCON_REG_RTC_FLAG: syscon->rtc_flag = data; return;\n        case S14X_SYSCON_REG_WATCHDOG_FLAG2: syscon->watchdog_flag2 = data; return;\n        case S14X_SYSCON_REG_BATTERY_LEVEL: syscon->battery_level = data; return;\n        case S14X_SYSCON_REG_SRAM_WRITE_FLAG: syscon->sram_write_flag = data; return;\n        case S14X_SYSCON_REG_SECURITY_UNLOCK_SET1: syscon->security_unlock_set1 = data; return;\n        case S14X_SYSCON_REG_SECURITY_UNLOCK_SET2: syscon->security_unlock_set2 = data; return;\n        default: return; // printf(\"s14x_syscon: Unknown register write %08x %08lx\\n\", addr, data); return;\n    }\n}\n\nvoid s14x_syscon_destroy(struct s14x_syscon* syscon) {\n    free(syscon);\n}"
  },
  {
    "path": "src/s14x/syscon.h",
    "content": "// 10C00000 - 10C07FFF: S14X SRAM (32 KB)\n\n#ifndef S14X_SYSCON_H\n#define S14X_SYSCON_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdio.h>\n\n#define S14X_SYSCON_REG_LED 1\n#define S14X_SYSCON_REG_SECURITY_UNLOCK 2\n#define S14X_SYSCON_REG_RTC_FLAG 4\n#define S14X_SYSCON_REG_WATCHDOG_FLAG2 5\n#define S14X_SYSCON_REG_BATTERY_LEVEL 6\n#define S14X_SYSCON_REG_SRAM_WRITE_FLAG 7\n#define S14X_SYSCON_REG_SECURITY_UNLOCK_SET1 12\n#define S14X_SYSCON_REG_SECURITY_UNLOCK_SET2 13\n\n#define S14X_RTC_STATE_READ_YEAR 0\n#define S14X_RTC_STATE_READ_MONTH 1\n#define S14X_RTC_STATE_READ_DAY 2\n#define S14X_RTC_STATE_READ_DOW 3\n#define S14X_RTC_STATE_READ_HOURS 4\n#define S14X_RTC_STATE_READ_MINUTES 5\n#define S14X_RTC_STATE_READ_SECONDS 6\n\nstruct s14x_syscon {\n    uint8_t led;\n    uint8_t security_unlock;\n    uint8_t rtc_flag;\n    uint8_t battery_level;\n    uint8_t watchdog_flag2;\n    uint8_t security_unlock_set1;\n    uint8_t security_unlock_set2;\n    int sram_write_flag;\n\n    int rtc_state;\n    int rtc_bit;\n};\n\nstruct s14x_syscon* s14x_syscon_create(void);\nvoid s14x_syscon_init(struct s14x_syscon* syscon);\nuint64_t s14x_syscon_read(struct s14x_syscon* syscon, uint32_t addr);\nvoid s14x_syscon_write(struct s14x_syscon* syscon, uint32_t addr, uint64_t data);\nvoid s14x_syscon_destroy(struct s14x_syscon* syscon);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/scheduler.c",
    "content": "#include <stdint.h>\n#include <stdlib.h>\n#include <stddef.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"scheduler.h\"\n\nstruct sched_state* sched_create(void) {\n    return malloc(sizeof(struct sched_state));\n}\n\nvoid sched_init(struct sched_state* sched) {\n    memset(sched, 0, sizeof(struct sched_state));\n\n    if (sched->events)\n        free(sched->events);\n\n    sched->nevents = 0;\n    sched->cap = 0;\n    sched->events = NULL;\n    sched->offset = 0;\n}\n\nint event_compare(const void* a, const void* b) {\n    return ((struct sched_event*)a)->cycles - ((struct sched_event*)b)->cycles;\n}\n\nvoid sched_schedule(struct sched_state* sched, struct sched_event event) {\n    if (!sched->nevents) {\n        sched->events = realloc(sched->events, sizeof(struct sched_event) * 32);\n\n        if (!sched->events) {\n            printf(\"sched: Failed to allocate new event\\n\");\n\n            exit(1);\n        }\n\n        sched->cap = 32;\n        sched->nevents = 1;\n\n        sched->events[0] = event;\n    } else if (sched->nevents == 1) {\n        if (sched->events[0].cycles > event.cycles) {\n            sched->events[1] = sched->events[0];\n            sched->events[0] = event;\n        } else {\n            sched->events[1] = event;\n        }\n\n        // Clear offset, if there were more events in the\n        // queue, then we should subtract the offset from all of\n        // those.\n        sched->offset = 0;\n\n        sched->nevents = 2;\n    } else {\n        if (sched->nevents == sched->cap) {\n            sched->cap <<= 1;\n            sched->events = realloc(sched->events, sizeof(struct sched_event) * sched->cap);\n        }\n\n        // Need to sync events\n        // Don't apply offset to the current nearest event (offset is applied by ticking)\n        for (int i = 1; i < sched->nevents; i++) {\n            sched->events[i].cycles -= sched->offset;\n        }\n\n        sched->offset = 0;\n\n        for (int i = 0; i < sched->nevents; i++) {\n            sched->events[sched->nevents - i] = sched->events[sched->nevents - i - 1];\n        }\n\n        ++sched->nevents;\n\n        sched->events[0] = event;\n\n        if (sched->events[0].cycles > sched->events[1].cycles) {\n            qsort(sched->events, sched->nevents, sizeof(struct sched_event), event_compare);\n        }\n    }\n}\n\nint sched_tick(struct sched_state* sched, int cycles) {\n    if (!sched->nevents)\n        return 0;\n\n    sched->events[0].cycles -= cycles;\n    sched->offset += cycles;\n\n    if (sched->events[0].cycles > 0)\n        return 0;\n\n    --sched->nevents;\n\n    struct sched_event event = sched->events[0];\n\n    for (int i = 0; i < sched->nevents; i++) {\n        sched->events[i] = sched->events[i + 1];\n        sched->events[i].cycles -= sched->offset;\n    }\n\n    // Provide callback with overshot cycles\n    event.callback(event.udata, event.cycles);\n\n    sched->offset = 0;\n\n    return 1;\n}\n\nconst struct sched_event* sched_next_event(struct sched_state* sched) {\n    return &sched->events[0];\n}\n\nvoid sched_reset(struct sched_state* sched) {\n    sched->nevents = 0;\n    sched->offset = 0;\n}\n\nvoid sched_destroy(struct sched_state* sched) {\n    free(sched->events);\n    free(sched);\n}"
  },
  {
    "path": "src/scheduler.h",
    "content": "#ifndef SCHED_H\n#define SCHED_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\nstruct sched_event {\n    long cycles;\n    void (*callback)(void*, int);\n    const char* name;\n    void* udata;\n};\n\nstruct sched_state {\n    struct sched_event* events;\n    int nevents;\n    int cap;\n    uint64_t offset;\n};\n\nstruct sched_state* sched_create(void);\nvoid sched_init(struct sched_state* sched);\nvoid sched_schedule(struct sched_state* sched, struct sched_event event);\nvoid sched_reset(struct sched_state* sched);\nint sched_tick(struct sched_state* sched, int cycles);\nconst struct sched_event* sched_next_event(struct sched_state* sched);\nvoid sched_destroy(struct sched_state* sched);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/shared/bios.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"bios.h\"\n\nstruct ps2_bios* ps2_bios_create(void) {\n    return malloc(sizeof(struct ps2_bios));\n}\n\nstatic int get_file_size(FILE* file) {\n    int prev = ftell(file);\n    int size;\n\n    fseek(file, 0, SEEK_END);\n\n    size = ftell(file);\n\n    fseek(file, 0, SEEK_SET);\n\n    return size;\n}\n\nvoid ps2_bios_init(struct ps2_bios* bios) {\n    memset(bios, 0, sizeof(struct ps2_bios));\n\n    // Initialize dummy data\n    bios->buf = malloc(0x400000);\n    bios->size = 0x3fffff;\n\n    memset(bios->buf, 0, 0x400000);\n\n    uint32_t* ptr = (uint32_t*)bios->buf;\n\n    ptr[0] = 0x1000fffe; // b 0x00000000\n}\n\nint ps2_bios_load(struct ps2_bios* bios, const char* path) {\n    if (!path)\n        return 1;\n\n    FILE* file = fopen(path, \"rb\");\n\n    if (!file)\n        return 1;\n\n    // Free dummy data\n    free(bios->buf);\n\n    // \"size\" is actually a mask\n    bios->size = get_file_size(file) - 1;\n    bios->buf = malloc(bios->size + 1);\n\n    if (!fread(bios->buf, 1, bios->size + 1, file)) {\n        printf(\"bios: Couldn't read binary from \\'%s\\'\\n\", path);\n\n        return 1;\n    }\n\n    return 0;\n}\n\nvoid ps2_bios_destroy(struct ps2_bios* bios) {\n    free(bios->buf);\n    free(bios);\n}\n\nuint64_t ps2_bios_read8(struct ps2_bios* bios, uint32_t addr) {\n    return *(uint8_t*)(bios->buf + (addr & bios->size));\n}\n\nuint64_t ps2_bios_read16(struct ps2_bios* bios, uint32_t addr) {\n    return *(uint16_t*)(bios->buf + (addr & bios->size));\n}\n\nuint64_t ps2_bios_read32(struct ps2_bios* bios, uint32_t addr) {\n    return *(uint32_t*)(bios->buf + (addr & bios->size));\n}\n\nuint64_t ps2_bios_read64(struct ps2_bios* bios, uint32_t addr) {\n    return *(uint64_t*)(bios->buf + (addr & bios->size));\n}\n\nuint128_t ps2_bios_read128(struct ps2_bios* bios, uint32_t addr) {\n    return *(uint128_t*)(bios->buf + (addr & bios->size));\n}\n\nvoid ps2_bios_write8(struct ps2_bios* bios, uint32_t addr, uint64_t data) {\n    *(uint8_t*)(bios->buf + (addr & bios->size)) = data;\n}\nvoid ps2_bios_write16(struct ps2_bios* bios, uint32_t addr, uint64_t data) {\n    *(uint16_t*)(bios->buf + (addr & bios->size)) = data;\n}\nvoid ps2_bios_write32(struct ps2_bios* bios, uint32_t addr, uint64_t data) {\n    *(uint32_t*)(bios->buf + (addr & bios->size)) = data;\n}\nvoid ps2_bios_write64(struct ps2_bios* bios, uint32_t addr, uint64_t data) {\n    *(uint64_t*)(bios->buf + (addr & bios->size)) = data;\n}\nvoid ps2_bios_write128(struct ps2_bios* bios, uint32_t addr, uint128_t data) {\n    *(uint128_t*)(bios->buf + (addr & bios->size)) = data;\n}"
  },
  {
    "path": "src/shared/bios.h",
    "content": "#ifndef BIOS_H\n#define BIOS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"u128.h\"\n\nstruct ps2_bios {\n    uint8_t* buf;\n    size_t size;\n};\n\nstruct ps2_bios* ps2_bios_create(void);\nvoid ps2_bios_init(struct ps2_bios* bios);\nint ps2_bios_load(struct ps2_bios* bios, const char* path);\nvoid ps2_bios_destroy(struct ps2_bios* bios);\n\nuint64_t ps2_bios_read8(struct ps2_bios* bios, uint32_t addr);\nuint64_t ps2_bios_read16(struct ps2_bios* bios, uint32_t addr);\nuint64_t ps2_bios_read32(struct ps2_bios* bios, uint32_t addr);\nuint64_t ps2_bios_read64(struct ps2_bios* bios, uint32_t addr);\nuint128_t ps2_bios_read128(struct ps2_bios* bios, uint32_t addr);\nvoid ps2_bios_write8(struct ps2_bios* bios, uint32_t addr, uint64_t data);\nvoid ps2_bios_write16(struct ps2_bios* bios, uint32_t addr, uint64_t data);\nvoid ps2_bios_write32(struct ps2_bios* bios, uint32_t addr, uint64_t data);\nvoid ps2_bios_write64(struct ps2_bios* bios, uint32_t addr, uint64_t data);\nvoid ps2_bios_write128(struct ps2_bios* bios, uint32_t addr, uint128_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/shared/dev9.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"dev9.h\"\n\nstruct ps2_dev9* ps2_dev9_create(void) {\n    return malloc(sizeof(struct ps2_dev9));\n}\n\nvoid ps2_dev9_init(struct ps2_dev9* dev9, int model) {\n    memset(dev9, 0, sizeof(struct ps2_dev9));\n\n    dev9->rev = model;\n}\n\nvoid ps2_dev9_destroy(struct ps2_dev9* dev9) {\n    free(dev9);\n}\n\nuint64_t ps2_dev9_read8(struct ps2_dev9* dev9, uint32_t addr) {\n    switch (addr) {\n        case 0x1f80146e: return dev9->rev;\n    }\n\n    printf(\"dev9: Unknown 8-bit read at address %08x\\n\", addr);\n\n    return 0;\n}\n\nuint64_t ps2_dev9_read16(struct ps2_dev9* dev9, uint32_t addr) {\n    switch (addr) {\n        case 0x1f80146e: return dev9->rev;\n    }\n\n    printf(\"dev9: Unknown 16-bit read at address %08x\\n\", addr);\n\n    return 0;\n}\n\nuint64_t ps2_dev9_read32(struct ps2_dev9* dev9, uint32_t addr) {\n    switch (addr) {\n        case 0x1f80146e: return dev9->rev;\n    }\n\n    printf(\"dev9: Unknown 32-bit read at address %08x\\n\", addr);\n\n    return 0;\n}\n\nvoid ps2_dev9_write8(struct ps2_dev9* dev9, uint32_t addr, uint64_t data) {\n    printf(\"dev9: Unknown 8-bit write at address %08x (%04lx)\\n\", addr, data);\n\n    return;\n}\n\nvoid ps2_dev9_write16(struct ps2_dev9* dev9, uint32_t addr, uint64_t data) {\n    printf(\"dev9: Unknown 16-bit write at address %08x (%04lx)\\n\", addr, data);\n\n    return;\n}\n\nvoid ps2_dev9_write32(struct ps2_dev9* dev9, uint32_t addr, uint64_t data) {\n    printf(\"dev9: Unknown 32-bit write at address %08x (%04lx)\\n\", addr, data);\n\n    return;\n}"
  },
  {
    "path": "src/shared/dev9.h",
    "content": "#ifndef DEV9_H\n#define DEV9_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdlib.h>\n\n#define DEV9_TYPE_PCMCIA 0x20 // CXD9566\n#define DEV9_TYPE_EXPBAY 0x30 // CXD9611\n\nstruct ps2_dev9 {\n    uint16_t r_1460;\n    uint16_t r_1462;\n    uint16_t r_1464;\n    uint16_t r_1466;\n    uint16_t r_1468;\n    uint16_t r_146a;\n    uint16_t power;\n    uint16_t rev;\n    uint16_t r_1470;\n    uint16_t r_1472;\n    uint16_t r_1474;\n    uint16_t r_1476;\n    uint16_t r_1478;\n    uint16_t r_147a;\n    uint16_t r_147c;\n    uint16_t r_147e;\n};\n\nstruct ps2_dev9* ps2_dev9_create(void);\nvoid ps2_dev9_init(struct ps2_dev9* dev9, int model);\nvoid ps2_dev9_destroy(struct ps2_dev9* dev9);\nuint64_t ps2_dev9_read8(struct ps2_dev9* dev9, uint32_t addr);\nuint64_t ps2_dev9_read16(struct ps2_dev9* dev9, uint32_t addr);\nuint64_t ps2_dev9_read32(struct ps2_dev9* dev9, uint32_t addr);\nvoid ps2_dev9_write8(struct ps2_dev9* dev9, uint32_t addr, uint64_t data);\nvoid ps2_dev9_write16(struct ps2_dev9* dev9, uint32_t addr, uint64_t data);\nvoid ps2_dev9_write32(struct ps2_dev9* dev9, uint32_t addr, uint64_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/shared/ram.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n\n#include \"ram.h\"\n\nstruct ps2_ram* ps2_ram_create(void) {\n    return malloc(sizeof(struct ps2_ram));\n}\n\nvoid ps2_ram_init(struct ps2_ram* ram, int size) {\n    memset(ram, 0, sizeof(struct ps2_ram));\n\n    ram->buf = malloc(size);\n    ram->size = size;\n\n    memset(ram->buf, 0, ram->size);\n}\n\nvoid ps2_ram_reset(struct ps2_ram* ram) {\n    memset(ram->buf, 0, ram->size);\n}\n\nvoid ps2_ram_destroy(struct ps2_ram* ram) {\n    free(ram->buf);\n    free(ram);\n}\n\nuint64_t ps2_ram_read8(struct ps2_ram* ram, uint32_t addr) {\n    return *(uint8_t*)(ram->buf + (addr & (ram->size - 1)));\n}\n\nuint64_t ps2_ram_read16(struct ps2_ram* ram, uint32_t addr) {\n    return *(uint16_t*)(ram->buf + (addr & (ram->size - 1)));\n}\n\nuint64_t ps2_ram_read32(struct ps2_ram* ram, uint32_t addr) {\n    return *(uint32_t*)(ram->buf + (addr & (ram->size - 1)));\n}\n\nuint64_t ps2_ram_read64(struct ps2_ram* ram, uint32_t addr) {\n    return *(uint64_t*)(ram->buf + (addr & (ram->size - 1)));\n}\n\nuint128_t ps2_ram_read128(struct ps2_ram* ram, uint32_t addr) {\n    return *(uint128_t*)(ram->buf + (addr & (ram->size - 1)));\n}\n\nvoid ps2_ram_write8(struct ps2_ram* ram, uint32_t addr, uint64_t data) {\n    *(uint8_t*)(ram->buf + (addr & (ram->size - 1))) = data;\n}\n\nvoid ps2_ram_write16(struct ps2_ram* ram, uint32_t addr, uint64_t data) {\n    *(uint16_t*)(ram->buf + (addr & (ram->size - 1))) = data;\n}\n\nvoid ps2_ram_write32(struct ps2_ram* ram, uint32_t addr, uint64_t data) {\n    *(uint32_t*)(ram->buf + (addr & (ram->size - 1))) = data;\n}\n\nvoid ps2_ram_write64(struct ps2_ram* ram, uint32_t addr, uint64_t data) {\n    *(uint64_t*)(ram->buf + (addr & (ram->size - 1))) = data;\n}\n\nvoid ps2_ram_write128(struct ps2_ram* ram, uint32_t addr, uint128_t data) {\n    *(uint128_t*)(ram->buf + (addr & (ram->size - 1))) = data;\n}\n"
  },
  {
    "path": "src/shared/ram.h",
    "content": "#ifndef RAM_H\n#define RAM_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stddef.h>\n\n#include \"u128.h\"\n\nstruct ps2_ram {\n    uint8_t* buf;\n    size_t size;\n};\n\n#define RAM_SIZE_1KB 0x400\n#define RAM_SIZE_2MB 0x200000\n#define RAM_SIZE_4MB 0x400000\n#define RAM_SIZE_8MB 0x800000\n#define RAM_SIZE_16MB 0x1000000\n#define RAM_SIZE_32MB 0x2000000\n#define RAM_SIZE_64MB 0x4000000\n#define RAM_SIZE_128MB 0x8000000\n#define RAM_SIZE_256MB 0x10000000\n\nstruct ps2_ram* ps2_ram_create(void);\nvoid ps2_ram_init(struct ps2_ram* ram, int size);\nvoid ps2_ram_reset(struct ps2_ram* ram);\nvoid ps2_ram_destroy(struct ps2_ram* ram);\n\nuint64_t ps2_ram_read8(struct ps2_ram* ram, uint32_t addr);\nuint64_t ps2_ram_read16(struct ps2_ram* ram, uint32_t addr);\nuint64_t ps2_ram_read32(struct ps2_ram* ram, uint32_t addr);\nuint64_t ps2_ram_read64(struct ps2_ram* ram, uint32_t addr);\nuint128_t ps2_ram_read128(struct ps2_ram* ram, uint32_t addr);\nvoid ps2_ram_write8(struct ps2_ram* ram, uint32_t addr, uint64_t data);\nvoid ps2_ram_write16(struct ps2_ram* ram, uint32_t addr, uint64_t data);\nvoid ps2_ram_write32(struct ps2_ram* ram, uint32_t addr, uint64_t data);\nvoid ps2_ram_write64(struct ps2_ram* ram, uint32_t addr, uint64_t data);\nvoid ps2_ram_write128(struct ps2_ram* ram, uint32_t addr, uint128_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/shared/sbus.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n\n#include \"sbus.h\"\n\nstruct ps2_sbus* ps2_sbus_create(void) {\n    return malloc(sizeof(struct ps2_sbus));\n}\n\nvoid ps2_sbus_init(struct ps2_sbus* sbus, struct ps2_intc* ee_intc, struct ps2_iop_intc* iop_intc, struct sched_state* sched) {\n    memset(sbus, 0, sizeof(struct ps2_sbus));\n\n    sbus->ee_intc = ee_intc;\n    sbus->iop_intc = iop_intc;\n    sbus->sched = sched;\n}\n\nvoid ps2_sbus_destroy(struct ps2_sbus* sbus) {\n    free(sbus);\n}\n\nuint64_t ps2_sbus_read8(struct ps2_sbus* sbus, uint32_t addr) {\n    printf(\"sbus: 8-bit read %08x\\n\", addr); exit(1);\n}\n\nuint64_t ps2_sbus_read16(struct ps2_sbus* sbus, uint32_t addr) {\n    printf(\"sbus: 16-bit read %08x\\n\", addr); exit(1);\n}\n\nvoid sbus_trigger_iop_irq(void* udata, int overshoot) {\n    struct ps2_sbus* sbus = (struct ps2_sbus*)udata;\n\n    ps2_iop_intc_irq(sbus->iop_intc, IOP_INTC_SBUS);\n}\n\nvoid ps2_sbus_write8(struct ps2_sbus* sbus, uint32_t addr, uint64_t data) {\n    printf(\"sbus: 8-bit write %08x <- %02lx\\n\", addr, data); exit(1);\n}\n\nvoid ps2_sbus_write16(struct ps2_sbus* sbus, uint32_t addr, uint64_t data) {\n    printf(\"sbus: 16-bit write %08x <- %04lx\\n\", addr, data); exit(1);\n}\n\nvoid ps2_sbus_write32(struct ps2_sbus* sbus, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        case 0x1f801450: {\n            if (data & 2) {\n                ps2_intc_irq(sbus->ee_intc, EE_INTC_SBUS);\n            }\n        } return;\n    }\n}"
  },
  {
    "path": "src/shared/sbus.h",
    "content": "#ifndef SBUS_H\n#define SBUS_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include \"ee/intc.h\"\n#include \"iop/intc.h\"\n#include \"scheduler.h\"\n\nstruct ps2_sbus {\n    struct ps2_intc* ee_intc;\n    struct ps2_iop_intc* iop_intc;\n    struct sched_state* sched;\n};\n\nstruct ps2_sbus* ps2_sbus_create(void);\nvoid ps2_sbus_init(struct ps2_sbus* sbus, struct ps2_intc* ee_intc, struct ps2_iop_intc* iop_intc, struct sched_state* sched);\nvoid ps2_sbus_destroy(struct ps2_sbus* sbus);\nuint64_t ps2_sbus_read8(struct ps2_sbus* sbus, uint32_t addr);\nuint64_t ps2_sbus_read16(struct ps2_sbus* sbus, uint32_t addr);\nuint64_t ps2_sbus_read32(struct ps2_sbus* sbus, uint32_t addr);\nvoid ps2_sbus_write8(struct ps2_sbus* sbus, uint32_t addr, uint64_t data);\nvoid ps2_sbus_write16(struct ps2_sbus* sbus, uint32_t addr, uint64_t data);\nvoid ps2_sbus_write32(struct ps2_sbus* sbus, uint32_t addr, uint64_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/shared/sif.c",
    "content": "#include <stdio.h>\n#include <stdlib.h>\n#include <string.h>\n\n#include \"sif.h\"\n\nstruct ps2_sif* ps2_sif_create(void) {\n    return malloc(sizeof(struct ps2_sif));\n}\n\nvoid ps2_sif_init(struct ps2_sif* sif, struct ps2_iop_intc* iop_intc) {\n    memset(sif, 0, sizeof(struct ps2_sif));\n\n    sif->ctrl = 0xf0000012;\n    sif->iop_intc = iop_intc;\n}\n\nvoid ps2_sif_destroy(struct ps2_sif* sif) {\n    free(sif->sif0.data);\n    free(sif->sif1.data);\n    free(sif);\n}\n\nuint64_t ps2_sif_read32(struct ps2_sif* sif, uint32_t addr) {\n    // IOP read\n    switch (addr) {\n        case 0x1d000000: return sif->mscom;\n        case 0x1d000010: return sif->smcom;\n        case 0x1d000020: return sif->msflg;\n        case 0x1d000030: return sif->smflg;\n        case 0x1d000040: return sif->ctrl | 0xf0000001;\n        case 0x1d000060: return sif->bd6;\n    }\n\n    // EE read\n    switch (addr) {\n        case 0x1000f200: return sif->mscom;\n        case 0x1000f210: return sif->smcom;\n        case 0x1000f220: return sif->msflg;\n        case 0x1000f230: return sif->smflg;\n        case 0x1000f240: return sif->ctrl | 0xf0000101;\n        case 0x1000f260: return sif->bd6;\n    }\n\n    return 0;\n}\n\nvoid ps2_sif_write32(struct ps2_sif* sif, uint32_t addr, uint64_t data) {\n    // IOP write\n    switch (addr) {\n        case 0x1d000000: sif->mscom = data; return;\n        case 0x1d000010: sif->smcom = data; return;\n        case 0x1d000020: sif->msflg &= ~data; return;\n        case 0x1d000030: sif->smflg |= data; return;\n        case 0x1d000040: sif->ctrl = data; return;\n        case 0x1d000060: sif->bd6 = data; return;\n    }\n\n    // EE write\n    switch (addr) {\n        case 0x1000f200: sif->mscom = data; return;\n        case 0x1000f210: sif->smcom = data; return;\n        case 0x1000f220: sif->msflg |= data; return;\n        case 0x1000f230: sif->smflg &= ~data; return;\n        case 0x1000f240: {\n            if (data & 0x40000) {\n                ps2_iop_intc_irq(sif->iop_intc, IOP_INTC_SBUS);\n            }\n\n            sif->ctrl = data & ~0x40000;\n        }return;\n        case 0x1000f260: sif->bd6 = data; return;\n    }\n}\n\nvoid ps2_sif0_write(struct ps2_sif* sif, uint128_t data) {\n    // printf(\"writing %016lx %016lx to SIF FIFO\\n\", data.u64[1], data.u64[0]);\n\n    if (!sif->sif0.capacity) {\n        sif->sif0.capacity = 4;\n        sif->sif0.data = malloc(sizeof(uint128_t) * 4);\n    } else if (sif->sif0.write_index == sif->sif0.capacity) {\n        sif->sif0.capacity *= 2;\n\n        uint128_t* ptr = realloc(sif->sif0.data, sizeof(uint128_t) * sif->sif0.capacity);\n        \n        if (!ptr) {\n            fprintf(stderr, \"sif: Couldn't resize SIF0 buffer\\n\");\n\n            exit(1);\n        }\n\n        sif->sif0.data = ptr;\n    }\n\n    sif->sif0.data[sif->sif0.write_index++] = data;\n}\n\nuint128_t ps2_sif0_read(struct ps2_sif* sif) {\n    // If EE requests more data than the IOP produced, then return the last\n    // QW that was actually transferred.\n    // This happens during SIF initialization. The IOP triggers a SIF0 transfer\n    // that sends 8 words of data (2 QW), the first QW is an EE tag that starts\n    // a 2 QW transfer from the SIF FIFO, but the IOP only ever wrote 2 QWs.\n    if (sif->sif0.read_index == sif->sif0.write_index)\n        return sif->sif0.data[sif->sif0.read_index - 1];\n\n    uint128_t q = sif->sif0.data[sif->sif0.read_index++];\n\n    return q;\n}\n\nvoid ps2_sif0_reset(struct ps2_sif* sif) {\n    sif->sif0.read_index = 0;\n    sif->sif0.write_index = 0;\n}\n\nint ps2_sif0_is_empty(struct ps2_sif* sif) {\n    return sif->sif0.read_index == sif->sif0.write_index;\n}\n\nvoid ps2_sif1_write(struct ps2_sif* sif, uint128_t data) {\n    // printf(\"writing %016lx %016lx to SIF FIFO\\n\", data.u64[1], data.u64[0]);\n\n    if (!sif->sif1.capacity) {\n        sif->sif1.capacity = 4;\n        sif->sif1.data = malloc(sizeof(uint128_t) * 4);\n    } else if (sif->sif1.write_index == sif->sif1.capacity) {\n        sif->sif1.capacity *= 2;\n\n        uint128_t* ptr = realloc(sif->sif1.data, sizeof(uint128_t) * sif->sif1.capacity);\n        \n        if (!ptr) {\n            fprintf(stderr, \"sif: Couldn't resize SIF1 buffer\\n\");\n\n            exit(1);\n        }\n\n        sif->sif1.data = ptr;\n    }\n\n    sif->sif1.data[sif->sif1.write_index++] = data;\n}\n\nuint128_t ps2_sif1_read(struct ps2_sif* sif) {\n    // If EE requests more data than the IOP produced, then return the last\n    // QW that was actually transferred.\n    // This happens during SIF initialization. The IOP triggers a SIF0 transfer\n    // that sends 8 words of data (2 QW), the first QW is an EE tag that starts\n    // a 2 QW transfer from the SIF FIFO, but the IOP only ever wrote 2 QWs.\n    if (sif->sif1.read_index == sif->sif1.write_index)\n        return sif->sif1.data[sif->sif1.read_index - 1];\n\n    uint128_t q = sif->sif1.data[sif->sif1.read_index++];\n\n    return q;\n}\n\nvoid ps2_sif1_reset(struct ps2_sif* sif) {\n    sif->sif1.read_index = 0;\n    sif->sif1.write_index = 0;\n}\n\nint ps2_sif1_is_empty(struct ps2_sif* sif) {\n    return sif->sif1.read_index == sif->sif1.write_index;\n}"
  },
  {
    "path": "src/shared/sif.h",
    "content": "#ifndef SIF_H\n#define SIF_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"iop/intc.h\"\n#include \"u128.h\"\n\n#define SIF_EE_SIDE 0\n#define SIF_IOP_SIDE 1\n\nstruct sif_fifo {\n    int read_index;\n    int write_index;\n    int ready;\n    uint128_t* data;\n    int capacity;\n};\n\nstruct ps2_sif {\n    uint32_t mscom;\n    uint32_t smcom;\n    uint32_t msflg;\n    uint32_t smflg;\n    uint32_t ctrl;\n    uint32_t bd6;\n\n    struct ps2_iop_intc* iop_intc;\n\n    struct sif_fifo sif0;\n    struct sif_fifo sif1;\n};\n\nstruct ps2_sif* ps2_sif_create(void);\nvoid ps2_sif_init(struct ps2_sif* sif, struct ps2_iop_intc* iop_intc);\nvoid ps2_sif_destroy(struct ps2_sif* sif);\nuint64_t ps2_sif_read32(struct ps2_sif* sif, uint32_t addr);\nvoid ps2_sif_write32(struct ps2_sif* sif, uint32_t addr, uint64_t data);\n\n// DMA stuff\nvoid ps2_sif0_write(struct ps2_sif* sif, uint128_t data);\nuint128_t ps2_sif0_read(struct ps2_sif* sif);\nvoid ps2_sif0_reset(struct ps2_sif* sif);\nint ps2_sif0_is_empty(struct ps2_sif* sif);\nvoid ps2_sif1_write(struct ps2_sif* sif, uint128_t data);\nuint128_t ps2_sif1_read(struct ps2_sif* sif);\nvoid ps2_sif1_reset(struct ps2_sif* sif);\nint ps2_sif1_is_empty(struct ps2_sif* sif);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/shared/speed/ata.c",
    "content": "#include <string.h>\n#include <stdio.h>\n\n#include \"ata.h\"\n\nstruct ps2_ata* ps2_ata_create(void) {\n    return malloc(sizeof(struct ps2_ata));\n}\n\nvoid ps2_ata_init(struct ps2_ata* ata, struct ps2_speed* speed) {\n    memset(ata, 0, sizeof(struct ps2_ata));\n\n    ata->speed = speed;\n    ata->nsector = 1;\n    ata->sector = 1;\n}\n\nint ps2_ata_load(struct ps2_ata* ata, const char* path) {\n    // To-do: Load HDD image\n\n    return 1;\n}\n\nvoid ps2_ata_destroy(struct ps2_ata* ata) {\n    free(ata);\n}\n\nuint64_t ps2_ata_read16(struct ps2_ata* ata, uint32_t addr) {\n    printf(\"ata: read16 %08x\\n\", addr);\n\n    switch (addr) {\n        case 0x0040: return ata->data;\n        case 0x0042: return ata->error;\n        case 0x0044: return ata->nsector;\n        case 0x0046: return ata->sector;\n        case 0x0048: return ata->lcyl;\n        case 0x004a: return ata->hcyl;\n        case 0x004c: return ata->select;\n        case 0x004e: return 0x48; // RDY | DRQ\n        case 0x005c: return 0x40;\n    }\n}\n\nuint64_t ps2_ata_read32(struct ps2_ata* ata, uint32_t addr) {\n    printf(\"ata: read32 %08x\\n\", addr);\n}\n\nvoid ps2_ata_write16(struct ps2_ata* ata, uint32_t addr, uint64_t data) {\n    printf(\"ata: write16 %08x %08lx\\n\", addr, data);\n\n    switch (addr) {\n        // case 0x0040: return ata->data;\n        // case 0x0042: return ata->error;\n        // case 0x0044: return ata->nsector;\n        // case 0x0046: return ata->sector;\n        case 0x0048: ata->lcyl = data; return;\n        case 0x004a: ata->hcyl = data; return;\n        case 0x004c: ata->select = data; return;\n        case 0x004e: {\n            // COMMAND\n            ps2_speed_send_irq(ata->speed, SPD_INTR_ATA0);\n\n            return;\n        } break;\n        // case 0x005c: return ata->control;\n    }\n}\n\nvoid ps2_ata_write32(struct ps2_ata* ata, uint32_t addr, uint64_t data) {\n    printf(\"ata: write32 %08x %08lx\\n\", addr, data);\n}"
  },
  {
    "path": "src/shared/speed/ata.h",
    "content": "#ifndef ATA_H\n#define ATA_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n    SPEED ATA docs\n    --------------\n\n    This is basically just a standard ATA interface connected to the SPEED chip.\n    It's mapped starting at 10000040 (EE side 14000040).\n\n    Registers (X=0 IOP side, X=4 EE side):\n    1X000040 - DATA\n    1X000042 - ERROR\n             - FEATURE\n    1X000044 - NSECTOR\n    1X000046 - SECTOR\n    1X000048 - LCYL\n    1X00004a - HCYL\n    1X00004c - SELECT\n    1X00004e - STATUS\n             - COMMAND\n    1X000050 - 1X00004B - unused\n    1X00005c - CONTROL\n*/\n\n#include <stdint.h>\n#include <stdlib.h>\n#include <stdio.h>\n\n#include \"../speed.h\"\n\nstruct ps2_ata {\n    uint16_t data;\n    uint16_t error;\n    uint16_t feature;\n    uint16_t nsector;\n    uint16_t sector;\n    uint16_t lcyl;\n    uint16_t hcyl;\n    uint16_t select;\n    uint16_t status;\n    uint16_t command;\n    uint16_t control;\n\n    struct ps2_speed* speed;\n};\n\nstruct ps2_ata* ps2_ata_create(void);\nvoid ps2_ata_init(struct ps2_ata* ata, struct ps2_speed* speed);\nint ps2_ata_load(struct ps2_ata* ata, const char* path);\nvoid ps2_ata_destroy(struct ps2_ata* ata);\nuint64_t ps2_ata_read16(struct ps2_ata* ata, uint32_t addr);\nuint64_t ps2_ata_read32(struct ps2_ata* ata, uint32_t addr);\nvoid ps2_ata_write16(struct ps2_ata* ata, uint32_t addr, uint64_t data);\nvoid ps2_ata_write32(struct ps2_ata* ata, uint32_t addr, uint64_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/shared/speed/eeprom.c",
    "content": "#include <string.h>\n#include <stdio.h>\n\n#include \"eeprom.h\"\n\nuint16_t default_data[32] = {\n    0x6D76, 0x6361, 0x3130, 0x0207,\n    0x0000, 0x0000, 0x0000, 0x0000,\n    0x0000, 0x1000, 0x0000, 0x0000,\n    0x0000, 0x0000, 0x0000, 0x0000,\n    0x0010, 0x0000, 0x0000, 0x0000,\n    0x0000, 0x0000, 0x0000, 0x0000,\n    0x1000, 0x0000, 0x0000, 0x0000,\n    0x0000, 0x0000, 0x0000, 0x0000\n};\n\nstruct ps2_eeprom* ps2_eeprom_create(void) {\n    return malloc(sizeof(struct ps2_eeprom));\n}\n\nvoid ps2_eeprom_init(struct ps2_eeprom* eeprom) {\n    memset(eeprom, 0, sizeof(struct ps2_eeprom));\n\n    memcpy(eeprom->buf, default_data, 32 * sizeof(uint16_t));\n}\n\nvoid ps2_eeprom_load(struct ps2_eeprom* eeprom, const uint16_t* data) {\n    memcpy(eeprom->buf, data, 32 * sizeof(uint16_t));\n}\n\nvoid ps2_eeprom_destroy(struct ps2_eeprom* eeprom) {\n    free(eeprom);\n}\n\nuint64_t ps2_eeprom_read(struct ps2_eeprom* eeprom) {\n    return eeprom->dout << PP_DOUT;\n}\n\nvoid eeprom_step(struct ps2_eeprom* eeprom) {\n    switch (eeprom->state) {\n        case EEPROM_S_CMD_START: {\n            eeprom->sequence = 0;\n\n            if (eeprom->din) {\n                eeprom->state = EEPROM_S_CMD_READ;\n            }\n        } break;\n\n        case EEPROM_S_CMD_READ: {\n            eeprom->cmd |= eeprom->din << (1 - eeprom->sequence);\n            eeprom->sequence++;\n\n            if (eeprom->sequence == 2) {\n                eeprom->sequence = 0;\n                eeprom->state = EEPROM_S_ADDR_READ;\n            }\n        } break;\n\n        case EEPROM_S_ADDR_READ: {\n            // Address read in from highest bit to lowest bit\n            eeprom->addr |= eeprom->din << (5 - eeprom->sequence);\n\n            // Don't know what the behaviour would be but lets prevent oob.\n            eeprom->addr = eeprom->addr & 31;\n\n            eeprom->sequence++;\n            if (eeprom->sequence == 6)\n            {\n                eeprom->state = EEPROM_S_TRANSMIT;\n                eeprom->sequence = 0;\n            }\n        } break;\n\n        // fire away, bit position increments every pulse\n        case EEPROM_S_TRANSMIT: {\n            if (eeprom->cmd == PP_OP_READ) {\n                eeprom->dout = (eeprom->buf[eeprom->addr] >> (15 - eeprom->sequence)) & 1;\n            }\n\n            if (eeprom->cmd == PP_OP_WRITE) {\n                eeprom->buf[eeprom->addr] = eeprom->buf[eeprom->addr] | (eeprom->din << (15 - eeprom->sequence));\n            }\n\n            eeprom->sequence++;\n\n            if (eeprom->sequence == 16) {\n                eeprom->sequence = 0;\n\n                // Let's prevent oob here too\n                eeprom->addr = (eeprom->addr + 1) & 31;\n            }\n        } break;\n    }\n    return;\n}\n\nvoid ps2_eeprom_write(struct ps2_eeprom* eeprom, uint64_t data) {\n    uint8_t csel = (data >> PP_CSEL) & 1;\n    uint8_t sclk = (data >> PP_SCLK) & 1;\n    uint8_t din = (data >> PP_DIN) & 1;\n\n    if (!csel) {\n        eeprom->sequence = 0;\n        eeprom->addr = 0;\n        eeprom->state = EEPROM_S_CMD_START;\n        eeprom->clk = 0;\n\n        return;\n    }\n\n    eeprom->din = din;\n\n    if (sclk && !eeprom->clk) {\n        eeprom_step(eeprom);\n    }\n\n    eeprom->clk = sclk;\n}"
  },
  {
    "path": "src/shared/speed/eeprom.h",
    "content": "#ifndef EEPROM_H\n#define EEPROM_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stdlib.h>\n\n#define PP_DOUT 4\n#define PP_DIN 5\n#define PP_SCLK 6\n#define PP_CSEL 7\n#define PP_OP_READ 2\n#define PP_OP_WRITE 1\n#define PP_OP_EWEN 0\n#define PP_OP_EWDS 0\n\nenum {\n    EEPROM_S_CMD_START,\n    EEPROM_S_CMD_READ,\n    EEPROM_S_ADDR_READ,\n    EEPROM_S_TRANSMIT\n};\n\nstruct ps2_eeprom {\n    int state;\n\n    // Pins\n    uint8_t clk;\n    uint8_t din;\n    uint8_t dout;\n\n    uint8_t cmd;\n    uint8_t sequence;\n    uint8_t addr;\n\n    uint16_t buf[32];\n};\n\nstruct ps2_eeprom* ps2_eeprom_create(void);\nvoid ps2_eeprom_init(struct ps2_eeprom* eeprom);\nvoid ps2_eeprom_load(struct ps2_eeprom* eeprom, const uint16_t* data);\nvoid ps2_eeprom_destroy(struct ps2_eeprom* eeprom);\nuint64_t ps2_eeprom_read(struct ps2_eeprom* eeprom);\nvoid ps2_eeprom_write(struct ps2_eeprom* eeprom, uint64_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/shared/speed/flash.c",
    "content": "#include <string.h>\n\n#include \"flash.h\"\n\nstatic unsigned char xor_table[256] = {\n    0x00, 0x87, 0x96, 0x11, 0xA5, 0x22, 0x33, 0xB4,\n    0xB4, 0x33, 0x22, 0xA5, 0x11, 0x96, 0x87, 0x00,\n    0xC3, 0x44, 0x55, 0xD2, 0x66, 0xE1, 0xF0, 0x77,\n    0x77, 0xF0, 0xE1, 0x66, 0xD2, 0x55, 0x44, 0xC3,\n    0xD2, 0x55, 0x44, 0xC3, 0x77, 0xF0, 0xE1, 0x66,\n    0x66, 0xE1, 0xF0, 0x77, 0xC3, 0x44, 0x55, 0xD2,\n    0x11, 0x96, 0x87, 0x00, 0xB4, 0x33, 0x22, 0xA5,\n    0xA5, 0x22, 0x33, 0xB4, 0x00, 0x87, 0x96, 0x11,\n    0xE1, 0x66, 0x77, 0xF0, 0x44, 0xC3, 0xD2, 0x55,\n    0x55, 0xD2, 0xC3, 0x44, 0xF0, 0x77, 0x66, 0xE1,\n    0x22, 0xA5, 0xB4, 0x33, 0x87, 0x00, 0x11, 0x96,\n    0x96, 0x11, 0x00, 0x87, 0x33, 0xB4, 0xA5, 0x22,\n    0x33, 0xB4, 0xA5, 0x22, 0x96, 0x11, 0x00, 0x87,\n    0x87, 0x00, 0x11, 0x96, 0x22, 0xA5, 0xB4, 0x33,\n    0xF0, 0x77, 0x66, 0xE1, 0x55, 0xD2, 0xC3, 0x44,\n    0x44, 0xC3, 0xD2, 0x55, 0xE1, 0x66, 0x77, 0xF0,\n    0xF0, 0x77, 0x66, 0xE1, 0x55, 0xD2, 0xC3, 0x44,\n    0x44, 0xC3, 0xD2, 0x55, 0xE1, 0x66, 0x77, 0xF0,\n    0x33, 0xB4, 0xA5, 0x22, 0x96, 0x11, 0x00, 0x87,\n    0x87, 0x00, 0x11, 0x96, 0x22, 0xA5, 0xB4, 0x33,\n    0x22, 0xA5, 0xB4, 0x33, 0x87, 0x00, 0x11, 0x96,\n    0x96, 0x11, 0x00, 0x87, 0x33, 0xB4, 0xA5, 0x22,\n    0xE1, 0x66, 0x77, 0xF0, 0x44, 0xC3, 0xD2, 0x55,\n    0x55, 0xD2, 0xC3, 0x44, 0xF0, 0x77, 0x66, 0xE1,\n    0x11, 0x96, 0x87, 0x00, 0xB4, 0x33, 0x22, 0xA5,\n    0xA5, 0x22, 0x33, 0xB4, 0x00, 0x87, 0x96, 0x11,\n    0xD2, 0x55, 0x44, 0xC3, 0x77, 0xF0, 0xE1, 0x66,\n    0x66, 0xE1, 0xF0, 0x77, 0xC3, 0x44, 0x55, 0xD2,\n    0xC3, 0x44, 0x55, 0xD2, 0x66, 0xE1, 0xF0, 0x77,\n    0x77, 0xF0, 0xE1, 0x66, 0xD2, 0x55, 0x44, 0xC3,\n    0x00, 0x87, 0x96, 0x11, 0xA5, 0x22, 0x33, 0xB4,\n    0xB4, 0x33, 0x22, 0xA5, 0x11, 0x96, 0x87, 0x00\n};\n\nstatic void flash_calculate_xor(unsigned char buffer[128], unsigned char blah[4]) {\n    unsigned char a = 0, b = 0, c = 0, i;\n\n    for (i = 0; i < 128; i++) {\n        a ^= xor_table[buffer[i]];\n\n        if (xor_table[buffer[i]] & 0x80) {\n            b ^= ~i;\n            c ^= i;\n        }\n    }\n\n    blah[0] = (~a) & 0x77;\n    blah[1] = (~b) & 0x7F;\n    blah[2] = (~c) & 0x7F;\n}\n\nstatic void flash_calculate_ecc(uint8_t page[FLASH_PAGE_SIZE_ECC]) {\n    memset(page + FLASH_PAGE_SIZE, 0, FLASH_ECC_SIZE);\n\n    flash_calculate_xor(page + 0 * (FLASH_PAGE_SIZE >> 2), page + FLASH_PAGE_SIZE + 0 * 3); //(ECC_SIZE>>2));\n    flash_calculate_xor(page + 1 * (FLASH_PAGE_SIZE >> 2), page + FLASH_PAGE_SIZE + 1 * 3); //(ECC_SIZE>>2));\n    flash_calculate_xor(page + 2 * (FLASH_PAGE_SIZE >> 2), page + FLASH_PAGE_SIZE + 2 * 3); //(ECC_SIZE>>2));\n    flash_calculate_xor(page + 3 * (FLASH_PAGE_SIZE >> 2), page + FLASH_PAGE_SIZE + 3 * 3); //(ECC_SIZE>>2));\n}\n\nstatic const char* flash_get_cmd_name(uint32_t cmd) {\n    switch (cmd) {\n        case SM_CMD_READ1: return \"READ1\";\n        case SM_CMD_READ2: return \"READ2\";\n        case SM_CMD_READ3: return \"READ3\";\n        case SM_CMD_RESET: return \"RESET\";\n        case SM_CMD_WRITEDATA: return \"WRITEDATA\";\n        case SM_CMD_PROGRAMPAGE: return \"PROGRAMPAGE\";\n        case SM_CMD_ERASEBLOCK: return \"ERASEBLOCK\";\n        case SM_CMD_ERASECONFIRM: return \"ERASECONFIRM\";\n        case SM_CMD_GETSTATUS: return \"GETSTATUS\";\n        case SM_CMD_READID: return \"READID\";\n    }\n\n    return \"<unknown>\";\n}\n\nstruct ps2_flash* ps2_flash_create(void) {\n    return malloc(sizeof(struct ps2_flash));\n}\n\nvoid ps2_flash_init(struct ps2_flash* flash) {\n    memset(flash, 0, sizeof(struct ps2_flash));\n\n    flash->counter = 0;\n    flash->addrbyte = 0;\n    flash->address = 0;\n    flash->ctrl = FLASH_CTRL_READY;\n\n    memset(flash->data, 0xff, FLASH_PAGE_SIZE);\n    memset(flash->file, 0xff, FLASH_CARD_SIZE_ECC);\n\n    flash_calculate_ecc(flash->data);\n}\n\nint ps2_flash_load(struct ps2_flash* flash, const char* path) {\n    FILE* fd = fopen(path, \"rb\");\n\n    if (!fd) {\n        memset(flash->file, 0xff, FLASH_CARD_SIZE_ECC);\n\n        return 0;\n    }\n\n    size_t size = fread(flash->file, 1, FLASH_CARD_SIZE_ECC, fd);\n\n    if (size != FLASH_CARD_SIZE_ECC) {\n        printf(\"flash: Flash file size incorrect (%zu bytes)\\n\", size);\n\n        return 0;\n    }\n\n    fclose(fd);\n\n    flash->id = FLASH_ID_64MBIT;\n\n    printf(\"flash: Dump \\'%s\\' loaded (%zu bytes)\\n\", path, size);\n\n    return 1;\n}\n\nvoid ps2_flash_destroy(struct ps2_flash* flash) {\n    // To-do: Flush to file\n\n    free(flash);\n}\n\nvoid ps2_flash_reset(struct ps2_flash* flash) {\n    flash->cmd = 0;\n    flash->ctrl = FLASH_CTRL_READY;\n    flash->counter = 0;\n    flash->addrbyte = 0;\n    flash->address = 0;\n\n    memset(flash->data, 0xff, FLASH_PAGE_SIZE);\n\n    flash_calculate_ecc(flash->data);\n}\n\nuint32_t flash_read_data(struct ps2_flash* flash, int size) {\n    uint32_t value, refill = 0;\n\n    memcpy(&value, &flash->data[flash->counter], size);\n\n    flash->counter += size;\n\n    if (flash->cmd == SM_CMD_READ3) {\n        if (flash->counter >= FLASH_PAGE_SIZE_ECC) {\n            flash->counter = FLASH_PAGE_SIZE;\n\n            refill = 1;\n        }\n    } else {\n        if ((flash->ctrl & FLASH_CTRL_NOECC) && (flash->counter >= FLASH_PAGE_SIZE)) {\n            flash->counter %= FLASH_PAGE_SIZE;\n\n            refill = 1;\n        } else if (!(flash->ctrl & FLASH_CTRL_NOECC) && (flash->counter >= FLASH_PAGE_SIZE_ECC)) {\n            flash->counter %= FLASH_PAGE_SIZE_ECC;\n\n            refill = 1;\n        }\n    }\n\n    if (refill) {\n        flash->address += FLASH_PAGE_SIZE;\n        flash->address %= FLASH_CARD_SIZE;\n\n        memcpy(flash->data, flash->file + (flash->address >> FLASH_PAGE_SIZE_BITS) * FLASH_PAGE_SIZE_ECC, FLASH_PAGE_SIZE);\n\n        flash_calculate_ecc(flash->data); // calculate ECC; should be in the file already\n\n        flash->ctrl |= FLASH_CTRL_READY;\n    }\n\n    return value;\n}\n\nuint32_t flash_read_id(struct ps2_flash* flash) {\n    switch (flash->cmd) {\n        case SM_CMD_READID: {\n            return flash->id;\n        } break;\n\n        case SM_CMD_GETSTATUS: {\n            return 0x80 | ((flash->ctrl & FLASH_CTRL_READY) ? 0x40 : 0x00);\n        } break;\n    }\n\n    return 0;\n}\n\nvoid flash_write_data(struct ps2_flash* flash, int size, uint32_t data) {\n    memcpy(&flash->data[flash->counter], &flash->data, size);\n\n    flash->counter += size;\n    flash->counter %= FLASH_PAGE_SIZE_ECC; //should not get past the last byte, but at the end\n}\n\nvoid flash_write_cmd(struct ps2_flash* flash, uint16_t value) {\n    // printf(\"flash: Command %02x (%s)\\n\", value, flash_get_cmd_name(value));\n\n    if (!(flash->ctrl & FLASH_CTRL_READY)) {\n        if ((value != SM_CMD_GETSTATUS) && (value != SM_CMD_RESET)) {\n            return;\n        }\n    }\n\n    if (flash->cmd == SM_CMD_WRITEDATA) {\n        if ((value != SM_CMD_PROGRAMPAGE) && (value != SM_CMD_RESET)) {\n            flash->ctrl &= ~FLASH_CTRL_READY; //go busy, reset is needed\n        }\n    }\n\n    switch (value) { // A8 bit is encoded in READ cmd;)\n        case SM_CMD_READ1: {\n            flash->counter = 0;\n\n            if (flash->cmd != SM_CMD_GETSTATUS)\n                flash->address = flash->counter;\n\n            flash->addrbyte = 0;\n        } break;\n\n        case SM_CMD_READ2: {\n            flash->counter = FLASH_PAGE_SIZE / 2;\n\n            if (flash->cmd != SM_CMD_GETSTATUS)\n                flash->address = flash->counter;\n\n            flash->addrbyte = 0;\n        } break;\n\n        case SM_CMD_READ3: {\n            flash->counter = FLASH_PAGE_SIZE;\n\n            if (flash->cmd != SM_CMD_GETSTATUS)\n                flash->address = flash->counter;\n\n            flash->addrbyte = 0;\n        } break;\n\n        case SM_CMD_RESET: {\n            ps2_flash_reset(flash);\n        } break;\n\n        case SM_CMD_WRITEDATA: {\n            flash->counter = 0;\n            flash->address = flash->counter;\n            flash->addrbyte = 0;\n        } break;\n\n        case SM_CMD_ERASEBLOCK: {\n            flash->counter = 0;\n\n            memset(flash->data, 0xff, FLASH_PAGE_SIZE);\n\n            flash->address = flash->counter;\n            flash->addrbyte = 1;\n        } break;\n            \n        case SM_CMD_PROGRAMPAGE: //fall\n        case SM_CMD_ERASECONFIRM: {\n            flash_calculate_ecc(flash->data);\n\n            memcpy(flash->file + (flash->address / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE_ECC, flash->data, FLASH_PAGE_SIZE_ECC);\n\n            /*write2file*/\n            flash->ctrl |= FLASH_CTRL_READY;\n        } break;\n\n        case SM_CMD_GETSTATUS: break;\n        case SM_CMD_READID: {\n            flash->counter = 0;\n            flash->address = flash->counter;\n            flash->addrbyte = 0;\n        } break;\n\n        default: {\n            flash->ctrl &= ~FLASH_CTRL_READY;\n\n            return;\n        } break;\n    }\n\n    flash->cmd = value;\n}\n\nvoid flash_write_addr(struct ps2_flash* flash, uint16_t value) {\n    flash->address |= (value & 0xff) << (flash->addrbyte == 0 ? 0 : (1 + 8 * flash->addrbyte));\n    flash->addrbyte++;\n\n    if (!(value & 0x100)) { // address is complete\n        if ((flash->cmd == SM_CMD_READ1) || (flash->cmd == SM_CMD_READ2) || (flash->cmd == SM_CMD_READ3)) {\n            memcpy(flash->data, flash->file + (flash->address >> FLASH_PAGE_SIZE_BITS) * FLASH_PAGE_SIZE_ECC, FLASH_PAGE_SIZE);\n\n            flash_calculate_ecc(flash->data); // calculate ECC; should be in the file already\n\n            flash->ctrl |= FLASH_CTRL_READY;\n        }\n\n        flash->addrbyte = 0; // address reset\n    }\n}\n\nvoid flash_write_ctrl(struct ps2_flash* flash, uint16_t value) {\n    flash->ctrl = (flash->ctrl & FLASH_CTRL_READY) | (value & ~FLASH_CTRL_READY);\n}\n\nuint64_t ps2_flash_read16(struct ps2_flash* flash, uint32_t addr) {\n    switch (addr) {\n        case 0x4800: return flash_read_data(flash, 2);\n        case 0x480c: return flash->ctrl;\n        case 0x4814: return flash_read_id(flash);\n    }\n\n    printf(\"flash: Unknown 16-bit read at address %08x\\n\", addr);\n\n    return 0;\n}\n\nuint64_t ps2_flash_read32(struct ps2_flash* flash, uint32_t addr) {\n    switch (addr) {\n        case 0x4800: return flash_read_data(flash, 4);\n        case 0x480c: return flash->ctrl;\n        case 0x4814: return flash_read_id(flash);\n    }\n\n    printf(\"flash: Unknown 32-bit read at address %08x\\n\", addr);\n\n    return 0;\n}\n\nvoid ps2_flash_write16(struct ps2_flash* flash, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        case 0x4800: flash_write_data(flash, 2, data); return;\n        case 0x4804: flash_write_cmd(flash, data); return;\n        case 0x4808: flash_write_addr(flash, data); return;\n        case 0x480c: flash_write_ctrl(flash, data); return;\n    }\n\n    printf(\"flash: Unknown 16-bit write at address %08x (%08lx)\\n\", addr, data);\n}\n\nvoid ps2_flash_write32(struct ps2_flash* flash, uint32_t addr, uint64_t data) {\n    switch (addr) {\n        case 0x4800: flash_write_data(flash, 4, data); return;\n        case 0x4804: flash_write_cmd(flash, data); return;\n        case 0x4808: flash_write_addr(flash, data); return;\n        case 0x480c: flash_write_ctrl(flash, data); return;\n    }\n\n    printf(\"flash: Unknown 32-bit write at address %08x (%08lx)\\n\", addr, data);\n}"
  },
  {
    "path": "src/shared/speed/flash.h",
    "content": "#ifndef FLASH_H\n#define FLASH_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n/*\n    SPEED FLASH docs\n    ----------------\n\n    Also known as XFROM (eXternal Flash ROM), this flash memory is connected to\n    the SPEED chip, and is accessed through the registers listed below.\n    The actual chip seems to be the same chip used for the memory cards, but bigger.\n    The chip is controlled using \"SmartMedia\" commands sent through the COMMAND register.\n\n    Registers (X=0 IOP side, X=4 EE side):\n    1X004800 - DATA register\n        Used by WRITEDATA/PROGRAMPAGE/READ3/READ1, this is where data is\n        written or read from\n    1X004804 - CMD register\n        Write to this register to send a command\n    1X004808 - ADDR register\n        Probably used to set page offsets\n    1X00480c - CTRL register\n        bit 0 seems to be some kind of READY bit\n    1X004810 - unused?\n    1X004814 - ID/STATUS? (used by READID/GETSTATUS)\n        Contains the size (or ID) of the flash chip after sending READID\n        and some kind of status after sending GETSTATUS.\n\n    SmartMedia commands list:\n    0x00 - READ1\n    0x01 - READ2\n    0x50 - READ3\n    0xff - RESET\n    0x80 - WRITEDATA\n    0x10 - PROGRAMPAGE\n    0x60 - ERASEBLOCK\n    0xd0 - ERASECONFIRM\n    0x70 - GETSTATUS\n    0x90 - READID\n*/\n\n#include <stdint.h>\n#include <stdlib.h>\n#include <stdio.h>\n\n#define FLASH_ID_64MBIT     0xe6\n#define FLASH_ID_128MBIT    0x73\n#define FLASH_ID_256MBIT    0x75\n#define FLASH_ID_512MBIT    0x76\n#define FLASH_ID_1024MBIT   0x79\n\n/* SmartMedia commands.  */\n#define SM_CMD_READ1        0x00\n#define SM_CMD_READ2        0x01\n#define SM_CMD_READ3        0x50\n#define SM_CMD_RESET        0xff\n#define SM_CMD_WRITEDATA    0x80\n#define SM_CMD_PROGRAMPAGE  0x10\n#define SM_CMD_ERASEBLOCK   0x60\n#define SM_CMD_ERASECONFIRM 0xd0\n#define SM_CMD_GETSTATUS    0x70\n#define SM_CMD_READID       0x90\n\n#define FLASH_CTRL_READY (1 << 0)   // r/w /BUSY\n#define FLASH_CTRL_WRITE (1 << 7)   // -/w WRITE data\n#define FLASH_CTRL_CSEL (1 << 8)    // -/w CS\n#define FLASH_CTRL_READ (1 << 11)   // -/w READ data\n#define FLASH_CTRL_NOECC (1 << 12)  // -/w ECC disabled\n\n#define FLASH_PAGE_SIZE_BITS 9\n#define FLASH_PAGE_SIZE (1 << FLASH_PAGE_SIZE_BITS)\n#define FLASH_ECC_SIZE (16)\n#define FLASH_PAGE_SIZE_ECC (FLASH_PAGE_SIZE + FLASH_ECC_SIZE)\n#define FLASH_BLOCK_SIZE (16 * FLASH_PAGE_SIZE)\n#define FLASH_BLOCK_SIZE_ECC (16 * FLASH_PAGE_SIZE_ECC)\n#define FLASH_CARD_SIZE (1024 * FLASH_BLOCK_SIZE)\n#define FLASH_CARD_SIZE_ECC (1024 * FLASH_BLOCK_SIZE_ECC)\n\nstruct ps2_flash {\n    uint16_t cmd; // 10004804\n    uint16_t addr; // 10004808\n    uint16_t ctrl;  // 1000480c\n    uint16_t id; // 10004814\n\n    int counter;\n    int addrbyte;\n    int address;\n    uint8_t data[FLASH_PAGE_SIZE_ECC];\n    uint8_t file[FLASH_CARD_SIZE_ECC];\n};\n\nstruct ps2_flash* ps2_flash_create(void);\nvoid ps2_flash_init(struct ps2_flash* flash);\nint ps2_flash_load(struct ps2_flash* flash, const char* path);\nvoid ps2_flash_destroy(struct ps2_flash* flash);\nuint64_t ps2_flash_read16(struct ps2_flash* flash, uint32_t addr);\nuint64_t ps2_flash_read32(struct ps2_flash* flash, uint32_t addr);\nvoid ps2_flash_write16(struct ps2_flash* flash, uint32_t addr, uint64_t data);\nvoid ps2_flash_write32(struct ps2_flash* flash, uint32_t addr, uint64_t data);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/shared/speed.c",
    "content": "#include <stdlib.h>\n#include <string.h>\n#include <stdio.h>\n\n#include \"speed.h\"\n\nstruct ps2_speed* ps2_speed_create(void) {\n    return malloc(sizeof(struct ps2_speed));\n}\n\nvoid ps2_speed_init(struct ps2_speed* speed, struct ps2_iop_intc* iop_intc) {\n    memset(speed, 0, sizeof(struct ps2_speed));\n\n    speed->iop_intc = iop_intc;\n    speed->flash = ps2_flash_create();\n    speed->ata = ps2_ata_create();\n    speed->eeprom = ps2_eeprom_create();\n\n    ps2_flash_init(speed->flash);\n    ps2_ata_init(speed->ata, speed);\n    ps2_eeprom_init(speed->eeprom);\n\n    speed->rev8 |= 2;\n}\n\nvoid ps2_speed_destroy(struct ps2_speed* speed) {\n    ps2_flash_destroy(speed->flash);\n    ps2_ata_destroy(speed->ata);\n    ps2_eeprom_destroy(speed->eeprom);\n\n    free(speed);\n}\n\nuint64_t ps2_speed_read8(struct ps2_speed* speed, uint32_t addr) {\n    addr &= 0xffff;\n\n    printf(\"speed: read8 %08x %08x\\n\", addr);\n\n    switch (addr) {\n        case 0x002e: return ps2_eeprom_read(speed->eeprom);\n    }\n\n    return 0;\n\n    // exit(1);\n}\nuint64_t ps2_speed_read16(struct ps2_speed* speed, uint32_t addr) {\n    addr &= 0xffff;\n\n    if (addr >= 0x4800 && addr < 0x4820) {\n        return ps2_flash_read16(speed->flash, addr);\n    }\n\n    if (addr >= 0x0040 && addr < 0x0060) {\n        return ps2_ata_read16(speed->ata, addr);\n    }\n\n    switch (addr) {\n        case 0x0000: return speed->rev;\n        case 0x0002: return speed->rev1;\n        case 0x0004: return speed->rev3;\n        case 0x000e: return speed->rev8;\n        case 0x0024: return speed->dma_ctrl;\n        case 0x0028: return speed->intr_stat;\n        case 0x002a: return speed->intr_mask;\n        case 0x0032: return speed->xfr_ctrl;\n        case 0x0038: return speed->unknown38;\n        case 0x0064: return speed->if_ctrl;\n    }\n\n    printf(\"speed: read16 %08x\\n\", addr); // exit(1);\n\n    return 0;\n}\nuint64_t ps2_speed_read32(struct ps2_speed* speed, uint32_t addr) {\n    addr &= 0xffff;\n\n    if (addr >= 0x4800 && addr < 0x4820) {\n        return ps2_flash_read32(speed->flash, addr);\n    }\n\n    printf(\"speed: read32 %08x\\n\", addr); // exit(1);\n\n    return 0;\n}\nvoid ps2_speed_write8(struct ps2_speed* speed, uint32_t addr, uint64_t data) {\n    addr &= 0xffff;\n\n    printf(\"speed: write8 %08x %08x\\n\", addr, data);\n\n    switch (addr) {\n        case 0x002c: speed->pio_dir = data; return;\n        case 0x002e: ps2_eeprom_write(speed->eeprom, data); return;\n    }\n\n    // exit(1);\n}\nvoid ps2_speed_write16(struct ps2_speed* speed, uint32_t addr, uint64_t data) {\n    addr &= 0xffff;\n\n    if (addr >= 0x4800 && addr < 0x4820) {\n        ps2_flash_write16(speed->flash, addr, data);\n\n        return;\n    }\n\n    if (addr >= 0x0040 && addr < 0x0060) {\n        ps2_ata_write16(speed->ata, addr, data);\n\n        return;\n    }\n\n    switch (addr) {\n        case 0x0024: speed->dma_ctrl = data; return;\n        case 0x0032: speed->xfr_ctrl = data; return;\n        case 0x0038: speed->unknown38 = data; /* ??? */ return;\n        case 0x0064: speed->if_ctrl = data; return;\n        case 0x0070: speed->pio_mode = data; return;\n        case 0x0072: speed->mwdma_mode = data; return;\n        case 0x0074: speed->udma_mode = data; return;\n        case 0x002a: speed->intr_mask = data; return;\n    }\n\n    printf(\"speed: write16 %08x %08x\\n\", addr, data); // exit(1);\n}\nvoid ps2_speed_write32(struct ps2_speed* speed, uint32_t addr, uint64_t data) {\n    addr &= 0xffff;\n\n    if (addr >= 0x4800 && addr < 0x4820) {\n        ps2_flash_write32(speed->flash, addr, data);\n\n        return;\n    }\n\n    printf(\"speed: write32 %08x %08x\\n\", addr, data); // exit(1);\n}\n\nvoid ps2_speed_send_irq(struct ps2_speed* speed, uint16_t irq) {\n    speed->intr_stat |= irq;\n\n    if (speed->intr_stat & speed->intr_mask) {\n        ps2_iop_intc_irq(speed->iop_intc, IOP_INTC_DEV9);\n    }\n}\n\nint ps2_speed_load_flash(struct ps2_speed* speed, const char* path) {\n    int ret = ps2_flash_load(speed->flash, path);\n\n    if (ret) {\n        speed->rev3 |= SPD_CAPS_FLASH;\n    }\n\n    return ret;\n}\n\nvoid ps2_speed_set_mac_address(struct ps2_speed* speed, const uint8_t* mac) {\n    uint16_t data[32] = {\n        0x0000, 0x0000, 0x0000, 0x0000,\n        0x0000, 0x0000, 0x0000, 0x0000,\n        0x0000, 0x1000, 0x0000, 0x0000,\n        0x0000, 0x0000, 0x0000, 0x0000,\n        0x0010, 0x0000, 0x0000, 0x0000,\n        0x0000, 0x0000, 0x0000, 0x0000,\n        0x1000, 0x0000, 0x0000, 0x0000,\n        0x0000, 0x0000, 0x0000, 0x0000\n    };\n\n    data[0] = (mac[1] << 8) | mac[0];\n    data[1] = (mac[3] << 8) | mac[2];\n    data[2] = (mac[5] << 8) | mac[4];\n    data[3] = data[0] + data[1] + data[2];\n\n    ps2_eeprom_load(speed->eeprom, data);\n}"
  },
  {
    "path": "src/shared/speed.h",
    "content": "#ifndef SPEED_H\n#define SPEED_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n\n#include \"iop/intc.h\"\n#include \"speed/ata.h\"\n#include \"speed/flash.h\"\n#include \"speed/eeprom.h\"\n\n// SPEED is a chip presented as a register interface to speed devices.\n// This includes:\n// - SMAP (Ethernet?) at 0x10000100\n// - ATA (HDD) at 0x10000040\n// - UART (Maxim MAX232 UART, used in arcade units?) at ?\n// - DVR (PSX DESR Digital Video Recorder)\n// - Flash (also known as EROM, used in PSX DESR units) at 0x10004800\n// It is mapped to phys 0x10000000 on the IOP side, and phys 0x14000000\n// on the EE side.\n// Software won't even try to access SPEED if it isn't detected through\n// the speed_REV (1f80146e) register first.\n\n// Note: SMAP also provides access to the unit's MAC address (used by the BIOS)\n\n// Notes: USB and i.Link (FireWire) are completely separate\n//        and not connected to the SPEED interface at all.\n// \n//        USB is presented through an OHCI interface at 0x1F801600\n//        i.Link is presented through a FireWire interface at 0x1F808400\n\n#define SPD_INTR_ATA0   0x0001\n#define SPD_INTR_ATA1   0x0002\n#define SPD_INTR_DVR    0x0200 // mask=0x0200\n#define SPD_INTR_UART   0x1000 // mask=0x1000\n#define SPD_INTR_ATA    (SPD_INTR_ATA0 | SPD_INTR_ATA1) // mask=0x0003\n\n#define SPD_CAPS_SMAP  (1 << 0)\n#define SPD_CAPS_ATA   (1 << 1)\n#define SPD_CAPS_UART  (1 << 3)\n#define SPD_CAPS_DVR   (1 << 4)\n#define SPD_CAPS_FLASH (1 << 5)\n\nstruct ps2_speed {\n    uint16_t rev; // 10000000\n    uint16_t rev1; // 10000002\n    uint16_t rev3; // 10000004\n    uint16_t rev8; // 1000000e\n    uint32_t dma_ctrl; // 10000024\n    uint16_t intr_stat; // 10000028\n    uint16_t intr_mask; // 1000002a\n    uint16_t pio_dir; // 1000002c\n    uint16_t pio_data; // 1000002e\n    uint32_t xfr_ctrl; // 10000032\n    uint32_t unknown38; // 10000038\n    uint32_t if_ctrl; // 10000064\n    uint32_t pio_mode; // 10000070\n    uint32_t mwdma_mode; // 10000072\n    uint32_t udma_mode; // 10000074\n\n    struct ps2_ata* ata;\n    struct ps2_flash* flash;\n    struct ps2_eeprom* eeprom;\n\n    struct ps2_iop_intc* iop_intc;\n};\n\nstruct ps2_speed* ps2_speed_create(void);\nvoid ps2_speed_init(struct ps2_speed* speed, struct ps2_iop_intc* iop_intc);\nvoid ps2_speed_destroy(struct ps2_speed* speed);\nuint64_t ps2_speed_read8(struct ps2_speed* speed, uint32_t addr);\nuint64_t ps2_speed_read16(struct ps2_speed* speed, uint32_t addr);\nuint64_t ps2_speed_read32(struct ps2_speed* speed, uint32_t addr);\nvoid ps2_speed_write8(struct ps2_speed* speed, uint32_t addr, uint64_t data);\nvoid ps2_speed_write16(struct ps2_speed* speed, uint32_t addr, uint64_t data);\nvoid ps2_speed_write32(struct ps2_speed* speed, uint32_t addr, uint64_t data);\nvoid ps2_speed_send_irq(struct ps2_speed* speed, uint16_t irq);\nint ps2_speed_load_flash(struct ps2_speed* speed, const char* path);\nvoid ps2_speed_set_mac_address(struct ps2_speed* speed, const uint8_t* mac);\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  },
  {
    "path": "src/u128.h",
    "content": "#ifndef U128_H\n#define U128_H\n\n#ifdef __cplusplus\nextern \"C\" {\n#endif\n\n#include <stdint.h>\n#include <stddef.h>\n\ntypedef union {\n    // unsigned __int128 u128;\n    uint64_t u64[2];\n    uint32_t u32[4];\n    uint16_t u16[8];\n    uint8_t u8[16];\n    int64_t s64[2];\n    int32_t s32[4];\n    int16_t s16[8];\n    int8_t s8[16];\n    uint64_t ul64;\n    uint32_t ul32;\n    uint16_t ul16;\n    uint8_t ul8;\n    int64_t sl64;\n    int32_t sl32;\n    int16_t sl16;\n    int8_t sl8;\n} uint128_t;\n\n#ifdef __cplusplus\n}\n#endif\n\n#endif"
  }
]