Repository: allkern/iris Branch: master Commit: 90dae6afaa05 Files: 245 Total size: 2.4 MB Directory structure: gitextract_3bqhuqc4/ ├── .github/ │ ├── FUNDING.yml │ └── workflows/ │ ├── linux.yml │ ├── macos.yml │ ├── release.yml │ └── windows.yml ├── .gitignore ├── .gitmodules ├── AppImage.cmake ├── CMakeLists.txt ├── Info.plist ├── LICENSE ├── MoltenVK_icd.json ├── README.md ├── compat.txt ├── frontend/ │ ├── arcade.hpp │ ├── audio.cpp │ ├── config.hpp │ ├── elf.cpp │ ├── emu.cpp │ ├── handlers.cpp │ ├── imgui.cpp │ ├── input.cpp │ ├── iris.cpp │ ├── iris.hpp │ ├── notifications.cpp │ ├── platform/ │ │ ├── stub.cpp │ │ └── windows.cpp │ ├── render.cpp │ ├── res/ │ │ └── IconsMaterialSymbols.h │ ├── settings.cpp │ ├── shaders.cpp │ ├── ui/ │ │ ├── about.cpp │ │ ├── bios_setting.cpp │ │ ├── breakpoints.cpp │ │ ├── control.cpp │ │ ├── dma.cpp │ │ ├── gs.cpp │ │ ├── intc.cpp │ │ ├── logs.cpp │ │ ├── memory.cpp │ │ ├── memory_card_tool.cpp │ │ ├── memory_search.cpp │ │ ├── memory_viewer.h │ │ ├── menubar.cpp │ │ ├── modules.cpp │ │ ├── overlay.cpp │ │ ├── pad.cpp │ │ ├── settings.cpp │ │ ├── spu2.cpp │ │ ├── state.cpp │ │ ├── statusbar.cpp │ │ ├── symbols.cpp │ │ ├── threads.cpp │ │ └── vu_disassembly.cpp │ └── vulkan.cpp ├── main.cpp ├── res/ │ ├── iris.icns │ ├── iris.rc │ └── iris.res ├── shaders/ │ ├── curvature.frag │ ├── curvature.spv │ ├── decoder.frag │ ├── decoder.spv │ ├── default.frag │ ├── default.vert │ ├── encoder.frag │ ├── encoder.spv │ ├── fragment.spv │ ├── noise.frag │ ├── noise.spv │ ├── scanlines.frag │ ├── scanlines.spv │ ├── shader.spv │ ├── shader.vert │ └── vertex.spv └── src/ ├── dev/ │ ├── ds.c │ ├── ds.h │ ├── guncon.c │ ├── guncon.h │ ├── mcd.c │ ├── mcd.h │ ├── mtap.c │ ├── mtap.h │ ├── ps1_mcd.c │ └── ps1_mcd.h ├── ee/ │ ├── bus.c │ ├── bus.h │ ├── bus_decl.h │ ├── dmac.c │ ├── dmac.h │ ├── ee.h │ ├── ee_cached.cpp │ ├── ee_def.hpp │ ├── ee_dis.c │ ├── ee_dis.h │ ├── ee_uncached.c │ ├── ee_uncached.h │ ├── gif.c │ ├── gif.h │ ├── intc.c │ ├── intc.h │ ├── syscall.h │ ├── timers.c │ ├── timers.h │ ├── vif.c │ ├── vif.h │ ├── vu.h │ ├── vu_cached.cpp │ ├── vu_def.hpp │ ├── vu_dis.c │ ├── vu_dis.h │ └── vu_uncached.c ├── elf.h ├── gs/ │ ├── gs.c │ ├── gs.h │ └── renderer/ │ ├── config.hpp │ ├── hardware.cpp │ ├── hardware.hpp │ ├── null.cpp │ ├── null.hpp │ ├── renderer.cpp │ ├── renderer.hpp │ ├── software_thread.cpp │ └── software_thread.hpp ├── iop/ │ ├── bus.c │ ├── bus.h │ ├── bus_decl.h │ ├── cdvd.c │ ├── cdvd.h │ ├── disc/ │ │ ├── bin.c │ │ ├── bin.h │ │ ├── chd.c │ │ ├── chd.h │ │ ├── ciso.c │ │ ├── ciso.h │ │ ├── cue.c │ │ ├── cue.h │ │ ├── iso.c │ │ └── iso.h │ ├── disc.c │ ├── disc.h │ ├── dma.c │ ├── dma.h │ ├── fw.c │ ├── fw.h │ ├── hle/ │ │ ├── ioman.cpp │ │ ├── ioman.h │ │ ├── loadcore.c │ │ ├── loadcore.h │ │ ├── sysmem.c │ │ └── sysmem.h │ ├── intc.c │ ├── intc.h │ ├── iop.c │ ├── iop.h │ ├── iop_dis.c │ ├── iop_dis.h │ ├── iop_export.c │ ├── iop_export.h │ ├── rpc.c │ ├── rpc.h │ ├── sio2.c │ ├── sio2.h │ ├── spu2.c │ ├── spu2.h │ ├── timers.c │ ├── timers.h │ ├── usb.c │ └── usb.h ├── ipu/ │ ├── chromtable.cpp │ ├── chromtable.hpp │ ├── codedblockpattern.cpp │ ├── codedblockpattern.hpp │ ├── dct_coeff.cpp │ ├── dct_coeff.hpp │ ├── dct_coeff_table0.cpp │ ├── dct_coeff_table0.hpp │ ├── dct_coeff_table1.cpp │ ├── dct_coeff_table1.hpp │ ├── ipu.cpp │ ├── ipu.h │ ├── ipu.hpp │ ├── ipu_fifo.cpp │ ├── ipu_fifo.hpp │ ├── lumtable.cpp │ ├── lumtable.hpp │ ├── mac_addr_inc.cpp │ ├── mac_addr_inc.hpp │ ├── mac_b_pic.cpp │ ├── mac_b_pic.hpp │ ├── mac_i_pic.cpp │ ├── mac_i_pic.hpp │ ├── mac_p_pic.cpp │ ├── mac_p_pic.hpp │ ├── motioncode.cpp │ ├── motioncode.hpp │ ├── vlc_table.cpp │ └── vlc_table.hpp ├── list.c ├── list.h ├── md5.c ├── md5.h ├── ps2.c ├── ps2.h ├── ps2_elf.c ├── ps2_elf.h ├── ps2_iso9660.c ├── ps2_iso9660.h ├── queue.c ├── queue.h ├── rom.c ├── rom.h ├── s14x/ │ ├── aiboard.c │ ├── aiboard.h │ ├── ioboard.c │ ├── ioboard.h │ ├── link.c │ ├── link.h │ ├── nand.c │ ├── nand.h │ ├── sram.c │ ├── sram.h │ ├── syscon.c │ └── syscon.h ├── scheduler.c ├── scheduler.h ├── shared/ │ ├── bios.c │ ├── bios.h │ ├── dev9.c │ ├── dev9.h │ ├── ram.c │ ├── ram.h │ ├── sbus.c │ ├── sbus.h │ ├── sif.c │ ├── sif.h │ ├── speed/ │ │ ├── ata.c │ │ ├── ata.h │ │ ├── eeprom.c │ │ ├── eeprom.h │ │ ├── flash.c │ │ └── flash.h │ ├── speed.c │ └── speed.h └── u128.h ================================================ FILE CONTENTS ================================================ ================================================ FILE: .github/FUNDING.yml ================================================ # These are supported funding model platforms github: # Replace with up to 4 GitHub Sponsors-enabled usernames e.g., [user1, user2] patreon: # Replace with a single Patreon username open_collective: # Replace with a single Open Collective username ko_fi: # Replace with a single Ko-fi username tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry liberapay: # Replace with a single Liberapay username issuehunt: # Replace with a single IssueHunt username lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry polar: # Replace with a single Polar username buy_me_a_coffee: allkern custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2'] ================================================ FILE: .github/workflows/linux.yml ================================================ name: Ubuntu CI on: push: branches: [ "master" ] jobs: build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: submodules: 'recursive' fetch-depth: 0 - name: Install deps run: | sudo add-apt-repository universe sudo apt-get install build-essential git make \ pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \ libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev \ libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \ libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \ libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libfuse2t64 - name: Build and pack Iris run: | git fetch --all --tags cmake -S . -B build cmake --build build -j8 sudo cmake --install build mkdir dist mv ./build/iris ./dist mkdir appimage mv ./build/*.AppImage ./appimage - uses: actions/upload-artifact@v7 with: name: iris-latest-linux path: ./dist - uses: actions/upload-artifact@v7 with: name: iris-latest-linux-appimage path: ./appimage ================================================ FILE: .github/workflows/macos.yml ================================================ name: macOS CI on: push: branches: [ "master" ] jobs: macos-universal-build: runs-on: macos-latest steps: - uses: actions/checkout@v6 with: submodules: 'recursive' fetch-depth: 0 - name: Install Vulkan SDK uses: jakoch/install-vulkan-sdk-action@v1 with: optional_components: com.lunarg.vulkan.volk install_runtime: true cache: true stripdown: true - name: Build and pack Iris run: | git fetch --all --tags cmake -S . -B build -G "Unix Makefiles" cmake --build build -j8 sudo cmake --install build mkdir -p ./build/dist cp -R ./build/iris.app ./build/dist - uses: actions/upload-artifact@v7 with: name: iris-latest-macos-universal path: ./build/dist ================================================ FILE: .github/workflows/release.yml ================================================ name: Release CI on: push: tags: - '0.*' jobs: windows-build: runs-on: windows-latest steps: - uses: actions/checkout@v6 with: submodules: 'recursive' fetch-depth: 0 - name: Build and pack Iris run: | git fetch --all --tags cmake -S . -B build -G "MinGW Makefiles" -DCMAKE_BUILD_TYPE=Release cmake --build build -j8 New-Item -Path "dist" -ItemType Directory Copy-Item -Path ".\build\iris.exe" "dist" Copy-Item ".\build\*.dll" "dist" Copy-Item "C:\mingw64\bin\*.dll" "dist" - name: Generate artifact name id: generate-name run: | echo "::set-output name=artifact::iris-${{ github.ref_name }}-windows" - name: Zip artifact run: | $artifactName = "${{ steps.generate-name.outputs.artifact }}" Compress-Archive -Path "dist\*" -DestinationPath "$artifactName.zip" - name: Upload to release uses: softprops/action-gh-release@v2.6.1 with: files: | ${{ steps.generate-name.outputs.artifact }}.zip linux-build: runs-on: ubuntu-latest steps: - uses: actions/checkout@v6 with: submodules: 'recursive' fetch-depth: 0 - name: Install deps run: | sudo add-apt-repository universe sudo apt-get install build-essential git make \ pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \ libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev \ libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \ libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \ libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libfuse2t64 - name: Build and pack Iris run: | git fetch --all --tags cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build -j8 sudo cmake --install build mkdir dist mv ./build/iris ./dist mkdir appimage mv ./build/*.AppImage ./appimage - name: Generate artifact name id: generate-name run: | echo "::set-output name=artifact::iris-${{ github.ref_name }}-linux" - name: Generate AppImage artifact name id: generate-name-appimage run: | echo "::set-output name=artifact::iris-${{ github.ref_name }}-linux-appimage" - name: Zip artifact run: | zip -rj ${{ steps.generate-name.outputs.artifact }}.zip dist - name: Zip AppImage run: | zip -rj ${{ steps.generate-name-appimage.outputs.artifact }}.zip appimage - name: Upload normal binary to release uses: softprops/action-gh-release@vv2.6.1 with: files: | ${{ steps.generate-name.outputs.artifact }}.zip - name: Upload AppImage to release uses: softprops/action-gh-release@v2.6.1 with: files: | ${{ steps.generate-name-appimage.outputs.artifact }}.zip macos-universal-build: runs-on: macos-latest steps: - uses: actions/checkout@v6 with: submodules: 'recursive' fetch-depth: 0 - name: Install Vulkan SDK uses: jakoch/install-vulkan-sdk-action@v1 with: optional_components: com.lunarg.vulkan.volk install_runtime: true cache: true stripdown: true - name: Build and pack Iris run: | git fetch --all --tags cmake -S . -B build -DCMAKE_BUILD_TYPE=Release cmake --build build -j8 sudo cmake --install build - name: Generate artifact name id: generate-name run: | echo "::set-output name=artifact::iris-${{ github.ref_name }}-macos-universal" - name: Zip artifact run: | cd build; zip -r ../${{ steps.generate-name.outputs.artifact }}.zip ./iris.app; cd .. - name: Upload to release uses: softprops/action-gh-release@v2.6.1 with: files: | ${{ steps.generate-name.outputs.artifact }}.zip ================================================ FILE: .github/workflows/windows.yml ================================================ name: Windows CI on: push: branches: [ "master" ] jobs: build: runs-on: windows-latest steps: - uses: actions/checkout@v6 with: submodules: 'recursive' fetch-depth: 0 - name: Build and pack Iris run: | git fetch --all --tags cmake -S . -B build -G "MinGW Makefiles" cmake --build build -j8 New-Item -Path "dist" -ItemType Directory Copy-Item -Path ".\build\iris.exe" "dist" Copy-Item ".\build\*.dll" "dist" Copy-Item "C:\mingw64\bin\*.dll" "dist" - name: Upload artifact uses: actions/upload-artifact@v7 with: name: iris-latest-windows path: dist/ ================================================ FILE: .gitignore ================================================ SDL2-2.32.0/ .vscode/ build/ bin/ tools/ elf/ roms/ .vs/ ac/ screens/ main2.cpp compare compare.cpp imgui.ini *.AppImage *.expected *.dump *.cue *.bin *.elf *.exe *.iso *.mec *.nvm *.mcd *.wav *.irx *.o .DS_Store ================================================ FILE: .gitmodules ================================================ [submodule "imgui"] path = deps/imgui url = https://github.com/ocornut/imgui branch = docking [submodule "tomlplusplus"] path = deps/tomlplusplus url = https://github.com/marzer/tomlplusplus [submodule "SDL"] path = deps/SDL url = https://github.com/libsdl-org/SDL branch = release-3.2.24 [submodule "incbin"] path = deps/incbin url = https://github.com/graphitemaster/incbin [submodule "implot"] path = deps/implot url = https://github.com/epezent/implot [submodule "parallel-gs"] path = deps/parallel-gs url = https://github.com/Arntzen-software/parallel-gs [submodule "libchdr"] path = deps/libchdr url = https://github.com/rtissera/libchdr [submodule "stb"] path = deps/stb url = https://github.com/nothings/stb [submodule "portable-file-dialogs"] path = deps/portable-file-dialogs url = https://github.com/samhocevar/portable-file-dialogs [submodule "deps/libdeflate"] path = deps/libdeflate url = https://github.com/ebiggers/libdeflate [submodule "deps/lz4"] path = deps/lz4 url = https://github.com/lz4/lz4 [submodule "deps/SDL_GameControllerDB"] path = deps/SDL_GameControllerDB url = https://github.com/mdqinc/SDL_GameControllerDB [submodule "deps/asmjit"] path = deps/asmjit url = https://github.com/asmjit/asmjit ================================================ FILE: AppImage.cmake ================================================ function(make_appimage) set(optional) set(args EXE NAME DIR_ICON ICON OUTPUT_NAME) set(list_args ASSETS) cmake_parse_arguments( PARSE_ARGV 0 ARGS "${optional}" "${args}" "${list_args}" ) if(${ARGS_UNPARSED_ARGUMENTS}) message(WARNING "Unparsed arguments: ${ARGS_UNPARSED_ARGUMENTS}") endif() # download AppImageTool if needed (TODO: non-x86 build machine?) SET(AIT_PATH "${CMAKE_BINARY_DIR}/AppImageTool.AppImage" CACHE INTERNAL "") if (NOT EXISTS "${AIT_PATH}") file(DOWNLOAD https://github.com/AppImage/AppImageKit/releases/download/continuous/appimagetool-x86_64.AppImage "${AIT_PATH}") execute_process(COMMAND chmod +x ${AIT_PATH}) endif() # make the AppDir set(APPDIR "${CMAKE_BINARY_DIR}/AppDir") file(REMOVE_RECURSE "${APPDIR}") # remove if leftover file(MAKE_DIRECTORY "${APPDIR}") # copy executable to appdir file(COPY "${ARGS_EXE}" DESTINATION "${APPDIR}" FOLLOW_SYMLINK_CHAIN) get_filename_component(EXE_NAME "${ARGS_EXE}" NAME) # create the script that will launch the AppImage file(WRITE "${APPDIR}/AppRun" "#!/bin/sh cd \"$(dirname \"$0\")\"; ./${EXE_NAME} $@" ) execute_process(COMMAND chmod +x "${APPDIR}/AppRun") # copy assets to appdir file(COPY ${ARGS_ASSETS} DESTINATION "${APPDIR}") # copy icon thumbnail file(COPY ${ARGS_DIR_ICON} DESTINATION "${APPDIR}") get_filename_component(THUMB_NAME "${ARGS_DIR_ICON}" NAME) file(RENAME "${APPDIR}/${THUMB_NAME}" "${APPDIR}/.DirIcon") # copy icon highres file(COPY ${ARGS_ICON} DESTINATION "${APPDIR}") get_filename_component(ICON_NAME "${ARGS_ICON}" NAME) get_filename_component(ICON_EXT "${ARGS_ICON}" EXT) file(RENAME "${APPDIR}/${ICON_NAME}" "${APPDIR}/${ARGS_NAME}${ICON_EXT}") # Create the .desktop file file(WRITE "${APPDIR}/${ARGS_NAME}.desktop" "[Desktop Entry] Type=Application Name=${ARGS_NAME} Icon=${ARGS_NAME} Categories=X-None;" ) # Invoke AppImageTool execute_process(COMMAND ${AIT_PATH} ${APPDIR} ${ARGS_OUTPUT_NAME}) file(REMOVE_RECURSE "${APPDIR}") endfunction() ================================================ FILE: CMakeLists.txt ================================================ cmake_minimum_required(VERSION 3.21) project(iris LANGUAGES C CXX) set(WIN_ICON ${CMAKE_CURRENT_SOURCE_DIR}/res/iris.rc) set(CMAKE_OSX_ARCHITECTURES "x86_64;arm64") set(CMAKE_OSX_DEPLOYMENT_TARGET "10.15") set(OSX_ICON ${CMAKE_CURRENT_SOURCE_DIR}/res/iris.icns) set_source_files_properties(${OSX_ICON} PROPERTIES MACOSX_PACKAGE_LOCATION "Resources") # Statically link SDL3 for Linux targets set(SDL_STATIC ON) set(ASMJIT_STATIC ON) set(CMAKE_POSITION_INDEPENDENT_CODE ON) find_package(Git QUIET) if (GIT_FOUND) execute_process( COMMAND ${GIT_EXECUTABLE} describe --tags --always --dirty --match "0.*" WORKING_DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} OUTPUT_VARIABLE GIT_VERSION_STRING OUTPUT_STRIP_TRAILING_WHITESPACE ERROR_QUIET ) if(NOT GIT_VERSION_STRING) # Fallback if git describe fails (e.g., no tags in the history) set(GIT_VERSION_STRING "unknown-version") endif() else() set(GIT_VERSION_STRING "git-not-found") endif() message(STATUS "Project Version: ${GIT_VERSION_STRING}") # You can then use GIT_VERSION_STRING in your project, e.g., to generate a header file: # configure_file( # ${CMAKE_CURRENT_SOURCE_DIR}/version.h.in # ${CMAKE_CURRENT_BINARY_DIR}/version.h # @ONLY # ) include(CheckIPOSupported) check_ipo_supported(RESULT LTO_SUPPORTED OUTPUT LTO_ERROR) set(BUILD_SHARED_LIBS OFF) # And this part tells CMake where to find and install the file itself add_executable(iris MACOSX_BUNDLE ${OSX_ICON} ${WIN_ICON}) target_compile_definitions(iris PUBLIC IMGUI_IMPL_VULKAN_NO_PROTOTYPES) target_compile_definitions(iris PUBLIC IMGUI_IMPL_VULKAN_USE_VOLK) target_compile_options(iris PUBLIC "-Wno-deprecated-declarations") # target_compile_definitions(iris PUBLIC _DEBUG) set(PARALLEL_GS_STANDALONE ON CACHE BOOL "" FORCE) add_subdirectory(deps/asmjit EXCLUDE_FROM_ALL) add_subdirectory(deps/tomlplusplus EXCLUDE_FROM_ALL) add_subdirectory(deps/libdeflate EXCLUDE_FROM_ALL) add_subdirectory(deps/parallel-gs EXCLUDE_FROM_ALL) add_subdirectory(deps/libchdr EXCLUDE_FROM_ALL) add_subdirectory(deps/SDL EXCLUDE_FROM_ALL) if (CMAKE_SYSTEM_PROCESSOR MATCHES "AMD64") target_compile_options(iris PRIVATE -D_EE_USE_INTRINSICS -mssse3 -msse4.1) endif() if (X11_API) target_compile_definitions(granite-volk PUBLIC VK_USE_PLATFORM_XLIB_KHR) endif() if (WAYLAND_API) target_compile_definitions(granite-volk PUBLIC VK_USE_PLATFORM_WAYLAND_KHR) endif() if (WIN32) target_compile_definitions(granite-volk PUBLIC VK_USE_PLATFORM_WIN32_KHR) endif() if (NOT CMAKE_SYSTEM_NAME MATCHES "Windows") if (LTO_SUPPORTED) message(STATUS "IPO/LTO enabled") set_property(TARGET iris PROPERTY INTERPROCEDURAL_OPTIMIZATION TRUE) else() message(STATUS "IPO/LTO not supported: ${LTO_ERROR}") endif() endif() set_property(TARGET iris PROPERTY CXX_STANDARD 20) add_definitions("-D_IRIS_VERSION=${GIT_VERSION_STRING}") target_sources(iris PRIVATE main.cpp frontend/audio.cpp frontend/handlers.cpp frontend/vulkan.cpp frontend/imgui.cpp frontend/emu.cpp frontend/render.cpp frontend/shaders.cpp frontend/input.cpp frontend/iris.cpp frontend/elf.cpp frontend/notifications.cpp frontend/settings.cpp frontend/ui/about.cpp frontend/ui/bios_setting.cpp frontend/ui/breakpoints.cpp frontend/ui/control.cpp frontend/ui/dma.cpp frontend/ui/gs.cpp frontend/ui/intc.cpp frontend/ui/logs.cpp frontend/ui/memory.cpp frontend/ui/memory_card_tool.cpp frontend/ui/memory_search.cpp frontend/ui/menubar.cpp frontend/ui/modules.cpp frontend/ui/overlay.cpp frontend/ui/pad.cpp frontend/ui/settings.cpp frontend/ui/spu2.cpp frontend/ui/state.cpp frontend/ui/statusbar.cpp frontend/ui/symbols.cpp frontend/ui/threads.cpp frontend/ui/vu_disassembly.cpp src/ps2.c src/ps2_elf.c src/ps2_iso9660.c src/queue.c src/rom.c src/md5.c src/list.c src/scheduler.c src/dev/ds.c src/dev/guncon.c src/dev/mcd.c src/dev/mtap.c src/dev/ps1_mcd.c src/dev/ps1_mcd.c src/ee/ee_cached.cpp src/ee/bus.c src/ee/dmac.c src/ee/ee_dis.c src/ee/gif.c src/ee/intc.c src/ee/timers.c src/ee/vif.c src/ee/vu_cached.cpp src/ee/vu_dis.c src/gs/gs.c src/gs/renderer/null.cpp src/gs/renderer/renderer.cpp src/gs/renderer/hardware.cpp src/iop/bus.c src/iop/cdvd.c src/iop/disc.c src/iop/dma.c src/iop/fw.c src/iop/intc.c src/iop/iop.c src/iop/iop_dis.c src/iop/iop_export.c src/iop/rpc.c src/iop/sio2.c src/iop/spu2.c src/iop/timers.c src/iop/usb.c src/iop/disc/bin.c src/iop/disc/cue.c src/iop/disc/chd.c src/iop/disc/ciso.c src/iop/disc/iso.c src/iop/hle/ioman.cpp src/iop/hle/loadcore.c src/iop/hle/sysmem.c src/ipu/chromtable.cpp src/ipu/codedblockpattern.cpp src/ipu/dct_coeff.cpp src/ipu/dct_coeff_table0.cpp src/ipu/dct_coeff_table1.cpp src/ipu/ipu.cpp src/ipu/ipu_fifo.cpp src/ipu/lumtable.cpp src/ipu/mac_addr_inc.cpp src/ipu/mac_b_pic.cpp src/ipu/mac_i_pic.cpp src/ipu/mac_p_pic.cpp src/ipu/motioncode.cpp src/ipu/vlc_table.cpp src/shared/bios.c src/shared/dev9.c src/shared/ram.c src/shared/sbus.c src/shared/sif.c src/shared/speed.c src/shared/speed/ata.c src/shared/speed/eeprom.c src/shared/speed/flash.c src/s14x/nand.c src/s14x/syscon.c src/s14x/sram.c src/s14x/link.c src/s14x/ioboard.c src/s14x/aiboard.c deps/imgui/imgui.cpp deps/imgui/imgui_demo.cpp deps/imgui/imgui_draw.cpp deps/imgui/imgui_tables.cpp deps/imgui/imgui_widgets.cpp deps/imgui/backends/imgui_impl_sdl3.cpp deps/imgui/backends/imgui_impl_vulkan.cpp deps/implot/implot_demo.cpp deps/implot/implot_items.cpp deps/implot/implot.cpp deps/lz4/lib/lz4.c ) target_include_directories(iris PRIVATE deps/asmjit deps/imgui deps/imgui/backends deps/implot deps/SDL/include deps/incbin deps/parallel-gs deps/libchdr/include deps/lz4/lib deps/stb deps/portable-file-dialogs frontend src res ) target_link_libraries(iris PUBLIC asmjit::asmjit libdeflate::libdeflate_static tomlplusplus::tomlplusplus SDL3::SDL3-static parallel-gs chdr-static ) if (CMAKE_SYSTEM_NAME MATCHES "Darwin") find_package(Vulkan COMPONENTS MoltenVK) message(STATUS "VulkanSDK path: $ENV{VULKAN_SDK}") find_library(MOLTENVK_LIBRARY NAMES libMoltenVK.dylib PATHS ENV VULKAN_SDK PATH_SUFFIXES /lib ) target_link_libraries(iris PUBLIC Vulkan::Vulkan ${MOLTENVK_LIBRARY} ) endif() if (WIN32) target_link_libraries(iris PRIVATE dwmapi) target_sources(iris PRIVATE frontend/platform/windows.cpp) else() target_sources(iris PRIVATE frontend/platform/stub.cpp) endif() set_target_properties(iris PROPERTIES # On macOS, make a proper .app bundle instead of a bare executable MACOSX_BUNDLE TRUE # Set the Info.plist file for Apple Mobile platforms. Without this file, your app # will not launch. MACOSX_BUNDLE_INFO_PLIST "${CMAKE_CURRENT_SOURCE_DIR}/Info.plist" MACOSX_BUNDLE_ICON_FILE iris.icns # in Xcode, create a Scheme in the schemes dropdown for the app. XCODE_GENERATE_SCHEME TRUE # Identification for Xcode XCODE_ATTRIBUTE_BUNDLE_IDENTIFIER "com.allkern.iris" XCODE_ATTRIBUTE_PRODUCT_BUNDLE_IDENTIFIER "com.allkern.iris" XCODE_ATTRIBUTE_CURRENTYEAR "${CURRENTYEAR}" RESOURCE "${RESOURCE_FILES}" ) # on Visual Studio, set our app as the default project set_property(DIRECTORY ${CMAKE_CURRENT_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT "${EXECUTABLE_NAME}") # On macOS Platforms, ensure that the bundle is valid for distribution by calling fixup_bundle. # note that fixup_bundle does not work on iOS, so you will want to use static libraries # or manually copy dylibs and set rpaths message(STATUS "CMake System Name: ${CMAKE_SYSTEM_NAME}") if (CMAKE_SYSTEM_NAME MATCHES "Darwin") # tell Install about the target, otherwise fixup won't know about the transitive dependencies install(TARGETS iris BUNDLE DESTINATION ./install COMPONENT Runtime RUNTIME DESTINATION ./install/bin COMPONENT Runtime ) set(BUNDLE_PATH "${CMAKE_BINARY_DIR}/iris.app") # where to look for dependencies when fixing up file(GLOB VULKAN_DYLIBS $ENV{VULKAN_SDK}/lib/libvulkan.*.dylib) set(VULKAN_DYLIBS ${VULKAN_DYLIBS} $ENV{VULKAN_SDK}/lib/libMoltenVK.dylib) install(CODE " execute_process(COMMAND echo \"Preparing to bundle iris...\") execute_process(COMMAND echo \"Bundle path: ${BUNDLE_PATH}\") execute_process(COMMAND echo \"Vulkan dylibs: ${VULKAN_DYLIBS}\") execute_process(COMMAND echo \"Adding local RPATH...\") execute_process( COMMAND install_name_tool -add_rpath \"@executable_path/../Frameworks\" \"${BUNDLE_PATH}/Contents/MacOS/iris\" ) execute_process(COMMAND echo \"Creating ICD JSON directory...\") make_directory(${BUNDLE_PATH}/Contents/Resources/vulkan/icd.d) execute_process(COMMAND echo \"Creating Frameworks directory...\") make_directory(${BUNDLE_PATH}/Contents/Frameworks) execute_process(COMMAND echo \"Copying files...\") file(COPY ${CMAKE_CURRENT_SOURCE_DIR}/MoltenVK_icd.json DESTINATION ${BUNDLE_PATH}/Contents/Resources/vulkan/icd.d/ ) file(COPY ${VULKAN_DYLIBS} DESTINATION ${BUNDLE_PATH}/Contents/Frameworks/ ) execute_process(COMMAND echo \"Signing app bundle...\") execute_process(COMMAND codesign --force --deep --sign - ${BUNDLE_PATH} RESULT_VARIABLE codesign_result OUTPUT_VARIABLE codesign_output ) if (codesign_result) execute_process(COMMAND echo \"${codesign_output}\") endif() ") set(CPACK_GENERATOR "DragNDrop") include(CPack) endif() if (CMAKE_SYSTEM_NAME MATCHES "Linux") install(CODE "include(${CMAKE_CURRENT_SOURCE_DIR}/AppImage.cmake) make_appimage( EXE \"${CMAKE_CURRENT_SOURCE_DIR}/build/iris\" NAME \"Iris\" ICON \"${CMAKE_CURRENT_SOURCE_DIR}/res/iris.png\" DIR_ICON \"${CMAKE_CURRENT_SOURCE_DIR}/res/iris.png\" OUTPUT_NAME \"${CMAKE_CURRENT_SOURCE_DIR}/build/Iris-${GIT_VERSION_STRING}.AppImage\" ) " COMPONENT Runtime ) endif() ================================================ FILE: Info.plist ================================================ CFBundleDevelopmentRegion en CFBundleExecutable iris CFBundleIdentifier iris.app.0 CFBundleInfoDictionaryVersion 6.0 CFBundleName Iris CFBundlePackageType APPL CFBundleShortVersionString 0.10-alpha CFBundleSignature iris CFBundleVersion 0.10-alpha LSApplicationCategoryType public.app-category.games LSMinimumSystemVersion 10.15 NSHighResolutionCapable NSPrincipalClass NSApplication CFBundleIconFile iris ================================================ FILE: LICENSE ================================================ MIT License Copyright (c) 2025 Allkern/Lisandro Alarcon Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. ================================================ FILE: MoltenVK_icd.json ================================================ { "file_format_version" : "1.0.0", "ICD": { "library_path": "../../../Frameworks/libMoltenVK.dylib", "api_version" : "1.4.0", "is_portability_driver": true } } ================================================ FILE: README.md ================================================
# 🐣 Iris Sony PlayStation 2 emulator for Windows, Linux and macOS ## Screenshots
Metal Gear Solid 3 - Snake Eater (Japan) Resident Evil 4 (USA) God of War II (USA) Kingdom Hearts II (USA) Ace Combat Zero - The Belkan War (USA) Virtua Fighter 4 (USA) Devil May Cry 3 - Dante's Awakening (USA) Ico (USA)
## Usage > [!WARNING] > This emulator is under development, most games WILL run at very low/unplayable framerates. ### GUI Navigate over to `Iris > Open...` and choose a disc image or ELF executable, drag-and-drop is also supported ### CLI ``` Usage: iris [OPTION]... -b, --bios Specify a PlayStation 2 BIOS dump file --rom1 Specify a DVD player dump file --rom2 Specify a ROM2 dump file -d, --boot Specify a direct kernel boot path -i, --disc Specify a path to a disc image file -x, --executable Specify a path to an ELF executable to be loaded on system startup --slot1 Specify a path to a memory card file to be inserted on slot 1 --slot2 Specify a path to a memory card file to be inserted on slot 2 -h, --help Display this help and exit -v, --version Output version information and exit ``` ## Features - Support for ISO, BIN/CUE, CHD and CSO/ZSO disc image formats - Hardware-accelerated Vulkan GS renderer with support for up to 16x SSAA - Feature-packed debugger - Easy to use graphical interface - Game controller support with input remapping - Support for post-processing shaders ## Building > [!WARNING] > Building requires CMake and the Vulkan SDK on all supported platforms ### Linux Building on Linux requires installing SDL3 dependencies and FUSE if you wish to generate AppImages. ``` sudo apt update sudo apt upgrade sudo add-apt-repository universe sudo apt-get install build-essential git make \ pkg-config cmake ninja-build gnome-desktop-testing libasound2-dev libpulse-dev \ libaudio-dev libjack-dev libsndio-dev libx11-dev libxext-dev \ libxrandr-dev libxcursor-dev libxfixes-dev libxi-dev libxss-dev libxtst-dev \ libxkbcommon-dev libdrm-dev libgbm-dev libgl1-mesa-dev libgles2-mesa-dev \ libegl1-mesa-dev libdbus-1-dev libibus-1.0-dev libudev-dev libfuse2t64 ``` Then clone the repository and run CMake: ``` git clone https://github.com/allkern/iris --recursive cd iris cmake -S . -B build cmake --build build -j8 ``` Optionally run `cmake --install build` to generate an AppImage. ### Windows We 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! ``` git clone https://github.com/allkern/iris --recursive cd iris cmake -S . -B build -G "MinGW Makefiles" cmake --build build -j8 ``` ### macOS ``` git clone https://github.com/allkern/iris --recursive cd iris cmake -S . -B build cmake --build build -j8 ``` Optionally run `sudo cmake --install build` to generate a macOS App Bundle ## Progress/Insights Iris 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. The 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. In 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: - Scheduling (done) - Software fastmem (done) - EE interpreter caching (done) - Hardware-accelerated GS rendering (done) - EE JIT/Dynarec (coming up) - VU JIT/Dynarec (soon) - Hardware fastmem (eventually) - etc. Integrating 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. ## Preservation Iris 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). In 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. It'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. In 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. # Special thanks and acknowledgements I would like to thank the emudev Discord server, Ziemas, Nelson (ncarrillo), cakehonolulu, PSI-rockin, noumi and the PCSX2 team for their kind support. This project makes use of the following third-party libraries: - [ImGui](https://github.com/ocornut/imgui) - [ImPlot](https://github.com/epezent/implot) - [SDL3](https://github.com/libsdl-org/SDL) - [SDL_GameControllerDB](https://github.com/mdqinc/SDL_GameControllerDB) - [incbin](https://github.com/graphitemaster/incbin) - [Parallel-GS](https://github.com/Arntzen-software/parallel-gs) - [libchdr](https://github.com/rtissera/libchdr) - [libdeflate](https://github.com/ebiggers/libdeflate) - [lz4](https://github.com/lz4/lz4) - [toml++](https://marzer.github.io/tomlplusplus/) - [Portable File Dialogs](https://github.com/samhocevar/portable-file-dialogs) - [stb_image](https://github.com/nothings/stb) Credit goes out to the developers of these libraries, Iris wouldn't have been possible without your outstanding work. ### Components This console is significantly more complex compared to the PS1, here's a rough list of components: ``` 🟡 EE (R5900) CPU - 🟡 FPU - 🟡 MMI (SIMD) - 🟡 TLB - 🟡 DMAC - 🟢 INTC - 🟡 Timers - 🟢 GIF - 🟡 GS - 🟡 VU0 = 🟡 Macro mode = 🟡 Micro mode = 🟡 VIF0 - 🟡 VU1 (always micro mode) = 🟡 VIF1 - 🟡 IPU 🟢 IOP (R3000) CPU - 🟡 DMAC - 🟢 INTC - 🟡 Timers - 🟢 CDVD - 🟢 SIO2 (controllers and Memory Cards) - 🟢 SPU2 - 🟡 DEV9 - 🟡 USB/FireWire? - 🔴 Ethernet - 🔴 PS1 backcompat (PS1 hardware) 🟢 SIF ``` ================================================ FILE: compat.txt ================================================ ".hack - Infection (USA)" - Boots to menus, runs very slow "007 - Nightfire (USA)" - Hangs trying to play an FMV at the very beginning "Arcana Heart (USA)" - REGRESSION! Seems to trash IOP memory with what looks like ADPCM data "Babe (Australia)" - Boots, won't boot with FMV skipping enabled though "Batman - Vengeance (USA)" - Requires MFIFO "Big Mutha Truckers (USA)" - Prints "CD Error 0xfd, ditching this request", reads sectors 1eb02,1eb12,1eb22 repeatedly "Buffy the Vampire Slayer - Chaos Bleeds (USA)" - Gets stuck waiting for GIF_STAT.8-9 to be non-zero "Bully (USA)" - Boots in-game, runs incredibly slow, textures broken, image looks yellow tinted (similar to GoW) "Dead or Alive 2 (Japan)" - Nothing, prints "Read Time Out 15000(msec)" to the terminal, possibly CDVD-related? "Final Fantasy XII (USA)" - Enables MFIFO through an 8-bit write to CTRL, uses transfer "mode 3" (actually just chain mode) "Gallop Racer 2006 (USA)" - Requires proper EE timings (at most 2x underclock) "Klonoa 2 - Lunatea's Veil (USA)" - Doesn't work on Release builds (!), requires 16-bit DMAC writes "Namco Museum 50th Anniversary (USA)" - Requires MFIFO "R-Type Final (Japan)" - Requires MFIFO "Sega Ages 2500 Series Vol. 23 - Sega Memorial Selection (Japan)" - GIF DMA read from NULL "Sega Genesis Collection (USA)" - Works, "forgets" to change the videomode causing wrong graphics at startup (needs 24-bit 640x448 interlaced?) "Simpsons, The - Hit & Run (USA)" - Needs MFIFO "Tekken 4 (USA)" - Needs MFIFO "Tekken Tag Tournament (USA) (v1.00)" - Needs MFIFO "Thunder Force VI (Japan)" - Crashes on an invalid GIF DMA address when starting up (compare against Dobie) "Virtua Fighter 4 - Evolution (USA)" - Works, uses filling mode "Virtua Fighter - Cyber Generation - Judgment Six no Yabou (Japan)" - PMTHL unimplemented "We Love Katamari (USA)" - Uses filling mode, gets stuck trying to play an FMV after the Namco logo? ================================================ FILE: frontend/arcade.hpp ================================================ #include #include "ps2.h" const toml::table g_arcade_definitions = toml::table { { "pacmanap", toml::table { { "system", PS2_SYSTEM_NAMCO_S147 }, { "name", "Pac-Man's Arcade Party" }, { "nand", "kp007a_k9k8g08u0b_pmaam12-na-c.ic26" }, { "bios", "common_system147b_bootrom.ic1" }, { "boot", "atfile0:PMAAC.elf" } }}, { "pacmanbr", toml::table { { "system", PS2_SYSTEM_NAMCO_S147 }, { "name", "Pac-Man: Battle Royale" }, { "nand", "pbr102-2-na-mpro-a13_kp006b.ic26" }, { "bios", "common_system147b_bootrom.ic1" }, { "boot", "atfile0:pacmanBR.elf" } }}, { "akaiser", toml::table { { "system", PS2_SYSTEM_NAMCO_S147 }, { "name", "Animal Kaiser: The King of Animals" }, { "nand", "kp005a_ana1004-na-b.ic26" }, { "bios", "common_system147b_bootrom.ic1" }, { "boot", "atfile0:main.elf" }, { "ioboard_mode", 1 } }}, { "akaievo", toml::table { { "system", PS2_SYSTEM_NAMCO_S147 }, { "name", "Animal Kaiser Evolution" }, { "nand", "kp012b_k9k8g08u0b.ic31" }, { "bios", "common_system147b_bootrom.ic1" }, { "boot", "atfile0:main.elf" }, { "ioboard_mode", 1 } }}, { "umilucky", toml::table { { "system", PS2_SYSTEM_NAMCO_S148 }, { "name", "Umimonogatari Lucky Marine Theater" }, { "nand", "uls100-1-na-mpro-b01_kp008a.ic31" }, { "bios", "common_system148_bootrom.ic1" }, { "boot", "atfile0:prog.elf" } }} }; ================================================ FILE: frontend/audio.cpp ================================================ #include #include #include "iris.hpp" namespace iris::audio { void update(void* userdata, SDL_AudioStream* stream, int additional_amount, int total_amount) { iris::instance* iris = (iris::instance*)userdata; if (iris->pause) std::this_thread::sleep_for(std::chrono::milliseconds(1)); if (iris->pause || !additional_amount) return; iris->audio_buf.resize(additional_amount); for (int i = 0; i < additional_amount; i++) { iris->audio_buf[i] = ps2_spu2_get_sample(iris->ps2->spu2, !iris->mute_adma); iris->audio_buf[i].s16[0] *= iris->mute ? 0.0f : iris->volume; iris->audio_buf[i].s16[1] *= iris->mute ? 0.0f : iris->volume; } SDL_PutAudioStreamData(stream, (void*)iris->audio_buf.data(), additional_amount * sizeof(spu2_sample)); } bool init(iris::instance* iris) { SDL_AudioSpec spec; spec.channels = 2; spec.format = SDL_AUDIO_S16; spec.freq = 48000; iris->stream = SDL_OpenAudioDeviceStream(SDL_AUDIO_DEVICE_DEFAULT_PLAYBACK, &spec, iris::audio::update, iris); if (!iris->stream) { fprintf(stderr, "audio: Failed to open audio device\n"); return false; } /* SDL_OpenAudioDeviceStream starts the device paused. You have to tell it to start! */ SDL_ResumeAudioStreamDevice(iris->stream); return true; } void close(iris::instance* iris) { if (!iris->stream) { return; } SDL_PauseAudioStreamDevice(iris->stream); SDL_DestroyAudioStream(iris->stream); iris->stream = nullptr; } bool mute(iris::instance* iris) { iris->prev_mute = iris->mute; iris->mute = true; return iris->prev_mute; } void unmute(iris::instance* iris) { iris->mute = iris->prev_mute; } } ================================================ FILE: frontend/config.hpp ================================================ #pragma once #ifndef _IRIS_VERSION #define _IRIS_VERSION latest #endif #ifndef _IRIS_COMMIT #define _IRIS_COMMIT latest #endif #ifndef _IRIS_OSVERSION #define _IRIS_OSVERSION unknown #endif #define STR1(m) #m #define STR(m) STR1(m) #define IRIS_TITLE "Iris (" STR(_IRIS_VERSION) ")" #define IRIS_VULKAN_API_VERSION VK_API_VERSION_1_2 ================================================ FILE: frontend/elf.cpp ================================================ #include #include #include #include "iris.hpp" #ifdef __linux__ #include #else #include "elf.h" #endif namespace iris::elf { void load_symbols_from_memory(iris::instance* iris, char* buf) { if (!buf) return; // Clear previous symbols iris->symbols.clear(); iris->strtab.clear(); Elf32_Ehdr* ehdr = (Elf32_Ehdr*)buf; // Parse ELF header if (strncmp((char*)ehdr->e_ident, "\x7f" "ELF", 4) != 0) { printf("elf: Invalid ELF magic number\n"); return; } // Read symbol table header Elf32_Shdr* symtab = nullptr; for (int i = 0; i < ehdr->e_shnum; i++) { Elf32_Shdr* shdr = (Elf32_Shdr*)(buf + ehdr->e_shoff + (i * ehdr->e_shentsize)); if ((shdr->sh_type == SHT_STRTAB) && (i != ehdr->e_shstrndx)) { printf("elf: Loading string table size=%x offset=%x\n", shdr->sh_size, shdr->sh_offset); // Get string table iris->strtab.resize(shdr->sh_size); memcpy(iris->strtab.data(), buf + shdr->sh_offset, shdr->sh_size); } if (shdr->sh_type == SHT_SYMTAB) { symtab = shdr; printf("elf: Found symbol table size=%x offset=%x\n", symtab->sh_size, symtab->sh_offset); } } // No symbol table present if (!symtab) { printf("elf: No symbol table found\n"); return; } if (!symtab->sh_entsize) { printf("elf: Invalid symbol table entry size\n"); return; } if (!symtab->sh_size) { printf("elf: Symbol table is empty\n"); return; } size_t symbol_count = symtab->sh_size / symtab->sh_entsize; printf("elf: Found symbol table with %d symbols\n", symbol_count); // Read symbol table Elf32_Sym* sym; for (int i = 0; i < symbol_count; i++) { sym = (Elf32_Sym*)(buf + symtab->sh_offset + (i * symtab->sh_entsize)); if (ELF32_ST_TYPE(sym->st_info) != STT_FUNC) continue; elf_symbol symbol; symbol.name = (char*)(iris->strtab.data() + sym->st_name); symbol.addr = sym->st_value; symbol.size = sym->st_size; // printf("symbol: %s at 0x%08x\n", symbol.name, symbol.addr); iris->symbols.push_back(symbol); } } bool load_symbols_from_disc(iris::instance* iris) { if (!iris->ps2 || !iris->ps2->cdvd || !iris->ps2->cdvd->disc) { printf("elf: No disc loaded\n"); return false; } char* elf = disc_read_boot_elf(iris->ps2->cdvd->disc, 0); load_symbols_from_memory(iris, elf); free(elf); return true; } bool load_symbols_from_file(iris::instance* iris, std::string path) { if (path.empty()) { printf("elf: No file path provided\n"); return false; } FILE* file = fopen(path.c_str(), "rb"); if (!file) { printf("elf: Failed to open file %s\n", path.c_str()); return false; } fseek(file, 0, SEEK_END); size_t size = ftell(file); fseek(file, 0, SEEK_SET); char* buf = new char[size]; fread(buf, 1, size, file); fclose(file); load_symbols_from_memory(iris, buf); delete[] buf; return true; } } ================================================ FILE: frontend/emu.cpp ================================================ #include "iris.hpp" #include "arcade.hpp" #include #include namespace iris::emu { bool init(iris::instance* iris) { // Initialize our emulator state iris->ps2 = ps2_create(); ps2_init(iris->ps2); ps2_init_tty_handler(iris->ps2, PS2_TTY_EE, iris::handle_ee_tty_event, iris); ps2_init_tty_handler(iris->ps2, PS2_TTY_IOP, iris::handle_iop_tty_event, iris); ps2_init_tty_handler(iris->ps2, PS2_TTY_SYSMEM, iris::handle_sysmem_tty_event, iris); iris->ds[0] = ds_attach(iris->ps2->sio2, 0); return true; } void destroy(iris::instance* iris) { if (iris->ps2) ps2_destroy(iris->ps2); } const char* get_extension(const char* path) { const char* dot = strrchr(path, '.'); if (!dot || dot == path) return nullptr; return dot + 1; } template std::optional query_arcade_value(std::string arcade_name, std::string key) { auto it = g_arcade_definitions.find(arcade_name); if (it == g_arcade_definitions.end()) return {}; auto arcade_table = it->second.as_table(); auto key_it = arcade_table->find(key); if (key_it == arcade_table->end()) return {}; if constexpr (std::is_same_v) { return key_it->second.as_string()->get(); } else if constexpr (std::is_integral_v) { return key_it->second.as_integer()->get(); } else if constexpr (std::is_same_v) { return key_it->second.as_boolean()->get(); } else if constexpr (std::is_floating_point_v) { return key_it->second.as_floating_point()->get(); } else if constexpr (std::is_array_v) { return key_it->second.as_array(); } else { return {}; } return {}; } bool load_arcade(iris::instance* iris, std::string path) { std::filesystem::path base_path(path); std::string id = base_path.stem().string(); std::string name = query_arcade_value(id, "name").value_or(""); if (!name.size()) { return false; } printf("emu: Loading arcade game \"%s\"...\n", id.c_str(), name.c_str()); int system = query_arcade_value(id, "system").value_or(PS2_SYSTEM_AUTO); switch (system) { case PS2_SYSTEM_NAMCO_S147: case PS2_SYSTEM_NAMCO_S148: { std::string bios = query_arcade_value(id, "bios").value_or(""); std::string nand = query_arcade_value(id, "nand").value_or(""); int ioboard_mode = query_arcade_value(id, "ioboard_mode").value_or(0); std::filesystem::path bios_path = base_path / bios; std::filesystem::path nand_path = base_path / nand; std::filesystem::path sram_path = base_path / "sram.bin"; if (!std::filesystem::exists(bios_path)) { printf("emu: Couldn't find bootrom file \"%s\"\n", bios_path.string().c_str()); push_info(iris, "Couldn't start arcade game (Missing bootrom)"); return false; } if (!std::filesystem::exists(nand_path)) { printf("emu: Couldn't find NAND file \"%s\"\n", nand_path.string().c_str()); push_info(iris, "Couldn't start arcade game (Missing NAND)"); return false; } ps2_set_system(iris->ps2, system); ps2_load_bios(iris->ps2, bios_path.string().c_str()); s14x_nand_load(iris->ps2->s14x_nand, nand_path.string().c_str()); s14x_sram_load(iris->ps2->s14x_sram, sram_path.string().c_str()); if (iris->ps2->s14x_ioboard) { iris->ps2->s14x_ioboard->mode = ioboard_mode; } ps2_reset(iris->ps2); iris->loaded = name + " (" + id + ")"; if (iris->autostart) { iris->pause = false; } return true; } break; default: { const char* names[] = { "Auto", "Retail (Fat)", "Retail (Slim)", "PSX DESR", "TEST unit (DTL-H)", "TOOL unit (DTL-T)", "Konami Python", "Konami Python 2", "Namco System 147", "Namco System 148", "Namco System 246", "Namco System 256" }; printf("emu: %s isn't supported yet\n", names[system]); } break; } return false; } int attach_memory_card(iris::instance* iris, int slot, const char* path) { detach_memory_card(iris, slot); FILE* file = fopen(path, "rb"); if (!file) { return 0; } fseek(file, 0, SEEK_END); int size = ftell(file); fclose(file); if (size < 0x800000) { struct ps1_mcd_state* mcd = ps1_mcd_attach(iris->ps2->sio2, slot+2, path); std::string ext = get_extension(path); if (ext == "psm" || ext == "pocket") { ps1_mcd_set_type(mcd, 1); iris->mcd_slot_type[slot] = 3; } else { ps1_mcd_set_type(mcd, 0); iris->mcd_slot_type[slot] = 2; } return 1; } mcd_attach(iris->ps2->sio2, slot+2, path); iris->mcd_slot_type[slot] = 1; return 1; } void detach_memory_card(iris::instance* iris, int slot) { iris->mcd_slot_type[slot] = 0; ps2_sio2_detach_device(iris->ps2->sio2, slot+2); } const char* g_system_names[] = { "Auto", "PlayStation 2 (Fat)", "PlayStation 2 (Slim)", "PSX DESR", "TEST Unit", "TOOL Unit", "Konami Python", "Konami Python 2", "Namco System 147", "Namco System 148", "Namco System 246", "Namco System 256" }; const char* get_system_name(iris::instance* iris, int system) { return g_system_names[system]; } const char* get_current_system_name(iris::instance* iris) { switch (iris->system) { case PS2_SYSTEM_AUTO: return get_system_name(iris, iris->ps2->detected_system); case PS2_SYSTEM_RETAIL: case PS2_SYSTEM_RETAIL_DECKARD: case PS2_SYSTEM_DESR: case PS2_SYSTEM_TEST: case PS2_SYSTEM_TOOL: case PS2_SYSTEM_KONAMI_PYTHON: case PS2_SYSTEM_KONAMI_PYTHON2: case PS2_SYSTEM_NAMCO_S147: case PS2_SYSTEM_NAMCO_S148: case PS2_SYSTEM_NAMCO_S246: case PS2_SYSTEM_NAMCO_S256: return g_system_names[iris->system]; default: return "Unknown"; } } int get_system_count(iris::instance* iris) { return sizeof(g_system_names) / sizeof(const char*); } } ================================================ FILE: frontend/handlers.cpp ================================================ #include "iris.hpp" namespace iris { void handle_ee_tty_event(void* udata, char c) { iris::instance* iris = (iris::instance*)udata; if (c == '\r') return; if (c == '\n') { iris->ee_log.push_back(""); } else { iris->ee_log.back().push_back(c); } } void handle_iop_tty_event(void* udata, char c) { iris::instance* iris = (iris::instance*)udata; if (c == '\r') return; if (c == '\n') { iris->iop_log.push_back(""); } else { iris->iop_log.back().push_back(c); } } void handle_sysmem_tty_event(void* udata, char c) { iris::instance* iris = (iris::instance*)udata; if (c == '\r') return; if (c == '\n') { iris->sysmem_log.push_back(""); } else { iris->sysmem_log.back().push_back(c); } } } ================================================ FILE: frontend/imgui.cpp ================================================ #include "iris.hpp" #include "imgui.h" #include "imgui_impl_sdl3.h" #include "imgui_impl_vulkan.h" #include "implot.h" #include #include #include // External includes #include "res/IconsMaterialSymbols.h" // INCBIN stuff #define INCBIN_PREFIX g_ #define INCBIN_STYLE INCBIN_STYLE_SNAKE #include "incbin.h" INCBIN(roboto, "../res/Roboto-Regular.ttf"); INCBIN(roboto_black, "../res/Roboto-Black.ttf"); INCBIN(symbols, "../res/MaterialSymbolsRounded.ttf"); INCBIN(firacode, "../res/FiraCode-Regular.ttf"); INCBIN(ps1_memory_card_icon, "../res/ps1_mcd.png"); INCBIN(ps2_memory_card_icon, "../res/ps2_mcd.png"); INCBIN(dualshock2_icon, "../res/ds2.png"); INCBIN(pocketstation_icon, "../res/pocketstation.png"); INCBIN(iris_icon, "../res/iris.png"); INCBIN(vertex_shader, "../shaders/vertex.spv"); INCBIN(fragment_shader, "../shaders/fragment.spv"); #include "stb_image.h" #define VOLK_IMPLEMENTATION #include namespace iris::imgui { static constexpr uint32_t DESCRIPTOR_SET_RING_SIZE = 8; static const ImWchar g_icon_range[] = { ICON_MIN_MS, ICON_MAX_16_MS, 0 }; static bool setup_vulkan_window(iris::instance* iris, ImGui_ImplVulkanH_Window* wd, int width, int height, bool vsync) { wd->Surface = iris->surface; VkAttachmentDescription attachment = {}; attachment.format = wd->SurfaceFormat.format; attachment.samples = VK_SAMPLE_COUNT_1_BIT; attachment.loadOp = VK_ATTACHMENT_LOAD_OP_LOAD; attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; attachment.initialLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; attachment.finalLayout = VK_IMAGE_LAYOUT_PRESENT_SRC_KHR; wd->AttachmentDesc = attachment; // Check for WSI support VkBool32 res; vkGetPhysicalDeviceSurfaceSupportKHR(iris->physical_device, iris->queue_family, wd->Surface, &res); if (!res) { fprintf(stderr, "imgui: No WSI support on physical device\n"); return false; } // Select Surface Format const VkFormat requestSurfaceImageFormat[] = { VK_FORMAT_B8G8R8A8_UNORM, VK_FORMAT_R8G8B8A8_UNORM, VK_FORMAT_B8G8R8_UNORM, VK_FORMAT_R8G8B8_UNORM }; const VkColorSpaceKHR requestSurfaceColorSpace = VK_COLORSPACE_SRGB_NONLINEAR_KHR; wd->SurfaceFormat = ImGui_ImplVulkanH_SelectSurfaceFormat( iris->physical_device, wd->Surface, requestSurfaceImageFormat, (size_t)IM_ARRAYSIZE(requestSurfaceImageFormat), requestSurfaceColorSpace ); // Select Present Mode std::vector present_modes; if (vsync) { present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); } else { present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); present_modes.push_back(VK_PRESENT_MODE_IMMEDIATE_KHR); present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); } wd->PresentMode = ImGui_ImplVulkanH_SelectPresentMode( iris->physical_device, wd->Surface, present_modes.data(), present_modes.size() ); // Create SwapChain, RenderPass, Framebuffer, etc. IM_ASSERT(iris->min_image_count >= 2); ImGui_ImplVulkanH_CreateOrResizeWindow( iris->instance, iris->physical_device, iris->device, wd, iris->queue_family, VK_NULL_HANDLE, width, height, iris->min_image_count, 0 ); return true; } void set_vsync(iris::instance* iris, bool vsync) { std::vector present_modes; if (vsync) { present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); } else { present_modes.push_back(VK_PRESENT_MODE_MAILBOX_KHR); present_modes.push_back(VK_PRESENT_MODE_IMMEDIATE_KHR); present_modes.push_back(VK_PRESENT_MODE_FIFO_KHR); } iris->main_window_data.PresentMode = ImGui_ImplVulkanH_SelectPresentMode( iris->physical_device, iris->main_window_data.Surface, present_modes.data(), present_modes.size() ); render::refresh(iris); } bool setup_fonts(iris::instance* iris, ImGuiIO& io) { io.Fonts->AddFontDefault(); ImFontConfig config; config.MergeMode = true; config.GlyphMinAdvanceX = 13.0f; config.GlyphOffset = ImVec2(0.0f, 4.0f); config.FontDataOwnedByAtlas = false; ImFontConfig config_no_own; config_no_own.FontDataOwnedByAtlas = false; iris->font_small_code = io.Fonts->AddFontFromMemoryTTF((void*)g_firacode_data, g_firacode_size, 12.0F, &config_no_own); iris->font_code = io.Fonts->AddFontFromMemoryTTF((void*)g_firacode_data, g_firacode_size, 16.0F, &config_no_own); iris->font_small = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_data, g_roboto_size, 12.0F, &config_no_own); iris->font_heading = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_data, g_roboto_size, 20.0F, &config_no_own); iris->font_body = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_data, g_roboto_size, 16.0F, &config_no_own); iris->font_icons = io.Fonts->AddFontFromMemoryTTF((void*)g_symbols_data, g_symbols_size, 20.0F, &config, g_icon_range); iris->font_icons_big = io.Fonts->AddFontFromMemoryTTF((void*)g_symbols_data, g_symbols_size, 50.0F, &config_no_own, g_icon_range); iris->font_black = io.Fonts->AddFontFromMemoryTTF((void*)g_roboto_black_data, g_roboto_black_size, 30.0F, &config_no_own); if (!iris->font_small_code || !iris->font_code || !iris->font_small || !iris->font_heading || !iris->font_body || !iris->font_icons || !iris->font_icons_big || !iris->font_black) { return false; } io.FontDefault = iris->font_icons; return true; } void set_theme(iris::instance* iris, int theme, bool set_bg_color) { // Init 'Granite' theme ImGuiStyle& style = ImGui::GetStyle(); style.WindowPadding = ImVec2(8.0, 8.0); style.FramePadding = ImVec2(5.0, 5.0); style.ItemSpacing = ImVec2(8.0, 6.0); style.WindowBorderSize = 0; style.ChildBorderSize = 0; style.FrameBorderSize = 1; style.PopupBorderSize = 0; style.TabBorderSize = 0; style.TabBarBorderSize = 0; style.WindowRounding = 6; style.ChildRounding = 4; style.FrameRounding = 4; style.PopupRounding = 4; style.ScrollbarRounding = 9; style.GrabRounding = 2; style.TabRounding = 4; style.WindowTitleAlign = ImVec2(0.5, 0.5); style.DockingSeparatorSize = 0; style.SeparatorTextBorderSize = 1; style.SeparatorTextPadding = ImVec2(20, 0); // Use ImGui's default dark style as a base for our own style ImGui::StyleColorsDark(); switch (theme) { case IRIS_THEME_GRANITE: { ImVec4* colors = style.Colors; colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_TextDisabled] = ImVec4(0.35f, 0.35f, 0.35f, 1.00f); colors[ImGuiCol_WindowBg] = ImVec4(0.02f, 0.02f, 0.02f, 1.00f); colors[ImGuiCol_ChildBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_PopupBg] = ImVec4(0.07f, 0.09f, 0.10f, 1.00f); colors[ImGuiCol_Border] = ImVec4(0.10f, 0.12f, 0.13f, 1.00f); colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_FrameBg] = ImVec4(0.10f, 0.12f, 0.13f, 0.50f); colors[ImGuiCol_FrameBgHovered] = ImVec4(0.20f, 0.24f, 0.26f, 0.50f); colors[ImGuiCol_FrameBgActive] = ImVec4(0.29f, 0.35f, 0.39f, 0.50f); colors[ImGuiCol_TitleBg] = ImVec4(0.04f, 0.04f, 0.04f, 1.00f); colors[ImGuiCol_TitleBgActive] = ImVec4(0.16f, 0.20f, 0.22f, 1.00f); colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f); colors[ImGuiCol_MenuBarBg] = ImVec4(0.14f, 0.14f, 0.14f, 1.00f); colors[ImGuiCol_ScrollbarBg] = ImVec4(0.02f, 0.02f, 0.02f, 0.53f); colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.31f, 0.31f, 0.31f, 1.00f); colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.41f, 0.41f, 0.41f, 1.00f); colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.51f, 0.51f, 0.51f, 1.00f); colors[ImGuiCol_CheckMark] = ImVec4(0.88f, 0.88f, 0.88f, 1.00f); colors[ImGuiCol_SliderGrab] = ImVec4(0.39f, 0.47f, 0.52f, 0.50f); colors[ImGuiCol_SliderGrabActive] = ImVec4(0.49f, 0.59f, 0.65f, 0.50f); colors[ImGuiCol_Button] = ImVec4(0.13f, 0.16f, 0.17f, 0.25f); colors[ImGuiCol_ButtonHovered] = ImVec4(0.20f, 0.24f, 0.26f, 0.50f); colors[ImGuiCol_ButtonActive] = ImVec4(0.29f, 0.35f, 0.39f, 0.50f); colors[ImGuiCol_Header] = ImVec4(0.13f, 0.16f, 0.17f, 0.50f); colors[ImGuiCol_HeaderHovered] = ImVec4(0.20f, 0.24f, 0.26f, 0.50f); colors[ImGuiCol_HeaderActive] = ImVec4(0.29f, 0.35f, 0.39f, 0.50f); colors[ImGuiCol_Separator] = ImVec4(0.23f, 0.28f, 0.30f, 1.00f); colors[ImGuiCol_SeparatorHovered] = ImVec4(0.33f, 0.39f, 0.43f, 1.00f); colors[ImGuiCol_SeparatorActive] = ImVec4(0.38f, 0.46f, 0.51f, 1.00f); colors[ImGuiCol_ResizeGrip] = ImVec4(0.15f, 0.20f, 0.22f, 1.00f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.00f, 0.30f, 0.25f, 1.00f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.00f, 0.39f, 0.32f, 1.00f); colors[ImGuiCol_InputTextCursor] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_TabHovered] = ImVec4(0.23f, 0.28f, 0.30f, 0.59f); colors[ImGuiCol_Tab] = ImVec4(0.20f, 0.24f, 0.26f, 0.59f); colors[ImGuiCol_TabSelected] = ImVec4(0.26f, 0.31f, 0.35f, 0.59f); colors[ImGuiCol_TabSelectedOverline] = ImVec4(0.00f, 0.39f, 0.32f, 1.00f); colors[ImGuiCol_TabDimmed] = ImVec4(0.07f, 0.10f, 0.15f, 0.97f); colors[ImGuiCol_TabDimmedSelected] = ImVec4(0.10f, 0.12f, 0.13f, 1.00f); colors[ImGuiCol_TabDimmedSelectedOverline] = ImVec4(0.50f, 0.50f, 0.50f, 0.00f); colors[ImGuiCol_DockingPreview] = ImVec4(0.15f, 0.20f, 0.22f, 1.00f); colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(1.00f, 0.43f, 0.35f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(0.90f, 0.70f, 0.00f, 1.00f); colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); colors[ImGuiCol_TableHeaderBg] = ImVec4(0.19f, 0.19f, 0.20f, 1.00f); colors[ImGuiCol_TableBorderStrong] = ImVec4(0.31f, 0.31f, 0.35f, 1.00f); colors[ImGuiCol_TableBorderLight] = ImVec4(0.23f, 0.23f, 0.25f, 1.00f); colors[ImGuiCol_TableRowBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); colors[ImGuiCol_TableRowBgAlt] = ImVec4(1.00f, 1.00f, 1.00f, 0.06f); colors[ImGuiCol_TextLink] = ImVec4(0.26f, 0.59f, 0.98f, 1.00f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.15f, 0.20f, 0.22f, 1.00f); colors[ImGuiCol_DragDropTarget] = ImVec4(0.29f, 0.38f, 0.42f, 1.00f); colors[ImGuiCol_NavCursor] = ImVec4(0.15f, 0.20f, 0.22f, 1.00f); colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.00f, 0.00f, 0.00f, 0.35f); if (!set_bg_color) break; iris->clear_value.color.float32[0] = 0.11f; iris->clear_value.color.float32[1] = 0.11f; iris->clear_value.color.float32[2] = 0.11f; iris->clear_value.color.float32[3] = 1.00f; } break; case IRIS_THEME_IMGUI_DARK: { ImGui::StyleColorsDark(); if (!set_bg_color) break; iris->clear_value.color.float32[0] = 0.11f; iris->clear_value.color.float32[1] = 0.11f; iris->clear_value.color.float32[2] = 0.11f; iris->clear_value.color.float32[3] = 1.00f; } break; case IRIS_THEME_IMGUI_LIGHT: { ImGui::StyleColorsLight(); if (!set_bg_color) break; iris->clear_value.color.float32[0] = 0.89f; iris->clear_value.color.float32[1] = 0.89f; iris->clear_value.color.float32[2] = 0.89f; iris->clear_value.color.float32[3] = 1.00f; } break; case IRIS_THEME_IMGUI_CLASSIC: { ImGui::StyleColorsClassic(); if (!set_bg_color) break; iris->clear_value.color.float32[0] = 0.11f; iris->clear_value.color.float32[1] = 0.11f; iris->clear_value.color.float32[2] = 0.11f; iris->clear_value.color.float32[3] = 1.00f; } break; case IRIS_THEME_CHERRY: { // cherry colors, 3 intensities #define HI(v) ImVec4(0.502f, 0.075f, 0.256f, v) #define MED(v) ImVec4(0.455f, 0.198f, 0.301f, v) #define LOW(v) ImVec4(0.232f, 0.201f, 0.271f, v) // backgrounds #define BG(v) ImVec4(0.200f, 0.220f, 0.270f, v) // text #define TEXT(v) ImVec4(0.860f, 0.930f, 0.890f, v) auto &style = ImGui::GetStyle(); style.Colors[ImGuiCol_Text] = TEXT(0.78f); style.Colors[ImGuiCol_TextDisabled] = TEXT(0.28f); style.Colors[ImGuiCol_WindowBg] = ImVec4(0.13f, 0.14f, 0.17f, 1.00f); style.Colors[ImGuiCol_ChildBg] = BG( 0.58f); style.Colors[ImGuiCol_PopupBg] = BG( 0.9f); style.Colors[ImGuiCol_Border] = ImVec4(0.31f, 0.31f, 1.00f, 0.00f); style.Colors[ImGuiCol_BorderShadow] = ImVec4(0.00f, 0.00f, 0.00f, 0.00f); style.Colors[ImGuiCol_FrameBg] = BG( 1.00f); style.Colors[ImGuiCol_FrameBgHovered] = MED( 0.78f); style.Colors[ImGuiCol_FrameBgActive] = MED( 1.00f); style.Colors[ImGuiCol_TitleBg] = LOW( 1.00f); style.Colors[ImGuiCol_TitleBgActive] = HI( 1.00f); style.Colors[ImGuiCol_TitleBgCollapsed] = BG( 0.75f); style.Colors[ImGuiCol_MenuBarBg] = BG( 0.47f); style.Colors[ImGuiCol_ScrollbarBg] = BG( 1.00f); style.Colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.09f, 0.15f, 0.16f, 1.00f); style.Colors[ImGuiCol_ScrollbarGrabHovered] = MED( 0.78f); style.Colors[ImGuiCol_ScrollbarGrabActive] = MED( 1.00f); style.Colors[ImGuiCol_CheckMark] = ImVec4(0.71f, 0.22f, 0.27f, 1.00f); style.Colors[ImGuiCol_SliderGrab] = ImVec4(0.47f, 0.77f, 0.83f, 0.14f); style.Colors[ImGuiCol_SliderGrabActive] = ImVec4(0.71f, 0.22f, 0.27f, 1.00f); style.Colors[ImGuiCol_Button] = ImVec4(0.47f, 0.77f, 0.83f, 0.14f); style.Colors[ImGuiCol_ButtonHovered] = MED( 0.86f); style.Colors[ImGuiCol_ButtonActive] = MED( 1.00f); style.Colors[ImGuiCol_Header] = MED( 0.76f); style.Colors[ImGuiCol_HeaderHovered] = MED( 0.86f); style.Colors[ImGuiCol_HeaderActive] = HI( 1.00f); style.Colors[ImGuiCol_ResizeGrip] = ImVec4(0.47f, 0.77f, 0.83f, 0.04f); style.Colors[ImGuiCol_ResizeGripHovered] = MED( 0.78f); style.Colors[ImGuiCol_ResizeGripActive] = MED( 1.00f); style.Colors[ImGuiCol_PlotLines] = TEXT(0.63f); style.Colors[ImGuiCol_PlotLinesHovered] = MED( 1.00f); style.Colors[ImGuiCol_PlotHistogram] = TEXT(0.63f); style.Colors[ImGuiCol_PlotHistogramHovered] = MED( 1.00f); style.Colors[ImGuiCol_TextSelectedBg] = MED( 0.43f); style.Colors[ImGuiCol_ModalWindowDimBg] = BG( 0.73f); #undef HI #undef MED #undef LOW #undef BG #undef TEXT if (!set_bg_color) break; iris->clear_value.color.float32[0] = 0.20f * 0.5f; iris->clear_value.color.float32[1] = 0.22f * 0.5f; iris->clear_value.color.float32[2] = 0.27f * 0.5f; iris->clear_value.color.float32[3] = 1.00f; } break; case IRIS_THEME_SOURCE: { ImVec4* colors = ImGui::GetStyle().Colors; colors[ImGuiCol_Text] = ImVec4(1.00f, 1.00f, 1.00f, 1.00f); colors[ImGuiCol_TextDisabled] = ImVec4(0.50f, 0.50f, 0.50f, 1.00f); colors[ImGuiCol_WindowBg] = ImVec4(0.29f, 0.34f, 0.26f, 1.00f); colors[ImGuiCol_ChildBg] = ImVec4(0.29f, 0.34f, 0.26f, 1.00f); colors[ImGuiCol_PopupBg] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f); colors[ImGuiCol_Border] = ImVec4(0.54f, 0.57f, 0.51f, 0.50f); colors[ImGuiCol_BorderShadow] = ImVec4(0.14f, 0.16f, 0.11f, 0.52f); colors[ImGuiCol_FrameBg] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f); colors[ImGuiCol_FrameBgHovered] = ImVec4(0.27f, 0.30f, 0.23f, 1.00f); colors[ImGuiCol_FrameBgActive] = ImVec4(0.30f, 0.34f, 0.26f, 1.00f); colors[ImGuiCol_TitleBg] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f); colors[ImGuiCol_TitleBgActive] = ImVec4(0.29f, 0.34f, 0.26f, 1.00f); colors[ImGuiCol_TitleBgCollapsed] = ImVec4(0.00f, 0.00f, 0.00f, 0.51f); colors[ImGuiCol_MenuBarBg] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f); colors[ImGuiCol_ScrollbarBg] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f); colors[ImGuiCol_ScrollbarGrab] = ImVec4(0.28f, 0.32f, 0.24f, 1.00f); colors[ImGuiCol_ScrollbarGrabHovered] = ImVec4(0.25f, 0.30f, 0.22f, 1.00f); colors[ImGuiCol_ScrollbarGrabActive] = ImVec4(0.23f, 0.27f, 0.21f, 1.00f); colors[ImGuiCol_CheckMark] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f); colors[ImGuiCol_SliderGrab] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f); colors[ImGuiCol_SliderGrabActive] = ImVec4(0.54f, 0.57f, 0.51f, 0.50f); colors[ImGuiCol_Button] = ImVec4(0.29f, 0.34f, 0.26f, 0.40f); colors[ImGuiCol_ButtonHovered] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f); colors[ImGuiCol_ButtonActive] = ImVec4(0.54f, 0.57f, 0.51f, 0.50f); colors[ImGuiCol_Header] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f); colors[ImGuiCol_HeaderHovered] = ImVec4(0.35f, 0.42f, 0.31f, 0.60f); colors[ImGuiCol_HeaderActive] = ImVec4(0.54f, 0.57f, 0.51f, 0.50f); colors[ImGuiCol_Separator] = ImVec4(0.14f, 0.16f, 0.11f, 1.00f); colors[ImGuiCol_SeparatorHovered] = ImVec4(0.54f, 0.57f, 0.51f, 1.00f); colors[ImGuiCol_SeparatorActive] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f); colors[ImGuiCol_ResizeGrip] = ImVec4(0.19f, 0.23f, 0.18f, 0.00f); colors[ImGuiCol_ResizeGripHovered] = ImVec4(0.54f, 0.57f, 0.51f, 1.00f); colors[ImGuiCol_ResizeGripActive] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f); colors[ImGuiCol_Tab] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f); colors[ImGuiCol_TabHovered] = ImVec4(0.54f, 0.57f, 0.51f, 0.78f); colors[ImGuiCol_TabActive] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f); colors[ImGuiCol_TabUnfocused] = ImVec4(0.24f, 0.27f, 0.20f, 1.00f); colors[ImGuiCol_TabUnfocusedActive] = ImVec4(0.35f, 0.42f, 0.31f, 1.00f); colors[ImGuiCol_DockingPreview] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f); colors[ImGuiCol_DockingEmptyBg] = ImVec4(0.20f, 0.20f, 0.20f, 1.00f); colors[ImGuiCol_PlotLines] = ImVec4(0.61f, 0.61f, 0.61f, 1.00f); colors[ImGuiCol_PlotLinesHovered] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f); colors[ImGuiCol_PlotHistogram] = ImVec4(1.00f, 0.78f, 0.28f, 1.00f); colors[ImGuiCol_PlotHistogramHovered] = ImVec4(1.00f, 0.60f, 0.00f, 1.00f); colors[ImGuiCol_TextSelectedBg] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f); colors[ImGuiCol_DragDropTarget] = ImVec4(0.73f, 0.67f, 0.24f, 1.00f); colors[ImGuiCol_NavHighlight] = ImVec4(0.59f, 0.54f, 0.18f, 1.00f); colors[ImGuiCol_NavWindowingHighlight] = ImVec4(1.00f, 1.00f, 1.00f, 0.70f); colors[ImGuiCol_NavWindowingDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.20f); colors[ImGuiCol_ModalWindowDimBg] = ImVec4(0.80f, 0.80f, 0.80f, 0.35f); if (!set_bg_color) break; iris->clear_value.color.float32[0] = 0.13f; iris->clear_value.color.float32[1] = 0.15f; iris->clear_value.color.float32[2] = 0.11f; iris->clear_value.color.float32[3] = 1.00f; } break; } ImPlotStyle& pstyle = ImPlot::GetStyle(); pstyle.MinorGridSize = ImVec2(0.0f, 0.0f); pstyle.MajorGridSize = ImVec2(0.0f, 0.0f); pstyle.MinorTickLen = ImVec2(0.0f, 0.0f); pstyle.MajorTickLen = ImVec2(0.0f, 0.0f); pstyle.PlotDefaultSize = ImVec2(250.0f, 150.0f); pstyle.PlotPadding = ImVec2(0.0f, 0.0f); pstyle.LegendPadding = ImVec2(0.0f, 0.0f); pstyle.LegendInnerPadding = ImVec2(0.0f, 0.0f); pstyle.LineWeight = 2.0f; pstyle.Colors[ImPlotCol_Line] = ImVec4(0.0f, 1.0f, 0.2f, 1.0f); pstyle.Colors[ImPlotCol_FrameBg] = ImVec4(0.0f, 0.0f, 0.0f, 0.0f); pstyle.Colors[ImPlotCol_PlotBg] = ImVec4(0.0f, 0.0f, 0.0f, 0.0f); } void set_codeview_scheme(iris::instance* iris, int scheme) { switch (scheme) { default: case IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_DARK: { iris->codeview_color_text = IM_COL32(131, 148, 150, 255); iris->codeview_color_comment = IM_COL32(88, 110, 117, 255); iris->codeview_color_mnemonic = IM_COL32(211, 167, 30, 255); iris->codeview_color_number = IM_COL32(138, 143, 226, 255); iris->codeview_color_register = IM_COL32(68, 169, 240, 255); iris->codeview_color_other = IM_COL32(89, 89, 89, 255); iris->codeview_color_background = IM_COL32(0, 43, 54, 255); iris->codeview_color_highlight = IM_COL32(7, 54, 66, 255); } break; case IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_LIGHT: { iris->codeview_color_text = IM_COL32(101, 123, 131, 255); iris->codeview_color_comment = IM_COL32(147, 161, 161, 255); iris->codeview_color_mnemonic = IM_COL32(147, 101, 21, 255); iris->codeview_color_number = IM_COL32(101, 123, 179, 255); iris->codeview_color_register = IM_COL32(38, 139, 210, 255); iris->codeview_color_other = IM_COL32(88, 110, 117, 255); iris->codeview_color_background = IM_COL32(253, 246, 227, 255); iris->codeview_color_highlight = IM_COL32(238, 232, 213, 255); } break; case IRIS_CODEVIEW_COLOR_SCHEME_ONE_DARK_PRO: { iris->codeview_color_text = IM_COL32(171, 178, 191, 255); iris->codeview_color_comment = IM_COL32(92, 99, 112, 255); iris->codeview_color_mnemonic = IM_COL32(198, 120, 221, 255); iris->codeview_color_number = IM_COL32(209, 154, 102, 255); iris->codeview_color_register = IM_COL32(97, 175, 239, 255); iris->codeview_color_other = IM_COL32(171, 178, 191, 255); iris->codeview_color_background = IM_COL32(40, 44, 52, 255); iris->codeview_color_highlight = IM_COL32(60, 64, 72, 255); } break; case IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_LATTE: { iris->codeview_color_text = IM_COL32(76, 79, 105, 255); iris->codeview_color_comment = IM_COL32(124, 127, 147, 255); iris->codeview_color_mnemonic = IM_COL32(136, 57, 239, 255); iris->codeview_color_number = IM_COL32(254, 100, 11, 255); iris->codeview_color_register = IM_COL32(4, 165, 229, 255); iris->codeview_color_other = IM_COL32(114, 135, 253, 255); iris->codeview_color_background = IM_COL32(239, 241, 245, 255); iris->codeview_color_highlight = IM_COL32(204, 208, 218, 255); } break; case IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_FRAPPE: { iris->codeview_color_text = IM_COL32(198, 208, 245, 255); iris->codeview_color_comment = IM_COL32(148, 156, 187, 255); iris->codeview_color_mnemonic = IM_COL32(202, 158, 230, 255); iris->codeview_color_number = IM_COL32(239, 159, 118, 255); iris->codeview_color_register = IM_COL32(153, 209, 219, 255); iris->codeview_color_other = IM_COL32(186, 187, 241, 255); iris->codeview_color_background = IM_COL32(48, 52, 70, 255); iris->codeview_color_highlight = IM_COL32(81, 87, 109, 255); } break; case IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MACCHIATO: { iris->codeview_color_text = IM_COL32(174, 178, 208, 255); iris->codeview_color_comment = IM_COL32(134, 138, 162, 255); iris->codeview_color_mnemonic = IM_COL32(190, 132, 255, 255); iris->codeview_color_number = IM_COL32(245, 142, 110, 255); iris->codeview_color_register = IM_COL32(125, 182, 191, 255); iris->codeview_color_other = IM_COL32(166, 167, 222, 255); iris->codeview_color_background = IM_COL32(58, 60, 79, 255); iris->codeview_color_highlight = IM_COL32(97, 100, 120, 255); } break; case IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MOCHA: { iris->codeview_color_text = IM_COL32(205, 214, 244, 255); iris->codeview_color_comment = IM_COL32(145, 151, 181, 255); iris->codeview_color_mnemonic = IM_COL32(220, 162, 255, 255); iris->codeview_color_number = IM_COL32(248, 159, 128, 255); iris->codeview_color_register = IM_COL32(159, 226, 235, 255); iris->codeview_color_other = IM_COL32(189, 191, 248, 255); iris->codeview_color_background = IM_COL32(46, 49, 64, 255); iris->codeview_color_highlight = IM_COL32(76, 80, 100, 255); } break; } } VkShaderModule create_shader(iris::instance* iris, uint32_t* code, size_t size) { VkShaderModuleCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; info.pCode = code; info.codeSize = size; VkShaderModule shader; if (vkCreateShaderModule(iris->device, &info, nullptr, &shader) != VK_SUCCESS) { return VK_NULL_HANDLE; } return shader; } VkPipeline create_pipeline(iris::instance* iris, VkShaderModule vert_shader, VkShaderModule frag_shader) { // Create pipeline layout VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; VkPipelineLayoutCreateInfo pipeline_layout_info = {}; pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_info.setLayoutCount = 1; pipeline_layout_info.pSetLayouts = &iris->descriptor_set_layout; pipeline_layout_info.pushConstantRangeCount = 0; pipeline_layout_info.pPushConstantRanges = VK_NULL_HANDLE; if (vkCreatePipelineLayout(iris->device, &pipeline_layout_info, nullptr, &pipeline_layout) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to create pipeline layout\n"); return VK_NULL_HANDLE; } iris->pipeline_layout = pipeline_layout; VkRenderPass render_pass = iris->main_window_data.RenderPass; // Create graphics pipeline VkPipelineShaderStageCreateInfo shader_stages[2] = {}; shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; shader_stages[0].module = vert_shader; shader_stages[0].pName = "main"; shader_stages[0].pNext = VK_NULL_HANDLE; shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; shader_stages[1].module = frag_shader; shader_stages[1].pName = "main"; shader_stages[1].pNext = VK_NULL_HANDLE; static const VkDynamicState dynamic_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamic_state_info = {}; dynamic_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamic_state_info.dynamicStateCount = 2; dynamic_state_info.pDynamicStates = dynamic_states; const auto binding_description = vertex::get_binding_description(); const auto attribute_descriptions = vertex::get_attribute_descriptions(); VkPipelineVertexInputStateCreateInfo vertex_input_info = {}; vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_input_info.vertexBindingDescriptionCount = 1; vertex_input_info.pVertexBindingDescriptions = &binding_description; vertex_input_info.vertexAttributeDescriptionCount = 2; vertex_input_info.pVertexAttributeDescriptions = attribute_descriptions.data(); VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {}; input_assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; input_assembly_info.primitiveRestartEnable = VK_FALSE; VkViewport viewport = {}; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = (float)iris->main_window_data.Width; viewport.height = (float)iris->main_window_data.Height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; VkExtent2D extent = {}; extent.width = iris->main_window_data.Width; extent.height = iris->main_window_data.Height; VkRect2D scissor = {}; scissor.offset = {0, 0}; scissor.extent = extent; VkPipelineViewportStateCreateInfo viewport_state_info = {}; viewport_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewport_state_info.viewportCount = 1; viewport_state_info.pViewports = &viewport; viewport_state_info.scissorCount = 1; viewport_state_info.pScissors = &scissor; VkPipelineRasterizationStateCreateInfo rasterizer_info = {}; rasterizer_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer_info.depthClampEnable = VK_FALSE; rasterizer_info.rasterizerDiscardEnable = VK_FALSE; rasterizer_info.polygonMode = VK_POLYGON_MODE_FILL; rasterizer_info.lineWidth = 1.0f; rasterizer_info.cullMode = VK_CULL_MODE_NONE; rasterizer_info.frontFace = VK_FRONT_FACE_CLOCKWISE; rasterizer_info.depthBiasEnable = VK_FALSE; VkPipelineColorBlendAttachmentState blend_attachment_state = {}; blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; blend_attachment_state.blendEnable = VK_FALSE; VkPipelineColorBlendStateCreateInfo blend_state_info{}; blend_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; blend_state_info.logicOpEnable = VK_FALSE; blend_state_info.attachmentCount = 1; blend_state_info.pAttachments = &blend_attachment_state; VkPipelineMultisampleStateCreateInfo multisampling_state_info = {}; multisampling_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling_state_info.sampleShadingEnable = VK_FALSE; multisampling_state_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; VkGraphicsPipelineCreateInfo pipeline_info = {}; pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipeline_info.stageCount = 2; pipeline_info.pStages = shader_stages; pipeline_info.pVertexInputState = &vertex_input_info; pipeline_info.pInputAssemblyState = &input_assembly_info; pipeline_info.pViewportState = &viewport_state_info; pipeline_info.pRasterizationState = &rasterizer_info; pipeline_info.pMultisampleState = &multisampling_state_info; pipeline_info.pDepthStencilState = nullptr; // Optional pipeline_info.pColorBlendState = &blend_state_info; pipeline_info.pDynamicState = &dynamic_state_info; pipeline_info.layout = pipeline_layout; pipeline_info.renderPass = render_pass; pipeline_info.subpass = 0; pipeline_info.pTessellationState = VK_NULL_HANDLE; pipeline_info.basePipelineHandle = VK_NULL_HANDLE; // Optional pipeline_info.basePipelineIndex = -1; // Optional VkPipeline pipeline = VK_NULL_HANDLE; if (vkCreateGraphicsPipelines(iris->device, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &pipeline) != VK_SUCCESS) { return VK_NULL_HANDLE; } vkDestroyShaderModule(iris->device, frag_shader, nullptr); vkDestroyShaderModule(iris->device, vert_shader, nullptr); return pipeline; } bool init(iris::instance* iris) { VkDescriptorSetLayoutBinding sampler_layout_binding = {}; sampler_layout_binding.binding = 0; sampler_layout_binding.descriptorCount = 1; sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; sampler_layout_binding.pImmutableSamplers = nullptr; sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; // VkDescriptorBindingFlags flags = {}; // flags = VK_DESCRIPTOR_BINDING_UPDATE_AFTER_BIND_BIT; // VkDescriptorSetLayoutBindingFlagsCreateInfo binding_flags = {}; // binding_flags.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_BINDING_FLAGS_CREATE_INFO; // binding_flags.pNext = nullptr; // binding_flags.pBindingFlags = &flags; // binding_flags.bindingCount = 1; VkDescriptorSetLayoutCreateInfo layout_info = {}; layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layout_info.bindingCount = 1; layout_info.pBindings = &sampler_layout_binding; // layout_info.flags = VK_DESCRIPTOR_SET_LAYOUT_CREATE_UPDATE_AFTER_BIND_POOL_BIT; // layout_info.pNext = &binding_flags; if (vkCreateDescriptorSetLayout(iris->device, &layout_info, nullptr, &iris->descriptor_set_layout) != VK_SUCCESS) { fprintf(stderr, "imgui: Failed to create descriptor set layout\n"); return false; } std::vector layouts(DESCRIPTOR_SET_RING_SIZE, iris->descriptor_set_layout); iris->descriptor_sets.resize(DESCRIPTOR_SET_RING_SIZE, VK_NULL_HANDLE); VkDescriptorSetAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.descriptorPool = iris->descriptor_pool; alloc_info.descriptorSetCount = DESCRIPTOR_SET_RING_SIZE; alloc_info.pSetLayouts = layouts.data(); if (vkAllocateDescriptorSets(iris->device, &alloc_info, iris->descriptor_sets.data()) != VK_SUCCESS) { fprintf(stderr, "imgui: Failed to allocate descriptor sets\n"); return false; } iris->descriptor_set = iris->descriptor_sets[0]; if (!SDL_Vulkan_CreateSurface(iris->window, iris->instance, VK_NULL_HANDLE, &iris->surface)) { printf("imgui: Failed to create Vulkan surface\n"); return false; } if (!setup_vulkan_window(iris, &iris->main_window_data, iris->window_width, iris->window_height, iris->vsync)) { printf("imgui: Failed to setup Vulkan window\n"); return false; } iris->ini_path = iris->pref_path + "imgui.ini"; IMGUI_CHECKVERSION(); ImGui::CreateContext(); ImPlot::CreateContext(); ImGuiIO& io = ImGui::GetIO(); (void)io; io.ConfigFlags |= ImGuiConfigFlags_NavEnableKeyboard; // Enable Keyboard Controls io.ConfigFlags |= ImGuiConfigFlags_DockingEnable; // Enable Docking if (iris->imgui_enable_viewports) { io.ConfigFlags |= ImGuiConfigFlags_ViewportsEnable; io.ConfigViewportsNoDecoration = false; io.ConfigViewportsNoAutoMerge = true; } io.IniFilename = iris->ini_path.c_str(); // Setup scaling ImGuiStyle& style = ImGui::GetStyle(); style.ScaleAllSizes(iris->main_scale); style.FontScaleDpi = iris->main_scale; style.FontScaleMain = iris->ui_scale; io.ConfigDpiScaleFonts = true; io.ConfigDpiScaleViewports = true; // Setup Platform/Renderer backends if (!ImGui_ImplSDL3_InitForVulkan(iris->window)) { fprintf(stderr, "imgui: Failed to initialize SDL3/Vulkan backend\n"); return false; } ImGui_ImplVulkan_InitInfo init_info = {}; init_info.ApiVersion = IRIS_VULKAN_API_VERSION; init_info.Instance = iris->instance; init_info.PhysicalDevice = iris->physical_device; init_info.Device = iris->device; init_info.QueueFamily = iris->queue_family; init_info.Queue = iris->queue; init_info.PipelineCache = VK_NULL_HANDLE; init_info.DescriptorPool = iris->descriptor_pool; init_info.MinImageCount = iris->min_image_count; init_info.ImageCount = iris->main_window_data.ImageCount; init_info.Allocator = VK_NULL_HANDLE; init_info.PipelineInfoMain.RenderPass = iris->main_window_data.RenderPass; init_info.PipelineInfoMain.Subpass = 0; init_info.PipelineInfoMain.MSAASamples = VK_SAMPLE_COUNT_1_BIT; init_info.CheckVkResultFn = VK_NULL_HANDLE; if (!ImGui_ImplVulkan_Init(&init_info)) { fprintf(stderr, "imgui: Failed to initialize Vulkan backend\n"); return false; } if (!setup_fonts(iris, io)) { fprintf(stderr, "imgui: Failed to setup fonts\n"); return false; } set_theme(iris, iris->theme, false); set_codeview_scheme(iris, iris->codeview_color_scheme); // Initialize our pipeline VkShaderModule vert_shader = create_shader(iris, (uint32_t*)g_vertex_shader_data, g_vertex_shader_size); VkShaderModule frag_shader = create_shader(iris, (uint32_t*)g_fragment_shader_data, g_fragment_shader_size); if (!vert_shader || !frag_shader) { fprintf(stderr, "vulkan: Failed to create shader modules\n"); return false; } iris->pipeline = create_pipeline(iris, vert_shader, frag_shader); if (!iris->pipeline) { fprintf(stderr, "imgui: Failed to create graphics pipeline\n"); return false; } auto load_texture = [iris](const stbi_uc* data, size_t size) -> texture { int x, y, c; stbi_uc* buf = stbi_load_from_memory(data, size, &x, &y, &c, 4); auto tex = vulkan::upload_texture(iris, buf, x, y, c); stbi_image_free(buf); return tex; }; iris->ps1_memory_card_icon = load_texture(g_ps1_memory_card_icon_data, g_ps1_memory_card_icon_size); iris->ps2_memory_card_icon = load_texture(g_ps2_memory_card_icon_data, g_ps2_memory_card_icon_size); iris->pocketstation_icon = load_texture(g_pocketstation_icon_data, g_pocketstation_icon_size); iris->dualshock2_icon = load_texture(g_dualshock2_icon_data, g_dualshock2_icon_size); iris->iris_icon = load_texture(g_iris_icon_data, g_iris_icon_size); return true; } void cleanup(iris::instance* iris) { vulkan::wait_idle(iris); vulkan::free_texture(iris, iris->ps1_memory_card_icon); vulkan::free_texture(iris, iris->ps2_memory_card_icon); vulkan::free_texture(iris, iris->pocketstation_icon); vulkan::free_texture(iris, iris->dualshock2_icon); vulkan::free_texture(iris, iris->iris_icon); ImGui_ImplVulkan_Shutdown(); ImGui_ImplSDL3_Shutdown(); ImPlot::DestroyContext(); ImGui::DestroyContext(); ImGui_ImplVulkanH_DestroyWindow(iris->instance, iris->device, &iris->main_window_data, VK_NULL_HANDLE); iris->instance = NULL; } bool render_frame(iris::instance* iris, ImDrawData* draw_data) { if (iris->swapchain_rebuild) return true; ImGui_ImplVulkanH_Window* wd = &iris->main_window_data; VkSemaphore acquire_semaphore = wd->FrameSemaphores[wd->SemaphoreIndex].ImageAcquiredSemaphore; uint32_t image_index; VkResult err; err = vkAcquireNextImageKHR( iris->device, wd->Swapchain, UINT64_MAX, acquire_semaphore, VK_NULL_HANDLE, &image_index ); VkSemaphore submit_semaphore = wd->FrameSemaphores[image_index].RenderCompleteSemaphore; if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) { iris->swapchain_rebuild = true; return true; } else if (err != VK_SUCCESS) { fprintf(stderr, "imgui: Failed to acquire next image\n"); return false; } wd->FrameIndex = image_index; ImGui_ImplVulkanH_Frame* fd = &wd->Frames[wd->FrameIndex]; if (vkWaitForFences(iris->device, 1, &fd->Fence, VK_TRUE, UINT64_MAX) != VK_SUCCESS) { fprintf(stderr, "imgui: Failed to wait for fence\n"); return false; } if (vkResetFences(iris->device, 1, &fd->Fence) != VK_SUCCESS) { fprintf(stderr, "imgui: Failed to reset fence\n"); return false; } if (vkResetCommandPool(iris->device, fd->CommandPool, 0) != VK_SUCCESS) { fprintf(stderr, "imgui: Failed to reset command pool\n"); return false; } VkCommandBufferBeginInfo begin_info = {}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; if (vkBeginCommandBuffer(fd->CommandBuffer, &begin_info) != VK_SUCCESS) { fprintf(stderr, "imgui: Failed to begin command buffer\n"); return false; } if (iris->instance) { render::render_frame(iris, fd->CommandBuffer, fd->Framebuffer); } { VkRenderPassBeginInfo render_pass_info = {}; render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; render_pass_info.renderPass = wd->RenderPass; render_pass_info.framebuffer = fd->Framebuffer; render_pass_info.renderArea.extent.width = wd->Width; render_pass_info.renderArea.extent.height = wd->Height; render_pass_info.clearValueCount = 1; render_pass_info.pClearValues = &iris->clear_value; vkCmdBeginRenderPass(fd->CommandBuffer, &render_pass_info, VK_SUBPASS_CONTENTS_INLINE); } // Record dear imgui primitives into command buffer ImGui_ImplVulkan_RenderDrawData(draw_data, fd->CommandBuffer); // Submit command buffer vkCmdEndRenderPass(fd->CommandBuffer); { VkPipelineStageFlags wait_stage = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; VkSubmitInfo submit_info = {}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.waitSemaphoreCount = 1; submit_info.pWaitSemaphores = &acquire_semaphore; submit_info.pWaitDstStageMask = &wait_stage; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &fd->CommandBuffer; submit_info.signalSemaphoreCount = 1; submit_info.pSignalSemaphores = &submit_semaphore; if (vkEndCommandBuffer(fd->CommandBuffer) != VK_SUCCESS) { fprintf(stderr, "imgui: Failed to end command buffer\n"); return false; } if (vkQueueSubmit(iris->queue, 1, &submit_info, fd->Fence) != VK_SUCCESS) { fprintf(stderr, "imgui: Failed to submit queue\n"); return false; } } if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); } VkPresentInfoKHR present_info = {}; present_info.sType = VK_STRUCTURE_TYPE_PRESENT_INFO_KHR; present_info.waitSemaphoreCount = 1; present_info.pWaitSemaphores = &submit_semaphore; present_info.swapchainCount = 1; present_info.pSwapchains = &wd->Swapchain; present_info.pImageIndices = &image_index; err = vkQueuePresentKHR(iris->queue, &present_info); if (err == VK_ERROR_OUT_OF_DATE_KHR || err == VK_SUBOPTIMAL_KHR) { iris->swapchain_rebuild = true; return true; } else if (err != VK_SUCCESS) { fprintf(stderr, "imgui: Failed to acquire next image\n"); return false; } wd->SemaphoreIndex = (wd->SemaphoreIndex + 1) % wd->SemaphoreCount; return true; } bool BeginEx(const char* name, bool* p_open, ImGuiWindowFlags flags) { ImGui::SetNextWindowSize(ImVec2(600.0, 600.0), ImGuiCond_FirstUseEver); ImGui::SetNextWindowPos(ImVec2(50.0, 50.0), ImGuiCond_FirstUseEver); if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { flags |= ImGuiWindowFlags_NoTitleBar; } return ImGui::Begin(name, p_open, flags); } } ================================================ FILE: frontend/input.cpp ================================================ #include #include #include "iris.hpp" #define STB_IMAGE_WRITE_IMPLEMENTATION #include "stb_image_write.h" #define INCBIN_PREFIX g_ #define INCBIN_STYLE INCBIN_STYLE_SNAKE #include "incbin.h" INCBIN(gamecontrollerdb, "../deps/SDL_GameControllerDB/gamecontrollerdb.txt"); namespace iris { void keyboard_device::handle_event(iris::instance* iris, SDL_Event* event) { auto ievent = input::sdl_event_to_input_event(event); auto action = input::get_input_action(iris, m_slot, ievent.u64); if (!action) return; input::execute_action(iris, *action, m_slot, event->type == SDL_EVENT_KEY_DOWN ? 1.0f : 0.0f); } void gamepad_device::handle_event(iris::instance* iris, SDL_Event* event) { auto ievent = input::sdl_event_to_input_event(event); auto action = input::get_input_action(iris, m_slot, ievent.u64); if (!action) return; if (event->type == SDL_EVENT_GAMEPAD_BUTTON_DOWN) { input::execute_action(iris, *action, m_slot, 1.0f); } else if (event->type == SDL_EVENT_GAMEPAD_BUTTON_UP) { input::execute_action(iris, *action, m_slot, 0.0f); } else if (event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION) { // Convert from -32768->32767 to -1.0->1.0 and take absolute value float value = fabs(event->gaxis.value / 32767.0f); input::execute_action(iris, *action, m_slot, value); } } } namespace iris::input { void load_db_default(iris::instance* iris) { SDL_IOStream* ios = SDL_IOFromConstMem(g_gamecontrollerdb_data, g_gamecontrollerdb_size); SDL_AddGamepadMappingsFromIO(ios, true); } bool load_db_from_file(iris::instance* iris, const char* path) { if (SDL_AddGamepadMappingsFromFile(path) == -1) return false; return true; } #define IEVENT(event, id, mod) \ (((uint64_t)event << 32) | (((id & 0xf0000fff) | ((mod & 0xffff) << 12)) & 0xffffffff)) void init_default_mapping(iris::instance* iris, int id) { mapping& map = iris->input_maps[id]; if (id == 0) { map.name = "Keyboard (default)"; map.map.clear(); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_X , SDL_KMOD_NONE), IRIS_DS_BT_CROSS); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_A , SDL_KMOD_NONE), IRIS_DS_BT_SQUARE); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_W , SDL_KMOD_NONE), IRIS_DS_BT_TRIANGLE); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_D , SDL_KMOD_NONE), IRIS_DS_BT_CIRCLE); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_RETURN, SDL_KMOD_NONE), IRIS_DS_BT_START); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_S , SDL_KMOD_NONE), IRIS_DS_BT_SELECT); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_UP , SDL_KMOD_NONE), IRIS_DS_BT_UP); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_DOWN , SDL_KMOD_NONE), IRIS_DS_BT_DOWN); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_LEFT , SDL_KMOD_NONE), IRIS_DS_BT_LEFT); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_RIGHT , SDL_KMOD_NONE), IRIS_DS_BT_RIGHT); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_Q , SDL_KMOD_NONE), IRIS_DS_BT_L1); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_E , SDL_KMOD_NONE), IRIS_DS_BT_R1); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_1 , SDL_KMOD_NONE), IRIS_DS_BT_L2); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_3 , SDL_KMOD_NONE), IRIS_DS_BT_R2); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_Z , SDL_KMOD_NONE), IRIS_DS_BT_L3); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_C , SDL_KMOD_NONE), IRIS_DS_BT_R3); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_I , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_POS); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_J , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_NEG); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_K , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_NEG); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_L , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_POS); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_T , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_POS); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_F , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_NEG); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_G , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_NEG); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_H , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_POS); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_0 , SDL_KMOD_NONE), IRIS_S14X_SW_SERVICE); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_9 , SDL_KMOD_NONE), IRIS_S14X_SW_TEST); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_8 , SDL_KMOD_NONE), IRIS_S14X_SW_ENTER); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_7 , SDL_KMOD_NONE), IRIS_S14X_SW_UP); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_6 , SDL_KMOD_NONE), IRIS_S14X_SW_DOWN); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_1 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P1_START); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_2 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P2_START); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_3 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P3_START); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_4 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P4_START); } else { map.name = "Gamepad (default)"; map.map.clear(); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_SOUTH , SDL_KMOD_NONE), IRIS_DS_BT_CROSS); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_WEST , SDL_KMOD_NONE), IRIS_DS_BT_SQUARE); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_NORTH , SDL_KMOD_NONE), IRIS_DS_BT_TRIANGLE); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_EAST , SDL_KMOD_NONE), IRIS_DS_BT_CIRCLE); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_START , SDL_KMOD_NONE), IRIS_DS_BT_START); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_BACK , SDL_KMOD_NONE), IRIS_DS_BT_SELECT); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_UP , SDL_KMOD_NONE), IRIS_DS_BT_UP); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_DOWN , SDL_KMOD_NONE), IRIS_DS_BT_DOWN); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_LEFT , SDL_KMOD_NONE), IRIS_DS_BT_LEFT); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_RIGHT , SDL_KMOD_NONE), IRIS_DS_BT_RIGHT); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_LEFT_SHOULDER , SDL_KMOD_NONE), IRIS_DS_BT_L1); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, SDL_KMOD_NONE), IRIS_DS_BT_R1); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_LEFT_STICK , SDL_KMOD_NONE), IRIS_DS_BT_L3); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_RIGHT_STICK , SDL_KMOD_NONE), IRIS_DS_BT_R3); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFT_TRIGGER , SDL_KMOD_NONE), IRIS_DS_BT_L2); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER , SDL_KMOD_NONE), IRIS_DS_BT_R2); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFTY , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_POS); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_LEFTY , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_NEG); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFTX , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_POS); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_LEFTX , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_NEG); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHTY , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_POS); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_RIGHTY , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_NEG); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHTX , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_POS); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_RIGHTX , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_NEG); } } bool init(iris::instance* iris) { if (!iris->gcdb_path.size()) { fprintf(stdout, "input: Adding default database\n"); load_db_default(iris); } else { fprintf(stdout, "input: Adding database from file \'%s\'\n", iris->gcdb_path.c_str()); load_db_from_file(iris, iris->gcdb_path.c_str()); } iris->input_devices[0] = new iris::keyboard_device(); if (iris->input_maps.size() == 0) { mapping map; map.name = "Keyboard (default)"; map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_X , SDL_KMOD_NONE), IRIS_DS_BT_CROSS); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_A , SDL_KMOD_NONE), IRIS_DS_BT_SQUARE); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_W , SDL_KMOD_NONE), IRIS_DS_BT_TRIANGLE); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_D , SDL_KMOD_NONE), IRIS_DS_BT_CIRCLE); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_RETURN, SDL_KMOD_NONE), IRIS_DS_BT_START); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_S , SDL_KMOD_NONE), IRIS_DS_BT_SELECT); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_UP , SDL_KMOD_NONE), IRIS_DS_BT_UP); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_DOWN , SDL_KMOD_NONE), IRIS_DS_BT_DOWN); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_LEFT , SDL_KMOD_NONE), IRIS_DS_BT_LEFT); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_RIGHT , SDL_KMOD_NONE), IRIS_DS_BT_RIGHT); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_Q , SDL_KMOD_NONE), IRIS_DS_BT_L1); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_E , SDL_KMOD_NONE), IRIS_DS_BT_R1); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_1 , SDL_KMOD_NONE), IRIS_DS_BT_L2); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_3 , SDL_KMOD_NONE), IRIS_DS_BT_R2); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_Z , SDL_KMOD_NONE), IRIS_DS_BT_L3); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_C , SDL_KMOD_NONE), IRIS_DS_BT_R3); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_I , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_POS); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_J , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_NEG); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_K , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_NEG); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_L , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_POS); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_T , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_POS); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_F , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_NEG); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_G , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_NEG); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_H , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_POS); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_0 , SDL_KMOD_NONE), IRIS_S14X_SW_SERVICE); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_9 , SDL_KMOD_NONE), IRIS_S14X_SW_TEST); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_8 , SDL_KMOD_NONE), IRIS_S14X_SW_ENTER); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_7 , SDL_KMOD_NONE), IRIS_S14X_SW_UP); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_6 , SDL_KMOD_NONE), IRIS_S14X_SW_DOWN); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_1 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P1_START); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_2 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P2_START); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_3 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P3_START); map.map.insert(IEVENT(IRIS_EVENT_KEYBOARD, SDLK_4 , SDL_KMOD_LSHIFT), IRIS_S14X_SW_P4_START); iris->input_maps.push_back(map); map.map.clear(); map = {}; map.name = "Gamepad (default)"; map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_SOUTH , SDL_KMOD_NONE), IRIS_DS_BT_CROSS); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_WEST , SDL_KMOD_NONE), IRIS_DS_BT_SQUARE); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_NORTH , SDL_KMOD_NONE), IRIS_DS_BT_TRIANGLE); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_EAST , SDL_KMOD_NONE), IRIS_DS_BT_CIRCLE); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_START , SDL_KMOD_NONE), IRIS_DS_BT_START); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_BACK , SDL_KMOD_NONE), IRIS_DS_BT_SELECT); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_UP , SDL_KMOD_NONE), IRIS_DS_BT_UP); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_DOWN , SDL_KMOD_NONE), IRIS_DS_BT_DOWN); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_LEFT , SDL_KMOD_NONE), IRIS_DS_BT_LEFT); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_DPAD_RIGHT , SDL_KMOD_NONE), IRIS_DS_BT_RIGHT); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_LEFT_SHOULDER , SDL_KMOD_NONE), IRIS_DS_BT_L1); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_RIGHT_SHOULDER, SDL_KMOD_NONE), IRIS_DS_BT_R1); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_LEFT_STICK , SDL_KMOD_NONE), IRIS_DS_BT_L3); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_BUTTON , SDL_GAMEPAD_BUTTON_RIGHT_STICK , SDL_KMOD_NONE), IRIS_DS_BT_R3); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFT_TRIGGER , SDL_KMOD_NONE), IRIS_DS_BT_L2); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHT_TRIGGER , SDL_KMOD_NONE), IRIS_DS_BT_R2); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFTY , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_POS); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_LEFTY , SDL_KMOD_NONE), IRIS_DS_AX_LEFTV_NEG); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_LEFTX , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_POS); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_LEFTX , SDL_KMOD_NONE), IRIS_DS_AX_LEFTH_NEG); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHTY , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_POS); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_RIGHTY , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTV_NEG); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_POS, SDL_GAMEPAD_AXIS_RIGHTX , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_POS); map.map.insert(IEVENT(IRIS_EVENT_GAMEPAD_AXIS_NEG, SDL_GAMEPAD_AXIS_RIGHTX , SDL_KMOD_NONE), IRIS_DS_AX_RIGHTH_NEG); iris->input_maps.push_back(map); } #undef IEVENT // Ensure default mappings are in the correct order if (iris->input_maps[0].name == "Gamepad (default)") { auto map = iris->input_maps[0]; iris->input_maps[0] = iris->input_maps[1]; iris->input_maps[1] = map; } // Use keyboard mapping for slot 0 and none for slot 1 by default if (iris->input_map[0] <= 1) { iris->input_map[0] = 0; } if (iris->input_map[1] <= 1) { iris->input_map[1] = -1; } return true; } input_action* get_input_action(iris::instance* iris, int slot, uint64_t input) { if (iris->input_map[slot] == -1) return nullptr; return iris->input_maps[iris->input_map[slot]].map.get_value(input); } static inline void change_button(iris::instance* iris, int slot, float value, uint32_t button) { if (!iris->ds[slot]) return; if (value > 0.5f) { ds_button_press(iris->ds[slot], button); } else { ds_button_release(iris->ds[slot], button); } } static inline void change_s14x_switch(iris::instance* iris, float value, uint32_t mask) { if (!iris->ps2->s14x_ioboard) return; if (value > 0.5) { s14x_ioboard_press_switch(iris->ps2->s14x_ioboard, mask); } else { s14x_ioboard_release_switch(iris->ps2->s14x_ioboard, mask); } } void execute_action(iris::instance* iris, input_action action, int slot, float value) { if (!iris->ds[slot]) return; switch (action) { case IRIS_DS_BT_SELECT: change_button(iris, slot, value, DS_BT_SELECT); break; case IRIS_DS_BT_L3: change_button(iris, slot, value, DS_BT_L3); break; case IRIS_DS_BT_R3: change_button(iris, slot, value, DS_BT_R3); break; case IRIS_DS_BT_START: change_button(iris, slot, value, DS_BT_START); break; case IRIS_DS_BT_UP: change_button(iris, slot, value, DS_BT_UP); break; case IRIS_DS_BT_RIGHT: change_button(iris, slot, value, DS_BT_RIGHT); break; case IRIS_DS_BT_DOWN: change_button(iris, slot, value, DS_BT_DOWN); break; case IRIS_DS_BT_LEFT: change_button(iris, slot, value, DS_BT_LEFT); break; case IRIS_DS_BT_L2: change_button(iris, slot, value, DS_BT_L2); break; case IRIS_DS_BT_R2: change_button(iris, slot, value, DS_BT_R2); break; case IRIS_DS_BT_L1: change_button(iris, slot, value, DS_BT_L1); break; case IRIS_DS_BT_R1: change_button(iris, slot, value, DS_BT_R1); break; case IRIS_DS_BT_TRIANGLE: change_button(iris, slot, value, DS_BT_TRIANGLE); break; case IRIS_DS_BT_CIRCLE: change_button(iris, slot, value, DS_BT_CIRCLE); break; case IRIS_DS_BT_CROSS: change_button(iris, slot, value, DS_BT_CROSS); break; case IRIS_DS_BT_SQUARE: change_button(iris, slot, value, DS_BT_SQUARE); break; case IRIS_DS_BT_ANALOG: change_button(iris, slot, value, DS_BT_ANALOG); break; case IRIS_DS_AX_RIGHTV_POS: ds_analog_change(iris->ds[slot], DS_AX_RIGHT_V, 0x7f + (value * 0x80)); break; case IRIS_DS_AX_RIGHTV_NEG: ds_analog_change(iris->ds[slot], DS_AX_RIGHT_V, 0x7f - (value * 0x7f)); break; case IRIS_DS_AX_RIGHTH_POS: ds_analog_change(iris->ds[slot], DS_AX_RIGHT_H, 0x7f + (value * 0x80)); break; case IRIS_DS_AX_RIGHTH_NEG: ds_analog_change(iris->ds[slot], DS_AX_RIGHT_H, 0x7f - (value * 0x7f)); break; case IRIS_DS_AX_LEFTV_POS: ds_analog_change(iris->ds[slot], DS_AX_LEFT_V, 0x7f + (value * 0x80)); break; case IRIS_DS_AX_LEFTV_NEG: ds_analog_change(iris->ds[slot], DS_AX_LEFT_V, 0x7f - (value * 0x7f)); break; case IRIS_DS_AX_LEFTH_POS: ds_analog_change(iris->ds[slot], DS_AX_LEFT_H, 0x7f + (value * 0x80)); break; case IRIS_DS_AX_LEFTH_NEG: ds_analog_change(iris->ds[slot], DS_AX_LEFT_H, 0x7f - (value * 0x7f)); break; case IRIS_S14X_SW_SERVICE: change_s14x_switch(iris, value, S14X_IOBOARD_SW_SERVICE); break; case IRIS_S14X_SW_TEST: change_s14x_switch(iris, value, S14X_IOBOARD_SW_TEST); break; case IRIS_S14X_SW_ENTER: change_s14x_switch(iris, value, S14X_IOBOARD_SW_ENTER); break; case IRIS_S14X_SW_UP: change_s14x_switch(iris, value, S14X_IOBOARD_SW_UP); break; case IRIS_S14X_SW_DOWN: change_s14x_switch(iris, value, S14X_IOBOARD_SW_DOWN); break; case IRIS_S14X_SW_P1_START: change_s14x_switch(iris, value, S14X_IOBOARD_SW_P1_START); break; case IRIS_S14X_SW_P2_START: change_s14x_switch(iris, value, S14X_IOBOARD_SW_P2_START); break; case IRIS_S14X_SW_P3_START: change_s14x_switch(iris, value, S14X_IOBOARD_SW_P3_START); break; case IRIS_S14X_SW_P4_START: change_s14x_switch(iris, value, S14X_IOBOARD_SW_P4_START); break; } } input_event sdl_event_to_input_event(SDL_Event* event) { input_event ievent = {}; switch (event->type) { case SDL_EVENT_KEY_DOWN: case SDL_EVENT_KEY_UP: { ievent.type = IRIS_EVENT_KEYBOARD; ievent.id = event->key.key; // Devious hack, we have enough spare bits in the // SDL_Keycode so we can actually do this const uint16_t mask = SDL_KMOD_LSHIFT | SDL_KMOD_RSHIFT | SDL_KMOD_LCTRL | SDL_KMOD_RCTRL | SDL_KMOD_LALT | SDL_KMOD_RALT; ievent.id |= (event->key.mod & mask) << 12; } break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: { ievent.type = IRIS_EVENT_GAMEPAD_BUTTON; ievent.id = event->gbutton.button; } break; case SDL_EVENT_GAMEPAD_AXIS_MOTION: { if (event->gaxis.value > 0) { ievent.type = IRIS_EVENT_GAMEPAD_AXIS_POS; } else { ievent.type = IRIS_EVENT_GAMEPAD_AXIS_NEG; } ievent.id = event->gaxis.axis; } break; } return ievent; } std::string get_default_screenshot_filename(iris::instance* iris) { SDL_Time t; SDL_DateTime dt; SDL_GetCurrentTime(&t); SDL_TimeToDateTime(t, &dt, true); char buf[512]; sprintf(buf, "Screenshot-%04d-%02d-%02d_%02d-%02d-%02d-%d", dt.year, dt.month, dt.day, dt.hour, dt.minute, dt.second, iris->screenshot_counter + 1 ); std::string str(buf); switch (iris->screenshot_format) { case IRIS_SCREENSHOT_FORMAT_PNG: str += ".png"; break; case IRIS_SCREENSHOT_FORMAT_BMP: str += ".bmp"; break; case IRIS_SCREENSHOT_FORMAT_JPG: str += ".jpg"; break; case IRIS_SCREENSHOT_FORMAT_TGA: str += ".tga"; break; } return str; } int get_screenshot_jpg_quality(iris::instance* iris) { switch (iris->screenshot_jpg_quality_mode) { case IRIS_SCREENSHOT_JPG_QUALITY_MINIMUM: return 1; case IRIS_SCREENSHOT_JPG_QUALITY_LOW: return 25; case IRIS_SCREENSHOT_JPG_QUALITY_MEDIUM: return 50; case IRIS_SCREENSHOT_JPG_QUALITY_HIGH: return 90; case IRIS_SCREENSHOT_JPG_QUALITY_MAXIMUM: return 100; case IRIS_SCREENSHOT_JPG_QUALITY_CUSTOM: return iris->screenshot_jpg_quality; } return 90; } bool save_screenshot(iris::instance* iris, std::string path) { std::filesystem::path fn(path); std::string directory = iris->snap_path; if (iris->snap_path.empty()) { directory = "snap"; } std::filesystem::path p(directory); std::string absolute_path; std::string filename; if (path.size()) { filename = path; } else { filename = get_default_screenshot_filename(iris); } if (p.is_absolute()) { absolute_path = p.string(); } else { absolute_path = iris->pref_path + p.string(); } absolute_path += "/" + filename; if (fn.is_absolute()) { absolute_path = fn.string(); } void* ptr = nullptr; int width = 0, height = 0, offset = 0; if (iris->screenshot_mode == IRIS_SCREENSHOT_MODE_INTERNAL) { renderer_image* image = iris->screenshot_shader_processing ? &iris->output_image : &iris->image; ptr = vulkan::read_image(iris, image->image, image->format, image->width, image->height ); width = image->width; height = image->height; } else { ptr = vulkan::read_image(iris, iris->main_window_data.Frames[0].Backbuffer, iris->main_window_data.SurfaceFormat.format, iris->main_window_data.Width, iris->main_window_data.Height ); width = iris->main_window_data.Width; height = iris->main_window_data.Height; if (!iris->fullscreen) { offset = iris->menubar_height; height -= iris->menubar_height; } } if (!ptr) { push_info(iris, "Couldn't save screenshot"); return false; } uint32_t* buf = (uint32_t*)malloc((width * 4) * height); memcpy(buf, ((uint32_t*)ptr) + offset * width, (width * 4) * height); for (int y = 0; y < height; y++) { for (int x = 0; x < width; x++) { buf[x + (y * width)] |= 0xff000000; } } int r = 0; switch (iris->screenshot_format) { case IRIS_SCREENSHOT_FORMAT_PNG: r = stbi_write_png(absolute_path.c_str(), width, height, 4, buf, width * 4); break; case IRIS_SCREENSHOT_FORMAT_BMP: r = stbi_write_bmp(absolute_path.c_str(), width, height, 4, buf); break; case IRIS_SCREENSHOT_FORMAT_JPG: r = stbi_write_jpg(absolute_path.c_str(), width, height, 4, buf, get_screenshot_jpg_quality(iris)); break; case IRIS_SCREENSHOT_FORMAT_TGA: r = stbi_write_tga(absolute_path.c_str(), width, height, 4, buf); break; } printf("Saving screenshot to '%s' (%dx%d, %d bpp): %s\n", absolute_path.c_str(), width, height, 32, r ? "Success" : "Failure" ); free(ptr); free(buf); if (!r) { push_info(iris, "Couldn't save screenshot"); return false; } iris->screenshot_counter++; push_info(iris, "Screenshot saved as '" + filename + "'"); return true; } void handle_keydown_event(iris::instance* iris, SDL_Event* event) { SDL_Keycode key = event->key.key; switch (key) { case SDLK_SPACE: { iris->pause = !iris->pause; // vulkan::wait_idle(iris); } break; case SDLK_F9: { vulkan::wait_idle(iris); bool saved = save_screenshot(iris); } break; case SDLK_F11: { iris->fullscreen = !iris->fullscreen; SDL_SetWindowFullscreen(iris->window, iris->fullscreen ? true : false); } break; case SDLK_F1: { printf("ps2: Sending poweroff signal\n"); ps2_cdvd_power_off(iris->ps2->cdvd); } break; } iris->last_input_event_read = false; iris->last_input_event_value = 1.0f; iris->last_input_event = sdl_event_to_input_event(event); if (iris->input_devices[0]) iris->input_devices[0]->handle_event(iris, event); if (iris->input_devices[1]) iris->input_devices[1]->handle_event(iris, event); } void handle_keyup_event(iris::instance* iris, SDL_Event* event) { // Add special keyup handling here if needed if (iris->input_devices[0]) iris->input_devices[0]->handle_event(iris, event); if (iris->input_devices[1]) iris->input_devices[1]->handle_event(iris, event); } } ================================================ FILE: frontend/iris.cpp ================================================ // Standard includes #include #include #include #include #include #include // Iris includes #include "iris.hpp" #include "config.hpp" #include "ee/ee_def.hpp" #include "ee/vu_def.hpp" // SDL3 includes #include // External includes #include "res/IconsMaterialSymbols.h" namespace iris { void add_recent(iris::instance* iris, std::string file, int type) { auto it = std::find_if(iris->recents.begin(), iris->recents.end(), [file, type](const recent& a) { return a.type == type && a.path == file; }); if (it != iris->recents.end()) { iris->recents.erase(it); iris->recents.push_front({file, type}); return; } iris->recents.push_front({file, type}); if (iris->recents.size() == 11) iris->recents.pop_back(); } int open_file(iris::instance* iris, std::string file) { std::filesystem::path path(file); std::string ext = path.extension().string(); for (char& c : ext) c = tolower(c); // Load disc image if (ext == ".iso" || ext == ".bin" || ext == ".cue" || ext == ".chd" || ext == ".cso" || ext == ".zso") { if (ps2_cdvd_open(iris->ps2->cdvd, file.c_str(), 0)) return 1; char* boot_file = disc_get_boot_path(iris->ps2->cdvd->disc); if (!boot_file) return 2; elf::load_symbols_from_disc(iris); renderer_reset(iris->renderer); ps2_set_system(iris->ps2, iris->system); ps2_load_bios(iris->ps2, iris->bios_path.c_str()); ps2_boot_file(iris->ps2, boot_file); iris->loaded = file; if (iris->autostart) { iris->pause = false; } return 0; } elf::load_symbols_from_file(iris, file); // Note: We need the trailing whitespaces here because of IOMAN HLE // Load executable file = "host: " + file; renderer_reset(iris->renderer); ps2_set_system(iris->ps2, iris->system); ps2_load_bios(iris->ps2, iris->bios_path.c_str()); ps2_boot_file(iris->ps2, file.c_str()); iris->loaded = file; if (iris->autostart) { iris->pause = false; } return 0; } void update_title(iris::instance* iris) { char buf[512]; std::string base = ""; if (iris->loaded.size()) { base = std::filesystem::path(iris->loaded).filename().string(); } sprintf(buf, base.size() ? IRIS_TITLE " | %s" : IRIS_TITLE, base.c_str() ); SDL_SetWindowTitle(iris->window, buf); } void update_time(iris::instance* iris) { int t = SDL_GetTicks() - iris->ticks; if (t < 500) return; if (iris->fps == 0.0f) { iris->fps = (float)iris->frames; } else { iris->fps += (float)iris->frames; iris->fps /= 2.0f; } iris->ticks = SDL_GetTicks(); iris->frames = 0; } void sleep_limiter(iris::instance* iris) { uint32_t ticks = (1.0f / iris->fps_cap) * 1000.0f; std::this_thread::sleep_for(std::chrono::milliseconds(ticks / 2)); // uint32_t now = SDL_GetTicks(); // while ((SDL_GetTicks() - now) < ticks) { // std::this_thread::sleep_for(std::chrono::milliseconds(ticks / 4)); // } } static inline void do_cycle(iris::instance* iris) { ps2_cycle(iris->ps2); if (iris->step_out) { // jr $ra if (iris->ps2->ee->opcode == 0x03e00008) { iris->step_out = false; iris->pause = true; // Consume the delay slot ps2_cycle(iris->ps2); } } if (iris->step_over) { if (iris->ps2->ee->pc == iris->step_over_addr) { iris->step_over = false; iris->pause = true; } } for (const iris::breakpoint& b : iris->breakpoints) { if (b.cpu == iris::BKPT_CPU_EE) { if (iris->ps2->ee->pc == b.addr) { iris->pause = true; } } else { if (iris->ps2->iop->pc == b.addr) { iris->pause = true; } } } } void update_window(iris::instance* iris) { using namespace ImGui; // Limit FPS to 60 only when paused if (iris->limit_fps && iris->pause) sleep_limiter(iris); update_title(iris); update_time(iris); ImGuiIO& io = ImGui::GetIO(); // Start the Dear ImGui frame if (SDL_GetWindowFlags(iris->window) & SDL_WINDOW_MINIMIZED) { SDL_Delay(1); return; } // Resize swapchain? int width, height; SDL_GetWindowSize(iris->window, &width, &height); if (width > 0 && height > 0 && (iris->swapchain_rebuild || iris->main_window_data.Width != width || iris->main_window_data.Height != height)) { ImGui_ImplVulkan_SetMinImageCount(iris->min_image_count); ImGui_ImplVulkanH_CreateOrResizeWindow( iris->instance, iris->physical_device, iris->device, &iris->main_window_data, iris->queue_family, nullptr, width, height, iris->min_image_count, 0 ); iris->main_window_data.FrameIndex = 0; iris->swapchain_rebuild = false; } // Start the Dear ImGui frame ImGui_ImplVulkan_NewFrame(); ImGui_ImplSDL3_NewFrame(); ImGui::NewFrame(); if (!iris->fullscreen) { show_main_menubar(iris); } DockSpaceOverViewport(0, GetMainViewport(), ImGuiDockNodeFlags_PassthruCentralNode); // Drop file fade animation if (iris->drop_file_active) { iris->drop_file_alpha += iris->drop_file_alpha_delta; if (iris->drop_file_alpha_delta > 0.0f) { if (iris->drop_file_alpha >= 1.0f) { iris->drop_file_alpha = 1.0f; iris->drop_file_alpha_delta = 0.0f; } } else { if (iris->drop_file_alpha <= 0.0f) { iris->drop_file_alpha = 0.0f; iris->drop_file_alpha_delta = 0.0f; iris->drop_file_active = false; } } GetForegroundDrawList()->AddRectFilled( ImVec2(0, 0), ImVec2(width, height), ImColor(0.0f, 0.0f, 0.0f, iris->drop_file_alpha * 0.35f) ); ImVec2 text_size = CalcTextSize("Drop file here to launch"); PushFont(iris->font_icons_big); ImVec2 icon_size = CalcTextSize(ICON_MS_DOWNLOAD); ImVec2 total_size = ImVec2( std::max(icon_size.x, text_size.x), icon_size.y + text_size.y ); GetForegroundDrawList()->AddText( ImVec2(width / 2 - icon_size.x / 2, height / 2 - icon_size.y), ImColor(1.0f, 1.0f, 1.0f, iris->drop_file_alpha), ICON_MS_DOWNLOAD ); PopFont(); GetForegroundDrawList()->AddText( ImVec2(width / 2 - text_size.x / 2, height / 2), ImColor(1.0f, 1.0f, 1.0f, iris->drop_file_alpha), "Drop file here to launch" ); } if (iris->show_ee_control) show_ee_control(iris); if (iris->show_ee_state) show_ee_state(iris); if (iris->show_ee_logs) show_ee_logs(iris); if (iris->show_ee_interrupts) show_ee_interrupts(iris); if (iris->show_ee_dmac) show_ee_dmac(iris); if (iris->show_iop_control) show_iop_control(iris); if (iris->show_iop_state) show_iop_state(iris); if (iris->show_iop_logs) show_iop_logs(iris); if (iris->show_iop_interrupts) show_iop_interrupts(iris); if (iris->show_iop_modules) show_iop_modules(iris); if (iris->show_iop_dma) show_iop_dma(iris); if (iris->show_gs_debugger) show_gs_debugger(iris); if (iris->show_spu2_debugger) show_spu2_debugger(iris); if (iris->show_memory_viewer) show_memory_viewer(iris); if (iris->show_vu_disassembler) show_vu_disassembler(iris); if (iris->show_status_bar && !iris->fullscreen) show_status_bar(iris); if (iris->show_breakpoints) show_breakpoints(iris); if (iris->show_about_window) show_about_window(iris); if (iris->show_settings) show_settings(iris); if (iris->show_pad_debugger) show_pad_debugger(iris); if (iris->show_symbols) show_symbols(iris); if (iris->show_threads) show_threads(iris); if (iris->show_sysmem_logs) show_sysmem_logs(iris); if (iris->show_memory_card_tool) show_memory_card_tool(iris); if (iris->show_memory_search) show_memory_search(iris); // if (iris->show_gamelist) show_gamelist(iris); if (iris->show_imgui_demo) ShowDemoWindow(&iris->show_imgui_demo); if (iris->show_bios_setting_window) show_bios_setting_window(iris); if (iris->show_overlay) show_overlay(iris); // Display little pause icon in the top right corner if (iris->pause) { ImVec2 ts = CalcTextSize(ICON_MS_PAUSE); ImVec2 offset = ImVec2(10.0f, 10.0f); ImVec2 padding = ImVec2(0.0f, 0.0f); ts.x -= 1.0f; int menubar_offset = 0; if (!iris->fullscreen) { menubar_offset += iris->menubar_height; } // GetBackgroundDrawList()->AddRectFilled( // ImVec2(width - ts.x - offset.x - padding.x, menubar_offset + offset.y - padding.y), // ImVec2(width - offset.x + padding.x, menubar_offset + ts.y + offset.y + padding.y), // GetColorU32(GetStyleColorVec4(ImGuiCol_WindowBg)), 8.0f // ); GetBackgroundDrawList(GetMainViewport())->AddText( ImVec2(width - ts.x - offset.x, menubar_offset + offset.y), GetColorU32(GetStyleColorVec4(ImGuiCol_Text)), ICON_MS_PAUSE ); } handle_animations(iris); // Rendering ImGui::Render(); ImDrawData* draw_data = ImGui::GetDrawData(); const bool main_is_minimized = (draw_data->DisplaySize.x <= 0.0f || draw_data->DisplaySize.y <= 0.0f); iris->main_window_data.ClearValue.color.float32[0] = 0.0f; iris->main_window_data.ClearValue.color.float32[1] = 0.0f; iris->main_window_data.ClearValue.color.float32[2] = 0.0f; iris->main_window_data.ClearValue.color.float32[3] = 1.0f; if (!main_is_minimized) { if (!imgui::render_frame(iris, draw_data)) { printf("iris: Failed to render ImGui frame\n"); } } iris->frames++; } iris::instance* create() { return new iris::instance(); } bool init(iris::instance* iris, int argc, const char* argv[]) { if (!SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_EVENTS | SDL_INIT_GAMEPAD)) { fprintf(stderr, "iris: Failed to init SDL \'%s\'\n", SDL_GetError()); return false; } // Create and check window iris->main_scale = SDL_GetDisplayContentScale(SDL_GetPrimaryDisplay()); // Init preferences path if (std::filesystem::exists("portable") || std::filesystem::exists("portable.txt")) { iris->pref_path = "./"; } else { char* pref = SDL_GetPrefPath("Allkern", "Iris"); iris->pref_path = std::string(pref); SDL_free(pref); } if (!iris::emu::init(iris)) { fprintf(stderr, "iris: Failed to initialize emulator state\n"); return false; } if (!iris::settings::init(iris, argc, argv)) { fprintf(stderr, "iris: Failed to initialize settings\n"); return false; } iris->window = SDL_CreateWindow( IRIS_TITLE, iris->window_width, iris->window_height, SDL_WINDOW_VULKAN | SDL_WINDOW_RESIZABLE | SDL_WINDOW_HIGH_PIXEL_DENSITY | SDL_WINDOW_HIDDEN ); if (!iris->window) { printf("iris: Failed to create SDL window \'%s\'\n", SDL_GetError()); return false; } if (!iris::vulkan::init(iris, iris->vulkan_enable_validation_layers)) { fprintf(stderr, "iris: Failed to initialize Vulkan\n"); return false; } if (!iris::imgui::init(iris)) { fprintf(stderr, "iris: Failed to initialize ImGui\n"); return false; } if (!iris::platform::init(iris)) { fprintf(stderr, "iris: Failed to initialize platform\n"); return false; } if (!iris::audio::init(iris)) { fprintf(stderr, "iris: Failed to initialize audio\n"); return false; } if (!iris::render::init(iris)) { fprintf(stderr, "iris: Failed to initialize render state\n"); return false; } if (!iris::input::init(iris)) { fprintf(stderr, "iris: Failed to initialize input\n"); return false; } for (const std::string& s : iris->shader_passes_pending) shaders::push(iris, s); iris->shader_passes_pending.clear(); // Sadly we need to start a frame here to measure menubar height ImGui_ImplVulkan_NewFrame(); ImGui_ImplSDL3_NewFrame(); ImGui::NewFrame(); iris->menubar_height = ImGui::GetFrameHeight(); ImGui::EndFrame(); if (ImGui::GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { ImGui::UpdatePlatformWindows(); ImGui::RenderPlatformWindowsDefault(); } SDL_SetWindowSize(iris->window, iris->window_width, iris->window_height + get_menubar_height(iris)); SDL_ShowWindow(iris->window); return true; } SDL_AppResult update(iris::instance* iris) { if (iris->double_click_counter) { iris->double_click_counter--; } if (iris->pause) { iris->step_out = false; iris->step_over = false; if (iris->step) { ps2_step_ee(iris->ps2); iris->step = false; } iris::update_window(iris); return SDL_APP_CONTINUE; } // Execute until VBlank while (!ps2_gs_is_vblank(iris->ps2->gs)) { do_cycle(iris); if (iris->pause) { iris::update_window(iris); return SDL_APP_CONTINUE; } } // Draw frame iris::update_window(iris); // Execute until vblank is over while (ps2_gs_is_vblank(iris->ps2->gs)) { do_cycle(iris); if (iris->pause) { iris::update_window(iris); return SDL_APP_CONTINUE; } } // float p = ((float)iris->ps2->ee->eenull_counter / (float)(4920115)) * 100.0f; // 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); iris->ps2->ee->eenull_counter = 0; iris->ps2->ee->intc_reads = 0; iris->ps2->ee->csr_reads = 0; return SDL_APP_CONTINUE; } SDL_AppResult handle_events(iris::instance* iris, SDL_Event* event) { ImGui_ImplSDL3_ProcessEvent(event); switch (event->type) { case SDL_EVENT_QUIT: { return SDL_APP_SUCCESS; } break; case SDL_EVENT_MOUSE_BUTTON_DOWN: { if (ImGui::GetIO().WantCaptureMouse) { break; } if (event->button.button == SDL_BUTTON_LEFT && event->button.windowID == SDL_GetWindowID(iris->window)) { if (iris->double_click_counter) { if ((SDL_GetTicks() - iris->double_click_counter) > iris->double_click_interval) { iris->double_click_counter = SDL_GetTicks(); } else { iris->fullscreen = !iris->fullscreen; SDL_SetWindowFullscreen(iris->window, iris->fullscreen); } } else { iris->double_click_counter = SDL_GetTicks(); } } } break; case SDL_EVENT_GAMEPAD_ADDED: { SDL_Gamepad* gamepad = SDL_OpenGamepad(event->gdevice.which); if (!gamepad) { SDL_Log("Failed to open gamepad ID %u: %s", (unsigned int) event->gdevice.which, SDL_GetError()); } if (iris->ds[0] && ((iris->input_devices[0] == nullptr) || (iris->input_devices[0]->get_type() == 0))) { if (iris->input_devices[0]) delete iris->input_devices[0]; iris->input_devices[0] = new iris::gamepad_device(event->gdevice.which); iris->input_devices[0]->set_slot(0); if (iris->input_map[0] <= 1) { iris->input_map[0] = 1; } push_info(iris, "\'" + std::string(SDL_GetGamepadName(gamepad)) + "\' connected to slot 1"); } else if (iris->ds[1] && ((iris->input_devices[1] == nullptr) || (iris->input_devices[1]->get_type() == 0))) { if (iris->input_devices[1]) delete iris->input_devices[1]; iris->input_devices[1] = new iris::gamepad_device(event->gdevice.which); iris->input_devices[1]->set_slot(1); if (iris->input_map[1] <= 1) { iris->input_map[1] = 1; } push_info(iris, "\'" + std::string(SDL_GetGamepadName(gamepad)) + "\' connected to slot 2"); } else { push_info(iris, "\'" + std::string(SDL_GetGamepadName(gamepad)) + "\' connected"); } iris->gamepads[event->gdevice.which] = gamepad; } break; case SDL_EVENT_GAMEPAD_REMOVED: { SDL_Gamepad* gamepad = iris->gamepads[event->gdevice.which]; for (int i = 0; i < 2; i++) { if (iris->input_devices[i] && iris->input_devices[i]->get_type() == 1) { iris::gamepad_device* gp = static_cast(iris->input_devices[i]); if (gp->get_id() == event->gdevice.which) { delete iris->input_devices[i]; iris->input_devices[i] = new iris::keyboard_device(); if (iris->input_map[i] <= 1) { iris->input_map[i] = 0; } push_info(iris, "\'" + std::string(SDL_GetGamepadName(gamepad)) + "\' in slot " + std::to_string(i + 1) + " disconnected"); } } } if (gamepad) { SDL_CloseGamepad(gamepad); iris->gamepads.erase(event->gdevice.which); } } break; case SDL_EVENT_WINDOW_CLOSE_REQUESTED: { if (event->window.windowID == SDL_GetWindowID(iris->window)) { return SDL_APP_SUCCESS; } } break; case SDL_EVENT_GAMEPAD_BUTTON_DOWN: case SDL_EVENT_GAMEPAD_BUTTON_UP: case SDL_EVENT_GAMEPAD_AXIS_MOTION: case SDL_EVENT_KEY_UP: { iris->last_input_event_read = false; iris->last_input_event = input::sdl_event_to_input_event(event); if (event->type == SDL_EVENT_GAMEPAD_AXIS_MOTION) { iris->last_input_event_value = fabs(event->gaxis.value / 32767.0f); } else { iris->last_input_event_value = 1.0f; } if (iris->input_devices[0]) iris->input_devices[0]->handle_event(iris, event); if (iris->input_devices[1]) iris->input_devices[1]->handle_event(iris, event); } break; case SDL_EVENT_KEY_DOWN: { input::handle_keydown_event(iris, event); } break; case SDL_EVENT_DROP_BEGIN: { iris->drop_file_active = true; iris->drop_file_alpha = 0.0f; iris->drop_file_alpha_delta = 1.0f / 10.0f; iris->drop_file_alpha_target = 1.0f; } break; case SDL_EVENT_DROP_COMPLETE: { iris->drop_file_active = true; iris->drop_file_alpha = iris->drop_file_alpha_target; iris->drop_file_alpha_delta = -(1.0f / 10.0f); iris->drop_file_alpha_target = 0.0f; } break; case SDL_EVENT_DROP_FILE: { if (!event->drop.data) break; std::string path(event->drop.data); std::filesystem::path p(path); if (std::filesystem::is_regular_file(p)) { if (open_file(iris, path)) { push_info(iris, "Failed to open file: " + path); } else { add_recent(iris, path, RECENT_TYPE_PS2); } } else { if (emu::load_arcade(iris, path)) { add_recent(iris, path, RECENT_TYPE_ARCADE); } else { push_info(iris, "Failed to boot arcade: " + path); } } // Maybe not needed anymore? // SDL_free(event->drop.data); } break; } return SDL_APP_CONTINUE; } int get_menubar_height(iris::instance* iris) { if (iris->show_status_bar) { return iris->menubar_height * 2; } return iris->menubar_height; } void destroy(iris::instance* iris) { for (int i = 0; i < 2; i++) { if (iris->input_devices[i]) { delete iris->input_devices[i]; iris->input_devices[i] = nullptr; } } if (iris->imgui_enable_viewports) { iris->show_ee_control = false; iris->show_ee_state = false; iris->show_ee_logs = false; iris->show_ee_interrupts = false; iris->show_ee_dmac = false; iris->show_iop_control = false; iris->show_iop_state = false; iris->show_iop_logs = false; iris->show_iop_interrupts = false; iris->show_iop_modules = false; iris->show_iop_dma = false; iris->show_gs_debugger = false; iris->show_spu2_debugger = false; iris->show_memory_viewer = false; iris->show_memory_search = false; iris->show_vu_disassembler = false; iris->show_breakpoints = false; iris->show_threads = false; iris->show_sysmem_logs = false; iris->show_imgui_demo = false; iris->show_overlay = false; } if (iris->window) SDL_HideWindow(iris->window); iris::imgui::cleanup(iris); iris::audio::close(iris); iris::settings::close(iris); iris::render::destroy(iris); iris::vulkan::cleanup(iris); iris::platform::destroy(iris); iris::emu::destroy(iris); if (iris->window) SDL_DestroyWindow(iris->window); SDL_Quit(); delete iris; } } ================================================ FILE: frontend/iris.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include "gs/renderer/renderer.hpp" #include "gs/renderer/config.hpp" #include #include #include "imgui.h" #include "imgui_impl_sdl3.h" #include "imgui_impl_vulkan.h" #include "ps2.h" #include "config.hpp" namespace iris { #define RENDER_ASPECT_NATIVE 0 #define RENDER_ASPECT_STRETCH 1 #define RENDER_ASPECT_STRETCH_KEEP 2 #define RENDER_ASPECT_4_3 3 #define RENDER_ASPECT_16_9 4 #define RENDER_ASPECT_5_4 5 #define RENDER_ASPECT_AUTO 6 #define IRIS_THEME_GRANITE 0 #define IRIS_THEME_IMGUI_DARK 1 #define IRIS_THEME_IMGUI_LIGHT 2 #define IRIS_THEME_IMGUI_CLASSIC 3 #define IRIS_THEME_CHERRY 4 #define IRIS_THEME_SOURCE 5 #define IRIS_SCREENSHOT_FORMAT_PNG 0 #define IRIS_SCREENSHOT_FORMAT_BMP 1 #define IRIS_SCREENSHOT_FORMAT_JPG 2 #define IRIS_SCREENSHOT_FORMAT_TGA 3 #define IRIS_SCREENSHOT_MODE_INTERNAL 0 #define IRIS_SCREENSHOT_MODE_DISPLAY 1 #define IRIS_SCREENSHOT_JPG_QUALITY_MINIMUM 0 #define IRIS_SCREENSHOT_JPG_QUALITY_LOW 1 #define IRIS_SCREENSHOT_JPG_QUALITY_MEDIUM 2 #define IRIS_SCREENSHOT_JPG_QUALITY_HIGH 3 #define IRIS_SCREENSHOT_JPG_QUALITY_MAXIMUM 4 #define IRIS_SCREENSHOT_JPG_QUALITY_CUSTOM 5 #define IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_DARK 0 #define IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_LIGHT 1 #define IRIS_CODEVIEW_COLOR_SCHEME_ONE_DARK_PRO 2 #define IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_LATTE 3 #define IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_FRAPPE 4 #define IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MACCHIATO 5 #define IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MOCHA 6 #define IRIS_TITLEBAR_DEFAULT 0 #define IRIS_TITLEBAR_SEAMLESS 1 class instance; // class widget { // public: // virtual bool init(iris::instance* iris) = 0; // virtual void render(iris::instance* iris) = 0; // virtual ~widget() = default; // }; enum : int { BKPT_CPU_EE, BKPT_CPU_IOP }; struct breakpoint { uint32_t addr; const char* symbol = nullptr; int cpu; bool cond_r, cond_w, cond_x; int size; bool enabled; }; struct move_animation { int frames; int frames_remaining; float source_x, source_y; float target_x, target_y; float x, y; }; struct fade_animation { int frames; int frames_remaining; int source_alpha, target_alpha; int alpha; }; struct notification { int type; int state; int frames; int frames_remaining; float width, height; float text_width, text_height; bool end; move_animation move; fade_animation fade; std::string text; }; struct elf_symbol { char* name; uint32_t addr; uint32_t size; }; enum { RECENT_TYPE_PS2, RECENT_TYPE_ARCADE }; enum { INPUT_CONTROLLER_DUALSHOCK2 // Large To-do list here, we're missing the Namco GunCon // controllers, JogCon, NegCon, Buzz! Buzzer, the Train // controllers, Taiko Drum Master controller, the Dance Dance // Revolution mat, Guitar Hero controllers, etc. }; struct input_device { int m_slot; void set_slot(int slot) { this->m_slot = slot; } int get_slot() { return this->m_slot; } virtual int get_type() = 0; virtual void handle_event(iris::instance* iris, SDL_Event* event) = 0; }; class keyboard_device : public input_device { public: int get_type() override { return 0; } void handle_event(iris::instance* iris, SDL_Event* event) override; }; class gamepad_device : public input_device { SDL_JoystickID id; public: gamepad_device(SDL_JoystickID id) : id(id) {} int get_type() override { return 1; } SDL_JoystickID get_id() { return id; } void handle_event(iris::instance* iris, SDL_Event* event) override; }; union input_event { struct { uint32_t id; uint32_t type; }; uint64_t u64; }; enum event { IRIS_EVENT_KEYBOARD, IRIS_EVENT_GAMEPAD_BUTTON, IRIS_EVENT_GAMEPAD_AXIS_POS, IRIS_EVENT_GAMEPAD_AXIS_NEG }; enum input_action : uint32_t { IRIS_DS_BT_CROSS, IRIS_DS_BT_CIRCLE, IRIS_DS_BT_SQUARE, IRIS_DS_BT_TRIANGLE, IRIS_DS_BT_START, IRIS_DS_BT_SELECT, IRIS_DS_BT_ANALOG, IRIS_DS_BT_UP, IRIS_DS_BT_DOWN, IRIS_DS_BT_LEFT, IRIS_DS_BT_RIGHT, IRIS_DS_BT_L1, IRIS_DS_BT_R1, IRIS_DS_BT_L2, IRIS_DS_BT_R2, IRIS_DS_BT_L3, IRIS_DS_BT_R3, IRIS_DS_AX_RIGHTV_POS, IRIS_DS_AX_RIGHTV_NEG, IRIS_DS_AX_RIGHTH_POS, IRIS_DS_AX_RIGHTH_NEG, IRIS_DS_AX_LEFTV_POS, IRIS_DS_AX_LEFTV_NEG, IRIS_DS_AX_LEFTH_POS, IRIS_DS_AX_LEFTH_NEG, IRIS_S14X_SW_DOWN, IRIS_S14X_SW_UP, IRIS_S14X_SW_ENTER, IRIS_S14X_SW_TEST, IRIS_S14X_SW_SERVICE, IRIS_S14X_SW_P1_START, IRIS_S14X_SW_P2_START, IRIS_S14X_SW_P3_START, IRIS_S14X_SW_P4_START, IRIS_INPUT_ACTION_MAX }; struct vertex { struct { float x, y; } pos, uv; static constexpr const VkVertexInputBindingDescription get_binding_description() { VkVertexInputBindingDescription binding_description = {}; binding_description.binding = 0; binding_description.stride = sizeof(vertex); binding_description.inputRate = VK_VERTEX_INPUT_RATE_VERTEX; return binding_description; } static constexpr const std::array get_attribute_descriptions() { std::array attribute_descriptions = {}; attribute_descriptions[0].binding = 0; attribute_descriptions[0].location = 0; attribute_descriptions[0].format = VK_FORMAT_R32G32_SFLOAT; attribute_descriptions[0].offset = offsetof(vertex, pos); attribute_descriptions[1].binding = 0; attribute_descriptions[1].location = 1; attribute_descriptions[1].format = VK_FORMAT_R32G32_SFLOAT; attribute_descriptions[1].offset = offsetof(vertex, uv); return attribute_descriptions; } }; struct texture { int width = 0, height = 0, stride = 0; VkDeviceSize image_size = 0; VkImage image = VK_NULL_HANDLE; VkImageView image_view = VK_NULL_HANDLE; VkSampler sampler = VK_NULL_HANDLE; VkDeviceMemory image_memory = VK_NULL_HANDLE; VkDescriptorSet descriptor_set = VK_NULL_HANDLE; }; namespace shaders { class pass; } struct vulkan_gpu { VkPhysicalDeviceType type = VK_PHYSICAL_DEVICE_TYPE_OTHER; VkPhysicalDevice device = VK_NULL_HANDLE; std::string name = ""; uint32_t api_version = 0; }; template class bidirectional_map { std::unordered_map m_forward_map; std::unordered_map m_reverse_map; public: void insert(const Key& key, const Value& value) { m_forward_map[key] = value; m_reverse_map[value] = key; } std::unordered_map & forward_map() { return m_forward_map; } std::unordered_map & reverse_map() { return m_reverse_map; } bool erase_by_key(const Key& key) { auto it = m_forward_map.find(key); if (it != m_forward_map.end()) { Value value = it->second; m_forward_map.erase(it); m_reverse_map.erase(value); return true; } return false; } bool erase_by_value(const Value& value) { auto it = m_reverse_map.find(value); if (it != m_reverse_map.end()) { Key key = it->second; m_reverse_map.erase(it); m_forward_map.erase(key); return true; } return false; } void clear() { m_forward_map.clear(); m_reverse_map.clear(); } Value* get_value(const Key& key) { auto it = m_forward_map.find(key); if (it != m_forward_map.end()) { return &it->second; } return nullptr; } Key* get_key(const Value& value) { auto it = m_reverse_map.find(value); if (it != m_reverse_map.end()) { return &it->second; } return nullptr; } }; struct mapping { std::string name; bidirectional_map map; }; struct recent { std::string path; int type; }; struct instance { SDL_Window* window = nullptr; SDL_AudioStream* stream = nullptr; // Vulkan state std::vector instance_extensions; std::vector instance_layers; std::vector device_extensions; std::vector device_layers; std::vector enabled_instance_extensions; std::vector enabled_instance_layers; std::vector enabled_device_extensions; std::vector enabled_device_layers; std::vector vulkan_gpus; VkApplicationInfo app_info = {}; VkInstanceCreateInfo instance_create_info = {}; VkDeviceCreateInfo device_create_info = {}; VkInstance instance = VK_NULL_HANDLE; VkPhysicalDevice physical_device = VK_NULL_HANDLE; VkPhysicalDeviceFeatures2 device_features = {}; VkDeviceQueueCreateInfo queue_create_info = {}; uint32_t queue_family = (uint32_t)-1; VkQueue queue = VK_NULL_HANDLE; VkDevice device = VK_NULL_HANDLE; VkDescriptorPool descriptor_pool = VK_NULL_HANDLE; ImGui_ImplVulkanH_Window main_window_data = {}; uint32_t min_image_count = 2; bool swapchain_rebuild = false; VkSurfaceKHR surface = VK_NULL_HANDLE; float main_scale = 1; VkPhysicalDeviceVulkan11Features vulkan_11_features = {}; VkPhysicalDeviceVulkan12Features vulkan_12_features = {}; VkPhysicalDeviceSubgroupSizeControlFeatures subgroup_size_control_features = {}; VkSampler sampler[3] = { VK_NULL_HANDLE }; bool cubic_supported = false; VkDescriptorSetLayout descriptor_set_layout = VK_NULL_HANDLE; VkDescriptorSet descriptor_set = VK_NULL_HANDLE; std::vector descriptor_sets = {}; VkPipelineLayout pipeline_layout = VK_NULL_HANDLE; VkRenderPass render_pass = VK_NULL_HANDLE; VkPipeline pipeline = VK_NULL_HANDLE; VkClearValue clear_value = { 0.11, 0.11, 0.11, 1.0 }; VkBuffer vertex_buffer = VK_NULL_HANDLE; VkDeviceMemory vertex_buffer_memory = VK_NULL_HANDLE; VkBuffer vertex_staging_buffer = VK_NULL_HANDLE; VkDeviceMemory vertex_staging_buffer_memory = VK_NULL_HANDLE; VkDeviceSize vertex_buffer_size = 0; VkBuffer index_buffer = VK_NULL_HANDLE; VkDeviceMemory index_buffer_memory = VK_NULL_HANDLE; std::array vertices = {}; std::array indices = {}; renderer_image image = {}; renderer_image output_image = {}; // Multipass shader stuff std::vector shader_passes_pending; std::vector shader_passes = {}; VkDescriptorSetLayout shader_descriptor_set_layout = VK_NULL_HANDLE; VkDescriptorSet shader_descriptor_set = VK_NULL_HANDLE; std::vector shader_descriptor_sets = {}; VkShaderModule default_vert_shader = VK_NULL_HANDLE; struct { VkImage image = VK_NULL_HANDLE; VkDeviceMemory memory = VK_NULL_HANDLE; VkImageView view = VK_NULL_HANDLE; } shader_framebuffers[2]; std::vector > shader_pass_framebuffers = {}; struct ps2_state* ps2 = nullptr; unsigned int window_width = 960; unsigned int window_height = 720; unsigned int render_width = 640; unsigned int render_height = 480; unsigned int renderer_backend = RENDERER_BACKEND_HARDWARE; renderer_state* renderer = nullptr; texture ps2_memory_card_icon = {}; texture ps1_memory_card_icon = {}; texture pocketstation_icon = {}; texture dualshock2_icon = {}; texture iris_icon = {}; ImFont* font_small_code = nullptr; ImFont* font_code = nullptr; ImFont* font_small = nullptr; ImFont* font_heading = nullptr; ImFont* font_body = nullptr; ImFont* font_icons = nullptr; ImFont* font_icons_big = nullptr; ImFont* font_black = nullptr; std::string elf_path = ""; std::string boot_path = ""; std::string bios_path = ""; std::string rom1_path = ""; std::string rom2_path = ""; std::string nvram_path = ""; std::string disc_path = ""; std::string pref_path = ""; std::string mcd0_path = ""; std::string mcd1_path = ""; std::string snap_path = ""; std::string flash_path = ""; std::string ini_path = ""; std::string gcdb_path = ""; uint8_t mac_address[6] = { 0 }; bool core0_mute[24] = { false }; bool core1_mute[24] = { false }; int core0_solo = -1; int core1_solo = -1; bool open = false; bool pause = true; bool step = false; bool step_over = false; bool step_out = false; uint32_t step_over_addr = 0; bool show_ee_control = false; bool show_ee_state = false; bool show_ee_logs = false; bool show_ee_interrupts = false; bool show_ee_dmac = false; bool show_iop_control = false; bool show_iop_state = false; bool show_iop_logs = false; bool show_iop_interrupts = false; bool show_iop_modules = false; bool show_iop_dma = false; bool show_sysmem_logs = false; bool show_gs_debugger = false; bool show_spu2_debugger = false; bool show_memory_viewer = false; bool show_status_bar = true; bool show_breakpoints = false; bool show_settings = false; bool show_pad_debugger = false; bool show_symbols = false; bool show_threads = false; bool show_memory_card_tool = false; bool show_imgui_demo = false; bool show_vu_disassembler = false; bool show_overlay = false; bool show_memory_search = false; // Special windows bool show_bios_setting_window = false; bool show_about_window = false; bool fullscreen = false; int aspect_mode = RENDER_ASPECT_AUTO; int filter = 1; bool integer_scaling = false; float scale = 1.5f; int window_mode = 0; bool ee_control_follow_pc = true; bool iop_control_follow_pc = true; uint32_t ee_control_address = 0; uint32_t iop_control_address = 0; bool skip_fmv = false; int system = PS2_SYSTEM_AUTO; int theme = IRIS_THEME_GRANITE; bool enable_shaders = false; int vulkan_physical_device = -1; int vulkan_selected_device_index = 0; bool vulkan_enable_validation_layers = false; bool imgui_enable_viewports = false; int codeview_color_scheme = 0; ImColor codeview_color_text = IM_COL32(131, 148, 150, 255); ImColor codeview_color_comment = IM_COL32(88, 110, 117, 255); ImColor codeview_color_mnemonic = IM_COL32(211, 167, 30, 255); ImColor codeview_color_number = IM_COL32(138, 143, 226, 255); ImColor codeview_color_register = IM_COL32(68, 169, 240, 255); ImColor codeview_color_other = IM_COL32(89, 89, 89, 255); ImColor codeview_color_background = IM_COL32(30, 30, 30, 255); ImColor codeview_color_highlight = IM_COL32(75, 75, 75, 255); float codeview_font_scale = 1.0f; bool codeview_use_theme_background = true; bool autostart = true; int angle = 0; bool flip_x = false; bool flip_y = false; uint64_t double_click_interval = 500; uint64_t double_click_counter = 0; std::deque recents; bool dump_to_file = true; std::string settings_path = ""; std::string mappings_path = ""; int frames = 0; float fps = 0.0f; unsigned int ticks = 0; int menubar_height = 0; bool mute = false; bool prev_mute = false; float volume = 1.0f; int timescale = 8; bool mute_adma = true; bool vsync = true; float ui_scale = 1.0f; int screenshot_format = IRIS_SCREENSHOT_FORMAT_PNG; int screenshot_jpg_quality_mode = IRIS_SCREENSHOT_JPG_QUALITY_MAXIMUM; int screenshot_jpg_quality = 50; int screenshot_mode = IRIS_SCREENSHOT_MODE_INTERNAL; int docking_mode = 0; bool screenshot_shader_processing = false; input_device* input_devices[2] = { nullptr }; std::unordered_map gamepads; std::vector input_maps = {}; int input_map[2] = { -1, -1 }; input_event last_input_event = {}; bool last_input_event_read = true; float last_input_event_value = 0.0f; bool limit_fps = true; float fps_cap = 60.0f; std::string loaded = ""; std::vector ee_log = { "" }; std::vector iop_log = { "" }; std::vector sysmem_log = { "" }; std::vector breakpoints = {}; std::deque notifications = {}; struct ds_state* ds[2] = { nullptr }; struct mcd_state* mcd[2] = { nullptr }; int mcd_slot_type[2] = { 0 }; // input_device* device[2]; float drop_file_alpha = 0.0f; float drop_file_alpha_delta = 0.0f; float drop_file_alpha_target = 0.0f; bool drop_file_active = false; // Debug std::vector symbols; std::vector strtab; std::vector audio_buf; float avg_fps; float avg_frames; int screenshot_counter = 0; // Renderer configs hardware_config hardware_backend_config; // Windows-specific settings #ifdef _WIN32 int windows_titlebar_style = IRIS_TITLEBAR_DEFAULT; bool windows_enable_borders = true; bool windows_dark_mode = true; #endif }; struct push_constants { float resolution[2]; int frame; }; namespace audio { bool init(iris::instance* iris); void close(iris::instance* iris); void update(void* udata, SDL_AudioStream* stream, int additional_amount, int total_amount); bool mute(iris::instance* iris); void unmute(iris::instance* iris); } namespace settings { bool init(iris::instance* iris, int argc, const char* argv[]); bool check_for_quick_exit(int argc, const char* argv[]); void close(iris::instance* iris); } namespace shaders { class pass { VkPipelineLayout m_pipeline_layout = VK_NULL_HANDLE; VkPipeline m_pipeline = VK_NULL_HANDLE; VkRenderPass m_render_pass = VK_NULL_HANDLE; VkImageView m_input = VK_NULL_HANDLE; VkShaderModule m_vert_shader = VK_NULL_HANDLE; VkShaderModule m_frag_shader = VK_NULL_HANDLE; iris::instance* m_iris = nullptr; std::string m_id; public: void destroy(); bool init(iris::instance* iris, const void* data, size_t size, std::string id); void swap(pass& rhs) { VkPipelineLayout pipeline_layout = m_pipeline_layout; VkPipeline pipeline = m_pipeline; VkRenderPass render_pass = m_render_pass; VkImageView input = m_input; VkShaderModule vert_shader = m_vert_shader; VkShaderModule frag_shader = m_frag_shader; iris::instance* iris = m_iris; std::string id = m_id; m_pipeline_layout = rhs.m_pipeline_layout; m_pipeline = rhs.m_pipeline; m_render_pass = rhs.m_render_pass; m_input = rhs.m_input; m_vert_shader = rhs.m_vert_shader; m_frag_shader = rhs.m_frag_shader; m_iris = rhs.m_iris; m_id = rhs.m_id; rhs.m_pipeline_layout = pipeline_layout; rhs.m_pipeline = pipeline; rhs.m_render_pass = render_pass; rhs.m_input = input; rhs.m_vert_shader = vert_shader; rhs.m_frag_shader = frag_shader; rhs.m_iris = iris; rhs.m_id = id; } pass(iris::instance* iris, const void* data, size_t size, std::string id); pass(pass&& other); pass() = default; ~pass(); pass& operator=(pass&& other); VkPipelineLayout& get_pipeline_layout(); VkPipeline& get_pipeline(); VkRenderPass& get_render_pass(); VkImageView& get_input(); VkShaderModule& get_vert_shader(); VkShaderModule& get_frag_shader(); std::string get_id() const; bool bypass = false; bool ready(); bool rebuild(); }; void push(iris::instance* iris, void* data, size_t size, std::string id); void push(iris::instance* iris, std::string id); void pop(iris::instance* iris); void insert(iris::instance* iris, int i, void* data, size_t size, std::string id); void insert(iris::instance* iris, std::string id); void erase(iris::instance* iris, int i); pass* at(iris::instance* iris, int i); void swap(iris::instance* iris, int i1, int i2); pass* front(iris::instance* iris); pass* back(iris::instance* iris); size_t count(iris::instance* iris); void clear(iris::instance* iris); std::vector & vector(iris::instance* iris); } namespace imgui { bool init(iris::instance* iris); void set_theme(iris::instance* iris, int theme, bool set_bg_color = true); void set_codeview_scheme(iris::instance* iris, int scheme); bool render_frame(iris::instance* iris, ImDrawData* draw_data); void cleanup(iris::instance* iris); void set_vsync(iris::instance* iris, bool vsync); // Wrapper for ImGui::Begin that sets a default size bool BeginEx(const char* name, bool* p_open, ImGuiWindowFlags flags = 0); } namespace vulkan { bool init(iris::instance* iris, bool enable_validation = false); void cleanup(iris::instance* iris); texture upload_texture(iris::instance* iris, void* pixels, int width, int height, int stride); void free_texture(iris::instance* iris, texture& tex); void* read_image(iris::instance* iris, VkImage image, VkFormat format, int width, int height); void wait_idle(iris::instance* iris); } namespace platform { bool init(iris::instance* iris); bool apply_settings(iris::instance* iris); void destroy(iris::instance* iris); } namespace elf { bool load_symbols_from_disc(iris::instance* iris); bool load_symbols_from_file(iris::instance* iris, std::string path); } namespace emu { bool init(iris::instance* iris); void destroy(iris::instance* iris); bool load_arcade(iris::instance* iris, std::string path); int attach_memory_card(iris::instance* iris, int slot, const char* path); void detach_memory_card(iris::instance* iris, int slot); const char* get_system_name(iris::instance* iris, int system); const char* get_current_system_name(iris::instance* iris); int get_system_count(iris::instance* iris); } namespace render { bool init(iris::instance* iris); void destroy(iris::instance* iris); bool render_frame(iris::instance* iris, VkCommandBuffer command_buffer, VkFramebuffer framebuffer); bool save_screenshot(iris::instance* iris, std::string path); void switch_backend(iris::instance* iris, int backend); void refresh(iris::instance* iris); } namespace input { bool init(iris::instance* iris); void init_default_mapping(iris::instance* iris, int id); void load_db_default(iris::instance* iris); bool load_db_from_file(iris::instance* iris, const char* path); input_action* get_input_action(iris::instance* iris, int slot, uint64_t input); input_event sdl_event_to_input_event(SDL_Event* event); std::string get_default_screenshot_filename(iris::instance* iris); void execute_action(iris::instance* iris, input_action action, int slot, float value); bool save_screenshot(iris::instance* iris, std::string path = ""); void handle_keydown_event(iris::instance* iris, SDL_Event* event); void handle_keyup_event(iris::instance* iris, SDL_Event* event); } iris::instance* create(); bool init(iris::instance* iris, int argc, const char* argv[]); void destroy(iris::instance* iris); SDL_AppResult handle_events(iris::instance* iris, SDL_Event* event); SDL_AppResult update(iris::instance* iris); void update_window(iris::instance* iris); int get_menubar_height(iris::instance* iris); void show_main_menubar(iris::instance* iris); void show_ee_control(iris::instance* iris); void show_ee_state(iris::instance* iris); void show_ee_logs(iris::instance* iris); void show_ee_interrupts(iris::instance* iris); void show_ee_dmac(iris::instance* iris); void show_iop_control(iris::instance* iris); void show_iop_state(iris::instance* iris); void show_iop_logs(iris::instance* iris); void show_iop_interrupts(iris::instance* iris); void show_iop_modules(iris::instance* iris); void show_iop_dma(iris::instance* iris); void show_sysmem_logs(iris::instance* iris); void show_gs_debugger(iris::instance* iris); void show_spu2_debugger(iris::instance* iris); void show_memory_viewer(iris::instance* iris); void show_vu_disassembler(iris::instance* iris); void show_status_bar(iris::instance* iris); void show_breakpoints(iris::instance* iris); void show_about_window(iris::instance* iris); void show_settings(iris::instance* iris); void show_pad_debugger(iris::instance* iris); void show_symbols(iris::instance* iris); void show_threads(iris::instance* iris); void show_overlay(iris::instance* iris); void show_memory_card_tool(iris::instance* iris); void show_bios_setting_window(iris::instance* iris); void show_memory_search(iris::instance* iris); // void show_gamelist(iris::instance* iris); void handle_keydown_event(iris::instance* iris, SDL_Event* event); void handle_keyup_event(iris::instance* iris, SDL_Event* event); void handle_scissor_event(void* udata); void handle_drag_and_drop_event(void* udata, const char* path); void handle_ee_tty_event(void* udata, char c); void handle_iop_tty_event(void* udata, char c); void handle_sysmem_tty_event(void* udata, char c); void handle_animations(iris::instance* iris); void push_info(iris::instance* iris, std::string text); void add_recent(iris::instance* iris, std::string file, int type); int open_file(iris::instance* iris, std::string file); } ================================================ FILE: frontend/notifications.cpp ================================================ #include "iris.hpp" #include "res/IconsMaterialSymbols.h" namespace iris { enum { STATE_IDLE, STATE_MOVING = 1, STATE_FADING = 2 }; enum { TYPE_INFO = 0, }; static inline int seconds_to_frames(float s) { return s * 60.0f; } float ease_in_out(float t) { if (t <= 0.5f) return 2.0f * t * t; t -= 0.5f; return 2.0f * t * (1.0f - t) + 0.5f; } void handle_move(iris::notification* notif) { float f = (float)notif->move.frames_remaining / (float)notif->move.frames; f = ease_in_out(f); float fs = f; float ft = 1.0 - f; notif->move.x = notif->move.source_x * fs + notif->move.target_x * ft; notif->move.y = notif->move.source_y * fs + notif->move.target_y * ft; if (!notif->move.frames_remaining) { notif->state &= ~STATE_MOVING; } else { notif->move.frames_remaining--; } } void handle_fade(iris::notification* notif) { float f = (float)notif->fade.frames_remaining / (float)notif->fade.frames; f = ease_in_out(f); float fs = f; float ft = 1.0 - f; notif->fade.alpha = notif->fade.source_alpha * fs + notif->fade.target_alpha * ft; if (!notif->fade.frames_remaining) { notif->state &= ~STATE_FADING; } else { notif->fade.frames_remaining--; } } void render_notification(iris::instance* iris, iris::notification* notif) { using namespace ImGui; ImGuiStyle& style = ImGui::GetStyle(); ImVec4 bg = style.Colors[ImGuiCol_MenuBarBg]; ImVec4 tc = style.Colors[ImGuiCol_Text]; ImVec4 td = style.Colors[ImGuiCol_TextDisabled]; uint32_t alpha = (notif->fade.alpha) << 24; uint32_t bg_col = ((uint32_t)(bg.x * 255.0) << 0 ) | ((uint32_t)(bg.y * 255.0) << 8 ) | ((uint32_t)(bg.z * 255.0) << 16) | alpha; uint32_t text_col = ((uint32_t)(tc.x * 255.0) << 0 ) | ((uint32_t)(tc.y * 255.0) << 8 ) | ((uint32_t)(tc.z * 255.0) << 16) | alpha; uint32_t icon_col = 0x799fa7 | alpha; uint32_t bar_col = ((uint32_t)(td.x * 255.0) << 0 ) | ((uint32_t)(td.y * 255.0) << 8 ) | ((uint32_t)(td.z * 255.0) << 16) | alpha; float x = notif->move.x; float y = notif->move.y; float width = notif->width; float height = notif->height; GetForegroundDrawList()->AddRectFilled( ImVec2(x, y), ImVec2(x + width, y + height), bg_col, 10.0, ImDrawFlags_RoundCornersAll ); GetForegroundDrawList()->AddText( ImVec2(x + 10, y + (height / 2) - (notif->text_height / 2)), icon_col, ICON_MS_INFO ); GetForegroundDrawList()->AddText( ImVec2(x + 35, y + (height / 2) - (notif->text_height / 2)), text_col, notif->text.c_str() ); int progress_width = width * (1.0 - ((float)notif->frames_remaining / (float)notif->frames)); GetForegroundDrawList()->AddRectFilled( ImVec2(x, y + height - 2), ImVec2(x + progress_width, y + height), bar_col, 10.0, ImDrawFlags_RoundCornersBottom ); } void remove_notification(iris::instance* iris, int i) { iris::notification& n = iris->notifications.at(i); for (unsigned int j = i + 1; j < iris->notifications.size(); j++) { iris::notification& n1 = iris->notifications.at(j); int target_x = n1.state & STATE_MOVING ? n1.move.target_x : n1.move.x; int target_y = n1.state & STATE_MOVING ? n1.move.target_y : n1.move.y; n1.move.source_x = n1.move.x; n1.move.source_y = n1.move.y; n1.move.target_x = target_x; n1.move.target_y = target_y + n.height + 10; n1.move.frames = seconds_to_frames(0.25); n1.move.frames_remaining = n.move.frames; n1.state |= STATE_MOVING; } iris->notifications.erase(std::begin(iris->notifications) + i); } void handle_animations(iris::instance* iris) { for (unsigned int i = 0; i < iris->notifications.size(); i++) { iris::notification& n = iris->notifications.at(i); render_notification(iris, &n); if (n.frames_remaining) { n.frames_remaining--; if (!n.frames_remaining) { n.fade.source_alpha = n.fade.alpha; n.fade.target_alpha = 0; n.fade.frames = seconds_to_frames(0.25); n.fade.frames_remaining = n.fade.frames; n.state |= STATE_FADING; n.end = true; } } if (n.state == STATE_IDLE) { if (n.end) { remove_notification(iris, i); } continue; } if (n.state & STATE_MOVING) handle_move(&n); if (n.state & STATE_FADING) handle_fade(&n); } } void push_notification(iris::instance* iris, iris::notification notif) { for (iris::notification& n : iris->notifications) { int target_x = n.state & STATE_MOVING ? n.move.target_x : n.move.x; int target_y = n.state & STATE_MOVING ? n.move.target_y : n.move.y; n.move.source_x = n.move.x; n.move.source_y = n.move.y; n.move.target_x = target_x; n.move.target_y = target_y - notif.height - 10; n.move.frames = seconds_to_frames(0.25); n.move.frames_remaining = n.move.frames; n.state |= STATE_MOVING; } iris->notifications.push_front(notif); } void push_info(iris::instance* iris, std::string text) { using namespace ImGui; iris::notification notif; int window_width, window_height; int statusbar_offset = iris->show_status_bar ? iris->menubar_height : 0; SDL_GetWindowSizeInPixels(iris->window, &window_width, &window_height); ImVec2 ts = CalcTextSize(text.c_str()); notif.text = text; notif.text_width = ts.x; notif.text_height = ts.y; notif.width = notif.text_width + 50; notif.height = notif.text_height + 25; notif.frames = seconds_to_frames(5.0); notif.frames_remaining = notif.frames; notif.state = STATE_MOVING | STATE_FADING; notif.move.source_x = window_width + 5; notif.move.source_y = window_height - notif.height - 10 - statusbar_offset; notif.move.target_x = window_width - notif.width - 10; notif.move.target_y = notif.move.source_y; notif.move.frames = seconds_to_frames(0.25); notif.move.frames_remaining = notif.move.frames; notif.fade.source_alpha = 0; notif.fade.target_alpha = 255; notif.fade.frames = seconds_to_frames(0.25); notif.fade.frames_remaining = notif.fade.frames; notif.type = TYPE_INFO; notif.end = false; push_notification(iris, notif); } } ================================================ FILE: frontend/platform/stub.cpp ================================================ #include "iris.hpp" // Note: This is a stub implementation for platforms that // do not need special initialization namespace iris::platform { bool init(iris::instance* iris) { return true; } bool apply_settings(iris::instance* iris) { return true; } void destroy(iris::instance* iris) {} } ================================================ FILE: frontend/platform/windows.cpp ================================================ #include "iris.hpp" #include "imgui.h" #include namespace iris::platform { bool init(iris::instance* iris) { apply_settings(iris); return true; } bool apply_settings(iris::instance* iris) { SDL_PropertiesID props = SDL_GetWindowProperties(iris->window); HWND hwnd = (HWND)SDL_GetPointerProperty(props, SDL_PROP_WINDOW_WIN32_HWND_POINTER, NULL); COLORREF border_color = iris->windows_enable_borders ? DWMWA_COLOR_DEFAULT : DWMWA_COLOR_NONE; if (!SUCCEEDED(DwmSetWindowAttribute( hwnd, DWMWINDOWATTRIBUTE::DWMWA_BORDER_COLOR, &border_color, sizeof(border_color) ))) { return false; } if (iris->windows_titlebar_style == IRIS_TITLEBAR_DEFAULT) { BOOL dark_mode = iris->windows_dark_mode; if (!SUCCEEDED(DwmSetWindowAttribute( hwnd, DWMWINDOWATTRIBUTE::DWMWA_USE_IMMERSIVE_DARK_MODE, &dark_mode, sizeof(BOOL) ))) { printf("iris: Failed to set immersive dark mode\n"); return false; } } bool result = false; switch (iris->windows_titlebar_style) { case IRIS_TITLEBAR_DEFAULT: { COLORREF color = DWMWA_COLOR_DEFAULT; result = SUCCEEDED(DwmSetWindowAttribute( hwnd, DWMWINDOWATTRIBUTE::DWMWA_CAPTION_COLOR, &color, sizeof(color) )); } break; case IRIS_TITLEBAR_SEAMLESS: { COLORREF color = 0; ImVec4 menubar_color = ImGui::GetStyleColorVec4(ImGuiCol_MenuBarBg); color = (uint32_t)(menubar_color.x * 255) | ((uint32_t)(menubar_color.y * 255) << 8) | ((uint32_t)(menubar_color.z * 255) << 16); result = SUCCEEDED(DwmSetWindowAttribute( hwnd, DWMWINDOWATTRIBUTE::DWMWA_CAPTION_COLOR, &color, sizeof(color) )); } break; } // ShowWindow(hwnd, SW_MINIMIZE); // ShowWindow(hwnd, SW_RESTORE); return result; } void destroy(iris::instance* iris) {} } ================================================ FILE: frontend/render.cpp ================================================ #include #include "iris.hpp" #define RENDER_MAX_SHADER_PASSES 16 // INCBIN stuff #define INCBIN_PREFIX g_ #define INCBIN_STYLE INCBIN_STYLE_SNAKE #include "incbin.h" INCBIN(default_vertex_shader, "../shaders/shader.spv"); namespace iris::render { static int frame = 0; static constexpr uint32_t DESCRIPTOR_SET_RING_SIZE = 8; bool create_image(iris::instance* iris, uint32_t width, uint32_t height, VkFormat format, VkImageUsageFlags usage, VkImage& image, VkImageView& view, VkDeviceMemory& memory) { VkImageCreateInfo image_info = {}; image_info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; image_info.imageType = VK_IMAGE_TYPE_2D; image_info.extent.width = width; image_info.extent.height = height; image_info.extent.depth = 1; image_info.mipLevels = 1; image_info.arrayLayers = 1; image_info.format = format; image_info.tiling = VK_IMAGE_TILING_OPTIMAL; image_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_info.usage = usage; image_info.samples = VK_SAMPLE_COUNT_1_BIT; image_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; if (vkCreateImage(iris->device, &image_info, nullptr, &image) != VK_SUCCESS) { return false; } VkMemoryRequirements memory_requirements; vkGetImageMemoryRequirements(iris->device, image, &memory_requirements); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = memory_requirements.size; VkPhysicalDeviceMemoryProperties memory_properties; vkGetPhysicalDeviceMemoryProperties(iris->physical_device, &memory_properties); for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) { if ((memory_requirements.memoryTypeBits & (1 << i)) && (memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { alloc_info.memoryTypeIndex = i; break; } } if (vkAllocateMemory(iris->device, &alloc_info, nullptr, &memory) != VK_SUCCESS) { fprintf(stderr, "render: Failed to allocate image memory\n"); vkDestroyImage(iris->device, image, nullptr); return false; } vkBindImageMemory(iris->device, image, memory, 0); VkImageViewCreateInfo image_view_info = {}; image_view_info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; image_view_info.image = image; image_view_info.viewType = VK_IMAGE_VIEW_TYPE_2D; image_view_info.format = format; image_view_info.components.r = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_info.components.g = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_info.components.b = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_info.components.a = VK_COMPONENT_SWIZZLE_IDENTITY; image_view_info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; image_view_info.subresourceRange.baseMipLevel = 0; image_view_info.subresourceRange.levelCount = 1; image_view_info.subresourceRange.baseArrayLayer = 0; image_view_info.subresourceRange.layerCount = 1; if (vkCreateImageView(iris->device, &image_view_info, nullptr, &view) != VK_SUCCESS) { return false; } return true; } bool rebuild_framebuffers(iris::instance* iris) { if (!shaders::count(iris)) return true; vulkan::wait_idle(iris); for (auto& pass_framebuffers : iris->shader_pass_framebuffers) { for (VkFramebuffer& framebuffer : pass_framebuffers) { if (framebuffer) { vkDestroyFramebuffer(iris->device, framebuffer, nullptr); framebuffer = VK_NULL_HANDLE; } } } iris->shader_pass_framebuffers.clear(); for (auto& fb : iris->shader_framebuffers) { if (fb.view) vkDestroyImageView(iris->device, fb.view, nullptr); if (fb.image) vkDestroyImage(iris->device, fb.image, nullptr); if (fb.memory) vkFreeMemory(iris->device, fb.memory, nullptr); bool res = create_image(iris, iris->image.width, iris->image.height, iris->image.format, VK_IMAGE_USAGE_COLOR_ATTACHMENT_BIT | VK_IMAGE_USAGE_SAMPLED_BIT, fb.image, fb.view, fb.memory ); if (!res) { fprintf(stderr, "render: Failed to create shader pass framebuffer image\n"); return false; } } const size_t pass_count = shaders::count(iris); iris->shader_pass_framebuffers.resize(pass_count); for (size_t pass_index = 0; pass_index < pass_count; pass_index++) { auto* pass = shaders::at(iris, (int)pass_index); if (!pass || !pass->ready()) { iris->shader_pass_framebuffers[pass_index] = { VK_NULL_HANDLE, VK_NULL_HANDLE }; continue; } for (int framebuffer_index = 0; framebuffer_index < 2; framebuffer_index++) { VkFramebufferCreateInfo framebuffer_info = {}; framebuffer_info.sType = VK_STRUCTURE_TYPE_FRAMEBUFFER_CREATE_INFO; framebuffer_info.renderPass = pass->get_render_pass(); framebuffer_info.attachmentCount = 1; framebuffer_info.pAttachments = &iris->shader_framebuffers[framebuffer_index].view; framebuffer_info.width = iris->image.width; framebuffer_info.height = iris->image.height; framebuffer_info.layers = 1; if (vkCreateFramebuffer(iris->device, &framebuffer_info, nullptr, &iris->shader_pass_framebuffers[pass_index][framebuffer_index]) != VK_SUCCESS) { fprintf(stderr, "render: Failed to create shader pass framebuffer\n"); return false; } } } return true; } bool init(iris::instance* iris) { // Initialize our renderer iris->renderer = renderer_create(); renderer_create_info info = {}; info.backend = iris->renderer_backend; info.gif = iris->ps2->gif; info.gs = iris->ps2->gs; info.instance = iris->instance; info.device = iris->device; info.physical_device = iris->physical_device; info.instance_create_info = iris->instance_create_info; info.device_create_info = iris->device_create_info; switch (info.backend) { case RENDERER_BACKEND_HARDWARE: { info.config = &iris->hardware_backend_config; } break; } if (!renderer_init(iris->renderer, info)) { fprintf(stderr, "render: Failed to initialize renderer backend\n"); return false; } VkSamplerCreateInfo nearest_sampler_info = {}; nearest_sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; nearest_sampler_info.magFilter = VK_FILTER_NEAREST; nearest_sampler_info.minFilter = VK_FILTER_NEAREST; nearest_sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; nearest_sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; nearest_sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; nearest_sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; nearest_sampler_info.minLod = -1000; nearest_sampler_info.maxLod = 1000; nearest_sampler_info.maxAnisotropy = 1.0f; if (vkCreateSampler(iris->device, &nearest_sampler_info, VK_NULL_HANDLE, &iris->sampler[0]) != VK_SUCCESS) { fprintf(stderr, "render: Failed to create nearest texture sampler\n"); return false; } VkSamplerCreateInfo bilinear_sampler_info = {}; bilinear_sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; bilinear_sampler_info.magFilter = VK_FILTER_LINEAR; bilinear_sampler_info.minFilter = VK_FILTER_LINEAR; bilinear_sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; bilinear_sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; bilinear_sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; bilinear_sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; bilinear_sampler_info.minLod = -1000; bilinear_sampler_info.maxLod = 1000; bilinear_sampler_info.maxAnisotropy = 1.0f; if (vkCreateSampler(iris->device, &bilinear_sampler_info, VK_NULL_HANDLE, &iris->sampler[1]) != VK_SUCCESS) { fprintf(stderr, "render: Failed to create bilinear texture sampler\n"); return false; } if (iris->cubic_supported) { VkSamplerCreateInfo cubic_sampler_info = {}; cubic_sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; cubic_sampler_info.magFilter = VK_FILTER_LINEAR; cubic_sampler_info.minFilter = VK_FILTER_LINEAR; cubic_sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; cubic_sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; cubic_sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; cubic_sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_CLAMP_TO_EDGE; cubic_sampler_info.minLod = -1000; cubic_sampler_info.maxLod = 1000; cubic_sampler_info.maxAnisotropy = 1.0f; if (vkCreateSampler(iris->device, &cubic_sampler_info, VK_NULL_HANDLE, &iris->sampler[2]) != VK_SUCCESS) { fprintf(stderr, "render: Failed to create cubic texture sampler\n"); return false; } } VkShaderModuleCreateInfo vert_shader_create_info = {}; vert_shader_create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; vert_shader_create_info.pCode = (const uint32_t*)g_default_vertex_shader_data; vert_shader_create_info.codeSize = g_default_vertex_shader_size; if (vkCreateShaderModule(iris->device, &vert_shader_create_info, nullptr, &iris->default_vert_shader) != VK_SUCCESS) { fprintf(stderr, "render: Failed to create default vertex shader module\n"); return false; } // Create descriptor set VkDescriptorSetLayoutBinding sampler_layout_binding = {}; sampler_layout_binding.binding = 0; sampler_layout_binding.descriptorCount = 1; sampler_layout_binding.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; sampler_layout_binding.pImmutableSamplers = nullptr; sampler_layout_binding.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; VkDescriptorSetLayoutCreateInfo layout_info = {}; layout_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_LAYOUT_CREATE_INFO; layout_info.bindingCount = 1; layout_info.pBindings = &sampler_layout_binding; if (vkCreateDescriptorSetLayout(iris->device, &layout_info, nullptr, &iris->shader_descriptor_set_layout) != VK_SUCCESS) { fprintf(stderr, "render: Failed to create descriptor set layout\n"); return false; } const uint32_t shader_descriptor_set_count = DESCRIPTOR_SET_RING_SIZE * RENDER_MAX_SHADER_PASSES; std::vector shader_layouts(shader_descriptor_set_count, iris->shader_descriptor_set_layout); iris->shader_descriptor_sets.resize(shader_descriptor_set_count, VK_NULL_HANDLE); VkDescriptorSetAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.descriptorPool = iris->descriptor_pool; alloc_info.descriptorSetCount = shader_descriptor_set_count; alloc_info.pSetLayouts = shader_layouts.data(); if (vkAllocateDescriptorSets(iris->device, &alloc_info, iris->shader_descriptor_sets.data()) != VK_SUCCESS) { fprintf(stderr, "render: Failed to allocate descriptor sets\n"); return false; } iris->shader_descriptor_set = iris->shader_descriptor_sets[0]; return true; } static inline VkDescriptorSet get_frame_descriptor_set(iris::instance* iris) { if (!iris->descriptor_sets.size()) { return iris->descriptor_set; } const uint32_t frame_index = iris->main_window_data.FrameIndex; return iris->descriptor_sets[frame_index % iris->descriptor_sets.size()]; } static inline VkDescriptorSet get_frame_shader_descriptor_set(iris::instance* iris, uint32_t pass_index) { if (!iris->shader_descriptor_sets.size()) { return iris->shader_descriptor_set; } const uint32_t frame_index = iris->main_window_data.FrameIndex % DESCRIPTOR_SET_RING_SIZE; const uint32_t slot = (frame_index * RENDER_MAX_SHADER_PASSES) + (pass_index % RENDER_MAX_SHADER_PASSES); return iris->shader_descriptor_sets[slot % iris->shader_descriptor_sets.size()]; } static inline void update_vertex_buffer(iris::instance* iris, VkCommandBuffer command_buffer) { SDL_Rect size, rect, display; const int normalized_angle = ((iris->angle % 360) + 360) % 360; const bool swap_axes = normalized_angle == 90 || normalized_angle == 270; float aspect_ratio = 4.0 / 3.0; if (swap_axes) { aspect_ratio = 3.0 / 4.0; } float aspect_ratio_inv = 1.0f / aspect_ratio; SDL_GetWindowSize(iris->window, &size.w, &size.h); display.w = size.w; display.h = size.h; display.x = 0; display.y = 0; if (!iris->fullscreen) { display.h -= iris->menubar_height; display.y += iris->menubar_height; if (iris->show_status_bar) { display.h -= iris->menubar_height; } } rect.w = iris->image.width; rect.h = iris->image.height; float scale = iris->integer_scaling ? floorf(iris->scale) : iris->scale; switch (iris->aspect_mode) { case RENDER_ASPECT_NATIVE: { rect.w *= scale; rect.h *= scale; } break; case RENDER_ASPECT_4_3: { rect.w *= scale; rect.h = (float)rect.w * (3.0f / 4.0f); } break; case RENDER_ASPECT_16_9: { rect.w *= scale; rect.h = (float)rect.w * (9.0f / 16.0f); } break; case RENDER_ASPECT_5_4: { rect.w *= scale; rect.h = (float)rect.w * (4.0f / 5.0f); } break; case RENDER_ASPECT_STRETCH: { rect.w = display.w; rect.h = display.h; } break; case RENDER_ASPECT_AUTO: case RENDER_ASPECT_STRETCH_KEEP: { if (swap_axes) { std::swap(rect.w, rect.h); } rect.h = display.h; rect.w = (float)rect.h * aspect_ratio; // Scale vertically if the rect ends up being bigger // than our display area if (rect.w > display.w) { rect.w = display.w; rect.h = (float)rect.w * aspect_ratio_inv; } } break; } if (iris->aspect_mode != RENDER_ASPECT_AUTO && iris->aspect_mode != RENDER_ASPECT_STRETCH_KEEP) { if (swap_axes) { std::swap(rect.w, rect.h); } } iris->render_width = rect.w; iris->render_height = rect.h; rect.x = display.x + ((display.w / 2) - (rect.w / 2)); rect.y = display.y + ((display.h / 2) - (rect.h / 2)); float x0 = (rect.x / ((float)size.w / 2.0f)) - 1.0f; float y0 = (rect.y / ((float)size.h / 2.0f)) - 1.0f; float x1 = ((rect.x + rect.w) / ((float)size.w / 2.0f)) - 1.0f; float y1 = ((rect.y + rect.h) / ((float)size.h / 2.0f)) - 1.0f; float uvs[4][2] = { {0.0f, 1.0f}, {1.0f, 1.0f}, {1.0f, 0.0f}, {0.0f, 0.0f} }; for (int i = 0; i < 4; i++) { float u = uvs[i][0]; float v = uvs[i][1]; if (iris->flip_x) { u = 1.0f - u; } if (iris->flip_y) { v = 1.0f - v; } switch (normalized_angle) { case 90: { // Rotate clockwise in UV space. const float prev_u = u; u = v; v = 1.0f - prev_u; } break; case 180: { u = 1.0f - u; v = 1.0f - v; } break; case 270: { // Rotate counter-clockwise in UV space. const float prev_u = u; u = 1.0f - v; v = prev_u; } break; } uvs[i][0] = u; uvs[i][1] = v; } iris->vertices[0] = vertex{ { x0, y0 }, {uvs[0][0], uvs[0][1]} }; iris->vertices[1] = vertex{ { x1, y0 }, {uvs[1][0], uvs[1][1]} }; iris->vertices[2] = vertex{ { x1, y1 }, {uvs[2][0], uvs[2][1]} }; iris->vertices[3] = vertex{ { x0, y1 }, {uvs[3][0], uvs[3][1]} }; void* ptr; vkMapMemory(iris->device, iris->vertex_staging_buffer_memory, 0, iris->vertex_buffer_size, 0, &ptr); memcpy(ptr, iris->vertices.data(), (size_t)iris->vertex_buffer_size); vkUnmapMemory(iris->device, iris->vertex_staging_buffer_memory); static VkBufferCopy region = {}; region.srcOffset = 0; region.dstOffset = 0; region.size = iris->vertex_buffer_size; vkCmdCopyBuffer( command_buffer, iris->vertex_staging_buffer, iris->vertex_buffer, 1, ®ion ); } static inline void update_descriptor_set(iris::instance* iris, VkDescriptorSet set, VkImageView view, VkSampler sampler) { VkDescriptorImageInfo image_info = {}; image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image_info.imageView = view; image_info.sampler = sampler; VkWriteDescriptorSet descriptor_write = {}; descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; descriptor_write.dstSet = set; descriptor_write.dstBinding = 0; descriptor_write.dstArrayElement = 0; descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptor_write.descriptorCount = 1; descriptor_write.pImageInfo = &image_info; vkUpdateDescriptorSets(iris->device, 1, &descriptor_write, 0, nullptr); } void render_shader_passes(iris::instance* iris, VkCommandBuffer command_buffer, VkImageView& output_view, VkImage& output_image) { if (!shaders::count(iris)) return; if (iris->shader_pass_framebuffers.size() != shaders::count(iris)) { if (!rebuild_framebuffers(iris)) { fprintf(stderr, "render: Failed to rebuild shader framebuffers\n"); return; } } int i = 0; for (size_t pass_index = 0; pass_index < shaders::vector(iris).size(); pass_index++) { auto& pass = shaders::vector(iris)[pass_index]; if (pass->bypass || !pass->ready()) continue; const int fb = i & 1; const VkImageView input_view = i == 0 ? iris->image.view : iris->shader_framebuffers[fb ^ 1].view; VkFramebuffer framebuffer = iris->shader_pass_framebuffers[pass_index][fb]; if (framebuffer == VK_NULL_HANDLE) { if (!rebuild_framebuffers(iris)) { fprintf(stderr, "render: Failed to rebuild shader framebuffers\n"); return; } framebuffer = iris->shader_pass_framebuffers[pass_index][fb]; if (framebuffer == VK_NULL_HANDLE) { fprintf(stderr, "render: Shader framebuffer is null after rebuild\n"); return; } } output_view = iris->shader_framebuffers[fb].view; output_image = iris->shader_framebuffers[fb].image; VkRenderPassBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.renderPass = pass->get_render_pass(); info.framebuffer = framebuffer; info.renderArea.extent.width = iris->image.width; info.renderArea.extent.height = iris->image.height; info.clearValueCount = 1; info.pClearValues = &iris->clear_value; VkDescriptorImageInfo image_info = {}; image_info.imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; image_info.imageView = input_view; image_info.sampler = iris->sampler[iris->filter]; VkWriteDescriptorSet descriptor_write = {}; descriptor_write.sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; const VkDescriptorSet shader_descriptor_set = get_frame_shader_descriptor_set(iris, i); descriptor_write.dstSet = shader_descriptor_set; descriptor_write.dstBinding = 0; descriptor_write.dstArrayElement = 0; descriptor_write.descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; descriptor_write.descriptorCount = 1; descriptor_write.pImageInfo = &image_info; vkUpdateDescriptorSets(iris->device, 1, &descriptor_write, 0, nullptr); vkCmdBeginRenderPass(command_buffer, &info, VK_SUBPASS_CONTENTS_INLINE); vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pass->get_pipeline()); VkDeviceSize offsets[] = { 0 }; vkCmdBindIndexBuffer(command_buffer, iris->index_buffer, 0, VK_INDEX_TYPE_UINT16); vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, pass->get_pipeline_layout(), 0, 1, &shader_descriptor_set, 0, nullptr); VkViewport viewport = {}; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = iris->image.width; viewport.height = iris->image.height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vkCmdSetViewport(command_buffer, 0, 1, &viewport); VkRect2D scissor = {}; scissor.offset = {0, 0}; scissor.extent = { (uint32_t)iris->image.width, (uint32_t)iris->image.height }; vkCmdSetScissor(command_buffer, 0, 1, &scissor); push_constants constants = { .resolution = { (float)iris->image.width, (float)iris->image.height }, .frame = frame }; vkCmdPushConstants(command_buffer, pass->get_pipeline_layout(), VK_SHADER_STAGE_FRAGMENT_BIT, 0, sizeof(push_constants), &constants); vkCmdDrawIndexed(command_buffer, 6, 1, 0, 0, 0); vkCmdEndRenderPass(command_buffer); i++; } } bool render_frame(iris::instance* iris, VkCommandBuffer command_buffer, VkFramebuffer framebuffer) { renderer_image image; if (iris->pause) { image = iris->image; } else { image = renderer_get_frame(iris->renderer); } bool need_rebuild = image.width != iris->image.width || image.height != iris->image.height || image.format != iris->image.format; iris->image = image; if (need_rebuild && image.view != VK_NULL_HANDLE) { vulkan::wait_idle(iris); for (auto& pass : shaders::vector(iris)) { if (!pass->rebuild()) { fprintf(stderr, "render: Failed to rebuild shader pass\n"); return false; } } if (!rebuild_framebuffers(iris)) { fprintf(stderr, "render: Failed to rebuild shader pass framebuffers\n"); return false; } } // Process shader passes here iris->output_image = iris->image; if (iris->enable_shaders && iris->output_image.view != VK_NULL_HANDLE) { render_shader_passes(iris, command_buffer, iris->output_image.view, iris->output_image.image); } VkRenderPassBeginInfo info = {}; info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_BEGIN_INFO; info.renderPass = iris->main_window_data.RenderPass; info.framebuffer = framebuffer; info.renderArea.extent.width = iris->main_window_data.Width; info.renderArea.extent.height = iris->main_window_data.Height; info.clearValueCount = 1; info.pClearValues = &iris->clear_value; if (iris->output_image.view != VK_NULL_HANDLE) { const VkDescriptorSet descriptor_set = get_frame_descriptor_set(iris); update_vertex_buffer(iris, command_buffer); update_descriptor_set(iris, descriptor_set, iris->output_image.view, iris->sampler[iris->filter]); } vkCmdBeginRenderPass(command_buffer, &info, VK_SUBPASS_CONTENTS_INLINE); VkClearAttachment clear_attachment = {}; clear_attachment.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; clear_attachment.colorAttachment = 0; clear_attachment.clearValue = iris->clear_value; VkClearRect clear_rect = {}; clear_rect.rect.offset = { 0, 0 }; clear_rect.rect.extent = { (uint32_t)iris->main_window_data.Width, (uint32_t)iris->main_window_data.Height }; clear_rect.baseArrayLayer = 0; clear_rect.layerCount = 1; vkCmdClearAttachments(command_buffer, 1, &clear_attachment, 1, &clear_rect); vkCmdBindPipeline(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, iris->pipeline); if (iris->output_image.view != VK_NULL_HANDLE) { VkDeviceSize offsets[] = { 0 }; const VkDescriptorSet descriptor_set = get_frame_descriptor_set(iris); vkCmdBindVertexBuffers(command_buffer, 0, 1, &iris->vertex_buffer, offsets); vkCmdBindIndexBuffer(command_buffer, iris->index_buffer, 0, VK_INDEX_TYPE_UINT16); vkCmdBindDescriptorSets(command_buffer, VK_PIPELINE_BIND_POINT_GRAPHICS, iris->pipeline_layout, 0, 1, &descriptor_set, 0, nullptr); } VkViewport viewport = {}; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = iris->main_window_data.Width; viewport.height = iris->main_window_data.Height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; vkCmdSetViewport(command_buffer, 0, 1, &viewport); VkRect2D scissor = {}; scissor.offset = {0, 0}; scissor.extent = { (uint32_t)iris->main_window_data.Width, (uint32_t)iris->main_window_data.Height }; vkCmdSetScissor(command_buffer, 0, 1, &scissor); if (iris->output_image.view != VK_NULL_HANDLE) { vkCmdDrawIndexed(command_buffer, 6, 1, 0, 0, 0); } vkCmdEndRenderPass(command_buffer); if (!iris->pause) frame++; return true; } void switch_backend(iris::instance* iris, int backend) { if (iris->renderer_backend == backend) return; renderer_destroy(iris->renderer); iris->renderer = renderer_create(); renderer_create_info info = {}; info.backend = backend; info.gif = iris->ps2->gif; info.gs = iris->ps2->gs; info.instance = iris->instance; info.device = iris->device; info.physical_device = iris->physical_device; info.instance_create_info = iris->instance_create_info; info.device_create_info = iris->device_create_info; switch (info.backend) { case RENDERER_BACKEND_HARDWARE: { info.config = &iris->hardware_backend_config; } break; } if (!renderer_init(iris->renderer, info)) { fprintf(stderr, "render: Failed to initialize renderer backend\n"); } else { iris->renderer_backend = backend; } } void refresh(iris::instance* iris) { switch (iris->renderer_backend) { case RENDERER_BACKEND_HARDWARE: { renderer_set_config(iris->renderer, &iris->hardware_backend_config); } break; } iris->image = renderer_get_frame(iris->renderer); if (iris->image.view == VK_NULL_HANDLE) return; if (shaders::count(iris) == 0) return; vulkan::wait_idle(iris); for (auto& pass : shaders::vector(iris)) { pass->rebuild(); } rebuild_framebuffers(iris); } void destroy(iris::instance* iris) { if (!iris->window) return; vulkan::wait_idle(iris); for (auto& pass_framebuffers : iris->shader_pass_framebuffers) { for (VkFramebuffer& framebuffer : pass_framebuffers) { if (framebuffer) { vkDestroyFramebuffer(iris->device, framebuffer, nullptr); } } } iris->shader_pass_framebuffers.clear(); for (auto& fb : iris->shader_framebuffers) { if (fb.view) vkDestroyImageView(iris->device, fb.view, nullptr); if (fb.image) vkDestroyImage(iris->device, fb.image, nullptr); if (fb.memory) vkFreeMemory(iris->device, fb.memory, nullptr); } if (iris->shader_descriptor_set_layout) { vkDestroyDescriptorSetLayout(iris->device, iris->shader_descriptor_set_layout, nullptr); } if (iris->default_vert_shader) { vkDestroyShaderModule(iris->device, iris->default_vert_shader, nullptr); } shaders::clear(iris); if (iris->renderer) renderer_destroy(iris->renderer); } } ================================================ FILE: frontend/res/IconsMaterialSymbols.h ================================================ // Generated by https://github.com/juliettef/IconFontCppHeaders script GenerateIconFontCppHeaders.py // for C and C++ // from codepoints https://github.com/google/material-design-icons/raw/master/variablefont/MaterialSymbolsOutlined%5BFILL%2CGRAD%2Copsz%2Cwght%5D.codepoints // 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 #pragma once #define FONT_ICON_FILE_NAME_MSO "MaterialSymbolsOutlined[FILL,GRAD,opsz,wght].ttf" #define FONT_ICON_FILE_NAME_MSR "MaterialSymbolsRounded[FILL,GRAD,opsz,wght].ttf" #define FONT_ICON_FILE_NAME_MSS "MaterialSymbolsSharp[FILL,GRAD,opsz,wght].ttf" #define ICON_MIN_MS 0xe003 #define ICON_MAX_16_MS 0xf8ff #define ICON_MAX_MS 0xf8ff #define ICON_MS_10K "\xee\xa5\x91" // U+e951 #define ICON_MS_10MP "\xee\xa5\x92" // U+e952 #define ICON_MS_11MP "\xee\xa5\x93" // U+e953 #define ICON_MS_123 "\xee\xae\x8d" // U+eb8d #define ICON_MS_12MP "\xee\xa5\x94" // U+e954 #define ICON_MS_13MP "\xee\xa5\x95" // U+e955 #define ICON_MS_14MP "\xee\xa5\x96" // U+e956 #define ICON_MS_15MP "\xee\xa5\x97" // U+e957 #define ICON_MS_16MP "\xee\xa5\x98" // U+e958 #define ICON_MS_17MP "\xee\xa5\x99" // U+e959 #define ICON_MS_18_UP_RATING "\xef\xa3\xbd" // U+f8fd #define ICON_MS_18MP "\xee\xa5\x9a" // U+e95a #define ICON_MS_19MP "\xee\xa5\x9b" // U+e95b #define ICON_MS_1K "\xee\xa5\x9c" // U+e95c #define ICON_MS_1K_PLUS "\xee\xa5\x9d" // U+e95d #define ICON_MS_1X_MOBILEDATA "\xee\xbf\x8d" // U+efcd #define ICON_MS_1X_MOBILEDATA_BADGE "\xef\x9f\xb1" // U+f7f1 #define ICON_MS_20MP "\xee\xa5\x9e" // U+e95e #define ICON_MS_21MP "\xee\xa5\x9f" // U+e95f #define ICON_MS_22MP "\xee\xa5\xa0" // U+e960 #define ICON_MS_23MP "\xee\xa5\xa1" // U+e961 #define ICON_MS_24MP "\xee\xa5\xa2" // U+e962 #define ICON_MS_2D "\xee\xbc\xb7" // U+ef37 #define ICON_MS_2K "\xee\xa5\xa3" // U+e963 #define ICON_MS_2K_PLUS "\xee\xa5\xa4" // U+e964 #define ICON_MS_2MP "\xee\xa5\xa5" // U+e965 #define ICON_MS_30FPS "\xee\xbf\x8e" // U+efce #define ICON_MS_30FPS_SELECT "\xee\xbf\x8f" // U+efcf #define ICON_MS_360 "\xee\x95\xb7" // U+e577 #define ICON_MS_3D_ROTATION "\xee\xa1\x8d" // U+e84d #define ICON_MS_3G_MOBILEDATA "\xee\xbf\x90" // U+efd0 #define ICON_MS_3G_MOBILEDATA_BADGE "\xef\x9f\xb0" // U+f7f0 #define ICON_MS_3K "\xee\xa5\xa6" // U+e966 #define ICON_MS_3K_PLUS "\xee\xa5\xa7" // U+e967 #define ICON_MS_3MP "\xee\xa5\xa8" // U+e968 #define ICON_MS_3P "\xee\xbf\x91" // U+efd1 #define ICON_MS_4G_MOBILEDATA "\xee\xbf\x92" // U+efd2 #define ICON_MS_4G_MOBILEDATA_BADGE "\xef\x9f\xaf" // U+f7ef #define ICON_MS_4G_PLUS_MOBILEDATA "\xee\xbf\x93" // U+efd3 #define ICON_MS_4K "\xee\x81\xb2" // U+e072 #define ICON_MS_4K_PLUS "\xee\xa5\xa9" // U+e969 #define ICON_MS_4MP "\xee\xa5\xaa" // U+e96a #define ICON_MS_50MP "\xef\x9b\xb3" // U+f6f3 #define ICON_MS_5G "\xee\xbc\xb8" // U+ef38 #define ICON_MS_5G_MOBILEDATA_BADGE "\xef\x9f\xae" // U+f7ee #define ICON_MS_5K "\xee\xa5\xab" // U+e96b #define ICON_MS_5K_PLUS "\xee\xa5\xac" // U+e96c #define ICON_MS_5MP "\xee\xa5\xad" // U+e96d #define ICON_MS_60FPS "\xee\xbf\x94" // U+efd4 #define ICON_MS_60FPS_SELECT "\xee\xbf\x95" // U+efd5 #define ICON_MS_6_FT_APART "\xef\x88\x9e" // U+f21e #define ICON_MS_6K "\xee\xa5\xae" // U+e96e #define ICON_MS_6K_PLUS "\xee\xa5\xaf" // U+e96f #define ICON_MS_6MP "\xee\xa5\xb0" // U+e970 #define ICON_MS_7K "\xee\xa5\xb1" // U+e971 #define ICON_MS_7K_PLUS "\xee\xa5\xb2" // U+e972 #define ICON_MS_7MP "\xee\xa5\xb3" // U+e973 #define ICON_MS_8K "\xee\xa5\xb4" // U+e974 #define ICON_MS_8K_PLUS "\xee\xa5\xb5" // U+e975 #define ICON_MS_8MP "\xee\xa5\xb6" // U+e976 #define ICON_MS_9K "\xee\xa5\xb7" // U+e977 #define ICON_MS_9K_PLUS "\xee\xa5\xb8" // U+e978 #define ICON_MS_9MP "\xee\xa5\xb9" // U+e979 #define ICON_MS_ABC "\xee\xae\x94" // U+eb94 #define ICON_MS_AC_UNIT "\xee\xac\xbb" // U+eb3b #define ICON_MS_ACCESS_ALARM "\xee\xa1\x95" // U+e855 #define ICON_MS_ACCESS_ALARMS "\xee\xa1\x95" // U+e855 #define ICON_MS_ACCESS_TIME "\xee\xbf\x96" // U+efd6 #define ICON_MS_ACCESS_TIME_FILLED "\xee\xbf\x96" // U+efd6 #define ICON_MS_ACCESSIBILITY "\xee\xa1\x8e" // U+e84e #define ICON_MS_ACCESSIBILITY_NEW "\xee\xa4\xac" // U+e92c #define ICON_MS_ACCESSIBLE "\xee\xa4\x94" // U+e914 #define ICON_MS_ACCESSIBLE_FORWARD "\xee\xa4\xb4" // U+e934 #define ICON_MS_ACCOUNT_BALANCE "\xee\xa1\x8f" // U+e84f #define ICON_MS_ACCOUNT_BALANCE_WALLET "\xee\xa1\x90" // U+e850 #define ICON_MS_ACCOUNT_BOX "\xee\xa1\x91" // U+e851 #define ICON_MS_ACCOUNT_CHILD "\xee\xa1\x92" // U+e852 #define ICON_MS_ACCOUNT_CHILD_INVERT "\xee\x99\x99" // U+e659 #define ICON_MS_ACCOUNT_CIRCLE "\xef\x88\x8b" // U+f20b #define ICON_MS_ACCOUNT_CIRCLE_FILLED "\xef\x88\x8b" // U+f20b #define ICON_MS_ACCOUNT_CIRCLE_OFF "\xef\x9e\xb3" // U+f7b3 #define ICON_MS_ACCOUNT_TREE "\xee\xa5\xba" // U+e97a #define ICON_MS_ACTION_KEY "\xef\x94\x82" // U+f502 #define ICON_MS_ACTIVITY_ZONE "\xee\x87\xa6" // U+e1e6 #define ICON_MS_ACUTE "\xee\x93\x8b" // U+e4cb #define ICON_MS_AD "\xee\x99\x9a" // U+e65a #define ICON_MS_AD_GROUP "\xee\x99\x9b" // U+e65b #define ICON_MS_AD_GROUP_OFF "\xee\xab\xa5" // U+eae5 #define ICON_MS_AD_OFF "\xef\x9e\xb2" // U+f7b2 #define ICON_MS_AD_UNITS "\xee\xbc\xb9" // U+ef39 #define ICON_MS_ADAPTIVE_AUDIO_MIC "\xef\x93\x8c" // U+f4cc #define ICON_MS_ADAPTIVE_AUDIO_MIC_OFF "\xef\x93\x8b" // U+f4cb #define ICON_MS_ADB "\xee\x98\x8e" // U+e60e #define ICON_MS_ADD "\xee\x85\x85" // U+e145 #define ICON_MS_ADD_A_PHOTO "\xee\x90\xb9" // U+e439 #define ICON_MS_ADD_AD "\xee\x9c\xaa" // U+e72a #define ICON_MS_ADD_ALARM "\xee\xa1\x96" // U+e856 #define ICON_MS_ADD_ALERT "\xee\x80\x83" // U+e003 #define ICON_MS_ADD_BOX "\xee\x85\x86" // U+e146 #define ICON_MS_ADD_BUSINESS "\xee\x9c\xa9" // U+e729 #define ICON_MS_ADD_CALL "\xef\x82\xb7" // U+f0b7 #define ICON_MS_ADD_CARD "\xee\xae\x86" // U+eb86 #define ICON_MS_ADD_CHART "\xee\xbc\xbc" // U+ef3c #define ICON_MS_ADD_CIRCLE "\xee\x8e\xba" // U+e3ba #define ICON_MS_ADD_CIRCLE_OUTLINE "\xee\x8e\xba" // U+e3ba #define ICON_MS_ADD_COMMENT "\xee\x89\xa6" // U+e266 #define ICON_MS_ADD_DIAMOND "\xef\x92\x9c" // U+f49c #define ICON_MS_ADD_HOME "\xef\xa3\xab" // U+f8eb #define ICON_MS_ADD_HOME_WORK "\xef\xa3\xad" // U+f8ed #define ICON_MS_ADD_IC_CALL "\xef\x82\xb7" // U+f0b7 #define ICON_MS_ADD_LINK "\xee\x85\xb8" // U+e178 #define ICON_MS_ADD_LOCATION "\xee\x95\xa7" // U+e567 #define ICON_MS_ADD_LOCATION_ALT "\xee\xbc\xba" // U+ef3a #define ICON_MS_ADD_MODERATOR "\xee\xa5\xbd" // U+e97d #define ICON_MS_ADD_NOTES "\xee\x82\x91" // U+e091 #define ICON_MS_ADD_PHOTO_ALTERNATE "\xee\x90\xbe" // U+e43e #define ICON_MS_ADD_REACTION "\xee\x87\x93" // U+e1d3 #define ICON_MS_ADD_ROAD "\xee\xbc\xbb" // U+ef3b #define ICON_MS_ADD_SHOPPING_CART "\xee\xa1\x94" // U+e854 #define ICON_MS_ADD_TASK "\xef\x88\xba" // U+f23a #define ICON_MS_ADD_TO_DRIVE "\xee\x99\x9c" // U+e65c #define ICON_MS_ADD_TO_HOME_SCREEN "\xee\x87\xbe" // U+e1fe #define ICON_MS_ADD_TO_PHOTOS "\xee\x8e\x9d" // U+e39d #define ICON_MS_ADD_TO_QUEUE "\xee\x81\x9c" // U+e05c #define ICON_MS_ADD_TRIANGLE "\xef\x92\x8e" // U+f48e #define ICON_MS_ADDCHART "\xee\xbc\xbc" // U+ef3c #define ICON_MS_ADF_SCANNER "\xee\xab\x9a" // U+eada #define ICON_MS_ADJUST "\xee\x8e\x9e" // U+e39e #define ICON_MS_ADMIN_MEDS "\xee\x92\x8d" // U+e48d #define ICON_MS_ADMIN_PANEL_SETTINGS "\xee\xbc\xbd" // U+ef3d #define ICON_MS_ADS_CLICK "\xee\x9d\xa2" // U+e762 #define ICON_MS_AGENDER "\xef\xa2\x88" // U+f888 #define ICON_MS_AGRICULTURE "\xee\xa9\xb9" // U+ea79 #define ICON_MS_AIR "\xee\xbf\x98" // U+efd8 #define ICON_MS_AIR_FRESHENER "\xee\x8b\x8a" // U+e2ca #define ICON_MS_AIR_PURIFIER "\xee\xa5\xbe" // U+e97e #define ICON_MS_AIR_PURIFIER_GEN "\xee\xa0\xa9" // U+e829 #define ICON_MS_AIRLINE_SEAT_FLAT "\xee\x98\xb0" // U+e630 #define ICON_MS_AIRLINE_SEAT_FLAT_ANGLED "\xee\x98\xb1" // U+e631 #define ICON_MS_AIRLINE_SEAT_INDIVIDUAL_SUITE "\xee\x98\xb2" // U+e632 #define ICON_MS_AIRLINE_SEAT_LEGROOM_EXTRA "\xee\x98\xb3" // U+e633 #define ICON_MS_AIRLINE_SEAT_LEGROOM_NORMAL "\xee\x98\xb4" // U+e634 #define ICON_MS_AIRLINE_SEAT_LEGROOM_REDUCED "\xee\x98\xb5" // U+e635 #define ICON_MS_AIRLINE_SEAT_RECLINE_EXTRA "\xee\x98\xb6" // U+e636 #define ICON_MS_AIRLINE_SEAT_RECLINE_NORMAL "\xee\x98\xb7" // U+e637 #define ICON_MS_AIRLINE_STOPS "\xee\x9f\x90" // U+e7d0 #define ICON_MS_AIRLINES "\xee\x9f\x8a" // U+e7ca #define ICON_MS_AIRPLANE_TICKET "\xee\xbf\x99" // U+efd9 #define ICON_MS_AIRPLANEMODE_ACTIVE "\xee\x94\xbd" // U+e53d #define ICON_MS_AIRPLANEMODE_INACTIVE "\xee\x86\x94" // U+e194 #define ICON_MS_AIRPLAY "\xee\x81\x95" // U+e055 #define ICON_MS_AIRPORT_SHUTTLE "\xee\xac\xbc" // U+eb3c #define ICON_MS_AIRWARE "\xef\x85\x94" // U+f154 #define ICON_MS_AIRWAVE "\xef\x85\x94" // U+f154 #define ICON_MS_ALARM "\xee\xa1\x95" // U+e855 #define ICON_MS_ALARM_ADD "\xee\xa1\x96" // U+e856 #define ICON_MS_ALARM_OFF "\xee\xa1\x97" // U+e857 #define ICON_MS_ALARM_ON "\xee\xa1\x98" // U+e858 #define ICON_MS_ALARM_SMART_WAKE "\xef\x9a\xb0" // U+f6b0 #define ICON_MS_ALBUM "\xee\x80\x99" // U+e019 #define ICON_MS_ALIGN_CENTER "\xee\x8d\x96" // U+e356 #define ICON_MS_ALIGN_END "\xef\x9e\x97" // U+f797 #define ICON_MS_ALIGN_FLEX_CENTER "\xef\x9e\x96" // U+f796 #define ICON_MS_ALIGN_FLEX_END "\xef\x9e\x95" // U+f795 #define ICON_MS_ALIGN_FLEX_START "\xef\x9e\x94" // U+f794 #define ICON_MS_ALIGN_HORIZONTAL_CENTER "\xee\x80\x8f" // U+e00f #define ICON_MS_ALIGN_HORIZONTAL_LEFT "\xee\x80\x8d" // U+e00d #define ICON_MS_ALIGN_HORIZONTAL_RIGHT "\xee\x80\x90" // U+e010 #define ICON_MS_ALIGN_ITEMS_STRETCH "\xef\x9e\x93" // U+f793 #define ICON_MS_ALIGN_JUSTIFY_CENTER "\xef\x9e\x92" // U+f792 #define ICON_MS_ALIGN_JUSTIFY_FLEX_END "\xef\x9e\x91" // U+f791 #define ICON_MS_ALIGN_JUSTIFY_FLEX_START "\xef\x9e\x90" // U+f790 #define ICON_MS_ALIGN_JUSTIFY_SPACE_AROUND "\xef\x9e\x8f" // U+f78f #define ICON_MS_ALIGN_JUSTIFY_SPACE_BETWEEN "\xef\x9e\x8e" // U+f78e #define ICON_MS_ALIGN_JUSTIFY_SPACE_EVEN "\xef\x9e\x8d" // U+f78d #define ICON_MS_ALIGN_JUSTIFY_STRETCH "\xef\x9e\x8c" // U+f78c #define ICON_MS_ALIGN_SELF_STRETCH "\xef\x9e\x8b" // U+f78b #define ICON_MS_ALIGN_SPACE_AROUND "\xef\x9e\x8a" // U+f78a #define ICON_MS_ALIGN_SPACE_BETWEEN "\xef\x9e\x89" // U+f789 #define ICON_MS_ALIGN_SPACE_EVEN "\xef\x9e\x88" // U+f788 #define ICON_MS_ALIGN_START "\xef\x9e\x87" // U+f787 #define ICON_MS_ALIGN_STRETCH "\xef\x9e\x86" // U+f786 #define ICON_MS_ALIGN_VERTICAL_BOTTOM "\xee\x80\x95" // U+e015 #define ICON_MS_ALIGN_VERTICAL_CENTER "\xee\x80\x91" // U+e011 #define ICON_MS_ALIGN_VERTICAL_TOP "\xee\x80\x8c" // U+e00c #define ICON_MS_ALL_INBOX "\xee\xa5\xbf" // U+e97f #define ICON_MS_ALL_INCLUSIVE "\xee\xac\xbd" // U+eb3d #define ICON_MS_ALL_MATCH "\xee\x82\x93" // U+e093 #define ICON_MS_ALL_OUT "\xee\xa4\x8b" // U+e90b #define ICON_MS_ALLERGIES "\xee\x82\x94" // U+e094 #define ICON_MS_ALLERGY "\xee\x99\x8e" // U+e64e #define ICON_MS_ALT_ROUTE "\xef\x86\x84" // U+f184 #define ICON_MS_ALTERNATE_EMAIL "\xee\x83\xa6" // U+e0e6 #define ICON_MS_ALTITUDE "\xef\xa1\xb3" // U+f873 #define ICON_MS_AMBIENT_SCREEN "\xef\x9b\x84" // U+f6c4 #define ICON_MS_AMBULANCE "\xef\xa0\x83" // U+f803 #define ICON_MS_AMEND "\xef\xa0\x82" // U+f802 #define ICON_MS_AMP_STORIES "\xee\xa8\x93" // U+ea13 #define ICON_MS_ANALYTICS "\xee\xbc\xbe" // U+ef3e #define ICON_MS_ANCHOR "\xef\x87\x8d" // U+f1cd #define ICON_MS_ANDROID "\xee\xa1\x99" // U+e859 #define ICON_MS_ANIMATED_IMAGES "\xef\x92\x9a" // U+f49a #define ICON_MS_ANIMATION "\xee\x9c\x9c" // U+e71c #define ICON_MS_ANNOUNCEMENT "\xee\xa1\xbf" // U+e87f #define ICON_MS_AOD "\xee\xbf\x9a" // U+efda #define ICON_MS_AOD_TABLET "\xef\xa2\x9f" // U+f89f #define ICON_MS_AOD_WATCH "\xef\x9a\xac" // U+f6ac #define ICON_MS_APARTMENT "\xee\xa9\x80" // U+ea40 #define ICON_MS_API "\xef\x86\xb7" // U+f1b7 #define ICON_MS_APK_DOCUMENT "\xef\xa2\x8e" // U+f88e #define ICON_MS_APK_INSTALL "\xef\xa2\x8f" // U+f88f #define ICON_MS_APP_BADGING "\xef\x9c\xaf" // U+f72f #define ICON_MS_APP_BLOCKING "\xee\xbc\xbf" // U+ef3f #define ICON_MS_APP_PROMO "\xee\xa6\x81" // U+e981 #define ICON_MS_APP_REGISTRATION "\xee\xbd\x80" // U+ef40 #define ICON_MS_APP_SETTINGS_ALT "\xee\xbd\x81" // U+ef41 #define ICON_MS_APP_SHORTCUT "\xee\xab\xa4" // U+eae4 #define ICON_MS_APPAREL "\xee\xbd\xbb" // U+ef7b #define ICON_MS_APPROVAL "\xee\xa6\x82" // U+e982 #define ICON_MS_APPROVAL_DELEGATION "\xef\xa1\x8a" // U+f84a #define ICON_MS_APPS "\xee\x97\x83" // U+e5c3 #define ICON_MS_APPS_OUTAGE "\xee\x9f\x8c" // U+e7cc #define ICON_MS_AQ "\xef\x95\x9a" // U+f55a #define ICON_MS_AQ_INDOOR "\xef\x95\x9b" // U+f55b #define ICON_MS_AR_ON_YOU "\xee\xbd\xbc" // U+ef7c #define ICON_MS_AR_STICKERS "\xee\xa6\x83" // U+e983 #define ICON_MS_ARCHITECTURE "\xee\xa8\xbb" // U+ea3b #define ICON_MS_ARCHIVE "\xee\x85\x89" // U+e149 #define ICON_MS_AREA_CHART "\xee\x9d\xb0" // U+e770 #define ICON_MS_ARMING_COUNTDOWN "\xee\x9e\x8a" // U+e78a #define ICON_MS_ARROW_AND_EDGE "\xef\x97\x97" // U+f5d7 #define ICON_MS_ARROW_BACK "\xee\x97\x84" // U+e5c4 #define ICON_MS_ARROW_BACK_IOS "\xee\x97\xa0" // U+e5e0 #define ICON_MS_ARROW_BACK_IOS_NEW "\xee\x8b\xaa" // U+e2ea #define ICON_MS_ARROW_CIRCLE_DOWN "\xef\x86\x81" // U+f181 #define ICON_MS_ARROW_CIRCLE_LEFT "\xee\xaa\xa7" // U+eaa7 #define ICON_MS_ARROW_CIRCLE_RIGHT "\xee\xaa\xaa" // U+eaaa #define ICON_MS_ARROW_CIRCLE_UP "\xef\x86\x82" // U+f182 #define ICON_MS_ARROW_COOL_DOWN "\xef\x92\xb6" // U+f4b6 #define ICON_MS_ARROW_DOWNWARD "\xee\x97\x9b" // U+e5db #define ICON_MS_ARROW_DOWNWARD_ALT "\xee\xa6\x84" // U+e984 #define ICON_MS_ARROW_DROP_DOWN "\xee\x97\x85" // U+e5c5 #define ICON_MS_ARROW_DROP_DOWN_CIRCLE "\xee\x97\x86" // U+e5c6 #define ICON_MS_ARROW_DROP_UP "\xee\x97\x87" // U+e5c7 #define ICON_MS_ARROW_FORWARD "\xee\x97\x88" // U+e5c8 #define ICON_MS_ARROW_FORWARD_IOS "\xee\x97\xa1" // U+e5e1 #define ICON_MS_ARROW_INSERT "\xef\xa0\xb7" // U+f837 #define ICON_MS_ARROW_LEFT "\xee\x97\x9e" // U+e5de #define ICON_MS_ARROW_LEFT_ALT "\xee\xbd\xbd" // U+ef7d #define ICON_MS_ARROW_OR_EDGE "\xef\x97\x96" // U+f5d6 #define ICON_MS_ARROW_OUTWARD "\xef\xa3\x8e" // U+f8ce #define ICON_MS_ARROW_RANGE "\xef\x9a\x9b" // U+f69b #define ICON_MS_ARROW_RIGHT "\xee\x97\x9f" // U+e5df #define ICON_MS_ARROW_RIGHT_ALT "\xee\xa5\x81" // U+e941 #define ICON_MS_ARROW_SELECTOR_TOOL "\xef\xa0\xaf" // U+f82f #define ICON_MS_ARROW_SPLIT "\xee\xa8\x84" // U+ea04 #define ICON_MS_ARROW_TOP_LEFT "\xef\x9c\xae" // U+f72e #define ICON_MS_ARROW_TOP_RIGHT "\xef\x9c\xad" // U+f72d #define ICON_MS_ARROW_UPWARD "\xee\x97\x98" // U+e5d8 #define ICON_MS_ARROW_UPWARD_ALT "\xee\xa6\x86" // U+e986 #define ICON_MS_ARROW_WARM_UP "\xef\x92\xb5" // U+f4b5 #define ICON_MS_ARROWS_MORE_DOWN "\xef\xa2\xab" // U+f8ab #define ICON_MS_ARROWS_MORE_UP "\xef\xa2\xac" // U+f8ac #define ICON_MS_ARROWS_OUTWARD "\xef\x9c\xac" // U+f72c #define ICON_MS_ART_TRACK "\xee\x81\xa0" // U+e060 #define ICON_MS_ARTICLE "\xee\xbd\x82" // U+ef42 #define ICON_MS_ARTICLE_SHORTCUT "\xef\x96\x87" // U+f587 #define ICON_MS_ARTIST "\xee\x80\x9a" // U+e01a #define ICON_MS_ASPECT_RATIO "\xee\xa1\x9b" // U+e85b #define ICON_MS_ASSESSMENT "\xef\x83\x8c" // U+f0cc #define ICON_MS_ASSIGNMENT "\xee\xa1\x9d" // U+e85d #define ICON_MS_ASSIGNMENT_ADD "\xef\xa1\x88" // U+f848 #define ICON_MS_ASSIGNMENT_IND "\xee\xa1\x9e" // U+e85e #define ICON_MS_ASSIGNMENT_LATE "\xee\xa1\x9f" // U+e85f #define ICON_MS_ASSIGNMENT_RETURN "\xee\xa1\xa0" // U+e860 #define ICON_MS_ASSIGNMENT_RETURNED "\xee\xa1\xa1" // U+e861 #define ICON_MS_ASSIGNMENT_TURNED_IN "\xee\xa1\xa2" // U+e862 #define ICON_MS_ASSIST_WALKER "\xef\xa3\x95" // U+f8d5 #define ICON_MS_ASSISTANT "\xee\x8e\x9f" // U+e39f #define ICON_MS_ASSISTANT_DEVICE "\xee\xa6\x87" // U+e987 #define ICON_MS_ASSISTANT_DIRECTION "\xee\xa6\x88" // U+e988 #define ICON_MS_ASSISTANT_NAVIGATION "\xee\xa6\x89" // U+e989 #define ICON_MS_ASSISTANT_ON_HUB "\xef\x9b\x81" // U+f6c1 #define ICON_MS_ASSISTANT_PHOTO "\xef\x83\x86" // U+f0c6 #define ICON_MS_ASSURED_WORKLOAD "\xee\xad\xaf" // U+eb6f #define ICON_MS_ASTERISK "\xef\x94\xa5" // U+f525 #define ICON_MS_ASTROPHOTOGRAPHY_AUTO "\xef\x87\x99" // U+f1d9 #define ICON_MS_ASTROPHOTOGRAPHY_OFF "\xef\x87\x9a" // U+f1da #define ICON_MS_ATM "\xee\x95\xb3" // U+e573 #define ICON_MS_ATR "\xee\xaf\x87" // U+ebc7 #define ICON_MS_ATTACH_EMAIL "\xee\xa9\x9e" // U+ea5e #define ICON_MS_ATTACH_FILE "\xee\x88\xa6" // U+e226 #define ICON_MS_ATTACH_FILE_ADD "\xef\xa1\x81" // U+f841 #define ICON_MS_ATTACH_FILE_OFF "\xef\x93\x99" // U+f4d9 #define ICON_MS_ATTACH_MONEY "\xee\x88\xa7" // U+e227 #define ICON_MS_ATTACHMENT "\xee\x8a\xbc" // U+e2bc #define ICON_MS_ATTRACTIONS "\xee\xa9\x92" // U+ea52 #define ICON_MS_ATTRIBUTION "\xee\xbf\x9b" // U+efdb #define ICON_MS_AUDIO_DESCRIPTION "\xef\x96\x8c" // U+f58c #define ICON_MS_AUDIO_FILE "\xee\xae\x82" // U+eb82 #define ICON_MS_AUDIO_VIDEO_RECEIVER "\xef\x97\x93" // U+f5d3 #define ICON_MS_AUDIOTRACK "\xee\x90\x85" // U+e405 #define ICON_MS_AUTO_ACTIVITY_ZONE "\xef\xa2\xad" // U+f8ad #define ICON_MS_AUTO_AWESOME "\xee\x99\x9f" // U+e65f #define ICON_MS_AUTO_AWESOME_MOSAIC "\xee\x99\xa0" // U+e660 #define ICON_MS_AUTO_AWESOME_MOTION "\xee\x99\xa1" // U+e661 #define ICON_MS_AUTO_DELETE "\xee\xa9\x8c" // U+ea4c #define ICON_MS_AUTO_DETECT_VOICE "\xef\xa0\xbe" // U+f83e #define ICON_MS_AUTO_DRAW_SOLID "\xee\xa6\x8a" // U+e98a #define ICON_MS_AUTO_FIX "\xee\x99\xa3" // U+e663 #define ICON_MS_AUTO_FIX_HIGH "\xee\x99\xa3" // U+e663 #define ICON_MS_AUTO_FIX_NORMAL "\xee\x99\xa4" // U+e664 #define ICON_MS_AUTO_FIX_OFF "\xee\x99\xa5" // U+e665 #define ICON_MS_AUTO_GRAPH "\xee\x93\xbb" // U+e4fb #define ICON_MS_AUTO_LABEL "\xef\x9a\xbe" // U+f6be #define ICON_MS_AUTO_MEETING_ROOM "\xef\x9a\xbf" // U+f6bf #define ICON_MS_AUTO_MODE "\xee\xb0\xa0" // U+ec20 #define ICON_MS_AUTO_READ_PAUSE "\xef\x88\x99" // U+f219 #define ICON_MS_AUTO_READ_PLAY "\xef\x88\x96" // U+f216 #define ICON_MS_AUTO_SCHEDULE "\xee\x88\x94" // U+e214 #define ICON_MS_AUTO_STORIES "\xee\x99\xa6" // U+e666 #define ICON_MS_AUTO_TIMER "\xee\xbd\xbf" // U+ef7f #define ICON_MS_AUTO_TOWING "\xee\x9c\x9e" // U+e71e #define ICON_MS_AUTO_TRANSMISSION "\xef\x94\xbf" // U+f53f #define ICON_MS_AUTO_VIDEOCAM "\xef\x9b\x80" // U+f6c0 #define ICON_MS_AUTOFPS_SELECT "\xee\xbf\x9c" // U+efdc #define ICON_MS_AUTOPAUSE "\xef\x9a\xb6" // U+f6b6 #define ICON_MS_AUTOPAY "\xef\xa1\x8b" // U+f84b #define ICON_MS_AUTOPLAY "\xef\x9a\xb5" // U+f6b5 #define ICON_MS_AUTORENEW "\xee\xa1\xa3" // U+e863 #define ICON_MS_AUTOSTOP "\xef\x9a\x82" // U+f682 #define ICON_MS_AV1 "\xef\x92\xb0" // U+f4b0 #define ICON_MS_AV_TIMER "\xee\x80\x9b" // U+e01b #define ICON_MS_AVC "\xef\x92\xaf" // U+f4af #define ICON_MS_AVG_PACE "\xef\x9a\xbb" // U+f6bb #define ICON_MS_AVG_TIME "\xef\xa0\x93" // U+f813 #define ICON_MS_AWARD_STAR "\xef\x98\x92" // U+f612 #define ICON_MS_AZM "\xef\x9b\xac" // U+f6ec #define ICON_MS_BABY_CHANGING_STATION "\xef\x86\x9b" // U+f19b #define ICON_MS_BACK_HAND "\xee\x9d\xa4" // U+e764 #define ICON_MS_BACK_TO_TAB "\xef\x9c\xab" // U+f72b #define ICON_MS_BACKGROUND_DOT_LARGE "\xef\x9e\x9e" // U+f79e #define ICON_MS_BACKGROUND_DOT_SMALL "\xef\x94\x94" // U+f514 #define ICON_MS_BACKGROUND_GRID_SMALL "\xef\x9e\x9d" // U+f79d #define ICON_MS_BACKGROUND_REPLACE "\xef\x88\x8a" // U+f20a #define ICON_MS_BACKLIGHT_HIGH "\xef\x9f\xad" // U+f7ed #define ICON_MS_BACKLIGHT_HIGH_OFF "\xef\x93\xaf" // U+f4ef #define ICON_MS_BACKLIGHT_LOW "\xef\x9f\xac" // U+f7ec #define ICON_MS_BACKPACK "\xef\x86\x9c" // U+f19c #define ICON_MS_BACKSPACE "\xee\x85\x8a" // U+e14a #define ICON_MS_BACKUP "\xee\xa1\xa4" // U+e864 #define ICON_MS_BACKUP_TABLE "\xee\xbd\x83" // U+ef43 #define ICON_MS_BADGE "\xee\xa9\xa7" // U+ea67 #define ICON_MS_BADGE_CRITICAL_BATTERY "\xef\x85\x96" // U+f156 #define ICON_MS_BAKERY_DINING "\xee\xa9\x93" // U+ea53 #define ICON_MS_BALANCE "\xee\xab\xb6" // U+eaf6 #define ICON_MS_BALCONY "\xee\x96\x8f" // U+e58f #define ICON_MS_BALLOT "\xee\x85\xb2" // U+e172 #define ICON_MS_BAR_CHART "\xee\x89\xab" // U+e26b #define ICON_MS_BAR_CHART_4_BARS "\xef\x9a\x81" // U+f681 #define ICON_MS_BARCODE "\xee\x9c\x8b" // U+e70b #define ICON_MS_BARCODE_READER "\xef\xa1\x9c" // U+f85c #define ICON_MS_BARCODE_SCANNER "\xee\x9c\x8c" // U+e70c #define ICON_MS_BAREFOOT "\xef\xa1\xb1" // U+f871 #define ICON_MS_BATCH_PREDICTION "\xef\x83\xb5" // U+f0f5 #define ICON_MS_BATH_OUTDOOR "\xef\x9b\xbb" // U+f6fb #define ICON_MS_BATH_PRIVATE "\xef\x9b\xba" // U+f6fa #define ICON_MS_BATH_PUBLIC_LARGE "\xef\x9b\xb9" // U+f6f9 #define ICON_MS_BATHROOM "\xee\xbf\x9d" // U+efdd #define ICON_MS_BATHTUB "\xee\xa9\x81" // U+ea41 #define ICON_MS_BATTERY_0_BAR "\xee\xaf\x9c" // U+ebdc #define ICON_MS_BATTERY_1_BAR "\xef\x82\x9c" // U+f09c #define ICON_MS_BATTERY_20 "\xef\x82\x9c" // U+f09c #define ICON_MS_BATTERY_2_BAR "\xef\x82\x9d" // U+f09d #define ICON_MS_BATTERY_30 "\xef\x82\x9d" // U+f09d #define ICON_MS_BATTERY_3_BAR "\xef\x82\x9e" // U+f09e #define ICON_MS_BATTERY_4_BAR "\xef\x82\x9f" // U+f09f #define ICON_MS_BATTERY_50 "\xef\x82\x9e" // U+f09e #define ICON_MS_BATTERY_5_BAR "\xef\x82\xa0" // U+f0a0 #define ICON_MS_BATTERY_60 "\xef\x82\x9f" // U+f09f #define ICON_MS_BATTERY_6_BAR "\xef\x82\xa1" // U+f0a1 #define ICON_MS_BATTERY_80 "\xef\x82\xa0" // U+f0a0 #define ICON_MS_BATTERY_90 "\xef\x82\xa1" // U+f0a1 #define ICON_MS_BATTERY_ALERT "\xee\x86\x9c" // U+e19c #define ICON_MS_BATTERY_CHANGE "\xef\x9f\xab" // U+f7eb #define ICON_MS_BATTERY_CHARGING_20 "\xef\x82\xa2" // U+f0a2 #define ICON_MS_BATTERY_CHARGING_30 "\xef\x82\xa3" // U+f0a3 #define ICON_MS_BATTERY_CHARGING_50 "\xef\x82\xa4" // U+f0a4 #define ICON_MS_BATTERY_CHARGING_60 "\xef\x82\xa5" // U+f0a5 #define ICON_MS_BATTERY_CHARGING_80 "\xef\x82\xa6" // U+f0a6 #define ICON_MS_BATTERY_CHARGING_90 "\xef\x82\xa7" // U+f0a7 #define ICON_MS_BATTERY_CHARGING_FULL "\xee\x86\xa3" // U+e1a3 #define ICON_MS_BATTERY_ERROR "\xef\x9f\xaa" // U+f7ea #define ICON_MS_BATTERY_FULL "\xee\x86\xa5" // U+e1a5 #define ICON_MS_BATTERY_FULL_ALT "\xef\x84\xbb" // U+f13b #define ICON_MS_BATTERY_HORIZ_000 "\xef\xa2\xae" // U+f8ae #define ICON_MS_BATTERY_HORIZ_050 "\xef\xa2\xaf" // U+f8af #define ICON_MS_BATTERY_HORIZ_075 "\xef\xa2\xb0" // U+f8b0 #define ICON_MS_BATTERY_LOW "\xef\x85\x95" // U+f155 #define ICON_MS_BATTERY_PLUS "\xef\x9f\xa9" // U+f7e9 #define ICON_MS_BATTERY_PROFILE "\xee\x88\x86" // U+e206 #define ICON_MS_BATTERY_SAVER "\xee\xbf\x9e" // U+efde #define ICON_MS_BATTERY_SHARE "\xef\x99\xbe" // U+f67e #define ICON_MS_BATTERY_STATUS_GOOD "\xef\x99\xbd" // U+f67d #define ICON_MS_BATTERY_STD "\xee\x86\xa5" // U+e1a5 #define ICON_MS_BATTERY_UNKNOWN "\xee\x86\xa6" // U+e1a6 #define ICON_MS_BATTERY_VERT_005 "\xef\xa2\xb1" // U+f8b1 #define ICON_MS_BATTERY_VERT_020 "\xef\xa2\xb2" // U+f8b2 #define ICON_MS_BATTERY_VERT_050 "\xef\xa2\xb3" // U+f8b3 #define ICON_MS_BATTERY_VERY_LOW "\xef\x85\x96" // U+f156 #define ICON_MS_BEACH_ACCESS "\xee\xac\xbe" // U+eb3e #define ICON_MS_BED "\xee\xbf\x9f" // U+efdf #define ICON_MS_BEDROOM_BABY "\xee\xbf\xa0" // U+efe0 #define ICON_MS_BEDROOM_CHILD "\xee\xbf\xa1" // U+efe1 #define ICON_MS_BEDROOM_PARENT "\xee\xbf\xa2" // U+efe2 #define ICON_MS_BEDTIME "\xee\xbd\x84" // U+ef44 #define ICON_MS_BEDTIME_OFF "\xee\xad\xb6" // U+eb76 #define ICON_MS_BEENHERE "\xee\x94\xad" // U+e52d #define ICON_MS_BENTO "\xef\x87\xb4" // U+f1f4 #define ICON_MS_BIA "\xef\x9b\xab" // U+f6eb #define ICON_MS_BID_LANDSCAPE "\xee\x99\xb8" // U+e678 #define ICON_MS_BID_LANDSCAPE_DISABLED "\xee\xbe\x81" // U+ef81 #define ICON_MS_BIGTOP_UPDATES "\xee\x99\xa9" // U+e669 #define ICON_MS_BIKE_SCOOTER "\xee\xbd\x85" // U+ef45 #define ICON_MS_BIOTECH "\xee\xa8\xba" // U+ea3a #define ICON_MS_BLANKET "\xee\xa0\xa8" // U+e828 #define ICON_MS_BLENDER "\xee\xbf\xa3" // U+efe3 #define ICON_MS_BLIND "\xef\xa3\x96" // U+f8d6 #define ICON_MS_BLINDS "\xee\x8a\x86" // U+e286 #define ICON_MS_BLINDS_CLOSED "\xee\xb0\x9f" // U+ec1f #define ICON_MS_BLOCK "\xef\x82\x8c" // U+f08c #define ICON_MS_BLOOD_PRESSURE "\xee\x82\x97" // U+e097 #define ICON_MS_BLOODTYPE "\xee\xbf\xa4" // U+efe4 #define ICON_MS_BLUETOOTH "\xee\x86\xa7" // U+e1a7 #define ICON_MS_BLUETOOTH_AUDIO "\xee\x98\x8f" // U+e60f #define ICON_MS_BLUETOOTH_CONNECTED "\xee\x86\xa8" // U+e1a8 #define ICON_MS_BLUETOOTH_DISABLED "\xee\x86\xa9" // U+e1a9 #define ICON_MS_BLUETOOTH_DRIVE "\xee\xbf\xa5" // U+efe5 #define ICON_MS_BLUETOOTH_SEARCHING "\xee\x98\x8f" // U+e60f #define ICON_MS_BLUR_CIRCULAR "\xee\x8e\xa2" // U+e3a2 #define ICON_MS_BLUR_LINEAR "\xee\x8e\xa3" // U+e3a3 #define ICON_MS_BLUR_MEDIUM "\xee\xa1\x8c" // U+e84c #define ICON_MS_BLUR_OFF "\xee\x8e\xa4" // U+e3a4 #define ICON_MS_BLUR_ON "\xee\x8e\xa5" // U+e3a5 #define ICON_MS_BLUR_SHORT "\xee\xa3\x8f" // U+e8cf #define ICON_MS_BODY_FAT "\xee\x82\x98" // U+e098 #define ICON_MS_BODY_SYSTEM "\xee\x82\x99" // U+e099 #define ICON_MS_BOLT "\xee\xa8\x8b" // U+ea0b #define ICON_MS_BOMB "\xef\x95\xa8" // U+f568 #define ICON_MS_BOOK "\xee\xa1\xae" // U+e86e #define ICON_MS_BOOK_2 "\xef\x94\xbe" // U+f53e #define ICON_MS_BOOK_3 "\xef\x94\xbd" // U+f53d #define ICON_MS_BOOK_4 "\xef\x94\xbc" // U+f53c #define ICON_MS_BOOK_5 "\xef\x94\xbb" // U+f53b #define ICON_MS_BOOK_ONLINE "\xef\x88\x97" // U+f217 #define ICON_MS_BOOKMARK "\xee\xa3\xa7" // U+e8e7 #define ICON_MS_BOOKMARK_ADD "\xee\x96\x98" // U+e598 #define ICON_MS_BOOKMARK_ADDED "\xee\x96\x99" // U+e599 #define ICON_MS_BOOKMARK_BORDER "\xee\xa3\xa7" // U+e8e7 #define ICON_MS_BOOKMARK_MANAGER "\xef\x9e\xb1" // U+f7b1 #define ICON_MS_BOOKMARK_REMOVE "\xee\x96\x9a" // U+e59a #define ICON_MS_BOOKMARKS "\xee\xa6\x8b" // U+e98b #define ICON_MS_BORDER_ALL "\xee\x88\xa8" // U+e228 #define ICON_MS_BORDER_BOTTOM "\xee\x88\xa9" // U+e229 #define ICON_MS_BORDER_CLEAR "\xee\x88\xaa" // U+e22a #define ICON_MS_BORDER_COLOR "\xee\x88\xab" // U+e22b #define ICON_MS_BORDER_HORIZONTAL "\xee\x88\xac" // U+e22c #define ICON_MS_BORDER_INNER "\xee\x88\xad" // U+e22d #define ICON_MS_BORDER_LEFT "\xee\x88\xae" // U+e22e #define ICON_MS_BORDER_OUTER "\xee\x88\xaf" // U+e22f #define ICON_MS_BORDER_RIGHT "\xee\x88\xb0" // U+e230 #define ICON_MS_BORDER_STYLE "\xee\x88\xb1" // U+e231 #define ICON_MS_BORDER_TOP "\xee\x88\xb2" // U+e232 #define ICON_MS_BORDER_VERTICAL "\xee\x88\xb3" // U+e233 #define ICON_MS_BOTTOM_APP_BAR "\xee\x9c\xb0" // U+e730 #define ICON_MS_BOTTOM_DRAWER "\xee\x9c\xad" // U+e72d #define ICON_MS_BOTTOM_NAVIGATION "\xee\xa6\x8c" // U+e98c #define ICON_MS_BOTTOM_PANEL_CLOSE "\xef\x9c\xaa" // U+f72a #define ICON_MS_BOTTOM_PANEL_OPEN "\xef\x9c\xa9" // U+f729 #define ICON_MS_BOTTOM_RIGHT_CLICK "\xef\x9a\x84" // U+f684 #define ICON_MS_BOTTOM_SHEETS "\xee\xa6\x8d" // U+e98d #define ICON_MS_BOX "\xef\x96\xa4" // U+f5a4 #define ICON_MS_BOX_ADD "\xef\x96\xa5" // U+f5a5 #define ICON_MS_BOX_EDIT "\xef\x96\xa6" // U+f5a6 #define ICON_MS_BOY "\xee\xad\xa7" // U+eb67 #define ICON_MS_BRAND_AWARENESS "\xee\xa6\x8e" // U+e98e #define ICON_MS_BRAND_FAMILY "\xef\x93\xb1" // U+f4f1 #define ICON_MS_BRANDING_WATERMARK "\xee\x81\xab" // U+e06b #define ICON_MS_BREAKFAST_DINING "\xee\xa9\x94" // U+ea54 #define ICON_MS_BREAKING_NEWS "\xee\xa8\x88" // U+ea08 #define ICON_MS_BREAKING_NEWS_ALT_1 "\xef\x82\xba" // U+f0ba #define ICON_MS_BREASTFEEDING "\xef\xa1\x96" // U+f856 #define ICON_MS_BRIGHTNESS_1 "\xee\x8f\xba" // U+e3fa #define ICON_MS_BRIGHTNESS_2 "\xef\x80\xb6" // U+f036 #define ICON_MS_BRIGHTNESS_3 "\xee\x8e\xa8" // U+e3a8 #define ICON_MS_BRIGHTNESS_4 "\xee\x8e\xa9" // U+e3a9 #define ICON_MS_BRIGHTNESS_5 "\xee\x8e\xaa" // U+e3aa #define ICON_MS_BRIGHTNESS_6 "\xee\x8e\xab" // U+e3ab #define ICON_MS_BRIGHTNESS_7 "\xee\x8e\xac" // U+e3ac #define ICON_MS_BRIGHTNESS_ALERT "\xef\x97\x8f" // U+f5cf #define ICON_MS_BRIGHTNESS_AUTO "\xee\x86\xab" // U+e1ab #define ICON_MS_BRIGHTNESS_EMPTY "\xef\x9f\xa8" // U+f7e8 #define ICON_MS_BRIGHTNESS_HIGH "\xee\x86\xac" // U+e1ac #define ICON_MS_BRIGHTNESS_LOW "\xee\x86\xad" // U+e1ad #define ICON_MS_BRIGHTNESS_MEDIUM "\xee\x86\xae" // U+e1ae #define ICON_MS_BRING_YOUR_OWN_IP "\xee\x80\x96" // U+e016 #define ICON_MS_BROADCAST_ON_HOME "\xef\xa3\xb8" // U+f8f8 #define ICON_MS_BROADCAST_ON_PERSONAL "\xef\xa3\xb9" // U+f8f9 #define ICON_MS_BROKEN_IMAGE "\xee\x8e\xad" // U+e3ad #define ICON_MS_BROWSE "\xee\xac\x93" // U+eb13 #define ICON_MS_BROWSE_ACTIVITY "\xef\xa2\xa5" // U+f8a5 #define ICON_MS_BROWSE_GALLERY "\xee\xaf\x91" // U+ebd1 #define ICON_MS_BROWSER_NOT_SUPPORTED "\xee\xbd\x87" // U+ef47 #define ICON_MS_BROWSER_UPDATED "\xee\x9f\x8f" // U+e7cf #define ICON_MS_BRUNCH_DINING "\xee\xa9\xb3" // U+ea73 #define ICON_MS_BRUSH "\xee\x8e\xae" // U+e3ae #define ICON_MS_BUBBLE "\xee\xbe\x83" // U+ef83 #define ICON_MS_BUBBLE_CHART "\xee\x9b\x9d" // U+e6dd #define ICON_MS_BUBBLES "\xef\x99\x8e" // U+f64e #define ICON_MS_BUG_REPORT "\xee\xa1\xa8" // U+e868 #define ICON_MS_BUILD "\xef\xa3\x8d" // U+f8cd #define ICON_MS_BUILD_CIRCLE "\xee\xbd\x88" // U+ef48 #define ICON_MS_BUNGALOW "\xee\x96\x91" // U+e591 #define ICON_MS_BURST_MODE "\xee\x90\xbc" // U+e43c #define ICON_MS_BUS_ALERT "\xee\xa6\x8f" // U+e98f #define ICON_MS_BUSINESS "\xee\x9f\xae" // U+e7ee #define ICON_MS_BUSINESS_CENTER "\xee\xac\xbf" // U+eb3f #define ICON_MS_BUSINESS_CHIP "\xef\xa1\x8c" // U+f84c #define ICON_MS_BUSINESS_MESSAGES "\xee\xbe\x84" // U+ef84 #define ICON_MS_BUTTONS_ALT "\xee\x9c\xaf" // U+e72f #define ICON_MS_CABIN "\xee\x96\x89" // U+e589 #define ICON_MS_CABLE "\xee\xbf\xa6" // U+efe6 #define ICON_MS_CACHED "\xee\xa1\xaa" // U+e86a #define ICON_MS_CADENCE "\xef\x92\xb4" // U+f4b4 #define ICON_MS_CAKE "\xee\x9f\xa9" // U+e7e9 #define ICON_MS_CAKE_ADD "\xef\xa1\x9b" // U+f85b #define ICON_MS_CALCULATE "\xee\xa9\x9f" // U+ea5f #define ICON_MS_CALENDAR_ADD_ON "\xee\xbe\x85" // U+ef85 #define ICON_MS_CALENDAR_APPS_SCRIPT "\xef\x82\xbb" // U+f0bb #define ICON_MS_CALENDAR_CLOCK "\xef\x95\x80" // U+f540 #define ICON_MS_CALENDAR_MONTH "\xee\xaf\x8c" // U+ebcc #define ICON_MS_CALENDAR_TODAY "\xee\xa4\xb5" // U+e935 #define ICON_MS_CALENDAR_VIEW_DAY "\xee\xa4\xb6" // U+e936 #define ICON_MS_CALENDAR_VIEW_MONTH "\xee\xbf\xa7" // U+efe7 #define ICON_MS_CALENDAR_VIEW_WEEK "\xee\xbf\xa8" // U+efe8 #define ICON_MS_CALL "\xef\x83\x94" // U+f0d4 #define ICON_MS_CALL_END "\xef\x82\xbc" // U+f0bc #define ICON_MS_CALL_END_ALT "\xef\x82\xbc" // U+f0bc #define ICON_MS_CALL_LOG "\xee\x82\x8e" // U+e08e #define ICON_MS_CALL_MADE "\xee\x82\xb2" // U+e0b2 #define ICON_MS_CALL_MERGE "\xee\x82\xb3" // U+e0b3 #define ICON_MS_CALL_MISSED "\xee\x82\xb4" // U+e0b4 #define ICON_MS_CALL_MISSED_OUTGOING "\xee\x83\xa4" // U+e0e4 #define ICON_MS_CALL_QUALITY "\xef\x99\x92" // U+f652 #define ICON_MS_CALL_RECEIVED "\xee\x82\xb5" // U+e0b5 #define ICON_MS_CALL_SPLIT "\xee\x82\xb6" // U+e0b6 #define ICON_MS_CALL_TO_ACTION "\xee\x81\xac" // U+e06c #define ICON_MS_CAMERA "\xee\x8e\xaf" // U+e3af #define ICON_MS_CAMERA_ALT "\xee\x90\x92" // U+e412 #define ICON_MS_CAMERA_ENHANCE "\xee\xa3\xbc" // U+e8fc #define ICON_MS_CAMERA_FRONT "\xee\x8e\xb1" // U+e3b1 #define ICON_MS_CAMERA_INDOOR "\xee\xbf\xa9" // U+efe9 #define ICON_MS_CAMERA_OUTDOOR "\xee\xbf\xaa" // U+efea #define ICON_MS_CAMERA_REAR "\xee\x8e\xb2" // U+e3b2 #define ICON_MS_CAMERA_ROLL "\xee\x8e\xb3" // U+e3b3 #define ICON_MS_CAMERA_VIDEO "\xef\x9e\xa6" // U+f7a6 #define ICON_MS_CAMERASWITCH "\xee\xbf\xab" // U+efeb #define ICON_MS_CAMPAIGN "\xee\xbd\x89" // U+ef49 #define ICON_MS_CAMPING "\xef\xa2\xa2" // U+f8a2 #define ICON_MS_CANCEL "\xee\xa2\x88" // U+e888 #define ICON_MS_CANCEL_PRESENTATION "\xee\x83\xa9" // U+e0e9 #define ICON_MS_CANCEL_SCHEDULE_SEND "\xee\xa8\xb9" // U+ea39 #define ICON_MS_CANDLE "\xef\x96\x88" // U+f588 #define ICON_MS_CANDLESTICK_CHART "\xee\xab\x94" // U+ead4 #define ICON_MS_CAPTIVE_PORTAL "\xef\x9c\xa8" // U+f728 #define ICON_MS_CAPTURE "\xef\x9c\xa7" // U+f727 #define ICON_MS_CAR_CRASH "\xee\xaf\xb2" // U+ebf2 #define ICON_MS_CAR_RENTAL "\xee\xa9\x95" // U+ea55 #define ICON_MS_CAR_REPAIR "\xee\xa9\x96" // U+ea56 #define ICON_MS_CAR_TAG "\xef\x93\xa3" // U+f4e3 #define ICON_MS_CARD_GIFTCARD "\xee\xa3\xb6" // U+e8f6 #define ICON_MS_CARD_MEMBERSHIP "\xee\xa3\xb7" // U+e8f7 #define ICON_MS_CARD_TRAVEL "\xee\xa3\xb8" // U+e8f8 #define ICON_MS_CARDIO_LOAD "\xef\x92\xb9" // U+f4b9 #define ICON_MS_CARDIOLOGY "\xee\x82\x9c" // U+e09c #define ICON_MS_CARDS "\xee\xa6\x91" // U+e991 #define ICON_MS_CARPENTER "\xef\x87\xb8" // U+f1f8 #define ICON_MS_CARRY_ON_BAG "\xee\xac\x88" // U+eb08 #define ICON_MS_CARRY_ON_BAG_CHECKED "\xee\xac\x8b" // U+eb0b #define ICON_MS_CARRY_ON_BAG_INACTIVE "\xee\xac\x8a" // U+eb0a #define ICON_MS_CARRY_ON_BAG_QUESTION "\xee\xac\x89" // U+eb09 #define ICON_MS_CASES "\xee\xa6\x92" // U+e992 #define ICON_MS_CASINO "\xee\xad\x80" // U+eb40 #define ICON_MS_CAST "\xee\x8c\x87" // U+e307 #define ICON_MS_CAST_CONNECTED "\xee\x8c\x88" // U+e308 #define ICON_MS_CAST_FOR_EDUCATION "\xee\xbf\xac" // U+efec #define ICON_MS_CAST_PAUSE "\xef\x97\xb0" // U+f5f0 #define ICON_MS_CAST_WARNING "\xef\x97\xaf" // U+f5ef #define ICON_MS_CASTLE "\xee\xaa\xb1" // U+eab1 #define ICON_MS_CATEGORY "\xee\x95\xb4" // U+e574 #define ICON_MS_CELEBRATION "\xee\xa9\xa5" // U+ea65 #define ICON_MS_CELL_MERGE "\xef\xa0\xae" // U+f82e #define ICON_MS_CELL_TOWER "\xee\xae\xba" // U+ebba #define ICON_MS_CELL_WIFI "\xee\x83\xac" // U+e0ec #define ICON_MS_CENTER_FOCUS_STRONG "\xee\x8e\xb4" // U+e3b4 #define ICON_MS_CENTER_FOCUS_WEAK "\xee\x8e\xb5" // U+e3b5 #define ICON_MS_CHAIR "\xee\xbf\xad" // U+efed #define ICON_MS_CHAIR_ALT "\xee\xbf\xae" // U+efee #define ICON_MS_CHALET "\xee\x96\x85" // U+e585 #define ICON_MS_CHANGE_CIRCLE "\xee\x8b\xa7" // U+e2e7 #define ICON_MS_CHANGE_HISTORY "\xee\xa1\xab" // U+e86b #define ICON_MS_CHARGER "\xee\x8a\xae" // U+e2ae #define ICON_MS_CHARGING_STATION "\xef\x86\x9d" // U+f19d #define ICON_MS_CHART_DATA "\xee\x91\xb3" // U+e473 #define ICON_MS_CHAT "\xee\x83\x89" // U+e0c9 #define ICON_MS_CHAT_ADD_ON "\xef\x83\xb3" // U+f0f3 #define ICON_MS_CHAT_APPS_SCRIPT "\xef\x82\xbd" // U+f0bd #define ICON_MS_CHAT_BUBBLE "\xee\x83\x8b" // U+e0cb #define ICON_MS_CHAT_BUBBLE_OUTLINE "\xee\x83\x8b" // U+e0cb #define ICON_MS_CHAT_ERROR "\xef\x9e\xac" // U+f7ac #define ICON_MS_CHAT_INFO "\xef\x94\xab" // U+f52b #define ICON_MS_CHAT_PASTE_GO "\xef\x9a\xbd" // U+f6bd #define ICON_MS_CHECK "\xee\x97\x8a" // U+e5ca #define ICON_MS_CHECK_BOX "\xee\xa0\xb4" // U+e834 #define ICON_MS_CHECK_BOX_OUTLINE_BLANK "\xee\xa0\xb5" // U+e835 #define ICON_MS_CHECK_CIRCLE "\xef\x82\xbe" // U+f0be #define ICON_MS_CHECK_CIRCLE_FILLED "\xef\x82\xbe" // U+f0be #define ICON_MS_CHECK_CIRCLE_OUTLINE "\xef\x82\xbe" // U+f0be #define ICON_MS_CHECK_IN_OUT "\xef\x9b\xb6" // U+f6f6 #define ICON_MS_CHECK_INDETERMINATE_SMALL "\xef\xa2\x8a" // U+f88a #define ICON_MS_CHECK_SMALL "\xef\xa2\x8b" // U+f88b #define ICON_MS_CHECKBOOK "\xee\x9c\x8d" // U+e70d #define ICON_MS_CHECKED_BAG "\xee\xac\x8c" // U+eb0c #define ICON_MS_CHECKED_BAG_QUESTION "\xee\xac\x8d" // U+eb0d #define ICON_MS_CHECKLIST "\xee\x9a\xb1" // U+e6b1 #define ICON_MS_CHECKLIST_RTL "\xee\x9a\xb3" // U+e6b3 #define ICON_MS_CHECKROOM "\xef\x86\x9e" // U+f19e #define ICON_MS_CHEER "\xef\x9a\xa8" // U+f6a8 #define ICON_MS_CHESS "\xef\x97\xa7" // U+f5e7 #define ICON_MS_CHEVRON_LEFT "\xee\x97\x8b" // U+e5cb #define ICON_MS_CHEVRON_RIGHT "\xee\x97\x8c" // U+e5cc #define ICON_MS_CHILD_CARE "\xee\xad\x81" // U+eb41 #define ICON_MS_CHILD_FRIENDLY "\xee\xad\x82" // U+eb42 #define ICON_MS_CHIP_EXTRACTION "\xef\xa0\xa1" // U+f821 #define ICON_MS_CHIPS "\xee\xa6\x93" // U+e993 #define ICON_MS_CHROME_READER_MODE "\xee\xa1\xad" // U+e86d #define ICON_MS_CHROMECAST_2 "\xef\x85\xbb" // U+f17b #define ICON_MS_CHROMECAST_DEVICE "\xee\xa0\xbc" // U+e83c #define ICON_MS_CHRONIC "\xee\xae\xb2" // U+ebb2 #define ICON_MS_CHURCH "\xee\xaa\xae" // U+eaae #define ICON_MS_CINEMATIC_BLUR "\xef\xa1\x93" // U+f853 #define ICON_MS_CIRCLE "\xee\xbd\x8a" // U+ef4a #define ICON_MS_CIRCLE_NOTIFICATIONS "\xee\xa6\x94" // U+e994 #define ICON_MS_CIRCLES "\xee\x9f\xaa" // U+e7ea #define ICON_MS_CIRCLES_EXT "\xee\x9f\xac" // U+e7ec #define ICON_MS_CLARIFY "\xef\x82\xbf" // U+f0bf #define ICON_MS_CLASS "\xee\xa1\xae" // U+e86e #define ICON_MS_CLEAN_HANDS "\xef\x88\x9f" // U+f21f #define ICON_MS_CLEANING "\xee\xa6\x95" // U+e995 #define ICON_MS_CLEANING_BUCKET "\xef\xa2\xb4" // U+f8b4 #define ICON_MS_CLEANING_SERVICES "\xef\x83\xbf" // U+f0ff #define ICON_MS_CLEAR "\xee\x97\x8d" // U+e5cd #define ICON_MS_CLEAR_ALL "\xee\x82\xb8" // U+e0b8 #define ICON_MS_CLEAR_DAY "\xef\x85\x97" // U+f157 #define ICON_MS_CLEAR_NIGHT "\xef\x85\x99" // U+f159 #define ICON_MS_CLIMATE_MINI_SPLIT "\xef\xa2\xb5" // U+f8b5 #define ICON_MS_CLINICAL_NOTES "\xee\x82\x9e" // U+e09e #define ICON_MS_CLOCK_LOADER_10 "\xef\x9c\xa6" // U+f726 #define ICON_MS_CLOCK_LOADER_20 "\xef\x9c\xa5" // U+f725 #define ICON_MS_CLOCK_LOADER_40 "\xef\x9c\xa4" // U+f724 #define ICON_MS_CLOCK_LOADER_60 "\xef\x9c\xa3" // U+f723 #define ICON_MS_CLOCK_LOADER_80 "\xef\x9c\xa2" // U+f722 #define ICON_MS_CLOCK_LOADER_90 "\xef\x9c\xa1" // U+f721 #define ICON_MS_CLOSE "\xee\x97\x8d" // U+e5cd #define ICON_MS_CLOSE_FULLSCREEN "\xef\x87\x8f" // U+f1cf #define ICON_MS_CLOSE_SMALL "\xef\x94\x88" // U+f508 #define ICON_MS_CLOSED_CAPTION "\xee\xa6\x96" // U+e996 #define ICON_MS_CLOSED_CAPTION_ADD "\xef\x92\xae" // U+f4ae #define ICON_MS_CLOSED_CAPTION_DISABLED "\xef\x87\x9c" // U+f1dc #define ICON_MS_CLOSED_CAPTION_OFF "\xee\xa6\x96" // U+e996 #define ICON_MS_CLOUD "\xef\x85\x9c" // U+f15c #define ICON_MS_CLOUD_CIRCLE "\xee\x8a\xbe" // U+e2be #define ICON_MS_CLOUD_DONE "\xee\x8a\xbf" // U+e2bf #define ICON_MS_CLOUD_DOWNLOAD "\xee\x8b\x80" // U+e2c0 #define ICON_MS_CLOUD_OFF "\xee\x8b\x81" // U+e2c1 #define ICON_MS_CLOUD_QUEUE "\xef\x85\x9c" // U+f15c #define ICON_MS_CLOUD_SYNC "\xee\xad\x9a" // U+eb5a #define ICON_MS_CLOUD_UPLOAD "\xee\x8b\x83" // U+e2c3 #define ICON_MS_CLOUDY "\xef\x85\x9c" // U+f15c #define ICON_MS_CLOUDY_FILLED "\xef\x85\x9c" // U+f15c #define ICON_MS_CLOUDY_SNOWING "\xee\xa0\x90" // U+e810 #define ICON_MS_CO2 "\xee\x9e\xb0" // U+e7b0 #define ICON_MS_CO_PRESENT "\xee\xab\xb0" // U+eaf0 #define ICON_MS_CODE "\xee\xa1\xaf" // U+e86f #define ICON_MS_CODE_BLOCKS "\xef\xa1\x8d" // U+f84d #define ICON_MS_CODE_OFF "\xee\x93\xb3" // U+e4f3 #define ICON_MS_COFFEE "\xee\xbf\xaf" // U+efef #define ICON_MS_COFFEE_MAKER "\xee\xbf\xb0" // U+eff0 #define ICON_MS_COGNITION "\xee\x82\x9f" // U+e09f #define ICON_MS_COLLAPSE_ALL "\xee\xa5\x84" // U+e944 #define ICON_MS_COLLAPSE_CONTENT "\xef\x94\x87" // U+f507 #define ICON_MS_COLLECTIONS "\xee\x8f\x93" // U+e3d3 #define ICON_MS_COLLECTIONS_BOOKMARK "\xee\x90\xb1" // U+e431 #define ICON_MS_COLOR_LENS "\xee\x90\x8a" // U+e40a #define ICON_MS_COLORIZE "\xee\x8e\xb8" // U+e3b8 #define ICON_MS_COLORS "\xee\xa6\x97" // U+e997 #define ICON_MS_COMEDY_MASK "\xef\x93\x96" // U+f4d6 #define ICON_MS_COMIC_BUBBLE "\xef\x97\x9d" // U+f5dd #define ICON_MS_COMMENT "\xee\x89\x8c" // U+e24c #define ICON_MS_COMMENT_BANK "\xee\xa9\x8e" // U+ea4e #define ICON_MS_COMMENTS_DISABLED "\xee\x9e\xa2" // U+e7a2 #define ICON_MS_COMMIT "\xee\xab\xb5" // U+eaf5 #define ICON_MS_COMMUNICATION "\xee\x89\xbc" // U+e27c #define ICON_MS_COMMUNITIES "\xee\xac\x96" // U+eb16 #define ICON_MS_COMMUNITIES_FILLED "\xee\xac\x96" // U+eb16 #define ICON_MS_COMMUTE "\xee\xa5\x80" // U+e940 #define ICON_MS_COMPARE "\xee\x8e\xb9" // U+e3b9 #define ICON_MS_COMPARE_ARROWS "\xee\xa4\x95" // U+e915 #define ICON_MS_COMPASS_CALIBRATION "\xee\x95\xbc" // U+e57c #define ICON_MS_COMPONENT_EXCHANGE "\xef\x87\xa7" // U+f1e7 #define ICON_MS_COMPOST "\xee\x9d\xa1" // U+e761 #define ICON_MS_COMPRESS "\xee\xa5\x8d" // U+e94d #define ICON_MS_COMPUTER "\xee\x8c\x9e" // U+e31e #define ICON_MS_CONCIERGE "\xef\x95\xa1" // U+f561 #define ICON_MS_CONDITIONS "\xee\x82\xa0" // U+e0a0 #define ICON_MS_CONFIRMATION_NUMBER "\xee\x98\xb8" // U+e638 #define ICON_MS_CONGENITAL "\xee\x82\xa1" // U+e0a1 #define ICON_MS_CONNECT_WITHOUT_CONTACT "\xef\x88\xa3" // U+f223 #define ICON_MS_CONNECTED_TV "\xee\xa6\x98" // U+e998 #define ICON_MS_CONNECTING_AIRPORTS "\xee\x9f\x89" // U+e7c9 #define ICON_MS_CONSTRUCTION "\xee\xa8\xbc" // U+ea3c #define ICON_MS_CONTACT_EMERGENCY "\xef\xa3\x91" // U+f8d1 #define ICON_MS_CONTACT_MAIL "\xee\x83\x90" // U+e0d0 #define ICON_MS_CONTACT_PAGE "\xef\x88\xae" // U+f22e #define ICON_MS_CONTACT_PHONE "\xef\x83\x80" // U+f0c0 #define ICON_MS_CONTACT_PHONE_FILLED "\xef\x83\x80" // U+f0c0 #define ICON_MS_CONTACT_SUPPORT "\xee\xa5\x8c" // U+e94c #define ICON_MS_CONTACTLESS "\xee\xa9\xb1" // U+ea71 #define ICON_MS_CONTACTLESS_OFF "\xef\xa1\x98" // U+f858 #define ICON_MS_CONTACTS "\xee\x82\xba" // U+e0ba #define ICON_MS_CONTACTS_PRODUCT "\xee\xa6\x99" // U+e999 #define ICON_MS_CONTENT_COPY "\xee\x85\x8d" // U+e14d #define ICON_MS_CONTENT_CUT "\xee\x85\x8e" // U+e14e #define ICON_MS_CONTENT_PASTE "\xee\x85\x8f" // U+e14f #define ICON_MS_CONTENT_PASTE_GO "\xee\xaa\x8e" // U+ea8e #define ICON_MS_CONTENT_PASTE_OFF "\xee\x93\xb8" // U+e4f8 #define ICON_MS_CONTENT_PASTE_SEARCH "\xee\xaa\x9b" // U+ea9b #define ICON_MS_CONTRACT "\xef\x96\xa0" // U+f5a0 #define ICON_MS_CONTRACT_DELETE "\xef\x96\xa2" // U+f5a2 #define ICON_MS_CONTRACT_EDIT "\xef\x96\xa1" // U+f5a1 #define ICON_MS_CONTRAST "\xee\xac\xb7" // U+eb37 #define ICON_MS_CONTRAST_CIRCLE "\xef\x92\x9f" // U+f49f #define ICON_MS_CONTRAST_RTL_OFF "\xee\xb1\xb2" // U+ec72 #define ICON_MS_CONTRAST_SQUARE "\xef\x92\xa0" // U+f4a0 #define ICON_MS_CONTROL_CAMERA "\xee\x81\xb4" // U+e074 #define ICON_MS_CONTROL_POINT "\xee\x8e\xba" // U+e3ba #define ICON_MS_CONTROL_POINT_DUPLICATE "\xee\x8e\xbb" // U+e3bb #define ICON_MS_CONTROLLER_GEN "\xee\xa0\xbd" // U+e83d #define ICON_MS_CONVERSION_PATH "\xef\x83\x81" // U+f0c1 #define ICON_MS_CONVERSION_PATH_OFF "\xef\x9e\xb4" // U+f7b4 #define ICON_MS_CONVEYOR_BELT "\xef\xa1\xa7" // U+f867 #define ICON_MS_COOKIE "\xee\xaa\xac" // U+eaac #define ICON_MS_COOKIE_OFF "\xef\x9e\x9a" // U+f79a #define ICON_MS_COOKING "\xee\x8a\xb6" // U+e2b6 #define ICON_MS_COOL_TO_DRY "\xee\x89\xb6" // U+e276 #define ICON_MS_COPY_ALL "\xee\x8b\xac" // U+e2ec #define ICON_MS_COPYRIGHT "\xee\xa4\x8c" // U+e90c #define ICON_MS_CORONAVIRUS "\xef\x88\xa1" // U+f221 #define ICON_MS_CORPORATE_FARE "\xef\x87\x90" // U+f1d0 #define ICON_MS_COTTAGE "\xee\x96\x87" // U+e587 #define ICON_MS_COUNTER_0 "\xef\x9e\x85" // U+f785 #define ICON_MS_COUNTER_1 "\xef\x9e\x84" // U+f784 #define ICON_MS_COUNTER_2 "\xef\x9e\x83" // U+f783 #define ICON_MS_COUNTER_3 "\xef\x9e\x82" // U+f782 #define ICON_MS_COUNTER_4 "\xef\x9e\x81" // U+f781 #define ICON_MS_COUNTER_5 "\xef\x9e\x80" // U+f780 #define ICON_MS_COUNTER_6 "\xef\x9d\xbf" // U+f77f #define ICON_MS_COUNTER_7 "\xef\x9d\xbe" // U+f77e #define ICON_MS_COUNTER_8 "\xef\x9d\xbd" // U+f77d #define ICON_MS_COUNTER_9 "\xef\x9d\xbc" // U+f77c #define ICON_MS_COUNTERTOPS "\xef\x87\xb7" // U+f1f7 #define ICON_MS_CREATE "\xef\x82\x97" // U+f097 #define ICON_MS_CREATE_NEW_FOLDER "\xee\x8b\x8c" // U+e2cc #define ICON_MS_CREDIT_CARD "\xee\xa2\xa1" // U+e8a1 #define ICON_MS_CREDIT_CARD_GEAR "\xef\x94\xad" // U+f52d #define ICON_MS_CREDIT_CARD_HEART "\xef\x94\xac" // U+f52c #define ICON_MS_CREDIT_CARD_OFF "\xee\x93\xb4" // U+e4f4 #define ICON_MS_CREDIT_SCORE "\xee\xbf\xb1" // U+eff1 #define ICON_MS_CRIB "\xee\x96\x88" // U+e588 #define ICON_MS_CRISIS_ALERT "\xee\xaf\xa9" // U+ebe9 #define ICON_MS_CROP "\xee\x8e\xbe" // U+e3be #define ICON_MS_CROP_16_9 "\xee\x8e\xbc" // U+e3bc #define ICON_MS_CROP_3_2 "\xee\x8e\xbd" // U+e3bd #define ICON_MS_CROP_5_4 "\xee\x8e\xbf" // U+e3bf #define ICON_MS_CROP_7_5 "\xee\x8f\x80" // U+e3c0 #define ICON_MS_CROP_9_16 "\xef\x95\x89" // U+f549 #define ICON_MS_CROP_DIN "\xee\x8f\x86" // U+e3c6 #define ICON_MS_CROP_FREE "\xee\x8f\x82" // U+e3c2 #define ICON_MS_CROP_LANDSCAPE "\xee\x8f\x83" // U+e3c3 #define ICON_MS_CROP_ORIGINAL "\xee\x8f\xb4" // U+e3f4 #define ICON_MS_CROP_PORTRAIT "\xee\x8f\x85" // U+e3c5 #define ICON_MS_CROP_ROTATE "\xee\x90\xb7" // U+e437 #define ICON_MS_CROP_SQUARE "\xee\x8f\x86" // U+e3c6 #define ICON_MS_CROSSWORD "\xef\x97\xa5" // U+f5e5 #define ICON_MS_CROWDSOURCE "\xee\xac\x98" // U+eb18 #define ICON_MS_CRUELTY_FREE "\xee\x9e\x99" // U+e799 #define ICON_MS_CSS "\xee\xae\x93" // U+eb93 #define ICON_MS_CSV "\xee\x9b\x8f" // U+e6cf #define ICON_MS_CURRENCY_BITCOIN "\xee\xaf\x85" // U+ebc5 #define ICON_MS_CURRENCY_EXCHANGE "\xee\xad\xb0" // U+eb70 #define ICON_MS_CURRENCY_FRANC "\xee\xab\xba" // U+eafa #define ICON_MS_CURRENCY_LIRA "\xee\xab\xaf" // U+eaef #define ICON_MS_CURRENCY_POUND "\xee\xab\xb1" // U+eaf1 #define ICON_MS_CURRENCY_RUBLE "\xee\xab\xac" // U+eaec #define ICON_MS_CURRENCY_RUPEE "\xee\xab\xb7" // U+eaf7 #define ICON_MS_CURRENCY_YEN "\xee\xab\xbb" // U+eafb #define ICON_MS_CURRENCY_YUAN "\xee\xab\xb9" // U+eaf9 #define ICON_MS_CURTAINS "\xee\xb0\x9e" // U+ec1e #define ICON_MS_CURTAINS_CLOSED "\xee\xb0\x9d" // U+ec1d #define ICON_MS_CUSTOM_TYPOGRAPHY "\xee\x9c\xb2" // U+e732 #define ICON_MS_CUT "\xef\x82\x8b" // U+f08b #define ICON_MS_CYCLE "\xef\xa1\x94" // U+f854 #define ICON_MS_CYCLONE "\xee\xaf\x95" // U+ebd5 #define ICON_MS_DANGEROUS "\xee\xa6\x9a" // U+e99a #define ICON_MS_DARK_MODE "\xee\x94\x9c" // U+e51c #define ICON_MS_DASHBOARD "\xee\xa1\xb1" // U+e871 #define ICON_MS_DASHBOARD_CUSTOMIZE "\xee\xa6\x9b" // U+e99b #define ICON_MS_DATA_ALERT "\xef\x9f\xb6" // U+f7f6 #define ICON_MS_DATA_ARRAY "\xee\xab\x91" // U+ead1 #define ICON_MS_DATA_CHECK "\xef\x9f\xb2" // U+f7f2 #define ICON_MS_DATA_EXPLORATION "\xee\x9d\xaf" // U+e76f #define ICON_MS_DATA_INFO_ALERT "\xef\x9f\xb5" // U+f7f5 #define ICON_MS_DATA_LOSS_PREVENTION "\xee\x8b\x9c" // U+e2dc #define ICON_MS_DATA_OBJECT "\xee\xab\x93" // U+ead3 #define ICON_MS_DATA_SAVER_OFF "\xee\xbf\xb2" // U+eff2 #define ICON_MS_DATA_SAVER_ON "\xee\xbf\xb3" // U+eff3 #define ICON_MS_DATA_TABLE "\xee\xa6\x9c" // U+e99c #define ICON_MS_DATA_THRESHOLDING "\xee\xae\x9f" // U+eb9f #define ICON_MS_DATA_USAGE "\xee\xbf\xb2" // U+eff2 #define ICON_MS_DATABASE "\xef\x88\x8e" // U+f20e #define ICON_MS_DATASET "\xef\xa3\xae" // U+f8ee #define ICON_MS_DATASET_LINKED "\xef\xa3\xaf" // U+f8ef #define ICON_MS_DATE_RANGE "\xee\xa4\x96" // U+e916 #define ICON_MS_DEBLUR "\xee\xad\xb7" // U+eb77 #define ICON_MS_DECEASED "\xee\x82\xa5" // U+e0a5 #define ICON_MS_DECIMAL_DECREASE "\xef\xa0\xad" // U+f82d #define ICON_MS_DECIMAL_INCREASE "\xef\xa0\xac" // U+f82c #define ICON_MS_DECK "\xee\xa9\x82" // U+ea42 #define ICON_MS_DEHAZE "\xee\x8f\x87" // U+e3c7 #define ICON_MS_DELETE "\xee\xa4\xae" // U+e92e #define ICON_MS_DELETE_FOREVER "\xee\xa4\xab" // U+e92b #define ICON_MS_DELETE_HISTORY "\xef\x94\x98" // U+f518 #define ICON_MS_DELETE_OUTLINE "\xee\xa4\xae" // U+e92e #define ICON_MS_DELETE_SWEEP "\xee\x85\xac" // U+e16c #define ICON_MS_DEMOGRAPHY "\xee\x92\x89" // U+e489 #define ICON_MS_DENSITY_LARGE "\xee\xae\xa9" // U+eba9 #define ICON_MS_DENSITY_MEDIUM "\xee\xae\x9e" // U+eb9e #define ICON_MS_DENSITY_SMALL "\xee\xae\xa8" // U+eba8 #define ICON_MS_DENTISTRY "\xee\x82\xa6" // U+e0a6 #define ICON_MS_DEPARTURE_BOARD "\xee\x95\xb6" // U+e576 #define ICON_MS_DEPLOYED_CODE "\xef\x9c\xa0" // U+f720 #define ICON_MS_DEPLOYED_CODE_ACCOUNT "\xef\x94\x9b" // U+f51b #define ICON_MS_DEPLOYED_CODE_ALERT "\xef\x97\xb2" // U+f5f2 #define ICON_MS_DEPLOYED_CODE_HISTORY "\xef\x97\xb3" // U+f5f3 #define ICON_MS_DEPLOYED_CODE_UPDATE "\xef\x97\xb4" // U+f5f4 #define ICON_MS_DERMATOLOGY "\xee\x82\xa7" // U+e0a7 #define ICON_MS_DESCRIPTION "\xee\xa1\xb3" // U+e873 #define ICON_MS_DESELECT "\xee\xae\xb6" // U+ebb6 #define ICON_MS_DESIGN_SERVICES "\xef\x84\x8a" // U+f10a #define ICON_MS_DESK "\xef\xa3\xb4" // U+f8f4 #define ICON_MS_DESKPHONE "\xef\x9f\xba" // U+f7fa #define ICON_MS_DESKTOP_ACCESS_DISABLED "\xee\xa6\x9d" // U+e99d #define ICON_MS_DESKTOP_MAC "\xee\x8c\x8b" // U+e30b #define ICON_MS_DESKTOP_WINDOWS "\xee\x8c\x8c" // U+e30c #define ICON_MS_DESTRUCTION "\xef\x96\x85" // U+f585 #define ICON_MS_DETAILS "\xee\x8f\x88" // U+e3c8 #define ICON_MS_DETECTION_AND_ZONE "\xee\x8a\x9f" // U+e29f #define ICON_MS_DETECTOR "\xee\x8a\x82" // U+e282 #define ICON_MS_DETECTOR_ALARM "\xee\x87\xb7" // U+e1f7 #define ICON_MS_DETECTOR_BATTERY "\xee\x88\x84" // U+e204 #define ICON_MS_DETECTOR_CO "\xee\x8a\xaf" // U+e2af #define ICON_MS_DETECTOR_OFFLINE "\xee\x88\xa3" // U+e223 #define ICON_MS_DETECTOR_SMOKE "\xee\x8a\x85" // U+e285 #define ICON_MS_DETECTOR_STATUS "\xee\x87\xa8" // U+e1e8 #define ICON_MS_DEVELOPER_BOARD "\xee\x8c\x8d" // U+e30d #define ICON_MS_DEVELOPER_BOARD_OFF "\xee\x93\xbf" // U+e4ff #define ICON_MS_DEVELOPER_GUIDE "\xee\xa6\x9e" // U+e99e #define ICON_MS_DEVELOPER_MODE "\xee\x86\xb0" // U+e1b0 #define ICON_MS_DEVELOPER_MODE_TV "\xee\xa1\xb4" // U+e874 #define ICON_MS_DEVICE_HUB "\xee\x8c\xb5" // U+e335 #define ICON_MS_DEVICE_RESET "\xee\xa2\xb3" // U+e8b3 #define ICON_MS_DEVICE_THERMOSTAT "\xee\x87\xbf" // U+e1ff #define ICON_MS_DEVICE_UNKNOWN "\xee\x8c\xb9" // U+e339 #define ICON_MS_DEVICES "\xee\x8c\xa6" // U+e326 #define ICON_MS_DEVICES_FOLD "\xee\xaf\x9e" // U+ebde #define ICON_MS_DEVICES_OFF "\xef\x9e\xa5" // U+f7a5 #define ICON_MS_DEVICES_OTHER "\xee\x8c\xb7" // U+e337 #define ICON_MS_DEVICES_WEARABLES "\xef\x9a\xab" // U+f6ab #define ICON_MS_DEW_POINT "\xef\xa1\xb9" // U+f879 #define ICON_MS_DIAGNOSIS "\xee\x82\xa8" // U+e0a8 #define ICON_MS_DIALER_SIP "\xee\x82\xbb" // U+e0bb #define ICON_MS_DIALOGS "\xee\xa6\x9f" // U+e99f #define ICON_MS_DIALPAD "\xee\x82\xbc" // U+e0bc #define ICON_MS_DIAMOND "\xee\xab\x95" // U+ead5 #define ICON_MS_DICTIONARY "\xef\x94\xb9" // U+f539 #define ICON_MS_DIFFERENCE "\xee\xad\xbd" // U+eb7d #define ICON_MS_DIGITAL_OUT_OF_HOME "\xef\x87\x9e" // U+f1de #define ICON_MS_DIGITAL_WELLBEING "\xee\xbe\x86" // U+ef86 #define ICON_MS_DINING "\xee\xbf\xb4" // U+eff4 #define ICON_MS_DINNER_DINING "\xee\xa9\x97" // U+ea57 #define ICON_MS_DIRECTIONS "\xee\x94\xae" // U+e52e #define ICON_MS_DIRECTIONS_ALT "\xef\xa2\x80" // U+f880 #define ICON_MS_DIRECTIONS_ALT_OFF "\xef\xa2\x81" // U+f881 #define ICON_MS_DIRECTIONS_BIKE "\xee\x94\xaf" // U+e52f #define ICON_MS_DIRECTIONS_BOAT "\xee\xbf\xb5" // U+eff5 #define ICON_MS_DIRECTIONS_BOAT_FILLED "\xee\xbf\xb5" // U+eff5 #define ICON_MS_DIRECTIONS_BUS "\xee\xbf\xb6" // U+eff6 #define ICON_MS_DIRECTIONS_BUS_FILLED "\xee\xbf\xb6" // U+eff6 #define ICON_MS_DIRECTIONS_CAR "\xee\xbf\xb7" // U+eff7 #define ICON_MS_DIRECTIONS_CAR_FILLED "\xee\xbf\xb7" // U+eff7 #define ICON_MS_DIRECTIONS_OFF "\xef\x84\x8f" // U+f10f #define ICON_MS_DIRECTIONS_RAILWAY "\xee\xbf\xb8" // U+eff8 #define ICON_MS_DIRECTIONS_RAILWAY_FILLED "\xee\xbf\xb8" // U+eff8 #define ICON_MS_DIRECTIONS_RUN "\xee\x95\xa6" // U+e566 #define ICON_MS_DIRECTIONS_SUBWAY "\xee\xbf\xba" // U+effa #define ICON_MS_DIRECTIONS_SUBWAY_FILLED "\xee\xbf\xba" // U+effa #define ICON_MS_DIRECTIONS_TRANSIT "\xee\xbf\xba" // U+effa #define ICON_MS_DIRECTIONS_TRANSIT_FILLED "\xee\xbf\xba" // U+effa #define ICON_MS_DIRECTIONS_WALK "\xee\x94\xb6" // U+e536 #define ICON_MS_DIRECTORY_SYNC "\xee\x8e\x94" // U+e394 #define ICON_MS_DIRTY_LENS "\xee\xbd\x8b" // U+ef4b #define ICON_MS_DISABLED_BY_DEFAULT "\xef\x88\xb0" // U+f230 #define ICON_MS_DISABLED_VISIBLE "\xee\x9d\xae" // U+e76e #define ICON_MS_DISC_FULL "\xee\x98\x90" // U+e610 #define ICON_MS_DISCOVER_TUNE "\xee\x80\x98" // U+e018 #define ICON_MS_DISHWASHER "\xee\xa6\xa0" // U+e9a0 #define ICON_MS_DISHWASHER_GEN "\xee\xa0\xb2" // U+e832 #define ICON_MS_DISPLAY_EXTERNAL_INPUT "\xef\x9f\xa7" // U+f7e7 #define ICON_MS_DISPLAY_SETTINGS "\xee\xae\x97" // U+eb97 #define ICON_MS_DISTANCE "\xef\x9b\xaa" // U+f6ea #define ICON_MS_DIVERSITY_1 "\xef\xa3\x97" // U+f8d7 #define ICON_MS_DIVERSITY_2 "\xef\xa3\x98" // U+f8d8 #define ICON_MS_DIVERSITY_3 "\xef\xa3\x99" // U+f8d9 #define ICON_MS_DIVERSITY_4 "\xef\xa1\x97" // U+f857 #define ICON_MS_DNS "\xee\xa1\xb5" // U+e875 #define ICON_MS_DO_DISTURB "\xef\x82\x8c" // U+f08c #define ICON_MS_DO_DISTURB_ALT "\xef\x82\x8d" // U+f08d #define ICON_MS_DO_DISTURB_OFF "\xef\x82\x8e" // U+f08e #define ICON_MS_DO_DISTURB_ON "\xef\x82\x8f" // U+f08f #define ICON_MS_DO_NOT_DISTURB "\xef\x82\x8d" // U+f08d #define ICON_MS_DO_NOT_DISTURB_ALT "\xef\x82\x8c" // U+f08c #define ICON_MS_DO_NOT_DISTURB_OFF "\xef\x82\x8e" // U+f08e #define ICON_MS_DO_NOT_DISTURB_ON "\xef\x82\x8f" // U+f08f #define ICON_MS_DO_NOT_DISTURB_ON_TOTAL_SILENCE "\xee\xbf\xbb" // U+effb #define ICON_MS_DO_NOT_STEP "\xef\x86\x9f" // U+f19f #define ICON_MS_DO_NOT_TOUCH "\xef\x86\xb0" // U+f1b0 #define ICON_MS_DOCK "\xee\x8c\x8e" // U+e30e #define ICON_MS_DOCK_TO_BOTTOM "\xef\x9f\xa6" // U+f7e6 #define ICON_MS_DOCK_TO_LEFT "\xef\x9f\xa5" // U+f7e5 #define ICON_MS_DOCK_TO_RIGHT "\xef\x9f\xa4" // U+f7e4 #define ICON_MS_DOCS_ADD_ON "\xef\x83\x82" // U+f0c2 #define ICON_MS_DOCS_APPS_SCRIPT "\xef\x83\x83" // U+f0c3 #define ICON_MS_DOCUMENT_SCANNER "\xee\x97\xba" // U+e5fa #define ICON_MS_DOMAIN "\xee\x9f\xae" // U+e7ee #define ICON_MS_DOMAIN_ADD "\xee\xad\xa2" // U+eb62 #define ICON_MS_DOMAIN_DISABLED "\xee\x83\xaf" // U+e0ef #define ICON_MS_DOMAIN_VERIFICATION "\xee\xbd\x8c" // U+ef4c #define ICON_MS_DOMAIN_VERIFICATION_OFF "\xef\x9e\xb0" // U+f7b0 #define ICON_MS_DOMINO_MASK "\xef\x97\xa4" // U+f5e4 #define ICON_MS_DONE "\xee\xa1\xb6" // U+e876 #define ICON_MS_DONE_ALL "\xee\xa1\xb7" // U+e877 #define ICON_MS_DONE_OUTLINE "\xee\xa4\xaf" // U+e92f #define ICON_MS_DONUT_LARGE "\xee\xa4\x97" // U+e917 #define ICON_MS_DONUT_SMALL "\xee\xa4\x98" // U+e918 #define ICON_MS_DOOR_BACK "\xee\xbf\xbc" // U+effc #define ICON_MS_DOOR_FRONT "\xee\xbf\xbd" // U+effd #define ICON_MS_DOOR_OPEN "\xee\x9d\xbc" // U+e77c #define ICON_MS_DOOR_SENSOR "\xee\x8a\x8a" // U+e28a #define ICON_MS_DOOR_SLIDING "\xee\xbf\xbe" // U+effe #define ICON_MS_DOORBELL "\xee\xbf\xbf" // U+efff #define ICON_MS_DOORBELL_3P "\xee\x87\xa7" // U+e1e7 #define ICON_MS_DOORBELL_CHIME "\xee\x87\xb3" // U+e1f3 #define ICON_MS_DOUBLE_ARROW "\xee\xa9\x90" // U+ea50 #define ICON_MS_DOWNHILL_SKIING "\xee\x94\x89" // U+e509 #define ICON_MS_DOWNLOAD "\xef\x82\x90" // U+f090 #define ICON_MS_DOWNLOAD_2 "\xef\x94\xa3" // U+f523 #define ICON_MS_DOWNLOAD_DONE "\xef\x82\x91" // U+f091 #define ICON_MS_DOWNLOAD_FOR_OFFLINE "\xef\x80\x80" // U+f000 #define ICON_MS_DOWNLOADING "\xef\x80\x81" // U+f001 #define ICON_MS_DRAFT "\xee\x99\xad" // U+e66d #define ICON_MS_DRAFT_ORDERS "\xee\x9e\xb3" // U+e7b3 #define ICON_MS_DRAFTS "\xee\x85\x91" // U+e151 #define ICON_MS_DRAG_CLICK "\xef\x9c\x9f" // U+f71f #define ICON_MS_DRAG_HANDLE "\xee\x89\x9d" // U+e25d #define ICON_MS_DRAG_INDICATOR "\xee\xa5\x85" // U+e945 #define ICON_MS_DRAG_PAN "\xef\x9c\x9e" // U+f71e #define ICON_MS_DRAW "\xee\x9d\x86" // U+e746 #define ICON_MS_DRAW_ABSTRACT "\xef\x9f\xb8" // U+f7f8 #define ICON_MS_DRAW_COLLAGE "\xef\x9f\xb7" // U+f7f7 #define ICON_MS_DRAWING_RECOGNITION "\xee\xac\x80" // U+eb00 #define ICON_MS_DRESSER "\xee\x88\x90" // U+e210 #define ICON_MS_DRIVE_ETA "\xee\xbf\xb7" // U+eff7 #define ICON_MS_DRIVE_FILE_MOVE "\xee\xa6\xa1" // U+e9a1 #define ICON_MS_DRIVE_FILE_MOVE_OUTLINE "\xee\xa6\xa1" // U+e9a1 #define ICON_MS_DRIVE_FILE_MOVE_RTL "\xee\xa6\xa1" // U+e9a1 #define ICON_MS_DRIVE_FILE_RENAME_OUTLINE "\xee\xa6\xa2" // U+e9a2 #define ICON_MS_DRIVE_FOLDER_UPLOAD "\xee\xa6\xa3" // U+e9a3 #define ICON_MS_DRIVE_FUSIONTABLE "\xee\x99\xb8" // U+e678 #define ICON_MS_DROPDOWN "\xee\xa6\xa4" // U+e9a4 #define ICON_MS_DRY "\xef\x86\xb3" // U+f1b3 #define ICON_MS_DRY_CLEANING "\xee\xa9\x98" // U+ea58 #define ICON_MS_DUAL_SCREEN "\xef\x9b\x8f" // U+f6cf #define ICON_MS_DUO "\xee\xa6\xa5" // U+e9a5 #define ICON_MS_DVR "\xee\x86\xb2" // U+e1b2 #define ICON_MS_DYNAMIC_FEED "\xee\xa8\x94" // U+ea14 #define ICON_MS_DYNAMIC_FORM "\xef\x86\xbf" // U+f1bf #define ICON_MS_E911_AVATAR "\xef\x84\x9a" // U+f11a #define ICON_MS_E911_EMERGENCY "\xef\x84\x99" // U+f119 #define ICON_MS_E_MOBILEDATA "\xef\x80\x82" // U+f002 #define ICON_MS_E_MOBILEDATA_BADGE "\xef\x9f\xa3" // U+f7e3 #define ICON_MS_EARBUDS "\xef\x80\x83" // U+f003 #define ICON_MS_EARBUDS_BATTERY "\xef\x80\x84" // U+f004 #define ICON_MS_EARLY_ON "\xee\x8a\xba" // U+e2ba #define ICON_MS_EARTHQUAKE "\xef\x99\x8f" // U+f64f #define ICON_MS_EAST "\xef\x87\x9f" // U+f1df #define ICON_MS_ECG "\xef\xa0\x8f" // U+f80f #define ICON_MS_ECG_HEART "\xef\x9b\xa9" // U+f6e9 #define ICON_MS_ECO "\xee\xa8\xb5" // U+ea35 #define ICON_MS_EDA "\xef\x9b\xa8" // U+f6e8 #define ICON_MS_EDGESENSOR_HIGH "\xef\x80\x85" // U+f005 #define ICON_MS_EDGESENSOR_LOW "\xef\x80\x86" // U+f006 #define ICON_MS_EDIT "\xef\x82\x97" // U+f097 #define ICON_MS_EDIT_ATTRIBUTES "\xee\x95\xb8" // U+e578 #define ICON_MS_EDIT_CALENDAR "\xee\x9d\x82" // U+e742 #define ICON_MS_EDIT_DOCUMENT "\xef\xa2\x8c" // U+f88c #define ICON_MS_EDIT_LOCATION "\xee\x95\xa8" // U+e568 #define ICON_MS_EDIT_LOCATION_ALT "\xee\x87\x85" // U+e1c5 #define ICON_MS_EDIT_NOTE "\xee\x9d\x85" // U+e745 #define ICON_MS_EDIT_NOTIFICATIONS "\xee\x94\xa5" // U+e525 #define ICON_MS_EDIT_OFF "\xee\xa5\x90" // U+e950 #define ICON_MS_EDIT_ROAD "\xee\xbd\x8d" // U+ef4d #define ICON_MS_EDIT_SQUARE "\xef\xa2\x8d" // U+f88d #define ICON_MS_EDITOR_CHOICE "\xef\x94\xa8" // U+f528 #define ICON_MS_EGG "\xee\xab\x8c" // U+eacc #define ICON_MS_EGG_ALT "\xee\xab\x88" // U+eac8 #define ICON_MS_EJECT "\xee\xa3\xbb" // U+e8fb #define ICON_MS_ELDERLY "\xef\x88\x9a" // U+f21a #define ICON_MS_ELDERLY_WOMAN "\xee\xad\xa9" // U+eb69 #define ICON_MS_ELECTRIC_BIKE "\xee\xac\x9b" // U+eb1b #define ICON_MS_ELECTRIC_BOLT "\xee\xb0\x9c" // U+ec1c #define ICON_MS_ELECTRIC_CAR "\xee\xac\x9c" // U+eb1c #define ICON_MS_ELECTRIC_METER "\xee\xb0\x9b" // U+ec1b #define ICON_MS_ELECTRIC_MOPED "\xee\xac\x9d" // U+eb1d #define ICON_MS_ELECTRIC_RICKSHAW "\xee\xac\x9e" // U+eb1e #define ICON_MS_ELECTRIC_SCOOTER "\xee\xac\x9f" // U+eb1f #define ICON_MS_ELECTRICAL_SERVICES "\xef\x84\x82" // U+f102 #define ICON_MS_ELEVATION "\xef\x9b\xa7" // U+f6e7 #define ICON_MS_ELEVATOR "\xef\x86\xa0" // U+f1a0 #define ICON_MS_EMAIL "\xee\x85\x99" // U+e159 #define ICON_MS_EMERGENCY "\xee\x87\xab" // U+e1eb #define ICON_MS_EMERGENCY_HEAT "\xef\x85\x9d" // U+f15d #define ICON_MS_EMERGENCY_HEAT_2 "\xef\x93\xa5" // U+f4e5 #define ICON_MS_EMERGENCY_HOME "\xee\xa0\xaa" // U+e82a #define ICON_MS_EMERGENCY_RECORDING "\xee\xaf\xb4" // U+ebf4 #define ICON_MS_EMERGENCY_SHARE "\xee\xaf\xb6" // U+ebf6 #define ICON_MS_EMERGENCY_SHARE_OFF "\xef\x96\x9e" // U+f59e #define ICON_MS_EMOJI_EMOTIONS "\xee\xa8\xa2" // U+ea22 #define ICON_MS_EMOJI_EVENTS "\xee\xa8\xa3" // U+ea23 #define ICON_MS_EMOJI_FLAGS "\xef\x83\x86" // U+f0c6 #define ICON_MS_EMOJI_FOOD_BEVERAGE "\xee\xa8\x9b" // U+ea1b #define ICON_MS_EMOJI_LANGUAGE "\xef\x93\x8d" // U+f4cd #define ICON_MS_EMOJI_NATURE "\xee\xa8\x9c" // U+ea1c #define ICON_MS_EMOJI_OBJECTS "\xee\xa8\xa4" // U+ea24 #define ICON_MS_EMOJI_PEOPLE "\xee\xa8\x9d" // U+ea1d #define ICON_MS_EMOJI_SYMBOLS "\xee\xa8\x9e" // U+ea1e #define ICON_MS_EMOJI_TRANSPORTATION "\xee\xa8\x9f" // U+ea1f #define ICON_MS_EMOTICON "\xee\x97\xb3" // U+e5f3 #define ICON_MS_EMPTY_DASHBOARD "\xef\xa1\x84" // U+f844 #define ICON_MS_ENABLE "\xef\x86\x88" // U+f188 #define ICON_MS_ENCRYPTED "\xee\x96\x93" // U+e593 #define ICON_MS_ENDOCRINOLOGY "\xee\x82\xa9" // U+e0a9 #define ICON_MS_ENERGY "\xee\xa6\xa6" // U+e9a6 #define ICON_MS_ENERGY_PROGRAM_SAVING "\xef\x85\x9f" // U+f15f #define ICON_MS_ENERGY_PROGRAM_TIME_USED "\xef\x85\xa1" // U+f161 #define ICON_MS_ENERGY_SAVINGS_LEAF "\xee\xb0\x9a" // U+ec1a #define ICON_MS_ENGINEERING "\xee\xa8\xbd" // U+ea3d #define ICON_MS_ENHANCED_ENCRYPTION "\xee\x98\xbf" // U+e63f #define ICON_MS_ENT "\xee\x82\xaa" // U+e0aa #define ICON_MS_ENTERPRISE "\xee\x9c\x8e" // U+e70e #define ICON_MS_ENTERPRISE_OFF "\xee\xad\x8d" // U+eb4d #define ICON_MS_EQUAL "\xef\x9d\xbb" // U+f77b #define ICON_MS_EQUALIZER "\xee\x80\x9d" // U+e01d #define ICON_MS_ERROR "\xef\xa2\xb6" // U+f8b6 #define ICON_MS_ERROR_CIRCLE_ROUNDED "\xef\xa2\xb6" // U+f8b6 #define ICON_MS_ERROR_MED "\xee\x92\x9b" // U+e49b #define ICON_MS_ERROR_OUTLINE "\xef\xa2\xb6" // U+f8b6 #define ICON_MS_ESCALATOR "\xef\x86\xa1" // U+f1a1 #define ICON_MS_ESCALATOR_WARNING "\xef\x86\xac" // U+f1ac #define ICON_MS_EURO "\xee\xa8\x95" // U+ea15 #define ICON_MS_EURO_SYMBOL "\xee\xa4\xa6" // U+e926 #define ICON_MS_EV_CHARGER "\xee\x95\xad" // U+e56d #define ICON_MS_EV_MOBILEDATA_BADGE "\xef\x9f\xa2" // U+f7e2 #define ICON_MS_EV_SHADOW "\xee\xbe\x8f" // U+ef8f #define ICON_MS_EV_SHADOW_ADD "\xef\x96\x80" // U+f580 #define ICON_MS_EV_SHADOW_MINUS "\xef\x95\xbf" // U+f57f #define ICON_MS_EV_STATION "\xee\x95\xad" // U+e56d #define ICON_MS_EVENT "\xee\xa1\xb8" // U+e878 #define ICON_MS_EVENT_AVAILABLE "\xee\x98\x94" // U+e614 #define ICON_MS_EVENT_BUSY "\xee\x98\x95" // U+e615 #define ICON_MS_EVENT_LIST "\xef\x9a\x83" // U+f683 #define ICON_MS_EVENT_NOTE "\xee\x98\x96" // U+e616 #define ICON_MS_EVENT_REPEAT "\xee\xad\xbb" // U+eb7b #define ICON_MS_EVENT_SEAT "\xee\xa4\x83" // U+e903 #define ICON_MS_EVENT_UPCOMING "\xef\x88\xb8" // U+f238 #define ICON_MS_EXCLAMATION "\xef\x88\xaf" // U+f22f #define ICON_MS_EXERCISE "\xef\x9b\xa6" // U+f6e6 #define ICON_MS_EXIT_TO_APP "\xee\xa1\xb9" // U+e879 #define ICON_MS_EXPAND "\xee\xa5\x8f" // U+e94f #define ICON_MS_EXPAND_ALL "\xee\xa5\x86" // U+e946 #define ICON_MS_EXPAND_CIRCLE_DOWN "\xee\x9f\x8d" // U+e7cd #define ICON_MS_EXPAND_CIRCLE_RIGHT "\xef\x96\x91" // U+f591 #define ICON_MS_EXPAND_CIRCLE_UP "\xef\x97\x92" // U+f5d2 #define ICON_MS_EXPAND_CONTENT "\xef\xa0\xb0" // U+f830 #define ICON_MS_EXPAND_LESS "\xee\x97\x8e" // U+e5ce #define ICON_MS_EXPAND_MORE "\xee\x97\x8f" // U+e5cf #define ICON_MS_EXPERIMENT "\xee\x9a\x86" // U+e686 #define ICON_MS_EXPLICIT "\xee\x80\x9e" // U+e01e #define ICON_MS_EXPLORE "\xee\xa1\xba" // U+e87a #define ICON_MS_EXPLORE_NEARBY "\xee\x94\xb8" // U+e538 #define ICON_MS_EXPLORE_OFF "\xee\xa6\xa8" // U+e9a8 #define ICON_MS_EXPLOSION "\xef\x9a\x85" // U+f685 #define ICON_MS_EXPORT_NOTES "\xee\x82\xac" // U+e0ac #define ICON_MS_EXPOSURE "\xee\x8f\xb6" // U+e3f6 #define ICON_MS_EXPOSURE_NEG_1 "\xee\x8f\x8b" // U+e3cb #define ICON_MS_EXPOSURE_NEG_2 "\xee\x8f\x8c" // U+e3cc #define ICON_MS_EXPOSURE_PLUS_1 "\xee\xa0\x80" // U+e800 #define ICON_MS_EXPOSURE_PLUS_2 "\xee\x8f\x8e" // U+e3ce #define ICON_MS_EXPOSURE_ZERO "\xee\x8f\x8f" // U+e3cf #define ICON_MS_EXTENSION "\xee\xa1\xbb" // U+e87b #define ICON_MS_EXTENSION_OFF "\xee\x93\xb5" // U+e4f5 #define ICON_MS_EYE_TRACKING "\xef\x93\x89" // U+f4c9 #define ICON_MS_EYEGLASSES "\xef\x9b\xae" // U+f6ee #define ICON_MS_FACE "\xef\x80\x88" // U+f008 #define ICON_MS_FACE_2 "\xef\xa3\x9a" // U+f8da #define ICON_MS_FACE_3 "\xef\xa3\x9b" // U+f8db #define ICON_MS_FACE_4 "\xef\xa3\x9c" // U+f8dc #define ICON_MS_FACE_5 "\xef\xa3\x9d" // U+f8dd #define ICON_MS_FACE_6 "\xef\xa3\x9e" // U+f8de #define ICON_MS_FACE_RETOUCHING_NATURAL "\xee\xbd\x8e" // U+ef4e #define ICON_MS_FACE_RETOUCHING_OFF "\xef\x80\x87" // U+f007 #define ICON_MS_FACE_UNLOCK "\xef\x80\x88" // U+f008 #define ICON_MS_FACT_CHECK "\xef\x83\x85" // U+f0c5 #define ICON_MS_FACTORY "\xee\xae\xbc" // U+ebbc #define ICON_MS_FALLING "\xef\x98\x8d" // U+f60d #define ICON_MS_FAMILIAR_FACE_AND_ZONE "\xee\x88\x9c" // U+e21c #define ICON_MS_FAMILY_HISTORY "\xee\x82\xad" // U+e0ad #define ICON_MS_FAMILY_HOME "\xee\xac\xa6" // U+eb26 #define ICON_MS_FAMILY_LINK "\xee\xac\x99" // U+eb19 #define ICON_MS_FAMILY_RESTROOM "\xef\x86\xa2" // U+f1a2 #define ICON_MS_FAMILY_STAR "\xef\x94\xa7" // U+f527 #define ICON_MS_FARSIGHT_DIGITAL "\xef\x95\x99" // U+f559 #define ICON_MS_FAST_FORWARD "\xee\x80\x9f" // U+e01f #define ICON_MS_FAST_REWIND "\xee\x80\xa0" // U+e020 #define ICON_MS_FASTFOOD "\xee\x95\xba" // U+e57a #define ICON_MS_FAUCET "\xee\x89\xb8" // U+e278 #define ICON_MS_FAVORITE "\xee\xa1\xbe" // U+e87e #define ICON_MS_FAVORITE_BORDER "\xee\xa1\xbe" // U+e87e #define ICON_MS_FAX "\xee\xab\x98" // U+ead8 #define ICON_MS_FEATURE_SEARCH "\xee\xa6\xa9" // U+e9a9 #define ICON_MS_FEATURED_PLAY_LIST "\xee\x81\xad" // U+e06d #define ICON_MS_FEATURED_SEASONAL_AND_GIFTS "\xee\xbe\x91" // U+ef91 #define ICON_MS_FEATURED_VIDEO "\xee\x81\xae" // U+e06e #define ICON_MS_FEED "\xef\x80\x89" // U+f009 #define ICON_MS_FEEDBACK "\xee\xa1\xbf" // U+e87f #define ICON_MS_FEMALE "\xee\x96\x90" // U+e590 #define ICON_MS_FEMUR "\xef\xa2\x91" // U+f891 #define ICON_MS_FEMUR_ALT "\xef\xa2\x92" // U+f892 #define ICON_MS_FENCE "\xef\x87\xb6" // U+f1f6 #define ICON_MS_FERTILE "\xef\x9b\xa5" // U+f6e5 #define ICON_MS_FESTIVAL "\xee\xa9\xa8" // U+ea68 #define ICON_MS_FIBER_DVR "\xee\x81\x9d" // U+e05d #define ICON_MS_FIBER_MANUAL_RECORD "\xee\x81\xa1" // U+e061 #define ICON_MS_FIBER_NEW "\xee\x81\x9e" // U+e05e #define ICON_MS_FIBER_PIN "\xee\x81\xaa" // U+e06a #define ICON_MS_FIBER_SMART_RECORD "\xee\x81\xa2" // U+e062 #define ICON_MS_FILE_COPY "\xee\x85\xb3" // U+e173 #define ICON_MS_FILE_COPY_OFF "\xef\x93\x98" // U+f4d8 #define ICON_MS_FILE_DOWNLOAD "\xef\x82\x90" // U+f090 #define ICON_MS_FILE_DOWNLOAD_DONE "\xef\x82\x91" // U+f091 #define ICON_MS_FILE_DOWNLOAD_OFF "\xee\x93\xbe" // U+e4fe #define ICON_MS_FILE_MAP "\xee\x8b\x85" // U+e2c5 #define ICON_MS_FILE_OPEN "\xee\xab\xb3" // U+eaf3 #define ICON_MS_FILE_PRESENT "\xee\xa8\x8e" // U+ea0e #define ICON_MS_FILE_SAVE "\xef\x85\xbf" // U+f17f #define ICON_MS_FILE_SAVE_OFF "\xee\x94\x85" // U+e505 #define ICON_MS_FILE_UPLOAD "\xef\x82\x9b" // U+f09b #define ICON_MS_FILE_UPLOAD_OFF "\xef\xa2\x86" // U+f886 #define ICON_MS_FILTER "\xee\x8f\x93" // U+e3d3 #define ICON_MS_FILTER_1 "\xee\x8f\x90" // U+e3d0 #define ICON_MS_FILTER_2 "\xee\x8f\x91" // U+e3d1 #define ICON_MS_FILTER_3 "\xee\x8f\x92" // U+e3d2 #define ICON_MS_FILTER_4 "\xee\x8f\x94" // U+e3d4 #define ICON_MS_FILTER_5 "\xee\x8f\x95" // U+e3d5 #define ICON_MS_FILTER_6 "\xee\x8f\x96" // U+e3d6 #define ICON_MS_FILTER_7 "\xee\x8f\x97" // U+e3d7 #define ICON_MS_FILTER_8 "\xee\x8f\x98" // U+e3d8 #define ICON_MS_FILTER_9 "\xee\x8f\x99" // U+e3d9 #define ICON_MS_FILTER_9_PLUS "\xee\x8f\x9a" // U+e3da #define ICON_MS_FILTER_ALT "\xee\xbd\x8f" // U+ef4f #define ICON_MS_FILTER_ALT_OFF "\xee\xac\xb2" // U+eb32 #define ICON_MS_FILTER_B_AND_W "\xee\x8f\x9b" // U+e3db #define ICON_MS_FILTER_CENTER_FOCUS "\xee\x8f\x9c" // U+e3dc #define ICON_MS_FILTER_DRAMA "\xee\x8f\x9d" // U+e3dd #define ICON_MS_FILTER_FRAMES "\xee\x8f\x9e" // U+e3de #define ICON_MS_FILTER_HDR "\xee\x8f\x9f" // U+e3df #define ICON_MS_FILTER_LIST "\xee\x85\x92" // U+e152 #define ICON_MS_FILTER_LIST_ALT "\xee\xa5\x8e" // U+e94e #define ICON_MS_FILTER_LIST_OFF "\xee\xad\x97" // U+eb57 #define ICON_MS_FILTER_NONE "\xee\x8f\xa0" // U+e3e0 #define ICON_MS_FILTER_RETROLUX "\xee\x8f\xa1" // U+e3e1 #define ICON_MS_FILTER_TILT_SHIFT "\xee\x8f\xa2" // U+e3e2 #define ICON_MS_FILTER_VINTAGE "\xee\x8f\xa3" // U+e3e3 #define ICON_MS_FINANCE "\xee\x9a\xbf" // U+e6bf #define ICON_MS_FINANCE_CHIP "\xef\xa1\x8e" // U+f84e #define ICON_MS_FINANCE_MODE "\xee\xbe\x92" // U+ef92 #define ICON_MS_FIND_IN_PAGE "\xee\xa2\x80" // U+e880 #define ICON_MS_FIND_REPLACE "\xee\xa2\x81" // U+e881 #define ICON_MS_FINGERPRINT "\xee\xa4\x8d" // U+e90d #define ICON_MS_FINGERPRINT_OFF "\xef\x92\x9d" // U+f49d #define ICON_MS_FIRE_EXTINGUISHER "\xef\x87\x98" // U+f1d8 #define ICON_MS_FIRE_HYDRANT "\xef\x86\xa3" // U+f1a3 #define ICON_MS_FIRE_TRUCK "\xef\xa3\xb2" // U+f8f2 #define ICON_MS_FIREPLACE "\xee\xa9\x83" // U+ea43 #define ICON_MS_FIRST_PAGE "\xee\x97\x9c" // U+e5dc #define ICON_MS_FIT_PAGE "\xef\x9d\xba" // U+f77a #define ICON_MS_FIT_SCREEN "\xee\xa8\x90" // U+ea10 #define ICON_MS_FIT_WIDTH "\xef\x9d\xb9" // U+f779 #define ICON_MS_FITNESS_CENTER "\xee\xad\x83" // U+eb43 #define ICON_MS_FLAG "\xef\x83\x86" // U+f0c6 #define ICON_MS_FLAG_CIRCLE "\xee\xab\xb8" // U+eaf8 #define ICON_MS_FLAG_FILLED "\xef\x83\x86" // U+f0c6 #define ICON_MS_FLAKY "\xee\xbd\x90" // U+ef50 #define ICON_MS_FLARE "\xee\x8f\xa4" // U+e3e4 #define ICON_MS_FLASH_AUTO "\xee\x8f\xa5" // U+e3e5 #define ICON_MS_FLASH_OFF "\xee\x8f\xa6" // U+e3e6 #define ICON_MS_FLASH_ON "\xee\x8f\xa7" // U+e3e7 #define ICON_MS_FLASHLIGHT_OFF "\xef\x80\x8a" // U+f00a #define ICON_MS_FLASHLIGHT_ON "\xef\x80\x8b" // U+f00b #define ICON_MS_FLATWARE "\xef\x80\x8c" // U+f00c #define ICON_MS_FLEX_DIRECTION "\xef\x9d\xb8" // U+f778 #define ICON_MS_FLEX_NO_WRAP "\xef\x9d\xb7" // U+f777 #define ICON_MS_FLEX_WRAP "\xef\x9d\xb6" // U+f776 #define ICON_MS_FLIGHT "\xee\x94\xb9" // U+e539 #define ICON_MS_FLIGHT_CLASS "\xee\x9f\x8b" // U+e7cb #define ICON_MS_FLIGHT_LAND "\xee\xa4\x84" // U+e904 #define ICON_MS_FLIGHT_TAKEOFF "\xee\xa4\x85" // U+e905 #define ICON_MS_FLIGHTS_AND_HOTELS "\xee\xa6\xab" // U+e9ab #define ICON_MS_FLIGHTSMODE "\xee\xbe\x93" // U+ef93 #define ICON_MS_FLIP "\xee\x8f\xa8" // U+e3e8 #define ICON_MS_FLIP_CAMERA_ANDROID "\xee\xa8\xb7" // U+ea37 #define ICON_MS_FLIP_CAMERA_IOS "\xee\xa8\xb8" // U+ea38 #define ICON_MS_FLIP_TO_BACK "\xee\xa2\x82" // U+e882 #define ICON_MS_FLIP_TO_FRONT "\xee\xa2\x83" // U+e883 #define ICON_MS_FLOOD "\xee\xaf\xa6" // U+ebe6 #define ICON_MS_FLOOR "\xef\x9b\xa4" // U+f6e4 #define ICON_MS_FLOOR_LAMP "\xee\x88\x9e" // U+e21e #define ICON_MS_FLOURESCENT "\xef\x81\xbd" // U+f07d #define ICON_MS_FLOWSHEET "\xee\x82\xae" // U+e0ae #define ICON_MS_FLUID "\xee\x92\x83" // U+e483 #define ICON_MS_FLUID_BALANCE "\xef\xa0\x8d" // U+f80d #define ICON_MS_FLUID_MED "\xef\xa0\x8c" // U+f80c #define ICON_MS_FLUORESCENT "\xef\x81\xbd" // U+f07d #define ICON_MS_FLUTTER "\xef\x87\x9d" // U+f1dd #define ICON_MS_FLUTTER_DASH "\xee\x80\x8b" // U+e00b #define ICON_MS_FMD_BAD "\xef\x80\x8e" // U+f00e #define ICON_MS_FMD_GOOD "\xef\x87\x9b" // U+f1db #define ICON_MS_FOGGY "\xee\xa0\x98" // U+e818 #define ICON_MS_FOLDED_HANDS "\xef\x97\xad" // U+f5ed #define ICON_MS_FOLDER "\xee\x8b\x87" // U+e2c7 #define ICON_MS_FOLDER_COPY "\xee\xae\xbd" // U+ebbd #define ICON_MS_FOLDER_DATA "\xef\x96\x86" // U+f586 #define ICON_MS_FOLDER_DELETE "\xee\xac\xb4" // U+eb34 #define ICON_MS_FOLDER_LIMITED "\xef\x93\xa4" // U+f4e4 #define ICON_MS_FOLDER_MANAGED "\xef\x9d\xb5" // U+f775 #define ICON_MS_FOLDER_OFF "\xee\xae\x83" // U+eb83 #define ICON_MS_FOLDER_OPEN "\xee\x8b\x88" // U+e2c8 #define ICON_MS_FOLDER_SHARED "\xee\x8b\x89" // U+e2c9 #define ICON_MS_FOLDER_SPECIAL "\xee\x98\x97" // U+e617 #define ICON_MS_FOLDER_SUPERVISED "\xef\x9d\xb4" // U+f774 #define ICON_MS_FOLDER_ZIP "\xee\xac\xac" // U+eb2c #define ICON_MS_FOLLOW_THE_SIGNS "\xef\x88\xa2" // U+f222 #define ICON_MS_FONT_DOWNLOAD "\xee\x85\xa7" // U+e167 #define ICON_MS_FONT_DOWNLOAD_OFF "\xee\x93\xb9" // U+e4f9 #define ICON_MS_FOOD_BANK "\xef\x87\xb2" // U+f1f2 #define ICON_MS_FOOT_BONES "\xef\xa2\x93" // U+f893 #define ICON_MS_FOOTPRINT "\xef\xa1\xbd" // U+f87d #define ICON_MS_FOR_YOU "\xee\xa6\xac" // U+e9ac #define ICON_MS_FOREST "\xee\xaa\x99" // U+ea99 #define ICON_MS_FORK_LEFT "\xee\xae\xa0" // U+eba0 #define ICON_MS_FORK_RIGHT "\xee\xae\xac" // U+ebac #define ICON_MS_FORKLIFT "\xef\xa1\xa8" // U+f868 #define ICON_MS_FORMAT_ALIGN_CENTER "\xee\x88\xb4" // U+e234 #define ICON_MS_FORMAT_ALIGN_JUSTIFY "\xee\x88\xb5" // U+e235 #define ICON_MS_FORMAT_ALIGN_LEFT "\xee\x88\xb6" // U+e236 #define ICON_MS_FORMAT_ALIGN_RIGHT "\xee\x88\xb7" // U+e237 #define ICON_MS_FORMAT_BOLD "\xee\x88\xb8" // U+e238 #define ICON_MS_FORMAT_CLEAR "\xee\x88\xb9" // U+e239 #define ICON_MS_FORMAT_COLOR_FILL "\xee\x88\xba" // U+e23a #define ICON_MS_FORMAT_COLOR_RESET "\xee\x88\xbb" // U+e23b #define ICON_MS_FORMAT_COLOR_TEXT "\xee\x88\xbc" // U+e23c #define ICON_MS_FORMAT_H1 "\xef\xa1\x9d" // U+f85d #define ICON_MS_FORMAT_H2 "\xef\xa1\x9e" // U+f85e #define ICON_MS_FORMAT_H3 "\xef\xa1\x9f" // U+f85f #define ICON_MS_FORMAT_H4 "\xef\xa1\xa0" // U+f860 #define ICON_MS_FORMAT_H5 "\xef\xa1\xa1" // U+f861 #define ICON_MS_FORMAT_H6 "\xef\xa1\xa2" // U+f862 #define ICON_MS_FORMAT_IMAGE_LEFT "\xef\xa1\xa3" // U+f863 #define ICON_MS_FORMAT_IMAGE_RIGHT "\xef\xa1\xa4" // U+f864 #define ICON_MS_FORMAT_INDENT_DECREASE "\xee\x88\xbd" // U+e23d #define ICON_MS_FORMAT_INDENT_INCREASE "\xee\x88\xbe" // U+e23e #define ICON_MS_FORMAT_INK_HIGHLIGHTER "\xef\xa0\xab" // U+f82b #define ICON_MS_FORMAT_ITALIC "\xee\x88\xbf" // U+e23f #define ICON_MS_FORMAT_LETTER_SPACING "\xef\x9d\xb3" // U+f773 #define ICON_MS_FORMAT_LETTER_SPACING_2 "\xef\x98\x98" // U+f618 #define ICON_MS_FORMAT_LETTER_SPACING_STANDARD "\xef\x98\x97" // U+f617 #define ICON_MS_FORMAT_LETTER_SPACING_WIDE "\xef\x98\x96" // U+f616 #define ICON_MS_FORMAT_LETTER_SPACING_WIDER "\xef\x98\x95" // U+f615 #define ICON_MS_FORMAT_LINE_SPACING "\xee\x89\x80" // U+e240 #define ICON_MS_FORMAT_LIST_BULLETED "\xee\x89\x81" // U+e241 #define ICON_MS_FORMAT_LIST_BULLETED_ADD "\xef\xa1\x89" // U+f849 #define ICON_MS_FORMAT_LIST_NUMBERED "\xee\x89\x82" // U+e242 #define ICON_MS_FORMAT_LIST_NUMBERED_RTL "\xee\x89\xa7" // U+e267 #define ICON_MS_FORMAT_OVERLINE "\xee\xad\xa5" // U+eb65 #define ICON_MS_FORMAT_PAINT "\xee\x89\x83" // U+e243 #define ICON_MS_FORMAT_PARAGRAPH "\xef\xa1\xa5" // U+f865 #define ICON_MS_FORMAT_QUOTE "\xee\x89\x84" // U+e244 #define ICON_MS_FORMAT_SHAPES "\xee\x89\x9e" // U+e25e #define ICON_MS_FORMAT_SIZE "\xee\x89\x85" // U+e245 #define ICON_MS_FORMAT_STRIKETHROUGH "\xee\x89\x86" // U+e246 #define ICON_MS_FORMAT_TEXT_CLIP "\xef\xa0\xaa" // U+f82a #define ICON_MS_FORMAT_TEXT_OVERFLOW "\xef\xa0\xa9" // U+f829 #define ICON_MS_FORMAT_TEXT_WRAP "\xef\xa0\xa8" // U+f828 #define ICON_MS_FORMAT_TEXTDIRECTION_L_TO_R "\xee\x89\x87" // U+e247 #define ICON_MS_FORMAT_TEXTDIRECTION_R_TO_L "\xee\x89\x88" // U+e248 #define ICON_MS_FORMAT_TEXTDIRECTION_VERTICAL "\xef\x92\xb8" // U+f4b8 #define ICON_MS_FORMAT_UNDERLINED "\xee\x89\x89" // U+e249 #define ICON_MS_FORMAT_UNDERLINED_SQUIGGLE "\xef\xa2\x85" // U+f885 #define ICON_MS_FORMS_ADD_ON "\xef\x83\x87" // U+f0c7 #define ICON_MS_FORMS_APPS_SCRIPT "\xef\x83\x88" // U+f0c8 #define ICON_MS_FORT "\xee\xaa\xad" // U+eaad #define ICON_MS_FORUM "\xee\xa2\xaf" // U+e8af #define ICON_MS_FORWARD "\xef\x95\xba" // U+f57a #define ICON_MS_FORWARD_10 "\xee\x81\x96" // U+e056 #define ICON_MS_FORWARD_30 "\xee\x81\x97" // U+e057 #define ICON_MS_FORWARD_5 "\xee\x81\x98" // U+e058 #define ICON_MS_FORWARD_CIRCLE "\xef\x9b\xb5" // U+f6f5 #define ICON_MS_FORWARD_MEDIA "\xef\x9b\xb4" // U+f6f4 #define ICON_MS_FORWARD_TO_INBOX "\xef\x86\x87" // U+f187 #define ICON_MS_FOUNDATION "\xef\x88\x80" // U+f200 #define ICON_MS_FRAME_INSPECT "\xef\x9d\xb2" // U+f772 #define ICON_MS_FRAME_PERSON "\xef\xa2\xa6" // U+f8a6 #define ICON_MS_FRAME_PERSON_MIC "\xef\x93\x95" // U+f4d5 #define ICON_MS_FRAME_PERSON_OFF "\xef\x9f\x91" // U+f7d1 #define ICON_MS_FRAME_RELOAD "\xef\x9d\xb1" // U+f771 #define ICON_MS_FRAME_SOURCE "\xef\x9d\xb0" // U+f770 #define ICON_MS_FREE_BREAKFAST "\xee\xad\x84" // U+eb44 #define ICON_MS_FREE_CANCELLATION "\xee\x9d\x88" // U+e748 #define ICON_MS_FRONT_HAND "\xee\x9d\xa9" // U+e769 #define ICON_MS_FRONT_LOADER "\xef\xa1\xa9" // U+f869 #define ICON_MS_FULL_COVERAGE "\xee\xac\x92" // U+eb12 #define ICON_MS_FULL_HD "\xef\x96\x8b" // U+f58b #define ICON_MS_FULL_STACKED_BAR_CHART "\xef\x88\x92" // U+f212 #define ICON_MS_FULLSCREEN "\xee\x97\x90" // U+e5d0 #define ICON_MS_FULLSCREEN_EXIT "\xee\x97\x91" // U+e5d1 #define ICON_MS_FUNCTION "\xef\xa1\xa6" // U+f866 #define ICON_MS_FUNCTIONS "\xee\x89\x8a" // U+e24a #define ICON_MS_G_MOBILEDATA "\xef\x80\x90" // U+f010 #define ICON_MS_G_MOBILEDATA_BADGE "\xef\x9f\xa1" // U+f7e1 #define ICON_MS_G_TRANSLATE "\xee\xa4\xa7" // U+e927 #define ICON_MS_GALLERY_THUMBNAIL "\xef\xa1\xaf" // U+f86f #define ICON_MS_GAMEPAD "\xee\x8c\x8f" // U+e30f #define ICON_MS_GAMES "\xee\x8c\x8f" // U+e30f #define ICON_MS_GARAGE "\xef\x80\x91" // U+f011 #define ICON_MS_GARAGE_DOOR "\xee\x9c\x94" // U+e714 #define ICON_MS_GARAGE_HOME "\xee\xa0\xad" // U+e82d #define ICON_MS_GARDEN_CART "\xef\xa2\xa9" // U+f8a9 #define ICON_MS_GAS_METER "\xee\xb0\x99" // U+ec19 #define ICON_MS_GASTROENTEROLOGY "\xee\x83\xb1" // U+e0f1 #define ICON_MS_GATE "\xee\x89\xb7" // U+e277 #define ICON_MS_GAVEL "\xee\xa4\x8e" // U+e90e #define ICON_MS_GENERAL_DEVICE "\xee\x9b\x9e" // U+e6de #define ICON_MS_GENERATING_TOKENS "\xee\x9d\x89" // U+e749 #define ICON_MS_GENETICS "\xee\x83\xb3" // U+e0f3 #define ICON_MS_GENRES "\xee\x9b\xae" // U+e6ee #define ICON_MS_GESTURE "\xee\x85\x95" // U+e155 #define ICON_MS_GESTURE_SELECT "\xef\x99\x97" // U+f657 #define ICON_MS_GET_APP "\xef\x82\x90" // U+f090 #define ICON_MS_GIF "\xee\xa4\x88" // U+e908 #define ICON_MS_GIF_BOX "\xee\x9e\xa3" // U+e7a3 #define ICON_MS_GIRL "\xee\xad\xa8" // U+eb68 #define ICON_MS_GITE "\xee\x96\x8b" // U+e58b #define ICON_MS_GLASS_CUP "\xef\x9b\xa3" // U+f6e3 #define ICON_MS_GLOBE "\xee\x99\x8c" // U+e64c #define ICON_MS_GLOBE_ASIA "\xef\x9e\x99" // U+f799 #define ICON_MS_GLOBE_UK "\xef\x9e\x98" // U+f798 #define ICON_MS_GLUCOSE "\xee\x92\xa0" // U+e4a0 #define ICON_MS_GLYPHS "\xef\xa2\xa3" // U+f8a3 #define ICON_MS_GO_TO_LINE "\xef\x9c\x9d" // U+f71d #define ICON_MS_GOLF_COURSE "\xee\xad\x85" // U+eb45 #define ICON_MS_GOOGLE_HOME_DEVICES "\xee\x9c\x95" // U+e715 #define ICON_MS_GOOGLE_PLUS_RESHARE "\xef\x95\xba" // U+f57a #define ICON_MS_GOOGLE_TV_REMOTE "\xef\x97\x9b" // U+f5db #define ICON_MS_GOOGLE_WIFI "\xef\x95\xb9" // U+f579 #define ICON_MS_GPP_BAD "\xef\x80\x92" // U+f012 #define ICON_MS_GPP_GOOD "\xef\x80\x93" // U+f013 #define ICON_MS_GPP_MAYBE "\xef\x80\x94" // U+f014 #define ICON_MS_GPS_FIXED "\xee\x95\x9c" // U+e55c #define ICON_MS_GPS_NOT_FIXED "\xee\x86\xb7" // U+e1b7 #define ICON_MS_GPS_OFF "\xee\x86\xb6" // U+e1b6 #define ICON_MS_GRADE "\xee\xa2\x85" // U+e885 #define ICON_MS_GRADIENT "\xee\x8f\xa9" // U+e3e9 #define ICON_MS_GRADING "\xee\xa9\x8f" // U+ea4f #define ICON_MS_GRAIN "\xee\x8f\xaa" // U+e3ea #define ICON_MS_GRAPHIC_EQ "\xee\x86\xb8" // U+e1b8 #define ICON_MS_GRASS "\xef\x88\x85" // U+f205 #define ICON_MS_GRID_3X3 "\xef\x80\x95" // U+f015 #define ICON_MS_GRID_3X3_OFF "\xef\x99\xbc" // U+f67c #define ICON_MS_GRID_4X4 "\xef\x80\x96" // U+f016 #define ICON_MS_GRID_GOLDENRATIO "\xef\x80\x97" // U+f017 #define ICON_MS_GRID_GUIDES "\xef\x9d\xaf" // U+f76f #define ICON_MS_GRID_OFF "\xee\x8f\xab" // U+e3eb #define ICON_MS_GRID_ON "\xee\x8f\xac" // U+e3ec #define ICON_MS_GRID_VIEW "\xee\xa6\xb0" // U+e9b0 #define ICON_MS_GROCERY "\xee\xbe\x97" // U+ef97 #define ICON_MS_GROUP "\xee\xa8\xa1" // U+ea21 #define ICON_MS_GROUP_ADD "\xee\x9f\xb0" // U+e7f0 #define ICON_MS_GROUP_OFF "\xee\x9d\x87" // U+e747 #define ICON_MS_GROUP_REMOVE "\xee\x9e\xad" // U+e7ad #define ICON_MS_GROUP_WORK "\xee\xa2\x86" // U+e886 #define ICON_MS_GROUPED_BAR_CHART "\xef\x88\x91" // U+f211 #define ICON_MS_GROUPS "\xef\x88\xb3" // U+f233 #define ICON_MS_GROUPS_2 "\xef\xa3\x9f" // U+f8df #define ICON_MS_GROUPS_3 "\xef\xa3\xa0" // U+f8e0 #define ICON_MS_GUARDIAN "\xef\x93\x81" // U+f4c1 #define ICON_MS_GYNECOLOGY "\xee\x83\xb4" // U+e0f4 #define ICON_MS_H_MOBILEDATA "\xef\x80\x98" // U+f018 #define ICON_MS_H_MOBILEDATA_BADGE "\xef\x9f\xa0" // U+f7e0 #define ICON_MS_H_PLUS_MOBILEDATA "\xef\x80\x99" // U+f019 #define ICON_MS_H_PLUS_MOBILEDATA_BADGE "\xef\x9f\x9f" // U+f7df #define ICON_MS_HAIL "\xee\xa6\xb1" // U+e9b1 #define ICON_MS_HALLWAY "\xee\x9b\xb8" // U+e6f8 #define ICON_MS_HAND_BONES "\xef\xa2\x94" // U+f894 #define ICON_MS_HAND_GESTURE "\xee\xbe\x9c" // U+ef9c #define ICON_MS_HANDHELD_CONTROLLER "\xef\x93\x86" // U+f4c6 #define ICON_MS_HANDSHAKE "\xee\xaf\x8b" // U+ebcb #define ICON_MS_HANDWRITING_RECOGNITION "\xee\xac\x82" // U+eb02 #define ICON_MS_HANDYMAN "\xef\x84\x8b" // U+f10b #define ICON_MS_HANGOUT_VIDEO "\xee\x83\x81" // U+e0c1 #define ICON_MS_HANGOUT_VIDEO_OFF "\xee\x83\x82" // U+e0c2 #define ICON_MS_HARD_DRIVE "\xef\xa0\x8e" // U+f80e #define ICON_MS_HARD_DRIVE_2 "\xef\x9e\xa4" // U+f7a4 #define ICON_MS_HARDWARE "\xee\xa9\x99" // U+ea59 #define ICON_MS_HD "\xee\x81\x92" // U+e052 #define ICON_MS_HDR_AUTO "\xef\x80\x9a" // U+f01a #define ICON_MS_HDR_AUTO_SELECT "\xef\x80\x9b" // U+f01b #define ICON_MS_HDR_ENHANCED_SELECT "\xee\xbd\x91" // U+ef51 #define ICON_MS_HDR_OFF "\xee\x8f\xad" // U+e3ed #define ICON_MS_HDR_OFF_SELECT "\xef\x80\x9c" // U+f01c #define ICON_MS_HDR_ON "\xee\x8f\xae" // U+e3ee #define ICON_MS_HDR_ON_SELECT "\xef\x80\x9d" // U+f01d #define ICON_MS_HDR_PLUS "\xef\x80\x9e" // U+f01e #define ICON_MS_HDR_PLUS_OFF "\xee\x8f\xaf" // U+e3ef #define ICON_MS_HDR_STRONG "\xee\x8f\xb1" // U+e3f1 #define ICON_MS_HDR_WEAK "\xee\x8f\xb2" // U+e3f2 #define ICON_MS_HEAD_MOUNTED_DEVICE "\xef\x93\x85" // U+f4c5 #define ICON_MS_HEADPHONES "\xef\x80\x9f" // U+f01f #define ICON_MS_HEADPHONES_BATTERY "\xef\x80\xa0" // U+f020 #define ICON_MS_HEADSET "\xef\x80\x9f" // U+f01f #define ICON_MS_HEADSET_MIC "\xee\x8c\x91" // U+e311 #define ICON_MS_HEADSET_OFF "\xee\x8c\xba" // U+e33a #define ICON_MS_HEALING "\xee\x8f\xb3" // U+e3f3 #define ICON_MS_HEALTH_AND_BEAUTY "\xee\xbe\x9d" // U+ef9d #define ICON_MS_HEALTH_AND_SAFETY "\xee\x87\x95" // U+e1d5 #define ICON_MS_HEALTH_METRICS "\xef\x9b\xa2" // U+f6e2 #define ICON_MS_HEAP_SNAPSHOT_LARGE "\xef\x9d\xae" // U+f76e #define ICON_MS_HEAP_SNAPSHOT_MULTIPLE "\xef\x9d\xad" // U+f76d #define ICON_MS_HEAP_SNAPSHOT_THUMBNAIL "\xef\x9d\xac" // U+f76c #define ICON_MS_HEARING "\xee\x80\xa3" // U+e023 #define ICON_MS_HEARING_DISABLED "\xef\x84\x84" // U+f104 #define ICON_MS_HEART_BROKEN "\xee\xab\x82" // U+eac2 #define ICON_MS_HEART_CHECK "\xef\x98\x8a" // U+f60a #define ICON_MS_HEART_MINUS "\xef\xa2\x83" // U+f883 #define ICON_MS_HEART_PLUS "\xef\xa2\x84" // U+f884 #define ICON_MS_HEAT "\xef\x94\xb7" // U+f537 #define ICON_MS_HEAT_PUMP "\xee\xb0\x98" // U+ec18 #define ICON_MS_HEAT_PUMP_BALANCE "\xee\x89\xbe" // U+e27e #define ICON_MS_HEIGHT "\xee\xa8\x96" // U+ea16 #define ICON_MS_HELICOPTER "\xef\x98\x8c" // U+f60c #define ICON_MS_HELP "\xee\xa3\xbd" // U+e8fd #define ICON_MS_HELP_CENTER "\xef\x87\x80" // U+f1c0 #define ICON_MS_HELP_CLINIC "\xef\xa0\x90" // U+f810 #define ICON_MS_HELP_OUTLINE "\xee\xa3\xbd" // U+e8fd #define ICON_MS_HEMATOLOGY "\xee\x83\xb6" // U+e0f6 #define ICON_MS_HEVC "\xef\x80\xa1" // U+f021 #define ICON_MS_HEXAGON "\xee\xac\xb9" // U+eb39 #define ICON_MS_HIDE "\xee\xbe\x9e" // U+ef9e #define ICON_MS_HIDE_IMAGE "\xef\x80\xa2" // U+f022 #define ICON_MS_HIDE_SOURCE "\xef\x80\xa3" // U+f023 #define ICON_MS_HIGH_DENSITY "\xef\x9e\x9c" // U+f79c #define ICON_MS_HIGH_QUALITY "\xee\x80\xa4" // U+e024 #define ICON_MS_HIGH_RES "\xef\x95\x8b" // U+f54b #define ICON_MS_HIGHLIGHT "\xee\x89\x9f" // U+e25f #define ICON_MS_HIGHLIGHT_KEYBOARD_FOCUS "\xef\x94\x90" // U+f510 #define ICON_MS_HIGHLIGHT_MOUSE_CURSOR "\xef\x94\x91" // U+f511 #define ICON_MS_HIGHLIGHT_OFF "\xee\xa2\x88" // U+e888 #define ICON_MS_HIGHLIGHT_TEXT_CURSOR "\xef\x94\x92" // U+f512 #define ICON_MS_HIGHLIGHTER_SIZE_1 "\xef\x9d\xab" // U+f76b #define ICON_MS_HIGHLIGHTER_SIZE_2 "\xef\x9d\xaa" // U+f76a #define ICON_MS_HIGHLIGHTER_SIZE_3 "\xef\x9d\xa9" // U+f769 #define ICON_MS_HIGHLIGHTER_SIZE_4 "\xef\x9d\xa8" // U+f768 #define ICON_MS_HIGHLIGHTER_SIZE_5 "\xef\x9d\xa7" // U+f767 #define ICON_MS_HIKING "\xee\x94\x8a" // U+e50a #define ICON_MS_HISTORY "\xee\xa2\xb3" // U+e8b3 #define ICON_MS_HISTORY_EDU "\xee\xa8\xbe" // U+ea3e #define ICON_MS_HISTORY_OFF "\xef\x93\x9a" // U+f4da #define ICON_MS_HISTORY_TOGGLE_OFF "\xef\x85\xbd" // U+f17d #define ICON_MS_HIVE "\xee\xaa\xa6" // U+eaa6 #define ICON_MS_HLS "\xee\xae\x8a" // U+eb8a #define ICON_MS_HLS_OFF "\xee\xae\x8c" // U+eb8c #define ICON_MS_HOLIDAY_VILLAGE "\xee\x96\x8a" // U+e58a #define ICON_MS_HOME "\xee\xa6\xb2" // U+e9b2 #define ICON_MS_HOME_AND_GARDEN "\xee\xbe\x9f" // U+ef9f #define ICON_MS_HOME_APP_LOGO "\xee\x8a\x95" // U+e295 #define ICON_MS_HOME_FILLED "\xee\xa6\xb2" // U+e9b2 #define ICON_MS_HOME_HEALTH "\xee\x92\xb9" // U+e4b9 #define ICON_MS_HOME_IMPROVEMENT_AND_TOOLS "\xee\xbe\xa0" // U+efa0 #define ICON_MS_HOME_IOT_DEVICE "\xee\x8a\x83" // U+e283 #define ICON_MS_HOME_MAX "\xef\x80\xa4" // U+f024 #define ICON_MS_HOME_MAX_DOTS "\xee\xa1\x89" // U+e849 #define ICON_MS_HOME_MINI "\xef\x80\xa5" // U+f025 #define ICON_MS_HOME_PIN "\xef\x85\x8d" // U+f14d #define ICON_MS_HOME_REPAIR_SERVICE "\xef\x84\x80" // U+f100 #define ICON_MS_HOME_SPEAKER "\xef\x84\x9c" // U+f11c #define ICON_MS_HOME_STORAGE "\xef\xa1\xac" // U+f86c #define ICON_MS_HOME_WORK "\xef\x80\xb0" // U+f030 #define ICON_MS_HORIZONTAL_DISTRIBUTE "\xee\x80\x94" // U+e014 #define ICON_MS_HORIZONTAL_RULE "\xef\x84\x88" // U+f108 #define ICON_MS_HORIZONTAL_SPLIT "\xee\xa5\x87" // U+e947 #define ICON_MS_HOT_TUB "\xee\xad\x86" // U+eb46 #define ICON_MS_HOTEL "\xee\x95\x89" // U+e549 #define ICON_MS_HOTEL_CLASS "\xee\x9d\x83" // U+e743 #define ICON_MS_HOURGLASS "\xee\xaf\xbf" // U+ebff #define ICON_MS_HOURGLASS_BOTTOM "\xee\xa9\x9c" // U+ea5c #define ICON_MS_HOURGLASS_DISABLED "\xee\xbd\x93" // U+ef53 #define ICON_MS_HOURGLASS_EMPTY "\xee\xa2\x8b" // U+e88b #define ICON_MS_HOURGLASS_FULL "\xee\xa2\x8c" // U+e88c #define ICON_MS_HOURGLASS_TOP "\xee\xa9\x9b" // U+ea5b #define ICON_MS_HOUSE "\xee\xa9\x84" // U+ea44 #define ICON_MS_HOUSE_SIDING "\xef\x88\x82" // U+f202 #define ICON_MS_HOUSE_WITH_SHIELD "\xee\x9e\x86" // U+e786 #define ICON_MS_HOUSEBOAT "\xee\x96\x84" // U+e584 #define ICON_MS_HOUSEHOLD_SUPPLIES "\xee\xbe\xa1" // U+efa1 #define ICON_MS_HOW_TO_REG "\xee\x85\xb4" // U+e174 #define ICON_MS_HOW_TO_VOTE "\xee\x85\xb5" // U+e175 #define ICON_MS_HR_RESTING "\xef\x9a\xba" // U+f6ba #define ICON_MS_HTML "\xee\xad\xbe" // U+eb7e #define ICON_MS_HTTP "\xee\xa4\x82" // U+e902 #define ICON_MS_HTTPS "\xee\xa2\x99" // U+e899 #define ICON_MS_HUB "\xee\xa7\xb4" // U+e9f4 #define ICON_MS_HUMERUS "\xef\xa2\x95" // U+f895 #define ICON_MS_HUMERUS_ALT "\xef\xa2\x96" // U+f896 #define ICON_MS_HUMIDITY_HIGH "\xef\x85\xa3" // U+f163 #define ICON_MS_HUMIDITY_INDOOR "\xef\x95\x98" // U+f558 #define ICON_MS_HUMIDITY_LOW "\xef\x85\xa4" // U+f164 #define ICON_MS_HUMIDITY_MID "\xef\x85\xa5" // U+f165 #define ICON_MS_HUMIDITY_PERCENTAGE "\xef\xa1\xbe" // U+f87e #define ICON_MS_HVAC "\xef\x84\x8e" // U+f10e #define ICON_MS_ICE_SKATING "\xee\x94\x8b" // U+e50b #define ICON_MS_ICECREAM "\xee\xa9\xa9" // U+ea69 #define ICON_MS_ID_CARD "\xef\x93\x8a" // U+f4ca #define ICON_MS_IFL "\xee\x80\xa5" // U+e025 #define ICON_MS_IFRAME "\xef\x9c\x9b" // U+f71b #define ICON_MS_IFRAME_OFF "\xef\x9c\x9c" // U+f71c #define ICON_MS_IMAGE "\xee\x8f\xb4" // U+e3f4 #define ICON_MS_IMAGE_ASPECT_RATIO "\xee\x8f\xb5" // U+e3f5 #define ICON_MS_IMAGE_NOT_SUPPORTED "\xef\x84\x96" // U+f116 #define ICON_MS_IMAGE_SEARCH "\xee\x90\xbf" // U+e43f #define ICON_MS_IMAGESEARCH_ROLLER "\xee\xa6\xb4" // U+e9b4 #define ICON_MS_IMAGESMODE "\xee\xbe\xa2" // U+efa2 #define ICON_MS_IMMUNOLOGY "\xee\x83\xbb" // U+e0fb #define ICON_MS_IMPORT_CONTACTS "\xee\x83\xa0" // U+e0e0 #define ICON_MS_IMPORT_EXPORT "\xee\xa3\x95" // U+e8d5 #define ICON_MS_IMPORTANT_DEVICES "\xee\xa4\x92" // U+e912 #define ICON_MS_IN_HOME_MODE "\xee\xa0\xb3" // U+e833 #define ICON_MS_INACTIVE_ORDER "\xee\x83\xbc" // U+e0fc #define ICON_MS_INBOX "\xee\x85\x96" // U+e156 #define ICON_MS_INBOX_CUSTOMIZE "\xef\xa1\x99" // U+f859 #define ICON_MS_INCOMPLETE_CIRCLE "\xee\x9e\x9b" // U+e79b #define ICON_MS_INDETERMINATE_CHECK_BOX "\xee\xa4\x89" // U+e909 #define ICON_MS_INDETERMINATE_QUESTION_BOX "\xef\x95\xad" // U+f56d #define ICON_MS_INFO "\xee\xa2\x8e" // U+e88e #define ICON_MS_INFO_I "\xef\x96\x9b" // U+f59b #define ICON_MS_INFRARED "\xef\xa1\xbc" // U+f87c #define ICON_MS_INK_ERASER "\xee\x9b\x90" // U+e6d0 #define ICON_MS_INK_ERASER_OFF "\xee\x9f\xa3" // U+e7e3 #define ICON_MS_INK_HIGHLIGHTER "\xee\x9b\x91" // U+e6d1 #define ICON_MS_INK_HIGHLIGHTER_MOVE "\xef\x94\xa4" // U+f524 #define ICON_MS_INK_MARKER "\xee\x9b\x92" // U+e6d2 #define ICON_MS_INK_PEN "\xee\x9b\x93" // U+e6d3 #define ICON_MS_INPATIENT "\xee\x83\xbe" // U+e0fe #define ICON_MS_INPUT "\xee\xa2\x90" // U+e890 #define ICON_MS_INPUT_CIRCLE "\xef\x9c\x9a" // U+f71a #define ICON_MS_INSERT_CHART "\xef\x83\x8c" // U+f0cc #define ICON_MS_INSERT_CHART_FILLED "\xef\x83\x8c" // U+f0cc #define ICON_MS_INSERT_CHART_OUTLINED "\xef\x83\x8c" // U+f0cc #define ICON_MS_INSERT_COMMENT "\xee\x89\x8c" // U+e24c #define ICON_MS_INSERT_DRIVE_FILE "\xee\x99\xad" // U+e66d #define ICON_MS_INSERT_EMOTICON "\xee\xa8\xa2" // U+ea22 #define ICON_MS_INSERT_INVITATION "\xee\xa1\xb8" // U+e878 #define ICON_MS_INSERT_LINK "\xee\x89\x90" // U+e250 #define ICON_MS_INSERT_PAGE_BREAK "\xee\xab\x8a" // U+eaca #define ICON_MS_INSERT_PHOTO "\xee\x8f\xb4" // U+e3f4 #define ICON_MS_INSERT_TEXT "\xef\xa0\xa7" // U+f827 #define ICON_MS_INSIGHTS "\xef\x82\x92" // U+f092 #define ICON_MS_INSTALL_DESKTOP "\xee\xad\xb1" // U+eb71 #define ICON_MS_INSTALL_MOBILE "\xee\xad\xb2" // U+eb72 #define ICON_MS_INSTANT_MIX "\xee\x80\xa6" // U+e026 #define ICON_MS_INTEGRATION_INSTRUCTIONS "\xee\xbd\x94" // U+ef54 #define ICON_MS_INTERACTIVE_SPACE "\xef\x9f\xbf" // U+f7ff #define ICON_MS_INTERESTS "\xee\x9f\x88" // U+e7c8 #define ICON_MS_INTERPRETER_MODE "\xee\xa0\xbb" // U+e83b #define ICON_MS_INVENTORY "\xee\x85\xb9" // U+e179 #define ICON_MS_INVENTORY_2 "\xee\x86\xa1" // U+e1a1 #define ICON_MS_INVERT_COLORS "\xee\xa2\x91" // U+e891 #define ICON_MS_INVERT_COLORS_OFF "\xee\x83\x84" // U+e0c4 #define ICON_MS_IOS "\xee\x80\xa7" // U+e027 #define ICON_MS_IOS_SHARE "\xee\x9a\xb8" // U+e6b8 #define ICON_MS_IRON "\xee\x96\x83" // U+e583 #define ICON_MS_ISO "\xee\x8f\xb6" // U+e3f6 #define ICON_MS_JAMBOARD_KIOSK "\xee\xa6\xb5" // U+e9b5 #define ICON_MS_JAVASCRIPT "\xee\xad\xbc" // U+eb7c #define ICON_MS_JOIN "\xef\xa1\x8f" // U+f84f #define ICON_MS_JOIN_FULL "\xef\xa1\x8f" // U+f84f #define ICON_MS_JOIN_INNER "\xee\xab\xb4" // U+eaf4 #define ICON_MS_JOIN_LEFT "\xee\xab\xb2" // U+eaf2 #define ICON_MS_JOIN_RIGHT "\xee\xab\xaa" // U+eaea #define ICON_MS_JOYSTICK "\xef\x97\xae" // U+f5ee #define ICON_MS_JUMP_TO_ELEMENT "\xef\x9c\x99" // U+f719 #define ICON_MS_KAYAKING "\xee\x94\x8c" // U+e50c #define ICON_MS_KEBAB_DINING "\xee\xa1\x82" // U+e842 #define ICON_MS_KEEP "\xef\x80\xa6" // U+f026 #define ICON_MS_KEEP_OFF "\xee\x9b\xb9" // U+e6f9 #define ICON_MS_KEEP_PIN "\xef\x80\xa6" // U+f026 #define ICON_MS_KEEP_PUBLIC "\xef\x95\xaf" // U+f56f #define ICON_MS_KETTLE "\xee\x8a\xb9" // U+e2b9 #define ICON_MS_KEY "\xee\x9c\xbc" // U+e73c #define ICON_MS_KEY_OFF "\xee\xae\x84" // U+eb84 #define ICON_MS_KEY_VERTICAL "\xef\x94\x9a" // U+f51a #define ICON_MS_KEY_VISUALIZER "\xef\x86\x99" // U+f199 #define ICON_MS_KEYBOARD "\xee\x8c\x92" // U+e312 #define ICON_MS_KEYBOARD_ALT "\xef\x80\xa8" // U+f028 #define ICON_MS_KEYBOARD_ARROW_DOWN "\xee\x8c\x93" // U+e313 #define ICON_MS_KEYBOARD_ARROW_LEFT "\xee\x8c\x94" // U+e314 #define ICON_MS_KEYBOARD_ARROW_RIGHT "\xee\x8c\x95" // U+e315 #define ICON_MS_KEYBOARD_ARROW_UP "\xee\x8c\x96" // U+e316 #define ICON_MS_KEYBOARD_BACKSPACE "\xee\x8c\x97" // U+e317 #define ICON_MS_KEYBOARD_CAPSLOCK "\xee\x8c\x98" // U+e318 #define ICON_MS_KEYBOARD_CAPSLOCK_BADGE "\xef\x9f\x9e" // U+f7de #define ICON_MS_KEYBOARD_COMMAND_KEY "\xee\xab\xa7" // U+eae7 #define ICON_MS_KEYBOARD_CONTROL_KEY "\xee\xab\xa6" // U+eae6 #define ICON_MS_KEYBOARD_DOUBLE_ARROW_DOWN "\xee\xab\x90" // U+ead0 #define ICON_MS_KEYBOARD_DOUBLE_ARROW_LEFT "\xee\xab\x83" // U+eac3 #define ICON_MS_KEYBOARD_DOUBLE_ARROW_RIGHT "\xee\xab\x89" // U+eac9 #define ICON_MS_KEYBOARD_DOUBLE_ARROW_UP "\xee\xab\x8f" // U+eacf #define ICON_MS_KEYBOARD_EXTERNAL_INPUT "\xef\x9f\x9d" // U+f7dd #define ICON_MS_KEYBOARD_FULL "\xef\x9f\x9c" // U+f7dc #define ICON_MS_KEYBOARD_HIDE "\xee\x8c\x9a" // U+e31a #define ICON_MS_KEYBOARD_KEYS "\xef\x99\xbb" // U+f67b #define ICON_MS_KEYBOARD_LOCK "\xef\x92\x92" // U+f492 #define ICON_MS_KEYBOARD_LOCK_OFF "\xef\x92\x91" // U+f491 #define ICON_MS_KEYBOARD_OFF "\xef\x99\xba" // U+f67a #define ICON_MS_KEYBOARD_ONSCREEN "\xef\x9f\x9b" // U+f7db #define ICON_MS_KEYBOARD_OPTION_KEY "\xee\xab\xa8" // U+eae8 #define ICON_MS_KEYBOARD_PREVIOUS_LANGUAGE "\xef\x9f\x9a" // U+f7da #define ICON_MS_KEYBOARD_RETURN "\xee\x8c\x9b" // U+e31b #define ICON_MS_KEYBOARD_TAB "\xee\x8c\x9c" // U+e31c #define ICON_MS_KEYBOARD_TAB_RTL "\xee\xb1\xb3" // U+ec73 #define ICON_MS_KEYBOARD_VOICE "\xee\x8c\x9d" // U+e31d #define ICON_MS_KID_STAR "\xef\x94\xa6" // U+f526 #define ICON_MS_KING_BED "\xee\xa9\x85" // U+ea45 #define ICON_MS_KITCHEN "\xee\xad\x87" // U+eb47 #define ICON_MS_KITESURFING "\xee\x94\x8d" // U+e50d #define ICON_MS_LAB_PANEL "\xee\x84\x83" // U+e103 #define ICON_MS_LAB_PROFILE "\xee\x84\x84" // U+e104 #define ICON_MS_LAB_RESEARCH "\xef\xa0\x8b" // U+f80b #define ICON_MS_LABEL "\xee\xa2\x93" // U+e893 #define ICON_MS_LABEL_IMPORTANT "\xee\xa5\x88" // U+e948 #define ICON_MS_LABEL_IMPORTANT_OUTLINE "\xee\xa5\x88" // U+e948 #define ICON_MS_LABEL_OFF "\xee\xa6\xb6" // U+e9b6 #define ICON_MS_LABEL_OUTLINE "\xee\xa2\x93" // U+e893 #define ICON_MS_LABS "\xee\x84\x85" // U+e105 #define ICON_MS_LAN "\xee\xac\xaf" // U+eb2f #define ICON_MS_LANDSCAPE "\xee\x95\xa4" // U+e564 #define ICON_MS_LANDSCAPE_2 "\xef\x93\x84" // U+f4c4 #define ICON_MS_LANDSCAPE_2_OFF "\xef\x93\x83" // U+f4c3 #define ICON_MS_LANDSLIDE "\xee\xaf\x97" // U+ebd7 #define ICON_MS_LANGUAGE "\xee\xa2\x94" // U+e894 #define ICON_MS_LANGUAGE_CHINESE_ARRAY "\xef\x9d\xa6" // U+f766 #define ICON_MS_LANGUAGE_CHINESE_CANGJIE "\xef\x9d\xa5" // U+f765 #define ICON_MS_LANGUAGE_CHINESE_DAYI "\xef\x9d\xa4" // U+f764 #define ICON_MS_LANGUAGE_CHINESE_PINYIN "\xef\x9d\xa3" // U+f763 #define ICON_MS_LANGUAGE_CHINESE_QUICK "\xef\x9d\xa2" // U+f762 #define ICON_MS_LANGUAGE_CHINESE_WUBI "\xef\x9d\xa1" // U+f761 #define ICON_MS_LANGUAGE_FRENCH "\xef\x9d\xa0" // U+f760 #define ICON_MS_LANGUAGE_GB_ENGLISH "\xef\x9d\x9f" // U+f75f #define ICON_MS_LANGUAGE_INTERNATIONAL "\xef\x9d\x9e" // U+f75e #define ICON_MS_LANGUAGE_JAPANESE_KANA "\xef\x94\x93" // U+f513 #define ICON_MS_LANGUAGE_KOREAN_LATIN "\xef\x9d\x9d" // U+f75d #define ICON_MS_LANGUAGE_PINYIN "\xef\x9d\x9c" // U+f75c #define ICON_MS_LANGUAGE_SPANISH "\xef\x97\xa9" // U+f5e9 #define ICON_MS_LANGUAGE_US "\xef\x9d\x99" // U+f759 #define ICON_MS_LANGUAGE_US_COLEMAK "\xef\x9d\x9b" // U+f75b #define ICON_MS_LANGUAGE_US_DVORAK "\xef\x9d\x9a" // U+f75a #define ICON_MS_LAPS "\xef\x9a\xb9" // U+f6b9 #define ICON_MS_LAPTOP "\xee\x8c\x9e" // U+e31e #define ICON_MS_LAPTOP_CHROMEBOOK "\xee\x8c\x9f" // U+e31f #define ICON_MS_LAPTOP_MAC "\xee\x8c\xa0" // U+e320 #define ICON_MS_LAPTOP_WINDOWS "\xee\x8c\xa1" // U+e321 #define ICON_MS_LASSO_SELECT "\xee\xac\x83" // U+eb03 #define ICON_MS_LAST_PAGE "\xee\x97\x9d" // U+e5dd #define ICON_MS_LAUNCH "\xee\xa2\x9e" // U+e89e #define ICON_MS_LAUNDRY "\xee\x8a\xa8" // U+e2a8 #define ICON_MS_LAYERS "\xee\x94\xbb" // U+e53b #define ICON_MS_LAYERS_CLEAR "\xee\x94\xbc" // U+e53c #define ICON_MS_LDA "\xee\x84\x86" // U+e106 #define ICON_MS_LEADERBOARD "\xef\x88\x8c" // U+f20c #define ICON_MS_LEAK_ADD "\xee\x8f\xb8" // U+e3f8 #define ICON_MS_LEAK_REMOVE "\xee\x8f\xb9" // U+e3f9 #define ICON_MS_LEFT_CLICK "\xef\x9c\x98" // U+f718 #define ICON_MS_LEFT_PANEL_CLOSE "\xef\x9c\x97" // U+f717 #define ICON_MS_LEFT_PANEL_OPEN "\xef\x9c\x96" // U+f716 #define ICON_MS_LEGEND_TOGGLE "\xef\x84\x9b" // U+f11b #define ICON_MS_LENS "\xee\x8f\xba" // U+e3fa #define ICON_MS_LENS_BLUR "\xef\x80\xa9" // U+f029 #define ICON_MS_LETTER_SWITCH "\xef\x9d\x98" // U+f758 #define ICON_MS_LIBRARY_ADD "\xee\x80\xbc" // U+e03c #define ICON_MS_LIBRARY_ADD_CHECK "\xee\xa6\xb7" // U+e9b7 #define ICON_MS_LIBRARY_BOOKS "\xee\x80\xaf" // U+e02f #define ICON_MS_LIBRARY_MUSIC "\xee\x80\xb0" // U+e030 #define ICON_MS_LICENSE "\xee\xac\x84" // U+eb04 #define ICON_MS_LIFT_TO_TALK "\xee\xbe\xa3" // U+efa3 #define ICON_MS_LIGHT "\xef\x80\xaa" // U+f02a #define ICON_MS_LIGHT_GROUP "\xee\x8a\x8b" // U+e28b #define ICON_MS_LIGHT_MODE "\xee\x94\x98" // U+e518 #define ICON_MS_LIGHT_OFF "\xee\xa6\xb8" // U+e9b8 #define ICON_MS_LIGHTBULB "\xee\xa4\x8f" // U+e90f #define ICON_MS_LIGHTBULB_CIRCLE "\xee\xaf\xbe" // U+ebfe #define ICON_MS_LIGHTBULB_OUTLINE "\xee\xa4\x8f" // U+e90f #define ICON_MS_LIGHTNING_STAND "\xee\xbe\xa4" // U+efa4 #define ICON_MS_LINE_AXIS "\xee\xaa\x9a" // U+ea9a #define ICON_MS_LINE_CURVE "\xef\x9d\x97" // U+f757 #define ICON_MS_LINE_END "\xef\xa0\xa6" // U+f826 #define ICON_MS_LINE_END_ARROW "\xef\xa0\x9d" // U+f81d #define ICON_MS_LINE_END_ARROW_NOTCH "\xef\xa0\x9c" // U+f81c #define ICON_MS_LINE_END_CIRCLE "\xef\xa0\x9b" // U+f81b #define ICON_MS_LINE_END_DIAMOND "\xef\xa0\x9a" // U+f81a #define ICON_MS_LINE_END_SQUARE "\xef\xa0\x99" // U+f819 #define ICON_MS_LINE_START "\xef\xa0\xa5" // U+f825 #define ICON_MS_LINE_START_ARROW "\xef\xa0\x98" // U+f818 #define ICON_MS_LINE_START_ARROW_NOTCH "\xef\xa0\x97" // U+f817 #define ICON_MS_LINE_START_CIRCLE "\xef\xa0\x96" // U+f816 #define ICON_MS_LINE_START_DIAMOND "\xef\xa0\x95" // U+f815 #define ICON_MS_LINE_START_SQUARE "\xef\xa0\x94" // U+f814 #define ICON_MS_LINE_STYLE "\xee\xa4\x99" // U+e919 #define ICON_MS_LINE_WEIGHT "\xee\xa4\x9a" // U+e91a #define ICON_MS_LINEAR_SCALE "\xee\x89\xa0" // U+e260 #define ICON_MS_LINK "\xee\x89\x90" // U+e250 #define ICON_MS_LINK_OFF "\xee\x85\xaf" // U+e16f #define ICON_MS_LINKED_CAMERA "\xee\x90\xb8" // U+e438 #define ICON_MS_LINKED_SERVICES "\xef\x94\xb5" // U+f535 #define ICON_MS_LIQUOR "\xee\xa9\xa0" // U+ea60 #define ICON_MS_LIST "\xee\xa2\x96" // U+e896 #define ICON_MS_LIST_ALT "\xee\x83\xae" // U+e0ee #define ICON_MS_LIST_ALT_ADD "\xef\x9d\x96" // U+f756 #define ICON_MS_LISTS "\xee\xa6\xb9" // U+e9b9 #define ICON_MS_LIVE_HELP "\xee\x83\x86" // U+e0c6 #define ICON_MS_LIVE_TV "\xee\x98\xba" // U+e63a #define ICON_MS_LIVING "\xef\x80\xab" // U+f02b #define ICON_MS_LOCAL_ACTIVITY "\xee\x95\x93" // U+e553 #define ICON_MS_LOCAL_AIRPORT "\xee\x94\xbd" // U+e53d #define ICON_MS_LOCAL_ATM "\xee\x94\xbe" // U+e53e #define ICON_MS_LOCAL_BAR "\xee\x95\x80" // U+e540 #define ICON_MS_LOCAL_CAFE "\xee\xad\x84" // U+eb44 #define ICON_MS_LOCAL_CAR_WASH "\xee\x95\x82" // U+e542 #define ICON_MS_LOCAL_CONVENIENCE_STORE "\xee\x95\x83" // U+e543 #define ICON_MS_LOCAL_DINING "\xee\x95\xa1" // U+e561 #define ICON_MS_LOCAL_DRINK "\xee\x95\x84" // U+e544 #define ICON_MS_LOCAL_FIRE_DEPARTMENT "\xee\xbd\x95" // U+ef55 #define ICON_MS_LOCAL_FLORIST "\xee\x95\x85" // U+e545 #define ICON_MS_LOCAL_GAS_STATION "\xee\x95\x86" // U+e546 #define ICON_MS_LOCAL_GROCERY_STORE "\xee\xa3\x8c" // U+e8cc #define ICON_MS_LOCAL_HOSPITAL "\xee\x95\x88" // U+e548 #define ICON_MS_LOCAL_HOTEL "\xee\x95\x89" // U+e549 #define ICON_MS_LOCAL_LAUNDRY_SERVICE "\xee\x95\x8a" // U+e54a #define ICON_MS_LOCAL_LIBRARY "\xee\x95\x8b" // U+e54b #define ICON_MS_LOCAL_MALL "\xee\x95\x8c" // U+e54c #define ICON_MS_LOCAL_MOVIES "\xee\xa3\x9a" // U+e8da #define ICON_MS_LOCAL_OFFER "\xef\x81\x9b" // U+f05b #define ICON_MS_LOCAL_PARKING "\xee\x95\x8f" // U+e54f #define ICON_MS_LOCAL_PHARMACY "\xee\x95\x90" // U+e550 #define ICON_MS_LOCAL_PHONE "\xef\x83\x94" // U+f0d4 #define ICON_MS_LOCAL_PIZZA "\xee\x95\x92" // U+e552 #define ICON_MS_LOCAL_PLAY "\xee\x95\x93" // U+e553 #define ICON_MS_LOCAL_POLICE "\xee\xbd\x96" // U+ef56 #define ICON_MS_LOCAL_POST_OFFICE "\xee\x95\x94" // U+e554 #define ICON_MS_LOCAL_PRINTSHOP "\xee\xa2\xad" // U+e8ad #define ICON_MS_LOCAL_SEE "\xee\x95\x97" // U+e557 #define ICON_MS_LOCAL_SHIPPING "\xee\x95\x98" // U+e558 #define ICON_MS_LOCAL_TAXI "\xee\x95\x99" // U+e559 #define ICON_MS_LOCATION_AUTOMATION "\xef\x85\x8f" // U+f14f #define ICON_MS_LOCATION_AWAY "\xef\x85\x90" // U+f150 #define ICON_MS_LOCATION_CHIP "\xef\xa1\x90" // U+f850 #define ICON_MS_LOCATION_CITY "\xee\x9f\xb1" // U+e7f1 #define ICON_MS_LOCATION_DISABLED "\xee\x86\xb6" // U+e1b6 #define ICON_MS_LOCATION_HOME "\xef\x85\x92" // U+f152 #define ICON_MS_LOCATION_OFF "\xee\x83\x87" // U+e0c7 #define ICON_MS_LOCATION_ON "\xef\x87\x9b" // U+f1db #define ICON_MS_LOCATION_PIN "\xef\x87\x9b" // U+f1db #define ICON_MS_LOCATION_SEARCHING "\xee\x86\xb7" // U+e1b7 #define ICON_MS_LOCATOR_TAG "\xef\xa3\x81" // U+f8c1 #define ICON_MS_LOCK "\xee\xa2\x99" // U+e899 #define ICON_MS_LOCK_CLOCK "\xee\xbd\x97" // U+ef57 #define ICON_MS_LOCK_OPEN "\xee\xa2\x98" // U+e898 #define ICON_MS_LOCK_OPEN_RIGHT "\xef\x99\x96" // U+f656 #define ICON_MS_LOCK_OUTLINE "\xee\xa2\x99" // U+e899 #define ICON_MS_LOCK_PERSON "\xef\xa3\xb3" // U+f8f3 #define ICON_MS_LOCK_RESET "\xee\xab\x9e" // U+eade #define ICON_MS_LOGIN "\xee\xa9\xb7" // U+ea77 #define ICON_MS_LOGO_DEV "\xee\xab\x96" // U+ead6 #define ICON_MS_LOGOUT "\xee\xa6\xba" // U+e9ba #define ICON_MS_LOOKS "\xee\x8f\xbc" // U+e3fc #define ICON_MS_LOOKS_3 "\xee\x8f\xbb" // U+e3fb #define ICON_MS_LOOKS_4 "\xee\x8f\xbd" // U+e3fd #define ICON_MS_LOOKS_5 "\xee\x8f\xbe" // U+e3fe #define ICON_MS_LOOKS_6 "\xee\x8f\xbf" // U+e3ff #define ICON_MS_LOOKS_ONE "\xee\x90\x80" // U+e400 #define ICON_MS_LOOKS_TWO "\xee\x90\x81" // U+e401 #define ICON_MS_LOOP "\xee\xa1\xa3" // U+e863 #define ICON_MS_LOUPE "\xee\x90\x82" // U+e402 #define ICON_MS_LOW_DENSITY "\xef\x9e\x9b" // U+f79b #define ICON_MS_LOW_PRIORITY "\xee\x85\xad" // U+e16d #define ICON_MS_LOWERCASE "\xef\x92\x8a" // U+f48a #define ICON_MS_LOYALTY "\xee\xa2\x9a" // U+e89a #define ICON_MS_LTE_MOBILEDATA "\xef\x80\xac" // U+f02c #define ICON_MS_LTE_MOBILEDATA_BADGE "\xef\x9f\x99" // U+f7d9 #define ICON_MS_LTE_PLUS_MOBILEDATA "\xef\x80\xad" // U+f02d #define ICON_MS_LTE_PLUS_MOBILEDATA_BADGE "\xef\x9f\x98" // U+f7d8 #define ICON_MS_LUGGAGE "\xef\x88\xb5" // U+f235 #define ICON_MS_LUNCH_DINING "\xee\xa9\xa1" // U+ea61 #define ICON_MS_LYRICS "\xee\xb0\x8b" // U+ec0b #define ICON_MS_MACRO_AUTO "\xef\x9b\xb2" // U+f6f2 #define ICON_MS_MACRO_OFF "\xef\xa3\x92" // U+f8d2 #define ICON_MS_MAGIC_BUTTON "\xef\x84\xb6" // U+f136 #define ICON_MS_MAGIC_EXCHANGE "\xef\x9f\xb4" // U+f7f4 #define ICON_MS_MAGIC_TETHER "\xef\x9f\x97" // U+f7d7 #define ICON_MS_MAGNIFICATION_LARGE "\xef\xa0\xbd" // U+f83d #define ICON_MS_MAGNIFICATION_SMALL "\xef\xa0\xbc" // U+f83c #define ICON_MS_MAGNIFY_DOCKED "\xef\x9f\x96" // U+f7d6 #define ICON_MS_MAGNIFY_FULLSCREEN "\xef\x9f\x95" // U+f7d5 #define ICON_MS_MAIL "\xee\x85\x99" // U+e159 #define ICON_MS_MAIL_LOCK "\xee\xb0\x8a" // U+ec0a #define ICON_MS_MAIL_OFF "\xef\x92\x8b" // U+f48b #define ICON_MS_MAIL_OUTLINE "\xee\x85\x99" // U+e159 #define ICON_MS_MALE "\xee\x96\x8e" // U+e58e #define ICON_MS_MAN "\xee\x93\xab" // U+e4eb #define ICON_MS_MAN_2 "\xef\xa3\xa1" // U+f8e1 #define ICON_MS_MAN_3 "\xef\xa3\xa2" // U+f8e2 #define ICON_MS_MAN_4 "\xef\xa3\xa3" // U+f8e3 #define ICON_MS_MANAGE_ACCOUNTS "\xef\x80\xae" // U+f02e #define ICON_MS_MANAGE_HISTORY "\xee\xaf\xa7" // U+ebe7 #define ICON_MS_MANAGE_SEARCH "\xef\x80\xaf" // U+f02f #define ICON_MS_MANGA "\xef\x97\xa3" // U+f5e3 #define ICON_MS_MANUFACTURING "\xee\x9c\xa6" // U+e726 #define ICON_MS_MAP "\xee\x95\x9b" // U+e55b #define ICON_MS_MAPS_HOME_WORK "\xef\x80\xb0" // U+f030 #define ICON_MS_MAPS_UGC "\xee\xbd\x98" // U+ef58 #define ICON_MS_MARGIN "\xee\xa6\xbb" // U+e9bb #define ICON_MS_MARK_AS_UNREAD "\xee\xa6\xbc" // U+e9bc #define ICON_MS_MARK_CHAT_READ "\xef\x86\x8b" // U+f18b #define ICON_MS_MARK_CHAT_UNREAD "\xef\x86\x89" // U+f189 #define ICON_MS_MARK_EMAIL_READ "\xef\x86\x8c" // U+f18c #define ICON_MS_MARK_EMAIL_UNREAD "\xef\x86\x8a" // U+f18a #define ICON_MS_MARK_UNREAD_CHAT_ALT "\xee\xae\x9d" // U+eb9d #define ICON_MS_MARKDOWN "\xef\x95\x92" // U+f552 #define ICON_MS_MARKDOWN_COPY "\xef\x95\x93" // U+f553 #define ICON_MS_MARKDOWN_PASTE "\xef\x95\x94" // U+f554 #define ICON_MS_MARKUNREAD "\xee\x85\x99" // U+e159 #define ICON_MS_MARKUNREAD_MAILBOX "\xee\xa2\x9b" // U+e89b #define ICON_MS_MASKED_TRANSITIONS "\xee\x9c\xae" // U+e72e #define ICON_MS_MASKS "\xef\x88\x98" // U+f218 #define ICON_MS_MATCH_CASE "\xef\x9b\xb1" // U+f6f1 #define ICON_MS_MATCH_WORD "\xef\x9b\xb0" // U+f6f0 #define ICON_MS_MATTER "\xee\xa4\x87" // U+e907 #define ICON_MS_MAXIMIZE "\xee\xa4\xb0" // U+e930 #define ICON_MS_MEASURING_TAPE "\xef\x9a\xaf" // U+f6af #define ICON_MS_MEDIA_BLUETOOTH_OFF "\xef\x80\xb1" // U+f031 #define ICON_MS_MEDIA_BLUETOOTH_ON "\xef\x80\xb2" // U+f032 #define ICON_MS_MEDIA_LINK "\xef\xa0\xbf" // U+f83f #define ICON_MS_MEDIA_OUTPUT "\xef\x93\xb2" // U+f4f2 #define ICON_MS_MEDIA_OUTPUT_OFF "\xef\x93\xb3" // U+f4f3 #define ICON_MS_MEDIATION "\xee\xbe\xa7" // U+efa7 #define ICON_MS_MEDICAL_INFORMATION "\xee\xaf\xad" // U+ebed #define ICON_MS_MEDICAL_MASK "\xef\xa0\x8a" // U+f80a #define ICON_MS_MEDICAL_SERVICES "\xef\x84\x89" // U+f109 #define ICON_MS_MEDICATION "\xef\x80\xb3" // U+f033 #define ICON_MS_MEDICATION_LIQUID "\xee\xaa\x87" // U+ea87 #define ICON_MS_MEETING_ROOM "\xee\xad\x8f" // U+eb4f #define ICON_MS_MEMORY "\xee\x8c\xa2" // U+e322 #define ICON_MS_MEMORY_ALT "\xef\x9e\xa3" // U+f7a3 #define ICON_MS_MENSTRUAL_HEALTH "\xef\x9b\xa1" // U+f6e1 #define ICON_MS_MENU "\xee\x97\x92" // U+e5d2 #define ICON_MS_MENU_BOOK "\xee\xa8\x99" // U+ea19 #define ICON_MS_MENU_OPEN "\xee\xa6\xbd" // U+e9bd #define ICON_MS_MERGE "\xee\xae\x98" // U+eb98 #define ICON_MS_MERGE_TYPE "\xee\x89\x92" // U+e252 #define ICON_MS_MESSAGE "\xee\x83\x89" // U+e0c9 #define ICON_MS_METABOLISM "\xee\x84\x8b" // U+e10b #define ICON_MS_MFG_NEST_YALE_LOCK "\xef\x84\x9d" // U+f11d #define ICON_MS_MIC "\xee\x8c\x9d" // U+e31d #define ICON_MS_MIC_DOUBLE "\xef\x97\x91" // U+f5d1 #define ICON_MS_MIC_EXTERNAL_OFF "\xee\xbd\x99" // U+ef59 #define ICON_MS_MIC_EXTERNAL_ON "\xee\xbd\x9a" // U+ef5a #define ICON_MS_MIC_NONE "\xee\x8c\x9d" // U+e31d #define ICON_MS_MIC_OFF "\xee\x80\xab" // U+e02b #define ICON_MS_MICROBIOLOGY "\xee\x84\x8c" // U+e10c #define ICON_MS_MICROWAVE "\xef\x88\x84" // U+f204 #define ICON_MS_MICROWAVE_GEN "\xee\xa1\x87" // U+e847 #define ICON_MS_MILITARY_TECH "\xee\xa8\xbf" // U+ea3f #define ICON_MS_MIMO "\xee\xa6\xbe" // U+e9be #define ICON_MS_MIMO_DISCONNECT "\xee\xa6\xbf" // U+e9bf #define ICON_MS_MINDFULNESS "\xef\x9b\xa0" // U+f6e0 #define ICON_MS_MINIMIZE "\xee\xa4\xb1" // U+e931 #define ICON_MS_MINOR_CRASH "\xee\xaf\xb1" // U+ebf1 #define ICON_MS_MINTMARK "\xee\xbe\xa9" // U+efa9 #define ICON_MS_MISSED_VIDEO_CALL "\xef\x83\x8e" // U+f0ce #define ICON_MS_MISSED_VIDEO_CALL_FILLED "\xef\x83\x8e" // U+f0ce #define ICON_MS_MISSING_CONTROLLER "\xee\x9c\x81" // U+e701 #define ICON_MS_MIST "\xee\x86\x88" // U+e188 #define ICON_MS_MITRE "\xef\x95\x87" // U+f547 #define ICON_MS_MIXTURE_MED "\xee\x93\x88" // U+e4c8 #define ICON_MS_MMS "\xee\x98\x98" // U+e618 #define ICON_MS_MOBILE_FRIENDLY "\xee\x88\x80" // U+e200 #define ICON_MS_MOBILE_OFF "\xee\x88\x81" // U+e201 #define ICON_MS_MOBILE_SCREEN_SHARE "\xee\x83\xa7" // U+e0e7 #define ICON_MS_MOBILEDATA_OFF "\xef\x80\xb4" // U+f034 #define ICON_MS_MODE "\xef\x82\x97" // U+f097 #define ICON_MS_MODE_COMMENT "\xee\x89\x93" // U+e253 #define ICON_MS_MODE_COOL "\xef\x85\xa6" // U+f166 #define ICON_MS_MODE_COOL_OFF "\xef\x85\xa7" // U+f167 #define ICON_MS_MODE_DUAL "\xef\x95\x97" // U+f557 #define ICON_MS_MODE_EDIT "\xef\x82\x97" // U+f097 #define ICON_MS_MODE_EDIT_OUTLINE "\xef\x82\x97" // U+f097 #define ICON_MS_MODE_FAN "\xef\x85\xa8" // U+f168 #define ICON_MS_MODE_FAN_OFF "\xee\xb0\x97" // U+ec17 #define ICON_MS_MODE_HEAT "\xef\x85\xaa" // U+f16a #define ICON_MS_MODE_HEAT_COOL "\xef\x85\xab" // U+f16b #define ICON_MS_MODE_HEAT_OFF "\xef\x85\xad" // U+f16d #define ICON_MS_MODE_NIGHT "\xef\x80\xb6" // U+f036 #define ICON_MS_MODE_OF_TRAVEL "\xee\x9f\x8e" // U+e7ce #define ICON_MS_MODE_OFF_ON "\xef\x85\xaf" // U+f16f #define ICON_MS_MODE_STANDBY "\xef\x80\xb7" // U+f037 #define ICON_MS_MODEL_TRAINING "\xef\x83\x8f" // U+f0cf #define ICON_MS_MONETIZATION_ON "\xee\x89\xa3" // U+e263 #define ICON_MS_MONEY "\xee\x95\xbd" // U+e57d #define ICON_MS_MONEY_OFF "\xef\x80\xb8" // U+f038 #define ICON_MS_MONEY_OFF_CSRED "\xef\x80\xb8" // U+f038 #define ICON_MS_MONITOR "\xee\xbd\x9b" // U+ef5b #define ICON_MS_MONITOR_HEART "\xee\xaa\xa2" // U+eaa2 #define ICON_MS_MONITOR_WEIGHT "\xef\x80\xb9" // U+f039 #define ICON_MS_MONITOR_WEIGHT_GAIN "\xef\x9b\x9f" // U+f6df #define ICON_MS_MONITOR_WEIGHT_LOSS "\xef\x9b\x9e" // U+f6de #define ICON_MS_MONITORING "\xef\x86\x90" // U+f190 #define ICON_MS_MONOCHROME_PHOTOS "\xee\x90\x83" // U+e403 #define ICON_MS_MOOD "\xee\xa8\xa2" // U+ea22 #define ICON_MS_MOOD_BAD "\xee\x9f\xb3" // U+e7f3 #define ICON_MS_MOP "\xee\x8a\x8d" // U+e28d #define ICON_MS_MORE "\xee\x98\x99" // U+e619 #define ICON_MS_MORE_DOWN "\xef\x86\x96" // U+f196 #define ICON_MS_MORE_HORIZ "\xee\x97\x93" // U+e5d3 #define ICON_MS_MORE_TIME "\xee\xa9\x9d" // U+ea5d #define ICON_MS_MORE_UP "\xef\x86\x97" // U+f197 #define ICON_MS_MORE_VERT "\xee\x97\x94" // U+e5d4 #define ICON_MS_MOSQUE "\xee\xaa\xb2" // U+eab2 #define ICON_MS_MOTION_BLUR "\xef\x83\x90" // U+f0d0 #define ICON_MS_MOTION_MODE "\xef\xa1\x82" // U+f842 #define ICON_MS_MOTION_PHOTOS_AUTO "\xef\x80\xba" // U+f03a #define ICON_MS_MOTION_PHOTOS_OFF "\xee\xa7\x80" // U+e9c0 #define ICON_MS_MOTION_PHOTOS_ON "\xee\xa7\x81" // U+e9c1 #define ICON_MS_MOTION_PHOTOS_PAUSE "\xef\x88\xa7" // U+f227 #define ICON_MS_MOTION_PHOTOS_PAUSED "\xef\x88\xa7" // U+f227 #define ICON_MS_MOTION_SENSOR_ACTIVE "\xee\x9e\x92" // U+e792 #define ICON_MS_MOTION_SENSOR_ALERT "\xee\x9e\x84" // U+e784 #define ICON_MS_MOTION_SENSOR_IDLE "\xee\x9e\x83" // U+e783 #define ICON_MS_MOTION_SENSOR_URGENT "\xee\x9e\x8e" // U+e78e #define ICON_MS_MOTORCYCLE "\xee\xa4\x9b" // U+e91b #define ICON_MS_MOUNTAIN_FLAG "\xef\x97\xa2" // U+f5e2 #define ICON_MS_MOUSE "\xee\x8c\xa3" // U+e323 #define ICON_MS_MOUSE_LOCK "\xef\x92\x90" // U+f490 #define ICON_MS_MOUSE_LOCK_OFF "\xef\x92\x8f" // U+f48f #define ICON_MS_MOVE "\xee\x9d\x80" // U+e740 #define ICON_MS_MOVE_DOWN "\xee\xad\xa1" // U+eb61 #define ICON_MS_MOVE_GROUP "\xef\x9c\x95" // U+f715 #define ICON_MS_MOVE_ITEM "\xef\x87\xbf" // U+f1ff #define ICON_MS_MOVE_LOCATION "\xee\x9d\x81" // U+e741 #define ICON_MS_MOVE_SELECTION_DOWN "\xef\x9c\x94" // U+f714 #define ICON_MS_MOVE_SELECTION_LEFT "\xef\x9c\x93" // U+f713 #define ICON_MS_MOVE_SELECTION_RIGHT "\xef\x9c\x92" // U+f712 #define ICON_MS_MOVE_SELECTION_UP "\xef\x9c\x91" // U+f711 #define ICON_MS_MOVE_TO_INBOX "\xee\x85\xa8" // U+e168 #define ICON_MS_MOVE_UP "\xee\xad\xa4" // U+eb64 #define ICON_MS_MOVED_LOCATION "\xee\x96\x94" // U+e594 #define ICON_MS_MOVIE "\xee\x90\x84" // U+e404 #define ICON_MS_MOVIE_CREATION "\xee\x90\x84" // U+e404 #define ICON_MS_MOVIE_EDIT "\xef\xa1\x80" // U+f840 #define ICON_MS_MOVIE_FILTER "\xee\x90\xba" // U+e43a #define ICON_MS_MOVIE_INFO "\xee\x80\xad" // U+e02d #define ICON_MS_MOVIE_OFF "\xef\x92\x99" // U+f499 #define ICON_MS_MOVING "\xee\x94\x81" // U+e501 #define ICON_MS_MOVING_BEDS "\xee\x9c\xbd" // U+e73d #define ICON_MS_MOVING_MINISTRY "\xee\x9c\xbe" // U+e73e #define ICON_MS_MP "\xee\xa7\x83" // U+e9c3 #define ICON_MS_MULTICOOKER "\xee\x8a\x93" // U+e293 #define ICON_MS_MULTILINE_CHART "\xee\x9b\x9f" // U+e6df #define ICON_MS_MULTIPLE_STOP "\xef\x86\xb9" // U+f1b9 #define ICON_MS_MUSEUM "\xee\xa8\xb6" // U+ea36 #define ICON_MS_MUSIC_CAST "\xee\xac\x9a" // U+eb1a #define ICON_MS_MUSIC_NOTE "\xee\x90\x85" // U+e405 #define ICON_MS_MUSIC_OFF "\xee\x91\x80" // U+e440 #define ICON_MS_MUSIC_VIDEO "\xee\x81\xa3" // U+e063 #define ICON_MS_MY_LOCATION "\xee\x95\x9c" // U+e55c #define ICON_MS_MYSTERY "\xef\x97\xa1" // U+f5e1 #define ICON_MS_NAT "\xee\xbd\x9c" // U+ef5c #define ICON_MS_NATURE "\xee\x90\x86" // U+e406 #define ICON_MS_NATURE_PEOPLE "\xee\x90\x87" // U+e407 #define ICON_MS_NAVIGATE_BEFORE "\xee\x90\x88" // U+e408 #define ICON_MS_NAVIGATE_NEXT "\xee\x90\x89" // U+e409 #define ICON_MS_NAVIGATION "\xee\x95\x9d" // U+e55d #define ICON_MS_NEAR_ME "\xee\x95\xa9" // U+e569 #define ICON_MS_NEAR_ME_DISABLED "\xef\x87\xaf" // U+f1ef #define ICON_MS_NEARBY "\xee\x9a\xb7" // U+e6b7 #define ICON_MS_NEARBY_ERROR "\xef\x80\xbb" // U+f03b #define ICON_MS_NEARBY_OFF "\xef\x80\xbc" // U+f03c #define ICON_MS_NEPHROLOGY "\xee\x84\x8d" // U+e10d #define ICON_MS_NEST_AUDIO "\xee\xae\xbf" // U+ebbf #define ICON_MS_NEST_CAM_FLOODLIGHT "\xef\xa2\xb7" // U+f8b7 #define ICON_MS_NEST_CAM_INDOOR "\xef\x84\x9e" // U+f11e #define ICON_MS_NEST_CAM_IQ "\xef\x84\x9f" // U+f11f #define ICON_MS_NEST_CAM_IQ_OUTDOOR "\xef\x84\xa0" // U+f120 #define ICON_MS_NEST_CAM_MAGNET_MOUNT "\xef\xa2\xb8" // U+f8b8 #define ICON_MS_NEST_CAM_OUTDOOR "\xef\x84\xa1" // U+f121 #define ICON_MS_NEST_CAM_STAND "\xef\xa2\xb9" // U+f8b9 #define ICON_MS_NEST_CAM_WALL_MOUNT "\xef\xa2\xba" // U+f8ba #define ICON_MS_NEST_CAM_WIRED_STAND "\xee\xb0\x96" // U+ec16 #define ICON_MS_NEST_CLOCK_FARSIGHT_ANALOG "\xef\xa2\xbb" // U+f8bb #define ICON_MS_NEST_CLOCK_FARSIGHT_DIGITAL "\xef\xa2\xbc" // U+f8bc #define ICON_MS_NEST_CONNECT "\xef\x84\xa2" // U+f122 #define ICON_MS_NEST_DETECT "\xef\x84\xa3" // U+f123 #define ICON_MS_NEST_DISPLAY "\xef\x84\xa4" // U+f124 #define ICON_MS_NEST_DISPLAY_MAX "\xef\x84\xa5" // U+f125 #define ICON_MS_NEST_DOORBELL_VISITOR "\xef\xa2\xbd" // U+f8bd #define ICON_MS_NEST_ECO_LEAF "\xef\xa2\xbe" // U+f8be #define ICON_MS_NEST_FARSIGHT_WEATHER "\xef\xa2\xbf" // U+f8bf #define ICON_MS_NEST_FOUND_SAVINGS "\xef\xa3\x80" // U+f8c0 #define ICON_MS_NEST_GALE_WIFI "\xef\x95\xb9" // U+f579 #define ICON_MS_NEST_HEAT_LINK_E "\xef\x84\xa6" // U+f126 #define ICON_MS_NEST_HEAT_LINK_GEN_3 "\xef\x84\xa7" // U+f127 #define ICON_MS_NEST_HELLO_DOORBELL "\xee\xa0\xac" // U+e82c #define ICON_MS_NEST_LOCATOR_TAG "\xef\xa3\x81" // U+f8c1 #define ICON_MS_NEST_MINI "\xee\x9e\x89" // U+e789 #define ICON_MS_NEST_MULTI_ROOM "\xef\xa3\x82" // U+f8c2 #define ICON_MS_NEST_PROTECT "\xee\x9a\x8e" // U+e68e #define ICON_MS_NEST_REMOTE "\xef\x97\x9b" // U+f5db #define ICON_MS_NEST_REMOTE_COMFORT_SENSOR "\xef\x84\xaa" // U+f12a #define ICON_MS_NEST_SECURE_ALARM "\xef\x84\xab" // U+f12b #define ICON_MS_NEST_SUNBLOCK "\xef\xa3\x83" // U+f8c3 #define ICON_MS_NEST_TAG "\xef\xa3\x81" // U+f8c1 #define ICON_MS_NEST_THERMOSTAT "\xee\x9a\x8f" // U+e68f #define ICON_MS_NEST_THERMOSTAT_E_EU "\xef\x84\xad" // U+f12d #define ICON_MS_NEST_THERMOSTAT_GEN_3 "\xef\x84\xae" // U+f12e #define ICON_MS_NEST_THERMOSTAT_SENSOR "\xef\x84\xaf" // U+f12f #define ICON_MS_NEST_THERMOSTAT_SENSOR_EU "\xef\x84\xb0" // U+f130 #define ICON_MS_NEST_THERMOSTAT_ZIRCONIUM_EU "\xef\x84\xb1" // U+f131 #define ICON_MS_NEST_TRUE_RADIANT "\xef\xa3\x84" // U+f8c4 #define ICON_MS_NEST_WAKE_ON_APPROACH "\xef\xa3\x85" // U+f8c5 #define ICON_MS_NEST_WAKE_ON_PRESS "\xef\xa3\x86" // U+f8c6 #define ICON_MS_NEST_WIFI_GALE "\xef\x84\xb2" // U+f132 #define ICON_MS_NEST_WIFI_MISTRAL "\xef\x84\xb3" // U+f133 #define ICON_MS_NEST_WIFI_POINT "\xef\x84\xb4" // U+f134 #define ICON_MS_NEST_WIFI_POINT_VENTO "\xef\x84\xb4" // U+f134 #define ICON_MS_NEST_WIFI_PRO "\xef\x95\xab" // U+f56b #define ICON_MS_NEST_WIFI_PRO_2 "\xef\x95\xaa" // U+f56a #define ICON_MS_NEST_WIFI_ROUTER "\xef\x84\xb3" // U+f133 #define ICON_MS_NETWORK_CELL "\xee\x86\xb9" // U+e1b9 #define ICON_MS_NETWORK_CHECK "\xee\x99\x80" // U+e640 #define ICON_MS_NETWORK_INTELLIGENCE_HISTORY "\xef\x97\xb6" // U+f5f6 #define ICON_MS_NETWORK_INTELLIGENCE_UPDATE "\xef\x97\xb5" // U+f5f5 #define ICON_MS_NETWORK_LOCKED "\xee\x98\x9a" // U+e61a #define ICON_MS_NETWORK_MANAGE "\xef\x9e\xab" // U+f7ab #define ICON_MS_NETWORK_NODE "\xef\x95\xae" // U+f56e #define ICON_MS_NETWORK_PING "\xee\xaf\x8a" // U+ebca #define ICON_MS_NETWORK_WIFI "\xee\x86\xba" // U+e1ba #define ICON_MS_NETWORK_WIFI_1_BAR "\xee\xaf\xa4" // U+ebe4 #define ICON_MS_NETWORK_WIFI_1_BAR_LOCKED "\xef\x96\x8f" // U+f58f #define ICON_MS_NETWORK_WIFI_2_BAR "\xee\xaf\x96" // U+ebd6 #define ICON_MS_NETWORK_WIFI_2_BAR_LOCKED "\xef\x96\x8e" // U+f58e #define ICON_MS_NETWORK_WIFI_3_BAR "\xee\xaf\xa1" // U+ebe1 #define ICON_MS_NETWORK_WIFI_3_BAR_LOCKED "\xef\x96\x8d" // U+f58d #define ICON_MS_NETWORK_WIFI_LOCKED "\xef\x94\xb2" // U+f532 #define ICON_MS_NEUROLOGY "\xee\x84\x8e" // U+e10e #define ICON_MS_NEW_LABEL "\xee\x98\x89" // U+e609 #define ICON_MS_NEW_RELEASES "\xee\xbd\xb6" // U+ef76 #define ICON_MS_NEW_WINDOW "\xef\x9c\x90" // U+f710 #define ICON_MS_NEWS "\xee\x80\xb2" // U+e032 #define ICON_MS_NEWSMODE "\xee\xbe\xad" // U+efad #define ICON_MS_NEWSPAPER "\xee\xae\x81" // U+eb81 #define ICON_MS_NEWSSTAND "\xee\xa7\x84" // U+e9c4 #define ICON_MS_NEXT_PLAN "\xee\xbd\x9d" // U+ef5d #define ICON_MS_NEXT_WEEK "\xee\x85\xaa" // U+e16a #define ICON_MS_NFC "\xee\x86\xbb" // U+e1bb #define ICON_MS_NIGHT_SHELTER "\xef\x87\xb1" // U+f1f1 #define ICON_MS_NIGHT_SIGHT_AUTO "\xef\x87\x97" // U+f1d7 #define ICON_MS_NIGHT_SIGHT_AUTO_OFF "\xef\x87\xb9" // U+f1f9 #define ICON_MS_NIGHT_SIGHT_MAX "\xef\x9b\x83" // U+f6c3 #define ICON_MS_NIGHTLIFE "\xee\xa9\xa2" // U+ea62 #define ICON_MS_NIGHTLIGHT "\xef\x80\xbd" // U+f03d #define ICON_MS_NIGHTLIGHT_ROUND "\xef\x80\xbd" // U+f03d #define ICON_MS_NIGHTS_STAY "\xee\xa9\x86" // U+ea46 #define ICON_MS_NO_ACCOUNTS "\xef\x80\xbe" // U+f03e #define ICON_MS_NO_ADULT_CONTENT "\xef\xa3\xbe" // U+f8fe #define ICON_MS_NO_BACKPACK "\xef\x88\xb7" // U+f237 #define ICON_MS_NO_CRASH "\xee\xaf\xb0" // U+ebf0 #define ICON_MS_NO_DRINKS "\xef\x86\xa5" // U+f1a5 #define ICON_MS_NO_ENCRYPTION "\xef\x80\xbf" // U+f03f #define ICON_MS_NO_ENCRYPTION_GMAILERRORRED "\xef\x80\xbf" // U+f03f #define ICON_MS_NO_FLASH "\xef\x86\xa6" // U+f1a6 #define ICON_MS_NO_FOOD "\xef\x86\xa7" // U+f1a7 #define ICON_MS_NO_LUGGAGE "\xef\x88\xbb" // U+f23b #define ICON_MS_NO_MEALS "\xef\x87\x96" // U+f1d6 #define ICON_MS_NO_MEETING_ROOM "\xee\xad\x8e" // U+eb4e #define ICON_MS_NO_PHOTOGRAPHY "\xef\x86\xa8" // U+f1a8 #define ICON_MS_NO_SIM "\xee\x87\x8e" // U+e1ce #define ICON_MS_NO_SOUND "\xee\x9c\x90" // U+e710 #define ICON_MS_NO_STROLLER "\xef\x86\xaf" // U+f1af #define ICON_MS_NO_TRANSFER "\xef\x87\x95" // U+f1d5 #define ICON_MS_NOISE_AWARE "\xee\xaf\xac" // U+ebec #define ICON_MS_NOISE_CONTROL_OFF "\xee\xaf\xb3" // U+ebf3 #define ICON_MS_NOISE_CONTROL_ON "\xef\xa2\xa8" // U+f8a8 #define ICON_MS_NORDIC_WALKING "\xee\x94\x8e" // U+e50e #define ICON_MS_NORTH "\xef\x87\xa0" // U+f1e0 #define ICON_MS_NORTH_EAST "\xef\x87\xa1" // U+f1e1 #define ICON_MS_NORTH_WEST "\xef\x87\xa2" // U+f1e2 #define ICON_MS_NOT_ACCESSIBLE "\xef\x83\xbe" // U+f0fe #define ICON_MS_NOT_ACCESSIBLE_FORWARD "\xef\x95\x8a" // U+f54a #define ICON_MS_NOT_INTERESTED "\xef\x82\x8c" // U+f08c #define ICON_MS_NOT_LISTED_LOCATION "\xee\x95\xb5" // U+e575 #define ICON_MS_NOT_STARTED "\xef\x83\x91" // U+f0d1 #define ICON_MS_NOTE "\xee\x99\xad" // U+e66d #define ICON_MS_NOTE_ADD "\xee\xa2\x9c" // U+e89c #define ICON_MS_NOTE_ALT "\xef\x81\x80" // U+f040 #define ICON_MS_NOTE_STACK "\xef\x95\xa2" // U+f562 #define ICON_MS_NOTE_STACK_ADD "\xef\x95\xa3" // U+f563 #define ICON_MS_NOTES "\xee\x89\xac" // U+e26c #define ICON_MS_NOTIFICATION_ADD "\xee\x8e\x99" // U+e399 #define ICON_MS_NOTIFICATION_IMPORTANT "\xee\x80\x84" // U+e004 #define ICON_MS_NOTIFICATION_MULTIPLE "\xee\x9b\x82" // U+e6c2 #define ICON_MS_NOTIFICATIONS "\xee\x9f\xb5" // U+e7f5 #define ICON_MS_NOTIFICATIONS_ACTIVE "\xee\x9f\xb7" // U+e7f7 #define ICON_MS_NOTIFICATIONS_NONE "\xee\x9f\xb5" // U+e7f5 #define ICON_MS_NOTIFICATIONS_OFF "\xee\x9f\xb6" // U+e7f6 #define ICON_MS_NOTIFICATIONS_PAUSED "\xee\x9f\xb8" // U+e7f8 #define ICON_MS_NOTIFICATIONS_UNREAD "\xef\x93\xbe" // U+f4fe #define ICON_MS_NUMBERS "\xee\xab\x87" // U+eac7 #define ICON_MS_NUTRITION "\xee\x84\x90" // U+e110 #define ICON_MS_ODS "\xee\x9b\xa8" // U+e6e8 #define ICON_MS_ODT "\xee\x9b\xa9" // U+e6e9 #define ICON_MS_OFFLINE_BOLT "\xee\xa4\xb2" // U+e932 #define ICON_MS_OFFLINE_PIN "\xee\xa4\x8a" // U+e90a #define ICON_MS_OFFLINE_PIN_OFF "\xef\x93\x90" // U+f4d0 #define ICON_MS_OFFLINE_SHARE "\xee\xa7\x85" // U+e9c5 #define ICON_MS_OIL_BARREL "\xee\xb0\x95" // U+ec15 #define ICON_MS_ON_DEVICE_TRAINING "\xee\xaf\xbd" // U+ebfd #define ICON_MS_ON_HUB_DEVICE "\xee\x9b\x83" // U+e6c3 #define ICON_MS_ONCOLOGY "\xee\x84\x94" // U+e114 #define ICON_MS_ONDEMAND_VIDEO "\xee\x98\xba" // U+e63a #define ICON_MS_ONLINE_PREDICTION "\xef\x83\xab" // U+f0eb #define ICON_MS_ONSEN "\xef\x9b\xb8" // U+f6f8 #define ICON_MS_OPACITY "\xee\xa4\x9c" // U+e91c #define ICON_MS_OPEN_IN_BROWSER "\xee\xa2\x9d" // U+e89d #define ICON_MS_OPEN_IN_FULL "\xef\x87\x8e" // U+f1ce #define ICON_MS_OPEN_IN_NEW "\xee\xa2\x9e" // U+e89e #define ICON_MS_OPEN_IN_NEW_DOWN "\xef\x9c\x8f" // U+f70f #define ICON_MS_OPEN_IN_NEW_OFF "\xee\x93\xb6" // U+e4f6 #define ICON_MS_OPEN_IN_PHONE "\xee\x9c\x82" // U+e702 #define ICON_MS_OPEN_JAM "\xee\xbe\xae" // U+efae #define ICON_MS_OPEN_RUN "\xef\x92\xb7" // U+f4b7 #define ICON_MS_OPEN_WITH "\xee\xa2\x9f" // U+e89f #define ICON_MS_OPHTHALMOLOGY "\xee\x84\x95" // U+e115 #define ICON_MS_ORAL_DISEASE "\xee\x84\x96" // U+e116 #define ICON_MS_ORDER_APPROVE "\xef\xa0\x92" // U+f812 #define ICON_MS_ORDER_PLAY "\xef\xa0\x91" // U+f811 #define ICON_MS_ORDERS "\xee\xac\x94" // U+eb14 #define ICON_MS_ORTHOPEDICS "\xef\xa2\x97" // U+f897 #define ICON_MS_OTHER_ADMISSION "\xee\x91\xbb" // U+e47b #define ICON_MS_OTHER_HOUSES "\xee\x96\x8c" // U+e58c #define ICON_MS_OUTBOUND "\xee\x87\x8a" // U+e1ca #define ICON_MS_OUTBOX "\xee\xbd\x9f" // U+ef5f #define ICON_MS_OUTBOX_ALT "\xee\xac\x97" // U+eb17 #define ICON_MS_OUTDOOR_GARDEN "\xee\x88\x85" // U+e205 #define ICON_MS_OUTDOOR_GRILL "\xee\xa9\x87" // U+ea47 #define ICON_MS_OUTGOING_MAIL "\xef\x83\x92" // U+f0d2 #define ICON_MS_OUTLET "\xef\x87\x94" // U+f1d4 #define ICON_MS_OUTLINED_FLAG "\xef\x83\x86" // U+f0c6 #define ICON_MS_OUTPATIENT "\xee\x84\x98" // U+e118 #define ICON_MS_OUTPATIENT_MED "\xee\x84\x99" // U+e119 #define ICON_MS_OUTPUT "\xee\xae\xbe" // U+ebbe #define ICON_MS_OUTPUT_CIRCLE "\xef\x9c\x8e" // U+f70e #define ICON_MS_OVEN "\xee\xa7\x87" // U+e9c7 #define ICON_MS_OVEN_GEN "\xee\xa1\x83" // U+e843 #define ICON_MS_OVERVIEW "\xee\x92\xa7" // U+e4a7 #define ICON_MS_OVERVIEW_KEY "\xef\x9f\x94" // U+f7d4 #define ICON_MS_OXYGEN_SATURATION "\xee\x93\x9e" // U+e4de #define ICON_MS_P2P "\xef\x94\xaa" // U+f52a #define ICON_MS_PACE "\xef\x9a\xb8" // U+f6b8 #define ICON_MS_PACEMAKER "\xee\x99\x96" // U+e656 #define ICON_MS_PACKAGE "\xee\x92\x8f" // U+e48f #define ICON_MS_PACKAGE_2 "\xef\x95\xa9" // U+f569 #define ICON_MS_PADDING "\xee\xa7\x88" // U+e9c8 #define ICON_MS_PAGE_CONTROL "\xee\x9c\xb1" // U+e731 #define ICON_MS_PAGE_INFO "\xef\x98\x94" // U+f614 #define ICON_MS_PAGELESS "\xef\x94\x89" // U+f509 #define ICON_MS_PAGES "\xee\x9f\xb9" // U+e7f9 #define ICON_MS_PAGEVIEW "\xee\xa2\xa0" // U+e8a0 #define ICON_MS_PAID "\xef\x81\x81" // U+f041 #define ICON_MS_PALETTE "\xee\x90\x8a" // U+e40a #define ICON_MS_PALLET "\xef\xa1\xaa" // U+f86a #define ICON_MS_PAN_TOOL "\xee\xa4\xa5" // U+e925 #define ICON_MS_PAN_TOOL_ALT "\xee\xae\xb9" // U+ebb9 #define ICON_MS_PAN_ZOOM "\xef\x99\x95" // U+f655 #define ICON_MS_PANORAMA "\xee\x90\x8b" // U+e40b #define ICON_MS_PANORAMA_FISH_EYE "\xee\x90\x8c" // U+e40c #define ICON_MS_PANORAMA_HORIZONTAL "\xee\x90\x8d" // U+e40d #define ICON_MS_PANORAMA_PHOTOSPHERE "\xee\xa7\x89" // U+e9c9 #define ICON_MS_PANORAMA_VERTICAL "\xee\x90\x8e" // U+e40e #define ICON_MS_PANORAMA_WIDE_ANGLE "\xee\x90\x8f" // U+e40f #define ICON_MS_PARAGLIDING "\xee\x94\x8f" // U+e50f #define ICON_MS_PARK "\xee\xa9\xa3" // U+ea63 #define ICON_MS_PARTLY_CLOUDY_DAY "\xef\x85\xb2" // U+f172 #define ICON_MS_PARTLY_CLOUDY_NIGHT "\xef\x85\xb4" // U+f174 #define ICON_MS_PARTNER_EXCHANGE "\xef\x9f\xb9" // U+f7f9 #define ICON_MS_PARTNER_REPORTS "\xee\xbe\xaf" // U+efaf #define ICON_MS_PARTY_MODE "\xee\x9f\xba" // U+e7fa #define ICON_MS_PASSKEY "\xef\xa1\xbf" // U+f87f #define ICON_MS_PASSWORD "\xef\x81\x82" // U+f042 #define ICON_MS_PASSWORD_2 "\xef\x92\xa9" // U+f4a9 #define ICON_MS_PASSWORD_2_OFF "\xef\x92\xa8" // U+f4a8 #define ICON_MS_PATIENT_LIST "\xee\x99\x93" // U+e653 #define ICON_MS_PATTERN "\xef\x81\x83" // U+f043 #define ICON_MS_PAUSE "\xee\x80\xb4" // U+e034 #define ICON_MS_PAUSE_CIRCLE "\xee\x86\xa2" // U+e1a2 #define ICON_MS_PAUSE_CIRCLE_FILLED "\xee\x86\xa2" // U+e1a2 #define ICON_MS_PAUSE_CIRCLE_OUTLINE "\xee\x86\xa2" // U+e1a2 #define ICON_MS_PAUSE_PRESENTATION "\xee\x83\xaa" // U+e0ea #define ICON_MS_PAYMENT "\xee\xa2\xa1" // U+e8a1 #define ICON_MS_PAYMENTS "\xee\xbd\xa3" // U+ef63 #define ICON_MS_PEDAL_BIKE "\xee\xac\xa9" // U+eb29 #define ICON_MS_PEDIATRICS "\xee\x84\x9d" // U+e11d #define ICON_MS_PEN_SIZE_1 "\xef\x9d\x95" // U+f755 #define ICON_MS_PEN_SIZE_2 "\xef\x9d\x94" // U+f754 #define ICON_MS_PEN_SIZE_3 "\xef\x9d\x93" // U+f753 #define ICON_MS_PEN_SIZE_4 "\xef\x9d\x92" // U+f752 #define ICON_MS_PEN_SIZE_5 "\xef\x9d\x91" // U+f751 #define ICON_MS_PENDING "\xee\xbd\xa4" // U+ef64 #define ICON_MS_PENDING_ACTIONS "\xef\x86\xbb" // U+f1bb #define ICON_MS_PENTAGON "\xee\xad\x90" // U+eb50 #define ICON_MS_PEOPLE "\xee\xa8\xa1" // U+ea21 #define ICON_MS_PEOPLE_ALT "\xee\xa8\xa1" // U+ea21 #define ICON_MS_PEOPLE_OUTLINE "\xee\xa8\xa1" // U+ea21 #define ICON_MS_PERCENT "\xee\xad\x98" // U+eb58 #define ICON_MS_PERFORMANCE_MAX "\xee\x94\x9a" // U+e51a #define ICON_MS_PERGOLA "\xee\x88\x83" // U+e203 #define ICON_MS_PERM_CAMERA_MIC "\xee\xa2\xa2" // U+e8a2 #define ICON_MS_PERM_CONTACT_CALENDAR "\xee\xa2\xa3" // U+e8a3 #define ICON_MS_PERM_DATA_SETTING "\xee\xa2\xa4" // U+e8a4 #define ICON_MS_PERM_DEVICE_INFORMATION "\xee\xa2\xa5" // U+e8a5 #define ICON_MS_PERM_IDENTITY "\xef\x83\x93" // U+f0d3 #define ICON_MS_PERM_MEDIA "\xee\xa2\xa7" // U+e8a7 #define ICON_MS_PERM_PHONE_MSG "\xee\xa2\xa8" // U+e8a8 #define ICON_MS_PERM_SCAN_WIFI "\xee\xa2\xa9" // U+e8a9 #define ICON_MS_PERSON "\xef\x83\x93" // U+f0d3 #define ICON_MS_PERSON_2 "\xef\xa3\xa4" // U+f8e4 #define ICON_MS_PERSON_3 "\xef\xa3\xa5" // U+f8e5 #define ICON_MS_PERSON_4 "\xef\xa3\xa6" // U+f8e6 #define ICON_MS_PERSON_ADD "\xee\xa9\x8d" // U+ea4d #define ICON_MS_PERSON_ADD_ALT "\xee\xa9\x8d" // U+ea4d #define ICON_MS_PERSON_ADD_DISABLED "\xee\xa7\x8b" // U+e9cb #define ICON_MS_PERSON_ALERT "\xef\x95\xa7" // U+f567 #define ICON_MS_PERSON_APRON "\xef\x96\xa3" // U+f5a3 #define ICON_MS_PERSON_BOOK "\xef\x97\xa8" // U+f5e8 #define ICON_MS_PERSON_CANCEL "\xef\x95\xa6" // U+f566 #define ICON_MS_PERSON_CELEBRATE "\xef\x9f\xbe" // U+f7fe #define ICON_MS_PERSON_CHECK "\xef\x95\xa5" // U+f565 #define ICON_MS_PERSON_EDIT "\xef\x93\xba" // U+f4fa #define ICON_MS_PERSON_FILLED "\xef\x83\x93" // U+f0d3 #define ICON_MS_PERSON_OFF "\xee\x94\x90" // U+e510 #define ICON_MS_PERSON_OUTLINE "\xef\x83\x93" // U+f0d3 #define ICON_MS_PERSON_PIN "\xee\x95\x9a" // U+e55a #define ICON_MS_PERSON_PIN_CIRCLE "\xee\x95\xaa" // U+e56a #define ICON_MS_PERSON_PLAY "\xef\x9f\xbd" // U+f7fd #define ICON_MS_PERSON_RAISED_HAND "\xef\x96\x9a" // U+f59a #define ICON_MS_PERSON_REMOVE "\xee\xbd\xa6" // U+ef66 #define ICON_MS_PERSON_SEARCH "\xef\x84\x86" // U+f106 #define ICON_MS_PERSONAL_BAG "\xee\xac\x8e" // U+eb0e #define ICON_MS_PERSONAL_BAG_OFF "\xee\xac\x8f" // U+eb0f #define ICON_MS_PERSONAL_BAG_QUESTION "\xee\xac\x90" // U+eb10 #define ICON_MS_PERSONAL_INJURY "\xee\x9b\x9a" // U+e6da #define ICON_MS_PERSONAL_PLACES "\xee\x9c\x83" // U+e703 #define ICON_MS_PERSONAL_VIDEO "\xee\x98\xbb" // U+e63b #define ICON_MS_PEST_CONTROL "\xef\x83\xba" // U+f0fa #define ICON_MS_PEST_CONTROL_RODENT "\xef\x83\xbd" // U+f0fd #define ICON_MS_PET_SUPPLIES "\xee\xbe\xb1" // U+efb1 #define ICON_MS_PETS "\xee\xa4\x9d" // U+e91d #define ICON_MS_PHISHING "\xee\xab\x97" // U+ead7 #define ICON_MS_PHONE "\xef\x83\x94" // U+f0d4 #define ICON_MS_PHONE_ALT "\xef\x83\x94" // U+f0d4 #define ICON_MS_PHONE_ANDROID "\xee\x8c\xa4" // U+e324 #define ICON_MS_PHONE_BLUETOOTH_SPEAKER "\xee\x98\x9b" // U+e61b #define ICON_MS_PHONE_CALLBACK "\xee\x99\x89" // U+e649 #define ICON_MS_PHONE_DISABLED "\xee\xa7\x8c" // U+e9cc #define ICON_MS_PHONE_ENABLED "\xee\xa7\x8d" // U+e9cd #define ICON_MS_PHONE_FORWARDED "\xee\x98\x9c" // U+e61c #define ICON_MS_PHONE_IN_TALK "\xee\x98\x9d" // U+e61d #define ICON_MS_PHONE_IPHONE "\xee\x8c\xa5" // U+e325 #define ICON_MS_PHONE_LOCKED "\xee\x98\x9e" // U+e61e #define ICON_MS_PHONE_MISSED "\xee\x98\x9f" // U+e61f #define ICON_MS_PHONE_PAUSED "\xee\x98\xa0" // U+e620 #define ICON_MS_PHONELINK "\xee\x8c\xa6" // U+e326 #define ICON_MS_PHONELINK_ERASE "\xee\x83\x9b" // U+e0db #define ICON_MS_PHONELINK_LOCK "\xee\x83\x9c" // U+e0dc #define ICON_MS_PHONELINK_OFF "\xee\x8c\xa7" // U+e327 #define ICON_MS_PHONELINK_RING "\xee\x83\x9d" // U+e0dd #define ICON_MS_PHONELINK_RING_OFF "\xef\x9e\xaa" // U+f7aa #define ICON_MS_PHONELINK_SETUP "\xee\xbd\x81" // U+ef41 #define ICON_MS_PHOTO "\xee\x90\xb2" // U+e432 #define ICON_MS_PHOTO_ALBUM "\xee\x90\x91" // U+e411 #define ICON_MS_PHOTO_AUTO_MERGE "\xef\x94\xb0" // U+f530 #define ICON_MS_PHOTO_CAMERA "\xee\x90\x92" // U+e412 #define ICON_MS_PHOTO_CAMERA_BACK "\xee\xbd\xa8" // U+ef68 #define ICON_MS_PHOTO_CAMERA_FRONT "\xee\xbd\xa9" // U+ef69 #define ICON_MS_PHOTO_FILTER "\xee\x90\xbb" // U+e43b #define ICON_MS_PHOTO_FRAME "\xef\x83\x99" // U+f0d9 #define ICON_MS_PHOTO_LIBRARY "\xee\x90\x93" // U+e413 #define ICON_MS_PHOTO_PRINTS "\xee\xbe\xb2" // U+efb2 #define ICON_MS_PHOTO_SIZE_SELECT_ACTUAL "\xee\x90\xb2" // U+e432 #define ICON_MS_PHOTO_SIZE_SELECT_LARGE "\xee\x90\xb3" // U+e433 #define ICON_MS_PHOTO_SIZE_SELECT_SMALL "\xee\x90\xb4" // U+e434 #define ICON_MS_PHP "\xee\xae\x8f" // U+eb8f #define ICON_MS_PHYSICAL_THERAPY "\xee\x84\x9e" // U+e11e #define ICON_MS_PIANO "\xee\x94\xa1" // U+e521 #define ICON_MS_PIANO_OFF "\xee\x94\xa0" // U+e520 #define ICON_MS_PICTURE_AS_PDF "\xee\x90\x95" // U+e415 #define ICON_MS_PICTURE_IN_PICTURE "\xee\xa2\xaa" // U+e8aa #define ICON_MS_PICTURE_IN_PICTURE_ALT "\xee\xa4\x91" // U+e911 #define ICON_MS_PICTURE_IN_PICTURE_CENTER "\xef\x95\x90" // U+f550 #define ICON_MS_PICTURE_IN_PICTURE_LARGE "\xef\x95\x8f" // U+f54f #define ICON_MS_PICTURE_IN_PICTURE_MEDIUM "\xef\x95\x8e" // U+f54e #define ICON_MS_PICTURE_IN_PICTURE_MOBILE "\xef\x94\x97" // U+f517 #define ICON_MS_PICTURE_IN_PICTURE_OFF "\xef\x94\xaf" // U+f52f #define ICON_MS_PICTURE_IN_PICTURE_SMALL "\xef\x95\x8d" // U+f54d #define ICON_MS_PIE_CHART "\xef\x83\x9a" // U+f0da #define ICON_MS_PIE_CHART_FILLED "\xef\x83\x9a" // U+f0da #define ICON_MS_PIE_CHART_OUTLINE "\xef\x83\x9a" // U+f0da #define ICON_MS_PIE_CHART_OUTLINED "\xef\x83\x9a" // U+f0da #define ICON_MS_PILL "\xee\x84\x9f" // U+e11f #define ICON_MS_PILL_OFF "\xef\xa0\x89" // U+f809 #define ICON_MS_PIN "\xef\x81\x85" // U+f045 #define ICON_MS_PIN_DROP "\xee\x95\x9e" // U+e55e #define ICON_MS_PIN_END "\xee\x9d\xa7" // U+e767 #define ICON_MS_PIN_INVOKE "\xee\x9d\xa3" // U+e763 #define ICON_MS_PINCH "\xee\xac\xb8" // U+eb38 #define ICON_MS_PINCH_ZOOM_IN "\xef\x87\xba" // U+f1fa #define ICON_MS_PINCH_ZOOM_OUT "\xef\x87\xbb" // U+f1fb #define ICON_MS_PIP "\xef\x99\x8d" // U+f64d #define ICON_MS_PIP_EXIT "\xef\x9c\x8d" // U+f70d #define ICON_MS_PIVOT_TABLE_CHART "\xee\xa7\x8e" // U+e9ce #define ICON_MS_PLACE "\xef\x87\x9b" // U+f1db #define ICON_MS_PLACE_ITEM "\xef\x87\xb0" // U+f1f0 #define ICON_MS_PLAGIARISM "\xee\xa9\x9a" // U+ea5a #define ICON_MS_PLANNER_BANNER_AD_PT "\xee\x9a\x92" // U+e692 #define ICON_MS_PLANNER_REVIEW "\xee\x9a\x94" // U+e694 #define ICON_MS_PLAY_ARROW "\xee\x80\xb7" // U+e037 #define ICON_MS_PLAY_CIRCLE "\xee\x87\x84" // U+e1c4 #define ICON_MS_PLAY_DISABLED "\xee\xbd\xaa" // U+ef6a #define ICON_MS_PLAY_FOR_WORK "\xee\xa4\x86" // U+e906 #define ICON_MS_PLAY_LESSON "\xef\x81\x87" // U+f047 #define ICON_MS_PLAY_MUSIC "\xee\x9b\xae" // U+e6ee #define ICON_MS_PLAY_PAUSE "\xef\x84\xb7" // U+f137 #define ICON_MS_PLAY_SHAPES "\xef\x9f\xbc" // U+f7fc #define ICON_MS_PLAYING_CARDS "\xef\x97\x9c" // U+f5dc #define ICON_MS_PLAYLIST_ADD "\xee\x80\xbb" // U+e03b #define ICON_MS_PLAYLIST_ADD_CHECK "\xee\x81\xa5" // U+e065 #define ICON_MS_PLAYLIST_ADD_CHECK_CIRCLE "\xee\x9f\xa6" // U+e7e6 #define ICON_MS_PLAYLIST_ADD_CIRCLE "\xee\x9f\xa5" // U+e7e5 #define ICON_MS_PLAYLIST_PLAY "\xee\x81\x9f" // U+e05f #define ICON_MS_PLAYLIST_REMOVE "\xee\xae\x80" // U+eb80 #define ICON_MS_PLUMBING "\xef\x84\x87" // U+f107 #define ICON_MS_PLUS_ONE "\xee\xa0\x80" // U+e800 #define ICON_MS_PODCASTS "\xef\x81\x88" // U+f048 #define ICON_MS_PODIATRY "\xee\x84\xa0" // U+e120 #define ICON_MS_PODIUM "\xef\x9f\xbb" // U+f7fb #define ICON_MS_POINT_OF_SALE "\xef\x85\xbe" // U+f17e #define ICON_MS_POINT_SCAN "\xef\x9c\x8c" // U+f70c #define ICON_MS_POKER_CHIP "\xef\x92\x9b" // U+f49b #define ICON_MS_POLICY "\xee\xa8\x97" // U+ea17 #define ICON_MS_POLL "\xef\x83\x8c" // U+f0cc #define ICON_MS_POLYLINE "\xee\xae\xbb" // U+ebbb #define ICON_MS_POLYMER "\xee\xa2\xab" // U+e8ab #define ICON_MS_POOL "\xee\xad\x88" // U+eb48 #define ICON_MS_PORTABLE_WIFI_OFF "\xef\x82\x87" // U+f087 #define ICON_MS_PORTRAIT "\xee\xa1\x91" // U+e851 #define ICON_MS_POSITION_BOTTOM_LEFT "\xef\x9c\x8b" // U+f70b #define ICON_MS_POSITION_BOTTOM_RIGHT "\xef\x9c\x8a" // U+f70a #define ICON_MS_POSITION_TOP_RIGHT "\xef\x9c\x89" // U+f709 #define ICON_MS_POST "\xee\x9c\x85" // U+e705 #define ICON_MS_POST_ADD "\xee\xa8\xa0" // U+ea20 #define ICON_MS_POTTED_PLANT "\xef\xa2\xaa" // U+f8aa #define ICON_MS_POWER "\xee\x98\xbc" // U+e63c #define ICON_MS_POWER_INPUT "\xee\x8c\xb6" // U+e336 #define ICON_MS_POWER_OFF "\xee\x99\x86" // U+e646 #define ICON_MS_POWER_ROUNDED "\xef\xa3\x87" // U+f8c7 #define ICON_MS_POWER_SETTINGS_NEW "\xef\xa3\x87" // U+f8c7 #define ICON_MS_PRAYER_TIMES "\xef\xa0\xb8" // U+f838 #define ICON_MS_PRECISION_MANUFACTURING "\xef\x81\x89" // U+f049 #define ICON_MS_PREGNANCY "\xef\x97\xb1" // U+f5f1 #define ICON_MS_PREGNANT_WOMAN "\xef\x97\xb1" // U+f5f1 #define ICON_MS_PRELIMINARY "\xee\x9f\x98" // U+e7d8 #define ICON_MS_PRESCRIPTIONS "\xee\x84\xa1" // U+e121 #define ICON_MS_PRESENT_TO_ALL "\xee\x83\x9f" // U+e0df #define ICON_MS_PREVIEW "\xef\x87\x85" // U+f1c5 #define ICON_MS_PREVIEW_OFF "\xef\x9e\xaf" // U+f7af #define ICON_MS_PRICE_CHANGE "\xef\x81\x8a" // U+f04a #define ICON_MS_PRICE_CHECK "\xef\x81\x8b" // U+f04b #define ICON_MS_PRINT "\xee\xa2\xad" // U+e8ad #define ICON_MS_PRINT_ADD "\xef\x9e\xa2" // U+f7a2 #define ICON_MS_PRINT_CONNECT "\xef\x9e\xa1" // U+f7a1 #define ICON_MS_PRINT_DISABLED "\xee\xa7\x8f" // U+e9cf #define ICON_MS_PRINT_ERROR "\xef\x9e\xa0" // U+f7a0 #define ICON_MS_PRINT_LOCK "\xef\x99\x91" // U+f651 #define ICON_MS_PRIORITY "\xee\x86\x9f" // U+e19f #define ICON_MS_PRIORITY_HIGH "\xee\x99\x85" // U+e645 #define ICON_MS_PRIVACY "\xef\x85\x88" // U+f148 #define ICON_MS_PRIVACY_TIP "\xef\x83\x9c" // U+f0dc #define ICON_MS_PRIVATE_CONNECTIVITY "\xee\x9d\x84" // U+e744 #define ICON_MS_PROBLEM "\xee\x84\xa2" // U+e122 #define ICON_MS_PROCEDURE "\xee\x99\x91" // U+e651 #define ICON_MS_PROCESS_CHART "\xef\xa1\x95" // U+f855 #define ICON_MS_PRODUCTION_QUANTITY_LIMITS "\xee\x87\x91" // U+e1d1 #define ICON_MS_PRODUCTIVITY "\xee\x8a\x96" // U+e296 #define ICON_MS_PROGRESS_ACTIVITY "\xee\xa7\x90" // U+e9d0 #define ICON_MS_PROMPT_SUGGESTION "\xef\x93\xb6" // U+f4f6 #define ICON_MS_PROPANE "\xee\xb0\x94" // U+ec14 #define ICON_MS_PROPANE_TANK "\xee\xb0\x93" // U+ec13 #define ICON_MS_PSYCHIATRY "\xee\x84\xa3" // U+e123 #define ICON_MS_PSYCHOLOGY "\xee\xa9\x8a" // U+ea4a #define ICON_MS_PSYCHOLOGY_ALT "\xef\xa3\xaa" // U+f8ea #define ICON_MS_PUBLIC "\xee\xa0\x8b" // U+e80b #define ICON_MS_PUBLIC_OFF "\xef\x87\x8a" // U+f1ca #define ICON_MS_PUBLISH "\xee\x89\x95" // U+e255 #define ICON_MS_PUBLISHED_WITH_CHANGES "\xef\x88\xb2" // U+f232 #define ICON_MS_PULMONOLOGY "\xee\x84\xa4" // U+e124 #define ICON_MS_PULSE_ALERT "\xef\x94\x81" // U+f501 #define ICON_MS_PUNCH_CLOCK "\xee\xaa\xa8" // U+eaa8 #define ICON_MS_PUSH_PIN "\xef\x84\x8d" // U+f10d #define ICON_MS_QR_CODE "\xee\xbd\xab" // U+ef6b #define ICON_MS_QR_CODE_2 "\xee\x80\x8a" // U+e00a #define ICON_MS_QR_CODE_2_ADD "\xef\x99\x98" // U+f658 #define ICON_MS_QR_CODE_SCANNER "\xef\x88\x86" // U+f206 #define ICON_MS_QUERY_BUILDER "\xee\xbf\x96" // U+efd6 #define ICON_MS_QUERY_STATS "\xee\x93\xbc" // U+e4fc #define ICON_MS_QUESTION_ANSWER "\xee\xa2\xaf" // U+e8af #define ICON_MS_QUESTION_EXCHANGE "\xef\x9f\xb3" // U+f7f3 #define ICON_MS_QUESTION_MARK "\xee\xae\x8b" // U+eb8b #define ICON_MS_QUEUE "\xee\x80\xbc" // U+e03c #define ICON_MS_QUEUE_MUSIC "\xee\x80\xbd" // U+e03d #define ICON_MS_QUEUE_PLAY_NEXT "\xee\x81\xa6" // U+e066 #define ICON_MS_QUICK_PHRASES "\xee\x9f\x91" // U+e7d1 #define ICON_MS_QUICK_REFERENCE "\xee\x91\xae" // U+e46e #define ICON_MS_QUICK_REFERENCE_ALL "\xef\xa0\x81" // U+f801 #define ICON_MS_QUICK_REORDER "\xee\xac\x95" // U+eb15 #define ICON_MS_QUICKREPLY "\xee\xbd\xac" // U+ef6c #define ICON_MS_QUIET_TIME "\xee\x87\xb9" // U+e1f9 #define ICON_MS_QUIET_TIME_ACTIVE "\xee\x8a\x91" // U+e291 #define ICON_MS_QUIZ "\xef\x81\x8c" // U+f04c #define ICON_MS_R_MOBILEDATA "\xef\x81\x8d" // U+f04d #define ICON_MS_RADAR "\xef\x81\x8e" // U+f04e #define ICON_MS_RADIO "\xee\x80\xbe" // U+e03e #define ICON_MS_RADIO_BUTTON_CHECKED "\xee\xa0\xb7" // U+e837 #define ICON_MS_RADIO_BUTTON_PARTIAL "\xef\x95\xa0" // U+f560 #define ICON_MS_RADIO_BUTTON_UNCHECKED "\xee\xa0\xb6" // U+e836 #define ICON_MS_RADIOLOGY "\xee\x84\xa5" // U+e125 #define ICON_MS_RAILWAY_ALERT "\xee\xa7\x91" // U+e9d1 #define ICON_MS_RAINY "\xef\x85\xb6" // U+f176 #define ICON_MS_RAINY_HEAVY "\xef\x98\x9f" // U+f61f #define ICON_MS_RAINY_LIGHT "\xef\x98\x9e" // U+f61e #define ICON_MS_RAINY_SNOW "\xef\x98\x9d" // U+f61d #define ICON_MS_RAMEN_DINING "\xee\xa9\xa4" // U+ea64 #define ICON_MS_RAMP_LEFT "\xee\xae\x9c" // U+eb9c #define ICON_MS_RAMP_RIGHT "\xee\xae\x96" // U+eb96 #define ICON_MS_RANGE_HOOD "\xee\x87\xaa" // U+e1ea #define ICON_MS_RATE_REVIEW "\xee\x95\xa0" // U+e560 #define ICON_MS_RAVEN "\xef\x95\x95" // U+f555 #define ICON_MS_RAW_OFF "\xef\x81\x8f" // U+f04f #define ICON_MS_RAW_ON "\xef\x81\x90" // U+f050 #define ICON_MS_READ_MORE "\xee\xbd\xad" // U+ef6d #define ICON_MS_READINESS_SCORE "\xef\x9b\x9d" // U+f6dd #define ICON_MS_REAL_ESTATE_AGENT "\xee\x9c\xba" // U+e73a #define ICON_MS_REAR_CAMERA "\xef\x9b\x82" // U+f6c2 #define ICON_MS_REBASE "\xef\xa1\x85" // U+f845 #define ICON_MS_REBASE_EDIT "\xef\xa1\x86" // U+f846 #define ICON_MS_RECEIPT "\xee\xa2\xb0" // U+e8b0 #define ICON_MS_RECEIPT_LONG "\xee\xbd\xae" // U+ef6e #define ICON_MS_RECENT_ACTORS "\xee\x80\xbf" // U+e03f #define ICON_MS_RECENT_PATIENT "\xef\xa0\x88" // U+f808 #define ICON_MS_RECENTER "\xef\x93\x80" // U+f4c0 #define ICON_MS_RECOMMEND "\xee\xa7\x92" // U+e9d2 #define ICON_MS_RECORD_VOICE_OVER "\xee\xa4\x9f" // U+e91f #define ICON_MS_RECTANGLE "\xee\xad\x94" // U+eb54 #define ICON_MS_RECYCLING "\xee\x9d\xa0" // U+e760 #define ICON_MS_REDEEM "\xee\xa3\xb6" // U+e8f6 #define ICON_MS_REDO "\xee\x85\x9a" // U+e15a #define ICON_MS_REDUCE_CAPACITY "\xef\x88\x9c" // U+f21c #define ICON_MS_REFRESH "\xee\x97\x95" // U+e5d5 #define ICON_MS_REGULAR_EXPRESSION "\xef\x9d\x90" // U+f750 #define ICON_MS_RELAX "\xef\x9b\x9c" // U+f6dc #define ICON_MS_RELEASE_ALERT "\xef\x99\x94" // U+f654 #define ICON_MS_REMEMBER_ME "\xef\x81\x91" // U+f051 #define ICON_MS_REMINDER "\xee\x9b\x86" // U+e6c6 #define ICON_MS_REMINDERS_ALT "\xee\x9b\x86" // U+e6c6 #define ICON_MS_REMOTE_GEN "\xee\xa0\xbe" // U+e83e #define ICON_MS_REMOVE "\xee\x85\x9b" // U+e15b #define ICON_MS_REMOVE_CIRCLE "\xef\x82\x8f" // U+f08f #define ICON_MS_REMOVE_CIRCLE_OUTLINE "\xef\x82\x8f" // U+f08f #define ICON_MS_REMOVE_DONE "\xee\xa7\x93" // U+e9d3 #define ICON_MS_REMOVE_FROM_QUEUE "\xee\x81\xa7" // U+e067 #define ICON_MS_REMOVE_MODERATOR "\xee\xa7\x94" // U+e9d4 #define ICON_MS_REMOVE_RED_EYE "\xee\xa3\xb4" // U+e8f4 #define ICON_MS_REMOVE_ROAD "\xee\xaf\xbc" // U+ebfc #define ICON_MS_REMOVE_SELECTION "\xee\xa7\x95" // U+e9d5 #define ICON_MS_REMOVE_SHOPPING_CART "\xee\xa4\xa8" // U+e928 #define ICON_MS_REOPEN_WINDOW "\xef\x9c\x88" // U+f708 #define ICON_MS_REORDER "\xee\xa3\xbe" // U+e8fe #define ICON_MS_REPARTITION "\xef\xa3\xa8" // U+f8e8 #define ICON_MS_REPEAT "\xee\x81\x80" // U+e040 #define ICON_MS_REPEAT_ON "\xee\xa7\x96" // U+e9d6 #define ICON_MS_REPEAT_ONE "\xee\x81\x81" // U+e041 #define ICON_MS_REPEAT_ONE_ON "\xee\xa7\x97" // U+e9d7 #define ICON_MS_REPLAY "\xee\x81\x82" // U+e042 #define ICON_MS_REPLAY_10 "\xee\x81\x99" // U+e059 #define ICON_MS_REPLAY_30 "\xee\x81\x9a" // U+e05a #define ICON_MS_REPLAY_5 "\xee\x81\x9b" // U+e05b #define ICON_MS_REPLAY_CIRCLE_FILLED "\xee\xa7\x98" // U+e9d8 #define ICON_MS_REPLY "\xee\x85\x9e" // U+e15e #define ICON_MS_REPLY_ALL "\xee\x85\x9f" // U+e15f #define ICON_MS_REPORT "\xef\x81\x92" // U+f052 #define ICON_MS_REPORT_GMAILERRORRED "\xef\x81\x92" // U+f052 #define ICON_MS_REPORT_OFF "\xee\x85\xb0" // U+e170 #define ICON_MS_REPORT_PROBLEM "\xef\x82\x83" // U+f083 #define ICON_MS_REQUEST_PAGE "\xef\x88\xac" // U+f22c #define ICON_MS_REQUEST_QUOTE "\xef\x86\xb6" // U+f1b6 #define ICON_MS_RESET_IMAGE "\xef\xa0\xa4" // U+f824 #define ICON_MS_RESET_TV "\xee\xa7\x99" // U+e9d9 #define ICON_MS_RESET_WRENCH "\xef\x95\xac" // U+f56c #define ICON_MS_RESIZE "\xef\x9c\x87" // U+f707 #define ICON_MS_RESPIRATORY_RATE "\xee\x84\xa7" // U+e127 #define ICON_MS_RESPONSIVE_LAYOUT "\xee\xa7\x9a" // U+e9da #define ICON_MS_RESTART_ALT "\xef\x81\x93" // U+f053 #define ICON_MS_RESTAURANT "\xee\x95\xac" // U+e56c #define ICON_MS_RESTAURANT_MENU "\xee\x95\xa1" // U+e561 #define ICON_MS_RESTORE "\xee\xa2\xb3" // U+e8b3 #define ICON_MS_RESTORE_FROM_TRASH "\xee\xa4\xb8" // U+e938 #define ICON_MS_RESTORE_PAGE "\xee\xa4\xa9" // U+e929 #define ICON_MS_RESUME "\xef\x9f\x90" // U+f7d0 #define ICON_MS_REVIEWS "\xef\x81\xbc" // U+f07c #define ICON_MS_REWARDED_ADS "\xee\xbe\xb6" // U+efb6 #define ICON_MS_RHEUMATOLOGY "\xee\x84\xa8" // U+e128 #define ICON_MS_RIB_CAGE "\xef\xa2\x98" // U+f898 #define ICON_MS_RICE_BOWL "\xef\x87\xb5" // U+f1f5 #define ICON_MS_RIGHT_CLICK "\xef\x9c\x86" // U+f706 #define ICON_MS_RIGHT_PANEL_CLOSE "\xef\x9c\x85" // U+f705 #define ICON_MS_RIGHT_PANEL_OPEN "\xef\x9c\x84" // U+f704 #define ICON_MS_RING_VOLUME "\xef\x83\x9d" // U+f0dd #define ICON_MS_RING_VOLUME_FILLED "\xef\x83\x9d" // U+f0dd #define ICON_MS_RIPPLES "\xee\xa7\x9b" // U+e9db #define ICON_MS_ROBOT "\xef\xa2\x82" // U+f882 #define ICON_MS_ROBOT_2 "\xef\x97\x90" // U+f5d0 #define ICON_MS_ROCKET "\xee\xae\xa5" // U+eba5 #define ICON_MS_ROCKET_LAUNCH "\xee\xae\x9b" // U+eb9b #define ICON_MS_ROLLER_SHADES "\xee\xb0\x92" // U+ec12 #define ICON_MS_ROLLER_SHADES_CLOSED "\xee\xb0\x91" // U+ec11 #define ICON_MS_ROLLER_SKATING "\xee\xaf\x8d" // U+ebcd #define ICON_MS_ROOFING "\xef\x88\x81" // U+f201 #define ICON_MS_ROOM "\xef\x87\x9b" // U+f1db #define ICON_MS_ROOM_PREFERENCES "\xef\x86\xb8" // U+f1b8 #define ICON_MS_ROOM_SERVICE "\xee\xad\x89" // U+eb49 #define ICON_MS_ROTATE_90_DEGREES_CCW "\xee\x90\x98" // U+e418 #define ICON_MS_ROTATE_90_DEGREES_CW "\xee\xaa\xab" // U+eaab #define ICON_MS_ROTATE_LEFT "\xee\x90\x99" // U+e419 #define ICON_MS_ROTATE_RIGHT "\xee\x90\x9a" // U+e41a #define ICON_MS_ROUNDABOUT_LEFT "\xee\xae\x99" // U+eb99 #define ICON_MS_ROUNDABOUT_RIGHT "\xee\xae\xa3" // U+eba3 #define ICON_MS_ROUNDED_CORNER "\xee\xa4\xa0" // U+e920 #define ICON_MS_ROUTE "\xee\xab\x8d" // U+eacd #define ICON_MS_ROUTER "\xee\x8c\xa8" // U+e328 #define ICON_MS_ROUTINE "\xee\x88\x8c" // U+e20c #define ICON_MS_ROWING "\xee\xa4\xa1" // U+e921 #define ICON_MS_RSS_FEED "\xee\x83\xa5" // U+e0e5 #define ICON_MS_RSVP "\xef\x81\x95" // U+f055 #define ICON_MS_RTT "\xee\xa6\xad" // U+e9ad #define ICON_MS_RUBRIC "\xee\xac\xa7" // U+eb27 #define ICON_MS_RULE "\xef\x87\x82" // U+f1c2 #define ICON_MS_RULE_FOLDER "\xef\x87\x89" // U+f1c9 #define ICON_MS_RULE_SETTINGS "\xef\x99\x8c" // U+f64c #define ICON_MS_RUN_CIRCLE "\xee\xbd\xaf" // U+ef6f #define ICON_MS_RUNNING_WITH_ERRORS "\xee\x94\x9d" // U+e51d #define ICON_MS_RV_HOOKUP "\xee\x99\x82" // U+e642 #define ICON_MS_SAFETY_CHECK "\xee\xaf\xaf" // U+ebef #define ICON_MS_SAFETY_CHECK_OFF "\xef\x96\x9d" // U+f59d #define ICON_MS_SAFETY_DIVIDER "\xee\x87\x8c" // U+e1cc #define ICON_MS_SAILING "\xee\x94\x82" // U+e502 #define ICON_MS_SALINITY "\xef\xa1\xb6" // U+f876 #define ICON_MS_SANITIZER "\xef\x88\x9d" // U+f21d #define ICON_MS_SATELLITE "\xee\x95\xa2" // U+e562 #define ICON_MS_SATELLITE_ALT "\xee\xac\xba" // U+eb3a #define ICON_MS_SAUNA "\xef\x9b\xb7" // U+f6f7 #define ICON_MS_SAVE "\xee\x85\xa1" // U+e161 #define ICON_MS_SAVE_ALT "\xef\x82\x90" // U+f090 #define ICON_MS_SAVE_AS "\xee\xad\xa0" // U+eb60 #define ICON_MS_SAVED_SEARCH "\xee\xa8\x91" // U+ea11 #define ICON_MS_SAVINGS "\xee\x8b\xab" // U+e2eb #define ICON_MS_SCALE "\xee\xad\x9f" // U+eb5f #define ICON_MS_SCAN "\xef\x9d\x8e" // U+f74e #define ICON_MS_SCAN_DELETE "\xef\x9d\x8f" // U+f74f #define ICON_MS_SCANNER "\xee\x8c\xa9" // U+e329 #define ICON_MS_SCATTER_PLOT "\xee\x89\xa8" // U+e268 #define ICON_MS_SCENE "\xee\x8a\xa7" // U+e2a7 #define ICON_MS_SCHEDULE "\xee\xbf\x96" // U+efd6 #define ICON_MS_SCHEDULE_SEND "\xee\xa8\x8a" // U+ea0a #define ICON_MS_SCHEMA "\xee\x93\xbd" // U+e4fd #define ICON_MS_SCHOOL "\xee\xa0\x8c" // U+e80c #define ICON_MS_SCIENCE "\xee\xa9\x8b" // U+ea4b #define ICON_MS_SCIENCE_OFF "\xef\x95\x82" // U+f542 #define ICON_MS_SCORE "\xee\x89\xa9" // U+e269 #define ICON_MS_SCOREBOARD "\xee\xaf\x90" // U+ebd0 #define ICON_MS_SCREEN_LOCK_LANDSCAPE "\xee\x86\xbe" // U+e1be #define ICON_MS_SCREEN_LOCK_PORTRAIT "\xee\x86\xbf" // U+e1bf #define ICON_MS_SCREEN_LOCK_ROTATION "\xee\x87\x80" // U+e1c0 #define ICON_MS_SCREEN_RECORD "\xef\x99\xb9" // U+f679 #define ICON_MS_SCREEN_ROTATION "\xee\x87\x81" // U+e1c1 #define ICON_MS_SCREEN_ROTATION_ALT "\xee\xaf\xae" // U+ebee #define ICON_MS_SCREEN_ROTATION_UP "\xef\x99\xb8" // U+f678 #define ICON_MS_SCREEN_SEARCH_DESKTOP "\xee\xbd\xb0" // U+ef70 #define ICON_MS_SCREEN_SHARE "\xee\x83\xa2" // U+e0e2 #define ICON_MS_SCREENSHOT "\xef\x81\x96" // U+f056 #define ICON_MS_SCREENSHOT_FRAME "\xef\x99\xb7" // U+f677 #define ICON_MS_SCREENSHOT_KEYBOARD "\xef\x9f\x93" // U+f7d3 #define ICON_MS_SCREENSHOT_MONITOR "\xee\xb0\x88" // U+ec08 #define ICON_MS_SCREENSHOT_REGION "\xef\x9f\x92" // U+f7d2 #define ICON_MS_SCREENSHOT_TABLET "\xef\x9a\x97" // U+f697 #define ICON_MS_SCROLLABLE_HEADER "\xee\xa7\x9c" // U+e9dc #define ICON_MS_SCUBA_DIVING "\xee\xaf\x8e" // U+ebce #define ICON_MS_SD "\xee\xa7\x9d" // U+e9dd #define ICON_MS_SD_CARD "\xee\x98\xa3" // U+e623 #define ICON_MS_SD_CARD_ALERT "\xef\x81\x97" // U+f057 #define ICON_MS_SD_STORAGE "\xee\x98\xa3" // U+e623 #define ICON_MS_SDK "\xee\x9c\xa0" // U+e720 #define ICON_MS_SEARCH "\xee\xa2\xb6" // U+e8b6 #define ICON_MS_SEARCH_CHECK "\xef\xa0\x80" // U+f800 #define ICON_MS_SEARCH_HANDS_FREE "\xee\x9a\x96" // U+e696 #define ICON_MS_SEARCH_INSIGHTS "\xef\x92\xbc" // U+f4bc #define ICON_MS_SEARCH_OFF "\xee\xa9\xb6" // U+ea76 #define ICON_MS_SECURITY "\xee\x8c\xaa" // U+e32a #define ICON_MS_SECURITY_KEY "\xef\x94\x83" // U+f503 #define ICON_MS_SECURITY_UPDATE "\xef\x81\xb2" // U+f072 #define ICON_MS_SECURITY_UPDATE_GOOD "\xef\x81\xb3" // U+f073 #define ICON_MS_SECURITY_UPDATE_WARNING "\xef\x81\xb4" // U+f074 #define ICON_MS_SEGMENT "\xee\xa5\x8b" // U+e94b #define ICON_MS_SELECT "\xef\x9d\x8d" // U+f74d #define ICON_MS_SELECT_ALL "\xee\x85\xa2" // U+e162 #define ICON_MS_SELECT_CHECK_BOX "\xef\x87\xbe" // U+f1fe #define ICON_MS_SELECT_TO_SPEAK "\xef\x9f\x8f" // U+f7cf #define ICON_MS_SELECT_WINDOW "\xee\x9b\xba" // U+e6fa #define ICON_MS_SELECT_WINDOW_2 "\xef\x93\x88" // U+f4c8 #define ICON_MS_SELECT_WINDOW_OFF "\xee\x94\x86" // U+e506 #define ICON_MS_SELF_CARE "\xef\xa1\xad" // U+f86d #define ICON_MS_SELF_IMPROVEMENT "\xee\xa9\xb8" // U+ea78 #define ICON_MS_SELL "\xef\x81\x9b" // U+f05b #define ICON_MS_SEND "\xee\x85\xa3" // U+e163 #define ICON_MS_SEND_AND_ARCHIVE "\xee\xa8\x8c" // U+ea0c #define ICON_MS_SEND_MONEY "\xee\xa2\xb7" // U+e8b7 #define ICON_MS_SEND_TIME_EXTENSION "\xee\xab\x9b" // U+eadb #define ICON_MS_SEND_TO_MOBILE "\xef\x81\x9c" // U+f05c #define ICON_MS_SENSOR_DOOR "\xef\x86\xb5" // U+f1b5 #define ICON_MS_SENSOR_OCCUPIED "\xee\xb0\x90" // U+ec10 #define ICON_MS_SENSOR_WINDOW "\xef\x86\xb4" // U+f1b4 #define ICON_MS_SENSORS "\xee\x94\x9e" // U+e51e #define ICON_MS_SENSORS_KRX "\xef\x95\x96" // U+f556 #define ICON_MS_SENSORS_KRX_OFF "\xef\x94\x95" // U+f515 #define ICON_MS_SENSORS_OFF "\xee\x94\x9f" // U+e51f #define ICON_MS_SENTIMENT_CALM "\xef\x9a\xa7" // U+f6a7 #define ICON_MS_SENTIMENT_CONTENT "\xef\x9a\xa6" // U+f6a6 #define ICON_MS_SENTIMENT_DISSATISFIED "\xee\xa0\x91" // U+e811 #define ICON_MS_SENTIMENT_EXCITED "\xef\x9a\xa5" // U+f6a5 #define ICON_MS_SENTIMENT_EXTREMELY_DISSATISFIED "\xef\x86\x94" // U+f194 #define ICON_MS_SENTIMENT_FRUSTRATED "\xef\x9a\xa4" // U+f6a4 #define ICON_MS_SENTIMENT_NEUTRAL "\xee\xa0\x92" // U+e812 #define ICON_MS_SENTIMENT_SAD "\xef\x9a\xa3" // U+f6a3 #define ICON_MS_SENTIMENT_SATISFIED "\xee\xa0\x93" // U+e813 #define ICON_MS_SENTIMENT_SATISFIED_ALT "\xee\xa0\x93" // U+e813 #define ICON_MS_SENTIMENT_STRESSED "\xef\x9a\xa2" // U+f6a2 #define ICON_MS_SENTIMENT_VERY_DISSATISFIED "\xee\xa0\x94" // U+e814 #define ICON_MS_SENTIMENT_VERY_SATISFIED "\xee\xa0\x95" // U+e815 #define ICON_MS_SENTIMENT_WORRIED "\xef\x9a\xa1" // U+f6a1 #define ICON_MS_SERIF "\xef\x92\xac" // U+f4ac #define ICON_MS_SERVICE_TOOLBOX "\xee\x9c\x97" // U+e717 #define ICON_MS_SET_MEAL "\xef\x87\xaa" // U+f1ea #define ICON_MS_SETTINGS "\xee\xa2\xb8" // U+e8b8 #define ICON_MS_SETTINGS_ACCESSIBILITY "\xef\x81\x9d" // U+f05d #define ICON_MS_SETTINGS_ACCOUNT_BOX "\xef\xa0\xb5" // U+f835 #define ICON_MS_SETTINGS_ALERT "\xef\x85\x83" // U+f143 #define ICON_MS_SETTINGS_APPLICATIONS "\xee\xa2\xb9" // U+e8b9 #define ICON_MS_SETTINGS_B_ROLL "\xef\x98\xa5" // U+f625 #define ICON_MS_SETTINGS_BACKUP_RESTORE "\xee\xa2\xba" // U+e8ba #define ICON_MS_SETTINGS_BLUETOOTH "\xee\xa2\xbb" // U+e8bb #define ICON_MS_SETTINGS_BRIGHTNESS "\xee\xa2\xbd" // U+e8bd #define ICON_MS_SETTINGS_CELL "\xee\xa2\xbc" // U+e8bc #define ICON_MS_SETTINGS_CINEMATIC_BLUR "\xef\x98\xa4" // U+f624 #define ICON_MS_SETTINGS_ETHERNET "\xee\xa2\xbe" // U+e8be #define ICON_MS_SETTINGS_HEART "\xef\x94\xa2" // U+f522 #define ICON_MS_SETTINGS_INPUT_ANTENNA "\xee\xa2\xbf" // U+e8bf #define ICON_MS_SETTINGS_INPUT_COMPONENT "\xee\xa3\x81" // U+e8c1 #define ICON_MS_SETTINGS_INPUT_COMPOSITE "\xee\xa3\x81" // U+e8c1 #define ICON_MS_SETTINGS_INPUT_HDMI "\xee\xa3\x82" // U+e8c2 #define ICON_MS_SETTINGS_INPUT_SVIDEO "\xee\xa3\x83" // U+e8c3 #define ICON_MS_SETTINGS_MOTION_MODE "\xef\xa0\xb3" // U+f833 #define ICON_MS_SETTINGS_NIGHT_SIGHT "\xef\xa0\xb2" // U+f832 #define ICON_MS_SETTINGS_OVERSCAN "\xee\xa3\x84" // U+e8c4 #define ICON_MS_SETTINGS_PANORAMA "\xef\xa0\xb1" // U+f831 #define ICON_MS_SETTINGS_PHONE "\xee\xa3\x85" // U+e8c5 #define ICON_MS_SETTINGS_PHOTO_CAMERA "\xef\xa0\xb4" // U+f834 #define ICON_MS_SETTINGS_POWER "\xee\xa3\x86" // U+e8c6 #define ICON_MS_SETTINGS_REMOTE "\xee\xa3\x87" // U+e8c7 #define ICON_MS_SETTINGS_SLOW_MOTION "\xef\x98\xa3" // U+f623 #define ICON_MS_SETTINGS_SUGGEST "\xef\x81\x9e" // U+f05e #define ICON_MS_SETTINGS_SYSTEM_DAYDREAM "\xee\x87\x83" // U+e1c3 #define ICON_MS_SETTINGS_TIMELAPSE "\xef\x98\xa2" // U+f622 #define ICON_MS_SETTINGS_VIDEO_CAMERA "\xef\x98\xa1" // U+f621 #define ICON_MS_SETTINGS_VOICE "\xee\xa3\x88" // U+e8c8 #define ICON_MS_SETTOP_COMPONENT "\xee\x8a\xac" // U+e2ac #define ICON_MS_SEVERE_COLD "\xee\xaf\x93" // U+ebd3 #define ICON_MS_SHADOW "\xee\xa7\x9f" // U+e9df #define ICON_MS_SHADOW_ADD "\xef\x96\x84" // U+f584 #define ICON_MS_SHADOW_MINUS "\xef\x96\x83" // U+f583 #define ICON_MS_SHAPE_LINE "\xef\xa3\x93" // U+f8d3 #define ICON_MS_SHAPE_RECOGNITION "\xee\xac\x81" // U+eb01 #define ICON_MS_SHAPES "\xee\x98\x82" // U+e602 #define ICON_MS_SHARE "\xee\xa0\x8d" // U+e80d #define ICON_MS_SHARE_LOCATION "\xef\x81\x9f" // U+f05f #define ICON_MS_SHARE_OFF "\xef\x9b\x8b" // U+f6cb #define ICON_MS_SHARE_REVIEWS "\xef\xa2\xa4" // U+f8a4 #define ICON_MS_SHARE_WINDOWS "\xef\x98\x93" // U+f613 #define ICON_MS_SHEETS_RTL "\xef\xa0\xa3" // U+f823 #define ICON_MS_SHELF_AUTO_HIDE "\xef\x9c\x83" // U+f703 #define ICON_MS_SHELF_POSITION "\xef\x9c\x82" // U+f702 #define ICON_MS_SHELVES "\xef\xa1\xae" // U+f86e #define ICON_MS_SHIELD "\xee\xa7\xa0" // U+e9e0 #define ICON_MS_SHIELD_LOCK "\xef\x9a\x86" // U+f686 #define ICON_MS_SHIELD_LOCKED "\xef\x96\x92" // U+f592 #define ICON_MS_SHIELD_MOON "\xee\xaa\xa9" // U+eaa9 #define ICON_MS_SHIELD_PERSON "\xef\x99\x90" // U+f650 #define ICON_MS_SHIELD_QUESTION "\xef\x94\xa9" // U+f529 #define ICON_MS_SHIELD_WITH_HEART "\xee\x9e\x8f" // U+e78f #define ICON_MS_SHIELD_WITH_HOUSE "\xee\x9e\x8d" // U+e78d #define ICON_MS_SHIFT "\xee\x97\xb2" // U+e5f2 #define ICON_MS_SHIFT_LOCK "\xef\x9e\xae" // U+f7ae #define ICON_MS_SHIFT_LOCK_OFF "\xef\x92\x83" // U+f483 #define ICON_MS_SHOP "\xee\xa3\x89" // U+e8c9 #define ICON_MS_SHOP_2 "\xee\xa3\x8a" // U+e8ca #define ICON_MS_SHOP_TWO "\xee\xa3\x8a" // U+e8ca #define ICON_MS_SHOPPING_BAG "\xef\x87\x8c" // U+f1cc #define ICON_MS_SHOPPING_BASKET "\xee\xa3\x8b" // U+e8cb #define ICON_MS_SHOPPING_CART "\xee\xa3\x8c" // U+e8cc #define ICON_MS_SHOPPING_CART_CHECKOUT "\xee\xae\x88" // U+eb88 #define ICON_MS_SHOPPING_CART_OFF "\xef\x93\xb7" // U+f4f7 #define ICON_MS_SHOPPINGMODE "\xee\xbe\xb7" // U+efb7 #define ICON_MS_SHORT_STAY "\xee\x93\x90" // U+e4d0 #define ICON_MS_SHORT_TEXT "\xee\x89\xa1" // U+e261 #define ICON_MS_SHORTCUT "\xef\x95\xba" // U+f57a #define ICON_MS_SHOW_CHART "\xee\x9b\xa1" // U+e6e1 #define ICON_MS_SHOWER "\xef\x81\xa1" // U+f061 #define ICON_MS_SHUFFLE "\xee\x81\x83" // U+e043 #define ICON_MS_SHUFFLE_ON "\xee\xa7\xa1" // U+e9e1 #define ICON_MS_SHUTTER_SPEED "\xee\x90\xbd" // U+e43d #define ICON_MS_SHUTTER_SPEED_ADD "\xef\x95\xbe" // U+f57e #define ICON_MS_SHUTTER_SPEED_MINUS "\xef\x95\xbd" // U+f57d #define ICON_MS_SICK "\xef\x88\xa0" // U+f220 #define ICON_MS_SIDE_NAVIGATION "\xee\xa7\xa2" // U+e9e2 #define ICON_MS_SIGN_LANGUAGE "\xee\xaf\xa5" // U+ebe5 #define ICON_MS_SIGNAL_CELLULAR_0_BAR "\xef\x82\xa8" // U+f0a8 #define ICON_MS_SIGNAL_CELLULAR_1_BAR "\xef\x82\xa9" // U+f0a9 #define ICON_MS_SIGNAL_CELLULAR_2_BAR "\xef\x82\xaa" // U+f0aa #define ICON_MS_SIGNAL_CELLULAR_3_BAR "\xef\x82\xab" // U+f0ab #define ICON_MS_SIGNAL_CELLULAR_4_BAR "\xee\x87\x88" // U+e1c8 #define ICON_MS_SIGNAL_CELLULAR_ADD "\xef\x9e\xa9" // U+f7a9 #define ICON_MS_SIGNAL_CELLULAR_ALT "\xee\x88\x82" // U+e202 #define ICON_MS_SIGNAL_CELLULAR_ALT_1_BAR "\xee\xaf\x9f" // U+ebdf #define ICON_MS_SIGNAL_CELLULAR_ALT_2_BAR "\xee\xaf\xa3" // U+ebe3 #define ICON_MS_SIGNAL_CELLULAR_CONNECTED_NO_INTERNET_0_BAR "\xef\x82\xac" // U+f0ac #define ICON_MS_SIGNAL_CELLULAR_CONNECTED_NO_INTERNET_4_BAR "\xee\x87\x8d" // U+e1cd #define ICON_MS_SIGNAL_CELLULAR_NO_SIM "\xee\x87\x8e" // U+e1ce #define ICON_MS_SIGNAL_CELLULAR_NODATA "\xef\x81\xa2" // U+f062 #define ICON_MS_SIGNAL_CELLULAR_NULL "\xee\x87\x8f" // U+e1cf #define ICON_MS_SIGNAL_CELLULAR_OFF "\xee\x87\x90" // U+e1d0 #define ICON_MS_SIGNAL_CELLULAR_PAUSE "\xef\x96\xa7" // U+f5a7 #define ICON_MS_SIGNAL_DISCONNECTED "\xef\x88\xb9" // U+f239 #define ICON_MS_SIGNAL_WIFI_0_BAR "\xef\x82\xb0" // U+f0b0 #define ICON_MS_SIGNAL_WIFI_4_BAR "\xef\x81\xa5" // U+f065 #define ICON_MS_SIGNAL_WIFI_4_BAR_LOCK "\xee\x87\xa1" // U+e1e1 #define ICON_MS_SIGNAL_WIFI_BAD "\xef\x81\xa4" // U+f064 #define ICON_MS_SIGNAL_WIFI_CONNECTED_NO_INTERNET_4 "\xef\x81\xa4" // U+f064 #define ICON_MS_SIGNAL_WIFI_OFF "\xee\x87\x9a" // U+e1da #define ICON_MS_SIGNAL_WIFI_STATUSBAR_4_BAR "\xef\x81\xa5" // U+f065 #define ICON_MS_SIGNAL_WIFI_STATUSBAR_NOT_CONNECTED "\xef\x83\xaf" // U+f0ef #define ICON_MS_SIGNAL_WIFI_STATUSBAR_NULL "\xef\x81\xa7" // U+f067 #define ICON_MS_SIGNATURE "\xef\x9d\x8c" // U+f74c #define ICON_MS_SIGNPOST "\xee\xae\x91" // U+eb91 #define ICON_MS_SIM_CARD "\xee\x8c\xab" // U+e32b #define ICON_MS_SIM_CARD_ALERT "\xef\x81\x97" // U+f057 #define ICON_MS_SIM_CARD_DOWNLOAD "\xef\x81\xa8" // U+f068 #define ICON_MS_SINGLE_BED "\xee\xa9\x88" // U+ea48 #define ICON_MS_SIP "\xef\x81\xa9" // U+f069 #define ICON_MS_SKATEBOARDING "\xee\x94\x91" // U+e511 #define ICON_MS_SKELETON "\xef\xa2\x99" // U+f899 #define ICON_MS_SKILLET "\xef\x95\x83" // U+f543 #define ICON_MS_SKILLET_COOKTOP "\xef\x95\x84" // U+f544 #define ICON_MS_SKIP_NEXT "\xee\x81\x84" // U+e044 #define ICON_MS_SKIP_PREVIOUS "\xee\x81\x85" // U+e045 #define ICON_MS_SKULL "\xef\xa2\x9a" // U+f89a #define ICON_MS_SLAB_SERIF "\xef\x92\xab" // U+f4ab #define ICON_MS_SLEDDING "\xee\x94\x92" // U+e512 #define ICON_MS_SLEEP "\xee\x88\x93" // U+e213 #define ICON_MS_SLEEP_SCORE "\xef\x9a\xb7" // U+f6b7 #define ICON_MS_SLIDE_LIBRARY "\xef\xa0\xa2" // U+f822 #define ICON_MS_SLIDERS "\xee\xa7\xa3" // U+e9e3 #define ICON_MS_SLIDESHOW "\xee\x90\x9b" // U+e41b #define ICON_MS_SLOW_MOTION_VIDEO "\xee\x81\xa8" // U+e068 #define ICON_MS_SMART_BUTTON "\xef\x87\x81" // U+f1c1 #define ICON_MS_SMART_CARD_READER "\xef\x92\xa5" // U+f4a5 #define ICON_MS_SMART_CARD_READER_OFF "\xef\x92\xa6" // U+f4a6 #define ICON_MS_SMART_DISPLAY "\xef\x81\xaa" // U+f06a #define ICON_MS_SMART_OUTLET "\xee\xa1\x84" // U+e844 #define ICON_MS_SMART_SCREEN "\xef\x81\xab" // U+f06b #define ICON_MS_SMART_TOY "\xef\x81\xac" // U+f06c #define ICON_MS_SMARTPHONE "\xee\x8c\xac" // U+e32c #define ICON_MS_SMB_SHARE "\xef\x9d\x8b" // U+f74b #define ICON_MS_SMOKE_FREE "\xee\xad\x8a" // U+eb4a #define ICON_MS_SMOKING_ROOMS "\xee\xad\x8b" // U+eb4b #define ICON_MS_SMS "\xee\x98\xa5" // U+e625 #define ICON_MS_SMS_FAILED "\xee\xa1\xbf" // U+e87f #define ICON_MS_SNIPPET_FOLDER "\xef\x87\x87" // U+f1c7 #define ICON_MS_SNOOZE "\xee\x81\x86" // U+e046 #define ICON_MS_SNOWBOARDING "\xee\x94\x93" // U+e513 #define ICON_MS_SNOWING "\xee\xa0\x8f" // U+e80f #define ICON_MS_SNOWING_HEAVY "\xef\x98\x9c" // U+f61c #define ICON_MS_SNOWMOBILE "\xee\x94\x83" // U+e503 #define ICON_MS_SNOWSHOEING "\xee\x94\x94" // U+e514 #define ICON_MS_SOAP "\xef\x86\xb2" // U+f1b2 #define ICON_MS_SOCIAL_DISTANCE "\xee\x87\x8b" // U+e1cb #define ICON_MS_SOCIAL_LEADERBOARD "\xef\x9a\xa0" // U+f6a0 #define ICON_MS_SOLAR_POWER "\xee\xb0\x8f" // U+ec0f #define ICON_MS_SORT "\xee\x85\xa4" // U+e164 #define ICON_MS_SORT_BY_ALPHA "\xee\x81\x93" // U+e053 #define ICON_MS_SOS "\xee\xaf\xb7" // U+ebf7 #define ICON_MS_SOUND_DETECTION_DOG_BARKING "\xef\x85\x89" // U+f149 #define ICON_MS_SOUND_DETECTION_GLASS_BREAK "\xef\x85\x8a" // U+f14a #define ICON_MS_SOUND_DETECTION_LOUD_SOUND "\xef\x85\x8b" // U+f14b #define ICON_MS_SOUND_SAMPLER "\xef\x9a\xb4" // U+f6b4 #define ICON_MS_SOUP_KITCHEN "\xee\x9f\x93" // U+e7d3 #define ICON_MS_SOURCE "\xef\x87\x88" // U+f1c8 #define ICON_MS_SOURCE_ENVIRONMENT "\xee\x94\xa7" // U+e527 #define ICON_MS_SOURCE_NOTES "\xee\x84\xad" // U+e12d #define ICON_MS_SOUTH "\xef\x87\xa3" // U+f1e3 #define ICON_MS_SOUTH_AMERICA "\xee\x9f\xa4" // U+e7e4 #define ICON_MS_SOUTH_EAST "\xef\x87\xa4" // U+f1e4 #define ICON_MS_SOUTH_WEST "\xef\x87\xa5" // U+f1e5 #define ICON_MS_SPA "\xee\xad\x8c" // U+eb4c #define ICON_MS_SPACE_BAR "\xee\x89\x96" // U+e256 #define ICON_MS_SPACE_DASHBOARD "\xee\x99\xab" // U+e66b #define ICON_MS_SPATIAL_AUDIO "\xee\xaf\xab" // U+ebeb #define ICON_MS_SPATIAL_AUDIO_OFF "\xee\xaf\xa8" // U+ebe8 #define ICON_MS_SPATIAL_SPEAKER "\xef\x93\x8f" // U+f4cf #define ICON_MS_SPATIAL_TRACKING "\xee\xaf\xaa" // U+ebea #define ICON_MS_SPEAKER "\xee\x8c\xad" // U+e32d #define ICON_MS_SPEAKER_GROUP "\xee\x8c\xae" // U+e32e #define ICON_MS_SPEAKER_NOTES "\xee\xa3\x8d" // U+e8cd #define ICON_MS_SPEAKER_NOTES_OFF "\xee\xa4\xaa" // U+e92a #define ICON_MS_SPEAKER_PHONE "\xee\x83\x92" // U+e0d2 #define ICON_MS_SPECIAL_CHARACTER "\xef\x9d\x8a" // U+f74a #define ICON_MS_SPECIFIC_GRAVITY "\xef\xa1\xb2" // U+f872 #define ICON_MS_SPEECH_TO_TEXT "\xef\xa2\xa7" // U+f8a7 #define ICON_MS_SPEED "\xee\xa7\xa4" // U+e9e4 #define ICON_MS_SPEED_0_25 "\xef\x93\x94" // U+f4d4 #define ICON_MS_SPEED_0_2X "\xef\x92\x98" // U+f498 #define ICON_MS_SPEED_0_5 "\xef\x93\xa2" // U+f4e2 #define ICON_MS_SPEED_0_5X "\xef\x92\x97" // U+f497 #define ICON_MS_SPEED_0_75 "\xef\x93\x93" // U+f4d3 #define ICON_MS_SPEED_0_7X "\xef\x92\x96" // U+f496 #define ICON_MS_SPEED_1_2 "\xef\x93\xa1" // U+f4e1 #define ICON_MS_SPEED_1_25 "\xef\x93\x92" // U+f4d2 #define ICON_MS_SPEED_1_2X "\xef\x92\x95" // U+f495 #define ICON_MS_SPEED_1_5 "\xef\x93\xa0" // U+f4e0 #define ICON_MS_SPEED_1_5X "\xef\x92\x94" // U+f494 #define ICON_MS_SPEED_1_75 "\xef\x93\x91" // U+f4d1 #define ICON_MS_SPEED_1_7X "\xef\x92\x93" // U+f493 #define ICON_MS_SPEED_2X "\xef\x93\xab" // U+f4eb #define ICON_MS_SPELLCHECK "\xee\xa3\x8e" // U+e8ce #define ICON_MS_SPLITSCREEN "\xef\x81\xad" // U+f06d #define ICON_MS_SPLITSCREEN_ADD "\xef\x93\xbd" // U+f4fd #define ICON_MS_SPLITSCREEN_BOTTOM "\xef\x99\xb6" // U+f676 #define ICON_MS_SPLITSCREEN_LEFT "\xef\x99\xb5" // U+f675 #define ICON_MS_SPLITSCREEN_RIGHT "\xef\x99\xb4" // U+f674 #define ICON_MS_SPLITSCREEN_TOP "\xef\x99\xb3" // U+f673 #define ICON_MS_SPLITSCREEN_VERTICAL_ADD "\xef\x93\xbc" // U+f4fc #define ICON_MS_SPO2 "\xef\x9b\x9b" // U+f6db #define ICON_MS_SPOKE "\xee\xa6\xa7" // U+e9a7 #define ICON_MS_SPORTS "\xee\xa8\xb0" // U+ea30 #define ICON_MS_SPORTS_AND_OUTDOORS "\xee\xbe\xb8" // U+efb8 #define ICON_MS_SPORTS_BAR "\xef\x87\xb3" // U+f1f3 #define ICON_MS_SPORTS_BASEBALL "\xee\xa9\x91" // U+ea51 #define ICON_MS_SPORTS_BASKETBALL "\xee\xa8\xa6" // U+ea26 #define ICON_MS_SPORTS_CRICKET "\xee\xa8\xa7" // U+ea27 #define ICON_MS_SPORTS_ESPORTS "\xee\xa8\xa8" // U+ea28 #define ICON_MS_SPORTS_FOOTBALL "\xee\xa8\xa9" // U+ea29 #define ICON_MS_SPORTS_GOLF "\xee\xa8\xaa" // U+ea2a #define ICON_MS_SPORTS_GYMNASTICS "\xee\xaf\x84" // U+ebc4 #define ICON_MS_SPORTS_HANDBALL "\xee\xa8\xb3" // U+ea33 #define ICON_MS_SPORTS_HOCKEY "\xee\xa8\xab" // U+ea2b #define ICON_MS_SPORTS_KABADDI "\xee\xa8\xb4" // U+ea34 #define ICON_MS_SPORTS_MARTIAL_ARTS "\xee\xab\xa9" // U+eae9 #define ICON_MS_SPORTS_MMA "\xee\xa8\xac" // U+ea2c #define ICON_MS_SPORTS_MOTORSPORTS "\xee\xa8\xad" // U+ea2d #define ICON_MS_SPORTS_RUGBY "\xee\xa8\xae" // U+ea2e #define ICON_MS_SPORTS_SCORE "\xef\x81\xae" // U+f06e #define ICON_MS_SPORTS_SOCCER "\xee\xa8\xaf" // U+ea2f #define ICON_MS_SPORTS_TENNIS "\xee\xa8\xb2" // U+ea32 #define ICON_MS_SPORTS_VOLLEYBALL "\xee\xa8\xb1" // U+ea31 #define ICON_MS_SPRINKLER "\xee\x8a\x9a" // U+e29a #define ICON_MS_SPRINT "\xef\xa0\x9f" // U+f81f #define ICON_MS_SQUARE "\xee\xac\xb6" // U+eb36 #define ICON_MS_SQUARE_FOOT "\xee\xa9\x89" // U+ea49 #define ICON_MS_SSID_CHART "\xee\xad\xa6" // U+eb66 #define ICON_MS_STACK "\xef\x98\x89" // U+f609 #define ICON_MS_STACK_OFF "\xef\x98\x88" // U+f608 #define ICON_MS_STACK_STAR "\xef\x98\x87" // U+f607 #define ICON_MS_STACKED_BAR_CHART "\xee\xa7\xa6" // U+e9e6 #define ICON_MS_STACKED_EMAIL "\xee\x9b\x87" // U+e6c7 #define ICON_MS_STACKED_INBOX "\xee\x9b\x89" // U+e6c9 #define ICON_MS_STACKED_LINE_CHART "\xef\x88\xab" // U+f22b #define ICON_MS_STACKS "\xef\x94\x80" // U+f500 #define ICON_MS_STADIA_CONTROLLER "\xef\x84\xb5" // U+f135 #define ICON_MS_STADIUM "\xee\xae\x90" // U+eb90 #define ICON_MS_STAIRS "\xef\x86\xa9" // U+f1a9 #define ICON_MS_STAR "\xef\x82\x9a" // U+f09a #define ICON_MS_STAR_BORDER "\xef\x82\x9a" // U+f09a #define ICON_MS_STAR_BORDER_PURPLE500 "\xef\x82\x9a" // U+f09a #define ICON_MS_STAR_HALF "\xee\xa0\xb9" // U+e839 #define ICON_MS_STAR_OUTLINE "\xef\x82\x9a" // U+f09a #define ICON_MS_STAR_PURPLE500 "\xef\x82\x9a" // U+f09a #define ICON_MS_STAR_RATE "\xef\x83\xac" // U+f0ec #define ICON_MS_STAR_RATE_HALF "\xee\xb1\x85" // U+ec45 #define ICON_MS_STARS "\xee\xa3\x90" // U+e8d0 #define ICON_MS_START "\xee\x82\x89" // U+e089 #define ICON_MS_STAT_0 "\xee\x9a\x97" // U+e697 #define ICON_MS_STAT_1 "\xee\x9a\x98" // U+e698 #define ICON_MS_STAT_2 "\xee\x9a\x99" // U+e699 #define ICON_MS_STAT_3 "\xee\x9a\x9a" // U+e69a #define ICON_MS_STAT_MINUS_1 "\xee\x9a\x9b" // U+e69b #define ICON_MS_STAT_MINUS_2 "\xee\x9a\x9c" // U+e69c #define ICON_MS_STAT_MINUS_3 "\xee\x9a\x9d" // U+e69d #define ICON_MS_STAY_CURRENT_LANDSCAPE "\xee\x83\x93" // U+e0d3 #define ICON_MS_STAY_CURRENT_PORTRAIT "\xee\x83\x94" // U+e0d4 #define ICON_MS_STAY_PRIMARY_LANDSCAPE "\xee\x83\x95" // U+e0d5 #define ICON_MS_STAY_PRIMARY_PORTRAIT "\xee\x83\x96" // U+e0d6 #define ICON_MS_STEP "\xef\x9b\xbe" // U+f6fe #define ICON_MS_STEP_INTO "\xef\x9c\x81" // U+f701 #define ICON_MS_STEP_OUT "\xef\x9c\x80" // U+f700 #define ICON_MS_STEP_OVER "\xef\x9b\xbf" // U+f6ff #define ICON_MS_STEPPERS "\xee\xa7\xa7" // U+e9e7 #define ICON_MS_STEPS "\xef\x9b\x9a" // U+f6da #define ICON_MS_STETHOSCOPE "\xef\xa0\x85" // U+f805 #define ICON_MS_STETHOSCOPE_ARROW "\xef\xa0\x87" // U+f807 #define ICON_MS_STETHOSCOPE_CHECK "\xef\xa0\x86" // U+f806 #define ICON_MS_STICKY_NOTE "\xee\xa7\xa8" // U+e9e8 #define ICON_MS_STICKY_NOTE_2 "\xef\x87\xbc" // U+f1fc #define ICON_MS_STOCK_MEDIA "\xef\x95\xb0" // U+f570 #define ICON_MS_STOCKPOT "\xef\x95\x85" // U+f545 #define ICON_MS_STOP "\xee\x81\x87" // U+e047 #define ICON_MS_STOP_CIRCLE "\xee\xbd\xb1" // U+ef71 #define ICON_MS_STOP_SCREEN_SHARE "\xee\x83\xa3" // U+e0e3 #define ICON_MS_STORAGE "\xee\x87\x9b" // U+e1db #define ICON_MS_STORE "\xee\xa3\x91" // U+e8d1 #define ICON_MS_STORE_MALL_DIRECTORY "\xee\xa3\x91" // U+e8d1 #define ICON_MS_STOREFRONT "\xee\xa8\x92" // U+ea12 #define ICON_MS_STORM "\xef\x81\xb0" // U+f070 #define ICON_MS_STRAIGHT "\xee\xae\x95" // U+eb95 #define ICON_MS_STRAIGHTEN "\xee\x90\x9c" // U+e41c #define ICON_MS_STRATEGY "\xef\x97\x9f" // U+f5df #define ICON_MS_STREAM "\xee\xa7\xa9" // U+e9e9 #define ICON_MS_STREAM_APPS "\xef\x9e\x9f" // U+f79f #define ICON_MS_STREETVIEW "\xee\x95\xae" // U+e56e #define ICON_MS_STRESS_MANAGEMENT "\xef\x9b\x99" // U+f6d9 #define ICON_MS_STRIKETHROUGH_S "\xee\x89\x97" // U+e257 #define ICON_MS_STROKE_FULL "\xef\x9d\x89" // U+f749 #define ICON_MS_STROKE_PARTIAL "\xef\x9d\x88" // U+f748 #define ICON_MS_STROLLER "\xef\x86\xae" // U+f1ae #define ICON_MS_STYLE "\xee\x90\x9d" // U+e41d #define ICON_MS_STYLER "\xee\x89\xb3" // U+e273 #define ICON_MS_STYLUS "\xef\x98\x84" // U+f604 #define ICON_MS_STYLUS_LASER_POINTER "\xef\x9d\x87" // U+f747 #define ICON_MS_STYLUS_NOTE "\xef\x98\x83" // U+f603 #define ICON_MS_SUBDIRECTORY_ARROW_LEFT "\xee\x97\x99" // U+e5d9 #define ICON_MS_SUBDIRECTORY_ARROW_RIGHT "\xee\x97\x9a" // U+e5da #define ICON_MS_SUBHEADER "\xee\xa7\xaa" // U+e9ea #define ICON_MS_SUBJECT "\xee\xa3\x92" // U+e8d2 #define ICON_MS_SUBSCRIPT "\xef\x84\x91" // U+f111 #define ICON_MS_SUBSCRIPTIONS "\xee\x81\xa4" // U+e064 #define ICON_MS_SUBTITLES "\xee\x81\x88" // U+e048 #define ICON_MS_SUBTITLES_OFF "\xee\xbd\xb2" // U+ef72 #define ICON_MS_SUBWAY "\xee\x95\xaf" // U+e56f #define ICON_MS_SUMMARIZE "\xef\x81\xb1" // U+f071 #define ICON_MS_SUNNY "\xee\xa0\x9a" // U+e81a #define ICON_MS_SUNNY_SNOWING "\xee\xa0\x99" // U+e819 #define ICON_MS_SUPERSCRIPT "\xef\x84\x92" // U+f112 #define ICON_MS_SUPERVISED_USER_CIRCLE "\xee\xa4\xb9" // U+e939 #define ICON_MS_SUPERVISED_USER_CIRCLE_OFF "\xef\x98\x8e" // U+f60e #define ICON_MS_SUPERVISOR_ACCOUNT "\xee\xa3\x93" // U+e8d3 #define ICON_MS_SUPPORT "\xee\xbd\xb3" // U+ef73 #define ICON_MS_SUPPORT_AGENT "\xef\x83\xa2" // U+f0e2 #define ICON_MS_SURFING "\xee\x94\x95" // U+e515 #define ICON_MS_SURGICAL "\xee\x84\xb1" // U+e131 #define ICON_MS_SURROUND_SOUND "\xee\x81\x89" // U+e049 #define ICON_MS_SWAP_CALLS "\xee\x83\x97" // U+e0d7 #define ICON_MS_SWAP_DRIVING_APPS "\xee\x9a\x9e" // U+e69e #define ICON_MS_SWAP_DRIVING_APPS_WHEEL "\xee\x9a\x9f" // U+e69f #define ICON_MS_SWAP_HORIZ "\xee\xa3\x94" // U+e8d4 #define ICON_MS_SWAP_HORIZONTAL_CIRCLE "\xee\xa4\xb3" // U+e933 #define ICON_MS_SWAP_VERT "\xee\xa3\x95" // U+e8d5 #define ICON_MS_SWAP_VERTICAL_CIRCLE "\xee\xa3\x96" // U+e8d6 #define ICON_MS_SWEEP "\xee\x9a\xac" // U+e6ac #define ICON_MS_SWIPE "\xee\xa7\xac" // U+e9ec #define ICON_MS_SWIPE_DOWN "\xee\xad\x93" // U+eb53 #define ICON_MS_SWIPE_DOWN_ALT "\xee\xac\xb0" // U+eb30 #define ICON_MS_SWIPE_LEFT "\xee\xad\x99" // U+eb59 #define ICON_MS_SWIPE_LEFT_ALT "\xee\xac\xb3" // U+eb33 #define ICON_MS_SWIPE_RIGHT "\xee\xad\x92" // U+eb52 #define ICON_MS_SWIPE_RIGHT_ALT "\xee\xad\x96" // U+eb56 #define ICON_MS_SWIPE_UP "\xee\xac\xae" // U+eb2e #define ICON_MS_SWIPE_UP_ALT "\xee\xac\xb5" // U+eb35 #define ICON_MS_SWIPE_VERTICAL "\xee\xad\x91" // U+eb51 #define ICON_MS_SWITCH "\xee\x87\xb4" // U+e1f4 #define ICON_MS_SWITCH_ACCESS "\xef\x9b\xbd" // U+f6fd #define ICON_MS_SWITCH_ACCESS_2 "\xef\x94\x86" // U+f506 #define ICON_MS_SWITCH_ACCESS_SHORTCUT "\xee\x9f\xa1" // U+e7e1 #define ICON_MS_SWITCH_ACCESS_SHORTCUT_ADD "\xee\x9f\xa2" // U+e7e2 #define ICON_MS_SWITCH_ACCOUNT "\xee\xa7\xad" // U+e9ed #define ICON_MS_SWITCH_CAMERA "\xee\x90\x9e" // U+e41e #define ICON_MS_SWITCH_LEFT "\xef\x87\x91" // U+f1d1 #define ICON_MS_SWITCH_RIGHT "\xef\x87\x92" // U+f1d2 #define ICON_MS_SWITCH_VIDEO "\xee\x90\x9f" // U+e41f #define ICON_MS_SWITCHES "\xee\x9c\xb3" // U+e733 #define ICON_MS_SWORD_ROSE "\xef\x97\x9e" // U+f5de #define ICON_MS_SWORDS "\xef\xa2\x89" // U+f889 #define ICON_MS_SYMPTOMS "\xee\x84\xb2" // U+e132 #define ICON_MS_SYNAGOGUE "\xee\xaa\xb0" // U+eab0 #define ICON_MS_SYNC "\xee\x98\xa7" // U+e627 #define ICON_MS_SYNC_ALT "\xee\xa8\x98" // U+ea18 #define ICON_MS_SYNC_DISABLED "\xee\x98\xa8" // U+e628 #define ICON_MS_SYNC_LOCK "\xee\xab\xae" // U+eaee #define ICON_MS_SYNC_PROBLEM "\xee\x98\xa9" // U+e629 #define ICON_MS_SYNC_SAVED_LOCALLY "\xef\xa0\xa0" // U+f820 #define ICON_MS_SYRINGE "\xee\x84\xb3" // U+e133 #define ICON_MS_SYSTEM_SECURITY_UPDATE "\xef\x81\xb2" // U+f072 #define ICON_MS_SYSTEM_SECURITY_UPDATE_GOOD "\xef\x81\xb3" // U+f073 #define ICON_MS_SYSTEM_SECURITY_UPDATE_WARNING "\xef\x81\xb4" // U+f074 #define ICON_MS_SYSTEM_UPDATE "\xef\x81\xb2" // U+f072 #define ICON_MS_SYSTEM_UPDATE_ALT "\xee\xa3\x97" // U+e8d7 #define ICON_MS_TAB "\xee\xa3\x98" // U+e8d8 #define ICON_MS_TAB_CLOSE "\xef\x9d\x85" // U+f745 #define ICON_MS_TAB_CLOSE_RIGHT "\xef\x9d\x86" // U+f746 #define ICON_MS_TAB_DUPLICATE "\xef\x9d\x84" // U+f744 #define ICON_MS_TAB_GROUP "\xef\x9d\x83" // U+f743 #define ICON_MS_TAB_MOVE "\xef\x9d\x82" // U+f742 #define ICON_MS_TAB_NEW_RIGHT "\xef\x9d\x81" // U+f741 #define ICON_MS_TAB_RECENT "\xef\x9d\x80" // U+f740 #define ICON_MS_TAB_UNSELECTED "\xee\xa3\x99" // U+e8d9 #define ICON_MS_TABLE "\xef\x86\x91" // U+f191 #define ICON_MS_TABLE_BAR "\xee\xab\x92" // U+ead2 #define ICON_MS_TABLE_CHART "\xee\x89\xa5" // U+e265 #define ICON_MS_TABLE_CHART_VIEW "\xef\x9b\xaf" // U+f6ef #define ICON_MS_TABLE_LAMP "\xee\x87\xb2" // U+e1f2 #define ICON_MS_TABLE_RESTAURANT "\xee\xab\x86" // U+eac6 #define ICON_MS_TABLE_ROWS "\xef\x84\x81" // U+f101 #define ICON_MS_TABLE_ROWS_NARROW "\xef\x9c\xbf" // U+f73f #define ICON_MS_TABLE_VIEW "\xef\x86\xbe" // U+f1be #define ICON_MS_TABLET "\xee\x8c\xaf" // U+e32f #define ICON_MS_TABLET_ANDROID "\xee\x8c\xb0" // U+e330 #define ICON_MS_TABLET_MAC "\xee\x8c\xb1" // U+e331 #define ICON_MS_TABS "\xee\xa7\xae" // U+e9ee #define ICON_MS_TACTIC "\xef\x95\xa4" // U+f564 #define ICON_MS_TAG "\xee\xa7\xaf" // U+e9ef #define ICON_MS_TAG_FACES "\xee\xa8\xa2" // U+ea22 #define ICON_MS_TAKEOUT_DINING "\xee\xa9\xb4" // U+ea74 #define ICON_MS_TAMPER_DETECTION_OFF "\xee\xa0\xae" // U+e82e #define ICON_MS_TAMPER_DETECTION_ON "\xef\xa3\x88" // U+f8c8 #define ICON_MS_TAP_AND_PLAY "\xee\x98\xab" // U+e62b #define ICON_MS_TAPAS "\xef\x87\xa9" // U+f1e9 #define ICON_MS_TARGET "\xee\x9c\x99" // U+e719 #define ICON_MS_TASK "\xef\x81\xb5" // U+f075 #define ICON_MS_TASK_ALT "\xee\x8b\xa6" // U+e2e6 #define ICON_MS_TAUNT "\xef\x9a\x9f" // U+f69f #define ICON_MS_TAXI_ALERT "\xee\xbd\xb4" // U+ef74 #define ICON_MS_TEAM_DASHBOARD "\xee\x80\x93" // U+e013 #define ICON_MS_TEMP_PREFERENCES_CUSTOM "\xef\xa3\x89" // U+f8c9 #define ICON_MS_TEMP_PREFERENCES_ECO "\xef\xa3\x8a" // U+f8ca #define ICON_MS_TEMPLE_BUDDHIST "\xee\xaa\xb3" // U+eab3 #define ICON_MS_TEMPLE_HINDU "\xee\xaa\xaf" // U+eaaf #define ICON_MS_TENANCY "\xef\x83\xa3" // U+f0e3 #define ICON_MS_TERMINAL "\xee\xae\x8e" // U+eb8e #define ICON_MS_TERRAIN "\xee\x95\xa4" // U+e564 #define ICON_MS_TEXT_AD "\xee\x9c\xa8" // U+e728 #define ICON_MS_TEXT_DECREASE "\xee\xab\x9d" // U+eadd #define ICON_MS_TEXT_FIELDS "\xee\x89\xa2" // U+e262 #define ICON_MS_TEXT_FIELDS_ALT "\xee\xa7\xb1" // U+e9f1 #define ICON_MS_TEXT_FORMAT "\xee\x85\xa5" // U+e165 #define ICON_MS_TEXT_INCREASE "\xee\xab\xa2" // U+eae2 #define ICON_MS_TEXT_ROTATE_UP "\xee\xa4\xba" // U+e93a #define ICON_MS_TEXT_ROTATE_VERTICAL "\xee\xa4\xbb" // U+e93b #define ICON_MS_TEXT_ROTATION_ANGLEDOWN "\xee\xa4\xbc" // U+e93c #define ICON_MS_TEXT_ROTATION_ANGLEUP "\xee\xa4\xbd" // U+e93d #define ICON_MS_TEXT_ROTATION_DOWN "\xee\xa4\xbe" // U+e93e #define ICON_MS_TEXT_ROTATION_NONE "\xee\xa4\xbf" // U+e93f #define ICON_MS_TEXT_SELECT_END "\xef\x9c\xbe" // U+f73e #define ICON_MS_TEXT_SELECT_JUMP_TO_BEGINNING "\xef\x9c\xbd" // U+f73d #define ICON_MS_TEXT_SELECT_JUMP_TO_END "\xef\x9c\xbc" // U+f73c #define ICON_MS_TEXT_SELECT_MOVE_BACK_CHARACTER "\xef\x9c\xbb" // U+f73b #define ICON_MS_TEXT_SELECT_MOVE_BACK_WORD "\xef\x9c\xba" // U+f73a #define ICON_MS_TEXT_SELECT_MOVE_DOWN "\xef\x9c\xb9" // U+f739 #define ICON_MS_TEXT_SELECT_MOVE_FORWARD_CHARACTER "\xef\x9c\xb8" // U+f738 #define ICON_MS_TEXT_SELECT_MOVE_FORWARD_WORD "\xef\x9c\xb7" // U+f737 #define ICON_MS_TEXT_SELECT_MOVE_UP "\xef\x9c\xb6" // U+f736 #define ICON_MS_TEXT_SELECT_START "\xef\x9c\xb5" // U+f735 #define ICON_MS_TEXT_SNIPPET "\xef\x87\x86" // U+f1c6 #define ICON_MS_TEXT_TO_SPEECH "\xef\x86\xbc" // U+f1bc #define ICON_MS_TEXT_UP "\xef\x92\x9e" // U+f49e #define ICON_MS_TEXTSMS "\xee\x98\xa5" // U+e625 #define ICON_MS_TEXTURE "\xee\x90\xa1" // U+e421 #define ICON_MS_TEXTURE_ADD "\xef\x95\xbc" // U+f57c #define ICON_MS_TEXTURE_MINUS "\xef\x95\xbb" // U+f57b #define ICON_MS_THEATER_COMEDY "\xee\xa9\xa6" // U+ea66 #define ICON_MS_THEATERS "\xee\xa3\x9a" // U+e8da #define ICON_MS_THERMOMETER "\xee\xa1\x86" // U+e846 #define ICON_MS_THERMOMETER_ADD "\xef\x96\x82" // U+f582 #define ICON_MS_THERMOMETER_GAIN "\xef\x9b\x98" // U+f6d8 #define ICON_MS_THERMOMETER_LOSS "\xef\x9b\x97" // U+f6d7 #define ICON_MS_THERMOMETER_MINUS "\xef\x96\x81" // U+f581 #define ICON_MS_THERMOSTAT "\xef\x81\xb6" // U+f076 #define ICON_MS_THERMOSTAT_AUTO "\xef\x81\xb7" // U+f077 #define ICON_MS_THERMOSTAT_CARBON "\xef\x85\xb8" // U+f178 #define ICON_MS_THINGS_TO_DO "\xee\xac\xaa" // U+eb2a #define ICON_MS_THREAD_UNREAD "\xef\x93\xb9" // U+f4f9 #define ICON_MS_THUMB_DOWN "\xef\x95\xb8" // U+f578 #define ICON_MS_THUMB_DOWN_ALT "\xef\x95\xb8" // U+f578 #define ICON_MS_THUMB_DOWN_FILLED "\xef\x95\xb8" // U+f578 #define ICON_MS_THUMB_DOWN_OFF "\xef\x95\xb8" // U+f578 #define ICON_MS_THUMB_DOWN_OFF_ALT "\xef\x95\xb8" // U+f578 #define ICON_MS_THUMB_UP "\xef\x95\xb7" // U+f577 #define ICON_MS_THUMB_UP_ALT "\xef\x95\xb7" // U+f577 #define ICON_MS_THUMB_UP_FILLED "\xef\x95\xb7" // U+f577 #define ICON_MS_THUMB_UP_OFF "\xef\x95\xb7" // U+f577 #define ICON_MS_THUMB_UP_OFF_ALT "\xef\x95\xb7" // U+f577 #define ICON_MS_THUMBNAIL_BAR "\xef\x9c\xb4" // U+f734 #define ICON_MS_THUMBS_UP_DOWN "\xee\xa3\x9d" // U+e8dd #define ICON_MS_THUNDERSTORM "\xee\xaf\x9b" // U+ebdb #define ICON_MS_TIBIA "\xef\xa2\x9b" // U+f89b #define ICON_MS_TIBIA_ALT "\xef\xa2\x9c" // U+f89c #define ICON_MS_TIME_AUTO "\xef\x83\xa4" // U+f0e4 #define ICON_MS_TIME_TO_LEAVE "\xee\xbf\xb7" // U+eff7 #define ICON_MS_TIMELAPSE "\xee\x90\xa2" // U+e422 #define ICON_MS_TIMELINE "\xee\xa4\xa2" // U+e922 #define ICON_MS_TIMER "\xee\x90\xa5" // U+e425 #define ICON_MS_TIMER_10 "\xee\x90\xa3" // U+e423 #define ICON_MS_TIMER_10_ALT_1 "\xee\xbe\xbf" // U+efbf #define ICON_MS_TIMER_10_SELECT "\xef\x81\xba" // U+f07a #define ICON_MS_TIMER_3 "\xee\x90\xa4" // U+e424 #define ICON_MS_TIMER_3_ALT_1 "\xee\xbf\x80" // U+efc0 #define ICON_MS_TIMER_3_SELECT "\xef\x81\xbb" // U+f07b #define ICON_MS_TIMER_5 "\xef\x92\xb1" // U+f4b1 #define ICON_MS_TIMER_5_SHUTTER "\xef\x92\xb2" // U+f4b2 #define ICON_MS_TIMER_OFF "\xee\x90\xa6" // U+e426 #define ICON_MS_TIMER_PAUSE "\xef\x92\xbb" // U+f4bb #define ICON_MS_TIMER_PLAY "\xef\x92\xba" // U+f4ba #define ICON_MS_TIPS_AND_UPDATES "\xee\x9e\x9a" // U+e79a #define ICON_MS_TIRE_REPAIR "\xee\xaf\x88" // U+ebc8 #define ICON_MS_TITLE "\xee\x89\xa4" // U+e264 #define ICON_MS_TITLECASE "\xef\x92\x89" // U+f489 #define ICON_MS_TOAST "\xee\xbf\x81" // U+efc1 #define ICON_MS_TOC "\xee\xa3\x9e" // U+e8de #define ICON_MS_TODAY "\xee\xa3\x9f" // U+e8df #define ICON_MS_TOGGLE_OFF "\xee\xa7\xb5" // U+e9f5 #define ICON_MS_TOGGLE_ON "\xee\xa7\xb6" // U+e9f6 #define ICON_MS_TOKEN "\xee\xa8\xa5" // U+ea25 #define ICON_MS_TOLL "\xee\xa3\xa0" // U+e8e0 #define ICON_MS_TONALITY "\xee\x90\xa7" // U+e427 #define ICON_MS_TOOLBAR "\xee\xa7\xb7" // U+e9f7 #define ICON_MS_TOOLS_FLAT_HEAD "\xef\xa3\x8b" // U+f8cb #define ICON_MS_TOOLS_INSTALLATION_KIT "\xee\x8a\xab" // U+e2ab #define ICON_MS_TOOLS_LADDER "\xee\x8b\x8b" // U+e2cb #define ICON_MS_TOOLS_LEVEL "\xee\x9d\xbb" // U+e77b #define ICON_MS_TOOLS_PHILLIPS "\xef\xa3\x8c" // U+f8cc #define ICON_MS_TOOLS_PLIERS_WIRE_STRIPPER "\xee\x8a\xaa" // U+e2aa #define ICON_MS_TOOLS_POWER_DRILL "\xee\x87\xa9" // U+e1e9 #define ICON_MS_TOOLS_WRENCH "\xef\xa3\x8d" // U+f8cd #define ICON_MS_TOOLTIP "\xee\xa7\xb8" // U+e9f8 #define ICON_MS_TOP_PANEL_CLOSE "\xef\x9c\xb3" // U+f733 #define ICON_MS_TOP_PANEL_OPEN "\xef\x9c\xb2" // U+f732 #define ICON_MS_TOPIC "\xef\x87\x88" // U+f1c8 #define ICON_MS_TORNADO "\xee\x86\x99" // U+e199 #define ICON_MS_TOTAL_DISSOLVED_SOLIDS "\xef\xa1\xb7" // U+f877 #define ICON_MS_TOUCH_APP "\xee\xa4\x93" // U+e913 #define ICON_MS_TOUCHPAD_MOUSE "\xef\x9a\x87" // U+f687 #define ICON_MS_TOUCHPAD_MOUSE_OFF "\xef\x93\xa6" // U+f4e6 #define ICON_MS_TOUR "\xee\xbd\xb5" // U+ef75 #define ICON_MS_TOYS "\xee\x8c\xb2" // U+e332 #define ICON_MS_TOYS_AND_GAMES "\xee\xbf\x82" // U+efc2 #define ICON_MS_TOYS_FAN "\xef\xa2\x87" // U+f887 #define ICON_MS_TRACK_CHANGES "\xee\xa3\xa1" // U+e8e1 #define ICON_MS_TRACKPAD_INPUT "\xef\x93\x87" // U+f4c7 #define ICON_MS_TRAFFIC "\xee\x95\xa5" // U+e565 #define ICON_MS_TRAIL_LENGTH "\xee\xad\x9e" // U+eb5e #define ICON_MS_TRAIL_LENGTH_MEDIUM "\xee\xad\xa3" // U+eb63 #define ICON_MS_TRAIL_LENGTH_SHORT "\xee\xad\xad" // U+eb6d #define ICON_MS_TRAIN "\xee\x95\xb0" // U+e570 #define ICON_MS_TRAM "\xee\x95\xb1" // U+e571 #define ICON_MS_TRANSCRIBE "\xef\xa3\xac" // U+f8ec #define ICON_MS_TRANSFER_WITHIN_A_STATION "\xee\x95\xb2" // U+e572 #define ICON_MS_TRANSFORM "\xee\x90\xa8" // U+e428 #define ICON_MS_TRANSGENDER "\xee\x96\x8d" // U+e58d #define ICON_MS_TRANSIT_ENTEREXIT "\xee\x95\xb9" // U+e579 #define ICON_MS_TRANSITION_CHOP "\xef\x94\x8e" // U+f50e #define ICON_MS_TRANSITION_DISSOLVE "\xef\x94\x8d" // U+f50d #define ICON_MS_TRANSITION_FADE "\xef\x94\x8c" // U+f50c #define ICON_MS_TRANSITION_PUSH "\xef\x94\x8b" // U+f50b #define ICON_MS_TRANSITION_SLIDE "\xef\x94\x8a" // U+f50a #define ICON_MS_TRANSLATE "\xee\xa3\xa2" // U+e8e2 #define ICON_MS_TRANSPORTATION "\xee\x88\x9d" // U+e21d #define ICON_MS_TRAVEL "\xee\xbe\x93" // U+ef93 #define ICON_MS_TRAVEL_EXPLORE "\xee\x8b\x9b" // U+e2db #define ICON_MS_TRAVEL_LUGGAGE_AND_BAGS "\xee\xbf\x83" // U+efc3 #define ICON_MS_TRENDING_DOWN "\xee\xa3\xa3" // U+e8e3 #define ICON_MS_TRENDING_FLAT "\xee\xa3\xa4" // U+e8e4 #define ICON_MS_TRENDING_UP "\xee\xa3\xa5" // U+e8e5 #define ICON_MS_TRIP "\xee\x9b\xbb" // U+e6fb #define ICON_MS_TRIP_ORIGIN "\xee\x95\xbb" // U+e57b #define ICON_MS_TROLLEY "\xef\xa1\xab" // U+f86b #define ICON_MS_TROPHY "\xee\xa8\xa3" // U+ea23 #define ICON_MS_TROUBLESHOOT "\xee\x87\x92" // U+e1d2 #define ICON_MS_TRY "\xef\x81\xbc" // U+f07c #define ICON_MS_TSUNAMI "\xee\xaf\x98" // U+ebd8 #define ICON_MS_TSV "\xee\x9b\x96" // U+e6d6 #define ICON_MS_TTY "\xef\x86\xaa" // U+f1aa #define ICON_MS_TUNE "\xee\x90\xa9" // U+e429 #define ICON_MS_TUNGSTEN "\xef\x81\xbd" // U+f07d #define ICON_MS_TURN_LEFT "\xee\xae\xa6" // U+eba6 #define ICON_MS_TURN_RIGHT "\xee\xae\xab" // U+ebab #define ICON_MS_TURN_SHARP_LEFT "\xee\xae\xa7" // U+eba7 #define ICON_MS_TURN_SHARP_RIGHT "\xee\xae\xaa" // U+ebaa #define ICON_MS_TURN_SLIGHT_LEFT "\xee\xae\xa4" // U+eba4 #define ICON_MS_TURN_SLIGHT_RIGHT "\xee\xae\x9a" // U+eb9a #define ICON_MS_TURNED_IN "\xee\xa3\xa7" // U+e8e7 #define ICON_MS_TURNED_IN_NOT "\xee\xa3\xa7" // U+e8e7 #define ICON_MS_TV "\xee\x98\xbb" // U+e63b #define ICON_MS_TV_GEN "\xee\xa0\xb0" // U+e830 #define ICON_MS_TV_GUIDE "\xee\x87\x9c" // U+e1dc #define ICON_MS_TV_OFF "\xee\x99\x87" // U+e647 #define ICON_MS_TV_OPTIONS_EDIT_CHANNELS "\xee\x87\x9d" // U+e1dd #define ICON_MS_TV_OPTIONS_INPUT_SETTINGS "\xee\x87\x9e" // U+e1de #define ICON_MS_TV_REMOTE "\xef\x97\x99" // U+f5d9 #define ICON_MS_TV_SIGNIN "\xee\x9c\x9b" // U+e71b #define ICON_MS_TV_WITH_ASSISTANT "\xee\x9e\x85" // U+e785 #define ICON_MS_TWO_PAGER "\xef\x94\x9f" // U+f51f #define ICON_MS_TWO_WHEELER "\xee\xa7\xb9" // U+e9f9 #define ICON_MS_TYPE_SPECIMEN "\xef\xa3\xb0" // U+f8f0 #define ICON_MS_U_TURN_LEFT "\xee\xae\xa1" // U+eba1 #define ICON_MS_U_TURN_RIGHT "\xee\xae\xa2" // U+eba2 #define ICON_MS_ULNA_RADIUS "\xef\xa2\x9d" // U+f89d #define ICON_MS_ULNA_RADIUS_ALT "\xef\xa2\x9e" // U+f89e #define ICON_MS_UMBRELLA "\xef\x86\xad" // U+f1ad #define ICON_MS_UNARCHIVE "\xee\x85\xa9" // U+e169 #define ICON_MS_UNDO "\xee\x85\xa6" // U+e166 #define ICON_MS_UNFOLD_LESS "\xee\x97\x96" // U+e5d6 #define ICON_MS_UNFOLD_LESS_DOUBLE "\xef\xa3\x8f" // U+f8cf #define ICON_MS_UNFOLD_MORE "\xee\x97\x97" // U+e5d7 #define ICON_MS_UNFOLD_MORE_DOUBLE "\xef\xa3\x90" // U+f8d0 #define ICON_MS_UNGROUP "\xef\x9c\xb1" // U+f731 #define ICON_MS_UNIVERSAL_CURRENCY "\xee\xa7\xba" // U+e9fa #define ICON_MS_UNIVERSAL_CURRENCY_ALT "\xee\x9c\xb4" // U+e734 #define ICON_MS_UNIVERSAL_LOCAL "\xee\xa7\xbb" // U+e9fb #define ICON_MS_UNKNOWN_2 "\xef\x92\x9f" // U+f49f #define ICON_MS_UNKNOWN_5 "\xee\x9a\xa5" // U+e6a5 #define ICON_MS_UNKNOWN_7 "\xef\x92\x9e" // U+f49e #define ICON_MS_UNKNOWN_DOCUMENT "\xef\xa0\x84" // U+f804 #define ICON_MS_UNKNOWN_MED "\xee\xaa\xbd" // U+eabd #define ICON_MS_UNLICENSE "\xee\xac\x85" // U+eb05 #define ICON_MS_UNPIN "\xee\x9b\xb9" // U+e6f9 #define ICON_MS_UNPUBLISHED "\xef\x88\xb6" // U+f236 #define ICON_MS_UNSUBSCRIBE "\xee\x83\xab" // U+e0eb #define ICON_MS_UPCOMING "\xef\x81\xbe" // U+f07e #define ICON_MS_UPDATE "\xee\xa4\xa3" // U+e923 #define ICON_MS_UPDATE_DISABLED "\xee\x81\xb5" // U+e075 #define ICON_MS_UPGRADE "\xef\x83\xbb" // U+f0fb #define ICON_MS_UPLOAD "\xef\x82\x9b" // U+f09b #define ICON_MS_UPLOAD_2 "\xef\x94\xa1" // U+f521 #define ICON_MS_UPLOAD_FILE "\xee\xa7\xbc" // U+e9fc #define ICON_MS_UPPERCASE "\xef\x92\x88" // U+f488 #define ICON_MS_UROLOGY "\xee\x84\xb7" // U+e137 #define ICON_MS_USB "\xee\x87\xa0" // U+e1e0 #define ICON_MS_USB_OFF "\xee\x93\xba" // U+e4fa #define ICON_MS_USER_ATTRIBUTES "\xee\x9c\x88" // U+e708 #define ICON_MS_VACCINES "\xee\x84\xb8" // U+e138 #define ICON_MS_VACUUM "\xee\xbf\x85" // U+efc5 #define ICON_MS_VALVE "\xee\x88\xa4" // U+e224 #define ICON_MS_VAPE_FREE "\xee\xaf\x86" // U+ebc6 #define ICON_MS_VAPING_ROOMS "\xee\xaf\x8f" // U+ebcf #define ICON_MS_VARIABLE_ADD "\xef\x94\x9e" // U+f51e #define ICON_MS_VARIABLE_INSERT "\xef\x94\x9d" // U+f51d #define ICON_MS_VARIABLE_REMOVE "\xef\x94\x9c" // U+f51c #define ICON_MS_VARIABLES "\xef\xa1\x91" // U+f851 #define ICON_MS_VENTILATOR "\xee\x84\xb9" // U+e139 #define ICON_MS_VERIFIED "\xee\xbd\xb6" // U+ef76 #define ICON_MS_VERIFIED_USER "\xef\x80\x93" // U+f013 #define ICON_MS_VERTICAL_ALIGN_BOTTOM "\xee\x89\x98" // U+e258 #define ICON_MS_VERTICAL_ALIGN_CENTER "\xee\x89\x99" // U+e259 #define ICON_MS_VERTICAL_ALIGN_TOP "\xee\x89\x9a" // U+e25a #define ICON_MS_VERTICAL_DISTRIBUTE "\xee\x81\xb6" // U+e076 #define ICON_MS_VERTICAL_SHADES "\xee\xb0\x8e" // U+ec0e #define ICON_MS_VERTICAL_SHADES_CLOSED "\xee\xb0\x8d" // U+ec0d #define ICON_MS_VERTICAL_SPLIT "\xee\xa5\x89" // U+e949 #define ICON_MS_VIBRATION "\xee\x98\xad" // U+e62d #define ICON_MS_VIDEO_CALL "\xee\x81\xb0" // U+e070 #define ICON_MS_VIDEO_CAMERA_BACK "\xef\x81\xbf" // U+f07f #define ICON_MS_VIDEO_CAMERA_FRONT "\xef\x82\x80" // U+f080 #define ICON_MS_VIDEO_CAMERA_FRONT_OFF "\xef\xa0\xbb" // U+f83b #define ICON_MS_VIDEO_CHAT "\xef\xa2\xa0" // U+f8a0 #define ICON_MS_VIDEO_FILE "\xee\xae\x87" // U+eb87 #define ICON_MS_VIDEO_LABEL "\xee\x81\xb1" // U+e071 #define ICON_MS_VIDEO_LIBRARY "\xee\x81\x8a" // U+e04a #define ICON_MS_VIDEO_SEARCH "\xee\xbf\x86" // U+efc6 #define ICON_MS_VIDEO_SETTINGS "\xee\xa9\xb5" // U+ea75 #define ICON_MS_VIDEO_STABLE "\xef\x82\x81" // U+f081 #define ICON_MS_VIDEOCAM "\xee\x81\x8b" // U+e04b #define ICON_MS_VIDEOCAM_OFF "\xee\x81\x8c" // U+e04c #define ICON_MS_VIDEOGAME_ASSET "\xee\x8c\xb8" // U+e338 #define ICON_MS_VIDEOGAME_ASSET_OFF "\xee\x94\x80" // U+e500 #define ICON_MS_VIEW_AGENDA "\xee\xa3\xa9" // U+e8e9 #define ICON_MS_VIEW_ARRAY "\xee\xa3\xaa" // U+e8ea #define ICON_MS_VIEW_CAROUSEL "\xee\xa3\xab" // U+e8eb #define ICON_MS_VIEW_COLUMN "\xee\xa3\xac" // U+e8ec #define ICON_MS_VIEW_COLUMN_2 "\xef\xa1\x87" // U+f847 #define ICON_MS_VIEW_COMFY "\xee\x90\xaa" // U+e42a #define ICON_MS_VIEW_COMFY_ALT "\xee\xad\xb3" // U+eb73 #define ICON_MS_VIEW_COMPACT "\xee\x90\xab" // U+e42b #define ICON_MS_VIEW_COMPACT_ALT "\xee\xad\xb4" // U+eb74 #define ICON_MS_VIEW_COZY "\xee\xad\xb5" // U+eb75 #define ICON_MS_VIEW_DAY "\xee\xa3\xad" // U+e8ed #define ICON_MS_VIEW_HEADLINE "\xee\xa3\xae" // U+e8ee #define ICON_MS_VIEW_IN_AR "\xee\xbf\x89" // U+efc9 #define ICON_MS_VIEW_IN_AR_NEW "\xee\xbf\x89" // U+efc9 #define ICON_MS_VIEW_IN_AR_OFF "\xef\x98\x9b" // U+f61b #define ICON_MS_VIEW_KANBAN "\xee\xad\xbf" // U+eb7f #define ICON_MS_VIEW_LIST "\xee\xa3\xaf" // U+e8ef #define ICON_MS_VIEW_MODULE "\xee\xa3\xb0" // U+e8f0 #define ICON_MS_VIEW_QUILT "\xee\xa3\xb1" // U+e8f1 #define ICON_MS_VIEW_REAL_SIZE "\xef\x93\x82" // U+f4c2 #define ICON_MS_VIEW_SIDEBAR "\xef\x84\x94" // U+f114 #define ICON_MS_VIEW_STREAM "\xee\xa3\xb2" // U+e8f2 #define ICON_MS_VIEW_TIMELINE "\xee\xae\x85" // U+eb85 #define ICON_MS_VIEW_WEEK "\xee\xa3\xb3" // U+e8f3 #define ICON_MS_VIGNETTE "\xee\x90\xb5" // U+e435 #define ICON_MS_VILLA "\xee\x96\x86" // U+e586 #define ICON_MS_VISIBILITY "\xee\xa3\xb4" // U+e8f4 #define ICON_MS_VISIBILITY_LOCK "\xef\x99\x93" // U+f653 #define ICON_MS_VISIBILITY_OFF "\xee\xa3\xb5" // U+e8f5 #define ICON_MS_VITAL_SIGNS "\xee\x99\x90" // U+e650 #define ICON_MS_VITALS "\xee\x84\xbb" // U+e13b #define ICON_MS_VO2_MAX "\xef\x92\xaa" // U+f4aa #define ICON_MS_VOICE_CHAT "\xee\x98\xae" // U+e62e #define ICON_MS_VOICE_OVER_OFF "\xee\xa5\x8a" // U+e94a #define ICON_MS_VOICE_SELECTION "\xef\x96\x8a" // U+f58a #define ICON_MS_VOICEMAIL "\xee\x83\x99" // U+e0d9 #define ICON_MS_VOLCANO "\xee\xaf\x9a" // U+ebda #define ICON_MS_VOLUME_DOWN "\xee\x81\x8d" // U+e04d #define ICON_MS_VOLUME_DOWN_ALT "\xee\x9e\x9c" // U+e79c #define ICON_MS_VOLUME_MUTE "\xee\x81\x8e" // U+e04e #define ICON_MS_VOLUME_OFF "\xee\x81\x8f" // U+e04f #define ICON_MS_VOLUME_UP "\xee\x81\x90" // U+e050 #define ICON_MS_VOLUNTEER_ACTIVISM "\xee\xa9\xb0" // U+ea70 #define ICON_MS_VOTING_CHIP "\xef\xa1\x92" // U+f852 #define ICON_MS_VPN_KEY "\xee\x83\x9a" // U+e0da #define ICON_MS_VPN_KEY_ALERT "\xef\x9b\x8c" // U+f6cc #define ICON_MS_VPN_KEY_OFF "\xee\xad\xba" // U+eb7a #define ICON_MS_VPN_LOCK "\xee\x98\xaf" // U+e62f #define ICON_MS_VR180_CREATE2D "\xee\xbf\x8a" // U+efca #define ICON_MS_VR180_CREATE2D_OFF "\xef\x95\xb1" // U+f571 #define ICON_MS_VRPANO "\xef\x82\x82" // U+f082 #define ICON_MS_WALL_ART "\xee\xbf\x8b" // U+efcb #define ICON_MS_WALL_LAMP "\xee\x8a\xb4" // U+e2b4 #define ICON_MS_WALLET "\xef\xa3\xbf" // U+f8ff #define ICON_MS_WALLPAPER "\xee\x86\xbc" // U+e1bc #define ICON_MS_WALLPAPER_SLIDESHOW "\xef\x99\xb2" // U+f672 #define ICON_MS_WARD "\xee\x84\xbc" // U+e13c #define ICON_MS_WAREHOUSE "\xee\xae\xb8" // U+ebb8 #define ICON_MS_WARNING "\xef\x82\x83" // U+f083 #define ICON_MS_WARNING_AMBER "\xef\x82\x83" // U+f083 #define ICON_MS_WARNING_OFF "\xef\x9e\xad" // U+f7ad #define ICON_MS_WASH "\xef\x86\xb1" // U+f1b1 #define ICON_MS_WATCH "\xee\x8c\xb4" // U+e334 #define ICON_MS_WATCH_BUTTON_PRESS "\xef\x9a\xaa" // U+f6aa #define ICON_MS_WATCH_LATER "\xee\xbf\x96" // U+efd6 #define ICON_MS_WATCH_OFF "\xee\xab\xa3" // U+eae3 #define ICON_MS_WATCH_SCREENTIME "\xef\x9a\xae" // U+f6ae #define ICON_MS_WATCH_WAKE "\xef\x9a\xa9" // U+f6a9 #define ICON_MS_WATER "\xef\x82\x84" // U+f084 #define ICON_MS_WATER_BOTTLE "\xef\x9a\x9d" // U+f69d #define ICON_MS_WATER_BOTTLE_LARGE "\xef\x9a\x9e" // U+f69e #define ICON_MS_WATER_DAMAGE "\xef\x88\x83" // U+f203 #define ICON_MS_WATER_DO "\xef\xa1\xb0" // U+f870 #define ICON_MS_WATER_DROP "\xee\x9e\x98" // U+e798 #define ICON_MS_WATER_EC "\xef\xa1\xb5" // U+f875 #define ICON_MS_WATER_FULL "\xef\x9b\x96" // U+f6d6 #define ICON_MS_WATER_HEATER "\xee\x8a\x84" // U+e284 #define ICON_MS_WATER_LOCK "\xef\x9a\xad" // U+f6ad #define ICON_MS_WATER_LOSS "\xef\x9b\x95" // U+f6d5 #define ICON_MS_WATER_LUX "\xef\xa1\xb4" // U+f874 #define ICON_MS_WATER_MEDIUM "\xef\x9b\x94" // U+f6d4 #define ICON_MS_WATER_ORP "\xef\xa1\xb8" // U+f878 #define ICON_MS_WATER_PH "\xef\xa1\xba" // U+f87a #define ICON_MS_WATER_PUMP "\xef\x97\x98" // U+f5d8 #define ICON_MS_WATER_VOC "\xef\xa1\xbb" // U+f87b #define ICON_MS_WATERFALL_CHART "\xee\xa8\x80" // U+ea00 #define ICON_MS_WAVES "\xee\x85\xb6" // U+e176 #define ICON_MS_WAVING_HAND "\xee\x9d\xa6" // U+e766 #define ICON_MS_WB_AUTO "\xee\x90\xac" // U+e42c #define ICON_MS_WB_CLOUDY "\xef\x85\x9c" // U+f15c #define ICON_MS_WB_INCANDESCENT "\xee\x90\xae" // U+e42e #define ICON_MS_WB_IRIDESCENT "\xef\x81\xbd" // U+f07d #define ICON_MS_WB_SHADE "\xee\xa8\x81" // U+ea01 #define ICON_MS_WB_SUNNY "\xee\x90\xb0" // U+e430 #define ICON_MS_WB_TWILIGHT "\xee\x87\x86" // U+e1c6 #define ICON_MS_WC "\xee\x98\xbd" // U+e63d #define ICON_MS_WEATHER_HAIL "\xef\x99\xbf" // U+f67f #define ICON_MS_WEATHER_MIX "\xef\x98\x8b" // U+f60b #define ICON_MS_WEATHER_SNOWY "\xee\x8b\x8d" // U+e2cd #define ICON_MS_WEB "\xee\x81\x91" // U+e051 #define ICON_MS_WEB_ASSET "\xee\x81\xa9" // U+e069 #define ICON_MS_WEB_ASSET_OFF "\xee\xbd\x87" // U+ef47 #define ICON_MS_WEB_STORIES "\xee\x96\x95" // U+e595 #define ICON_MS_WEB_TRAFFIC "\xee\xa8\x83" // U+ea03 #define ICON_MS_WEBHOOK "\xee\xae\x92" // U+eb92 #define ICON_MS_WEEKEND "\xee\x85\xab" // U+e16b #define ICON_MS_WEIGHT "\xee\x84\xbd" // U+e13d #define ICON_MS_WEST "\xef\x87\xa6" // U+f1e6 #define ICON_MS_WHATSHOT "\xee\xa0\x8e" // U+e80e #define ICON_MS_WHEELCHAIR_PICKUP "\xef\x86\xab" // U+f1ab #define ICON_MS_WHERE_TO_VOTE "\xee\x85\xb7" // U+e177 #define ICON_MS_WIDGETS "\xee\x86\xbd" // U+e1bd #define ICON_MS_WIDTH "\xef\x9c\xb0" // U+f730 #define ICON_MS_WIDTH_FULL "\xef\xa3\xb5" // U+f8f5 #define ICON_MS_WIDTH_NORMAL "\xef\xa3\xb6" // U+f8f6 #define ICON_MS_WIDTH_WIDE "\xef\xa3\xb7" // U+f8f7 #define ICON_MS_WIFI "\xee\x98\xbe" // U+e63e #define ICON_MS_WIFI_1_BAR "\xee\x93\x8a" // U+e4ca #define ICON_MS_WIFI_2_BAR "\xee\x93\x99" // U+e4d9 #define ICON_MS_WIFI_ADD "\xef\x9e\xa8" // U+f7a8 #define ICON_MS_WIFI_CALLING "\xee\xbd\xb7" // U+ef77 #define ICON_MS_WIFI_CALLING_1 "\xef\x83\xb6" // U+f0f6 #define ICON_MS_WIFI_CALLING_2 "\xef\x83\xb6" // U+f0f6 #define ICON_MS_WIFI_CALLING_3 "\xef\x83\xb6" // U+f0f6 #define ICON_MS_WIFI_CHANNEL "\xee\xad\xaa" // U+eb6a #define ICON_MS_WIFI_FIND "\xee\xac\xb1" // U+eb31 #define ICON_MS_WIFI_HOME "\xef\x99\xb1" // U+f671 #define ICON_MS_WIFI_LOCK "\xee\x87\xa1" // U+e1e1 #define ICON_MS_WIFI_NOTIFICATION "\xef\x99\xb0" // U+f670 #define ICON_MS_WIFI_OFF "\xee\x99\x88" // U+e648 #define ICON_MS_WIFI_PASSWORD "\xee\xad\xab" // U+eb6b #define ICON_MS_WIFI_PROTECTED_SETUP "\xef\x83\xbc" // U+f0fc #define ICON_MS_WIFI_PROXY "\xef\x9e\xa7" // U+f7a7 #define ICON_MS_WIFI_TETHERING "\xee\x87\xa2" // U+e1e2 #define ICON_MS_WIFI_TETHERING_ERROR "\xee\xab\x99" // U+ead9 #define ICON_MS_WIFI_TETHERING_OFF "\xef\x82\x87" // U+f087 #define ICON_MS_WIND_POWER "\xee\xb0\x8c" // U+ec0c #define ICON_MS_WINDOW "\xef\x82\x88" // U+f088 #define ICON_MS_WINDOW_CLOSED "\xee\x9d\xbe" // U+e77e #define ICON_MS_WINDOW_OPEN "\xee\x9e\x8c" // U+e78c #define ICON_MS_WINDOW_SENSOR "\xee\x8a\xbb" // U+e2bb #define ICON_MS_WINE_BAR "\xef\x87\xa8" // U+f1e8 #define ICON_MS_WOMAN "\xee\x84\xbe" // U+e13e #define ICON_MS_WOMAN_2 "\xef\xa3\xa7" // U+f8e7 #define ICON_MS_WORK "\xee\xa5\x83" // U+e943 #define ICON_MS_WORK_ALERT "\xef\x97\xb7" // U+f5f7 #define ICON_MS_WORK_HISTORY "\xee\xb0\x89" // U+ec09 #define ICON_MS_WORK_OFF "\xee\xa5\x82" // U+e942 #define ICON_MS_WORK_OUTLINE "\xee\xa5\x83" // U+e943 #define ICON_MS_WORK_UPDATE "\xef\x97\xb8" // U+f5f8 #define ICON_MS_WORKFLOW "\xee\xa8\x84" // U+ea04 #define ICON_MS_WORKSPACE_PREMIUM "\xee\x9e\xaf" // U+e7af #define ICON_MS_WORKSPACES "\xee\xa8\x8f" // U+ea0f #define ICON_MS_WORKSPACES_OUTLINE "\xee\xa8\x8f" // U+ea0f #define ICON_MS_WOUNDS_INJURIES "\xee\x84\xbf" // U+e13f #define ICON_MS_WRAP_TEXT "\xee\x89\x9b" // U+e25b #define ICON_MS_WRIST "\xef\x9a\x9c" // U+f69c #define ICON_MS_WRONG_LOCATION "\xee\xbd\xb8" // U+ef78 #define ICON_MS_WYSIWYG "\xef\x87\x83" // U+f1c3 #define ICON_MS_YARD "\xef\x82\x89" // U+f089 #define ICON_MS_YOUR_TRIPS "\xee\xac\xab" // U+eb2b #define ICON_MS_YOUTUBE_ACTIVITY "\xef\xa1\x9a" // U+f85a #define ICON_MS_YOUTUBE_SEARCHED_FOR "\xee\xa3\xba" // U+e8fa #define ICON_MS_ZONE_PERSON_ALERT "\xee\x9e\x81" // U+e781 #define ICON_MS_ZONE_PERSON_IDLE "\xee\x9d\xba" // U+e77a #define ICON_MS_ZONE_PERSON_URGENT "\xee\x9e\x88" // U+e788 #define ICON_MS_ZOOM_IN "\xee\xa3\xbf" // U+e8ff #define ICON_MS_ZOOM_IN_MAP "\xee\xac\xad" // U+eb2d #define ICON_MS_ZOOM_OUT "\xee\xa4\x80" // U+e900 #define ICON_MS_ZOOM_OUT_MAP "\xee\x95\xab" // U+e56b ================================================ FILE: frontend/settings.cpp ================================================ #include "iris.hpp" #include "config.hpp" #include "ps2_elf.h" #include "ps2_iso9660.h" #define TOML_EXCEPTIONS 0 #include namespace iris::settings { void print_version() { puts( "iris (" STR(_IRIS_VERSION) " " STR(_IRIS_OSVERSION) ")\n" "Copyright (C) 2026 Allkern/Lisandro Alarcon\n\n" "MIT License\n" "THE SOFTWARE IS PROVIDED \"AS IS\", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\n" "IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\n" "FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\n" "AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\n" "LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\n" "OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE\n" "SOFTWARE." ); } void print_help() { puts( "Usage: iris [OPTION]... \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" " --snap Specify a directory for storing screenshots\n" " -h, --help Display this help and exit\n" " -v, --version Output version information and exit\n" ); } bool parse_mappings_file(iris::instance* iris) { iris->mappings_path = iris->pref_path + "mappings.toml"; std::ifstream mappings_file(iris->mappings_path); if (!mappings_file.is_open()) return false; toml::parse_result result = toml::parse(mappings_file); if (!result) { std::string desc(result.error().description()); printf("iris: Couldn't parse mappings file: %s\n", desc.c_str()); return false; } toml::table& tbl = result.table(); for (auto& map : tbl) { printf("input: Parsing input map \"%s\"...\n", map.first.data()); mapping input_mapping {}; input_mapping.name = map.first.data(); input_mapping.map = bidirectional_map(); for (auto& input_map : *map.second.as_table()) { uint64_t key = std::stoull(input_map.first.data()); uint64_t value = input_map.second.as_integer()->get(); input_mapping.map.insert(key, static_cast(value)); // printf("entry: %d -> %d\n", std::stoull(input_map.first.data()), input_map.second.as_integer()->get()); } iris->input_maps.push_back(input_mapping); } return true; } bool parse_toml_settings(iris::instance* iris) { iris->settings_path = iris->pref_path + "settings.toml"; toml::parse_result result = toml::parse_file(iris->settings_path); if (!result) { std::string desc(result.error().description()); printf("iris: Couldn't parse settings file: %s\n", desc.c_str()); return false; } toml::table& tbl = result.table(); auto paths = tbl["paths"]; iris->bios_path = paths["bios_path"].value_or(""); iris->rom1_path = paths["rom1_path"].value_or(""); iris->rom2_path = paths["rom2_path"].value_or(""); iris->nvram_path = paths["nvram_path"].value_or(""); iris->mcd0_path = paths["mcd0_path"].value_or(""); iris->mcd1_path = paths["mcd1_path"].value_or(""); iris->snap_path = paths["snap_path"].value_or("snap"); iris->flash_path = paths["flash_path"].value_or(""); iris->gcdb_path = paths["gcdb_path"].value_or(""); auto window = tbl["window"]; iris->window_width = window["window_width"].value_or(960); iris->window_height = window["window_height"].value_or(720); iris->fullscreen = window["fullscreen"].value_or(0); auto display = tbl["display"]; iris->aspect_mode = display["aspect_mode"].value_or(RENDER_ASPECT_AUTO); iris->filter = display["filter"].value_or(true); iris->integer_scaling = display["integer_scaling"].value_or(false); iris->scale = display["scale"].value_or(1.5f); iris->renderer_backend = display["renderer"].value_or(RENDERER_BACKEND_HARDWARE); iris->window_width = display["window_width"].value_or(960); iris->window_height = display["window_height"].value_or(720); iris->menubar_height = display["menubar_height"].value_or(0); iris->angle = display["angle"].value_or(0); iris->flip_x = display["flip_x"].value_or(false); iris->flip_y = display["flip_y"].value_or(false); iris->vsync = display["vsync"].value_or(true); auto audio = tbl["audio"]; iris->mute = audio["mute"].value_or(false); iris->volume = audio["volume"].value_or(1.0); iris->mute_adma = audio["mute_adma"].value_or(true); auto debugger = tbl["debugger"]; iris->show_ee_control = debugger["show_ee_control"].value_or(false); iris->show_ee_state = debugger["show_ee_state"].value_or(false); iris->show_ee_logs = debugger["show_ee_logs"].value_or(false); iris->show_ee_interrupts = debugger["show_ee_interrupts"].value_or(false); iris->show_ee_dmac = debugger["show_ee_dmac"].value_or(false); iris->show_iop_control = debugger["show_iop_control"].value_or(false); iris->show_iop_state = debugger["show_iop_state"].value_or(false); iris->show_iop_logs = debugger["show_iop_logs"].value_or(false); iris->show_iop_interrupts = debugger["show_iop_interrupts"].value_or(false); iris->show_iop_modules = debugger["show_iop_modules"].value_or(false); iris->show_iop_dma = debugger["show_iop_dma"].value_or(false); iris->show_gs_debugger = debugger["show_gs_debugger"].value_or(false); iris->show_spu2_debugger = debugger["show_spu2_debugger"].value_or(false); iris->show_memory_viewer = debugger["show_memory_viewer"].value_or(false); iris->show_memory_search = debugger["show_memory_search"].value_or(false); iris->show_vu_disassembler = debugger["show_vu_disassembler"].value_or(false); iris->show_status_bar = debugger["show_status_bar"].value_or(true); iris->show_pad_debugger = debugger["show_pad_debugger"].value_or(false); iris->show_threads = debugger["show_threads"].value_or(false); iris->show_sysmem_logs = debugger["show_sysmem_logs"].value_or(false); iris->show_overlay = debugger["show_overlay"].value_or(false); // iris->show_symbols = debugger["show_symbols"].value_or(false); iris->show_breakpoints = debugger["show_breakpoints"].value_or(false); iris->show_imgui_demo = debugger["show_imgui_demo"].value_or(false); iris->skip_fmv = debugger["skip_fmv"].value_or(false); iris->timescale = debugger["timescale"].value_or(8); auto system = tbl["system"]; iris->system = system["model"].value_or(PS2_SYSTEM_AUTO); iris->autostart = system["autostart"].value_or(true); toml::array* mac_array = system["mac_address"].as_array(); if (mac_array && mac_array->size() == 6) { for (int i = 0; i < 6; i++) { iris->mac_address[i] = static_cast(mac_array->at(i).as_integer()->get()); } } else { // Default MAC address iris->mac_address[0] = 0x00; iris->mac_address[1] = 0x1A; iris->mac_address[2] = 0x2B; iris->mac_address[3] = 0x3C; iris->mac_address[4] = 0x4D; iris->mac_address[5] = 0x5E; } auto screenshots = tbl["screenshots"]; iris->screenshot_format = screenshots["format"].value_or(IRIS_SCREENSHOT_FORMAT_PNG); iris->screenshot_jpg_quality_mode = screenshots["jpg_quality_mode"].value_or(IRIS_SCREENSHOT_JPG_QUALITY_MAXIMUM); iris->screenshot_jpg_quality = screenshots["jpg_quality"].value_or(50); iris->screenshot_mode = screenshots["mode"].value_or(IRIS_SCREENSHOT_MODE_INTERNAL); iris->screenshot_shader_processing = screenshots["shader_processing"].value_or(false); auto hardware = tbl["hardware"]; iris->hardware_backend_config.super_sampling = hardware["super_sampling"].value_or(0); iris->hardware_backend_config.force_progressive = hardware["force_progressive"].value_or(false); iris->hardware_backend_config.overscan = hardware["overscan"].value_or(false); iris->hardware_backend_config.crtc_offsets = hardware["crtc_offsets"].value_or(false); iris->hardware_backend_config.disable_mipmaps = hardware["disable_mipmaps"].value_or(false); iris->hardware_backend_config.unsynced_readbacks = hardware["unsynced_readbacks"].value_or(false); iris->hardware_backend_config.backbuffer_promotion = hardware["backbuffer_promotion"].value_or(false); iris->hardware_backend_config.allow_blend_demote = hardware["allow_blend_demote"].value_or(false); auto vulkan = tbl["vulkan"]; iris->vulkan_physical_device = vulkan["physical_device"].value_or(-1); iris->vulkan_enable_validation_layers = vulkan["enable_validation_layers"].value_or(false); auto ui = tbl["ui"]; iris->theme = ui["theme"].value_or(IRIS_THEME_GRANITE); iris->codeview_font_scale = ui["codeview_font_scale"].value_or(1.0f); iris->codeview_color_scheme = ui["codeview_color_scheme"].value_or(IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_DARK); iris->codeview_use_theme_background = ui["codeview_use_theme_background"].value_or(true); iris->ui_scale = ui["scale"].value_or(1.0f); iris->imgui_enable_viewports = ui["enable_viewports"].value_or(false); toml::array* bgcolor = tbl["ui"]["bgcolor"].as_array(); if (bgcolor && bgcolor->size() == 3) { iris->clear_value.color.float32[0] = (float)bgcolor->at(0).as_floating_point()->get(); iris->clear_value.color.float32[1] = (float)bgcolor->at(1).as_floating_point()->get(); iris->clear_value.color.float32[2] = (float)bgcolor->at(2).as_floating_point()->get(); } else { iris->clear_value.color.float32[0] = 0.11f; iris->clear_value.color.float32[1] = 0.11f; iris->clear_value.color.float32[2] = 0.11f; } #ifdef _WIN32 iris->windows_titlebar_style = tbl["ui"]["windows_titlebar_style"].value_or(IRIS_TITLEBAR_DEFAULT); iris->windows_enable_borders = tbl["ui"]["windows_enable_borders"].value_or(true); iris->windows_dark_mode = tbl["ui"]["windows_dark_mode"].value_or(true); #endif toml::array* recents = tbl["recents"]["array"].as_array(); if (recents) { for (int i = 0; i < recents->size(); i++) { toml::table* entry = recents->at(i).as_table(); if (!entry) { // Provided for backcompat with older settings files std::string str = recents->at(i).as_string()->get(); iris->recents.push_back({ str, 0 }); continue; } iris::recent r = { entry->operator[]("path").value_or(std::string()), entry->operator[]("type").value_or(0) }; iris->recents.push_back(r); } } toml::array* shaders = tbl["shaders"]["array"].as_array(); iris->enable_shaders = tbl["shaders"]["enable"].value_or(false); if (shaders) { for (int i = 0; i < shaders->size(); i++) iris->shader_passes_pending.push_back(shaders->at(i).as_string()->get()); } return parse_mappings_file(iris); } bool check_for_quick_exit(int argc, const char* argv[]) { for (int i = 1; i < argc; i++) { std::string a(argv[i]); if (a == "-h" || a == "--help") { print_help(); return true; } else if (a == "-v" || a == "--version") { print_version(); return true; } } return false; } void parse_cli_settings(iris::instance* iris, int argc, const char* argv[]) { std::string bios_path; std::string rom1_path; std::string rom2_path; for (int i = 1; i < argc; i++) { std::string a(argv[i]); if (a == "-x" || a == "--executable") { iris->elf_path = argv[i+1]; ++i; } else if (a == "-d" || a == "--boot") { iris->boot_path = argv[i+1]; ++i; } else if (a == "-b" || a == "--bios") { bios_path = argv[i+1]; ++i; } else if (a == "--rom1") { rom1_path = argv[i+1]; ++i; } else if (a == "--rom2") { rom2_path = argv[i+1]; ++i; } else if (a == "-i" || a == "--disc") { iris->disc_path = argv[i+1]; ++i; } else if (a == "--slot1") { iris->mcd0_path = argv[i+1]; ++i; } else if (a == "--slot2") { iris->mcd1_path = argv[i+1]; ++i; } else { iris->disc_path = argv[i]; } } if (bios_path.size()) { if (!ps2_load_bios(iris->ps2, bios_path.c_str())) { // push_info(iris, "Couldn't load BIOS"); iris->show_bios_setting_window = true; } } else { if (iris->bios_path.size()) { if (!ps2_load_bios(iris->ps2, iris->bios_path.c_str())) { // push_info(iris, "Couldn't load BIOS"); iris->show_bios_setting_window = true; } } else { iris->show_bios_setting_window = true; } } if (rom1_path.size()) { if (!ps2_load_rom1(iris->ps2, rom1_path.c_str())) { // push_info(iris, "Couldn't load ROM1"); } } else { if (iris->rom1_path.size()) { if (!ps2_load_rom1(iris->ps2, iris->rom1_path.c_str())) { // push_info(iris, "Couldn't load ROM1"); } } } if (rom2_path.size()) { if (!ps2_load_rom2(iris->ps2, rom2_path.c_str())) { // push_info(iris, "Couldn't load ROM2"); } } else { if (iris->rom2_path.size()) { if (!ps2_load_rom2(iris->ps2, iris->rom2_path.c_str())) { // push_info(iris, "Couldn't load ROM2"); } } } if (iris->elf_path.size()) { ps2_set_system(iris->ps2, iris->system); ps2_load_bios(iris->ps2, iris->bios_path.c_str()); ps2_elf_load(iris->ps2, iris->elf_path.c_str()); iris->loaded = iris->elf_path; } if (iris->boot_path.size()) { ps2_set_system(iris->ps2, iris->system); ps2_load_bios(iris->ps2, iris->bios_path.c_str()); ps2_boot_file(iris->ps2, iris->boot_path.c_str()); iris->loaded = iris->boot_path; } if (iris->disc_path.size()) { if (ps2_cdvd_open(iris->ps2->cdvd, iris->disc_path.c_str(), 0)) return; char* boot_file = disc_get_boot_path(iris->ps2->cdvd->disc); if (!boot_file) return; ps2_set_system(iris->ps2, iris->system); ps2_load_bios(iris->ps2, iris->bios_path.c_str()); ps2_boot_file(iris->ps2, boot_file); iris->loaded = iris->disc_path; } } bool init(iris::instance* iris, int argc, const char* argv[]) { int r = parse_toml_settings(iris); parse_cli_settings(iris, argc, argv); if (iris->nvram_path.size()) ps2_cdvd_load_nvram(iris->ps2->cdvd, iris->nvram_path.c_str()); if (iris->mcd0_path.size()) emu::attach_memory_card(iris, 0, iris->mcd0_path.c_str()); if (iris->mcd1_path.size()) emu::attach_memory_card(iris, 1, iris->mcd1_path.c_str()); // Apply settings loaded from file/CLI ps2_set_timescale(iris->ps2, iris->timescale); ee_set_fmv_skip(iris->ps2->ee, iris->skip_fmv); ps2_set_system(iris->ps2, iris->system); ps2_speed_load_flash(iris->ps2->speed, iris->flash_path.c_str()); ps2_speed_set_mac_address(iris->ps2->speed, iris->mac_address); return true; } void close(iris::instance* iris) { if (!iris->dump_to_file) return; std::ofstream file(iris->settings_path); std::ofstream mappings_file(iris->pref_path + "mappings.toml"); file << "# File auto-generated by " IRIS_TITLE "\n\n"; auto tbl = toml::table { { "system", toml::table { { "model", iris->system }, { "mac_address", toml::array { iris->mac_address[0], iris->mac_address[1], iris->mac_address[2], iris->mac_address[3], iris->mac_address[4], iris->mac_address[5] } }, { "autostart", iris->autostart } } }, { "input", toml::table { { "slot1_device", iris->input_devices[0] ? iris->input_devices[0]->get_type() : 0 }, { "slot2_device", iris->input_devices[1] ? iris->input_devices[1]->get_type() : 0 }, { "slot1_mapping", iris->input_map[0] }, { "slot2_mapping", iris->input_map[1] } } }, { "screenshots", toml::table { { "format", iris->screenshot_format }, { "mode", iris->screenshot_mode }, { "jpg_quality_mode", iris->screenshot_jpg_quality_mode }, { "jpg_quality", iris->screenshot_jpg_quality }, { "shader_processing", iris->screenshot_shader_processing } } }, // To-do: Change this to "backends" and use dotted entries // e.g. // [backend_settings] // hardware.super_sampling = 2 // etc. { "hardware", toml::table { { "super_sampling", iris->hardware_backend_config.super_sampling }, { "force_progressive", iris->hardware_backend_config.force_progressive }, { "overscan", iris->hardware_backend_config.overscan }, { "crtc_offsets", iris->hardware_backend_config.crtc_offsets }, { "disable_mipmaps", iris->hardware_backend_config.disable_mipmaps }, { "unsynced_readbacks", iris->hardware_backend_config.unsynced_readbacks }, { "backbuffer_promotion", iris->hardware_backend_config.backbuffer_promotion }, { "allow_blend_demote", iris->hardware_backend_config.allow_blend_demote } } }, { "vulkan", toml::table { { "physical_device", iris->vulkan_physical_device }, { "enable_validation_layers", iris->vulkan_enable_validation_layers } } }, { "debugger", toml::table { { "show_ee_control", iris->show_ee_control }, { "show_ee_state", iris->show_ee_state }, { "show_ee_logs", iris->show_ee_logs }, { "show_ee_interrupts", iris->show_ee_interrupts }, { "show_ee_dmac", iris->show_ee_dmac }, { "show_iop_control", iris->show_iop_control }, { "show_iop_state", iris->show_iop_state }, { "show_iop_logs", iris->show_iop_logs }, { "show_iop_interrupts", iris->show_iop_interrupts }, { "show_iop_modules", iris->show_iop_modules}, { "show_iop_dma", iris->show_iop_dma }, { "show_gs_debugger", iris->show_gs_debugger }, { "show_spu2_debugger", iris->show_spu2_debugger }, { "show_memory_viewer", iris->show_memory_viewer }, { "show_memory_search", iris->show_memory_search }, { "show_vu_disassembler", iris->show_vu_disassembler }, { "show_status_bar", iris->show_status_bar }, { "show_pad_debugger", iris->show_pad_debugger }, { "show_breakpoints", iris->show_breakpoints }, { "show_threads", iris->show_threads }, { "show_sysmem_logs", iris->show_sysmem_logs }, { "show_imgui_demo", iris->show_imgui_demo }, { "show_overlay", iris->show_overlay }, { "skip_fmv", iris->skip_fmv }, { "timescale", iris->timescale } } }, { "display", toml::table { { "scale", iris->scale }, { "aspect_mode", iris->aspect_mode }, { "integer_scaling", iris->integer_scaling }, { "fullscreen", iris->fullscreen }, { "filter", iris->filter }, { "renderer", iris->renderer_backend }, { "window_width", iris->window_width }, { "window_height", iris->window_height }, { "menubar_height", iris->menubar_height }, { "angle", iris->angle }, { "flip_x", iris->flip_x }, { "flip_y", iris->flip_y }, { "vsync", iris->vsync } } }, { "ui", toml::table { { "theme", iris->theme }, { "codeview_color_scheme", iris->codeview_color_scheme }, { "codeview_font_scale", iris->codeview_font_scale }, { "codeview_use_theme_background", iris->codeview_use_theme_background }, { "scale", iris->ui_scale }, { "bgcolor", toml::array { iris->clear_value.color.float32[0], iris->clear_value.color.float32[1], iris->clear_value.color.float32[2] } }, { "enable_viewports", iris->imgui_enable_viewports }, #ifdef _WIN32 { "windows_titlebar_style", iris->windows_titlebar_style }, { "windows_enable_borders", iris->windows_enable_borders }, { "windows_dark_mode", iris->windows_dark_mode }, #endif } }, { "audio", toml::table { { "mute", iris->mute }, { "mute_adma", iris->mute_adma }, { "volume", iris->volume } } }, { "paths", toml::table { { "bios_path", iris->bios_path }, { "rom1_path", iris->rom1_path }, { "rom2_path", iris->rom2_path }, { "nvram_path", iris->nvram_path }, { "mcd0_path", iris->mcd0_path }, { "mcd1_path", iris->mcd1_path }, { "snap_path", iris->snap_path }, { "flash_path", iris->flash_path }, { "gcdb_path", iris->gcdb_path }, } }, { "recents", toml::table { { "array", toml::array() } } }, { "shaders", toml::table { { "enable", iris->enable_shaders }, { "array", toml::array() } } }, }; toml::array* recents = tbl["recents"]["array"].as_array(); for (const auto& s : iris->recents) recents->push_back(toml::table { { "type", s.type }, { "path", s.path } }); toml::array* shaders = tbl["shaders"]["array"].as_array(); for (auto& s : shaders::vector(iris)) shaders->push_back(s->get_id()); // Generate input mappings file mappings_file << "# File auto-generated by " IRIS_TITLE "\n\n"; toml::table mappings_tbl {}; for (auto& map : iris->input_maps) { toml::table map_tbl { { map.name, toml::table {} } }; for (auto& entry : map.map.forward_map()) { toml::table t { { std::to_string(entry.first), entry.second } }; t.is_inline(true); map_tbl[map.name].as_table()->insert(std::to_string(entry.first), entry.second); } mappings_tbl.insert(map.name, map_tbl[map.name]); } file << tbl; mappings_file << mappings_tbl; } } ================================================ FILE: frontend/shaders.cpp ================================================ #include #include #include "config.hpp" #include "iris.hpp" // INCBIN stuff #define INCBIN_PREFIX g_ #define INCBIN_STYLE INCBIN_STYLE_SNAKE #include "incbin.h" INCBIN(encoder_frag_shader, "../shaders/encoder.spv"); INCBIN(decoder_frag_shader, "../shaders/decoder.spv"); INCBIN(curvature_frag_shader, "../shaders/curvature.spv"); INCBIN(scanlines_frag_shader, "../shaders/scanlines.spv"); INCBIN(noise_frag_shader, "../shaders/noise.spv"); namespace iris::shaders { bool pass::init(iris::instance* iris, const void* data, size_t size, std::string id) { m_vert_shader = iris->default_vert_shader; m_iris = iris; m_id = id; VkShaderModuleCreateInfo create_info = {}; create_info.sType = VK_STRUCTURE_TYPE_SHADER_MODULE_CREATE_INFO; create_info.pCode = (const uint32_t*)data; create_info.codeSize = size; if (vkCreateShaderModule(m_iris->device, &create_info, nullptr, &m_frag_shader) != VK_SUCCESS) { fprintf(stderr, "render: Failed to create fragment shader module\n"); return false; } return rebuild(); } pass::pass(iris::instance* iris, const void* data, size_t size, std::string id) { init(iris, data, size, id); } pass::pass(pass&& other) { m_pipeline_layout = other.m_pipeline_layout; m_pipeline = other.m_pipeline; m_render_pass = other.m_render_pass; m_input = other.m_input; m_vert_shader = other.m_vert_shader; m_frag_shader = other.m_frag_shader; m_iris = other.m_iris; m_id = other.m_id; bypass = other.bypass; other.m_pipeline_layout = VK_NULL_HANDLE; other.m_pipeline = VK_NULL_HANDLE; other.m_render_pass = VK_NULL_HANDLE; other.m_input = VK_NULL_HANDLE; other.m_vert_shader = VK_NULL_HANDLE; other.m_frag_shader = VK_NULL_HANDLE; other.m_iris = nullptr; other.m_id = ""; other.bypass = false; } pass& pass::operator=(pass&& other) { if (this != &other) { m_pipeline_layout = other.m_pipeline_layout; m_pipeline = other.m_pipeline; m_render_pass = other.m_render_pass; m_input = other.m_input; m_vert_shader = other.m_vert_shader; m_frag_shader = other.m_frag_shader; m_iris = other.m_iris; m_id = other.m_id; bypass = other.bypass; } other.m_pipeline_layout = VK_NULL_HANDLE; other.m_pipeline = VK_NULL_HANDLE; other.m_render_pass = VK_NULL_HANDLE; other.m_input = VK_NULL_HANDLE; other.m_vert_shader = VK_NULL_HANDLE; other.m_frag_shader = VK_NULL_HANDLE; other.m_iris = nullptr; other.m_id = ""; other.bypass = false; return *this; } void pass::destroy() { if (!m_iris) return; vulkan::wait_idle(m_iris); if (m_pipeline_layout) vkDestroyPipelineLayout(m_iris->device, m_pipeline_layout, nullptr); if (m_pipeline) vkDestroyPipeline(m_iris->device, m_pipeline, nullptr); if (m_render_pass) vkDestroyRenderPass(m_iris->device, m_render_pass, nullptr); if (m_frag_shader) vkDestroyShaderModule(m_iris->device, m_frag_shader, nullptr); if (m_vert_shader != m_iris->default_vert_shader) if (m_vert_shader) vkDestroyShaderModule(m_iris->device, m_vert_shader, nullptr); } pass::~pass() { destroy(); } bool pass::rebuild() { vulkan::wait_idle(m_iris); if (m_pipeline_layout) vkDestroyPipelineLayout(m_iris->device, m_pipeline_layout, nullptr); if (m_pipeline) vkDestroyPipeline(m_iris->device, m_pipeline, nullptr); if (m_render_pass) vkDestroyRenderPass(m_iris->device, m_render_pass, nullptr); VkPushConstantRange push_constant_range = {}; push_constant_range.offset = 0; push_constant_range.size = sizeof(push_constants); push_constant_range.stageFlags = VK_SHADER_STAGE_FRAGMENT_BIT; VkPipelineLayoutCreateInfo pipeline_layout_info = {}; pipeline_layout_info.sType = VK_STRUCTURE_TYPE_PIPELINE_LAYOUT_CREATE_INFO; pipeline_layout_info.setLayoutCount = 1; pipeline_layout_info.pSetLayouts = &m_iris->shader_descriptor_set_layout; pipeline_layout_info.pushConstantRangeCount = 1; pipeline_layout_info.pPushConstantRanges = &push_constant_range; if (vkCreatePipelineLayout(m_iris->device, &pipeline_layout_info, nullptr, &m_pipeline_layout) != VK_SUCCESS) { fprintf(stderr, "render: Failed to create shader pipeline layout\n"); destroy(); return false; } // Create render pass VkAttachmentDescription color_attachment = {}; color_attachment.format = m_iris->image.format; color_attachment.samples = VK_SAMPLE_COUNT_1_BIT; color_attachment.loadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color_attachment.storeOp = VK_ATTACHMENT_STORE_OP_STORE; color_attachment.stencilLoadOp = VK_ATTACHMENT_LOAD_OP_DONT_CARE; color_attachment.stencilStoreOp = VK_ATTACHMENT_STORE_OP_DONT_CARE; color_attachment.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; color_attachment.finalLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkAttachmentReference color_attachment_ref = {}; color_attachment_ref.attachment = 0; color_attachment_ref.layout = VK_IMAGE_LAYOUT_COLOR_ATTACHMENT_OPTIMAL; VkSubpassDescription subpass = {}; subpass.pipelineBindPoint = VK_PIPELINE_BIND_POINT_GRAPHICS; subpass.colorAttachmentCount = 1; subpass.pColorAttachments = &color_attachment_ref; VkSubpassDependency dependency = {}; dependency.srcSubpass = VK_SUBPASS_EXTERNAL; dependency.dstSubpass = 0; dependency.srcStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependency.dstStageMask = VK_PIPELINE_STAGE_COLOR_ATTACHMENT_OUTPUT_BIT; dependency.srcAccessMask = 0; dependency.dstAccessMask = VK_ACCESS_COLOR_ATTACHMENT_WRITE_BIT | VK_ACCESS_COLOR_ATTACHMENT_READ_BIT; VkRenderPass render_pass = VK_NULL_HANDLE; VkRenderPassCreateInfo render_pass_info = {}; render_pass_info.sType = VK_STRUCTURE_TYPE_RENDER_PASS_CREATE_INFO; render_pass_info.attachmentCount = 1; render_pass_info.pAttachments = &color_attachment; render_pass_info.subpassCount = 1; render_pass_info.pSubpasses = &subpass; render_pass_info.dependencyCount = 1; render_pass_info.pDependencies = &dependency; if (vkCreateRenderPass(m_iris->device, &render_pass_info, nullptr, &m_render_pass) != VK_SUCCESS) { fprintf(stderr, "render: Failed to create shader render pass\n"); destroy(); return false; } // Create graphics pipeline VkPipelineShaderStageCreateInfo shader_stages[2] = {}; shader_stages[0].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shader_stages[0].stage = VK_SHADER_STAGE_VERTEX_BIT; shader_stages[0].module = m_iris->default_vert_shader; shader_stages[0].pName = "main"; shader_stages[0].pNext = VK_NULL_HANDLE; shader_stages[1].sType = VK_STRUCTURE_TYPE_PIPELINE_SHADER_STAGE_CREATE_INFO; shader_stages[1].stage = VK_SHADER_STAGE_FRAGMENT_BIT; shader_stages[1].module = m_frag_shader; shader_stages[1].pName = "main"; shader_stages[1].pNext = VK_NULL_HANDLE; static const VkDynamicState dynamic_states[] = { VK_DYNAMIC_STATE_VIEWPORT, VK_DYNAMIC_STATE_SCISSOR }; VkPipelineDynamicStateCreateInfo dynamic_state_info = {}; dynamic_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_DYNAMIC_STATE_CREATE_INFO; dynamic_state_info.dynamicStateCount = 2; dynamic_state_info.pDynamicStates = dynamic_states; VkPipelineVertexInputStateCreateInfo vertex_input_info = {}; vertex_input_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VERTEX_INPUT_STATE_CREATE_INFO; vertex_input_info.vertexBindingDescriptionCount = 0; vertex_input_info.pVertexBindingDescriptions = VK_NULL_HANDLE; vertex_input_info.vertexAttributeDescriptionCount = 0; vertex_input_info.pVertexAttributeDescriptions = VK_NULL_HANDLE; VkPipelineInputAssemblyStateCreateInfo input_assembly_info = {}; input_assembly_info.sType = VK_STRUCTURE_TYPE_PIPELINE_INPUT_ASSEMBLY_STATE_CREATE_INFO; input_assembly_info.topology = VK_PRIMITIVE_TOPOLOGY_TRIANGLE_LIST; input_assembly_info.primitiveRestartEnable = VK_FALSE; VkViewport viewport = {}; viewport.x = 0.0f; viewport.y = 0.0f; viewport.width = (float)m_iris->image.width; viewport.height = (float)m_iris->image.height; viewport.minDepth = 0.0f; viewport.maxDepth = 1.0f; VkExtent2D extent = {}; extent.width = m_iris->image.width; extent.height = m_iris->image.height; VkRect2D scissor = {}; scissor.offset = {0, 0}; scissor.extent = extent; VkPipelineViewportStateCreateInfo viewport_state_info = {}; viewport_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_VIEWPORT_STATE_CREATE_INFO; viewport_state_info.viewportCount = 1; viewport_state_info.pViewports = &viewport; viewport_state_info.scissorCount = 1; viewport_state_info.pScissors = &scissor; VkPipelineRasterizationStateCreateInfo rasterizer_info = {}; rasterizer_info.sType = VK_STRUCTURE_TYPE_PIPELINE_RASTERIZATION_STATE_CREATE_INFO; rasterizer_info.depthClampEnable = VK_FALSE; rasterizer_info.rasterizerDiscardEnable = VK_FALSE; rasterizer_info.polygonMode = VK_POLYGON_MODE_FILL; rasterizer_info.lineWidth = 1.0f; rasterizer_info.cullMode = VK_CULL_MODE_NONE; rasterizer_info.frontFace = VK_FRONT_FACE_CLOCKWISE; rasterizer_info.depthBiasEnable = VK_FALSE; VkPipelineColorBlendAttachmentState blend_attachment_state = {}; blend_attachment_state.colorWriteMask = VK_COLOR_COMPONENT_R_BIT | VK_COLOR_COMPONENT_G_BIT | VK_COLOR_COMPONENT_B_BIT | VK_COLOR_COMPONENT_A_BIT; blend_attachment_state.blendEnable = VK_FALSE; VkPipelineColorBlendStateCreateInfo blend_state_info{}; blend_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_COLOR_BLEND_STATE_CREATE_INFO; blend_state_info.logicOpEnable = VK_FALSE; blend_state_info.attachmentCount = 1; blend_state_info.pAttachments = &blend_attachment_state; VkPipelineMultisampleStateCreateInfo multisampling_state_info = {}; multisampling_state_info.sType = VK_STRUCTURE_TYPE_PIPELINE_MULTISAMPLE_STATE_CREATE_INFO; multisampling_state_info.sampleShadingEnable = VK_FALSE; multisampling_state_info.rasterizationSamples = VK_SAMPLE_COUNT_1_BIT; VkGraphicsPipelineCreateInfo pipeline_info = {}; pipeline_info.sType = VK_STRUCTURE_TYPE_GRAPHICS_PIPELINE_CREATE_INFO; pipeline_info.stageCount = 2; pipeline_info.pStages = shader_stages; pipeline_info.pVertexInputState = &vertex_input_info; pipeline_info.pInputAssemblyState = &input_assembly_info; pipeline_info.pViewportState = &viewport_state_info; pipeline_info.pRasterizationState = &rasterizer_info; pipeline_info.pMultisampleState = &multisampling_state_info; pipeline_info.pDepthStencilState = nullptr; // Optional pipeline_info.pColorBlendState = &blend_state_info; pipeline_info.pDynamicState = &dynamic_state_info; pipeline_info.layout = m_pipeline_layout; pipeline_info.renderPass = m_render_pass; pipeline_info.subpass = 0; pipeline_info.pTessellationState = VK_NULL_HANDLE; pipeline_info.basePipelineHandle = VK_NULL_HANDLE; // Optional pipeline_info.basePipelineIndex = -1; // Optional if (vkCreateGraphicsPipelines(m_iris->device, VK_NULL_HANDLE, 1, &pipeline_info, nullptr, &m_pipeline) != VK_SUCCESS) { fprintf(stderr, "render: Failed to create shader pipeline\n"); destroy(); return false; } return true; } bool pass::ready() { return m_pipeline_layout && m_pipeline && m_render_pass && m_vert_shader && m_frag_shader && m_iris; } VkPipelineLayout& pass::get_pipeline_layout() { return m_pipeline_layout; } VkPipeline& pass::get_pipeline() { return m_pipeline; } VkRenderPass& pass::get_render_pass() { return m_render_pass; } VkImageView& pass::get_input() { return m_input; } VkShaderModule& pass::get_vert_shader() { return m_vert_shader; } VkShaderModule& pass::get_frag_shader() { return m_frag_shader; } std::string pass::get_id() const { return m_id; } std::unordered_map > g_builtin_shaders = { { "iris-ntsc-encoder", { (void*)g_encoder_frag_shader_data, (size_t)g_encoder_frag_shader_size } }, { "iris-ntsc-decoder", { (void*)g_decoder_frag_shader_data, (size_t)g_decoder_frag_shader_size } }, { "iris-ntsc-curvature", { (void*)g_curvature_frag_shader_data, (size_t)g_curvature_frag_shader_size } }, { "iris-ntsc-scanlines", { (void*)g_scanlines_frag_shader_data, (size_t)g_scanlines_frag_shader_size } }, { "iris-ntsc-noise", { (void*)g_noise_frag_shader_data, (size_t)g_noise_frag_shader_size } } }; void push(iris::instance* iris, void* data, size_t size, std::string id) { iris->shader_passes.push_back(new pass(iris, data, size, id)); } void push(iris::instance* iris, std::string id) { auto s = g_builtin_shaders.find(id); if (s != g_builtin_shaders.end()) { push(iris, s->second.first, s->second.second, id); } } void pop(iris::instance* iris) { delete iris->shader_passes.back(); iris->shader_passes.pop_back(); } void insert(iris::instance* iris, int i, void* data, size_t size, std::string id) { iris->shader_passes.insert( iris->shader_passes.begin() + i, new pass(iris, data, size, id) ); } void erase(iris::instance* iris, int i) { delete iris->shader_passes.at(i); iris->shader_passes.erase(iris->shader_passes.begin() + i); } pass* at(iris::instance* iris, int i) { return iris->shader_passes.at(i); } void swap(iris::instance* iris, int a, int b) { pass* t = iris->shader_passes.at(a); iris->shader_passes[a] = iris->shader_passes[b]; iris->shader_passes[b] = t; } pass* front(iris::instance* iris) { return iris->shader_passes.front(); } pass* back(iris::instance* iris) { return iris->shader_passes.back(); } size_t count(iris::instance* iris) { return iris->shader_passes.size(); } void clear(iris::instance* iris) { for (auto& pass : iris->shader_passes) { delete pass; } iris->shader_passes.clear(); } std::vector & vector(iris::instance* iris) { return iris->shader_passes; } } ================================================ FILE: frontend/ui/about.cpp ================================================ #include #include #include #include "iris.hpp" #include "config.hpp" #include "res/IconsMaterialSymbols.h" namespace iris { void show_about_window(iris::instance* iris) { using namespace ImGui; static ImGuiWindowFlags flags = ImGuiWindowFlags_NoResize | ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking | ImGuiWindowFlags_AlwaysAutoResize; if (GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable && !GetIO().ConfigViewportsNoDecoration) flags |= ImGuiWindowFlags_NoTitleBar; if (Begin("About", &iris->show_about_window, flags)) { if (BeginChild("##iconchild", ImVec2(100.0, 220.0), ImGuiChildFlags_AutoResizeY)) { Image((ImTextureID)(intptr_t)iris->iris_icon.descriptor_set, ImVec2(100.0, 100.0)); } EndChild(); SameLine(0.0, 10.0); if (BeginChild("##textchild", ImVec2(350.0, 0.0))) { PushFont(iris->font_heading); Text(IRIS_TITLE); PopFont(); Separator(); Text("Experimental PlayStation 2 emulator"); Text(""); Text("Available at "); SameLine(0.0, 0.0); TextLinkOpenURL("https://github.com/allkern/iris", "https://github.com/allkern/iris"); Text(""); TextWrapped( "Special thanks to: The emudev Discord server, Ziemas, " "refraction, ncarrillo, cakehonolulu, Layle, and " "the PCSX2 team for their kind support." ); Text(""); Text("Please file any issues to "); SameLine(0.0, 0.0); TextLinkOpenURL("our GitHub issues page", "https://github.com/allkern/iris/issues"); SameLine(0.0, 0.0); Text("."); } EndChild(); } End(); } } ================================================ FILE: frontend/ui/bios_setting.cpp ================================================ #include "iris.hpp" #include "res/IconsMaterialSymbols.h" #include "portable-file-dialogs.h" #include "rom.h" namespace iris { int stage = 0; bool bios_checked = false; int bios_valid = false; int is_valid(const char* path) { FILE* f = fopen(path, "rb"); if (!f) return 0; fseek(f, 0, SEEK_END); size_t size = ftell(f); fseek(f, 0, SEEK_SET); uint8_t* buf = new uint8_t[size]; fread(buf, 1, size, f); int valid = ps2_rom0_is_valid(buf, size); fclose(f); delete[] buf; return valid ? 2 : 1; } void show_memory_card_stage(iris::instance* iris) { using namespace ImGui; if (BeginChild("##iconchild", ImVec2(100.0, 0.0), ImGuiChildFlags_AutoResizeY)) { Image((ImTextureID)(intptr_t)iris->iris_icon.descriptor_set, ImVec2(100.0, 100.0)); } EndChild(); SameLine(0.0, 10.0); if (BeginChild("##textchild", ImVec2(360.0, 0.0), ImGuiChildFlags_AutoResizeY)) { PushFont(iris->font_heading); Text("Done!"); PopFont(); Separator(); Text("You may now close this window and start using Iris.\n\n" "Additionally you might want to check out the settings\n" "menu to configure memory cards, display, audio, and\n" "input options.\n\n" ); Text(ICON_MS_WARNING " Please note that Iris is a work-in-progress emulator.\n" "You might encounter bugs or experience poor performance\n" "in general. We will appreciate any feedback or bug reports\n" "you may provide.\n\n" ); Text("You can report issues or provide feedback at:"); TextLinkOpenURL("https://github.com/allkern/iris/issues"); Separator(); static bool open_settings = true; if (Button("Done")) { CloseCurrentPopup(); iris->show_bios_setting_window = false; if (open_settings) { iris->show_settings = true; } } SameLine(); Checkbox("Open settings menu", &open_settings); } EndChild(); } void show_bios_stage(iris::instance* iris) { using namespace ImGui; static char buf[512]; PushFont(iris->font_heading); Text("Welcome to Iris!"); PopFont(); Separator(); Text( "Iris requires a PlayStation 2 BIOS file in order to run.\n\n" "Please select one using the box below or provide one using the\n" "command line arguments." ); if (InputTextWithHint("##BIOS", "e.g. scph10000.bin", buf, 512, ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_EnterReturnsTrue)) { bios_checked = true; bios_valid = is_valid(buf); } SameLine(); if (Button(ICON_MS_FOLDER)) { audio::mute(iris); auto f = pfd::open_file("Select BIOS file", "", { "All File Types (*.bin; *.rom0)", "*.bin *.rom0", "All Files (*.*)", "*" }); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { strncpy(buf, f.result().at(0).c_str(), 512); bios_checked = true; bios_valid = is_valid(buf); } } if (bios_checked) { ImVec4 col = bios_valid ? ImVec4(0.0f, 1.0f, 0.0f, 1.0f) : ImVec4(1.0f, 1.0f, 0.0f, 1.0f); const char* text = nullptr; switch (bios_valid) { case 0: { col = ImVec4(0.86f, 0.19f, 0.18f, 1.0f); text = ICON_MS_CLOSE " Couldn't open the specified file."; } break; case 1: { col = ImVec4(0.90f, 0.73f, 0.2f, 1.0f); text = ICON_MS_WARNING " BIOS is unknown, it might not work as expected."; } break; case 2: { col = ImVec4(0.42f, 0.85f, 0.1f, 1.0f); text = ICON_MS_CHECK " BIOS is known!"; } break; } TextColored(col, text); } // To-do: Add file validation Separator(); BeginDisabled(!bios_valid || !buf[0]); if (Button("Next")) { iris->bios_path = buf; iris->dump_to_file = true; if (!ps2_load_bios(iris->ps2, iris->bios_path.c_str())) { push_info(iris, "Couldn't load BIOS"); stage = 0; } else { stage = 1; } } EndDisabled(); } void show_bios_setting_window(iris::instance* iris) { using namespace ImGui; OpenPopup("Welcome"); // Always center this window when appearing ImVec2 center = GetMainViewport()->GetCenter(); SetNextWindowPos(center, ImGuiCond_Always, ImVec2(0.5f, 0.5f)); if (BeginPopupModal("Welcome", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { switch (stage) { case 0: show_bios_stage(iris); break; case 1: show_memory_card_stage(iris); break; } EndPopup(); } } } ================================================ FILE: frontend/ui/breakpoints.cpp ================================================ #include #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" namespace iris { const char* cpu_names[] = { "EE", "IOP" }; static breakpoint* selected = nullptr; static breakpoint editable; void show_breakpoints_table(iris::instance* iris) { using namespace ImGui; if (BeginTable("##breakpoints", 5, ImGuiTableFlags_RowBg | ImGuiTableFlags_Sortable)) { PushFont(iris->font_small_code); TableSetupColumn("Address"); TableSetupColumn("CPU"); TableSetupColumn("Flags"); TableSetupColumn("Size"); TableSetupColumn("Actions"); TableHeadersRow(); PopFont(); TableNextRow(); int i = 0; for (breakpoint& b : iris->breakpoints) { TableSetColumnIndex(0); char buf[16]; sprintf(buf, "##d%x", i); if (Selectable(buf, &b == selected, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns)) { selected = &b; } SameLine(0.0, 0.0); PushFont(iris->font_code); if (b.symbol) { Text("%s", b.symbol); } else { Text("%08x", b.addr); } PopFont(); TableSetColumnIndex(1); Text(b.cpu == BKPT_CPU_EE ? "EE" : "IOP"); TableSetColumnIndex(2); PushFont(iris->font_code); Text("%c%c%c", b.cond_r ? 'R' : '.', b.cond_w ? 'W' : '.', b.cond_x ? 'X' : '.' ); PopFont(); TableSetColumnIndex(3); Text("%d", b.size); TableSetColumnIndex(4); sprintf(buf, b.enabled ? ICON_MS_CHECK "##%x" : "##%x", i); if (Selectable(buf, false, 0, ImVec2(20, 0))) { b.enabled = !b.enabled; } SameLine(); sprintf(buf, ICON_MS_DELETE "##%x", i); if (Selectable(buf, false, 0, ImVec2(20, 0))) { selected = nullptr; iris->breakpoints.erase(iris->breakpoints.begin() + i); } i++; TableNextRow(); } EndTable(); } } uint32_t parse_address(iris::instance* iris, const char* buf, const char** name) { if (!buf || !buf[0]) return 0; if (isalpha(buf[0]) || buf[0] == '_') { for (iris::elf_symbol& sym : iris->symbols) { if (strcmp(sym.name, buf) == 0) { *name = sym.name; return sym.addr; } } } else { *name = nullptr; return strtoul(buf, NULL, 16); } return 0; } void show_breakpoint_editor(iris::instance* iris) { using namespace ImGui; if (BeginCombo("CPU", cpu_names[editable.cpu], ImGuiComboFlags_HeightSmall)) { for (int i = 0; i < 2; i++) { if (Selectable(cpu_names[i], editable.cpu == i)) { editable.cpu = i; } } EndCombo(); } static char buf[512]; PushFont(iris->font_code); if (InputText("##address", buf, 512, ImGuiInputTextFlags_EnterReturnsTrue)) { editable.addr = parse_address(iris, buf, &editable.symbol); } SameLine(); PopFont(); Text("Address"); Checkbox("##enabled", &editable.enabled); SameLine(); Checkbox("##r", &editable.cond_r); SameLine(); Checkbox("##w", &editable.cond_w); SameLine(); Checkbox("Flags", &editable.cond_x); BeginDisabled(!selected); if (Button("Edit breakpoint")) { editable.addr = parse_address(iris, buf, &editable.symbol); *selected = editable; } SameLine(); EndDisabled(); if (Button("New breakpoint")) { editable.addr = parse_address(iris, buf, &editable.symbol); iris->breakpoints.push_back(editable); selected = &iris->breakpoints.back(); } } void show_breakpoints(iris::instance* iris) { using namespace ImGui; if (imgui::BeginEx("Breakpoints", &iris->show_breakpoints, ImGuiWindowFlags_MenuBar)) { if (BeginMenuBar()) { MenuItem("Settings"); EndMenuBar(); } if (Button(ICON_MS_DELETE, ImVec2(50, 0))) { selected = nullptr; iris->breakpoints.clear(); } SameLine(); if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) { SetTooltip("Clear all"); } if (Button(ICON_MS_REMOVE_SELECTION)) { for (breakpoint& b : iris->breakpoints) { b.enabled = false; } } SameLine(); if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) { SetTooltip("Disable all"); } if (Button(ICON_MS_SELECT)) { for (breakpoint& b : iris->breakpoints) { b.enabled = true; } } if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) { SetTooltip("Enable all"); } Separator(); if (BeginChild("##tablechild", ImVec2(0, GetContentRegionAvail().y / 2.0f))) { show_breakpoints_table(iris); } EndChild(); SeparatorText("Add breakpoint"); if (BeginChild("##tablechild2")) { show_breakpoint_editor(iris); } EndChild(); } End(); } } ================================================ FILE: frontend/ui/control.cpp ================================================ #include #include #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" #include "ee/ee_dis.h" #include "ee/ee_def.hpp" #include "ee/vu_def.hpp" #include "iop/iop_dis.h" #define IM_RGB(r, g, b) ImVec4(((float)r / 255.0f), ((float)g / 255.0f), ((float)b / 255.0f), 1.0) namespace iris { struct ee_dis_state g_ee_dis_state; struct iop_dis_state g_iop_dis_state; void print_highlighted(iris::instance* iris, const char* buf) { using namespace ImGui; std::vector tokens; std::string text; while (*buf) { text.clear(); if (isalpha(*buf)) { while (isalpha(*buf) || isdigit(*buf) || (*buf == '.')) text.push_back(*buf++); } else if (isxdigit(*buf) || (*buf == '-')) { while (isxdigit(*buf) || (*buf == 'x') || (*buf == '-')) text.push_back(*buf++); } else if (*buf == '$') { while (*buf == '$' || isdigit(*buf) || isalpha(*buf) || *buf == '_') text.push_back(*buf++); } else if (*buf == ',') { while (*buf == ',') text.push_back(*buf++); } else if (*buf == '(') { while (*buf == '(') text.push_back(*buf++); } else if (*buf == ')') { while (*buf == ')') text.push_back(*buf++); } else if (*buf == '<') { while (*buf != '>') text.push_back(*buf++); text.push_back(*buf++); } else if (*buf == '_') { text.push_back(*buf++); } else if (*buf == '.') { text.push_back(*buf++); } else { printf("unhandled char %c (%d) \"%s\"\n", *buf, *buf, buf); exit(1); } while (isspace(*buf)) text.push_back(*buf++); tokens.push_back(text); } for (const std::string& t : tokens) { if (isalpha(t[0])) { ImVec4 col = ImVec4( iris->codeview_color_mnemonic.Value.x, iris->codeview_color_mnemonic.Value.y, iris->codeview_color_mnemonic.Value.z, iris->codeview_color_mnemonic.Value.w ); TextColored(col, "%s", t.c_str()); } else if (isdigit(t[0]) || t[0] == '-') { ImVec4 col = ImVec4( iris->codeview_color_number.Value.x, iris->codeview_color_number.Value.y, iris->codeview_color_number.Value.z, iris->codeview_color_number.Value.w ); TextColored(col, "%s", t.c_str()); } else if (t[0] == '$') { ImVec4 col = ImVec4( iris->codeview_color_register.Value.x, iris->codeview_color_register.Value.y, iris->codeview_color_register.Value.z, iris->codeview_color_register.Value.w ); TextColored(col, "%s", t.c_str()); } else if (t[0] == '<') { ImVec4 col = ImVec4( iris->codeview_color_other.Value.x, iris->codeview_color_other.Value.y, iris->codeview_color_other.Value.z, iris->codeview_color_other.Value.w ); TextColored(col, "%s", t.c_str()); } else { Text("%s", t.c_str()); } SameLine(0.0f, 0.0f); } NewLine(); } static void show_ee_disassembly_view(iris::instance* iris) { using namespace ImGui; float font_scale = GetStyle().FontScaleMain; GetStyle().FontScaleMain = iris->codeview_font_scale; PushFont(iris->font_code); if (!iris->codeview_use_theme_background) { PushStyleColor(ImGuiCol_TableRowBg, ImVec4( iris->codeview_color_background.Value.x, iris->codeview_color_background.Value.y, iris->codeview_color_background.Value.z, iris->codeview_color_background.Value.w )); PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4( iris->codeview_color_background.Value.x, iris->codeview_color_background.Value.y, iris->codeview_color_background.Value.z, iris->codeview_color_background.Value.w )); PushStyleColor(ImGuiCol_Text, ImVec4( iris->codeview_color_text.Value.x, iris->codeview_color_text.Value.y, iris->codeview_color_text.Value.z, iris->codeview_color_text.Value.w )); PushStyleColor(ImGuiCol_TextDisabled, ImVec4( iris->codeview_color_comment.Value.x, iris->codeview_color_comment.Value.y, iris->codeview_color_comment.Value.z, iris->codeview_color_comment.Value.w )); } if (BeginTable("table1", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) { TableSetupColumn("a", ImGuiTableColumnFlags_NoResize, 15.0f); TableSetupColumn("b", ImGuiTableColumnFlags_NoResize, 15.0f); TableSetupColumn("c", ImGuiTableColumnFlags_NoResize); for (int row = -64; row < 64; row++) { if (iris->ee_control_follow_pc) { g_ee_dis_state.pc = iris->ps2->ee->pc + (row * 4); } else { g_ee_dis_state.pc = iris->ee_control_address + (row * 4); } TableNextRow(); TableSetColumnIndex(0); PushFont(iris->font_icons); auto v = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) { return a.addr == g_ee_dis_state.pc && a.cpu == BKPT_CPU_EE; }); if (v != iris->breakpoints.end()) { Text(" " ICON_MS_FIBER_MANUAL_RECORD " "); } TableSetColumnIndex(1); if (g_ee_dis_state.pc == iris->ps2->ee->pc) Text(ICON_MS_CHEVRON_RIGHT " "); PopFont(); TableSetColumnIndex(2); for (elf_symbol& sym : iris->symbols) { if (sym.addr == g_ee_dis_state.pc) { ImVec4 col = ImVec4( iris->codeview_color_mnemonic.Value.x, iris->codeview_color_mnemonic.Value.y, iris->codeview_color_mnemonic.Value.z, iris->codeview_color_mnemonic.Value.w ); PushFont(iris->font_icons); TextColored(col, ICON_MS_STAT_0); SameLine(); PopFont(); TextColored(col, "%s", sym.name); break; } } uint32_t opcode = ee_bus_read32(iris->ps2->ee_bus, g_ee_dis_state.pc & 0x1fffffff); char buf[128], id[16]; char addr_str[9]; sprintf(addr_str, "%08x", g_ee_dis_state.pc); char opcode_str[9]; sprintf(opcode_str, "%08x", opcode); char* disassembly = ee_disassemble(buf, opcode, &g_ee_dis_state); sprintf(id, "##%d", row); Selectable(id, false, ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns); if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left)) { breakpoint b; b.addr = g_ee_dis_state.pc; b.cond_r = false; b.cond_w = false; b.cond_x = true; b.cpu = BKPT_CPU_EE; b.size = 4; b.enabled = true; auto addr = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) { return a.addr == g_ee_dis_state.pc && a.cpu == BKPT_CPU_EE; }); if (addr == iris->breakpoints.end()) { iris->breakpoints.push_back(b); } else { iris->breakpoints.erase(addr); } } SameLine(); if (BeginPopupContextItem()) { PushFont(iris->font_small_code); TextDisabled("0x%08x", g_ee_dis_state.pc); PopFont(); PushFont(iris->font_body); if (BeginMenu(ICON_MS_CONTENT_COPY " Copy")) { if (Selectable(ICON_MS_SORT " Address")) { SDL_SetClipboardText(addr_str); } if (Selectable(ICON_MS_SORT " Opcode")) { SDL_SetClipboardText(opcode_str); } if (Selectable(ICON_MS_SORT " Disassembly")) { SDL_SetClipboardText(disassembly); } ImGui::EndMenu(); } auto addr = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) { return a.addr == g_ee_dis_state.pc && a.cpu == BKPT_CPU_EE; }); if (addr != iris->breakpoints.end()) { if (MenuItem(ICON_MS_CANCEL " Remove this breakpoint")) { iris->breakpoints.erase(addr); } } else { if (MenuItem(ICON_MS_ADD_CIRCLE " Add breakpoint here")) { breakpoint b; b.addr = g_ee_dis_state.pc; b.cond_r = false; b.cond_w = false; b.cond_x = true; b.cpu = BKPT_CPU_EE; b.size = 4; b.enabled = true; iris->breakpoints.push_back(b); } } PopFont(); EndPopup(); } Text("%s ", addr_str); SameLine(); TextDisabled("%s ", opcode_str); SameLine(); if (true) { print_highlighted(iris, disassembly); } else { Text("%s", disassembly); } if (iris->ee_control_follow_pc) { if (g_ee_dis_state.pc == iris->ps2->ee->pc) SetScrollHereY(0.5f); } else { if (g_ee_dis_state.pc == iris->ee_control_address) SetScrollHereY(0.5f); } } EndTable(); } if (!iris->codeview_use_theme_background) { PopStyleColor(4); } PopFont(); GetStyle().FontScaleMain = font_scale; } static void show_iop_disassembly_view(iris::instance* iris) { using namespace ImGui; float font_scale = GetStyle().FontScaleMain; GetStyle().FontScaleMain = iris->codeview_font_scale; PushFont(iris->font_code); if (!iris->codeview_use_theme_background) { PushStyleColor(ImGuiCol_TableRowBg, ImVec4( iris->codeview_color_background.Value.x, iris->codeview_color_background.Value.y, iris->codeview_color_background.Value.z, iris->codeview_color_background.Value.w )); PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4( iris->codeview_color_background.Value.x, iris->codeview_color_background.Value.y, iris->codeview_color_background.Value.z, iris->codeview_color_background.Value.w )); PushStyleColor(ImGuiCol_Text, ImVec4( iris->codeview_color_text.Value.x, iris->codeview_color_text.Value.y, iris->codeview_color_text.Value.z, iris->codeview_color_text.Value.w )); PushStyleColor(ImGuiCol_TextDisabled, ImVec4( iris->codeview_color_comment.Value.x, iris->codeview_color_comment.Value.y, iris->codeview_color_comment.Value.z, iris->codeview_color_comment.Value.w )); } if (BeginTable("table2", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) { TableSetupColumn("a", ImGuiTableColumnFlags_NoResize, 15.0f); TableSetupColumn("b", ImGuiTableColumnFlags_NoResize, 15.0f); TableSetupColumn("c", ImGuiTableColumnFlags_NoResize); for (int row = -64; row < 64; row++) { if (iris->iop_control_follow_pc) { g_iop_dis_state.addr = iris->ps2->iop->pc + (row * 4); } else { g_iop_dis_state.addr = iris->iop_control_address + (row * 4); } TableNextRow(); TableSetColumnIndex(0); PushFont(iris->font_icons); auto v = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) { return a.addr == g_iop_dis_state.addr && a.cpu == BKPT_CPU_IOP; }); if (v != iris->breakpoints.end()) { Text(" " ICON_MS_FIBER_MANUAL_RECORD " "); } TableSetColumnIndex(1); if (g_iop_dis_state.addr == iris->ps2->iop->pc) Text(ICON_MS_CHEVRON_RIGHT " "); PopFont(); TableSetColumnIndex(2); uint32_t opcode = iop_bus_read32(iris->ps2->iop_bus, g_iop_dis_state.addr & 0x1fffffff); char buf[128]; char addr_str[9]; sprintf(addr_str, "%08x", g_iop_dis_state.addr); char opcode_str[9]; sprintf(opcode_str, "%08x", opcode); char* disassembly = iop_disassemble(buf, opcode, &g_iop_dis_state); Text("%s ", addr_str); SameLine(); TextDisabled("%s ", opcode_str); SameLine(); char id[16]; sprintf(id, "##%d", row); Selectable(id, false, ImGuiSelectableFlags_AllowDoubleClick | ImGuiSelectableFlags_SpanAllColumns); if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left)) { breakpoint b; b.addr = g_iop_dis_state.addr; b.cond_r = false; b.cond_w = false; b.cond_x = true; b.cpu = BKPT_CPU_IOP; b.size = 4; b.enabled = true; auto addr = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) { return a.addr == g_iop_dis_state.addr && a.cpu == BKPT_CPU_IOP; }); if (addr == iris->breakpoints.end()) { iris->breakpoints.push_back(b); } else { iris->breakpoints.erase(addr); } } SameLine(); if (BeginPopupContextItem()) { PushFont(iris->font_small_code); TextDisabled("0x%08x", g_iop_dis_state.addr); PopFont(); PushFont(iris->font_body); if (BeginMenu(ICON_MS_CONTENT_COPY " Copy")) { if (Selectable(ICON_MS_SORT " Address")) { SDL_SetClipboardText(addr_str); } if (Selectable(ICON_MS_SORT " Opcode")) { SDL_SetClipboardText(opcode_str); } if (Selectable(ICON_MS_SORT " Disassembly")) { SDL_SetClipboardText(disassembly); } ImGui::EndMenu(); } auto addr = std::find_if(iris->breakpoints.begin(), iris->breakpoints.end(), [](breakpoint& a) { return a.addr == g_iop_dis_state.addr && a.cpu == BKPT_CPU_IOP; }); if (addr != iris->breakpoints.end()) { if (MenuItem(ICON_MS_CANCEL " Remove this breakpoint")) { iris->breakpoints.erase(addr); } } else { if (MenuItem(ICON_MS_ADD_CIRCLE " Add breakpoint here")) { breakpoint b; b.addr = g_iop_dis_state.addr; b.cond_r = false; b.cond_w = false; b.cond_x = true; b.cpu = BKPT_CPU_IOP; b.size = 4; b.enabled = true; iris->breakpoints.push_back(b); } } PopFont(); EndPopup(); } if (true) { print_highlighted(iris, disassembly); } else { Text("%s", disassembly); } if (iris->iop_control_follow_pc) { if (g_iop_dis_state.addr == iris->ps2->iop->pc) SetScrollHereY(0.5f); } else { if (g_iop_dis_state.addr == iris->iop_control_address) SetScrollHereY(0.5f); } } EndTable(); } PopFont(); if (!iris->codeview_use_theme_background) { PopStyleColor(4); } GetStyle().FontScaleMain = font_scale; } void show_ee_control(iris::instance* iris) { using namespace ImGui; PushFont(iris->font_icons); if (imgui::BeginEx("EE (R5900)", &iris->show_ee_control)) { if (Button(iris->pause ? ICON_MS_PLAY_ARROW : ICON_MS_PAUSE, ImVec2(50, 0))) { iris->pause = !iris->pause; } SameLine(); if (Button(ICON_MS_STEP_INTO)) { iris->pause = true; iris->step = true; } SameLine(); if (Button(ICON_MS_STEP_OVER)) { iris->step_over = true; iris->step_over_addr = iris->ps2->ee->pc + 4; iris->pause = false; } SameLine(); if (Button(ICON_MS_STEP_OUT)) { iris->step_out = true; iris->pause = false; } SameLine(); if (Button(ICON_MS_MOVE_DOWN)) { iris->ee_control_follow_pc = true; } SameLine(); if (Button(ICON_MS_AUTORENEW)) { ee_flush_cache(iris->ps2->ee); } if (iris->symbols.size()) { TextDisabled("Current function:"); SameLine(); const char* func = ""; for (elf_symbol& sym : iris->symbols) { if (iris->ps2->ee->pc >= sym.addr && iris->ps2->ee->pc < (sym.addr + sym.size)) { func = sym.name; break; } } Text("%s", func); } SeparatorText("Disassembly"); if (BeginChild("ee##disassembly")) { show_ee_disassembly_view(iris); } EndChild(); } End(); PopFont(); } void show_iop_control(iris::instance* iris) { using namespace ImGui; PushFont(iris->font_icons); if (imgui::BeginEx("IOP (R3000)", &iris->show_iop_control)) { if (Button(iris->pause ? ICON_MS_PLAY_ARROW : ICON_MS_PAUSE, ImVec2(50, 0))) { iris->pause = !iris->pause; } SameLine(); if (Button(ICON_MS_STEP)) { iris->pause = true; ps2_step_iop(iris->ps2); } if (InputInt("Address", (int32_t*)&iris->iop_control_address, 0, 0, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) { iris->iop_control_follow_pc = false; } if (Button(ICON_MS_MOVE_DOWN)) { iris->iop_control_follow_pc = true; } SameLine(); SeparatorText("Disassembly"); if (BeginChild("iop##disassembly")) { show_iop_disassembly_view(iris); } EndChild(); } End(); PopFont(); } } ================================================ FILE: frontend/ui/dma.cpp ================================================ #include #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" namespace iris { void show_ee_dmac(iris::instance* iris) { using namespace ImGui; if (imgui::BeginEx("EE DMAC", &iris->show_ee_dmac)) { } End(); } void show_iop_dma(iris::instance* iris) { using namespace ImGui; if (imgui::BeginEx("IOP DMA", &iris->show_iop_dma)) { } End(); } } ================================================ FILE: frontend/ui/gs.cpp ================================================ #include #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" namespace iris { static const char* gs_internal_reg_names[] = { "prim", "rgbaq", "st", "uv", "xyzf2", "xyz2", "fog", "xyzf3", "xyz3", "prmodecont", "prmode", "texclut", "scanmsk", "texa", "fogcol", "texflush", "dimx", "dthe", "colclamp", "pabe", "bitbltbuf", "trxpos", "trxreg", "trxdir", "hwreg", "signal", "finish", "label" }; static const char* gs_gp_reg_names[] = { "frame", "zbuf", "tex0", "tex1", "tex2", "miptbp1", "miptbp2", "clamp", "test", "alpha", "xyoffset", "scissor", "fba" }; static const char* gs_privileged_reg_names[] = { "pmode", "smode1", "smode2", "srfsh", "synch1", "synch2", "syncv", "dispfb1", "display1", "dispfb2", "display2", "extbuf", "extdata", "extwrite", "bgcolor", "csr", "imr", "busdir", "siglblid" }; static const char* gs_debug_names[] = { "Registers", "Buffers", "Textures", "Queue", "Memory" }; static const char* sizing_combo_items[] = { ICON_MS_FIT_WIDTH " Fixed fit", ICON_MS_FULLSCREEN " Fixed same", ICON_MS_FULLSCREEN " Stretch prop", ICON_MS_FULLSCREEN " Stretch same" }; static ImGuiTableFlags table_sizing_flags[] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame }; static int gs_table_sizing = 3; static int gs_sizing_index = 0; static int gs_debug_index = 0; static const char* gs_context_names[] = { "Privileged", "Context 1", "Context 2", "Internal" }; static int gs_context = 0; void show_privileged_registers(iris::instance* iris) { using namespace ImGui; struct ps2_gs* gs = iris->ps2->gs; PushFont(iris->font_code); uint64_t* regs = &gs->pmode; if (BeginTable("table6", 2, ImGuiTableFlags_RowBg)) { PushFont(iris->font_small_code); TableSetupColumn("Register"); TableSetupColumn("Value"); TableHeadersRow(); PopFont(); for (int i = 0; i < 19; i++) { TableNextRow(); TableSetColumnIndex(0); Text("%s", gs_privileged_reg_names[i]); TableSetColumnIndex(1); Text("%08lx%08lx", regs[i] >> 32, regs[i] & 0xffffffff); } EndTable(); } PopFont(); } void show_context_registers(iris::instance* iris, int ctx) { using namespace ImGui; struct ps2_gs* gs = iris->ps2->gs; PushFont(iris->font_code); uint64_t* regs = &gs->context[ctx].frame; if (BeginTable("table9", 2, ImGuiTableFlags_RowBg)) { PushFont(iris->font_small_code); TableSetupColumn("Register"); TableSetupColumn("Value"); TableHeadersRow(); PopFont(); for (int i = 0; i < 13; i++) { TableNextRow(); TableSetColumnIndex(0); Text("%s", gs_gp_reg_names[i]); TableSetColumnIndex(1); Text("%08lx%08lx", regs[i] >> 32, regs[i] & 0xffffffff); } EndTable(); } PopFont(); } void show_internal_registers(iris::instance* iris) { using namespace ImGui; struct ps2_gs* gs = iris->ps2->gs; PushFont(iris->font_code); uint64_t* regs = &gs->prim; if (BeginTable("table6", 2, ImGuiTableFlags_RowBg)) { PushFont(iris->font_small_code); TableSetupColumn("Register"); TableSetupColumn("Value"); TableHeadersRow(); PopFont(); for (int i = 0; i < 28; i++) { TableNextRow(); TableSetColumnIndex(0); Text("%s", gs_internal_reg_names[i]); TableSetColumnIndex(1); Text("%08lx%08lx", regs[i] >> 32, regs[i] & 0xffffffff); } EndTable(); } PopFont(); } static inline void show_gs_vertex_xy(iris::instance* iris, const gs_vertex* vtx) { using namespace ImGui; TableSetColumnIndex(0); Text("Position (XY)"); TableSetColumnIndex(1); PushFont(iris->font_code); Text("0x%04lx, 0x%04lx", vtx->xyz & 0xffff, (vtx->xyz >> 16) & 0xffff); TableSetColumnIndex(2); Text("%d.%04ld, %d.%04ld", vtx->x, (vtx->xyz & 0xf) * 625, vtx->y, ((vtx->xyz >> 16) & 0xf) * 625); PopFont(); TableNextRow(); } static inline void show_gs_vertex_z(iris::instance* iris, const gs_vertex* vtx) { using namespace ImGui; TableSetColumnIndex(0); Text("Depth (Z)"); TableSetColumnIndex(1); PushFont(iris->font_code); Text("0x%08x", vtx->z); TableSetColumnIndex(2); Text("%d", vtx->z); PopFont(); TableNextRow(); } static inline void show_gs_vertex_stq(iris::instance* iris, const gs_vertex* vtx) { using namespace ImGui; TableSetColumnIndex(0); Text("STQ"); TableSetColumnIndex(1); PushFont(iris->font_code); Text("0x%08lx, 0x%08lx, 0x%08lx", vtx->st & 0xffffffff, vtx->st >> 32, vtx->rgbaq >> 32); TableSetColumnIndex(2); Text("%f, %f, %f", vtx->s, vtx->t, vtx->q); PopFont(); TableNextRow(); } static inline void show_gs_vertex_uv(iris::instance* iris, const gs_vertex* vtx) { using namespace ImGui; TableSetColumnIndex(0); Text("UV"); TableSetColumnIndex(1); PushFont(iris->font_code); Text("0x%08lx, 0x%08lx", vtx->uv & 0x3fff, (vtx->uv >> 16) & 0x3fff ); TableSetColumnIndex(2); Text("%d.%04ld, %d.%04ld", vtx->u, (vtx->uv & 0xf) * 625, vtx->v, ((vtx->uv >> 16) & 0xf) * 625 ); PopFont(); TableNextRow(); } static inline void show_gs_vertex_rgba(iris::instance* iris, const gs_vertex* vtx) { using namespace ImGui; TableSetColumnIndex(0); Text("Color (RGBA)"); TableSetColumnIndex(1); PushFont(iris->font_code); Text("0x%08lx", vtx->rgbaq & 0xffffffff); TableSetColumnIndex(2); Text("0x%02lx, 0x%02lx, 0x%02lx, 0x%02lx", vtx->rgbaq & 0xff, (vtx->rgbaq >> 8) & 0xff, (vtx->rgbaq >> 16) & 0xff, (vtx->rgbaq >> 24) & 0xff ); SameLine(); ImVec4 color = ImVec4( (float)(vtx->rgbaq & 0xff) / 255.0f, (float)((vtx->rgbaq >> 8) & 0xff) / 255.0f, (float)((vtx->rgbaq >> 16) & 0xff) / 255.0f, (float)((vtx->rgbaq >> 24) & 0xff) / 255.0f ); ColorEdit4("MyColor##3", (float*)&color, ImGuiColorEditFlags_NoInputs | ImGuiColorEditFlags_NoLabel | ImGuiColorEditFlags_NoPicker | ImGuiColorEditFlags_AlphaPreviewHalf ); PopFont(); TableNextRow(); } void show_gs_vertex(iris::instance* iris, const gs_vertex* vtx) { using namespace ImGui; if (BeginTable("table10", 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) { PushFont(iris->font_small_code); TableSetupColumn("Attribute"); TableSetupColumn("Raw"); TableSetupColumn("Value"); TableHeadersRow(); PopFont(); TableNextRow(); show_gs_vertex_xy(iris, vtx); show_gs_vertex_z(iris, vtx); show_gs_vertex_stq(iris, vtx); show_gs_vertex_uv(iris, vtx); show_gs_vertex_rgba(iris, vtx); EndTable(); } } void show_gs_queue(iris::instance* iris) { using namespace ImGui; struct ps2_gs* gs = iris->ps2->gs; for (unsigned int i = 0; i < gs->vqi; i++) { char buf[32]; sprintf(buf, "Vertex %d", i+1); if (TreeNode(buf)) { show_gs_vertex(iris, &gs->vq[i]); TreePop(); } } if (TreeNode("Uninitialized")) { for (int i = gs->vqi; i < 4; i++) { char buf[32]; sprintf(buf, "Vertex %d", i+1); if (TreeNode(buf)) { show_gs_vertex(iris, &gs->vq[i]); TreePop(); } } TreePop(); } } void show_gs_registers(iris::instance* iris) { using namespace ImGui; if (BeginTable("table6", 4, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV)) { TableNextRow(); for (int i = 0; i < 4; i++) { TableSetColumnIndex(i); if (Selectable(gs_context_names[i], gs_context == i)) { gs_context = i; } } EndTable(); } if (BeginChild("##gsr")) { switch (gs_context) { case 0: { show_privileged_registers(iris); } break; case 1: { show_context_registers(iris, 0); } break; case 2: { show_context_registers(iris, 1); } break; case 3: { show_internal_registers(iris); } break; } } EndChild(); } static uint32_t addr = 0, width = 0, height = 0; static bool unswizzle = false; static float scale = 1.0f; const char* format_names[] = { "32-bit/24-bit", "16-bit" }; int format = 0; static SDL_GPUTexture* tex = nullptr; void show_gs_memory(iris::instance* iris) { using namespace ImGui; struct ps2_gs* gs = iris->ps2->gs; static char buf0[16]; static char buf1[16]; static char buf2[16]; static char buf3[16]; if (InputText("Address##", buf0, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) { if (buf0[0]) { addr = strtoul(buf0, NULL, 16); } } if (InputText("Width##", buf1, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) { if (buf1[0]) { width = strtoul(buf1, NULL, 0); } } if (InputText("Height##", buf2, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) { if (buf2[0]) { height = strtoul(buf2, NULL, 0); } } sprintf(buf3, "%.1f", scale); if (BeginCombo("Scale", buf3, ImGuiComboFlags_HeightSmall)) { char buf4[16]; for (int i = 0; i < 6; i++) { sprintf(buf4, "%.1f", 1.0f + (0.5f * i)); if (Selectable(buf4, scale == 1.0f + (0.5f * i))) { scale = 1.0f + (0.5f * i); } } EndCombo(); } if (BeginCombo("Format", format_names[format], ImGuiComboFlags_HeightSmall)) { for (int i = 0; i < 2; i++) { if (Selectable(format_names[i], i == format)) { format = i; } } EndCombo(); } SDL_GPUTextureFormat fmt; int stride = 0; switch (format) { case 0: { fmt = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM; stride = 4; } break; case 1: { fmt = SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM; stride = 2; } break; } // To-do: GS memory viewer // if (Button("View")) { // addr = strtoul(buf0, NULL, 16); // width = strtoul(buf1, NULL, 0); // height = strtoul(buf2, NULL, 0); // if (tex) { // SDL_ReleaseGPUTexture(iris->device, tex); // } // SDL_GPUTextureCreateInfo tci = {}; // tci.format = fmt; // tci.type = SDL_GPU_TEXTURETYPE_2D; // tci.usage = SDL_GPU_TEXTUREUSAGE_SAMPLER; // tci.width = width; // tci.height = height; // tci.layer_count_or_depth = 1; // tci.num_levels = 1; // tci.sample_count = SDL_GPU_SAMPLECOUNT_1; // tex = SDL_CreateGPUTexture(iris->device, &tci); // SDL_GPUSamplerCreateInfo sci = { // .min_filter = SDL_GPU_FILTER_NEAREST, // .mag_filter = SDL_GPU_FILTER_NEAREST, // .mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST, // .address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, // .address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, // .address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, // }; // } // if (!tex) // return; // SDL_GPUCommandBuffer* cb = SDL_AcquireGPUCommandBuffer(iris->device); // SDL_GPUCopyPass* cp = SDL_BeginGPUCopyPass(cb); // SDL_GPUTransferBufferCreateInfo ttbci = { // .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, // .size = (width * stride) * height // }; // SDL_GPUTransferBuffer* ttb = SDL_CreateGPUTransferBuffer(iris->device, &ttbci); // // fill the transfer buffer // switch (format) { // case 0: { // uint32_t* ptr = (uint32_t*)(((uint8_t*)gs->vram) + addr); // uint32_t* data = (uint32_t*)SDL_MapGPUTransferBuffer(iris->device, ttb, false); // for (int y = 0; y < height; y++) { // for (int x = 0; x < width; x++) { // data[x + (y * width)] = ptr[x + (y * width)] | 0xff000000; // } // } // } break; // case 1: { // uint16_t* ptr = (uint16_t*)(((uint8_t*)gs->vram) + addr); // uint16_t* data = (uint16_t*)SDL_MapGPUTransferBuffer(iris->device, ttb, false); // for (int y = 0; y < height; y++) { // for (int x = 0; x < width; x++) { // data[x + (y * width)] = ptr[x + (y * width)] | 0x8000; // } // } // } break; // } // // SDL_memcpy(data, ptr, (width * stride) * height); // SDL_UnmapGPUTransferBuffer(iris->device, ttb); // SDL_GPUTextureTransferInfo tti = { // .transfer_buffer = ttb, // .offset = 0, // }; // SDL_GPUTextureRegion tr = { // .texture = tex, // .w = width, // .h = height, // .d = 1, // }; // SDL_UploadToGPUTexture(cp, &tti, &tr, false); // // end the copy pass // SDL_EndGPUCopyPass(cp); // SDL_SubmitGPUCommandBuffer(cb); // 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)); } void show_gs_debugger(iris::instance* iris) { using namespace ImGui; if (imgui::BeginEx("GS", &iris->show_gs_debugger, ImGuiWindowFlags_MenuBar)) { if (BeginMenuBar()) { if (BeginMenu("Settings")) { if (BeginMenu(ICON_MS_CROP " Sizing")) { for (int i = 0; i < 4; i++) { if (Selectable(sizing_combo_items[i], i == gs_sizing_index)) { gs_table_sizing = table_sizing_flags[i]; gs_sizing_index = i; } } ImGui::EndMenu(); } ImGui::EndMenu(); } EndMenuBar(); } if (BeginTable("table5", 5, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV)) { TableNextRow(); for (int i = 0; i < 5; i++) { TableSetColumnIndex(i); if (Selectable(gs_debug_names[i], gs_debug_index == i)) { gs_debug_index = i; } } EndTable(); } if (BeginChild("##gschild")) { switch (gs_debug_index) { case 0: { show_gs_registers(iris); } break; case 3: { show_gs_queue(iris); } break; case 4: { show_gs_memory(iris); } break; } } EndChild(); } End(); } } ================================================ FILE: frontend/ui/intc.cpp ================================================ #include #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" namespace iris { const char* ee_irq_sources[] = { "GS", "SBUS", "Vblank In", "Vblank Out", "VIF0", "VIF1", "VU0", "VU1", "IPU", "Timer 0", "Timer 1", "Timer 2", "Timer 3", "SFIFO", "VU0 Watchdog" }; const char* iop_irq_sources[] = { "Vblank In", "GPU", "CDVD", "DMA", "Timer 0", "Timer 1", "Timer 2", "SIO0", "SIO1", "SPU2", "PIO", "Vblank Out", "DVD", "PCMCIA", "Timer 3", "Timer 4", "Timer 5", "SIO2", "HTR0", "HTR1", "HTR2", "HTR3", "USB", "EXTR", "FWRE", "FDMA" }; void show_ee_intc_interrupts(iris::instance* iris) { using namespace ImGui; struct ps2_intc* intc = iris->ps2->ee_intc; if (BeginTable("##eeintc", 3, ImGuiTableFlags_RowBg)) { PushFont(iris->font_small_code); TableSetupColumn("Source"); TableSetupColumn("Status"); TableSetupColumn("Mask"); TableHeadersRow(); PopFont(); for (int i = 0; i < 15; i++) { TableNextRow(); TableSetColumnIndex(0); Text("%s", ee_irq_sources[i]); TableSetColumnIndex(1); int status = intc->stat & (1 << i); int mask = intc->mask & (1 << i); char label[16]; sprintf(label, "%s##s%x", status ? ICON_MS_CHECK : "", i); if (Selectable(label)) { intc->stat ^= 1 << i; } TableSetColumnIndex(2); sprintf(label, "%s##m%x", mask ? ICON_MS_CHECK : "", i); if (Selectable(label)) { intc->mask ^= 1 << i; } } EndTable(); } } void show_ee_interrupts(iris::instance* iris) { using namespace ImGui; struct ps2_intc* intc = iris->ps2->ee_intc; if (imgui::BeginEx("EE Interrupts", &iris->show_ee_interrupts)) { if (Button(ICON_MS_REMOVE_SELECTION)) { intc->mask = 0; } SameLine(); if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) { SetTooltip("Disable all"); } if (Button(ICON_MS_SELECT)) { intc->mask |= 0xffff; } if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) { SetTooltip("Enable all"); } if (BeginChild("##eeintcchild")) { show_ee_intc_interrupts(iris); } EndChild(); } End(); } void show_iop_intc_interrupts(iris::instance* iris) { using namespace ImGui; struct ps2_iop_intc* intc = iris->ps2->iop_intc; if (BeginTable("##iopintc", 3, ImGuiTableFlags_RowBg)) { PushFont(iris->font_small_code); TableSetupColumn("Source"); TableSetupColumn("Status"); TableSetupColumn("Mask"); TableHeadersRow(); PopFont(); for (int i = 0; i < 26; i++) { TableNextRow(); TableSetColumnIndex(0); Text("%s", iop_irq_sources[i]); TableSetColumnIndex(1); int status = intc->stat & (1 << i); int mask = intc->mask & (1 << i); char label[16]; sprintf(label, "%s##s%x", status ? ICON_MS_CHECK : "", i); if (Selectable(label)) { intc->stat ^= 1 << i; } TableSetColumnIndex(2); sprintf(label, "%s##m%x", mask ? ICON_MS_CHECK : "", i); if (Selectable(label)) { intc->mask ^= 1 << i; } } EndTable(); } } void show_iop_interrupts(iris::instance* iris) { using namespace ImGui; struct ps2_iop_intc* intc = iris->ps2->iop_intc; if (imgui::BeginEx("IOP Interrupts", &iris->show_iop_interrupts)) { if (Button(ICON_MS_REMOVE_SELECTION)) { intc->mask = 0; } SameLine(); if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) { SetTooltip("Disable all"); } if (Button(ICON_MS_SELECT)) { intc->mask |= 0xffff; } if (IsItemHovered(ImGuiHoveredFlags_AllowWhenDisabled | ImGuiHoveredFlags_DelayNormal)) { SetTooltip("Enable all"); } if (BeginChild("##iopintcchild")) { show_iop_intc_interrupts(iris); } EndChild(); } End(); } } ================================================ FILE: frontend/ui/logs.cpp ================================================ #include #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" namespace iris { bool ee_follow = true; bool iop_follow = true; bool sysmem_follow = true; void show_logs(iris::instance* iris, const std::vector & logs, bool follow) { using namespace ImGui; PushFont(iris->font_code); if (BeginTable("##logstable", 2, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit)) { for (unsigned int i = 0; i < logs.size(); i++) { TableNextRow(); TableSetColumnIndex(0); Text(" %-3d ", i+1); TableSetColumnIndex(1); char buf[16]; sprintf(buf, "##l%d", i); if (Selectable(buf, false, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns)) { // Do something with text } SameLine(0.0, 0.0); Text("%s", logs[i].c_str()); } if (follow) { SetScrollHereY(1.0f); } EndTable(); } PopFont(); } void show_ee_logs(iris::instance* iris) { using namespace ImGui; if (imgui::BeginEx("EE logs", &iris->show_ee_logs)) { if (Button(ICON_MS_DELETE)) { iris->ee_log.clear(); } SameLine(); if (Button(ICON_MS_CONTENT_COPY)) { std::string buf; for (const std::string& s : iris->ee_log) { buf.append(s); buf.push_back('\n'); } SDL_SetClipboardText(buf.c_str()); } if (BeginChild("##eelog")) { show_logs(iris, iris->ee_log, ee_follow); } EndChild(); } End(); } void show_iop_logs(iris::instance* iris) { using namespace ImGui; if (imgui::BeginEx("IOP logs", &iris->show_iop_logs, ImGuiWindowFlags_MenuBar)) { if (BeginMenuBar()) { if (BeginMenu("Settings")) { if (MenuItem(iop_follow ? ICON_MS_CHECK_BOX " Follow" : ICON_MS_CHECK_BOX_OUTLINE_BLANK " Follow", nullptr)) { iop_follow = !iop_follow; } ImGui::EndMenu(); } EndMenuBar(); } if (Button(ICON_MS_DELETE)) { iris->iop_log.clear(); } SameLine(); if (Button(ICON_MS_CONTENT_COPY)) { std::string buf; for (const std::string& s : iris->iop_log) { buf.append(s); buf.push_back('\n'); } SDL_SetClipboardText(buf.c_str()); } if (BeginChild("##ioplog")) { show_logs(iris, iris->iop_log, iop_follow); } EndChild(); } End(); } void show_sysmem_logs(iris::instance* iris) { using namespace ImGui; if (imgui::BeginEx("SYSMEM logs", &iris->show_sysmem_logs, ImGuiWindowFlags_MenuBar)) { if (BeginMenuBar()) { if (BeginMenu("Settings")) { if (MenuItem(sysmem_follow ? ICON_MS_CHECK_BOX " Follow" : ICON_MS_CHECK_BOX_OUTLINE_BLANK " Follow", nullptr)) { sysmem_follow = !sysmem_follow; } ImGui::EndMenu(); } EndMenuBar(); } if (Button(ICON_MS_DELETE)) { iris->sysmem_log.clear(); } SameLine(); if (Button(ICON_MS_CONTENT_COPY)) { std::string buf; for (const std::string& s : iris->sysmem_log) { buf.append(s); buf.push_back('\n'); } SDL_SetClipboardText(buf.c_str()); } if (BeginChild("##sysmemlog")) { show_logs(iris, iris->sysmem_log, sysmem_follow); } EndChild(); } End(); } } ================================================ FILE: frontend/ui/memory.cpp ================================================ #include #include #include #include "iris.hpp" #include "ee/ee_def.hpp" #include "ee/vu_def.hpp" #include "res/IconsMaterialSymbols.h" #include "memory_viewer.h" namespace iris { static MemoryEditor editor; void show_memory_viewer(iris::instance* iris) { using namespace ImGui; editor.FontOptions = iris->font_body; struct ps2_state* ps2 = iris->ps2; if (imgui::BeginEx("Memory", &iris->show_memory_viewer)) { if (BeginTabBar("##tabbar")) { if (BeginTabItem("EE")) { PushFont(iris->font_code); editor.DrawContents(ps2->ee_ram->buf, ps2->ee_ram->size, 0); PopFont(); EndTabItem(); } if (BeginTabItem("EE SPR")) { PushFont(iris->font_code); editor.DrawContents(ps2->ee->spr->buf, 0x4000, 0); PopFont(); EndTabItem(); } if (BeginTabItem("IOP")) { PushFont(iris->font_code); editor.DrawContents(ps2->iop_ram->buf, ps2->iop_ram->size, 0); PopFont(); EndTabItem(); } if (BeginTabItem("IOP SPR")) { PushFont(iris->font_code); editor.DrawContents(ps2->iop_spr->buf, RAM_SIZE_1KB, 0); PopFont(); EndTabItem(); } if (BeginTabItem("VRAM")) { PushFont(iris->font_code); editor.DrawContents(ps2->gs->vram, 0x400000, 0); PopFont(); EndTabItem(); } if (BeginTabItem("SPU2")) { PushFont(iris->font_code); editor.DrawContents(ps2->spu2->ram, RAM_SIZE_2MB, 0); PopFont(); EndTabItem(); } if (BeginTabItem("VU0 IMEM")) { PushFont(iris->font_code); editor.DrawContents(ps2->vu0->micro_mem, 0x1000, 0); PopFont(); EndTabItem(); } if (BeginTabItem("VU0 DMEM")) { PushFont(iris->font_code); editor.DrawContents(ps2->vu0->vu_mem, 0x1000, 0); PopFont(); EndTabItem(); } if (BeginTabItem("VU1 IMEM")) { PushFont(iris->font_code); editor.DrawContents(ps2->vu1->micro_mem, 0x4000, 0); PopFont(); EndTabItem(); } if (BeginTabItem("VU1 DMEM")) { PushFont(iris->font_code); editor.DrawContents(ps2->vu1->vu_mem, 0x4000, 0); PopFont(); EndTabItem(); } if (ps2->s14x_sram) { if (BeginTabItem("S14X SRAM")) { PushFont(iris->font_code); editor.DrawContents(ps2->s14x_sram->buf, 0x8000, 0); PopFont(); EndTabItem(); } } if (ps2->s14x_link) { if (BeginTabItem("CircLink RAM")) { PushFont(iris->font_code); editor.DrawContents(ps2->s14x_link->ram, 1024, 0); PopFont(); EndTabItem(); } } EndTabBar(); } } End(); } } ================================================ FILE: frontend/ui/memory_card_tool.cpp ================================================ #include #include #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" #include "portable-file-dialogs.h" namespace iris { enum : int { MEMCARD_TYPE_PS1, MEMCARD_TYPE_PS2, MEMCARD_TYPE_POCKETSTATION }; static const char* const type_names[] = { "PS1 Memory Card", "PS2 Memory Card", "PocketStation" }; int size = 0; int type = MEMCARD_TYPE_PS2; int slot = 0; const char* fpath = nullptr; void show_memory_card_tool(iris::instance* iris) { using namespace ImGui; SetNextWindowSizeConstraints(ImVec2(350, 320), ImVec2(FLT_MAX, FLT_MAX)); if (imgui::BeginEx("Create memory card", &iris->show_memory_card_tool, ImGuiWindowFlags_NoCollapse)) { Text("Type"); if (BeginCombo("##type", type_names[type])) { for (int i = 0; i < 3; i++) { if (Selectable(type_names[i], i == type)) { type = i; } } EndCombo(); } Text("Size"); if (type == MEMCARD_TYPE_PS2) { char buf[16]; sprintf(buf, "%d MB", 8 << size); if (BeginCombo("##size", buf)) { for (int i = 0; i < 5; i++) { sprintf(buf, "%d MB", 8 << i); if (Selectable(buf, i == size)) { size = i; } } EndCombo(); } } else { BeginDisabled(true); BeginCombo("##size", "128 KiB"); EndDisabled(); } Text("Attach to"); if (BeginCombo("##slot", slot == -1 ? "None" : slot == 0 ? "Slot 1" : "Slot 2")) { if (Selectable("None", slot == -1)) { slot = -1; } if (Selectable("Slot 1", slot == 0)) { slot = 0; } if (Selectable("Slot 2", slot == 1)) { slot = 1; } EndCombo(); } if (Button("Create")) { // Create memory card file int size_in_bytes; if (type == MEMCARD_TYPE_PS2) { // Calculate data + ECC area (nsects*512 + nsects*16) size_in_bytes = 0x840000 << size; } else { size_in_bytes = 128 * 1024; } audio::mute(iris); std::string default_path = iris->pref_path + "image.mcd"; if (type == MEMCARD_TYPE_POCKETSTATION) { default_path = iris->pref_path + "image.psm"; } auto f = pfd::save_file("Save Memory Card image", default_path, { "Iris Memory Card Image (*.mcd)", "*.mcd", "PCSX2 Memory Card Image (*.ps2)", "*.ps2", "PocketStation Image (*.psm; *.pocket)", "*.psm *.pocket", "All Files (*.*)", "*" }); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { FILE* file = fopen(f.result().c_str(), "wb"); const int shift = 4; void* buf = malloc(size_in_bytes >> shift); memset(buf, 0xff, size_in_bytes >> shift); fseek(file, 0, SEEK_SET); for (int i = 0; i < 1 << shift; i++) { fwrite(buf, size_in_bytes >> shift, 1, file); } fclose(file); char msg[1024]; sprintf(msg, "Created memory card image: \"%s\"", f.result().c_str()); push_info(iris, std::string(msg)); free(buf); if (slot != -1) { if (iris->mcd_slot_type[slot]) { fpath = f.result().c_str(); OpenPopup("Confirm detach"); } else { // Attach memory card to slot if (emu::attach_memory_card(iris, slot, f.result().c_str())) { push_info(iris, "Memory card attached successfully."); if (slot == 0) { iris->mcd0_path = f.result(); } else { iris->mcd1_path = f.result(); } } else { push_info(iris, "Failed to attach memory card."); } } } } } if (fpath && imgui::BeginEx("Confirm detach", NULL, ImGuiWindowFlags_AlwaysAutoResize)) { Text("A memory card is already attached to this slot. Do you want to detach it?"); if (Button("Yes")) { if (emu::attach_memory_card(iris, slot, fpath)) { if (slot == 0) { iris->mcd0_path = std::string(fpath); } else { iris->mcd1_path = std::string(fpath); } push_info(iris, "Memory card attached successfully."); } else { push_info(iris, "Failed to attach memory card."); } fpath = nullptr; } SameLine(); if (Button("No")) { fpath = nullptr; } End(); } } End(); } } ================================================ FILE: frontend/ui/memory_search.cpp ================================================ #include #include #include #include #include #include "iris.hpp" #include "ee/ee_def.hpp" #include "ee/vu_def.hpp" #include "res/IconsMaterialSymbols.h" #include "portable-file-dialogs.h" namespace iris { enum { SEARCH_CMP_EQUAL, SEARCH_CMP_NOT_EQUAL, SEARCH_CMP_LESS_THAN, SEARCH_CMP_GREATER_THAN, SEARCH_CMP_LESS_EQUAL, SEARCH_CMP_GREATER_EQUAL }; const char* search_cmp_names[] = { "Equal", "Not Equal", "Less Than", "Greater Than", "Less Than or Equal", "Greater Than or Equal" }; enum { SEARCH_CPU_EE, SEARCH_CPU_IOP }; const char* search_cpu_names[] = { "EE", "IOP" }; enum { SEARCH_TYPE_U8, SEARCH_TYPE_U16, SEARCH_TYPE_U32, SEARCH_TYPE_U64, SEARCH_TYPE_S8, SEARCH_TYPE_S16, SEARCH_TYPE_S32, SEARCH_TYPE_S64, SEARCH_TYPE_F32, SEARCH_TYPE_F64 }; const char* search_type_names[] = { "Uint8", "Uint16", "Uint32", "Uint64", "Sint8", "Sint16", "Sint32", "Sint64", "Float32", "Float64" }; union value { uint8_t u8[8]; uint16_t u16[4]; uint32_t u32[2]; uint64_t u64; int8_t s8[8]; int16_t s16[4]; int32_t s32[2]; int64_t s64; float f32[2]; double f64; }; struct match { uint32_t address; value prev_value, curr_value; std::string description; int cpu; int type; }; std::vector search_matches; std::vector address_list; int search_type = SEARCH_TYPE_U32; int search_cmp = SEARCH_CMP_EQUAL; int search_cpu = SEARCH_CPU_EE; bool display_hex = false; bool search_aligned = true; template bool compare_values(int cmp, T a, T b) { switch (cmp) { case SEARCH_CMP_EQUAL: return a == b; case SEARCH_CMP_NOT_EQUAL: return a != b; case SEARCH_CMP_LESS_THAN: return a < b; case SEARCH_CMP_GREATER_THAN: return a > b; case SEARCH_CMP_LESS_EQUAL: return a <= b; case SEARCH_CMP_GREATER_EQUAL: return a >= b; } return false; } void search_memory(struct ps2_state* ps2, int cpu, int type, int cmp, const char* value_str, bool aligned) { search_matches.clear(); struct ps2_ram* mem = cpu == SEARCH_CPU_EE ? ps2->ee_ram : ps2->iop_ram; int size = 0; switch (type) { case SEARCH_TYPE_U8: case SEARCH_TYPE_S8: size = 1; break; case SEARCH_TYPE_U16: case SEARCH_TYPE_S16: size = 2; break; case SEARCH_TYPE_U32: case SEARCH_TYPE_S32: case SEARCH_TYPE_F32: size = 4; break; case SEARCH_TYPE_U64: case SEARCH_TYPE_S64: case SEARCH_TYPE_F64: size = 8; break; } for (int addr = 0; addr < mem->size - size; addr += (aligned ? size : 1)) { match m; m.address = addr; m.curr_value.u64 = *(uint64_t*)&mem->buf[addr]; m.prev_value = m.curr_value; m.cpu = cpu; m.type = type; switch (type) { case SEARCH_TYPE_U8: { uint8_t val = (uint8_t)std::strtoul(value_str, nullptr, 0); if (compare_values(cmp, m.curr_value.u8[0], val)) { search_matches.push_back(m); } } break; case SEARCH_TYPE_U16: { uint16_t val = (uint16_t)std::strtoul(value_str, nullptr, 0); if (compare_values(cmp, m.curr_value.u16[0], val)) { search_matches.push_back(m); } } break; case SEARCH_TYPE_U32: { uint32_t val = (uint32_t)std::strtoul(value_str, nullptr, 0); if (compare_values(cmp, m.curr_value.u32[0], val)) { search_matches.push_back(m); } } break; case SEARCH_TYPE_U64: { uint64_t val = (uint64_t)std::strtoull(value_str, nullptr, 0); if (compare_values(cmp, m.curr_value.u64, val)) { search_matches.push_back(m); } } break; case SEARCH_TYPE_S8: { int8_t val = (int8_t)std::strtol(value_str, nullptr, 0); if (compare_values(cmp, m.curr_value.s8[0], val)) { search_matches.push_back(m); } } break; case SEARCH_TYPE_S16: { int16_t val = (int16_t)std::strtol(value_str, nullptr, 0); if (compare_values(cmp, m.curr_value.s16[0], val)) { search_matches.push_back(m); } } break; case SEARCH_TYPE_S32: { int32_t val = (int32_t)std::strtol(value_str, nullptr, 0); if (compare_values(cmp, m.curr_value.s32[0], val)) { search_matches.push_back(m); } } break; case SEARCH_TYPE_S64: { int64_t val = (int64_t)std::strtoll(value_str, nullptr, 0); if (compare_values(cmp, m.curr_value.s64, val)) { search_matches.push_back(m); } } break; case SEARCH_TYPE_F32: { float val = std::strtof(value_str, nullptr); if (compare_values(cmp, m.curr_value.f32[0], val)) { search_matches.push_back(m); } } break; case SEARCH_TYPE_F64: { double val = std::strtod(value_str, nullptr); if (compare_values(cmp, m.curr_value.f64, val)) { search_matches.push_back(m); } } break; } } } void filter_results(int type, int cmp, const char* value_str) { // TODO switch (type) { case SEARCH_TYPE_U8: { uint8_t val = (uint8_t)std::strtoul(value_str, nullptr, 0); std::erase_if(search_matches, [cmp, val](const match& m) { return !compare_values(cmp, m.curr_value.u8[0], val); }); } break; case SEARCH_TYPE_U16: { uint16_t val = (uint16_t)std::strtoul(value_str, nullptr, 0); std::erase_if(search_matches, [cmp, val](const match& m) { return !compare_values(cmp, m.curr_value.u16[0], val); }); } break; case SEARCH_TYPE_U32: { uint32_t val = (uint32_t)std::strtoul(value_str, nullptr, 0); std::erase_if(search_matches, [cmp, val](const match& m) { return !compare_values(cmp, m.curr_value.u32[0], val); }); } break; case SEARCH_TYPE_U64: { uint64_t val = (uint64_t)std::strtoull(value_str, nullptr, 0); std::erase_if(search_matches, [cmp, val](const match& m) { return !compare_values(cmp, m.curr_value.u64, val); }); } break; case SEARCH_TYPE_S8: { int8_t val = (int8_t)std::strtol(value_str, nullptr, 0); std::erase_if(search_matches, [cmp, val](const match& m) { return !compare_values(cmp, m.curr_value.s8[0], val); }); } break; case SEARCH_TYPE_S16: { int16_t val = (int16_t)std::strtol(value_str, nullptr, 0); std::erase_if(search_matches, [cmp, val](const match& m) { return !compare_values(cmp, m.curr_value.s16[0], val); }); } break; case SEARCH_TYPE_S32: { int32_t val = (int32_t)std::strtol(value_str, nullptr, 0); std::erase_if(search_matches, [cmp, val](const match& m) { return !compare_values(cmp, m.curr_value.s32[0], val); }); } break; case SEARCH_TYPE_S64: { int64_t val = (int64_t)std::strtoll(value_str, nullptr, 0); std::erase_if(search_matches, [cmp, val](const match& m) { return !compare_values(cmp, m.curr_value.s64, val); }); } break; case SEARCH_TYPE_F32: { float val = std::strtof(value_str, nullptr); std::erase_if(search_matches, [cmp, val](const match& m) { return !compare_values(cmp, m.curr_value.f32[0], val); }); } break; case SEARCH_TYPE_F64: { double val = std::strtod(value_str, nullptr); std::erase_if(search_matches, [cmp, val](const match& m) { return !compare_values(cmp, m.curr_value.f64, val); }); } break; } } void write_match_value(struct ps2_state* ps2, int cpu, match& m, int type) { struct ps2_ram* mem = cpu == SEARCH_CPU_EE ? ps2->ee_ram : ps2->iop_ram; switch (type) { case SEARCH_TYPE_U8: case SEARCH_TYPE_S8: { *(uint8_t*)&mem->buf[m.address] = m.curr_value.u8[0]; } break; case SEARCH_TYPE_U16: case SEARCH_TYPE_S16: { *(uint16_t*)&mem->buf[m.address] = m.curr_value.u16[0]; } break; case SEARCH_TYPE_U32: case SEARCH_TYPE_F32: case SEARCH_TYPE_S32: { *(uint32_t*)&mem->buf[m.address] = m.curr_value.u32[0]; } break; case SEARCH_TYPE_U64: case SEARCH_TYPE_F64: case SEARCH_TYPE_S64: { *(uint64_t*)&mem->buf[m.address] = m.curr_value.u64; } break; } } void sprintf_match(const value& v, char* buf, size_t size, int type, int hex) { switch (type) { case SEARCH_TYPE_U8: snprintf(buf, size, hex ? "0x%02x" : "%u", v.u8[0]); break; case SEARCH_TYPE_U16: snprintf(buf, size, hex ? "0x%04x" : "%u", v.u16[0]); break; case SEARCH_TYPE_U32: snprintf(buf, size, hex ? "0x%08x" : "%u", v.u32[0]); break; case SEARCH_TYPE_U64: snprintf(buf, size, hex ? "0x%016llx" : "%llu", v.u64); break; case SEARCH_TYPE_S8: snprintf(buf, size, "%d", v.s8[0]); break; case SEARCH_TYPE_S16: snprintf(buf, size, "%d", v.s16[0]); break; case SEARCH_TYPE_S32: snprintf(buf, size, "%d", v.s32[0]); break; case SEARCH_TYPE_S64: snprintf(buf, size, "%lld", v.s64); break; case SEARCH_TYPE_F32: snprintf(buf, size, "%f", v.f32[0]); break; case SEARCH_TYPE_F64: snprintf(buf, size, "%lf", v.f64); break; } } void show_match_change_dialog(iris::instance* iris, match& m, char* label, int search_type, int search_cpu) { using namespace ImGui; struct ps2_state* ps2 = iris->ps2; static char new_value[32]; PushFont(iris->font_small); TextDisabled("Edit "); SameLine(0.0, 0.0); PushFont(iris->font_small_code); Text("%s", label); PopFont(); PopFont(); PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0)); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0)); PushFont(iris->font_body); PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35)); AlignTextToFramePadding(); Text(ICON_MS_EDIT); SameLine(); SetNextItemWidth(100); PushFont(iris->font_code); if (InputText("##", new_value, 32, ImGuiInputTextFlags_EnterReturnsTrue)) { if (new_value[0]) { if (strchr(new_value, '.')) { if (search_type == SEARCH_TYPE_F64) { m.curr_value.f64 = std::strtod(new_value, nullptr); } else { m.curr_value.f32[0] = std::strtof(new_value, nullptr); } } else { m.curr_value.u64 = std::strtoull(new_value, nullptr, 0); } write_match_value(ps2, search_cpu, const_cast(m), search_type); } CloseCurrentPopup(); } PopFont(); if (Button("Change")) { if (new_value[0]) { if (strchr(new_value, '.')) { if (search_type == SEARCH_TYPE_F64) { m.curr_value.f64 = std::strtod(new_value, nullptr); } else { m.curr_value.f32[0] = std::strtof(new_value, nullptr); } } else { m.curr_value.u64 = std::strtoull(new_value, nullptr, 0); } write_match_value(ps2, search_cpu, const_cast(m), search_type); } CloseCurrentPopup(); } SameLine(); if (Button("Cancel")) CloseCurrentPopup(); PopStyleColor(); PopStyleVar(2); PopFont(); } void show_description_change_dialog(iris::instance* iris, match& m) { using namespace ImGui; struct ps2_state* ps2 = iris->ps2; static char new_value[32]; PushFont(iris->font_small); TextDisabled("Edit description"); PopFont(); PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0)); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0)); PushFont(iris->font_body); PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35)); AlignTextToFramePadding(); Text(ICON_MS_EDIT); SameLine(); SetNextItemWidth(100); static char desc_buf[512]; if (desc_buf[0] == '\0') { strncpy(desc_buf, m.description.c_str(), sizeof(desc_buf)); } desc_buf[sizeof(desc_buf) - 1] = '\0'; if (InputText("##desc", desc_buf, sizeof(desc_buf), ImGuiInputTextFlags_EnterReturnsTrue)) { m.description = "\"" + std::string(desc_buf) + "\""; CloseCurrentPopup(); } if (Button("Change")) { m.description = "\"" + std::string(desc_buf) + "\""; CloseCurrentPopup(); } SameLine(); if (Button("Cancel")) CloseCurrentPopup(); PopStyleColor(); PopStyleVar(2); PopFont(); } int frame = 0; void show_search_table(iris::instance* iris, struct ps2_state* ps2, int type, int cpu) { using namespace ImGui; static uint32_t selected_address = 0; if (search_matches.empty()) { SeparatorText("Search results"); } else { char buf[256]; if (search_matches.size() > 9999) { snprintf(buf, sizeof(buf), "Search results (9999+ matches)"); } else { snprintf(buf, sizeof(buf), "Search results (%zu matches)", search_matches.size()); } SeparatorText(buf); } if (BeginTable("Matches", 3, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_SizingStretchProp)) { TableSetupColumn("Address"); TableSetupColumn("Previous Value"); TableSetupColumn("Current Value"); TableHeadersRow(); for (match& m : search_matches) { TableNextRow(); TableSetColumnIndex(0); char addr_label[16]; char prev_value_label[64]; char curr_value_label[64]; sprintf(addr_label, "0x%08x", m.address); sprintf_match(m.prev_value, prev_value_label, sizeof(prev_value_label), type, display_hex); sprintf_match(m.curr_value, curr_value_label, sizeof(curr_value_label), type, display_hex); PushFont(iris->font_code); Selectable(addr_label, false, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns); if (IsItemClicked(ImGuiMouseButton_Right)) { OpenPopup("context_menu"); selected_address = m.address; } if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left)) { OpenPopup("edit_value_popup"); selected_address = m.address; } if (selected_address == m.address) if (BeginPopup("context_menu")) { PushFont(iris->font_small_code); TextDisabled("0x%08x", m.address); PopFont(); PushFont(iris->font_body); if (BeginMenu(ICON_MS_CONTENT_COPY " Copy")) { if (Selectable("Address")) { ImGui::SetClipboardText(addr_label); } if (Selectable("Previous Value")) { ImGui::SetClipboardText(prev_value_label); } if (Selectable("Current Value")) { ImGui::SetClipboardText(curr_value_label); } ImGui::EndMenu(); } if (BeginMenu(ICON_MS_EDIT " Edit value")) { show_match_change_dialog(iris, m, addr_label, type, cpu); ImGui::EndMenu(); } bool in_address_list = false; for (const match& am : address_list) { if (am.address == m.address) { in_address_list = true; break; } } if (Selectable(in_address_list ? ICON_MS_REMOVE " Remove from address list" : ICON_MS_ADD " Add to address list")) { if (in_address_list) { std::erase_if(address_list, [m](const match& am) { return am.address == m.address; }); } else { m.description = "\"No description\""; m.cpu = cpu; m.type = type; address_list.push_back(m); } } PopFont(); EndPopup(); } if (selected_address == m.address) if (BeginPopup("edit_value_popup")) { show_match_change_dialog(iris, m, addr_label, type, cpu); EndPopup(); } TableSetColumnIndex(1); Text("%s", prev_value_label); TableSetColumnIndex(2); Text("%s", curr_value_label); PopFont(); } EndTable(); } } void show_address_list(iris::instance* iris) { using namespace ImGui; struct ps2_state* ps2 = iris->ps2; static uint32_t selected_address = 0; if (BeginTable("Addresses", 6, ImGuiTableFlags_Borders | ImGuiTableFlags_RowBg | ImGuiTableFlags_ScrollY | ImGuiTableFlags_Resizable)) { TableSetupColumn("Address", ImGuiTableColumnFlags_WidthFixed, 100.0f); TableSetupColumn("CPU", ImGuiTableColumnFlags_WidthFixed, 20.0f); TableSetupColumn("Type", ImGuiTableColumnFlags_WidthFixed, 30.0f); TableSetupColumn("Description"); TableSetupColumn("Previous Value", ImGuiTableColumnFlags_WidthFixed, 150.0f); TableSetupColumn("Current Value"); TableHeadersRow(); for (match& m : address_list) { TableNextRow(); TableSetColumnIndex(0); char addr_label[16]; char prev_value_label[64]; char curr_value_label[64]; sprintf(addr_label, "0x%08x", m.address); sprintf_match(m.prev_value, prev_value_label, sizeof(prev_value_label), m.type, display_hex); sprintf_match(m.curr_value, curr_value_label, sizeof(curr_value_label), m.type, display_hex); PushFont(iris->font_code); Selectable(addr_label, false, ImGuiSelectableFlags_AllowOverlap | ImGuiSelectableFlags_SpanAllColumns); if (IsItemClicked(ImGuiMouseButton_Right)) { OpenPopup("context_menu_al"); selected_address = m.address; } if (IsItemHovered() && IsMouseDoubleClicked(ImGuiMouseButton_Left)) { OpenPopup("edit_value_popup_al"); selected_address = m.address; } if (selected_address == m.address) if (BeginPopup("context_menu_al")) { PushFont(iris->font_small_code); TextDisabled("0x%08x", m.address); PopFont(); PushFont(iris->font_body); if (BeginMenu(ICON_MS_CONTENT_COPY " Copy")) { if (Selectable("Address")) { ImGui::SetClipboardText(addr_label); } if (Selectable("Previous Value")) { ImGui::SetClipboardText(prev_value_label); } if (Selectable("Current Value")) { ImGui::SetClipboardText(curr_value_label); } ImGui::EndMenu(); } if (BeginMenu(ICON_MS_EDIT " Edit value")) { show_match_change_dialog(iris, m, addr_label, m.type, m.cpu); ImGui::EndMenu(); } if (BeginMenu(ICON_MS_EDIT " Edit description")) { show_description_change_dialog(iris, m); ImGui::EndMenu(); } if (BeginMenu(ICON_MS_EDIT " Edit type")) { for (int i = 0; i < IM_ARRAYSIZE(search_type_names); i++) { if (MenuItem(search_type_names[i], nullptr, m.type == i)) { m.type = i; } } ImGui::EndMenu(); } if (Selectable(ICON_MS_REMOVE " Remove from address list")) { std::erase_if(address_list, [m](const match& am) { return am.address == m.address; }); } PopFont(); EndPopup(); } if (selected_address == m.address) if (BeginPopup("edit_value_popup_al")) { show_match_change_dialog(iris, m, addr_label, m.type, m.cpu); EndPopup(); } TableSetColumnIndex(1); Text("%s", m.cpu == SEARCH_CPU_EE ? "EE" : "IOP"); TableSetColumnIndex(2); Text("%s", search_type_names[m.type]); TableSetColumnIndex(3); PushFont(iris->font_body); Text("%s", m.description.c_str()); PopFont(); TableSetColumnIndex(4); Text("%s", prev_value_label); TableSetColumnIndex(5); Text("%s", curr_value_label); PopFont(); } } EndTable(); } void show_search_options(iris::instance* iris) { using namespace ImGui; struct ps2_state* ps2 = iris->ps2; SeparatorText("Search options"); PushItemWidth(-FLT_MIN); Text("Type"); if (BeginCombo("##type", search_type_names[search_type])) { for (int i = 0; i < IM_ARRAYSIZE(search_type_names); i++) { if (Selectable(search_type_names[i], search_type == i)) { search_type = i; } } EndCombo(); } PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0f); Checkbox("Aligned", &search_aligned); PopStyleVar(); Text("Comparison"); if (BeginCombo("##comparison", search_cmp_names[search_cmp])) { for (int i = 0; i < IM_ARRAYSIZE(search_cmp_names); i++) { if (Selectable(search_cmp_names[i], search_cmp == i)) { search_cmp = i; } } EndCombo(); } Text("Memory"); if (BeginCombo("##memory", search_cpu_names[search_cpu])) { for (int i = 0; i < IM_ARRAYSIZE(search_cpu_names); i++) { if (Selectable(search_cpu_names[i], search_cpu == i)) { search_cpu = i; } } EndCombo(); } static char buf[64]; Text("Value"); if (InputText("##value", buf, sizeof(buf), ImGuiInputTextFlags_EnterReturnsTrue)) { search_memory(ps2, search_cpu, search_type, search_cmp, buf, search_aligned); } PopItemWidth(); BeginDisabled(buf[0] == '\0'); if (Button("Search")) { search_memory(ps2, search_cpu, search_type, search_cmp, buf, search_aligned); } SameLine(); EndDisabled(); BeginDisabled(buf[0] == '\0' || search_matches.empty()); if (Button("Filter")) { filter_results(search_type, search_cmp, buf); } EndDisabled(); } void update_search_matches(struct ps2_state* ps2, int cpu) { if (frame != 4) { frame++; return; } for (match& m : search_matches) { struct ps2_ram* mem = m.cpu == SEARCH_CPU_EE ? ps2->ee_ram : ps2->iop_ram; m.curr_value.u64 = *(uint64_t*)&mem->buf[m.address]; } for (match& m : address_list) { struct ps2_ram* mem = m.cpu == SEARCH_CPU_EE ? ps2->ee_ram : ps2->iop_ram; m.curr_value.u64 = *(uint64_t*)&mem->buf[m.address]; } frame = 0; } std::string serialize_address_list() { std::stringstream ss; for (const match& m : address_list) { ss << "0x" << std::hex << m.address << "," << m.description << "," << m.cpu << "," << m.type << "\n"; } return ss.str(); } void import_address_list_from_stream(std::istream& stream) { address_list.clear(); std::string line; while (std::getline(stream, line)) { std::stringstream linestream(line); std::string address_str; std::string description; std::string cpu_str; std::string type_str; bool valid = true; valid = std::getline(linestream, address_str, ',') && std::getline(linestream, description, ',') && std::getline(linestream, cpu_str, ',') && std::getline(linestream, type_str); if (valid) { match m; m.address = (uint32_t)std::strtoul(address_str.c_str(), nullptr, 0); m.description = description; m.prev_value.u64 = 0; m.curr_value.u64 = 0; m.cpu = std::strtoul(cpu_str.c_str(), nullptr, 0); m.type = std::strtoul(type_str.c_str(), nullptr, 0); // To-do: Remove double quotes from description if present address_list.push_back(m); } } } void show_memory_search(iris::instance* iris) { using namespace ImGui; struct ps2_state* ps2 = iris->ps2; update_search_matches(ps2, search_cpu); SetNextWindowSizeConstraints(ImVec2(600, 560), ImVec2(FLT_MAX, FLT_MAX)); int top_shelf_height = GetContentRegionAvail().y - 220; if (imgui::BeginEx("Memory search", &iris->show_memory_search, ImGuiWindowFlags_MenuBar)) { if (BeginMenuBar()) { if (BeginMenu("File")) { if (BeginMenu("Address list")) { if (MenuItem("Export to file...")) { std::string str = serialize_address_list(); pfd::save_file file("Export Address List", "address_list.csv", { "CSV Files (*.csv)", "*.csv", "All files (*.*)", "" }); if (!file.result().empty()) { FILE* f = fopen(file.result().c_str(), "w"); if (f) { fwrite(str.c_str(), 1, str.size(), f); fclose(f); } else { push_info(iris, "Failed to open file for writing address list"); } } } if (MenuItem("Import from file...")) { pfd::open_file file("Import Address List", "", { "CSV Files (*.csv)", "*.csv", "All Files (*.*)", "*" }); if (!file.result().empty()) { std::ifstream f(file.result().at(0)); if (f.is_open()) { import_address_list_from_stream(f); } else { push_info(iris, "Failed to open file for reading address list"); } } } if (MenuItem("Export to clipboard")) { std::string str = serialize_address_list(); SDL_SetClipboardText(str.c_str()); } if (MenuItem("Import from clipboard")) { if (SDL_HasClipboardText()) { std::string clip_text = SDL_GetClipboardText(); std::istringstream iss(clip_text); import_address_list_from_stream(iss); } } if (MenuItem("Clear")) { address_list.clear(); } ImGui::EndMenu(); } ImGui::EndMenu(); } if (BeginMenu("View")) { MenuItem("Display as hex", nullptr, &display_hex); ImGui::EndMenu(); } EndMenuBar(); } if (BeginChild("##search_table", ImVec2(GetContentRegionAvail().x - 225, GetContentRegionAvail().y - 220))) { show_search_table(iris, ps2, search_type, search_cpu); } EndChild(); SameLine(); if (BeginChild("##search_options", ImVec2(0, GetContentRegionAvail().y - 220))) { show_search_options(iris); } EndChild(); SeparatorText("Address list"); if (BeginChild("##address_list")) { show_address_list(iris); } EndChild(); } End(); } } ================================================ FILE: frontend/ui/memory_viewer.h ================================================ // Mini memory editor for Dear ImGui (to embed in your game/tools) // Get latest version at http://www.github.com/ocornut/imgui_club // Licensed under The MIT License (MIT) // Right-click anywhere to access the Options menu! // You can adjust the keyboard repeat delay/rate in ImGuiIO. // The code assume a mono-space font for simplicity! // If you don't use the default font, use ImGui::PushFont()/PopFont() to switch to a mono-space font before calling this. // // Usage: // // Create a window and draw memory editor inside it: // static MemoryEditor mem_edit_1; // static char data[0x10000]; // size_t data_size = 0x10000; // mem_edit_1.DrawWindow("Memory Editor", data, data_size); // // Usage: // // If you already have a window, use DrawContents() instead: // static MemoryEditor mem_edit_2; // ImGui::Begin("MyWindow") // mem_edit_2.DrawContents(this, sizeof(*this), (size_t)this); // ImGui::End(); // // Changelog: // - v0.10: initial version // - v0.23 (2017/08/17): added to github. fixed right-arrow triggering a byte write. // - v0.24 (2018/06/02): changed DragInt("Rows" to use a %d data format (which is desirable since imgui 1.61). // - v0.25 (2018/07/11): fixed wording: all occurrences of "Rows" renamed to "Columns". // - v0.26 (2018/08/02): fixed clicking on hex region // - v0.30 (2018/08/02): added data preview for common data types // - v0.31 (2018/10/10): added OptUpperCaseHex option to select lower/upper casing display [@samhocevar] // - v0.32 (2018/10/10): changed signatures to use void* instead of unsigned char* // - v0.33 (2018/10/10): added OptShowOptions option to hide all the interactive option setting. // - v0.34 (2019/05/07): binary preview now applies endianness setting [@nicolasnoble] // - v0.35 (2020/01/29): using ImGuiDataType available since Dear ImGui 1.69. // - v0.36 (2020/05/05): minor tweaks, minor refactor. // - 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. // - v0.41 (2020/10/05): fix when using with keyboard/gamepad navigation enabled. // - v0.42 (2020/10/14): fix for . character in ASCII view always being greyed out. // - v0.43 (2021/03/12): added OptFooterExtraHeight to allow for custom drawing at the bottom of the editor [@leiradel] // - v0.44 (2021/03/12): use ImGuiInputTextFlags_AlwaysOverwrite in 1.82 + fix hardcoded width. // - 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. // - v0.51 (2024/02/22): fix for layout change in 1.89 when using IMGUI_DISABLE_OBSOLETE_FUNCTIONS. (#34) // - v0.52 (2024/03/08): removed unnecessary GetKeyIndex() calls, they are a no-op since 1.87. // - v0.53 (2024/05/27): fixed right-click popup from not appearing when using DrawContents(). warning fixes. (#35) // - v0.54 (2024/07/29): allow ReadOnly mode to still select and preview data. (#46) [@DeltaGW2]) // - v0.55 (2024/08/19): added BgColorFn to allow setting background colors independently from highlighted selection. (#27) [@StrikerX3] // added MouseHoveredAddr public readable field. (#47, #27) [@StrikerX3] // fixed a data preview crash with 1.91.0 WIP. fixed contiguous highlight color when using data preview. // *BREAKING* added UserData field passed to all optional function handlers: ReadFn, WriteFn, HighlightFn, BgColorFn. (#50) [@silverweed] // - v0.56 (2024/11/04): fixed MouseHovered, MouseHoveredAddr not being set when hovering a byte being edited. (#54) // // TODO: // - This is generally old/crappy code, it should work but isn't very good.. to be rewritten some day. // - 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... // - Arrows are being sent to the InputText() about to disappear which for LeftArrow makes the text cursor appear at position 1 for one frame. // - Using InputText() is awkward and maybe overkill here, consider implementing something custom. #pragma once #include // sprintf, scanf #include // uint8_t, etc. #if defined(_MSC_VER) || defined(_UCRT) #define _PRISizeT "I" #define ImSnprintf _snprintf #else #define _PRISizeT "z" #define ImSnprintf snprintf #endif #if defined(_MSC_VER) || defined(_UCRT) #pragma warning (push) #pragma warning (disable: 4996) // warning C4996: 'sprintf': This function or variable may be unsafe. #endif struct MemoryEditor { enum DataFormat { DataFormat_Bin = 0, DataFormat_Dec = 1, DataFormat_Hex = 2, DataFormat_COUNT }; ImFont* FontOptions = nullptr; // Settings bool Open; // = true // set to false when DrawWindow() was closed. ignore if not using DrawWindow(). bool ReadOnly; // = false // disable any editing. int Cols; // = 16 // number of columns to display. bool OptShowOptions; // = true // display options button/context menu. when disabled, options will be locked unless you provide your own UI for them. bool OptShowDataPreview; // = false // display a footer previewing the decimal/binary/hex/float representation of the currently selected bytes. bool OptShowHexII; // = false // display values in HexII representation instead of regular hexadecimal: hide null/zero bytes, ascii values as ".X". bool OptShowAscii; // = true // display ASCII representation on the right side. bool OptGreyOutZeroes; // = true // display null/zero bytes using the TextDisabled color. bool OptUpperCaseHex; // = true // display hexadecimal values as "FF" instead of "ff". int OptMidColsCount; // = 8 // set to 0 to disable extra spacing between every mid-cols. int OptAddrDigitsCount; // = 0 // number of addr digits to display (default calculated based on maximum displayed addr). float OptFooterExtraHeight; // = 0 // space to reserve at the bottom of the widget to add custom widgets ImU32 HighlightColor; // // background color of highlighted bytes. // Function handlers ImU8 (*ReadFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to read bytes. void (*WriteFn)(ImU8* mem, size_t off, ImU8 d, void* user_data); // = 0 // optional handler to write bytes. bool (*HighlightFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return Highlight property (to support non-contiguous highlighting). ImU32 (*BgColorFn)(const ImU8* mem, size_t off, void* user_data); // = 0 // optional handler to return custom background color of individual bytes. void* UserData; // = NULL // user data forwarded to the function handlers // Public read-only data bool MouseHovered; // set when mouse is hovering a value. size_t MouseHoveredAddr; // the address currently being hovered if MouseHovered is set. // [Internal State] bool ContentsWidthChanged; size_t DataPreviewAddr; size_t DataEditingAddr; bool DataEditingTakeFocus; char DataInputBuf[32]; char AddrInputBuf[32]; size_t GotoAddr; size_t HighlightMin, HighlightMax; int PreviewEndianness; ImGuiDataType PreviewDataType; MemoryEditor() { // Settings Open = true; ReadOnly = false; Cols = 16; OptShowOptions = true; OptShowDataPreview = true; OptShowHexII = false; OptShowAscii = true; OptGreyOutZeroes = true; OptUpperCaseHex = true; OptMidColsCount = 8; OptAddrDigitsCount = 0; OptFooterExtraHeight = 0.0f; ReadFn = nullptr; WriteFn = nullptr; HighlightFn = nullptr; BgColorFn = nullptr; UserData = nullptr; FontOptions = nullptr; // State/Internals ContentsWidthChanged = false; DataPreviewAddr = DataEditingAddr = (size_t)-1; DataEditingTakeFocus = false; memset(DataInputBuf, 0, sizeof(DataInputBuf)); memset(AddrInputBuf, 0, sizeof(AddrInputBuf)); GotoAddr = (size_t)-1; MouseHovered = false; MouseHoveredAddr = 0; HighlightMin = HighlightMax = (size_t)-1; PreviewEndianness = 0; PreviewDataType = ImGuiDataType_S32; } void GotoAddrAndHighlight(size_t addr_min, size_t addr_max) { GotoAddr = addr_min; HighlightMin = addr_min; HighlightMax = addr_max; } struct Sizes { int AddrDigitsCount; float LineHeight; float GlyphWidth; float HexCellWidth; float SpacingBetweenMidCols; float PosHexStart; float PosHexEnd; float PosAsciiStart; float PosAsciiEnd; float WindowWidth; Sizes() { memset(this, 0, sizeof(*this)); } }; void CalcSizes(Sizes& s, size_t mem_size, size_t base_display_addr) { ImGuiStyle& style = ImGui::GetStyle(); s.AddrDigitsCount = OptAddrDigitsCount; if (s.AddrDigitsCount == 0) for (size_t n = base_display_addr + mem_size - 1; n > 0; n >>= 4) s.AddrDigitsCount++; s.LineHeight = ImGui::GetTextLineHeight(); s.GlyphWidth = ImGui::CalcTextSize("F").x + 1; // We assume the font is mono-space s.HexCellWidth = (float)(int)(s.GlyphWidth * 2.5f); // "FF " we include trailing space in the width to easily catch clicks everywhere s.SpacingBetweenMidCols = (float)(int)(s.HexCellWidth * 0.25f); // Every OptMidColsCount columns we add a bit of extra spacing s.PosHexStart = (s.AddrDigitsCount + 2) * s.GlyphWidth; s.PosHexEnd = s.PosHexStart + (s.HexCellWidth * Cols); s.PosAsciiStart = s.PosAsciiEnd = s.PosHexEnd; if (OptShowAscii) { s.PosAsciiStart = s.PosHexEnd + s.GlyphWidth * 1; if (OptMidColsCount > 0) s.PosAsciiStart += (float)((Cols + OptMidColsCount - 1) / OptMidColsCount) * s.SpacingBetweenMidCols; s.PosAsciiEnd = s.PosAsciiStart + Cols * s.GlyphWidth; } s.WindowWidth = s.PosAsciiEnd + style.ScrollbarSize + style.WindowPadding.x * 2 + s.GlyphWidth; } // Standalone Memory Editor window void DrawWindow(const char* title, void* mem_data, size_t mem_size, size_t base_display_addr = 0x0000) { Sizes s; CalcSizes(s, mem_size, base_display_addr); ImGui::SetNextWindowSize(ImVec2(s.WindowWidth, s.WindowWidth * 0.60f), ImGuiCond_FirstUseEver); ImGui::SetNextWindowSizeConstraints(ImVec2(0.0f, 0.0f), ImVec2(s.WindowWidth, FLT_MAX)); Open = true; if (ImGui::Begin(title, &Open, ImGuiWindowFlags_NoScrollbar)) { DrawContents(mem_data, mem_size, base_display_addr); if (ContentsWidthChanged) { CalcSizes(s, mem_size, base_display_addr); ImGui::SetWindowSize(ImVec2(s.WindowWidth, ImGui::GetWindowSize().y)); } } ImGui::End(); } // Memory Editor contents only void DrawContents(void* mem_data_void, size_t mem_size, size_t base_display_addr = 0x0000) { HighlightColor = ImGui::GetColorU32(ImGuiCol_Text, 0.25f); if (Cols < 1) Cols = 1; ImU8* mem_data = (ImU8*)mem_data_void; Sizes s; CalcSizes(s, mem_size, base_display_addr); ImGuiStyle& style = ImGui::GetStyle(); const ImVec2 contents_pos_start = ImGui::GetCursorScreenPos(); // We begin into our scrolling region with the 'ImGuiWindowFlags_NoMove' in order to prevent click from moving the window. // 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. const float height_separator = style.ItemSpacing.y + 4.0f; float footer_height = OptFooterExtraHeight; if (OptShowOptions) footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1; if (OptShowDataPreview) footer_height += height_separator + ImGui::GetFrameHeightWithSpacing() * 1 + ImGui::GetTextLineHeightWithSpacing() * 3; ImGui::BeginChild("##scrolling", ImVec2(-FLT_MIN, -footer_height), ImGuiChildFlags_None, ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoNav); ImDrawList* draw_list = ImGui::GetWindowDrawList(); ImGui::PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(0, 0)); ImGui::PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(0, 0)); // We are not really using the clipper API correctly here, because we rely on visible_start_addr/visible_end_addr for our scrolling function. const int line_total_count = (int)((mem_size + Cols - 1) / Cols); ImGuiListClipper clipper; clipper.Begin(line_total_count, s.LineHeight); bool data_next = false; if (DataEditingAddr >= mem_size) DataEditingAddr = (size_t)-1; if (DataPreviewAddr >= mem_size) DataPreviewAddr = (size_t)-1; size_t preview_data_type_size = OptShowDataPreview ? DataTypeGetSize(PreviewDataType) : 0; size_t data_editing_addr_next = (size_t)-1; if (DataEditingAddr != (size_t)-1) { // 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) if (ImGui::IsKeyPressed(ImGuiKey_UpArrow) && (ptrdiff_t)DataEditingAddr >= (ptrdiff_t)Cols) { data_editing_addr_next = DataEditingAddr - Cols; } else if (ImGui::IsKeyPressed(ImGuiKey_DownArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - Cols){ data_editing_addr_next = DataEditingAddr + Cols; } else if (ImGui::IsKeyPressed(ImGuiKey_LeftArrow) && (ptrdiff_t)DataEditingAddr > (ptrdiff_t)0) { data_editing_addr_next = DataEditingAddr - 1; } else if (ImGui::IsKeyPressed(ImGuiKey_RightArrow) && (ptrdiff_t)DataEditingAddr < (ptrdiff_t)mem_size - 1) { data_editing_addr_next = DataEditingAddr + 1; } } // Draw vertical separator ImVec2 window_pos = ImGui::GetWindowPos(); if (OptShowAscii) 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)); const ImU32 color_text = ImGui::GetColorU32(ImGuiCol_Text); const ImU32 color_disabled = OptGreyOutZeroes ? ImGui::GetColorU32(ImGuiCol_TextDisabled) : color_text; const char* format_address = OptUpperCaseHex ? "%0*" _PRISizeT "X " : "%0*" _PRISizeT "x "; const char* format_data = OptUpperCaseHex ? "%0*" _PRISizeT "X" : "%0*" _PRISizeT "x"; const char* format_byte = OptUpperCaseHex ? "%02X" : "%02x"; const char* format_byte_space = OptUpperCaseHex ? "%02X " : "%02x "; MouseHovered = false; MouseHoveredAddr = 0; while (clipper.Step()) for (int line_i = clipper.DisplayStart; line_i < clipper.DisplayEnd; line_i++) // display only visible lines { size_t addr = (size_t)line_i * Cols; ImGui::Text(format_address, s.AddrDigitsCount, base_display_addr + addr); // Draw Hexadecimal for (int n = 0; n < Cols && addr < mem_size; n++, addr++) { float byte_pos_x = s.PosHexStart + s.HexCellWidth * n; if (OptMidColsCount > 0) byte_pos_x += (float)(n / OptMidColsCount) * s.SpacingBetweenMidCols; ImGui::SameLine(byte_pos_x); // Draw highlight or custom background color const bool is_highlight_from_user_range = (addr >= HighlightMin && addr < HighlightMax); const bool is_highlight_from_user_func = (HighlightFn && HighlightFn(mem_data, addr, UserData)); const bool is_highlight_from_preview = (addr >= DataPreviewAddr && addr < DataPreviewAddr + preview_data_type_size); ImU32 bg_color = 0; bool is_next_byte_highlighted = false; if (is_highlight_from_user_range || is_highlight_from_user_func || is_highlight_from_preview) { 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)); bg_color = HighlightColor; } else if (BgColorFn != nullptr) { is_next_byte_highlighted = (addr + 1 < mem_size) && ((BgColorFn(mem_data, addr + 1, UserData) & IM_COL32_A_MASK) != 0); bg_color = BgColorFn(mem_data, addr, UserData); } if (bg_color != 0) { float bg_width = s.GlyphWidth * 2; if (is_next_byte_highlighted || (n + 1 == Cols)) { bg_width = s.HexCellWidth; if (OptMidColsCount > 0 && n > 0 && (n + 1) < Cols && ((n + 1) % OptMidColsCount) == 0) bg_width += s.SpacingBetweenMidCols; } ImVec2 pos = ImGui::GetCursorScreenPos(); draw_list->AddRectFilled(pos, ImVec2(pos.x + bg_width, pos.y + s.LineHeight), bg_color); } if (DataEditingAddr == addr) { // Display text input on current byte bool data_write = false; ImGui::PushID((void*)addr); if (DataEditingTakeFocus) { ImGui::SetKeyboardFocusHere(0); ImSnprintf(AddrInputBuf, 32, format_data, s.AddrDigitsCount, base_display_addr + addr); ImSnprintf(DataInputBuf, 32, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]); } struct InputTextUserData { // 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. static int Callback(ImGuiInputTextCallbackData* data) { InputTextUserData* user_data = (InputTextUserData*)data->UserData; if (!data->HasSelection()) user_data->CursorPos = data->CursorPos; #if IMGUI_VERSION_NUM < 19102 if (data->Flags & ImGuiInputTextFlags_ReadOnly) return 0; #endif if (data->SelectionStart == 0 && data->SelectionEnd == data->BufTextLen) { // When not editing a byte, always refresh its InputText content pulled from underlying memory data // (this is a bit tricky, since InputText technically "owns" the master copy of the buffer we edit it in there) data->DeleteChars(0, data->BufTextLen); data->InsertChars(0, user_data->CurrentBufOverwrite); data->SelectionStart = 0; data->SelectionEnd = 2; data->CursorPos = 0; } return 0; } char CurrentBufOverwrite[3]; // Input int CursorPos; // Output }; InputTextUserData input_text_user_data; input_text_user_data.CursorPos = -1; ImSnprintf(input_text_user_data.CurrentBufOverwrite, 3, format_byte, ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]); ImGuiInputTextFlags flags = ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_AutoSelectAll | ImGuiInputTextFlags_NoHorizontalScroll | ImGuiInputTextFlags_CallbackAlways; if (ReadOnly) flags |= ImGuiInputTextFlags_ReadOnly; flags |= ImGuiInputTextFlags_AlwaysOverwrite; // was ImGuiInputTextFlags_AlwaysInsertMode ImGui::SetNextItemWidth(s.GlyphWidth * 2); if (ImGui::InputText("##data", DataInputBuf, IM_ARRAYSIZE(DataInputBuf), flags, InputTextUserData::Callback, &input_text_user_data)) data_write = data_next = true; else if (!DataEditingTakeFocus && !ImGui::IsItemActive()) DataEditingAddr = data_editing_addr_next = (size_t)-1; DataEditingTakeFocus = false; if (input_text_user_data.CursorPos >= 2) data_write = data_next = true; if (data_editing_addr_next != (size_t)-1) data_write = data_next = false; unsigned int data_input_value = 0; if (!ReadOnly && data_write && sscanf(DataInputBuf, "%X", &data_input_value) == 1) { if (WriteFn) WriteFn(mem_data, addr, (ImU8)data_input_value, UserData); else mem_data[addr] = (ImU8)data_input_value; } if (ImGui::IsItemHovered()) { MouseHovered = true; MouseHoveredAddr = addr; } ImGui::PopID(); } else { // NB: The trailing space is not visible but ensure there's no gap that the mouse cannot click on. ImU8 b = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]; if (OptShowHexII) { if ((b >= 32 && b < 128)) ImGui::Text(".%c ", b); else if (b == 0xFF && OptGreyOutZeroes) ImGui::TextDisabled("## "); else if (b == 0x00) ImGui::Text(" "); else ImGui::Text(format_byte_space, b); } else { if (b == 0 && OptGreyOutZeroes) ImGui::TextDisabled("00 "); else ImGui::Text(format_byte_space, b); } if (ImGui::IsItemHovered()) { MouseHovered = true; MouseHoveredAddr = addr; if (ImGui::IsMouseClicked(0)) { DataEditingTakeFocus = true; data_editing_addr_next = addr; } } } } if (OptShowAscii) { // Draw ASCII values ImGui::SameLine(s.PosAsciiStart); ImVec2 pos = ImGui::GetCursorScreenPos(); addr = (size_t)line_i * Cols; const float mouse_off_x = ImGui::GetIO().MousePos.x - pos.x; 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; ImGui::PushID(line_i); if (ImGui::InvisibleButton("ascii", ImVec2(s.PosAsciiEnd - s.PosAsciiStart, s.LineHeight))) { DataEditingAddr = DataPreviewAddr = mouse_addr; DataEditingTakeFocus = true; } if (ImGui::IsItemHovered()) { MouseHovered = true; MouseHoveredAddr = mouse_addr; } ImGui::PopID(); for (int n = 0; n < Cols && addr < mem_size; n++, addr++) { if (addr == DataEditingAddr) { draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_FrameBg)); draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), ImGui::GetColorU32(ImGuiCol_TextSelectedBg)); } else if (BgColorFn) { draw_list->AddRectFilled(pos, ImVec2(pos.x + s.GlyphWidth, pos.y + s.LineHeight), BgColorFn(mem_data, addr, UserData)); } unsigned char c = ReadFn ? ReadFn(mem_data, addr, UserData) : mem_data[addr]; char display_c = (c < 32 || c >= 128) ? '.' : c; draw_list->AddText(pos, (display_c == c) ? color_text : color_disabled, &display_c, &display_c + 1); pos.x += s.GlyphWidth; } } } ImGui::PopStyleVar(2); const float child_width = ImGui::GetWindowSize().x; ImGui::EndChild(); // Notify the main window of our ideal child content size (FIXME: we are missing an API to get the contents size from the child) ImGui::SetCursorPosX(s.WindowWidth); ImGui::Dummy(ImVec2(0.0f, 0.0f)); if (data_next && DataEditingAddr + 1 < mem_size) { DataEditingAddr = DataPreviewAddr = DataEditingAddr + 1; DataEditingTakeFocus = true; } else if (data_editing_addr_next != (size_t)-1) { DataEditingAddr = DataPreviewAddr = data_editing_addr_next; DataEditingTakeFocus = true; } const bool lock_show_data_preview = OptShowDataPreview; if (FontOptions) ImGui::PushFont(FontOptions); if (OptShowOptions) { ImGui::Separator(); DrawOptionsLine(s, mem_data, mem_size, base_display_addr); } if (lock_show_data_preview) { ImGui::Separator(); DrawPreviewLine(s, mem_data, mem_size, base_display_addr); } if (FontOptions) ImGui::PopFont(); const ImVec2 contents_pos_end(contents_pos_start.x + child_width, ImGui::GetCursorScreenPos().y); //ImGui::GetForegroundDrawList()->AddRect(contents_pos_start, contents_pos_end, IM_COL32(255, 0, 0, 255)); if (OptShowOptions) if (ImGui::IsMouseHoveringRect(contents_pos_start, contents_pos_end)) if (ImGui::IsWindowHovered(ImGuiHoveredFlags_ChildWindows) && ImGui::IsMouseReleased(ImGuiMouseButton_Right)) ImGui::OpenPopup("OptionsPopup"); if (ImGui::BeginPopup("OptionsPopup")) { if (FontOptions) ImGui::PushFont(FontOptions); ImGui::SetNextItemWidth(s.GlyphWidth * 7 + style.FramePadding.x * 2.0f); if (ImGui::DragInt("##cols", &Cols, 0.2f, 4, 32, "%d cols")) { ContentsWidthChanged = true; if (Cols < 1) Cols = 1; } ImGui::PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0f); ImGui::Checkbox("Show Data Preview", &OptShowDataPreview); ImGui::Checkbox("Show HexII", &OptShowHexII); if (ImGui::Checkbox("Show Ascii", &OptShowAscii)) { ContentsWidthChanged = true; } ImGui::Checkbox("Grey out zeroes", &OptGreyOutZeroes); ImGui::Checkbox("Uppercase Hex", &OptUpperCaseHex); ImGui::PopStyleVar(); ImGui::PopFont(); ImGui::EndPopup(); } } void DrawOptionsLine(const Sizes& s, void* mem_data, size_t mem_size, size_t base_display_addr) { IM_UNUSED(mem_data); ImGuiStyle& style = ImGui::GetStyle(); const char* format_range = OptUpperCaseHex ? "%0*" _PRISizeT "X..%0*" _PRISizeT "X" : "%0*" _PRISizeT "x..%0*" _PRISizeT "x"; // Options menu if (ImGui::Button("Options")) ImGui::OpenPopup("OptionsPopup"); ImGui::SameLine(); ImGui::Text("Range "); ImGui::SameLine(0.0, 0.0); if (FontOptions) ImGui::PopFont(); ImGui::Text(format_range, s.AddrDigitsCount, base_display_addr, s.AddrDigitsCount, base_display_addr + mem_size - 1); ImGui::SameLine(); ImGui::SetNextItemWidth((s.AddrDigitsCount + 1) * s.GlyphWidth + style.FramePadding.x * 2.0f); if (ImGui::InputText("##addr", AddrInputBuf, IM_ARRAYSIZE(AddrInputBuf), ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) { size_t goto_addr; if (sscanf(AddrInputBuf, "%" _PRISizeT "X", &goto_addr) == 1) { GotoAddr = goto_addr - base_display_addr; HighlightMin = HighlightMax = (size_t)-1; } } if (FontOptions) ImGui::PushFont(FontOptions); if (GotoAddr != (size_t)-1) { if (GotoAddr < mem_size) { ImGui::BeginChild("##scrolling"); ImGui::SetScrollFromPosY(ImGui::GetCursorStartPos().y + (GotoAddr / Cols) * ImGui::GetTextLineHeight()); ImGui::EndChild(); DataEditingAddr = DataPreviewAddr = GotoAddr; DataEditingTakeFocus = true; } GotoAddr = (size_t)-1; } //if (MouseHovered) //{ // ImGui::SameLine(); // ImGui::Text("Hovered: %p", MouseHoveredAddr); //} } void DrawPreviewLine(const Sizes& s, void* mem_data_void, size_t mem_size, size_t base_display_addr) { IM_UNUSED(base_display_addr); ImU8* mem_data = (ImU8*)mem_data_void; ImGuiStyle& style = ImGui::GetStyle(); ImGui::AlignTextToFramePadding(); ImGui::Text("Preview as:"); ImGui::SameLine(); ImGui::SetNextItemWidth((s.GlyphWidth * 10.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x); 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 }; if (ImGui::BeginCombo("##combo_type", DataTypeGetDesc(PreviewDataType), ImGuiComboFlags_HeightLargest)) { for (int n = 0; n < IM_ARRAYSIZE(supported_data_types); n++) { ImGuiDataType data_type = supported_data_types[n]; if (ImGui::Selectable(DataTypeGetDesc(data_type), PreviewDataType == data_type)) PreviewDataType = data_type; } ImGui::EndCombo(); } ImGui::SameLine(); ImGui::SetNextItemWidth((s.GlyphWidth * 6.0f) + style.FramePadding.x * 2.0f + style.ItemInnerSpacing.x); ImGui::Combo("##combo_endianness", &PreviewEndianness, "LE\0BE\0\0"); char buf[128] = ""; float x = s.GlyphWidth * 6.0f; bool has_value = DataPreviewAddr != (size_t)-1; if (FontOptions) ImGui::PopFont(); if (has_value) DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Dec, buf, (size_t)IM_ARRAYSIZE(buf)); ImGui::Text("Dec"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A"); if (has_value) DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Hex, buf, (size_t)IM_ARRAYSIZE(buf)); ImGui::Text("Hex"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A"); if (has_value) DrawPreviewData(DataPreviewAddr, mem_data, mem_size, PreviewDataType, DataFormat_Bin, buf, (size_t)IM_ARRAYSIZE(buf)); buf[IM_ARRAYSIZE(buf) - 1] = 0; ImGui::Text("Bin"); ImGui::SameLine(x); ImGui::TextUnformatted(has_value ? buf : "N/A"); if (FontOptions) ImGui::PushFont(FontOptions); } // Utilities for Data Preview (since we don't access imgui_internal.h) // FIXME: This technically depends on ImGuiDataType order. const char* DataTypeGetDesc(ImGuiDataType data_type) const { const char* descs[] = { "Int8", "Uint8", "Int16", "Uint16", "Int32", "Uint32", "Int64", "Uint64", "Float", "Double" }; IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(descs)); return descs[data_type]; } size_t DataTypeGetSize(ImGuiDataType data_type) const { const size_t sizes[] = { 1, 1, 2, 2, 4, 4, 8, 8, sizeof(float), sizeof(double) }; IM_ASSERT(data_type >= 0 && data_type < IM_ARRAYSIZE(sizes)); return sizes[data_type]; } const char* DataFormatGetDesc(DataFormat data_format) const { const char* descs[] = { "Bin", "Dec", "Hex" }; IM_ASSERT(data_format >= 0 && data_format < DataFormat_COUNT); return descs[data_format]; } bool IsBigEndian() const { uint16_t x = 1; char c[2]; memcpy(c, &x, 2); return c[0] != 0; } static void* EndiannessCopyBigEndian(void* _dst, void* _src, size_t s, int is_little_endian) { if (is_little_endian) { uint8_t* dst = (uint8_t*)_dst; uint8_t* src = (uint8_t*)_src + s - 1; for (int i = 0, n = (int)s; i < n; ++i) memcpy(dst++, src--, 1); return _dst; } else { return memcpy(_dst, _src, s); } } static void* EndiannessCopyLittleEndian(void* _dst, void* _src, size_t s, int is_little_endian) { if (is_little_endian) { return memcpy(_dst, _src, s); } else { uint8_t* dst = (uint8_t*)_dst; uint8_t* src = (uint8_t*)_src + s - 1; for (int i = 0, n = (int)s; i < n; ++i) memcpy(dst++, src--, 1); return _dst; } } void* EndiannessCopy(void* dst, void* src, size_t size) const { static void* (*fp)(void*, void*, size_t, int) = nullptr; if (fp == nullptr) fp = IsBigEndian() ? EndiannessCopyBigEndian : EndiannessCopyLittleEndian; return fp(dst, src, size, PreviewEndianness); } const char* FormatBinary(const uint8_t* buf, int width) const { IM_ASSERT(width <= 64); size_t out_n = 0; static char out_buf[64 + 8 + 1]; int n = width / 8; for (int j = n - 1; j >= 0; --j) { for (int i = 0; i < 8; ++i) out_buf[out_n++] = (buf[j] & (1 << (7 - i))) ? '1' : '0'; out_buf[out_n++] = ' '; } IM_ASSERT(out_n < IM_ARRAYSIZE(out_buf)); out_buf[out_n] = 0; return out_buf; } // [Internal] 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 { uint8_t buf[8]; size_t elem_size = DataTypeGetSize(data_type); size_t size = addr + elem_size > mem_size ? mem_size - addr : elem_size; if (ReadFn) for (int i = 0, n = (int)size; i < n; ++i) buf[i] = ReadFn(mem_data, addr + i, UserData); else memcpy(buf, mem_data + addr, size); if (data_format == DataFormat_Bin) { uint8_t binbuf[8]; EndiannessCopy(binbuf, buf, size); ImSnprintf(out_buf, out_buf_size, "%s", FormatBinary(binbuf, (int)size * 8)); return; } out_buf[0] = 0; switch (data_type) { case ImGuiDataType_S8: { int8_t data = 0; EndiannessCopy(&data, buf, size); if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhd", data); return; } if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0xFF); return; } break; } case ImGuiDataType_U8: { uint8_t data = 0; EndiannessCopy(&data, buf, size); if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hhu", data); return; } if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%02x", data & 0XFF); return; } break; } case ImGuiDataType_S16: { int16_t data = 0; EndiannessCopy(&data, buf, size); if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hd", data); return; } if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; } break; } case ImGuiDataType_U16: { uint16_t data = 0; EndiannessCopy(&data, buf, size); if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%hu", data); return; } if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%04x", data & 0xFFFF); return; } break; } case ImGuiDataType_S32: { int32_t data = 0; EndiannessCopy(&data, buf, size); if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%d", data); return; } if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; } break; } case ImGuiDataType_U32: { uint32_t data = 0; EndiannessCopy(&data, buf, size); if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%u", data); return; } if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%08x", data); return; } break; } case ImGuiDataType_S64: { int64_t data = 0; EndiannessCopy(&data, buf, size); if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%lld", (long long)data); return; } if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; } break; } case ImGuiDataType_U64: { uint64_t data = 0; EndiannessCopy(&data, buf, size); if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%llu", (long long)data); return; } if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "0x%016llx", (long long)data); return; } break; } case ImGuiDataType_Float: { float data = 0.0f; EndiannessCopy(&data, buf, size); if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; } if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; } break; } case ImGuiDataType_Double: { double data = 0.0; EndiannessCopy(&data, buf, size); if (data_format == DataFormat_Dec) { ImSnprintf(out_buf, out_buf_size, "%f", data); return; } if (data_format == DataFormat_Hex) { ImSnprintf(out_buf, out_buf_size, "%a", data); return; } break; } default: case ImGuiDataType_COUNT: break; } // Switch IM_ASSERT(0); // Shouldn't reach } }; #undef _PRISizeT #undef ImSnprintf #ifdef _MSC_VER #pragma warning (pop) #endif ================================================ FILE: frontend/ui/menubar.cpp ================================================ #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" #include "portable-file-dialogs.h" #include "ps2_elf.h" #include "ps2_iso9660.h" namespace iris { const char* aspect_mode_names[] = { "Native", "Stretch", "Stretch (Keep aspect ratio)", "Force 4:3 (NTSC)", "Force 16:9 (Widescreen)", "Force 5:4 (PAL)", "Auto" }; const char* renderer_names[] = { "Null", "Software", "Hardware (Vulkan)" }; const char* fullscreen_names[] = { "Windowed", "Fullscreen" }; const char* rotation_names[] = { "0 degrees", "90 degrees", "180 degrees", "270 degrees" }; int fullscreen_flags[] = { 0, SDL_WINDOW_FULLSCREEN }; void show_main_menubar(iris::instance* iris) { using namespace ImGui; PushFont(iris->font_icons); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 7.0)); if (BeginMainMenuBar()) { ImVec2 size = GetWindowSize(); if (BeginMenu("Iris")) { if (MenuItem(ICON_MS_DRIVE_FILE_MOVE " Open...")) { audio::mute(iris); auto f = pfd::open_file("Select a file to load", "", { "All File Types (*.iso; *.bin; *.cue; *.chd; *.cso; *.zso; *.elf)", "*.iso *.bin *.cue *.chd *.cso *.zso *.elf", "Disc Images (*.iso; *.bin; *.cue; *.chd; *.cso; *.zso)", "*.iso *.bin *.cue *.chd *.cso *.zso", "CD Images (*.bin; *.cue; *.chd)", "*.bin *.cue *.chd", "DVD Images (*.iso; *.chd; *.cso; *.zso)", "*.iso *.chd *.cso *.zso", "ISO Files (*.iso)", "*.iso", "CUE Files (*.cue)", "*.cue", "BIN Files (*.bin)", "*.bin", "CHD Files (*.chd)", "*.chd", "CSO/ZSO Files (*.cso; *.zso)", "*.cso *.zso", "ELF Executables (*.elf)", "*.elf", "All Files (*.*)", "*" }); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { std::string path = f.result().at(0); if (path.size()) { if (open_file(iris, path)) { push_info(iris, "Failed to open file: " + path); } else { add_recent(iris, path, RECENT_TYPE_PS2); } } } } if (BeginMenu(ICON_MS_HISTORY " Open Recent", iris->recents.size())) { for (const auto& recent : iris->recents) { if (MenuItem(recent.path.c_str())) { if (recent.type == RECENT_TYPE_PS2) { if (open_file(iris, recent.path)) { push_info(iris, "Failed to open file: " + recent.path); } else { add_recent(iris, recent.path, recent.type); } } else { if (!emu::load_arcade(iris, recent.path)) { push_info(iris, "Failed to boot arcade: " + recent.path); } else { add_recent(iris, recent.path, RECENT_TYPE_ARCADE); } } } } Separator(); if (MenuItem(ICON_MS_DELETE_HISTORY " Clear all recents")) { iris->recents.clear(); } // To-do: Use try_open_file // if (MenuItem("Clear invalid recents")) { // iris->recents.clear(); // } // To-do // if (MenuItem("Stop recents history")) { // } ImGui::EndMenu(); } if (MenuItem(ICON_MS_JOYSTICK " Open Arcade...")) { audio::mute(iris); auto f = pfd::select_folder("Select arcade game folder", "", pfd::opt::none); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { std::string path = f.result(); if (path.size()) { if (!emu::load_arcade(iris, path)) { push_info(iris, "Failed to boot arcade: " + path); } else { add_recent(iris, path, RECENT_TYPE_ARCADE); } } } } // if (MenuItem(ICON_MS_DRIVE_FILE_MOVE " Load disc...")) { // const char* patterns[3] = { "*.iso", "*.bin", "*.cue" }; // const char* file = tinyfd_openFileDialog( // "Select CD/DVD image", // "", // 3, // patterns, // "Disc images", // 0 // ); // if (file) { // struct iso9660_state* iso = iso9660_open(file); // if (!iso) { // printf("iris: Couldn't open disc image \"%s\"\n", file); // exit(1); // return; // } // char* boot_file = iso9660_get_boot_path(iso); // if (!boot_file) // return; // // Temporarily disable window updates // struct gs_callback cb = *ps2_gs_get_callback(iris->ps2->gs, GS_EVENT_VBLANK); // ps2_gs_remove_callback(iris->ps2->gs, GS_EVENT_VBLANK); // ps2_boot_file(iris->ps2, boot_file); // // Re-enable window updates // ps2_gs_init_callback(iris->ps2->gs, GS_EVENT_VBLANK, cb.func, cb.udata); // ps2_cdvd_open(iris->ps2->cdvd, file); // iso9660_close(iso); // iris->loaded = file; // } // } // if (MenuItem(ICON_MS_DRAFT " Load executable...")) { // const char* patterns[3] = { "*.elf" }; // const char* file = tinyfd_openFileDialog( // "Select ELF executable", // "", // 1, // patterns, // "ELF executables", // 0 // ); // if (file) { // std::string str(file); // str = "host:" + str; // // Temporarily disable window updates // struct gs_callback cb = *ps2_gs_get_callback(iris->ps2->gs, GS_EVENT_VBLANK); // ps2_gs_remove_callback(iris->ps2->gs, GS_EVENT_VBLANK); // ps2_boot_file(iris->ps2, str.c_str()); // // ps2_elf_load(iris->ps2, file); // // Re-enable window updates // ps2_gs_init_callback(iris->ps2->gs, GS_EVENT_VBLANK, cb.func, cb.udata); // iris->loaded = file; // } // } Separator(); if (MenuItem(iris->pause ? ICON_MS_PLAY_ARROW " Run" : ICON_MS_PAUSE " Pause", "Space")) { iris->pause = !iris->pause; } // To-do: Show confirm dialog maybe? if (MenuItem(ICON_MS_REFRESH " Reset")) { ps2_reset(iris->ps2); } if (MenuItem(ICON_MS_FOLDER " Change disc...")) { audio::mute(iris); auto f = pfd::open_file("Select CD/DVD image", "", { "Disc Images (*.iso; *.bin; *.cue; *.chd; *.cso; *.zso)", "*.iso *.bin *.cue *.chd *.cso *.zso", "CD Images (*.bin; *.cue; *.chd)", "*.bin *.cue *.chd", "DVD Images (*.iso; *.chd; *.cso; *.zso)", "*.iso *.chd *.cso *.zso", "ISO Files (*.iso)", "*.iso", "CUE Files (*.cue)", "*.cue", "BIN Files (*.bin)", "*.bin", "CHD Files (*.chd)", "*.chd", "CSO/ZSO Files (*.cso; *.zso)", "*.cso *.zso", "All Files (*.*)", "*" }); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { // 2-second delay to allow the disc to spin up if (!ps2_cdvd_open(iris->ps2->cdvd, f.result().at(0).c_str(), 38860800*2)) { iris->loaded = f.result().at(0); } } } if (MenuItem(ICON_MS_EJECT " Eject disc")) { iris->loaded = ""; ps2_cdvd_close(iris->ps2->cdvd); } ImGui::EndMenu(); } if (BeginMenu("Settings")) { if (BeginMenu(ICON_MS_MONITOR " Display")) { if (BeginMenu(ICON_MS_BRUSH " Renderer")) { for (int i = 0; i < 3; i++) { BeginDisabled(i == RENDERER_BACKEND_SOFTWARE); if (MenuItem(renderer_names[i], nullptr, i == iris->renderer_backend)) { render::switch_backend(iris, i); } EndDisabled(); } ImGui::EndMenu(); } if (BeginMenu(ICON_MS_CROP " Scale")) { for (int i = 2; i <= 6; i++) { char buf[16]; snprintf(buf, 16, "%.1fx", (float)i * 0.5f); if (MenuItem(buf, nullptr, ((float)i * 0.5f) == iris->scale)) { iris->scale = (float)i * 0.5f; // renderer_set_scale(iris->ctx, iris->scale); } } ImGui::EndMenu(); } if (BeginMenu(ICON_MS_ASPECT_RATIO " Aspect mode")) { for (int i = 0; i < 7; i++) { if (MenuItem(aspect_mode_names[i], nullptr, iris->aspect_mode == i)) { iris->aspect_mode = i; // renderer_set_aspect_mode(iris->ctx, iris->aspect_mode); } } ImGui::EndMenu(); } if (BeginMenu(ICON_MS_FILTER " Scaling filter")) { const char* filter_names[] = { "Nearest", "Bilinear", "Cubic" }; for (int i = 0; i < 3; i++) { BeginDisabled(i == 2 && !iris->cubic_supported); if (MenuItem(filter_names[i], nullptr, iris->filter == i)) { iris->filter = i; } EndDisabled(); } ImGui::EndMenu(); } if (BeginMenu(ICON_MS_SCREEN_ROTATION " Rotation")) { const int normalized_angle = ((iris->angle % 360) + 360) % 360; const int rotation_index = normalized_angle / 90; for (int i = 0; i < 4; i++) { if (MenuItem(rotation_names[i], nullptr, rotation_index == i)) { iris->angle = i * 90; } } ImGui::EndMenu(); } if (BeginMenu(ICON_MS_ASPECT_RATIO " Window size")) { const char* sizes[] = { "640x480", "800x600", "960x720", "1024x768", "1280x720", "1280x960" }; int widths[] = { 640, 800, 960, 1024, 1280, 1280 }; int heights[] = { 480, 600, 720, 768, 720, 960 }; for (int i = 0; i < 6; i++) { bool selected = iris->window_width == widths[i] && iris->window_height == heights[i]; if (MenuItem(sizes[i], nullptr, selected)) { iris->window_width = widths[i]; iris->window_height = heights[i]; SDL_SetWindowSize(iris->window, iris->window_width, iris->window_height + get_menubar_height(iris)); } } ImGui::EndMenu(); } if (MenuItem(ICON_MS_SPEED_2X " Integer scaling", nullptr, &iris->integer_scaling)) { // renderer_set_integer_scaling(iris->ctx, iris->integer_scaling); } MenuItem(ICON_MS_FLIP " Flip horizontally", nullptr, &iris->flip_x); MenuItem(ICON_MS_FLIP " Flip vertically", nullptr, &iris->flip_y); if (MenuItem(ICON_MS_FULLSCREEN " Fullscreen", "F11", &iris->fullscreen)) { SDL_SetWindowFullscreen(iris->window, iris->fullscreen); } if (MenuItem(ICON_MS_SYNC " VSync", nullptr, &iris->vsync)) { imgui::set_vsync(iris, iris->vsync); iris->swapchain_rebuild = true; } if (MenuItem(ICON_MS_IMAGE " Enable shaders", nullptr, &iris->enable_shaders)) { // renderer_set_shaders_enabled(iris->ctx, iris->enable_shaders); } ImGui::EndMenu(); } if (BeginMenu(ICON_MS_MUSIC_NOTE " Audio")) { PushStyleVarY(ImGuiStyleVar_FramePadding, 0.0f); AlignTextToFramePadding(); const char* icon = ICON_MS_VOLUME_UP; if (iris->volume == 0.0f) { icon = ICON_MS_VOLUME_MUTE; } else if (iris->volume <= 0.5f) { icon = ICON_MS_VOLUME_DOWN; } Text(icon); SameLine(); SetNextItemWidth(100.0f); SliderFloat("Volume", &iris->volume, 0.0f, 1.0f, "%.1f"); PopStyleVar(); MenuItem(ICON_MS_VOLUME_OFF " Mute", nullptr, &iris->mute); MenuItem(ICON_MS_MUSIC_OFF " Mute ADMA", nullptr, &iris->mute_adma); ImGui::EndMenu(); } if (MenuItem(ICON_MS_DOCK_TO_BOTTOM " Show status bar", nullptr, &iris->show_status_bar)) { SDL_SetWindowSize(iris->window, iris->window_width, iris->window_height + get_menubar_height(iris)); } if (MenuItem(ICON_MS_OPEN_IN_NEW " Open data folder")) { SDL_OpenURL(iris->pref_path.c_str()); } Separator(); if (MenuItem(ICON_MS_MANUFACTURING " Settings...")) { iris->show_settings = true; } ImGui::EndMenu(); } if (BeginMenu("Tools")) { if (MenuItem(ICON_MS_BUILD " ImGui Demo", NULL, &iris->show_imgui_demo)); if (MenuItem(ICON_MS_SEARCH " Memory search", NULL, &iris->show_memory_search)); if (MenuItem(ICON_MS_PHOTO_CAMERA " Take screenshot...", "F9")) { audio::mute(iris); std::string filename = input::get_default_screenshot_filename(iris); auto f = pfd::save_file("Save screenshot", filename, { "PNG (*.png)", "*.png", "JPG (*.jpg)", "*.jpg", "BMP (*.bmp)", "*.bmp", "TGA (*.tga)", "*.tga", "All Files (*.*)", "*" }); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { input::save_screenshot(iris, f.result()); } } if (MenuItem(ICON_MS_SD_CARD " Memory Card tool")) { iris->show_memory_card_tool = true; } ImGui::EndMenu(); } if (BeginMenu("Debug")) { SeparatorText("EE"); // if (BeginMenu(ICON_MS_BUG_REPORT " EE")) { if (MenuItem(ICON_MS_SETTINGS " Control##ee", NULL, &iris->show_ee_control)); if (MenuItem(ICON_MS_EDIT_NOTE " State##ee", NULL, &iris->show_ee_state)); if (MenuItem(ICON_MS_TERMINAL " Logs##ee", NULL, &iris->show_ee_logs)); if (MenuItem(ICON_MS_BOLT " Interrupts##ee", NULL, &iris->show_ee_interrupts)); BeginDisabled(iris->symbols.empty()); if (MenuItem(ICON_MS_CODE " Symbols##ee", NULL, &iris->show_symbols)); EndDisabled(); if (MenuItem(ICON_MS_ACCOUNT_TREE " Threads##ee", NULL, &iris->show_threads)); // ImGui::EndMenu(); // } SeparatorText("IOP"); // if (BeginMenu(ICON_MS_BUG_REPORT " IOP")) { if (MenuItem(ICON_MS_SETTINGS " Control##iop", NULL, &iris->show_iop_control)); if (MenuItem(ICON_MS_EDIT_NOTE " State##iop", NULL, &iris->show_iop_state)); if (MenuItem(ICON_MS_TERMINAL " Logs##iop", NULL, &iris->show_iop_logs)); if (MenuItem(ICON_MS_BOLT " Interrupts##iop", NULL, &iris->show_iop_interrupts)); if (MenuItem(ICON_MS_EXTENSION " Modules##iop", NULL, &iris->show_iop_modules)); // ImGui::EndMenu(); // } Separator(); if (MenuItem(ICON_MS_BUG_REPORT " Breakpoints", NULL, &iris->show_breakpoints)); if (MenuItem(ICON_MS_BRUSH " GS debugger", NULL, &iris->show_gs_debugger)); if (MenuItem(ICON_MS_MUSIC_NOTE " SPU2 debugger", NULL, &iris->show_spu2_debugger)); if (MenuItem(ICON_MS_MEMORY " Memory viewer", NULL, &iris->show_memory_viewer)); if (MenuItem(ICON_MS_VIEW_IN_AR " VU disassembler", NULL, &iris->show_vu_disassembler)); if (MenuItem(ICON_MS_GAMEPAD " DualShock debugger", NULL, &iris->show_pad_debugger)); if (MenuItem(ICON_MS_BUG_REPORT " Performance overlay", NULL, &iris->show_overlay)); if (MenuItem(ICON_MS_TERMINAL " SYSMEM logs", NULL, &iris->show_sysmem_logs)); Separator(); if (BeginMenu(ICON_MS_MORE_TIME " Timescale")) { for (int i = 0; i < 9; i++) { char buf[16]; snprintf(buf, 16, "%dx", 1 << i); if (MenuItem(buf, nullptr, iris->timescale == (1 << i))) { iris->timescale = (1 << i); ps2_set_timescale(iris->ps2, iris->timescale); } } ImGui::EndMenu(); } if (MenuItem(ICON_MS_SKIP_NEXT " Skip FMVs", NULL, &iris->skip_fmv)) { printf("Skip FMVs: %d\n", iris->skip_fmv); ee_set_fmv_skip(iris->ps2->ee, iris->skip_fmv); } if (MenuItem(ICON_MS_CLOSE " Close all")) { iris->show_ee_control = false; iris->show_ee_state = false; iris->show_ee_logs = false; iris->show_ee_interrupts = false; iris->show_ee_dmac = false; iris->show_iop_control = false; iris->show_iop_state = false; iris->show_iop_logs = false; iris->show_iop_interrupts = false; iris->show_iop_modules = false; iris->show_iop_dma = false; iris->show_gs_debugger = false; iris->show_spu2_debugger = false; iris->show_memory_viewer = false; iris->show_memory_search = false; iris->show_vu_disassembler = false; iris->show_status_bar = false; iris->show_breakpoints = false; iris->show_threads = false; iris->show_sysmem_logs = false; iris->show_imgui_demo = false; iris->show_overlay = false; } ImGui::EndMenu(); } if (BeginMenu("Help")) { if (MenuItem(ICON_MS_INFO " About")) { iris->show_about_window = true; } if (MenuItem(ICON_MS_EXCLAMATION " Report an issue")) { SDL_OpenURL("https://github.com/allkern/iris/issues/new"); } ImGui::EndMenu(); } EndMainMenuBar(); } PopStyleVar(); PopFont(); } } ================================================ FILE: frontend/ui/modules.cpp ================================================ #include "iris.hpp" #include "iop/hle/loadcore.h" #include "res/IconsMaterialSymbols.h" namespace iris { static const char* sizing_combo_items[] = { ICON_MS_FIT_WIDTH " Fixed fit", ICON_MS_FULLSCREEN " Fixed same", ICON_MS_FULLSCREEN " Stretch prop", ICON_MS_FULLSCREEN " Stretch same" }; static ImGuiTableFlags table_sizing_flags[] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame }; static int table_sizing_combo = 0; static int table_sizing = ImGuiTableFlags_SizingStretchProp; static inline void show_modules_table(iris::instance* iris) { using namespace ImGui; struct iop_state* iop = iris->ps2->iop; if (BeginTable("##iopmodules", 4, ImGuiTableFlags_RowBg | table_sizing)) { PushFont(iris->font_small_code); TableSetupColumn("Name"); TableSetupColumn("Text Start"); TableSetupColumn("Data Start"); TableSetupColumn("BSS Start"); TableHeadersRow(); PopFont(); for (int i = 0; i < iop->module_count; i++) { struct iop_module *mod = &iop->module_list[i]; TableNextRow(); TableSetColumnIndex(0); Text("%s", mod->name); // text section TableSetColumnIndex(1); PushFont(iris->font_code); uint32_t addr = mod->text_addr; Text("0x%08x", addr); // data section TableSetColumnIndex(2); addr += mod->text_size; Text("0x%08x", addr); // bss section TableSetColumnIndex(3); addr += mod->data_size; Text("0x%08x", addr); PopFont(); } EndTable(); } } static inline struct iop_module* find_iop_module(iris::instance* iris, uint32_t addr) { struct iop_state* iop = iris->ps2->iop; for (int i = 0; i < iop->module_count; i++) { struct iop_module* mod = &iop->module_list[i]; if ((addr >= mod->text_addr) && (addr < (mod->text_addr + mod->text_size))) return mod; } return NULL; } void show_iop_modules(iris::instance* iris) { using namespace ImGui; struct iop_state* iop = iris->ps2->iop; if (imgui::BeginEx("IOP Modules", &iris->show_iop_modules, ImGuiWindowFlags_MenuBar)) { if (BeginMenuBar()) { if (BeginMenu("Settings")) { if (BeginMenu(ICON_MS_CROP " Sizing")) { for (int i = 0; i < 4; i++) { if (Selectable(sizing_combo_items[i], i == table_sizing_combo)) { table_sizing = table_sizing_flags[i]; table_sizing_combo = i; } } ImGui::EndMenu(); } ImGui::EndMenu(); } EndMenuBar(); } if (Button(ICON_MS_REFRESH)) { refresh_module_list(iris->ps2->iop); } Separator(); if (BeginChild("##tablechild", ImVec2(0, GetContentRegionAvail().y * 0.9))) { show_modules_table(iris); EndChild(); } Separator(); const struct iop_module* mod = find_iop_module(iris, iop->pc); if (!mod) { Text("Current module: \n"); } else { Text("Current module: %s (%08x-%08x)\n", mod->name, mod->text_addr, mod->text_addr + mod->text_size); } } End(); } } ================================================ FILE: frontend/ui/overlay.cpp ================================================ #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" #include "implot.h" #define MAX_SAMPLES 100 namespace iris { std::vector fps_history = { 0 }; std::vector fps_history_avg = { 0 }; ImVec2 pos = ImVec2(10, 10); const ImVec2 padding = ImVec2(5, 5); const ImVec2 size = ImVec2(250, 100); const float opacity = 0.75f; float max = 0.0; void update_overlay(iris::instance* iris) { // if (fps_history.size() == MAX_SAMPLES) { // if (fps_history.front() >= max) { // max = 0.0; // for (int i = 1; i < MAX_SAMPLES; i++) { // if (fps_history[i] > max) { // max = fps_history[i]; // } // } // } // fps_history.pop_front(); // } float sample = 1.0 / ImGui::GetIO().DeltaTime; if (!iris->pause) { if (fps_history.size() == MAX_SAMPLES) fps_history.erase(fps_history.begin()); fps_history.push_back(sample); if (fps_history_avg.size() == MAX_SAMPLES) fps_history_avg.erase(fps_history_avg.begin()); fps_history_avg.push_back(std::roundf(ImGui::GetIO().Framerate)); } } void show_overlay(iris::instance* iris) { using namespace ImGui; using namespace ImPlot; SetNextWindowBgAlpha(0.5f); ImVec2 pos = ImVec2(10.0f, 10.0f + iris->menubar_height); if (GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable) { pos.x = GetMainViewport()->Pos.x + GetMainViewport()->Size.x + 10.0f; pos.y = GetMainViewport()->Pos.y; } SetNextWindowPos(pos, ImGuiCond_Always); SetNextWindowViewport(0); if (Begin("Overlay", nullptr, ImGuiWindowFlags_NoDecoration | ImGuiWindowFlags_NoMove | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_NoFocusOnAppearing | ImGuiWindowFlags_NoNav | ImGuiWindowFlags_NoDocking)) { update_overlay(iris); ImPlotFlags flags = ImPlotFlags_NoTitle | ImPlotFlags_NoLegend | ImPlotFlags_NoMouseText | ImPlotFlags_NoBoxSelect | ImPlotFlags_NoFrame | ImPlotFlags_NoMenus | ImPlotFlags_CanvasOnly | ImPlotFlags_NoInputs; ImPlotAxisFlags axis_flags = ImPlotAxisFlags_NoTickLabels | ImPlotAxisFlags_NoGridLines; if (BeginPlot("##overlay_plot", ImVec2(0, 0), flags)) { SetupAxes(nullptr, nullptr, axis_flags, axis_flags); SetupAxesLimits(0, (double)MAX_SAMPLES - 1, 0, 60, ImGuiCond_Always); PlotLine("FPS", fps_history.data(), (int)fps_history.size()); EndPlot(); } renderer_stats* stats; // = renderer_get_debug_stats(iris->ctx); PushFont(iris->font_black); Text("%d fps", (int)std::roundf(1.0 / ImGui::GetIO().DeltaTime)); PopFont(); // Text("Primitives: %d", stats->primitives); // Text("Texture uploads: %d", stats->texture_uploads); // Text("Texture blits: %d", stats->texture_blits); } End(); } } ================================================ FILE: frontend/ui/pad.cpp ================================================ #include #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" namespace iris { void show_pad_debugger(iris::instance* iris) { using namespace ImGui; if (imgui::BeginEx("DualShock 2", &iris->show_pad_debugger)) { if (BeginTabBar("##padtabbar")) { if (BeginTabItem("Slot 1")) { struct ds_state* ds = (struct ds_state*)iris->ps2->sio2->port[0].udata; if (!ds) { Text("No controller connected"); } else { Text("Buttons: %04x", ds->buttons); Text("AxisRH: %04x", ds->ax_right_x); Text("AxisLH: %04x", ds->ax_left_x); Text("AxisRV: %04x", ds->ax_right_y); Text("AxisLV: %04x", ds->ax_left_y); Text("Config Mode: %d", ds->config_mode); Text("Active Index: %d", ds->act_index); Text("Mode Index: %d", ds->mode_index); Text("Mode: %d", ds->mode); Text("Vibration 0: %d", ds->vibration[0]); Text("Vibration 1: %d", ds->vibration[1]); Text("Mask 0: %d", ds->mask[0]); Text("Mask 1: %d", ds->mask[1]); Text("Lock: %d", ds->lock); } EndTabItem(); } if (BeginTabItem("Slot 2")) { struct ds_state* ds = (struct ds_state*)iris->ps2->sio2->port[1].udata; if (!ds) { Text("No controller connected"); } else { Text("Buttons: %04x", ds->buttons); Text("AxisRH: %04x", ds->ax_right_x); Text("AxisLH: %04x", ds->ax_left_x); Text("AxisRV: %04x", ds->ax_right_y); Text("AxisLV: %04x", ds->ax_left_y); Text("Config Mode: %d", ds->config_mode); Text("Active Index: %d", ds->act_index); Text("Mode Index: %d", ds->mode_index); Text("Mode: %d", ds->mode); Text("Vibration 0: %d", ds->vibration[0]); Text("Vibration 1: %d", ds->vibration[1]); Text("Mask 0: %d", ds->mask[0]); Text("Mask 1: %d", ds->mask[1]); Text("Lock: %d", ds->lock); } EndTabItem(); } EndTabBar(); } } End(); } } ================================================ FILE: frontend/ui/settings.cpp ================================================ #include #include #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" #include "portable-file-dialogs.h" namespace iris { bool hovered = false; std::string tooltip = ""; int selected_settings = 0; int saved = 0; mapping* get_input_mapping(iris::instance* iris, int slot) { if (iris->input_map[slot] == -1) return nullptr; return &iris->input_maps[iris->input_map[slot]]; } const char* get_input_name(input_action action) { switch (action) { case IRIS_DS_BT_SELECT: return "Select"; case IRIS_DS_BT_L3: return "L3"; case IRIS_DS_BT_R3: return "R3"; case IRIS_DS_BT_START: return "Start"; case IRIS_DS_BT_UP: return "D-pad Up"; case IRIS_DS_BT_RIGHT: return "D-pad Right"; case IRIS_DS_BT_DOWN: return "D-pad Down"; case IRIS_DS_BT_LEFT: return "D-pad Left"; case IRIS_DS_BT_L2: return "L2"; case IRIS_DS_BT_R2: return "R2"; case IRIS_DS_BT_L1: return "L1"; case IRIS_DS_BT_R1: return "R1"; case IRIS_DS_BT_TRIANGLE: return "Triangle"; case IRIS_DS_BT_CIRCLE: return "Circle"; case IRIS_DS_BT_CROSS: return "Cross"; case IRIS_DS_BT_SQUARE: return "Square"; case IRIS_DS_BT_ANALOG: return "Analog"; case IRIS_DS_AX_RIGHTV_POS: return "Right Stick Vertical+"; case IRIS_DS_AX_RIGHTV_NEG: return "Right Stick Vertical-"; case IRIS_DS_AX_RIGHTH_POS: return "Right Stick Horizontal+"; case IRIS_DS_AX_RIGHTH_NEG: return "Right Stick Horizontal-"; case IRIS_DS_AX_LEFTV_POS: return "Left Stick Vertical+"; case IRIS_DS_AX_LEFTV_NEG: return "Left Stick Vertical-"; case IRIS_DS_AX_LEFTH_POS: return "Left Stick Horizontal+"; case IRIS_DS_AX_LEFTH_NEG: return "Left Stick Horizontal-"; case IRIS_S14X_SW_DOWN: return "System 147/148 Down"; case IRIS_S14X_SW_UP: return "System 147/148 Up"; case IRIS_S14X_SW_ENTER: return "System 147/148 Enter"; case IRIS_S14X_SW_TEST: return "System 147/148 Test"; case IRIS_S14X_SW_SERVICE: return "System 147/148 Service"; case IRIS_S14X_SW_P1_START: return "System 147/148 P1 Start"; case IRIS_S14X_SW_P2_START: return "System 147/148 P2 Start"; case IRIS_S14X_SW_P3_START: return "System 147/148 P3 Start"; case IRIS_S14X_SW_P4_START: return "System 147/148 P4 Start"; } return ""; } std::string get_event_name(const input_event& event) { std::string name; switch (event.type) { case IRIS_EVENT_KEYBOARD: { SDL_Keycode keycode = static_cast(event.id); name = SDL_GetKeyName(keycode & 0xf0000fff); // Append modifier names if ((keycode >> 12) & SDL_KMOD_LSHIFT) name = "Left Shift + " + name; if ((keycode >> 12) & SDL_KMOD_RSHIFT) name = "Right Shift + " + name; if ((keycode >> 12) & SDL_KMOD_LCTRL) name = "Left Ctrl + " + name; if ((keycode >> 12) & SDL_KMOD_RCTRL) name = "Right Ctrl + " + name; if ((keycode >> 12) & SDL_KMOD_LALT) name = "Left Alt + " + name; if ((keycode >> 12) & SDL_KMOD_RALT) name = "Right Alt + " + name; } break; case IRIS_EVENT_GAMEPAD_BUTTON: { SDL_GamepadButton button = static_cast(event.id); name = SDL_GetGamepadStringForButton(button); } break; case IRIS_EVENT_GAMEPAD_AXIS_POS: { SDL_GamepadAxis axis = static_cast(event.id); name = SDL_GetGamepadStringForAxis(axis) + std::string("+"); } break; case IRIS_EVENT_GAMEPAD_AXIS_NEG: { SDL_GamepadAxis axis = static_cast(event.id); name = SDL_GetGamepadStringForAxis(axis) + std::string("-"); } break; default: { name = "unknown"; } break; } // Capitalize first letter if (!name.empty()) { name[0] = std::toupper(name[0]); } return name; } const char* settings_renderer_names[] = { "Null", "Software", "Software (Threaded)" }; const char* settings_aspect_mode_names[] = { "Native", "Stretch", "Stretch (Keep aspect ratio)", "Force 4:3 (NTSC)", "Force 16:9 (Widescreen)", "Force 5:4 (PAL)", "Auto" }; const char* settings_fullscreen_names[] = { "Windowed", "Fullscreen (Desktop)", }; const char* settings_rotation_names[] = { "0 degrees", "90 degrees", "180 degrees", "270 degrees" }; int settings_fullscreen_flags[] = { 0, SDL_WINDOW_FULLSCREEN }; const char* settings_buttons[] = { " " ICON_MS_DEPLOYED_CODE " System", " " ICON_MS_FOLDER " Paths", " " ICON_MS_MONITOR " Graphics", " " ICON_MS_BRUSH " Shaders", " " ICON_MS_STADIA_CONTROLLER " Input", " " ICON_MS_SD_CARD " Memory cards", " " ICON_MS_MORE_HORIZ " Misc.", nullptr }; const char* system_names[] = { "Auto", "Retail (Fat)", "Retail (Slim)", "PSX DESR", "TEST unit (DTL-H)", "TOOL unit (DTL-T)", "Konami Python", "Konami Python 2", "Namco System 147", "Namco System 148", "Namco System 246", "Namco System 256" }; const char* mechacon_model_names[] = { "SPC970", "Dragon" }; void show_system_settings(iris::instance* iris) { using namespace ImGui; Text("Model"); if (BeginCombo("##combo", system_names[iris->system])) { for (int i = 0; i < IM_ARRAYSIZE(system_names); i++) { if (Selectable(system_names[i], i == iris->system)) { iris->system = i; ps2_set_system(iris->ps2, i); } } EndCombo(); } if (BeginTable("##specs-table", 2, ImGuiTableFlags_SizingFixedSame)) { TableNextRow(); if (iris->system == 0) { TableSetColumnIndex(0); TextDisabled("Detected system"); TableSetColumnIndex(1); Text("%s", system_names[iris->ps2->detected_system]); TableNextRow(); } TableSetColumnIndex(0); TextDisabled("Main RAM"); TableSetColumnIndex(1); Text("%d MB", iris->ps2->ee_ram->size / (1024 * 1024)); TableNextRow(); TableSetColumnIndex(0); TextDisabled("IOP RAM"); TableSetColumnIndex(1); Text("%d MB", iris->ps2->iop_ram->size / (1024 * 1024)); TableNextRow(); TableSetColumnIndex(0); TextDisabled("MechaCon Model"); TableSetColumnIndex(1); Text("%s", mechacon_model_names[iris->ps2->cdvd->mechacon_model]); EndTable(); } Text("\nTimescale"); char buf[16]; sprintf(buf, "%dx", iris->timescale); if (BeginCombo("##timescale", buf)) { for (int i = 0; i < 9; i++) { char buf[16]; snprintf(buf, 16, "%dx", 1 << i); if (Selectable(buf, iris->timescale == (1 << i))) { iris->timescale = (1 << i); ps2_set_timescale(iris->ps2, iris->timescale); } } EndCombo(); } if (BeginTable("##effective-clock", 2, ImGuiTableFlags_SizingFixedSame)) { TableNextRow(); TableSetColumnIndex(0); TextDisabled("Effective frequency"); TableSetColumnIndex(1); Text("%.3f MHz", 294.912f / iris->timescale); EndTable(); } SeparatorText("Network"); // To-do: Improve MAC address input by using a single text input // that fills in the colons automatically Text("MAC Address"); PushFont(iris->font_code); float w = CalcTextSize("FFFFFFFFFFFF").x; SetNextItemWidth(w * 2.0); if (InputScalarN("##macaddress", ImGuiDataType_U8, iris->mac_address, 6, nullptr, nullptr, "%02X", ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_CharsUppercase)) { ps2_set_mac_address(iris->ps2, iris->mac_address); } SameLine(); PopFont(); if (Button(ICON_MS_REFRESH "##macaddress")) { ps2_set_mac_address(iris->ps2, iris->mac_address); } SeparatorText("Misc."); PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F); Checkbox("Start games automatically", &iris->autostart); Checkbox("Skip FMVs", &iris->skip_fmv); PopStyleVar(); } const char* ssaa_names[] = { "Disabled", "2x", "4x", "8x", "16x" }; void show_hardware_renderer_settings(iris::instance* iris) { using namespace ImGui; Text("SSAA"); if (BeginCombo("##ssaa", ssaa_names[iris->hardware_backend_config.super_sampling])) { for (int i = 0; i < IM_ARRAYSIZE(ssaa_names); i++) { if (Selectable(ssaa_names[i], iris->hardware_backend_config.super_sampling == i)) { iris->hardware_backend_config.super_sampling = i; if (i != 0) { iris->hardware_backend_config.force_progressive = true; } render::refresh(iris); } } EndCombo(); } PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F); // BeginDisabled(iris->hardware_backend_config.super_sampling != 0); if (Checkbox(" Force progressive scan", &iris->hardware_backend_config.force_progressive)) { render::refresh(iris); } // EndDisabled(); if (Checkbox(" Overscan", &iris->hardware_backend_config.overscan)) { render::refresh(iris); } PopStyleVar(); SeparatorText("Advanced"); PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F); if (Checkbox(" CRTC Offsets", &iris->hardware_backend_config.crtc_offsets)) { render::refresh(iris); } if (Checkbox(" Disable Mipmaps", &iris->hardware_backend_config.disable_mipmaps)) { render::refresh(iris); } if (Checkbox(" Unsynced Readbacks", &iris->hardware_backend_config.unsynced_readbacks)) { render::refresh(iris); } if (Checkbox(" Backbuffer Promotion", &iris->hardware_backend_config.backbuffer_promotion)) { render::refresh(iris); } if (Checkbox(" Allow Blend Demote", &iris->hardware_backend_config.allow_blend_demote)) { render::refresh(iris); } PopStyleVar(); } void show_graphics_settings(iris::instance* iris) { using namespace ImGui; static const char* settings_renderer_names[] = { "Null", "Software", "Hardware" }; Text("Renderer"); if (BeginCombo("##renderer", settings_renderer_names[iris->renderer_backend], ImGuiComboFlags_HeightSmall)) { for (int i = 0; i < 3; i++) { BeginDisabled(i == RENDERER_BACKEND_SOFTWARE); if (Selectable(settings_renderer_names[i], i == iris->renderer_backend)) { render::switch_backend(iris, i); } EndDisabled(); } EndCombo(); } Text("Aspect mode"); if (BeginCombo("##aspectmode", settings_aspect_mode_names[iris->aspect_mode])) { for (int i = 0; i < 7; i++) { if (Selectable(settings_aspect_mode_names[i], iris->aspect_mode == i)) { iris->aspect_mode = i; } } EndCombo(); } BeginDisabled( iris->aspect_mode == RENDER_ASPECT_AUTO || iris->aspect_mode == RENDER_ASPECT_STRETCH || iris->aspect_mode == RENDER_ASPECT_STRETCH_KEEP ); Text("Scale"); char buf[16]; snprintf(buf, 16, "%.1fx", (float)iris->scale); if (BeginCombo("##scale", buf, ImGuiComboFlags_HeightSmall)) { for (int i = 2; i <= 6; i++) { snprintf(buf, 16, "%.1fx", (float)i * 0.5f); if (Selectable(buf, ((float)i * 0.5f) == iris->scale)) { iris->scale = (float)i * 0.5f; } } EndCombo(); } EndDisabled(); Text("Scaling"); const char* filter_names[] = { "Nearest", "Bilinear", "Cubic" }; if (BeginCombo("##scalingfilter", filter_names[iris->filter])) { for (int i = 0; i < 3; i++) { BeginDisabled(i == 2 && !iris->cubic_supported); if (Selectable(filter_names[i], iris->filter == i)) { iris->filter = i; } EndDisabled(); } EndCombo(); } const int normalized_angle = ((iris->angle % 360) + 360) % 360; const int rotation_index = normalized_angle / 90; Text("Rotation"); if (BeginCombo("##rotation", settings_rotation_names[rotation_index])) { for (int i = 0; i < 4; i++) { if (Selectable(settings_rotation_names[i], rotation_index == i)) { iris->angle = i * 90; } } EndCombo(); } Text("Window mode"); if (BeginCombo("##windowmode", settings_fullscreen_names[iris->fullscreen])) { for (int i = 0; i < 2; i++) { if (Selectable(settings_fullscreen_names[i], iris->fullscreen == i)) { iris->fullscreen = i; SDL_SetWindowFullscreen(iris->window, settings_fullscreen_flags[i]); } } EndCombo(); } SeparatorText("Misc."); PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F); if (Checkbox(" VSync", &iris->vsync)) { imgui::set_vsync(iris, iris->vsync); iris->swapchain_rebuild = true; } Checkbox(" Integer scaling", &iris->integer_scaling); Checkbox(" Flip horizontally", &iris->flip_x); Checkbox(" Flip vertically", &iris->flip_y); PopStyleVar(); if (iris->renderer_backend == RENDERER_BACKEND_HARDWARE) { SeparatorText("Renderer settings"); show_hardware_renderer_settings(iris); } SeparatorText("Vulkan settings"); Text("GPU"); static bool changed = false; const char* hint; const auto& selected_device = iris->vulkan_gpus[iris->vulkan_selected_device_index]; if (iris->vulkan_physical_device < 0) { hint = "Auto"; } else { hint = iris->vulkan_gpus[iris->vulkan_physical_device].name.c_str(); } if (changed) { SameLine(); 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"); } PushStyleVarY(ImGuiStyleVar_ItemSpacing, 5.0F); if (BeginCombo("##gpu", hint)) { if (Selectable("Auto", iris->vulkan_physical_device < 0)) { iris->vulkan_physical_device = -1; } for (int i = 0; i < iris->vulkan_gpus.size(); i++) { const auto& device = iris->vulkan_gpus[i]; std::string name = device.name; if (device.device == selected_device.device) { name += " (Current)"; } if (Selectable(name.c_str(), device.device == selected_device.device)) { changed = iris->vulkan_physical_device != i; iris->vulkan_physical_device = i; } } EndCombo(); } PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F); if (Checkbox(" Enable validation layers", &iris->vulkan_enable_validation_layers)) { changed = true; } PopStyleVar(2); } void show_controller_slot(iris::instance* iris, int slot) { using namespace ImGui; char label[9] = "Slot #"; label[5] = '1' + slot; ImVec4 col = GetStyleColorVec4(iris->ds[slot] ? ImGuiCol_Text : ImGuiCol_TextDisabled); col.w = 1.0; if (BeginChild(label, ImVec2(GetContentRegionAvail().x / 2.0 - 10.0, 50 * iris->ui_scale))) { Text("Controller"); std::string controller_name = "None"; if (iris->ds[slot]) { controller_name = "DualShock 2"; } float avail_width = GetContentRegionAvail().x; SetNextItemWidth(avail_width); if (BeginCombo("##controller", controller_name.c_str())) { if (Selectable("None")) { if (iris->ds[slot]) { ps2_sio2_detach_device(iris->ps2->sio2, slot); iris->ds[slot] = nullptr; } } if (Selectable("DualShock 2")) { if (!iris->ds[slot]) { iris->ds[slot] = ds_attach(iris->ps2->sio2, slot); } } EndCombo(); } } EndChild(); SameLine(0.0, 10.0); if (BeginChild((std::string(label) + "##icon").c_str(), ImVec2(0, 50 * iris->ui_scale))) { BeginDisabled(!iris->ds[slot]); float avail_width = GetContentRegionAvail().x; Text("Input device"); std::string name = "None"; if (!iris->input_devices[slot]) { name = "None"; } else if (iris->input_devices[slot]->get_type() == 0) { name = "Keyboard"; } else if (iris->input_devices[slot]->get_type() == 1) { gamepad_device* gp = static_cast(iris->input_devices[slot]); name = SDL_GetGamepadNameForID(gp->get_id()); } SetNextItemWidth(avail_width); if (BeginCombo("##devicetype", name.c_str())) { if (Selectable("None")) { if (iris->input_devices[slot]) { delete iris->input_devices[slot]; iris->input_devices[slot] = nullptr; } } if (Selectable("Keyboard")) { if (iris->input_devices[slot]) { delete iris->input_devices[slot]; iris->input_devices[slot] = nullptr; } iris->input_devices[slot] = new keyboard_device(); iris->input_devices[slot]->set_slot(slot); if (iris->input_map[slot] <= 1) { iris->input_map[slot] = 0; } } for (auto gamepad : iris->gamepads) { if (Selectable(SDL_GetGamepadNameForID(gamepad.first))) { if (iris->input_devices[slot]) { delete iris->input_devices[slot]; iris->input_devices[slot] = nullptr; } iris->input_devices[slot] = new gamepad_device(gamepad.first); iris->input_devices[slot]->set_slot(slot); if (iris->input_map[slot] <= 1) { iris->input_map[slot] = 1; } } } EndCombo(); } EndDisabled(); } EndChild(); InvisibleButton("##slot0", ImVec2(10, 10)); texture* tex = &iris->dualshock2_icon; float width = 250.0f; float height = (tex->height * width) / tex->width; SetCursorPosX((GetContentRegionAvail().x / 2.0) - (width / 2.0)); Image( (ImTextureID)(intptr_t)tex->descriptor_set, ImVec2(width, height), ImVec2(0, 0), ImVec2(1, 1), col, ImVec4(0.0, 0.0, 0.0, 0.0) ); InvisibleButton("##pad1", ImVec2(10, 10)); Text("Mapping"); SetNextItemWidth(GetContentRegionAvail().x / 2.0 - 10.0); mapping* mapping = get_input_mapping(iris, slot); if (BeginCombo("##mapping", mapping ? mapping->name.c_str() : "None")) { if (Selectable("None", mapping == nullptr)) { iris->input_map[slot] = -1; } int i = 0; for (auto& map : iris->input_maps) { if (Selectable(map.name.c_str(), mapping == &map)) { iris->input_map[slot] = i; } i++; } EndCombo(); } } bool event_is_mod_key(const input_event& event) { if (event.type != IRIS_EVENT_KEYBOARD) { return false; } SDL_Keycode keycode = static_cast(event.id); return (keycode & 0xf0000fff) == SDLK_LSHIFT || (keycode & 0xf0000fff) == SDLK_RSHIFT || (keycode & 0xf0000fff) == SDLK_LCTRL || (keycode & 0xf0000fff) == SDLK_RCTRL || (keycode & 0xf0000fff) == SDLK_LALT || (keycode & 0xf0000fff) == SDLK_RALT; } int selected_mapping = 0; bool waiting_for_input = false; uint64_t mapping_editing = 0; void show_mappings_editor(iris::instance* iris) { using namespace ImGui; static char buf[1024] = { 0 }; const char* hint = iris->gcdb_path.size() ? iris->gcdb_path.c_str() : "Not configured (using default)"; Text("Game controller DB"); SetNextItemWidth(300); if (InputTextWithHint("##gcdbinput", hint, buf, 1024, ImGuiInputTextFlags_EscapeClearsAll | ImGuiInputTextFlags_EnterReturnsTrue)) { iris->gcdb_path = std::string(buf); // To-do: Check return value input::load_db_from_file(iris, iris->gcdb_path.c_str()); } SameLine(); if (Button(ICON_MS_FOLDER "##gcdbbtn")) { audio::mute(iris); auto f = pfd::open_file("Select Game controller DB file", "", { "Game controller DB (*.txt)", "*.txt", "All Files (*.*)", "*" }); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { strncpy(buf, f.result().at(0).c_str(), 1024); iris->gcdb_path = std::string(buf); // To-do: Check return value input::load_db_from_file(iris, iris->gcdb_path.c_str()); } } SameLine(); if (Button(ICON_MS_CLEAR "##gcdbclear")) { iris->gcdb_path = ""; memset(buf, 0, 1024); input::load_db_default(iris); } Text("Mapping"); if (BeginCombo("##mapping", iris->input_maps[selected_mapping].name.c_str())) { int i = 0; for (auto& map : iris->input_maps) { if (Selectable(map.name.c_str(), selected_mapping == i)) { selected_mapping = i; } i++; } EndCombo(); } SameLine(); if (selected_mapping <= 1) { if (Button(ICON_MS_REFRESH " Default")) { input::init_default_mapping(iris, selected_mapping); } } SetNextItemWidth(GetContentRegionAvail().x); if (BeginTable("##mappingeditor", 2, ImGuiTableFlags_SizingStretchProp)) { TableSetupColumn("Input"); TableSetupColumn("Mapping"); std::vector> elems( iris->input_maps[selected_mapping].map.forward_map().begin(), iris->input_maps[selected_mapping].map.forward_map().end()); std::sort(elems.begin(), elems.end(), [](const std::pair& a, const std::pair& b) { return a.second < b.second; }); for (auto& entry : elems) { TableNextRow(); std::string key_name = get_input_name(static_cast(entry.second)); TableSetColumnIndex(0); AlignTextToFramePadding(); Text("%s", key_name.c_str()); TableSetColumnIndex(1); input_event event; event.u64 = entry.first; std::string value_name = get_event_name(event) + "##" + key_name; if (waiting_for_input && (mapping_editing == entry.first)) { PushStyleColor(ImGuiCol_Text, GetStyleColorVec4(ImGuiCol_TextDisabled)); if (Button("Press a key or button...", ImVec2(GetContentRegionAvail().x, 0))) { waiting_for_input = false; } PopStyleColor(); if (iris->last_input_event_read == false && iris->last_input_event_value > 0.5f && !event_is_mod_key(iris->last_input_event)) { iris->last_input_event_read = true; waiting_for_input = false; mapping_editing = 0; auto event = iris->last_input_event; auto action = entry.second; // printf("Mapping input event %s (%llu) to action %s (%llu)\n", // get_event_name(iris->last_input_event).c_str(), // iris->last_input_event.u64, // get_input_name(action), // static_cast(entry.second) // ); auto* value_ptr = iris->input_maps[selected_mapping].map.get_value(event.u64); if (value_ptr != nullptr) { // Remove previous mapping for this input event auto value = *value_ptr; auto key = *iris->input_maps[selected_mapping].map.get_key(action); // printf("Removing previous mapping of event %s (%llu) to action %s (%llu)\n", // get_event_name(event).c_str(), // event.u64, // get_input_name(value), // static_cast(value) // ); iris->input_maps[selected_mapping].map.erase_by_key(event.u64); iris->input_maps[selected_mapping].map.erase_by_value(action); iris->input_maps[selected_mapping].map.insert(event.u64, action); iris->input_maps[selected_mapping].map.insert(key, value); } else { iris->input_maps[selected_mapping].map.erase_by_value(action); iris->input_maps[selected_mapping].map.insert(event.u64, action); } } } else { if (Button(value_name.c_str(), ImVec2(GetContentRegionAvail().x, 0))) { iris->last_input_event_read = true; waiting_for_input = true; mapping_editing = entry.first; } } // if (IsMouseDoubleClicked(ImGuiMouseButton_Left) && IsItemHovered()) { // iris->last_input_event_read = true; // waiting_for_input = true; // mapping_editing = entry.first; // } } EndTable(); } } void show_input_settings(iris::instance* iris) { using namespace ImGui; if (BeginTabBar("##inputtabs")) { if (BeginTabItem("Slot 1")) { show_controller_slot(iris, 0); EndTabItem(); } if (BeginTabItem("Slot 2")) { show_controller_slot(iris, 1); EndTabItem(); } if (BeginTabItem("Mappings")) { show_mappings_editor(iris); EndTabItem(); } EndTabBar(); } } void show_paths_settings(iris::instance* iris) { using namespace ImGui; static char buf[512]; static char dvd_buf[512]; static char rom2_buf[512]; static char nvram_buf[512]; static char flash_buf[512]; Text("BIOS (rom0)"); if (IsItemHovered()) { hovered = true; tooltip = ICON_MS_INFO " Select a BIOS file, this is required for the emulator to function properly"; } const char* bios_hint = iris->bios_path.size() ? iris->bios_path.c_str() : "e.g. scph10000.bin"; const char* rom1_hint = iris->rom1_path.size() ? iris->rom1_path.c_str() : "Not configured"; const char* rom2_hint = iris->rom2_path.size() ? iris->rom2_path.c_str() : "Not configured"; const char* nvram_hint = iris->nvram_path.size() ? iris->nvram_path.c_str() : "Not configured"; const char* flash_hint = iris->flash_path.size() ? iris->flash_path.c_str() : "Not configured"; SetNextItemWidth(300); InputTextWithHint("##rom0", bios_hint, buf, 512, ImGuiInputTextFlags_EscapeClearsAll); SameLine(); if (Button(ICON_MS_FOLDER "##rom0")) { audio::mute(iris); auto f = pfd::open_file("Select BIOS file", "", { "BIOS dumps (*.bin; *.rom0)", "*.bin *.rom0", "All Files (*.*)", "*" }); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { strncpy(buf, f.result().at(0).c_str(), 512); ps2_load_bios(iris->ps2, buf); } } if (BeginTable("##rom-info", 2, ImGuiTableFlags_SizingFixedFit)) { TableNextRow(); TableSetColumnIndex(0); TextDisabled("Model" " "); TableSetColumnIndex(1); Text("%s", iris->ps2->rom0_info.model); TableNextRow(); TableSetColumnIndex(0); TextDisabled("Version" " "); TableSetColumnIndex(1); Text("%s", iris->ps2->rom0_info.version); TableNextRow(); TableSetColumnIndex(0); TextDisabled("Region" " "); TableSetColumnIndex(1); Text("%s", iris->ps2->rom0_info.region); TableNextRow(); TableSetColumnIndex(0); TextDisabled("MD5 hash" " "); TableSetColumnIndex(1); Text("%s", iris->ps2->rom0_info.md5hash); SameLine(); if (SmallButton(ICON_MS_CONTENT_COPY)) { SDL_SetClipboardText(iris->ps2->rom0_info.md5hash); } EndTable(); } Separator(); Text("DVD Player (rom1)"); SetNextItemWidth(300); InputTextWithHint("##rom1", rom1_hint, dvd_buf, 512, ImGuiInputTextFlags_EscapeClearsAll); SameLine(); if (Button(ICON_MS_FOLDER "##rom1")) { audio::mute(iris); auto f = pfd::open_file("Select DVD BIOS file", "", { "DVD BIOS dumps (*.bin; *.rom1)", "*.bin *.rom1", "All Files (*.*)", "*" }); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { strncpy(dvd_buf, f.result().at(0).c_str(), 512); ps2_load_rom1(iris->ps2, dvd_buf); } } SameLine(); if (Button(ICON_MS_CLEAR "##rom1")) { iris->rom1_path = ""; memset(dvd_buf, 0, 512); } if (iris->rom1_path.size()) { if (BeginTable("##rom1-info", 2, ImGuiTableFlags_SizingFixedFit)) { TableNextRow(); TableSetColumnIndex(0); TextDisabled("Version" " "); TableSetColumnIndex(1); Text("%s", iris->ps2->rom1_info.version); TableNextRow(); TableSetColumnIndex(0); TextDisabled("MD5 hash" " "); TableSetColumnIndex(1); Text("%s", iris->ps2->rom1_info.md5hash); SameLine(); if (SmallButton(ICON_MS_CONTENT_COPY)) { SDL_SetClipboardText(iris->ps2->rom1_info.md5hash); } EndTable(); } Separator(); } Text("Chinese extensions (rom2)"); SetNextItemWidth(300); InputTextWithHint("##rom2", rom2_hint, rom2_buf, 512, ImGuiInputTextFlags_EscapeClearsAll); SameLine(); if (Button(ICON_MS_FOLDER "##rom2")) { audio::mute(iris); auto f = pfd::open_file("Select ROM2 file", "", { "ROM2 dumps (*.bin; *.rom2)", "*.bin *.rom2", "All Files (*.*)", "*" }); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { strncpy(rom2_buf, f.result().at(0).c_str(), 512); } } SameLine(); if (Button(ICON_MS_CLEAR "##rom2")) { iris->rom2_path = ""; memset(rom2_buf, 0, 512); } Text("EEPROM memory (nvram)"); SetNextItemWidth(300); InputTextWithHint("##nvram", nvram_hint, nvram_buf, 512, ImGuiInputTextFlags_EscapeClearsAll); SameLine(); if (Button(ICON_MS_FOLDER "##nvram")) { audio::mute(iris); auto f = pfd::open_file("Select NVRAM file", "", { "NVRAM dumps (*.nvm; *.bin)", "*.nvm *.bin", "All Files (*.*)", "*" }); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { strncpy(nvram_buf, f.result().at(0).c_str(), 512); } } SameLine(); if (Button(ICON_MS_CLEAR "##nvram")) { iris->nvram_path = ""; memset(nvram_buf, 0, 512); } Text("Flash memory (xfrom)"); SetNextItemWidth(300); InputTextWithHint("##flash", flash_hint, flash_buf, 512, ImGuiInputTextFlags_EscapeClearsAll); SameLine(); if (Button(ICON_MS_FOLDER "##flash")) { audio::mute(iris); auto f = pfd::open_file("Select Flash/XFROM dump file", "", { "XFROM dumps (*.bin)", "*.bin", "All Files (*.*)", "*" }); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { strncpy(flash_buf, f.result().at(0).c_str(), 512); } } SameLine(); if (Button(ICON_MS_CLEAR "##xfrom")) { iris->flash_path = ""; memset(flash_buf, 0, 512); } if (Button(ICON_MS_SAVE " Save")) { std::string bios_path = buf; std::string rom1_path = dvd_buf; std::string rom2_path = rom2_buf; std::string flash_path = flash_buf; std::string nvram_path = nvram_buf; if (bios_path.size()) iris->bios_path = bios_path; if (rom1_path.size()) iris->rom1_path = rom1_path; if (rom2_path.size()) iris->rom2_path = rom2_path; if (flash_path.size()) iris->flash_path = flash_path; if (nvram_path.size()) iris->nvram_path = nvram_path; saved = 1; } SameLine(); if (saved) { 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"); } } static char slot0_buf[1024]; static char slot1_buf[1024]; void show_memory_card(iris::instance* iris, int slot) { using namespace ImGui; char label[9] = "##mcard0"; label[7] = '0' + slot; if (BeginChild(label, ImVec2(GetContentRegionAvail().x / (slot ? 1.0 : 2.0) - 10.0, 0))) { std::string& path = slot ? iris->mcd1_path : iris->mcd0_path; ImVec4 col = GetStyleColorVec4(iris->mcd_slot_type[slot] ? ImGuiCol_Text : ImGuiCol_TextDisabled); col.w = 1.0; InvisibleButton("##pad0", ImVec2(10, 10)); texture* tex = &iris->ps2_memory_card_icon; if (iris->mcd_slot_type[slot] == 2) { tex = &iris->ps1_memory_card_icon; } else if (iris->mcd_slot_type[slot] == 3) { tex = &iris->pocketstation_icon; } SetCursorPosX((GetContentRegionAvail().x / 2.0) - (tex->width / 2.0)); Image( (ImTextureID)(intptr_t)tex->descriptor_set, ImVec2(tex->width, tex->height), ImVec2(0, 0), ImVec2(1, 1), col, ImVec4(0.0, 0.0, 0.0, 0.0) ); InvisibleButton("##pad1", ImVec2(10, 10)); if (path.size() && !iris->mcd_slot_type[slot]) { TextColored(ImVec4(211.0/255.0, 167.0/255.0, 30.0/255.0, 1.0), ICON_MS_WARNING " Check file"); if (IsItemHovered(ImGuiHoveredFlags_DelayNormal)) { if (BeginTooltip()) { Text("Please check files "); EndTooltip(); } } } PushFont(iris->font_heading); Text("Slot %d", slot+1); PopFont(); char* buf = slot ? slot1_buf : slot0_buf; const char* hint = path.size() ? path.c_str() : "Not configured"; char it_label[7] = "##mcd0"; char bt_label[10] = ICON_MS_FOLDER "##mcd0"; char ed_label[10]; snprintf(ed_label, 10, "%s##mcd0", iris->mcd_slot_type[slot] ? ICON_MS_ARROW_DOWNWARD : ICON_MS_ARROW_UPWARD); it_label[5] = '0' + slot; bt_label[8] = '0' + slot; ed_label[8] = '0' + slot; InputTextWithHint(it_label, hint, buf, 512, ImGuiInputTextFlags_EscapeClearsAll); SameLine(); if (Button(bt_label)) { audio::mute(iris); auto f = pfd::open_file("Select Memory Card file for Slot 1", iris->pref_path, { "Memory Card files (*.ps2; *.mcd; *.bin; *.psm; *.pocket)", "*.ps2 *.mcd *.bin *.psm *.pocket", "All Files (*.*)", "*" }); while (!f.ready()); audio::unmute(iris); if (f.result().size()) { strncpy(buf, f.result().at(0).c_str(), 512); path = f.result().at(0); emu::attach_memory_card(iris, slot, path.c_str()); } } SameLine(); BeginDisabled((!iris->mcd_slot_type[slot]) && (!path.size())); if (Button(ed_label)) { if (iris->mcd_slot_type[slot]) { emu::detach_memory_card(iris, slot); } else { emu::attach_memory_card(iris, slot, path.c_str()); } } EndDisabled(); } EndChild(); } void show_memory_card_settings(iris::instance* iris) { using namespace ImGui; if (Button(ICON_MS_EDIT " Create memory cards...")) { // Launch memory card utility iris->show_memory_card_tool = true; } Separator(); show_memory_card(iris, 0); SameLine(0.0, 10.0); show_memory_card(iris, 1); } static const char* const theme_names[] = { "Granite", "ImGui Dark", "ImGui Light", "ImGui Classic", "Cherry", "Source" }; static const char* const codeview_color_scheme_names[] = { "Solarized Dark", "Solarized Light", "One Dark Pro", "Catppuccin Latte", "Catppuccin Frappé", "Catppuccin Macchiato", "Catppuccin Mocha" }; #ifdef _WIN32 static const char* titlebar_style_names[] = { "Default", "Seamless" }; #endif void show_misc_settings(iris::instance* iris) { using namespace ImGui; SeparatorText("Style"); Text("Theme"); if (BeginCombo("##theme", theme_names[iris->theme])) { for (int i = 0; i < IM_ARRAYSIZE(theme_names); i++) { if (Selectable(theme_names[i], iris->theme == i)) { iris->theme = i; imgui::set_theme(iris, i); platform::apply_settings(iris); } } EndCombo(); } Text("Background color"); ColorEdit3("##bgcolor", (float*)&iris->clear_value.color); Text("UI scale"); DragFloat("##uiscale", &iris->ui_scale, 0.05f, 0.5f, 1.5f, "%.1f"); GetStyle().FontScaleMain = iris->ui_scale; PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F); Checkbox("Enable viewports", &iris->imgui_enable_viewports); SameLine(); PopStyleVar(); TextDisabled(ICON_MS_WARNING " Experimental feature, requires restart"); #ifdef _WIN32 Text("Titlebar style (Windows only)"); if (BeginCombo("##titlebar_style", titlebar_style_names[iris->windows_titlebar_style])) { for (int i = 0; i < 2; i++) { if (Selectable(titlebar_style_names[i], iris->windows_titlebar_style == i)) { iris->windows_titlebar_style = i; platform::apply_settings(iris); } } EndCombo(); } PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F); BeginDisabled(iris->windows_titlebar_style != IRIS_TITLEBAR_DEFAULT); if (Checkbox(" Immersive dark mode", &iris->windows_dark_mode)) { platform::apply_settings(iris); } EndDisabled(); if (Checkbox(" Show window borders", &iris->windows_enable_borders)) { platform::apply_settings(iris); } PopStyleVar(); #endif SeparatorText("Codeview"); #define SCHEME(str, id) \ if (Selectable(str, iris->codeview_color_scheme == id)) { \ iris->codeview_color_scheme = id; \ imgui::set_codeview_scheme(iris, id); \ } Text("Color scheme"); if (BeginCombo("##codeview_color_scheme", codeview_color_scheme_names[iris->codeview_color_scheme])) { PushFont(iris->font_small); TextDisabled("Dark"); PopFont(); SCHEME("Solarized Dark", IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_DARK); SCHEME("One Dark Pro", IRIS_CODEVIEW_COLOR_SCHEME_ONE_DARK_PRO); SCHEME("Catppuccin Mocha", IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MOCHA); SCHEME("Catppuccin Macchiato", IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_MACCHIATO); SCHEME("Catppuccin Frappé", IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_FRAPPE); PushFont(iris->font_small); TextDisabled("Light"); PopFont(); SCHEME("Solarized Light", IRIS_CODEVIEW_COLOR_SCHEME_SOLARIZED_LIGHT); SCHEME("Catppuccin Latte", IRIS_CODEVIEW_COLOR_SCHEME_CATPPUCCIN_LATTE); EndCombo(); } #undef SCHEME static bool use_theme_background = !iris->codeview_use_theme_background; PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F); if (Checkbox("Use scheme background", &use_theme_background)) { iris->codeview_use_theme_background = !use_theme_background; } PopStyleVar(); Text("Font scale"); DragFloat("##codeview_font_scale", &iris->codeview_font_scale, 0.05f, 0.75f, 1.5f, "%.1f"); SeparatorText("Screenshots"); const char* format_names[] = { "PNG", "BMP", "JPG", "TGA" }; const char* jpg_quality_names[] = { "Minimum", // 1 "Low", // 25 "Medium", // 50 "High", // 90 "Maximum", // 100 "Custom..." }; const char* mode_names[] = { "Internal", "Display" }; Text("Format"); if (BeginCombo("##screenshotformat", format_names[iris->screenshot_format])) { for (int i = 0; i < 4; i++) { if (Selectable(format_names[i], iris->screenshot_format == i)) { iris->screenshot_format = i; } } EndCombo(); } Text("Resolution mode"); if (BeginCombo("##screenshotmode", mode_names[iris->screenshot_mode])) { for (int i = 0; i < 2; i++) { if (Selectable(mode_names[i], iris->screenshot_mode == i)) { iris->screenshot_mode = i; } } EndCombo(); } if (iris->screenshot_format == IRIS_SCREENSHOT_FORMAT_JPG) { Text("JPG Quality"); if (BeginCombo("##jpgquality", jpg_quality_names[iris->screenshot_jpg_quality_mode])) { for (int i = 0; i < 6; i++) { if (Selectable(jpg_quality_names[i], iris->screenshot_jpg_quality_mode == i)) { iris->screenshot_jpg_quality_mode = i; } } EndCombo(); } if (iris->screenshot_jpg_quality_mode == IRIS_SCREENSHOT_JPG_QUALITY_CUSTOM) { SliderInt("Quality##jpgqualitycustom", &iris->screenshot_jpg_quality, 1, 100, "%d", ImGuiSliderFlags_AlwaysClamp); } } PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0F); Checkbox(" Include shader processing", &iris->screenshot_shader_processing); PopStyleVar(); } const char* builtin_shader_names[] = { "iris-ntsc-encoder", "iris-ntsc-decoder", "iris-ntsc-curvature", "iris-ntsc-scanlines", "iris-ntsc-noise", }; const char* presets[] = { "NTSC codec", 0 }; void show_shader_settings(iris::instance* iris) { using namespace ImGui; static const char* selected_shader = ""; PushStyleVarY(ImGuiStyleVar_FramePadding, 2.0f); Checkbox(" Enable shaders", &iris->enable_shaders); PopStyleVar(); Separator(); Text("Add shader"); if (BeginCombo("##combo", selected_shader)) { for (int i = 0; i < IM_ARRAYSIZE(builtin_shader_names); i++) { if (Selectable(builtin_shader_names[i], selected_shader == builtin_shader_names[i])) { selected_shader = builtin_shader_names[i]; } } EndCombo(); } SameLine(); if (Button(ICON_MS_ADD)) { if (selected_shader && selected_shader[0]) { std::string shader(selected_shader); shaders::push(iris, selected_shader); } } SameLine(); if (Button(ICON_MS_REMOVE_SELECTION)) { shaders::clear(iris); } // Text("Preset"); // if (BeginCombo("##presets", selected_shader)) { // for (int i = 0; i < 3; i++) { // if (Selectable(presets[i], selected_shader == builtin_shader_names[i])) { // selected_shader = builtin_shader_names[i]; // } // } // EndCombo(); // } if (BeginTable("##shaders", 1, ImGuiTableFlags_SizingFixedSame | ImGuiTableFlags_RowBg)) { for (int i = 0; i < shaders::count(iris); i++) { TableNextRow(); char bypass[16]; char del[16]; char id[1024]; sprintf(bypass, "%s##%d", shaders::at(iris, i)->bypass ? ICON_MS_CHECK_BOX_OUTLINE_BLANK : ICON_MS_CHECK_BOX, i); sprintf(del, ICON_MS_DELETE "##%d", i); sprintf(id, "%s##%d", shaders::at(iris, i)->get_id().c_str(), i); TableSetColumnIndex(0); if (SmallButton(del)) { iris->shader_passes.erase(iris->shader_passes.begin() + i); break; } SameLine(); if (SmallButton(bypass)) { shaders::at(iris, i)->bypass = !shaders::at(iris, i)->bypass; } SameLine(); Selectable(id, false, ImGuiSelectableFlags_SpanAllColumns); if (BeginDragDropSource()) { SetDragDropPayload("SHADER_DND_PAYLOAD", &i, sizeof(int)); EndDragDropSource(); } if (BeginDragDropTarget()) { if (const ImGuiPayload* payload = AcceptDragDropPayload("SHADER_DND_PAYLOAD")) { int src = *(int*)payload->Data; shaders::swap(iris, src, i); } EndDragDropTarget(); } } EndTable(); } } void show_settings(iris::instance* iris) { using namespace ImGui; hovered = false; static ImGuiWindowFlags flags = ImGuiWindowFlags_NoCollapse | ImGuiWindowFlags_NoDocking; SetNextWindowSize(ImVec2(675, 500), ImGuiCond_FirstUseEver); PushStyleVar(ImGuiStyleVar_WindowMinSize, ImVec2(675, 500)); if (GetIO().ConfigFlags & ImGuiConfigFlags_ViewportsEnable && !GetIO().ConfigViewportsNoDecoration) flags |= ImGuiWindowFlags_NoTitleBar; if (Begin("Settings", &iris->show_settings, flags)) { PushStyleVarX(ImGuiStyleVar_ButtonTextAlign, 0.0); PushStyleVarY(ImGuiStyleVar_ItemSpacing, 6.0); if (BeginChild("##sidebar", ImVec2(175, GetContentRegionAvail().y), ImGuiChildFlags_AutoResizeY | ImGuiChildFlags_Borders)) { for (int i = 0; settings_buttons[i]; i++) { if (selected_settings == i) PushStyleColor(ImGuiCol_Button, GetStyle().Colors[ImGuiCol_ButtonHovered]); bool pressed = Button(settings_buttons[i], ImVec2(175, 35)); if (selected_settings == i) PopStyleColor(); if (pressed) { selected_settings = i; } } } EndChild(); SameLine(0.0, 10.0); PopStyleVar(2); if (BeginChild("##content", ImVec2(0, GetContentRegionAvail().y), ImGuiChildFlags_AutoResizeY)) { switch (selected_settings) { case 0: show_system_settings(iris); break; case 1: show_paths_settings(iris); break; case 2: show_graphics_settings(iris); break; case 3: show_shader_settings(iris); break; case 4: show_input_settings(iris); break; case 5: show_memory_card_settings(iris); break; case 6: show_misc_settings(iris); break; } } EndChild(); // Separator(); // if (hovered) { // TextWrapped(tooltip.c_str()); // } else { // Text("Hover over an item to get more information"); // } } End(); PopStyleVar(); } } ================================================ FILE: frontend/ui/spu2.cpp ================================================ #include #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" namespace iris { int core0_selected = -1; int core1_selected = -1; float selected_adsr[120]; int adsr_index; const char* get_adsr_stage_name(int s) { switch (s) { case 0: return "Attack"; case 1: return "Decay"; case 2: return "Sustain"; case 3: return "Release"; case 4: return "End"; } return "None"; } void show_spu2_core(iris::instance* iris, int c) { using namespace ImGui; const struct spu2_core* core = &iris->ps2->spu2->c[c]; bool* mute = c ? iris->core1_mute : iris->core0_mute; int* solo = c ? &iris->core1_solo : &iris->core0_solo; int* selected = c ? &core1_selected : &core0_selected; Text("IRQ address: %08x", core->irqa); char id[] = "##spu2coreX"; id[10] = '0' + c; if (BeginTable(id, 7, ImGuiTableFlags_RowBg)) { PushFont(iris->font_small_code); TableSetupColumn("Voice"); TableSetupColumn("ENVX"); TableSetupColumn("NAX"); TableSetupColumn("SSA"); TableSetupColumn("LSAX"); TableSetupColumn("Stage"); TableSetupColumn("Cycles"); TableHeadersRow(); PopFont(); for (int i = 0; i < 24; i++) { TableNextRow(); TableSetColumnIndex(0); char voice[9]; sprintf(voice, "Voice %d", i); if (Selectable(voice, i == *selected)) { if (i == *selected) { *selected = -1; } else { *selected = i; } } TableSetColumnIndex(1); PushFont(iris->font_code); Text("%04x", core->v[i].envx); TableSetColumnIndex(2); Text("%08x", core->v[i].nax); TableSetColumnIndex(3); Text("%08x", core->v[i].ssa); TableSetColumnIndex(4); Text("%08x", core->v[i].lsax); TableSetColumnIndex(5); PopFont(); Text("%s", get_adsr_stage_name(core->v[i].adsr_phase)); TableSetColumnIndex(6); Text("%d", core->v[i].adsr_cycles); } EndTable(); } } void show_spu2_tab(iris::instance* iris, int c) { using namespace ImGui; const struct spu2_core* core = &iris->ps2->spu2->c[c]; show_spu2_core(iris, c); Separator(); int* selected = c ? &core1_selected : &core0_selected; if (*selected == -1) { Text("No voice selected"); return; } if (adsr_index == 119) { for (int i = 0; i < 119; i++) selected_adsr[i] = selected_adsr[i+1]; selected_adsr[119] = core->v[*selected].envx / 32767.0f; } else { selected_adsr[adsr_index++] = core->v[*selected].envx / 32767.0f; } PlotLines("ADSR", selected_adsr, IM_ARRAYSIZE(selected_adsr), 0, NULL, 0.0f, 1.0f, { 250.0, 80.0 }); } void show_spu2_debugger(iris::instance* iris) { using namespace ImGui; const struct ps2_spu2* spu2 = iris->ps2->spu2; if (imgui::BeginEx("SPU2", &iris->show_spu2_debugger)) { if (BeginTabBar("##spu2tabbar")) { if (BeginTabItem("CORE0")) { show_spu2_tab(iris, 0); EndTabItem(); } if (BeginTabItem("CORE1")) { show_spu2_tab(iris, 1); EndTabItem(); } EndTabBar(); } } End(); } } ================================================ FILE: frontend/ui/state.cpp ================================================ #include #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" #include "ee/ee_dis.h" #include "ee/ee_def.hpp" #include "ee/vu_def.hpp" #include "iop/iop_dis.h" #define IM_RGB(r, g, b) ImVec4(((float)r / 255.0f), ((float)g / 255.0f), ((float)b / 255.0f), 1.0) namespace iris { static const char* ee_cop0_r[] = { "Index", "Random", "EntryLo0", "EntryLo1", "Context", "PageMask", "Wired", "Unused7", "BadVAddr", "Count", "EntryHi", "Compare", "Status", "Cause", "EPC", "PRId", "Config", "Unused17", "Unused18", "Unused19", "Unused20", "Unused21", "Unused22", "BadPAddr", "Debug", "Perf", "Unused26", "Unused27", "TagLo", "TagHi", "ErrorEPC", "Unused31" }; static const char* mips_cc_r[] = { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" }; static const char* vu0i_regs[] = { "vi00", "vi01", "vi02", "vi03", "vi04", "vi05", "vi06", "vi07", "vi08", "vi09", "vi10", "vi11", "vi12", "vi13", "vi14", "vi15", "Status", "MAC", "Clip", "Reserved", "R", "I", "Q", "Reserved", "Reserved", "Reserved", "TPC", "CMSAR0", "FBRST", "VPUSTAT", "Reserved", "CMSAR1" }; uint128_t ee_prev[32]; uint128_t ee_frames[32]; uint32_t ee_cop0_prev[32]; uint32_t ee_cop0_frames[32]; uint32_t vu0i_prev[32]; uint32_t vu0i_frames[32]; uint32_t ee_fpu_prev[32]; uint32_t ee_fpu_frames[32]; struct vu_reg128 vu0f_prev[32]; uint128_t vu0f_frames[32]; uint32_t iop_prev[32]; uint32_t iop_frames[32]; bool vu0f_float; static ImGuiTableFlags ee_table_sizing = ImGuiTableFlags_SizingStretchSame; static ImGuiTableFlags iop_table_sizing = ImGuiTableFlags_SizingStretchProp; static inline void show_ee_main_registers(iris::instance* iris) { using namespace ImGui; struct ee_state* ee = iris->ps2->ee; for (int i = 0; i < 32; i++) { for (int j = 0; j < 4; j++) { if (ee_prev[i].u32[j] != ee->r[i].u32[j]) ee_frames[i].u32[j] = 60; } ee_prev[i] = ee->r[i]; } PushFont(iris->font_code); if (BeginTable("ee#registers", 5, ImGuiTableFlags_RowBg | ee_table_sizing)) { PushFont(iris->font_small_code); TableSetupColumn("Reg"); TableSetupColumn("96-127"); TableSetupColumn("64-95"); TableSetupColumn("32-63"); TableSetupColumn("0-31"); TableHeadersRow(); PopFont(); for (int i = 0; i < 32; i++) { TableNextRow(); TableSetColumnIndex(0); Text("%s", mips_cc_r[i]); for (int j = 0; j < 4; j++) { float a = (float)ee_frames[i].u32[j] / 60.0f; TableSetColumnIndex(1+(3-j)); char label[5]; sprintf(label, "##%02x", (i << 2) | j); if (Selectable(label, false, ImGuiSelectableFlags_AllowOverlap)) { OpenPopup("Change register value"); } SameLine(0.0, 0.0); if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) { static char new_value[9]; PushFont(iris->font_small_code); TextDisabled("Edit "); SameLine(0.0, 0.0); Text("%s", mips_cc_r[i]); SameLine(0.0, 0.0); TextDisabled(" (bits %d-%d)", j*32, ((j+1)*32)-1); PopFont(); PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0)); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0)); PushFont(iris->font_body); PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35)); AlignTextToFramePadding(); Text(ICON_MS_EDIT); SameLine(); SetNextItemWidth(100); PushFont(iris->font_code); if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) { if (new_value[0]) ee->r[i].u32[j] = strtoul(new_value, NULL, 16); CloseCurrentPopup(); } PopFont(); if (Button("Change")) { if (new_value[0]) ee->r[i].u32[j] = strtoul(new_value, NULL, 16); CloseCurrentPopup(); } SameLine(); if (Button("Cancel")) CloseCurrentPopup(); PopStyleColor(); PopStyleVar(2); PopFont(); EndPopup(); } TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%08x", ee->r[i].u32[j]); } } } EndTable(); PopFont(); } static inline void show_ee_cop0_registers(iris::instance* iris) { using namespace ImGui; struct ee_state* ee = iris->ps2->ee; for (int i = 0; i < 32; i++) { if (ee_cop0_prev[i] != ee->cop0_r[i]) ee_cop0_frames[i] = 60; ee_cop0_prev[i] = ee->cop0_r[i]; } PushFont(iris->font_code); if (BeginTable("ee#cop0registers", 5, ImGuiTableFlags_RowBg | ee_table_sizing)) { PushFont(iris->font_small_code); TableSetupColumn("Reg"); TableSetupColumn("96-127"); TableSetupColumn("64-95"); TableSetupColumn("32-63"); TableSetupColumn("0-31"); TableHeadersRow(); PopFont(); for (int i = 0; i < 32; i++) { float a = (float)ee_cop0_frames[i] / 60.0f; TableNextRow(); TableSetColumnIndex(0); Text("%s", ee_cop0_r[i]); TableSetColumnIndex(4); char label[8]; sprintf(label, "##e%d", (i << 2)); if (Selectable(label, false, ImGuiSelectableFlags_AllowOverlap)) { OpenPopup("Change register value"); } SameLine(0.0, 0.0); if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) { static char new_value[9]; PushFont(iris->font_small_code); TextDisabled("Edit "); SameLine(0.0, 0.0); Text("%s", ee_cop0_r[i]); PopFont(); PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0)); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0)); PushFont(iris->font_body); PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35)); AlignTextToFramePadding(); Text(ICON_MS_EDIT); SameLine(); SetNextItemWidth(100); PushFont(iris->font_code); if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) { if (new_value[0]) ee->cop0_r[i] = strtoul(new_value, NULL, 16); CloseCurrentPopup(); } PopFont(); if (Button("Change")) { if (new_value[0]) ee->cop0_r[i] = strtoul(new_value, NULL, 16); CloseCurrentPopup(); } SameLine(); if (Button("Cancel")) CloseCurrentPopup(); PopStyleColor(); PopStyleVar(2); PopFont(); EndPopup(); } TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%08x", ee->cop0_r[i]); } } EndTable(); PopFont(); } static inline void show_ee_fpu_registers(iris::instance* iris) { using namespace ImGui; struct ee_state* ee = iris->ps2->ee; for (int i = 0; i < 32; i++) { if (ee_fpu_prev[i] != ee->f[i].u32) ee_fpu_frames[i] = 60; ee_fpu_prev[i] = ee->f[i].u32; } PushFont(iris->font_code); if (BeginTable("ee#fpuregisters", 3, ImGuiTableFlags_RowBg | ee_table_sizing)) { PushFont(iris->font_small_code); TableSetupColumn("Reg"); TableSetupColumn("u32"); TableSetupColumn("float"); TableHeadersRow(); PopFont(); for (int i = 0; i < 32; i++) { float a = (float)ee_fpu_frames[i] / 60.0f; TableNextRow(); TableSetColumnIndex(0); Text("f%-2d", i); TableSetColumnIndex(1); char label1[8]; sprintf(label1, "##u%d", i); if (Selectable(label1, false, ImGuiSelectableFlags_AllowOverlap)) { OpenPopup("Change register value"); } SameLine(0.0, 0.0); if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) { static char new_value[9]; PushFont(iris->font_small_code); TextDisabled("Edit "); SameLine(0.0, 0.0); Text("f%d ", i); SameLine(0.0, 0.0); TextDisabled("(as u32)"); PopFont(); PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0)); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0)); PushFont(iris->font_body); PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35)); AlignTextToFramePadding(); Text(ICON_MS_EDIT); SameLine(); SetNextItemWidth(100); PushFont(iris->font_code); if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) { if (new_value[0]) ee->f[i].u32 = strtoul(new_value, NULL, 16); CloseCurrentPopup(); } PopFont(); if (Button("Change")) { if (new_value[0]) ee->f[i].u32 = strtoul(new_value, NULL, 16); CloseCurrentPopup(); } SameLine(); if (Button("Cancel")) CloseCurrentPopup(); PopStyleColor(); PopStyleVar(2); PopFont(); EndPopup(); } TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%08x", ee->f[i].u32); TableSetColumnIndex(2); char label2[8]; sprintf(label2, "##f%d", i); if (Selectable(label2, false, ImGuiSelectableFlags_AllowOverlap)) { OpenPopup("Change register value"); } SameLine(0.0, 0.0); if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) { static char new_value[9]; PushFont(iris->font_small_code); TextDisabled("Edit "); SameLine(0.0, 0.0); Text("f%d ", i); SameLine(0.0, 0.0); TextDisabled("(as float)"); PopFont(); PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0)); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0)); PushFont(iris->font_body); PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35)); AlignTextToFramePadding(); Text(ICON_MS_EDIT); SameLine(); SetNextItemWidth(100); PushFont(iris->font_code); if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_EnterReturnsTrue)) { if (new_value[0]) ee->f[i].f = strtof(new_value, NULL); CloseCurrentPopup(); } PopFont(); if (Button("Change")) { if (new_value[0]) ee->f[i].f = strtof(new_value, NULL); CloseCurrentPopup(); } SameLine(); if (Button("Cancel")) CloseCurrentPopup(); PopStyleColor(); PopStyleVar(2); PopFont(); EndPopup(); } TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%.7f", ee->f[i].f); } } EndTable(); PopFont(); } static inline void show_vu0_float(iris::instance* iris) { using namespace ImGui; struct vu_state* vu0 = iris->ps2->ee->vu0; for (int i = 0; i < 32; i++) { for (int j = 0; j < 4; j++) { if (vu0f_prev[i].u32[j] != vu0->vf[i].u32[j]) vu0f_frames[i].u32[j] = 60; } vu0f_prev[i] = vu0->vf[i]; } PushFont(iris->font_code); if (BeginTable("ee#registers", 5, ImGuiTableFlags_RowBg | ee_table_sizing)) { PushFont(iris->font_small_code); TableSetupColumn("Reg"); TableSetupColumn("W"); TableSetupColumn("Z"); TableSetupColumn("Y"); TableSetupColumn("X"); TableHeadersRow(); PopFont(); for (int i = 0; i < 32; i++) { TableNextRow(); TableSetColumnIndex(0); Text("vf%02d", i); for (int j = 0; j < 4; j++) { float a = (float)vu0f_frames[i].u32[j] / 60.0f; TableSetColumnIndex(1+(3-j)); char label[5]; sprintf(label, "##%02x", (i << 2) | j); if (Selectable(label, false, ImGuiSelectableFlags_AllowOverlap)) { OpenPopup("Change register value"); } SameLine(0.0, 0.0); if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) { static char new_value[9]; PushFont(iris->font_small_code); TextDisabled("Edit "); SameLine(0.0, 0.0); Text("vf%02d", i); SameLine(0.0, 0.0); TextDisabled(" (%c field)", "XYZW"[j]); PopFont(); PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0)); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0)); PushFont(iris->font_body); PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35)); AlignTextToFramePadding(); Text(ICON_MS_EDIT); SameLine(); SetNextItemWidth(100); PushFont(iris->font_code); if (vu0f_float) { if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsScientific | ImGuiInputTextFlags_EnterReturnsTrue)) { if (new_value[0]) vu0->vf[i].f[j] = strtof(new_value, NULL); CloseCurrentPopup(); } } else { if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) { if (new_value[0]) vu0->vf[i].u32[j] = strtoul(new_value, NULL, 16); CloseCurrentPopup(); } } PopFont(); if (Button("Change")) { if (vu0f_float) { if (new_value[0]) vu0->vf[i].f[j] = strtof(new_value, NULL); } else { if (new_value[0]) vu0->vf[i].u32[j] = strtoul(new_value, NULL, 16); } CloseCurrentPopup(); } SameLine(); if (Button("Cancel")) CloseCurrentPopup(); PopStyleColor(); PopStyleVar(2); PopFont(); EndPopup(); } if (vu0f_float) { TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%.7f", vu0->vf[i].f[j]); } else { TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%08x", vu0->vf[i].u32[j]); } } } EndTable(); } PopFont(); } static inline void show_vu0_integer(iris::instance* iris) { using namespace ImGui; struct vu_state* vu0 = iris->ps2->ee->vu0; for (int i = 0; i < 32; i++) { if (vu0i_prev[i] != (i < 16 ? vu0->vi[i] : vu0->cr[i-16])) vu0i_frames[i] = 60; vu0i_prev[i] = i < 16 ? vu0->vi[i] : vu0->cr[i-16]; } PushFont(iris->font_code); if (BeginTable("ee#cop0registers", 5, ImGuiTableFlags_RowBg | ee_table_sizing)) { PushFont(iris->font_small_code); TableSetupColumn("Reg"); TableSetupColumn("96-127"); TableSetupColumn("64-95"); TableSetupColumn("32-63"); TableSetupColumn("0-31"); TableHeadersRow(); PopFont(); for (int i = 0; i < 32; i++) { float a = (float)vu0i_frames[i] / 60.0f; TableNextRow(); TableSetColumnIndex(0); Text("%s", vu0i_regs[i]); TableSetColumnIndex(4); char label[8]; sprintf(label, "##e%d", (i << 2)); if (Selectable(label, false, ImGuiSelectableFlags_AllowOverlap)) { OpenPopup("Change register value"); } SameLine(0.0, 0.0); if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) { static char new_value[9]; PushFont(iris->font_small_code); TextDisabled("Edit "); SameLine(0.0, 0.0); Text("%s", vu0i_regs[i]); PopFont(); PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0)); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0)); PushFont(iris->font_body); PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35)); AlignTextToFramePadding(); Text(ICON_MS_EDIT); SameLine(); SetNextItemWidth(100); PushFont(iris->font_code); if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) { if (new_value[0]) { if (i < 16) { vu0->vi[i] = strtoul(new_value, NULL, 16); } else { vu0->cr[i-16] = strtoul(new_value, NULL, 16); } } CloseCurrentPopup(); } PopFont(); if (Button("Change")) { if (i < 16) { vu0->vi[i] = strtoul(new_value, NULL, 16); } else { vu0->cr[i-16] = strtoul(new_value, NULL, 16); } CloseCurrentPopup(); } SameLine(); if (Button("Cancel")) CloseCurrentPopup(); PopStyleColor(); PopStyleVar(2); PopFont(); EndPopup(); } TextColored(ImVec4(0.6+a, 0.6, 0.6, 1.0), "%08x", i < 16 ? vu0->vi[i] : vu0->cr[i-16]); } } EndTable(); PopFont(); } static inline void show_iop_main_registers(iris::instance* iris) { using namespace ImGui; PushFont(iris->font_code); struct iop_state* iop = iris->ps2->iop; for (int i = 0; i < 32; i++) { if (iop_prev[i] != iop->r[i]) iop_frames[i] = 60; iop_prev[i] = iop->r[i]; } if (BeginTable("table3", 4, ImGuiTableFlags_RowBg | iop_table_sizing)) { for (int i = 0; i < 32;) { TableNextRow(); for (int x = 0; (x < 4) && (i < 32); x++) { float a = (float)iop_frames[i] / 60.0f; char id[5]; sprintf(id, "##%02u", i); TableSetColumnIndex(x); if (Selectable(id, false, ImGuiSelectableFlags_AllowOverlap)) { OpenPopup("Change register value"); } SameLine(0.0, 0.0); if (BeginPopupContextItem(NULL, ImGuiPopupFlags_MouseButtonLeft)) { static char new_value[9]; PushFont(iris->font_small_code); TextDisabled("Edit %s", mips_cc_r[i]); PopFont(); PushStyleVar(ImGuiStyleVar_FramePadding, ImVec2(6.0, 2.0)); PushStyleVar(ImGuiStyleVar_ItemSpacing, ImVec2(8.0, 8.0)); PushFont(iris->font_body); PushStyleColor(ImGuiCol_FrameBg, ImVec4(0.35, 0.35, 0.35, 0.35)); AlignTextToFramePadding(); Text(ICON_MS_EDIT); SameLine(); SetNextItemWidth(100); PushFont(iris->font_code); if (InputText("##", new_value, 9, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue)) { if (new_value[0]) iop->r[i] = strtoul(new_value, NULL, 16); CloseCurrentPopup(); } PopFont(); if (Button("Change")) { if (new_value[0]) iop->r[i] = strtoul(new_value, NULL, 16); CloseCurrentPopup(); } SameLine(); if (Button("Cancel")) CloseCurrentPopup(); PopStyleColor(); PopStyleVar(2); PopFont(); EndPopup(); } SameLine(0.0, 0.0); Text("%2s", mips_cc_r[i]); SameLine(); TextColored(ImVec4(0.6 + a, 0.6, 0.6, 1.0), "%08x", iop->r[i]); ++i; } } EndTable(); } PopFont(); } static inline void show_work_in_progress(iris::instance* iris) { using namespace ImGui; Text("Work-in-progress!"); } static const char* sizing_combo_items[] = { ICON_MS_FIT_WIDTH " Fixed fit", ICON_MS_FULLSCREEN " Fixed same", ICON_MS_FULLSCREEN " Stretch prop", ICON_MS_FULLSCREEN " Stretch same" }; static ImGuiTableFlags table_sizing_flags[] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame }; static int ee_sizing_combo = 3; static int iop_sizing_combo = 0; static const char* ee_reg_group_names[] = { "Main", "COP0", "FPU", "VU0f", "VU0i" }; static int ee_reg_group = 0; void show_ee_state(iris::instance* iris) { using namespace ImGui; if (imgui::BeginEx("EE state", &iris->show_ee_state, ImGuiWindowFlags_MenuBar)) { if (BeginMenuBar()) { if (BeginMenu("Settings")) { if (BeginMenu(ICON_MS_CROP " Sizing")) { for (int i = 0; i < 4; i++) { if (Selectable(sizing_combo_items[i], i == ee_sizing_combo)) { ee_table_sizing = table_sizing_flags[i]; ee_sizing_combo = i; } } ImGui::EndMenu(); } if (MenuItem("Display VU0f as floats", nullptr, &vu0f_float)); ImGui::EndMenu(); } EndMenuBar(); } if (BeginTable("table4", 5, ImGuiTableFlags_SizingStretchSame | ImGuiTableFlags_BordersInnerV)) { TableNextRow(); for (int i = 0; i < 5; i++) { TableSetColumnIndex(i); if (Selectable(ee_reg_group_names[i], ee_reg_group == i)) { ee_reg_group = i; } } EndTable(); } if (BeginChild("ee#child")) { switch (ee_reg_group) { case 0: { show_ee_main_registers(iris); } break; case 1: { show_ee_cop0_registers(iris); } break; case 2: { show_ee_fpu_registers(iris); } break; case 3: { show_vu0_float(iris); } break; case 4: { show_vu0_integer(iris); } break; } EndChild(); } } End(); for (int i = 0; i < 32; i++) { if (ee_fpu_frames[i]) ee_fpu_frames[i]--; if (ee_cop0_frames[i]) ee_cop0_frames[i]--; if (vu0i_frames[i]) vu0i_frames[i]--; for (int j = 0; j < 4; j++) if (ee_frames[i].u32[j]) ee_frames[i].u32[j]--; for (int j = 0; j < 4; j++) if (vu0f_frames[i].u32[j]) vu0f_frames[i].u32[j]--; } } void show_iop_state(iris::instance* iris) { using namespace ImGui; if (imgui::BeginEx("IOP state", &iris->show_iop_state, ImGuiWindowFlags_MenuBar)) { if (BeginMenuBar()) { if (BeginMenu("Settings")) { if (BeginMenu(ICON_MS_CROP " Sizing")) { for (int i = 0; i < 4; i++) { if (Selectable(sizing_combo_items[i], i == iop_sizing_combo)) { iop_table_sizing = table_sizing_flags[i]; iop_sizing_combo = i; } } ImGui::EndMenu(); } ImGui::EndMenu(); } EndMenuBar(); } if (BeginChild("iop#child")) { show_iop_main_registers(iris); EndChild(); } } End(); for (int i = 0; i < 32; i++) if (iop_frames[i]) iop_frames[i]--; } } ================================================ FILE: frontend/ui/statusbar.cpp ================================================ #include "imgui_internal.h" #include "iris.hpp" #include "res/IconsMaterialSymbols.h" namespace ImGui { bool BeginMainStatusBar() { ImGuiContext& g = *GetCurrentContext(); ImGuiViewportP* viewport = (ImGuiViewportP*)(void*)GetMainViewport(); // Notify of viewport change so GetFrameHeight() can be accurate in case of DPI change SetCurrentViewport(NULL, viewport); // For the main menu bar, which cannot be moved, we honor g.Style.DisplaySafeAreaPadding to ensure text can be visible on a TV set. // FIXME: This could be generalized as an opt-in way to clamp window->DC.CursorStartPos to avoid SafeArea? // FIXME: Consider removing support for safe area down the line... it's messy. Nowadays consoles have support for TV calibration in OS settings. g.NextWindowData.MenuBarOffsetMinVal = ImVec2(g.Style.DisplaySafeAreaPadding.x, ImMax(g.Style.DisplaySafeAreaPadding.y - g.Style.FramePadding.y, 0.0f)); ImGuiWindowFlags window_flags = ImGuiWindowFlags_NoScrollbar | ImGuiWindowFlags_NoSavedSettings | ImGuiWindowFlags_MenuBar; float height = GetFrameHeight(); bool is_open = BeginViewportSideBar("##MainStatusBar", viewport, ImGuiDir_Down, height, window_flags); g.NextWindowData.MenuBarOffsetMinVal = ImVec2(0.0f, 0.0f); if (is_open) BeginMenuBar(); else End(); return is_open; } void EndMainStatusBar() { EndMenuBar(); // When the user has left the menu layer (typically: closed menus through activation of an item), we restore focus to the previous window // FIXME: With this strategy we won't be able to restore a NULL focus. ImGuiContext& g = *GImGui; if (g.CurrentWindow == g.NavWindow && g.NavLayer == ImGuiNavLayer_Main && !g.NavAnyRequest) FocusTopMostWindowUnderOne(g.NavWindow, NULL, NULL, ImGuiFocusRequestFlags_UnlessBelowModal | ImGuiFocusRequestFlags_RestoreFocusedChild); End(); } } namespace iris { int get_format_bpp(VkFormat fmt) { switch (fmt) { case GS_PSMCT32: return 32; case GS_PSMCT24: return 24; case GS_PSMCT16: case GS_PSMCT16S: return 16; } return 0; } void show_status_bar(iris::instance* iris) { using namespace ImGui; if (BeginMainStatusBar()) { static const char* const modes[] = { "Progressive", "Interlaced (Field)", "Progressive", "Interlaced (Frame)" }; static const char* const renderers[] = { "Null", "Software", "Hardware (Vulkan)" }; int dispfb = 0; char buf[128]; sprintf(buf, "%s", emu::get_current_system_name(iris)); float width = CalcTextSize(buf).x; ImVec4 col = GetStyleColorVec4(ImGuiCol_Text); col.w = 0.2; PushStyleColor(ImGuiCol_Separator, col); PushStyleVarX(ImGuiStyleVar_ItemSpacing, 4.0f); SetCursorPosX(5.0); if (!iris->image.image) { Text("%s", renderers[iris->renderer_backend]); SeparatorEx(ImGuiSeparatorFlags_Vertical); Text("No image"); SeparatorEx(ImGuiSeparatorFlags_Vertical); Text("%.1f fps", GetIO().Framerate); SetCursorPosX(GetWindowWidth() - width - 5); Text(buf); } else { // | %dx%d | %dx%d | %s | %dbpp | %.1f fps", Text("%s", renderers[iris->renderer_backend]); SeparatorEx(ImGuiSeparatorFlags_Vertical); Text("%dx%d", iris->render_width, iris->render_height); SeparatorEx(ImGuiSeparatorFlags_Vertical); Text("%dx%d", iris->image.width, iris->image.height); SeparatorEx(ImGuiSeparatorFlags_Vertical); Text("%s", modes[iris->ps2->gs->smode2 & 3]); SeparatorEx(ImGuiSeparatorFlags_Vertical); Text("%.1f fps", GetIO().Framerate); SetCursorPosX(GetWindowWidth() - width - 5); Text(buf); } PopStyleColor(); PopStyleVar(); // if (vp_w) { // Text(ICON_MS_MONITOR " %s | %dx%d | %dx%d | %s | %dbpp | %.1f fps", // "None", // renderer_get_name(iris->ctx), // disp_w, disp_h, // vp_w, vp_h, // mode == 3 ? "Interlaced" : "Progressive", // get_format_bpp(disp_fmt), // GetIO().Framerate // ); // // iris->avg_frames++; // // iris->avg_fps += iris->fps; // } else { // Text(ICON_MS_MONITOR " %s | No image", // "None" // renderer_get_name(iris->ctx) // ); // } EndMainStatusBar(); } } } ================================================ FILE: frontend/ui/symbols.cpp ================================================ #include #include #include #include #include #include "iris.hpp" #include "res/IconsMaterialSymbols.h" namespace iris { static const char* symbols_sizing_combo_items[] = { ICON_MS_FIT_WIDTH " Fixed fit", ICON_MS_FULLSCREEN " Fixed same", ICON_MS_FULLSCREEN " Stretch prop", ICON_MS_FULLSCREEN " Stretch same" }; static ImGuiTableFlags symbols_table_sizing_flags[] = { ImGuiTableFlags_SizingFixedFit, ImGuiTableFlags_SizingFixedSame, ImGuiTableFlags_SizingStretchProp, ImGuiTableFlags_SizingStretchSame }; static int symbols_table_sizing_combo = 0; static int symbols_table_sizing = ImGuiTableFlags_SizingStretchProp; std::vector symbols_list; bool regex = false; bool case_sensitive = false; bool autosearch = true; void filter_symbols(iris::instance* iris, const char* filter, bool regex, bool case_sensitive) { symbols_list.clear(); if (filter[0] == '\0') { symbols_list = iris->symbols; return; } std::string filter_str(filter); for (const iris::elf_symbol& sym : iris->symbols) { if (regex) { std::regex r(filter_str, std::regex::ECMAScript | (case_sensitive ? std::regex_constants::syntax_option_type(0) : std::regex::icase)); if (std::regex_match(sym.name, r)) { symbols_list.push_back(sym); } } else { std::string sym_name_str(sym.name); if (!case_sensitive) { std::transform(sym_name_str.begin(), sym_name_str.end(), sym_name_str.begin(), tolower); std::transform(filter_str.begin(), filter_str.end(), filter_str.begin(), tolower); } auto it = sym_name_str.find(filter_str); if (it != std::string::npos) { symbols_list.push_back(sym); } } } } int edit_callback(ImGuiInputTextCallbackData* data) { iris::instance* iris = (iris::instance*)data->UserData; filter_symbols(iris, data->Buf, regex, case_sensitive); return 0; } void show_symbols(iris::instance* iris) { using namespace ImGui; if (imgui::BeginEx("Symbols", &iris->show_symbols, ImGuiWindowFlags_MenuBar)) { if (BeginMenuBar()) { if (BeginMenu("Settings")) { if (BeginMenu(ICON_MS_CROP " Sizing")) { for (int i = 0; i < 4; i++) { if (Selectable(symbols_sizing_combo_items[i], i == symbols_table_sizing_combo)) { symbols_table_sizing = symbols_table_sizing_flags[i]; symbols_table_sizing_combo = i; } } ImGui::EndMenu(); } MenuItem(ICON_MS_SEARCH_CHECK " Auto search", NULL, &autosearch); ImGui::EndMenu(); } EndMenuBar(); } static char buf[512]; SetNextItemWidth(200.0f); ImGuiInputFlags flags = ImGuiInputTextFlags_EnterReturnsTrue; if (autosearch) { flags |= ImGuiInputTextFlags_CallbackEdit; } if (InputTextWithHint("##search", "Search symbols...", buf, 512, flags, edit_callback, (void*)iris)) { filter_symbols(iris, buf, regex, case_sensitive); } SameLine(); if (Button(ICON_MS_SEARCH)) { filter_symbols(iris, buf, regex, case_sensitive); } SameLine(); if (BeginPopupContextItem("symbols_settings")) { if (MenuItem(ICON_MS_REGULAR_EXPRESSION " Regex mode", NULL, ®ex)) { filter_symbols(iris, buf, regex, case_sensitive); } if (MenuItem(ICON_MS_MATCH_CASE " Case-sensitive", NULL, &case_sensitive)) { filter_symbols(iris, buf, regex, case_sensitive); } EndPopup(); } if (Button(ICON_MS_SETTINGS, ImVec2(50, 0))) { OpenPopup("symbols_settings"); } int text_height = GetTextLineHeightWithSpacing(); ImVec2 outer_size = ImVec2(0, GetContentRegionAvail().y - text_height); ImGuiTableFlags table_flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Sortable | ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY; table_flags |= symbols_table_sizing; if (BeginTable("##symbolstb", 3, table_flags, outer_size)) { if (ImGuiTableSortSpecs* sort_specs = ImGui::TableGetSortSpecs()) { if (sort_specs->SpecsDirty) { if (buf[0] == '\0') { symbols_list = iris->symbols; } // Sort by symbol if (sort_specs->Specs->ColumnIndex == 0) { std::sort(symbols_list.begin(), symbols_list.end(), [=](const iris::elf_symbol& a, const iris::elf_symbol& b) { return sort_specs->Specs->SortDirection == ImGuiSortDirection_Ascending ? std::string(a.name) < std::string(b.name) : std::string(a.name) > std::string(b.name); }); } // Sort by address if (sort_specs->Specs->ColumnIndex == 1) { std::sort(symbols_list.begin(), symbols_list.end(), [=](const iris::elf_symbol& a, const iris::elf_symbol& b) { return sort_specs->Specs->SortDirection == ImGuiSortDirection_Ascending ? a.addr < b.addr : a.addr > b.addr; }); } // Sort by size if (sort_specs->Specs->ColumnIndex == 2) { std::sort(symbols_list.begin(), symbols_list.end(), [=](const iris::elf_symbol& a, const iris::elf_symbol& b) { return sort_specs->Specs->SortDirection == ImGuiSortDirection_Ascending ? a.size < b.size : a.size > b.size; }); } sort_specs->SpecsDirty = false; } } TableSetupScrollFreeze(0, 1); TableSetupColumn("Symbol"); TableSetupColumn("Address"); TableSetupColumn("Size"); PushFont(iris->font_small_code); TableHeadersRow(); PopFont(); PushFont(iris->font_code); int index = 0; for (const iris::elf_symbol& symbol : symbols_list) { TableNextRow(); TableSetColumnIndex(0); Text("%s", symbol.name); TableSetColumnIndex(1); Text("%08x", symbol.addr); TableSetColumnIndex(2); char label[64]; sprintf(label, "%zu##%d", symbol.size, index++); if (Selectable(label, false, ImGuiSelectableFlags_SpanAllColumns | ImGuiSelectableFlags_AllowDoubleClick)) { if (IsMouseDoubleClicked(ImGuiMouseButton_Left)) { iris->show_ee_control = true; iris->ee_control_follow_pc = false; iris->ee_control_address = symbol.addr; } } if (BeginPopupContextItem()) { PushFont(iris->font_small_code); TextDisabled("%s", symbol.name); PopFont(); PushFont(iris->font_body); if (Selectable(ICON_MS_ARROW_FORWARD " Go to this address")) { iris->show_ee_control = true; iris->ee_control_follow_pc = false; iris->ee_control_address = symbol.addr; } if (Selectable(ICON_MS_ADD_CIRCLE " Set a breakpoint here")) { breakpoint b; b.addr = symbol.addr; b.cond_r = false; b.cond_w = false; b.cond_x = true; b.cpu = BKPT_CPU_EE; b.size = 4; b.enabled = true; iris->breakpoints.push_back(b); } PopFont(); EndPopup(); } } PopFont(); EndTable(); } Text("%zu ", symbols_list.size()); SameLine(0.0, 0.0); TextDisabled("symbols found "); SameLine(0.0, 0.0); Text("| "); SameLine(0.0, 0.0); TextDisabled("%s ", regex ? "Regex mode" : "Normal mode"); SameLine(0.0, 0.0); Text("| "); SameLine(0.0, 0.0); TextDisabled("%s ", case_sensitive ? "Case-sensitive" : "Case-insensitive"); } End(); } } ================================================ FILE: frontend/ui/threads.cpp ================================================ #include #include #include #include #include #include "iris.hpp" #include "ee/ee_def.hpp" #include "res/IconsMaterialSymbols.h" namespace iris { static inline const char* get_status_string(int status) { switch (status) { case THS_RUN: return "RUN"; case THS_READY: return "READY"; case THS_WAIT: return "WAIT"; case THS_SUSPEND: return "SUSPEND"; case THS_WAITSUSPEND: return "WAIT/SUSPEND"; case THS_DORMANT: return "DORMANT"; } return ""; } static const char* get_entry_symbol(iris::instance* iris, uint32_t addr) { // Look up the address in the symbol table if (addr == 0x81fc0) return "EE Idle Thread"; for (const iris::elf_symbol& sym : iris->symbols) { if ((sym.addr >= addr) && (sym.addr < (addr + sym.size))) { return sym.name; } } return nullptr; } void show_thread_list(iris::instance* iris) { using namespace ImGui; struct ee_state* ee = iris->ps2->ee; ImGuiTableFlags table_flags = ImGuiTableFlags_RowBg | ImGuiTableFlags_Sortable | ImGuiTableFlags_Hideable | ImGuiTableFlags_ScrollY; if (BeginTable("##threadlist", 5, table_flags)) { TableSetupScrollFreeze(0, 1); TableSetupColumn("ID"); TableSetupColumn("Priority"); TableSetupColumn("Entry"); TableSetupColumn("PC"); TableSetupColumn("Status"); PushFont(iris->font_small_code); TableHeadersRow(); PopFont(); struct ee_thread* thr = (struct ee_thread*)&iris->ps2->ee_ram->buf[ee->thread_list_base & 0x1fffffff]; int id = 0; while (thr->status) { TableNextRow(); TableSetColumnIndex(0); Text("%d", id++); TableSetColumnIndex(1); Text("%d", thr->current_priority); TableSetColumnIndex(2); const char* symbol = get_entry_symbol(iris, thr->func); if (symbol) { Text("%s", symbol); } else { Text("0x%08X", thr->func); } uint32_t argv = *(uint32_t*)&iris->ps2->ee_ram->buf[(thr->argv + 4) & 0x1fffffff]; // if (thr->argc) { // printf("argv=%08x *argv=%08x\n", thr->argv, argv); // } TableSetColumnIndex(3); Text("%s", thr->argc ? (char*)&iris->ps2->ee_ram->buf[argv & 0x1fffffff] : "NULL"); TableSetColumnIndex(4); Text("%s", get_status_string(thr->status)); thr++; } EndTable(); } } void show_threads(iris::instance* iris) { using namespace ImGui; if (imgui::BeginEx("Threads", &iris->show_threads)) { if (!iris->ps2->ee->thread_list_base) { ImVec2 size = CalcTextSize(ICON_MS_WARNING " Thread list hasn't been initialized yet"); ImVec2 pos = ImVec2(GetContentRegionAvail().x / 2 - size.x / 2, GetContentRegionAvail().y / 2 - size.y / 2); ImVec4 col = GetStyle().Colors[ImGuiCol_Text]; SetCursorPos(pos); TextDisabled(ICON_MS_WARNING " Thread list hasn't been initialized yet"); End(); return; } show_thread_list(iris); } End(); } } ================================================ FILE: frontend/ui/vu_disassembly.cpp ================================================ #include #include #include #include #include "iris.hpp" #include "portable-file-dialogs.h" #include "res/IconsMaterialSymbols.h" #include "ee/vu_dis.h" #include "ee/vu_def.hpp" #include #include #include // Trim from the start (in place) inline void ltrim_impl(std::string &s) { s.erase(s.begin(), std::find_if(s.begin(), s.end(), [](unsigned char ch) { return !std::isspace(ch); })); } // Trim from the end (in place) inline void rtrim_impl(std::string &s) { s.erase(std::find_if(s.rbegin(), s.rend(), [](unsigned char ch) { return !std::isspace(ch); }).base(), s.end()); } // Trim from both ends (in place) inline void trim_impl(std::string &s) { rtrim_impl(s); ltrim_impl(s); } // Trim from the start (copying) inline std::string ltrim(std::string s) { ltrim_impl(s); return s; } // Trim from the end (copying) inline std::string rtrim(std::string s) { rtrim_impl(s); return s; } // Trim from both ends (copying) inline std::string trim(std::string s) { trim_impl(s); return s; } #define IM_RGB(r, g, b) ImVec4(((float)r / 255.0f), ((float)g / 255.0f), ((float)b / 255.0f), 1.0) namespace iris { struct vu_dis_state g_vu_dis_state = { 0 }; uint32_t addr = 0; bool stop_at_e_bit = false; bool disassemble_all = false; bool add_padding = true; bool compact_view = false; bool show_address_opcode = true; void print_highlighted_vu1(iris::instance* iris, const char* buf) { using namespace ImGui; std::vector tokens; std::string text; while (*buf) { text.clear(); if (isalpha(*buf)) { while (isalpha(*buf) || isdigit(*buf) || (*buf == '.')) text.push_back(*buf++); } else if (isxdigit(*buf) || (*buf == '-')) { while (isxdigit(*buf) || (*buf == 'x') || (*buf == '-')) text.push_back(*buf++); } else if (*buf == '$') { while (*buf == '$' || isdigit(*buf) || isalpha(*buf) || *buf == '_') text.push_back(*buf++); } else if (*buf == ',') { while (*buf == ',') text.push_back(*buf++); } else if (*buf == '(') { while (*buf == '(') text.push_back(*buf++); } else if (*buf == ')') { while (*buf == ')') text.push_back(*buf++); } else if (*buf == '<') { while (*buf != '>') text.push_back(*buf++); text.push_back(*buf++); } else if (*buf == '_') { text.push_back(*buf++); } else if (*buf == '.') { text.push_back(*buf++); } else if (*buf == '+') { text.push_back(*buf++); } else if (*buf == '-') { text.push_back(*buf++); } else { printf("unhandled char %c (%d) \"%s\"\n", *buf, *buf, buf); exit(1); } while (isspace(*buf)) text.push_back(*buf++); tokens.push_back(text); } for (const std::string& t : tokens) { ImVec4 col = ImVec4( iris->codeview_color_register.Value.x, iris->codeview_color_register.Value.y, iris->codeview_color_register.Value.z, iris->codeview_color_register.Value.w ); if (t[0] == 'v' && (t[1] == 'f' || t[1] == 'i')) { TextColored(col, "%s", t.c_str()); } else if (rtrim(t) == "acc") { TextColored(col, "%s", t.c_str()); } else if (rtrim(t) == "q") { TextColored(col, "%s", t.c_str()); } else if (rtrim(t) == "p") { TextColored(col, "%s", t.c_str()); } else if (rtrim(t) == "i") { TextColored(col, "%s", t.c_str()); } else if (rtrim(t) == "r") { TextColored(col, "%s", t.c_str()); } else if (isalpha(t[0])) { col = ImVec4( iris->codeview_color_mnemonic.Value.x, iris->codeview_color_mnemonic.Value.y, iris->codeview_color_mnemonic.Value.z, iris->codeview_color_mnemonic.Value.w ); TextColored(col, "%s", t.c_str()); } else if (isdigit(t[0]) || t[0] == '-') { col = ImVec4( iris->codeview_color_number.Value.x, iris->codeview_color_number.Value.y, iris->codeview_color_number.Value.z, iris->codeview_color_number.Value.w ); TextColored(col, "%s", t.c_str()); } else if (t[0] == '<') { col = ImVec4( iris->codeview_color_other.Value.x, iris->codeview_color_other.Value.y, iris->codeview_color_other.Value.z, iris->codeview_color_other.Value.w ); TextColored(col, "%s", t.c_str()); } else { Text("%s", t.c_str()); } SameLine(0.0f, 0.0f); } NewLine(); } static void show_vu_disassembly_view(iris::instance* iris, uint64_t* mem, size_t size) { using namespace ImGui; float font_scale = GetStyle().FontScaleMain; GetStyle().FontScaleMain = iris->codeview_font_scale; PushFont(iris->font_code); if (!iris->codeview_use_theme_background) { PushStyleColor(ImGuiCol_TableRowBg, ImVec4( iris->codeview_color_background.Value.x, iris->codeview_color_background.Value.y, iris->codeview_color_background.Value.z, iris->codeview_color_background.Value.w )); PushStyleColor(ImGuiCol_TableRowBgAlt, ImVec4( iris->codeview_color_background.Value.x, iris->codeview_color_background.Value.y, iris->codeview_color_background.Value.z, iris->codeview_color_background.Value.w )); PushStyleColor(ImGuiCol_Text, ImVec4( iris->codeview_color_text.Value.x, iris->codeview_color_text.Value.y, iris->codeview_color_text.Value.z, iris->codeview_color_text.Value.w )); PushStyleColor(ImGuiCol_TextDisabled, ImVec4( iris->codeview_color_comment.Value.x, iris->codeview_color_comment.Value.y, iris->codeview_color_comment.Value.z, iris->codeview_color_comment.Value.w )); } if (BeginTable("table1", compact_view ? 2 : 3, ImGuiTableFlags_RowBg | ImGuiTableFlags_SizingFixedFit | ImGuiTableFlags_Hideable)) { PushFont(iris->font_small_code); TableSetupColumn(" Address/Opcode"); TableSetupColumn(compact_view ? "Upper/Lower" : "Upper"); if (!compact_view) { TableSetupColumn("Lower"); } TableHeadersRow(); PopFont(); int e_bit = 0; for (int row = disassemble_all ? 0 : addr; row < size; row++) { g_vu_dis_state.addr = row * 8; TableNextRow(); TableSetColumnIndex(0); uint64_t u = mem[row] >> 32; uint64_t l = mem[row] & 0xffffffff; if (!compact_view) { TextDisabled("%04x: %08x %08x", row, u, l); SameLine(); } else { TextDisabled("%04x: %08x", row, u); SameLine(); } TableSetColumnIndex(1); char upper[512], lower[512]; g_vu_dis_state.addr = row; vu_disassemble_upper(upper, u, &g_vu_dis_state); vu_disassemble_lower(lower, l, &g_vu_dis_state); if (add_padding && !compact_view) { #ifdef _WIN32 sprintf_s(upper, "%-40s", upper); sprintf_s(lower, "%-40s", lower); #else sprintf(upper, "%-40s", upper); sprintf(lower, "%-40s", lower); #endif } print_highlighted_vu1(iris, upper); if (!compact_view) { TableSetColumnIndex(2); } print_highlighted_vu1(iris, lower); if (e_bit && stop_at_e_bit && !disassemble_all) break; e_bit = (u & 0x40000000) ? 1 : 0; } EndTable(); } PopFont(); if (!iris->codeview_use_theme_background) { PopStyleColor(4); } GetStyle().FontScaleMain = font_scale; } void save_disassembly(FILE* file, uint64_t* mem, size_t size) { int e_bit = 0; for (int row = disassemble_all ? 0 : addr; row < size; row++) { g_vu_dis_state.addr = row * 8; uint64_t u = mem[row] >> 32; uint64_t l = mem[row] & 0xffffffff; char upper[512], lower[512]; g_vu_dis_state.addr = row; vu_disassemble_upper(upper, u, &g_vu_dis_state); vu_disassemble_lower(lower, l, &g_vu_dis_state); if (add_padding && !compact_view) { #ifdef _WIN32 sprintf_s(upper, "%-40s", upper); sprintf_s(lower, "%-40s", lower); #else sprintf(upper, "%-40s", upper); sprintf(lower, "%-40s", lower); #endif } if (compact_view) { fprintf(file, "%04x: %08x %s\n", row, u, upper); fprintf(file, " %08x %s\n", l, lower); } else { fprintf(file, "%04x: %08x %08x %s %s\n", row, u, l, upper, lower); } if (e_bit && stop_at_e_bit && !disassemble_all) break; e_bit = (u & 0x40000000) ? 1 : 0; } } void show_vu_disassembler(iris::instance* iris) { using namespace ImGui; PushFont(iris->font_icons); if (imgui::BeginEx("VU disassembler", &iris->show_vu_disassembler, ImGuiWindowFlags_MenuBar)) { if (BeginMenuBar()) { if (BeginMenu("File")) { if (MenuItem(ICON_MS_FILE_SAVE " Save disassembly as...", NULL)) { } ImGui::EndMenu(); } if (BeginMenu("Settings")) { MenuItem(ICON_MS_FORMAT_LETTER_SPACING_WIDER " Add padding", NULL, &add_padding); MenuItem(ICON_MS_COLLAPSE_ALL " Compact view", NULL, &compact_view); ImGui::EndMenu(); } EndMenuBar(); } if (BeginTabBar("##vudistabbar", ImGuiTabBarFlags_Reorderable)) { if (BeginTabItem("VU0")) { BeginDisabled(disassemble_all); AlignTextToFramePadding(); Text("Address"); SameLine(); SetNextItemWidth(100.0f); PushFont(iris->font_code); if (InputInt("##address", (int*)&addr, 0, 0, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll)); PopFont(); SameLine(); Checkbox("Stop at E bit", &stop_at_e_bit); EndDisabled(); SameLine(); Checkbox("Disassemble all", &disassemble_all); SameLine(); if (Button(ICON_MS_SAVE)) { pfd::save_file file("Save VU0 disassembly", "vu0.s", { "Text files", "*.txt" }); if (!file.result().empty()) { FILE* f = fopen(file.result().c_str(), "w"); if (f) { uint64_t* ptr = vu_get_micro_mem_ptr(iris->ps2->vu0, 0); save_disassembly(f, ptr, 512); fclose(f); } else { pfd::message("Error", "Failed to open file for writing.", pfd::choice::ok, pfd::icon::error); } } } SameLine(); BeginDisabled(!stop_at_e_bit); if (Button(ICON_MS_ARROW_RIGHT_ALT " Next program")) { int prev = addr; addr = 0; for (int i = prev; i < 512; i++) { uint32_t upper = *vu_get_micro_mem_ptr(iris->ps2->vu0, i) >> 32; if (upper & 0x40000000) { addr = i + 2; break; } } } EndDisabled(); SeparatorText("Disassembly"); if (BeginChild("vu0##disassembly")) { uint64_t* ptr = vu_get_micro_mem_ptr(iris->ps2->vu0, 0); show_vu_disassembly_view(iris, ptr, 512); } EndChild(); EndTabItem(); } if (BeginTabItem("VU1")) { BeginDisabled(disassemble_all); AlignTextToFramePadding(); Text("Address"); SameLine(); SetNextItemWidth(100.0f); PushFont(iris->font_code); if (InputInt("##address", (int*)&addr, 0, 0, ImGuiInputTextFlags_CharsHexadecimal | ImGuiInputTextFlags_EnterReturnsTrue | ImGuiInputTextFlags_EscapeClearsAll)); PopFont(); SameLine(); Checkbox("Stop at E bit", &stop_at_e_bit); EndDisabled(); SameLine(); Checkbox("Disassemble all", &disassemble_all); SameLine(); if (Button(ICON_MS_SAVE)) { pfd::save_file file("Save VU1 disassembly", "vu1.s", { "Text files", "*.txt" }); if (!file.result().empty()) { FILE* f = fopen(file.result().c_str(), "w"); if (f) { save_disassembly(f, iris->ps2->vu1->micro_mem, 2048); fclose(f); } else { pfd::message("Error", "Failed to open file for writing.", pfd::choice::ok, pfd::icon::error); } } } SameLine(); BeginDisabled(!stop_at_e_bit); if (Button(ICON_MS_ARROW_RIGHT_ALT " Next program")) { int prev = addr; addr = 0; for (int i = prev; i < 2048; i++) { uint32_t upper = iris->ps2->vu1->micro_mem[i] >> 32; if (upper & 0x40000000) { addr = i + 2; break; } } } EndDisabled(); SeparatorText("Disassembly"); if (BeginChild("vu1##disassembly")) { show_vu_disassembly_view(iris, iris->ps2->vu1->micro_mem, 2048); } EndChild(); EndTabItem(); } EndTabBar(); } } End(); PopFont(); } } ================================================ FILE: frontend/vulkan.cpp ================================================ #include #include "config.hpp" #include "iris.hpp" #include #include namespace iris::vulkan { std::vector get_instance_extensions() { std::vector extensions; uint32_t count; vkEnumerateInstanceExtensionProperties(nullptr, &count, nullptr); extensions.resize(count); if (vkEnumerateInstanceExtensionProperties(nullptr, &count, extensions.data()) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to enumerate instance extensions\n"); return {}; } return extensions; } std::vector get_instance_layers() { std::vector layers; uint32_t count; vkEnumerateInstanceLayerProperties(&count, nullptr); layers.resize(count); if (vkEnumerateInstanceLayerProperties(&count, layers.data()) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to enumerate instance layers\n"); return {}; } return layers; } bool is_instance_extension_supported(iris::instance* iris, const char* name) { return std::find_if( iris->instance_extensions.begin(), iris->instance_extensions.end(), [name](const VkExtensionProperties& ext) { return strncmp(ext.extensionName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; } ) != iris->instance_extensions.end(); } bool is_instance_layer_supported(iris::instance* iris, const char* name) { return std::find_if( iris->instance_layers.begin(), iris->instance_layers.end(), [name](const VkLayerProperties& layer) { return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; } ) != iris->instance_layers.end(); } bool is_device_extension_supported(iris::instance* iris, const char* name) { return std::find_if( iris->device_extensions.begin(), iris->device_extensions.end(), [name](const VkExtensionProperties& ext) { return strncmp(ext.extensionName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; } ) != iris->device_extensions.end(); } bool is_device_layer_supported(iris::instance* iris, const char* name) { return std::find_if( iris->device_layers.begin(), iris->device_layers.end(), [name](const VkLayerProperties& layer) { return strncmp(layer.layerName, name, VK_MAX_EXTENSION_NAME_SIZE) == 0; } ) != iris->device_layers.end(); } std::vector get_device_extensions(iris::instance* iris) { std::vector extensions; uint32_t count; vkEnumerateDeviceExtensionProperties(iris->physical_device, nullptr, &count, nullptr); extensions.resize(count); if (vkEnumerateDeviceExtensionProperties(iris->physical_device, nullptr, &count, extensions.data()) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to enumerate device extensions\n"); return {}; } return extensions; } std::vector get_device_layers(iris::instance* iris) { std::vector layers; uint32_t count; vkEnumerateDeviceLayerProperties(iris->physical_device, &count, nullptr); layers.resize(count); if (vkEnumerateDeviceLayerProperties(iris->physical_device, &count, layers.data()) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to enumerate device layers\n"); return {}; } return layers; } struct instance_create_info { std::vector enabled_extensions; std::vector enabled_layers; VkInstanceCreateFlags flags = 0; }; VkInstance create_instance(iris::instance* iris, const instance_create_info& info) { VkInstance instance = VK_NULL_HANDLE; for (const char* ext : info.enabled_extensions) { if (!is_instance_extension_supported(iris, ext)) { fprintf(stderr, "vulkan: Requested instance extension not supported: %s\n", ext); continue; } iris->enabled_instance_extensions.push_back(ext); } for (const char* layer : info.enabled_layers) { if (!is_instance_layer_supported(iris, layer)) { fprintf(stderr, "vulkan: Requested instance layer not supported: %s\n", layer); continue; } iris->enabled_instance_layers.push_back(layer); } iris->app_info.sType = VK_STRUCTURE_TYPE_APPLICATION_INFO; iris->app_info.pApplicationName = IRIS_TITLE; iris->app_info.applicationVersion = VK_MAKE_VERSION(1, 0, 0); iris->app_info.pEngineName = "Vulkan"; iris->app_info.engineVersion = VK_MAKE_VERSION(1, 1, 0); iris->app_info.apiVersion = IRIS_VULKAN_API_VERSION; iris->app_info.pNext = VK_NULL_HANDLE; iris->instance_create_info.sType = VK_STRUCTURE_TYPE_INSTANCE_CREATE_INFO; iris->instance_create_info.pApplicationInfo = &iris->app_info; iris->instance_create_info.enabledExtensionCount = iris->enabled_instance_extensions.size(); iris->instance_create_info.ppEnabledExtensionNames = iris->enabled_instance_extensions.data(); iris->instance_create_info.enabledLayerCount = iris->enabled_instance_layers.size(); iris->instance_create_info.ppEnabledLayerNames = iris->enabled_instance_layers.data(); iris->instance_create_info.flags = info.flags; if (vkCreateInstance(&iris->instance_create_info, nullptr, &instance) != VK_SUCCESS) { return VK_NULL_HANDLE; } return instance; } static inline uint32_t find_memory_type(iris::instance* iris, uint32_t filter, VkMemoryPropertyFlags properties) { VkPhysicalDeviceMemoryProperties mp; vkGetPhysicalDeviceMemoryProperties(iris->physical_device, &mp); for (uint32_t i = 0; i < mp.memoryTypeCount; i++) { if ((filter & (1 << i)) && (mp.memoryTypes[i].propertyFlags & properties) == properties) { return i; } } return 0; } struct device_create_info { std::vector enabled_extensions; std::vector enabled_layers; VkPhysicalDeviceFeatures enabled_features = {}; void* data; }; VkDevice create_device(iris::instance* iris, const device_create_info& info) { VkDevice device = VK_NULL_HANDLE; for (const char* ext : info.enabled_extensions) { if (!is_device_extension_supported(iris, ext)) { fprintf(stderr, "vulkan: Requested device extension not supported: %s\n", ext); continue; } iris->enabled_device_extensions.push_back(ext); } iris->cubic_supported = is_device_extension_supported(iris, VK_EXT_FILTER_CUBIC_EXTENSION_NAME); for (const char* layer : info.enabled_layers) { if (!is_device_layer_supported(iris, layer)) { fprintf(stderr, "vulkan: Requested device layer not supported: %s\n", layer); continue; } iris->enabled_device_layers.push_back(layer); } iris->device_features = {}; iris->device_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; iris->device_features.pNext = info.data; VkPhysicalDeviceFeatures2 supported_features = {}; supported_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_FEATURES_2; supported_features.pNext = nullptr; vkGetPhysicalDeviceFeatures2(iris->physical_device, &supported_features); #define SET_FEATURE(f) \ iris->device_features.features.f = (supported_features.features.f && info.enabled_features.f) SET_FEATURE(robustBufferAccess); SET_FEATURE(fullDrawIndexUint32); SET_FEATURE(imageCubeArray); SET_FEATURE(independentBlend); SET_FEATURE(geometryShader); SET_FEATURE(tessellationShader); SET_FEATURE(sampleRateShading); SET_FEATURE(dualSrcBlend); SET_FEATURE(logicOp); SET_FEATURE(multiDrawIndirect); SET_FEATURE(drawIndirectFirstInstance); SET_FEATURE(depthClamp); SET_FEATURE(depthBiasClamp); SET_FEATURE(fillModeNonSolid); SET_FEATURE(depthBounds); SET_FEATURE(wideLines); SET_FEATURE(largePoints); SET_FEATURE(alphaToOne); SET_FEATURE(multiViewport); SET_FEATURE(samplerAnisotropy); SET_FEATURE(textureCompressionETC2); SET_FEATURE(textureCompressionASTC_LDR); SET_FEATURE(textureCompressionBC); SET_FEATURE(occlusionQueryPrecise); SET_FEATURE(pipelineStatisticsQuery); SET_FEATURE(vertexPipelineStoresAndAtomics); SET_FEATURE(fragmentStoresAndAtomics); SET_FEATURE(shaderTessellationAndGeometryPointSize); SET_FEATURE(shaderImageGatherExtended); SET_FEATURE(shaderStorageImageExtendedFormats); SET_FEATURE(shaderStorageImageMultisample); SET_FEATURE(shaderStorageImageReadWithoutFormat); SET_FEATURE(shaderStorageImageWriteWithoutFormat); SET_FEATURE(shaderUniformBufferArrayDynamicIndexing); SET_FEATURE(shaderSampledImageArrayDynamicIndexing); SET_FEATURE(shaderStorageBufferArrayDynamicIndexing); SET_FEATURE(shaderStorageImageArrayDynamicIndexing); SET_FEATURE(shaderClipDistance); SET_FEATURE(shaderCullDistance); SET_FEATURE(shaderFloat64); SET_FEATURE(shaderInt64); SET_FEATURE(shaderInt16); SET_FEATURE(shaderResourceResidency); SET_FEATURE(shaderResourceMinLod); SET_FEATURE(sparseBinding); SET_FEATURE(sparseResidencyBuffer); SET_FEATURE(sparseResidencyImage2D); SET_FEATURE(sparseResidencyImage3D); SET_FEATURE(sparseResidency2Samples); SET_FEATURE(sparseResidency4Samples); SET_FEATURE(sparseResidency8Samples); SET_FEATURE(sparseResidency16Samples); SET_FEATURE(sparseResidencyAliased); SET_FEATURE(variableMultisampleRate); SET_FEATURE(inheritedQueries); #undef SET_FEATURE const float queue_priority[] = { 1.0f }; iris->queue_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_QUEUE_CREATE_INFO; iris->queue_create_info.queueFamilyIndex = iris->queue_family; iris->queue_create_info.queueCount = 1; iris->queue_create_info.pQueuePriorities = queue_priority; iris->device_create_info.sType = VK_STRUCTURE_TYPE_DEVICE_CREATE_INFO; iris->device_create_info.queueCreateInfoCount = 1; iris->device_create_info.pQueueCreateInfos = &iris->queue_create_info; iris->device_create_info.enabledExtensionCount = iris->enabled_device_extensions.size(); iris->device_create_info.ppEnabledExtensionNames = iris->enabled_device_extensions.data(); iris->device_create_info.enabledLayerCount = iris->enabled_device_layers.size(); iris->device_create_info.ppEnabledLayerNames = iris->enabled_device_layers.data(); iris->device_create_info.pEnabledFeatures = VK_NULL_HANDLE; iris->device_create_info.pNext = &iris->device_features; if (vkCreateDevice(iris->physical_device, &iris->device_create_info, nullptr, &device) != VK_SUCCESS) { return VK_NULL_HANDLE; } return device; } void enumerate_physical_devices(iris::instance* iris) { uint32_t count = 0; vkEnumeratePhysicalDevices(iris->instance, &count, nullptr); if (!count) { return; } std::vector devices(count); vkEnumeratePhysicalDevices(iris->instance, &count, devices.data()); iris->vulkan_gpus.clear(); for (const VkPhysicalDevice& device : devices) { VkPhysicalDeviceProperties properties; vkGetPhysicalDeviceProperties(device, &properties); iris::vulkan_gpu gpu; gpu.device = device; gpu.type = properties.deviceType; gpu.name = properties.deviceName; gpu.api_version = properties.apiVersion; iris->vulkan_gpus.push_back(gpu); } } VkPhysicalDevice find_suitable_physical_device(iris::instance* iris) { if (!iris->vulkan_gpus.size()) return VK_NULL_HANDLE; for (int i = 0; i < iris->vulkan_gpus.size(); i++) { auto& dev = iris->vulkan_gpus[i]; if (dev.type == VK_PHYSICAL_DEVICE_TYPE_DISCRETE_GPU) { iris->vulkan_selected_device_index = i; return dev.device; } } iris->vulkan_selected_device_index = 0; // Just pick the first device for now return iris->vulkan_gpus[0].device; } int find_graphics_queue_family_index(iris::instance* iris) { uint32_t count = 0; vkGetPhysicalDeviceQueueFamilyProperties(iris->physical_device, &count, nullptr); if (!count) { return -1; } std::vector queue_families(count); vkGetPhysicalDeviceQueueFamilyProperties(iris->physical_device, &count, queue_families.data()); // Just return the first graphics-capable queue family, we should // actually be looking for dedicated compute/transfer queues for (uint32_t i = 0; i < count; i++) { if (queue_families[i].queueFlags & VK_QUEUE_GRAPHICS_BIT) { return i; } } return -1; } VkBuffer create_buffer(iris::instance* iris, VkDeviceSize size, VkBufferUsageFlags usage, VkMemoryPropertyFlags properties, VkDeviceMemory& buffer_memory) { VkBuffer buffer = VK_NULL_HANDLE; VkBufferCreateInfo buffer_info = {}; buffer_info.sType = VK_STRUCTURE_TYPE_BUFFER_CREATE_INFO; buffer_info.size = size; buffer_info.usage = usage; buffer_info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; if (vkCreateBuffer(iris->device, &buffer_info, nullptr, &buffer) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to create buffer\n"); return VK_NULL_HANDLE; } VkMemoryRequirements memory_requirements; vkGetBufferMemoryRequirements(iris->device, buffer, &memory_requirements); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = memory_requirements.size; VkPhysicalDeviceMemoryProperties memory_properties; vkGetPhysicalDeviceMemoryProperties(iris->physical_device, &memory_properties); for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) { if ((memory_requirements.memoryTypeBits & (1 << i)) && (memory_properties.memoryTypes[i].propertyFlags & properties) == properties) { alloc_info.memoryTypeIndex = i; break; } } if (vkAllocateMemory(iris->device, &alloc_info, nullptr, &buffer_memory) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to allocate buffer memory\n"); vkDestroyBuffer(iris->device, buffer, nullptr); return VK_NULL_HANDLE; } vkBindBufferMemory(iris->device, buffer, buffer_memory, 0); return buffer; } void load_buffer(iris::instance* iris, VkDeviceMemory buffer_memory, void* data, VkDeviceSize size) { void* ptr; vkMapMemory(iris->device, buffer_memory, 0, size, 0, &ptr); memcpy(ptr, data, (size_t)size); vkUnmapMemory(iris->device, buffer_memory); } bool copy_buffer(iris::instance* iris, VkBuffer src, VkBuffer dst, VkDeviceSize size) { VkCommandPool command_pool = VK_NULL_HANDLE; VkCommandPoolCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; info.queueFamilyIndex = iris->queue_family; if (vkCreateCommandPool(iris->device, &info, VK_NULL_HANDLE, &command_pool) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to create command pool\n"); return false; } VkCommandBufferAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.commandPool = command_pool; alloc_info.commandBufferCount = 1; VkCommandBuffer command_buffer; vkAllocateCommandBuffers(iris->device, &alloc_info, &command_buffer); VkCommandBufferBeginInfo begin_info{}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; vkBeginCommandBuffer(command_buffer, &begin_info); VkBufferCopy copy_region{}; copy_region.size = size; vkCmdCopyBuffer(command_buffer, src, dst, 1, ©_region); vkEndCommandBuffer(command_buffer); VkSubmitInfo submit_info{}; submit_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; submit_info.commandBufferCount = 1; submit_info.pCommandBuffers = &command_buffer; vkQueueSubmit(iris->queue, 1, &submit_info, VK_NULL_HANDLE); vkQueueWaitIdle(iris->queue); vkFreeCommandBuffers(iris->device, command_pool, 1, &command_buffer); vkDestroyCommandPool(iris->device, command_pool, VK_NULL_HANDLE); return true; } bool create_descriptor_pool(iris::instance* iris) { VkDescriptorPoolSize pool_sizes[] = { { VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER, 512 } }; VkDescriptorPoolCreateInfo pool_info = {}; pool_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_POOL_CREATE_INFO; pool_info.flags = VK_DESCRIPTOR_POOL_CREATE_FREE_DESCRIPTOR_SET_BIT; pool_info.maxSets = 0; for (VkDescriptorPoolSize& pool_size : pool_sizes) pool_info.maxSets += pool_size.descriptorCount; pool_info.poolSizeCount = (uint32_t)IM_ARRAYSIZE(pool_sizes); pool_info.pPoolSizes = pool_sizes; if (vkCreateDescriptorPool(iris->device, &pool_info, VK_NULL_HANDLE, &iris->descriptor_pool) != VK_SUCCESS) { fprintf(stderr, "imgui: Failed to create descriptor pool\n"); return false; } return true; } texture upload_texture(iris::instance* iris, void* pixels, int width, int height, int stride) { texture tex = {}; tex.width = width; tex.height = height; tex.stride = stride; tex.image_size = width * height * 4; VkDeviceMemory staging_buffer_memory = VK_NULL_HANDLE; VkBuffer staging_buffer = create_buffer( iris, tex.image_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, staging_buffer_memory ); if (staging_buffer == VK_NULL_HANDLE) return {}; load_buffer(iris, staging_buffer_memory, pixels, tex.image_size); // To-do: Transition image layout and copy buffer to image // Create the Vulkan image. { VkImageCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_CREATE_INFO; info.imageType = VK_IMAGE_TYPE_2D; info.format = VK_FORMAT_R8G8B8A8_UNORM; info.extent.width = width; info.extent.height = height; info.extent.depth = 1; info.mipLevels = 1; info.arrayLayers = 1; info.samples = VK_SAMPLE_COUNT_1_BIT; info.tiling = VK_IMAGE_TILING_OPTIMAL; info.usage = VK_IMAGE_USAGE_SAMPLED_BIT | VK_IMAGE_USAGE_TRANSFER_DST_BIT; info.sharingMode = VK_SHARING_MODE_EXCLUSIVE; info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; if (vkCreateImage(iris->device, &info, VK_NULL_HANDLE, &tex.image) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to create image\n"); return {}; } VkMemoryRequirements req; vkGetImageMemoryRequirements(iris->device, tex.image, &req); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; VkPhysicalDeviceMemoryProperties memory_properties; vkGetPhysicalDeviceMemoryProperties(iris->physical_device, &memory_properties); for (uint32_t i = 0; i < memory_properties.memoryTypeCount; i++) { if ((req.memoryTypeBits & (1 << i)) && (memory_properties.memoryTypes[i].propertyFlags & VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) == VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT) { alloc_info.memoryTypeIndex = i; break; } } if (vkAllocateMemory(iris->device, &alloc_info, VK_NULL_HANDLE, &tex.image_memory) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to allocate image memory\n"); return {}; } if (vkBindImageMemory(iris->device, tex.image, tex.image_memory, 0) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to bind image memory\n"); return {}; } } // Create the Image View { VkImageViewCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_IMAGE_VIEW_CREATE_INFO; info.image = tex.image; info.viewType = VK_IMAGE_VIEW_TYPE_2D; info.format = VK_FORMAT_R8G8B8A8_UNORM; info.subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; info.subresourceRange.levelCount = 1; info.subresourceRange.layerCount = 1; if (vkCreateImageView(iris->device, &info, VK_NULL_HANDLE, &tex.image_view) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to create image view\n"); return {}; } } // Create Sampler { VkSamplerCreateInfo sampler_info{}; sampler_info.sType = VK_STRUCTURE_TYPE_SAMPLER_CREATE_INFO; sampler_info.magFilter = VK_FILTER_LINEAR; sampler_info.minFilter = VK_FILTER_LINEAR; sampler_info.mipmapMode = VK_SAMPLER_MIPMAP_MODE_LINEAR; sampler_info.addressModeU = VK_SAMPLER_ADDRESS_MODE_REPEAT; // outside image bounds just use border color sampler_info.addressModeV = VK_SAMPLER_ADDRESS_MODE_REPEAT; sampler_info.addressModeW = VK_SAMPLER_ADDRESS_MODE_REPEAT; sampler_info.minLod = -1000; sampler_info.maxLod = 1000; sampler_info.maxAnisotropy = 1.0f; if (vkCreateSampler(iris->device, &sampler_info, VK_NULL_HANDLE, &tex.sampler) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to create sampler\n"); return {}; } } { VkDescriptorSetAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_DESCRIPTOR_SET_ALLOCATE_INFO; alloc_info.descriptorPool = iris->descriptor_pool; alloc_info.descriptorSetCount = 1; alloc_info.pSetLayouts = &iris->descriptor_set_layout; if (vkAllocateDescriptorSets(iris->device, &alloc_info, &tex.descriptor_set) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to allocate descriptor sets\n"); return {}; } } // Update the Descriptor Set: { VkDescriptorImageInfo desc_image[1] = {}; desc_image[0].sampler = tex.sampler; desc_image[0].imageView = tex.image_view; desc_image[0].imageLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; VkWriteDescriptorSet write_desc[1] = {}; write_desc[0].sType = VK_STRUCTURE_TYPE_WRITE_DESCRIPTOR_SET; write_desc[0].dstSet = tex.descriptor_set; write_desc[0].descriptorCount = 1; write_desc[0].descriptorType = VK_DESCRIPTOR_TYPE_COMBINED_IMAGE_SAMPLER; write_desc[0].pImageInfo = desc_image; vkUpdateDescriptorSets(iris->device, 1, write_desc, 0, nullptr); } VkCommandPool command_pool = VK_NULL_HANDLE; VkCommandPoolCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; info.queueFamilyIndex = iris->queue_family; if (vkCreateCommandPool(iris->device, &info, VK_NULL_HANDLE, &command_pool) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to create command pool\n"); return {}; } VkCommandBuffer command_buffer; { VkCommandBufferAllocateInfo alloc_info{}; alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; alloc_info.commandPool = command_pool; alloc_info.commandBufferCount = 1; if (vkAllocateCommandBuffers(iris->device, &alloc_info, &command_buffer) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to allocate command buffers\n"); return {}; } VkCommandBufferBeginInfo begin_info = {}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.flags |= VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) { printf("vulkan: Failed to begin command buffer\n"); return {}; } } // Copy to Image { VkImageMemoryBarrier copy_barrier[1] = {}; copy_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; copy_barrier[0].dstAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; copy_barrier[0].oldLayout = VK_IMAGE_LAYOUT_UNDEFINED; copy_barrier[0].newLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; copy_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; copy_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; copy_barrier[0].image = tex.image; copy_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; copy_barrier[0].subresourceRange.levelCount = 1; copy_barrier[0].subresourceRange.layerCount = 1; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_HOST_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, 0, 0, NULL, 0, NULL, 1, copy_barrier); VkBufferImageCopy region = {}; region.imageSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; region.imageSubresource.layerCount = 1; region.imageExtent.width = width; region.imageExtent.height = height; region.imageExtent.depth = 1; vkCmdCopyBufferToImage(command_buffer, staging_buffer, tex.image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, ®ion); VkImageMemoryBarrier use_barrier[1] = {}; use_barrier[0].sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; use_barrier[0].srcAccessMask = VK_ACCESS_TRANSFER_WRITE_BIT; use_barrier[0].dstAccessMask = VK_ACCESS_SHADER_READ_BIT; use_barrier[0].oldLayout = VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL; use_barrier[0].newLayout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; use_barrier[0].srcQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; use_barrier[0].dstQueueFamilyIndex = VK_QUEUE_FAMILY_IGNORED; use_barrier[0].image = tex.image; use_barrier[0].subresourceRange.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; use_barrier[0].subresourceRange.levelCount = 1; use_barrier[0].subresourceRange.layerCount = 1; vkCmdPipelineBarrier(command_buffer, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT, 0, 0, NULL, 0, NULL, 1, use_barrier); } // End command buffer { VkSubmitInfo end_info = {}; end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; end_info.commandBufferCount = 1; end_info.pCommandBuffers = &command_buffer; if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to end command buffer\n"); return {}; } if (vkQueueSubmit(iris->queue, 1, &end_info, VK_NULL_HANDLE) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to submit queue\n"); return {}; } if (vkDeviceWaitIdle(iris->device) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to wait device idle\n"); return {}; } } vkDestroyCommandPool(iris->device, command_pool, nullptr); vkDestroyBuffer(iris->device, staging_buffer, nullptr); vkFreeMemory(iris->device, staging_buffer_memory, nullptr); return tex; } void free_texture(iris::instance* iris, texture& tex) { if (!iris->device) return; if (tex.sampler) vkDestroySampler(iris->device, tex.sampler, nullptr); if (tex.image_view) vkDestroyImageView(iris->device, tex.image_view, nullptr); if (tex.image) vkDestroyImage(iris->device, tex.image, nullptr); if (tex.image_memory) vkFreeMemory(iris->device, tex.image_memory, nullptr); } bool init(iris::instance* iris, bool enable_validation) { if (volkInitialize() != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to initialize volk loader\n"); return false; } iris->instance_extensions = get_instance_extensions(); iris->instance_layers = get_instance_layers(); std::vector extensions = { VK_KHR_GET_PHYSICAL_DEVICE_PROPERTIES_2_EXTENSION_NAME }; std::vector layers; if (enable_validation) { layers.push_back("VK_LAYER_KHRONOS_validation"); } // Push SDL extensions uint32_t sdl_extension_count; auto sdl_extensions = SDL_Vulkan_GetInstanceExtensions(&sdl_extension_count); for (uint32_t i = 0; i < sdl_extension_count; i++) { extensions.push_back(sdl_extensions[i]); } VkInstanceCreateFlags flags = 0; // Needed for MoltenVK on macOS #if defined(__APPLE__) && defined(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME) extensions.push_back(VK_KHR_PORTABILITY_ENUMERATION_EXTENSION_NAME); flags |= VK_INSTANCE_CREATE_ENUMERATE_PORTABILITY_BIT_KHR; #endif instance_create_info instance_info = {}; instance_info.enabled_extensions = extensions; instance_info.enabled_layers = layers; instance_info.flags = flags; iris->instance = create_instance(iris, instance_info); if (!iris->instance) { fprintf(stderr, "vulkan: Failed to create Vulkan instance\n"); return false; } volkLoadInstance(iris->instance); // Find a suitable Vulkan physical device (GPU) enumerate_physical_devices(iris); iris->vulkan_selected_device_index = 0; if (iris->vulkan_physical_device < 0) { iris->physical_device = find_suitable_physical_device(iris); } else { if (iris->vulkan_physical_device >= iris->vulkan_gpus.size()) { iris->physical_device = find_suitable_physical_device(iris); iris->vulkan_physical_device = iris->vulkan_selected_device_index; } else { iris->physical_device = iris->vulkan_gpus[iris->vulkan_physical_device].device; iris->vulkan_selected_device_index = iris->vulkan_physical_device; } } if (!iris->physical_device) { fprintf(stderr, "vulkan: Failed to find a suitable Vulkan device\n"); return false; } VkPhysicalDeviceProperties properties; vkGetPhysicalDeviceProperties(iris->physical_device, &properties); printf("vulkan: Using Vulkan device \"%s\". API version %d.%d.%d.%d Driver version %x\n", properties.deviceName, VK_API_VERSION_MAJOR(properties.apiVersion), VK_API_VERSION_MINOR(properties.apiVersion), VK_API_VERSION_PATCH(properties.apiVersion), VK_API_VERSION_VARIANT(properties.apiVersion), properties.driverVersion ); iris->device_extensions = get_device_extensions(iris); iris->device_layers = get_device_layers(iris); // Find a graphics-capable queue family int queue_family = find_graphics_queue_family_index(iris); if (queue_family == -1) { fprintf(stderr, "vulkan: Failed to find a graphics-capable queue family\n"); return false; } iris->queue_family = queue_family; // To-do: Query required extensions/features from backends here. // For now we'll just initialize a fixed set of extensions // and features. device_create_info device_info = {}; device_info.enabled_extensions = { VK_KHR_SWAPCHAIN_EXTENSION_NAME, VK_EXT_DESCRIPTOR_INDEXING_EXTENSION_NAME, VK_KHR_TIMELINE_SEMAPHORE_EXTENSION_NAME, VK_KHR_8BIT_STORAGE_EXTENSION_NAME, // VK_KHR_16BIT_STORAGE_EXTENSION_NAME, VK_KHR_SHADER_FLOAT16_INT8_EXTENSION_NAME, VK_EXT_SCALAR_BLOCK_LAYOUT_EXTENSION_NAME, VK_KHR_PUSH_DESCRIPTOR_EXTENSION_NAME, VK_EXT_DESCRIPTOR_BUFFER_EXTENSION_NAME, VK_EXT_SHADER_SUBGROUP_VOTE_EXTENSION_NAME, VK_EXT_SHADER_SUBGROUP_BALLOT_EXTENSION_NAME, VK_EXT_SUBGROUP_SIZE_CONTROL_EXTENSION_NAME, VK_EXT_INDEX_TYPE_UINT8_EXTENSION_NAME, VK_KHR_SYNCHRONIZATION_2_EXTENSION_NAME, VK_KHR_CREATE_RENDERPASS_2_EXTENSION_NAME, // VK_KHR_MAINTENANCE_5_EXTENSION_NAME, VK_EXT_MEMORY_PRIORITY_EXTENSION_NAME, VK_EXT_MEMORY_BUDGET_EXTENSION_NAME, VK_EXT_PAGEABLE_DEVICE_LOCAL_MEMORY_EXTENSION_NAME, // VK_EXT_MESH_SHADER_EXTENSION_NAME, VK_EXT_EXTERNAL_MEMORY_HOST_EXTENSION_NAME, VK_KHR_LOAD_STORE_OP_NONE_EXTENSION_NAME, VK_KHR_BUFFER_DEVICE_ADDRESS_EXTENSION_NAME, VK_EXT_FILTER_CUBIC_EXTENSION_NAME }; device_info.enabled_layers = {}; #ifdef VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME device_info.enabled_extensions.push_back(VK_KHR_PORTABILITY_SUBSET_EXTENSION_NAME); #endif device_info.enabled_features = {}; device_info.enabled_features.shaderInt16 = VK_TRUE; iris->vulkan_11_features.pNext = &iris->vulkan_12_features; iris->vulkan_12_features.pNext = &iris->subgroup_size_control_features; iris->subgroup_size_control_features.pNext = VK_NULL_HANDLE; iris->vulkan_11_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_1_FEATURES; iris->vulkan_12_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_VULKAN_1_2_FEATURES; iris->subgroup_size_control_features.sType = VK_STRUCTURE_TYPE_PHYSICAL_DEVICE_SUBGROUP_SIZE_CONTROL_FEATURES; iris->vulkan_11_features.storageBuffer16BitAccess = VK_TRUE; iris->vulkan_11_features.uniformAndStorageBuffer16BitAccess = VK_TRUE; iris->vulkan_12_features.descriptorIndexing = VK_TRUE; iris->vulkan_12_features.descriptorBindingPartiallyBound = VK_TRUE; iris->vulkan_12_features.descriptorBindingVariableDescriptorCount = VK_TRUE; iris->vulkan_12_features.descriptorBindingSampledImageUpdateAfterBind = VK_TRUE; iris->vulkan_12_features.runtimeDescriptorArray = VK_TRUE; iris->vulkan_12_features.timelineSemaphore = VK_TRUE; iris->vulkan_12_features.bufferDeviceAddress = VK_TRUE; iris->vulkan_12_features.scalarBlockLayout = VK_TRUE; iris->vulkan_12_features.storageBuffer8BitAccess = VK_TRUE; iris->vulkan_12_features.uniformAndStorageBuffer8BitAccess = VK_TRUE; iris->subgroup_size_control_features.subgroupSizeControl = VK_TRUE; iris->subgroup_size_control_features.computeFullSubgroups = VK_TRUE; // Chain in all feature structs device_info.data = &iris->vulkan_11_features; iris->device = create_device(iris, device_info); if (!iris->device) { fprintf(stderr, "vulkan: Failed to create Vulkan device\n"); return false; } vkGetDeviceQueue(iris->device, iris->queue_family, 0, &iris->queue); iris->indices = { 0, 1, 2, 2, 3, 0 }; iris->vertex_buffer_size = sizeof(vertex) * iris->vertices.size(); // Create vertex and index buffers // Create and populate index buffer immediately by creating // a staging buffer, filling it, and then copying it over to // the device local index buffer. // The vertex buffer will be updated dynamically each frame. VkDeviceMemory index_staging_buffer_memory; VkDeviceSize index_buffer_size = sizeof(uint16_t) * iris->indices.size(); iris->index_buffer = create_buffer( iris, sizeof(uint16_t) * iris->indices.size(), VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_INDEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, iris->index_buffer_memory ); VkBuffer index_staging_buffer = create_buffer( iris, index_buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, index_staging_buffer_memory ); load_buffer(iris, index_staging_buffer_memory, iris->indices.data(), index_buffer_size); copy_buffer(iris, index_staging_buffer, iris->index_buffer, index_buffer_size); iris->vertex_buffer = create_buffer( iris, iris->vertex_buffer_size, VK_BUFFER_USAGE_TRANSFER_DST_BIT | VK_BUFFER_USAGE_VERTEX_BUFFER_BIT, VK_MEMORY_PROPERTY_DEVICE_LOCAL_BIT, iris->vertex_buffer_memory ); iris->vertex_staging_buffer = create_buffer( iris, iris->vertex_buffer_size, VK_BUFFER_USAGE_TRANSFER_SRC_BIT, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT, iris->vertex_staging_buffer_memory ); // We don't need the staging buffer anymore vkFreeMemory(iris->device, index_staging_buffer_memory, nullptr); vkDestroyBuffer(iris->device, index_staging_buffer, nullptr); create_descriptor_pool(iris); return true; } static int ic = 0; void cleanup(iris::instance* iris) { vulkan::wait_idle(iris); if (iris->descriptor_set_layout) vkDestroyDescriptorSetLayout(iris->device, iris->descriptor_set_layout, nullptr); if (iris->descriptor_pool) vkDestroyDescriptorPool(iris->device, iris->descriptor_pool, nullptr); for (int i = 0; i < 3; i++) if (iris->sampler[i]) vkDestroySampler(iris->device, iris->sampler[i], nullptr); if (iris->vertex_buffer) vkDestroyBuffer(iris->device, iris->vertex_buffer, nullptr); if (iris->vertex_staging_buffer) vkDestroyBuffer(iris->device, iris->vertex_staging_buffer, nullptr); if (iris->index_buffer) vkDestroyBuffer(iris->device, iris->index_buffer, nullptr); if (iris->vertex_staging_buffer_memory) vkFreeMemory(iris->device, iris->vertex_staging_buffer_memory, nullptr); if (iris->vertex_buffer_memory) vkFreeMemory(iris->device, iris->vertex_buffer_memory, nullptr); if (iris->index_buffer_memory) vkFreeMemory(iris->device, iris->index_buffer_memory, nullptr); if (iris->pipeline) vkDestroyPipeline(iris->device, iris->pipeline, nullptr); // ImGui takes care of this apparently (probably shouldn't) // if (iris->surface) vkDestroySurfaceKHR(iris->instance, iris->surface, nullptr); if (iris->render_pass) vkDestroyRenderPass(iris->device, iris->render_pass, nullptr); if (iris->pipeline_layout) vkDestroyPipelineLayout(iris->device, iris->pipeline_layout, nullptr); if (iris->device) vkDestroyDevice(iris->device, nullptr); if (iris->instance) vkDestroyInstance(iris->instance, nullptr); } void insert_image_memory_barrier( VkCommandBuffer buffer, VkImage image, VkAccessFlags src_access_mask, VkAccessFlags dst_access_mask, VkImageLayout old_image_layout, VkImageLayout new_image_layout, VkPipelineStageFlags src_stage_mask, VkPipelineStageFlags dst_stage_mask, VkImageSubresourceRange subresource_range) { VkImageMemoryBarrier image_memory_barrier = {}; image_memory_barrier.sType = VK_STRUCTURE_TYPE_IMAGE_MEMORY_BARRIER; image_memory_barrier.srcAccessMask = src_access_mask; image_memory_barrier.dstAccessMask = dst_access_mask; image_memory_barrier.oldLayout = old_image_layout; image_memory_barrier.newLayout = new_image_layout; image_memory_barrier.image = image; image_memory_barrier.subresourceRange = subresource_range; vkCmdPipelineBarrier( buffer, src_stage_mask, dst_stage_mask, 0, 0, nullptr, 0, nullptr, 1, &image_memory_barrier ); } void* read_image(iris::instance* iris, VkImage src_image, VkFormat format, int width, int height) { if (src_image == VK_NULL_HANDLE) { printf("vulkan: Source image is null\n"); return nullptr; } if (!width || !height) { printf("vulkan: Invalid image dimensions for readback (%dx%d)\n", width, height); return nullptr; } bool supports_blit = true; // Check blit support for source and destination VkFormatProperties format_props; // Check if the device supports blitting from optimal images (the swapchain images are in optimal format) vkGetPhysicalDeviceFormatProperties(iris->physical_device, format, &format_props); if (!(format_props.optimalTilingFeatures & VK_FORMAT_FEATURE_BLIT_SRC_BIT)) { printf("Device does not support blitting from optimal tiled images, using copy instead of blit!\n"); supports_blit = false; } // Check if the device supports blitting to linear images vkGetPhysicalDeviceFormatProperties(iris->physical_device, VK_FORMAT_R8G8B8A8_UNORM, &format_props); if (!(format_props.linearTilingFeatures & VK_FORMAT_FEATURE_BLIT_DST_BIT)) { printf("Device does not support blitting to linear tiled images, using copy instead of blit!\n"); supports_blit = false; } // Create the linear tiled destination image to copy to and to read the memory from VkImageCreateInfo image_create_info = {}; image_create_info.imageType = VK_IMAGE_TYPE_2D; image_create_info.format = VK_FORMAT_R8G8B8A8_UNORM; image_create_info.extent.width = width; image_create_info.extent.height = height; image_create_info.extent.depth = 1; image_create_info.arrayLayers = 1; image_create_info.mipLevels = 1; image_create_info.initialLayout = VK_IMAGE_LAYOUT_UNDEFINED; image_create_info.samples = VK_SAMPLE_COUNT_1_BIT; image_create_info.tiling = VK_IMAGE_TILING_LINEAR; image_create_info.usage = VK_IMAGE_USAGE_TRANSFER_DST_BIT; // Create the image VkImage dst_image; if (vkCreateImage(iris->device, &image_create_info, nullptr, &dst_image) != VK_SUCCESS) { printf("Failed to create image for readback\n"); return nullptr; } VkDeviceMemory dst_image_memory; VkMemoryRequirements req; vkGetImageMemoryRequirements(iris->device, dst_image, &req); VkMemoryAllocateInfo alloc_info = {}; alloc_info.sType = VK_STRUCTURE_TYPE_MEMORY_ALLOCATE_INFO; alloc_info.allocationSize = req.size; alloc_info.memoryTypeIndex = find_memory_type(iris, req.memoryTypeBits, VK_MEMORY_PROPERTY_HOST_VISIBLE_BIT | VK_MEMORY_PROPERTY_HOST_COHERENT_BIT); if (vkAllocateMemory(iris->device, &alloc_info, VK_NULL_HANDLE, &dst_image_memory) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to allocate image memory for readback\n"); return {}; } if (vkBindImageMemory(iris->device, dst_image, dst_image_memory, 0) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to bind image memory for readback\n"); return {}; } // Do the actual blit from the swapchain image to our host visible destination image VkCommandPool command_pool = VK_NULL_HANDLE; VkCommandPoolCreateInfo info = {}; info.sType = VK_STRUCTURE_TYPE_COMMAND_POOL_CREATE_INFO; info.flags = VK_COMMAND_POOL_CREATE_TRANSIENT_BIT; info.queueFamilyIndex = iris->queue_family; if (vkCreateCommandPool(iris->device, &info, VK_NULL_HANDLE, &command_pool) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to create command pool for readback\n"); return {}; } VkCommandBufferAllocateInfo cmd_buffer_alloc_info = {}; cmd_buffer_alloc_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_ALLOCATE_INFO; cmd_buffer_alloc_info.level = VK_COMMAND_BUFFER_LEVEL_PRIMARY; cmd_buffer_alloc_info.commandPool = command_pool; cmd_buffer_alloc_info.commandBufferCount = 1; VkCommandBuffer command_buffer; vkAllocateCommandBuffers(iris->device, &cmd_buffer_alloc_info, &command_buffer); VkCommandBufferBeginInfo begin_info{}; begin_info.sType = VK_STRUCTURE_TYPE_COMMAND_BUFFER_BEGIN_INFO; begin_info.flags = VK_COMMAND_BUFFER_USAGE_ONE_TIME_SUBMIT_BIT; if (vkBeginCommandBuffer(command_buffer, &begin_info) != VK_SUCCESS) { printf("vulkan: Failed to begin command buffer for readback\n"); return {}; } // Transition destination image to transfer destination layout insert_image_memory_barrier( command_buffer, dst_image, 0, VK_ACCESS_TRANSFER_WRITE_BIT, VK_IMAGE_LAYOUT_UNDEFINED, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } ); // Transition swapchain image from present to transfer source layout insert_image_memory_barrier( command_buffer, src_image, VK_ACCESS_MEMORY_READ_BIT, VK_ACCESS_TRANSFER_READ_BIT, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } ); // If source and destination support blit we'll blit as this also does automatic format conversion (e.g. from BGR to RGB) if (supports_blit) { // Define the region to blit (we will blit the whole swapchain image) VkOffset3D blitSize; blitSize.x = width; blitSize.y = height; blitSize.z = 1; VkImageBlit imageBlitRegion{}; imageBlitRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBlitRegion.srcSubresource.layerCount = 1; imageBlitRegion.srcOffsets[1] = blitSize; imageBlitRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageBlitRegion.dstSubresource.layerCount = 1; imageBlitRegion.dstOffsets[1] = blitSize; // Issue the blit command vkCmdBlitImage( command_buffer, src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageBlitRegion, VK_FILTER_NEAREST ); } else { // Otherwise use image copy (requires us to manually flip components) VkImageCopy imageCopyRegion = {}; imageCopyRegion.srcSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageCopyRegion.srcSubresource.layerCount = 1; imageCopyRegion.dstSubresource.aspectMask = VK_IMAGE_ASPECT_COLOR_BIT; imageCopyRegion.dstSubresource.layerCount = 1; imageCopyRegion.extent.width = width; imageCopyRegion.extent.height = height; imageCopyRegion.extent.depth = 1; // Issue the copy command vkCmdCopyImage( command_buffer, src_image, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, dst_image, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, 1, &imageCopyRegion ); } // Transition destination image to general layout, which is the required layout for mapping the image memory later on insert_image_memory_barrier( command_buffer, dst_image, VK_ACCESS_TRANSFER_WRITE_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_DST_OPTIMAL, VK_IMAGE_LAYOUT_GENERAL, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } ); // Transition back the swap chain image after the blit is done insert_image_memory_barrier( command_buffer, src_image, VK_ACCESS_TRANSFER_READ_BIT, VK_ACCESS_MEMORY_READ_BIT, VK_IMAGE_LAYOUT_TRANSFER_SRC_OPTIMAL, VK_IMAGE_LAYOUT_PRESENT_SRC_KHR, VK_PIPELINE_STAGE_TRANSFER_BIT, VK_PIPELINE_STAGE_TRANSFER_BIT, VkImageSubresourceRange{ VK_IMAGE_ASPECT_COLOR_BIT, 0, 1, 0, 1 } ); // End command buffer { VkSubmitInfo end_info = {}; end_info.sType = VK_STRUCTURE_TYPE_SUBMIT_INFO; end_info.commandBufferCount = 1; end_info.pCommandBuffers = &command_buffer; if (vkEndCommandBuffer(command_buffer) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to end command buffer\n"); return {}; } if (vkQueueSubmit(iris->queue, 1, &end_info, VK_NULL_HANDLE) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to submit queue\n"); return {}; } if (vkDeviceWaitIdle(iris->device) != VK_SUCCESS) { fprintf(stderr, "vulkan: Failed to wait device idle\n"); return {}; } } vkDestroyCommandPool(iris->device, command_pool, nullptr); // Get layout of the image (including row pitch) VkImageSubresource subresource { VK_IMAGE_ASPECT_COLOR_BIT, 0, 0 }; VkSubresourceLayout subresource_layout; vkGetImageSubresourceLayout(iris->device, dst_image, &subresource, &subresource_layout); // Map image memory so we can start copying from it const char* data; vkMapMemory(iris->device, dst_image_memory, 0, VK_WHOLE_SIZE, 0, (void**)&data); data += subresource_layout.offset; void* buf = malloc(width * height * 4); memcpy(buf, data, width * height * 4); // Clean up resources vkUnmapMemory(iris->device, dst_image_memory); vkFreeMemory(iris->device, dst_image_memory, nullptr); vkDestroyImage(iris->device, dst_image, nullptr); return buf; } void wait_idle(iris::instance* iris) { if (iris->device) { vkDeviceWaitIdle(iris->device); } else if (iris->queue) { vkQueueWaitIdle(iris->queue); } } } ================================================ FILE: main.cpp ================================================ // Standard includes #include #include #include #include // Iris includes #include "iris.hpp" #include "config.hpp" #include "ee/ee_def.hpp" #include "ee/vu_def.hpp" // ImGui includes #include "imgui.h" #include "imgui_impl_sdl3.h" #include "imgui_impl_vulkan.h" #include "implot.h" // SDL3 includes #include // stb_image stuff #define STB_IMAGE_IMPLEMENTATION #include "stb_image.h" #define SDL_MAIN_USE_CALLBACKS #include SDL_AppResult SDL_AppInit(void** appstate, int argc, char** argv) { SDL_SetAppMetadata("Iris", STR(_IRIS_VERSION), "com.allkern.iris"); // Check if we got --help or --version in the commandline args // if so, don't do anything else. if (iris::settings::check_for_quick_exit(argc, (const char**)argv)) { return SDL_APP_SUCCESS; } iris::instance* iris = iris::create(); if (!iris::init(iris, argc, (const char**)argv)) { fprintf(stderr, "iris: Failed to initialize instance\n"); return SDL_APP_FAILURE; } // Initialize appstate *appstate = iris; return SDL_APP_CONTINUE; } SDL_AppResult SDL_AppIterate(void* appstate) { iris::instance* iris = (iris::instance*)appstate; return iris::update(iris); } SDL_AppResult SDL_AppEvent(void* appstate, SDL_Event* event) { iris::instance* iris = (iris::instance*)appstate; return iris::handle_events(iris, event); } void SDL_AppQuit(void* appstate, SDL_AppResult result) { iris::instance* iris = (iris::instance*)appstate; iris::destroy(iris); } ================================================ FILE: res/iris.rc ================================================ id ICON "iris.ico" 1 VERSIONINFO FILEVERSION 1,0,0,0 PRODUCTVERSION 1,0,0,0 BEGIN BLOCK "StringFileInfo" BEGIN BLOCK "080904E4" BEGIN VALUE "CompanyName", "Allkern" VALUE "FileDescription", "An experimental PlayStation 2 Emulator" VALUE "FileVersion", "0.10" VALUE "ProductName", "Iris" VALUE "ProductVersion", "0.10" END END END ================================================ FILE: shaders/curvature.frag ================================================ #version 460 layout (location = 0) in vec2 TexCoord; layout (location = 0) out vec4 FragColor; layout (binding = 0) uniform sampler2D input_tex; layout(push_constant) uniform constants { vec2 resolution; int frame; } PushConstants; #define PI 3.14159265358979323846 #define FIR_WIDTH 1 vec2 curvature(vec2 coord, float bend) { // put in symmetrical coords coord = (coord - 0.5) * 2.0; coord *= 1.1; // deform coords coord.x *= 1.0 + pow((abs(coord.y) / bend), 2.0); coord.y *= 1.0 + pow((abs(coord.x) / bend), 2.0); // transform back to 0.0 - 1.0 space coord = (coord / 2.0) + 0.5; return coord; } const float a0 = 0.215578950; const float a1 = 0.416631580; const float a2 = 0.277263158; const float a3 = 0.083578947; const float a4 = 0.006947368; float flat_top(float x, float 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)); } const float freq = 9.0; float sinc(float x) { return sin(freq * PI * (x - 0.5)) / (freq * PI * (x - 0.5)); } void main() { vec2 uv = curvature(TexCoord, 5.5); if (uv.x < 0.0 || uv.x > 1.0 || uv.y < 0.0 || uv.y > 1.0) { FragColor = vec4(0.0); return; } FragColor = texture(input_tex, uv).xyzw; } ================================================ FILE: shaders/decoder.frag ================================================ #version 460 layout (location = 0) in vec2 TexCoord; layout (location = 0) out vec4 FragColor; layout (binding = 0) uniform sampler2D input_tex; layout(push_constant) uniform constants { vec2 resolution; int frame; } PushConstants; // Decoder or Demodulator // This pass takes the Composite signal generated on Buffer B // and decodes it #define PI 3.14159265358979323846 #define TAU 6.28318530717958647693 #define BRIGHTNESS_FACTOR 2.0 // The decoded IQ signals get multiplied by this // factor. Bigger values yield more color saturation #define CHROMA_SATURATION_FACTOR 2.0 // Size of the decoding FIR filter. bigger values // yield more smudgy video and are more expensive #define CHROMA_DECODER_FIR_SIZE 8 #define LUMA_DECODER_FIR_SIZE 4 float blackman(float n, float N) { return 0.42 - 0.5 * cos((2.0 * PI * n) / (N - 1.0)) + 0.08 * cos((4.0 * PI * n) / (N - 1.0)); } float sinc(float x) { if (x == 0.0) return 1.0; float pix = PI * x; return sin(pix) / pix; } float sinc_f(float cutoff, float n, float N) { float cut2 = cutoff * 2.0; return cut2 * sinc(cut2 * (n - ((N - 1.0) / 2.0))) * 48.0; } float lowpass(float cutoff, float n, float N) { return (sinc_f(cutoff, n, N) * 2.0 - 2.0) * blackman(n, N); } float highpass(float cutoff, float n, float N) { return -(sinc_f(cutoff, n, N) * blackman(n, N)); } // YIQ to RGB matrix const mat3 yiq_to_rgb = mat3( 1.000, 1.000, 1.000, 0.956, -0.272, -1.106, 0.621, -0.647, 1.703 ); void main() { vec2 uv = TexCoord * PushConstants.resolution; // Chroma decoder oscillator frequency float fc = 128.0; float counter = 0.0; // Sum and decode NTSC samples // This is essentially a simple averaging filter // that happens to be weighted by two cos and sin // oscillators at a very specific frequency vec3 yiq = vec3(0.0); // Decode Luma first for (int d = 0; d < 51; d++) { vec2 p = vec2(uv.x + float(d) - 26.0, uv.y); vec3 s = texture(input_tex, p / PushConstants.resolution).rgb; float filt = lowpass(1.0/6.0, float(d), 51.0); yiq.x += s.x * filt; } yiq.x /= 51.0; // Then decode chroma for (int d = 0; d < 51; d++) { vec2 p = vec2(uv.x + float(d) - 26.0, uv.y); vec3 s = texture(input_tex, p / PushConstants.resolution).rgb; float t = fc * (uv.x + float(d) - 26.0) + ((PI / 2.0) * uv.y) + (PI * float((PushConstants.frame >> 1) & 1)); // Apply Blackman window for smoother colors float filt = -highpass(0.25, float(d), 51.0); yiq.yz += s.yz * filt * vec2(cos(t), sin(t)); } yiq.yz /= 51.0; yiq.yz *= CHROMA_SATURATION_FACTOR; FragColor = vec4((yiq_to_rgb * yiq), 1.0); } ================================================ FILE: shaders/default.frag ================================================ #version 460 layout (location = 0) in vec2 TexCoord; layout (location = 0) out vec4 FragColor; layout (binding = 0) uniform sampler2D input_tex; void main() { FragColor = vec4(texture(input_tex, TexCoord).xyz, 1.0); } ================================================ FILE: shaders/default.vert ================================================ #version 460 layout (location = 0) in vec2 position; layout (location = 1) in vec2 in_uv; layout (location = 0) out vec2 out_uv; void main() { gl_Position = vec4(position, 0.0f, 1.0f); out_uv = vec2(in_uv.x, 1.0 - in_uv.y); } ================================================ FILE: shaders/encoder.frag ================================================ #version 460 layout (location = 0) in vec2 TexCoord; layout (location = 0) out vec4 FragColor; layout (binding = 0) uniform sampler2D input_tex; layout(push_constant) uniform constants { vec2 resolution; int frame; } PushConstants; // Encoder or Modulator // This pass converts RGB colors on iChannel0 to // a YIQ (NTSC) Composite signal. #define PI 3.14159265358979323846 #define TAU 6.28318530717958647693 const mat3 rgb_to_yiq = mat3( 0.299, 0.596, 0.211, 0.587, -0.274, -0.523, 0.114, -0.322, 0.312 ); float hash11(float p) { p = fract(p * .1031); p *= p + 33.33; p *= p + p; return fract(p); } float hash12(vec2 p) { vec3 p3 = fract(vec3(p.xyx) * .1031); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); } float hash13(vec3 p3) { p3 = fract(p3 * .1031); p3 += dot(p3, p3.zyx + 31.32); return fract((p3.x + p3.y) * p3.z); } void main() { vec2 uv = TexCoord * PushConstants.resolution; float noise_strength = 0.0; // Get a pixel from iChannel0 // float noise_y = hash12(vec2(float(int(uv.y) >> 1), float(frame))) * noise_strength; float noise_x = hash13(vec3(uv.xy, float(PushConstants.frame))) * noise_strength; // float noise = noise_x + noise_y; // Chroma encoder oscillator frequency float fc = 128.0; // Base oscillator angle for this dot float t = uv.x; // Get a pixel from input_texture vec3 rgb = texture(input_tex, uv / PushConstants.resolution).rgb; // Convert to YIQ vec3 yiq = rgb_to_yiq * rgb; // Final oscillator angle float f = fc * t + ((PI / 2.0) * uv.y) + (PI * float((PushConstants.frame >> 1) & 1)); // Modulate IQ signals float i = yiq.y * cos(f), // I signal q = yiq.z * sin(f); // Q signal // Add to Y to get the composite signal float c = yiq.x + (i + q); // Composite FragColor = vec4(vec3(c), 1.0); } ================================================ FILE: shaders/noise.frag ================================================ #version 460 layout (location = 0) in vec2 TexCoord; layout (location = 0) out vec4 FragColor; layout (binding = 0) uniform sampler2D input_tex; layout(push_constant) uniform constants { vec2 resolution; int frame; } PushConstants; float hash11(float p) { p = fract(p * .1031); p *= p + 33.33; p *= p + p; return fract(p); } float hash12(vec2 p) { vec3 p3 = fract(vec3(p.xyx) * .1031); p3 += dot(p3, p3.yzx + 33.33); return fract((p3.x + p3.y) * p3.z); } float hash13(vec3 p3) { p3 = fract(p3 * .1031); p3 += dot(p3, p3.zyx + 31.32); return fract((p3.x + p3.y) * p3.z); } void main() { vec3 tex = texture(input_tex, TexCoord).xyz; float noise = hash13(vec3(TexCoord * PushConstants.resolution, float(PushConstants.frame))); if (noise < 0.1) { if (noise < 0.05) { tex += noise * 10.0; } else { tex -= noise * 10.0; } } FragColor = vec4(tex, 1.0); } ================================================ FILE: shaders/scanlines.frag ================================================ #version 460 layout (location = 0) in vec2 TexCoord; layout (location = 0) out vec4 FragColor; layout (binding = 0) uniform sampler2D input_tex; layout(push_constant) uniform constants { vec2 resolution; int frame; } PushConstants; // NTSC-like shader // Simulates scanlines, horizontal blur, and basic color bleeding void main() { vec2 uv = TexCoord; vec2 oneX = vec2(1.0 / PushConstants.resolution.x, 0.0); // YIQ / RGB Matrices // RGB to YIQ const vec3 kY = vec3(0.299, 0.587, 0.114); const vec3 kI = vec3(0.596, -0.274, -0.322); const vec3 kQ = vec3(0.211, -0.523, 0.312); // 1. & 3. Bandwidth extraction (Luma) & Bleed (Chroma) // Sample a window for Chroma (Low Bandwidth) // Sample center for Luma (High Bandwidth) vec3 centerRGB = texture(input_tex, uv).rgb; float y = dot(centerRGB, kY); float iSum = 0.0; float qSum = 0.0; float weightSum = 0.0; // NTSC color carrier is ~3.58MHz, bandwidth allows for ~1/3 PushConstants.resolution of luma. // We smear chroma over a few pixels. float bleed = 2.0; for(float x = -bleed; x <= bleed; x += 1.0) { vec3 rgb = texture(input_tex, uv + oneX * x).rgb; float i = dot(rgb, kI); float q = dot(rgb, kQ); // Gaussianish weight float w = 1.0 / (1.0 + abs(x)); iSum += i * w; qSum += q * w; weightSum += w; } float finalI = iSum / weightSum; float finalQ = qSum / weightSum; // Convert back to RGB // R = Y + 0.956I + 0.621Q // G = Y - 0.272I - 0.647Q // B = Y - 1.106I + 1.703Q vec3 color = vec3( y + 0.956 * finalI + 0.621 * finalQ, y - 0.272 * finalI - 0.647 * finalQ, y - 1.106 * finalI + 1.703 * finalQ ); // 2. Scanlines // Based on UV.y. 240 lines. float scanline = sin(uv.y * 240.0 * 3.14159 * 2.0); color *= (0.95 + 0.05 * scanline); // 4. Gamma // Input is usually linear or sRGB. Output to sRGB. // Assuming input acts as sRGB for now. FragColor = vec4(color, 1.0); } ================================================ FILE: shaders/shader.vert ================================================ #version 460 layout (location = 0) out vec2 out_uv; const vec2 vertices[4] = vec2[]( vec2(-1.0, -1.0), vec2( 1.0, -1.0), vec2( 1.0, 1.0), vec2(-1.0, 1.0) ); const vec2 uvs[4] = vec2[]( vec2(0.0, 1.0), vec2(1.0, 1.0), vec2(1.0, 0.0), vec2(0.0, 0.0) ); void main() { gl_Position = vec4(vertices[gl_VertexIndex], 0.0f, 1.0f); out_uv = uvs[gl_VertexIndex]; out_uv.y = 1.0 - out_uv.y; } ================================================ FILE: src/dev/ds.c ================================================ #include "ds.h" #include #include #define printf(fmt, ...)(0) static inline uint8_t ds_get_model_byte(struct ds_state* ds) { switch (ds->mode) { case 0: return 0x41; case 1: return 0x73; case 2: return 0x79; } } static inline void ds_cmd_set_vref_param(struct ps2_sio2* sio2, struct ds_state* ds) { printf("ds: ds_cmd_set_vref_param\n"); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x02); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x5a); } static inline void ds_cmd_query_masked(struct ps2_sio2* sio2, struct ds_state* ds) { printf("ds: ds_cmd_query_masked\n"); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); if (!ds->mode) { queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); } else { queue_push(sio2->out, ds->mask[0]); queue_push(sio2->out, ds->mask[1]); queue_push(sio2->out, 0x03); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x5a); } } static inline void ds_cmd_read_data(struct ps2_sio2* sio2, struct ds_state* ds) { printf("ds: ds_cmd_read_data(%04x)\n", ds->buttons); queue_push(sio2->out, 0xff); queue_push(sio2->out, ds_get_model_byte(ds)); queue_push(sio2->out, 0x5a); queue_push(sio2->out, ds->buttons & 0xff); queue_push(sio2->out, ds->buttons >> 8); if (ds->mode) { queue_push(sio2->out, ds->ax_right_x); queue_push(sio2->out, ds->ax_right_y); queue_push(sio2->out, ds->ax_left_x); queue_push(sio2->out, ds->ax_left_y); // Push pressure bytes (only in DualShock 2 mode) // Note: Some games (e.g. OutRun 2 SP/2006) won't register inputs // if the pressure values are 0, so we push the max value // instead if (ds->mode == 2) { queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); } } } static inline void ds_cmd_config_mode(struct ps2_sio2* sio2, struct ds_state* ds) { printf("ds: ds_cmd_config_mode(%02x)\n", queue_at(sio2->in, 3)); // Same as read_data, but without pressure data in DualShock 2 mode if (!ds->config_mode) { queue_push(sio2->out, 0xff); // We don't use the model byte here because // config_mode returns the same data as analog (DS1) // when not in config mode regardless of the model queue_push(sio2->out, ds->mode ? 0x73 : 0x41); queue_push(sio2->out, 0x5a); queue_push(sio2->out, ds->buttons & 0xff); queue_push(sio2->out, ds->buttons >> 8); if (ds->mode) { queue_push(sio2->out, ds->ax_right_x); queue_push(sio2->out, ds->ax_right_y); queue_push(sio2->out, ds->ax_left_x); queue_push(sio2->out, ds->ax_left_y); } } else { queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); } ds->config_mode = queue_at(sio2->in, 3); } static inline void ds_cmd_set_mode(struct ps2_sio2* sio2, struct ds_state* ds) { printf("ds: ds_cmd_set_mode(%02x, %02x)\n", queue_at(sio2->in, 3), queue_at(sio2->in, 4)); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); int mode = queue_at(sio2->in, 3); int lock = queue_at(sio2->in, 4); if (mode < 2 && !ds->lock) { ds->mode = mode ? 1 : 0; } ds->lock = lock == 3; } static inline void ds_cmd_query_model(struct ps2_sio2* sio2, struct ds_state* ds) { printf("ds: ds_cmd_query_model\n"); queue_push(sio2->out, 0xff); // Header queue_push(sio2->out, 0xf3); // Mode (F3=Config) queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x03); // Model (01=Dualshock/Digital 03=Dualshock 2) queue_push(sio2->out, 0x02); queue_push(sio2->out, !!ds->mode); // Analog (00=no 01=yes) queue_push(sio2->out, 0x02); queue_push(sio2->out, 0x01); queue_push(sio2->out, 0x00); } static inline void ds_cmd_query_act(struct ps2_sio2* sio2, struct ds_state* ds) { printf("ds: ds_cmd_query_act(%02x)\n", queue_at(sio2->in, 3)); int index = queue_at(sio2->in, 3); if (!index) { queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x01); queue_push(sio2->out, 0x02); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x0a); } else { queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x01); queue_push(sio2->out, 0x01); queue_push(sio2->out, 0x01); queue_push(sio2->out, 0x14); } } static inline void ds_cmd_query_comb(struct ps2_sio2* sio2, struct ds_state* ds) { printf("ds: ds_cmd_query_comb\n"); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x02); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x01); queue_push(sio2->out, 0x00); } static inline void ds_cmd_query_mode(struct ps2_sio2* sio2, struct ds_state* ds) { printf("ds: ds_cmd_query_mode\n"); int index = queue_at(sio2->in, 3); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, index ? 7 : 4); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); } static inline void ds_cmd_vibration_toggle(struct ps2_sio2* sio2, struct ds_state* ds) { printf("ds: ds_cmd_vibration_toggle\n"); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, ds->vibration[0]); queue_push(sio2->out, ds->vibration[1]); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); ds->vibration[0] = queue_at(sio2->in, 3); ds->vibration[1] = queue_at(sio2->in, 4); } static inline void ds_cmd_set_native_mode(struct ps2_sio2* sio2, struct ds_state* ds) { printf("ds: ds_cmd_set_native_mode(%02x, %02x, %02x, %02x)\n", queue_at(sio2->in, 3), queue_at(sio2->in, 4), queue_at(sio2->in, 5), queue_at(sio2->in, 6) ); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x5a); ds->mask[0] = queue_at(sio2->in, 3); ds->mask[1] = queue_at(sio2->in, 4); int value = queue_at(sio2->in, 5); if ((value & 1) == 0) { // Digital mode ds->mode = 0; } else if ((value & 2) == 0) { // Analog mode ds->mode = 1; } else { // DualShock 2 mode ds->mode = 2; } } void ds_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) { struct ds_state* ds = (struct ds_state*)udata; switch (cmd) { case 0x40: ds_cmd_set_vref_param(sio2, ds); return; case 0x41: ds_cmd_query_masked(sio2, ds); return; case 0x42: ds_cmd_read_data(sio2, ds); return; case 0x43: ds_cmd_config_mode(sio2, ds); return; case 0x44: ds_cmd_set_mode(sio2, ds); return; case 0x45: ds_cmd_query_model(sio2, ds); return; case 0x46: ds_cmd_query_act(sio2, ds); return; case 0x47: ds_cmd_query_comb(sio2, ds); return; case 0x4C: ds_cmd_query_mode(sio2, ds); return; case 0x4D: ds_cmd_vibration_toggle(sio2, ds); return; case 0x4F: ds_cmd_set_native_mode(sio2, ds); return; } fprintf(stderr, "ds: Unhandled command %02x\n", cmd); // exit(1); } struct ds_state* ds_attach(struct ps2_sio2* sio2, int port) { struct ds_state* ds = malloc(sizeof(struct ds_state)); struct sio2_device dev; dev.detach = ds_detach; dev.handle_command = ds_handle_command; dev.udata = ds; memset(ds, 0, sizeof(struct ds_state)); ds->port = port; ds->ax_right_y = 0x7f; ds->ax_right_x = 0x7f; ds->ax_left_y = 0x7f; ds->ax_left_x = 0x7f; ds->buttons = 0xffff; ds->vibration[0] = 0xff; ds->vibration[1] = 0xff; ds->mask[0] = 0xff; ds->mask[1] = 0xff; // Start in digital mode ds->mode = 0; ds->lock = 0; ps2_sio2_attach_device(sio2, dev, port); return ds; } void ds_button_press(struct ds_state* ds, uint32_t mask) { if (mask == DS_BT_ANALOG) { if (!ds->lock) ds->mode = ds->mode ? 0 : 1; return; } ds->buttons &= ~mask; } void ds_button_release(struct ds_state* ds, uint32_t mask) { ds->buttons |= mask; } void ds_analog_change(struct ds_state* ds, int axis, uint8_t value) { switch (axis) { case 0: ds->ax_right_y = value; break; case 1: ds->ax_right_x = value; break; case 2: ds->ax_left_y = value; break; case 3: ds->ax_left_x = value; break; } } void ds_detach(void* udata) { struct ds_state* ds = (struct ds_state*)udata; free(ds); } ================================================ FILE: src/dev/ds.h ================================================ #ifndef DS_H #define DS_H #ifdef __cplusplus extern "C" { #endif #include #include #include "iop/sio2.h" #define DS_BT_SELECT 0x0001 #define DS_BT_L3 0x0002 #define DS_BT_R3 0x0004 #define DS_BT_START 0x0008 #define DS_BT_UP 0x0010 #define DS_BT_RIGHT 0x0020 #define DS_BT_DOWN 0x0040 #define DS_BT_LEFT 0x0080 #define DS_BT_L2 0x0100 #define DS_BT_R2 0x0200 #define DS_BT_L1 0x0400 #define DS_BT_R1 0x0800 #define DS_BT_TRIANGLE 0x1000 #define DS_BT_CIRCLE 0x2000 #define DS_BT_CROSS 0x4000 #define DS_BT_SQUARE 0x8000 #define DS_BT_ANALOG 0x10000 #define DS_AX_RIGHT_V 0 #define DS_AX_RIGHT_H 1 #define DS_AX_LEFT_V 2 #define DS_AX_LEFT_H 3 struct ds_state { int port; uint16_t buttons; uint8_t ax_right_y; uint8_t ax_right_x; uint8_t ax_left_y; uint8_t ax_left_x; int config_mode; int act_index; int mode_index; int mode; int vibration[2]; int mask[2]; int lock; }; struct ds_state* ds_attach(struct ps2_sio2* sio2, int port); void ds_button_press(struct ds_state* ds, uint32_t mask); void ds_button_release(struct ds_state* ds, uint32_t mask); void ds_analog_change(struct ds_state* ds, int axis, uint8_t value); void ds_detach(void* udata); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/dev/guncon.c ================================================ #include "guncon.h" #include #include // #define printf(fmt, ...)(0) static inline uint8_t guncon_get_model_byte(struct guncon_state* guncon) { return 0x63; } static inline void guncon_cmd_set_vref_param(struct ps2_sio2* sio2, struct guncon_state* guncon) { printf("guncon: guncon_cmd_set_vref_param\n"); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x02); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x5a); } static inline void guncon_cmd_query_masked(struct ps2_sio2* sio2, struct guncon_state* guncon) { printf("guncon: guncon_cmd_query_masked\n"); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); } static inline void guncon_cmd_read_data(struct ps2_sio2* sio2, struct guncon_state* guncon) { printf("guncon: guncon_cmd_read_data(%04x)\n", guncon->buttons); queue_push(sio2->out, 0xff); queue_push(sio2->out, guncon_get_model_byte(guncon)); queue_push(sio2->out, 0x5a); queue_push(sio2->out, guncon->buttons & 0xff); queue_push(sio2->out, guncon->buttons >> 8); queue_push(sio2->out, guncon->x & 0xff); queue_push(sio2->out, guncon->x >> 8); queue_push(sio2->out, guncon->y & 0xff); queue_push(sio2->out, guncon->y >> 8); } static inline void guncon_cmd_config_mode(struct ps2_sio2* sio2, struct guncon_state* guncon) { printf("guncon: guncon_cmd_config_mode(%02x)\n", queue_at(sio2->in, 3)); // Same as read_data, but without pressure data in DualShock 2 mode if (!guncon->config_mode) { queue_push(sio2->out, 0xff); // We don't use the model byte here because // config_mode returns the same data as analog (GUNCON1) // when not in config mode regardless of the model queue_push(sio2->out, 0x63); queue_push(sio2->out, 0x5a); queue_push(sio2->out, guncon->buttons & 0xff); queue_push(sio2->out, guncon->buttons >> 8); queue_push(sio2->out, guncon->x & 0xff); queue_push(sio2->out, guncon->x >> 8); queue_push(sio2->out, guncon->y & 0xff); queue_push(sio2->out, guncon->y >> 8); } else { queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); } guncon->config_mode = queue_at(sio2->in, 3); } static inline void guncon_cmd_set_mode(struct ps2_sio2* sio2, struct guncon_state* guncon) { printf("guncon: guncon_cmd_set_mode(%02x, %02x)\n", queue_at(sio2->in, 3), queue_at(sio2->in, 4)); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); } static inline void guncon_cmd_query_model(struct ps2_sio2* sio2, struct guncon_state* guncon) { printf("guncon: guncon_cmd_query_model\n"); queue_push(sio2->out, 0xff); // Header queue_push(sio2->out, 0xf3); // Mode (F3=Config) queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x03); // Model (01=Dualshock/Digital 03=Dualshock 2) queue_push(sio2->out, 0x02); queue_push(sio2->out, 0x00); // Analog (00=no 01=yes) queue_push(sio2->out, 0x02); queue_push(sio2->out, 0x01); queue_push(sio2->out, 0x00); } static inline void guncon_cmd_query_act(struct ps2_sio2* sio2, struct guncon_state* guncon) { printf("guncon: guncon_cmd_query_act(%02x)\n", queue_at(sio2->in, 3)); int index = queue_at(sio2->in, 3); if (!index) { queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x01); queue_push(sio2->out, 0x02); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x0a); } else { queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x01); queue_push(sio2->out, 0x01); queue_push(sio2->out, 0x01); queue_push(sio2->out, 0x14); } } static inline void guncon_cmd_query_comb(struct ps2_sio2* sio2, struct guncon_state* guncon) { printf("guncon: guncon_cmd_query_comb\n"); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x02); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x01); queue_push(sio2->out, 0x00); } static inline void guncon_cmd_query_mode(struct ps2_sio2* sio2, struct guncon_state* guncon) { printf("guncon: guncon_cmd_query_mode\n"); int index = queue_at(sio2->in, 3); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, index ? 7 : 4); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); } static inline void guncon_cmd_vibration_toggle(struct ps2_sio2* sio2, struct guncon_state* guncon) { printf("guncon: guncon_cmd_vibration_toggle\n"); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); } static inline void guncon_cmd_set_native_mode(struct ps2_sio2* sio2, struct guncon_state* guncon) { printf("guncon: guncon_cmd_set_native_mode(%02x, %02x, %02x, %02x)\n", queue_at(sio2->in, 3), queue_at(sio2->in, 4), queue_at(sio2->in, 5), queue_at(sio2->in, 6) ); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xf3); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x5a); } void guncon_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) { struct guncon_state* guncon = (struct guncon_state*)udata; switch (cmd) { case 0x40: guncon_cmd_set_vref_param(sio2, guncon); return; case 0x41: guncon_cmd_query_masked(sio2, guncon); return; case 0x42: guncon_cmd_read_data(sio2, guncon); return; case 0x43: guncon_cmd_config_mode(sio2, guncon); return; case 0x44: guncon_cmd_set_mode(sio2, guncon); return; case 0x45: guncon_cmd_query_model(sio2, guncon); return; case 0x46: guncon_cmd_query_act(sio2, guncon); return; case 0x47: guncon_cmd_query_comb(sio2, guncon); return; case 0x4C: guncon_cmd_query_mode(sio2, guncon); return; case 0x4D: guncon_cmd_vibration_toggle(sio2, guncon); return; case 0x4F: guncon_cmd_set_native_mode(sio2, guncon); return; } printf("guncon: Unhandled command %02x\n", cmd); exit(1); } struct guncon_state* guncon_attach(struct ps2_sio2* sio2, int port) { struct guncon_state* guncon = malloc(sizeof(struct guncon_state)); struct sio2_device dev; dev.detach = guncon_detach; dev.handle_command = guncon_handle_command; dev.udata = guncon; memset(guncon, 0, sizeof(struct guncon_state)); guncon->port = port; guncon->config_mode = 0; guncon->x = 0x10d; guncon->y = 0x88; guncon->buttons = 0xffff; ps2_sio2_attach_device(sio2, dev, port); return guncon; } void guncon_button_press(struct guncon_state* guncon, uint16_t mask) { guncon->buttons &= ~mask; } void guncon_button_release(struct guncon_state* guncon, uint16_t mask) { guncon->buttons |= mask; } void guncon_analog_change(struct guncon_state* guncon, int axis, uint8_t value) { switch (axis) { case 0: guncon->x = value; break; case 1: guncon->y = value; break; } } void guncon_detach(void* udata) { struct guncon_state* guncon = (struct guncon_state*)udata; free(guncon); } ================================================ FILE: src/dev/guncon.h ================================================ #ifndef GUNCON_H #define GUNCON_H #ifdef __cplusplus extern "C" { #endif #include #include #include "iop/sio2.h" #define GUNCON_BT_START 0x0008 #define GUNCON_BT_CIRCLE 0x2000 #define GUNCON_BT_CROSS 0x4000 #define GUNCON_AX_X 0 #define GUNCON_AX_Y 1 struct guncon_state { int port; uint16_t buttons; uint16_t x; uint16_t y; int config_mode; }; struct guncon_state* guncon_attach(struct ps2_sio2* sio2, int port); void guncon_button_press(struct guncon_state* guncon, uint16_t mask); void guncon_button_release(struct guncon_state* guncon, uint16_t mask); void guncon_analog_change(struct guncon_state* guncon, int axis, uint8_t value); void guncon_detach(void* udata); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/dev/mcd.c ================================================ #include "mcd.h" #include #include #include #define printf(fmt,...)(0) void mcd_flush_block(struct mcd_state* mcd, int addr, int size) { fseek(mcd->file, addr, SEEK_SET); fwrite(&mcd->buf[addr], 1, size, mcd->file); } void mcd_cmd_probe(struct ps2_sio2* sio2, struct mcd_state* mcd) { printf("mcd: mcd_cmd_probe\n"); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); } void mcd_cmd_unk_12(struct ps2_sio2* sio2, struct mcd_state* mcd) { printf("mcd: mcd_cmd_unk_12\n"); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); } void mcd_cmd_start_erase(struct ps2_sio2* sio2, struct mcd_state* mcd) { uint32_t lba = (sio2->in->buf[sio2->in->index + 2]) | (sio2->in->buf[sio2->in->index + 3] << 8) | (sio2->in->buf[sio2->in->index + 4] << 16) | (sio2->in->buf[sio2->in->index + 5] << 24); printf("mcd: mcd_cmd_start_erase(%08x)\n", lba); mcd->addr = lba * MCD_SECTOR_SIZE; queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); } void mcd_cmd_start_write(struct ps2_sio2* sio2, struct mcd_state* mcd) { uint32_t lba = (sio2->in->buf[sio2->in->index + 2]) | (sio2->in->buf[sio2->in->index + 3] << 8) | (sio2->in->buf[sio2->in->index + 4] << 16) | (sio2->in->buf[sio2->in->index + 5] << 24); printf("mcd: mcd_cmd_start_write(%08x)\n", lba); mcd->addr = lba * MCD_SECTOR_SIZE; queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); } void mcd_cmd_start_read(struct ps2_sio2* sio2, struct mcd_state* mcd) { uint32_t lba = (sio2->in->buf[sio2->in->index + 2]) | (sio2->in->buf[sio2->in->index + 3] << 8) | (sio2->in->buf[sio2->in->index + 4] << 16) | (sio2->in->buf[sio2->in->index + 5] << 24); printf("mcd: mcd_cmd_start_read(%08x)\n", lba); mcd->addr = lba * MCD_SECTOR_SIZE; queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); } void mcd_cmd_get_specs(struct ps2_sio2* sio2, struct mcd_state* mcd) { printf("mcd: mcd_cmd_get_specs\n", sio2->in->buf[2]); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, 0x00); // Sector size (2-byte) queue_push(sio2->out, 0x02); queue_push(sio2->out, 0x10); // Erase block size (2-byte) queue_push(sio2->out, 0x00); queue_push(sio2->out, (mcd->size >> 0) & 0xff); // Sector count (4-byte) queue_push(sio2->out, (mcd->size >> 8) & 0xff); queue_push(sio2->out, (mcd->size >> 16) & 0xff); queue_push(sio2->out, (mcd->size >> 24) & 0xff); queue_push(sio2->out, mcd->checksum); // Checksum queue_push(sio2->out, mcd->term); } void mcd_cmd_set_terminator(struct ps2_sio2* sio2, struct mcd_state* mcd) { printf("mcd: mcd_cmd_set_terminator(%02x)\n", sio2->in->buf[2]); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); mcd->term = sio2->in->buf[2]; } void mcd_cmd_get_terminator(struct ps2_sio2* sio2, struct mcd_state* mcd) { printf("mcd: mcd_cmd_get_terminator\n"); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); queue_push(sio2->out, 0x55); } void mcd_cmd_write_data(struct ps2_sio2* sio2, struct mcd_state* mcd) { uint8_t size = queue_at(sio2->in, 2); printf("mcd: mcd_cmd_write_data(%02x)\n", size); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); uint32_t addr = mcd->addr; for (int i = 0; i < size; i++) { mcd->buf[mcd->addr++] = queue_at(sio2->in, 3 + i); queue_push(sio2->out, 0); } mcd_flush_block(mcd, addr, size); queue_push(sio2->out, 0); queue_push(sio2->out, mcd->term); } void mcd_cmd_read_data(struct ps2_sio2* sio2, struct mcd_state* mcd) { printf("mcd: mcd_cmd_read_data(%02x)\n", queue_at(sio2->in, 2)); // assert(queue_at(sio2->in, 2) == 0x80); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); uint8_t checksum = 0; for (int i = 0; i < queue_at(sio2->in, 2); i++) { uint8_t data = mcd->buf[mcd->addr++]; checksum ^= data; queue_push(sio2->out, data); } queue_push(sio2->out, checksum); // XOR checksum queue_push(sio2->out, mcd->term); } void mcd_cmd_rw_end(struct ps2_sio2* sio2, struct mcd_state* mcd) { printf("mcd: mcd_cmd_rw_end\n"); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); } void mcd_cmd_erase_block(struct ps2_sio2* sio2, struct mcd_state* mcd) { printf("mcd: mcd_cmd_erase_block\n"); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); uint32_t addr = mcd->addr; for (int i = 0; i < MCD_SECTOR_SIZE * 16; i++) { mcd->buf[mcd->addr++] = 0xff; } mcd_flush_block(mcd, addr, MCD_SECTOR_SIZE * 16); } void mcd_cmd_auth_f0(struct ps2_sio2* sio2, struct mcd_state* mcd) { printf("mcd: mcd_cmd_auth_f0\n"); uint8_t param = sio2->in->buf[2]; switch (param) { case 0x01: case 0x02: case 0x04: case 0x0f: case 0x11: case 0x13: { // Handle checksum queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); uint8_t checksum = 0; for (int i = 0; i < 8; i++) { checksum ^= sio2->in->buf[i+3]; queue_push(sio2->out, 0x00); } queue_push(sio2->out, checksum); queue_push(sio2->out, mcd->term); } break; case 0x06: case 0x07: case 0x0b: { queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); } break; default: { queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); } break; } } void mcd_cmd_auth_f1(struct ps2_sio2* sio2, struct mcd_state* mcd) { fprintf(stderr, "mcd: mcd_cmd_auth_f1\n"); fprintf(stderr, "mcd: params="); for (int i = 0; i < 16; i++) { fprintf(stderr, "%02x ", sio2->in->buf[2 + i]); } fprintf(stderr, "\n"); exit(1); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); } void mcd_cmd_auth_f3(struct ps2_sio2* sio2, struct mcd_state* mcd) { printf("mcd: mcd_cmd_auth_f3\n"); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); } void mcd_cmd_auth_f7(struct ps2_sio2* sio2, struct mcd_state* mcd) { printf("mcd: mcd_cmd_auth_f7\n"); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); } void mcd_cmd_unk_bf(struct ps2_sio2* sio2, struct mcd_state* mcd) { printf("mcd: mcd_cmd_unk_bf\n"); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x2b); queue_push(sio2->out, mcd->term); } void mcd_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) { struct mcd_state* mcd = (struct mcd_state*)udata; switch (cmd) { case 0x11: mcd_cmd_probe(sio2, mcd); return; case 0x12: mcd_cmd_unk_12(sio2, mcd); return; case 0x21: mcd_cmd_start_erase(sio2, mcd); return; case 0x22: mcd_cmd_start_write(sio2, mcd); return; case 0x23: mcd_cmd_start_read(sio2, mcd); return; case 0x26: mcd_cmd_get_specs(sio2, mcd); return; case 0x27: mcd_cmd_set_terminator(sio2, mcd); return; case 0x28: mcd_cmd_get_terminator(sio2, mcd); return; case 0x42: mcd_cmd_write_data(sio2, mcd); return; case 0x43: mcd_cmd_read_data(sio2, mcd); return; // case 0x52: mcd_cmd_ps1_read(sio2, mcd); return; case 0x81: mcd_cmd_rw_end(sio2, mcd); return; case 0x82: mcd_cmd_erase_block(sio2, mcd); return; case 0xf0: mcd_cmd_auth_f0(sio2, mcd); return; case 0xf1: mcd_cmd_auth_f1(sio2, mcd); return; case 0xf3: mcd_cmd_auth_f3(sio2, mcd); return; case 0xf7: mcd_cmd_auth_f7(sio2, mcd); return; case 0xbf: mcd_cmd_unk_bf(sio2, mcd); return; } printf("mcd: Unhandled command %02x\n", cmd); exit(1); } struct mcd_state* mcd_attach(struct ps2_sio2* sio2, int port, const char* path) { FILE* file = fopen(path, "r+b"); if (!file) return NULL; struct mcd_state* mcd = malloc(sizeof(struct mcd_state)); struct sio2_device dev; memset(mcd, 0, sizeof(struct mcd_state)); // Get memcard size fseek(file, 0, SEEK_END); mcd->buf_size = ftell(file); fseek(file, 0, SEEK_SET); mcd->buf = (uint8_t*)malloc(mcd->buf_size); fread(mcd->buf, 1, mcd->buf_size, file); // Init card state mcd->term = 0x55; mcd->file = file; mcd->size = (1 << (31 - __builtin_clz(mcd->buf_size))) >> 9; mcd->checksum = 0x02 ^ 0x10; for (int i = 0; i < 4; i++) mcd->checksum ^= (mcd->size >> (i * 8)) & 0xff; printf("mcd: Memory card at \'%s\' initialized.\n\tTotal size: %x (%d)\n\tSize (in sectors): %x (%d)\n\tChecksum: %02x\n", path, mcd->buf_size, mcd->buf_size, mcd->size, mcd->size, mcd->checksum ); dev.detach = mcd_detach; dev.handle_command = mcd_handle_command; dev.udata = mcd; ps2_sio2_attach_device(sio2, dev, port); return mcd; } void mcd_detach(void* udata) { struct mcd_state* mcd = (struct mcd_state*)udata; // Flush buffer back to file fseek(mcd->file, 0, SEEK_SET); fwrite(mcd->buf, 1, mcd->buf_size, mcd->file); fclose(mcd->file); free(mcd->buf); free(mcd); } ================================================ FILE: src/dev/mcd.h ================================================ #ifndef MCD_H #define MCD_H #ifdef __cplusplus extern "C" { #endif #include #include #include "iop/sio2.h" #define MCD_SIZE_8MB 0x4000 #define MCD_SIZE_16MB 0x8000 #define MCD_SIZE_32MB 0x10000 #define MCD_SIZE_64MB 0x20000 // 512 bytes data + 16 bytes ECC #define MCD_SECTOR_SIZE (512+16) struct mcd_state { int port; uint8_t term; uint16_t buttons; uint8_t ax_right_y; uint8_t ax_right_x; uint8_t ax_left_y; uint8_t ax_left_x; int config_mode; int act_index; int mode_index; uint32_t size; uint8_t checksum; uint32_t addr; uint32_t buf_size; uint8_t* buf; FILE* file; }; struct mcd_state* mcd_attach(struct ps2_sio2* sio2, int port, const char* path); void mcd_detach(void* udata); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/dev/mtap.c ================================================ #include #include #include "mtap.h" #define printf(fmt,...)(0) void mtap_cmd_probe(struct ps2_sio2* sio2, struct mtap_state* mtap) { printf("mtap: mtap_cmd_probe\n"); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0x80); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x04); queue_push(sio2->out, 0x00); queue_push(sio2->out, 0x5a); } void mtap_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) { struct mtap_state* mtap = (struct mtap_state*)udata; switch (cmd) { case 0x12: mtap_cmd_probe(sio2, mtap); return; } printf("mtap: Unhandled command %02x\n", cmd); exit(1); } struct mtap_state* mtap_attach(struct ps2_sio2* sio2, int port) { struct mtap_state* mtap = malloc(sizeof(struct mtap_state)); struct sio2_device dev; dev.detach = mtap_detach; dev.handle_command = mtap_handle_command; dev.udata = mtap; memset(mtap, 0, sizeof(struct mtap_state)); ps2_sio2_attach_device(sio2, dev, port); return mtap; } void mtap_detach(void* udata) { struct mtap_state* mtap = (struct mtap_state*)udata; free(mtap); } ================================================ FILE: src/dev/mtap.h ================================================ #ifndef MTAP_H #define MTAP_H #ifdef __cplusplus extern "C" { #endif #include #include #include "iop/sio2.h" struct mtap_state { struct sio2_device port[8]; }; struct mtap_state* mtap_attach(struct ps2_sio2* sio2, int port); void mtap_detach(void* udata); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/dev/ps1_mcd.c ================================================ #include "ps1_mcd.h" #include #include #include #define printf(fmt,...)(0) void ps1_mcd_flush_block(struct ps1_mcd_state* mcd, int addr) { fseek(mcd->file, addr, SEEK_SET); fwrite(&mcd->buf[addr], 1, 128, mcd->file); } /* Send Reply Comment 81h N/A Memory card address 52h FLAG Send Read Command (ASCII "R"), Receive FLAG Byte 00h 5Ah Receive Memory Card ID1 00h 5Dh Receive Memory Card ID2 MSB (00h) Send Address MSB ;\sector number (0..3FFh) LSB (pre) Send Address LSB ;/ 00h 5Ch Receive Command Acknowledge 1 ;<-- late /ACK after this byte-pair 00h 5Dh Receive Command Acknowledge 2 00h MSB Receive Confirmed Address MSB 00h LSB Receive Confirmed Address LSB 00h ... Receive Data Sector (128 bytes) 00h CHK Receive Checksum (MSB xor LSB xor Data bytes) 00h 47h Receive Memory End Byte (should be always 47h="G"=Good for Read) */ void ps1_mcd_cmd_read(struct ps2_sio2* sio2, struct ps1_mcd_state* mcd) { uint16_t msb = queue_at(sio2->in, 4); uint16_t lsb = queue_at(sio2->in, 5); uint16_t addr = ((msb << 8) | lsb) * 128; printf("ps1_mcd: ps1_mcd_cmd_read(%04x)\n", (msb << 8) | lsb); queue_push(sio2->out, 0xff); queue_push(sio2->out, mcd->flag); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x5d); queue_push(sio2->out, 0x00); queue_push(sio2->out, msb); // (pre) queue_push(sio2->out, 0x5c); queue_push(sio2->out, 0x5d); queue_push(sio2->out, msb); queue_push(sio2->out, lsb); uint8_t checksum = msb ^ lsb; for (int i = 0; i < 128; i++) { checksum ^= mcd->buf[addr + i]; queue_push(sio2->out, mcd->buf[addr + i]); } queue_push(sio2->out, checksum); queue_push(sio2->out, 'G'); // 0x47 } /* Send Reply Comment 81h N/A Memory card address 57h FLAG Send Write Command (ASCII "W"), Receive FLAG Byte 00h 5Ah Receive Memory Card ID1 00h 5Dh Receive Memory Card ID2 MSB (00h) Send Address MSB ;\sector number (0..3FFh) LSB (pre) Send Address LSB ;/ ... (pre) Send Data Sector (128 bytes) CHK (pre) Send Checksum (MSB xor LSB xor Data bytes) 00h 5Ch Receive Command Acknowledge 1 00h 5Dh Receive Command Acknowledge 2 00h 4xh Receive Memory End Byte (47h=Good, 4Eh=BadChecksum, FFh=BadSector) */ void ps1_mcd_cmd_write(struct ps2_sio2* sio2, struct ps1_mcd_state* mcd) { uint16_t msb = queue_at(sio2->in, 4); uint16_t lsb = queue_at(sio2->in, 5); uint16_t addr = ((msb << 8) | lsb) * 128; printf("ps1_mcd: ps1_mcd_cmd_write(%04x)\n", (msb << 8) | lsb); queue_push(sio2->out, 0xff); queue_push(sio2->out, mcd->flag); queue_push(sio2->out, 0x5a); queue_push(sio2->out, 0x5d); queue_push(sio2->out, 0x00); queue_push(sio2->out, msb); // (pre) for (int i = 0; i < 128; i++) { mcd->buf[addr+i] = queue_at(sio2->in, 6+i); queue_push(sio2->out, queue_at(sio2->in, 5+i)); // (pre) } ps1_mcd_flush_block(mcd, addr); queue_push(sio2->out, queue_at(sio2->in, 133)); // (pre) queue_push(sio2->out, 0x5c); queue_push(sio2->out, 0x5d); queue_push(sio2->out, 'G'); // Reset directory read flag mcd->flag &= ~0x08; } void ps1_mcd_cmd_get_id(struct ps2_sio2* sio2, struct ps1_mcd_state* mcd) { printf("ps1_mcd: ps1_mcd_cmd_get_id\n"); exit(1); } void ps1_mcd_cmd_invalid(struct ps2_sio2* sio2, struct ps1_mcd_state* mcd) { printf("ps1_mcd: ps1_mcd_cmd_invalid(%02x)\n", queue_size(sio2->in)); queue_push(sio2->out, 0xff); queue_push(sio2->out, mcd->flag); for (int i = 2; i < queue_size(sio2->in); i++) queue_push(sio2->out, 0xff); } void ps1_mcd_cmd_detect_pocketstation(struct ps2_sio2* sio2, struct ps1_mcd_state* mcd) { printf("ps1_mcd: ps1_mcd_cmd_detect_pocketstation\n"); queue_push(sio2->out, 0xff); queue_push(sio2->out, mcd->flag); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); queue_push(sio2->out, 0xff); } void ps1_mcd_handle_command(struct ps2_sio2* sio2, void* udata, int cmd) { struct ps1_mcd_state* mcd = (struct ps1_mcd_state*)udata; switch (cmd) { case 0x52: ps1_mcd_cmd_read(sio2, mcd); return; case 0x53: ps1_mcd_cmd_get_id(sio2, mcd); return; case 0x57: ps1_mcd_cmd_write(sio2, mcd); return; case 0x58: { if (mcd->type == 0) break; ps1_mcd_cmd_detect_pocketstation(sio2, mcd); return; } } sio2->recv1 |= 0x2000; ps1_mcd_cmd_invalid(sio2, mcd); } void ps1_mcd_set_type(struct ps1_mcd_state* mcd, int type) { mcd->type = type; } struct ps1_mcd_state* ps1_mcd_attach(struct ps2_sio2* sio2, int port, const char* path) { FILE* file = fopen(path, "r+b"); if (!file) return NULL; struct ps1_mcd_state* mcd = malloc(sizeof(struct ps1_mcd_state)); struct sio2_device dev; memset(mcd, 0, sizeof(struct ps1_mcd_state)); mcd->file = file; mcd->flag = 0x08; fread(mcd->buf, 1, PS1_MCD_SIZE, file); printf("ps1_mcd: Memory card at \'%s\' initialized.\n", path ); dev.detach = ps1_mcd_detach; dev.handle_command = ps1_mcd_handle_command; dev.udata = mcd; ps2_sio2_attach_device(sio2, dev, port); return mcd; } void ps1_mcd_detach(void* udata) { struct ps1_mcd_state* mcd = (struct ps1_mcd_state*)udata; // Flush buffer back to file fseek(mcd->file, 0, SEEK_SET); fwrite(mcd->buf, 1, PS1_MCD_SIZE, mcd->file); fclose(mcd->file); free(mcd); } ================================================ FILE: src/dev/ps1_mcd.h ================================================ #ifndef PS1_MCD_H #define PS1_MCD_H #ifdef __cplusplus extern "C" { #endif #include #include #include "iop/sio2.h" // 128 bytes data #define PS1_MCD_SECTOR_SIZE (128) #define PS1_MCD_SIZE 0x20000 struct ps1_mcd_state { // 128 KiB uint8_t buf[PS1_MCD_SIZE]; uint8_t flag; int type; FILE* file; }; struct ps1_mcd_state* ps1_mcd_attach(struct ps2_sio2* sio2, int port, const char* path); void ps1_mcd_set_type(struct ps1_mcd_state* mcd, int type); void ps1_mcd_detach(void* udata); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/bus.c ================================================ #include #include #include #include "bus.h" #include "bus_decl.h" struct ee_bus* ee_bus_create(void) { return malloc(sizeof(struct ee_bus)); } #define likely(x) __builtin_expect(!!(x), 1) #define unlikely(x) __builtin_expect(!!(x), 0) void ee_bus_init(struct ee_bus* bus, const char* bios_path) { memset(bus, 0, sizeof(struct ee_bus)); for (int i = 0; i < 0x10000; i++) { bus->fastmem_r_table[i] = NULL; bus->fastmem_w_table[i] = NULL; } } void ee_bus_init_fastmem(struct ee_bus* bus, int ee_ram_size, int iop_ram_size) { memset(bus->fastmem_r_table, 0, sizeof(bus->fastmem_r_table)); memset(bus->fastmem_w_table, 0, sizeof(bus->fastmem_w_table)); // BIOS for (int i = 0; i < 0x200; i++) { bus->fastmem_r_table[i+0xfe00] = bus->bios->buf + (i * 0x2000); } // Main RAM for (int i = 0; i < (ee_ram_size / 0x2000); i++) { bus->fastmem_r_table[i+0x0000] = bus->ee_ram->buf + (i * 0x2000); bus->fastmem_w_table[i+0x0000] = bus->ee_ram->buf + (i * 0x2000); } // IOP RAM for (int i = 0; i < (iop_ram_size / 0x2000); i++) { bus->fastmem_r_table[i+0xe000] = bus->iop_ram->buf + (i * 0x2000); bus->fastmem_w_table[i+0xe000] = bus->iop_ram->buf + (i * 0x2000); } } void ee_bus_init_bios(struct ee_bus* bus, struct ps2_bios* bios) { bus->bios = bios; } void ee_bus_init_rom1(struct ee_bus* bus, struct ps2_bios* rom1) { bus->rom1 = rom1; } void ee_bus_init_rom2(struct ee_bus* bus, struct ps2_bios* rom2) { bus->rom2 = rom2; } void ee_bus_init_iop_ram(struct ee_bus* bus, struct ps2_ram* iop_ram) { bus->iop_ram = iop_ram; } void ee_bus_init_sif(struct ee_bus* bus, struct ps2_sif* sif) { bus->sif = sif; } void ee_bus_init_ram(struct ee_bus* bus, struct ps2_ram* ram) { bus->ee_ram = ram; } void ee_bus_init_dmac(struct ee_bus* bus, struct ps2_dmac* dmac) { bus->dmac = dmac; } void ee_bus_init_intc(struct ee_bus* bus, struct ps2_intc* intc) { bus->intc = intc; } void ee_bus_init_gif(struct ee_bus* bus, struct ps2_gif* gif) { bus->gif = gif; } void ee_bus_init_vif0(struct ee_bus* bus, struct ps2_vif* vif0) { bus->vif0 = vif0; } void ee_bus_init_vif1(struct ee_bus* bus, struct ps2_vif* vif1) { bus->vif1 = vif1; } void ee_bus_init_gs(struct ee_bus* bus, struct ps2_gs* gs) { bus->gs = gs; } void ee_bus_init_ipu(struct ee_bus* bus, struct ps2_ipu* ipu) { bus->ipu = ipu; } void ee_bus_init_timers(struct ee_bus* bus, struct ps2_ee_timers* timers) { bus->timers = timers; } void ee_bus_init_cdvd(struct ee_bus* bus, struct ps2_cdvd* cdvd) { bus->cdvd = cdvd; } void ee_bus_init_usb(struct ee_bus* bus, struct ps2_usb* usb) { bus->usb = usb; } void ee_bus_init_sbus(struct ee_bus* bus, struct ps2_sbus* sbus) { bus->sbus = sbus; } void ee_bus_init_dev9(struct ee_bus* bus, struct ps2_dev9* dev9) { bus->dev9 = dev9; } void ee_bus_init_speed(struct ee_bus* bus, struct ps2_speed* speed) { bus->speed = speed; } void ee_bus_init_vu0(struct ee_bus* bus, struct vu_state* vu) { bus->vu0 = vu; } void ee_bus_init_vu1(struct ee_bus* bus, struct vu_state* vu) { bus->vu1 = vu; } void ee_bus_init_kputchar(struct ee_bus* bus, void (*kputchar)(void*, char), void* udata) { bus->kputchar = kputchar; bus->kputchar_udata = udata; } void ee_bus_destroy(struct ee_bus* bus) { free(bus); } #define MAP_MEM_READ(b, l, u, d, n) \ if ((addr >= l) && (addr <= u)) return ps2_ ## d ## _read ## b(bus->n, addr - l); #define MAP_MEM_WRITE(b, l, u, d, n) \ if ((addr >= l) && (addr <= u)) { ps2_ ## d ## _write ## b(bus->n, addr - l, data); return; } #define MAP_REG_READ(b, l, u, d, n) \ if ((addr >= l) && (addr <= u)) return ps2_ ## d ## _read ## b(bus->n, addr); #define MAP_REG_WRITE(b, l, u, d, n) \ if ((addr >= l) && (addr <= u)) { ps2_ ## d ## _write ## b(bus->n, addr, data); return; } // Fast ranges: // - RAM 00000000-01FFFFFF -> 0000-0fff (1000) // - BIOS 1FC00000-1FFFFFFF -> fe00-ffff (200) // - VU 11000000-1100FFFF -> 8800-8807 (8) // - IOP 1C000000-1C1FFFFF -> e000-e0ff (100) uint64_t ee_bus_read8(void* udata, uint32_t addr) { struct ee_bus* bus = (struct ee_bus*)udata; void* ptr = bus->fastmem_r_table[addr >> 13]; if (likely(ptr)) return *((uint8_t*)(((uint8_t*)ptr) + (addr & 0x1fff))); // MAP_MEM_READ(8, 0x00000000, 0x01FFFFFF, ram, ee_ram); // MAP_MEM_READ(8, 0x20000000, 0x21FFFFFF, ram, ee_ram); // MAP_MEM_READ(8, 0x30000000, 0x31FFFFFF, ram, ee_ram); // MAP_MEM_READ(8, 0x1C000000, 0x1C1FFFFF, ram, iop_ram); // MAP_MEM_READ(8, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_MEM_READ(8, 0x11000000, 0x11007FFF, vu, vu0); MAP_MEM_READ(8, 0x11008000, 0x1100FFFF, vu, vu1); MAP_REG_READ(8, 0x10008000, 0x1000EFFF, dmac, dmac); MAP_REG_READ(8, 0x1000F520, 0x1000F5FF, dmac, dmac); MAP_REG_READ(8, 0x1F402004, 0x1F402018, cdvd, cdvd); MAP_MEM_READ(8, 0x1E000000, 0x1E3FFFFF, bios, rom1); MAP_MEM_READ(8, 0x1E400000, 0x1E7FFFFF, bios, rom2); MAP_REG_READ(64, 0x12000000, 0x12001FFF, gs, gs); // Reuse 64-bit function MAP_REG_READ(8, 0x1F801460, 0x1F80147F, dev9, dev9); MAP_REG_READ(8, 0x14000000, 0x1400FFFF, speed, speed); if ((addr >> 16) == 0x1f80) return 0; // printf("bus: Unhandled 8-bit read from physical address 0x%08x\n", addr); // *(int*)0 = 0; return 0; } uint64_t ee_bus_read16(void* udata, uint32_t addr) { struct ee_bus* bus = (struct ee_bus*)udata; void* ptr = bus->fastmem_r_table[addr >> 13]; if (likely(ptr)) return *((uint16_t*)(((uint8_t*)ptr) + (addr & 0x1fff))); // MAP_MEM_READ(16, 0x00000000, 0x01FFFFFF, ram, ee_ram); // MAP_MEM_READ(16, 0x20000000, 0x21FFFFFF, ram, ee_ram); // MAP_MEM_READ(16, 0x30000000, 0x31FFFFFF, ram, ee_ram); // MAP_MEM_READ(16, 0x1C000000, 0x1C1FFFFF, ram, iop_ram); // MAP_MEM_READ(16, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_REG_READ(16, 0x10008000, 0x1000EFFF, dmac, dmac); MAP_REG_READ(16, 0x1000F520, 0x1000F5FF, dmac, dmac); MAP_MEM_READ(16, 0x11000000, 0x11007FFF, vu, vu0); MAP_MEM_READ(16, 0x11008000, 0x1100FFFF, vu, vu1); MAP_MEM_READ(16, 0x1E000000, 0x1E3FFFFF, bios, rom1); MAP_MEM_READ(16, 0x1E400000, 0x1E7FFFFF, bios, rom2); MAP_REG_READ(16, 0x10000000, 0x10001FFF, ee_timers, timers); MAP_REG_READ(16, 0x1F801460, 0x1F80147F, dev9, dev9); MAP_REG_READ(16, 0x14000000, 0x1400FFFF, speed, speed); if (addr == 0x1a000010) return 0xffff; switch (addr) { case 0x1f803800: return 0; // SCPH-39001 stub case 0x1a000006: return 2; } fprintf(stderr, "bus: Unhandled 16-bit read from physical address 0x%08x\n", addr); // exit(1); return 0; } uint64_t ee_bus_read32(void* udata, uint32_t addr) { struct ee_bus* bus = (struct ee_bus*)udata; // pacmanbr // if (addr == 0x00189A40) return 0x34630009; // umilucky // if (addr == 0x001A2834) return 0x0; // if (addr == 0x001AAC9C) return 0x24040002; // if (addr == 0x001AAE08) return 0x0; // akaiser // if (addr == 0x00102110) return 0x0; // if (addr == 0x00102130) return 0x0; // if (addr == 0x0013e388) return 0x0; // if (addr == 0x001c2b0c) return 0x24027FFF; // if (addr == 0x00104d7c) return 0x240F0064; // akaievo // if (addr == 0x001021E8) return 0x0; // if (addr == 0x00102208) return 0x0; // if (addr == 0x0015FA18) return 0x0; // if (addr == 0x0015FC20) return 0x0; // if (addr == 0x001D8A0C) return 0x24027FFF; // if (addr == 0x001051b4) return 0x240F02BC; void* ptr = bus->fastmem_r_table[addr >> 13]; if (likely(ptr)) return *((uint32_t*)(((uint8_t*)ptr) + (addr & 0x1fff))); // MAP_MEM_READ(32, 0x00000000, 0x01FFFFFF, ram, ee_ram); // MAP_MEM_READ(32, 0x20000000, 0x21FFFFFF, ram, ee_ram); // MAP_MEM_READ(32, 0x30000000, 0x31FFFFFF, ram, ee_ram); // MAP_MEM_READ(32, 0x1C000000, 0x1C1FFFFF, ram, iop_ram); // MAP_MEM_READ(32, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_REG_READ(32, 0x1000F200, 0x1000F26F, sif, sif); MAP_REG_READ(32, 0x10008000, 0x1000EFFF, dmac, dmac); MAP_REG_READ(32, 0x1000F520, 0x1000F5FF, dmac, dmac); MAP_REG_READ(64, 0x10002000, 0x1000203F, ipu, ipu); MAP_REG_READ(64, 0x10007000, 0x1000701F, ipu, ipu); MAP_REG_READ(32, 0x10003000, 0x100037FF, gif, gif); MAP_REG_READ(32, 0x10003800, 0x10003BFF, vif, vif0); MAP_REG_READ(32, 0x10003C00, 0x10003FFF, vif, vif1); MAP_REG_READ(32, 0x10004000, 0x10004FFF, vif, vif0); MAP_REG_READ(32, 0x10005000, 0x10005FFF, vif, vif1); MAP_REG_READ(32, 0x1000F000, 0x1000F01F, intc, intc); MAP_REG_READ(64, 0x12000000, 0x12001FFF, gs, gs); // Reuse 64-bit function MAP_REG_READ(32, 0x10000000, 0x10001FFF, ee_timers, timers); MAP_MEM_READ(32, 0x11000000, 0x11007FFF, vu, vu0); MAP_MEM_READ(32, 0x11008000, 0x1100FFFF, vu, vu1); MAP_MEM_READ(32, 0x1E000000, 0x1E3FFFFF, bios, rom1); MAP_MEM_READ(32, 0x1E400000, 0x1E7FFFFF, bios, rom2); MAP_REG_READ(32, 0x1F801600, 0x1F8016FF, usb, usb); MAP_REG_READ(32, 0x1F801460, 0x1F80147F, dev9, dev9); MAP_REG_READ(32, 0x14000000, 0x1400FFFF, speed, speed); switch (addr) { case 0x1000F440: { uint8_t sop = (bus->mch_ricm >> 6) & 0xF; uint8_t sa = (bus->mch_ricm >> 16) & 0xFFF; if (!sop) { switch (sa) { case 0x21: { if (bus->rdram_sdevid < 2) { bus->rdram_sdevid++; return 0x1F; } return 0; } break; case 0x23: return 0x0D0D; case 0x24: return 0x0090; case 0x40: return bus->mch_ricm & 0x1F; } } return 0; } break; case 0x1000f130: case 0x1000f400: case 0x1000f410: case 0x1000f430: case 0x1f80141c: { return 0; } break; } // printf("bus: Unhandled 32-bit read from physical address 0x%08x\n", addr); if ((addr & 0xffff0000) == 0xfffe0000) exit(1); return 0; } uint64_t ee_bus_read64(void* udata, uint32_t addr) { struct ee_bus* bus = (struct ee_bus*)udata; void* ptr = bus->fastmem_r_table[addr >> 13]; if (likely(ptr)) return *((uint64_t*)(((uint8_t*)ptr) + (addr & 0x1fff))); // MAP_MEM_READ(64, 0x00000000, 0x01FFFFFF, ram, ee_ram); // MAP_MEM_READ(64, 0x20000000, 0x21FFFFFF, ram, ee_ram); // MAP_MEM_READ(64, 0x30000000, 0x31FFFFFF, ram, ee_ram); // MAP_MEM_READ(64, 0x1C000000, 0x1C1FFFFF, ram, iop_ram); // MAP_MEM_READ(64, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_REG_READ(64, 0x12000000, 0x12001FFF, gs, gs); MAP_REG_READ(64, 0x10002000, 0x1000203F, ipu, ipu); MAP_REG_READ(64, 0x10007000, 0x1000701F, ipu, ipu); MAP_REG_READ(32, 0x10008000, 0x1000EFFF, dmac, dmac); MAP_REG_READ(32, 0x1000F520, 0x1000F5FF, dmac, dmac); MAP_REG_READ(32, 0x10000000, 0x10001FFF, ee_timers, timers); // Reuse 32-bit function MAP_MEM_READ(64, 0x11000000, 0x11007FFF, vu, vu0); MAP_MEM_READ(64, 0x11008000, 0x1100FFFF, vu, vu1); MAP_MEM_READ(64, 0x1E000000, 0x1E3FFFFF, bios, rom1); MAP_MEM_READ(64, 0x1E400000, 0x1E7FFFFF, bios, rom2); fprintf(stderr, "bus: Unhandled 64-bit read from physical address 0x%08x\n", addr); // exit(1); return 0; } uint128_t ee_bus_read128(void* udata, uint32_t addr) { struct ee_bus* bus = (struct ee_bus*)udata; void* ptr = bus->fastmem_r_table[addr >> 13]; if (likely(ptr)) return *((uint128_t*)(((uint8_t*)ptr) + (addr & 0x1fff))); // MAP_MEM_READ(128, 0x00000000, 0x01FFFFFF, ram, ee_ram); // MAP_MEM_READ(128, 0x20000000, 0x21FFFFFF, ram, ee_ram); // MAP_MEM_READ(128, 0x30000000, 0x31FFFFFF, ram, ee_ram); // MAP_MEM_READ(128, 0x1C000000, 0x1C1FFFFF, ram, iop_ram); // MAP_MEM_READ(128, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_REG_READ(128, 0x10004000, 0x10004FFF, vif, vif0); MAP_REG_READ(128, 0x10005000, 0x10005FFF, vif, vif1); MAP_REG_READ(128, 0x10007000, 0x1000701F, ipu, ipu); MAP_MEM_READ(128, 0x11000000, 0x11007FFF, vu, vu0); MAP_MEM_READ(128, 0x11008000, 0x1100FFFF, vu, vu1); MAP_MEM_READ(128, 0x1E000000, 0x1E3FFFFF, bios, rom1); MAP_MEM_READ(128, 0x1E400000, 0x1E7FFFFF, bios, rom2); fprintf(stderr, "bus: Unhandled 128-bit read from physical address 0x%08x\n", addr); // exit(1); // *(int*)0 = 0; return (uint128_t){ .u64[0] = 0, .u64[1] = 0 }; } void ee_bus_write8(void* udata, uint32_t addr, uint64_t data) { struct ee_bus* bus = (struct ee_bus*)udata; void* ptr = bus->fastmem_w_table[addr >> 13]; if (likely(ptr)) { *((uint8_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data; return; } // MAP_MEM_WRITE(8, 0x00000000, 0x01FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(8, 0x20000000, 0x21FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(8, 0x30000000, 0x31FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(8, 0x1C000000, 0x1C1FFFFF, ram, iop_ram); // MAP_MEM_WRITE(8, 0x1FC00000, 0x1FFFFFFF, bios, bios); // BIOS Firmware update MAP_REG_WRITE(8, 0x10008000, 0x1000EFFF, dmac, dmac); MAP_REG_WRITE(8, 0x1000F520, 0x1000F5FF, dmac, dmac); MAP_REG_WRITE(8, 0x1F402004, 0x1F402018, cdvd, cdvd); MAP_MEM_WRITE(8, 0x11000000, 0x11007FFF, vu, vu0); MAP_MEM_WRITE(8, 0x11008000, 0x1100FFFF, vu, vu1); MAP_REG_WRITE(8, 0x1000F000, 0x1000F01F, intc, intc); MAP_REG_WRITE(8, 0x1F801460, 0x1F80147F, dev9, dev9); MAP_REG_WRITE(8, 0x14000000, 0x1400FFFF, speed, speed); if (addr == 0x1000f180) { bus->kputchar(bus->kputchar_udata, data & 0xff); return; } // printf("bus: Unhandled 8-bit write to physical address 0x%08x (0x%02lx)\n", addr, data); } void ee_bus_write16(void* udata, uint32_t addr, uint64_t data) { struct ee_bus* bus = (struct ee_bus*)udata; void* ptr = bus->fastmem_w_table[addr >> 13]; if (likely(ptr)) { *((uint16_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data; return; } // MAP_MEM_WRITE(16, 0x00000000, 0x01FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(16, 0x20000000, 0x21FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(16, 0x30000000, 0x31FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(16, 0x1C000000, 0x1C1FFFFF, ram, iop_ram); // MAP_MEM_WRITE(16, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_REG_WRITE(16, 0x10008000, 0x1000EFFF, dmac, dmac); MAP_REG_WRITE(16, 0x1000F520, 0x1000F5FF, dmac, dmac); MAP_MEM_WRITE(16, 0x11000000, 0x11007FFF, vu, vu0); MAP_MEM_WRITE(16, 0x11008000, 0x1100FFFF, vu, vu1); MAP_REG_WRITE(16, 0x1000F000, 0x1000F01F, intc, intc); MAP_REG_WRITE(16, 0x10000000, 0x10001FFF, ee_timers, timers); MAP_REG_WRITE(16, 0x1F801460, 0x1F80147F, dev9, dev9); MAP_REG_WRITE(16, 0x14000000, 0x1400FFFF, speed, speed); switch (addr) { case 0x1a000008: case 0x1f801470: case 0x1f801472: return; } printf("bus: Unhandled 16-bit write to physical address 0x%08x (0x%04lx)\n", addr, data); } void ee_bus_write32(void* udata, uint32_t addr, uint64_t data) { struct ee_bus* bus = (struct ee_bus*)udata; void* ptr = bus->fastmem_w_table[addr >> 13]; if (likely(ptr)) { *((uint32_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data; return; } // MAP_MEM_WRITE(32, 0x00000000, 0x01FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(32, 0x20000000, 0x21FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(32, 0x30000000, 0x31FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(32, 0x1C000000, 0x1C1FFFFF, ram, iop_ram); // MAP_MEM_WRITE(32, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_REG_WRITE(32, 0x10000000, 0x10001FFF, ee_timers, timers); MAP_REG_WRITE(64, 0x10002000, 0x1000203F, ipu, ipu); MAP_REG_WRITE(32, 0x10003000, 0x100037FF, gif, gif); MAP_REG_WRITE(64, 0x10007000, 0x1000701F, ipu, ipu); MAP_REG_WRITE(32, 0x10008000, 0x1000EFFF, dmac, dmac); MAP_REG_WRITE(32, 0x1000F000, 0x1000F01F, intc, intc); MAP_REG_WRITE(32, 0x1000F200, 0x1000F26F, sif, sif); MAP_REG_WRITE(32, 0x10003800, 0x10003BFF, vif, vif0); MAP_REG_WRITE(32, 0x10003C00, 0x10003FFF, vif, vif1); MAP_REG_WRITE(32, 0x10004000, 0x10004FFF, vif, vif0); MAP_REG_WRITE(32, 0x10005000, 0x10005FFF, vif, vif1); MAP_REG_WRITE(32, 0x1000F520, 0x1000F5FF, dmac, dmac); MAP_REG_WRITE(64, 0x12000000, 0x12001FFF, gs, gs); // Reuse 64-bit function MAP_MEM_WRITE(32, 0x11000000, 0x11007FFF, vu, vu0); MAP_MEM_WRITE(32, 0x11008000, 0x1100FFFF, vu, vu1); MAP_REG_WRITE(32, 0x1F801600, 0x1F8016FF, usb, usb); MAP_REG_WRITE(32, 0x1F801460, 0x1F80147F, dev9, dev9); MAP_REG_WRITE(32, 0x14000000, 0x1400FFFF, speed, speed); switch (addr) { case 0x1000f430: { uint8_t sa = (data >> 16) & 0xFFF; uint8_t sbc = (data >> 6) & 0xF; if ((sa == 0x21) && (sbc == 0x1) && ((bus->mch_drd >> 7) & 1) == 0) bus->rdram_sdevid = 0; bus->mch_ricm = data & ~0x80000000; } return; case 0x1000f440: { bus->mch_drd = data; } return; case 0x1000f100: case 0x1000f120: case 0x1000f140: case 0x1000f150: case 0x1000f400: case 0x1000f410: case 0x1000f420: case 0x1000f450: case 0x1000f460: case 0x1000f480: case 0x1000f490: case 0x1000f500: case 0x1000f510: case 0x1f80141c: return; } // fprintf(stderr, "bus: Unhandled 32-bit write to physical address 0x%08x (0x%08lx)\n", addr, data); if ((addr & 0xff000000) == 0x02000000) exit(1); } void ee_bus_write64(void* udata, uint32_t addr, uint64_t data) { struct ee_bus* bus = (struct ee_bus*)udata; void* ptr = bus->fastmem_w_table[addr >> 13]; if (likely(ptr)) { *((uint64_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data; return; } // MAP_MEM_WRITE(64, 0x00000000, 0x01FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(64, 0x20000000, 0x21FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(64, 0x30000000, 0x31FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(64, 0x1C000000, 0x1C1FFFFF, ram, iop_ram); // MAP_MEM_WRITE(64, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_REG_WRITE(64, 0x12000000, 0x12002000, gs, gs); MAP_REG_WRITE(64, 0x10002000, 0x1000203F, ipu, ipu); MAP_REG_WRITE(64, 0x10007000, 0x1000701F, ipu, ipu); MAP_REG_WRITE(32, 0x10008000, 0x1000EFFF, dmac, dmac); MAP_REG_WRITE(32, 0x1000F520, 0x1000F5FF, dmac, dmac); MAP_REG_WRITE(32, 0x10000000, 0x10001FFF, ee_timers, timers); // Reuse 32-bit function MAP_MEM_WRITE(64, 0x11000000, 0x11007FFF, vu, vu0); MAP_MEM_WRITE(64, 0x11008000, 0x1100FFFF, vu, vu1); MAP_MEM_WRITE(64, 0x1000F000, 0x1000F01F, intc, intc); printf("bus: Unhandled 64-bit write to physical address 0x%08x (0x%08lx%08lx)\n", addr, data >> 32, data & 0xffffffff); } void ee_bus_write128(void* udata, uint32_t addr, uint128_t data) { struct ee_bus* bus = (struct ee_bus*)udata; void* ptr = bus->fastmem_w_table[addr >> 13]; if (likely(ptr)) { *((uint128_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data; return; } // MAP_MEM_WRITE(128, 0x00000000, 0x01FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(128, 0x20000000, 0x21FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(128, 0x30000000, 0x31FFFFFF, ram, ee_ram); // MAP_MEM_WRITE(128, 0x1C000000, 0x1C1FFFFF, ram, iop_ram); // MAP_MEM_WRITE(128, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_REG_WRITE(128, 0x10006000, 0x10006FFF, gif, gif); MAP_REG_WRITE(128, 0x10007000, 0x1000701F, ipu, ipu); MAP_REG_WRITE(128, 0x10004000, 0x10004FFF, vif, vif0); MAP_REG_WRITE(128, 0x10005000, 0x10005FFF, vif, vif1); MAP_MEM_WRITE(128, 0x11000000, 0x11007FFF, vu, vu0); MAP_MEM_WRITE(128, 0x11008000, 0x1100FFFF, vu, vu1); // 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]); } ================================================ FILE: src/ee/bus.h ================================================ #ifndef EE_BUS_H #define EE_BUS_H #ifdef __cplusplus extern "C" { #endif #include "u128.h" #include "dmac.h" #include "gif.h" #include "intc.h" #include "timers.h" #include "vif.h" #include "ipu/ipu.h" #include "../iop/cdvd.h" #include "../iop/usb.h" #include "shared/ram.h" #include "shared/sif.h" #include "shared/bios.h" #include "shared/sbus.h" #include "shared/dev9.h" #include "shared/speed.h" struct ee_bus { // EE-only struct ps2_ram* ee_ram; struct ps2_dmac* dmac; struct ps2_gif* gif; struct ps2_gs* gs; struct ps2_ipu* ipu; struct ps2_intc* intc; struct ps2_vif* vif0; struct ps2_vif* vif1; struct vu_state* vu0; struct vu_state* vu1; struct ps2_ee_timers* timers; // EE/IOP struct ps2_cdvd* cdvd; struct ps2_usb* usb; struct ps2_bios* bios; struct ps2_bios* rom1; struct ps2_bios* rom2; struct ps2_ram* iop_ram; struct ps2_sif* sif; struct ps2_sbus* sbus; struct ps2_dev9* dev9; struct ps2_speed* speed; void* fastmem_r_table[0x10000]; void* fastmem_w_table[0x10000]; uint32_t mch_ricm; uint32_t mch_drd; uint32_t rdram_sdevid; void (*kputchar)(void*, char); void* kputchar_udata; }; void ee_bus_init_ram(struct ee_bus* bus, struct ps2_ram* ram); void ee_bus_init_dmac(struct ee_bus* bus, struct ps2_dmac* dmac); void ee_bus_init_intc(struct ee_bus* bus, struct ps2_intc* intc); void ee_bus_init_gif(struct ee_bus* bus, struct ps2_gif* gif); void ee_bus_init_vif0(struct ee_bus* bus, struct ps2_vif* vif0); void ee_bus_init_vif1(struct ee_bus* bus, struct ps2_vif* vif1); void ee_bus_init_gs(struct ee_bus* bus, struct ps2_gs* gs); void ee_bus_init_ipu(struct ee_bus* bus, struct ps2_ipu* ipu); void ee_bus_init_timers(struct ee_bus* bus, struct ps2_ee_timers* timers); void ee_bus_init_bios(struct ee_bus* bus, struct ps2_bios* bios); void ee_bus_init_rom1(struct ee_bus* bus, struct ps2_bios* rom1); void ee_bus_init_rom2(struct ee_bus* bus, struct ps2_bios* rom2); void ee_bus_init_iop_ram(struct ee_bus* bus, struct ps2_ram* iop_ram); void ee_bus_init_sif(struct ee_bus* bus, struct ps2_sif* sif); void ee_bus_init_cdvd(struct ee_bus* bus, struct ps2_cdvd* cdvd); void ee_bus_init_usb(struct ee_bus* bus, struct ps2_usb* usb); void ee_bus_init_sbus(struct ee_bus* bus, struct ps2_sbus* sbus); void ee_bus_init_dev9(struct ee_bus* bus, struct ps2_dev9* dev9); void ee_bus_init_speed(struct ee_bus* bus, struct ps2_speed* speed); void ee_bus_init_vu0(struct ee_bus* bus, struct vu_state* vu); void ee_bus_init_vu1(struct ee_bus* bus, struct vu_state* vu); void ee_bus_init_kputchar(struct ee_bus* bus, void (*kputchar)(void*, char), void* udata); void ee_bus_init_fastmem(struct ee_bus* bus, int ee_ram_size, int iop_ram_size); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/bus_decl.h ================================================ #ifndef EE_BUS_DECL_H #define EE_BUS_DECL_H #ifdef __cplusplus extern "C" { #endif #include "u128.h" struct ee_bus; struct ee_bus* ee_bus_create(void); void ee_bus_init(struct ee_bus* bus, const char* bios_path); void ee_bus_destroy(struct ee_bus* bus); uint64_t ee_bus_read8(void* udata, uint32_t addr); uint64_t ee_bus_read16(void* udata, uint32_t addr); uint64_t ee_bus_read32(void* udata, uint32_t addr); uint64_t ee_bus_read64(void* udata, uint32_t addr); uint128_t ee_bus_read128(void* udata, uint32_t addr); void ee_bus_write8(void* udata, uint32_t addr, uint64_t data); void ee_bus_write16(void* udata, uint32_t addr, uint64_t data); void ee_bus_write32(void* udata, uint32_t addr, uint64_t data); void ee_bus_write64(void* udata, uint32_t addr, uint64_t data); void ee_bus_write128(void* udata, uint32_t addr, uint128_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/dmac.c ================================================ #include #include #include #include #include "dmac.h" #define printf(fmt, ...)(0) static inline uint128_t dmac_read_qword(struct ps2_dmac* dmac, uint32_t addr) { int spr = addr & 0x80000000; if (!spr) return ee_bus_read128(dmac->bus, addr & 0xfffffff0); return ps2_ram_read128(dmac->spr, addr & 0x3ff0); } static inline void dmac_write_qword(struct ps2_dmac* dmac, uint32_t addr, int mem, uint128_t value) { int spr = mem || (addr & 0x80000000); if (!spr) { ee_bus_write128(dmac->bus, addr & 0xfffffff0, value); return; } ps2_ram_write128(dmac->spr, addr & 0x3ff0, value); } struct ps2_dmac* ps2_dmac_create(void) { return malloc(sizeof(struct ps2_dmac)); } void 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) { memset(dmac, 0, sizeof(struct ps2_dmac)); dmac->bus = bus; dmac->sif = sif; dmac->spr = spr; dmac->iop_dma = iop_dma; dmac->ee = ee; dmac->sched = sched; // v2+ BIOSes need this value on boot (smh...) dmac->enable = 0x1201; } void ps2_dmac_destroy(struct ps2_dmac* dmac) { free(dmac); } static inline struct dmac_channel* dmac_get_channel(struct ps2_dmac* dmac, uint32_t addr) { switch (addr & 0xff00) { case 0x8000: return &dmac->vif0; case 0x9000: return &dmac->vif1; case 0xA000: return &dmac->gif; case 0xB000: return &dmac->ipu_from; case 0xB400: return &dmac->ipu_to; case 0xC000: return &dmac->sif0; case 0xC400: return &dmac->sif1; case 0xC800: return &dmac->sif2; case 0xD000: return &dmac->spr_from; case 0xD400: return &dmac->spr_to; } return NULL; } static inline const char* dmac_get_channel_name(struct ps2_dmac* dmac, uint32_t addr) { switch (addr & 0xff00) { case 0x8000: return "vif0"; case 0x9000: return "vif1"; case 0xA000: return "gif"; case 0xB000: return "ipu_from"; case 0xB400: return "ipu_to"; case 0xC000: return "sif0"; case 0xC400: return "sif1"; case 0xC800: return "sif2"; case 0xD000: return "spr_from"; case 0xD400: return "spr_to"; } return NULL; } static inline int channel_is_done(struct dmac_channel* ch) { return ch->tag.end || (ch->tag.irq && (ch->chcr & 0x80)); } uint64_t ps2_dmac_read32(struct ps2_dmac* dmac, uint32_t addr) { struct dmac_channel* c = dmac_get_channel(dmac, addr); if (c) { switch (addr & 0xff) { case 0x00: return c->chcr; case 0x10: return c->madr; case 0x20: return c->qwc; case 0x30: return c->tadr; case 0x40: return c->asr0; case 0x50: return c->asr1; case 0x80: return c->sadr; } // printf("dmac: Unknown channel register %02x\n", addr & 0xff); return 0; } switch (addr) { case 0x1000E000: return dmac->ctrl; case 0x1000E010: return dmac->stat; case 0x1000E020: return dmac->pcr; case 0x1000E030: return dmac->sqwc; case 0x1000E040: return dmac->rbsr; case 0x1000E050: return dmac->rbor; case 0x1000F520: return dmac->enable; case 0x1000F590: break; // ENABLEW (W) } return 0; } static inline void dmac_process_source_tag(struct ps2_dmac* dmac, struct dmac_channel* c, uint128_t tag) { // Set CHCR TAG bytes c->chcr &= 0xffff; c->chcr |= tag.u32[0] & 0xffff0000; c->tag.qwc = TAG_QWC(tag); c->tag.pct = TAG_PCT(tag); c->tag.id = TAG_ID(tag); c->tag.irq = TAG_IRQ(tag); c->tag.addr = TAG_ADDR(tag); c->tag.data = TAG_DATA(tag); // if (dmac->mfifo_drain) // printf("ee: dmac tag %016lx %016lx qwc=%08x id=%d irq=%d addr=%08x mem=%d data=%016lx\n", // tag.u64[1], tag.u64[0], // c->tag.qwc, // c->tag.id, // c->tag.irq, // c->tag.addr, // c->tag.mem, // c->tag.data // ); c->tag.end = 0; c->qwc = c->tag.qwc; switch (c->tag.id) { case 0: { // REFE tag c->madr = c->tag.addr; c->tadr += 16; c->tag.end = 1; } break; case 1: { c->madr = c->tadr + 16; c->tadr = c->madr; } break; case 2: { c->madr = c->tadr + 16; c->tadr = c->tag.addr; } break; case 3: { c->madr = c->tag.addr; c->tadr += 16; } break; case 4: { c->madr = c->tag.addr; c->tadr += 16; } break; case 5: { c->madr = c->tadr + 16; int asp = (c->chcr >> 4) & 3; if (!asp) { c->asr0 = c->madr + (c->tag.qwc * 16); } else if (asp == 1) { c->asr1 = c->madr + (c->tag.qwc * 16); } c->tadr = c->tag.addr; c->chcr += 0x10; } break; case 6: { c->madr = c->tadr + 16; int asp = (c->chcr >> 4) & 3; if (asp == 2) { c->tadr = c->asr1; c->chcr -= 0x10; } else if (asp == 1) { c->tadr = c->asr0; c->chcr -= 0x10; } else { c->tag.end = 1; } } break; case 7: { c->madr = c->tadr + 16; c->tag.end = 1; } break; } // If TIE and TAG.IRQ are set, then end transfer if ((c->chcr & 0x80) && c->tag.irq) c->tag.end = 1; } static inline void dmac_process_dest_tag(struct ps2_dmac* dmac, struct dmac_channel* c, uint128_t tag) { // Set CHCR TAG bytes c->chcr &= 0xffff; c->chcr |= tag.u32[0] & 0xffff0000; c->tag.qwc = TAG_QWC(tag); c->tag.pct = TAG_PCT(tag); c->tag.id = TAG_ID(tag); c->tag.irq = TAG_IRQ(tag); c->tag.addr = TAG_ADDR(tag); c->tag.data = TAG_DATA(tag); c->qwc = c->tag.qwc; c->tag.end = dmac->sif0.tag.irq && (dmac->sif0.chcr & 0x80); switch (c->tag.id) { case 7: c->tag.end = 1; case 0: case 1: c->madr = c->tag.addr; } } static inline void dmac_test_cpcond0(struct ps2_dmac* dmac) { ee_set_cpcond0(dmac->ee, (((~dmac->pcr) | dmac->stat) & 0x3ff) == 0x3ff); } static inline void dmac_test_irq(struct ps2_dmac* dmac) { dmac_test_cpcond0(dmac); int meis = ((dmac->stat >> 14) & 1) & ((dmac->stat >> 30) & 1); int chirq = (dmac->stat & 0x3ff) & ((dmac->stat >> 16) & 0x3ff); ee_set_int1(dmac->ee, chirq || meis); } static inline void dmac_set_irq(struct ps2_dmac* dmac, int ch) { dmac->stat |= 1 << ch; // 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)); dmac_test_irq(dmac); } void dmac_handle_vif0_transfer(struct ps2_dmac* dmac) { // printf("ee: VIF0 DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x\n", // dmac->vif0.chcr & 1, // (dmac->vif0.chcr >> 2) & 3, // (dmac->vif0.chcr >> 6) & 1, // (dmac->vif0.chcr >> 7) & 1, // dmac->vif0.qwc, // dmac->vif0.madr, // dmac->vif0.tadr // ); int mode = (dmac->vif0.chcr >> 2) & 3; for (int i = 0; i < dmac->vif0.qwc; i++) { uint128_t q = dmac_read_qword(dmac, dmac->vif0.madr); // VIF0 FIFO address ee_bus_write128(dmac->bus, 0x10004000, q); dmac->vif0.madr += 16; } if (mode == 0) { dmac->vif0.chcr &= ~0x100; dmac->vif0.qwc = 0; dmac_set_irq(dmac, DMAC_VIF0); return; } // Chain mode do { uint128_t tag = dmac_read_qword(dmac, dmac->vif0.tadr); dmac_process_source_tag(dmac, &dmac->vif0, tag); // printf("ee: vif0 tag qwc=%08x madr=%08x tadr=%08x id=%d addr=%08x\n", // dmac->vif0.tag.qwc, // dmac->vif0.madr, // dmac->vif0.tadr, // dmac->vif0.tag.id, // dmac->vif0.tag.addr // ); // CHCR.TTE: Transfer tag DATA field if ((dmac->vif0.chcr >> 6) & 1) { ee_bus_write32(dmac->bus, 0x10004000, dmac->vif0.tag.data & 0xffffffff); ee_bus_write32(dmac->bus, 0x10004000, dmac->vif0.tag.data >> 32); } for (int i = 0; i < dmac->vif0.qwc; i++) { uint128_t q = dmac_read_qword(dmac, dmac->vif0.madr); // printf("ee: Sending %016lx%016lx from %08x to VIF0 FIFO\n", // q.u64[1], q.u64[0], // dmac->vif0.madr // ); ee_bus_write128(dmac->bus, 0x10004000, q); dmac->vif0.madr += 16; } if (dmac->vif0.tag.id == 1) { dmac->vif0.tadr = dmac->vif0.madr; } } while (!channel_is_done(&dmac->vif0)); dmac->vif0.chcr &= ~0x100; dmac->vif0.qwc = 0; dmac_set_irq(dmac, DMAC_VIF0); } void dmac_send_vif1_irq(void* udata, int overshoot) { struct ps2_dmac* dmac = (struct ps2_dmac*)udata; dmac->vif1.chcr &= ~0x100; dmac->vif1.qwc = 0; // printf("dmac: VIF1 interrupt\n"); dmac_set_irq(dmac, DMAC_VIF1); } void mfifo_handle_ref_tag(struct ps2_dmac* dmac) { struct dmac_channel* c = dmac->mfifo_drain; while (c->qwc) { uint128_t q = dmac_read_qword(dmac, c->madr); if (c == &dmac->vif1) { // VIF1 FIFO ee_bus_write128(dmac->bus, 0x10005000, q); } else { // GIF FIFO ps2_gif_fifo_write(dmac->bus->gif, q, GIF_PATH3); } c->madr += 16; c->qwc--; } if (channel_is_done(c)) { // fprintf(stdout, "dmac: mfifo channel done end=%d tte-irq=%d\n", c->tag.end, c->tag.irq && (c->chcr & 0x80)); dmac_set_irq(dmac, c == &dmac->vif1 ? DMAC_VIF1 : DMAC_GIF); c->chcr &= ~0x100; return; } if (c->tag.id == 1) { c->tadr = dmac->rbor | (c->madr & dmac->rbsr); } } void mfifo_write_qword(struct ps2_dmac* dmac, uint128_t q) { struct dmac_channel* c = dmac->mfifo_drain; if (c->qwc) { uint128_t q = dmac_read_qword(dmac, c->madr); if (c == &dmac->vif1) { // VIF1 FIFO ee_bus_write128(dmac->bus, 0x10005000, q); } else { // GIF FIFO ps2_gif_fifo_write(dmac->bus->gif, q, GIF_PATH3); } c->madr += 16; c->qwc--; // fprintf(stdout, "dmac: mfifo channel qwc=%d\n", c->qwc); if (c->qwc == 0) { if (channel_is_done(c)) { // fprintf(stdout, "dmac: mfifo channel done end=%d tte-irq=%d\n", c->tag.end, c->tag.irq && (c->chcr & 0x80)); dmac_set_irq(dmac, c == &dmac->vif1 ? DMAC_VIF1 : DMAC_GIF); c->chcr &= ~0x100; return; } if (c->tag.id == 1) { c->tadr = dmac->rbor | (c->madr & dmac->rbsr); } } return; } uint128_t tag = dmac_read_qword(dmac, c->tadr); dmac_process_source_tag(dmac, c, tag); if ((c->chcr >> 6) & 1) { ee_bus_write32(dmac->bus, 0x10005000, c->tag.data & 0xffffffff); ee_bus_write32(dmac->bus, 0x10005000, c->tag.data >> 32); } c->tadr = dmac->rbor | (c->tadr & dmac->rbsr); // 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); switch (c->tag.id) { case 1: case 2: case 5: case 6: case 7: { c->madr = dmac->rbor | (c->madr & dmac->rbsr); } break; default: { mfifo_handle_ref_tag(dmac); if (c->tadr == dmac->spr_from.madr) { // fprintf(stdout, "dmac: MFIFO empty\n"); dmac_set_irq(dmac, DMAC_MEIS); } } return; } if (c->qwc == 0) { if (channel_is_done(c)) { // fprintf(stdout, "dmac: mfifo channel done end=%d tte-irq=%d\n", c->tag.end, c->tag.irq && (c->chcr & 0x80)); dmac_set_irq(dmac, c == &dmac->vif1 ? DMAC_VIF1 : DMAC_GIF); c->chcr &= ~0x100; return; } } if (c->tadr == dmac->spr_from.madr) { // fprintf(stdout, "dmac: MFIFO empty\n"); dmac_set_irq(dmac, DMAC_MEIS); } } void dmac_handle_vif1_transfer(struct ps2_dmac* dmac) { // fprintf(stdout, "dmac: VIF1 DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x end=%d\n", // dmac->vif1.chcr & 1, // (dmac->vif1.chcr >> 2) & 3, // (dmac->vif1.chcr >> 6) & 1, // (dmac->vif1.chcr >> 7) & 1, // dmac->vif1.qwc, // dmac->vif1.madr, // dmac->vif1.tadr, // dmac->vif1.tag.end // ); int mfifo_drain = (dmac->ctrl >> 2) & 3; if (mfifo_drain == 2) { return; } int tte = (dmac->vif1.chcr >> 6) & 1; int mode = (dmac->vif1.chcr >> 2) & 3; if (mode == 3) mode = 1; struct sched_event event; event.name = "VIF1 DMA IRQ"; event.udata = dmac; event.callback = dmac_send_vif1_irq; event.cycles = 4000; // We don't handle VIF1 reads if ((dmac->vif1.chcr & 1) == 0) { // Gran Turismo 3 sends a VIF1 read with QWC=0, presumably to // wait until the GIF FIFO is actually full, so we shouldn't send // an interrupt there. if (dmac->vif1.qwc == 0) return; dmac->vif1.qwc = 0; sched_schedule(dmac->sched, event); return; } for (int i = 0; i < dmac->vif1.qwc; i++) { uint128_t q = dmac_read_qword(dmac, dmac->vif1.madr); // VIF1 FIFO address ee_bus_write32(dmac->bus, 0x10005000, q.u32[0]); ee_bus_write32(dmac->bus, 0x10005000, q.u32[1]); ee_bus_write32(dmac->bus, 0x10005000, q.u32[2]); ee_bus_write32(dmac->bus, 0x10005000, q.u32[3]); dmac->vif1.madr += 16; } dmac->vif1.qwc = 0; if (dmac->vif1.tag.end) { sched_schedule(dmac->sched, event); return; } // Chain mode do { uint128_t tag = dmac_read_qword(dmac, dmac->vif1.tadr); dmac_process_source_tag(dmac, &dmac->vif1, tag); // fprintf(stdout, "ee: vif1 tag qwc=%08x madr=%08x tadr=%08x id=%d addr=%08x data=%08x %08x tte=%d mem=%d\n", // dmac->vif1.tag.qwc, // dmac->vif1.madr, // dmac->vif1.tadr, // dmac->vif1.tag.id, // dmac->vif1.tag.addr, // dmac->vif1.tag.data & 0xffffffff, // dmac->vif1.tag.data >> 32, // (dmac->vif1.chcr >> 6) & 1, // dmac->vif1.tag.mem // ); // CHCR.TTE: Transfer tag DATA field if ((dmac->vif1.chcr >> 6) & 1) { ee_bus_write32(dmac->bus, 0x10005000, dmac->vif1.tag.data & 0xffffffff); ee_bus_write32(dmac->bus, 0x10005000, dmac->vif1.tag.data >> 32); } for (int i = 0; i < dmac->vif1.qwc; i++) { uint128_t q = dmac_read_qword(dmac, dmac->vif1.madr); ee_bus_write32(dmac->bus, 0x10005000, q.u32[0]); ee_bus_write32(dmac->bus, 0x10005000, q.u32[1]); ee_bus_write32(dmac->bus, 0x10005000, q.u32[2]); ee_bus_write32(dmac->bus, 0x10005000, q.u32[3]); dmac->vif1.madr += 16; } if (dmac->vif1.tag.id == 1) { dmac->vif1.tadr = dmac->vif1.madr; } } while (!channel_is_done(&dmac->vif1)); sched_schedule(dmac->sched, event); } void dmac_send_gif_irq(void* udata, int overshoot) { struct ps2_dmac* dmac = (struct ps2_dmac*)udata; dmac_set_irq(dmac, DMAC_GIF); dmac->gif.chcr &= ~0x100; dmac->gif.qwc = 0; } void dmac_handle_gif_transfer(struct ps2_dmac* dmac) { struct sched_event event; assert(((dmac->gif.chcr >> 6) & 1) == 0); int mode = (dmac->gif.chcr >> 2) & 3; event.name = "GIF DMA IRQ"; event.udata = dmac; event.callback = dmac_send_gif_irq; event.cycles = 1000; sched_schedule(dmac->sched, event); // fprintf(stderr, "dmac: GIF DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x\n", // dmac->gif.chcr & 1, // (dmac->gif.chcr >> 2) & 3, // (dmac->gif.chcr >> 6) & 1, // (dmac->gif.chcr >> 7) & 1, // dmac->gif.qwc, // dmac->gif.madr, // dmac->gif.tadr, // dmac->rbor, // dmac->rbsr, // dmac->spr_from.madr // ); int mfifo_drain = (dmac->ctrl >> 2) & 3; if (mfifo_drain == 3) return; // printf("ee: GIF DMA dir=%d mode=%d tte=%d tie=%d qwc=%d madr=%08x tadr=%08x\n", // dmac->gif.chcr & 1, // (dmac->gif.chcr >> 2) & 3, // (dmac->gif.chcr >> 6) & 1, // (dmac->gif.chcr >> 7) & 1, // dmac->gif.qwc, // dmac->gif.madr, // dmac->gif.tadr // ); for (int i = 0; i < dmac->gif.qwc; i++) { uint128_t q = dmac_read_qword(dmac, dmac->gif.madr); // fprintf(file, "ee: Sending %016lx%016lx from %08x to GIF FIFO (burst)\n", // q.u64[1], q.u64[0], // dmac->gif.madr // ); // GIF FIFO address ps2_gif_fifo_write(dmac->bus->gif, q, GIF_PATH3); dmac->gif.madr += 16; } if (dmac->gif.tag.end) { return; } // int id = (dmac->gif.chcr >> 28) & 7; // if ((mode == 1) && (id == 0 || id == 7) && dmac->gif.qwc) { // return; // } // Chain mode do { uint128_t tag = dmac_read_qword(dmac, dmac->gif.tadr); dmac_process_source_tag(dmac, &dmac->gif, tag); // 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); for (int i = 0; i < dmac->gif.qwc; i++) { uint128_t q = dmac_read_qword(dmac, dmac->gif.madr); // fprintf(file, "ee: Sending %016lx%016lx from %08x to GIF FIFO (chain)\n", // q.u64[1], q.u64[0], // dmac->gif.madr // ); ps2_gif_fifo_write(dmac->bus->gif, q, GIF_PATH3); dmac->gif.madr += 16; } if (dmac->gif.tag.id == 1) { dmac->gif.tadr = dmac->gif.madr; } } while (!channel_is_done(&dmac->gif)); } void dmac_handle_ipu_from_transfer(struct ps2_dmac* dmac) { if ((dmac->ipu_from.chcr & 0x100) == 0) { // printf("dmac: ipu_from channel not started\n"); return; } int mode = (dmac->ipu_from.chcr >> 2) & 3; // printf("dmac: ipu_from start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x dreq=%d\n", // dmac->ipu_from.chcr, // dmac->ipu_from.chcr & 1, // (dmac->ipu_from.chcr >> 2) & 3, // !!(dmac->ipu_from.chcr & 0x40), // dmac->ipu_from.madr, // dmac->ipu_from.qwc, // dmac->ipu_from.tadr, // dmac->ipu_from.dreq // ); if (mode != 0) { fprintf(stderr, "dmac: ipu_from mode %d not supported\n", mode); exit(1); return; } while (dmac->ipu_from.dreq && dmac->ipu_from.qwc) { uint128_t q = ee_bus_read128(dmac->bus, 0x10007000); dmac_write_qword(dmac, dmac->ipu_from.madr, 0, q); dmac->ipu_from.madr += 16; dmac->ipu_from.qwc--; } if (dmac->ipu_from.qwc == 0) { dmac_set_irq(dmac, DMAC_IPU_FROM); dmac->ipu_from.chcr &= ~0x100; dmac->ipu_from.qwc = 0; } } int dmac_transfer_ipu_to_qword(struct ps2_dmac* dmac) { if ((dmac->ipu_to.chcr & 0x100) == 0) { // printf("dmac: ipu_to channel not started\n"); return 0; } if (!dmac->ipu_to.dreq) { // printf("dmac: ipu_to dreq cleared\n"); return 0; } if (dmac->ipu_to.qwc) { uint128_t q = dmac_read_qword(dmac, dmac->ipu_to.madr); ee_bus_write128(dmac->bus, 0x10007010, q); dmac->ipu_to.madr += 16; dmac->ipu_to.qwc--; return 1; } if (channel_is_done(&dmac->ipu_to)) { dmac_set_irq(dmac, DMAC_IPU_TO); dmac->ipu_to.chcr &= ~0x100; dmac->ipu_to.qwc = 0; return 0; } if (dmac->ipu_to.tag.id == 1) { dmac->ipu_to.tadr = dmac->ipu_to.madr; fprintf(stderr, "dmac: ipu_to tag id=1, setting tadr to %08x\n", dmac->ipu_to.tadr); exit(1); } uint128_t tag = dmac_read_qword(dmac, dmac->ipu_to.tadr); dmac_process_source_tag(dmac, &dmac->ipu_to, tag); // 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", // dmac->ipu_to.tag.qwc, // dmac->ipu_to.qwc, // dmac->ipu_to.tag.id, // dmac->ipu_to.tag.irq, // dmac->ipu_to.tag.addr, // dmac->ipu_to.tag.mem, // dmac->ipu_to.tag.data, // dmac->ipu_to.tag.end, // (dmac->ipu_to.chcr >> 7) & 1 // ); return 1; } void dmac_handle_ipu_to_transfer(struct ps2_dmac* dmac) { if ((dmac->ipu_to.chcr & 0x100) == 0) { // printf("dmac: ipu_to channel not started\n"); return; } // printf("dmac: ipu_to start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x\n", // dmac->ipu_to.chcr, // dmac->ipu_to.chcr & 1, // (dmac->ipu_to.chcr >> 2) & 3, // !!(dmac->ipu_to.chcr & 0x40), // dmac->ipu_to.madr, // dmac->ipu_to.qwc, // dmac->ipu_to.tadr // ); while (dmac_transfer_ipu_to_qword(dmac)) { // Keep transferring until we run out of QWC or DREQ is cleared } } void dmac_handle_sif0_transfer(struct ps2_dmac* dmac) { // SIF FIFO is empty, keep waiting if (ps2_sif0_is_empty(dmac->sif)) { return; } // Data ready but channel isn't ready yet, keep waiting if (!(dmac->sif0.chcr & 0x100)) { return; } // fprintf(stdout, "dmac: sif0 start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x\n", // dmac->sif0.chcr, // dmac->sif0.chcr & 1, // (dmac->sif0.chcr >> 2) & 3, // !!(dmac->sif0.chcr & 0x40), // dmac->sif0.madr, // dmac->sif0.qwc, // dmac->sif0.tadr // ); while (!ps2_sif0_is_empty(dmac->sif)) { uint128_t tag = ps2_sif0_read(dmac->sif); dmac_process_dest_tag(dmac, &dmac->sif0, tag); // printf("ee: sif0 tag qwc=%08lx madr=%08lx id=%ld irq=%ld addr=%08lx mem=%ld data=%016lx tte=%d\n", // dmac->sif0.tag.qwc, // dmac->sif0.madr, // dmac->sif0.tag.id, // dmac->sif0.tag.irq, // dmac->sif0.tag.addr, // dmac->sif0.tag.mem, // dmac->sif0.tag.data, // dmac->sif0.chcr // ); for (int i = 0; i < dmac->sif0.qwc; i++) { if (ps2_sif0_is_empty(dmac->sif)) { printf("dmac: qwc != 0 FIFO empty\n"); if (channel_is_done(&dmac->sif0)) { printf("dmac: qwc != 0 FIFO empty\n"); dmac->sif0.chcr &= ~0x100; dmac->sif0.qwc = 0; dmac_set_irq(dmac, DMAC_SIF0); return; } } uint128_t q = ps2_sif0_read(dmac->sif); // printf("%08x: ", dmac->sif0.madr); // for (int i = 0; i < 16; i++) { // printf("%02x ", q.u8[i]); // } // putchar('|'); // for (int i = 0; i < 16; i++) { // printf("%c", isprint(q.u8[i]) ? q.u8[i] : '.'); // } // puts("|"); // printf("ee: Writing %016lx %016lx to %08x\n", q.u64[1], q.u64[0], dmac->sif0.madr); dmac_write_qword(dmac, dmac->sif0.madr, 0, q); dmac->sif0.madr += 16; } if (channel_is_done(&dmac->sif0)) { dmac->sif0.chcr &= ~0x100; dmac->sif0.qwc = 0; dmac_set_irq(dmac, DMAC_SIF0); // ps2_sif_fifo_reset(dmac->sif); return; } } // dmac->sif0.chcr &= ~0x100; // dmac_set_irq(dmac, DMAC_SIF0); // ps2_sif_fifo_reset(dmac->sif); // We shouldn't send an interrupt if tag end or irq/tie weren't // set } void dmac_handle_sif1_transfer(struct ps2_dmac* dmac) { assert(!dmac->sif1.qwc); assert(((dmac->sif1.chcr >> 2) & 3) == 1); // This should be ok? // if (!ps2_sif_fifo_is_empty(dmac->sif)) { // printf("dmac: WARNING!!! SIF FIFO not empty\n"); // } do { uint128_t tag = dmac_read_qword(dmac, dmac->sif1.tadr); dmac_process_source_tag(dmac, &dmac->sif1, tag); // printf("ee: sif1 tag qwc=%08lx id=%ld irq=%ld addr=%08lx mem=%ld data=%016lx end=%d tte=%d\n", // dmac->sif1.tag.qwc, // dmac->sif1.tag.id, // dmac->sif1.tag.irq, // dmac->sif1.tag.addr, // dmac->sif1.tag.mem, // dmac->sif1.tag.data, // dmac->sif1.tag.end, // (dmac->sif1.chcr >> 7) & 1 // ); // printf("ee: SIF1 tag madr=%08x\n", dmac->sif1.madr); for (int i = 0; i < dmac->sif1.qwc; i++) { uint128_t q = dmac_read_qword(dmac, dmac->sif1.madr); // printf("%08x: ", dmac->sif1.madr); // for (int i = 0; i < 16; i++) { // printf("%02x ", q.u8[i]); // } // putchar('|'); // for (int i = 0; i < 16; i++) { // printf("%c", isprint(q.u8[i]) ? q.u8[i] : '.'); // } // puts("|"); ps2_sif1_write(dmac->sif, q); dmac->sif1.madr += 16; } if (dmac->sif1.tag.id == 1) { dmac->sif1.tadr = dmac->sif1.madr; fprintf(stderr, "dmac: SIF1 tag id=1, setting TADR to MADR=%08x\n", dmac->sif1.madr); exit(1); } } while (!channel_is_done(&dmac->sif1)); iop_dma_handle_sif1_transfer(dmac->iop_dma); dmac_set_irq(dmac, DMAC_SIF1); dmac->sif1.chcr &= ~0x100; dmac->sif1.qwc = 0; } void dmac_handle_sif2_transfer(struct ps2_dmac* dmac) { fprintf(stderr, "ee: SIF2 channel unimplemented\n"); exit(1); } void dmac_spr_from_interleave(struct ps2_dmac* dmac) { uint32_t sqwc = dmac->sqwc & 0xff; uint32_t tqwc = (dmac->sqwc >> 16) & 0xff; // Note: When TQWC=0, it is set to QWC instead (undocumented) if (tqwc == 0) tqwc = dmac->spr_from.qwc; while (dmac->spr_from.qwc) { for (int i = 0; i < tqwc && dmac->spr_from.qwc; i++) { uint128_t q = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr); ee_bus_write128(dmac->bus, dmac->spr_from.madr, q); dmac->spr_from.madr += 0x10; dmac->spr_from.sadr += 0x10; dmac->spr_from.sadr &= 0x3ff0; dmac->spr_from.qwc--; } dmac->spr_from.madr += sqwc * 16; } } void dmac_handle_spr_from_transfer(struct ps2_dmac* dmac) { dmac_set_irq(dmac, DMAC_SPR_FROM); dmac->spr_from.chcr &= ~0x100; // 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", // dmac->spr_from.chcr, // dmac->spr_from.chcr & 1, // (dmac->spr_from.chcr >> 2) & 3, // !!(dmac->spr_from.chcr & 0x40), // dmac->spr_from.madr, // dmac->spr_from.qwc, // dmac->spr_from.tadr, // dmac->spr_from.sadr, // dmac->rbor, // dmac->rbsr // ); // exit(1); int mode = (dmac->spr_from.chcr >> 2) & 3; if (dmac->mfifo_drain) { assert(mode == 0); dmac->spr_from.madr = dmac->rbor | (dmac->spr_from.madr & dmac->rbsr); for (int i = 0; i < dmac->spr_from.qwc; i++) { uint128_t q = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr & 0x3ff0); ee_bus_write128(dmac->bus, dmac->spr_from.madr, q); mfifo_write_qword(dmac, q); dmac->spr_from.madr = dmac->rbor | ((dmac->spr_from.madr + 0x10) & dmac->rbsr); dmac->spr_from.sadr += 0x10; dmac->spr_from.sadr &= 0x3ff0; } dmac->spr_from.qwc = 0; return; } if (mode == 2) { dmac_spr_from_interleave(dmac); return; } for (int i = 0; i < dmac->spr_from.qwc; i++) { uint128_t q = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr & 0x3ff0); ee_bus_write128(dmac->bus, dmac->spr_from.madr, q); dmac->spr_from.madr += 0x10; dmac->spr_from.sadr += 0x10; dmac->spr_from.sadr &= 0x3ff0; } dmac->spr_from.qwc = 0; if (dmac->spr_from.tag.end) { return; } // Chain mode do { uint128_t tag = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr & 0x3ff0); dmac->spr_from.sadr += 0x10; dmac->spr_from.sadr &= 0x3ff0; dmac->spr_from.qwc = tag.u32[0] & 0xffff; dmac->spr_from.tag.id = (tag.u32[0] >> 28) & 0x7; dmac->spr_from.tag.irq = tag.u32[0] & 0x80000000; dmac->spr_from.tag.end = dmac->spr_from.tag.id == 0 || dmac->spr_from.tag.id == 7; dmac->spr_from.madr = tag.u32[1]; // 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", // dmac->spr_from.tag.qwc, // dmac->spr_from.madr, // dmac->spr_from.sadr, // dmac->spr_from.tadr, // dmac->spr_from.tag.id, // dmac->spr_from.tag.addr, // dmac->spr_from.tag.mem, // dmac->spr_from.tag.data, // dmac->spr_from.tag.irq, // dmac->spr_from.tag.end, // (dmac->spr_from.chcr >> 7) & 1 // ); for (int i = 0; i < dmac->spr_from.qwc; i++) { uint128_t q = ps2_ram_read128(dmac->spr, dmac->spr_from.sadr & 0x3ff0); ee_bus_write128(dmac->bus, dmac->spr_from.madr, q); dmac->spr_from.madr += 0x10; dmac->spr_from.sadr += 0x10; dmac->spr_from.sadr &= 0x3ff0; } } while (!channel_is_done(&dmac->spr_from)); } void dmac_spr_to_interleave(struct ps2_dmac* dmac) { uint32_t sqwc = dmac->sqwc & 0xff; uint32_t tqwc = (dmac->sqwc >> 16) & 0xff; // Note: When TQWC=0, it is set to QWC instead (undocumented) if (tqwc == 0) tqwc = dmac->spr_to.qwc; while (dmac->spr_to.qwc) { for (int i = 0; i < tqwc && dmac->spr_to.qwc; i++) { uint128_t q = dmac_read_qword(dmac, dmac->spr_to.madr); ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, q); dmac->spr_to.madr += 0x10; dmac->spr_to.sadr += 0x10; dmac->spr_to.sadr &= 0x3ff0; dmac->spr_to.qwc--; } dmac->spr_to.madr += sqwc * 16; } } void dmac_handle_spr_to_transfer(struct ps2_dmac* dmac) { dmac_set_irq(dmac, DMAC_SPR_TO); dmac->spr_to.chcr &= ~0x100; int mode = (dmac->spr_to.chcr >> 2) & 3; // printf("ee: spr_to start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x sadr=%08x\n", // dmac->spr_to.chcr, // dmac->spr_to.chcr & 1, // (dmac->spr_to.chcr >> 2) & 3, // !!(dmac->spr_to.chcr & 0x40), // dmac->spr_to.madr, // dmac->spr_to.qwc, // dmac->spr_to.tadr, // dmac->spr_to.sadr // ); if (mode == 2) { dmac_spr_to_interleave(dmac); return; } for (int i = 0; i < dmac->spr_to.qwc; i++) { uint128_t q = dmac_read_qword(dmac, dmac->spr_to.madr); ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, q); dmac->spr_to.madr += 0x10; dmac->spr_to.sadr += 0x10; dmac->spr_to.sadr &= 0x3ff0; } dmac->spr_to.qwc = 0; // We're done if (dmac->spr_to.tag.end) return; // Chain mode do { uint128_t tag = dmac_read_qword(dmac, dmac->spr_to.tadr); if ((dmac->spr_to.chcr >> 6) & 1) { ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, tag); dmac->spr_to.sadr += 0x10; } dmac_process_source_tag(dmac, &dmac->spr_to, tag); // printf("ee: spr_to tag qwc=%08lx madr=%08lx tadr=%08lx id=%ld addr=%08lx mem=%ld end=%d data=%08x%08x\n", // dmac->spr_to.tag.qwc, // dmac->spr_to.madr, // dmac->spr_to.tadr, // dmac->spr_to.tag.id, // dmac->spr_to.tag.addr, // dmac->spr_to.tag.mem, // dmac->spr_to.tag.end, // tag.u32[1], tag.u32[0] // ); for (int i = 0; i < dmac->spr_to.qwc; i++) { uint128_t q = dmac_read_qword(dmac, dmac->spr_to.madr); ps2_ram_write128(dmac->spr, dmac->spr_to.sadr, q); dmac->spr_to.madr += 0x10; dmac->spr_to.sadr += 0x10; dmac->spr_to.sadr &= 0x3ff0; } if (dmac->spr_to.tag.id == 1) { dmac->spr_to.tadr = dmac->spr_to.madr; } } while (!channel_is_done(&dmac->spr_to)); } static inline void dmac_handle_channel_start(struct ps2_dmac* dmac, uint32_t addr) { struct dmac_channel* c = dmac_get_channel(dmac, addr); // if (c == &dmac->ipu_to || c == &dmac->ipu_from) // printf("dmac: %s start data=%08x dir=%d mod=%d tte=%d madr=%08x qwc=%08x tadr=%08x rbsr=%08x rbor=%08x\n", // dmac_get_channel_name(dmac, addr), // c->chcr, // c->chcr & 1, // (c->chcr >> 2) & 3, // !!(c->chcr & 0x40), // c->madr, // c->qwc, // c->tadr, // dmac->rbsr, // dmac->rbor // ); // if (c == &dmac->ipu_to && c->qwc != 0) { // int mode = (c->chcr >> 2) & 3; // if (mode == 1) { // uint128_t tag; // tag.u32[0] = (c->chcr & 0xffff0000) | (c->qwc & 0xffff); // dmac_process_source_tag(dmac, c, tag); // } else { // c->tag.end = 1; // } // } int mode = (c->chcr >> 2) & 3; // Modes 1 and 3 are chain modes if ((mode & 1) == 0) { c->tag.end = 1; } else if (c->qwc != 0) { int id = c->chcr >> 28 & 7; int tie = (c->chcr >> 7) & 1; int irq = c->chcr & 0x80000000; c->tag.end = (id == 0 || id == 7) || (tie && irq); // printf("dmac: %s qwc != 0, madr=%08x tadr=%08x qwc=%08x tag=%08x end=%d\n", dmac_get_channel_name(dmac, addr), // c->madr, c->tadr, c->qwc, c->chcr >> 16, c->tag.end // ); } else { c->tag.end = 0; } switch (addr & 0xff00) { case 0x8000: dmac_handle_vif0_transfer(dmac); return; case 0x9000: dmac_handle_vif1_transfer(dmac); return; case 0xA000: dmac_handle_gif_transfer(dmac); return; case 0xB000: dmac_handle_ipu_from_transfer(dmac); return; case 0xB400: dmac_handle_ipu_to_transfer(dmac); return; case 0xC000: dmac_handle_sif0_transfer(dmac); return; case 0xC400: dmac_handle_sif1_transfer(dmac); return; case 0xC800: dmac_handle_sif2_transfer(dmac); return; case 0xD000: dmac_handle_spr_from_transfer(dmac); return; case 0xD400: dmac_handle_spr_to_transfer(dmac); return; } } void dmac_write_stat(struct ps2_dmac* dmac, uint32_t data) { uint32_t istat = data & 0x0000ffff; uint32_t imask = data & 0xffff0000; dmac->stat &= ~istat; dmac->stat ^= imask; // printf("dmac: stat=%08x istat=%08x imask=%08x\n", dmac->stat, istat, imask); dmac_test_irq(dmac); } void ps2_dmac_write32(struct ps2_dmac* dmac, uint32_t addr, uint64_t data) { struct dmac_channel* c = dmac_get_channel(dmac, addr); switch (addr) { case 0x1000E000: { dmac->ctrl = data; int mfifo_drain = (dmac->ctrl >> 2) & 3; int stall_ctrl = (dmac->ctrl >> 4) & 3; int stall_drain = (dmac->ctrl >> 6) & 3; if (mfifo_drain || stall_ctrl || stall_drain) { // fprintf(stdout, "dmac: 32-bit mfifo_drain=%d stall_ctrl=%d stall_drain=%d\n", // mfifo_drain, stall_ctrl, stall_drain // ); switch (mfifo_drain) { case 0: dmac->mfifo_drain = NULL; break; case 2: dmac->mfifo_drain = &dmac->vif1; break; case 3: dmac->mfifo_drain = &dmac->gif; break; default: fprintf(stderr, "dmac: Invalid MFIFO drain channel %d\n", mfifo_drain); exit(1); } } } return; case 0x1000E010: dmac_write_stat(dmac, data); return; case 0x1000E020: dmac->pcr = data; dmac_test_cpcond0(dmac); return; case 0x1000E030: dmac->sqwc = data; return; case 0x1000E040: dmac->rbsr = data; return; case 0x1000E050: dmac->rbor = data; return; case 0x1000F520: return; // ENABLER (R) case 0x1000F590: dmac->enable = data; return; } if (!c) return; switch (addr & 0xff) { case 0x00: { // Behavior required for IPU FMVs to work if ((c->chcr & 0x100) == 0) { c->chcr = data; if (data & 0x100) { dmac_handle_channel_start(dmac, addr); } } else { // printf("dmac: channel %s value=%08x chcr=%08x\n", dmac_get_channel_name(dmac, addr), data, c->chcr); c->chcr &= (data & 0x100) | 0xfffffeff; } } return; case 0x10: { c->madr = data; // Clear MADR's MSB on SPR channels if (c == &dmac->spr_to || c == &dmac->spr_from) { c->madr &= 0x7fffffff; } } return; case 0x20: c->qwc = data & 0xffff; return; case 0x30: c->tadr = data; return; case 0x40: c->asr0 = data; return; case 0x50: c->asr1 = data; return; case 0x80: c->sadr = data & 0x3ff0; return; } // printf("dmac: Unknown channel register %02x\n", addr & 0xff); return; } uint64_t ps2_dmac_read8(struct ps2_dmac* dmac, uint32_t addr) { if (addr == 0x10009000) { // printf("dmac: 8-bit read from chcr (%08x)\n", dmac->vif1.chcr & 0xff); return dmac->vif1.chcr & 0xff; } int shift = (addr & 0x3) * 8; return (ps2_dmac_read32(dmac, addr & ~0x3) >> shift) & 0xff; // struct dmac_channel* c = dmac_get_channel(dmac, addr & ~3); // if (!c) { // switch (addr) { // case 0x1000e000: { // return dmac->ctrl & 0xff; // } break; // } // printf("dmac: Unknown channel read8 at %08x\n", addr); // return 0; // } // switch (addr) { // case 0x10009000: // case 0x1000a000: // case 0x10008000: { // return c->chcr & 0xff; // } // case 0x10008001: // case 0x10009001: // case 0x1000a001: { // return (c->chcr >> 8) & 0xff; // } // } // printf("dmac: Unhandled 8-bit read from %08x\n", addr); // exit(1); // return 0; } void ps2_dmac_write8(struct ps2_dmac* dmac, uint32_t addr, uint64_t data) { struct dmac_channel* c = dmac_get_channel(dmac, addr & ~3); switch (addr) { case 0x10008000: case 0x10009000: case 0x1000a000: case 0x1000b000: case 0x1000b400: case 0x1000d400: { c->chcr &= 0xffffff00; c->chcr |= data & 0xff; return; } break; case 0x10008001: case 0x10009001: { ps2_dmac_write32(dmac, addr & ~0x3, (ps2_dmac_read32(dmac, addr & ~0x3) & 0xffff00ff) | ((data & 0xff) << 8)); // c->chcr &= 0xffff00ff; // c->chcr |= (data & 0xff) << 8; // if (c->chcr & 0x100) { // dmac_handle_channel_start(dmac, addr); // } return; } break; case 0x1000e000: { dmac->ctrl &= 0xffffff00; dmac->ctrl |= data; int mfifo_drain = (dmac->ctrl >> 2) & 3; int stall_ctrl = (dmac->ctrl >> 4) & 3; int stall_drain = (dmac->ctrl >> 6) & 3; if (mfifo_drain || stall_ctrl || stall_drain) { // fprintf(stdout, "dmac: 8-bit mfifo_drain=%d stall_ctrl=%d stall_drain=%d\n", // mfifo_drain, stall_ctrl, stall_drain // ); switch (mfifo_drain) { case 0: dmac->mfifo_drain = NULL; break; case 2: dmac->mfifo_drain = &dmac->vif1; break; case 3: dmac->mfifo_drain = &dmac->gif; break; default: fprintf(stderr, "dmac: Invalid MFIFO drain channel %d\n", mfifo_drain); exit(1); } } } return; // ENABLEW (byte 2) case 0x1000f592: { dmac->enable &= 0xff00ffff; dmac->enable |= (data & 0xff) << 16; } return; } fprintf(stderr, "dmac: 8-bit write to %08x (%02x)\n", addr, data); // exit(1); return; } uint64_t ps2_dmac_read16(struct ps2_dmac* dmac, uint32_t addr) { int shift = (addr & 2) * 16; addr = addr & ~3; return (ps2_dmac_read32(dmac, addr) >> shift) & 0xffff; } void ps2_dmac_write16(struct ps2_dmac* dmac, uint32_t addr, uint64_t data) { struct dmac_channel* c = dmac_get_channel(dmac, addr & ~3); switch (addr) { case 0x10008000: case 0x1000a000: case 0x1000d000: case 0x1000d400: case 0x1000d800: case 0x10009000: { if ((c->chcr & 0x100) == 0) { c->chcr &= 0xffff0000; c->chcr |= data & 0xffff; if (data & 0x100) { dmac_handle_channel_start(dmac, addr); } } else { // printf("dmac: channel %s value=%08x chcr=%08x\n", dmac_get_channel_name(dmac, addr), data, c->chcr); c->chcr &= (data & 0x100) | 0xfffffeff; } } return; } fprintf(stderr, "dmac: 16-bit write to %08x (%04x)\n", addr, data & 0xffff); exit(1); } ================================================ FILE: src/ee/dmac.h ================================================ struct ps2_dmac; #ifndef DMAC_H #define DMAC_H #ifdef __cplusplus extern "C" { #endif #include #include "u128.h" #include "shared/sif.h" #include "bus_decl.h" #include "ee.h" #include "iop/dma.h" #define TAG_QWC(d) (d.u64[0] & 0xffff) #define TAG_PCT(d) ((d.u64[0] >> 26) & 3) #define TAG_ID(d) ((d.u64[0] >> 28) & 7) #define TAG_IRQ(d) ((d.u64[0] >> 31) & 1) #define TAG_ADDR(d) ((d.u64[0] >> 32) & 0xfffffff0) #define TAG_DATA(d) (d.u64[1]) #define DMAC_VIF0 0 #define DMAC_VIF1 1 #define DMAC_GIF 2 #define DMAC_IPU_FROM 3 #define DMAC_IPU_TO 4 #define DMAC_SIF0 5 #define DMAC_SIF1 6 #define DMAC_SIF2 7 #define DMAC_SPR_FROM 8 #define DMAC_SPR_TO 9 #define DMAC_MEIS 14 struct dmac_tag { uint64_t qwc; uint64_t pct; uint64_t id; uint64_t irq; uint64_t addr; uint64_t data; int end; }; struct dmac_channel { uint32_t chcr; uint32_t madr; uint32_t tadr; uint32_t qwc; uint32_t asr0; uint32_t asr1; uint32_t sadr; int dreq; struct dmac_tag tag; }; struct ps2_dmac { struct ee_bus* bus; struct dmac_channel vif0; struct dmac_channel vif1; struct dmac_channel gif; struct dmac_channel ipu_from; struct dmac_channel ipu_to; struct dmac_channel sif0; struct dmac_channel sif1; struct dmac_channel sif2; struct dmac_channel spr_from; struct dmac_channel spr_to; struct dmac_channel* mfifo_drain; uint32_t ctrl; uint32_t stat; uint32_t pcr; uint32_t sqwc; uint32_t rbsr; uint32_t rbor; uint32_t enable; struct ps2_ram* spr; struct ps2_sif* sif; struct ps2_iop_dma* iop_dma; struct ee_state* ee; struct sched_state* sched; }; struct ps2_dmac* ps2_dmac_create(void); void 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); void ps2_dmac_destroy(struct ps2_dmac* dmac); uint64_t ps2_dmac_read8(struct ps2_dmac* dmac, uint32_t addr); uint64_t ps2_dmac_read16(struct ps2_dmac* dmac, uint32_t addr); uint64_t ps2_dmac_read32(struct ps2_dmac* dmac, uint32_t addr); void ps2_dmac_write8(struct ps2_dmac* dmac, uint32_t addr, uint64_t data); void ps2_dmac_write16(struct ps2_dmac* dmac, uint32_t addr, uint64_t data); void ps2_dmac_write32(struct ps2_dmac* dmac, uint32_t addr, uint64_t data); void dmac_handle_vif0_transfer(struct ps2_dmac* dmac); void dmac_handle_vif1_transfer(struct ps2_dmac* dmac); void dmac_handle_gif_transfer(struct ps2_dmac* dmac); void dmac_handle_ipu_from_transfer(struct ps2_dmac* dmac); void dmac_handle_ipu_to_transfer(struct ps2_dmac* dmac); void dmac_handle_sif0_transfer(struct ps2_dmac* dmac); void dmac_handle_sif1_transfer(struct ps2_dmac* dmac); void dmac_handle_sif2_transfer(struct ps2_dmac* dmac); void dmac_handle_spr_from_transfer(struct ps2_dmac* dmac); void dmac_handle_spr_to_transfer(struct ps2_dmac* dmac); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/ee.h ================================================ #ifndef EE_H #define EE_H #ifdef __cplusplus extern "C" { #endif #include #include "shared/ram.h" #include "u128.h" #include "vu.h" struct ee_bus_s { void* udata; uint64_t (*read8)(void* udata, uint32_t addr); uint64_t (*read16)(void* udata, uint32_t addr); uint64_t (*read32)(void* udata, uint32_t addr); uint64_t (*read64)(void* udata, uint32_t addr); uint128_t (*read128)(void* udata, uint32_t addr); void (*write8)(void* udata, uint32_t addr, uint64_t data); void (*write16)(void* udata, uint32_t addr, uint64_t data); void (*write32)(void* udata, uint32_t addr, uint64_t data); void (*write64)(void* udata, uint32_t addr, uint64_t data); void (*write128)(void* udata, uint32_t addr, uint128_t data); }; #define EE_SR_CU 0xf0000000 #define EE_SR_DEV 0x08000000 #define EE_SR_BEV 0x04000000 #define EE_SR_CH 0x00040000 #define EE_SR_EDI 0x00020000 #define EE_SR_EIE 0x00010000 #define EE_SR_IM7 0x00008000 #define EE_SR_BEM 0x00001000 #define EE_SR_IM3 0x00000800 #define EE_SR_IM2 0x00000400 #define EE_SR_KSU 0x00000018 #define EE_SR_ERL 0x00000004 #define EE_SR_EXL 0x00000002 #define EE_SR_IE 0x00000001 #define EE_CAUSE_BD 0x80000000 #define EE_CAUSE_BD2 0x40000000 #define EE_CAUSE_CE 0x30000000 #define EE_CAUSE_EXC2 0x00070000 #define EE_CAUSE_IP7 0x00008000 #define EE_CAUSE_IP3 0x00000800 #define EE_CAUSE_IP2 0x00000400 #define EE_CAUSE_EXC 0x0000007c #define CAUSE_EXC1_INT (0 << 2) #define CAUSE_EXC1_MOD (1 << 2) #define CAUSE_EXC1_TLBL (2 << 2) #define CAUSE_EXC1_TLBS (3 << 2) #define CAUSE_EXC1_ADEL (4 << 2) #define CAUSE_EXC1_ADES (5 << 2) #define CAUSE_EXC1_IBE (6 << 2) #define CAUSE_EXC1_DBE (7 << 2) #define CAUSE_EXC1_SYS (8 << 2) #define CAUSE_EXC1_BP (9 << 2) #define CAUSE_EXC1_RI (10 << 2) #define CAUSE_EXC1_CPU (11 << 2) #define CAUSE_EXC1_OV (12 << 2) #define CAUSE_EXC1_TR (13 << 2) #define CAUSE_EXC1_TLBIL (2 << 0) #define CAUSE_EXC1_TLBIS (3 << 0) #define CAUSE_EXC2_RES (0 << 16) #define CAUSE_EXC2_NMI (1 << 16) #define CAUSE_EXC2_PERFC (2 << 16) #define CAUSE_EXC2_DBG (3 << 16) #define EE_VEC_RESET 0xbfc00000 #define EE_VEC_TLB 0x00000000 #define EE_VEC_COUNTER 0x00000080 #define EE_VEC_DEBUG 0x00000100 #define EE_VEC_COMMON 0x00000180 #define EE_VEC_IRQ 0x00000200 #define FPU_FLG_C 0x00800000 #define FPU_FLG_I 0x00020000 #define FPU_FLG_D 0x00010000 #define FPU_FLG_O 0x00008000 #define FPU_FLG_U 0x00004000 #define FPU_FLG_SI 0x00000040 #define FPU_FLG_SD 0x00000020 #define FPU_FLG_SO 0x00000010 #define FPU_FLG_SU 0x00000008 /* 1 V0 - Even page valid. When not set, the memory referenced in this entry is not mapped. 2 D0 - Even page dirty. When not set, writes cause an exception. 3-5 C0 - Even page cache mode. 2=Uncached 3=Cached 7=Uncached accelerated 6-25 PFN0 - Even page frame number. 33 V1 - Odd page valid. 34 D1 - Odd page dirty. 35 C1 - Odd page cache mode. 38-57 PFN1 - Odd page frame number. 63 S - Scratchpad. When set, the virtual mapping goes to scratchpad instead of main memory. 64-71 ASID - Address Space ID. 76 G - Global. When set, ASID is ignored. 77-95 VPN2 - Virtual page number / 2. Even pages have a VPN of (VPN2 * 2) and odd pages have a VPN of (VPN2 * 2) + 1 109-120 MASK - Size of an even/odd page. */ struct ee_vtlb_entry { int v0; int d0; int c0; uint32_t pfn0; int v1; int d1; int c1; uint32_t pfn1; int s; int asid; int g; uint32_t vpn2; uint32_t mask; }; // Taken from PCSX2 struct ee_osd_config { /** 0=enabled, 1=disabled */ uint32_t spdif_mode : 1; /*00*/ /** 0=4:3, 1=fullscreen, 2=16:9 */ uint32_t screen_type : 2; /*01*/ /** 0=rgb(scart), 1=component */ uint32_t video_output : 1; /*03*/ /** 0=japanese, 1=english(non-japanese) */ /*04*/uint32_t jap_language : 1; /** Playstation driver settings. */ uint32_t ps1drv_config : 8; /*05*/ /** 0 = early Japanese OSD, 1 = OSD2, 2 = OSD2 with extended languages. * Early kernels cannot retain the value set in this field (Hence always 0). */ uint32_t version : 3; /*13*/ /** LANGUAGE_??? value */ uint32_t language : 5; /*16*/ /** timezone minutes offset from gmt */ uint32_t timezone_offset : 11; /*21*/ }; union ee_fpu_reg { float f; uint32_t u32; int32_t s32; }; struct ee_state; struct ee_state* ee_create(void); void ee_init(struct ee_state* ee, struct vu_state* vu0, struct vu_state* vu1, int ram_size, struct ee_bus_s bus); void ee_reset(struct ee_state* ee); void ee_destroy(struct ee_state* ee); void ee_set_int0(struct ee_state* ee, int v); void ee_set_int1(struct ee_state* ee, int v); void ee_set_cpcond0(struct ee_state* ee, int v); uint32_t ee_get_pc(struct ee_state* ee); struct ps2_ram* ee_get_spr(struct ee_state* ee); int ee_run_block(struct ee_state* ee, int cycles); int ee_step(struct ee_state* ee); void ee_set_fmv_skip(struct ee_state* ee, int v); void ee_reset_intc_reads(struct ee_state* ee); void ee_reset_csr_reads(struct ee_state* ee); void ee_flush_cache(struct ee_state* ee); void ee_set_ram_size(struct ee_state* ee, int ram_size); void ee_set_osd_config(struct ee_state* ee, struct ee_osd_config config); struct ee_osd_config ee_get_osd_config(struct ee_state* ee); #undef EE_ALIGNED16 #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/ee_cached.cpp ================================================ #include #include #include #include #include #include #include #include #ifdef _EE_USE_INTRINSICS #include #include #include #include #endif #include "ee.h" #include "vu.h" #include "ee_dis.h" #include "ee_def.hpp" #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) #if defined(__has_builtin) #if __has_builtin(__builtin_saddll_overflow) && __has_builtin(__builtin_saddll_overflow) #define SADDOVF64 __builtin_saddll_overflow #define SSUBOVF64 __builtin_ssubll_overflow #else #define SADDOVF64 __builtin_saddl_overflow #define SSUBOVF64 __builtin_ssubl_overflow #endif #else #define SADDOVF64 __builtin_saddll_overflow #define SSUBOVF64 __builtin_ssubll_overflow #endif #define VU_D_FLD (0x01e00000) #define VU_D_X (0x01000000) #define VU_D_Y (0x00800000) #define VU_D_Z (0x00400000) #define VU_D_W (0x00200000) // file = fopen("vu.dump", "a"); fprintf(file, #ins "\n"); fclose(file); #define VU_LOWER(ins) { ps2_vu_decode_lower(ee->vu0, i.opcode); vu_i_ ## ins(ee->vu0, &ee->vu0->lower); } #define VU_UPPER(ins) { ps2_vu_decode_upper(ee->vu0, i.opcode); vu_i_ ## ins(ee->vu0, &ee->vu0->upper); } #define VU_LOWER_TEMPLATE(ins) { \ ps2_vu_decode_lower(ee->vu0, i.opcode); \ switch ((i.opcode >> 21) & 0xf) { \ case 0: vu_i_ ## ins <0>(ee->vu0, &ee->vu0->lower); break; \ case 1: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 2: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 3: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 4: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 5: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 6: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 7: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 8: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 9: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 10: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 11: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 12: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 13: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 14: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ case 15: vu_i_ ## ins (ee->vu0, &ee->vu0->lower); break; \ default: __builtin_unreachable(); \ } } #define VU_UPPER_TEMPLATE(ins) { \ ps2_vu_decode_upper(ee->vu0, i.opcode); \ switch ((i.opcode >> 21) & 0xf) { \ case 0: vu_i_ ## ins <0>(ee->vu0, &ee->vu0->upper); break; \ case 1: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 2: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 3: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 4: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 5: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 6: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 7: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 8: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 9: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 10: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 11: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 12: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 13: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 14: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ case 15: vu_i_ ## ins (ee->vu0, &ee->vu0->upper); break; \ default: __builtin_unreachable(); \ } } static inline int fast_abs32(int a) { uint32_t m = a >> 31; return (a ^ m) + (m & 1); } static inline int16_t fast_abs16(int16_t a) { uint16_t m = a >> 15; return (a ^ m) + (m & 1); } static inline int16_t saturate16(int32_t word) { if (word > (int32_t)0x00007FFF) { return 0x7FFF; } else if (word < (int32_t)0xFFFF80000) { return 0x8000; } else { return (int16_t)word; } } static inline int32_t saturate32(int64_t word) { if (word > (int32_t)0x7FFFFFFF) { return 0x7FFFFFFF; } else if (word < (int32_t)0x80000000) { return 0x80000000; } else { return (int32_t)word; } } #ifdef _EE_USE_INTRINSICS static inline __m128i _mm_adds_epi32(__m128i a, __m128i b) { const __m128i m = _mm_set1_epi32(0x7fffffff); __m128i r = _mm_add_epi32(a, b); __m128i sb = _mm_srli_epi32(a, 31); __m128i sat = _mm_add_epi32(m, sb); __m128i sx = _mm_xor_si128(a, b); __m128i o = _mm_andnot_si128(sx, _mm_xor_si128(a, r)); // To-do: Use SSE3 version when SSE4.1 isn't available return _mm_castps_si128(_mm_blendv_ps(_mm_castsi128_ps(r), _mm_castsi128_ps(sat), _mm_castsi128_ps(o))); } static inline __m128i _mm_adds_epu32(__m128i a, __m128i b) { const __m128i m = _mm_set1_epi32(0xffffffff); __m128i x = _mm_xor_si128(a, m); __m128i c = _mm_min_epu32(b, x); return _mm_add_epi32(a, c); } #endif static inline uint32_t unpack_5551_8888(uint32_t v) { return ((v & 0x001f) << 3) | ((v & 0x03e0) << 6) | ((v & 0x7c00) << 9) | ((v & 0x8000) << 16); } #define EE_KUSEG 0 #define EE_KSEG0 1 #define EE_KSEG1 2 #define EE_KSSEG 3 #define EE_KSEG3 4 /* i.rs = (opcode >> 21) & 0x1f; i.rt = (opcode >> 16) & 0x1f; i.rd = (opcode >> 11) & 0x1f; i.fd = (opcode >> 6) & 0x1f; i.i15 = (opcode >> 6) & 0x7fff; i.i16 = opcode & 0xffff; i.i26 = opcode & 0x3ffffff; */ #define EE_D_RS (i.rs) #define EE_D_FS (i.rd) #define EE_D_RT (i.rt) #define EE_D_RD (i.rd) #define EE_D_FD (i.sa) #define EE_D_SA (i.sa) #define EE_D_I15 (i.i15) #define EE_D_I16 (i.i16) #define EE_D_I26 (i.i26) #define EE_D_SI26 ((int32_t)(EE_D_I26 << 6) >> 4) #define EE_D_SI16 ((int32_t)(EE_D_I16 << 16) >> 14) #define EE_RT ee->r[EE_D_RT].ul64 #define EE_RD ee->r[EE_D_RD].ul64 #define EE_RS ee->r[EE_D_RS].ul64 #define EE_RT32 ee->r[EE_D_RT].ul32 #define EE_RD32 ee->r[EE_D_RD].ul32 #define EE_RS32 ee->r[EE_D_RS].ul32 #define EE_FD ee->f[EE_D_FD].f #define EE_FT (fpu_cvtf(ee->f[EE_D_RT].f)) #define EE_FS (fpu_cvtf(ee->f[EE_D_FS].f)) #define EE_FT32 ee->f[EE_D_RT].u32 #define EE_FD32 ee->f[EE_D_FD].u32 #define EE_FS32 ee->f[EE_D_FS].u32 #define EE_HI0 ee->hi.u64[0] #define EE_LO0 ee->lo.u64[0] #define EE_HI1 ee->hi.u64[1] #define EE_LO1 ee->lo.u64[1] #define BRANCH(cond, offset) if (cond) { \ ee->next_pc = ee->next_pc + (offset); \ ee->next_pc = ee->next_pc - 4; \ ee->branch = 1; \ ee->branch_taken = 1; } #define BRANCH_LIKELY(cond, offset) \ BRANCH(cond, offset) else { ee->exception = 1; ee->pc += 4; ee->next_pc += 4; } #define SE6432(v) ((int64_t)((int32_t)(v))) #define SE6416(v) ((int64_t)((int16_t)(v))) #define SE648(v) ((int64_t)((int8_t)(v))) #define SE3216(v) ((int32_t)((int16_t)(v))) static inline void ee_print_disassembly(struct ee_state* ee, const ee_instruction& i) { char buf[128]; struct ee_dis_state ds; ds.print_address = 1; ds.print_opcode = 1; ds.pc = ee->pc; puts(ee_disassemble(buf, i.opcode, &ds)); } static inline int ee_get_segment(uint32_t virt) { switch (virt & 0xe0000000) { case 0x00000000: return EE_KUSEG; case 0x20000000: return EE_KUSEG; case 0x40000000: return EE_KUSEG; case 0x60000000: return EE_KUSEG; case 0x80000000: return EE_KSEG0; case 0xa0000000: return EE_KSEG1; case 0xc0000000: return EE_KSSEG; case 0xe0000000: return EE_KSEG3; } return EE_KUSEG; } const uint32_t ee_bus_region_mask_table[] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff, 0x1fffffff, 0xffffffff, 0xffffffff }; static inline float fpu_cvtf(float f) { uint32_t u32 = *(uint32_t*)&f; switch (u32 & 0x7f800000) { case 0x0: { u32 &= 0x80000000; return *(float*)&u32; } break; case 0x7f800000: { uint32_t result = (u32 & 0x80000000) | 0x7f7fffff; return *(float*)&result; } } return *(float*)&u32; } static inline float fpu_cvtsw(union ee_fpu_reg* reg) { switch (reg->u32 & 0x7F800000) { case 0x0: { reg->u32 &= 0x80000000; } break; case 0x7F800000: { reg->u32 = (reg->u32 & 0x80000000) | 0x7F7FFFFF; } break; } return reg->f; } static inline void fpu_cvtws(union ee_fpu_reg* d, union ee_fpu_reg* s) { if ((s->u32 & 0x7F800000) <= 0x4E800000) d->s32 = (int32_t)fpu_cvtf(s->f); else if ((s->u32 & 0x80000000) == 0) d->u32 = 0x7FFFFFFF; else d->u32 = 0x80000000; } static inline int fpu_check_overflow(struct ee_state* ee, union ee_fpu_reg* reg) { if ((reg->u32 & ~0x80000000) == 0x7f800000) { reg->u32 = (reg->u32 & 0x80000000) | 0x7f7fffff; ee->fcr |= FPU_FLG_O | FPU_FLG_SO; return 1; } ee->fcr &= ~FPU_FLG_O; return 0; } static inline int fpu_check_underflow(struct ee_state* ee, union ee_fpu_reg* reg) { if (((reg->u32 & 0x7F800000) == 0) && ((reg->u32 & 0x007FFFFF) != 0)) { reg->u32 &= 0x80000000; ee->fcr |= FPU_FLG_U | FPU_FLG_SU; return 1; } ee->fcr &= ~FPU_FLG_U; return 0; } static inline int fpu_check_overflow_no_flags(struct ee_state* ee, union ee_fpu_reg* reg) { if ((reg->u32 & ~0x80000000) == 0x7f800000) { reg->u32 = (reg->u32 & 0x80000000) | 0x7f7fffff; return 1; } return 0; } static inline int fpu_check_underflow_no_flags(struct ee_state* ee, union ee_fpu_reg* reg) { if (((reg->u32 & 0x7F800000) == 0) && ((reg->u32 & 0x007FFFFF) != 0)) { reg->u32 &= 0x80000000; return 1; } return 0; } static inline int fpu_max(int32_t a, int32_t b) { return (a < 0 && b < 0) ? min(a, b) : max(a, b); } static inline int fpu_min(int32_t a, int32_t b) { return (a < 0 && b < 0) ? max(a, b) : min(a, b); } void ee_exception_level1(struct ee_state* ee, uint32_t cause); #ifdef _EE_USE_MMU static inline struct ee_vtlb_entry* ee_search_vtlb(struct ee_state* ee, uint32_t virt) { for (int i = 0; i < 48; i++) { struct ee_vtlb_entry* e = &ee->vtlb[i]; if (e->s) { uint32_t mask = 0xffffc000; if ((virt & mask) == (e->vpn2 & mask)) { return e; } } uint32_t mask = (~e->mask) & 0xffffe000; // printf("ee: TLB search index=%d virt=%08x vpn2=%08x mask=%08x\n", // i, // virt, // e->vpn2, // mask // ); if ((virt & mask) == (e->vpn2 & mask)) { return e; } } return nullptr; } static inline int ee_translate_virt(struct ee_state* ee, uint32_t virt, uint32_t* phys, int load) { int seg = ee_get_segment(virt); // Assume we're in kernel mode if (seg == EE_KSEG0 || seg == EE_KSEG1) { *phys = virt & 0x1fffffff; return 0; } struct ee_vtlb_entry* entry = ee_search_vtlb(ee, virt); if (!entry) { ee_exception_level1(ee, load ? CAUSE_EXC1_TLBL : CAUSE_EXC1_TLBS); ee->context &= 0x7ffff0; ee->context |= (virt & 0xFFFFE000) >> 9; printf("ee: TLB miss on %s at virt=%08x\n", load ? "load" : "store", virt); return -1; } if (entry->s) { *phys = virt & 0x00003fff; return 1; } // 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", // virt, // entry->vpn2, // entry->pfn0, // entry->v0, // entry->d0, // entry->pfn1, // entry->v1, // entry->d1, // entry->mask, // entry->s, // entry->g // ); uint32_t nmask = 0xfffff000 & ~(entry->mask >> 1); uint32_t pfn = entry->pfn0; int odd = (virt & ((entry->mask >> 1) + 0x1000)) ? 1 : 0; if (odd) { pfn = entry->pfn1; } *phys = pfn | (virt & ~nmask); // printf("ee: Translated virt=%08x to phys=%08x\n", virt, *phys); // if (odd) exit(1); return 0; } #define BUS_READ_FUNC(b) \ static inline uint64_t bus_read ## b(struct ee_state* ee, uint32_t addr) { \ uint32_t phys; \ if (ee_translate_virt(ee, addr, &phys, 1) == 1) \ return ps2_ram_read ## b(ee->spr, phys); \ if (phys == 0x1000f000) ee->intc_reads++; \ if (phys == 0x12001000) ee->csr_reads++; \ return ee->bus.read ## b(ee->bus.udata, phys); \ } #define BUS_WRITE_FUNC(b) \ static inline void bus_write ## b(struct ee_state* ee, uint32_t addr, uint64_t data) { \ uint32_t phys; \ if (ee_translate_virt(ee, addr, &phys, 0) == 1) \ { ps2_ram_write ## b(ee->spr, phys, data); return; } \ ee->bus.write ## b(ee->bus.udata, phys, data); \ } BUS_READ_FUNC(8) BUS_READ_FUNC(16) BUS_READ_FUNC(32) BUS_READ_FUNC(64) static inline uint128_t bus_read128(struct ee_state* ee, uint32_t addr) { uint32_t phys; if (ee_translate_virt(ee, addr, &phys, 1) == 1) return ps2_ram_read128(ee->spr, phys); return ee->bus.read128(ee->bus.udata, phys); } BUS_WRITE_FUNC(8) BUS_WRITE_FUNC(16) BUS_WRITE_FUNC(32) BUS_WRITE_FUNC(64) static inline void bus_write128(struct ee_state* ee, uint32_t addr, uint128_t data) { uint32_t phys; if (ee_translate_virt(ee, addr, &phys, 0) == 1) { ps2_ram_write128(ee->spr, phys, data); return; } ee->bus.write128(ee->bus.udata, phys, data); } #else static inline int ee_translate_virt(struct ee_state* ee, uint32_t virt, uint32_t* phys) { int seg = ee_get_segment(virt); // Assume we're in kernel mode if (seg == EE_KSEG0 || seg == EE_KSEG1) { *phys = virt & 0x1fffffff; return 0; } if (virt >= 0x00000000 && virt <= ee->ram_size) { *phys = virt & ee->ram_size; return 0; } if (virt >= 0x10000000 && virt <= 0x1FFFFFFF) { *phys = virt & 0x1FFFFFFF; return 0; } if (virt >= 0x20000000 && virt <= (0x20000000 | ee->ram_size)) { *phys = virt & ee->ram_size; return 0; } if (virt >= 0x30000000 && virt <= (0x30000000 | ee->ram_size)) { *phys = virt & ee->ram_size; return 0; } // DECI2 area if (virt >= 0xFFFF8000) { *phys = (virt - 0xFFFF8000) + 0x78000; return 0; } *phys = virt & ee_bus_region_mask_table[virt >> 29]; // printf("ee: Unhandled virtual address %08x @ cyc=%ld pc=%08x\n", virt, ee->total_cycles, ee->pc); // *(int*)0 = 0; // exit(1); // To-do: MMU mapping *phys = virt & 0x1fffffff; return 0; } #ifndef _EE_CACHE_PAGESIZE #define _EE_CACHE_PAGESIZE 512 #endif #define EE_CACHE_PAGECOUNT (0x100000000ull / _EE_CACHE_PAGESIZE) #define INVALIDATE_CACHE_PAGE(addr) { \ if (ee->block_cache[(addr) / _EE_CACHE_PAGESIZE]) { \ delete[] ee->block_cache[(addr) / _EE_CACHE_PAGESIZE]; \ ee->block_cache[(addr) / _EE_CACHE_PAGESIZE] = nullptr; \ } \ } #define BUS_READ_FUNC(b) \ static inline uint64_t bus_read ## b(struct ee_state* ee, uint32_t addr) { \ if ((addr & 0xf0000000) == 0x70000000) \ return ps2_ram_read ## b(ee->spr, addr & 0x3fff); \ uint32_t phys; \ ee_translate_virt(ee, addr, &phys); \ if (phys == 0x1000f000) ee->intc_reads++; \ if (phys == 0x12001000) ee->csr_reads++; \ return ee->bus.read ## b(ee->bus.udata, phys); \ } #define BUS_WRITE_FUNC(b) \ static inline void bus_write ## b(struct ee_state* ee, uint32_t addr, uint64_t data) { \ INVALIDATE_CACHE_PAGE(addr); \ if ((addr & 0xf0000000) == 0x70000000) \ { ps2_ram_write ## b(ee->spr, addr & 0x3fff, data); return; } \ uint32_t phys; \ ee_translate_virt(ee, addr, &phys); \ ee->bus.write ## b(ee->bus.udata, phys, data); \ } BUS_READ_FUNC(8) BUS_READ_FUNC(16) BUS_READ_FUNC(32) BUS_READ_FUNC(64) static inline uint128_t bus_read128(struct ee_state* ee, uint32_t addr) { if ((addr & 0xf0000000) == 0x70000000) return ps2_ram_read128(ee->spr, addr & 0x3ff0); uint32_t phys; ee_translate_virt(ee, addr, &phys); return ee->bus.read128(ee->bus.udata, phys); } BUS_WRITE_FUNC(8) BUS_WRITE_FUNC(16) BUS_WRITE_FUNC(32) BUS_WRITE_FUNC(64) static inline void bus_write128(struct ee_state* ee, uint32_t addr, uint128_t data) { INVALIDATE_CACHE_PAGE(addr); if ((addr & 0xf0000000) == 0x70000000) { ps2_ram_write128(ee->spr, addr & 0x3ff0, data); return; } uint32_t phys; ee_translate_virt(ee, addr, &phys); ee->bus.write128(ee->bus.udata, phys, data); } #endif #undef BUS_READ_FUNC #undef BUS_WRITE_FUNC static inline int ee_skip_fmv(struct ee_state* ee, uint32_t addr) { if (bus_read32(ee, addr + 4) != 0x03E00008) return 0; uint32_t code = bus_read32(ee, addr); uint32_t p1 = 0x8c800040; uint32_t p2 = 0x8c020000 | (code & 0x1f0000) << 5; if ((code & 0xffe0ffff) != p1) { return 0; } if (bus_read32(ee, addr + 8) != p2) { return 0; } printf("ee: Skipping FMV\n"); return 1; } static inline void ee_set_pc(struct ee_state* ee, uint32_t addr) { if (ee->fmv_skip) { if (ee_skip_fmv(ee, addr)) return; } ee->pc = addr; ee->next_pc = addr + 4; } static inline void ee_set_pc_delayed(struct ee_state* ee, uint32_t addr) { if (ee->fmv_skip) { if (ee_skip_fmv(ee, addr)) return; } ee->next_pc = addr; ee->branch = 1; } void ee_exception_level1(struct ee_state* ee, uint32_t cause) { ee->exception = 1; uint32_t vec = EE_VEC_COMMON; switch (cause) { case CAUSE_EXC1_TLBL: case CAUSE_EXC1_TLBS: vec = EE_VEC_TLB; break; case CAUSE_EXC1_TLBIL: case CAUSE_EXC1_TLBIS: cause <<= 2; break; case CAUSE_EXC1_INT: vec = EE_VEC_IRQ; break; } ee->cause &= ~EE_CAUSE_EXC; ee->cause |= cause; if (!(ee->status & EE_SR_EXL)) { ee->epc = ee->pc - 4; if (ee->delay_slot) { ee->epc -= 4; ee->cause |= EE_CAUSE_BD; } else { ee->cause &= ~EE_CAUSE_BD; } } ee->status |= EE_SR_EXL; uint32_t addr = ((ee->status & EE_SR_BEV) ? 0xbfc00200 : 0x80000000) + vec; ee_set_pc(ee, addr); } static inline void ee_exception_level2(struct ee_state* ee, uint32_t cause) { uint32_t vec; ee->cause &= ~EE_CAUSE_EXC2; ee->cause |= cause; ee->errorepc = ee->pc - 4; if (ee->delay_slot) { ee->errorepc -= 4; ee->cause |= EE_CAUSE_BD2; } else { ee->cause &= ~EE_CAUSE_BD2; } ee->status |= EE_SR_ERL; if ((cause == CAUSE_EXC2_RES) | (cause == CAUSE_EXC2_NMI)) { ee_set_pc(ee, EE_VEC_RESET); return; } if (cause == CAUSE_EXC2_PERFC) { vec = EE_VEC_COUNTER; } else { vec = EE_VEC_DEBUG; } ee_set_pc(ee, ((ee->status & EE_SR_DEV) ? 0xbfc00200 : 0x80000000) + vec); } static inline int ee_check_irq(struct ee_state* ee) { int irq_enabled = (ee->status & EE_SR_IE) && (ee->status & EE_SR_EIE) && (!(ee->status & EE_SR_EXL)) && (!(ee->status & EE_SR_ERL)); int int0_pending = (ee->status & EE_SR_IM2) && (ee->cause & EE_CAUSE_IP2); int int1_pending = (ee->status & EE_SR_IM3) && (ee->cause & EE_CAUSE_IP3); if (irq_enabled && (int0_pending || int1_pending)) { ee->pc += 4; // printf("ee: Handling irq at pc=%08x (int0=%d (%d) int1=%d (%d)) sr=%08x delay_slot=%d\n", // ee->pc, // int0_pending, !!(ee->status & EE_SR_IM2), // int1_pending, !!(ee->status & EE_SR_IM3), // ee->status, // ee->delay_slot // ); ee->intc_reads = 0; ee_exception_level1(ee, CAUSE_EXC1_INT); return 1; } return 0; } void ee_set_int0(struct ee_state* ee, int v) { if (v) { ee->cause |= EE_CAUSE_IP2; } else { ee->cause &= ~EE_CAUSE_IP2; } } void ee_set_int1(struct ee_state* ee, int v) { if (v) { ee->cause |= EE_CAUSE_IP3; } else { ee->cause &= ~EE_CAUSE_IP3; } } void ee_set_cpcond0(struct ee_state* ee, int v) { ee->cpcond0 = v; } static inline void ee_i_abss(struct ee_state* ee, const ee_instruction& i) { ee->f[EE_D_FD].u32 = ee->f[EE_D_FS].u32 & 0x7fffffff; // EE_FD = fabsf(EE_FS); } static inline void ee_i_add(struct ee_state* ee, const ee_instruction& i) { int32_t s = EE_RS; int32_t t = EE_RT; int32_t r = s + t; uint32_t o = (s ^ r) & (t ^ r); if (o & 0x80000000) { ee_exception_level1(ee, CAUSE_EXC1_OV); } else { EE_RD = SE6432(r); } } static inline void ee_i_addas(struct ee_state* ee, const ee_instruction& i) { ee->a.f = EE_FS + EE_FT; if (fpu_check_overflow(ee, &ee->a)) return; fpu_check_underflow(ee, &ee->a); } static inline void ee_i_addi(struct ee_state* ee, const ee_instruction& i) { int32_t s = EE_RS; int32_t t = SE3216(EE_D_I16); int32_t r; if (__builtin_sadd_overflow(s, t, &r)) { ee_exception_level1(ee, CAUSE_EXC1_OV); } else { EE_RT = SE6432(r); } } static inline void ee_i_addiu(struct ee_state* ee, const ee_instruction& i) { EE_RT = SE6432(EE_RS32 + SE3216(EE_D_I16)); } static inline void ee_i_adds(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_FD; ee->f[d].f = EE_FS + EE_FT; if (fpu_check_overflow(ee, &ee->f[d])) return; fpu_check_underflow(ee, &ee->f[d]); } static inline void ee_i_addu(struct ee_state* ee, const ee_instruction& i) { EE_RD = SE6432(EE_RS + EE_RT); } static inline void ee_i_and(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_RS & EE_RT; } static inline void ee_i_andi(struct ee_state* ee, const ee_instruction& i) { EE_RT = EE_RS & EE_D_I16; } static inline void ee_i_bc0f(struct ee_state* ee, const ee_instruction& i) { BRANCH(!ee->cpcond0, EE_D_SI16); } static inline void ee_i_bc0fl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY(!ee->cpcond0, EE_D_SI16); } static inline void ee_i_bc0t(struct ee_state* ee, const ee_instruction& i) { BRANCH(ee->cpcond0, EE_D_SI16); } static inline void ee_i_bc0tl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY(ee->cpcond0, EE_D_SI16); } static inline void ee_i_bc1f(struct ee_state* ee, const ee_instruction& i) { BRANCH((ee->fcr & FPU_FLG_C) == 0, EE_D_SI16); } static inline void ee_i_bc1fl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY((ee->fcr & FPU_FLG_C) == 0, EE_D_SI16); } static inline void ee_i_bc1t(struct ee_state* ee, const ee_instruction& i) { BRANCH((ee->fcr & FPU_FLG_C) != 0, EE_D_SI16); } static inline void ee_i_bc1tl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY((ee->fcr & FPU_FLG_C) != 0, EE_D_SI16); } static inline void ee_i_bc2f(struct ee_state* ee, const ee_instruction& i) { BRANCH(1, EE_D_SI16); } static inline void ee_i_bc2fl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY(1, EE_D_SI16); } static inline void ee_i_bc2t(struct ee_state* ee, const ee_instruction& i) { BRANCH(0, EE_D_SI16); } static inline void ee_i_bc2tl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY(0, EE_D_SI16); } static inline void ee_i_beq(struct ee_state* ee, const ee_instruction& i) { BRANCH(EE_RS == EE_RT, EE_D_SI16); } static inline void ee_i_beql(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY(EE_RS == EE_RT, EE_D_SI16); } static inline void ee_i_bgez(struct ee_state* ee, const ee_instruction& i) { BRANCH((int64_t)EE_RS >= (int64_t)0, EE_D_SI16); } static inline void ee_i_bgezal(struct ee_state* ee, const ee_instruction& i) { ee->r[31].ul64 = ee->next_pc; BRANCH((int64_t)EE_RS >= (int64_t)0, EE_D_SI16); } static inline void ee_i_bgezall(struct ee_state* ee, const ee_instruction& i) { ee->r[31].ul64 = ee->next_pc; BRANCH_LIKELY((int64_t)EE_RS >= (int64_t)0, EE_D_SI16); } static inline void ee_i_bgezl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY((int64_t)EE_RS >= (int64_t)0, EE_D_SI16); } static inline void ee_i_bgtz(struct ee_state* ee, const ee_instruction& i) { BRANCH((int64_t)EE_RS > (int64_t)0, EE_D_SI16); } static inline void ee_i_bgtzl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY((int64_t)EE_RS > (int64_t)0, EE_D_SI16); } static inline void ee_i_blez(struct ee_state* ee, const ee_instruction& i) { BRANCH((int64_t)EE_RS <= (int64_t)0, EE_D_SI16); } static inline void ee_i_blezl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY((int64_t)EE_RS <= (int64_t)0, EE_D_SI16); } static inline void ee_i_bltz(struct ee_state* ee, const ee_instruction& i) { BRANCH((int64_t)EE_RS < (int64_t)0, EE_D_SI16); } static inline void ee_i_bltzal(struct ee_state* ee, const ee_instruction& i) { ee->r[31].ul64 = ee->next_pc; BRANCH((int64_t)EE_RS < (int64_t)0, EE_D_SI16); } static inline void ee_i_bltzall(struct ee_state* ee, const ee_instruction& i) { ee->r[31].ul64 = ee->next_pc; BRANCH_LIKELY((int64_t)EE_RS < (int64_t)0, EE_D_SI16); } static inline void ee_i_bltzl(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY((int64_t)EE_RS < (int64_t)0, EE_D_SI16); } static inline void ee_i_bne(struct ee_state* ee, const ee_instruction& i) { BRANCH(EE_RS != EE_RT, EE_D_SI16); } static inline void ee_i_bnel(struct ee_state* ee, const ee_instruction& i) { BRANCH_LIKELY(EE_RS != EE_RT, EE_D_SI16); } static inline void ee_i_break(struct ee_state* ee, const ee_instruction& i) { ee_exception_level1(ee, CAUSE_EXC1_BP); } static inline void ee_i_cache(struct ee_state* ee, const ee_instruction& i) { /* To-do: Cache emulation */ switch (EE_D_RT) { // CACHE.IXIN case 0x07: { ee->block_cache.clear(); } break; } } static inline void ee_i_ceq(struct ee_state* ee, const ee_instruction& i) { if (EE_FS == EE_FT) { ee->fcr |= FPU_FLG_C; } else { ee->fcr &= ~FPU_FLG_C; } } static inline void ee_i_cf(struct ee_state* ee, const ee_instruction& i) { ee->fcr &= ~FPU_FLG_C; } static inline void ee_i_cfc1(struct ee_state* ee, const ee_instruction& i) { EE_RT = SE6432((EE_D_FS >= 16) ? ee->fcr : 0x2e30); } static inline void ee_i_cfc2(struct ee_state* ee, const ee_instruction& i) { EE_RT = SE6432(ps2_vu_read_vi(ee->vu0, EE_D_RD)); } static inline void ee_i_cle(struct ee_state* ee, const ee_instruction& i) { if (EE_FS <= EE_FT) { ee->fcr |= FPU_FLG_C; } else { ee->fcr &= ~FPU_FLG_C; } } static inline void ee_i_clt(struct ee_state* ee, const ee_instruction& i) { if (EE_FS < EE_FT) { ee->fcr |= FPU_FLG_C; } else { ee->fcr &= ~FPU_FLG_C; } } static inline void ee_i_ctc1(struct ee_state* ee, const ee_instruction& i) { if (EE_D_FS < 16) return; ee->fcr = EE_RT32; // (ee->fcr & ~(0x83c078)) | (EE_RT & 0x83c078); } static inline void ee_i_ctc2(struct ee_state* ee, const ee_instruction& i) { // To-do: Handle FBRST, VPU_STAT, CMSAR1 int d = EE_D_RD; static const char* regs[] = { "Status flag", "MAC flag", "clipping flag", "reserved", "R", "I", "Q", "reserved", "reserved", "reserved", "TPC", "CMSAR0", "FBRST", "VPU-STAT", "reserved", "CMSAR1", }; ps2_vu_write_vi(ee->vu0, d, EE_RT32); } static inline void ee_i_cvts(struct ee_state* ee, const ee_instruction& i) { EE_FD = (float)ee->f[EE_D_FS].s32; EE_FD = fpu_cvtsw(&ee->f[EE_D_FD]); } static inline void ee_i_cvtw(struct ee_state* ee, const ee_instruction& i) { fpu_cvtws(&ee->f[EE_D_FD], &ee->f[EE_D_FS]); } static inline void ee_i_dadd(struct ee_state* ee, const ee_instruction& i) { long long r; if (SADDOVF64((int64_t)EE_RS, (int64_t)EE_RT, &r)) { ee_exception_level1(ee, CAUSE_EXC1_OV); } else { EE_RD = r; } } static inline void ee_i_daddi(struct ee_state* ee, const ee_instruction& i) { long long r; if (SADDOVF64((int64_t)EE_RS, SE6416(EE_D_I16), &r)) { ee_exception_level1(ee, CAUSE_EXC1_OV); } else { EE_RT = r; } } static inline void ee_i_daddiu(struct ee_state* ee, const ee_instruction& i) { EE_RT = EE_RS + SE6416(EE_D_I16); } static inline void ee_i_daddu(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_RS + EE_RT; } static inline void ee_i_di(struct ee_state* ee, const ee_instruction& i) { int edi = ee->status & EE_SR_EDI; int exl = ee->status & EE_SR_EXL; int erl = ee->status & EE_SR_ERL; int ksu = ee->status & EE_SR_KSU; if (edi || exl || erl || !ksu) ee->status &= ~EE_SR_EIE; } static inline void ee_i_div(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int s = EE_D_RS; if (ee->r[s].ul32 == 0x80000000 && ee->r[t].ul32 == 0xffffffff) { EE_LO0 = (int32_t)0x80000000; EE_HI0 = 0; } else if (ee->r[t].ul32 != 0) { EE_HI0 = SE6432(ee->r[s].sl32 % ee->r[t].sl32); EE_LO0 = SE6432(ee->r[s].sl32 / ee->r[t].sl32); } else { EE_HI0 = SE6432(ee->r[s].ul32); EE_LO0 = ((int32_t)ee->r[s].ul32 < 0) ? 1 : -1; } } static inline void ee_i_div1(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int s = EE_D_RS; if (ee->r[s].ul32 == 0x80000000 && ee->r[t].ul32 == 0xffffffff) { EE_LO1 = (int32_t)0x80000000; EE_HI1 = 0; } else if (ee->r[t].ul32 != 0) { EE_HI1 = SE6432(ee->r[s].sl32 % ee->r[t].sl32); EE_LO1 = SE6432(ee->r[s].sl32 / ee->r[t].sl32); } else { EE_HI1 = SE6432(ee->r[s].ul32); EE_LO1 = ((int32_t)ee->r[s].ul32 < 0) ? 1 : -1; } } static inline void ee_i_divs(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int d = EE_D_FD; int s = EE_D_FS; ee->fcr &= ~(FPU_FLG_I | FPU_FLG_D); // If both the dividend and divisor are zero, set I/SI, // else set D/SD if ((ee->f[t].u32 & 0x7F800000) == 0) { if ((ee->f[s].u32 & 0x7F800000) == 0) { ee->fcr |= FPU_FLG_I | FPU_FLG_SI; } else { ee->fcr |= FPU_FLG_D | FPU_FLG_SD; } ee->f[d].u32 = ((ee->f[t].u32 ^ ee->f[s].u32) & 0x80000000) | 0x7f7fffff; return; } ee->f[d].f = EE_FS / EE_FT; if (fpu_check_overflow_no_flags(ee, &ee->f[d])) return; fpu_check_underflow_no_flags(ee, &ee->f[d]); } static inline void ee_i_divu(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int s = EE_D_RS; if (!ee->r[t].ul32) { EE_LO0 = -1; EE_HI0 = SE6432(ee->r[s].ul32); return; } EE_HI0 = SE6432(ee->r[s].ul32 % ee->r[t].ul32); EE_LO0 = SE6432(ee->r[s].ul32 / ee->r[t].ul32); } static inline void ee_i_divu1(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int s = EE_D_RS; if (!ee->r[t].ul32) { EE_LO1 = -1; EE_HI1 = SE6432(ee->r[s].ul32); return; } EE_HI1 = SE6432(ee->r[s].ul32 % ee->r[t].ul32); EE_LO1 = SE6432(ee->r[s].ul32 / ee->r[t].ul32); } static inline void ee_i_dsll(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_RT << EE_D_SA; } static inline void ee_i_dsll32(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_RT << (EE_D_SA + 32); } static inline void ee_i_dsllv(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_RT << (EE_RS & 0x3f); } static inline void ee_i_dsra(struct ee_state* ee, const ee_instruction& i) { EE_RD = ((int64_t)EE_RT) >> EE_D_SA; } static inline void ee_i_dsra32(struct ee_state* ee, const ee_instruction& i) { EE_RD = ((int64_t)EE_RT) >> (EE_D_SA + 32); } static inline void ee_i_dsrav(struct ee_state* ee, const ee_instruction& i) { EE_RD = ((int64_t)EE_RT) >> (EE_RS & 0x3f); } static inline void ee_i_dsrl(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_RT >> EE_D_SA; } static inline void ee_i_dsrl32(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_RT >> (EE_D_SA + 32); } static inline void ee_i_dsrlv(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_RT >> (EE_RS & 0x3f); } static inline void ee_i_dsub(struct ee_state* ee, const ee_instruction& i) { long long r; if (SSUBOVF64((int64_t)EE_RS, (int64_t)EE_RT, &r)) { ee_exception_level1(ee, CAUSE_EXC1_OV); } else { EE_RD = r; } } static inline void ee_i_dsubu(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_RS - EE_RT; } static inline void ee_i_ei(struct ee_state* ee, const ee_instruction& i) { int edi = ee->status & EE_SR_EDI; int exl = ee->status & EE_SR_EXL; int erl = ee->status & EE_SR_ERL; int ksu = ee->status & EE_SR_KSU; if (edi || exl || erl || !ksu) ee->status |= EE_SR_EIE; } static inline void ee_i_eret(struct ee_state* ee, const ee_instruction& i) { if (ee->status & EE_SR_ERL) { ee_set_pc(ee, ee->errorepc); ee->status &= ~EE_SR_ERL; } else { ee_set_pc(ee, ee->epc); ee->status &= ~EE_SR_EXL; } } static inline void ee_i_j(struct ee_state* ee, const ee_instruction& i) { ee_set_pc_delayed(ee, (ee->next_pc & 0xf0000000) | (EE_D_I26 << 2)); } static inline void ee_i_jal(struct ee_state* ee, const ee_instruction& i) { ee->r[31].ul64 = ee->next_pc; ee_set_pc_delayed(ee, (ee->next_pc & 0xf0000000) | (EE_D_I26 << 2)); } static inline void ee_i_jalr(struct ee_state* ee, const ee_instruction& i) { uint32_t next_pc = ee->next_pc; ee_set_pc_delayed(ee, EE_RS32); EE_RD = next_pc; } static inline void ee_i_jr(struct ee_state* ee, const ee_instruction& i) { ee_set_pc_delayed(ee, EE_RS32); } static inline void ee_i_lb(struct ee_state* ee, const ee_instruction& i) { EE_RT = SE648(bus_read8(ee, EE_RS32 + SE3216(EE_D_I16))); } static inline void ee_i_lbu(struct ee_state* ee, const ee_instruction& i) { EE_RT = bus_read8(ee, EE_RS32 + SE3216(EE_D_I16)); } static inline void ee_i_ld(struct ee_state* ee, const ee_instruction& i) { EE_RT = bus_read64(ee, EE_RS32 + SE3216(EE_D_I16)); } static inline void ee_i_ldl(struct ee_state* ee, const ee_instruction& i) { static const uint8_t ldl_shift[8] = { 56, 48, 40, 32, 24, 16, 8, 0 }; static const uint64_t ldl_mask[8] = { 0x00ffffffffffffffULL, 0x0000ffffffffffffULL, 0x000000ffffffffffULL, 0x00000000ffffffffULL, 0x0000000000ffffffULL, 0x000000000000ffffULL, 0x00000000000000ffULL, 0x0000000000000000ULL }; uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t shift = addr & 7; uint64_t data = bus_read64(ee, addr & ~7); EE_RT = (EE_RT & ldl_mask[shift]) | (data << ldl_shift[shift]); } static inline void ee_i_ldr(struct ee_state* ee, const ee_instruction& i) { static const uint8_t ldr_shift[8] = { 0, 8, 16, 24, 32, 40, 48, 56 }; static const uint64_t ldr_mask[8] = { 0x0000000000000000ULL, 0xff00000000000000ULL, 0xffff000000000000ULL, 0xffffff0000000000ULL, 0xffffffff00000000ULL, 0xffffffffff000000ULL, 0xffffffffffff0000ULL, 0xffffffffffffff00ULL }; uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t shift = addr & 7; uint64_t data = bus_read64(ee, addr & ~7); EE_RT = (EE_RT & ldr_mask[shift]) | (data >> ldr_shift[shift]); } static inline void ee_i_lh(struct ee_state* ee, const ee_instruction& i) { EE_RT = SE6416(bus_read16(ee, EE_RS32 + SE3216(EE_D_I16))); } static inline void ee_i_lhu(struct ee_state* ee, const ee_instruction& i) { EE_RT = bus_read16(ee, EE_RS32 + SE3216(EE_D_I16)); } static inline void ee_i_lq(struct ee_state* ee, const ee_instruction& i) { ee->r[EE_D_RT] = bus_read128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf); } static inline void ee_i_lqc2(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RT; if (!d) return; ee->vu0->vf[EE_D_RT].u128 = bus_read128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf); } static inline void ee_i_lui(struct ee_state* ee, const ee_instruction& i) { EE_RT = SE6432(EE_D_I16 << 16); } static inline void ee_i_lw(struct ee_state* ee, const ee_instruction& i) { EE_RT = SE6432(bus_read32(ee, EE_RS32 + SE3216(EE_D_I16))); } static inline void ee_i_lwc1(struct ee_state* ee, const ee_instruction& i) { EE_FT32 = bus_read32(ee, EE_RS32 + SE3216(EE_D_I16)); } static const uint32_t LWL_MASK[4] = { 0x00ffffff, 0x0000ffff, 0x000000ff, 0x00000000 }; static const uint32_t LWR_MASK[4] = { 0x00000000, 0xff000000, 0xffff0000, 0xffffff00 }; static const int LWL_SHIFT[4] = { 24, 16, 8, 0 }; static const int LWR_SHIFT[4] = { 0, 8, 16, 24 }; static inline void ee_i_lwl(struct ee_state* ee, const ee_instruction& i) { uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t shift = addr & 3; uint32_t mem = bus_read32(ee, addr & ~3); // ensure the compiler does correct sign extension into 64 bits by using s32 EE_RT = (int32_t)((EE_RT32 & LWL_MASK[shift]) | (mem << LWL_SHIFT[shift])); } static inline void ee_i_lwr(struct ee_state* ee, const ee_instruction& i) { uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t shift = addr & 3; uint32_t data = bus_read32(ee, addr & ~3); // Use unsigned math here, and conditionally sign extend below, when needed. data = (EE_RT32 & LWR_MASK[shift]) | (data >> LWR_SHIFT[shift]); if (!shift) { // This special case requires sign extension into the full 64 bit dest. EE_RT = (int32_t)data; } else { // This case sets the lower 32 bits of the target register. Upper // 32 bits are always preserved. EE_RT32 = data; } // printf("lwr mem=%08x reg=%016lx addr=%08x shift=%d\n", data, ee->r[EE_D_RT].u64[0], addr, shift); } static inline void ee_i_lwu(struct ee_state* ee, const ee_instruction& i) { EE_RT = bus_read32(ee, EE_RS32 + SE3216(EE_D_I16)); } static inline void ee_i_madd(struct ee_state* ee, const ee_instruction& i) { uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32); uint64_t d = (uint64_t)ee->lo.u32[0] | (ee->hi.u64[0] << 32); d += r; EE_LO0 = SE6432(d & 0xffffffff); EE_HI0 = SE6432(d >> 32); EE_RD = EE_LO0; } static inline void ee_i_madd1(struct ee_state* ee, const ee_instruction& i) { uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32); uint64_t d = (EE_LO1 & 0xffffffff) | (EE_HI1 << 32); d += r; EE_LO1 = SE6432(d & 0xffffffff); EE_HI1 = SE6432(d >> 32); EE_RD = EE_LO1; } static inline void ee_i_maddas(struct ee_state* ee, const ee_instruction& i) { ee->a.f += EE_FS * EE_FT; if (fpu_check_overflow(ee, &ee->a)) return; fpu_check_underflow(ee, &ee->a); } static inline void ee_i_madds(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int d = EE_D_FD; int s = EE_D_FS; float temp = fpu_cvtf(ee->f[s].f) * fpu_cvtf(ee->f[t].f); ee->f[d].f = fpu_cvtf(ee->a.f) + fpu_cvtf(temp); if (fpu_check_overflow(ee, &ee->f[d])) return; fpu_check_underflow(ee, &ee->f[d]); } static inline void ee_i_maddu(struct ee_state* ee, const ee_instruction& i) { uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32; uint64_t d = (uint64_t)ee->lo.u32[0] | (ee->hi.u64[0] << 32); d += r; EE_LO0 = SE6432(d & 0xffffffff); EE_HI0 = SE6432(d >> 32); EE_RD = EE_LO0; } static inline void ee_i_maddu1(struct ee_state* ee, const ee_instruction& i) { uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32; uint64_t d = (uint64_t)ee->lo.u32[2] | (ee->hi.u64[1] << 32); d += r; EE_LO1 = SE6432(d & 0xffffffff); EE_HI1 = SE6432(d >> 32); EE_RD = EE_LO1; } static inline void ee_i_maxs(struct ee_state* ee, const ee_instruction& i) { ee->f[EE_D_FD].u32 = fpu_max(ee->f[EE_D_FS].u32, ee->f[EE_D_RT].u32); ee->fcr &= ~(FPU_FLG_O | FPU_FLG_U); } static inline void ee_i_mfc0(struct ee_state* ee, const ee_instruction& i) { EE_RT = SE6432(ee->cop0_r[EE_D_RD]); } static inline void ee_i_mfc1(struct ee_state* ee, const ee_instruction& i) { EE_RT = SE6432(EE_FS32); } static inline void ee_i_mfhi(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_HI0; } static inline void ee_i_mfhi1(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_HI1; } static inline void ee_i_mflo(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_LO0; } static inline void ee_i_mflo1(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_LO1; } static inline void ee_i_mfsa(struct ee_state* ee, const ee_instruction& i) { EE_RD = ee->sa & 0xf; } static inline void ee_i_mins(struct ee_state* ee, const ee_instruction& i) { ee->f[EE_D_FD].u32 = fpu_min(ee->f[EE_D_FS].u32, ee->f[EE_D_RT].u32); ee->fcr &= ~(FPU_FLG_O | FPU_FLG_U); } static inline void ee_i_movn(struct ee_state* ee, const ee_instruction& i) { if (EE_RT) EE_RD = EE_RS; } static inline void ee_i_movs(struct ee_state* ee, const ee_instruction& i) { EE_FD32 = EE_FS32; } static inline void ee_i_movz(struct ee_state* ee, const ee_instruction& i) { if (!EE_RT) EE_RD = EE_RS; } static inline void ee_i_msubas(struct ee_state* ee, const ee_instruction& i) { ee->a.f -= EE_FS * EE_FT; if (fpu_check_overflow(ee, &ee->a)) return; fpu_check_underflow(ee, &ee->a); } static inline void ee_i_msubs(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int d = EE_D_FD; int s = EE_D_FS; float temp = fpu_cvtf(ee->f[s].f) * fpu_cvtf(ee->f[t].f); ee->f[d].f = fpu_cvtf(ee->a.f) - fpu_cvtf(temp); if (fpu_check_overflow(ee, &ee->f[d])) return; fpu_check_underflow(ee, &ee->f[d]); } static inline void ee_i_mtc0(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; if (d == 4) { ee->cop0_r[4] &= 0x7ffff0; ee->cop0_r[4] |= (EE_RT32 & 0xff800000); } else { ee->cop0_r[EE_D_RD] = EE_RT32; } } static inline void ee_i_mtc1(struct ee_state* ee, const ee_instruction& i) { EE_FS32 = EE_RT32; } static inline void ee_i_mthi(struct ee_state* ee, const ee_instruction& i) { EE_HI0 = EE_RS; } static inline void ee_i_mthi1(struct ee_state* ee, const ee_instruction& i) { EE_HI1 = EE_RS; } static inline void ee_i_mtlo(struct ee_state* ee, const ee_instruction& i) { EE_LO0 = EE_RS; } static inline void ee_i_mtlo1(struct ee_state* ee, const ee_instruction& i) { EE_LO1 = EE_RS; } static inline void ee_i_mtsa(struct ee_state* ee, const ee_instruction& i) { ee->sa = ((uint32_t)EE_RS) & 0xf; } static inline void ee_i_mtsab(struct ee_state* ee, const ee_instruction& i) { ee->sa = (EE_RS ^ EE_D_I16) & 15; } static inline void ee_i_mtsah(struct ee_state* ee, const ee_instruction& i) { ee->sa = ((EE_RS ^ EE_D_I16) & 7) << 1; } static inline void ee_i_mulas(struct ee_state* ee, const ee_instruction& i) { ee->a.f = EE_FS * EE_FT; if (fpu_check_overflow(ee, &ee->a)) return; fpu_check_underflow(ee, &ee->a); } static inline void ee_i_muls(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_FD; ee->f[d].f = EE_FS * EE_FT; if (fpu_check_overflow(ee, &ee->f[d])) return; fpu_check_underflow(ee, &ee->f[d]); } static inline void ee_i_mult(struct ee_state* ee, const ee_instruction& i) { uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32); EE_LO0 = SE6432(r & 0xffffffff); EE_HI0 = SE6432(r >> 32); EE_RD = EE_LO0; } static inline void ee_i_mult1(struct ee_state* ee, const ee_instruction& i) { uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32); EE_LO1 = SE6432(r & 0xffffffff); EE_HI1 = SE6432(r >> 32); EE_RD = EE_LO1; } static inline void ee_i_multu(struct ee_state* ee, const ee_instruction& i) { uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32; EE_LO0 = SE6432(r & 0xffffffff); EE_HI0 = SE6432(r >> 32); EE_RD = EE_LO0; } static inline void ee_i_multu1(struct ee_state* ee, const ee_instruction& i) { uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32; EE_LO1 = SE6432(r & 0xffffffff); EE_HI1 = SE6432(r >> 32); EE_RD = EE_LO1; } static inline void ee_i_negs(struct ee_state* ee, const ee_instruction& i) { ee->f[EE_D_FD].u32 = ee->f[EE_D_FS].u32 ^ 0x80000000; ee->fcr &= ~(FPU_FLG_O | FPU_FLG_U); } static inline void ee_i_nor(struct ee_state* ee, const ee_instruction& i) { EE_RD = ~(EE_RS | EE_RT); } static inline void ee_i_or(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_RS | EE_RT; } static inline void ee_i_ori(struct ee_state* ee, const ee_instruction& i) { EE_RT = EE_RS | EE_D_I16; } static inline void ee_i_pabsh(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 8; i++) { d->u16[i] = (t->u16[i] == 0x8000) ? 0x7fff : fast_abs16(t->u16[i]); } #else __m128i b = _mm_set1_epi16((unsigned short)0x8000); __m128i a = _mm_load_si128((const __m128i*)t); __m128i f = _mm_cmpeq_epi16(a, b); __m128i r = _mm_add_epi16(_mm_abs_epi16(a), f); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_pabsw(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 4; i++) { d->u32[i] = (t->u32[i] == 0x80000000) ? 0x7fffffff : fast_abs32(t->u32[i]); } #else __m128i b = _mm_set1_epi32(0x80000000); __m128i a = _mm_load_si128((const __m128i*)t); __m128i f = _mm_cmpeq_epi32(a, b); __m128i r = _mm_add_epi32(_mm_abs_epi32(a), f); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_paddb(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 16; i++) { d->u8[i] = s->u8[i] + t->u8[i]; } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_add_epi8(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_paddh(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 8; i++) { d->u16[i] = s->u16[i] + t->u16[i]; } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_add_epi16(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_paddsb(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 16; i++) { int32_t r = ((int32_t)(int8_t)s->u8[i]) + ((int32_t)(int8_t)t->u8[i]); d->u8[i] = (r > 0x7f) ? 0x7f : ((r < -128) ? 0x80 : r); } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_adds_epi8(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_paddsh(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 8; i++) { int32_t r = (SE3216(s->u16[i])) + (SE3216(t->u16[i])); d->u16[i] = (r > 0x7fff) ? 0x7fff : ((r < -0x8000) ? 0x8000 : r); } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_adds_epi16(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_paddsw(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 4; i++) { int64_t r = (SE6432(s->u32[i])) + (SE6432(t->u32[i])); d->u32[i] = (r >= 0x7fffffff) ? 0x7fffffff : ((r < (int32_t)0x80000000) ? 0x80000000 : r); } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_adds_epi32(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_paddub(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 16; i++) { uint32_t r = (uint32_t)s->u8[i] + (uint32_t)t->u8[i]; d->u8[i] = (r > 0xff) ? 0xff : r; } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_adds_epu8(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_padduh(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 8; i++) { uint32_t r = (uint32_t)s->u16[i] + (uint32_t)t->u16[i]; d->u16[i] = (r > 0xffff) ? 0xffff : r; } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_adds_epu16(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_padduw(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 4; i++) { uint64_t r = (uint64_t)s->u32[i] + (uint64_t)t->u32[i]; d->u32[i] = (r > 0xffffffff) ? 0xffffffff : r; } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_adds_epu32(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_paddw(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 4; i++) { d->u32[i] = s->u32[i] + t->u32[i]; } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_add_epi32(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_padsbh(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS d->u16[0] = s->u16[0] - t->u16[0]; d->u16[1] = s->u16[1] - t->u16[1]; d->u16[2] = s->u16[2] - t->u16[2]; d->u16[3] = s->u16[3] - t->u16[3]; d->u16[4] = s->u16[4] + t->u16[4]; d->u16[5] = s->u16[5] + t->u16[5]; d->u16[6] = s->u16[6] + t->u16[6]; d->u16[7] = s->u16[7] + t->u16[7]; #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i x = _mm_sub_epi16(a, b); __m128i y = _mm_add_epi16(a, b); __m128i r = _mm_blend_epi16(x, y, 15); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_pand(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS d->u64[0] = s->u64[0] & t->u64[0]; d->u64[1] = s->u64[1] & t->u64[1]; #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_and_si128(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_pceqb(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 16; i++) { d->u8[i] = (s->u8[i] == t->u8[i]) ? 0xff : 0; } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_cmpeq_epi8(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_pceqh(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 8; i++) { d->u16[i] = (s->u16[i] == t->u16[i]) ? 0xffff : 0; } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_cmpeq_epi16(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_pceqw(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 4; i++) { d->u32[i] = (s->u32[i] == t->u32[i]) ? 0xffffffff : 0; } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_cmpeq_epi32(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_pcgtb(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 16; i++) { d->u8[i] = ((int8_t)s->u8[i] > (int8_t)t->u8[i]) ? 0xff : 0; } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_cmpgt_epi8(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_pcgth(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 8; i++) { d->u16[i] = ((int16_t)s->u16[i] > (int16_t)t->u16[i]) ? 0xffff : 0; } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_cmpgt_epi16(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_pcgtw(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 4; i++) { d->u32[i] = ((int32_t)s->u32[i] > (int32_t)t->u32[i]) ? 0xffffffff : 0; } #else __m128i a = _mm_load_si128((__m128i*)s); __m128i b = _mm_load_si128((__m128i*)t); __m128i r = _mm_cmpgt_epi32(a, b); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_pcpyh(struct ee_state* ee, const ee_instruction& i) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS uint128_t tc = *t; d->u16[0] = tc.u16[0]; d->u16[1] = tc.u16[0]; d->u16[2] = tc.u16[0]; d->u16[3] = tc.u16[0]; d->u16[4] = tc.u16[4]; d->u16[5] = tc.u16[4]; d->u16[6] = tc.u16[4]; d->u16[7] = tc.u16[4]; #else static const uint64_t mask[] = { 0x0100010001000100, 0x0908090809080908 }; __m128i m = _mm_load_si128((__m128i*)mask); __m128i a = _mm_load_si128((__m128i*)t); __m128i r = _mm_shuffle_epi8(a, m); _mm_store_si128((__m128i*)d, r); #endif } static inline void ee_i_pcpyld(struct ee_state* ee, const ee_instruction& i) { #ifndef _EE_USE_INTRINSICS uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u64[0] = rt.u64[0]; ee->r[d].u64[1] = rs.u64[0]; #else __m128i a = _mm_load_si128((__m128i*)&ee->r[EE_D_RT]); __m128i b = _mm_load_si128((__m128i*)&ee->r[EE_D_RS]); __m128i r = _mm_unpacklo_epi64(a, b); _mm_store_si128((__m128i*)&ee->r[EE_D_RD], r); #endif } static inline void ee_i_pcpyud(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u64[0] = rs.u64[1]; ee->r[d].u64[1] = rt.u64[1]; } static inline void ee_i_pdivbw(struct ee_state* ee, const ee_instruction& i) { int s = EE_D_RS; int t = EE_D_RT; for (int i = 0; i < 4; i++) { if (ee->r[s].u32[i] == 0x80000000 && ee->r[t].u16[0] == 0xffff) { ee->lo.u32[i] = 0x80000000; ee->hi.u32[i] = 0; } else if (ee->r[t].u16[0] != 0) { ee->lo.u32[i] = ee->r[s].s32[i] / ee->r[t].s16[0]; ee->hi.u32[i] = ee->r[s].s32[i] % ee->r[t].s16[0]; } else { if (ee->r[s].s32[i] < 0) { ee->lo.u32[i] = 1; } else { ee->lo.u32[i] = -1; } ee->hi.u32[i] = ee->r[s].s32[i]; } } // ee->hi.u32[0] = SE3216(ee->r[s].s32[0] % ee->r[t].s16[0]); // ee->hi.u32[1] = SE3216(ee->r[s].s32[1] % ee->r[t].s16[0]); // ee->hi.u32[2] = SE3216(ee->r[s].s32[2] % ee->r[t].s16[0]); // ee->hi.u32[3] = SE3216(ee->r[s].s32[3] % ee->r[t].s16[0]); // ee->lo.u32[0] = ee->r[s].s32[0] / ee->r[t].s16[0]; // ee->lo.u32[1] = ee->r[s].s32[1] / ee->r[t].s16[0]; // ee->lo.u32[2] = ee->r[s].s32[2] / ee->r[t].s16[0]; // ee->lo.u32[3] = ee->r[s].s32[3] / ee->r[t].s16[0]; } static inline void ee_i_pdivuw(struct ee_state* ee, const ee_instruction& i) { int s = EE_D_RS; int t = EE_D_RT; for (int i = 0; i < 4; i += 2) { if (ee->r[t].u32[i] != 0) { ee->lo.u64[i/2] = SE6432(ee->r[s].u32[i] / ee->r[t].u32[i]); ee->hi.u64[i/2] = SE6432(ee->r[s].u32[i] % ee->r[t].u32[i]); } else { ee->lo.u64[i/2] = (int64_t)-1; ee->hi.u64[i/2] = (int64_t)ee->r[s].s32[i]; } } } static inline void ee_i_pdivw(struct ee_state* ee, const ee_instruction& i) { int s = EE_D_RS; int t = EE_D_RT; for (int i = 0; i < 4; i += 2) { if (ee->r[s].u32[i] == 0x80000000 && ee->r[t].u32[i] == 0xffffffff) { ee->lo.u64[i/2] = (int64_t)(int32_t)0x80000000; ee->hi.u64[i/2] = 0; } else if (ee->r[t].u32[i] != 0) { ee->lo.u64[i/2] = SE6432(ee->r[s].s32[i] / ee->r[t].s32[i]); ee->hi.u64[i/2] = SE6432(ee->r[s].s32[i] % ee->r[t].s32[i]); } else { if (ee->r[s].s32[i] < 0) { ee->lo.u64[i/2] = 1; } else { ee->lo.u64[i/2] = (int64_t)-1; } ee->hi.u64[i/2] = (int64_t)ee->r[s].s32[i]; } } // ee->hi.u64[0] = SE6432(ee->r[s].s32[0] % ee->r[t].s32[0]); // ee->hi.u64[1] = SE6432(ee->r[s].s32[2] % ee->r[t].s32[2]); // ee->lo.u64[0] = SE6432(ee->r[s].s32[0] / ee->r[t].s32[0]); // ee->lo.u64[1] = SE6432(ee->r[s].s32[2] / ee->r[t].s32[2]); } static inline void ee_i_pexch(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[0]; ee->r[d].u16[1] = rt.u16[2]; ee->r[d].u16[2] = rt.u16[1]; ee->r[d].u16[3] = rt.u16[3]; ee->r[d].u16[4] = rt.u16[4]; ee->r[d].u16[5] = rt.u16[6]; ee->r[d].u16[6] = rt.u16[5]; ee->r[d].u16[7] = rt.u16[7]; } static inline void ee_i_pexcw(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u32[0] = rt.u32[0]; ee->r[d].u32[1] = rt.u32[2]; ee->r[d].u32[2] = rt.u32[1]; ee->r[d].u32[3] = rt.u32[3]; } static inline void ee_i_pexeh(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[2]; ee->r[d].u16[1] = rt.u16[1]; ee->r[d].u16[2] = rt.u16[0]; ee->r[d].u16[3] = rt.u16[3]; ee->r[d].u16[4] = rt.u16[6]; ee->r[d].u16[5] = rt.u16[5]; ee->r[d].u16[6] = rt.u16[4]; ee->r[d].u16[7] = rt.u16[7]; } static inline void ee_i_pexew(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u32[0] = rt.u32[2]; ee->r[d].u32[1] = rt.u32[1]; ee->r[d].u32[2] = rt.u32[0]; ee->r[d].u32[3] = rt.u32[3]; } static inline void ee_i_pext5(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u32[0] = unpack_5551_8888(rt.u32[0]); ee->r[d].u32[1] = unpack_5551_8888(rt.u32[1]); ee->r[d].u32[2] = unpack_5551_8888(rt.u32[2]); ee->r[d].u32[3] = unpack_5551_8888(rt.u32[3]); } static inline void ee_i_pextlb(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u8[ 0] = rt.u8[0]; ee->r[d].u8[ 1] = rs.u8[0]; ee->r[d].u8[ 2] = rt.u8[1]; ee->r[d].u8[ 3] = rs.u8[1]; ee->r[d].u8[ 4] = rt.u8[2]; ee->r[d].u8[ 5] = rs.u8[2]; ee->r[d].u8[ 6] = rt.u8[3]; ee->r[d].u8[ 7] = rs.u8[3]; ee->r[d].u8[ 8] = rt.u8[4]; ee->r[d].u8[ 9] = rs.u8[4]; ee->r[d].u8[10] = rt.u8[5]; ee->r[d].u8[11] = rs.u8[5]; ee->r[d].u8[12] = rt.u8[6]; ee->r[d].u8[13] = rs.u8[6]; ee->r[d].u8[14] = rt.u8[7]; ee->r[d].u8[15] = rs.u8[7]; } static inline void ee_i_pextlh(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[0]; ee->r[d].u16[1] = rs.u16[0]; ee->r[d].u16[2] = rt.u16[1]; ee->r[d].u16[3] = rs.u16[1]; ee->r[d].u16[4] = rt.u16[2]; ee->r[d].u16[5] = rs.u16[2]; ee->r[d].u16[6] = rt.u16[3]; ee->r[d].u16[7] = rs.u16[3]; } static inline void ee_i_pextlw(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u32[0] = rt.u32[0]; ee->r[d].u32[1] = rs.u32[0]; ee->r[d].u32[2] = rt.u32[1]; ee->r[d].u32[3] = rs.u32[1]; } static inline void ee_i_pextub(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u8[ 0] = rt.u8[ 8]; ee->r[d].u8[ 1] = rs.u8[ 8]; ee->r[d].u8[ 2] = rt.u8[ 9]; ee->r[d].u8[ 3] = rs.u8[ 9]; ee->r[d].u8[ 4] = rt.u8[10]; ee->r[d].u8[ 5] = rs.u8[10]; ee->r[d].u8[ 6] = rt.u8[11]; ee->r[d].u8[ 7] = rs.u8[11]; ee->r[d].u8[ 8] = rt.u8[12]; ee->r[d].u8[ 9] = rs.u8[12]; ee->r[d].u8[10] = rt.u8[13]; ee->r[d].u8[11] = rs.u8[13]; ee->r[d].u8[12] = rt.u8[14]; ee->r[d].u8[13] = rs.u8[14]; ee->r[d].u8[14] = rt.u8[15]; ee->r[d].u8[15] = rs.u8[15]; } static inline void ee_i_pextuh(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[4]; ee->r[d].u16[1] = rs.u16[4]; ee->r[d].u16[2] = rt.u16[5]; ee->r[d].u16[3] = rs.u16[5]; ee->r[d].u16[4] = rt.u16[6]; ee->r[d].u16[5] = rs.u16[6]; ee->r[d].u16[6] = rt.u16[7]; ee->r[d].u16[7] = rs.u16[7]; } static inline void ee_i_pextuw(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u32[0] = rt.u32[2]; ee->r[d].u32[1] = rs.u32[2]; ee->r[d].u32[2] = rt.u32[3]; ee->r[d].u32[3] = rs.u32[3]; } static inline void ee_i_phmadh(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; int32_t r0 = (int32_t)(rs.s16[1] * rt.s16[1]); int32_t r1 = (int32_t)(rs.s16[3] * rt.s16[3]); int32_t r2 = (int32_t)(rs.s16[5] * rt.s16[5]); int32_t r3 = (int32_t)(rs.s16[7] * rt.s16[7]); ee->r[d].u32[0] = r0 + ((int16_t)rs.u16[0] * (int16_t)rt.u16[0]); ee->r[d].u32[1] = r1 + ((int16_t)rs.u16[2] * (int16_t)rt.u16[2]); ee->r[d].u32[2] = r2 + ((int16_t)rs.u16[4] * (int16_t)rt.u16[4]); ee->r[d].u32[3] = r3 + ((int16_t)rs.u16[6] * (int16_t)rt.u16[6]); ee->lo.u32[0] = ee->r[d].u32[0]; ee->lo.u32[1] = r0; ee->hi.u32[0] = ee->r[d].u32[1]; ee->hi.u32[1] = r1; ee->lo.u32[2] = ee->r[d].u32[2]; ee->lo.u32[3] = r2; ee->hi.u32[2] = ee->r[d].u32[3]; ee->hi.u32[3] = r3; } static inline void ee_i_phmsbh(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int32_t r1 = ee->r[s].s16[1] * ee->r[t].s16[1]; int32_t r3 = ee->r[s].s16[3] * ee->r[t].s16[3]; int32_t r5 = ee->r[s].s16[5] * ee->r[t].s16[5]; int32_t r7 = ee->r[s].s16[7] * ee->r[t].s16[7]; ee->r[d].u32[0] = (int32_t)(r1 - (ee->r[s].s16[0] * ee->r[t].s16[0])); ee->r[d].u32[1] = (int32_t)(r3 - (ee->r[s].s16[2] * ee->r[t].s16[2])); ee->r[d].u32[2] = (int32_t)(r5 - (ee->r[s].s16[4] * ee->r[t].s16[4])); ee->r[d].u32[3] = (int32_t)(r7 - (ee->r[s].s16[6] * ee->r[t].s16[6])); ee->lo.u32[0] = ee->r[d].u32[0]; ee->lo.u32[1] = ~r1; ee->hi.u32[0] = ee->r[d].u32[1]; ee->hi.u32[1] = ~r3; ee->lo.u32[2] = ee->r[d].u32[2]; ee->lo.u32[3] = ~r5; ee->hi.u32[2] = ee->r[d].u32[3]; ee->hi.u32[3] = ~r7; } static inline void ee_i_pinteh(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[0]; ee->r[d].u16[1] = rs.u16[0]; ee->r[d].u16[2] = rt.u16[2]; ee->r[d].u16[3] = rs.u16[2]; ee->r[d].u16[4] = rt.u16[4]; ee->r[d].u16[5] = rs.u16[4]; ee->r[d].u16[6] = rt.u16[6]; ee->r[d].u16[7] = rs.u16[6]; } static inline void ee_i_pinth(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[0]; ee->r[d].u16[1] = rs.u16[4]; ee->r[d].u16[2] = rt.u16[1]; ee->r[d].u16[3] = rs.u16[5]; ee->r[d].u16[4] = rt.u16[2]; ee->r[d].u16[5] = rs.u16[6]; ee->r[d].u16[6] = rt.u16[3]; ee->r[d].u16[7] = rs.u16[7]; } static inline void ee_i_plzcw(struct ee_state* ee, const ee_instruction& i) { for (int j = 0; j < 2; j++) { uint32_t word = ee->r[EE_D_RS].u32[j]; int msb = word & 0x80000000; word = (msb ? ~word : word); ee->r[EE_D_RD].u32[j] = (word ? (__builtin_clz(word) - 1) : 0x1f); } } static inline void ee_i_pmaddh(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; uint32_t r0 = SE3216(ee->r[s].u16[0]) * SE3216(ee->r[t].u16[0]); uint32_t r1 = SE3216(ee->r[s].u16[1]) * SE3216(ee->r[t].u16[1]); uint32_t r2 = SE3216(ee->r[s].u16[2]) * SE3216(ee->r[t].u16[2]); uint32_t r3 = SE3216(ee->r[s].u16[3]) * SE3216(ee->r[t].u16[3]); uint32_t r4 = SE3216(ee->r[s].u16[4]) * SE3216(ee->r[t].u16[4]); uint32_t r5 = SE3216(ee->r[s].u16[5]) * SE3216(ee->r[t].u16[5]); uint32_t r6 = SE3216(ee->r[s].u16[6]) * SE3216(ee->r[t].u16[6]); uint32_t r7 = SE3216(ee->r[s].u16[7]) * SE3216(ee->r[t].u16[7]); uint32_t c0 = ee->lo.u32[0]; uint32_t c1 = ee->lo.u32[1]; uint32_t c2 = ee->hi.u32[0]; uint32_t c3 = ee->hi.u32[1]; uint32_t c4 = ee->lo.u32[2]; uint32_t c5 = ee->lo.u32[3]; uint32_t c6 = ee->hi.u32[2]; uint32_t c7 = ee->hi.u32[3]; ee->r[d].u32[0] = r0 + c0; ee->lo.u32[1] = r1 + c1; ee->r[d].u32[1] = r2 + c2; ee->hi.u32[1] = r3 + c3; ee->r[d].u32[2] = r4 + c4; ee->lo.u32[3] = r5 + c5; ee->r[d].u32[3] = r6 + c6; ee->hi.u32[3] = r7 + c7; ee->lo.u32[0] = ee->r[d].u32[0]; ee->hi.u32[0] = ee->r[d].u32[1]; ee->lo.u32[2] = ee->r[d].u32[2]; ee->hi.u32[2] = ee->r[d].u32[3]; } static inline void ee_i_pmadduw(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; uint64_t r0 = (uint64_t)ee->r[s].u32[0] * (uint64_t)ee->r[t].u32[0]; uint64_t r1 = (uint64_t)ee->r[s].u32[2] * (uint64_t)ee->r[t].u32[2]; ee->r[d].u64[0] = r0 + ((ee->hi.u64[0] << 32) | (uint64_t)ee->lo.u32[0]); ee->r[d].u64[1] = r1 + ((ee->hi.u64[1] << 32) | (uint64_t)ee->lo.u32[2]); ee->lo.u64[0] = SE6432(ee->r[d].u32[0]); ee->hi.u64[0] = SE6432(ee->r[d].u32[1]); ee->lo.u64[1] = SE6432(ee->r[d].u32[2]); ee->hi.u64[1] = SE6432(ee->r[d].u32[3]); } static inline void ee_i_pmaddw(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; uint64_t r0 = (int64_t)ee->r[s].s32[0] * (int64_t)ee->r[t].s32[0]; uint64_t r1 = (int64_t)ee->r[s].s32[2] * (int64_t)ee->r[t].s32[2]; ee->r[d].u64[0] = r0 + ((((uint64_t)ee->hi.u32[0]) << 32) | (uint64_t)ee->lo.u32[0]); ee->r[d].u64[1] = r1 + ((((uint64_t)ee->hi.u32[2]) << 32) | (uint64_t)ee->lo.u32[2]); ee->lo.u64[0] = SE6432(ee->r[d].u32[0]); ee->hi.u64[0] = SE6432(ee->r[d].u32[1]); ee->lo.u64[1] = SE6432(ee->r[d].u32[2]); ee->hi.u64[1] = SE6432(ee->r[d].u32[3]); } static inline void ee_i_pmaxh(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; 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]; 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]; 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]; 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]; 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]; 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]; 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]; 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]; } static inline void ee_i_pmaxw(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; 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]; 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]; 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]; 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]; } static inline void ee_i_pmfhi(struct ee_state* ee, const ee_instruction& i) { ee->r[EE_D_RD] = ee->hi; } static inline void ee_i_pmfhllw(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; ee->r[d].u32[0] = ee->lo.u32[0]; ee->r[d].u32[1] = ee->hi.u32[0]; ee->r[d].u32[2] = ee->lo.u32[2]; ee->r[d].u32[3] = ee->hi.u32[2]; } static inline void ee_i_pmfhluw(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; ee->r[d].u32[0] = ee->lo.u32[1]; ee->r[d].u32[1] = ee->hi.u32[1]; ee->r[d].u32[2] = ee->lo.u32[3]; ee->r[d].u32[3] = ee->hi.u32[3]; } static inline void ee_i_pmfhlslw(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; ee->r[d].u64[0] = SE6432(saturate32(((uint64_t)ee->lo.u32[0]) | (ee->hi.u64[0] << 32))); ee->r[d].u64[1] = SE6432(saturate32(((uint64_t)ee->lo.u32[2]) | (ee->hi.u64[1] << 32))); } static inline void ee_i_pmfhllh(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; ee->r[d].u16[0] = ee->lo.u16[0]; ee->r[d].u16[1] = ee->lo.u16[2]; ee->r[d].u16[2] = ee->hi.u16[0]; ee->r[d].u16[3] = ee->hi.u16[2]; ee->r[d].u16[4] = ee->lo.u16[4]; ee->r[d].u16[5] = ee->lo.u16[6]; ee->r[d].u16[6] = ee->hi.u16[4]; ee->r[d].u16[7] = ee->hi.u16[6]; } static inline void ee_i_pmfhlsh(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; ee->r[d].u16[0] = saturate16(ee->lo.u32[0]); ee->r[d].u16[1] = saturate16(ee->lo.u32[1]); ee->r[d].u16[2] = saturate16(ee->hi.u32[0]); ee->r[d].u16[3] = saturate16(ee->hi.u32[1]); ee->r[d].u16[4] = saturate16(ee->lo.u32[2]); ee->r[d].u16[5] = saturate16(ee->lo.u32[3]); ee->r[d].u16[6] = saturate16(ee->hi.u32[2]); ee->r[d].u16[7] = saturate16(ee->hi.u32[3]); } static inline void ee_i_pmflo(struct ee_state* ee, const ee_instruction& i) { ee->r[EE_D_RD] = ee->lo; } static inline void ee_i_pminh(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; 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]; 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]; 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]; 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]; 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]; 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]; 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]; 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]; } static inline void ee_i_pminw(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; 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]; 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]; 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]; 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]; } static inline void ee_i_pmsubh(struct ee_state* ee, const ee_instruction& i) { int s = EE_D_RS; int t = EE_D_RT; int d = EE_D_RD; int32_t r0 = (int32_t)ee->r[s].s16[0] * (int32_t)ee->r[t].s16[0]; int32_t r1 = (int32_t)ee->r[s].s16[1] * (int32_t)ee->r[t].s16[1]; int32_t r2 = (int32_t)ee->r[s].s16[2] * (int32_t)ee->r[t].s16[2]; int32_t r3 = (int32_t)ee->r[s].s16[3] * (int32_t)ee->r[t].s16[3]; int32_t r4 = (int32_t)ee->r[s].s16[4] * (int32_t)ee->r[t].s16[4]; int32_t r5 = (int32_t)ee->r[s].s16[5] * (int32_t)ee->r[t].s16[5]; int32_t r6 = (int32_t)ee->r[s].s16[6] * (int32_t)ee->r[t].s16[6]; int32_t r7 = (int32_t)ee->r[s].s16[7] * (int32_t)ee->r[t].s16[7]; ee->r[d].u32[0] = ee->lo.u32[0] - r0; ee->r[d].u32[1] = ee->hi.u32[0] - r2; ee->r[d].u32[2] = ee->lo.u32[2] - r4; ee->r[d].u32[3] = ee->hi.u32[2] - r6; ee->lo.u32[0] = ee->r[d].u32[0]; ee->hi.u32[0] = ee->r[d].u32[1]; ee->lo.u32[2] = ee->r[d].u32[2]; ee->hi.u32[2] = ee->r[d].u32[3]; ee->lo.u32[1] = ee->lo.u32[1] - r1; ee->lo.u32[3] = ee->lo.u32[3] - r5; ee->hi.u32[1] = ee->hi.u32[1] - r3; ee->hi.u32[3] = ee->hi.u32[3] - r7; } static inline void ee_i_pmsubw(struct ee_state* ee, const ee_instruction& i) { int s = EE_D_RS; int t = EE_D_RT; int d = EE_D_RD; // int64_t r0 = ee->r[s].s32[0] * ee->r[t].s32[0]; // int64_t r1 = ee->r[s].s32[2] * ee->r[t].s32[2]; // ee->r[d].u64[0] = ((ee->hi.u64[0] << 32) | (uint64_t)ee->lo.u32[0]) - r0; // ee->r[d].u64[1] = ((ee->hi.u64[1] << 32) | (uint64_t)ee->lo.u32[2]) - r0; // ee->lo.u64[0] = SE6432(ee->r[d].u32[0]); // ee->hi.u64[0] = SE6432(ee->r[d].u32[1]); // ee->lo.u64[1] = SE6432(ee->r[d].u32[2]); // ee->hi.u64[1] = SE6432(ee->r[d].u32[3]); int64_t op1 = (int64_t)ee->r[s].s32[0]; int64_t op2 = (int64_t)ee->r[t].s32[0]; int64_t result = op1 * op2; int64_t result2 = ((int64_t)ee->hi.s32[0] << 32) - result; result2 = (int32_t)(result2 / 4294967295); ee->lo.s64[0] = ee->lo.s32[0] - (int32_t)(result & 0xffffffff); ee->hi.s64[0] = (int32_t)result2; op1 = (int64_t)ee->r[s].s32[2]; op2 = (int64_t)ee->r[t].s32[2]; result = op1 * op2; result2 = ((int64_t)ee->hi.s32[2] << 32) - result; result2 = (int32_t)(result2 / 4294967295); ee->lo.s64[1] = ee->lo.s32[2] - (int32_t)(result & 0xffffffff); ee->hi.s64[1] = (int32_t)result2; ee->r[d].u32[0] = ee->lo.u32[0]; ee->r[d].u32[1] = ee->hi.u32[0]; ee->r[d].u32[2] = ee->lo.u32[2]; ee->r[d].u32[3] = ee->hi.u32[2]; } static inline void ee_i_pmthi(struct ee_state* ee, const ee_instruction& i) { ee->hi = ee->r[EE_D_RS]; } static inline void ee_i_pmthl(struct ee_state* ee, const ee_instruction& i) { int s = EE_D_RS; ee->lo.u32[0] = ee->r[s].u32[0]; ee->lo.u32[2] = ee->r[s].u32[2]; ee->hi.u32[0] = ee->r[s].u32[1]; ee->hi.u32[2] = ee->r[s].u32[3]; } static inline void ee_i_pmtlo(struct ee_state* ee, const ee_instruction& i) { ee->lo = ee->r[EE_D_RS]; } static inline void ee_i_pmulth(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; ee->lo.u32[0] = (int32_t)(int16_t)ee->r[s].u16[0] * (int32_t)(int16_t)ee->r[t].u16[0]; ee->lo.u32[1] = (int32_t)(int16_t)ee->r[s].u16[1] * (int32_t)(int16_t)ee->r[t].u16[1]; ee->hi.u32[0] = (int32_t)(int16_t)ee->r[s].u16[2] * (int32_t)(int16_t)ee->r[t].u16[2]; ee->hi.u32[1] = (int32_t)(int16_t)ee->r[s].u16[3] * (int32_t)(int16_t)ee->r[t].u16[3]; ee->lo.u32[2] = (int32_t)(int16_t)ee->r[s].u16[4] * (int32_t)(int16_t)ee->r[t].u16[4]; ee->lo.u32[3] = (int32_t)(int16_t)ee->r[s].u16[5] * (int32_t)(int16_t)ee->r[t].u16[5]; ee->hi.u32[2] = (int32_t)(int16_t)ee->r[s].u16[6] * (int32_t)(int16_t)ee->r[t].u16[6]; ee->hi.u32[3] = (int32_t)(int16_t)ee->r[s].u16[7] * (int32_t)(int16_t)ee->r[t].u16[7]; ee->r[d].u32[0] = ee->lo.u32[0]; ee->r[d].u32[1] = ee->hi.u32[0]; ee->r[d].u32[2] = ee->lo.u32[2]; ee->r[d].u32[3] = ee->hi.u32[2]; } static inline void ee_i_pmultuw(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; ee->r[d].u64[0] = (uint64_t)ee->r[s].u32[0] * (uint64_t)ee->r[t].u32[0]; ee->r[d].u64[1] = (uint64_t)ee->r[s].u32[2] * (uint64_t)ee->r[t].u32[2]; ee->lo.u64[0] = SE6432(ee->r[d].u32[0]); ee->lo.u64[1] = SE6432(ee->r[d].u32[2]); ee->hi.u64[0] = SE6432(ee->r[d].u32[1]); ee->hi.u64[1] = SE6432(ee->r[d].u32[3]); } static inline void ee_i_pmultw(struct ee_state* ee, const ee_instruction& i) { int s = EE_D_RS; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u64[0] = SE6432(ee->r[s].u32[0]) * SE6432(ee->r[t].u32[0]); ee->r[d].u64[1] = SE6432(ee->r[s].u32[2]) * SE6432(ee->r[t].u32[2]); ee->lo.u64[0] = SE6432(ee->r[d].u32[0]); ee->lo.u64[1] = SE6432(ee->r[d].u32[2]); ee->hi.u64[0] = SE6432(ee->r[d].u32[1]); ee->hi.u64[1] = SE6432(ee->r[d].u32[3]); } static inline void ee_i_pnor(struct ee_state* ee, const ee_instruction& i) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u64[0] = ~(rs.u64[0] | rt.u64[0]); ee->r[d].u64[1] = ~(rs.u64[1] | rt.u64[1]); } static inline void ee_i_por(struct ee_state* ee, const ee_instruction& i) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u64[0] = rs.u64[0] | rt.u64[0]; ee->r[d].u64[1] = rs.u64[1] | rt.u64[1]; } static inline void ee_i_ppac5(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u32[0] = ((rt.u32[0] & 0x000000f8) >> 3) | ((rt.u32[0] & 0x0000f800) >> 6) | ((rt.u32[0] & 0x00f80000) >> 9) | ((rt.u32[0] & 0x80000000) >> 16); ee->r[d].u32[1] = ((rt.u32[1] & 0x000000f8) >> 3) | ((rt.u32[1] & 0x0000f800) >> 6) | ((rt.u32[1] & 0x00f80000) >> 9) | ((rt.u32[1] & 0x80000000) >> 16); ee->r[d].u32[2] = ((rt.u32[2] & 0x000000f8) >> 3) | ((rt.u32[2] & 0x0000f800) >> 6) | ((rt.u32[2] & 0x00f80000) >> 9) | ((rt.u32[2] & 0x80000000) >> 16); ee->r[d].u32[3] = ((rt.u32[3] & 0x000000f8) >> 3) | ((rt.u32[3] & 0x0000f800) >> 6) | ((rt.u32[3] & 0x00f80000) >> 9) | ((rt.u32[3] & 0x80000000) >> 16); } static inline void ee_i_ppacb(struct ee_state* ee, const ee_instruction& i) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u8[0 ] = rt.u8[ 0]; ee->r[d].u8[1 ] = rt.u8[ 2]; ee->r[d].u8[2 ] = rt.u8[ 4]; ee->r[d].u8[3 ] = rt.u8[ 6]; ee->r[d].u8[4 ] = rt.u8[ 8]; ee->r[d].u8[5 ] = rt.u8[10]; ee->r[d].u8[6 ] = rt.u8[12]; ee->r[d].u8[7 ] = rt.u8[14]; ee->r[d].u8[8 ] = rs.u8[ 0]; ee->r[d].u8[9 ] = rs.u8[ 2]; ee->r[d].u8[10] = rs.u8[ 4]; ee->r[d].u8[11] = rs.u8[ 6]; ee->r[d].u8[12] = rs.u8[ 8]; ee->r[d].u8[13] = rs.u8[10]; ee->r[d].u8[14] = rs.u8[12]; ee->r[d].u8[15] = rs.u8[14]; } static inline void ee_i_ppach(struct ee_state* ee, const ee_instruction& i) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[0]; ee->r[d].u16[1] = rt.u16[2]; ee->r[d].u16[2] = rt.u16[4]; ee->r[d].u16[3] = rt.u16[6]; ee->r[d].u16[4] = rs.u16[0]; ee->r[d].u16[5] = rs.u16[2]; ee->r[d].u16[6] = rs.u16[4]; ee->r[d].u16[7] = rs.u16[6]; } static inline void ee_i_ppacw(struct ee_state* ee, const ee_instruction& i) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u32[0] = rt.u32[0]; ee->r[d].u32[1] = rt.u32[2]; ee->r[d].u32[2] = rs.u32[0]; ee->r[d].u32[3] = rs.u32[2]; } static inline void ee_i_pref(struct ee_state* ee, const ee_instruction& i) { // Does nothing } static inline void ee_i_prevh(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[3]; ee->r[d].u16[1] = rt.u16[2]; ee->r[d].u16[2] = rt.u16[1]; ee->r[d].u16[3] = rt.u16[0]; ee->r[d].u16[4] = rt.u16[7]; ee->r[d].u16[5] = rt.u16[6]; ee->r[d].u16[6] = rt.u16[5]; ee->r[d].u16[7] = rt.u16[4]; } static inline void ee_i_prot3w(struct ee_state* ee, const ee_instruction& i) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u32[0] = rt.u32[1]; ee->r[d].u32[1] = rt.u32[2]; ee->r[d].u32[2] = rt.u32[0]; ee->r[d].u32[3] = rt.u32[3]; } static inline void ee_i_psllh(struct ee_state* ee, const ee_instruction& i) { int sa = EE_D_SA & 0xf; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u16[0] = ee->r[t].u16[0] << sa; ee->r[d].u16[1] = ee->r[t].u16[1] << sa; ee->r[d].u16[2] = ee->r[t].u16[2] << sa; ee->r[d].u16[3] = ee->r[t].u16[3] << sa; ee->r[d].u16[4] = ee->r[t].u16[4] << sa; ee->r[d].u16[5] = ee->r[t].u16[5] << sa; ee->r[d].u16[6] = ee->r[t].u16[6] << sa; ee->r[d].u16[7] = ee->r[t].u16[7] << sa; } static inline void ee_i_psllvw(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int d = EE_D_RD; int s = EE_D_RS; ee->r[d].u64[0] = SE6432(ee->r[t].u32[0] << (ee->r[s].u32[0] & 31)); ee->r[d].u64[1] = SE6432(ee->r[t].u32[2] << (ee->r[s].u32[2] & 31)); } static inline void ee_i_psllw(struct ee_state* ee, const ee_instruction& i) { int sa = EE_D_SA; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u32[0] = ee->r[t].u32[0] << sa; ee->r[d].u32[1] = ee->r[t].u32[1] << sa; ee->r[d].u32[2] = ee->r[t].u32[2] << sa; ee->r[d].u32[3] = ee->r[t].u32[3] << sa; } static inline void ee_i_psrah(struct ee_state* ee, const ee_instruction& i) { int sa = EE_D_SA & 0xf; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u16[0] = ((int16_t)ee->r[t].u16[0]) >> sa; ee->r[d].u16[1] = ((int16_t)ee->r[t].u16[1]) >> sa; ee->r[d].u16[2] = ((int16_t)ee->r[t].u16[2]) >> sa; ee->r[d].u16[3] = ((int16_t)ee->r[t].u16[3]) >> sa; ee->r[d].u16[4] = ((int16_t)ee->r[t].u16[4]) >> sa; ee->r[d].u16[5] = ((int16_t)ee->r[t].u16[5]) >> sa; ee->r[d].u16[6] = ((int16_t)ee->r[t].u16[6]) >> sa; ee->r[d].u16[7] = ((int16_t)ee->r[t].u16[7]) >> sa; } static inline void ee_i_psravw(struct ee_state* ee, const ee_instruction& i) { int s = EE_D_RS; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u64[0] = SE6432((int32_t)ee->r[t].u32[0] >> (ee->r[s].u32[0] & 31)); ee->r[d].u64[1] = SE6432((int32_t)ee->r[t].u32[2] >> (ee->r[s].u32[2] & 31)); } static inline void ee_i_psraw(struct ee_state* ee, const ee_instruction& i) { int sa = EE_D_SA; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u32[0] = ((int32_t)ee->r[t].u32[0]) >> sa; ee->r[d].u32[1] = ((int32_t)ee->r[t].u32[1]) >> sa; ee->r[d].u32[2] = ((int32_t)ee->r[t].u32[2]) >> sa; ee->r[d].u32[3] = ((int32_t)ee->r[t].u32[3]) >> sa; } static inline void ee_i_psrlh(struct ee_state* ee, const ee_instruction& i) { int sa = EE_D_SA & 0xf; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u16[0] = ee->r[t].u16[0] >> sa; ee->r[d].u16[1] = ee->r[t].u16[1] >> sa; ee->r[d].u16[2] = ee->r[t].u16[2] >> sa; ee->r[d].u16[3] = ee->r[t].u16[3] >> sa; ee->r[d].u16[4] = ee->r[t].u16[4] >> sa; ee->r[d].u16[5] = ee->r[t].u16[5] >> sa; ee->r[d].u16[6] = ee->r[t].u16[6] >> sa; ee->r[d].u16[7] = ee->r[t].u16[7] >> sa; } static inline void ee_i_psrlvw(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; ee->r[d].u64[0] = SE6432(ee->r[t].u32[0] >> (ee->r[s].u32[0] & 31)); ee->r[d].u64[1] = SE6432(ee->r[t].u32[2] >> (ee->r[s].u32[2] & 31)); } static inline void ee_i_psrlw(struct ee_state* ee, const ee_instruction& i) { int sa = EE_D_SA; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u32[0] = ee->r[t].u32[0] >> sa; ee->r[d].u32[1] = ee->r[t].u32[1] >> sa; ee->r[d].u32[2] = ee->r[t].u32[2] >> sa; ee->r[d].u32[3] = ee->r[t].u32[3] >> sa; } static inline void ee_i_psubb(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int s = EE_D_RS; int d = EE_D_RD; ee->r[d].u8[0 ] = ee->r[s].u8[0 ] - ee->r[t].u8[0 ]; ee->r[d].u8[1 ] = ee->r[s].u8[1 ] - ee->r[t].u8[1 ]; ee->r[d].u8[2 ] = ee->r[s].u8[2 ] - ee->r[t].u8[2 ]; ee->r[d].u8[3 ] = ee->r[s].u8[3 ] - ee->r[t].u8[3 ]; ee->r[d].u8[4 ] = ee->r[s].u8[4 ] - ee->r[t].u8[4 ]; ee->r[d].u8[5 ] = ee->r[s].u8[5 ] - ee->r[t].u8[5 ]; ee->r[d].u8[6 ] = ee->r[s].u8[6 ] - ee->r[t].u8[6 ]; ee->r[d].u8[7 ] = ee->r[s].u8[7 ] - ee->r[t].u8[7 ]; ee->r[d].u8[8 ] = ee->r[s].u8[8 ] - ee->r[t].u8[8 ]; ee->r[d].u8[9 ] = ee->r[s].u8[9 ] - ee->r[t].u8[9 ]; ee->r[d].u8[10] = ee->r[s].u8[10] - ee->r[t].u8[10]; ee->r[d].u8[11] = ee->r[s].u8[11] - ee->r[t].u8[11]; ee->r[d].u8[12] = ee->r[s].u8[12] - ee->r[t].u8[12]; ee->r[d].u8[13] = ee->r[s].u8[13] - ee->r[t].u8[13]; ee->r[d].u8[14] = ee->r[s].u8[14] - ee->r[t].u8[14]; ee->r[d].u8[15] = ee->r[s].u8[15] - ee->r[t].u8[15]; } static inline void ee_i_psubh(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int s = EE_D_RS; int d = EE_D_RD; ee->r[d].u16[0] = ee->r[s].u16[0] - ee->r[t].u16[0]; ee->r[d].u16[1] = ee->r[s].u16[1] - ee->r[t].u16[1]; ee->r[d].u16[2] = ee->r[s].u16[2] - ee->r[t].u16[2]; ee->r[d].u16[3] = ee->r[s].u16[3] - ee->r[t].u16[3]; ee->r[d].u16[4] = ee->r[s].u16[4] - ee->r[t].u16[4]; ee->r[d].u16[5] = ee->r[s].u16[5] - ee->r[t].u16[5]; ee->r[d].u16[6] = ee->r[s].u16[6] - ee->r[t].u16[6]; ee->r[d].u16[7] = ee->r[s].u16[7] - ee->r[t].u16[7]; } static inline void ee_i_psubsb(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int32_t r0 = ((int32_t)(int8_t)ee->r[s].u8[0 ]) - ((int32_t)(int8_t)ee->r[t].u8[0 ]); int32_t r1 = ((int32_t)(int8_t)ee->r[s].u8[1 ]) - ((int32_t)(int8_t)ee->r[t].u8[1 ]); int32_t r2 = ((int32_t)(int8_t)ee->r[s].u8[2 ]) - ((int32_t)(int8_t)ee->r[t].u8[2 ]); int32_t r3 = ((int32_t)(int8_t)ee->r[s].u8[3 ]) - ((int32_t)(int8_t)ee->r[t].u8[3 ]); int32_t r4 = ((int32_t)(int8_t)ee->r[s].u8[4 ]) - ((int32_t)(int8_t)ee->r[t].u8[4 ]); int32_t r5 = ((int32_t)(int8_t)ee->r[s].u8[5 ]) - ((int32_t)(int8_t)ee->r[t].u8[5 ]); int32_t r6 = ((int32_t)(int8_t)ee->r[s].u8[6 ]) - ((int32_t)(int8_t)ee->r[t].u8[6 ]); int32_t r7 = ((int32_t)(int8_t)ee->r[s].u8[7 ]) - ((int32_t)(int8_t)ee->r[t].u8[7 ]); int32_t r8 = ((int32_t)(int8_t)ee->r[s].u8[8 ]) - ((int32_t)(int8_t)ee->r[t].u8[8 ]); int32_t r9 = ((int32_t)(int8_t)ee->r[s].u8[9 ]) - ((int32_t)(int8_t)ee->r[t].u8[9 ]); int32_t r10 = ((int32_t)(int8_t)ee->r[s].u8[10]) - ((int32_t)(int8_t)ee->r[t].u8[10]); int32_t r11 = ((int32_t)(int8_t)ee->r[s].u8[11]) - ((int32_t)(int8_t)ee->r[t].u8[11]); int32_t r12 = ((int32_t)(int8_t)ee->r[s].u8[12]) - ((int32_t)(int8_t)ee->r[t].u8[12]); int32_t r13 = ((int32_t)(int8_t)ee->r[s].u8[13]) - ((int32_t)(int8_t)ee->r[t].u8[13]); int32_t r14 = ((int32_t)(int8_t)ee->r[s].u8[14]) - ((int32_t)(int8_t)ee->r[t].u8[14]); int32_t r15 = ((int32_t)(int8_t)ee->r[s].u8[15]) - ((int32_t)(int8_t)ee->r[t].u8[15]); ee->r[d].u8[0 ] = (r0 >= 0x7f) ? 0x7f : ((r0 < -0x80) ? 0x80 : r0); ee->r[d].u8[1 ] = (r1 >= 0x7f) ? 0x7f : ((r1 < -0x80) ? 0x80 : r1); ee->r[d].u8[2 ] = (r2 >= 0x7f) ? 0x7f : ((r2 < -0x80) ? 0x80 : r2); ee->r[d].u8[3 ] = (r3 >= 0x7f) ? 0x7f : ((r3 < -0x80) ? 0x80 : r3); ee->r[d].u8[4 ] = (r4 >= 0x7f) ? 0x7f : ((r4 < -0x80) ? 0x80 : r4); ee->r[d].u8[5 ] = (r5 >= 0x7f) ? 0x7f : ((r5 < -0x80) ? 0x80 : r5); ee->r[d].u8[6 ] = (r6 >= 0x7f) ? 0x7f : ((r6 < -0x80) ? 0x80 : r6); ee->r[d].u8[7 ] = (r7 >= 0x7f) ? 0x7f : ((r7 < -0x80) ? 0x80 : r7); ee->r[d].u8[8 ] = (r8 >= 0x7f) ? 0x7f : ((r8 < -0x80) ? 0x80 : r8); ee->r[d].u8[9 ] = (r9 >= 0x7f) ? 0x7f : ((r9 < -0x80) ? 0x80 : r9); ee->r[d].u8[10] = (r10 >= 0x7f) ? 0x7f : ((r10 < -0x80) ? 0x80 : r10); ee->r[d].u8[11] = (r11 >= 0x7f) ? 0x7f : ((r11 < -0x80) ? 0x80 : r11); ee->r[d].u8[12] = (r12 >= 0x7f) ? 0x7f : ((r12 < -0x80) ? 0x80 : r12); ee->r[d].u8[13] = (r13 >= 0x7f) ? 0x7f : ((r13 < -0x80) ? 0x80 : r13); ee->r[d].u8[14] = (r14 >= 0x7f) ? 0x7f : ((r14 < -0x80) ? 0x80 : r14); ee->r[d].u8[15] = (r15 >= 0x7f) ? 0x7f : ((r15 < -0x80) ? 0x80 : r15); } static inline void ee_i_psubsh(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int32_t r0 = SE3216(ee->r[s].u16[0]) - SE3216(ee->r[t].u16[0]); int32_t r1 = SE3216(ee->r[s].u16[1]) - SE3216(ee->r[t].u16[1]); int32_t r2 = SE3216(ee->r[s].u16[2]) - SE3216(ee->r[t].u16[2]); int32_t r3 = SE3216(ee->r[s].u16[3]) - SE3216(ee->r[t].u16[3]); int32_t r4 = SE3216(ee->r[s].u16[4]) - SE3216(ee->r[t].u16[4]); int32_t r5 = SE3216(ee->r[s].u16[5]) - SE3216(ee->r[t].u16[5]); int32_t r6 = SE3216(ee->r[s].u16[6]) - SE3216(ee->r[t].u16[6]); int32_t r7 = SE3216(ee->r[s].u16[7]) - SE3216(ee->r[t].u16[7]); ee->r[d].u16[0] = (r0 >= 0x7fff) ? 0x7fff : ((r0 < -0x8000) ? 0x8000 : r0); ee->r[d].u16[1] = (r1 >= 0x7fff) ? 0x7fff : ((r1 < -0x8000) ? 0x8000 : r1); ee->r[d].u16[2] = (r2 >= 0x7fff) ? 0x7fff : ((r2 < -0x8000) ? 0x8000 : r2); ee->r[d].u16[3] = (r3 >= 0x7fff) ? 0x7fff : ((r3 < -0x8000) ? 0x8000 : r3); ee->r[d].u16[4] = (r4 >= 0x7fff) ? 0x7fff : ((r4 < -0x8000) ? 0x8000 : r4); ee->r[d].u16[5] = (r5 >= 0x7fff) ? 0x7fff : ((r5 < -0x8000) ? 0x8000 : r5); ee->r[d].u16[6] = (r6 >= 0x7fff) ? 0x7fff : ((r6 < -0x8000) ? 0x8000 : r6); ee->r[d].u16[7] = (r7 >= 0x7fff) ? 0x7fff : ((r7 < -0x8000) ? 0x8000 : r7); } static inline void ee_i_psubsw(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int64_t r0 = SE6432(ee->r[s].u32[0]) - SE6432(ee->r[t].u32[0]); int64_t r1 = SE6432(ee->r[s].u32[1]) - SE6432(ee->r[t].u32[1]); int64_t r2 = SE6432(ee->r[s].u32[2]) - SE6432(ee->r[t].u32[2]); int64_t r3 = SE6432(ee->r[s].u32[3]) - SE6432(ee->r[t].u32[3]); ee->r[d].u32[0] = (r0 >= 0x7fffffff) ? 0x7fffffff : ((r0 < (int32_t)0x80000000) ? 0x80000000 : r0); ee->r[d].u32[1] = (r1 >= 0x7fffffff) ? 0x7fffffff : ((r1 < (int32_t)0x80000000) ? 0x80000000 : r1); ee->r[d].u32[2] = (r2 >= 0x7fffffff) ? 0x7fffffff : ((r2 < (int32_t)0x80000000) ? 0x80000000 : r2); ee->r[d].u32[3] = (r3 >= 0x7fffffff) ? 0x7fffffff : ((r3 < (int32_t)0x80000000) ? 0x80000000 : r3); } static inline void ee_i_psubub(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int32_t r0 = ((int32_t)ee->r[s].u8[0 ]) - ((int32_t)ee->r[t].u8[0 ]); int32_t r1 = ((int32_t)ee->r[s].u8[1 ]) - ((int32_t)ee->r[t].u8[1 ]); int32_t r2 = ((int32_t)ee->r[s].u8[2 ]) - ((int32_t)ee->r[t].u8[2 ]); int32_t r3 = ((int32_t)ee->r[s].u8[3 ]) - ((int32_t)ee->r[t].u8[3 ]); int32_t r4 = ((int32_t)ee->r[s].u8[4 ]) - ((int32_t)ee->r[t].u8[4 ]); int32_t r5 = ((int32_t)ee->r[s].u8[5 ]) - ((int32_t)ee->r[t].u8[5 ]); int32_t r6 = ((int32_t)ee->r[s].u8[6 ]) - ((int32_t)ee->r[t].u8[6 ]); int32_t r7 = ((int32_t)ee->r[s].u8[7 ]) - ((int32_t)ee->r[t].u8[7 ]); int32_t r8 = ((int32_t)ee->r[s].u8[8 ]) - ((int32_t)ee->r[t].u8[8 ]); int32_t r9 = ((int32_t)ee->r[s].u8[9 ]) - ((int32_t)ee->r[t].u8[9 ]); int32_t r10 = ((int32_t)ee->r[s].u8[10]) - ((int32_t)ee->r[t].u8[10]); int32_t r11 = ((int32_t)ee->r[s].u8[11]) - ((int32_t)ee->r[t].u8[11]); int32_t r12 = ((int32_t)ee->r[s].u8[12]) - ((int32_t)ee->r[t].u8[12]); int32_t r13 = ((int32_t)ee->r[s].u8[13]) - ((int32_t)ee->r[t].u8[13]); int32_t r14 = ((int32_t)ee->r[s].u8[14]) - ((int32_t)ee->r[t].u8[14]); int32_t r15 = ((int32_t)ee->r[s].u8[15]) - ((int32_t)ee->r[t].u8[15]); ee->r[d].u8[0 ] = (r0 < 0) ? 0 : r0; ee->r[d].u8[1 ] = (r1 < 0) ? 0 : r1; ee->r[d].u8[2 ] = (r2 < 0) ? 0 : r2; ee->r[d].u8[3 ] = (r3 < 0) ? 0 : r3; ee->r[d].u8[4 ] = (r4 < 0) ? 0 : r4; ee->r[d].u8[5 ] = (r5 < 0) ? 0 : r5; ee->r[d].u8[6 ] = (r6 < 0) ? 0 : r6; ee->r[d].u8[7 ] = (r7 < 0) ? 0 : r7; ee->r[d].u8[8 ] = (r8 < 0) ? 0 : r8; ee->r[d].u8[9 ] = (r9 < 0) ? 0 : r9; ee->r[d].u8[10] = (r10 < 0) ? 0 : r10; ee->r[d].u8[11] = (r11 < 0) ? 0 : r11; ee->r[d].u8[12] = (r12 < 0) ? 0 : r12; ee->r[d].u8[13] = (r13 < 0) ? 0 : r13; ee->r[d].u8[14] = (r14 < 0) ? 0 : r14; ee->r[d].u8[15] = (r15 < 0) ? 0 : r15; } static inline void ee_i_psubuh(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int32_t r0 = (int32_t)(ee->r[s].u16[0]) - (int32_t)(ee->r[t].u16[0]); int32_t r1 = (int32_t)(ee->r[s].u16[1]) - (int32_t)(ee->r[t].u16[1]); int32_t r2 = (int32_t)(ee->r[s].u16[2]) - (int32_t)(ee->r[t].u16[2]); int32_t r3 = (int32_t)(ee->r[s].u16[3]) - (int32_t)(ee->r[t].u16[3]); int32_t r4 = (int32_t)(ee->r[s].u16[4]) - (int32_t)(ee->r[t].u16[4]); int32_t r5 = (int32_t)(ee->r[s].u16[5]) - (int32_t)(ee->r[t].u16[5]); int32_t r6 = (int32_t)(ee->r[s].u16[6]) - (int32_t)(ee->r[t].u16[6]); int32_t r7 = (int32_t)(ee->r[s].u16[7]) - (int32_t)(ee->r[t].u16[7]); ee->r[d].u16[0] = (r0 < 0) ? 0 : r0; ee->r[d].u16[1] = (r1 < 0) ? 0 : r1; ee->r[d].u16[2] = (r2 < 0) ? 0 : r2; ee->r[d].u16[3] = (r3 < 0) ? 0 : r3; ee->r[d].u16[4] = (r4 < 0) ? 0 : r4; ee->r[d].u16[5] = (r5 < 0) ? 0 : r5; ee->r[d].u16[6] = (r6 < 0) ? 0 : r6; ee->r[d].u16[7] = (r7 < 0) ? 0 : r7; } static inline void ee_i_psubuw(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int64_t r0 = (int64_t)ee->r[s].u32[0] - (int64_t)ee->r[t].u32[0]; int64_t r1 = (int64_t)ee->r[s].u32[1] - (int64_t)ee->r[t].u32[1]; int64_t r2 = (int64_t)ee->r[s].u32[2] - (int64_t)ee->r[t].u32[2]; int64_t r3 = (int64_t)ee->r[s].u32[3] - (int64_t)ee->r[t].u32[3]; ee->r[d].u32[0] = (r0 < 0) ? 0 : r0; ee->r[d].u32[1] = (r1 < 0) ? 0 : r1; ee->r[d].u32[2] = (r2 < 0) ? 0 : r2; ee->r[d].u32[3] = (r3 < 0) ? 0 : r3; } static inline void ee_i_psubw(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; ee->r[d].u32[0] = ee->r[s].u32[0] - ee->r[t].u32[0]; ee->r[d].u32[1] = ee->r[s].u32[1] - ee->r[t].u32[1]; ee->r[d].u32[2] = ee->r[s].u32[2] - ee->r[t].u32[2]; ee->r[d].u32[3] = ee->r[s].u32[3] - ee->r[t].u32[3]; } static inline void ee_i_pxor(struct ee_state* ee, const ee_instruction& i) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u64[0] = rs.u64[0] ^ rt.u64[0]; ee->r[d].u64[1] = rs.u64[1] ^ rt.u64[1]; } static inline void ee_i_qfsrv(struct ee_state* ee, const ee_instruction& i) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; int shift = ee->sa * 8; uint128_t v; if (!shift) { v = rt; } else { if (shift < 64) { v.u64[0] = rt.u64[0] >> shift; v.u64[1] = rt.u64[1] >> shift; v.u64[0] |= rt.u64[1] << (64 - shift); v.u64[1] |= rs.u64[0] << (64 - shift); } else { v.u64[0] = rt.u64[1] >> (shift - 64); v.u64[1] = rs.u64[0] >> (shift - 64); if (shift != 64) { v.u64[0] |= rs.u64[0] << (128u - shift); v.u64[1] |= rs.u64[1] << (128u - shift); } } } ee->r[d] = v; } static inline void ee_i_qmfc2(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int d = EE_D_RD; ee->r[t].u64[0] = ee->vu0->vf[d].u64[0]; ee->r[t].u64[1] = ee->vu0->vf[d].u64[1]; } static inline void ee_i_qmtc2(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int d = EE_D_RD; if (!d) return; ee->vu0->vf[d].u128 = ee->r[t]; } static inline void ee_i_rsqrts(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int d = EE_D_FD; ee->fcr &= ~(FPU_FLG_I | FPU_FLG_D); if ((ee->f[t].u32 & 0x7f800000) == 0) { ee->fcr |= FPU_FLG_D | FPU_FLG_SD; ee->f[d].u32 = (ee->f[t].u32 & 0x80000000) | 0x7f7fffff; return; } else if (ee->f[t].u32 & 0x80000000) { ee->fcr |= FPU_FLG_I | FPU_FLG_SI; ee->f[d].f = EE_FS / sqrtf(fabsf(fpu_cvtf(ee->f[t].f))); } else { ee->f[d].f = EE_FS / sqrtf(fpu_cvtf(ee->f[t].f)); } if (fpu_check_overflow_no_flags(ee, &ee->f[d])) return; fpu_check_underflow_no_flags(ee, &ee->f[d]); } static inline void ee_i_sb(struct ee_state* ee, const ee_instruction& i) { bus_write8(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT); } static inline void ee_i_sd(struct ee_state* ee, const ee_instruction& i) { bus_write64(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT); } static inline void ee_i_sdl(struct ee_state* ee, const ee_instruction& i) { static const uint8_t sdl_shift[8] = { 56, 48, 40, 32, 24, 16, 8, 0 }; static const uint64_t sdl_mask[8] = { 0xffffffffffffff00ULL, 0xffffffffffff0000ULL, 0xffffffffff000000ULL, 0xffffffff00000000ULL, 0xffffff0000000000ULL, 0xffff000000000000ULL, 0xff00000000000000ULL, 0x0000000000000000ULL }; uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t shift = addr & 7; uint64_t data = bus_read64(ee, addr & ~7); bus_write64(ee, addr & ~7, (EE_RT >> sdl_shift[shift]) | (data & sdl_mask[shift])); } static inline void ee_i_sdr(struct ee_state* ee, const ee_instruction& i) { static const uint8_t sdr_shift[8] = { 0, 8, 16, 24, 32, 40, 48, 56 }; static const uint64_t sdr_mask[8] = { 0x0000000000000000ULL, 0x00000000000000ffULL, 0x000000000000ffffULL, 0x0000000000ffffffULL, 0x00000000ffffffffULL, 0x000000ffffffffffULL, 0x0000ffffffffffffULL, 0x00ffffffffffffffULL }; uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t shift = addr & 7; uint64_t data = bus_read64(ee, addr & ~7); bus_write64(ee, addr & ~7, (EE_RT << sdr_shift[shift]) | (data & sdr_mask[shift])); } static inline void ee_i_sh(struct ee_state* ee, const ee_instruction& i) { bus_write16(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT); } static inline void ee_i_sll(struct ee_state* ee, const ee_instruction& i) { EE_RD = SE6432(EE_RT32 << EE_D_SA); } static inline void ee_i_sllv(struct ee_state* ee, const ee_instruction& i) { EE_RD = SE6432(EE_RT32 << (EE_RS & 0x1f)); } static inline void ee_i_slt(struct ee_state* ee, const ee_instruction& i) { EE_RD = (int64_t)EE_RS < (int64_t)EE_RT; } static inline void ee_i_slti(struct ee_state* ee, const ee_instruction& i) { EE_RT = ((int64_t)EE_RS) < SE6416(EE_D_I16); } static inline void ee_i_sltiu(struct ee_state* ee, const ee_instruction& i) { EE_RT = EE_RS < (uint64_t)(SE6416(EE_D_I16)); } static inline void ee_i_sltu(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_RS < EE_RT; } static inline void ee_i_sq(struct ee_state* ee, const ee_instruction& i) { bus_write128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf, ee->r[EE_D_RT]); } static inline void ee_i_sqc2(struct ee_state* ee, const ee_instruction& i) { bus_write128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf, ee->vu0->vf[EE_D_RT].u128); } static inline void ee_i_sqrts(struct ee_state* ee, const ee_instruction& i) { int t = EE_D_RT; int d = EE_D_FD; ee->fcr &= ~(FPU_FLG_I | FPU_FLG_D); if ((ee->f[t].u32 & 0x7f800000) == 0) { ee->f[d].u32 = ee->f[t].u32 & 0x80000000; } else if (ee->f[t].u32 & 0x80000000) { ee->fcr |= FPU_FLG_I | FPU_FLG_SI; ee->f[d].f = sqrtf(fabsf(fpu_cvtf(ee->f[t].f))); } else { ee->f[d].f = sqrtf(fpu_cvtf(ee->f[t].f)); } } static inline void ee_i_sra(struct ee_state* ee, const ee_instruction& i) { EE_RD = SE6432(((int32_t)EE_RT32) >> EE_D_SA); } static inline void ee_i_srav(struct ee_state* ee, const ee_instruction& i) { EE_RD = SE6432(((int32_t)EE_RT32) >> (EE_RS & 0x1f)); } static inline void ee_i_srl(struct ee_state* ee, const ee_instruction& i) { EE_RD = SE6432(EE_RT32 >> EE_D_SA); } static inline void ee_i_srlv(struct ee_state* ee, const ee_instruction& i) { EE_RD = SE6432(EE_RT32 >> (EE_RS & 0x1f)); } static inline void ee_i_sub(struct ee_state* ee, const ee_instruction& i) { int32_t r; int o = __builtin_ssub_overflow(EE_RS32, EE_RT32, &r); if (o) { ee_exception_level1(ee, CAUSE_EXC1_OV); } else { EE_RD = SE6432(r); } } static inline void ee_i_subas(struct ee_state* ee, const ee_instruction& i) { ee->a.f = EE_FS - EE_FT; if (fpu_check_overflow(ee, &ee->a)) return; fpu_check_underflow(ee, &ee->a); } static inline void ee_i_subs(struct ee_state* ee, const ee_instruction& i) { int d = EE_D_FD; ee->f[d].f = EE_FS - EE_FT; if (fpu_check_overflow(ee, &ee->f[d])) return; fpu_check_underflow(ee, &ee->f[d]); } static inline void ee_i_subu(struct ee_state* ee, const ee_instruction& i) { EE_RD = SE6432(EE_RS - EE_RT); } static inline void ee_i_sw(struct ee_state* ee, const ee_instruction& i) { bus_write32(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT32); } static inline void ee_i_swc1(struct ee_state* ee, const ee_instruction& i) { bus_write32(ee, EE_RS32 + SE3216(EE_D_I16), EE_FT32); } static inline void ee_i_swl(struct ee_state* ee, const ee_instruction& i) { static const uint32_t swl_mask[4] = { 0xffffff00, 0xffff0000, 0xff000000, 0x00000000 }; static const uint8_t swl_shift[4] = { 24, 16, 8, 0 }; uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t mem = bus_read32(ee, addr & ~3); int shift = addr & 3; bus_write32(ee, addr & ~3, (EE_RT32 >> swl_shift[shift] | (mem & swl_mask[shift]))); // 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); } static inline void ee_i_swr(struct ee_state* ee, const ee_instruction& i) { static const uint32_t swr_mask[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff }; static const uint8_t swr_shift[4] = { 0, 8, 16, 24 }; uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t mem = bus_read32(ee, addr & ~3); int shift = addr & 3; bus_write32(ee, addr & ~3, (EE_RT32 << swr_shift[shift]) | (mem & swr_mask[shift])); // 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); } static inline void ee_i_sync(struct ee_state* ee, const ee_instruction& i) { /* Do nothing */ } // #include "syscall.h" static inline void ee_get_thread_list(struct ee_state* ee) { if (ee->thread_list_base) return; uint32_t offset = 0; while (offset < 0x5000) { uint32_t inst[3]; uint32_t addr = 0x80000000 + offset; inst[0] = bus_read32(ee, addr); inst[1] = bus_read32(ee, addr + 4); inst[2] = bus_read32(ee, addr + 8); if (inst[0] == 0xac420000 && inst[1] == 0 && inst[2] == 0) { uint32_t op = bus_read32(ee, 0x80000000 + offset + (4 * 6)); ee->thread_list_base = 0x80010000 + (op & 0xffff) - 8; printf("ee: Found thread list base at %08x\n", ee->thread_list_base); break; } offset += 4; } } static inline void ee_i_syscall(struct ee_state* ee, const ee_instruction& i) { uint32_t id = ee->r[3].ul64; if (id & 0x80000000) { id = (~id) + 1; } // 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); switch (id) { // ChangeThreadPriority case 0x29: { ee_get_thread_list(ee); } break; // SetOsdConfigParam case 0x4a: { uint32_t ptr = ee->r[4].u32[0]; uint32_t value = bus_read32(ee, ptr); memcpy(&ee->osd_config, &value, 4); return; } break; // GetOsdConfigParam case 0x4b: { uint32_t ptr = ee->r[4].u32[0]; uint32_t value; memcpy(&value, &ee->osd_config, 4); bus_write32(ee, ptr, value); return; } break; // FlushCache case 0x64: { // printf("ee: Flushed %zd blocks\n", ee->block_cache.size()); ee->block_cache.clear(); } break; } ee_exception_level1(ee, CAUSE_EXC1_SYS); } static inline void ee_i_teq(struct ee_state* ee, const ee_instruction& i) { if (EE_RS == EE_RT) ee_exception_level1(ee, CAUSE_EXC1_TR); } static inline void ee_i_teqi(struct ee_state* ee, const ee_instruction& i) { if (EE_RS == SE6416(EE_D_I16)) ee_exception_level1(ee, CAUSE_EXC1_TR); } static inline void ee_i_tge(struct ee_state* ee, const ee_instruction& i) { if (EE_RS >= EE_RT) ee_exception_level1(ee, CAUSE_EXC1_TR); } static inline void ee_i_tgei(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, "ee: tgei unimplemented\n"); exit(1); } static inline void ee_i_tgeiu(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, "ee: tgeiu unimplemented\n"); exit(1); } static inline void ee_i_tgeu(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, "ee: tgeu unimplemented\n"); exit(1); } static inline void ee_i_tlbp(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, "ee: tlbp unimplemented\n"); exit(1); } static inline void ee_i_tlbr(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, "ee: tlbr unimplemented\n"); exit(1); } static inline void ee_i_tlbwi(struct ee_state* ee, const ee_instruction& i) { struct ee_vtlb_entry* entry = &ee->vtlb[ee->index & 0x3f]; entry->asid = ee->entryhi & 0xff; entry->pfn0 = (ee->entrylo0 & 0x3ffffc0) << 6; entry->pfn1 = (ee->entrylo1 & 0x3ffffc0) << 6; entry->mask = ee->pagemask & 0x1ffe000; entry->vpn2 = ee->entryhi & 0xffffe000; entry->v0 = (ee->entrylo0 >> 1) & 1; entry->d0 = (ee->entrylo0 >> 2) & 1; entry->c0 = (ee->entrylo0 >> 3) & 7; entry->v1 = (ee->entrylo1 >> 1) & 1; entry->d1 = (ee->entrylo1 >> 2) & 1; entry->c1 = (ee->entrylo1 >> 3) & 7; entry->s = (ee->entrylo0 >> 31) & 1; entry->g = (ee->entrylo0 & 1) && (ee->entrylo1 & 1); 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", ee->index, entry->vpn2, entry->pfn0, entry->v0, entry->d0, entry->pfn1, entry->v1, entry->d1, entry->mask, entry->s, entry->g ); } static inline void ee_i_tlbwr(struct ee_state* ee, const ee_instruction& i) { int index = (ee->count % (48 - ee->wired)) + ee->wired; struct ee_vtlb_entry* entry = &ee->vtlb[index]; entry->asid = ee->entryhi & 0xff; entry->pfn0 = (ee->entrylo0 & 0x3ffffc0) << 6; entry->pfn1 = (ee->entrylo1 & 0x3ffffc0) << 6; entry->mask = ee->pagemask & 0x1ffe000; entry->vpn2 = ee->entryhi & 0xffffe000; entry->v0 = (ee->entrylo0 >> 1) & 1; entry->d0 = (ee->entrylo0 >> 2) & 1; entry->c0 = (ee->entrylo0 >> 3) & 7; entry->v1 = (ee->entrylo1 >> 1) & 1; entry->d1 = (ee->entrylo1 >> 2) & 1; entry->c1 = (ee->entrylo1 >> 3) & 7; entry->s = (ee->entrylo0 >> 31) & 1; entry->g = (ee->entrylo0 & 1) && (ee->entrylo1 & 1); 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", index, entry->vpn2, entry->pfn0, entry->v0, entry->d0, entry->pfn1, entry->v1, entry->d1, entry->mask, entry->s, entry->g ); } static inline void ee_i_tlt(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, "ee: tlt unimplemented\n"); exit(1); } static inline void ee_i_tlti(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, "ee: tlti unimplemented\n"); exit(1); } static inline void ee_i_tltiu(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, "ee: tltiu unimplemented\n"); exit(1); } static inline void ee_i_tltu(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, "ee: tltu unimplemented\n"); exit(1); } static inline void ee_i_tne(struct ee_state* ee, const ee_instruction& i) { if (EE_RS != EE_RT) ee_exception_level1(ee, CAUSE_EXC1_TR); } static inline void ee_i_tnei(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, "ee: tnei unimplemented\n"); exit(1); } static inline void ee_i_vabs(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(abs) } static inline void ee_i_vadd(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(add) } static inline void ee_i_vadda(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(adda) } static inline void ee_i_vaddai(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addai) } static inline void ee_i_vaddaq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addaq) } static inline void ee_i_vaddaw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addaw) } static inline void ee_i_vaddax(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addax) } static inline void ee_i_vadday(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(adday) } static inline void ee_i_vaddaz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addaz) } static inline void ee_i_vaddi(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addi) } static inline void ee_i_vaddq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addq) } static inline void ee_i_vaddw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addw) } static inline void ee_i_vaddx(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addx) } static inline void ee_i_vaddy(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addy) } static inline void ee_i_vaddz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(addz) } static inline void ee_i_vcallms(struct ee_state* ee, const ee_instruction& i) { vu_execute_program(ee->vu0, EE_D_I15); } static inline void ee_i_vcallmsr(struct ee_state* ee, const ee_instruction& i) { vu_execute_program(ee->vu0, ee->vu0->cmsar0); } static inline void ee_i_vclipw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER(clip) } static inline void ee_i_vdiv(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(div) } static inline void ee_i_vftoi0(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(ftoi0) } static inline void ee_i_vftoi12(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(ftoi12) } static inline void ee_i_vftoi15(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(ftoi15) } static inline void ee_i_vftoi4(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(ftoi4) } static inline void ee_i_viadd(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(iadd) } static inline void ee_i_viaddi(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(iaddi) } static inline void ee_i_viand(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(iand) } static inline void ee_i_vilwr(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(ilwr) } static inline void ee_i_vior(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(ior) } static inline void ee_i_visub(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(isub) } static inline void ee_i_viswr(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(iswr) } static inline void ee_i_vitof0(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(itof0) } static inline void ee_i_vitof12(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(itof12) } static inline void ee_i_vitof15(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(itof15) } static inline void ee_i_vitof4(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(itof4) } static inline void ee_i_vlqd(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(lqd) } static inline void ee_i_vlqi(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(lqi) } static inline void ee_i_vmadd(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(madd) } static inline void ee_i_vmadda(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(madda) } static inline void ee_i_vmaddai(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddai) } static inline void ee_i_vmaddaq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddaq) } static inline void ee_i_vmaddaw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddaw) } static inline void ee_i_vmaddax(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddax) } static inline void ee_i_vmadday(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(madday) } static inline void ee_i_vmaddaz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddaz) } static inline void ee_i_vmaddi(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddi) } static inline void ee_i_vmaddq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddq) } static inline void ee_i_vmaddw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddw) } static inline void ee_i_vmaddx(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddx) } static inline void ee_i_vmaddy(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddy) } static inline void ee_i_vmaddz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maddz) } static inline void ee_i_vmax(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(max) } static inline void ee_i_vmaxi(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maxi) } static inline void ee_i_vmaxw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maxw) } static inline void ee_i_vmaxx(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maxx) } static inline void ee_i_vmaxy(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maxy) } static inline void ee_i_vmaxz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(maxz) } static inline void ee_i_vmfir(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mfir) } static inline void ee_i_vmini(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mini) } static inline void ee_i_vminii(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(minii) } static inline void ee_i_vminiw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(miniw) } static inline void ee_i_vminix(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(minix) } static inline void ee_i_vminiy(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(miniy) } static inline void ee_i_vminiz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(miniz) } static inline void ee_i_vmove(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(move) } static inline void ee_i_vmr32(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(mr32) } static inline void ee_i_vmsub(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msub) } static inline void ee_i_vmsuba(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msuba) } static inline void ee_i_vmsubai(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubai) } static inline void ee_i_vmsubaq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubaq) } static inline void ee_i_vmsubaw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubaw) } static inline void ee_i_vmsubax(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubax) } static inline void ee_i_vmsubay(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubay) } static inline void ee_i_vmsubaz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubaz) } static inline void ee_i_vmsubi(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubi) } static inline void ee_i_vmsubq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubq) } static inline void ee_i_vmsubw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubw) } static inline void ee_i_vmsubx(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubx) } static inline void ee_i_vmsuby(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msuby) } static inline void ee_i_vmsubz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(msubz) } static inline void ee_i_vmtir(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(mtir) } static inline void ee_i_vmul(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mul) } static inline void ee_i_vmula(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mula) } static inline void ee_i_vmulai(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulai) } static inline void ee_i_vmulaq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulaq) } static inline void ee_i_vmulaw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulaw) } static inline void ee_i_vmulax(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulax) } static inline void ee_i_vmulay(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulay) } static inline void ee_i_vmulaz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulaz) } static inline void ee_i_vmuli(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(muli) } static inline void ee_i_vmulq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulq) } static inline void ee_i_vmulw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulw) } static inline void ee_i_vmulx(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulx) } static inline void ee_i_vmuly(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(muly) } static inline void ee_i_vmulz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(mulz) } static inline void ee_i_vnop(struct ee_state* ee, const ee_instruction& i) { VU_UPPER(nop) } static inline void ee_i_vopmsub(struct ee_state* ee, const ee_instruction& i) { VU_UPPER(opmsub) } static inline void ee_i_vopmula(struct ee_state* ee, const ee_instruction& i) { VU_UPPER(opmula) } static inline void ee_i_vrget(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(rget) } static inline void ee_i_vrinit(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(rinit) } static inline void ee_i_vrnext(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(rnext) } static inline void ee_i_vrsqrt(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(rsqrt) } static inline void ee_i_vrxor(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(rxor) } static inline void ee_i_vsqd(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(sqd) } static inline void ee_i_vsqi(struct ee_state* ee, const ee_instruction& i) { VU_LOWER_TEMPLATE(sqi) } static inline void ee_i_vsqrt(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(sqrt) } static inline void ee_i_vsub(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(sub) } static inline void ee_i_vsuba(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(suba) } static inline void ee_i_vsubai(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subai) } static inline void ee_i_vsubaq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subaq) } static inline void ee_i_vsubaw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subaw) } static inline void ee_i_vsubax(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subax) } static inline void ee_i_vsubay(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subay) } static inline void ee_i_vsubaz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subaz) } static inline void ee_i_vsubi(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subi) } static inline void ee_i_vsubq(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subq) } static inline void ee_i_vsubw(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subw) } static inline void ee_i_vsubx(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subx) } static inline void ee_i_vsuby(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(suby) } static inline void ee_i_vsubz(struct ee_state* ee, const ee_instruction& i) { VU_UPPER_TEMPLATE(subz) } static inline void ee_i_vwaitq(struct ee_state* ee, const ee_instruction& i) { VU_LOWER(waitq) } static inline void ee_i_xor(struct ee_state* ee, const ee_instruction& i) { EE_RD = EE_RS ^ EE_RT; } static inline void ee_i_xori(struct ee_state* ee, const ee_instruction& i) { EE_RT = EE_RS ^ EE_D_I16; } static inline void ee_i_invalid(struct ee_state* ee, const ee_instruction& i) { fprintf(stderr, "ee: Invalid instruction %08x at PC=%08x\n", i.opcode, ee->pc); exit(1); } static inline void ee_i_nop(struct ee_state* ee, const ee_instruction& i) { } struct ee_state* ee_create(void) { return new ee_state(); } void ee_init(struct ee_state* ee, struct vu_state* vu0, struct vu_state* vu1, int ram_size, struct ee_bus_s bus) { ee->prid = 0x2e20; ee->pc = EE_VEC_RESET; ee->next_pc = ee->pc + 4; ee->bus = bus; ee->vu0 = vu0; ee->vu1 = vu1; // Initialize block lookup cache (intentionally mismatches so first real lookup succeeds) ee->last_block_lookup_pc = ~0u; ee->last_block_ptr = nullptr; // To-do: Set SR ee->spr = ps2_ram_create(); ps2_ram_init(ee->spr, 0x4000); // EE's FPU uses round to zero by default fesetround(FE_TOWARDZERO); ee->fcr = 0x01000001; ee->ram_size = ram_size - 1; ee->osd_config.screen_type = 0; // 4:3 ee->osd_config.ps1drv_config = 0; // ??? ee->osd_config.spdif_mode = 0; // Enabled ee->osd_config.timezone_offset = 0; ee->osd_config.video_output = 0; // RGB ee->osd_config.jap_language = 1; // Indicates not Japanese ee->osd_config.language = 1; // English ee->osd_config.version = 1; // Indicates normal kernel without extended language settings ee->block_cache.clear(); ee->block_cache.resize(EE_CACHE_PAGECOUNT); } void ee_reset(struct ee_state* ee) { for (int i = 0; i < 32; i++) ee->r[i] = { 0 }; for (int i = 0; i < 32; i++) ee->f[i].u32 = 0; for (int i = 0; i < 32; i++) ee->cop0_r[i] = 0; ee->a.u32 = 0; ee->hi = { 0 }; ee->lo = { 0 }; ee->pc = 0xbfc00000; ee->next_pc = ee->pc + 4; ee->opcode = 0; ee->sa = 0; ee->branch = 0; ee->branch_taken = 0; ee->delay_slot = 0; ee->prid = 0x2e20; ee->pc = EE_VEC_RESET; ee->next_pc = ee->pc + 4; ee->intc_reads = 0; ee->csr_reads = 0; ee->block_cache.clear(); ee->block_cache.resize(EE_CACHE_PAGECOUNT); // Clear block lookup cache ee->last_block_lookup_pc = ~0u; ee->last_block_ptr = nullptr; fesetround(FE_TOWARDZERO); ps2_ram_reset(ee->spr); ee->fcr = 0x01000001; } void ee_destroy(struct ee_state* ee) { ps2_ram_destroy(ee->spr); delete ee; } #define EE_BRANCH_NORMAL 1 #define EE_BRANCH_IMMEDIATE 2 #define EE_BRANCH_LIKELY 3 #define EE_BRANCH_COND 4 ee_instruction ee_decode(uint32_t opcode) { ee_instruction i; i.opcode = opcode; i.rs = (opcode >> 21) & 0x1f; i.rt = (opcode >> 16) & 0x1f; i.rd = (opcode >> 11) & 0x1f; i.sa = (opcode >> 6) & 0x1f; i.i15 = (opcode >> 6) & 0x7fff; i.i16 = opcode & 0xffff; i.i26 = opcode & 0x3ffffff; i.branch = 0; i.cycles = 0; switch ((opcode & 0xFC000000) >> 26) { case 0x00000000 >> 26: { // special switch (opcode & 0x0000003F) { case 0x00000000: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sll; return i; case 0x00000002: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_srl; return i; case 0x00000003: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sra; return i; case 0x00000004: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sllv; return i; case 0x00000006: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_srlv; return i; case 0x00000007: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_srav; return i; case 0x00000008: i.cycles = EE_CYC_DEFAULT; i.branch = 1; i.func = ee_i_jr; return i; case 0x00000009: i.cycles = EE_CYC_DEFAULT; i.branch = 1; i.func = ee_i_jalr; return i; case 0x0000000A: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_movz; return i; case 0x0000000B: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_movn; return i; case 0x0000000C: i.cycles = EE_CYC_DEFAULT; i.branch = 2; i.func = ee_i_syscall; return i; case 0x0000000D: i.cycles = EE_CYC_DEFAULT; i.branch = 2; i.func = ee_i_break; return i; case 0x0000000F: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sync; return i; case 0x00000010: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mfhi; return i; case 0x00000011: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mthi; return i; case 0x00000012: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mflo; return i; case 0x00000013: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mtlo; return i; case 0x00000014: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsllv; return i; case 0x00000016: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsrlv; return i; case 0x00000017: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsrav; return i; case 0x00000018: i.cycles = EE_CYC_MULT; i.func = ee_i_mult; return i; case 0x00000019: i.cycles = EE_CYC_MULT; i.func = ee_i_multu; return i; case 0x0000001A: i.cycles = EE_CYC_DIV; i.func = ee_i_div; return i; case 0x0000001B: i.cycles = EE_CYC_DIV; i.func = ee_i_divu; return i; case 0x00000020: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_add; return i; case 0x00000021: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_addu; return i; case 0x00000022: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_sub; return i; case 0x00000023: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_subu; return i; case 0x00000024: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_and; return i; case 0x00000025: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_or; return i; case 0x00000026: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_xor; return i; case 0x00000027: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_nor; return i; case 0x00000028: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mfsa; return i; case 0x00000029: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mtsa; return i; case 0x0000002A: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_slt; return i; case 0x0000002B: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sltu; return i; case 0x0000002C: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_dadd; return i; case 0x0000002D: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_daddu; return i; case 0x0000002E: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_dsub; return i; case 0x0000002F: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsubu; return i; case 0x00000030: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tge; return i; case 0x00000031: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tgeu; return i; case 0x00000032: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tlt; return i; case 0x00000033: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tltu; return i; case 0x00000034: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_teq; return i; case 0x00000036: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tne; return i; case 0x00000038: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsll; return i; case 0x0000003A: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsrl; return i; case 0x0000003B: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsra; return i; case 0x0000003C: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsll32; return i; case 0x0000003E: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsrl32; return i; case 0x0000003F: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_dsra32; return i; } } break; case 0x04000000 >> 26: { // regimm switch ((opcode & 0x001F0000) >> 16) { case 0x00000000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bltz; return i; case 0x00010000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bgez; return i; case 0x00020000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bltzl; return i; case 0x00030000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bgezl; return i; case 0x00080000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tgei; return i; case 0x00090000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tgeiu; return i; case 0x000A0000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tlti; return i; case 0x000B0000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tltiu; return i; case 0x000C0000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_teqi; return i; case 0x000E0000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 4; i.func = ee_i_tnei; return i; case 0x00100000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bltzal; return i; case 0x00110000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bgezal; return i; case 0x00120000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bltzall; return i; case 0x00130000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bgezall; return i; case 0x00180000 >> 16: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mtsab; return i; case 0x00190000 >> 16: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_mtsah; return i; } } break; case 0x08000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 1; i.func = ee_i_j; return i; case 0x0C000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 1; i.func = ee_i_jal; return i; case 0x10000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_beq; return i; case 0x14000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bne; return i; case 0x18000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_blez; return i; case 0x1C000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bgtz; return i; case 0x20000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_addi; return i; case 0x24000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_addiu; return i; case 0x28000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_slti; return i; case 0x2C000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_sltiu; return i; case 0x30000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_andi; return i; case 0x34000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_ori; return i; case 0x38000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_xori; return i; case 0x3C000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_lui; return i; case 0x40000000 >> 26: { // cop0 switch ((opcode & 0x03E00000) >> 21) { case 0x00000000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mfc0; return i; case 0x00800000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mtc0; return i; case 0x01000000 >> 21: { switch ((opcode & 0x001F0000) >> 16) { case 0x00000000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc0f; return i; case 0x00010000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc0t; return i; case 0x00020000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc0fl; return i; case 0x00030000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc0tl; return i; } } break; case 0x02000000 >> 21: { switch (opcode & 0x0000003F) { case 0x00000001: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_tlbr; return i; case 0x00000002: i.cycles = EE_CYC_COP_DEFAULT; i.branch = 2; i.func = ee_i_tlbwi; return i; case 0x00000006: i.cycles = EE_CYC_COP_DEFAULT; i.branch = 2; i.func = ee_i_tlbwr; return i; case 0x00000008: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_tlbp; return i; case 0x00000018: i.cycles = EE_CYC_COP_DEFAULT; i.branch = 2; i.func = ee_i_eret; return i; case 0x00000038: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_ei; return i; case 0x00000039: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_di; return i; } } break; } } break; case 0x44000000 >> 26: { // cop1 switch ((opcode & 0x03E00000) >> 21) { case 0x00000000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mfc1; return i; case 0x00400000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cfc1; return i; case 0x00800000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mtc1; return i; case 0x00C00000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_ctc1; return i; case 0x01000000 >> 21: { switch ((opcode & 0x001F0000) >> 16) { case 0x00000000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc1f; return i; case 0x00010000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc1t; return i; case 0x00020000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc1fl; return i; case 0x00030000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc1tl; return i; } } break; case 0x02000000 >> 21: { switch (opcode & 0x0000003F) { case 0x00000000: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_adds; return i; case 0x00000001: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_subs; return i; case 0x00000002: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_muls; return i; case 0x00000003: i.cycles = EE_CYC_FPU_DIV; i.func = ee_i_divs; return i; case 0x00000004: i.cycles = EE_CYC_FPU_DIV; i.func = ee_i_sqrts; return i; case 0x00000005: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_abss; return i; case 0x00000006: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_movs; return i; case 0x00000007: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_negs; return i; case 0x00000016: i.cycles = EE_CYC_FPU_DIV; i.func = ee_i_rsqrts; return i; case 0x00000018: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_addas; return i; case 0x00000019: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_subas; return i; case 0x0000001A: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_mulas; return i; case 0x0000001C: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_madds; return i; case 0x0000001D: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_msubs; return i; case 0x0000001E: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_maddas; return i; case 0x0000001F: i.cycles = EE_CYC_FPU_MULT; i.func = ee_i_msubas; return i; case 0x00000024: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cvtw; return i; case 0x00000028: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_maxs; return i; case 0x00000029: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mins; return i; case 0x00000030: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cf; return i; case 0x00000032: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_ceq; return i; case 0x00000034: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_clt; return i; case 0x00000036: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cle; return i; } } break; case 0x02800000 >> 21: { switch (opcode & 0x0000003F) { case 0x00000020: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cvts; return i; } } break; } } break; case 0x48000000 >> 26: { // cop2 switch ((opcode & 0x03E00000) >> 21) { case 0x00200000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_qmfc2; return i; case 0x00400000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_cfc2; return i; case 0x00A00000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_qmtc2; return i; case 0x00C00000 >> 21: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_ctc2; return i; case 0x01000000 >> 21: { switch ((opcode & 0x001F0000) >> 16) { case 0x00000000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc2f; return i; case 0x00010000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 1; i.func = ee_i_bc2t; return i; case 0x00020000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc2fl; return i; case 0x00030000 >> 16: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bc2tl; return i; } } break; case 0x02000000 >> 21: case 0x02200000 >> 21: case 0x02400000 >> 21: case 0x02600000 >> 21: case 0x02800000 >> 21: case 0x02A00000 >> 21: case 0x02C00000 >> 21: case 0x02E00000 >> 21: case 0x03000000 >> 21: case 0x03200000 >> 21: case 0x03400000 >> 21: case 0x03600000 >> 21: case 0x03800000 >> 21: case 0x03A00000 >> 21: case 0x03C00000 >> 21: case 0x03E00000 >> 21: { switch (opcode & 0x0000003F) { case 0x00000000: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddx; return i; case 0x00000001: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddy; return i; case 0x00000002: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddz; return i; case 0x00000003: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddw; return i; case 0x00000004: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubx; return i; case 0x00000005: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsuby; return i; case 0x00000006: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubz; return i; case 0x00000007: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubw; return i; case 0x00000008: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddx; return i; case 0x00000009: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddy; return i; case 0x0000000A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddz; return i; case 0x0000000B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddw; return i; case 0x0000000C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubx; return i; case 0x0000000D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsuby; return i; case 0x0000000E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubz; return i; case 0x0000000F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubw; return i; case 0x00000010: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxx; return i; case 0x00000011: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxy; return i; case 0x00000012: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxz; return i; case 0x00000013: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxw; return i; case 0x00000014: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminix; return i; case 0x00000015: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminiy; return i; case 0x00000016: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminiz; return i; case 0x00000017: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminiw; return i; case 0x00000018: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulx; return i; case 0x00000019: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmuly; return i; case 0x0000001A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulz; return i; case 0x0000001B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulw; return i; case 0x0000001C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulq; return i; case 0x0000001D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaxi; return i; case 0x0000001E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmuli; return i; case 0x0000001F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vminii; return i; case 0x00000020: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddq; return i; case 0x00000021: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddq; return i; case 0x00000022: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddi; return i; case 0x00000023: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddi; return i; case 0x00000024: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubq; return i; case 0x00000025: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubq; return i; case 0x00000026: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubi; return i; case 0x00000027: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubi; return i; case 0x00000028: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vadd; return i; case 0x00000029: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmadd; return i; case 0x0000002A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmul; return i; case 0x0000002B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmax; return i; case 0x0000002C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsub; return i; case 0x0000002D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsub; return i; case 0x0000002E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vopmsub; return i; case 0x0000002F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmini; return i; case 0x00000030: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_viadd; return i; case 0x00000031: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_visub; return i; case 0x00000032: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_viaddi; return i; case 0x00000034: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_viand; return i; case 0x00000035: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vior; return i; case 0x00000038: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vcallms; return i; case 0x00000039: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vcallmsr; return i; case 0x0000003C: case 0x0000003D: case 0x0000003E: case 0x0000003F: { uint32_t func = (opcode & 3) | ((opcode & 0x7c0) >> 4); switch (func) { case 0x00000000: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddax; return i; case 0x00000001: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vadday; return i; case 0x00000002: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddaz; return i; case 0x00000003: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddaw; return i; case 0x00000004: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubax; return i; case 0x00000005: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubay; return i; case 0x00000006: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubaz; return i; case 0x00000007: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubaw; return i; case 0x00000008: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddax; return i; case 0x00000009: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmadday; return i; case 0x0000000A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddaz; return i; case 0x0000000B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddaw; return i; case 0x0000000C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubax; return i; case 0x0000000D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubay; return i; case 0x0000000E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubaz; return i; case 0x0000000F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubaw; return i; case 0x00000010: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vitof0; return i; case 0x00000011: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vitof4; return i; case 0x00000012: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vitof12; return i; case 0x00000013: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vitof15; return i; case 0x00000014: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vftoi0; return i; case 0x00000015: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vftoi4; return i; case 0x00000016: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vftoi12; return i; case 0x00000017: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vftoi15; return i; case 0x00000018: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulax; return i; case 0x00000019: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulay; return i; case 0x0000001A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulaz; return i; case 0x0000001B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulaw; return i; case 0x0000001C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulaq; return i; case 0x0000001D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vabs; return i; case 0x0000001E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmulai; return i; case 0x0000001F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vclipw; return i; case 0x00000020: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddaq; return i; case 0x00000021: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddaq; return i; case 0x00000022: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vaddai; return i; case 0x00000023: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmaddai; return i; case 0x00000024: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubaq; return i; case 0x00000025: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubaq; return i; case 0x00000026: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsubai; return i; case 0x00000027: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsubai; return i; case 0x00000028: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vadda; return i; case 0x00000029: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmadda; return i; case 0x0000002A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmula; return i; case 0x0000002C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsuba; return i; case 0x0000002D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmsuba; return i; case 0x0000002E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vopmula; return i; case 0x0000002F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vnop; return i; case 0x00000030: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmove; return i; case 0x00000031: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmr32; return i; case 0x00000034: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vlqi; return i; case 0x00000035: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsqi; return i; case 0x00000036: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vlqd; return i; case 0x00000037: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsqd; return i; case 0x00000038: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vdiv; return i; case 0x00000039: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vsqrt; return i; case 0x0000003A: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrsqrt; return i; case 0x0000003B: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vwaitq; return i; case 0x0000003C: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmtir; return i; case 0x0000003D: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vmfir; return i; case 0x0000003E: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vilwr; return i; case 0x0000003F: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_viswr; return i; case 0x00000040: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrnext; return i; case 0x00000041: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrget; return i; case 0x00000042: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrinit; return i; case 0x00000043: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_vrxor; return i; } } break; } } break; } } break; case 0x50000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_beql; return i; case 0x54000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bnel; return i; case 0x58000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_blezl; return i; case 0x5C000000 >> 26: i.cycles = EE_CYC_BRANCH; i.branch = 3; i.func = ee_i_bgtzl; return i; case 0x60000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 4; i.func = ee_i_daddi; return i; case 0x64000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_daddiu; return i; case 0x68000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_ldl; return i; case 0x6C000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_ldr; return i; case 0x70000000 >> 26: { // mmi switch (opcode & 0x0000003F) { case 0x00000000: i.cycles = EE_CYC_MULT; i.func = ee_i_madd; return i; case 0x00000001: i.cycles = EE_CYC_MULT; i.func = ee_i_maddu; return i; case 0x00000004: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_plzcw; return i; case 0x00000008: { switch ((opcode & 0x000007C0) >> 6) { case 0x00000000 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddw; return i; case 0x00000040 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubw; return i; case 0x00000080 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcgtw; return i; case 0x000000C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmaxw; return i; case 0x00000100 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddh; return i; case 0x00000140 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubh; return i; case 0x00000180 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcgth; return i; case 0x000001C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmaxh; return i; case 0x00000200 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddb; return i; case 0x00000240 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubb; return i; case 0x00000280 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcgtb; return i; case 0x00000400 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddsw; return i; case 0x00000440 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubsw; return i; case 0x00000480 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextlw; return i; case 0x000004C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_ppacw; return i; case 0x00000500 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddsh; return i; case 0x00000540 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubsh; return i; case 0x00000580 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextlh; return i; case 0x000005C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_ppach; return i; case 0x00000600 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddsb; return i; case 0x00000640 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubsb; return i; case 0x00000680 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextlb; return i; case 0x000006C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_ppacb; return i; case 0x00000780 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pext5; return i; case 0x000007C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_ppac5; return i; } } break; case 0x00000009: { switch ((opcode & 0x000007C0) >> 6) { case 0x00000000 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmaddw; return i; case 0x00000080 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psllvw; return i; case 0x000000C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psrlvw; return i; case 0x00000100 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmsubw; return i; case 0x00000200 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhi; return i; case 0x00000240 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmflo; return i; case 0x00000280 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pinth; return i; case 0x00000300 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmultw; return i; case 0x00000340 >> 6: i.cycles = EE_CYC_MMI_DIV; i.func = ee_i_pdivw; return i; case 0x00000380 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcpyld; return i; case 0x00000400 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmaddh; return i; case 0x00000440 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_phmadh; return i; case 0x00000480 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pand; return i; case 0x000004C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pxor; return i; case 0x00000500 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmsubh; return i; case 0x00000540 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_phmsbh; return i; case 0x00000680 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pexeh; return i; case 0x000006C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_prevh; return i; case 0x00000700 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmulth; return i; case 0x00000740 >> 6: i.cycles = EE_CYC_MMI_DIV; i.func = ee_i_pdivbw; return i; case 0x00000780 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pexew; return i; case 0x000007C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_prot3w; return i; } } break; case 0x00000010: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mfhi1; return i; case 0x00000011: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mthi1; return i; case 0x00000012: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mflo1; return i; case 0x00000013: i.cycles = EE_CYC_COP_DEFAULT; i.func = ee_i_mtlo1; return i; case 0x00000018: i.cycles = EE_CYC_MULT; i.func = ee_i_mult1; return i; case 0x00000019: i.cycles = EE_CYC_MULT; i.func = ee_i_multu1; return i; case 0x0000001A: i.cycles = EE_CYC_DIV; i.func = ee_i_div1; return i; case 0x0000001B: i.cycles = EE_CYC_DIV; i.func = ee_i_divu1; return i; case 0x00000020: i.cycles = EE_CYC_MULT; i.func = ee_i_madd1; return i; case 0x00000021: i.cycles = EE_CYC_MULT; i.func = ee_i_maddu1; return i; case 0x00000028: { switch ((opcode & 0x000007C0) >> 6) { case 0x00000040 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pabsw; return i; case 0x00000080 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pceqw; return i; case 0x000000C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pminw; return i; case 0x00000100 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_padsbh; return i; case 0x00000140 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pabsh; return i; case 0x00000180 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pceqh; return i; case 0x000001C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pminh; return i; case 0x00000280 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pceqb; return i; case 0x00000400 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_padduw; return i; case 0x00000440 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubuw; return i; case 0x00000480 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextuw; return i; case 0x00000500 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_padduh; return i; case 0x00000540 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubuh; return i; case 0x00000580 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextuh; return i; case 0x00000600 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_paddub; return i; case 0x00000640 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psubub; return i; case 0x00000680 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pextub; return i; case 0x000006C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_qfsrv; return i; } } break; case 0x00000029: { switch ((opcode & 0x000007C0) >> 6) { case 0x00000000 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmadduw; return i; case 0x000000C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psravw; return i; case 0x00000200 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmthi; return i; case 0x00000240 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmtlo; return i; case 0x00000280 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pinteh; return i; case 0x00000300 >> 6: i.cycles = EE_CYC_MMI_MULT; i.func = ee_i_pmultuw; return i; case 0x00000340 >> 6: i.cycles = EE_CYC_MMI_DIV; i.func = ee_i_pdivuw; return i; case 0x00000380 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcpyud; return i; case 0x00000480 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_por; return i; case 0x000004C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pnor; return i; case 0x00000680 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pexch; return i; case 0x000006C0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pcpyh; return i; case 0x00000780 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pexcw; return i; } } break; case 0x00000030: { switch ((opcode & 0x000007C0) >> 6) { case 0x00000000 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhllw; return i; case 0x00000040 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhluw; return i; case 0x00000080 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhlslw; return i; case 0x000000c0 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhllh; return i; case 0x00000100 >> 6: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmfhlsh; return i; } } break; case 0x00000031: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_pmthl; return i; case 0x00000034: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psllh; return i; case 0x00000036: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psrlh; return i; case 0x00000037: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psrah; return i; case 0x0000003C: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psllw; return i; case 0x0000003E: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psrlw; return i; case 0x0000003F: i.cycles = EE_CYC_MMI_DEFAULT; i.func = ee_i_psraw; return i; } } break; case 0x78000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lq; return i; case 0x7C000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_sq; return i; case 0x80000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lb; return i; case 0x84000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lh; return i; case 0x88000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lwl; return i; case 0x8C000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lw; return i; case 0x90000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lbu; return i; case 0x94000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lhu; return i; case 0x98000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lwr; return i; case 0x9C000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lwu; return i; case 0xA0000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sb; return i; case 0xA4000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sh; return i; case 0xA8000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_swl; return i; case 0xAC000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sw; return i; case 0xB0000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sdl; return i; case 0xB4000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sdr; return i; case 0xB8000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_swr; return i; case 0xBC000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.branch = 2; i.func = ee_i_cache; return i; case 0xC4000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lwc1; return i; case 0xCC000000 >> 26: i.cycles = EE_CYC_DEFAULT; i.func = ee_i_pref; return i; case 0xD8000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_lqc2; return i; case 0xDC000000 >> 26: i.cycles = EE_CYC_LOAD; i.func = ee_i_ld; return i; case 0xE4000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_swc1; return i; case 0xF8000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sqc2; return i; case 0xFC000000 >> 26: i.cycles = EE_CYC_STORE; i.func = ee_i_sd; return i; } i.func = ee_i_invalid; return i; } static inline struct ee_block* ee_cache_block(struct ee_state* ee, int max_cycles) { uint32_t page = ee->pc / _EE_CACHE_PAGESIZE; uint32_t offset = (ee->pc & (_EE_CACHE_PAGESIZE - 1)) >> 2; if (!ee->block_cache[page]) { ee->block_cache[page] = new struct ee_block[_EE_CACHE_PAGESIZE >> 2]; } struct ee_block& block = ee->block_cache[page][offset]; uint32_t pc = ee->pc; uint32_t block_pc = ee->pc; ee_instruction i; block.cycles = 0; block.instructions.reserve(max_cycles); while (max_cycles) { ee->opcode = bus_read32(ee, pc); if (ee->exception) { // An exception occurred while fetching the instruction // Stop caching the block here ee->exception = 0; // Cache at the new location (handler) return ee_cache_block(ee, max_cycles); } if (ee->opcode != 0) { i = ee_decode(ee->opcode); block.instructions.push_back(i); } else { i.func = ee_i_nop; i.branch = 0; block.instructions.push_back(i); } block.cycles += i.cycles; if (i.branch == 1 || i.branch == 3) { max_cycles = 2; } else if (i.branch != 0) { max_cycles = 1; } max_cycles--; pc += 4; } return █ } static inline struct ee_block* ee_find_block(struct ee_state* ee, uint32_t pc) { // Fast path: check single-entry cache first (avoids hash computation) if (pc == ee->last_block_lookup_pc) { return ee->last_block_ptr; } uint32_t page = pc / _EE_CACHE_PAGESIZE; if (!ee->block_cache[page]) { return nullptr; } uint32_t offset = (pc & (_EE_CACHE_PAGESIZE - 1)) >> 2; struct ee_block& block = ee->block_cache[page][offset]; if (!block.cycles) { return nullptr; } // Update cache for next lookup ee->last_block_lookup_pc = pc; ee->last_block_ptr = █ return █ } int ee_run_block(struct ee_state* ee, int max_cycles) { // This is the entrypoint to the EENULL thread. // If we hit this address, the program is basically idling // so we "fast-forward" 1024 cycles ee->branch = 0; ee->delay_slot = 0; if (ee_check_irq(ee)) return 0; if (ee->pc == 0x81fc0 || ee->intc_reads >= 10000) { // ee->csr_reads >= 1000 ee->total_cycles += 2048; ee->count += 2048; // ee->eenull_counter += 8 * 64; ee->idle_skips++; return 2048; } struct ee_block* block = ee_find_block(ee, ee->pc); if (!block) { ee->cache_misses++; block = ee_cache_block(ee, max_cycles); } ee->block_pc = ee->pc; int cycles = 0; for (const auto& i : block->instructions) { ee->delay_slot = ee->branch; ee->branch = 0; // If we need to handle an interrupt, break out of the loop // if (ee_check_irq(ee)) // break; ee->pc = ee->next_pc; ee->next_pc += 4; i.func(ee, i); ee->count++; ee->r[0] = { 0 }; cycles++; // An exception occurred or likely branch was taken // break immediately and clear the exception flag if (ee->exception) { ee->exception = 0; break; } } // printf("ee: Block executed with %d cycles pc=%08x\n", cycles, ee->pc); return cycles; } int ee_step(struct ee_state* ee) { static ee_instruction i; ee->delay_slot = ee->branch; ee->branch = 0; // Would check for interrupts here, but we do this outside of the core // to reduce overhead ee_check_irq(ee); ee->prev_pc = ee->pc; ee->opcode = bus_read32(ee, ee->pc); ee->pc = ee->next_pc; ee->next_pc += 4; i = ee_decode(ee->opcode); i.func(ee, i); ++ee->total_cycles; ++ee->count; ee->r[0].u64[0] = 0; ee->r[0].u64[1] = 0; return 1; } void ee_flush_cache(struct ee_state* ee) { for (auto& page : ee->block_cache) { delete[] page; page = nullptr; } ee->last_block_lookup_pc = ~0u; ee->last_block_ptr = nullptr; } uint32_t ee_get_pc(struct ee_state* ee) { return ee->pc; } struct ps2_ram* ee_get_spr(struct ee_state* ee) { return ee->spr; } void ee_set_fmv_skip(struct ee_state* ee, int v) { ee->fmv_skip = v; } void ee_reset_intc_reads(struct ee_state* ee) { ee->intc_reads = 0; } void ee_reset_csr_reads(struct ee_state* ee) { ee->csr_reads = 0; } void ee_set_ram_size(struct ee_state* ee, int ram_size) { ee->ram_size = ram_size - 1; } void ee_set_osd_config(struct ee_state* ee, struct ee_osd_config config) { ee->osd_config = config; } struct ee_osd_config ee_get_osd_config(struct ee_state* ee) { return ee->osd_config; } ================================================ FILE: src/ee/ee_def.hpp ================================================ #pragma once #include #include "shared/ram.h" #include "u128.h" #include "vu.h" #include "vu_def.hpp" #include #include #ifdef _EE_USE_INTRINSICS #ifdef _MSC_VER #define EE_ALIGNED16 __declspec(align(16)) #else #define EE_ALIGNED16 __attribute__((aligned(16))) #endif #else #define EE_ALIGNED16 #endif #define EE_CYC_DEFAULT 9 #define EE_CYC_BRANCH 11 #define EE_CYC_COP_DEFAULT 7 #define EE_CYC_MULT 2*8 #define EE_CYC_DIV 14*8 #define EE_CYC_MMI_MULT 3*8 #define EE_CYC_MMI_DIV 22*8 #define EE_CYC_MMI_DEFAULT 14 #define EE_CYC_FPU_MULT 4*8 #define EE_CYC_FPU_DIV 6*8 #define EE_CYC_STORE 14 #define EE_CYC_LOAD 14 struct ee_instruction { uint32_t opcode; int32_t rs; int32_t rt; int32_t rd; int32_t sa; int32_t i15; int32_t i16; int32_t i26; // 0 - no branch // 1 - delayed branch // 2 - immediate branch // 3 - likely branch // 4 - conditional exception int branch; int cycles; void (*func)(struct ee_state*, const ee_instruction&); }; struct ee_block { std::vector instructions; uint32_t cycles = 0; }; struct ee_state { struct ee_bus_s bus; uint32_t block_pc; std::vector block_cache; // Single-entry block cache for fast lookup (avoid hash computation) // Exploits temporal locality since we execute the same block repeatedly uint32_t last_block_lookup_pc; struct ee_block* last_block_ptr; uint128_t r[32] EE_ALIGNED16; uint128_t hi EE_ALIGNED16; uint128_t lo EE_ALIGNED16; uint64_t total_cycles; int exception; int fmv_skip; uint32_t prev_pc; uint32_t pc; uint32_t next_pc; uint32_t opcode; uint64_t sa; int branch, branch_taken, delay_slot; struct ps2_ram* spr; int cpcond0; union { uint32_t cop0_r[32]; struct { uint32_t index; uint32_t random; uint32_t entrylo0; uint32_t entrylo1; uint32_t context; uint32_t pagemask; uint32_t wired; uint32_t unused7; uint32_t badvaddr; uint32_t count; uint32_t entryhi; uint32_t compare; uint32_t status; uint32_t cause; uint32_t epc; uint32_t prid; uint32_t config; uint32_t unused16; uint32_t unused17; uint32_t unused18; uint32_t unused19; uint32_t unused20; uint32_t unused21; uint32_t badpaddr; uint32_t debug; uint32_t perf; uint32_t unused25; uint32_t unused26; uint32_t taglo; uint32_t taghi; uint32_t errorepc; uint32_t unused30; uint32_t unused31; }; }; uint32_t thread_list_base; union ee_fpu_reg f[32]; union ee_fpu_reg a; uint32_t fcr; struct vu_state* vu0; struct vu_state* vu1; struct ee_vtlb_entry vtlb[48]; struct ee_osd_config osd_config; int eenull_counter; int csr_reads; int intc_reads; int ram_size; // Stats uint64_t cache_misses; uint64_t cache_hits; uint64_t idle_skips; }; #define THS_RUN 0x01 #define THS_READY 0x02 #define THS_WAIT 0x04 #define THS_SUSPEND 0x08 #define THS_WAITSUSPEND 0x0C // THS_WAIT | THS_SUSPEND #define THS_DORMANT 0x10 struct ee_thread { uint32_t prev; // TCB* uint32_t next; // TCB* int status; uint32_t func; // void* uint32_t current_stack; // void* uint32_t gp_reg; // void* short current_priority; short init_priority; int wait_type; //0=not waiting, 1=sleeping, 2=waiting on semaphore int sema_id; int wakeup_count; int attr; int option; uint32_t func_; // void* ??? int argc; uint32_t argv; // char** uint32_t initial_stack; // void* int stack_size; uint32_t root; // int* function to return to when exiting thread? uint32_t heap_base; // void* }; ================================================ FILE: src/ee/ee_dis.c ================================================ #include #include #include #include #include "ee_dis.h" static struct ee_dis_state *s; char *ptr; #define EE_D_RS ((opcode >> 21) & 0x1f) #define EE_D_RT ((opcode >> 16) & 0x1f) #define EE_D_RD ((opcode >> 11) & 0x1f) #define EE_D_SA ((opcode >> 6) & 0x1f) #define EE_D_I16 (opcode & 0xffff) #define EE_D_I26 (opcode & 0x3ffffff) #define EE_D_SI26 ((int32_t)(EE_D_I26 << 6) >> 4) #define EE_D_SI16 ((int32_t)(EE_D_I16 << 16) >> 14) static const char *ee_cop0_r[] = { "Index", "Random", "EntryLo0", "EntryLo1", "Context", "PageMask", "Wired", "Unused7", "BadVAddr", "Count", "EntryHi", "Compare", "Status", "Cause", "EPC", "PRId", "Config", "Unused17", "Unused18", "Unused19", "Unused20", "Unused21", "Unused22", "BadPAddr", "Debug", "Perf", "Unused26", "Unused27", "TagLo", "TagHi", "ErrorEPC", "Unused31"}; static const char *ee_cc_r[] = { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" }; static inline void ee_d_abss(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $f%d, $f%d", "abs.s", EE_D_RD, EE_D_RS); } static 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]); } static inline void ee_d_addas(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $f%d, $f%d", "adda.s", EE_D_RS, EE_D_RT); } static 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); } static 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); } static 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); } static 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]); } static 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]); } static 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); } static inline void ee_d_bc0f(uint32_t opcode) { ptr += sprintf(ptr, "%-8s 0x%x", "bc0f", s->pc + 4 + EE_D_SI16); } static inline void ee_d_bc0fl(uint32_t opcode) { ptr += sprintf(ptr, "%-8s 0x%x", "bc0fl", s->pc + 4 + EE_D_SI16); } static inline void ee_d_bc0t(uint32_t opcode) { ptr += sprintf(ptr, "%-8s 0x%x", "bc0t", s->pc + 4 + EE_D_SI16); } static inline void ee_d_bc0tl(uint32_t opcode) { ptr += sprintf(ptr, "%-8s 0x%x", "bc0tl", s->pc + 4 + EE_D_SI16); } static inline void ee_d_bc1f(uint32_t opcode) { ptr += sprintf(ptr, "%-8s 0x%x", "bc1f", s->pc + 4 + EE_D_SI16); } static inline void ee_d_bc1fl(uint32_t opcode) { ptr += sprintf(ptr, "%-8s 0x%x", "bc1fl", s->pc + 4 + EE_D_SI16); } static inline void ee_d_bc1t(uint32_t opcode) { ptr += sprintf(ptr, "%-8s 0x%x", "bc1t", s->pc + 4 + EE_D_SI16); } static inline void ee_d_bc1tl(uint32_t opcode) { ptr += sprintf(ptr, "%-8s 0x%x", "bc1tl", s->pc + 4 + EE_D_SI16); } static inline void ee_d_bc2f(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "bc2f"); } static inline void ee_d_bc2fl(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "bc2fl"); } static inline void ee_d_bc2t(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "bc2t"); } static inline void ee_d_bc2tl(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "bc2tl"); } static 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); } static 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); } static 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); } static 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); } static 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); } static 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); } static 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); } static 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); } static 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); } static 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); } static 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); } static 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); } static 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); } static 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); } static 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); } static 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); } static inline void ee_d_break(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "break"); } static 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]); } static inline void ee_d_callmsr(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "callmsr"); } static 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); } static 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); } static inline void ee_d_cfc2(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "cfc2"); } static 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); } static 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); } static 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); } static 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); } static inline void ee_d_ctc2(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "ctc2"); } static 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); } static 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); } static 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]); } static 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); } static 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); } static 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]); } static inline void ee_d_di(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "di"); } static 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]); } static 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]); } static 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); } static 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]); } static 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]); } static 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); } static 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); } static 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]); } static 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); } static 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); } static 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]); } static 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); } static 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); } static 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]); } static 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]); } static 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]); } static inline void ee_d_ei(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "ei"); } static inline void ee_d_eret(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "eret"); } static inline void ee_d_j(uint32_t opcode) { ptr += sprintf(ptr, "%-8s 0x%08x", "j", ((s->pc + 4) & 0xf0000000) | (EE_D_I26 << 2)); } static inline void ee_d_jal(uint32_t opcode) { ptr += sprintf(ptr, "%-8s 0x%08x", "jal", ((s->pc + 4) & 0xf0000000) | (EE_D_I26 << 2)); } static 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]); } static inline void ee_d_jr(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "jr", ee_cc_r[EE_D_RS]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static inline void ee_d_lqc2(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "lqc2"); } static inline void ee_d_lui(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s, %d", "lui", ee_cc_r[EE_D_RT], EE_D_I16); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static inline void ee_d_maddas(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $f%d, $f%d", "madda.s", EE_D_RS, EE_D_RT); } static 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); } static 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]); } static 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]); } static 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); } static 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]); } static 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); } static inline void ee_d_mfhi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "mfhi", ee_cc_r[EE_D_RD]); } static inline void ee_d_mfhi1(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "mfhi1", ee_cc_r[EE_D_RD]); } static inline void ee_d_mflo(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "mflo", ee_cc_r[EE_D_RD]); } static inline void ee_d_mflo1(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "mflo1", ee_cc_r[EE_D_RD]); } static inline void ee_d_mfsa(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "mfsa", ee_cc_r[EE_D_RD]); } static 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); } static 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]); } static inline void ee_d_movs(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $f%d, $f%d", "mov.s", EE_D_RD, EE_D_RS); } static 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]); } static inline void ee_d_msubas(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $f%d, $f%d", "msuba.s", EE_D_RS, EE_D_RT); } static 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); } static 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]); } static 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); } static inline void ee_d_mthi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "mthi", ee_cc_r[EE_D_RS]); } static inline void ee_d_mthi1(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "mthi1", ee_cc_r[EE_D_RS]); } static inline void ee_d_mtlo(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "mtlo", ee_cc_r[EE_D_RS]); } static inline void ee_d_mtlo1(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "mtlo1", ee_cc_r[EE_D_RS]); } static inline void ee_d_mtsa(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "mtsa", ee_cc_r[EE_D_RS]); } static inline void ee_d_mtsab(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s, %d", "mtsab", ee_cc_r[EE_D_RS], EE_D_I16); } static inline void ee_d_mtsah(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s, %d", "mtsah", ee_cc_r[EE_D_RS], EE_D_I16); } static inline void ee_d_mulas(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $f%d, $f%d", "mula.s", EE_D_RS, EE_D_RT); } static 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); } static 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]); } static 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]); } static 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]); } static 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]); } static inline void ee_d_negs(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $f%d, $f%d", "neg.s", EE_D_RD, EE_D_RS); } static 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]); } static 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]); } static 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); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static inline void ee_d_pmfhi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "pmfhi", ee_cc_r[EE_D_RD]); } static inline void ee_d_pmfhllw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "pmfhl.lw", ee_cc_r[EE_D_RD]); } static inline void ee_d_pmfhluw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "pmfhl.uw", ee_cc_r[EE_D_RD]); } static inline void ee_d_pmfhlslw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "pmfhl.slw", ee_cc_r[EE_D_RD]); } static inline void ee_d_pmfhllh(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "pmfhl.lh", ee_cc_r[EE_D_RD]); } static inline void ee_d_pmfhlsh(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "pmfhl.sh", ee_cc_r[EE_D_RD]); } static inline void ee_d_pmflo(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "pmflo", ee_cc_r[EE_D_RD]); } static 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]); } static 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]); } static 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]); } static 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]); } static inline void ee_d_pmthi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "pmthi", ee_cc_r[EE_D_RS]); } static inline void ee_d_pmthl(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "pmthl", ee_cc_r[EE_D_RS]); } static inline void ee_d_pmtlo(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s", "pmtlo", ee_cc_r[EE_D_RS]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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); } static 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]); } static 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); } static 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); } static 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]); } static 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); } static 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); } static 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]); } static 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); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static inline void ee_d_qmfc2(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "qmfc2"); } static inline void ee_d_qmtc2(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "qmtc2"); } static 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); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static 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); } static 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]); } static 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]); } static 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); } static 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); } static 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]); } static 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]); } static inline void ee_d_sqc2(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "sqc2"); } static inline void ee_d_sqrts(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $f%d, $f%d", "sqrt.s", EE_D_RD, EE_D_RT); } static 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); } static 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]); } static 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); } static 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]); } static 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]); } static inline void ee_d_subas(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $f%d, $f%d", "suba.s", EE_D_RS, EE_D_RT); } static 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); } static 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]); } static 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]); } static 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]); } static 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]); } static 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]); } static inline void ee_d_sync(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "sync"); } static inline void ee_d_syscall(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "syscall"); } static 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]); } static inline void ee_d_teqi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s, %d", "teqi", ee_cc_r[EE_D_RS], EE_D_I16); } static 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]); } static inline void ee_d_tgei(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s, %d", "tgei", ee_cc_r[EE_D_RS], EE_D_I16); } static inline void ee_d_tgeiu(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s, %d", "tgeiu", ee_cc_r[EE_D_RS], EE_D_I16); } static 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]); } static inline void ee_d_tlbp(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "tlbp"); } static inline void ee_d_tlbr(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "tlbr"); } static inline void ee_d_tlbwi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "tlbwi"); } static inline void ee_d_tlbwr(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "tlbwr"); } static 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]); } static inline void ee_d_tlti(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s, %d", "tlti", ee_cc_r[EE_D_RS], EE_D_I16); } static inline void ee_d_tltiu(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s, %d", "tltiu", ee_cc_r[EE_D_RS], EE_D_I16); } static 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]); } static 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]); } static inline void ee_d_tnei(uint32_t opcode) { ptr += sprintf(ptr, "%-8s $%s, %d", "tnei", ee_cc_r[EE_D_RS], EE_D_I16); } static inline void ee_d_vabs(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vabs"); } static inline void ee_d_vadd(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vadd"); } static inline void ee_d_vadda(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vadda"); } static inline void ee_d_vaddai(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vaddai"); } static inline void ee_d_vaddaq(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vaddaq"); } static inline void ee_d_vaddaw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vaddaw"); } static inline void ee_d_vaddax(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vaddax"); } static inline void ee_d_vadday(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vadday"); } static inline void ee_d_vaddaz(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vaddaz"); } static inline void ee_d_vaddi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vaddi"); } static inline void ee_d_vaddq(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vaddq"); } static inline void ee_d_vaddw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vaddw"); } static inline void ee_d_vaddx(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vaddx"); } static inline void ee_d_vaddy(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vaddy"); } static inline void ee_d_vaddz(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vaddz"); } static inline void ee_d_vcallms(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vcallms"); } static inline void ee_d_vclipw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vclipw"); } static inline void ee_d_vdiv(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vdiv"); } static inline void ee_d_vftoi0(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vftoi0"); } static inline void ee_d_vftoi12(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vftoi12"); } static inline void ee_d_vftoi15(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vftoi15"); } static inline void ee_d_vftoi4(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vftoi4"); } static inline void ee_d_viadd(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "viadd"); } static inline void ee_d_viaddi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "viaddi"); } static inline void ee_d_viand(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "viand"); } static inline void ee_d_vilwr(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vilwr"); } static inline void ee_d_vior(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vior"); } static inline void ee_d_visub(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "visub"); } static inline void ee_d_viswr(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "viswr"); } static inline void ee_d_vitof0(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vitof0"); } static inline void ee_d_vitof12(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vitof12"); } static inline void ee_d_vitof15(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vitof15"); } static inline void ee_d_vitof4(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vitof4"); } static inline void ee_d_vlqd(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vlqd"); } static inline void ee_d_vlqi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vlqi"); } static inline void ee_d_vmadd(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmadd"); } static inline void ee_d_vmadda(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmadda"); } static inline void ee_d_vmaddai(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaddai"); } static inline void ee_d_vmaddaq(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaddaq"); } static inline void ee_d_vmaddaw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaddaw"); } static inline void ee_d_vmaddax(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaddax"); } static inline void ee_d_vmadday(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmadday"); } static inline void ee_d_vmaddaz(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaddaz"); } static inline void ee_d_vmaddi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaddi"); } static inline void ee_d_vmaddq(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaddq"); } static inline void ee_d_vmaddw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaddw"); } static inline void ee_d_vmaddx(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaddx"); } static inline void ee_d_vmaddy(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaddy"); } static inline void ee_d_vmaddz(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaddz"); } static inline void ee_d_vmax(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmax"); } static inline void ee_d_vmaxi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaxi"); } static inline void ee_d_vmaxw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaxw"); } static inline void ee_d_vmaxx(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaxx"); } static inline void ee_d_vmaxy(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaxy"); } static inline void ee_d_vmaxz(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmaxz"); } static inline void ee_d_vmfir(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmfir"); } static inline void ee_d_vmini(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmini"); } static inline void ee_d_vminii(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vminii"); } static inline void ee_d_vminiw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vminiw"); } static inline void ee_d_vminix(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vminix"); } static inline void ee_d_vminiy(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vminiy"); } static inline void ee_d_vminiz(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vminiz"); } static inline void ee_d_vmove(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmove"); } static inline void ee_d_vmr32(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmr32"); } static inline void ee_d_vmsub(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsub"); } static inline void ee_d_vmsuba(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsuba"); } static inline void ee_d_vmsubai(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsubai"); } static inline void ee_d_vmsubaq(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsubaq"); } static inline void ee_d_vmsubaw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsubaw"); } static inline void ee_d_vmsubax(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsubax"); } static inline void ee_d_vmsubay(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsubay"); } static inline void ee_d_vmsubaz(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsubaz"); } static inline void ee_d_vmsubi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsubi"); } static inline void ee_d_vmsubq(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsubq"); } static inline void ee_d_vmsubw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsubw"); } static inline void ee_d_vmsubx(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsubx"); } static inline void ee_d_vmsuby(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsuby"); } static inline void ee_d_vmsubz(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmsubz"); } static inline void ee_d_vmtir(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmtir"); } static inline void ee_d_vmul(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmul"); } static inline void ee_d_vmula(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmula"); } static inline void ee_d_vmulai(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmulai"); } static inline void ee_d_vmulaq(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmulaq"); } static inline void ee_d_vmulaw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmulaw"); } static inline void ee_d_vmulax(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmulax"); } static inline void ee_d_vmulay(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmulay"); } static inline void ee_d_vmulaz(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmulaz"); } static inline void ee_d_vmuli(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmuli"); } static inline void ee_d_vmulq(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmulq"); } static inline void ee_d_vmulw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmulw"); } static inline void ee_d_vmulx(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmulx"); } static inline void ee_d_vmuly(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmuly"); } static inline void ee_d_vmulz(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vmulz"); } static inline void ee_d_vnop(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vnop"); } static inline void ee_d_vopmsub(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vopmsub"); } static inline void ee_d_vopmula(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vopmula"); } static inline void ee_d_vrget(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vrget"); } static inline void ee_d_vrinit(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vrinit"); } static inline void ee_d_vrnext(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vrnext"); } static inline void ee_d_vrsqrt(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vrsqrt"); } static inline void ee_d_vrxor(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vrxor"); } static inline void ee_d_vsqd(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsqd"); } static inline void ee_d_vsqi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsqi"); } static inline void ee_d_vsqrt(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsqrt"); } static inline void ee_d_vsub(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsub"); } static inline void ee_d_vsuba(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsuba"); } static inline void ee_d_vsubai(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsubai"); } static inline void ee_d_vsubaq(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsubaq"); } static inline void ee_d_vsubaw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsubaw"); } static inline void ee_d_vsubax(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsubax"); } static inline void ee_d_vsubay(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsubay"); } static inline void ee_d_vsubaz(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsubaz"); } static inline void ee_d_vsubi(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsubi"); } static inline void ee_d_vsubq(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsubq"); } static inline void ee_d_vsubw(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsubw"); } static inline void ee_d_vsubx(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsubx"); } static inline void ee_d_vsuby(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsuby"); } static inline void ee_d_vsubz(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vsubz"); } static inline void ee_d_vwaitq(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", "vwaitq"); } static 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]); } static 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); } static inline void ee_d_invalid(uint32_t opcode) { ptr += sprintf(ptr, "%-8s", ""); } char *ee_disassemble(char *buf, uint32_t opcode, struct ee_dis_state *dis_state) { s = dis_state; ptr = buf; if (dis_state) if (dis_state->print_address) ptr += sprintf(ptr, "%08x: ", dis_state->pc); if (dis_state) if (dis_state->print_opcode) ptr += sprintf(ptr, "%08x ", opcode); switch (opcode & 0xFC000000) { case 0x00000000: { // special switch (opcode & 0x0000003F) { case 0x00000000: ee_d_sll(opcode); return buf; case 0x00000002: ee_d_srl(opcode); return buf; case 0x00000003: ee_d_sra(opcode); return buf; case 0x00000004: ee_d_sllv(opcode); return buf; case 0x00000006: ee_d_srlv(opcode); return buf; case 0x00000007: ee_d_srav(opcode); return buf; case 0x00000008: ee_d_jr(opcode); return buf; case 0x00000009: ee_d_jalr(opcode); return buf; case 0x0000000A: ee_d_movz(opcode); return buf; case 0x0000000B: ee_d_movn(opcode); return buf; case 0x0000000C: ee_d_syscall(opcode); return buf; case 0x0000000D: ee_d_break(opcode); return buf; case 0x0000000F: ee_d_sync(opcode); return buf; case 0x00000010: ee_d_mfhi(opcode); return buf; case 0x00000011: ee_d_mthi(opcode); return buf; case 0x00000012: ee_d_mflo(opcode); return buf; case 0x00000013: ee_d_mtlo(opcode); return buf; case 0x00000014: ee_d_dsllv(opcode); return buf; case 0x00000016: ee_d_dsrlv(opcode); return buf; case 0x00000017: ee_d_dsrav(opcode); return buf; case 0x00000018: ee_d_mult(opcode); return buf; case 0x00000019: ee_d_multu(opcode); return buf; case 0x0000001A: ee_d_div(opcode); return buf; case 0x0000001B: ee_d_divu(opcode); return buf; case 0x00000020: ee_d_add(opcode); return buf; case 0x00000021: ee_d_addu(opcode); return buf; case 0x00000022: ee_d_sub(opcode); return buf; case 0x00000023: ee_d_subu(opcode); return buf; case 0x00000024: ee_d_and(opcode); return buf; case 0x00000025: ee_d_or(opcode); return buf; case 0x00000026: ee_d_xor(opcode); return buf; case 0x00000027: ee_d_nor(opcode); return buf; case 0x00000028: ee_d_mfsa(opcode); return buf; case 0x00000029: ee_d_mtsa(opcode); return buf; case 0x0000002A: ee_d_slt(opcode); return buf; case 0x0000002B: ee_d_sltu(opcode); return buf; case 0x0000002C: ee_d_dadd(opcode); return buf; case 0x0000002D: ee_d_daddu(opcode); return buf; case 0x0000002E: ee_d_dsub(opcode); return buf; case 0x0000002F: ee_d_dsubu(opcode); return buf; case 0x00000030: ee_d_tge(opcode); return buf; case 0x00000031: ee_d_tgeu(opcode); return buf; case 0x00000032: ee_d_tlt(opcode); return buf; case 0x00000033: ee_d_tltu(opcode); return buf; case 0x00000034: ee_d_teq(opcode); return buf; case 0x00000036: ee_d_tne(opcode); return buf; case 0x00000038: ee_d_dsll(opcode); return buf; case 0x0000003A: ee_d_dsrl(opcode); return buf; case 0x0000003B: ee_d_dsra(opcode); return buf; case 0x0000003C: ee_d_dsll32(opcode); return buf; case 0x0000003E: ee_d_dsrl32(opcode); return buf; case 0x0000003F: ee_d_dsra32(opcode); return buf; } } break; case 0x04000000: { // regimm switch (opcode & 0x001F0000) { case 0x00000000: ee_d_bltz(opcode); return buf; case 0x00010000: ee_d_bgez(opcode); return buf; case 0x00020000: ee_d_bltzl(opcode); return buf; case 0x00030000: ee_d_bgezl(opcode); return buf; case 0x00080000: ee_d_tgei(opcode); return buf; case 0x00090000: ee_d_tgeiu(opcode); return buf; case 0x000A0000: ee_d_tlti(opcode); return buf; case 0x000B0000: ee_d_tltiu(opcode); return buf; case 0x000C0000: ee_d_teqi(opcode); return buf; case 0x000E0000: ee_d_tnei(opcode); return buf; case 0x00100000: ee_d_bltzal(opcode); return buf; case 0x00110000: ee_d_bgezal(opcode); return buf; case 0x00120000: ee_d_bltzall(opcode); return buf; case 0x00130000: ee_d_bgezall(opcode); return buf; case 0x00180000: ee_d_mtsab(opcode); return buf; case 0x00190000: ee_d_mtsah(opcode); return buf; } } break; case 0x08000000: ee_d_j(opcode); return buf; case 0x0C000000: ee_d_jal(opcode); return buf; case 0x10000000: ee_d_beq(opcode); return buf; case 0x14000000: ee_d_bne(opcode); return buf; case 0x18000000: ee_d_blez(opcode); return buf; case 0x1C000000: ee_d_bgtz(opcode); return buf; case 0x20000000: ee_d_addi(opcode); return buf; case 0x24000000: ee_d_addiu(opcode); return buf; case 0x28000000: ee_d_slti(opcode); return buf; case 0x2C000000: ee_d_sltiu(opcode); return buf; case 0x30000000: ee_d_andi(opcode); return buf; case 0x34000000: ee_d_ori(opcode); return buf; case 0x38000000: ee_d_xori(opcode); return buf; case 0x3C000000: ee_d_lui(opcode); return buf; case 0x40000000: { // cop0 switch (opcode & 0x03E00000) { case 0x00000000: ee_d_mfc0(opcode); return buf; case 0x00800000: ee_d_mtc0(opcode); return buf; case 0x01000000: { switch (opcode & 0x001F0000) { case 0x00000000: ee_d_bc0f(opcode); return buf; case 0x00010000: ee_d_bc0t(opcode); return buf; case 0x00020000: ee_d_bc0fl(opcode); return buf; case 0x00030000: ee_d_bc0tl(opcode); return buf; } } break; case 0x02000000: { switch (opcode & 0x0000003F) { case 0x00000001: ee_d_tlbr(opcode); return buf; case 0x00000002: ee_d_tlbwi(opcode); return buf; case 0x00000006: ee_d_tlbwr(opcode); return buf; case 0x00000008: ee_d_tlbp(opcode); return buf; case 0x00000018: ee_d_eret(opcode); return buf; case 0x00000038: ee_d_ei(opcode); return buf; case 0x00000039: ee_d_di(opcode); return buf; } } break; } } break; case 0x44000000: { // cop1 switch (opcode & 0x03E00000) { case 0x00000000: ee_d_mfc1(opcode); return buf; case 0x00400000: ee_d_cfc1(opcode); return buf; case 0x00800000: ee_d_mtc1(opcode); return buf; case 0x00C00000: ee_d_ctc1(opcode); return buf; case 0x01000000: { switch (opcode & 0x001F0000) { case 0x00000000: ee_d_bc1f(opcode); return buf; case 0x00010000: ee_d_bc1t(opcode); return buf; case 0x00020000: ee_d_bc1fl(opcode); return buf; case 0x00030000: ee_d_bc1tl(opcode); return buf; } } break; case 0x02000000: { switch (opcode & 0x0000003F) { case 0x00000000: ee_d_adds(opcode); return buf; case 0x00000001: ee_d_subs(opcode); return buf; case 0x00000002: ee_d_muls(opcode); return buf; case 0x00000003: ee_d_divs(opcode); return buf; case 0x00000004: ee_d_sqrts(opcode); return buf; case 0x00000005: ee_d_abss(opcode); return buf; case 0x00000006: ee_d_movs(opcode); return buf; case 0x00000007: ee_d_negs(opcode); return buf; case 0x00000016: ee_d_rsqrts(opcode); return buf; case 0x00000018: ee_d_addas(opcode); return buf; case 0x00000019: ee_d_subas(opcode); return buf; case 0x0000001A: ee_d_mulas(opcode); return buf; case 0x0000001C: ee_d_madds(opcode); return buf; case 0x0000001D: ee_d_msubs(opcode); return buf; case 0x0000001E: ee_d_maddas(opcode); return buf; case 0x0000001F: ee_d_msubas(opcode); return buf; case 0x00000024: ee_d_cvtw(opcode); return buf; case 0x00000028: ee_d_maxs(opcode); return buf; case 0x00000029: ee_d_mins(opcode); return buf; case 0x00000030: ee_d_cf(opcode); return buf; case 0x00000032: ee_d_ceq(opcode); return buf; case 0x00000034: ee_d_clt(opcode); return buf; case 0x00000036: ee_d_cle(opcode); return buf; } } break; case 0x02800000: { switch (opcode & 0x0000003F) { case 0x00000020: ee_d_cvts(opcode); return buf; } } break; } } break; case 0x48000000: { // cop2 switch (opcode & 0x03E00000) { case 0x00200000: ee_d_qmfc2(opcode); return buf; case 0x00400000: ee_d_cfc2(opcode); return buf; case 0x00A00000: ee_d_qmtc2(opcode); return buf; case 0x00C00000: ee_d_ctc2(opcode); return buf; case 0x01000000: { switch (opcode & 0x001F0000) { case 0x00000000: ee_d_bc2f(opcode); return buf; case 0x00010000: ee_d_bc2t(opcode); return buf; case 0x00020000: ee_d_bc2fl(opcode); return buf; case 0x00030000: ee_d_bc2tl(opcode); return buf; } } break; case 0x02000000: case 0x02200000: case 0x02400000: case 0x02600000: case 0x02800000: case 0x02A00000: case 0x02C00000: case 0x02E00000: case 0x03000000: case 0x03200000: case 0x03400000: case 0x03600000: case 0x03800000: case 0x03A00000: case 0x03C00000: case 0x03E00000: { switch (opcode & 0x0000003F) { case 0x00000000: ee_d_vaddx(opcode); return buf; case 0x00000001: ee_d_vaddy(opcode); return buf; case 0x00000002: ee_d_vaddz(opcode); return buf; case 0x00000003: ee_d_vaddw(opcode); return buf; case 0x00000004: ee_d_vsubx(opcode); return buf; case 0x00000005: ee_d_vsuby(opcode); return buf; case 0x00000006: ee_d_vsubz(opcode); return buf; case 0x00000007: ee_d_vsubw(opcode); return buf; case 0x00000008: ee_d_vmaddx(opcode); return buf; case 0x00000009: ee_d_vmaddy(opcode); return buf; case 0x0000000A: ee_d_vmaddz(opcode); return buf; case 0x0000000B: ee_d_vmaddw(opcode); return buf; case 0x0000000C: ee_d_vmsubx(opcode); return buf; case 0x0000000D: ee_d_vmsuby(opcode); return buf; case 0x0000000E: ee_d_vmsubz(opcode); return buf; case 0x0000000F: ee_d_vmsubw(opcode); return buf; case 0x00000010: ee_d_vmaxx(opcode); return buf; case 0x00000011: ee_d_vmaxy(opcode); return buf; case 0x00000012: ee_d_vmaxz(opcode); return buf; case 0x00000013: ee_d_vmaxw(opcode); return buf; case 0x00000014: ee_d_vminix(opcode); return buf; case 0x00000015: ee_d_vminiy(opcode); return buf; case 0x00000016: ee_d_vminiz(opcode); return buf; case 0x00000017: ee_d_vminiw(opcode); return buf; case 0x00000018: ee_d_vmulx(opcode); return buf; case 0x00000019: ee_d_vmuly(opcode); return buf; case 0x0000001A: ee_d_vmulz(opcode); return buf; case 0x0000001B: ee_d_vmulw(opcode); return buf; case 0x0000001C: ee_d_vmulq(opcode); return buf; case 0x0000001D: ee_d_vmaxi(opcode); return buf; case 0x0000001E: ee_d_vmuli(opcode); return buf; case 0x0000001F: ee_d_vminii(opcode); return buf; case 0x00000020: ee_d_vaddq(opcode); return buf; case 0x00000021: ee_d_vmaddq(opcode); return buf; case 0x00000022: ee_d_vaddi(opcode); return buf; case 0x00000023: ee_d_vmaddi(opcode); return buf; case 0x00000024: ee_d_vsubq(opcode); return buf; case 0x00000025: ee_d_vmsubq(opcode); return buf; case 0x00000026: ee_d_vsubi(opcode); return buf; case 0x00000027: ee_d_vmsubi(opcode); return buf; case 0x00000028: ee_d_vadd(opcode); return buf; case 0x00000029: ee_d_vmadd(opcode); return buf; case 0x0000002A: ee_d_vmul(opcode); return buf; case 0x0000002B: ee_d_vmax(opcode); return buf; case 0x0000002C: ee_d_vsub(opcode); return buf; case 0x0000002D: ee_d_vmsub(opcode); return buf; case 0x0000002E: ee_d_vopmsub(opcode); return buf; case 0x0000002F: ee_d_vmini(opcode); return buf; case 0x00000030: ee_d_viadd(opcode); return buf; case 0x00000031: ee_d_visub(opcode); return buf; case 0x00000032: ee_d_viaddi(opcode); return buf; case 0x00000034: ee_d_viand(opcode); return buf; case 0x00000035: ee_d_vior(opcode); return buf; case 0x00000038: ee_d_vcallms(opcode); return buf; case 0x00000039: ee_d_callmsr(opcode); return buf; case 0x0000003C: case 0x0000003D: case 0x0000003E: case 0x0000003F: { uint32_t func = (opcode & 3) | ((opcode & 0x7c0) >> 4); switch (func) { case 0x00000000: ee_d_vaddax(opcode); return buf; case 0x00000001: ee_d_vadday(opcode); return buf; case 0x00000002: ee_d_vaddaz(opcode); return buf; case 0x00000003: ee_d_vaddaw(opcode); return buf; case 0x00000004: ee_d_vsubax(opcode); return buf; case 0x00000005: ee_d_vsubay(opcode); return buf; case 0x00000006: ee_d_vsubaz(opcode); return buf; case 0x00000007: ee_d_vsubaw(opcode); return buf; case 0x00000008: ee_d_vmaddax(opcode); return buf; case 0x00000009: ee_d_vmadday(opcode); return buf; case 0x0000000A: ee_d_vmaddaz(opcode); return buf; case 0x0000000B: ee_d_vmaddaw(opcode); return buf; case 0x0000000C: ee_d_vmsubax(opcode); return buf; case 0x0000000D: ee_d_vmsubay(opcode); return buf; case 0x0000000E: ee_d_vmsubaz(opcode); return buf; case 0x0000000F: ee_d_vmsubaw(opcode); return buf; case 0x00000010: ee_d_vitof0(opcode); return buf; case 0x00000011: ee_d_vitof4(opcode); return buf; case 0x00000012: ee_d_vitof12(opcode); return buf; case 0x00000013: ee_d_vitof15(opcode); return buf; case 0x00000014: ee_d_vftoi0(opcode); return buf; case 0x00000015: ee_d_vftoi4(opcode); return buf; case 0x00000016: ee_d_vftoi12(opcode); return buf; case 0x00000017: ee_d_vftoi15(opcode); return buf; case 0x00000018: ee_d_vmulax(opcode); return buf; case 0x00000019: ee_d_vmulay(opcode); return buf; case 0x0000001A: ee_d_vmulaz(opcode); return buf; case 0x0000001B: ee_d_vmulaw(opcode); return buf; case 0x0000001C: ee_d_vmulaq(opcode); return buf; case 0x0000001D: ee_d_vabs(opcode); return buf; case 0x0000001E: ee_d_vmulai(opcode); return buf; case 0x0000001F: ee_d_vclipw(opcode); return buf; case 0x00000020: ee_d_vaddaq(opcode); return buf; case 0x00000021: ee_d_vmaddaq(opcode); return buf; case 0x00000022: ee_d_vaddai(opcode); return buf; case 0x00000023: ee_d_vmaddai(opcode); return buf; case 0x00000024: ee_d_vsubaq(opcode); return buf; case 0x00000025: ee_d_vmsubaq(opcode); return buf; case 0x00000026: ee_d_vsubai(opcode); return buf; case 0x00000027: ee_d_vmsubai(opcode); return buf; case 0x00000028: ee_d_vadda(opcode); return buf; case 0x00000029: ee_d_vmadda(opcode); return buf; case 0x0000002A: ee_d_vmula(opcode); return buf; case 0x0000002C: ee_d_vsuba(opcode); return buf; case 0x0000002D: ee_d_vmsuba(opcode); return buf; case 0x0000002E: ee_d_vopmula(opcode); return buf; case 0x0000002F: ee_d_vnop(opcode); return buf; case 0x00000030: ee_d_vmove(opcode); return buf; case 0x00000031: ee_d_vmr32(opcode); return buf; case 0x00000034: ee_d_vlqi(opcode); return buf; case 0x00000035: ee_d_vsqi(opcode); return buf; case 0x00000036: ee_d_vlqd(opcode); return buf; case 0x00000037: ee_d_vsqd(opcode); return buf; case 0x00000038: ee_d_vdiv(opcode); return buf; case 0x00000039: ee_d_vsqrt(opcode); return buf; case 0x0000003A: ee_d_vrsqrt(opcode); return buf; case 0x0000003B: ee_d_vwaitq(opcode); return buf; case 0x0000003C: ee_d_vmtir(opcode); return buf; case 0x0000003D: ee_d_vmfir(opcode); return buf; case 0x0000003E: ee_d_vilwr(opcode); return buf; case 0x0000003F: ee_d_viswr(opcode); return buf; case 0x00000040: ee_d_vrnext(opcode); return buf; case 0x00000041: ee_d_vrget(opcode); return buf; case 0x00000042: ee_d_vrinit(opcode); return buf; case 0x00000043: ee_d_vrxor(opcode); return buf; } } break; } } break; } } break; case 0x50000000: ee_d_beql(opcode); return buf; case 0x54000000: ee_d_bnel(opcode); return buf; case 0x58000000: ee_d_blezl(opcode); return buf; case 0x5C000000: ee_d_bgtzl(opcode); return buf; case 0x60000000: ee_d_daddi(opcode); return buf; case 0x64000000: ee_d_daddiu(opcode); return buf; case 0x68000000: ee_d_ldl(opcode); return buf; case 0x6C000000: ee_d_ldr(opcode); return buf; case 0x70000000: { // mmi switch (opcode & 0x0000003F) { case 0x00000000: ee_d_madd(opcode); return buf; case 0x00000001: ee_d_maddu(opcode); return buf; case 0x00000004: ee_d_plzcw(opcode); return buf; case 0x00000008: { switch (opcode & 0x000007C0) { case 0x00000000: ee_d_paddw(opcode); return buf; case 0x00000040: ee_d_psubw(opcode); return buf; case 0x00000080: ee_d_pcgtw(opcode); return buf; case 0x000000C0: ee_d_pmaxw(opcode); return buf; case 0x00000100: ee_d_paddh(opcode); return buf; case 0x00000140: ee_d_psubh(opcode); return buf; case 0x00000180: ee_d_pcgth(opcode); return buf; case 0x000001C0: ee_d_pmaxh(opcode); return buf; case 0x00000200: ee_d_paddb(opcode); return buf; case 0x00000240: ee_d_psubb(opcode); return buf; case 0x00000280: ee_d_pcgtb(opcode); return buf; case 0x00000400: ee_d_paddsw(opcode); return buf; case 0x00000440: ee_d_psubsw(opcode); return buf; case 0x00000480: ee_d_pextlw(opcode); return buf; case 0x000004C0: ee_d_ppacw(opcode); return buf; case 0x00000500: ee_d_paddsh(opcode); return buf; case 0x00000540: ee_d_psubsh(opcode); return buf; case 0x00000580: ee_d_pextlh(opcode); return buf; case 0x000005C0: ee_d_ppach(opcode); return buf; case 0x00000600: ee_d_paddsb(opcode); return buf; case 0x00000640: ee_d_psubsb(opcode); return buf; case 0x00000680: ee_d_pextlb(opcode); return buf; case 0x000006C0: ee_d_ppacb(opcode); return buf; case 0x00000780: ee_d_pext5(opcode); return buf; case 0x000007C0: ee_d_ppac5(opcode); return buf; } } break; case 0x00000009: { switch (opcode & 0x000007C0) { case 0x00000000: ee_d_pmaddw(opcode); return buf; case 0x00000080: ee_d_psllvw(opcode); return buf; case 0x000000C0: ee_d_psrlvw(opcode); return buf; case 0x00000100: ee_d_pmsubw(opcode); return buf; case 0x00000200: ee_d_pmfhi(opcode); return buf; case 0x00000240: ee_d_pmflo(opcode); return buf; case 0x00000280: ee_d_pinth(opcode); return buf; case 0x00000300: ee_d_pmultw(opcode); return buf; case 0x00000340: ee_d_pdivw(opcode); return buf; case 0x00000380: ee_d_pcpyld(opcode); return buf; case 0x00000400: ee_d_pmaddh(opcode); return buf; case 0x00000440: ee_d_phmadh(opcode); return buf; case 0x00000480: ee_d_pand(opcode); return buf; case 0x000004C0: ee_d_pxor(opcode); return buf; case 0x00000500: ee_d_pmsubh(opcode); return buf; case 0x00000540: ee_d_phmsbh(opcode); return buf; case 0x00000680: ee_d_pexeh(opcode); return buf; case 0x000006C0: ee_d_prevh(opcode); return buf; case 0x00000700: ee_d_pmulth(opcode); return buf; case 0x00000740: ee_d_pdivbw(opcode); return buf; case 0x00000780: ee_d_pexew(opcode); return buf; case 0x000007C0: ee_d_prot3w(opcode); return buf; } } break; case 0x00000010: ee_d_mfhi1(opcode); return buf; case 0x00000011: ee_d_mthi1(opcode); return buf; case 0x00000012: ee_d_mflo1(opcode); return buf; case 0x00000013: ee_d_mtlo1(opcode); return buf; case 0x00000018: ee_d_mult1(opcode); return buf; case 0x00000019: ee_d_multu1(opcode); return buf; case 0x0000001A: ee_d_div1(opcode); return buf; case 0x0000001B: ee_d_divu1(opcode); return buf; case 0x00000020: ee_d_madd1(opcode); return buf; case 0x00000021: ee_d_maddu1(opcode); return buf; case 0x00000028: { switch (opcode & 0x000007C0) { case 0x00000040: ee_d_pabsw(opcode); return buf; case 0x00000080: ee_d_pceqw(opcode); return buf; case 0x000000C0: ee_d_pminw(opcode); return buf; case 0x00000100: ee_d_padsbh(opcode); return buf; case 0x00000140: ee_d_pabsh(opcode); return buf; case 0x00000180: ee_d_pceqh(opcode); return buf; case 0x000001C0: ee_d_pminh(opcode); return buf; case 0x00000280: ee_d_pceqb(opcode); return buf; case 0x00000400: ee_d_padduw(opcode); return buf; case 0x00000440: ee_d_psubuw(opcode); return buf; case 0x00000480: ee_d_pextuw(opcode); return buf; case 0x00000500: ee_d_padduh(opcode); return buf; case 0x00000540: ee_d_psubuh(opcode); return buf; case 0x00000580: ee_d_pextuh(opcode); return buf; case 0x00000600: ee_d_paddub(opcode); return buf; case 0x00000640: ee_d_psubub(opcode); return buf; case 0x00000680: ee_d_pextub(opcode); return buf; case 0x000006C0: ee_d_qfsrv(opcode); return buf; } } break; case 0x00000029: { switch (opcode & 0x000007C0) { case 0x00000000: ee_d_pmadduw(opcode); return buf; case 0x000000C0: ee_d_psravw(opcode); return buf; case 0x00000200: ee_d_pmthi(opcode); return buf; case 0x00000240: ee_d_pmtlo(opcode); return buf; case 0x00000280: ee_d_pinteh(opcode); return buf; case 0x00000300: ee_d_pmultuw(opcode); return buf; case 0x00000340: ee_d_pdivuw(opcode); return buf; case 0x00000380: ee_d_pcpyud(opcode); return buf; case 0x00000480: ee_d_por(opcode); return buf; case 0x000004C0: ee_d_pnor(opcode); return buf; case 0x00000680: ee_d_pexch(opcode); return buf; case 0x000006C0: ee_d_pcpyh(opcode); return buf; case 0x00000780: ee_d_pexcw(opcode); return buf; } } break; case 0x00000030: { switch (opcode & 0x000007C0) { case 0x00000000: ee_d_pmfhllw(opcode); return buf; case 0x00000040: ee_d_pmfhluw(opcode); return buf; case 0x00000080: ee_d_pmfhlslw(opcode); return buf; case 0x000000c0: ee_d_pmfhllh(opcode); return buf; case 0x00000100: ee_d_pmfhlsh(opcode); return buf; } } break; case 0x00000031: ee_d_pmthl(opcode); return buf; case 0x00000034: ee_d_psllh(opcode); return buf; case 0x00000036: ee_d_psrlh(opcode); return buf; case 0x00000037: ee_d_psrah(opcode); return buf; case 0x0000003C: ee_d_psllw(opcode); return buf; case 0x0000003E: ee_d_psrlw(opcode); return buf; case 0x0000003F: ee_d_psraw(opcode); return buf; } } break; case 0x78000000: ee_d_lq(opcode); return buf; case 0x7C000000: ee_d_sq(opcode); return buf; case 0x80000000: ee_d_lb(opcode); return buf; case 0x84000000: ee_d_lh(opcode); return buf; case 0x88000000: ee_d_lwl(opcode); return buf; case 0x8C000000: ee_d_lw(opcode); return buf; case 0x90000000: ee_d_lbu(opcode); return buf; case 0x94000000: ee_d_lhu(opcode); return buf; case 0x98000000: ee_d_lwr(opcode); return buf; case 0x9C000000: ee_d_lwu(opcode); return buf; case 0xA0000000: ee_d_sb(opcode); return buf; case 0xA4000000: ee_d_sh(opcode); return buf; case 0xA8000000: ee_d_swl(opcode); return buf; case 0xAC000000: ee_d_sw(opcode); return buf; case 0xB0000000: ee_d_sdl(opcode); return buf; case 0xB4000000: ee_d_sdr(opcode); return buf; case 0xB8000000: ee_d_swr(opcode); return buf; case 0xBC000000: ee_d_cache(opcode); return buf; case 0xC4000000: ee_d_lwc1(opcode); return buf; case 0xCC000000: ee_d_pref(opcode); return buf; case 0xD8000000: ee_d_lqc2(opcode); return buf; case 0xDC000000: ee_d_ld(opcode); return buf; case 0xE4000000: ee_d_swc1(opcode); return buf; case 0xF8000000: ee_d_sqc2(opcode); return buf; case 0xFC000000: ee_d_sd(opcode); return buf; } ee_d_invalid(opcode); return buf; } ================================================ FILE: src/ee/ee_dis.h ================================================ #ifndef EE_DIS_H #define EE_DIS_H #ifdef __cplusplus extern "C" { #endif #include struct ee_dis_state { int print_address; int print_opcode; uint32_t pc; }; char* ee_disassemble(char* buf, uint32_t opcode, struct ee_dis_state* dis_state); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/ee_uncached.c ================================================ #include #include #include #include #include #include #include #ifdef _EE_USE_INTRINSICS #include #include #include #include #endif #include "ee_uncached.h" #include "ee_dis.h" #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) #ifdef _WIN32 #define SSUBOVF64 __builtin_ssubll_overflow #define SADDOVF64 __builtin_saddll_overflow #else #define SSUBOVF64 __builtin_ssubl_overflow #define SADDOVF64 __builtin_saddl_overflow #endif // file = fopen("vu.dump", "a"); fprintf(file, #ins "\n"); fclose(file); #define VU_LOWER(ins) { ee->vu0->lower = ee->opcode; vu_i_ ## ins(ee->vu0); } #define VU_UPPER(ins) { ee->vu0->upper = ee->opcode; vu_i_ ## ins(ee->vu0); } static FILE* file; static int p = 0; static inline int fast_abs32(int a) { uint32_t m = a >> 31; return (a ^ m) + (m & 1); } static inline int16_t fast_abs16(int16_t a) { uint16_t m = a >> 15; return (a ^ m) + (m & 1); } static inline int16_t saturate16(int32_t word) { if (word > (int32_t)0x00007FFF) { return 0x7FFF; } else if (word < (int32_t)0xFFFF80000) { return 0x8000; } else { return (int16_t)word; } } #ifdef _EE_USE_INTRINSICS static inline __m128i _mm_adds_epi32(__m128i a, __m128i b) { const __m128i m = _mm_set1_epi32(0x7fffffff); __m128i r = _mm_add_epi32(a, b); __m128i sb = _mm_srli_epi32(a, 31); __m128i sat = _mm_add_epi32(m, sb); __m128i sx = _mm_xor_si128(a, b); __m128i o = _mm_andnot_si128(sx, _mm_xor_si128(a, r)); // To-do: Use SSE3 version when SSE4.1 isn't available return _mm_castps_si128(_mm_blendv_ps(_mm_castsi128_ps(r), _mm_castsi128_ps(sat), _mm_castsi128_ps(o))); } static inline __m128i _mm_adds_epu32(__m128i a, __m128i b) { const __m128i m = _mm_set1_epi32(0xffffffff); __m128i x = _mm_xor_si128(a, m); __m128i c = _mm_min_epu32(b, x); return _mm_add_epi32(a, c); } #endif static inline uint32_t unpack_5551_8888(uint32_t v) { return ((v & 0x001f) << 3) | ((v & 0x03e0) << 6) | ((v & 0x7c00) << 9) | ((v & 0x8000) << 16); } #define EE_KUSEG 0 #define EE_KSEG0 1 #define EE_KSEG1 2 #define EE_KSSEG 3 #define EE_KSEG3 4 #define EE_D_RS ((ee->opcode >> 21) & 0x1f) #define EE_D_FS ((ee->opcode >> 11) & 0x1f) #define EE_D_RT ((ee->opcode >> 16) & 0x1f) #define EE_D_RD ((ee->opcode >> 11) & 0x1f) #define EE_D_FD ((ee->opcode >> 6) & 0x1f) #define EE_D_SA ((ee->opcode >> 6) & 0x1f) #define EE_D_I15 ((ee->opcode >> 6) & 0x7fff) #define EE_D_I16 (ee->opcode & 0xffff) #define EE_D_I26 (ee->opcode & 0x3ffffff) #define EE_D_SI26 ((int32_t)(EE_D_I26 << 6) >> 4) #define EE_D_SI16 ((int32_t)(EE_D_I16 << 16) >> 14) #define EE_RT ee->r[EE_D_RT].ul64 #define EE_RD ee->r[EE_D_RD].ul64 #define EE_RS ee->r[EE_D_RS].ul64 #define EE_RT32 ee->r[EE_D_RT].ul32 #define EE_RD32 ee->r[EE_D_RD].ul32 #define EE_RS32 ee->r[EE_D_RS].ul32 #define EE_FD ee->f[EE_D_FD].f #define EE_FT (fpu_cvtf(ee->f[EE_D_RT].f)) #define EE_FS (fpu_cvtf(ee->f[EE_D_FS].f)) #define EE_FT32 ee->f[EE_D_RT].u32 #define EE_FD32 ee->f[EE_D_FD].u32 #define EE_FS32 ee->f[EE_D_FS].u32 #define EE_HI0 ee->hi.u64[0] #define EE_LO0 ee->lo.u64[0] #define EE_HI1 ee->hi.u64[1] #define EE_LO1 ee->lo.u64[1] #define BRANCH(cond, offset) if (cond) { \ ee->next_pc = ee->next_pc + (offset); \ ee->next_pc = ee->next_pc - 4; \ ee->branch = 1; \ ee->branch_taken = 1; } #define BRANCH_LIKELY(cond, offset) \ BRANCH(cond, offset) else { ee->pc += 4; ee->next_pc += 4; } #define SE6432(v) ((int64_t)((int32_t)(v))) #define SE6416(v) ((int64_t)((int16_t)(v))) #define SE648(v) ((int64_t)((int8_t)(v))) #define SE3216(v) ((int32_t)((int16_t)(v))) static inline void ee_print_disassembly(struct ee_state* ee) { char buf[128]; struct ee_dis_state ds; ds.print_address = 1; ds.print_opcode = 1; ds.pc = ee->pc; puts(ee_disassemble(buf, ee->opcode, &ds)); } static inline int ee_get_segment(uint32_t virt) { switch (virt & 0xe0000000) { case 0x00000000: return EE_KUSEG; case 0x20000000: return EE_KUSEG; case 0x40000000: return EE_KUSEG; case 0x60000000: return EE_KUSEG; case 0x80000000: return EE_KSEG0; case 0xa0000000: return EE_KSEG1; case 0xc0000000: return EE_KSSEG; case 0xe0000000: return EE_KSEG3; } return EE_KUSEG; } const uint32_t ee_bus_region_mask_table[] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff, 0x1fffffff, 0xffffffff, 0xffffffff }; static inline float fpu_cvtf(float f) { uint32_t u32 = *(uint32_t*)&f; switch (u32 & 0x7f800000) { case 0x0: { u32 &= 0x80000000; return *(float*)&u32; } break; case 0x7f800000: { uint32_t result = (u32 & 0x80000000) | 0x7f7fffff; return *(float*)&result; } } return *(float*)&u32; } static inline float fpu_cvtsw(union ee_fpu_reg* reg) { switch (reg->u32 & 0x7F800000) { case 0x0: { reg->u32 &= 0x80000000; } break; case 0x7F800000: { reg->u32 = (reg->u32 & 0x80000000) | 0x7F7FFFFF; } break; } return reg->f; } static inline void fpu_cvtws(union ee_fpu_reg* d, union ee_fpu_reg* s) { if ((s->u32 & 0x7F800000) <= 0x4E800000) d->s32 = (int32_t)fpu_cvtf(s->f); else if ((s->u32 & 0x80000000) == 0) d->u32 = 0x7FFFFFFF; else d->u32 = 0x80000000; } static inline int fpu_check_overflow(struct ee_state* ee, union ee_fpu_reg* reg) { if ((reg->u32 & ~0x80000000) == 0x7f800000) { reg->u32 = (reg->u32 & 0x80000000) | 0x7f7fffff; ee->fcr |= FPU_FLG_O | FPU_FLG_SO; return 1; } ee->fcr &= ~FPU_FLG_O; return 0; } static inline int fpu_check_underflow(struct ee_state* ee, union ee_fpu_reg* reg) { if (((reg->u32 & 0x7F800000) == 0) && ((reg->u32 & 0x007FFFFF) != 0)) { reg->u32 &= 0x80000000; ee->fcr |= FPU_FLG_U | FPU_FLG_SU; return 1; } ee->fcr &= ~FPU_FLG_U; return 0; } static inline int fpu_check_overflow_no_flags(struct ee_state* ee, union ee_fpu_reg* reg) { if ((reg->u32 & ~0x80000000) == 0x7f800000) { reg->u32 = (reg->u32 & 0x80000000) | 0x7f7fffff; return 1; } return 0; } static inline int fpu_check_underflow_no_flags(struct ee_state* ee, union ee_fpu_reg* reg) { if (((reg->u32 & 0x7F800000) == 0) && ((reg->u32 & 0x007FFFFF) != 0)) { reg->u32 &= 0x80000000; return 1; } return 0; } static inline int fpu_max(int32_t a, int32_t b) { return (a < 0 && b < 0) ? min(a, b) : max(a, b); } static inline int fpu_min(int32_t a, int32_t b) { return (a < 0 && b < 0) ? max(a, b) : min(a, b); } static inline struct ee_vtlb_entry* ee_search_vtlb(struct ee_state* ee, uint32_t virt) { struct ee_vtlb_entry* entry = NULL; for (int i = 0; i < 48; i++) { ee->vtlb[i].mask; } } static inline int ee_translate_virt(struct ee_state* ee, uint32_t virt, uint32_t* phys) { int seg = ee_get_segment(virt); // Assume we're in kernel mode if (seg == EE_KSEG0 || seg == EE_KSEG1) { *phys = virt & 0x1fffffff; return 0; } if (virt >= 0x00000000 && virt <= 0x01FFFFFF) { *phys = virt & 0x1FFFFFF; return 0; } if (virt >= 0x10000000 && virt <= 0x1FFFFFFF) { *phys = virt & 0x1FFFFFFF; return 0; } if (virt >= 0x20000000 && virt <= 0x21FFFFFF) { *phys = virt & 0x1FFFFFF; return 0; } if (virt >= 0x30000000 && virt <= 0x31FFFFFF) { *phys = virt & 0x1FFFFFF; return 0; } // DECI2 area if (virt >= 0xFFFF8000) { *phys = (virt - 0xFFFF8000) + 0x78000; return 0; } *phys = virt & ee_bus_region_mask_table[virt >> 29]; // printf("ee: Unhandled virtual address %08x @ cyc=%ld\n", virt, ee->total_cycles); // *(int*)0 = 0; // exit(1); // To-do: MMU mapping *phys = virt & 0x1fffffff; return 0; } #define BUS_READ_FUNC(b) \ static inline uint64_t bus_read ## b(struct ee_state* ee, uint32_t addr) { \ if ((addr & 0xf0000000) == 0x70000000) \ return ps2_ram_read ## b(ee->scratchpad, addr & 0x3fff); \ uint32_t phys; \ if (ee_translate_virt(ee, addr, &phys)) { \ printf("ee: TLB mapping error\n"); \ exit(1); \ return 0; \ } \ return ee->bus.read ## b(ee->bus.udata, phys); \ } #define BUS_WRITE_FUNC(b) \ static inline void bus_write ## b(struct ee_state* ee, uint32_t addr, uint64_t data) { \ if ((addr & 0xf0000000) == 0x70000000) \ { ps2_ram_write ## b(ee->scratchpad, addr & 0x3fff, data); return; } \ uint32_t phys; \ if (ee_translate_virt(ee, addr, &phys)) { \ printf("ee: TLB mapping error\n"); \ exit(1); \ return; \ } \ ee->bus.write ## b(ee->bus.udata, phys, data); \ } BUS_READ_FUNC(8) BUS_READ_FUNC(16) BUS_READ_FUNC(32) BUS_READ_FUNC(64) static inline uint128_t bus_read128(struct ee_state* ee, uint32_t addr) { if ((addr & 0xf0000000) == 0x70000000) return ps2_ram_read128(ee->scratchpad, addr & 0x3ff0); uint32_t phys; if (ee_translate_virt(ee, addr, &phys)) { printf("ee: TLB mapping error\n"); exit(1); return (uint128_t){ .u64[0] = 0, .u64[1] = 0 }; } return ee->bus.read128(ee->bus.udata, phys); } BUS_WRITE_FUNC(8) BUS_WRITE_FUNC(16) BUS_WRITE_FUNC(32) BUS_WRITE_FUNC(64) static inline void bus_write128(struct ee_state* ee, uint32_t addr, uint128_t data) { if ((addr & 0xf0000000) == 0x70000000) { ps2_ram_write128(ee->scratchpad, addr & 0x3ff0, data); return; } uint32_t phys; if (ee_translate_virt(ee, addr, &phys)) { printf("ee: TLB mapping error\n"); exit(1); } ee->bus.write128(ee->bus.udata, phys, data); } #undef BUS_READ_FUNC #undef BUS_WRITE_FUNC static inline int ee_skip_fmv(struct ee_state* ee, uint32_t addr) { if (bus_read32(ee, addr + 4) != 0x03E00008) return 0; uint32_t code = bus_read32(ee, addr); uint32_t p1 = 0x8c800040; uint32_t p2 = 0x8c020000 | (code & 0x1f0000) << 5; if ((code & 0xffe0ffff) != p1) { return 0; } if (bus_read32(ee, addr + 8) != p2) { return 0; } printf("ee: Skipping FMV\n"); return 1; } static inline void ee_set_pc(struct ee_state* ee, uint32_t addr) { if (ee_skip_fmv(ee, addr)) return; ee->pc = addr; ee->next_pc = addr + 4; } static inline void ee_set_pc_delayed(struct ee_state* ee, uint32_t addr) { if (ee_skip_fmv(ee, addr)) return; ee->next_pc = addr; ee->branch = 1; } void ee_exception_level1(struct ee_state* ee, uint32_t cause) { uint32_t vec = EE_VEC_COMMON; ee->cause &= ~EE_CAUSE_EXC; ee->cause |= cause; if (!(ee->status & EE_SR_EXL)) { ee->epc = ee->pc - 4; if (ee->delay_slot) { ee->epc -= 4; ee->cause |= EE_CAUSE_BD; } else { ee->cause &= ~EE_CAUSE_BD; } } ee->status |= EE_SR_EXL; if (cause == CAUSE_EXC1_INT) vec = EE_VEC_IRQ; uint32_t addr = ((ee->status & EE_SR_BEV) ? 0xbfc00200 : 0x80000000) + vec; ee_set_pc(ee, addr); } static inline void ee_exception_level2(struct ee_state* ee, uint32_t cause) { uint32_t vec; ee->cause &= ~EE_CAUSE_EXC2; ee->cause |= cause; ee->errorepc = ee->pc - 4; if (ee->delay_slot) { ee->errorepc -= 4; ee->cause |= EE_CAUSE_BD2; } else { ee->cause &= ~EE_CAUSE_BD2; } ee->status |= EE_SR_ERL; if ((cause == CAUSE_EXC2_RES) | (cause == CAUSE_EXC2_NMI)) { ee_set_pc(ee, EE_VEC_RESET); return; } if (cause == CAUSE_EXC2_PERFC) { vec = EE_VEC_COUNTER; } else { vec = EE_VEC_DEBUG; } ee_set_pc(ee, ((ee->status & EE_SR_DEV) ? 0xbfc00200 : 0x80000000) + vec); } static inline void ee_check_irq(struct ee_state* ee) { int irq_enabled = (ee->status & EE_SR_IE) && (ee->status & EE_SR_EIE) && (!(ee->status & EE_SR_EXL)) && (!(ee->status & EE_SR_ERL)); int int0_pending = (ee->status & EE_SR_IM2) && (ee->cause & EE_CAUSE_IP2); int int1_pending = (ee->status & EE_SR_IM3) && (ee->cause & EE_CAUSE_IP3); if (irq_enabled && (int0_pending || int1_pending)) { ee->pc += 4; // printf("ee: Handling irq at pc=%08x (int0=%d (%d) int1=%d (%d)) sr=%08x delay_slot=%d\n", // ee->pc, // int0_pending, !!(ee->status & EE_SR_IM2), // int1_pending, !!(ee->status & EE_SR_IM3), // ee->status, // ee->delay_slot // ); ee_exception_level1(ee, CAUSE_EXC1_INT); } } void ee_set_int0(struct ee_state* ee, int v) { if (v) { ee->cause |= EE_CAUSE_IP2; } else { ee->cause &= ~EE_CAUSE_IP2; } } void ee_set_int1(struct ee_state* ee, int v) { if (v) { ee->cause |= EE_CAUSE_IP3; } else { ee->cause &= ~EE_CAUSE_IP3; } } void ee_set_cpcond0(struct ee_state* ee, int v) { ee->cpcond0 = v; } static inline void ee_i_abss(struct ee_state* ee) { ee->f[EE_D_FD].u32 = ee->f[EE_D_FS].u32 & 0x7fffffff; // EE_FD = fabsf(EE_FS); } static inline void ee_i_add(struct ee_state* ee) { int32_t s = EE_RS; int32_t t = EE_RT; int32_t r = s + t; uint32_t o = (s ^ r) & (t ^ r); if (o & 0x80000000) { ee_exception_level1(ee, CAUSE_EXC1_OV); } else { EE_RD = SE6432(r); } } static inline void ee_i_addas(struct ee_state* ee) { ee->a.f = EE_FS + EE_FT; if (fpu_check_overflow(ee, &ee->a)) return; fpu_check_underflow(ee, &ee->a); } static inline void ee_i_addi(struct ee_state* ee) { int32_t s = EE_RS; int32_t t = SE3216(EE_D_I16); int32_t r; if (__builtin_sadd_overflow(s, t, &r)) { ee_exception_level1(ee, CAUSE_EXC1_OV); } else { EE_RT = SE6432(r); } } static inline void ee_i_addiu(struct ee_state* ee) { EE_RT = SE6432(EE_RS32 + SE3216(EE_D_I16)); } static inline void ee_i_adds(struct ee_state* ee) { int d = EE_D_FD; ee->f[d].f = EE_FS + EE_FT; if (fpu_check_overflow(ee, &ee->f[d])) return; fpu_check_underflow(ee, &ee->f[d]); } static inline void ee_i_addu(struct ee_state* ee) { EE_RD = SE6432(EE_RS + EE_RT); } static inline void ee_i_and(struct ee_state* ee) { EE_RD = EE_RS & EE_RT; } static inline void ee_i_andi(struct ee_state* ee) { EE_RT = EE_RS & EE_D_I16; } static inline void ee_i_bc0f(struct ee_state* ee) { BRANCH(!ee->cpcond0, EE_D_SI16); } static inline void ee_i_bc0fl(struct ee_state* ee) { BRANCH_LIKELY(!ee->cpcond0, EE_D_SI16); } static inline void ee_i_bc0t(struct ee_state* ee) { BRANCH(ee->cpcond0, EE_D_SI16); } static inline void ee_i_bc0tl(struct ee_state* ee) { BRANCH_LIKELY(ee->cpcond0, EE_D_SI16); } static inline void ee_i_bc1f(struct ee_state* ee) { BRANCH((ee->fcr & (1 << 23)) == 0, EE_D_SI16); } static inline void ee_i_bc1fl(struct ee_state* ee) { BRANCH_LIKELY((ee->fcr & (1 << 23)) == 0, EE_D_SI16); } static inline void ee_i_bc1t(struct ee_state* ee) { BRANCH((ee->fcr & (1 << 23)) != 0, EE_D_SI16); } static inline void ee_i_bc1tl(struct ee_state* ee) { BRANCH_LIKELY((ee->fcr & (1 << 23)) != 0, EE_D_SI16); } static inline void ee_i_bc2f(struct ee_state* ee) { BRANCH(1, EE_D_SI16); } static inline void ee_i_bc2fl(struct ee_state* ee) { BRANCH_LIKELY(1, EE_D_SI16); } static inline void ee_i_bc2t(struct ee_state* ee) { BRANCH(0, EE_D_SI16); } static inline void ee_i_bc2tl(struct ee_state* ee) { BRANCH_LIKELY(0, EE_D_SI16); } static inline void ee_i_beq(struct ee_state* ee) { BRANCH(EE_RS == EE_RT, EE_D_SI16); } static inline void ee_i_beql(struct ee_state* ee) { BRANCH_LIKELY(EE_RS == EE_RT, EE_D_SI16); } static inline void ee_i_bgez(struct ee_state* ee) { BRANCH((int64_t)EE_RS >= (int64_t)0, EE_D_SI16); } static inline void ee_i_bgezal(struct ee_state* ee) { ee->r[31].ul64 = ee->next_pc; BRANCH((int64_t)EE_RS >= (int64_t)0, EE_D_SI16); } static inline void ee_i_bgezall(struct ee_state* ee) { ee->r[31].ul64 = ee->next_pc; BRANCH_LIKELY((int64_t)EE_RS >= (int64_t)0, EE_D_SI16); } static inline void ee_i_bgezl(struct ee_state* ee) { BRANCH_LIKELY((int64_t)EE_RS >= (int64_t)0, EE_D_SI16); } static inline void ee_i_bgtz(struct ee_state* ee) { BRANCH((int64_t)EE_RS > (int64_t)0, EE_D_SI16); } static inline void ee_i_bgtzl(struct ee_state* ee) { BRANCH_LIKELY((int64_t)EE_RS > (int64_t)0, EE_D_SI16); } static inline void ee_i_blez(struct ee_state* ee) { BRANCH((int64_t)EE_RS <= (int64_t)0, EE_D_SI16); } static inline void ee_i_blezl(struct ee_state* ee) { BRANCH_LIKELY((int64_t)EE_RS <= (int64_t)0, EE_D_SI16); } static inline void ee_i_bltz(struct ee_state* ee) { BRANCH((int64_t)EE_RS < (int64_t)0, EE_D_SI16); } static inline void ee_i_bltzal(struct ee_state* ee) { ee->r[31].ul64 = ee->next_pc; BRANCH((int64_t)EE_RS < (int64_t)0, EE_D_SI16); } static inline void ee_i_bltzall(struct ee_state* ee) { ee->r[31].ul64 = ee->next_pc; BRANCH_LIKELY((int64_t)EE_RS < (int64_t)0, EE_D_SI16); } static inline void ee_i_bltzl(struct ee_state* ee) { BRANCH_LIKELY((int64_t)EE_RS < (int64_t)0, EE_D_SI16); } static inline void ee_i_bne(struct ee_state* ee) { BRANCH(EE_RS != EE_RT, EE_D_SI16); } static inline void ee_i_bnel(struct ee_state* ee) { BRANCH_LIKELY(EE_RS != EE_RT, EE_D_SI16); } static inline void ee_i_break(struct ee_state* ee) { ee_exception_level1(ee, CAUSE_EXC1_BP); } static inline void ee_i_cache(struct ee_state* ee) { /* To-do: Cache emulation */ } static inline void ee_i_ceq(struct ee_state* ee) { if (EE_FS == EE_FT) { ee->fcr |= 1 << 23; } else { ee->fcr &= ~(1 << 23); } } static inline void ee_i_cf(struct ee_state* ee) { ee->fcr &= ~(1 << 23); } static inline void ee_i_cfc1(struct ee_state* ee) { EE_RT = (EE_D_FS >= 16) ? ee->fcr : 0x2e30; } static inline void ee_i_cfc2(struct ee_state* ee) { EE_RT = SE6432(ps2_vu_read_vi(ee->vu0, EE_D_RD)); } static inline void ee_i_cle(struct ee_state* ee) { if (EE_FS <= EE_FT) { ee->fcr |= 1 << 23; } else { ee->fcr &= ~(1 << 23); } } static inline void ee_i_clt(struct ee_state* ee) { if (EE_FS < EE_FT) { ee->fcr |= 1 << 23; } else { ee->fcr &= ~(1 << 23); } } static inline void ee_i_ctc1(struct ee_state* ee) { if (EE_D_FS < 16) return; ee->fcr = (ee->fcr & ~(0x83c078)) | (EE_RT & 0x83c078); } static inline void ee_i_ctc2(struct ee_state* ee) { // To-do: Handle FBRST, VPU_STAT, CMSAR1 int d = EE_D_RD; static const char* regs[] = { "Status flag", "MAC flag", "clipping flag", "reserved", "R", "I", "Q", "reserved", "reserved", "reserved", "TPC", "CMSAR0", "FBRST", "VPU-STAT", "reserved", "CMSAR1", }; ps2_vu_write_vi(ee->vu0, d, EE_RT32); } static inline void ee_i_cvts(struct ee_state* ee) { EE_FD = (float)ee->f[EE_D_FS].s32; EE_FD = fpu_cvtsw(&ee->f[EE_D_FD]); } static inline void ee_i_cvtw(struct ee_state* ee) { fpu_cvtws(&ee->f[EE_D_FD], &ee->f[EE_D_FS]); } static inline void ee_i_dadd(struct ee_state* ee) { int64_t r; if (SADDOVF64((int64_t)EE_RS, (int64_t)EE_RT, &r)) { ee_exception_level1(ee, CAUSE_EXC1_OV); } else { EE_RD = r; } } static inline void ee_i_daddi(struct ee_state* ee) { int64_t r; if (SADDOVF64((int64_t)EE_RS, SE6416(EE_D_I16), &r)) { ee_exception_level1(ee, CAUSE_EXC1_OV); } else { EE_RT = r; } } static inline void ee_i_daddiu(struct ee_state* ee) { EE_RT = EE_RS + SE6416(EE_D_I16); } static inline void ee_i_daddu(struct ee_state* ee) { EE_RD = EE_RS + EE_RT; } static inline void ee_i_di(struct ee_state* ee) { int edi = ee->status & EE_SR_EDI; int exl = ee->status & EE_SR_EXL; int erl = ee->status & EE_SR_ERL; int ksu = ee->status & EE_SR_KSU; if (edi || exl || erl || !ksu) ee->status &= ~EE_SR_EIE; } static inline void ee_i_div(struct ee_state* ee) { int t = EE_D_RT; int s = EE_D_RS; if (ee->r[s].ul32 == 0x80000000 && ee->r[t].ul32 == 0xffffffff) { EE_LO0 = (int32_t)0x80000000; EE_HI0 = 0; } else if (ee->r[t].ul32 != 0) { EE_HI0 = SE6432(ee->r[s].sl32 % ee->r[t].sl32); EE_LO0 = SE6432(ee->r[s].sl32 / ee->r[t].sl32); } else { EE_HI0 = SE6432(ee->r[s].ul32); EE_LO0 = ((int32_t)ee->r[s].ul32 < 0) ? 1 : -1; } } static inline void ee_i_div1(struct ee_state* ee) { int t = EE_D_RT; int s = EE_D_RS; if (ee->r[s].ul32 == 0x80000000 && ee->r[t].ul32 == 0xffffffff) { EE_LO1 = (int32_t)0x80000000; EE_HI1 = 0; } else if (ee->r[t].ul32 != 0) { EE_HI1 = SE6432(ee->r[s].sl32 % ee->r[t].sl32); EE_LO1 = SE6432(ee->r[s].sl32 / ee->r[t].sl32); } else { EE_HI1 = SE6432(ee->r[s].ul32); EE_LO1 = ((int32_t)ee->r[s].ul32 < 0) ? 1 : -1; } } static inline void ee_i_divs(struct ee_state* ee) { int t = EE_D_RT; int d = EE_D_FD; int s = EE_D_FS; ee->fcr &= ~(FPU_FLG_I | FPU_FLG_D); // If both the dividend and divisor are zero, set I/SI, // else set D/SD if ((ee->f[t].u32 & 0x7F800000) == 0) { if ((ee->f[s].u32 & 0x7F800000) == 0) { ee->fcr |= FPU_FLG_I | FPU_FLG_SI; } else { ee->fcr |= FPU_FLG_D | FPU_FLG_SD; } ee->f[d].u32 = ((ee->f[t].u32 ^ ee->f[s].u32) & 0x80000000) | 0x7f7fffff; return; } ee->f[d].f = EE_FS / EE_FT; if (fpu_check_overflow_no_flags(ee, &ee->f[d])) return; fpu_check_underflow_no_flags(ee, &ee->f[d]); } static inline void ee_i_divu(struct ee_state* ee) { int t = EE_D_RT; int s = EE_D_RS; if (!ee->r[t].ul32) { EE_LO0 = -1; EE_HI0 = SE6432(ee->r[s].ul32); return; } EE_HI0 = SE6432(ee->r[s].ul32 % ee->r[t].ul32); EE_LO0 = SE6432(ee->r[s].ul32 / ee->r[t].ul32); } static inline void ee_i_divu1(struct ee_state* ee) { int t = EE_D_RT; int s = EE_D_RS; if (!ee->r[t].ul32) { EE_LO1 = -1; EE_HI1 = SE6432(ee->r[s].ul32); return; } EE_HI1 = SE6432(ee->r[s].ul32 % ee->r[t].ul32); EE_LO1 = SE6432(ee->r[s].ul32 / ee->r[t].ul32); } static inline void ee_i_dsll(struct ee_state* ee) { EE_RD = EE_RT << EE_D_SA; } static inline void ee_i_dsll32(struct ee_state* ee) { EE_RD = EE_RT << (EE_D_SA + 32); } static inline void ee_i_dsllv(struct ee_state* ee) { EE_RD = EE_RT << (EE_RS & 0x3f); } static inline void ee_i_dsra(struct ee_state* ee) { EE_RD = ((int64_t)EE_RT) >> EE_D_SA; } static inline void ee_i_dsra32(struct ee_state* ee) { EE_RD = ((int64_t)EE_RT) >> (EE_D_SA + 32); } static inline void ee_i_dsrav(struct ee_state* ee) { EE_RD = ((int64_t)EE_RT) >> (EE_RS & 0x3f); } static inline void ee_i_dsrl(struct ee_state* ee) { EE_RD = EE_RT >> EE_D_SA; } static inline void ee_i_dsrl32(struct ee_state* ee) { EE_RD = EE_RT >> (EE_D_SA + 32); } static inline void ee_i_dsrlv(struct ee_state* ee) { EE_RD = EE_RT >> (EE_RS & 0x3f); } static inline void ee_i_dsub(struct ee_state* ee) { int64_t r; if (SSUBOVF64((int64_t)EE_RS, (int64_t)EE_RT, &r)) { ee_exception_level1(ee, CAUSE_EXC1_OV); } else { EE_RD = r; } } static inline void ee_i_dsubu(struct ee_state* ee) { EE_RD = EE_RS - EE_RT; } static inline void ee_i_ei(struct ee_state* ee) { int edi = ee->status & EE_SR_EDI; int exl = ee->status & EE_SR_EXL; int erl = ee->status & EE_SR_ERL; int ksu = ee->status & EE_SR_KSU; if (edi || exl || erl || !ksu) ee->status |= EE_SR_EIE; } static inline void ee_i_eret(struct ee_state* ee) { if (ee->status & EE_SR_ERL) { ee_set_pc(ee, ee->errorepc); ee->status &= ~EE_SR_ERL; } else { ee_set_pc(ee, ee->epc); ee->status &= ~EE_SR_EXL; } } static inline void ee_i_j(struct ee_state* ee) { ee_set_pc_delayed(ee, (ee->next_pc & 0xf0000000) | (EE_D_I26 << 2)); } static inline void ee_i_jal(struct ee_state* ee) { ee->r[31].ul64 = ee->next_pc; ee_set_pc_delayed(ee, (ee->next_pc & 0xf0000000) | (EE_D_I26 << 2)); } static inline void ee_i_jalr(struct ee_state* ee) { uint32_t next_pc = ee->next_pc; ee_set_pc_delayed(ee, EE_RS32); EE_RD = next_pc; } static inline void ee_i_jr(struct ee_state* ee) { ee_set_pc_delayed(ee, EE_RS32); } static inline void ee_i_lb(struct ee_state* ee) { EE_RT = SE648(bus_read8(ee, EE_RS32 + SE3216(EE_D_I16))); } static inline void ee_i_lbu(struct ee_state* ee) { EE_RT = bus_read8(ee, EE_RS32 + SE3216(EE_D_I16)); } static inline void ee_i_ld(struct ee_state* ee) { EE_RT = bus_read64(ee, EE_RS32 + SE3216(EE_D_I16)); } static inline void ee_i_ldl(struct ee_state* ee) { static const uint8_t ldl_shift[8] = { 56, 48, 40, 32, 24, 16, 8, 0 }; static const uint64_t ldl_mask[8] = { 0x00ffffffffffffffULL, 0x0000ffffffffffffULL, 0x000000ffffffffffULL, 0x00000000ffffffffULL, 0x0000000000ffffffULL, 0x000000000000ffffULL, 0x00000000000000ffULL, 0x0000000000000000ULL }; uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t shift = addr & 7; uint64_t data = bus_read64(ee, addr & ~7); EE_RT = (EE_RT & ldl_mask[shift]) | (data << ldl_shift[shift]); } static inline void ee_i_ldr(struct ee_state* ee) { static const uint8_t ldr_shift[8] = { 0, 8, 16, 24, 32, 40, 48, 56 }; static const uint64_t ldr_mask[8] = { 0x0000000000000000ULL, 0xff00000000000000ULL, 0xffff000000000000ULL, 0xffffff0000000000ULL, 0xffffffff00000000ULL, 0xffffffffff000000ULL, 0xffffffffffff0000ULL, 0xffffffffffffff00ULL }; uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t shift = addr & 7; uint64_t data = bus_read64(ee, addr & ~7); EE_RT = (EE_RT & ldr_mask[shift]) | (data >> ldr_shift[shift]); } static inline void ee_i_lh(struct ee_state* ee) { EE_RT = SE6416(bus_read16(ee, EE_RS32 + SE3216(EE_D_I16))); } static inline void ee_i_lhu(struct ee_state* ee) { EE_RT = bus_read16(ee, EE_RS32 + SE3216(EE_D_I16)); } static inline void ee_i_lq(struct ee_state* ee) { ee->r[EE_D_RT] = bus_read128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf); } static inline void ee_i_lqc2(struct ee_state* ee) { int d = EE_D_RT; if (!d) return; ee->vu0->vf[EE_D_RT].u128 = bus_read128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf); } static inline void ee_i_lui(struct ee_state* ee) { EE_RT = SE6432(EE_D_I16 << 16); } static inline void ee_i_lw(struct ee_state* ee) { EE_RT = SE6432(bus_read32(ee, EE_RS32 + SE3216(EE_D_I16))); } static inline void ee_i_lwc1(struct ee_state* ee) { EE_FT32 = bus_read32(ee, EE_RS32 + SE3216(EE_D_I16)); } static const uint32_t LWL_MASK[4] = { 0x00ffffff, 0x0000ffff, 0x000000ff, 0x00000000 }; static const uint32_t LWR_MASK[4] = { 0x00000000, 0xff000000, 0xffff0000, 0xffffff00 }; static const int LWL_SHIFT[4] = { 24, 16, 8, 0 }; static const int LWR_SHIFT[4] = { 0, 8, 16, 24 }; static inline void ee_i_lwl(struct ee_state* ee) { uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t shift = addr & 3; uint32_t mem = bus_read32(ee, addr & ~3); // ensure the compiler does correct sign extension into 64 bits by using s32 EE_RT = (int32_t)((EE_RT32 & LWL_MASK[shift]) | (mem << LWL_SHIFT[shift])); } static inline void ee_i_lwr(struct ee_state* ee) { uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t shift = addr & 3; uint32_t data = bus_read32(ee, addr & ~3); // Use unsigned math here, and conditionally sign extend below, when needed. data = (EE_RT32 & LWR_MASK[shift]) | (data >> LWR_SHIFT[shift]); if (!shift) { // This special case requires sign extension into the full 64 bit dest. EE_RT = (int32_t)data; } else { // This case sets the lower 32 bits of the target register. Upper // 32 bits are always preserved. EE_RT32 = data; } // printf("lwr mem=%08x reg=%016lx addr=%08x shift=%d\n", data, ee->r[EE_D_RT].u64[0], addr, shift); } static inline void ee_i_lwu(struct ee_state* ee) { EE_RT = bus_read32(ee, EE_RS32 + SE3216(EE_D_I16)); } static inline void ee_i_madd(struct ee_state* ee) { uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32); uint64_t d = (uint64_t)ee->lo.u32[0] | (ee->hi.u64[0] << 32); d += r; EE_LO0 = SE6432(d & 0xffffffff); EE_HI0 = SE6432(d >> 32); EE_RD = EE_LO0; } static inline void ee_i_madd1(struct ee_state* ee) { uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32); uint64_t d = (EE_LO1 & 0xffffffff) | (EE_HI1 << 32); d += r; EE_LO1 = SE6432(d & 0xffffffff); EE_HI1 = SE6432(d >> 32); EE_RD = EE_LO1; } static inline void ee_i_maddas(struct ee_state* ee) { ee->a.f += EE_FS * EE_FT; if (fpu_check_overflow(ee, &ee->a)) return; fpu_check_underflow(ee, &ee->a); } static inline void ee_i_madds(struct ee_state* ee) { int t = EE_D_RT; int d = EE_D_FD; int s = EE_D_FS; float temp = fpu_cvtf(ee->f[s].f) * fpu_cvtf(ee->f[t].f); ee->f[d].f = fpu_cvtf(ee->a.f) + fpu_cvtf(temp); if (fpu_check_overflow(ee, &ee->f[d])) return; fpu_check_underflow(ee, &ee->f[d]); } static inline void ee_i_maddu(struct ee_state* ee) { uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32; uint64_t d = (uint64_t)ee->lo.u32[0] | (ee->hi.u64[0] << 32); d += r; EE_LO0 = SE6432(d & 0xffffffff); EE_HI0 = SE6432(d >> 32); EE_RD = EE_LO0; } static inline void ee_i_maddu1(struct ee_state* ee) { uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32; uint64_t d = (uint64_t)ee->lo.u32[2] | (ee->hi.u64[1] << 32); d += r; EE_LO1 = SE6432(d & 0xffffffff); EE_HI1 = SE6432(d >> 32); EE_RD = EE_LO1; } static inline void ee_i_maxs(struct ee_state* ee) { ee->f[EE_D_FD].u32 = fpu_max(ee->f[EE_D_FS].u32, ee->f[EE_D_RT].u32); ee->fcr &= ~(FPU_FLG_O | FPU_FLG_U); } static inline void ee_i_mfc0(struct ee_state* ee) { EE_RT = SE6432(ee->cop0_r[EE_D_RD]); } static inline void ee_i_mfc1(struct ee_state* ee) { EE_RT = SE6432(EE_FS32); } static inline void ee_i_mfhi(struct ee_state* ee) { EE_RD = EE_HI0; } static inline void ee_i_mfhi1(struct ee_state* ee) { EE_RD = EE_HI1; } static inline void ee_i_mflo(struct ee_state* ee) { EE_RD = EE_LO0; } static inline void ee_i_mflo1(struct ee_state* ee) { EE_RD = EE_LO1; } static inline void ee_i_mfsa(struct ee_state* ee) { EE_RD = ee->sa & 0xf; } static inline void ee_i_mins(struct ee_state* ee) { ee->f[EE_D_FD].u32 = fpu_min(ee->f[EE_D_FS].u32, ee->f[EE_D_RT].u32); ee->fcr &= ~(FPU_FLG_O | FPU_FLG_U); } static inline void ee_i_movn(struct ee_state* ee) { if (EE_RT) EE_RD = EE_RS; } static inline void ee_i_movs(struct ee_state* ee) { EE_FD32 = EE_FS32; } static inline void ee_i_movz(struct ee_state* ee) { if (!EE_RT) EE_RD = EE_RS; } static inline void ee_i_msubas(struct ee_state* ee) { ee->a.f -= EE_FS * EE_FT; if (fpu_check_overflow(ee, &ee->a)) return; fpu_check_underflow(ee, &ee->a); } static inline void ee_i_msubs(struct ee_state* ee) { int t = EE_D_RT; int d = EE_D_FD; int s = EE_D_FS; float temp = fpu_cvtf(ee->f[s].f) * fpu_cvtf(ee->f[t].f); ee->f[d].f = fpu_cvtf(ee->a.f) - fpu_cvtf(temp); if (fpu_check_overflow(ee, &ee->f[d])) return; fpu_check_underflow(ee, &ee->f[d]); } static inline void ee_i_mtc0(struct ee_state* ee) { ee->cop0_r[EE_D_RD] = EE_RT32; } static inline void ee_i_mtc1(struct ee_state* ee) { EE_FS32 = EE_RT32; } static inline void ee_i_mthi(struct ee_state* ee) { EE_HI0 = EE_RS; } static inline void ee_i_mthi1(struct ee_state* ee) { EE_HI1 = EE_RS; } static inline void ee_i_mtlo(struct ee_state* ee) { EE_LO0 = EE_RS; } static inline void ee_i_mtlo1(struct ee_state* ee) { EE_LO1 = EE_RS; } static inline void ee_i_mtsa(struct ee_state* ee) { ee->sa = ((uint32_t)EE_RS) & 0xf; } static inline void ee_i_mtsab(struct ee_state* ee) { ee->sa = (EE_RS ^ EE_D_I16) & 15; } static inline void ee_i_mtsah(struct ee_state* ee) { ee->sa = ((EE_RS ^ EE_D_I16) & 7) << 1; } static inline void ee_i_mulas(struct ee_state* ee) { ee->a.f = EE_FS * EE_FT; if (fpu_check_overflow(ee, &ee->a)) return; fpu_check_underflow(ee, &ee->a); } static inline void ee_i_muls(struct ee_state* ee) { int d = EE_D_FD; ee->f[d].f = EE_FS * EE_FT; if (fpu_check_overflow(ee, &ee->f[d])) return; fpu_check_underflow(ee, &ee->f[d]); } static inline void ee_i_mult(struct ee_state* ee) { uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32); EE_LO0 = SE6432(r & 0xffffffff); EE_HI0 = SE6432(r >> 32); EE_RD = EE_LO0; } static inline void ee_i_mult1(struct ee_state* ee) { uint64_t r = SE6432(EE_RS32) * SE6432(EE_RT32); EE_LO1 = SE6432(r & 0xffffffff); EE_HI1 = SE6432(r >> 32); EE_RD = EE_LO1; } static inline void ee_i_multu(struct ee_state* ee) { uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32; EE_LO0 = SE6432(r & 0xffffffff); EE_HI0 = SE6432(r >> 32); EE_RD = EE_LO0; } static inline void ee_i_multu1(struct ee_state* ee) { uint64_t r = (uint64_t)EE_RS32 * (uint64_t)EE_RT32; EE_LO1 = SE6432(r & 0xffffffff); EE_HI1 = SE6432(r >> 32); EE_RD = EE_LO1; } static inline void ee_i_negs(struct ee_state* ee) { ee->f[EE_D_FD].u32 = ee->f[EE_D_FS].u32 ^ 0x80000000; ee->fcr &= ~(FPU_FLG_O | FPU_FLG_U); } static inline void ee_i_nor(struct ee_state* ee) { EE_RD = ~(EE_RS | EE_RT); } static inline void ee_i_or(struct ee_state* ee) { EE_RD = EE_RS | EE_RT; } static inline void ee_i_ori(struct ee_state* ee) { EE_RT = EE_RS | EE_D_I16; } static inline void ee_i_pabsh(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 8; i++) { d->u16[i] = (t->u16[i] == 0x8000) ? 0x7fff : fast_abs16(t->u16[i]); } #else __m128i b = _mm_set1_epi16((unsigned short)0x8000); __m128i a = _mm_load_si128((void*)t); __m128i f = _mm_cmpeq_epi16(a, b); __m128i r = _mm_add_epi16(_mm_abs_epi16(a), f); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_pabsw(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 4; i++) { d->u32[i] = (t->u32[i] == 0x80000000) ? 0x7fffffff : fast_abs32(t->u32[i]); } #else __m128i b = _mm_set1_epi32(0x80000000); __m128i a = _mm_load_si128((void*)t); __m128i f = _mm_cmpeq_epi32(a, b); __m128i r = _mm_add_epi32(_mm_abs_epi32(a), f); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_paddb(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 16; i++) { d->u8[i] = s->u8[i] + t->u8[i]; } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_add_epi8(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_paddh(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 8; i++) { d->u16[i] = s->u16[i] + t->u16[i]; } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_add_epi16(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_paddsb(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 16; i++) { int32_t r = ((int32_t)(int8_t)s->u8[i]) + ((int32_t)(int8_t)t->u8[i]); d->u8[i] = (r > 0x7f) ? 0x7f : ((r < -128) ? 0x80 : r); } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_adds_epi8(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_paddsh(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 8; i++) { int32_t r = (SE3216(s->u16[i])) + (SE3216(t->u16[i])); d->u16[i] = (r > 0x7fff) ? 0x7fff : ((r < -0x8000) ? 0x8000 : r); } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_adds_epi16(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_paddsw(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 4; i++) { int64_t r = (SE6432(s->u32[i])) + (SE6432(t->u32[i])); d->u32[i] = (r >= 0x7fffffff) ? 0x7fffffff : ((r < (int32_t)0x80000000) ? 0x80000000 : r); } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_adds_epi32(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_paddub(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 16; i++) { uint32_t r = (uint32_t)s->u8[i] + (uint32_t)t->u8[i]; d->u8[i] = (r > 0xff) ? 0xff : r; } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_adds_epu8(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_padduh(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 8; i++) { uint32_t r = (uint32_t)s->u16[i] + (uint32_t)t->u16[i]; d->u16[i] = (r > 0xffff) ? 0xffff : r; } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_adds_epu16(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_padduw(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 4; i++) { uint64_t r = (uint64_t)s->u32[i] + (uint64_t)t->u32[i]; d->u32[i] = (r > 0xffffffff) ? 0xffffffff : r; } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_adds_epu32(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_paddw(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 4; i++) { d->u32[i] = s->u32[i] + t->u32[i]; } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_add_epi32(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_padsbh(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS d->u16[0] = s->u16[0] - t->u16[0]; d->u16[1] = s->u16[1] - t->u16[1]; d->u16[2] = s->u16[2] - t->u16[2]; d->u16[3] = s->u16[3] - t->u16[3]; d->u16[4] = s->u16[4] + t->u16[4]; d->u16[5] = s->u16[5] + t->u16[5]; d->u16[6] = s->u16[6] + t->u16[6]; d->u16[7] = s->u16[7] + t->u16[7]; #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i x = _mm_sub_epi16(a, b); __m128i y = _mm_add_epi16(a, b); __m128i r = _mm_blend_epi16(x, y, 15); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_pand(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS d->u64[0] = s->u64[0] & t->u64[0]; d->u64[1] = s->u64[1] & t->u64[1]; #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_and_si128(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_pceqb(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 16; i++) { d->u8[i] = (s->u8[i] == t->u8[i]) ? 0xff : 0; } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_cmpeq_epi8(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_pceqh(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 8; i++) { d->u16[i] = (s->u16[i] == t->u16[i]) ? 0xffff : 0; } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_cmpeq_epi16(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_pceqw(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 4; i++) { d->u32[i] = (s->u32[i] == t->u32[i]) ? 0xffffffff : 0; } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_cmpeq_epi32(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_pcgtb(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 16; i++) { d->u8[i] = ((int8_t)s->u8[i] > (int8_t)t->u8[i]) ? 0xff : 0; } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_cmpgt_epi8(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_pcgth(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 8; i++) { d->u16[i] = ((int16_t)s->u16[i] > (int16_t)t->u16[i]) ? 0xffff : 0; } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_cmpgt_epi16(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_pcgtw(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* s = &ee->r[EE_D_RS]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS for (int i = 0; i < 4; i++) { d->u32[i] = ((int32_t)s->u32[i] > (int32_t)t->u32[i]) ? 0xffffffff : 0; } #else __m128i a = _mm_load_si128((void*)s); __m128i b = _mm_load_si128((void*)t); __m128i r = _mm_cmpgt_epi32(a, b); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_pcpyh(struct ee_state* ee) { uint128_t* d = &ee->r[EE_D_RD]; uint128_t* t = &ee->r[EE_D_RT]; #ifndef _EE_USE_INTRINSICS uint128_t tc = *t; d->u16[0] = tc.u16[0]; d->u16[1] = tc.u16[0]; d->u16[2] = tc.u16[0]; d->u16[3] = tc.u16[0]; d->u16[4] = tc.u16[4]; d->u16[5] = tc.u16[4]; d->u16[6] = tc.u16[4]; d->u16[7] = tc.u16[4]; #else static const uint64_t mask[] = { 0x0100010001000100, 0x0908090809080908 }; __m128i m = _mm_load_si128((void*)mask); __m128i a = _mm_load_si128((void*)t); __m128i r = _mm_shuffle_epi8(a, m); _mm_store_si128((void*)d, r); #endif } static inline void ee_i_pcpyld(struct ee_state* ee) { #ifndef _EE_USE_INTRINSICS uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u64[0] = rt.u64[0]; ee->r[d].u64[1] = rs.u64[0]; #else __m128i a = _mm_load_si128((void*)&ee->r[EE_D_RT]); __m128i b = _mm_load_si128((void*)&ee->r[EE_D_RS]); __m128i r = _mm_unpacklo_epi64(a, b); _mm_store_si128((void*)&ee->r[EE_D_RD], r); #endif } static inline void ee_i_pcpyud(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u64[0] = rs.u64[1]; ee->r[d].u64[1] = rt.u64[1]; } static inline void ee_i_pdivbw(struct ee_state* ee) { printf("ee: pdivbw unimplemented\n"); exit(1); } static inline void ee_i_pdivuw(struct ee_state* ee) { printf("ee: pdivuw unimplemented\n"); exit(1); } static inline void ee_i_pdivw(struct ee_state* ee) { printf("ee: pdivw unimplemented\n"); exit(1); } static inline void ee_i_pexch(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[0]; ee->r[d].u16[1] = rt.u16[2]; ee->r[d].u16[2] = rt.u16[1]; ee->r[d].u16[3] = rt.u16[3]; ee->r[d].u16[4] = rt.u16[4]; ee->r[d].u16[5] = rt.u16[6]; ee->r[d].u16[6] = rt.u16[5]; ee->r[d].u16[7] = rt.u16[7]; } static inline void ee_i_pexcw(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u32[0] = rt.u32[0]; ee->r[d].u32[1] = rt.u32[2]; ee->r[d].u32[2] = rt.u32[1]; ee->r[d].u32[3] = rt.u32[3]; } static inline void ee_i_pexeh(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[2]; ee->r[d].u16[1] = rt.u16[1]; ee->r[d].u16[2] = rt.u16[0]; ee->r[d].u16[3] = rt.u16[3]; ee->r[d].u16[4] = rt.u16[6]; ee->r[d].u16[5] = rt.u16[5]; ee->r[d].u16[6] = rt.u16[4]; ee->r[d].u16[7] = rt.u16[7]; } static inline void ee_i_pexew(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u32[0] = rt.u32[2]; ee->r[d].u32[1] = rt.u32[1]; ee->r[d].u32[2] = rt.u32[0]; ee->r[d].u32[3] = rt.u32[3]; } static inline void ee_i_pext5(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u32[0] = unpack_5551_8888(rt.u32[0]); ee->r[d].u32[1] = unpack_5551_8888(rt.u32[1]); ee->r[d].u32[2] = unpack_5551_8888(rt.u32[2]); ee->r[d].u32[3] = unpack_5551_8888(rt.u32[3]); } static inline void ee_i_pextlb(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u8[ 0] = rt.u8[0]; ee->r[d].u8[ 1] = rs.u8[0]; ee->r[d].u8[ 2] = rt.u8[1]; ee->r[d].u8[ 3] = rs.u8[1]; ee->r[d].u8[ 4] = rt.u8[2]; ee->r[d].u8[ 5] = rs.u8[2]; ee->r[d].u8[ 6] = rt.u8[3]; ee->r[d].u8[ 7] = rs.u8[3]; ee->r[d].u8[ 8] = rt.u8[4]; ee->r[d].u8[ 9] = rs.u8[4]; ee->r[d].u8[10] = rt.u8[5]; ee->r[d].u8[11] = rs.u8[5]; ee->r[d].u8[12] = rt.u8[6]; ee->r[d].u8[13] = rs.u8[6]; ee->r[d].u8[14] = rt.u8[7]; ee->r[d].u8[15] = rs.u8[7]; } static inline void ee_i_pextlh(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[0]; ee->r[d].u16[1] = rs.u16[0]; ee->r[d].u16[2] = rt.u16[1]; ee->r[d].u16[3] = rs.u16[1]; ee->r[d].u16[4] = rt.u16[2]; ee->r[d].u16[5] = rs.u16[2]; ee->r[d].u16[6] = rt.u16[3]; ee->r[d].u16[7] = rs.u16[3]; } static inline void ee_i_pextlw(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u32[0] = rt.u32[0]; ee->r[d].u32[1] = rs.u32[0]; ee->r[d].u32[2] = rt.u32[1]; ee->r[d].u32[3] = rs.u32[1]; } static inline void ee_i_pextub(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u8[ 0] = rt.u8[ 8]; ee->r[d].u8[ 1] = rs.u8[ 8]; ee->r[d].u8[ 2] = rt.u8[ 9]; ee->r[d].u8[ 3] = rs.u8[ 9]; ee->r[d].u8[ 4] = rt.u8[10]; ee->r[d].u8[ 5] = rs.u8[10]; ee->r[d].u8[ 6] = rt.u8[11]; ee->r[d].u8[ 7] = rs.u8[11]; ee->r[d].u8[ 8] = rt.u8[12]; ee->r[d].u8[ 9] = rs.u8[12]; ee->r[d].u8[10] = rt.u8[13]; ee->r[d].u8[11] = rs.u8[13]; ee->r[d].u8[12] = rt.u8[14]; ee->r[d].u8[13] = rs.u8[14]; ee->r[d].u8[14] = rt.u8[15]; ee->r[d].u8[15] = rs.u8[15]; } static inline void ee_i_pextuh(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[4]; ee->r[d].u16[1] = rs.u16[4]; ee->r[d].u16[2] = rt.u16[5]; ee->r[d].u16[3] = rs.u16[5]; ee->r[d].u16[4] = rt.u16[6]; ee->r[d].u16[5] = rs.u16[6]; ee->r[d].u16[6] = rt.u16[7]; ee->r[d].u16[7] = rs.u16[7]; } static inline void ee_i_pextuw(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u32[0] = rt.u32[2]; ee->r[d].u32[1] = rs.u32[2]; ee->r[d].u32[2] = rt.u32[3]; ee->r[d].u32[3] = rs.u32[3]; } static inline void ee_i_phmadh(struct ee_state* ee) { printf("ee: phmadh unimplemented\n"); exit(1); } static inline void ee_i_phmsbh(struct ee_state* ee) { printf("ee: phmsbh unimplemented\n"); exit(1); } static inline void ee_i_pinteh(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[0]; ee->r[d].u16[1] = rs.u16[0]; ee->r[d].u16[2] = rt.u16[2]; ee->r[d].u16[3] = rs.u16[2]; ee->r[d].u16[4] = rt.u16[4]; ee->r[d].u16[5] = rs.u16[4]; ee->r[d].u16[6] = rt.u16[6]; ee->r[d].u16[7] = rs.u16[6]; } static inline void ee_i_pinth(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; uint128_t rs = ee->r[EE_D_RS]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[0]; ee->r[d].u16[1] = rs.u16[4]; ee->r[d].u16[2] = rt.u16[1]; ee->r[d].u16[3] = rs.u16[5]; ee->r[d].u16[4] = rt.u16[2]; ee->r[d].u16[5] = rs.u16[6]; ee->r[d].u16[6] = rt.u16[3]; ee->r[d].u16[7] = rs.u16[7]; } static inline void ee_i_plzcw(struct ee_state* ee) { for (int i = 0; i < 2; i++) { uint32_t word = ee->r[EE_D_RS].u32[i]; int msb = word & 0x80000000; word = (msb ? ~word : word); ee->r[EE_D_RD].u32[i] = (word ? (__builtin_clz(word) - 1) : 0x1f); } } static inline void ee_i_pmaddh(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; uint32_t r0 = SE3216(ee->r[s].u16[0]) * SE3216(ee->r[t].u16[0]); uint32_t r1 = SE3216(ee->r[s].u16[1]) * SE3216(ee->r[t].u16[1]); uint32_t r2 = SE3216(ee->r[s].u16[2]) * SE3216(ee->r[t].u16[2]); uint32_t r3 = SE3216(ee->r[s].u16[3]) * SE3216(ee->r[t].u16[3]); uint32_t r4 = SE3216(ee->r[s].u16[4]) * SE3216(ee->r[t].u16[4]); uint32_t r5 = SE3216(ee->r[s].u16[5]) * SE3216(ee->r[t].u16[5]); uint32_t r6 = SE3216(ee->r[s].u16[6]) * SE3216(ee->r[t].u16[6]); uint32_t r7 = SE3216(ee->r[s].u16[7]) * SE3216(ee->r[t].u16[7]); uint32_t c0 = ee->lo.u32[0]; uint32_t c1 = ee->lo.u32[1]; uint32_t c2 = ee->hi.u32[0]; uint32_t c3 = ee->hi.u32[1]; uint32_t c4 = ee->lo.u32[2]; uint32_t c5 = ee->lo.u32[3]; uint32_t c6 = ee->hi.u32[2]; uint32_t c7 = ee->hi.u32[3]; ee->r[d].u32[0] = r0 + c0; ee->lo.u32[1] = r1 + c1; ee->r[d].u32[1] = r2 + c2; ee->hi.u32[1] = r3 + c3; ee->r[d].u32[2] = r4 + c4; ee->lo.u32[3] = r5 + c5; ee->r[d].u32[3] = r6 + c6; ee->hi.u32[3] = r7 + c7; ee->lo.u32[0] = ee->r[d].u32[0]; ee->hi.u32[0] = ee->r[d].u32[1]; ee->lo.u32[2] = ee->r[d].u32[2]; ee->hi.u32[2] = ee->r[d].u32[3]; } static inline void ee_i_pmadduw(struct ee_state* ee) { printf("ee: pmadduw unimplemented\n"); exit(1); } static inline void ee_i_pmaddw(struct ee_state* ee) { printf("ee: pmaddw unimplemented\n"); exit(1); } static inline void ee_i_pmaxh(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; 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]; 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]; 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]; 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]; 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]; 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]; 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]; 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]; } static inline void ee_i_pmaxw(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; 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]; 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]; 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]; 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]; } static inline void ee_i_pmfhi(struct ee_state* ee) { ee->r[EE_D_RD] = ee->hi; } static inline void ee_i_pmfhllw(struct ee_state* ee) { int d = EE_D_RD; ee->r[d].u32[0] = ee->lo.u32[0]; ee->r[d].u32[1] = ee->hi.u32[0]; ee->r[d].u32[2] = ee->lo.u32[2]; ee->r[d].u32[3] = ee->hi.u32[2]; } static inline void ee_i_pmfhluw(struct ee_state* ee) { int d = EE_D_RD; ee->r[d].u32[0] = ee->lo.u32[1]; ee->r[d].u32[1] = ee->hi.u32[1]; ee->r[d].u32[2] = ee->lo.u32[3]; ee->r[d].u32[3] = ee->hi.u32[3]; } static inline void ee_i_pmfhlslw(struct ee_state* ee) { printf("ee: pmfhlslw unimplemented\n"); exit(1); } static inline void ee_i_pmfhllh(struct ee_state* ee) { int d = EE_D_RD; ee->r[d].u16[0] = ee->lo.u16[0]; ee->r[d].u16[1] = ee->lo.u16[2]; ee->r[d].u16[2] = ee->hi.u16[0]; ee->r[d].u16[3] = ee->hi.u16[2]; ee->r[d].u16[4] = ee->lo.u16[4]; ee->r[d].u16[5] = ee->lo.u16[6]; ee->r[d].u16[6] = ee->hi.u16[4]; ee->r[d].u16[7] = ee->hi.u16[6]; } static inline void ee_i_pmfhlsh(struct ee_state* ee) { int d = EE_D_RD; ee->r[d].u16[0] = saturate16(ee->lo.u32[0]); ee->r[d].u16[1] = saturate16(ee->lo.u32[1]); ee->r[d].u16[2] = saturate16(ee->hi.u32[0]); ee->r[d].u16[3] = saturate16(ee->hi.u32[1]); ee->r[d].u16[4] = saturate16(ee->lo.u32[2]); ee->r[d].u16[5] = saturate16(ee->lo.u32[3]); ee->r[d].u16[6] = saturate16(ee->hi.u32[2]); ee->r[d].u16[7] = saturate16(ee->hi.u32[3]); } static inline void ee_i_pmflo(struct ee_state* ee) { ee->r[EE_D_RD] = ee->lo; } static inline void ee_i_pminh(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; 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]; 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]; 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]; 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]; 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]; 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]; 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]; 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]; } static inline void ee_i_pminw(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; 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]; 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]; 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]; 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]; } static inline void ee_i_pmsubh(struct ee_state* ee) { printf("ee: pmsubh unimplemented\n"); exit(1); } static inline void ee_i_pmsubw(struct ee_state* ee) { printf("ee: pmsubw unimplemented\n"); exit(1); } static inline void ee_i_pmthi(struct ee_state* ee) { ee->hi = ee->r[EE_D_RS]; } static inline void ee_i_pmthl(struct ee_state* ee) { printf("ee: pmthl unimplemented\n"); exit(1); } static inline void ee_i_pmtlo(struct ee_state* ee) { ee->lo = ee->r[EE_D_RS]; } static inline void ee_i_pmulth(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; ee->lo.u32[0] = (int32_t)(int16_t)ee->r[s].u16[0] * (int32_t)(int16_t)ee->r[t].u16[0]; ee->lo.u32[1] = (int32_t)(int16_t)ee->r[s].u16[1] * (int32_t)(int16_t)ee->r[t].u16[1]; ee->hi.u32[0] = (int32_t)(int16_t)ee->r[s].u16[2] * (int32_t)(int16_t)ee->r[t].u16[2]; ee->hi.u32[1] = (int32_t)(int16_t)ee->r[s].u16[3] * (int32_t)(int16_t)ee->r[t].u16[3]; ee->lo.u32[2] = (int32_t)(int16_t)ee->r[s].u16[4] * (int32_t)(int16_t)ee->r[t].u16[4]; ee->lo.u32[3] = (int32_t)(int16_t)ee->r[s].u16[5] * (int32_t)(int16_t)ee->r[t].u16[5]; ee->hi.u32[2] = (int32_t)(int16_t)ee->r[s].u16[6] * (int32_t)(int16_t)ee->r[t].u16[6]; ee->hi.u32[3] = (int32_t)(int16_t)ee->r[s].u16[7] * (int32_t)(int16_t)ee->r[t].u16[7]; ee->r[d].u32[0] = ee->lo.u32[0]; ee->r[d].u32[1] = ee->hi.u32[0]; ee->r[d].u32[2] = ee->lo.u32[2]; ee->r[d].u32[3] = ee->hi.u32[2]; } static inline void ee_i_pmultuw(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; ee->r[d].u64[0] = (uint64_t)ee->r[s].u32[0] * (uint64_t)ee->r[t].u32[0]; ee->r[d].u64[1] = (uint64_t)ee->r[s].u32[2] * (uint64_t)ee->r[t].u32[2]; ee->lo.u64[0] = SE6432(ee->r[d].u32[0]); ee->lo.u64[1] = SE6432(ee->r[d].u32[2]); ee->hi.u64[0] = SE6432(ee->r[d].u32[1]); ee->hi.u64[1] = SE6432(ee->r[d].u32[3]); } static inline void ee_i_pmultw(struct ee_state* ee) { int s = EE_D_RS; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u64[0] = SE6432(ee->r[s].u32[0]) * SE6432(ee->r[t].u32[0]); ee->r[d].u64[1] = SE6432(ee->r[s].u32[2]) * SE6432(ee->r[t].u32[2]); ee->lo.u64[0] = SE6432(ee->r[d].u32[0]); ee->lo.u64[1] = SE6432(ee->r[d].u32[2]); ee->hi.u64[0] = SE6432(ee->r[d].u32[1]); ee->hi.u64[1] = SE6432(ee->r[d].u32[3]); } static inline void ee_i_pnor(struct ee_state* ee) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u64[0] = ~(rs.u64[0] | rt.u64[0]); ee->r[d].u64[1] = ~(rs.u64[1] | rt.u64[1]); } static inline void ee_i_por(struct ee_state* ee) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u64[0] = rs.u64[0] | rt.u64[0]; ee->r[d].u64[1] = rs.u64[1] | rt.u64[1]; } static inline void ee_i_ppac5(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u32[0] = ((rt.u32[0] & 0x000000f8) >> 3) | ((rt.u32[0] & 0x0000f800) >> 6) | ((rt.u32[0] & 0x00f80000) >> 9) | ((rt.u32[0] & 0x80000000) >> 16); ee->r[d].u32[1] = ((rt.u32[1] & 0x000000f8) >> 3) | ((rt.u32[1] & 0x0000f800) >> 6) | ((rt.u32[1] & 0x00f80000) >> 9) | ((rt.u32[1] & 0x80000000) >> 16); ee->r[d].u32[2] = ((rt.u32[2] & 0x000000f8) >> 3) | ((rt.u32[2] & 0x0000f800) >> 6) | ((rt.u32[2] & 0x00f80000) >> 9) | ((rt.u32[2] & 0x80000000) >> 16); ee->r[d].u32[3] = ((rt.u32[3] & 0x000000f8) >> 3) | ((rt.u32[3] & 0x0000f800) >> 6) | ((rt.u32[3] & 0x00f80000) >> 9) | ((rt.u32[3] & 0x80000000) >> 16); } static inline void ee_i_ppacb(struct ee_state* ee) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u8[0 ] = rt.u8[ 0]; ee->r[d].u8[1 ] = rt.u8[ 2]; ee->r[d].u8[2 ] = rt.u8[ 4]; ee->r[d].u8[3 ] = rt.u8[ 6]; ee->r[d].u8[4 ] = rt.u8[ 8]; ee->r[d].u8[5 ] = rt.u8[10]; ee->r[d].u8[6 ] = rt.u8[12]; ee->r[d].u8[7 ] = rt.u8[14]; ee->r[d].u8[8 ] = rs.u8[ 0]; ee->r[d].u8[9 ] = rs.u8[ 2]; ee->r[d].u8[10] = rs.u8[ 4]; ee->r[d].u8[11] = rs.u8[ 6]; ee->r[d].u8[12] = rs.u8[ 8]; ee->r[d].u8[13] = rs.u8[10]; ee->r[d].u8[14] = rs.u8[12]; ee->r[d].u8[15] = rs.u8[14]; } static inline void ee_i_ppach(struct ee_state* ee) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[0]; ee->r[d].u16[1] = rt.u16[2]; ee->r[d].u16[2] = rt.u16[4]; ee->r[d].u16[3] = rt.u16[6]; ee->r[d].u16[4] = rs.u16[0]; ee->r[d].u16[5] = rs.u16[2]; ee->r[d].u16[6] = rs.u16[4]; ee->r[d].u16[7] = rs.u16[6]; } static inline void ee_i_ppacw(struct ee_state* ee) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u32[0] = rt.u32[0]; ee->r[d].u32[1] = rt.u32[2]; ee->r[d].u32[2] = rs.u32[0]; ee->r[d].u32[3] = rs.u32[2]; } static inline void ee_i_pref(struct ee_state* ee) { // Does nothing } static inline void ee_i_prevh(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u16[0] = rt.u16[3]; ee->r[d].u16[1] = rt.u16[2]; ee->r[d].u16[2] = rt.u16[1]; ee->r[d].u16[3] = rt.u16[0]; ee->r[d].u16[4] = rt.u16[7]; ee->r[d].u16[5] = rt.u16[6]; ee->r[d].u16[6] = rt.u16[5]; ee->r[d].u16[7] = rt.u16[4]; } static inline void ee_i_prot3w(struct ee_state* ee) { uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u32[0] = rt.u32[1]; ee->r[d].u32[1] = rt.u32[2]; ee->r[d].u32[2] = rt.u32[0]; ee->r[d].u32[3] = rt.u32[3]; } static inline void ee_i_psllh(struct ee_state* ee) { int sa = EE_D_SA & 0xf; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u16[0] = ee->r[t].u16[0] << sa; ee->r[d].u16[1] = ee->r[t].u16[1] << sa; ee->r[d].u16[2] = ee->r[t].u16[2] << sa; ee->r[d].u16[3] = ee->r[t].u16[3] << sa; ee->r[d].u16[4] = ee->r[t].u16[4] << sa; ee->r[d].u16[5] = ee->r[t].u16[5] << sa; ee->r[d].u16[6] = ee->r[t].u16[6] << sa; ee->r[d].u16[7] = ee->r[t].u16[7] << sa; } static inline void ee_i_psllvw(struct ee_state* ee) { printf("ee: psllvw unimplemented\n"); exit(1); } static inline void ee_i_psllw(struct ee_state* ee) { int sa = EE_D_SA; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u32[0] = ee->r[t].u32[0] << sa; ee->r[d].u32[1] = ee->r[t].u32[1] << sa; ee->r[d].u32[2] = ee->r[t].u32[2] << sa; ee->r[d].u32[3] = ee->r[t].u32[3] << sa; } static inline void ee_i_psrah(struct ee_state* ee) { int sa = EE_D_SA & 0xf; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u16[0] = ((int16_t)ee->r[t].u16[0]) >> sa; ee->r[d].u16[1] = ((int16_t)ee->r[t].u16[1]) >> sa; ee->r[d].u16[2] = ((int16_t)ee->r[t].u16[2]) >> sa; ee->r[d].u16[3] = ((int16_t)ee->r[t].u16[3]) >> sa; ee->r[d].u16[4] = ((int16_t)ee->r[t].u16[4]) >> sa; ee->r[d].u16[5] = ((int16_t)ee->r[t].u16[5]) >> sa; ee->r[d].u16[6] = ((int16_t)ee->r[t].u16[6]) >> sa; ee->r[d].u16[7] = ((int16_t)ee->r[t].u16[7]) >> sa; } static inline void ee_i_psravw(struct ee_state* ee) { printf("ee: psravw unimplemented\n"); exit(1); } static inline void ee_i_psraw(struct ee_state* ee) { int sa = EE_D_SA; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u32[0] = ((int32_t)ee->r[t].u32[0]) >> sa; ee->r[d].u32[1] = ((int32_t)ee->r[t].u32[1]) >> sa; ee->r[d].u32[2] = ((int32_t)ee->r[t].u32[2]) >> sa; ee->r[d].u32[3] = ((int32_t)ee->r[t].u32[3]) >> sa; } static inline void ee_i_psrlh(struct ee_state* ee) { int sa = EE_D_SA & 0xf; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u16[0] = ee->r[t].u16[0] >> sa; ee->r[d].u16[1] = ee->r[t].u16[1] >> sa; ee->r[d].u16[2] = ee->r[t].u16[2] >> sa; ee->r[d].u16[3] = ee->r[t].u16[3] >> sa; ee->r[d].u16[4] = ee->r[t].u16[4] >> sa; ee->r[d].u16[5] = ee->r[t].u16[5] >> sa; ee->r[d].u16[6] = ee->r[t].u16[6] >> sa; ee->r[d].u16[7] = ee->r[t].u16[7] >> sa; } static inline void ee_i_psrlvw(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; ee->r[d].u64[0] = SE6432(ee->r[t].u32[0] >> (ee->r[s].u32[0] & 31)); ee->r[d].u64[1] = SE6432(ee->r[t].u32[2] >> (ee->r[s].u32[2] & 31)); } static inline void ee_i_psrlw(struct ee_state* ee) { int sa = EE_D_SA; int t = EE_D_RT; int d = EE_D_RD; ee->r[d].u32[0] = ee->r[t].u32[0] >> sa; ee->r[d].u32[1] = ee->r[t].u32[1] >> sa; ee->r[d].u32[2] = ee->r[t].u32[2] >> sa; ee->r[d].u32[3] = ee->r[t].u32[3] >> sa; } static inline void ee_i_psubb(struct ee_state* ee) { int t = EE_D_RT; int s = EE_D_RS; int d = EE_D_RD; ee->r[d].u8[0 ] = ee->r[s].u8[0 ] - ee->r[t].u8[0 ]; ee->r[d].u8[1 ] = ee->r[s].u8[1 ] - ee->r[t].u8[1 ]; ee->r[d].u8[2 ] = ee->r[s].u8[2 ] - ee->r[t].u8[2 ]; ee->r[d].u8[3 ] = ee->r[s].u8[3 ] - ee->r[t].u8[3 ]; ee->r[d].u8[4 ] = ee->r[s].u8[4 ] - ee->r[t].u8[4 ]; ee->r[d].u8[5 ] = ee->r[s].u8[5 ] - ee->r[t].u8[5 ]; ee->r[d].u8[6 ] = ee->r[s].u8[6 ] - ee->r[t].u8[6 ]; ee->r[d].u8[7 ] = ee->r[s].u8[7 ] - ee->r[t].u8[7 ]; ee->r[d].u8[8 ] = ee->r[s].u8[8 ] - ee->r[t].u8[8 ]; ee->r[d].u8[9 ] = ee->r[s].u8[9 ] - ee->r[t].u8[9 ]; ee->r[d].u8[10] = ee->r[s].u8[10] - ee->r[t].u8[10]; ee->r[d].u8[11] = ee->r[s].u8[11] - ee->r[t].u8[11]; ee->r[d].u8[12] = ee->r[s].u8[12] - ee->r[t].u8[12]; ee->r[d].u8[13] = ee->r[s].u8[13] - ee->r[t].u8[13]; ee->r[d].u8[14] = ee->r[s].u8[14] - ee->r[t].u8[14]; ee->r[d].u8[15] = ee->r[s].u8[15] - ee->r[t].u8[15]; } static inline void ee_i_psubh(struct ee_state* ee) { int t = EE_D_RT; int s = EE_D_RS; int d = EE_D_RD; ee->r[d].u16[0] = ee->r[s].u16[0] - ee->r[t].u16[0]; ee->r[d].u16[1] = ee->r[s].u16[1] - ee->r[t].u16[1]; ee->r[d].u16[2] = ee->r[s].u16[2] - ee->r[t].u16[2]; ee->r[d].u16[3] = ee->r[s].u16[3] - ee->r[t].u16[3]; ee->r[d].u16[4] = ee->r[s].u16[4] - ee->r[t].u16[4]; ee->r[d].u16[5] = ee->r[s].u16[5] - ee->r[t].u16[5]; ee->r[d].u16[6] = ee->r[s].u16[6] - ee->r[t].u16[6]; ee->r[d].u16[7] = ee->r[s].u16[7] - ee->r[t].u16[7]; } static inline void ee_i_psubsb(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int32_t r0 = ((int32_t)(int8_t)ee->r[s].u8[0 ]) - ((int32_t)(int8_t)ee->r[t].u8[0 ]); int32_t r1 = ((int32_t)(int8_t)ee->r[s].u8[1 ]) - ((int32_t)(int8_t)ee->r[t].u8[1 ]); int32_t r2 = ((int32_t)(int8_t)ee->r[s].u8[2 ]) - ((int32_t)(int8_t)ee->r[t].u8[2 ]); int32_t r3 = ((int32_t)(int8_t)ee->r[s].u8[3 ]) - ((int32_t)(int8_t)ee->r[t].u8[3 ]); int32_t r4 = ((int32_t)(int8_t)ee->r[s].u8[4 ]) - ((int32_t)(int8_t)ee->r[t].u8[4 ]); int32_t r5 = ((int32_t)(int8_t)ee->r[s].u8[5 ]) - ((int32_t)(int8_t)ee->r[t].u8[5 ]); int32_t r6 = ((int32_t)(int8_t)ee->r[s].u8[6 ]) - ((int32_t)(int8_t)ee->r[t].u8[6 ]); int32_t r7 = ((int32_t)(int8_t)ee->r[s].u8[7 ]) - ((int32_t)(int8_t)ee->r[t].u8[7 ]); int32_t r8 = ((int32_t)(int8_t)ee->r[s].u8[8 ]) - ((int32_t)(int8_t)ee->r[t].u8[8 ]); int32_t r9 = ((int32_t)(int8_t)ee->r[s].u8[9 ]) - ((int32_t)(int8_t)ee->r[t].u8[9 ]); int32_t r10 = ((int32_t)(int8_t)ee->r[s].u8[10]) - ((int32_t)(int8_t)ee->r[t].u8[10]); int32_t r11 = ((int32_t)(int8_t)ee->r[s].u8[11]) - ((int32_t)(int8_t)ee->r[t].u8[11]); int32_t r12 = ((int32_t)(int8_t)ee->r[s].u8[12]) - ((int32_t)(int8_t)ee->r[t].u8[12]); int32_t r13 = ((int32_t)(int8_t)ee->r[s].u8[13]) - ((int32_t)(int8_t)ee->r[t].u8[13]); int32_t r14 = ((int32_t)(int8_t)ee->r[s].u8[14]) - ((int32_t)(int8_t)ee->r[t].u8[14]); int32_t r15 = ((int32_t)(int8_t)ee->r[s].u8[15]) - ((int32_t)(int8_t)ee->r[t].u8[15]); ee->r[d].u8[0 ] = (r0 >= 0x7f) ? 0x7f : ((r0 < -0x80) ? 0x80 : r0); ee->r[d].u8[1 ] = (r1 >= 0x7f) ? 0x7f : ((r1 < -0x80) ? 0x80 : r1); ee->r[d].u8[2 ] = (r2 >= 0x7f) ? 0x7f : ((r2 < -0x80) ? 0x80 : r2); ee->r[d].u8[3 ] = (r3 >= 0x7f) ? 0x7f : ((r3 < -0x80) ? 0x80 : r3); ee->r[d].u8[4 ] = (r4 >= 0x7f) ? 0x7f : ((r4 < -0x80) ? 0x80 : r4); ee->r[d].u8[5 ] = (r5 >= 0x7f) ? 0x7f : ((r5 < -0x80) ? 0x80 : r5); ee->r[d].u8[6 ] = (r6 >= 0x7f) ? 0x7f : ((r6 < -0x80) ? 0x80 : r6); ee->r[d].u8[7 ] = (r7 >= 0x7f) ? 0x7f : ((r7 < -0x80) ? 0x80 : r7); ee->r[d].u8[8 ] = (r8 >= 0x7f) ? 0x7f : ((r8 < -0x80) ? 0x80 : r8); ee->r[d].u8[9 ] = (r9 >= 0x7f) ? 0x7f : ((r9 < -0x80) ? 0x80 : r9); ee->r[d].u8[10] = (r10 >= 0x7f) ? 0x7f : ((r10 < -0x80) ? 0x80 : r10); ee->r[d].u8[11] = (r11 >= 0x7f) ? 0x7f : ((r11 < -0x80) ? 0x80 : r11); ee->r[d].u8[12] = (r12 >= 0x7f) ? 0x7f : ((r12 < -0x80) ? 0x80 : r12); ee->r[d].u8[13] = (r13 >= 0x7f) ? 0x7f : ((r13 < -0x80) ? 0x80 : r13); ee->r[d].u8[14] = (r14 >= 0x7f) ? 0x7f : ((r14 < -0x80) ? 0x80 : r14); ee->r[d].u8[15] = (r15 >= 0x7f) ? 0x7f : ((r15 < -0x80) ? 0x80 : r15); } static inline void ee_i_psubsh(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int32_t r0 = SE3216(ee->r[s].u16[0]) - SE3216(ee->r[t].u16[0]); int32_t r1 = SE3216(ee->r[s].u16[1]) - SE3216(ee->r[t].u16[1]); int32_t r2 = SE3216(ee->r[s].u16[2]) - SE3216(ee->r[t].u16[2]); int32_t r3 = SE3216(ee->r[s].u16[3]) - SE3216(ee->r[t].u16[3]); int32_t r4 = SE3216(ee->r[s].u16[4]) - SE3216(ee->r[t].u16[4]); int32_t r5 = SE3216(ee->r[s].u16[5]) - SE3216(ee->r[t].u16[5]); int32_t r6 = SE3216(ee->r[s].u16[6]) - SE3216(ee->r[t].u16[6]); int32_t r7 = SE3216(ee->r[s].u16[7]) - SE3216(ee->r[t].u16[7]); ee->r[d].u16[0] = (r0 >= 0x7fff) ? 0x7fff : ((r0 < -0x8000) ? 0x8000 : r0); ee->r[d].u16[1] = (r1 >= 0x7fff) ? 0x7fff : ((r1 < -0x8000) ? 0x8000 : r1); ee->r[d].u16[2] = (r2 >= 0x7fff) ? 0x7fff : ((r2 < -0x8000) ? 0x8000 : r2); ee->r[d].u16[3] = (r3 >= 0x7fff) ? 0x7fff : ((r3 < -0x8000) ? 0x8000 : r3); ee->r[d].u16[4] = (r4 >= 0x7fff) ? 0x7fff : ((r4 < -0x8000) ? 0x8000 : r4); ee->r[d].u16[5] = (r5 >= 0x7fff) ? 0x7fff : ((r5 < -0x8000) ? 0x8000 : r5); ee->r[d].u16[6] = (r6 >= 0x7fff) ? 0x7fff : ((r6 < -0x8000) ? 0x8000 : r6); ee->r[d].u16[7] = (r7 >= 0x7fff) ? 0x7fff : ((r7 < -0x8000) ? 0x8000 : r7); } static inline void ee_i_psubsw(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int64_t r0 = SE6432(ee->r[s].u32[0]) - SE6432(ee->r[t].u32[0]); int64_t r1 = SE6432(ee->r[s].u32[1]) - SE6432(ee->r[t].u32[1]); int64_t r2 = SE6432(ee->r[s].u32[2]) - SE6432(ee->r[t].u32[2]); int64_t r3 = SE6432(ee->r[s].u32[3]) - SE6432(ee->r[t].u32[3]); ee->r[d].u32[0] = (r0 >= 0x7fffffff) ? 0x7fffffff : ((r0 < (int32_t)0x80000000) ? 0x80000000 : r0); ee->r[d].u32[1] = (r1 >= 0x7fffffff) ? 0x7fffffff : ((r1 < (int32_t)0x80000000) ? 0x80000000 : r1); ee->r[d].u32[2] = (r2 >= 0x7fffffff) ? 0x7fffffff : ((r2 < (int32_t)0x80000000) ? 0x80000000 : r2); ee->r[d].u32[3] = (r3 >= 0x7fffffff) ? 0x7fffffff : ((r3 < (int32_t)0x80000000) ? 0x80000000 : r3); } static inline void ee_i_psubub(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int32_t r0 = ((int32_t)ee->r[s].u8[0 ]) - ((int32_t)ee->r[t].u8[0 ]); int32_t r1 = ((int32_t)ee->r[s].u8[1 ]) - ((int32_t)ee->r[t].u8[1 ]); int32_t r2 = ((int32_t)ee->r[s].u8[2 ]) - ((int32_t)ee->r[t].u8[2 ]); int32_t r3 = ((int32_t)ee->r[s].u8[3 ]) - ((int32_t)ee->r[t].u8[3 ]); int32_t r4 = ((int32_t)ee->r[s].u8[4 ]) - ((int32_t)ee->r[t].u8[4 ]); int32_t r5 = ((int32_t)ee->r[s].u8[5 ]) - ((int32_t)ee->r[t].u8[5 ]); int32_t r6 = ((int32_t)ee->r[s].u8[6 ]) - ((int32_t)ee->r[t].u8[6 ]); int32_t r7 = ((int32_t)ee->r[s].u8[7 ]) - ((int32_t)ee->r[t].u8[7 ]); int32_t r8 = ((int32_t)ee->r[s].u8[8 ]) - ((int32_t)ee->r[t].u8[8 ]); int32_t r9 = ((int32_t)ee->r[s].u8[9 ]) - ((int32_t)ee->r[t].u8[9 ]); int32_t r10 = ((int32_t)ee->r[s].u8[10]) - ((int32_t)ee->r[t].u8[10]); int32_t r11 = ((int32_t)ee->r[s].u8[11]) - ((int32_t)ee->r[t].u8[11]); int32_t r12 = ((int32_t)ee->r[s].u8[12]) - ((int32_t)ee->r[t].u8[12]); int32_t r13 = ((int32_t)ee->r[s].u8[13]) - ((int32_t)ee->r[t].u8[13]); int32_t r14 = ((int32_t)ee->r[s].u8[14]) - ((int32_t)ee->r[t].u8[14]); int32_t r15 = ((int32_t)ee->r[s].u8[15]) - ((int32_t)ee->r[t].u8[15]); ee->r[d].u8[0 ] = (r0 < 0) ? 0 : r0; ee->r[d].u8[1 ] = (r1 < 0) ? 0 : r1; ee->r[d].u8[2 ] = (r2 < 0) ? 0 : r2; ee->r[d].u8[3 ] = (r3 < 0) ? 0 : r3; ee->r[d].u8[4 ] = (r4 < 0) ? 0 : r4; ee->r[d].u8[5 ] = (r5 < 0) ? 0 : r5; ee->r[d].u8[6 ] = (r6 < 0) ? 0 : r6; ee->r[d].u8[7 ] = (r7 < 0) ? 0 : r7; ee->r[d].u8[8 ] = (r8 < 0) ? 0 : r8; ee->r[d].u8[9 ] = (r9 < 0) ? 0 : r9; ee->r[d].u8[10] = (r10 < 0) ? 0 : r10; ee->r[d].u8[11] = (r11 < 0) ? 0 : r11; ee->r[d].u8[12] = (r12 < 0) ? 0 : r12; ee->r[d].u8[13] = (r13 < 0) ? 0 : r13; ee->r[d].u8[14] = (r14 < 0) ? 0 : r14; ee->r[d].u8[15] = (r15 < 0) ? 0 : r15; } static inline void ee_i_psubuh(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int32_t r0 = (int32_t)(ee->r[s].u16[0]) - (int32_t)(ee->r[t].u16[0]); int32_t r1 = (int32_t)(ee->r[s].u16[1]) - (int32_t)(ee->r[t].u16[1]); int32_t r2 = (int32_t)(ee->r[s].u16[2]) - (int32_t)(ee->r[t].u16[2]); int32_t r3 = (int32_t)(ee->r[s].u16[3]) - (int32_t)(ee->r[t].u16[3]); int32_t r4 = (int32_t)(ee->r[s].u16[4]) - (int32_t)(ee->r[t].u16[4]); int32_t r5 = (int32_t)(ee->r[s].u16[5]) - (int32_t)(ee->r[t].u16[5]); int32_t r6 = (int32_t)(ee->r[s].u16[6]) - (int32_t)(ee->r[t].u16[6]); int32_t r7 = (int32_t)(ee->r[s].u16[7]) - (int32_t)(ee->r[t].u16[7]); ee->r[d].u16[0] = (r0 < 0) ? 0 : r0; ee->r[d].u16[1] = (r1 < 0) ? 0 : r1; ee->r[d].u16[2] = (r2 < 0) ? 0 : r2; ee->r[d].u16[3] = (r3 < 0) ? 0 : r3; ee->r[d].u16[4] = (r4 < 0) ? 0 : r4; ee->r[d].u16[5] = (r5 < 0) ? 0 : r5; ee->r[d].u16[6] = (r6 < 0) ? 0 : r6; ee->r[d].u16[7] = (r7 < 0) ? 0 : r7; } static inline void ee_i_psubuw(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; int64_t r0 = (int64_t)ee->r[s].u32[0] - (int64_t)ee->r[t].u32[0]; int64_t r1 = (int64_t)ee->r[s].u32[1] - (int64_t)ee->r[t].u32[1]; int64_t r2 = (int64_t)ee->r[s].u32[2] - (int64_t)ee->r[t].u32[2]; int64_t r3 = (int64_t)ee->r[s].u32[3] - (int64_t)ee->r[t].u32[3]; ee->r[d].u32[0] = (r0 < 0) ? 0 : r0; ee->r[d].u32[1] = (r1 < 0) ? 0 : r1; ee->r[d].u32[2] = (r2 < 0) ? 0 : r2; ee->r[d].u32[3] = (r3 < 0) ? 0 : r3; } static inline void ee_i_psubw(struct ee_state* ee) { int d = EE_D_RD; int s = EE_D_RS; int t = EE_D_RT; ee->r[d].u32[0] = ee->r[s].u32[0] - ee->r[t].u32[0]; ee->r[d].u32[1] = ee->r[s].u32[1] - ee->r[t].u32[1]; ee->r[d].u32[2] = ee->r[s].u32[2] - ee->r[t].u32[2]; ee->r[d].u32[3] = ee->r[s].u32[3] - ee->r[t].u32[3]; } static inline void ee_i_pxor(struct ee_state* ee) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; ee->r[d].u64[0] = rs.u64[0] ^ rt.u64[0]; ee->r[d].u64[1] = rs.u64[1] ^ rt.u64[1]; } static inline void ee_i_qfsrv(struct ee_state* ee) { uint128_t rs = ee->r[EE_D_RS]; uint128_t rt = ee->r[EE_D_RT]; int d = EE_D_RD; int shift = ee->sa * 8; uint128_t v; if (!shift) { v = rt; } else { if (shift < 64) { v.u64[0] = rt.u64[0] >> shift; v.u64[1] = rt.u64[1] >> shift; v.u64[0] |= rt.u64[1] << (64 - shift); v.u64[1] |= rs.u64[0] << (64 - shift); } else { v.u64[0] = rt.u64[1] >> (shift - 64); v.u64[1] = rs.u64[0] >> (shift - 64); if (shift != 64) { v.u64[0] |= rs.u64[0] << (128u - shift); v.u64[1] |= rs.u64[1] << (128u - shift); } } } ee->r[d] = v; } static inline void ee_i_qmfc2(struct ee_state* ee) { int t = EE_D_RT; int d = EE_D_RD; ee->r[t].u64[0] = ee->vu0->vf[d].u64[0]; ee->r[t].u64[1] = ee->vu0->vf[d].u64[1]; } static inline void ee_i_qmtc2(struct ee_state* ee) { int t = EE_D_RT; int d = EE_D_RD; if (!d) return; ee->vu0->vf[d].u128 = ee->r[t]; } static inline void ee_i_rsqrts(struct ee_state* ee) { int t = EE_D_RT; int d = EE_D_FD; ee->fcr &= ~(FPU_FLG_I | FPU_FLG_D); if ((ee->f[t].u32 & 0x7f800000) == 0) { ee->fcr |= FPU_FLG_D | FPU_FLG_SD; ee->f[d].u32 = (ee->f[t].u32 & 0x80000000) | 0x7f7fffff; return; } else if (ee->f[t].u32 & 0x80000000) { ee->fcr |= FPU_FLG_I | FPU_FLG_SI; ee->f[d].f = EE_FS / sqrtf(fabsf(fpu_cvtf(ee->f[t].f))); } else { ee->f[d].f = EE_FS / sqrtf(fpu_cvtf(ee->f[t].f)); } if (fpu_check_overflow_no_flags(ee, &ee->f[d])) return; fpu_check_underflow_no_flags(ee, &ee->f[d]); } static inline void ee_i_sb(struct ee_state* ee) { bus_write8(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT); } static inline void ee_i_sd(struct ee_state* ee) { bus_write64(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT); } static inline void ee_i_sdl(struct ee_state* ee) { static const uint8_t sdl_shift[8] = { 56, 48, 40, 32, 24, 16, 8, 0 }; static const uint64_t sdl_mask[8] = { 0xffffffffffffff00ULL, 0xffffffffffff0000ULL, 0xffffffffff000000ULL, 0xffffffff00000000ULL, 0xffffff0000000000ULL, 0xffff000000000000ULL, 0xff00000000000000ULL, 0x0000000000000000ULL }; uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t shift = addr & 7; uint64_t data = bus_read64(ee, addr & ~7); bus_write64(ee, addr & ~7, (EE_RT >> sdl_shift[shift]) | (data & sdl_mask[shift])); } static inline void ee_i_sdr(struct ee_state* ee) { static const uint8_t sdr_shift[8] = { 0, 8, 16, 24, 32, 40, 48, 56 }; static const uint64_t sdr_mask[8] = { 0x0000000000000000ULL, 0x00000000000000ffULL, 0x000000000000ffffULL, 0x0000000000ffffffULL, 0x00000000ffffffffULL, 0x000000ffffffffffULL, 0x0000ffffffffffffULL, 0x00ffffffffffffffULL }; uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t shift = addr & 7; uint64_t data = bus_read64(ee, addr & ~7); bus_write64(ee, addr & ~7, (EE_RT << sdr_shift[shift]) | (data & sdr_mask[shift])); } static inline void ee_i_sh(struct ee_state* ee) { bus_write16(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT); } static inline void ee_i_sll(struct ee_state* ee) { EE_RD = SE6432(EE_RT32 << EE_D_SA); } static inline void ee_i_sllv(struct ee_state* ee) { EE_RD = SE6432(EE_RT32 << (EE_RS & 0x1f)); } static inline void ee_i_slt(struct ee_state* ee) { EE_RD = (int64_t)EE_RS < (int64_t)EE_RT; } static inline void ee_i_slti(struct ee_state* ee) { EE_RT = ((int64_t)EE_RS) < SE6416(EE_D_I16); } static inline void ee_i_sltiu(struct ee_state* ee) { EE_RT = EE_RS < (uint64_t)(SE6416(EE_D_I16)); } static inline void ee_i_sltu(struct ee_state* ee) { EE_RD = EE_RS < EE_RT; } static inline void ee_i_sq(struct ee_state* ee) { bus_write128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf, ee->r[EE_D_RT]); } static inline void ee_i_sqc2(struct ee_state* ee) { bus_write128(ee, (EE_RS32 + SE3216(EE_D_I16)) & ~0xf, ee->vu0->vf[EE_D_RT].u128); } static inline void ee_i_sqrts(struct ee_state* ee) { int t = EE_D_RT; int d = EE_D_FD; ee->fcr &= ~(FPU_FLG_I | FPU_FLG_D); if ((ee->f[t].u32 & 0x7f800000) == 0) { ee->f[d].u32 = ee->f[t].u32 & 0x80000000; } else if (ee->f[t].u32 & 0x80000000) { ee->fcr |= FPU_FLG_I | FPU_FLG_SI; ee->f[d].f = sqrtf(fabsf(fpu_cvtf(ee->f[t].f))); } else { ee->f[d].f = sqrtf(fpu_cvtf(ee->f[t].f)); } } static inline void ee_i_sra(struct ee_state* ee) { EE_RD = SE6432(((int32_t)EE_RT32) >> EE_D_SA); } static inline void ee_i_srav(struct ee_state* ee) { EE_RD = SE6432(((int32_t)EE_RT32) >> (EE_RS & 0x1f)); } static inline void ee_i_srl(struct ee_state* ee) { EE_RD = SE6432(EE_RT32 >> EE_D_SA); } static inline void ee_i_srlv(struct ee_state* ee) { EE_RD = SE6432(EE_RT32 >> (EE_RS & 0x1f)); } static inline void ee_i_sub(struct ee_state* ee) { int32_t r; int o = __builtin_ssub_overflow(EE_RS32, EE_RT32, &r); if (o) { ee_exception_level1(ee, CAUSE_EXC1_OV); } else { EE_RD = SE6432(r); } } static inline void ee_i_subas(struct ee_state* ee) { ee->a.f = EE_FS - EE_FT; if (fpu_check_overflow(ee, &ee->a)) return; fpu_check_underflow(ee, &ee->a); } static inline void ee_i_subs(struct ee_state* ee) { int d = EE_D_FD; ee->f[d].f = EE_FS - EE_FT; if (fpu_check_overflow(ee, &ee->f[d])) return; fpu_check_underflow(ee, &ee->f[d]); } static inline void ee_i_subu(struct ee_state* ee) { EE_RD = SE6432(EE_RS - EE_RT); } static inline void ee_i_sw(struct ee_state* ee) { bus_write32(ee, EE_RS32 + SE3216(EE_D_I16), EE_RT32); } static inline void ee_i_swc1(struct ee_state* ee) { bus_write32(ee, EE_RS32 + SE3216(EE_D_I16), EE_FT32); } static inline void ee_i_swl(struct ee_state* ee) { static const uint32_t swl_mask[4] = { 0xffffff00, 0xffff0000, 0xff000000, 0x00000000 }; static const uint8_t swl_shift[4] = { 24, 16, 8, 0 }; uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t mem = bus_read32(ee, addr & ~3); int shift = addr & 3; bus_write32(ee, addr & ~3, (EE_RT32 >> swl_shift[shift] | (mem & swl_mask[shift]))); // 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); } static inline void ee_i_swr(struct ee_state* ee) { static const uint32_t swr_mask[4] = { 0x00000000, 0x000000ff, 0x0000ffff, 0x00ffffff }; static const uint8_t swr_shift[4] = { 0, 8, 16, 24 }; uint32_t addr = EE_RS32 + SE3216(EE_D_I16); uint32_t mem = bus_read32(ee, addr & ~3); int shift = addr & 3; bus_write32(ee, addr & ~3, (EE_RT32 << swr_shift[shift]) | (mem & swr_mask[shift])); // 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); } static inline void ee_i_sync(struct ee_state* ee) { /* Do nothing */ } // #include "syscall.h" static inline void ee_i_syscall(struct ee_state* ee) { // uint32_t n = ee->r[3].ul64; // if (n & 0x80000000) { // n = (~n) + 1; // } // 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); ee_exception_level1(ee, CAUSE_EXC1_SYS); } static inline void ee_i_teq(struct ee_state* ee) { if (EE_RS == EE_RT) ee_exception_level1(ee, CAUSE_EXC1_TR); } static inline void ee_i_teqi(struct ee_state* ee) { if (EE_RS == SE6416(EE_D_I16)) ee_exception_level1(ee, CAUSE_EXC1_TR); } static inline void ee_i_tge(struct ee_state* ee) { printf("ee: tge unimplemented\n"); exit(1); } static inline void ee_i_tgei(struct ee_state* ee) { printf("ee: tgei unimplemented\n"); exit(1); } static inline void ee_i_tgeiu(struct ee_state* ee) { printf("ee: tgeiu unimplemented\n"); exit(1); } static inline void ee_i_tgeu(struct ee_state* ee) { printf("ee: tgeu unimplemented\n"); exit(1); } static inline void ee_i_tlbp(struct ee_state* ee) { printf("ee: tlbp unimplemented\n"); return; exit(1); } static inline void ee_i_tlbr(struct ee_state* ee) { printf("ee: tlbr unimplemented\n"); return; exit(1); } static inline void ee_i_tlbwi(struct ee_state* ee) { printf("ee: Index=%d EntryLo0=%08x EntryLo1=%08x EntryHi=%08x PageMask=%08x\n", ee->index, ee->entrylo0, ee->entrylo1, ee->entryhi, ee->pagemask ); /* To-do: MMU */ } static inline void ee_i_tlbwr(struct ee_state* ee) { return; printf("ee: tlbwr unimplemented\n"); exit(1); } static inline void ee_i_tlt(struct ee_state* ee) { printf("ee: tlt unimplemented\n"); exit(1); } static inline void ee_i_tlti(struct ee_state* ee) { printf("ee: tlti unimplemented\n"); exit(1); } static inline void ee_i_tltiu(struct ee_state* ee) { printf("ee: tltiu unimplemented\n"); exit(1); } static inline void ee_i_tltu(struct ee_state* ee) { printf("ee: tltu unimplemented\n"); exit(1); } static inline void ee_i_tne(struct ee_state* ee) { if (EE_RS != EE_RT) ee_exception_level1(ee, CAUSE_EXC1_TR); } static inline void ee_i_tnei(struct ee_state* ee) { printf("ee: tnei unimplemented\n"); exit(1); } static inline void ee_i_vabs(struct ee_state* ee) { VU_UPPER(abs) } static inline void ee_i_vadd(struct ee_state* ee) { VU_UPPER(add) } static inline void ee_i_vadda(struct ee_state* ee) { VU_UPPER(adda) } static inline void ee_i_vaddai(struct ee_state* ee) { VU_UPPER(addai) } static inline void ee_i_vaddaq(struct ee_state* ee) { VU_UPPER(addaq) } static inline void ee_i_vaddaw(struct ee_state* ee) { VU_UPPER(addaw) } static inline void ee_i_vaddax(struct ee_state* ee) { VU_UPPER(addax) } static inline void ee_i_vadday(struct ee_state* ee) { VU_UPPER(adday) } static inline void ee_i_vaddaz(struct ee_state* ee) { VU_UPPER(addaz) } static inline void ee_i_vaddi(struct ee_state* ee) { VU_UPPER(addi) } static inline void ee_i_vaddq(struct ee_state* ee) { VU_UPPER(addq) } static inline void ee_i_vaddw(struct ee_state* ee) { VU_UPPER(addw) } static inline void ee_i_vaddx(struct ee_state* ee) { VU_UPPER(addx) } static inline void ee_i_vaddy(struct ee_state* ee) { VU_UPPER(addy) } static inline void ee_i_vaddz(struct ee_state* ee) { VU_UPPER(addz) } static inline void ee_i_vcallms(struct ee_state* ee) { vu_execute_program(ee->vu0, EE_D_I15); } static inline void ee_i_vcallmsr(struct ee_state* ee) { vu_execute_program(ee->vu0, ee->vu0->cmsar0); } static inline void ee_i_vclipw(struct ee_state* ee) { VU_UPPER(clip) } static inline void ee_i_vdiv(struct ee_state* ee) { VU_LOWER(div) } static inline void ee_i_vftoi0(struct ee_state* ee) { VU_UPPER(ftoi0) } static inline void ee_i_vftoi12(struct ee_state* ee) { VU_UPPER(ftoi12) } static inline void ee_i_vftoi15(struct ee_state* ee) { VU_UPPER(ftoi15) } static inline void ee_i_vftoi4(struct ee_state* ee) { VU_UPPER(ftoi4) } static inline void ee_i_viadd(struct ee_state* ee) { VU_LOWER(iadd) } static inline void ee_i_viaddi(struct ee_state* ee) { VU_LOWER(iaddi) } static inline void ee_i_viand(struct ee_state* ee) { VU_LOWER(iand) } static inline void ee_i_vilwr(struct ee_state* ee) { VU_LOWER(ilwr) } static inline void ee_i_vior(struct ee_state* ee) { VU_LOWER(ior) } static inline void ee_i_visub(struct ee_state* ee) { VU_LOWER(isub) } static inline void ee_i_viswr(struct ee_state* ee) { VU_LOWER(iswr) } static inline void ee_i_vitof0(struct ee_state* ee) { VU_UPPER(itof0) } static inline void ee_i_vitof12(struct ee_state* ee) { VU_UPPER(itof12) } static inline void ee_i_vitof15(struct ee_state* ee) { VU_UPPER(itof15) } static inline void ee_i_vitof4(struct ee_state* ee) { VU_UPPER(itof4) } static inline void ee_i_vlqd(struct ee_state* ee) { VU_LOWER(lqd) } static inline void ee_i_vlqi(struct ee_state* ee) { VU_LOWER(lqi) } static inline void ee_i_vmadd(struct ee_state* ee) { VU_UPPER(madd) } static inline void ee_i_vmadda(struct ee_state* ee) { VU_UPPER(madda) } static inline void ee_i_vmaddai(struct ee_state* ee) { VU_UPPER(maddai) } static inline void ee_i_vmaddaq(struct ee_state* ee) { VU_UPPER(maddaq) } static inline void ee_i_vmaddaw(struct ee_state* ee) { VU_UPPER(maddaw) } static inline void ee_i_vmaddax(struct ee_state* ee) { VU_UPPER(maddax) } static inline void ee_i_vmadday(struct ee_state* ee) { VU_UPPER(madday) } static inline void ee_i_vmaddaz(struct ee_state* ee) { VU_UPPER(maddaz) } static inline void ee_i_vmaddi(struct ee_state* ee) { VU_UPPER(maddi) } static inline void ee_i_vmaddq(struct ee_state* ee) { VU_UPPER(maddq) } static inline void ee_i_vmaddw(struct ee_state* ee) { VU_UPPER(maddw) } static inline void ee_i_vmaddx(struct ee_state* ee) { VU_UPPER(maddx) } static inline void ee_i_vmaddy(struct ee_state* ee) { VU_UPPER(maddy) } static inline void ee_i_vmaddz(struct ee_state* ee) { VU_UPPER(maddz) } static inline void ee_i_vmax(struct ee_state* ee) { VU_UPPER(max) } static inline void ee_i_vmaxi(struct ee_state* ee) { VU_UPPER(maxi) } static inline void ee_i_vmaxw(struct ee_state* ee) { VU_UPPER(maxw) } static inline void ee_i_vmaxx(struct ee_state* ee) { VU_UPPER(maxx) } static inline void ee_i_vmaxy(struct ee_state* ee) { VU_UPPER(maxy) } static inline void ee_i_vmaxz(struct ee_state* ee) { VU_UPPER(maxz) } static inline void ee_i_vmfir(struct ee_state* ee) { VU_UPPER(mfir) } static inline void ee_i_vmini(struct ee_state* ee) { VU_UPPER(mini) } static inline void ee_i_vminii(struct ee_state* ee) { VU_UPPER(minii) } static inline void ee_i_vminiw(struct ee_state* ee) { VU_UPPER(miniw) } static inline void ee_i_vminix(struct ee_state* ee) { VU_UPPER(minix) } static inline void ee_i_vminiy(struct ee_state* ee) { VU_UPPER(miniy) } static inline void ee_i_vminiz(struct ee_state* ee) { VU_UPPER(miniz) } static inline void ee_i_vmove(struct ee_state* ee) { VU_LOWER(move) } static inline void ee_i_vmr32(struct ee_state* ee) { VU_LOWER(mr32) } static inline void ee_i_vmsub(struct ee_state* ee) { VU_UPPER(msub) } static inline void ee_i_vmsuba(struct ee_state* ee) { VU_UPPER(msuba) } static inline void ee_i_vmsubai(struct ee_state* ee) { VU_UPPER(msubai) } static inline void ee_i_vmsubaq(struct ee_state* ee) { VU_UPPER(msubaq) } static inline void ee_i_vmsubaw(struct ee_state* ee) { VU_UPPER(msubaw) } static inline void ee_i_vmsubax(struct ee_state* ee) { VU_UPPER(msubax) } static inline void ee_i_vmsubay(struct ee_state* ee) { VU_UPPER(msubay) } static inline void ee_i_vmsubaz(struct ee_state* ee) { VU_UPPER(msubaz) } static inline void ee_i_vmsubi(struct ee_state* ee) { VU_UPPER(msubi) } static inline void ee_i_vmsubq(struct ee_state* ee) { VU_UPPER(msubq) } static inline void ee_i_vmsubw(struct ee_state* ee) { VU_UPPER(msubw) } static inline void ee_i_vmsubx(struct ee_state* ee) { VU_UPPER(msubx) } static inline void ee_i_vmsuby(struct ee_state* ee) { VU_UPPER(msuby) } static inline void ee_i_vmsubz(struct ee_state* ee) { VU_UPPER(msubz) } static inline void ee_i_vmtir(struct ee_state* ee) { VU_LOWER(mtir) } static inline void ee_i_vmul(struct ee_state* ee) { VU_UPPER(mul) } static inline void ee_i_vmula(struct ee_state* ee) { VU_UPPER(mula) } static inline void ee_i_vmulai(struct ee_state* ee) { VU_UPPER(mulai) } static inline void ee_i_vmulaq(struct ee_state* ee) { VU_UPPER(mulaq) } static inline void ee_i_vmulaw(struct ee_state* ee) { VU_UPPER(mulaw) } static inline void ee_i_vmulax(struct ee_state* ee) { VU_UPPER(mulax) } static inline void ee_i_vmulay(struct ee_state* ee) { VU_UPPER(mulay) } static inline void ee_i_vmulaz(struct ee_state* ee) { VU_UPPER(mulaz) } static inline void ee_i_vmuli(struct ee_state* ee) { VU_UPPER(muli) } static inline void ee_i_vmulq(struct ee_state* ee) { VU_UPPER(mulq) } static inline void ee_i_vmulw(struct ee_state* ee) { VU_UPPER(mulw) } static inline void ee_i_vmulx(struct ee_state* ee) { VU_UPPER(mulx) } static inline void ee_i_vmuly(struct ee_state* ee) { VU_UPPER(muly) } static inline void ee_i_vmulz(struct ee_state* ee) { VU_UPPER(mulz) } static inline void ee_i_vnop(struct ee_state* ee) { VU_UPPER(nop) } static inline void ee_i_vopmsub(struct ee_state* ee) { VU_UPPER(opmsub) } static inline void ee_i_vopmula(struct ee_state* ee) { VU_UPPER(opmula) } static inline void ee_i_vrget(struct ee_state* ee) { VU_LOWER(rget) } static inline void ee_i_vrinit(struct ee_state* ee) { VU_LOWER(rinit) } static inline void ee_i_vrnext(struct ee_state* ee) { VU_LOWER(rnext) } static inline void ee_i_vrsqrt(struct ee_state* ee) { VU_LOWER(rsqrt) } static inline void ee_i_vrxor(struct ee_state* ee) { VU_LOWER(rxor) } static inline void ee_i_vsqd(struct ee_state* ee) { VU_LOWER(sqd) } static inline void ee_i_vsqi(struct ee_state* ee) { VU_LOWER(sqi) } static inline void ee_i_vsqrt(struct ee_state* ee) { VU_LOWER(sqrt) } static inline void ee_i_vsub(struct ee_state* ee) { VU_UPPER(sub) } static inline void ee_i_vsuba(struct ee_state* ee) { VU_UPPER(suba) } static inline void ee_i_vsubai(struct ee_state* ee) { VU_UPPER(subai) } static inline void ee_i_vsubaq(struct ee_state* ee) { VU_UPPER(subaq) } static inline void ee_i_vsubaw(struct ee_state* ee) { VU_UPPER(subaw) } static inline void ee_i_vsubax(struct ee_state* ee) { VU_UPPER(subax) } static inline void ee_i_vsubay(struct ee_state* ee) { VU_UPPER(subay) } static inline void ee_i_vsubaz(struct ee_state* ee) { VU_UPPER(subaz) } static inline void ee_i_vsubi(struct ee_state* ee) { VU_UPPER(subi) } static inline void ee_i_vsubq(struct ee_state* ee) { VU_UPPER(subq) } static inline void ee_i_vsubw(struct ee_state* ee) { VU_UPPER(subw) } static inline void ee_i_vsubx(struct ee_state* ee) { VU_UPPER(subx) } static inline void ee_i_vsuby(struct ee_state* ee) { VU_UPPER(suby) } static inline void ee_i_vsubz(struct ee_state* ee) { VU_UPPER(subz) } static inline void ee_i_vwaitq(struct ee_state* ee) { VU_LOWER(waitq) } static inline void ee_i_xor(struct ee_state* ee) { EE_RD = EE_RS ^ EE_RT; } static inline void ee_i_xori(struct ee_state* ee) { EE_RT = EE_RS ^ EE_D_I16; } struct ee_state* ee_create(void) { return malloc(sizeof(struct ee_state)); } void ee_init(struct ee_state* ee, struct vu_state* vu0, struct vu_state* vu1, struct ee_bus_s bus) { memset(ee, 0, sizeof(struct ee_state)); ee->prid = 0x2e20; ee->pc = EE_VEC_RESET; ee->next_pc = ee->pc + 4; ee->bus = bus; ee->vu0 = vu0; ee->vu1 = vu1; // To-do: Set SR ee->scratchpad = ps2_ram_create(); ps2_ram_init(ee->scratchpad, 0x4000); // EE's FPU uses round to zero by default fesetround(FE_TOWARDZERO); ee->fcr = 0x01000001; } static inline void ee_execute(struct ee_state* ee) { switch ((ee->opcode & 0xFC000000) >> 26) { case 0x00000000 >> 26: { // special switch (ee->opcode & 0x0000003F) { case 0x00000000: ee_i_sll(ee); return; case 0x00000002: ee_i_srl(ee); return; case 0x00000003: ee_i_sra(ee); return; case 0x00000004: ee_i_sllv(ee); return; case 0x00000006: ee_i_srlv(ee); return; case 0x00000007: ee_i_srav(ee); return; case 0x00000008: ee_i_jr(ee); return; case 0x00000009: ee_i_jalr(ee); return; case 0x0000000A: ee_i_movz(ee); return; case 0x0000000B: ee_i_movn(ee); return; case 0x0000000C: ee_i_syscall(ee); return; case 0x0000000D: ee_i_break(ee); return; case 0x0000000F: ee_i_sync(ee); return; case 0x00000010: ee_i_mfhi(ee); return; case 0x00000011: ee_i_mthi(ee); return; case 0x00000012: ee_i_mflo(ee); return; case 0x00000013: ee_i_mtlo(ee); return; case 0x00000014: ee_i_dsllv(ee); return; case 0x00000016: ee_i_dsrlv(ee); return; case 0x00000017: ee_i_dsrav(ee); return; case 0x00000018: ee_i_mult(ee); return; case 0x00000019: ee_i_multu(ee); return; case 0x0000001A: ee_i_div(ee); return; case 0x0000001B: ee_i_divu(ee); return; case 0x00000020: ee_i_add(ee); return; case 0x00000021: ee_i_addu(ee); return; case 0x00000022: ee_i_sub(ee); return; case 0x00000023: ee_i_subu(ee); return; case 0x00000024: ee_i_and(ee); return; case 0x00000025: ee_i_or(ee); return; case 0x00000026: ee_i_xor(ee); return; case 0x00000027: ee_i_nor(ee); return; case 0x00000028: ee_i_mfsa(ee); return; case 0x00000029: ee_i_mtsa(ee); return; case 0x0000002A: ee_i_slt(ee); return; case 0x0000002B: ee_i_sltu(ee); return; case 0x0000002C: ee_i_dadd(ee); return; case 0x0000002D: ee_i_daddu(ee); return; case 0x0000002E: ee_i_dsub(ee); return; case 0x0000002F: ee_i_dsubu(ee); return; case 0x00000030: ee_i_tge(ee); return; case 0x00000031: ee_i_tgeu(ee); return; case 0x00000032: ee_i_tlt(ee); return; case 0x00000033: ee_i_tltu(ee); return; case 0x00000034: ee_i_teq(ee); return; case 0x00000036: ee_i_tne(ee); return; case 0x00000038: ee_i_dsll(ee); return; case 0x0000003A: ee_i_dsrl(ee); return; case 0x0000003B: ee_i_dsra(ee); return; case 0x0000003C: ee_i_dsll32(ee); return; case 0x0000003E: ee_i_dsrl32(ee); return; case 0x0000003F: ee_i_dsra32(ee); return; } } break; case 0x04000000 >> 26: { // regimm switch ((ee->opcode & 0x001F0000) >> 16) { case 0x00000000 >> 16: ee_i_bltz(ee); return; case 0x00010000 >> 16: ee_i_bgez(ee); return; case 0x00020000 >> 16: ee_i_bltzl(ee); return; case 0x00030000 >> 16: ee_i_bgezl(ee); return; case 0x00080000 >> 16: ee_i_tgei(ee); return; case 0x00090000 >> 16: ee_i_tgeiu(ee); return; case 0x000A0000 >> 16: ee_i_tlti(ee); return; case 0x000B0000 >> 16: ee_i_tltiu(ee); return; case 0x000C0000 >> 16: ee_i_teqi(ee); return; case 0x000E0000 >> 16: ee_i_tnei(ee); return; case 0x00100000 >> 16: ee_i_bltzal(ee); return; case 0x00110000 >> 16: ee_i_bgezal(ee); return; case 0x00120000 >> 16: ee_i_bltzall(ee); return; case 0x00130000 >> 16: ee_i_bgezall(ee); return; case 0x00180000 >> 16: ee_i_mtsab(ee); return; case 0x00190000 >> 16: ee_i_mtsah(ee); return; } } break; case 0x08000000 >> 26: ee_i_j(ee); return; case 0x0C000000 >> 26: ee_i_jal(ee); return; case 0x10000000 >> 26: ee_i_beq(ee); return; case 0x14000000 >> 26: ee_i_bne(ee); return; case 0x18000000 >> 26: ee_i_blez(ee); return; case 0x1C000000 >> 26: ee_i_bgtz(ee); return; case 0x20000000 >> 26: ee_i_addi(ee); return; case 0x24000000 >> 26: ee_i_addiu(ee); return; case 0x28000000 >> 26: ee_i_slti(ee); return; case 0x2C000000 >> 26: ee_i_sltiu(ee); return; case 0x30000000 >> 26: ee_i_andi(ee); return; case 0x34000000 >> 26: ee_i_ori(ee); return; case 0x38000000 >> 26: ee_i_xori(ee); return; case 0x3C000000 >> 26: ee_i_lui(ee); return; case 0x40000000 >> 26: { // cop0 switch ((ee->opcode & 0x03E00000) >> 21) { case 0x00000000 >> 21: ee_i_mfc0(ee); return; case 0x00800000 >> 21: ee_i_mtc0(ee); return; case 0x01000000 >> 21: { switch ((ee->opcode & 0x001F0000) >> 16) { case 0x00000000 >> 16: ee_i_bc0f(ee); return; case 0x00010000 >> 16: ee_i_bc0t(ee); return; case 0x00020000 >> 16: ee_i_bc0fl(ee); return; case 0x00030000 >> 16: ee_i_bc0tl(ee); return; } } break; case 0x02000000 >> 21: { switch (ee->opcode & 0x0000003F) { case 0x00000001: ee_i_tlbr(ee); return; case 0x00000002: ee_i_tlbwi(ee); return; case 0x00000006: ee_i_tlbwr(ee); return; case 0x00000008: ee_i_tlbp(ee); return; case 0x00000018: ee_i_eret(ee); return; case 0x00000038: ee_i_ei(ee); return; case 0x00000039: ee_i_di(ee); return; } } break; } } break; case 0x44000000 >> 26: { // cop1 switch ((ee->opcode & 0x03E00000) >> 21) { case 0x00000000 >> 21: ee_i_mfc1(ee); return; case 0x00400000 >> 21: ee_i_cfc1(ee); return; case 0x00800000 >> 21: ee_i_mtc1(ee); return; case 0x00C00000 >> 21: ee_i_ctc1(ee); return; case 0x01000000 >> 21: { switch ((ee->opcode & 0x001F0000) >> 16) { case 0x00000000 >> 16: ee_i_bc1f(ee); return; case 0x00010000 >> 16: ee_i_bc1t(ee); return; case 0x00020000 >> 16: ee_i_bc1fl(ee); return; case 0x00030000 >> 16: ee_i_bc1tl(ee); return; } } break; case 0x02000000 >> 21: { switch (ee->opcode & 0x0000003F) { case 0x00000000: ee_i_adds(ee); return; case 0x00000001: ee_i_subs(ee); return; case 0x00000002: ee_i_muls(ee); return; case 0x00000003: ee_i_divs(ee); return; case 0x00000004: ee_i_sqrts(ee); return; case 0x00000005: ee_i_abss(ee); return; case 0x00000006: ee_i_movs(ee); return; case 0x00000007: ee_i_negs(ee); return; case 0x00000016: ee_i_rsqrts(ee); return; case 0x00000018: ee_i_addas(ee); return; case 0x00000019: ee_i_subas(ee); return; case 0x0000001A: ee_i_mulas(ee); return; case 0x0000001C: ee_i_madds(ee); return; case 0x0000001D: ee_i_msubs(ee); return; case 0x0000001E: ee_i_maddas(ee); return; case 0x0000001F: ee_i_msubas(ee); return; case 0x00000024: ee_i_cvtw(ee); return; case 0x00000028: ee_i_maxs(ee); return; case 0x00000029: ee_i_mins(ee); return; case 0x00000030: ee_i_cf(ee); return; case 0x00000032: ee_i_ceq(ee); return; case 0x00000034: ee_i_clt(ee); return; case 0x00000036: ee_i_cle(ee); return; } } break; case 0x02800000 >> 21: { switch (ee->opcode & 0x0000003F) { case 0x00000020: ee_i_cvts(ee); return; } } break; } } break; case 0x48000000 >> 26: { // cop2 switch ((ee->opcode & 0x03E00000) >> 21) { case 0x00200000 >> 21: ee_i_qmfc2(ee); return; case 0x00400000 >> 21: ee_i_cfc2(ee); return; case 0x00A00000 >> 21: ee_i_qmtc2(ee); return; case 0x00C00000 >> 21: ee_i_ctc2(ee); return; case 0x01000000 >> 21: { switch ((ee->opcode & 0x001F0000) >> 16) { case 0x00000000 >> 16: ee_i_bc2f(ee); return; case 0x00010000 >> 16: ee_i_bc2t(ee); return; case 0x00020000 >> 16: ee_i_bc2fl(ee); return; case 0x00030000 >> 16: ee_i_bc2tl(ee); return; } } break; case 0x02000000 >> 21: case 0x02200000 >> 21: case 0x02400000 >> 21: case 0x02600000 >> 21: case 0x02800000 >> 21: case 0x02A00000 >> 21: case 0x02C00000 >> 21: case 0x02E00000 >> 21: case 0x03000000 >> 21: case 0x03200000 >> 21: case 0x03400000 >> 21: case 0x03600000 >> 21: case 0x03800000 >> 21: case 0x03A00000 >> 21: case 0x03C00000 >> 21: case 0x03E00000 >> 21: { switch (ee->opcode & 0x0000003F) { case 0x00000000: ee_i_vaddx(ee); return; case 0x00000001: ee_i_vaddy(ee); return; case 0x00000002: ee_i_vaddz(ee); return; case 0x00000003: ee_i_vaddw(ee); return; case 0x00000004: ee_i_vsubx(ee); return; case 0x00000005: ee_i_vsuby(ee); return; case 0x00000006: ee_i_vsubz(ee); return; case 0x00000007: ee_i_vsubw(ee); return; case 0x00000008: ee_i_vmaddx(ee); return; case 0x00000009: ee_i_vmaddy(ee); return; case 0x0000000A: ee_i_vmaddz(ee); return; case 0x0000000B: ee_i_vmaddw(ee); return; case 0x0000000C: ee_i_vmsubx(ee); return; case 0x0000000D: ee_i_vmsuby(ee); return; case 0x0000000E: ee_i_vmsubz(ee); return; case 0x0000000F: ee_i_vmsubw(ee); return; case 0x00000010: ee_i_vmaxx(ee); return; case 0x00000011: ee_i_vmaxy(ee); return; case 0x00000012: ee_i_vmaxz(ee); return; case 0x00000013: ee_i_vmaxw(ee); return; case 0x00000014: ee_i_vminix(ee); return; case 0x00000015: ee_i_vminiy(ee); return; case 0x00000016: ee_i_vminiz(ee); return; case 0x00000017: ee_i_vminiw(ee); return; case 0x00000018: ee_i_vmulx(ee); return; case 0x00000019: ee_i_vmuly(ee); return; case 0x0000001A: ee_i_vmulz(ee); return; case 0x0000001B: ee_i_vmulw(ee); return; case 0x0000001C: ee_i_vmulq(ee); return; case 0x0000001D: ee_i_vmaxi(ee); return; case 0x0000001E: ee_i_vmuli(ee); return; case 0x0000001F: ee_i_vminii(ee); return; case 0x00000020: ee_i_vaddq(ee); return; case 0x00000021: ee_i_vmaddq(ee); return; case 0x00000022: ee_i_vaddi(ee); return; case 0x00000023: ee_i_vmaddi(ee); return; case 0x00000024: ee_i_vsubq(ee); return; case 0x00000025: ee_i_vmsubq(ee); return; case 0x00000026: ee_i_vsubi(ee); return; case 0x00000027: ee_i_vmsubi(ee); return; case 0x00000028: ee_i_vadd(ee); return; case 0x00000029: ee_i_vmadd(ee); return; case 0x0000002A: ee_i_vmul(ee); return; case 0x0000002B: ee_i_vmax(ee); return; case 0x0000002C: ee_i_vsub(ee); return; case 0x0000002D: ee_i_vmsub(ee); return; case 0x0000002E: ee_i_vopmsub(ee); return; case 0x0000002F: ee_i_vmini(ee); return; case 0x00000030: ee_i_viadd(ee); return; case 0x00000031: ee_i_visub(ee); return; case 0x00000032: ee_i_viaddi(ee); return; case 0x00000034: ee_i_viand(ee); return; case 0x00000035: ee_i_vior(ee); return; case 0x00000038: ee_i_vcallms(ee); return; case 0x00000039: ee_i_vcallmsr(ee); return; case 0x0000003C: case 0x0000003D: case 0x0000003E: case 0x0000003F: { uint32_t func = (ee->opcode & 3) | ((ee->opcode & 0x7c0) >> 4); switch (func) { case 0x00000000: ee_i_vaddax(ee); return; case 0x00000001: ee_i_vadday(ee); return; case 0x00000002: ee_i_vaddaz(ee); return; case 0x00000003: ee_i_vaddaw(ee); return; case 0x00000004: ee_i_vsubax(ee); return; case 0x00000005: ee_i_vsubay(ee); return; case 0x00000006: ee_i_vsubaz(ee); return; case 0x00000007: ee_i_vsubaw(ee); return; case 0x00000008: ee_i_vmaddax(ee); return; case 0x00000009: ee_i_vmadday(ee); return; case 0x0000000A: ee_i_vmaddaz(ee); return; case 0x0000000B: ee_i_vmaddaw(ee); return; case 0x0000000C: ee_i_vmsubax(ee); return; case 0x0000000D: ee_i_vmsubay(ee); return; case 0x0000000E: ee_i_vmsubaz(ee); return; case 0x0000000F: ee_i_vmsubaw(ee); return; case 0x00000010: ee_i_vitof0(ee); return; case 0x00000011: ee_i_vitof4(ee); return; case 0x00000012: ee_i_vitof12(ee); return; case 0x00000013: ee_i_vitof15(ee); return; case 0x00000014: ee_i_vftoi0(ee); return; case 0x00000015: ee_i_vftoi4(ee); return; case 0x00000016: ee_i_vftoi12(ee); return; case 0x00000017: ee_i_vftoi15(ee); return; case 0x00000018: ee_i_vmulax(ee); return; case 0x00000019: ee_i_vmulay(ee); return; case 0x0000001A: ee_i_vmulaz(ee); return; case 0x0000001B: ee_i_vmulaw(ee); return; case 0x0000001C: ee_i_vmulaq(ee); return; case 0x0000001D: ee_i_vabs(ee); return; case 0x0000001E: ee_i_vmulai(ee); return; case 0x0000001F: ee_i_vclipw(ee); return; case 0x00000020: ee_i_vaddaq(ee); return; case 0x00000021: ee_i_vmaddaq(ee); return; case 0x00000022: ee_i_vaddai(ee); return; case 0x00000023: ee_i_vmaddai(ee); return; case 0x00000024: ee_i_vsubaq(ee); return; case 0x00000025: ee_i_vmsubaq(ee); return; case 0x00000026: ee_i_vsubai(ee); return; case 0x00000027: ee_i_vmsubai(ee); return; case 0x00000028: ee_i_vadda(ee); return; case 0x00000029: ee_i_vmadda(ee); return; case 0x0000002A: ee_i_vmula(ee); return; case 0x0000002C: ee_i_vsuba(ee); return; case 0x0000002D: ee_i_vmsuba(ee); return; case 0x0000002E: ee_i_vopmula(ee); return; case 0x0000002F: ee_i_vnop(ee); return; case 0x00000030: ee_i_vmove(ee); return; case 0x00000031: ee_i_vmr32(ee); return; case 0x00000034: ee_i_vlqi(ee); return; case 0x00000035: ee_i_vsqi(ee); return; case 0x00000036: ee_i_vlqd(ee); return; case 0x00000037: ee_i_vsqd(ee); return; case 0x00000038: ee_i_vdiv(ee); return; case 0x00000039: ee_i_vsqrt(ee); return; case 0x0000003A: ee_i_vrsqrt(ee); return; case 0x0000003B: ee_i_vwaitq(ee); return; case 0x0000003C: ee_i_vmtir(ee); return; case 0x0000003D: ee_i_vmfir(ee); return; case 0x0000003E: ee_i_vilwr(ee); return; case 0x0000003F: ee_i_viswr(ee); return; case 0x00000040: ee_i_vrnext(ee); return; case 0x00000041: ee_i_vrget(ee); return; case 0x00000042: ee_i_vrinit(ee); return; case 0x00000043: ee_i_vrxor(ee); return; } } break; } } break; } } break; case 0x50000000 >> 26: ee_i_beql(ee); return; case 0x54000000 >> 26: ee_i_bnel(ee); return; case 0x58000000 >> 26: ee_i_blezl(ee); return; case 0x5C000000 >> 26: ee_i_bgtzl(ee); return; case 0x60000000 >> 26: ee_i_daddi(ee); return; case 0x64000000 >> 26: ee_i_daddiu(ee); return; case 0x68000000 >> 26: ee_i_ldl(ee); return; case 0x6C000000 >> 26: ee_i_ldr(ee); return; case 0x70000000 >> 26: { // mmi switch (ee->opcode & 0x0000003F) { case 0x00000000: ee_i_madd(ee); return; case 0x00000001: ee_i_maddu(ee); return; case 0x00000004: ee_i_plzcw(ee); return; case 0x00000008: { switch ((ee->opcode & 0x000007C0) >> 6) { case 0x00000000 >> 6: ee_i_paddw(ee); return; case 0x00000040 >> 6: ee_i_psubw(ee); return; case 0x00000080 >> 6: ee_i_pcgtw(ee); return; case 0x000000C0 >> 6: ee_i_pmaxw(ee); return; case 0x00000100 >> 6: ee_i_paddh(ee); return; case 0x00000140 >> 6: ee_i_psubh(ee); return; case 0x00000180 >> 6: ee_i_pcgth(ee); return; case 0x000001C0 >> 6: ee_i_pmaxh(ee); return; case 0x00000200 >> 6: ee_i_paddb(ee); return; case 0x00000240 >> 6: ee_i_psubb(ee); return; case 0x00000280 >> 6: ee_i_pcgtb(ee); return; case 0x00000400 >> 6: ee_i_paddsw(ee); return; case 0x00000440 >> 6: ee_i_psubsw(ee); return; case 0x00000480 >> 6: ee_i_pextlw(ee); return; case 0x000004C0 >> 6: ee_i_ppacw(ee); return; case 0x00000500 >> 6: ee_i_paddsh(ee); return; case 0x00000540 >> 6: ee_i_psubsh(ee); return; case 0x00000580 >> 6: ee_i_pextlh(ee); return; case 0x000005C0 >> 6: ee_i_ppach(ee); return; case 0x00000600 >> 6: ee_i_paddsb(ee); return; case 0x00000640 >> 6: ee_i_psubsb(ee); return; case 0x00000680 >> 6: ee_i_pextlb(ee); return; case 0x000006C0 >> 6: ee_i_ppacb(ee); return; case 0x00000780 >> 6: ee_i_pext5(ee); return; case 0x000007C0 >> 6: ee_i_ppac5(ee); return; } } break; case 0x00000009: { switch ((ee->opcode & 0x000007C0) >> 6) { case 0x00000000 >> 6: ee_i_pmaddw(ee); return; case 0x00000080 >> 6: ee_i_psllvw(ee); return; case 0x000000C0 >> 6: ee_i_psrlvw(ee); return; case 0x00000100 >> 6: ee_i_pmsubw(ee); return; case 0x00000200 >> 6: ee_i_pmfhi(ee); return; case 0x00000240 >> 6: ee_i_pmflo(ee); return; case 0x00000280 >> 6: ee_i_pinth(ee); return; case 0x00000300 >> 6: ee_i_pmultw(ee); return; case 0x00000340 >> 6: ee_i_pdivw(ee); return; case 0x00000380 >> 6: ee_i_pcpyld(ee); return; case 0x00000400 >> 6: ee_i_pmaddh(ee); return; case 0x00000440 >> 6: ee_i_phmadh(ee); return; case 0x00000480 >> 6: ee_i_pand(ee); return; case 0x000004C0 >> 6: ee_i_pxor(ee); return; case 0x00000500 >> 6: ee_i_pmsubh(ee); return; case 0x00000540 >> 6: ee_i_phmsbh(ee); return; case 0x00000680 >> 6: ee_i_pexeh(ee); return; case 0x000006C0 >> 6: ee_i_prevh(ee); return; case 0x00000700 >> 6: ee_i_pmulth(ee); return; case 0x00000740 >> 6: ee_i_pdivbw(ee); return; case 0x00000780 >> 6: ee_i_pexew(ee); return; case 0x000007C0 >> 6: ee_i_prot3w(ee); return; } } break; case 0x00000010: ee_i_mfhi1(ee); return; case 0x00000011: ee_i_mthi1(ee); return; case 0x00000012: ee_i_mflo1(ee); return; case 0x00000013: ee_i_mtlo1(ee); return; case 0x00000018: ee_i_mult1(ee); return; case 0x00000019: ee_i_multu1(ee); return; case 0x0000001A: ee_i_div1(ee); return; case 0x0000001B: ee_i_divu1(ee); return; case 0x00000020: ee_i_madd1(ee); return; case 0x00000021: ee_i_maddu1(ee); return; case 0x00000028: { switch ((ee->opcode & 0x000007C0) >> 6) { case 0x00000040 >> 6: ee_i_pabsw(ee); return; case 0x00000080 >> 6: ee_i_pceqw(ee); return; case 0x000000C0 >> 6: ee_i_pminw(ee); return; case 0x00000100 >> 6: ee_i_padsbh(ee); return; case 0x00000140 >> 6: ee_i_pabsh(ee); return; case 0x00000180 >> 6: ee_i_pceqh(ee); return; case 0x000001C0 >> 6: ee_i_pminh(ee); return; case 0x00000280 >> 6: ee_i_pceqb(ee); return; case 0x00000400 >> 6: ee_i_padduw(ee); return; case 0x00000440 >> 6: ee_i_psubuw(ee); return; case 0x00000480 >> 6: ee_i_pextuw(ee); return; case 0x00000500 >> 6: ee_i_padduh(ee); return; case 0x00000540 >> 6: ee_i_psubuh(ee); return; case 0x00000580 >> 6: ee_i_pextuh(ee); return; case 0x00000600 >> 6: ee_i_paddub(ee); return; case 0x00000640 >> 6: ee_i_psubub(ee); return; case 0x00000680 >> 6: ee_i_pextub(ee); return; case 0x000006C0 >> 6: ee_i_qfsrv(ee); return; } } break; case 0x00000029: { switch ((ee->opcode & 0x000007C0) >> 6) { case 0x00000000 >> 6: ee_i_pmadduw(ee); return; case 0x000000C0 >> 6: ee_i_psravw(ee); return; case 0x00000200 >> 6: ee_i_pmthi(ee); return; case 0x00000240 >> 6: ee_i_pmtlo(ee); return; case 0x00000280 >> 6: ee_i_pinteh(ee); return; case 0x00000300 >> 6: ee_i_pmultuw(ee); return; case 0x00000340 >> 6: ee_i_pdivuw(ee); return; case 0x00000380 >> 6: ee_i_pcpyud(ee); return; case 0x00000480 >> 6: ee_i_por(ee); return; case 0x000004C0 >> 6: ee_i_pnor(ee); return; case 0x00000680 >> 6: ee_i_pexch(ee); return; case 0x000006C0 >> 6: ee_i_pcpyh(ee); return; case 0x00000780 >> 6: ee_i_pexcw(ee); return; } } break; case 0x00000030: { switch ((ee->opcode & 0x000007C0) >> 6) { case 0x00000000 >> 6: ee_i_pmfhllw(ee); return; case 0x00000040 >> 6: ee_i_pmfhluw(ee); return; case 0x00000080 >> 6: ee_i_pmfhlslw(ee); return; case 0x000000c0 >> 6: ee_i_pmfhllh(ee); return; case 0x00000100 >> 6: ee_i_pmfhlsh(ee); return; } } break; case 0x00000031: ee_i_pmthl(ee); return; case 0x00000034: ee_i_psllh(ee); return; case 0x00000036: ee_i_psrlh(ee); return; case 0x00000037: ee_i_psrah(ee); return; case 0x0000003C: ee_i_psllw(ee); return; case 0x0000003E: ee_i_psrlw(ee); return; case 0x0000003F: ee_i_psraw(ee); return; } } break; case 0x78000000 >> 26: ee_i_lq(ee); return; case 0x7C000000 >> 26: ee_i_sq(ee); return; case 0x80000000 >> 26: ee_i_lb(ee); return; case 0x84000000 >> 26: ee_i_lh(ee); return; case 0x88000000 >> 26: ee_i_lwl(ee); return; case 0x8C000000 >> 26: ee_i_lw(ee); return; case 0x90000000 >> 26: ee_i_lbu(ee); return; case 0x94000000 >> 26: ee_i_lhu(ee); return; case 0x98000000 >> 26: ee_i_lwr(ee); return; case 0x9C000000 >> 26: ee_i_lwu(ee); return; case 0xA0000000 >> 26: ee_i_sb(ee); return; case 0xA4000000 >> 26: ee_i_sh(ee); return; case 0xA8000000 >> 26: ee_i_swl(ee); return; case 0xAC000000 >> 26: ee_i_sw(ee); return; case 0xB0000000 >> 26: ee_i_sdl(ee); return; case 0xB4000000 >> 26: ee_i_sdr(ee); return; case 0xB8000000 >> 26: ee_i_swr(ee); return; case 0xBC000000 >> 26: ee_i_cache(ee); return; case 0xC4000000 >> 26: ee_i_lwc1(ee); return; case 0xCC000000 >> 26: ee_i_pref(ee); return; case 0xD8000000 >> 26: ee_i_lqc2(ee); return; case 0xDC000000 >> 26: ee_i_ld(ee); return; case 0xE4000000 >> 26: ee_i_swc1(ee); return; case 0xF8000000 >> 26: ee_i_sqc2(ee); return; case 0xFC000000 >> 26: ee_i_sd(ee); return; } printf("ee: Invalid instruction %08x @ pc=%08x (cyc=%ld)\n", ee->opcode, ee->prev_pc, ee->total_cycles); exit(1); } int loop = 0; void ee_cycle(struct ee_state* ee) { ee->delay_slot = ee->branch; ee->branch = 0; // Would check for interrupts here, but we do this outside of the core // to reduce overhead ee_check_irq(ee); // if (ee->pc == 0x160700) { // p = 5000; // file = fopen("eegs.dump", "w"); // } // if (ee->pc == 0x1c14bc) { // p = 100; // printf("got here\n"); // } // if (ee->pc == 0x1c1200) { // file = fopen("eegs.dump", "wb"); // p = 100; // } // if (ee->pc == 0x17e240) { // file = fopen("eegs.dump", "wb"); // p = 5000; // } // if (ee->pc == 0x17e2e4) { // if (ee->r[17].ul32 == 0x10000) { // file = fopen("eegs.dump", "w"); // p = 5000; // } // /// printf("s1=%08x s4=%08x\n", ee->r[17].ul32, ee->r[20].ul32); // } // if (ee->pc == 0x1000d8) { // p = 200000; // file = fopen("eegs.dump", "wb"); // } // // //if (ee->pc == 0x9fc411d8) { // if (ee->pc == 0x9fc41430) { // printf("ee: out\n"); // fclose(file); // exit(1); // // p = 510000; // } // vxprintf // if (ee->pc == 0x00106a20) { // if (loop == 1) { // printf("logging\n"); // file = fopen("eegs.dump", "w"); // p = 5000; // } else { // ++loop; // } // } // loop // if (ee->pc == 0x0010907c) { // if (p) { // if (file) fclose(file); // exit(1); // } // } ee->prev_pc = ee->pc; ee->opcode = bus_read32(ee, ee->pc); // if (p) { // // fwrite(&ee->pc, 4, 1, file); // // fwrite(&ee->opcode, 4, 1, file); // fprintf(file, "%08x %08x ", ee->pc, ee->opcode); // for (int i = 1; i < 32; i++) { // // fwrite(&ee->r[i].ul32, 4, 1, file); // fprintf(file, "%08x ", ee->r[i].ul32); // } // fputc('\n', file); // --p; // if (!p) { // if (file) fclose(file); // exit(1); // } // } // if (ee->total_cycles >= 250576836) { // ee_print_disassembly(ee); // } // if (p) { // ee_print_disassembly(ee); // --p; // if (!p) { // exit(1); // } // } ee->pc = ee->next_pc; ee->next_pc += 4; ee_execute(ee); ++ee->total_cycles; ++ee->count; // if (ee->count == ee->compare) // ee->cause |= EE_CAUSE_IP7; ee->r[0].u64[0] = 0; ee->r[0].u64[1] = 0; } void ee_reset(struct ee_state* ee) { for (int i = 0; i < 32; i++) ee->r[i] = (uint128_t){ .u64[0] = 0, .u64[1] = 0 }; for (int i = 0; i < 32; i++) ee->f[i].u32 = 0; for (int i = 0; i < 32; i++) ee->cop0_r[i] = 0; ee->a.u32 = 0; ee->hi = (uint128_t){ .u64[0] = 0, .u64[1] = 0 }; ee->lo = (uint128_t){ .u64[0] = 0, .u64[1] = 0 }; ee->pc = 0xbfc00000; ee->next_pc = ee->pc + 4; ee->opcode = 0; ee->sa = 0; ee->branch = 0; ee->branch_taken = 0; ee->delay_slot = 0; ee->prid = 0x2e20; ee->pc = EE_VEC_RESET; ee->next_pc = ee->pc + 4; fesetround(FE_TOWARDZERO); ee->fcr = 0x01000001; } void ee_destroy(struct ee_state* ee) { ps2_ram_destroy(ee->scratchpad); free(ee); } void ee_run_block(struct ee_state* ee, int cycles) { ee->delay_slot = ee->branch; ee_check_irq(ee); for (int i = 0; i < cycles; i++) { ee->opcode = bus_read32(ee, ee->pc); ee->branch = 0; ee->pc = ee->next_pc; ee->next_pc += 4; ee_execute(ee); ++ee->count; ee->r[0].u64[0] = 0; ee->r[0].u64[1] = 0; } } ================================================ FILE: src/ee/ee_uncached.h ================================================ #ifndef EE_UNCACHED_H #define EE_UNCACHED_H #ifdef __cplusplus extern "C" { #endif #include #include "shared/ram.h" #include "u128.h" #include "vu.h" struct ee_bus_s { void* udata; uint64_t (*read8)(void* udata, uint32_t addr); uint64_t (*read16)(void* udata, uint32_t addr); uint64_t (*read32)(void* udata, uint32_t addr); uint64_t (*read64)(void* udata, uint32_t addr); uint128_t (*read128)(void* udata, uint32_t addr); void (*write8)(void* udata, uint32_t addr, uint64_t data); void (*write16)(void* udata, uint32_t addr, uint64_t data); void (*write32)(void* udata, uint32_t addr, uint64_t data); void (*write64)(void* udata, uint32_t addr, uint64_t data); void (*write128)(void* udata, uint32_t addr, uint128_t data); }; #define EE_SR_CU 0xf0000000 #define EE_SR_DEV 0x08000000 #define EE_SR_BEV 0x04000000 #define EE_SR_CH 0x00040000 #define EE_SR_EDI 0x00020000 #define EE_SR_EIE 0x00010000 #define EE_SR_IM7 0x00008000 #define EE_SR_BEM 0x00001000 #define EE_SR_IM3 0x00000800 #define EE_SR_IM2 0x00000400 #define EE_SR_KSU 0x00000018 #define EE_SR_ERL 0x00000004 #define EE_SR_EXL 0x00000002 #define EE_SR_IE 0x00000001 #define EE_CAUSE_BD 0x80000000 #define EE_CAUSE_BD2 0x40000000 #define EE_CAUSE_CE 0x30000000 #define EE_CAUSE_EXC2 0x00070000 #define EE_CAUSE_IP7 0x00008000 #define EE_CAUSE_IP3 0x00000800 #define EE_CAUSE_IP2 0x00000400 #define EE_CAUSE_EXC 0x0000007c #define CAUSE_EXC1_INT (0 << 2) #define CAUSE_EXC1_MOD (1 << 2) #define CAUSE_EXC1_TLBL (2 << 2) #define CAUSE_EXC1_TLBS (3 << 2) #define CAUSE_EXC1_ADEL (4 << 2) #define CAUSE_EXC1_ADES (5 << 2) #define CAUSE_EXC1_IBE (6 << 2) #define CAUSE_EXC1_DBE (7 << 2) #define CAUSE_EXC1_SYS (8 << 2) #define CAUSE_EXC1_BP (9 << 2) #define CAUSE_EXC1_RI (10 << 2) #define CAUSE_EXC1_CPU (11 << 2) #define CAUSE_EXC1_OV (12 << 2) #define CAUSE_EXC1_TR (13 << 2) #define CAUSE_EXC2_RES (0 << 16) #define CAUSE_EXC2_NMI (1 << 16) #define CAUSE_EXC2_PERFC (2 << 16) #define CAUSE_EXC2_DBG (3 << 16) #define EE_VEC_RESET 0xbfc00000 #define EE_VEC_TLBR 0x00000000 #define EE_VEC_COUNTER 0x00000080 #define EE_VEC_DEBUG 0x00000100 #define EE_VEC_COMMON 0x00000180 #define EE_VEC_IRQ 0x00000200 #define FPU_FLG_C 0x00800000 #define FPU_FLG_I 0x00020000 #define FPU_FLG_D 0x00010000 #define FPU_FLG_O 0x00008000 #define FPU_FLG_U 0x00004000 #define FPU_FLG_SI 0x00000040 #define FPU_FLG_SD 0x00000020 #define FPU_FLG_SO 0x00000010 #define FPU_FLG_SU 0x00000008 /* 1 V0 - Even page valid. When not set, the memory referenced in this entry is not mapped. 2 D0 - Even page dirty. When not set, writes cause an exception. 3-5 C0 - Even page cache mode. 2=Uncached 3=Cached 7=Uncached accelerated 6-25 PFN0 - Even page frame number. 33 V1 - Odd page valid. 34 D1 - Odd page dirty. 35 C1 - Odd page cache mode. 38-57 PFN1 - Odd page frame number. 63 S - Scratchpad. When set, the virtual mapping goes to scratchpad instead of main memory. 64-71 ASID - Address Space ID. 76 G - Global. When set, ASID is ignored. 77-95 VPN2 - Virtual page number / 2. Even pages have a VPN of (VPN2 * 2) and odd pages have a VPN of (VPN2 * 2) + 1 109-120 MASK - Size of an even/odd page. */ struct ee_vtlb_entry { int v0; int d0; int c0; int pfn0; int v1; int d1; int c1; int pfn1; int s; int asid; int g; int vpn2; int mask; }; union ee_fpu_reg { float f; uint32_t u32; int32_t s32; }; #ifdef _EE_USE_INTRINSICS #ifdef _MSC_VER #define EE_ALIGNED16 __declspec(align(16)) #else #define EE_ALIGNED16 __attribute__((aligned(16))) #endif #else #define EE_ALIGNED16 #endif struct ee_state { struct ee_bus_s bus; uint128_t r[32] EE_ALIGNED16; uint128_t hi EE_ALIGNED16; uint128_t lo EE_ALIGNED16; uint64_t total_cycles; uint32_t prev_pc; uint32_t pc; uint32_t next_pc; uint32_t opcode; uint64_t sa; int branch, branch_taken, delay_slot; struct ps2_ram* scratchpad; int cpcond0; union { uint32_t cop0_r[32]; struct { uint32_t index; uint32_t random; uint32_t entrylo0; uint32_t entrylo1; uint32_t context; uint32_t pagemask; uint32_t wired; uint32_t unused7; uint32_t badvaddr; uint32_t count; uint32_t entryhi; uint32_t compare; uint32_t status; uint32_t cause; uint32_t epc; uint32_t prid; uint32_t config; uint32_t unused16; uint32_t unused17; uint32_t unused18; uint32_t unused19; uint32_t unused20; uint32_t unused21; uint32_t badpaddr; uint32_t debug; uint32_t perf; uint32_t unused25; uint32_t unused26; uint32_t taglo; uint32_t taghi; uint32_t errorepc; uint32_t unused30; uint32_t unused31; }; }; union ee_fpu_reg f[32]; union ee_fpu_reg a; uint32_t fcr; struct vu_state* vu0; struct vu_state* vu1; struct ee_vtlb_entry vtlb[48]; }; struct ee_state* ee_create(void); void ee_init(struct ee_state* ee, struct vu_state* vu0, struct vu_state* vu1, struct ee_bus_s bus); void ee_cycle(struct ee_state* ee); void ee_reset(struct ee_state* ee); void ee_destroy(struct ee_state* ee); void ee_set_int0(struct ee_state* ee, int v); void ee_set_int1(struct ee_state* ee, int v); void ee_set_cpcond0(struct ee_state* ee, int v); #undef EE_ALIGNED16 #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/gif.c ================================================ #include #include #include #include "gif.h" // Burnout games need the FQC field on GIF_STAT to change on // GIF DMA transfers, otherwise they'll hang on the initial // loading screen. // #define printf(fmt, ...)(0) static inline const char* gif_get_reg_name(uint8_t r) { switch (r) { case 0x00: return "PRIM"; case 0x01: return "RGBAQ"; case 0x02: return "ST"; case 0x03: return "UV"; case 0x04: return "XYZF2"; case 0x05: return "XYZ2"; case 0x06: return "TEX0_1"; case 0x07: return "TEX0_2"; case 0x08: return "CLAMP_1"; case 0x09: return "CLAMP_2"; case 0x0A: return "FOG"; case 0x0C: return "XYZF3"; case 0x0D: return "XYZ3"; case 0x14: return "TEX1_1"; case 0x15: return "TEX1_2"; case 0x16: return "TEX2_1"; case 0x17: return "TEX2_2"; case 0x18: return "XYOFFSET_1"; case 0x19: return "XYOFFSET_2"; case 0x1A: return "PRMODECONT"; case 0x1B: return "PRMODE"; case 0x1C: return "TEXCLUT"; case 0x22: return "SCANMSK"; case 0x34: return "MIPTBP1_1"; case 0x35: return "MIPTBP1_2"; case 0x36: return "MIPTBP2_1"; case 0x37: return "MIPTBP2_2"; case 0x3B: return "TEXA"; case 0x3D: return "FOGCOL"; case 0x3F: return "TEXFLUSH"; case 0x40: return "SCISSOR_1"; case 0x41: return "SCISSOR_2"; case 0x42: return "ALPHA_1"; case 0x43: return "ALPHA_2"; case 0x44: return "DIMX"; case 0x45: return "DTHE"; case 0x46: return "COLCLAMP"; case 0x47: return "TEST_1"; case 0x48: return "TEST_2"; case 0x49: return "PABE"; case 0x4A: return "FBA_1"; case 0x4B: return "FBA_2"; case 0x4C: return "FRAME_1"; case 0x4D: return "FRAME_2"; case 0x4E: return "ZBUF_1"; case 0x4F: return "ZBUF_2"; case 0x50: return "BITBLTBUF"; case 0x51: return "TRXPOS"; case 0x52: return "TRXREG"; case 0x53: return "TRXDIR"; case 0x54: return "HWREG"; case 0x60: return "SIGNAL"; case 0x61: return "FINISH"; case 0x62: return "LABEL"; } return ""; } struct ps2_gif* ps2_gif_create(void) { return malloc(sizeof(struct ps2_gif)); } void ps2_gif_init(struct ps2_gif* gif, struct vu_state* vu1, struct ps2_gs* gs) { memset(gif, 0, sizeof(struct ps2_gif)); gif->gs = gs; gif->vu1 = vu1; // A queue for each PATH for (int i = 0; i < 3; i++) { gif->queue[i] = queue_create(); queue_init(gif->queue[i]); } } void ps2_gif_reset(struct ps2_gif* gif) { gif->ctrl = 0; gif->mode = 0; gif->stat = 0; gif->tag0 = 0; gif->tag1 = 0; gif->tag2 = 0; gif->tag3 = 0; gif->cnt = 0; gif->p3cnt = 0; gif->p3tag = 0; gif->state = 0; gif->q = 0; memset(&gif->tag, 0, sizeof(struct gif_tag)); for (int i = 0; i < 3; i++) queue_clear(gif->queue[i]); } void ps2_gif_destroy(struct ps2_gif* gif) { for (int i = 0; i < 3; i++) queue_destroy(gif->queue[i]); free(gif); } uint64_t ps2_gif_read32(struct ps2_gif* gif, uint32_t addr) { switch (addr) { case 0x10003020: { // Clear FQC when reading GIF_STAT uint32_t v = gif->stat; gif->stat &= ~0x1f000000; return v; } break; case 0x10003040: return gif->tag0; case 0x10003050: return gif->tag1; case 0x10003060: return gif->tag2; case 0x10003070: return gif->tag3; case 0x10003080: return gif->cnt; case 0x10003090: return gif->p3cnt; case 0x100030A0: return gif->p3tag; } return 0; } void ps2_gif_write32(struct ps2_gif* gif, uint32_t addr, uint64_t data) { switch (addr) { case 0x10003000: { if (data & 1) { ps2_gif_reset(gif); } } return; case 0x10003010: { gif->mode = data; } return; } } // void gif_write_rgbaq(struct ps2_gif* gif, uint128_t data) { // uint64_t r = data.u64[0] & 0xff; // uint64_t g = (data.u64[0] >> 32) & 0xff; // uint64_t b = data.u64[1] & 0xff; // uint64_t a = (data.u64[1] >> 32) & 0xff; // uint64_t v = r | (g << 8) | (b << 16) | (a << 24) | (gif->q << 32); // ps2_gs_write_internal(gif->gs, GS_RGBAQ, v); // } // void gif_write_stq(struct ps2_gif* gif, uint128_t data) { // gif->q = data.u64[1] & 0xffffffff; // ps2_gs_write_internal(gif->gs, GS_ST, data.u64[0]); // } // void gif_write_uv(struct ps2_gif* gif, uint128_t data) { // ps2_gs_write_internal(gif->gs, GS_UV, (data.u64[0] & 0x3fff) | (data.u64[0] >> 16)); // } // void gif_write_xyzf23(struct ps2_gif* gif, uint128_t data) { // uint64_t x = data.u64[0] & 0xffff; // uint64_t y = (data.u64[0] >> 32) & 0xffff; // uint64_t z = (data.u64[1] >> 4) & 0xffffff; // uint64_t f = (data.u64[1] >> 36) & 0xff; // uint64_t v = x | (y << 16) | (z << 32) | (f << 56); // uint64_t adc = data.u64[1] & 0x800000000000ul; // ps2_gs_write_internal(gif->gs, adc ? GS_XYZF3 : GS_XYZF2, v); // } // void gif_write_xyz23(struct ps2_gif* gif, uint128_t data) { // uint64_t x = data.u64[0] & 0xffff; // uint64_t y = (data.u64[0] >> 32) & 0xffff; // uint64_t z = data.u64[1] & 0xffffffff; // uint64_t v = x | (y << 16) | (z << 32); // uint64_t adc = data.u64[1] & 0x800000000000ul; // ps2_gs_write_internal(gif->gs, adc ? GS_XYZ3 : GS_XYZ2, v); // } // void gif_write_fog(struct ps2_gif* gif, uint128_t data) { // ps2_gs_write_internal(gif->gs, GS_FOG, data.u64[1] << 20); // } void gif_handle_tag(struct ps2_gif* gif, uint128_t data) { // 1.0f gif->q = 0x3f800000; gif->tag.nloop = data.u64[0] & 0x7fff; gif->tag.prim = (data.u64[0] >> 47) & 0x3ff; gif->tag.eop = !!(data.u64[0] & 0x8000); gif->tag.pre = !!(data.u64[0] & 0x400000000000ull); gif->tag.fmt = (data.u64[0] >> 58) & 3; gif->tag.nregs = (data.u64[0] >> 60) & 0xf; gif->tag.reg = data.u64[1]; gif->tag.index = 0; if (gif->tag.nregs == 0) gif->tag.nregs = 16; switch (gif->tag.fmt) { case 0: { gif->tag.remaining = gif->tag.nregs * gif->tag.nloop; gif->tag.qwc = gif->tag.nloop * gif->tag.nregs; } break; case 1: { gif->tag.remaining = gif->tag.nregs * gif->tag.nloop; gif->tag.qwc = (gif->tag.nloop * gif->tag.nregs + 1) / 2; } break; case 2: case 3: { gif->tag.remaining = gif->tag.nloop; gif->tag.qwc = gif->tag.nloop; } break; } // fprintf(stdout, "giftag: nloop=%04lx eop=%d prim=%04x (pre=%d) fmt=%d nregs=%d reg=%08x%08x size=%d\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 // ); // if (gif->tag.pre) { // ps2_gs_write_internal(gif->gs, GS_PRIM, gif->tag.prim); // } if (gif->tag.remaining) { gif->state = GIF_STATE_PROCESSING; } } // void gif_handle_packed(struct ps2_gif* gif, uint128_t data) { // int index = (gif->tag.index++) % gif->tag.nregs; // int r = (gif->tag.reg >> (index * 4)) & 0xf; // switch (r) { // case 0x00: /* printf("gif: PRIM <- %016lx\n", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_PRIM, data.u64[0] & 0x3ff); break; // case 0x01: /* printf("gif: RGBAQ <- %016lx\n", data.u64[0]); */ gif_write_rgbaq(gif, data); break; // case 0x02: /* printf("gif: STQ <- %016lx\n", data.u64[0]); */ gif_write_stq(gif, data); break; // case 0x03: /* printf("gif: UV <- %016lx\n", data.u64[0]); */ gif_write_uv(gif, data); break; // 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; // case 0x05: /* printf("gif: XYZ23 <- %016lx\n", data.u64[0]); */ gif_write_xyz23(gif, data); break; // case 0x06: /* printf("gif: TEX0_1 <- %016lx\n", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_TEX0_1, data.u64[0]); break; // case 0x07: /* printf("gif: TEX0_2 <- %016lx\n", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_TEX0_2, data.u64[0]); break; // case 0x08: /* printf("gif: CLAMP_1 <- %016lx\n", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_CLAMP_1, data.u64[0]); break; // case 0x09: /* printf("gif: CLAMP_2 <- %016lx\n", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_CLAMP_2, data.u64[0]); break; // case 0x0a: /* printf("gif: FOG <- %016lx\n", data.u64[0]); */ gif_write_fog(gif, data); break; // case 0x0c: /* printf("gif: XYZF3 <- %016lx\n", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_XYZF3, data.u64[0]); break; // case 0x0d: /* printf("gif: XYZ3 <- %016lx\n", data.u64[0]); */ ps2_gs_write_internal(gif->gs, GS_XYZ3, data.u64[0]); break; // // A+D // case 0x0e: { // // printf("gif: write %s (A+D)\n", gif_get_reg_name(data.u64[1])); // ps2_gs_write_internal(gif->gs, data.u64[1], data.u64[0]); // } break; // // NOP // case 0x0f: break; // default: /* printf("gif: PACKED format for reg %d unimplemented\n", r); exit(1); */ break; // } // gif->tag.qwc--; // if (gif->tag.qwc == 0) { // gif->state = GIF_STATE_RECV_TAG; // return; // } // } // void gif_handle_reglist(struct ps2_gif* gif, uint128_t data) { // for (int i = 0; i < 2; i++) { // int index = (gif->tag.index++) % gif->tag.nregs; // int r = (gif->tag.reg >> (index * 4)) & 0xf; // switch (r) { // case 0x00: ps2_gs_write_internal(gif->gs, GS_PRIM, data.u64[i]); break; // case 0x01: ps2_gs_write_internal(gif->gs, GS_RGBAQ, data.u64[i]); break; // case 0x02: ps2_gs_write_internal(gif->gs, GS_ST, data.u64[i]); break; // case 0x03: ps2_gs_write_internal(gif->gs, GS_UV, data.u64[i]); break; // case 0x04: ps2_gs_write_internal(gif->gs, GS_XYZF2, data.u64[i]); break; // case 0x05: ps2_gs_write_internal(gif->gs, GS_XYZ2, data.u64[i]); break; // case 0x06: ps2_gs_write_internal(gif->gs, GS_TEX0_1, data.u64[i]); break; // case 0x07: ps2_gs_write_internal(gif->gs, GS_TEX0_2, data.u64[i]); break; // case 0x08: ps2_gs_write_internal(gif->gs, GS_CLAMP_1, data.u64[i]); break; // case 0x09: ps2_gs_write_internal(gif->gs, GS_CLAMP_2, data.u64[i]); break; // case 0x0a: ps2_gs_write_internal(gif->gs, GS_FOG, data.u64[i]); break; // case 0x0c: ps2_gs_write_internal(gif->gs, GS_XYZF3, data.u64[i]); break; // case 0x0d: ps2_gs_write_internal(gif->gs, GS_XYZ3, data.u64[i]); break; // // A+D // // NOP // case 0x0e: // case 0x0f: break; // // default: printf("gif: REGLIST format for reg %d unimplemented\n", r); break; // } // // Note: This handles odd NREGS*NLOOP case // if (gif->tag.index == gif->tag.remaining) // break; // } // gif->tag.qwc--; // if (gif->tag.qwc == 0) { // gif->state = GIF_STATE_RECV_TAG; // return; // } // } // void gif_handle_image(struct ps2_gif* gif, uint128_t data) { // ps2_gs_write_internal(gif->gs, GS_HWREG, data.u64[0]); // ps2_gs_write_internal(gif->gs, GS_HWREG, data.u64[1]); // gif->tag.qwc--; // if (gif->tag.qwc == 0) { // gif->state = GIF_STATE_RECV_TAG; // } // } void ps2_gif_write128(struct ps2_gif* gif, uint32_t addr, uint128_t data) { ps2_gif_fifo_write(gif, data, GIF_PATH3); } void ps2_gif_fifo_write(struct ps2_gif* gif, uint128_t data, int path) { // Set FQC when getting GIF FIFO writes gif->stat |= 0x1f000000; if (gif->state == GIF_STATE_RECV_TAG) { for (int i = 0; i < 4; i++) queue_push(gif->queue[path], data.u32[i]); gif_handle_tag(gif, data); return; } if (gif->tag.qwc) { struct queue_state* queue = gif->queue[path]; for (int i = 0; i < 4; i++) queue_push(queue, data.u32[i]); gif->tag.qwc--; if (!gif->tag.qwc) { gif->state = GIF_STATE_RECV_TAG; if (gif->transfer) gif->transfer(gif->udata, path, queue->buf, queue->size * sizeof(uint32_t)); queue_clear(queue); } } } void ps2_gif_set_backend(struct ps2_gif* gif, void* udata, void (*func)(void*, int, const void*, size_t)) { gif->udata = udata; gif->transfer = func; } #undef printf ================================================ FILE: src/ee/gif.h ================================================ #ifndef GIF_H #define GIF_H #ifdef __cplusplus extern "C" { #endif #include #include "gs/gs.h" #include "u128.h" #include "queue.h" #include "vu.h" #define GIF_STATE_RECV_TAG 0 #define GIF_STATE_PROCESSING 1 #define GIF_PATH1 0 #define GIF_PATH2 1 #define GIF_PATH3 2 struct gif_tag { uint64_t nloop; uint32_t prim; int eop; int pre; int fmt; int nregs; uint64_t reg; uint64_t qwc; int index; int remaining; }; struct ps2_gif { uint64_t ctrl; uint64_t mode; uint64_t stat; uint64_t tag0; uint64_t tag1; uint64_t tag2; uint64_t tag3; uint64_t cnt; uint64_t p3cnt; uint64_t p3tag; // Renderer state void* udata; void (*transfer)(void*, int, const void*, size_t); struct queue_state* queue[3]; struct ps2_gs* gs; struct vu_state* vu1; int state; struct gif_tag tag; // From ST(Q) to RGBA(Q) uint64_t q; }; struct ps2_gif* ps2_gif_create(void); void ps2_gif_init(struct ps2_gif* gif, struct vu_state* vu1, struct ps2_gs* gs); void ps2_gif_reset(struct ps2_gif* gif); void ps2_gif_destroy(struct ps2_gif* gif); uint64_t ps2_gif_read32(struct ps2_gif* gif, uint32_t addr); void ps2_gif_write32(struct ps2_gif* gif, uint32_t addr, uint64_t data); void ps2_gif_write128(struct ps2_gif* gif, uint32_t addr, uint128_t data); void ps2_gif_fifo_write(struct ps2_gif* gif, uint128_t data, int path); void ps2_gif_set_backend(struct ps2_gif* gif, void* udata, void (*func)(void*, int, const void*, size_t)); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/intc.c ================================================ #include #include #include #include #include "intc.h" static inline void intc_check_irq(struct ps2_intc* intc) { ee_set_int0(intc->ee, intc->stat & intc->mask); } struct ps2_intc* ps2_intc_create(void) { return malloc(sizeof(struct ps2_intc)); } void ps2_intc_init(struct ps2_intc* intc, struct ee_state* ee, struct sched_state* sched) { memset(intc, 0, sizeof(struct ps2_intc)); intc->ee = ee; intc->sched = sched; } void ps2_intc_destroy(struct ps2_intc* intc) { free(intc); } uint64_t ps2_intc_read32(struct ps2_intc* intc, uint32_t addr) { switch (addr) { case 0x1000f000: return intc->stat; case 0x1000f010: return intc->mask; default: printf("intc: Unhandled INTC read %08x\n", addr); exit(1); } return 0; } void ps2_intc_write8(struct ps2_intc* intc, uint32_t addr, uint64_t data) { printf("intc: write8 %04lx\n", data); exit(1); switch (addr) { case 0x1000f000: intc->stat &= ~data; break; case 0x1000f010: intc->mask ^= data; break; default: printf("intc: Unhandled INTC write %08x %08lx\n", addr, data); exit(1); } intc_check_irq(intc); } void ps2_intc_write16(struct ps2_intc* intc, uint32_t addr, uint64_t data) { printf("intc: write16 %04lx\n", data); exit(1); switch (addr) { case 0x1000f000: intc->stat &= ~data; break; case 0x1000f010: intc->mask ^= data; break; default: printf("intc: Unhandled INTC write %08x %08lx\n", addr, data); exit(1); } intc_check_irq(intc); } void intc_check_irq_event(void* udata, int overshoot) { struct ps2_intc* intc = (struct ps2_intc*)udata; intc_check_irq(intc); } void ps2_intc_write32(struct ps2_intc* intc, uint32_t addr, uint64_t data) { switch (addr) { case 0x1000f000: intc->stat &= ~data; break; case 0x1000f010: intc->mask ^= data; break; default: printf("intc: Unhandled INTC write %08x %08lx\n", addr, data); exit(1); } struct sched_event event; event.callback = intc_check_irq_event; event.cycles = 16; event.name = "INTC IRQ check"; event.udata = intc; sched_schedule(intc->sched, event); } void ps2_intc_write64(struct ps2_intc* intc, uint32_t addr, uint64_t data) { printf("intc: write64 %016lx\n", data); exit(1); switch (addr) { case 0x1000f000: intc->stat &= ~data; break; case 0x1000f010: intc->mask ^= data; break; default: printf("intc: Unhandled INTC write %08x %08lx\n", addr, data); exit(1); } intc_check_irq(intc); } void ps2_intc_irq(struct ps2_intc* intc, int dev) { intc->stat |= 1 << dev; static const char* dev_names[] = { "GS", "SBUS", "VBLANK_IN", "VBLANK_OUT", "VIF0", "VIF1", "VU0", "VU1", "IPU", "TIMER0", "TIMER1", "TIMER2", "TIMER3", "SFIFO", "VU0_WD" }; struct sched_event event; event.callback = intc_check_irq_event; event.cycles = 64; event.name = "INTC IRQ check"; event.udata = intc; // Notify EE that an interrupt has occurred ee_reset_intc_reads(intc->ee); sched_schedule(intc->sched, event); } ================================================ FILE: src/ee/intc.h ================================================ struct ps2_intc; #ifndef INTC_H #define INTC_H #ifdef __cplusplus extern "C" { #endif #include #include "u128.h" #include "ee.h" #include "scheduler.h" #define EE_INTC_GS 0 #define EE_INTC_SBUS 1 #define EE_INTC_VBLANK_IN 2 #define EE_INTC_VBLANK_OUT 3 #define EE_INTC_VIF0 4 #define EE_INTC_VIF1 5 #define EE_INTC_VU0 6 #define EE_INTC_VU1 7 #define EE_INTC_IPU 8 #define EE_INTC_TIMER0 9 #define EE_INTC_TIMER1 10 #define EE_INTC_TIMER2 11 #define EE_INTC_TIMER3 12 #define EE_INTC_SFIFO 13 #define EE_INTC_VU0_WD 14 struct ps2_intc { uint32_t stat; uint32_t mask; struct ee_state* ee; struct sched_state* sched; }; struct ps2_intc* ps2_intc_create(void); void ps2_intc_init(struct ps2_intc* intc, struct ee_state* ee, struct sched_state* sched); void ps2_intc_destroy(struct ps2_intc* intc); uint64_t ps2_intc_read32(struct ps2_intc* intc, uint32_t addr); void ps2_intc_write8(struct ps2_intc* intc, uint32_t addr, uint64_t data); void ps2_intc_write16(struct ps2_intc* intc, uint32_t addr, uint64_t data); void ps2_intc_write32(struct ps2_intc* intc, uint32_t addr, uint64_t data); void ps2_intc_write64(struct ps2_intc* intc, uint32_t addr, uint64_t data); void ps2_intc_irq(struct ps2_intc* intc, int dev); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/syscall.h ================================================ static const char* ee_get_syscall(int n) { switch (n) { case 0x01: return "void ResetEE(int reset_flag)"; case 0x02: return "void SetGsCrt(bool interlaced, int display_mode, bool frame)"; case 0x04: return "void Exit(int status)"; case 0x05: return "void _ExceptionEpilogue()"; case 0x06: return "void LoadExecPS2(const char* filename, int argc, char** argv)"; case 0x07: return "void ExecPS2(void* entry, void* gp, int argc, char** argv)"; case 0x10: return "int AddIntcHandler(int int_cause, int (*handler)(int), int next, void* arg, int flag)"; case 0x11: return "int RemoveIntcHandler(int int_cause, int handler_id)"; case 0x12: return "int AddDmacHandler(int dma_cause, int (*handler)(int), int next, void* arg, int flag)"; case 0x13: return "int RemoveDmacHandler(int dma_cause, int handler_id)"; case 0x14: return "bool _EnableIntc(int cause_bit)"; case 0x15: return "bool _DisableIntc(int cause_bit)"; case 0x16: return "bool _EnableDmac(int cause_bit)"; case 0x17: return "bool _DisableDmac(int cause_bit)"; case 0x20: return "int CreateThread(ThreadParam* t)"; case 0x21: return "void DeleteThread(int thread_id)"; case 0x22: return "void StartThread(int thread_id, void* arg)"; case 0x23: return "void ExitThread()"; case 0x24: return "void ExitDeleteThread()"; case 0x25: return "void TerminateThread(int thread_id)"; case 0x26: return "void iTerminateThread(int thread_id)"; case 0x29: return "int ChangeThreadPriority(int thread_id, int priority)"; case 0x2A: return "int iChangeThreadPriority(int thread_id, int priority)"; case 0x2B: return "void RotateThreadReadyQueue(int priority)"; case 0x2C: return "int _iRotateThreadReadyQueue(int priority)"; case 0x2D: return "void ReleaseWaitThread(int thread_id)"; case 0x2E: return "int iReleaseWaitThread(int thread_id)"; case 0x2F: return "int GetThreadId()"; case 0x30: return "int ReferThreadStatus(int thread_id, ThreadParam* status)"; case 0x31: return "int iReferThreadStatus(int thread_id, ThreadParam* status)"; case 0x32: return "void SleepThread()"; case 0x33: return "void WakeupThread(int thread_id)"; case 0x34: return "int iWakeupThread(int thread_id)"; case 0x35: return "int CancelWakeupThread(int thread_id)"; case 0x36: return "int iCancelWakeupThread(int thread_id)"; case 0x37: return "int SuspendThread(int thread_id)"; case 0x38: return "int iSuspendThread(int thread_id)"; case 0x39: return "void ResumeThread(int thread_id)"; case 0x3A: return "int iResumeThread(int thread_id)"; case 0x3B: return "void JoinThread()"; case 0x3C: return "void* InitMainThread(uint32 gp, void* stack, int stack_size, char* args, int root)"; case 0x3D: return "void* InitHeap(void* heap, int heap_size)"; case 0x3E: return "void* EndOfHeap()"; case 0x40: return "int CreateSema(SemaParam* s)"; case 0x41: return "int DeleteSema(int sema_id)"; case 0x42: return "int SignalSema(int sema_id)"; case 0x43: return "int iSignalSema(int sema_id)"; case 0x44: return "void WaitSema(int sema_id)"; case 0x45: return "int PollSema(int sema_id)"; case 0x46: return "int iPollSema(int sema_id)"; case 0x64: return "void FlushCache(int mode)"; case 0x70: return "uint64_t GsGetIMR()"; case 0x71: return "void GsPutIMR(uint64_t value)"; case 0x73: return "void SetVSyncFlag(int* vsync_occurred, u64* csr_stat_on_vsync)"; case 0x74: return "void SetSyscall(int index, int address)"; case 0x76: return "int SifDmaStat(unsigned int dma_id)"; case 0x77: return "unsigned int SifSetDma(SifDmaTransfer* trans, int len)"; case 0x78: return "void SifSetDChain()"; case 0x7B: return "void ExecOSD(int argc, char** argv)"; case 0x7D: return "void PSMode()"; case 0x7E: return "int MachineType()"; case 0x7F: return "int GetMemorySize()"; } return ""; } ================================================ FILE: src/ee/timers.c ================================================ #include #include #include #include "timers.h" #define EE_TIMER_SCHED_QUANTUM 64 static void ee_timers_schedule_next_irq_event(struct ps2_ee_timers* timers); struct ps2_ee_timers* ps2_ee_timers_create(void) { return malloc(sizeof(struct ps2_ee_timers)); } static inline void ee_timers_update_active_mask(struct ps2_ee_timers* timers, int t, int cue) { uint8_t bit = 1u << t; if (cue) { timers->active_mask |= bit; } else { timers->active_mask &= ~bit; } } void ps2_ee_timers_init(struct ps2_ee_timers* timers, struct ps2_intc* intc, struct sched_state* sched) { memset(timers, 0, sizeof(struct ps2_ee_timers)); timers->intc = intc; timers->sched = sched; timers->current_cycle = 0; timers->scheduler_advanced_cycles = 0; timers->irq_event_pending = 0; for (int i = 0; i < 4; i++) { timers->timer[i].id = i; timers->timer[i].last_sync_cycle = 0; } } static inline void ee_timers_update_event(struct ee_timer* t) { uint32_t counter = t->counter & 0xffff; uint32_t compare = t->compare & 0xffff; uint32_t cycles_until_compare; uint32_t cycles_until_overflow = 0x10000 - counter; if (counter < compare) { cycles_until_compare = compare - counter; } else { cycles_until_compare = 0x10000 - counter + compare; } // Compare events are needed for IRQ and for ZRET. if (!(t->cmpe || t->zret)) cycles_until_compare = 0x80000000; if (!t->ovfe) cycles_until_overflow = 0x80000000; t->cycles_until_check = cycles_until_compare < cycles_until_overflow ? cycles_until_compare : cycles_until_overflow; // printf("timer %d: cycles_until_check=%04x counter=%04x compare=%04x until_compare=%04x until_overflow=%04x\n", // t, t->cycles_until_check, t->counter, t->compare, cycles_until_compare, cycles_until_overflow // ); } static inline uint32_t ee_timers_cycles_until_check(struct ee_timer* t) { if (!t->cue || !t->check_enabled) return 0xffffffffu; uint32_t period = t->delta_reload; if (!period) return 0xffffffffu; uint64_t cycles = t->delta; if (t->cycles_until_check > 1) { cycles += (uint64_t)(t->cycles_until_check - 1) * period; } if (!cycles) cycles = 1; if (cycles > 0x7fffffffu) cycles = 0x7fffffffu; return (uint32_t)cycles; } static inline void ee_timers_advance_counter(struct ps2_ee_timers* timers, struct ee_timer* t, int i, uint32_t increments) { if (!increments) return; if (!t->check_enabled) { t->counter = (t->counter + increments) & 0xffff; return; } while (increments) { if (t->cycles_until_check > 1) { uint32_t skip = t->cycles_until_check - 1; if (skip > increments) skip = increments; t->counter = (t->counter + skip) & 0xffff; t->cycles_until_check -= skip; increments -= skip; if (!increments) break; } uint32_t counter = t->counter + 1; uint32_t low_counter = counter & 0xffff; int cmp = low_counter == (t->compare & 0xffff); int ovf = counter == 0x10000; if (!(cmp || ovf)) { fprintf(stderr, "timer %d: error counter=%04x compare=%04x cycles_until-check=%d\n", i, counter, t->compare, t->cycles_until_check); exit(1); } if (cmp) { if (t->cmpe) t->mode |= 0x400; if (t->zret) { counter = 0; } if (t->cmpe) { ps2_intc_irq(timers->intc, EE_INTC_TIMER0 + i); } t->counter = counter; ee_timers_update_event(t); counter = t->counter; } if (counter == 0x10000) { if (t->ovfe) t->mode |= 0x800; if (t->ovfe) { ps2_intc_irq(timers->intc, EE_INTC_TIMER0 + i); } t->counter = counter; ee_timers_update_event(t); counter = t->counter; } t->counter = counter & 0xffff; increments--; } } static inline void ee_timers_sync_timer(struct ps2_ee_timers* timers, struct ee_timer* t, int i) { if (!t->cue) { t->last_sync_cycle = timers->current_cycle; return; } uint64_t cycles_since_sync = timers->current_cycle - t->last_sync_cycle; if (!cycles_since_sync) return; t->last_sync_cycle = timers->current_cycle; if (cycles_since_sync < t->delta) { t->delta -= (uint32_t)cycles_since_sync; return; } cycles_since_sync -= t->delta; uint32_t increments = 1; uint32_t period = t->delta_reload; if (period) { increments += (uint32_t)(cycles_since_sync / period); uint32_t rem = (uint32_t)(cycles_since_sync % period); t->delta = period - rem; if (t->delta == 0) t->delta = period; } else { t->delta = 0; } ee_timers_advance_counter(timers, t, i, increments); } static void ee_timers_irq_event_cb(void* udata, int overshoot) { struct ps2_ee_timers* timers = (struct ps2_ee_timers*)udata; uint64_t elapsed = EE_TIMER_SCHED_QUANTUM; if (timers->sched && timers->sched->offset) elapsed = timers->sched->offset; if (overshoot < 0) elapsed += (uint64_t)(-overshoot); timers->irq_event_pending = 0; timers->current_cycle += elapsed; timers->scheduler_advanced_cycles += elapsed; for (int i = 0; i < 4; i++) { if (timers->timer[i].cue) { ee_timers_sync_timer(timers, &timers->timer[i], i); } } ee_timers_schedule_next_irq_event(timers); } static void ee_timers_schedule_next_irq_event(struct ps2_ee_timers* timers) { if (!timers->sched) return; if (timers->irq_event_pending) return; uint32_t min_cycles = 0xffffffffu; for (int i = 0; i < 4; i++) { if (timers->timer[i].cue) { ee_timers_sync_timer(timers, &timers->timer[i], i); uint32_t wait = ee_timers_cycles_until_check(&timers->timer[i]); if (wait < min_cycles) min_cycles = wait; } } if (min_cycles == 0xffffffffu) return; if (min_cycles > EE_TIMER_SCHED_QUANTUM) min_cycles = EE_TIMER_SCHED_QUANTUM; struct sched_event event; event.name = "EE Timer IRQ"; event.udata = timers; event.callback = ee_timers_irq_event_cb; event.cycles = (long)min_cycles; sched_schedule(timers->sched, event); timers->irq_event_pending = 1; } void ee_timers_write_counter(struct ps2_ee_timers* timers, int t, uint32_t data) { struct ee_timer* timer = &timers->timer[t]; // Sync to current cycle first ee_timers_sync_timer(timers, timer, t); // printf("timer %d: write counter=%04x data=%04x\n", t, timer->counter, data); timer->counter = data; timer->delta = timer->delta_reload; if (timer->check_enabled) { ee_timers_update_event(timer); } ee_timers_schedule_next_irq_event(timers); } void ee_timers_write_compare(struct ps2_ee_timers* timers, int t, uint32_t data) { struct ee_timer* timer = &timers->timer[t]; // Sync to current cycle first ee_timers_sync_timer(timers, timer, t); // printf("timer %d: write counter=%04x data=%04x\n", t, timer->counter, data); if (data < timer->counter) { // printf("timer %d: compare %04x >= counter %04x\n", t, data, timer->counter); // exit(1); } else if (data == timer->counter) { // printf("timer %d: compare %04x == counter %04x\n", t, data, timer->counter); // exit(1); } // timer->cycles_until_check = data - timer->counter; timer->compare = data; if (timer->check_enabled) { ee_timers_update_event(timer); } ee_timers_schedule_next_irq_event(timers); } void ps2_ee_timers_destroy(struct ps2_ee_timers* timers) { free(timers); } static inline void ee_timers_write_mode(struct ps2_ee_timers* timers, int t, uint32_t data) { struct ee_timer* timer = &timers->timer[t]; // Sync to current cycle first before changing mode ee_timers_sync_timer(timers, timer, t); timer->mode &= 0xc00; timer->mode |= data & (~0xc00); timer->mode &= ~(data & 0xc00); timer->clks = data & 3; timer->gate = (data >> 2) & 1; timer->gats = (data >> 3) & 1; timer->gatm = (data >> 4) & 3; timer->zret = (data >> 6) & 1; timer->cue = (data >> 7) & 1; timer->cmpe = (data >> 8) & 1; timer->ovfe = (data >> 9) & 1; ee_timers_update_active_mask(timers, t, timer->cue); if (!timer->cue) { timer->check_enabled = 0; return; } // Reset sync point when timer is enabled timer->last_sync_cycle = timers->current_cycle; if (timer->gate) { printf("timers: Timer %d gate write %08x\n", t, data); // exit(1); } // 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", // t, data, // timer->mode, // timer->counter, // timer->compare, // timer->clks, timer->gate, timer->gats, timer->gatm, // timer->zret, timer->cue, timer->cmpe, timer->ovfe // ); switch (timer->clks) { case 0: timer->delta = 1; break; case 1: timer->delta = 16; break; case 2: timer->delta = 256; break; case 3: timer->delta = 9370; break; } timer->delta_reload = timer->delta; if (timer->cmpe || timer->ovfe || timer->zret) { timer->check_enabled = 1; ee_timers_update_event(timer); } else { timer->check_enabled = 0; } ee_timers_schedule_next_irq_event(timers); } void ps2_ee_timers_tick(struct ps2_ee_timers* timers) { ps2_ee_timers_tick_cycles(timers, 1); } void ps2_ee_timers_tick_cycles(struct ps2_ee_timers* timers, uint32_t cycles) { // Lazy evaluation: just advance the global cycle counter // No timer updates happen until reads/writes or event checks if (timers->active_mask && cycles) { uint64_t step = cycles; if (timers->scheduler_advanced_cycles) { if (timers->scheduler_advanced_cycles >= step) { timers->scheduler_advanced_cycles -= step; step = 0; } else { step -= timers->scheduler_advanced_cycles; timers->scheduler_advanced_cycles = 0; } } timers->current_cycle += step; } } void ps2_ee_timers_write16(struct ps2_ee_timers* timers, uint32_t addr, uint64_t data) { int t = (addr >> 11) & 3; switch (addr & 0xff) { case 0x00: ee_timers_write_counter(timers, t, data & 0xffff); return; case 0x10: ee_timers_write_mode(timers, t, data & 0xffff); return; case 0x20: ee_timers_write_compare(timers, t, data & 0xffff); return; case 0x30: timers->timer[t].hold = data & 0xffff; return; } fprintf(stderr, "ee: timer %d write %08x to %02x\n", t, data, addr & 0xff); exit(1); } void ps2_ee_timers_write32(struct ps2_ee_timers* timers, uint32_t addr, uint64_t data) { int t = (addr >> 11) & 3; // printf("ee: timer %d write %08x to %02x\n", t, data, addr & 0xff); switch (addr & 0xff) { case 0x00: ee_timers_write_counter(timers, t, data & 0xffff); return; case 0x10: ee_timers_write_mode(timers, t, data & 0xffff); return; case 0x20: ee_timers_write_compare(timers, t, data & 0xffff); return; case 0x30: timers->timer[t].hold = data & 0xffff; return; } } uint64_t ps2_ee_timers_read16(struct ps2_ee_timers* timers, uint32_t addr) { int t = (addr >> 11) & 3; // printf("ee: timer %d read %08x\n", t, addr & 0xff); // Sync timer to current cycle before reading ee_timers_sync_timer(timers, &timers->timer[t], t); switch (addr & 0xff) { case 0x00: return timers->timer[t].counter & 0xffff; case 0x10: return timers->timer[t].mode & 0xffff; case 0x20: return timers->timer[t].compare & 0xffff; case 0x30: return timers->timer[t].hold & 0xffff; } fprintf(stderr, "ee: timers read16 %08x\n", addr); exit(1); return 0; } uint64_t ps2_ee_timers_read32(struct ps2_ee_timers* timers, uint32_t addr) { int t = (addr >> 11) & 3; // printf("ee: timer %d read %08x\n", t, addr & 0xff); // Sync timer to current cycle before reading ee_timers_sync_timer(timers, &timers->timer[t], t); switch (addr & 0xff) { case 0x00: return timers->timer[t].counter & 0xffff; case 0x10: return timers->timer[t].mode; case 0x20: return timers->timer[t].compare; case 0x30: return timers->timer[t].hold; } return 0; } ================================================ FILE: src/ee/timers.h ================================================ #ifndef EE_TIMERS_H #define EE_TIMERS_H #ifdef __cplusplus extern "C" { #endif #include #include "scheduler.h" #include "intc.h" struct ee_timer { uint32_t counter; uint16_t mode; uint32_t compare; uint16_t hold; // Internal state int id; uint32_t internal; uint32_t delta; uint32_t delta_reload; uint32_t check_reload; int cycles_until_compare; int cycles_until_overflow; int cycles_until_check; int check_enabled; // Lazy evaluation uint64_t last_sync_cycle; // Mode fields int clks; int gate; int gats; int gatm; int zret; int cue; int cmpe; int ovfe; }; struct ps2_ee_timers { struct ee_timer timer[4]; uint8_t active_mask; // Global cycle tracking for lazy evaluation uint64_t current_cycle; uint64_t scheduler_advanced_cycles; int irq_event_pending; struct ps2_intc* intc; struct sched_state* sched; }; struct ps2_ee_timers* ps2_ee_timers_create(void); void ps2_ee_timers_init(struct ps2_ee_timers* timers, struct ps2_intc* intc, struct sched_state* sched); void ps2_ee_timers_destroy(struct ps2_ee_timers* timers); uint64_t ps2_ee_timers_read16(struct ps2_ee_timers* timers, uint32_t addr); uint64_t ps2_ee_timers_read32(struct ps2_ee_timers* timers, uint32_t addr); void ps2_ee_timers_write32(struct ps2_ee_timers* timers, uint32_t addr, uint64_t data); void ps2_ee_timers_write16(struct ps2_ee_timers* timers, uint32_t addr, uint64_t data); void ps2_ee_timers_tick(struct ps2_ee_timers* timers); void ps2_ee_timers_tick_cycles(struct ps2_ee_timers* timers, uint32_t cycles); void ps2_ee_timers_handle_hblank(struct ps2_ee_timers* timers); void ps2_ee_timers_handle_vblank_in(struct ps2_ee_timers* timers); void ps2_ee_timers_handle_vblank_out(struct ps2_ee_timers* timers); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/vif.c ================================================ #include #include #include #include #include #include "vif.h" #define printf(fmt, ...)(0) struct ps2_vif* ps2_vif_create(void) { return malloc(sizeof(struct ps2_vif)); } void 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) { memset(vif, 0, sizeof(struct ps2_vif)); vif->sched = sched; vif->intc = intc; vif->gif = gif; vif->bus = bus; vif->vu = vu; vif->id = id; } void ps2_vif_destroy(struct ps2_vif* vif) { free(vif); } static inline void vif_write_vu_mem(struct ps2_vif* vif, uint128_t data) { // Process mask if (vif->unpack_mask) { int cycle = (vif->unpack_cycle > 3) ? 3 : vif->unpack_cycle; int m[4], shift = (cycle & 3) * 8; uint32_t mask = (vif->mask >> shift) & 0xff; m[0] = (mask >> 0) & 3; m[1] = (mask >> 2) & 3; m[2] = (mask >> 4) & 3; m[3] = (mask >> 6) & 3; // Note: Mode 3 is undocumented, it sets the row registers // to the value of the unpacked data, without changing // the unpacked data itself. for (int i = 0; i < 4; i++) { if (m[i] == 0) { // Normal mode, m==0 -> write value as is if (vif->mode == 0) { continue; } else if (vif->mode == 1) { // Addition decompression data.u32[i] = vif->r[i] + data.u32[i]; } else if (vif->mode == 2) { // Subtraction decompression data.u32[i] = vif->r[i] + data.u32[i]; vif->r[i] = data.u32[i]; } else if (vif->mode == 3) { vif->r[i] = data.u32[i]; } } else if (m[i] == 1) { data.u32[i] = vif->r[i]; } else if (m[i] == 2) { data.u32[i] = vif->c[cycle]; } else { // m=3 masks this fields' write, so we fetch // the value from VU mem instead data.u32[i] = vu_get_vu_mem_ptr(vif->vu, vif->addr)->u32[i]; } } } else { // Do mode processing only for (int i = 0; i < 4; i++) { if (vif->mode == 0) { continue; } else if (vif->mode == 1) { // Offset decompression data.u32[i] = vif->r[i] + data.u32[i]; } else if (vif->mode == 2) { // Difference decompression data.u32[i] = vif->r[i] + data.u32[i]; vif->r[i] = data.u32[i]; } else if (vif->mode == 3) { vif->r[i] = data.u32[i]; } } } if (vif->unpack_cl == vif->unpack_wl) { // Write data normally *vu_get_vu_mem_ptr(vif->vu, vif->addr++) = data; } else if (vif->unpack_cl > vif->unpack_wl) { // Write data until unpack_wl is reached, then skip unpack_skip *vu_get_vu_mem_ptr(vif->vu, vif->addr++) = data; } else { fprintf(stderr, "vif%d: Unpack error: unpack_cl (%d) < unpack_wl (%d)\n", vif->id, vif->unpack_cl, vif->unpack_wl); exit(1); } vif->unpack_cycle++; if (vif->unpack_cycle == vif->unpack_wl) { vif->addr += vif->unpack_skip; vif->unpack_cycle = 0; } } void vif0_send_irq(void* udata, int overshoot) { struct ps2_vif* vif = (struct ps2_vif*)udata; ps2_intc_irq(vif->intc, EE_INTC_VIF0); } void vif1_send_irq(void* udata, int overshoot) { struct ps2_vif* vif = (struct ps2_vif*)udata; ps2_intc_irq(vif->intc, EE_INTC_VIF1); } static inline void vif_handle_fifo_write(struct ps2_vif* vif, uint32_t data) { if (vif->state == VIF_IDLE) { vif->cmd = (data >> 24) & 0xff; if (vif->cmd & 0x80) { struct sched_event event; event.callback = vif->id ? vif1_send_irq : vif0_send_irq; event.cycles = 1000; event.name = vif->id ? "VIF1 Interrupt" : "VIF0 Interrupt"; event.udata = vif; sched_schedule(vif->sched, event); // fprintf(stderr, "vif%d: Requested IRQ command=%02x\n", vif->id, vif->cmd); vif->stat |= 0x00000c02; vif->stat ^= 0x0f000000; vif->code = data; } switch ((data >> 24) & 0x7f) { case VIF_CMD_NOP: { // printf("vif%d: NOP\n", vif->id); } break; case VIF_CMD_STCYCL: { // printf("vif%d: STCYCL(%04x)\n", vif->id, data & 0xffff); vif->cycle = data & 0xffff; } break; case VIF_CMD_OFFSET: { // printf("vif%d: OFFSET(%04x)\n", vif->id, data & 0xffff); // Set DBF to 0 vif->stat &= ~0x80; // Set TOPS to BASE vif->tops = vif->base; vif->ofst = data & 0x3ff; } break; case VIF_CMD_BASE: { // printf("vif%d: BASE(%04x)\n", vif->id, data & 0xffff); vif->base = data & 0x3ff; } break; case VIF_CMD_ITOP: { // printf("vif%d: ITOP(%04x)\n", vif->id, data & 0xffff); vif->itops = data & 0x3ff; } break; case VIF_CMD_STMOD: { // printf("vif%d: STMOD(%04x)\n", vif->id, data & 0xffff); vif->mode = data & 3; } break; case VIF_CMD_MSKPATH3: { // fprintf(stdout, "vif%d: MSKPATH3(%04x)\n", vif->id, data & 0xffff); if (data & 0x8000) { vif->gif->stat |= 2; } else { vif->gif->stat &= ~2; } } break; case VIF_CMD_MARK: { // printf("vif%d: MARK(%04x)\n", vif->id, data & 0xffff); vif->mark = data & 0xffff; } break; case VIF_CMD_FLUSHE: { // printf("vif%d: FLUSHE\n", vif->id); } break; case VIF_CMD_FLUSH: { // Note: MASSIVE GRAN TURISMO HACK! // GT3/4 expect IBT and stall bits to be set when a // VIF IRQ occurs, CODE also needs to be set to the // last command that caused a stall. // This is admittedly a huge hack, but we can't really // emulate any of this without properly implementing // DMA timings. // printf("vif%d: FLUSH\n", vif->id); } break; case VIF_CMD_FLUSHA: { // printf("vif%d: FLUSHA\n", vif->id); } break; case VIF_CMD_MSCAL: { // printf("vif%d: MSCAL(%04x)\n", vif->id, data & 0xffff); vif->top = vif->tops; // Toggle DBF vif->stat ^= 0x80; vif->tops = vif->base; vif->itop = vif->itops; if (vif->stat & 0x80) { vif->tops += vif->ofst; } vu_execute_program(vif->vu, data & 0xffff); } break; case VIF_CMD_MSCALF: { // printf("vif%d: MSCALF(%04x)\n", vif->id, data & 0xffff); vif->top = vif->tops; // Toggle DBF vif->stat ^= 0x80; vif->tops = vif->base; vif->itop = vif->itops; if (vif->stat & 0x80) { vif->tops += vif->ofst; } vu_execute_program(vif->vu, data & 0xffff); } break; case VIF_CMD_MSCNT: { // printf("vif%d: MSCNT(%08x)\n", vif->id, vu_get_tpc(vif->vu)); vif->top = vif->tops; // Toggle DBF vif->stat ^= 0x80; vif->tops = vif->base; vif->itop = vif->itops; if (vif->stat & 0x80) { vif->tops += vif->ofst; } vu_execute_program_tpc(vif->vu); } break; case VIF_CMD_STMASK: { // printf("vif%d: STMASK(%04x)\n", vif->id, data & 0xffff); vif->state = VIF_RECV_DATA; vif->pending_words = 1; } break; case VIF_CMD_STROW: { // printf("vif%d: STROW(%04x)\n", vif->id, data & 0xffff); vif->state = VIF_RECV_DATA; vif->pending_words = 4; } break; case VIF_CMD_STCOL: { // printf("vif%d: STCOL(%04x)\n", vif->id, data & 0xffff); vif->state = VIF_RECV_DATA; vif->pending_words = 4; } break; case VIF_CMD_MPG: { // printf("vif%d: MPG(%04x, %04x)\n", vif->id, (data >> 16) & 0xff, data & 0xffff); int num = (data >> 16) & 0xff; if (!num) num = 256; vif->addr = data & 0xffff; vif->state = VIF_RECV_DATA; vif->pending_words = num * 2; vif->shift = 0; vu_invalidate_range(vif->vu, vif->addr << 3, num << 3); } break; case VIF_CMD_DIRECT: { // fprintf(stdout, "vif%d: DIRECT(%04x)\n", vif->id, data & 0xffff); int imm = data & 0xffff; if (imm == 0) { imm = 0x10000; } vif->state = VIF_RECV_DATA; vif->pending_words = imm * 4; vif->shift = 0; } break; case VIF_CMD_DIRECTHL: { // fprintf(stdout, "vif%d: DIRECTHL(%04x)\n", vif->id, data & 0xffff); int imm = data & 0xffff; if (imm == 0) { imm = 0x10000; } vif->state = VIF_RECV_DATA; vif->pending_words = imm * 4; vif->shift = 0; } break; // UNPACK commands case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f: case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f: { vif->unpack_fmt = (data >> 24) & 0xf; vif->unpack_usn = (data >> 14) & 1; vif->unpack_num = (data >> 16) & 0xff; vif->unpack_cl = vif->cycle & 0xff; vif->unpack_wl = (vif->cycle >> 8) & 0xff; vif->unpack_mask = (data >> 28) & 1; vif->unpack_cycle = 0; int vl = (data >> 24) & 3; int vn = (data >> 26) & 3; int flg = (data >> 15) & 1; int addr = data & 0x3ff; int filling = vif->unpack_cl < vif->unpack_wl; if (!vif->unpack_num) vif->unpack_num = 256; if (flg) addr += vif->tops; if (filling) { // fprintf(stderr, "vif%d: Filling mode unimplemented\n", vif->id); return; // exit(1); } // To-do: Handle for filling vif->unpack_skip = vif->unpack_cl - vif->unpack_wl; vif->unpack_wl_count = 0; uint32_t pack_size = 16; if ((vl == 3 && vn == 3) == 0) pack_size = (32 >> vl) * (vn + 1); vif->pending_words = pack_size * vif->unpack_num; vif->pending_words = (vif->pending_words + 0x1F) & ~0x1F; vif->pending_words /= 32; vif->unpack_shift = 0; vif->state = VIF_RECV_DATA; vif->shift = 0; vif->addr = addr; // 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); } break; default: { // fprintf(stderr, "vif%d: Unhandled command %02x\n", vif->id, vif->cmd); // exit(1); } break; } } else { switch (vif->cmd) { case VIF_CMD_STMASK: { vif->mask = data; vif->state = VIF_IDLE; } break; case VIF_CMD_STROW: { vif->r[4 - (vif->pending_words--)] = data; if (!vif->pending_words) { vif->state = VIF_IDLE; } } break; case VIF_CMD_STCOL: { vif->c[4 - (vif->pending_words--)] = data; if (!vif->pending_words) { vif->state = VIF_IDLE; } } break; case VIF_CMD_MPG: { if (!vif->shift) { vif->data.u32[vif->shift++] = data; } else { vif->data.u32[1] = data; // fprintf(stdout, "vif%d: Writing %08x %08x to MicroMem addr=%04x\n", vif->id, vif->data.u32[0], vif->data.u32[1], vif->addr); *vu_get_micro_mem_ptr(vif->vu, vif->addr++) = vif->data.u64[0]; vif->shift = 0; } if (!(--vif->pending_words)) { vif->state = VIF_IDLE; } } break; case VIF_CMD_DIRECTHL: case VIF_CMD_DIRECT: { vif->data.u32[vif->shift++] = data; vif->pending_words--; if (vif->shift == 4) { // 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); ps2_gif_fifo_write(vif->gif, vif->data, GIF_PATH2); vif->shift = 0; } if (!vif->pending_words) { // fprintf(stdout, "vif%d: DIRECT complete\n", vif->id); vif->state = VIF_IDLE; } } break; case 0x60: case 0x61: case 0x62: case 0x63: case 0x64: case 0x65: case 0x66: case 0x67: case 0x68: case 0x69: case 0x6a: case 0x6b: case 0x6c: case 0x6d: case 0x6e: case 0x6f: case 0x70: case 0x71: case 0x72: case 0x73: case 0x74: case 0x75: case 0x76: case 0x77: case 0x78: case 0x79: case 0x7a: case 0x7b: case 0x7c: case 0x7d: case 0x7e: case 0x7f: { switch (vif->unpack_fmt) { // S-32 case 0x00: { vif->data.u32[0] = data; vif->data.u32[1] = data; vif->data.u32[2] = data; vif->data.u32[3] = data; vif_write_vu_mem(vif, vif->data); } break; // S-16 case 0x01: { for (int i = 0; i < 2; i++) { uint128_t q = { 0 }; q.u32[0] = (data >> (i * 16)) & 0xffff; if (!vif->unpack_usn) { q.u32[0] = (int32_t)((int16_t)q.u32[0]); } q.u32[1] = q.u32[0]; q.u32[2] = q.u32[0]; q.u32[3] = q.u32[0]; vif_write_vu_mem(vif, q); vif->unpack_num--; if (!vif->unpack_num) break; } } break; // S-8 case 0x02: { for (int i = 0; i < 4; i++) { uint128_t q = { 0 }; q.u32[0] = (data >> (i * 8)) & 0xff; if (!vif->unpack_usn) { q.u32[0] = (int32_t)((int8_t)q.u32[0]); } q.u32[1] = q.u32[0]; q.u32[2] = q.u32[0]; q.u32[3] = q.u32[0]; vif_write_vu_mem(vif, q); vif->unpack_num--; if (!vif->unpack_num) break; } } break; // V2-32 case 0x04: { vif->unpack_buf[vif->shift++] = data; if (vif->shift == 2) { uint128_t q = { 0 }; q.u32[0] = vif->unpack_buf[0]; q.u32[1] = vif->unpack_buf[1]; vif_write_vu_mem(vif, q); vif->shift = 0; vif->unpack_num--; if (!vif->unpack_num) break; } } break; // V2-16 case 0x05: { uint128_t q = { 0 }; q.u32[0] = data & 0xffff; q.u32[1] = data >> 16; if (!vif->unpack_usn) { q.u32[0] = (int32_t)((int16_t)q.u32[0]); q.u32[1] = (int32_t)((int16_t)q.u32[1]); } vif_write_vu_mem(vif, q); vif->unpack_num--; if (!vif->unpack_num) break; } break; // V2-8 case 0x06: { for (int i = 0; i < 2; i++) { uint128_t q = { 0 }; uint16_t d = data >> (i * 16); q.u32[0] = d & 0xff; q.u32[1] = d >> 8; if (!vif->unpack_usn) { q.u32[0] = (int32_t)((int8_t)q.u32[0]); q.u32[1] = (int32_t)((int8_t)q.u32[1]); } vif_write_vu_mem(vif, q); vif->unpack_num--; if (!vif->unpack_num) break; } } break; // V3-32 case 0x08: { vif->unpack_buf[vif->shift++] = data; if (vif->shift == 3) { uint128_t q = { 0 }; q.u32[0] = vif->unpack_buf[0]; q.u32[1] = vif->unpack_buf[1]; q.u32[2] = vif->unpack_buf[2]; vif_write_vu_mem(vif, q); vif->shift = 0; vif->unpack_num--; if (!vif->unpack_num) break; } } break; // V3-16 case 0x09: { vif->unpack_buf[vif->shift++] = data; if (vif->shift == (vif->unpack_shift ? 1 : 2)) { uint128_t q = { 0 }; if (!vif->unpack_shift) { q.u32[0] = vif->unpack_buf[0] & 0xffff; q.u32[1] = (vif->unpack_buf[0] >> 16) & 0xffff; q.u32[2] = vif->unpack_buf[1] & 0xffff; } else { q.u32[0] = vif->unpack_data; q.u32[1] = vif->unpack_buf[0] & 0xffff; q.u32[2] = vif->unpack_buf[0] >> 16; } if (!vif->unpack_usn) { q.u32[0] = (int32_t)((int16_t)q.u32[0]); q.u32[1] = (int32_t)((int16_t)q.u32[1]); q.u32[2] = (int32_t)((int16_t)q.u32[2]); } vif_write_vu_mem(vif, q); vif->shift = 0; vif->unpack_num--; vif->unpack_shift ^= 1; vif->unpack_data = vif->unpack_buf[1] >> 16; if (!vif->unpack_num) break; } } break; // V3-8 (disgusting) case 0x0a: { uint128_t q = { 0 }; switch (vif->unpack_shift) { case 0: { q.u32[0] = data & 0xff; q.u32[1] = (data >> 8) & 0xff; q.u32[2] = (data >> 16) & 0xff; vif->unpack_data = data >> 24; vif->unpack_shift++; if (!vif->unpack_usn) { q.u32[0] = (int32_t)((int8_t)q.u32[0]); q.u32[1] = (int32_t)((int8_t)q.u32[1]); q.u32[2] = (int32_t)((int8_t)q.u32[2]); } vif_write_vu_mem(vif, q); vif->unpack_num--; if (!vif->unpack_num) break; } break; case 1: { q.u32[0] = vif->unpack_data; q.u32[1] = data & 0xff; q.u32[2] = (data >> 8) & 0xff; vif->unpack_data = data >> 16; vif->unpack_shift++; if (!vif->unpack_usn) { q.u32[0] = (int32_t)((int8_t)q.u32[0]); q.u32[1] = (int32_t)((int8_t)q.u32[1]); q.u32[2] = (int32_t)((int8_t)q.u32[2]); } vif_write_vu_mem(vif, q); vif->unpack_num--; if (!vif->unpack_num) break; } break; case 2: { q.u32[0] = vif->unpack_data & 0xff; q.u32[1] = (vif->unpack_data >> 8) & 0xff; q.u32[2] = data & 0xff; vif->unpack_data = (data >> 8) & 0xffffff; vif->unpack_shift++; if (!vif->unpack_usn) { q.u32[0] = (int32_t)((int8_t)q.u32[0]); q.u32[1] = (int32_t)((int8_t)q.u32[1]); q.u32[2] = (int32_t)((int8_t)q.u32[2]); } vif_write_vu_mem(vif, q); vif->unpack_num--; if (!vif->unpack_num) break; q.u32[0] = (data >> 8) & 0xff; q.u32[1] = (data >> 16) & 0xff; q.u32[2] = (data >> 24) & 0xff; vif->unpack_shift = 0; if (!vif->unpack_usn) { q.u32[0] = (int32_t)((int8_t)q.u32[0]); q.u32[1] = (int32_t)((int8_t)q.u32[1]); q.u32[2] = (int32_t)((int8_t)q.u32[2]); } vif_write_vu_mem(vif, q); vif->unpack_num--; if (!vif->unpack_num) break; } break; } } break; // V4-32 case 0x0c: { vif->unpack_buf[vif->shift++] = data; if (vif->shift == 4) { uint128_t q = { 0 }; q.u32[0] = vif->unpack_buf[0]; q.u32[1] = vif->unpack_buf[1]; q.u32[2] = vif->unpack_buf[2]; q.u32[3] = vif->unpack_buf[3]; vif_write_vu_mem(vif, q); vif->shift = 0; } } break; // V4-16 case 0x0d: { vif->unpack_buf[vif->shift++] = data; if (vif->shift == 2) { uint128_t q = { 0 }; q.u32[0] = vif->unpack_buf[0] & 0xffff; q.u32[1] = vif->unpack_buf[0] >> 16; q.u32[2] = vif->unpack_buf[1] & 0xffff; q.u32[3] = vif->unpack_buf[1] >> 16; if (!vif->unpack_usn) { q.u32[0] = (int32_t)((int16_t)q.u32[0]); q.u32[1] = (int32_t)((int16_t)q.u32[1]); q.u32[2] = (int32_t)((int16_t)q.u32[2]); q.u32[3] = (int32_t)((int16_t)q.u32[3]); } vif_write_vu_mem(vif, q); vif->shift = 0; } } break; // V4-8 case 0x0e: { uint128_t q = { 0 }; q.u32[0] = data & 0xff; q.u32[1] = (data >> 8) & 0xff; q.u32[2] = (data >> 16) & 0xff; q.u32[3] = (data >> 24) & 0xff; if (!vif->unpack_usn) { q.u32[0] = (int32_t)((int8_t)q.u32[0]); q.u32[1] = (int32_t)((int8_t)q.u32[1]); q.u32[2] = (int32_t)((int8_t)q.u32[2]); q.u32[3] = (int32_t)((int8_t)q.u32[3]); } vif_write_vu_mem(vif, q); } break; // V4-5 case 0x0f: { uint128_t q = { 0 }; for (int i = 0; i < 2; i++) { uint16_t c = (data >> (i * 16)) & 0xffff; q.u32[0] = ((c >> 0) & 0x1f) << 3; q.u32[1] = ((c >> 5) & 0x1f) << 3; q.u32[2] = ((c >> 10) & 0x1f) << 3; q.u32[3] = ((c >> 15) & 1) << 7; vif_write_vu_mem(vif, q); vif->unpack_num--; if (!vif->unpack_num) break; } } break; default: { fprintf(stderr, "vif%d: Unimplemented unpack format %02x\n", vif->id, vif->unpack_fmt); exit(1); } break; } if (!(--vif->pending_words)) { vif->state = VIF_IDLE; } } break; } } } uint64_t ps2_vif_read32(struct ps2_vif* vif, uint32_t addr) { switch (addr) { // VIF0 registers case 0x10003800: return vif->stat; // case 0x10003810: return vif->fbrst; case 0x10003820: return vif->err; case 0x10003830: return vif->mark; case 0x10003840: return vif->cycle; case 0x10003850: return vif->mode; case 0x10003860: return vif->num; case 0x10003870: return vif->mask; case 0x10003880: return vif->code; case 0x10003890: return vif->itops; case 0x100038d0: return vif->itop; case 0x10003900: return vif->r[0]; case 0x10003910: return vif->r[1]; case 0x10003920: return vif->r[2]; case 0x10003930: return vif->r[3]; case 0x10003940: return vif->c[0]; case 0x10003950: return vif->c[1]; case 0x10003960: return vif->c[2]; case 0x10003970: return vif->c[3]; // VIF1 registers case 0x10003c00: { uint32_t stat = vif->stat; vif->stat = 0; return stat; } break; case 0x10003c10: return vif->fbrst; case 0x10003c20: return vif->err; case 0x10003c30: return vif->mark; case 0x10003c40: return vif->cycle; case 0x10003c50: return vif->mode; case 0x10003c60: return vif->num; case 0x10003c70: return vif->mask; case 0x10003c80: return vif->code; case 0x10003c90: return vif->itops; case 0x10003ca0: return vif->base; case 0x10003cb0: return vif->ofst; case 0x10003cc0: return vif->tops; case 0x10003cd0: return vif->itop; case 0x10003ce0: return vif->top; case 0x10003d00: return vif->r[0]; case 0x10003d10: return vif->r[1]; case 0x10003d20: return vif->r[2]; case 0x10003d30: return vif->r[3]; case 0x10003d40: return vif->c[0]; case 0x10003d50: return vif->c[1]; case 0x10003d60: return vif->c[2]; case 0x10003d70: return vif->c[3]; // VIF FIFOs case 0x10004000: // printf("vif%d: 32-bit FIFO read\n", vif->id); exit(1); break; case 0x10005000: // printf("vif%d: 32-bit FIFO read\n", vif->id); exit(1); break; default: { fprintf(stderr, "vif%d: Unhandled 32-bit read to %08x\n", vif->id, addr); exit(1); } break; } return 0; } void ps2_vif_write32(struct ps2_vif* vif, uint32_t addr, uint64_t data) { switch (addr) { // VIF0 registers case 0x10003810: { vif->fbrst = data; vif->state = VIF_IDLE; vif->pending_words = 0; vif->unpack_shift = 0; vif->shift = 0; } break; case 0x10003820: vif->err = data; break; case 0x10003830: vif->mark = data; break; // VIF1 registers case 0x10003c00: vif->stat &= 0x800000; vif->stat |= data & 0x800000; break; case 0x10003c10: { vif->fbrst = data; vif->state = VIF_IDLE; vif->pending_words = 0; vif->unpack_shift = 0; vif->shift = 0; } break; case 0x10003c20: vif->err = data; break; case 0x10003c30: vif->mark = data; break; case 0x10003c80: /* Unknown */ break; // VIF FIFOs case 0x10004000: vif_handle_fifo_write(vif, data); break; case 0x10005000: vif_handle_fifo_write(vif, data); break; default: { fprintf(stderr, "vif%d: Unhandled 32-bit write to %08x\n", vif->id, addr); // exit(1); } break; } } uint128_t ps2_vif_read128(struct ps2_vif* vif, uint32_t addr) { switch (addr) { case 0x10004000: break; // printf("vif%d: 128-bit FIFO read\n", vif->id); exit(1); break; case 0x10005000: break; // printf("vif%d: 128-bit FIFO read\n", vif->id); exit(1); break; default: { fprintf(stderr, "vif%d: Unhandled 128-bit read to %08x\n", vif->id, addr); exit(1); } break; } return (uint128_t){ .u64[0] = 0, .u64[1] = 0 }; } void ps2_vif_write128(struct ps2_vif* vif, uint32_t addr, uint128_t data) { switch (addr) { case 0x10004000: { vif_handle_fifo_write(vif, data.u32[0]); vif_handle_fifo_write(vif, data.u32[1]); vif_handle_fifo_write(vif, data.u32[2]); vif_handle_fifo_write(vif, data.u32[3]); } break; case 0x10005000: { vif_handle_fifo_write(vif, data.u32[0]); vif_handle_fifo_write(vif, data.u32[1]); vif_handle_fifo_write(vif, data.u32[2]); vif_handle_fifo_write(vif, data.u32[3]); } break; default: { fprintf(stderr, "vif%d: Unhandled 128-bit write to %08x\n", vif->id, addr); exit(1); } break; } } #undef printf ================================================ FILE: src/ee/vif.h ================================================ #ifndef VIF_H #define VIF_H #ifdef __cplusplus extern "C" { #endif #include #include "u128.h" #include "bus.h" #include "ee/intc.h" #include "ee/vu.h" #include "scheduler.h" enum { VIF_IDLE, VIF_RECV_DATA }; #define VIF_CMD_NOP 0x00 #define VIF_CMD_STCYCL 0x01 #define VIF_CMD_OFFSET 0x02 #define VIF_CMD_BASE 0x03 #define VIF_CMD_ITOP 0x04 #define VIF_CMD_STMOD 0x05 #define VIF_CMD_MSKPATH3 0x06 #define VIF_CMD_MARK 0x07 #define VIF_CMD_FLUSHE 0x10 #define VIF_CMD_FLUSH 0x11 #define VIF_CMD_FLUSHA 0x13 #define VIF_CMD_MSCAL 0x14 #define VIF_CMD_MSCALF 0x15 #define VIF_CMD_MSCNT 0x17 #define VIF_CMD_STMASK 0x20 #define VIF_CMD_STROW 0x30 #define VIF_CMD_STCOL 0x31 #define VIF_CMD_MPG 0x4A #define VIF_CMD_DIRECT 0x50 #define VIF_CMD_DIRECTHL 0x51 // 60h-7Fh UNPACK #define UNPACK_S_32 0 #define UNPACK_S_16 1 #define UNPACK_S_8 2 #define UNPACK_V2_32 4 #define UNPACK_V2_16 5 #define UNPACK_V2_8 6 #define UNPACK_V3_32 8 #define UNPACK_V3_16 9 #define UNPACK_V3_8 10 #define UNPACK_V4_32 12 #define UNPACK_V4_16 13 #define UNPACK_V4_8 14 #define UNPACK_V4_5 15 struct ps2_vif { uint32_t stat; uint32_t fbrst; uint32_t err; uint32_t mark; uint32_t cycle; uint32_t mode; uint32_t num; uint32_t mask; uint32_t code; uint32_t itops; uint32_t base; uint32_t ofst; uint32_t tops; uint32_t itop; uint32_t top; uint32_t r[4]; uint32_t c[4]; int state; int pending_words; int shift; uint32_t cmd; uint128_t data; uint32_t addr; uint32_t unpack_num; uint32_t unpack_fmt; uint32_t unpack_usn; uint32_t unpack_cl; uint32_t unpack_wl; uint32_t unpack_skip; uint32_t unpack_wl_count; uint32_t unpack_buf[16]; uint32_t unpack_shift; uint32_t unpack_data; int unpack_mask; int unpack_cycle; int id; struct vu_state* vu; struct sched_state* sched; struct ps2_gif* gif; struct ps2_intc* intc; struct ee_bus* bus; }; struct ps2_vif* ps2_vif_create(void); void 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); void ps2_vif_destroy(struct ps2_vif* vif); uint64_t ps2_vif_read32(struct ps2_vif* vif, uint32_t addr); void ps2_vif_write32(struct ps2_vif* vif, uint32_t addr, uint64_t data); uint128_t ps2_vif_read128(struct ps2_vif* vif, uint32_t addr); void ps2_vif_write128(struct ps2_vif* vif, uint32_t addr, uint128_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/vu.h ================================================ struct vu_state; #ifndef VU_H #define VU_H #ifdef __cplusplus extern "C" { #endif #include #include "u128.h" #include "vif.h" #include "gif.h" struct vu_reg128 { union { uint128_t u128; uint64_t u64[2]; uint32_t u32[4]; int32_t s32[4]; float f[4]; // Named fields struct { float x; float y; float z; float w; }; }; }; struct vu_reg32 { union { uint32_t u32; int32_t s32; float f; uint16_t u16[2]; int16_t s16[2]; uint8_t u8[4]; int8_t s8[4]; }; }; #define VU_REG_R 32 #define VU_REG_I 33 #define VU_REG_Q 34 #define VU_REG_P 35 struct vu_instruction { uint32_t ld_di[4]; uint32_t ld_d; uint32_t ld_s; uint32_t ld_t; uint32_t ld_sf; uint32_t ld_tf; int32_t ld_imm5; int32_t ld_imm11; uint32_t ld_imm12; uint32_t ld_imm15; uint32_t ld_imm24; uint32_t ud_di[4]; uint32_t ud_d; uint32_t ud_s; uint32_t ud_t; uint32_t opcode; int branch; struct { int reg; int field; } dst, src[2]; int vi_dst; int vi_src[2]; void (*func)(struct vu_state* vu, const struct vu_instruction* i); }; struct vu_state; struct vu_state* vu_create(void); void vu_init(struct vu_state* vu, int id, struct ps2_gif* gif, struct ps2_vif* vif, struct vu_state* vu1); void vu_destroy(struct vu_state* vu); // VU mem bus interface uint64_t ps2_vu_read8(struct vu_state* vu, uint32_t addr); uint64_t ps2_vu_read16(struct vu_state* vu, uint32_t addr); uint64_t ps2_vu_read32(struct vu_state* vu, uint32_t addr); uint64_t ps2_vu_read64(struct vu_state* vu, uint32_t addr); uint128_t ps2_vu_read128(struct vu_state* vu, uint32_t addr); void ps2_vu_write8(struct vu_state* vu, uint32_t addr, uint64_t data); void ps2_vu_write16(struct vu_state* vu, uint32_t addr, uint64_t data); void ps2_vu_write32(struct vu_state* vu, uint32_t addr, uint64_t data); void ps2_vu_write64(struct vu_state* vu, uint32_t addr, uint64_t data); void ps2_vu_write128(struct vu_state* vu, uint32_t addr, uint128_t data); void ps2_vu_write_vi(struct vu_state* vu, int index, uint32_t value); uint32_t ps2_vu_read_vi(struct vu_state* vu, int index); void ps2_vu_reset(struct vu_state* vu); void ps2_vu_decode_upper(struct vu_state* vu, uint32_t opcode); void ps2_vu_decode_lower(struct vu_state* vu, uint32_t opcode); void ps2_vu_execute_lower(struct vu_state* vu, uint32_t opcode); void ps2_vu_execute_upper(struct vu_state* vu, uint32_t opcode); void vu_cycle(struct vu_state* vu); void vu_execute_program(struct vu_state* vu, uint32_t addr); void vu_execute_program_tpc(struct vu_state* vu); uint128_t* vu_get_vu_mem_ptr(struct vu_state* vu, uint32_t addr); uint64_t* vu_get_micro_mem_ptr(struct vu_state* vu, uint32_t addr); uint32_t vu_get_tpc(struct vu_state* vu); void vu_clear_block_cache(struct vu_state* vu); void vu_invalidate_range(struct vu_state* vu, uint32_t addr, uint32_t size); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/vu_cached.cpp ================================================ #include #include #include #include #include #include #include "vu.h" #include "vu_def.hpp" #include "vu_dis.h" // #define printf(fmt, ...)(0) #define VU_LD_DI(i) (ins->ld_di[i]) #define VU_LD_D (ins->ld_d) #define VU_LD_S (ins->ld_s) #define VU_LD_T (ins->ld_t) #define VU_LD_SF (ins->ld_sf) #define VU_LD_TF (ins->ld_tf) #define VU_LD_IMM5 (ins->ld_imm5) #define VU_LD_IMM11 (ins->ld_imm11) #define VU_LD_IMM12 (ins->ld_imm12) #define VU_LD_IMM15 (ins->ld_imm15) #define VU_LD_IMM24 (ins->ld_imm24) #define VU_ID vu->vi[VU_LD_D] #define VU_IS vu->vi[VU_LD_S] #define VU_IT vu->vi[VU_LD_T] #define VU_UD_DI(i) (ins->ud_di[i]) #define VU_UD_D (ins->ud_d) #define VU_UD_S (ins->ud_s) #define VU_UD_T (ins->ud_t) #define VU_D_FLD (0x01e00000) #define VU_D_X (0x01000000) #define VU_D_Y (0x00800000) #define VU_D_Z (0x00400000) #define VU_D_W (0x00200000) [[noreturn]] inline void unreachable() { // Uses compiler specific extensions if possible. // Even if no extension is used, undefined behavior is still raised by // an empty function body and the noreturn attribute. #if defined(_MSC_VER) && !defined(__clang__) // MSVC __assume(false); #else // GCC, Clang __builtin_unreachable(); #endif } struct vu_state* vu_create(void) { return new struct vu_state; } void vu_init(struct vu_state* vu, int id, struct ps2_gif* gif, struct ps2_vif* vif, struct vu_state* vu1) { vu->id = id; vu->vu1 = vu1; vu->vif = vif; vu->gif = gif; if (!id) { vu->micro_mem_size = 0x1ff; vu->vu_mem_size = 0xff; } else { vu->micro_mem_size = 0x7ff; vu->vu_mem_size = 0x3ff; } vu->vf[0].x = 0.0; vu->vf[0].y = 0.0; vu->vf[0].z = 0.0; vu->vf[0].w = 1.0; ps2_vu_reset(vu); // VU uses round to zero by default fesetround(FE_TOWARDZERO); vu->block_cache_size = 0; vu->block_cache.clear(); vu->block_cache.resize(vu->micro_mem_size+1); } void vu_destroy(struct vu_state* vu) { delete vu; } #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) static inline uint32_t vu_max(int32_t a, int32_t b) { return (a < 0 && b < 0) ? min(a, b) : max(a, b); } static inline uint32_t vu_min(int32_t a, int32_t b) { return (a < 0 && b < 0) ? max(a, b) : min(a, b); } static inline float vu_atan(float t) { //In reality, VU1 uses an approximation to derive the result. This is shown here. const static float atan_const[] = { 0.999999344348907f, -0.333298563957214f, 0.199465364217758f, -0.139085337519646f, 0.096420042216778f, -0.055909886956215f, 0.021861229091883f, -0.004054057877511f }; float result = 0.785398185253143f; // pi/4 for (int i = 0; i < 8; i++) { result += atan_const[i] * powf(t, (i * 2) + 1); } return result; } static inline void vu_update_status(struct vu_state* vu) { vu->status &= ~0x3f; vu->status |= (vu->mac_pipeline[3] & 0x000f) ? 1 : 0; vu->status |= (vu->mac_pipeline[3] & 0x00f0) ? 2 : 0; vu->status |= (vu->mac_pipeline[3] & 0x0f00) ? 4 : 0; vu->status |= (vu->mac_pipeline[3] & 0xf000) ? 8 : 0; vu->status |= (vu->status & 0x3f) << 6; } static inline void vu_set_q(struct vu_state* vu, float value, int delay) { if (vu->q_delay == 0) { vu->prev_q.f = vu->q.f; } vu->q.f = value; vu->q_delay = delay; } static inline struct vu_reg32 vu_get_q(struct vu_state* vu) { if (!vu->q_delay) { return vu->q; } return vu->prev_q; } static inline float vu_update_flags(struct vu_state* vu, float value, int index) { uint32_t value_u = *(uint32_t*)&value; int flag_id = 3 - index; // Sign flag if (value_u & 0x80000000) vu->mac |= 0x10 << flag_id; else vu->mac &= ~(0x10 << flag_id); // Zero flag, clear under/overflow if ((value_u & 0x7FFFFFFF) == 0) { vu->mac |= 1 << flag_id; vu->mac &= ~(0x1100 << flag_id); return value; } switch ((value_u >> 23) & 0xFF) { //Underflow, set zero case 0: vu->mac |= 0x101 << flag_id; vu->mac &= ~(0x1000 << flag_id); value_u = value_u & 0x80000000; break; //Overflow case 255: vu->mac |= 0x1000 << flag_id; vu->mac &= ~(0x101 << flag_id); value_u = (value_u & 0x80000000) | 0x7F7FFFFF; break; //Clear all but sign default: vu->mac &= ~(0x1101 << flag_id); break; } return *(float*)&value_u; } static inline void vu_clear_flags(struct vu_state* vu, int index) { vu->mac &= ~(0x1111 << (3 - index)); } static inline float vu_cvtf(uint32_t value) { switch (value & 0x7f800000) { case 0x0: { value &= 0x80000000; return *(float*)&value; } break; case 0x7f800000: { uint32_t result = (value & 0x80000000) | 0x7f7fffff; return *(float*)&result; } } return *(float*)&value; } int32_t vu_cvti(float value) { if (value >= 2147483647.0) return 2147483647LL; if (value <= -2147483648.0) return -2147483648LL; return (int32_t)value; } static inline void vu_set_vf(struct vu_state* vu, int r, int f, float v) { if (r) vu->vf[r].f[f] = v; } static inline void vu_set_vfu(struct vu_state* vu, int r, int f, int32_t v) { if (r) vu->vf[r].s32[f] = v; } static inline void vu_set_vf_x(struct vu_state* vu, int r, float v) { if (r) vu->vf[r].x = v; } static inline void vu_set_vf_y(struct vu_state* vu, int r, float v) { if (r) vu->vf[r].y = v; } static inline void vu_set_vf_z(struct vu_state* vu, int r, float v) { if (r) vu->vf[r].z = v; } static inline void vu_set_vf_w(struct vu_state* vu, int r, float v) { if (r) vu->vf[r].w = v; } static inline void vu_set_vi(struct vu_state* vu, int r, uint16_t v) { if (r) vu->vi[r] = v; } static inline float vu_vf_i(struct vu_state* vu, int r, int i) { return vu_cvtf(vu->vf[r].u32[i]); } static inline float vu_vf_x(struct vu_state* vu, int r) { return vu_cvtf(vu->vf[r].u32[0]); } static inline float vu_vf_y(struct vu_state* vu, int r) { return vu_cvtf(vu->vf[r].u32[1]); } static inline float vu_vf_z(struct vu_state* vu, int r) { return vu_cvtf(vu->vf[r].u32[2]); } static inline float vu_vf_w(struct vu_state* vu, int r) { return vu_cvtf(vu->vf[r].u32[3]); } static inline float vu_acc_i(struct vu_state* vu, int i) { return vu_cvtf(vu->acc.u32[i]); } static inline void vu_mem_write(struct vu_state* vu, uint16_t addr, uint32_t data, int i) { if (!vu->id) { if (addr <= 0x3ff) { vu->vu_mem[addr & 0xff].u32[i] = data; } else { if ((addr >= 0x400) && (addr <= 0x41f)) { vu->vu1->vf[addr & 0x1f].u32[i] = data; } else if ((addr >= 0x420) && (addr <= 0x42f)) { vu->vu1->vi[addr & 0xf] = data; } else if (addr == 0x430) { vu->vu1->status = data; } else if (addr == 0x431) { vu->vu1->mac = data; } else if (addr == 0x432) { vu->vu1->clip = data; } else if (addr == 0x434) { vu->vu1->r.u32 = data; } else if (addr == 0x435) { vu->vu1->i.u32 = data; } else if (addr == 0x436) { vu->vu1->q.u32 = data; } else if (addr == 0x437) { vu->vu1->p.u32 = data; } else if (addr == 0x43a) { vu->vu1->tpc = data; } else { // printf("vu: oob write\n"); // exit(1); } } } else { // if (addr == 0x000001d3) *(int*)0 = 0; vu->vu_mem[addr & 0x3ff].u32[i] = data; } } static inline uint128_t vu_mem_read(struct vu_state* vu, uint32_t addr) { if (!vu->id) { if (addr <= 0x3ff) { return vu->vu_mem[addr & 0xff]; } else { if ((addr >= 0x400) && (addr <= 0x41f)) { return vu->vu1->vf[addr & 0x1f].u128; } else if ((addr >= 0x420) && (addr <= 0x42f)) { uint128_t result; result.u32[0] = vu->vu1->vi[addr & 0xf]; return result; } else if (addr == 0x430) { uint128_t result; result.u32[0] = vu->vu1->status; return result; } else if (addr == 0x431) { uint128_t result; result.u32[0] = vu->vu1->mac; return result; } else if (addr == 0x432) { uint128_t result; result.u32[0] = vu->vu1->clip; return result; } else if (addr == 0x434) { uint128_t result; result.u32[0] = vu->vu1->r.u32; return result; } else if (addr == 0x435) { uint128_t result; result.u32[0] = vu->vu1->i.u32; return result; } else if (addr == 0x436) { uint128_t result; result.u32[0] = vu->vu1->q.u32; return result; } else if (addr == 0x437) { uint128_t result; result.u32[0] = vu->vu1->p.u32; return result; } else if (addr == 0x43a) { uint128_t result; result.u32[0] = vu->vu1->tpc; return result; } } } return vu->vu_mem[addr & 0x3ff]; } static inline void vu_write_branch_pipeline(struct vu_state* vu, int dst) { if (!dst) return; //On repeat writes we need to remember the value from before the chain if (vu->vi_backup_cycles && dst == vu->vi_backup_reg) { vu->vi_backup_cycles = 2; return; } vu->vi_backup_cycles = 2; vu->vi_backup_reg = dst; vu->vi_backup_value = vu->vi[dst]; // printf("branch pipeline: dst=%d prev=%04x rw=%d\n", // vu->branch_pipeline_curr.reg, vu->branch_pipeline_curr.prev, // vu->branch_pipeline_curr.rw // ); } static inline uint16_t vu_get_branch_register(struct vu_state* vu, int reg) { if (vu->vi_backup_cycles && (vu->vi_backup_reg == reg)) { return vu->vi_backup_value; } return vu->vi[reg]; } void vu_xgkick(struct vu_state* vu) { uint16_t addr = vu->xgkick_addr; int eop = 1; do { uint128_t tag = vu_mem_read(vu, addr++); if ((tag.u64[0] | tag.u64[1]) == 0) break; // addr &= 0x3ff; // if (addr == 0) break; // printf("tag: addr=%08x %08x %08x %08x %08x\n", addr - 1, tag.u32[3], tag.u32[2], tag.u32[1], tag.u32[0]); ps2_gif_fifo_write(vu->gif, tag, GIF_PATH1); eop = (tag.u64[0] & 0x8000) != 0; int nloop = tag.u64[0] & 0x7fff; int flg = (tag.u64[0] >> 58) & 3; int nregs = (tag.u64[0] >> 60) & 0xf; if (!nloop) continue; // if (!nregs) // nregs = 16; int qwc = 0; switch (flg) { case 0: { qwc = nregs * nloop; } break; case 1: { qwc = (nregs * nloop + 1) / 2; // Round up for odd cases } break; case 2: case 3: { qwc = nloop; } break; } if (qwc >= 0x400) { fprintf(stderr, "vu: Weird xgkick tag nloop=%d nregs=%d eop=%d flg=%d qwc=%d\n", nloop, nregs, eop, flg, qwc ); exit(1); } for (int i = 0; i < qwc; i++) { // printf("vu: %08x: %08x %08x %08x %08x\n", // addr, // vu->vu_mem[addr].u32[3], // vu->vu_mem[addr].u32[2], // vu->vu_mem[addr].u32[1], // vu->vu_mem[addr].u32[0] // ); ps2_gif_fifo_write(vu->gif, vu_mem_read(vu, addr++), GIF_PATH1); addr &= 0x3ff; // if (addr == 0) { // eop = 1; // break; // } } } while (!eop); } template void seq(F f, std::index_sequence) { // Parameter pack expansion (f(std::integral_constant{}), ...); } template void template_seq(F f) { seq(f, std::make_index_sequence{}); } // Upper pipeline template void vu_i_abs(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_set_vf(vu, t, i, fabsf(vu_vf_i(vu, s, i))); } }); } template void vu_i_add(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + vu_vf_i(vu, t, i); vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_addi(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + vu->i.f; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_addq(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; float q = vu_get_q(vu).f; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + q; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_addx(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_addy(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_addz(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_addw(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_adda(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + vu_vf_i(vu, t, i); vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_addai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + vu->i.f; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_addaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; float q = vu_get_q(vu).f; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + q; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_addax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_adday(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_addaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_addaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) + bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_sub(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - vu_vf_i(vu, t, i); vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_subi(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - vu->i.f; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_subq(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; float q = vu_get_q(vu).f; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - q; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_subx(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_suby(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_subz(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_subw(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_suba(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - vu_vf_i(vu, t, i); vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_subai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - vu->i.f; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_subaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; float q = vu_get_q(vu).f; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - q; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_subax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_subay(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_subaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_subaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) - bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_mul(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i); vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_muli(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * vu->i.f; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_mulq(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; float q = vu_get_q(vu).f; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * q; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_mulx(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_muly(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_mulz(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_mulw(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_mula(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i); vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_mulai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * vu->i.f; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_mulaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; float q = vu_get_q(vu).f; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * q; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_mulax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_mulay(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_mulaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_mulaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_madd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i); vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_maddi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu->i.f; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_maddq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; float q = vu_get_q(vu).f; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * q; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_maddx(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_x(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_maddy(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_y(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_maddz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_z(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_maddw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_w(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_madda(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i); vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_maddai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu->i.f; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_maddaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; float q = vu_get_q(vu).f; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu->acc.f[i] + vu_vf_i(vu, s, i) * q; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_maddax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_madday(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_maddaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_maddaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msub(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i); vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msubi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu->i.f; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msubq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; float q = vu_get_q(vu).f; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - (vu_vf_i(vu, s, i) * q); vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msubx(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_x(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msuby(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_y(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msubz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_z(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msubw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_w(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msuba(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i); vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msubai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu->i.f; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msubaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; float q = vu_get_q(vu).f; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu->acc.f[i] - vu_vf_i(vu, s, i) * q; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msubax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msubay(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msubaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_msubaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } }); vu_update_status(vu); } template void vu_i_max(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], vu->vf[t].s32[i]); } }); } template void vu_i_maxi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; if (!d) return; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], vu->i.s32); } }); } template void vu_i_maxx(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[0]; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float fs = vu_vf_i(vu, s, i); vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc); } }); } template void vu_i_maxy(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[1]; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float fs = vu_vf_i(vu, s, i); vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc); } }); } template void vu_i_maxz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[2]; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float fs = vu_vf_i(vu, s, i); vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc); } }); } template void vu_i_maxw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[3]; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { float fs = vu_vf_i(vu, s, i); vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc); } }); } template void vu_i_mini(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], vu->vf[t].s32[i]); } }); } template void vu_i_minii(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; if (!d) return; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], vu->i.s32); } }); } template void vu_i_minix(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[0]; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc); } }); } template void vu_i_miniy(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[1]; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc); } }); } template void vu_i_miniz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[2]; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc); } }); } template void vu_i_miniw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[3]; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc); } }); } void vu_i_opmula(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; vu->acc.x = vu_vf_y(vu, s) * vu_vf_z(vu, t); vu->acc.y = vu_vf_z(vu, s) * vu_vf_x(vu, t); vu->acc.z = vu_vf_x(vu, s) * vu_vf_y(vu, t); vu->acc.x = vu_cvtf(vu->acc.u32[0]); vu->acc.y = vu_cvtf(vu->acc.u32[1]); vu->acc.z = vu_cvtf(vu->acc.u32[2]); vu->acc.x = vu_update_flags(vu, vu->acc.x, 0); vu->acc.y = vu_update_flags(vu, vu->acc.y, 1); vu->acc.z = vu_update_flags(vu, vu->acc.z, 2); // printf("s(%d)=%08x %08x %08x t(%d)=%08x %08x %08x acc=%08x %08x %08x prev=%08x %08x %08x\n", // s, // vu->vf[s].u32[0], // vu->vf[s].u32[1], // vu->vf[s].u32[2], // t, // vu->vf[t].u32[0], // vu->vf[t].u32[1], // vu->vf[t].u32[2], // vu->acc.u32[0], // vu->acc.u32[1], // vu->acc.u32[2], // acc.u32[0], // acc.u32[1], // acc.u32[2] // ); vu_clear_flags(vu, 3); vu_update_status(vu); } void vu_i_opmsub(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; struct vu_reg128 tmp; tmp.f[0] = vu->acc.x - vu_vf_y(vu, s) * vu_vf_z(vu, t); tmp.f[1] = vu->acc.y - vu_vf_z(vu, s) * vu_vf_x(vu, t); tmp.f[2] = vu->acc.z - vu_vf_x(vu, s) * vu_vf_y(vu, t); vu_set_vf_x(vu, d, vu_update_flags(vu, tmp.f[0], 0)); vu_set_vf_y(vu, d, vu_update_flags(vu, tmp.f[1], 1)); vu_set_vf_z(vu, d, vu_update_flags(vu, tmp.f[2], 2)); // printf("s(%d)=%08x %08x %08x t(%d)=%08x %08x %08x d=%08x %08x %08x dp=%08x %08x %08x\n", // s, // vu->vf[s].u32[0], // vu->vf[s].u32[1], // vu->vf[s].u32[2], // t, // vu->vf[t].u32[0], // vu->vf[t].u32[1], // vu->vf[t].u32[2], // vu->vf[d].u32[0], // vu->vf[d].u32[1], // vu->vf[d].u32[2], // dv.u32[0], // dv.u32[1], // dv.u32[2] // ); vu_clear_flags(vu, 3); vu_update_status(vu); } void vu_i_nop(struct vu_state* vu, const struct vu_instruction* ins) { // No operation } template void vu_i_ftoi0(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i))); } }); } template void vu_i_ftoi4(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.0625f))); } }); } template void vu_i_ftoi12(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.000244140625f))); } }); } template void vu_i_ftoi15(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.000030517578125f))); } }); } template void vu_i_itof0(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_set_vf(vu, t, i, (float)vu->vf[s].s32[i]); } }); } template void vu_i_itof4(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.0625f)); } }); } template void vu_i_itof12(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.000244140625f)); } }); } template void vu_i_itof15(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.000030517578125f)); } }); } void vu_i_clip(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_UD_T; int s = VU_UD_S; vu->clip <<= 6; float w = fabsf(vu_vf_w(vu, t)); float x = vu_vf_x(vu, s); float y = vu_vf_y(vu, s); float z = vu_vf_z(vu, s); vu->clip |= (x > +w); vu->clip |= (x < -w) << 1; vu->clip |= (y > +w) << 2; vu->clip |= (y < -w) << 3; vu->clip |= (z > +w) << 4; vu->clip |= (z < -w) << 5; vu->clip &= 0xFFFFFF; } // Lower pipeline void vu_i_b(struct vu_state* vu, const struct vu_instruction* ins) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } void vu_i_bal(struct vu_state* vu, const struct vu_instruction* ins) { // Instruction next to the delay slot VU_IT = vu->tpc + 1; vu->next_tpc = vu->tpc + VU_LD_IMM11; } void vu_i_div(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; int s = VU_LD_S; int tf = VU_LD_TF; int sf = VU_LD_SF; struct vu_reg32 q; q.f = vu_vf_i(vu, s, sf) / vu_vf_i(vu, t, tf); q.f = vu_cvtf(q.u32); vu_set_q(vu, q.f, 7); } void vu_i_eatan(struct vu_state* vu, const struct vu_instruction* ins) { float x = vu_vf_i(vu, VU_LD_S, VU_LD_SF); if (x == -1.0f) { vu->p.u32 = 0xFF7FFFFF; } else { x = (x - 1.0f) / (x + 1.0f); vu->p.f = vu_atan(x); } } void vu_i_eatanxy(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x = vu_vf_x(vu, s); float y = vu_vf_y(vu, s); if (y + x == 0.0f) { vu->p.u32 = 0x7F7FFFFF | (vu->vf[s].u32[1] & 0x80000000); } else { x = (y - 1.0f) / (y + x); vu->p.f = vu_atan(x); } } void vu_i_eatanxz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x = vu_vf_x(vu, s); float z = vu_vf_z(vu, s); //P = atan(z/x) if (z + x == 0.0f) { vu->p.u32 = 0x7F7FFFFF | (vu->vf[s].u32[2] & 0x80000000); } else { x = (z - x) / (z + x); vu->p.f = vu_atan(x); } } void vu_i_eexp(struct vu_state* vu, const struct vu_instruction* ins) { const static float coeffs[] = { 0.249998688697815f, 0.031257584691048f, 0.002591371303424f, 0.000171562001924f, 0.000005430199963f, 0.000000690600018f }; int s = VU_LD_S; int sf = VU_LD_SF; if (vu->vf[s].u32[sf] & 0x80000000) { vu->p.f = vu_vf_i(vu, s, sf); return; } float value = 1; float x = vu_vf_i(vu, s, sf); for (int exp = 1; exp <= 6; exp++) value += coeffs[exp - 1] * pow(x, exp); vu->p.f = 1.0 / value; } void vu_i_eleng(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s); float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s); float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s); vu->p.f = sqrtf(x2 + y2 + z2); } void vu_i_ercpr(struct vu_state* vu, const struct vu_instruction* ins) { vu->p.f = 1.0f / vu_vf_i(vu, VU_LD_S, VU_LD_SF); } void vu_i_erleng(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s); float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s); float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s); vu->p.f = 1.0f / sqrtf(x2 + y2 + z2); } void vu_i_ersadd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s); float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s); float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s); vu->p.f = 1.0f / (x2 + y2 + z2); } void vu_i_ersqrt(struct vu_state* vu, const struct vu_instruction* ins) { vu->p.f = 1.0f / sqrtf(vu_vf_i(vu, VU_LD_S, VU_LD_SF)); } void vu_i_esadd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s); float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s); float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s); vu->p.f = x2 + y2 + z2; } void vu_i_esin(struct vu_state* vu, const struct vu_instruction* ins) { vu->p.f = sinf(vu_vf_i(vu, VU_LD_S, VU_LD_SF)); } void vu_i_esqrt(struct vu_state* vu, const struct vu_instruction* ins) { vu->p.f = sqrtf(vu_vf_i(vu, VU_LD_S, VU_LD_SF)); } void vu_i_esum(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; vu->p.f = vu_vf_x(vu, s) + vu_vf_y(vu, s) + vu_vf_z(vu, s) + vu_vf_w(vu, s); } void vu_i_fcand(struct vu_state* vu, const struct vu_instruction* ins) { vu->vi[1] = ((vu->clip & 0xffffff) & VU_LD_IMM24) != 0; } void vu_i_fceq(struct vu_state* vu, const struct vu_instruction* ins) { vu->vi[1] = (vu->clip & 0xffffff) == VU_LD_IMM24; } void vu_i_fcget(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; vu->vi[VU_LD_T] = vu->clip & 0xfff; } void vu_i_fcor(struct vu_state* vu, const struct vu_instruction* ins) { vu->vi[1] = ((vu->clip & 0xffffff) | VU_LD_IMM24) == 0xffffff; } void vu_i_fcset(struct vu_state* vu, const struct vu_instruction* ins) { vu->clip = VU_LD_IMM24; } void vu_i_fmand(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, vu->mac_pipeline[3] & VU_IS); } void vu_i_fmeq(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, (VU_IS & 0xffff) == (vu->mac_pipeline[3] & 0xffff)); } void vu_i_fmor(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, (VU_IS & 0xffff) | (vu->mac_pipeline[3] & 0xffff)); } void vu_i_fsand(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, vu->status & VU_LD_IMM12); } void vu_i_fseq(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, (vu->status & 0xfff) == VU_LD_IMM12); } void vu_i_fsor(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, (vu->status & 0xfff) | VU_LD_IMM12); } void vu_i_fsset(struct vu_state* vu, const struct vu_instruction* ins) { vu->status &= 0x3f; vu->status |= VU_LD_IMM12 & 0xfc0; } void vu_i_iadd(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_D); vu_set_vi(vu, VU_LD_D, VU_IS + VU_IT); } void vu_i_iaddi(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_T); vu_set_vi(vu, VU_LD_T, VU_IS + VU_LD_IMM5); } void vu_i_iaddiu(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_T); vu_set_vi(vu, VU_LD_T, VU_IS + VU_LD_IMM15); } void vu_i_iand(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_T); vu_set_vi(vu, VU_LD_D, VU_IS & VU_IT); } void vu_i_ibeq(struct vu_state* vu, const struct vu_instruction* ins) { uint16_t t = vu_get_branch_register(vu, VU_LD_T); uint16_t s = vu_get_branch_register(vu, VU_LD_S); if (t == s) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } void vu_i_ibgez(struct vu_state* vu, const struct vu_instruction* ins) { int16_t s = vu_get_branch_register(vu, VU_LD_S); if (s >= 0) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } void vu_i_ibgtz(struct vu_state* vu, const struct vu_instruction* ins) { int16_t s = vu_get_branch_register(vu, VU_LD_S); if (s > 0) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } void vu_i_iblez(struct vu_state* vu, const struct vu_instruction* ins) { int16_t s = vu_get_branch_register(vu, VU_LD_S); if (s <= 0) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } void vu_i_ibltz(struct vu_state* vu, const struct vu_instruction* ins) { int16_t s = vu_get_branch_register(vu, VU_LD_S); if (s < 0) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } void vu_i_ibne(struct vu_state* vu, const struct vu_instruction* ins) { uint16_t t = vu_get_branch_register(vu, VU_LD_T); uint16_t s = vu_get_branch_register(vu, VU_LD_S); // printf("ibne vi%02u (%04x), vi%02u (%04x), 0x%08x\n", VU_LD_T, t, VU_LD_S, s, vu->tpc + VU_LD_IMM11); if (t != s) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } template void vu_i_ilw(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; uint32_t addr = VU_IS + VU_LD_IMM11; uint128_t data = vu_mem_read(vu, addr); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vi[t] = data.u32[i]; } }); } template void vu_i_ilwr(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; if (!t) return; uint32_t addr = vu->vi[s]; uint128_t data = vu_mem_read(vu, addr); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vi[t] = data.u32[i]; } }); } void vu_i_ior(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_D); vu_set_vi(vu, VU_LD_D, VU_IS | VU_IT); } void vu_i_isub(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_D); vu_set_vi(vu, VU_LD_D, VU_IS - VU_IT); } void vu_i_isubiu(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_T); vu_set_vi(vu, VU_LD_T, VU_IS - VU_LD_IMM15); } template void vu_i_isw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; uint32_t addr = vu->vi[s] + VU_LD_IMM11; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_mem_write(vu, addr, vu->vi[t], i); } }); } template void vu_i_iswr(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; uint32_t addr = vu->vi[s]; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_mem_write(vu, addr, vu->vi[t], i); } }); } void vu_i_jalr(struct vu_state* vu, const struct vu_instruction* ins) { uint16_t s = VU_IS; VU_IT = vu->tpc + 1; vu->next_tpc = s; } void vu_i_jr(struct vu_state* vu, const struct vu_instruction* ins) { vu->next_tpc = VU_IS; } template void vu_i_lq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; uint32_t addr = vu->vi[s] + VU_LD_IMM11; uint128_t data = vu_mem_read(vu, addr); if (!t) return; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[t].u32[i] = data.u32[i]; } }); } template void vu_i_lqd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; vu_write_branch_pipeline(vu, s); vu_set_vi(vu, s, vu->vi[s] - 1); uint32_t addr = vu->vi[s]; uint128_t data = vu_mem_read(vu, addr); if (!t) return; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[t].u32[i] = data.u32[i]; } }); } template void vu_i_lqi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; vu_write_branch_pipeline(vu, s); if (t) { uint32_t addr = vu->vi[s]; uint128_t data = vu_mem_read(vu, addr); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[t].u32[i] = data.u32[i]; } }); } vu_set_vi(vu, s, vu->vi[s] + 1); } template void vu_i_mfir(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[t].u32[i] = (int32_t)(int16_t)VU_IS; } }); } template void vu_i_mfp(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[t].u32[i] = vu->p.f; } }); } template void vu_i_move(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; if (!t) return; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[t].u32[i] = vu->vf[s].u32[i]; } }); } template void vu_i_mr32(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; int s = VU_LD_S; uint32_t x = vu->vf[s].u32[0]; // template_seq<4>([&](auto i) { // if constexpr (di & (VU_D_X >> i)) { // vu->vf[t].u32[i] = vu->vf[s].u32[(i + 1) & 3]; // } // }); if constexpr (di & VU_D_X) { vu->vf[t].u32[0] = vu->vf[s].u32[1]; } if constexpr (di & VU_D_Y) { vu->vf[t].u32[1] = vu->vf[s].u32[2]; } if constexpr (di & VU_D_Z) { vu->vf[t].u32[2] = vu->vf[s].u32[3]; } if constexpr (di & VU_D_W) { vu->vf[t].u32[3] = x; } } void vu_i_mtir(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_T); vu_set_vi(vu, VU_LD_T, vu->vf[VU_LD_S].u32[VU_LD_SF] & 0xffff); } template void vu_i_rget(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[t].u32[i] = vu->r.u32; } }); } void vu_i_rinit(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; vu->r.u32 = 0x3f800000; if (!s) return; vu->r.u32 |= vu->vf[s].u32[VU_LD_SF] & 0x007fffff; } template void vu_i_rnext(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; int x = (vu->r.u32 >> 4) & 1; int y = (vu->r.u32 >> 22) & 1; vu->r.u32 <<= 1; vu->r.u32 ^= x ^ y; vu->r.u32 = (vu->r.u32 & 0x7FFFFF) | 0x3F800000; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu->vf[t].u32[i] = vu->r.u32; } }); } void vu_i_rsqrt(struct vu_state* vu, const struct vu_instruction* ins) { struct vu_reg32 q; q.f = vu_vf_i(vu, VU_LD_S, VU_LD_SF) / sqrtf(vu_vf_i(vu, VU_LD_T, VU_LD_TF)); q.f = vu_cvtf(q.u32); vu_set_q(vu, q.f, 13); } void vu_i_rxor(struct vu_state* vu, const struct vu_instruction* ins) { vu->r.u32 = 0x3F800000 | ((vu->r.u32 ^ vu->vf[VU_LD_S].u32[VU_LD_SF]) & 0x007FFFFF); } template void vu_i_sq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; uint32_t addr = vu->vi[t] + VU_LD_IMM11; // 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]); template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_mem_write(vu, addr, vu->vf[s].u32[i], i); } }); } template void vu_i_sqd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; vu_write_branch_pipeline(vu, t); vu_set_vi(vu, t, vu->vi[t] - 1); uint32_t addr = vu->vi[t]; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_mem_write(vu, addr, vu->vf[s].u32[i], i); } }); } template void vu_i_sqi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; vu_write_branch_pipeline(vu, t); uint32_t addr = vu->vi[t]; template_seq<4>([&](auto i) { if constexpr (di & (VU_D_X >> i)) { vu_mem_write(vu, addr, vu->vf[s].u32[i], i); } }); vu_set_vi(vu, t, vu->vi[t] + 1); } void vu_i_sqrt(struct vu_state* vu, const struct vu_instruction* ins) { struct vu_reg32 q; q.f = sqrtf(vu_vf_i(vu, VU_LD_T, VU_LD_TF)); q.f = vu_cvtf(q.u32); vu_set_q(vu, q.f, 7); } void vu_i_waitp(struct vu_state* vu, const struct vu_instruction* ins) { // No operation } void vu_i_waitq(struct vu_state* vu, const struct vu_instruction* ins) { vu->q_delay = 0; } void vu_i_xgkick(struct vu_state* vu, const struct vu_instruction* ins) { // vu_xgkick(vu); // vu->xgkick_pending = 3; // vu->xgkick_addr = VU_IS; // return; uint16_t addr = VU_IS; int eop = 1; do { uint128_t tag = vu_mem_read(vu, addr++); if ((tag.u64[0] | tag.u64[1]) == 0) break; // addr &= 0x3ff; // if (addr == 0) break; // printf("tag: addr=%08x %08x %08x %08x %08x\n", addr - 1, tag.u32[3], tag.u32[2], tag.u32[1], tag.u32[0]); ps2_gif_fifo_write(vu->gif, tag, GIF_PATH1); eop = (tag.u64[0] & 0x8000) != 0; int nloop = tag.u64[0] & 0x7fff; int flg = (tag.u64[0] >> 58) & 3; int nregs = (tag.u64[0] >> 60) & 0xf; if (!nloop) continue; // if (!nregs) // nregs = 16; int qwc = 0; switch (flg) { case 0: { qwc = nregs * nloop; } break; case 1: { qwc = (nregs * nloop + 1) / 2; // Round up for odd cases } break; case 2: case 3: { qwc = nloop; } break; } if (qwc >= 0x400) { fprintf(stderr, "vu: Weird xgkick tag nloop=%d nregs=%d eop=%d flg=%d qwc=%d\n", nloop, nregs, eop, flg, qwc ); exit(1); } for (int i = 0; i < qwc; i++) { // printf("vu: %08x: %08x %08x %08x %08x\n", // addr, // vu->vu_mem[addr].u32[3], // vu->vu_mem[addr].u32[2], // vu->vu_mem[addr].u32[1], // vu->vu_mem[addr].u32[0] // ); ps2_gif_fifo_write(vu->gif, vu_mem_read(vu, addr++), GIF_PATH1); addr &= 0x3ff; // if (addr == 0) { // eop = 1; // break; // } } } while (!eop); } void vu_i_xitop(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, vu->vif->itop); } void vu_i_xtop(struct vu_state* vu, const struct vu_instruction* ins) { if (vu->id == 0) { printf("vu: xtop used in VU0\n"); // exit(1); } vu_set_vi(vu, VU_LD_T, vu->vif->top); } uint64_t ps2_vu_read8(struct vu_state* vu, uint32_t addr) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; return *(uint8_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]); } uint8_t* ptr = (uint8_t*)vu->vu_mem; return *(uint8_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]); } uint64_t ps2_vu_read16(struct vu_state* vu, uint32_t addr) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; return *(uint16_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]); } uint8_t* ptr = (uint8_t*)vu->vu_mem; return *(uint16_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]); } uint64_t ps2_vu_read32(struct vu_state* vu, uint32_t addr) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; return *(uint32_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]); } uint8_t* ptr = (uint8_t*)vu->vu_mem; return *(uint32_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]); } uint64_t ps2_vu_read64(struct vu_state* vu, uint32_t addr) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; return *(uint64_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]); } uint8_t* ptr = (uint8_t*)vu->vu_mem; return *(uint64_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]); } uint128_t ps2_vu_read128(struct vu_state* vu, uint32_t addr) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; return *(uint128_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]); } uint8_t* ptr = (uint8_t*)vu->vu_mem; return *(uint128_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]); } void ps2_vu_write8(struct vu_state* vu, uint32_t addr, uint64_t data) { if (addr <= 0x3FFF) { vu_invalidate_range(vu, addr, 1); uint8_t* ptr = (uint8_t*)vu->micro_mem; *(uint8_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data; } else { uint8_t* ptr = (uint8_t*)vu->vu_mem; *(uint8_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data; } } void ps2_vu_write16(struct vu_state* vu, uint32_t addr, uint64_t data) { if (addr <= 0x3FFF) { vu_invalidate_range(vu, addr, 2); uint8_t* ptr = (uint8_t*)vu->micro_mem; *(uint16_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data; } else { uint8_t* ptr = (uint8_t*)vu->vu_mem; *(uint16_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data; } } void ps2_vu_write32(struct vu_state* vu, uint32_t addr, uint64_t data) { if (addr <= 0x3FFF) { vu_invalidate_range(vu, addr, 4); uint8_t* ptr = (uint8_t*)vu->micro_mem; *(uint32_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data; } else { uint8_t* ptr = (uint8_t*)vu->vu_mem; *(uint32_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data; } } void ps2_vu_write64(struct vu_state* vu, uint32_t addr, uint64_t data) { if (addr <= 0x3FFF) { vu_invalidate_range(vu, addr, 8); uint8_t* ptr = (uint8_t*)vu->micro_mem; *(uint64_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data; } else { uint8_t* ptr = (uint8_t*)vu->vu_mem; *(uint64_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data; } } void ps2_vu_write128(struct vu_state* vu, uint32_t addr, uint128_t data) { if (addr <= 0x3FFF) { vu_invalidate_range(vu, addr, 16); uint8_t* ptr = (uint8_t*)vu->micro_mem; *(uint128_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data; } else { uint8_t* ptr = (uint8_t*)vu->vu_mem; *(uint128_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data; } } #define VU_FLD_X 1 #define VU_FLD_Y 2 #define VU_FLD_Z 4 #define VU_FLD_W 8 #define VU_DEC_UD_S_SRC_T_BROADCAST(bc, f) \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = (opcode >> 21) & 0xf; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = bc; \ vu->upper.func = f; #define VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(bc, f) \ vu->upper.dst.reg = vu->upper.ud_d; \ vu->upper.dst.field = (opcode >> 21) & 0xf; \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = vu->upper.dst.field; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = bc; \ vu->upper.func = f; #define VU_DEC_UD_D_DST_S_SRC_T_SRC(f) \ vu->upper.dst.reg = vu->upper.ud_d; \ vu->upper.dst.field = (opcode >> 21) & 0xf; \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = vu->upper.dst.field; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = vu->upper.dst.field; \ vu->upper.func = f; #define VU_DEC_UD_D_DST_S_SRC(f) \ vu->upper.dst.reg = vu->upper.ud_d; \ vu->upper.dst.field = (opcode >> 21) & 0xf; \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = vu->upper.dst.field; \ vu->upper.func = f; #define VU_DEC_UD_D_DST_S_SRC_Q_SRC(f) \ vu->upper.dst.reg = vu->upper.ud_d; \ vu->upper.dst.field = (opcode >> 21) & 0xf; \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = vu->upper.dst.field; \ vu->upper.src[1].reg = VU_REG_Q; \ vu->upper.func = f; #define VU_DEC_UD_T_DST_S_SRC(f) \ vu->upper.dst.reg = vu->upper.ud_t; \ vu->upper.dst.field = (opcode >> 21) & 0xf; \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = vu->upper.dst.field; \ vu->upper.func = f; #define VU_DEC_UD_S_SRC_T_SRC(f) \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = (opcode >> 21) & 0xf; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = vu->upper.src[0].field; \ vu->upper.func = f; #define VU_DEC_UD_S_SRC(f) \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = (opcode >> 21) & 0xf; \ vu->upper.func = f; #define VU_DEC_UD_S_SRC_Q_SRC(f) \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = (opcode >> 21) & 0xf; \ vu->upper.src[1].reg = VU_REG_Q; \ vu->upper.func = f; #define VU_DEC_OPMULA() \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = vu->upper.src[0].field; \ vu->upper.func = vu_i_opmula; #define VU_DEC_OPMSUB() \ vu->upper.dst.reg = vu->upper.ud_d; \ vu->upper.dst.field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = vu->upper.dst.field; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = vu->upper.dst.field; \ vu->upper.func = vu_i_opmsub; #define VU_DEC_CLIP() \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = VU_FLD_W; \ vu->upper.func = vu_i_clip; #define VU_DEC_LD_NONE(f) \ vu->lower.func = f; #define VU_DEC_UD_NONE(f) \ vu->upper.func = f; #define VU_DEC_LD_T_DST_S_VISRC(f) \ vu->lower.dst.reg = vu->lower.ld_t; \ vu->lower.dst.field = (opcode >> 21) & 0xf; \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.func = f; #define VU_DEC_LD_T_DST_S_VISRC_S_VIDST(f) \ vu->lower.dst.reg = vu->lower.ld_t; \ vu->lower.dst.field = (opcode >> 21) & 0xf; \ vu->lower.vi_dst = vu->lower.ld_s; \ vu->lower.vi_src[0] = vu->lower.vi_dst; \ vu->lower.func = f; #define VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(f) \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = (opcode >> 21) & 0xf; \ vu->lower.vi_dst = vu->lower.ld_t; \ vu->lower.vi_src[0] = vu->lower.vi_dst; \ vu->lower.func = f; #define VU_DEC_LD_S_SF_SRC_T_TF_SRC(f) \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = vu->lower.ld_sf; \ vu->lower.src[1].reg = vu->lower.ld_t; \ vu->lower.src[1].field = vu->lower.ld_tf; \ vu->lower.func = f; #define VU_DEC_LD_Q_DST_S_SF_SRC_T_TF_SRC(f) \ vu->lower.dst.reg = VU_REG_Q; \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = vu->lower.ld_sf; \ vu->lower.src[1].reg = vu->lower.ld_t; \ vu->lower.src[1].field = vu->lower.ld_tf; \ vu->lower.func = f; #define VU_DEC_LD_T_VIDST_S_SF_SRC(f) \ vu->lower.vi_dst = vu->lower.ld_t; \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = vu->lower.ld_sf; \ vu->lower.func = f; #define VU_DEC_LD_T_DST_S_VISRC(f) \ vu->lower.dst.reg = vu->lower.ld_t; \ vu->lower.dst.field = (opcode >> 21) & 0xf; \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.func = f; #define VU_DEC_LD_T_TF_SRC(f) \ vu->lower.src[0].reg = vu->lower.ld_t; \ vu->lower.src[0].field = vu->lower.ld_tf; \ vu->lower.func = f; #define VU_DEC_LD_Q_DST_T_TF_SRC(f) \ vu->lower.dst.reg = VU_REG_Q; \ vu->lower.src[0].reg = vu->lower.ld_t; \ vu->lower.src[0].field = vu->lower.ld_tf; \ vu->lower.func = f; #define VU_DEC_LD_S_SRC_T_VISRC(f) \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = (opcode >> 21) & 0xf; \ vu->lower.vi_src[0] = vu->lower.ld_t; \ vu->lower.func = f; #define VU_DEC_LD_T_VIDST_S_VISRC(f) \ vu->lower.vi_dst = vu->lower.ld_t; \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.func = f; #define VU_DEC_LD_S_VISRC_T_VISRC(f) \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.vi_src[1] = vu->lower.ld_t; \ vu->lower.func = f; #define VU_DEC_LD_T_VIDST_S_VISRC(f) \ vu->lower.vi_dst = vu->lower.ld_t; \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.func = f; #define VU_DEC_LD_T_VISRC_S_VISRC(f) \ vu->lower.vi_src[0] = vu->lower.ld_t; \ vu->lower.vi_src[1] = vu->lower.ld_s; \ vu->lower.func = f; #define VU_DEC_LD_VIDST(v, f) \ vu->lower.vi_dst = v; \ vu->lower.func = f; #define VU_DEC_LD_T_VIDST(f) \ vu->lower.vi_dst = vu->lower.ld_t; \ vu->lower.func = f; #define VU_DEC_LD_S_VISRC(f) \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.func = f; #define VU_DEC_LD_S_FLD_SRC(fld, f) \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = fld; \ vu->lower.func = f; #define VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(f) \ vu->lower.vi_dst = vu->lower.ld_d; \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.vi_src[1] = vu->lower.ld_t; \ vu->lower.func = f; #define VU_DEC_LD_T_DST_S_SRC(f) \ vu->lower.dst.reg = vu->lower.ld_t; \ vu->lower.dst.field = (opcode >> 21) & 0xf; \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = vu->lower.dst.field; \ vu->lower.func = f; #define VU_DEC_LD_T_DST(f) \ vu->lower.dst.reg = vu->lower.ld_t; \ vu->lower.dst.field = (opcode >> 21) & 0xf; \ vu->lower.func = f; #define VU_DEC_LD_S_SF_SRC(f) \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = vu->lower.ld_sf; \ vu->lower.func = f; #define VU_DEC_MR32(f) \ vu->lower.dst.reg = vu->lower.ld_t; \ vu->lower.dst.field = (opcode >> 21) & 0xf; \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = (vu->lower.dst.field >> 1) | ((vu->lower.dst.field & 1) << 3); \ vu->lower.func = f; #define GET_TEMPLATE_FN(i) \ [&](uint32_t opcode) { \ switch ((opcode >> 21) & 0xf) { \ case 0: return &i<0>; \ case 1: return &i; \ case 2: return &i; \ case 3: return &i; \ case 4: return &i; \ case 5: return &i; \ case 6: return &i; \ case 7: return &i; \ case 8: return &i; \ case 9: return &i; \ case 10: return &i; \ case 11: return &i; \ case 12: return &i; \ case 13: return &i; \ case 14: return &i; \ case 15: return &i; \ default: __builtin_unreachable(); \ } \ __builtin_unreachable(); }(opcode) void vu_decode_upper(struct vu_state* vu, uint32_t opcode) { vu->upper.opcode = opcode; vu->upper.ud_d = (opcode >> 6) & 0x1f; vu->upper.ud_s = (opcode >> 11) & 0x1f; vu->upper.ud_t = (opcode >> 16) & 0x1f; for (int i = 0; i < 4; i++) vu->upper.ud_di[i] = opcode & (1 << (24 - i)); vu->upper.func = NULL; vu->upper.dst.reg = 0; vu->upper.dst.field = 0; vu->upper.src[0].reg = 0; vu->upper.src[0].field = 0; vu->upper.src[1].reg = 0; vu->upper.src[1].field = 0; // Decode 000007FF style instruction if ((opcode & 0x3c) == 0x3c) { // 0EEEE 1111 EE // -0EE EE11 11EE // -------------- // bit 10 is always 0 // bits 2-5 are always 1 // -------------- // bits 0-1 and bits 6-9 (6 bits) are enough to decode // all of the following switch (((opcode & 0x3c0) >> 4) | (opcode & 3)) { case 0x00: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_addax)); return; case 0x01: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_adday)); return; case 0x02: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_addaz)); return; case 0x03: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_addaw)); return; case 0x04: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_subax)); return; case 0x05: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_subay)); return; case 0x06: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_subaz)); return; case 0x07: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_subaw)); return; case 0x08: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_maddax)); return; case 0x09: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_madday)); return; case 0x0A: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_maddaz)); return; case 0x0B: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_maddaw)); return; case 0x0C: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_msubax)); return; case 0x0D: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_msubay)); return; case 0x0E: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_msubaz)); return; case 0x0F: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_msubaw)); return; case 0x10: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_itof0)); return; case 0x11: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_itof4)); return; case 0x12: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_itof12)); return; case 0x13: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_itof15)); return; case 0x14: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_ftoi0)); return; case 0x15: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_ftoi4)); return; case 0x16: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_ftoi12)); return; case 0x17: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_ftoi15)); return; case 0x18: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_mulax)); return; case 0x19: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_mulay)); return; case 0x1A: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_mulaz)); return; case 0x1B: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_mulaw)); return; case 0x1C: VU_DEC_UD_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_mulaq)); return; case 0x1D: VU_DEC_UD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_abs)); return; case 0x1E: VU_DEC_UD_S_SRC(GET_TEMPLATE_FN(vu_i_mulai)); return; case 0x1F: VU_DEC_CLIP(); return; case 0x20: VU_DEC_UD_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_addaq)); return; case 0x21: VU_DEC_UD_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_maddaq)); return; case 0x22: VU_DEC_UD_S_SRC(GET_TEMPLATE_FN(vu_i_addai)); return; case 0x23: VU_DEC_UD_S_SRC(GET_TEMPLATE_FN(vu_i_maddai)); return; case 0x24: VU_DEC_UD_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_subaq)); return; case 0x25: VU_DEC_UD_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_msubaq)); return; case 0x26: VU_DEC_UD_S_SRC(GET_TEMPLATE_FN(vu_i_subai)); return; case 0x27: VU_DEC_UD_S_SRC(GET_TEMPLATE_FN(vu_i_msubai)); return; case 0x28: VU_DEC_UD_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_adda)); return; case 0x29: VU_DEC_UD_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_madda)); return; case 0x2A: VU_DEC_UD_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_mula)); return; case 0x2C: VU_DEC_UD_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_suba)); return; case 0x2D: VU_DEC_UD_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_msuba)); return; case 0x2E: VU_DEC_OPMULA(); return; case 0x2F: VU_DEC_UD_NONE(vu_i_nop); return; } } else { // Decode 0000003F style instruction switch (opcode & 0x3f) { case 0x00: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_addx)); return; case 0x01: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_addy)); return; case 0x02: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_addz)); return; case 0x03: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_addw)); return; case 0x04: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_subx)); return; case 0x05: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_suby)); return; case 0x06: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_subz)); return; case 0x07: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_subw)); return; case 0x08: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_maddx)); return; case 0x09: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_maddy)); return; case 0x0A: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_maddz)); return; case 0x0B: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_maddw)); return; case 0x0C: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_msubx)); return; case 0x0D: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_msuby)); return; case 0x0E: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_msubz)); return; case 0x0F: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_msubw)); return; case 0x10: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_maxx)); return; case 0x11: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_maxy)); return; case 0x12: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_maxz)); return; case 0x13: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_maxw)); return; case 0x14: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_minix)); return; case 0x15: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_miniy)); return; case 0x16: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_miniz)); return; case 0x17: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_miniw)); return; case 0x18: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, GET_TEMPLATE_FN(vu_i_mulx)); return; case 0x19: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, GET_TEMPLATE_FN(vu_i_muly)); return; case 0x1A: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, GET_TEMPLATE_FN(vu_i_mulz)); return; case 0x1B: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, GET_TEMPLATE_FN(vu_i_mulw)); return; case 0x1C: VU_DEC_UD_D_DST_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_mulq)); return; case 0x1D: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_maxi)); return; case 0x1E: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_muli)); return; case 0x1F: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_minii)); return; case 0x20: VU_DEC_UD_D_DST_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_addq)); return; case 0x21: VU_DEC_UD_D_DST_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_maddq)); return; case 0x22: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_addi)); return; case 0x23: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_maddi)); return; case 0x24: VU_DEC_UD_D_DST_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_subq)); return; case 0x25: VU_DEC_UD_D_DST_S_SRC_Q_SRC(GET_TEMPLATE_FN(vu_i_msubq)); return; case 0x26: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_subi)); return; case 0x27: VU_DEC_UD_D_DST_S_SRC(GET_TEMPLATE_FN(vu_i_msubi)); return; case 0x28: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_add)); return; case 0x29: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_madd)); return; case 0x2A: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_mul)); return; case 0x2B: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_max)); return; case 0x2C: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_sub)); return; case 0x2D: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_msub)); return; case 0x2E: VU_DEC_OPMSUB(); return; case 0x2F: VU_DEC_UD_D_DST_S_SRC_T_SRC(GET_TEMPLATE_FN(vu_i_mini)); return; } } } void vu_decode_lower(struct vu_state* vu, uint32_t opcode) { vu->lower.opcode = opcode; vu->lower.ld_d = (opcode >> 6) & 0x1f; vu->lower.ld_s = (opcode >> 11) & 0x1f; vu->lower.ld_t = (opcode >> 16) & 0x1f; vu->lower.ld_sf = (opcode >> 21) & 3; vu->lower.ld_tf = (opcode >> 23) & 3; vu->lower.ld_imm5 = ((int32_t)(((opcode >> 6) & 0x1f) << 27)) >> 27; vu->lower.ld_imm11 = ((int32_t)((opcode & 0x7ff) << 21)) >> 21; vu->lower.ld_imm12 = (((opcode >> 21) & 1) << 11) | (opcode & 0x7ff); vu->lower.ld_imm15 = (opcode & 0x7ff) | ((opcode & 0x1e00000) >> 10); vu->lower.ld_imm24 = opcode & 0xffffff; for (int i = 0; i < 4; i++) vu->lower.ld_di[i] = opcode & (1 << (24 - i)); vu->lower.func = NULL; vu->lower.dst.reg = 0; vu->lower.dst.field = 0; vu->lower.src[0].reg = 0; vu->lower.src[0].field = 0; vu->lower.src[1].reg = 0; vu->lower.src[1].field = 0; vu->lower.vi_src[0] = 0; vu->lower.vi_src[1] = 0; vu->lower.vi_dst = 0; vu->lower.branch = 0; switch ((opcode & 0xFE000000) >> 25) { case 0x00: VU_DEC_LD_T_DST_S_VISRC(GET_TEMPLATE_FN(vu_i_lq)); return; case 0x01: VU_DEC_LD_S_SRC_T_VISRC(GET_TEMPLATE_FN(vu_i_sq)); return; case 0x04: VU_DEC_LD_T_VIDST_S_VISRC(GET_TEMPLATE_FN(vu_i_ilw)); return; case 0x05: VU_DEC_LD_S_VISRC_T_VISRC(GET_TEMPLATE_FN(vu_i_isw)); return; case 0x08: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_iaddiu); return; case 0x09: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_isubiu); return; // Note: The flag check instructions clobber the destination register // "immediately", this means we don't actually need to generate // a dependency. case 0x10: VU_DEC_LD_NONE(vu_i_fceq); return; // VU_DEC_LD_VIDST(1, vu_i_fceq); return; case 0x11: VU_DEC_LD_NONE(vu_i_fcset); return; case 0x12: VU_DEC_LD_NONE(vu_i_fcand); return; // VU_DEC_LD_VIDST(1, vu_i_fcand); return; case 0x13: VU_DEC_LD_NONE(vu_i_fcor); return; // VU_DEC_LD_VIDST(1, vu_i_fcor); return; case 0x14: VU_DEC_LD_NONE(vu_i_fseq); return; // VU_DEC_LD_T_VIDST(vu_i_fseq); return; case 0x15: VU_DEC_LD_NONE(vu_i_fsset); return; case 0x16: VU_DEC_LD_NONE(vu_i_fsand); return; // VU_DEC_LD_T_VIDST(vu_i_fsand); return; case 0x17: VU_DEC_LD_NONE(vu_i_fsor); return; // VU_DEC_LD_T_VIDST(vu_i_fsor); return; case 0x18: VU_DEC_LD_S_VISRC(vu_i_fmeq); return; // VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmeq); return; case 0x1A: VU_DEC_LD_S_VISRC(vu_i_fmand); return; // VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmand); return; case 0x1B: VU_DEC_LD_S_VISRC(vu_i_fmor); return; // VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmor); return; case 0x1C: VU_DEC_LD_NONE(vu_i_fcget); return; // VU_DEC_LD_T_VIDST(vu_i_fcget); return; case 0x20: vu->lower.branch = 1; VU_DEC_LD_NONE(vu_i_b); return; case 0x21: vu->lower.branch = 1; VU_DEC_LD_T_VIDST(vu_i_bal); return; case 0x24: vu->lower.branch = 1; VU_DEC_LD_S_VISRC(vu_i_jr); return; case 0x25: vu->lower.branch = 1; VU_DEC_LD_T_VIDST_S_VISRC(vu_i_jalr); return; case 0x28: vu->lower.branch = 1; VU_DEC_LD_S_VISRC_T_VISRC(vu_i_ibeq); return; case 0x29: vu->lower.branch = 1; VU_DEC_LD_S_VISRC_T_VISRC(vu_i_ibne); return; case 0x2C: vu->lower.branch = 1; VU_DEC_LD_S_VISRC(vu_i_ibltz); return; case 0x2D: vu->lower.branch = 1; VU_DEC_LD_S_VISRC(vu_i_ibgtz); return; case 0x2E: vu->lower.branch = 1; VU_DEC_LD_S_VISRC(vu_i_iblez); return; case 0x2F: vu->lower.branch = 1; VU_DEC_LD_S_VISRC(vu_i_ibgez); return; case 0x40: { if ((opcode & 0x3C) == 0x3C) { switch (((opcode & 0x7C0) >> 4) | (opcode & 3)) { case 0x30: VU_DEC_LD_T_DST_S_SRC(GET_TEMPLATE_FN(vu_i_move)); return; case 0x31: VU_DEC_MR32(GET_TEMPLATE_FN(vu_i_mr32)); return; case 0x34: VU_DEC_LD_T_DST_S_VISRC_S_VIDST(GET_TEMPLATE_FN(vu_i_lqi)); return; case 0x35: VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(GET_TEMPLATE_FN(vu_i_sqi)); return; case 0x36: VU_DEC_LD_T_DST_S_VISRC_S_VIDST(GET_TEMPLATE_FN(vu_i_lqd)); return; case 0x37: VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(GET_TEMPLATE_FN(vu_i_sqd)); return; case 0x38: VU_DEC_LD_Q_DST_S_SF_SRC_T_TF_SRC(vu_i_div); return; case 0x39: VU_DEC_LD_Q_DST_T_TF_SRC(vu_i_sqrt); return; case 0x3A: VU_DEC_LD_Q_DST_S_SF_SRC_T_TF_SRC(vu_i_rsqrt); return; case 0x3B: VU_DEC_LD_NONE(vu_i_waitq); return; case 0x3C: VU_DEC_LD_T_VIDST_S_SF_SRC(vu_i_mtir); return; case 0x3D: VU_DEC_LD_T_DST_S_VISRC(GET_TEMPLATE_FN(vu_i_mfir)); return; case 0x3E: VU_DEC_LD_T_VIDST_S_VISRC(GET_TEMPLATE_FN(vu_i_ilwr)); return; case 0x3F: VU_DEC_LD_T_VISRC_S_VISRC(GET_TEMPLATE_FN(vu_i_iswr)); return; case 0x40: VU_DEC_LD_T_DST(GET_TEMPLATE_FN(vu_i_rnext)); return; case 0x41: VU_DEC_LD_T_DST(GET_TEMPLATE_FN(vu_i_rget)); return; case 0x42: VU_DEC_LD_S_SF_SRC(vu_i_rinit); return; case 0x43: VU_DEC_LD_S_SF_SRC(vu_i_rxor); return; case 0x64: VU_DEC_LD_T_DST(GET_TEMPLATE_FN(vu_i_mfp)); return; case 0x68: VU_DEC_LD_T_VIDST(vu_i_xtop); return; case 0x69: VU_DEC_LD_T_VIDST(vu_i_xitop); return; case 0x6C: VU_DEC_LD_S_VISRC(vu_i_xgkick); return; case 0x70: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_esadd); return; case 0x71: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_ersadd); return; case 0x72: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_eleng); return; case 0x73: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_erleng); return; case 0x74: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y, vu_i_eatanxy); return; case 0x75: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Z, vu_i_eatanxz); return; case 0x76: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z | VU_FLD_W, vu_i_esum); return; case 0x78: VU_DEC_LD_S_SF_SRC(vu_i_esqrt); return; case 0x79: VU_DEC_LD_S_SF_SRC(vu_i_ersqrt); return; case 0x7A: VU_DEC_LD_S_SF_SRC(vu_i_ercpr); return; case 0x7B: VU_DEC_LD_NONE(vu_i_waitp); return; case 0x7C: VU_DEC_LD_S_SF_SRC(vu_i_esin); return; case 0x7D: VU_DEC_LD_S_SF_SRC(vu_i_eatan); return; case 0x7E: VU_DEC_LD_S_SF_SRC(vu_i_eexp); return; } } else { switch (opcode & 0x3F) { case 0x30: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_iadd); return; case 0x31: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_isub); return; case 0x32: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_iaddi); return; case 0x34: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_iand); return; case 0x35: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_ior); return; } } } break; } } static inline void vu_advance_fmac_pipeline(struct vu_state* vu) { vu->upper_pipeline[3] = vu->upper_pipeline[2]; vu->upper_pipeline[2] = vu->upper_pipeline[1]; vu->upper_pipeline[1] = vu->upper_pipeline[0]; vu->upper_pipeline[0].dst.reg = vu->upper.dst.reg; vu->upper_pipeline[0].dst.field = vu->upper.dst.field; vu->lower_pipeline[3] = vu->lower_pipeline[2]; vu->lower_pipeline[2] = vu->lower_pipeline[1]; vu->lower_pipeline[1] = vu->lower_pipeline[0]; vu->lower_pipeline[0].dst.reg = vu->lower.dst.reg; vu->lower_pipeline[0].dst.field = vu->lower.dst.field; } static inline int vu_get_fmac_stall_cycles(struct vu_state* vu) { for (int i = 0; i < 0x4; i++) { for (int j = 0; j < 2; j++) { if (vu->upper.src[j].reg == vu->lower_pipeline[i].dst.reg) { if (vu->upper.src[j].field & vu->lower_pipeline[i].dst.field) { return 4 - i; } } } } return 0; } vu_block* vu_find_block(struct vu_state* vu, uint32_t tpc) { if (tpc == vu->last_block_lookup_tpc) { return vu->last_block_ptr; } vu_block* block = &vu->block_cache[tpc]; if (!block->cycles) { return nullptr; } // Update cache for next lookup vu->last_block_lookup_tpc = tpc; vu->last_block_ptr = block; return block; } static int c = 0; vu_block* vu_cache_block(struct vu_state* vu, uint32_t tpc, int max_cycles) { vu_block* block = &vu->block_cache[tpc]; vu->block_cache_size++; block->tpc = tpc; block->cycles = 0; block->entries.clear(); // printf("vu: caching block at %04x\n", tpc); for (int i = 0; i < max_cycles; i++) { vu_block_entry entry = { 0 }; uint64_t liw = vu->micro_mem[tpc++ & 0x7ff]; uint32_t upper = liw >> 32; uint32_t lower = liw & 0xffffffff; // LOI consumes the raw lower word when the i-bit is set. entry.lower.opcode = lower; entry.i_bit = (upper & 0x80000000) != 0; entry.e_bit = (upper & 0x40000000) != 0; vu_decode_upper(vu, upper & 0x7ffffff); entry.upper = vu->upper; if (!entry.i_bit) { vu_decode_lower(vu, lower); entry.lower = vu->lower; entry.branch = vu->lower.branch; entry.hazard0 = vu->upper.dst.reg == vu->lower.src[0].reg; entry.hazard1 = vu->upper.dst.reg == vu->lower.src[1].reg; entry.hazard2 = vu->upper.dst.reg == vu->lower.dst.reg; entry.hazard3 = vu->lower.dst.reg == VU_REG_Q; } // If this entry is a branch or has the E bit set, we end the block here if (entry.branch || entry.e_bit) { i = max_cycles - 2; } block->cycles++; block->entries.push_back(entry); } // vu_dis_state ds; // ds.addr = block->tpc; // ds.print_address = 0; // ds.print_opcode = 0; // for (const vu_block_entry& entry : block->entries) { // char upper_buf[512]; // char lower_buf[512]; // printf(" %s %04x: %08x %08x %-40s %s\n", // entry.i_bit ? "I" : entry.e_bit ? "E" : " ", // ds.addr++, // entry.upper.opcode, // entry.lower.opcode, // vu_disassemble_upper(upper_buf, entry.upper.opcode, &ds), // vu_disassemble_lower(lower_buf, entry.lower.opcode, &ds) // ); // } // Prime fast lookup with a pointer known to be valid after this insertion. vu->last_block_lookup_tpc = block->tpc; vu->last_block_ptr = block; return block; } bool vu_execute_block_entry(struct vu_state* vu, const vu_block_entry& entry) { if (vu->q_delay) vu->q_delay--; vu_update_status(vu); if (entry.i_bit) { entry.upper.func(vu, &entry.upper); // LOI vu->i.u32 = entry.lower.opcode; } else { if (entry.hazard3 && vu->q_delay) vu->q_delay = 0; bool waitq = entry.lower.func == vu_i_waitq; if (!entry.upper.dst.reg) { entry.upper.func(vu, &entry.upper); entry.lower.func(vu, &entry.lower); } else if (entry.hazard0 || entry.hazard1 || waitq) { // Upper instruction writes to a register that the lower // instruction reads from. In this case the lower instruction // gets the previous value of the register, executing the lower // instruction first does the trick. // We also execute WAITQ first, since it will stall the pipeline // if the upper instruction reads Q entry.lower.func(vu, &entry.lower); entry.upper.func(vu, &entry.upper); } else if (entry.hazard2) { // Upper and lower instructions write to the same register. // In this case the upper instruction takes priority, so we // restore the value of the register after executing the lower // instruction. entry.upper.func(vu, &entry.upper); struct vu_reg128 tmp = vu->vf[entry.upper.dst.reg]; entry.lower.func(vu, &entry.lower); vu->vf[entry.upper.dst.reg] = tmp; } else { entry.upper.func(vu, &entry.upper); entry.lower.func(vu, &entry.lower); } } // vu_advance_fmac_pipeline(vu); vu->mac_pipeline[3] = vu->mac_pipeline[2]; vu->mac_pipeline[2] = vu->mac_pipeline[1]; vu->mac_pipeline[1] = vu->mac_pipeline[0]; vu->mac_pipeline[0] = vu->mac; vu->clip_pipeline[3] = vu->clip_pipeline[2]; vu->clip_pipeline[2] = vu->clip_pipeline[1]; vu->clip_pipeline[1] = vu->clip_pipeline[0]; vu->clip_pipeline[0] = vu->clip; if (vu->vi_backup_cycles) { vu->vi_backup_cycles--; if (!vu->vi_backup_cycles) { vu->vi_backup_reg = 0; vu->vi_backup_value = 0; } } return entry.e_bit; } bool vu_execute_block(struct vu_state* vu, vu_block* block) { bool e_bit = false; // printf("vu: Input TPC %04x\n", vu->tpc); for (const vu_block_entry& entry : block->entries) { vu->tpc = vu->next_tpc; vu->next_tpc = vu->tpc + 1; vu->tpc &= 0x7ff; vu->next_tpc &= 0x7ff; e_bit |= vu_execute_block_entry(vu, entry); } // printf("vu: Output TPC %04x\n", vu->tpc); return e_bit; } void vu_execute_program(struct vu_state* vu, uint32_t addr) { vu->tpc = addr; vu->next_tpc = addr + 1; vu->i_bit = 0; vu->e_bit = 0; while (true) { vu_block* block = vu_find_block(vu, vu->tpc); if (!block) { vu->cache_misses++; block = vu_cache_block(vu, vu->tpc, 64); } else { vu->cache_hits++; } if (vu_execute_block(vu, block)) break; } } void ps2_vu_write_vi(struct vu_state* vu, int index, uint32_t value) { switch (index) { case 0: return; case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: { vu->vi[index] = value & 0xffff; } break; case 16: { vu->status &= ~0xfc0; vu->status |= value & 0xfc0; } break; case 17: return; // MAC flag register, read-only case 18: { vu->clip = value & 0xffffff; } break; case 19: return; // VU revision register? read-only case 20: { vu->r.u32 = value & 0x7fffff; } break; case 21: { vu->i.u32 = value; } break; case 22: { vu->q.u32 = value; } break; case 23: return; case 24: { vu->cr[8] = value & 0xc0c; } break; case 25: return; case 26: return; // VU TPC register, read-only case 27: { vu->cmsar0 = value & 0xffff; } break; case 28: { // To-do: Handle FBRST vu->fbrst = value & 0xc0c; if (value & 2) { // Reset VU0 ps2_vu_reset(vu); } if (value & 0x200) { // Reset VU1 ps2_vu_reset(vu->vu1); } } break; case 29: return; // VU VPU-STAT register, read-only case 30: return; // VU reserved register, read-only case 31: { vu->cmsar1 = value & 0xffff; vu_execute_program(vu->vu1, vu->cmsar1 >> 3); } break; } } uint32_t ps2_vu_read_vi(struct vu_state* vu, int index) { switch (index) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: { return vu->vi[index]; } break; case 19: { // VU revision register return 0x2e30; } break; default: { return vu->cr[index - 16]; } break; } } void ps2_vu_reset(struct vu_state* vu) { for (int i = 0; i < 16; i++) vu->vi[i] = 0; for (int i = 0; i < 32; i++) { vu->vf[i].u32[0] = 0; vu->vf[i].u32[1] = 0; vu->vf[i].u32[2] = 0; vu->vf[i].u32[3] = 0; } vu->r.u32 = 0x3f800000; vu->i.u32 = 0; vu->q.u32 = 0; vu->clip = 0; vu->status = 0; vu->fbrst = 0; vu->cmsar0 = 0; vu->cmsar1 = 0; vu->mac = 0; vu->mac_pipeline[0] = 0; vu->mac_pipeline[1] = 0; vu->mac_pipeline[2] = 0; vu->mac_pipeline[3] = 0; vu->clip_pipeline[0] = 0; vu->clip_pipeline[1] = 0; vu->clip_pipeline[2] = 0; vu->clip_pipeline[3] = 0; vu->tpc = 0; vu->next_tpc = 1; vu->i_bit = 0; vu->e_bit = 0; vu->m_bit = 0; vu->d_bit = 0; vu->t_bit = 0; vu->q_delay = 0; vu->prev_q.u32 = 0; vu->last_block_lookup_tpc = ~0u; vu->last_block_ptr = nullptr; vu->block_cache_size = 0; vu->block_cache.clear(); vu->block_cache.resize(vu->micro_mem_size+1); vu->vf[0].w = 1.0; } void ps2_vu_decode_upper(struct vu_state* vu, uint32_t opcode) { vu_decode_upper(vu, opcode); } void ps2_vu_decode_lower(struct vu_state* vu, uint32_t opcode) { vu_decode_lower(vu, opcode); } void vu_execute_program_tpc(struct vu_state* vu) { vu_execute_program(vu, vu->tpc); } uint128_t* vu_get_vu_mem_ptr(struct vu_state* vu, uint32_t addr) { return &vu->vu_mem[addr & vu->vu_mem_size]; } uint64_t* vu_get_micro_mem_ptr(struct vu_state* vu, uint32_t addr) { return &vu->micro_mem[addr & vu->micro_mem_size]; } uint32_t vu_get_tpc(struct vu_state* vu) { return vu->tpc; } void ps2_vu_execute_lower(struct vu_state* vu, uint32_t opcode) { vu_decode_lower(vu, opcode); vu->lower.func(vu, &vu->lower); } void ps2_vu_execute_upper(struct vu_state* vu, uint32_t opcode) { vu_decode_upper(vu, opcode); vu->upper.func(vu, &vu->upper); } void vu_clear_block_cache(struct vu_state* vu) { vu->block_cache_size = 0; vu->block_cache.clear(); vu->block_cache.resize(vu->micro_mem_size+1); vu->last_block_lookup_tpc = ~0u; vu->last_block_ptr = nullptr; } void vu_invalidate_range(struct vu_state* vu, uint32_t addr, uint32_t size) { if (!size || vu->block_cache.empty()) { return; } const uint32_t word_mask = (uint32_t)vu->micro_mem_size; const uint32_t word_count = word_mask + 1; const uint32_t byte_mask = (word_mask << 3) | 7; const uint32_t byte_count = word_count << 3; if (size >= byte_count) { for (vu_block& block : vu->block_cache) { if (!block.cycles) { continue; } block.cycles = 0; block.entries.clear(); } vu->block_cache_size = 0; vu->last_block_lookup_tpc = ~0u; vu->last_block_ptr = nullptr; return; } const uint32_t start_byte = addr & byte_mask; const uint32_t start_word = start_byte >> 3; const uint32_t offset_in_word = start_byte & 7; const uint32_t invalid_word_count = (offset_in_word + size + 7) >> 3; int invalidated = 0; for (vu_block& block : vu->block_cache) { if (!block.cycles) { continue; } bool intersects = false; for (int i = 0; i < block.cycles; i++) { const uint32_t block_word = (block.tpc + (uint32_t)i) & word_mask; const uint32_t rel = (block_word - start_word) & word_mask; if (rel < invalid_word_count) { intersects = true; break; } } if (!intersects) { continue; } block.cycles = 0; block.entries.clear(); invalidated++; } if (!invalidated) { return; } vu->block_cache_size -= invalidated; if (vu->block_cache_size < 0) { vu->block_cache_size = 0; } vu->last_block_lookup_tpc = ~0u; vu->last_block_ptr = nullptr; } // #undef printf ================================================ FILE: src/ee/vu_def.hpp ================================================ #pragma once #include #include #include #include "vu.h" struct vu_block_entry { struct vu_instruction upper, lower; int e_bit; int i_bit; int hazard0; int hazard1; int hazard2; int hazard3; int branch; }; struct vu_block { std::vector entries; uint32_t tpc; int cycles = 0; }; struct vu_state { struct vu_reg128 vf[32]; uint16_t vi[16]; struct vu_reg128 acc; std::vector block_cache; int block_cache_size; // Single-entry block cache for fast lookup (avoid hash computation) uint32_t last_block_lookup_tpc; struct vu_block* last_block_ptr; uint64_t cache_hits; uint64_t cache_misses; struct vu_instruction upper, lower; struct { struct { uint8_t reg; uint8_t field; } dst; } upper_pipeline[4], lower_pipeline[4]; int vi_backup_cycles; int vi_backup_reg; int vi_backup_value; uint64_t micro_mem[0x800]; uint128_t vu_mem[0x400]; int micro_mem_size; int vu_mem_size; int id; int i_bit; int e_bit; int m_bit; int d_bit; int t_bit; uint32_t next_tpc; // MAC flags pipeline uint32_t mac_pipeline[4]; uint32_t clip_pipeline[4]; int q_delay; struct vu_reg32 prev_q; struct vu_reg32 p; int xgkick_pending; int xgkick_addr; union { uint32_t cr[16]; struct { uint32_t status; uint32_t mac; uint32_t clip; uint32_t rsv0; struct vu_reg32 r; struct vu_reg32 i; struct vu_reg32 q; uint32_t rsv1; uint32_t rsv2; uint32_t rsv3; uint32_t tpc; uint32_t cmsar0; uint32_t fbrst; uint32_t vpu_stat; uint32_t rsv4; uint32_t cmsar1; }; }; struct ps2_gif* gif; struct ps2_vif* vif; struct vu_state* vu1; }; // Upper pipeline template void vu_i_abs(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_add(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_addi(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_addq(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_addx(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_addy(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_addz(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_addw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_adda(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_addai(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_addaq(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_addax(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_adday(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_addaz(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_addaw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_sub(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_subi(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_subq(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_subx(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_suby(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_subz(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_subw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_suba(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_subai(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_subaq(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_subax(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_subay(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_subaz(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_subaw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mul(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_muli(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mulq(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mulx(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_muly(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mulz(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mulw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mula(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mulai(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mulaq(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mulax(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mulay(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mulaz(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mulaw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_madd(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maddi(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maddq(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maddx(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maddy(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maddz(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maddw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_madda(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maddai(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maddaq(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maddax(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_madday(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maddaz(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maddaw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msub(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msubi(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msubq(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msubx(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msuby(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msubz(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msubw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msuba(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msubai(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msubaq(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msubax(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msubay(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msubaz(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_msubaw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_max(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maxi(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maxx(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maxy(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maxz(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_maxw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mini(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_minii(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_minix(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_miniy(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_miniz(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_miniw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_ftoi0(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_ftoi4(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_ftoi12(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_ftoi15(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_itof0(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_itof4(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_itof12(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_itof15(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_opmula(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_opmsub(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_nop(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_clip(struct vu_state* vu, const struct vu_instruction* ins); // Lower pipeline template void vu_i_ilw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_ilwr(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_isw(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_iswr(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_lq(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_lqd(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_lqi(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mfir(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mfp(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_move(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_mr32(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_rget(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_rnext(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_sq(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_sqd(struct vu_state* vu, const struct vu_instruction* ins); template void vu_i_sqi(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_b(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_bal(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_div(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_eatan(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_eatanxy(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_eatanxz(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_eexp(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_eleng(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_ercpr(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_erleng(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_ersadd(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_ersqrt(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_esadd(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_esin(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_esqrt(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_esum(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_fcand(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_fceq(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_fcget(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_fcor(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_fcset(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_fmand(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_fmeq(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_fmor(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_fsand(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_fseq(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_fsor(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_fsset(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_iadd(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_iaddi(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_iaddiu(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_iand(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_ibeq(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_ibgez(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_ibgtz(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_iblez(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_ibltz(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_ibne(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_ior(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_isub(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_isubiu(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_jalr(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_jr(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_mtir(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_rinit(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_rsqrt(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_rxor(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_sqrt(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_waitp(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_waitq(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_xgkick(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_xitop(struct vu_state* vu, const struct vu_instruction* ins); void vu_i_xtop(struct vu_state* vu, const struct vu_instruction* ins); ================================================ FILE: src/ee/vu_dis.c ================================================ #include #include "vu_dis.h" #define VU_LD_DEST ((o >> 21) & 0xf) #define VU_LD_DI(i) (o & (1 << (24 - i))) #define VU_LD_DX ((o >> 24) & 1) #define VU_LD_DY ((o >> 23) & 1) #define VU_LD_DZ ((o >> 22) & 1) #define VU_LD_DW ((o >> 21) & 1) #define VU_LD_D ((o >> 6) & 0x1f) #define VU_LD_S ((o >> 11) & 0x1f) #define VU_LD_T ((o >> 16) & 0x1f) #define VU_LD_SF ((o >> 21) & 3) #define VU_LD_TF ((o >> 23) & 3) #define VU_LD_IMM5 (((int32_t)(VU_LD_D << 27)) >> 27) #define VU_LD_IMM11 (((int32_t)((o & 0x7ff) << 21)) >> 21) #define VU_LD_IMM12 (o & 0x7ff) #define VU_LD_IMM15 ((o & 0x7ff) | ((o & 0x1e00000) >> 10)) #define VU_LD_IMM24 (o & 0xffffff) #define VU_UD_DEST ((o >> 21) & 0xf) #define VU_UD_DI(i) (o & (1 << (24 - i))) #define VU_UD_DX ((o >> 24) & 1) #define VU_UD_DY ((o >> 23) & 1) #define VU_UD_DZ ((o >> 22) & 1) #define VU_UD_DW ((o >> 21) & 1) #define VU_UD_D ((o >> 6) & 0x1f) #define VU_UD_S ((o >> 11) & 0x1f) #define VU_UD_T ((o >> 16) & 0x1f) // Print broadcast fields static inline char* vu_d_bc(struct vu_dis_state* s, char* p, uint32_t bc) { if (!bc) return p; *p++ = '.'; for (int i = 0; i < 4; i++) { if (bc & (1 << (3 - i))) *p++ = "xyzw"[i]; } *p = '\0'; return p; } static inline char* vu_d_mnemonic(struct vu_dis_state* s, char* p, const char* m, uint32_t bc) { char buf[32]; char* ptr = buf; ptr += sprintf(ptr, "%s", m); ptr = vu_d_bc(s, ptr, bc); p += sprintf(p, "%-12s", buf); return p; } static inline char* vu_d_mnemonic_nd(struct vu_dis_state* s, char* p, const char* m) { p += sprintf(p, "%-12s", m); return p; } static inline char* vu_d_addax(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_adday(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_addaz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_addaw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_subax(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_subay(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_subaz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_subaw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maddax(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_madday(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maddaz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maddaw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msubax(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msubay(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msubaz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msubaw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_itof0(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_itof4(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_itof12(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_itof15(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_ftoi0(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_ftoi4(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_ftoi12(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_ftoi15(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mulax(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mulay(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mulaz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mulaw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mulaq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_abs(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mulai(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_clip(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_addaq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maddaq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_addai(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maddai(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_subaq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msubaq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_subai(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msubai(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_adda(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_madda(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mula(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_suba(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msuba(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_opmula(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_nop(struct vu_dis_state* s, char* p, uint32_t o) { p += sprintf(p, "%s", "nop"); return p; } static inline char* vu_d_addx(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_addy(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_addz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_addw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_subx(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_suby(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_subz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_subw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maddx(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maddy(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maddz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maddw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msubx(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msuby(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msubz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msubw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maxx(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maxy(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maxz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maxw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_minix(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_miniy(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_miniz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_miniw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mulx(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_muly(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mulz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mulw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mulq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maxi(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_muli(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_minii(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_addq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maddq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_addi(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_maddi(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_subq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msubq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_subi(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msubi(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_add(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_madd(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mul(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_max(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_sub(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_msub(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_opmsub(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mini(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_lq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_sq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_ilw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_isw(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_iaddiu(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_isubiu(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_fceq(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "fceq"); p += sprintf(p, "vi01, 0x%06x", VU_LD_IMM24); return p; } static inline char* vu_d_fcset(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "fcset"); p += sprintf(p, "0x%06x", VU_LD_IMM24); return p; } static inline char* vu_d_fcand(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "fcand"); p += sprintf(p, "vi01, 0x%06x", VU_LD_IMM24); return p; } static inline char* vu_d_fcor(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "fcor"); p += sprintf(p, "vi01, 0x%06x", VU_LD_IMM24); return p; } static inline char* vu_d_fseq(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "fseq"); p += sprintf(p, "0x%04x", VU_LD_IMM12); return p; } static inline char* vu_d_fsset(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "fsset"); p += sprintf(p, "0x%04x", VU_LD_IMM12); return p; } static inline char* vu_d_fsand(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "fsand"); p += sprintf(p, "0x%04x", VU_LD_IMM12); return p; } static inline char* vu_d_fsor(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "fsor"); p += sprintf(p, "0x%04x", VU_LD_IMM12); return p; } static inline char* vu_d_fmeq(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "fmeq"); p += sprintf(p, "vi%02u, vi%02u", VU_LD_T, VU_LD_S); return p; } static inline char* vu_d_fmand(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "fmand"); p += sprintf(p, "vi%02u, vi%02u", VU_LD_T, VU_LD_S); return p; } static inline char* vu_d_fmor(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "fmor"); p += sprintf(p, "vi%02u, vi%02u", VU_LD_T, VU_LD_S); return p; } static inline char* vu_d_fcget(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "fcget"); p += sprintf(p, "vi%02u", VU_LD_T); return p; } static inline char* vu_d_b(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "b"); p += sprintf(p, "%04x", s ? (s->addr + 1 + VU_LD_IMM11) : VU_LD_IMM11); return p; } static inline char* vu_d_bal(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_jr(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "jr"); p += sprintf(p, "vi%02u", VU_LD_S); return p; } static inline char* vu_d_jalr(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "jalr"); p += sprintf(p, "vi%02u, vi%02u", VU_LD_T, VU_LD_S); return p; } static inline char* vu_d_ibeq(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_ibne(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_ibltz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_ibgtz(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_iblez(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_ibgez(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_move(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mr32(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_lqi(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_sqi(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_lqd(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_sqd(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_div(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_sqrt(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "sqrt"); p += sprintf(p, "q, vf%02u%c", VU_LD_T, "xyzw"[VU_LD_TF]); return p; } static inline char* vu_d_rsqrt(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_waitq(struct vu_dis_state* s, char* p, uint32_t o) { p += sprintf(p, "waitq"); return p; } static inline char* vu_d_mtir(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_mfir(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_ilwr(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_iswr(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_rnext(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic(s, p, "rnext", VU_LD_DEST); p += sprintf(p, "vf%02u, r", VU_LD_T); return p; } static inline char* vu_d_rget(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic(s, p, "rget", VU_LD_DEST); p += sprintf(p, "vf%02u, r", VU_LD_T); return p; } static inline char* vu_d_rinit(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "rinit"); p += sprintf(p, "r, vf%02u%c", VU_LD_S, "xyzw"[VU_LD_SF]); return p; } static inline char* vu_d_rxor(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "rxor"); p += sprintf(p, "r, vf%02u%c", VU_LD_S, "xyzw"[VU_LD_SF]); return p; } static inline char* vu_d_mfp(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic(s, p, "mfp", VU_LD_DEST); p += sprintf(p, "vf%02u, p", VU_LD_T); return p; } static inline char* vu_d_xtop(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "xtop"); p += sprintf(p, "vi%02u", VU_LD_T); return p; } static inline char* vu_d_xitop(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "xitop"); p += sprintf(p, "vi%02u", VU_LD_T); return p; } static inline char* vu_d_xgkick(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "xgkick"); p += sprintf(p, "vi%02u", VU_LD_S); return p; } static inline char* vu_d_esadd(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "esadd"); p += sprintf(p, "p, vf%02u", VU_LD_S); return p; } static inline char* vu_d_ersadd(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "ersadd"); p += sprintf(p, "p, vf%02u", VU_LD_S); return p; } static inline char* vu_d_eleng(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "eleng"); p += sprintf(p, "p, vf%02u", VU_LD_S); return p; } static inline char* vu_d_erleng(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "erleng"); p += sprintf(p, "p, vf%02u", VU_LD_S); return p; } static inline char* vu_d_eatanxy(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "eatan.xy"); p += sprintf(p, "p, vf%02u", VU_LD_S); return p; } static inline char* vu_d_eatanxz(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "eatan.xz"); p += sprintf(p, "p, vf%02u", VU_LD_S); return p; } static inline char* vu_d_esum(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "esum"); p += sprintf(p, "p, vf%02u", VU_LD_S); return p; } static inline char* vu_d_esqrt(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "esqrt"); p += sprintf(p, "p, vf%02u%c", VU_LD_S, "xyzw"[VU_LD_SF]); return p; } static inline char* vu_d_ersqrt(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "ersqrt"); p += sprintf(p, "p, vf%02u%c", VU_LD_S, "xyzw"[VU_LD_SF]); return p; } static inline char* vu_d_ercpr(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "ercpr"); p += sprintf(p, "p, vf%02u%c", VU_LD_S, "xyzw"[VU_LD_SF]); return p; } static inline char* vu_d_waitp(struct vu_dis_state* s, char* p, uint32_t o) { p += sprintf(p, "waitp"); return p; } static inline char* vu_d_esin(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "esin"); p += sprintf(p, "p, vf%02u%c", VU_LD_S, "xyzw"[VU_LD_SF]); return p; } static inline char* vu_d_eatan(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "eatan"); p += sprintf(p, "p, vf%02u%c", VU_LD_S, "xyzw"[VU_LD_SF]); return p; } static inline char* vu_d_eexp(struct vu_dis_state* s, char* p, uint32_t o) { p = vu_d_mnemonic_nd(s, p, "eexp"); p += sprintf(p, "p, vf%02u%c", VU_LD_S, "xyzw"[VU_LD_SF]); return p; } static inline char* vu_d_iadd(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_isub(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_iaddi(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_iand(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_ior(struct vu_dis_state* s, char* p, uint32_t o) { 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; } static inline char* vu_d_invalid(struct vu_dis_state* s, char* p, uint32_t o) { p += sprintf(p, ""); return p; } char* vu_disassemble_upper(char* buf, uint64_t opcode, struct vu_dis_state* s) { char* ptr = buf; ptr = buf; if (s) if (s->print_address) ptr += sprintf(ptr, "%08x: ", s->addr); if (s) if (s->print_opcode) ptr += sprintf(ptr, "%08x ", opcode); // Decode 000007FF style instruction if ((opcode & 0x3c) == 0x3c) { switch (((opcode & 0x3c0) >> 4) | (opcode & 3)) { case 0x00: vu_d_addax(s, ptr, opcode); return buf; case 0x01: vu_d_adday(s, ptr, opcode); return buf; case 0x02: vu_d_addaz(s, ptr, opcode); return buf; case 0x03: vu_d_addaw(s, ptr, opcode); return buf; case 0x04: vu_d_subax(s, ptr, opcode); return buf; case 0x05: vu_d_subay(s, ptr, opcode); return buf; case 0x06: vu_d_subaz(s, ptr, opcode); return buf; case 0x07: vu_d_subaw(s, ptr, opcode); return buf; case 0x08: vu_d_maddax(s, ptr, opcode); return buf; case 0x09: vu_d_madday(s, ptr, opcode); return buf; case 0x0A: vu_d_maddaz(s, ptr, opcode); return buf; case 0x0B: vu_d_maddaw(s, ptr, opcode); return buf; case 0x0C: vu_d_msubax(s, ptr, opcode); return buf; case 0x0D: vu_d_msubay(s, ptr, opcode); return buf; case 0x0E: vu_d_msubaz(s, ptr, opcode); return buf; case 0x0F: vu_d_msubaw(s, ptr, opcode); return buf; case 0x10: vu_d_itof0(s, ptr, opcode); return buf; case 0x11: vu_d_itof4(s, ptr, opcode); return buf; case 0x12: vu_d_itof12(s, ptr, opcode); return buf; case 0x13: vu_d_itof15(s, ptr, opcode); return buf; case 0x14: vu_d_ftoi0(s, ptr, opcode); return buf; case 0x15: vu_d_ftoi4(s, ptr, opcode); return buf; case 0x16: vu_d_ftoi12(s, ptr, opcode); return buf; case 0x17: vu_d_ftoi15(s, ptr, opcode); return buf; case 0x18: vu_d_mulax(s, ptr, opcode); return buf; case 0x19: vu_d_mulay(s, ptr, opcode); return buf; case 0x1A: vu_d_mulaz(s, ptr, opcode); return buf; case 0x1B: vu_d_mulaw(s, ptr, opcode); return buf; case 0x1C: vu_d_mulaq(s, ptr, opcode); return buf; case 0x1D: vu_d_abs(s, ptr, opcode); return buf; case 0x1E: vu_d_mulai(s, ptr, opcode); return buf; case 0x1F: vu_d_clip(s, ptr, opcode); return buf; case 0x20: vu_d_addaq(s, ptr, opcode); return buf; case 0x21: vu_d_maddaq(s, ptr, opcode); return buf; case 0x22: vu_d_addai(s, ptr, opcode); return buf; case 0x23: vu_d_maddai(s, ptr, opcode); return buf; case 0x24: vu_d_subaq(s, ptr, opcode); return buf; case 0x25: vu_d_msubaq(s, ptr, opcode); return buf; case 0x26: vu_d_subai(s, ptr, opcode); return buf; case 0x27: vu_d_msubai(s, ptr, opcode); return buf; case 0x28: vu_d_adda(s, ptr, opcode); return buf; case 0x29: vu_d_madda(s, ptr, opcode); return buf; case 0x2A: vu_d_mula(s, ptr, opcode); return buf; case 0x2C: vu_d_suba(s, ptr, opcode); return buf; case 0x2D: vu_d_msuba(s, ptr, opcode); return buf; case 0x2E: vu_d_opmula(s, ptr, opcode); return buf; case 0x2F: vu_d_nop(s, ptr, opcode); return buf; } } else { // Decode 0000003F style instruction switch (opcode & 0x3f) { case 0x00: vu_d_addx(s, ptr, opcode); return buf; case 0x01: vu_d_addy(s, ptr, opcode); return buf; case 0x02: vu_d_addz(s, ptr, opcode); return buf; case 0x03: vu_d_addw(s, ptr, opcode); return buf; case 0x04: vu_d_subx(s, ptr, opcode); return buf; case 0x05: vu_d_suby(s, ptr, opcode); return buf; case 0x06: vu_d_subz(s, ptr, opcode); return buf; case 0x07: vu_d_subw(s, ptr, opcode); return buf; case 0x08: vu_d_maddx(s, ptr, opcode); return buf; case 0x09: vu_d_maddy(s, ptr, opcode); return buf; case 0x0A: vu_d_maddz(s, ptr, opcode); return buf; case 0x0B: vu_d_maddw(s, ptr, opcode); return buf; case 0x0C: vu_d_msubx(s, ptr, opcode); return buf; case 0x0D: vu_d_msuby(s, ptr, opcode); return buf; case 0x0E: vu_d_msubz(s, ptr, opcode); return buf; case 0x0F: vu_d_msubw(s, ptr, opcode); return buf; case 0x10: vu_d_maxx(s, ptr, opcode); return buf; case 0x11: vu_d_maxy(s, ptr, opcode); return buf; case 0x12: vu_d_maxz(s, ptr, opcode); return buf; case 0x13: vu_d_maxw(s, ptr, opcode); return buf; case 0x14: vu_d_minix(s, ptr, opcode); return buf; case 0x15: vu_d_miniy(s, ptr, opcode); return buf; case 0x16: vu_d_miniz(s, ptr, opcode); return buf; case 0x17: vu_d_miniw(s, ptr, opcode); return buf; case 0x18: vu_d_mulx(s, ptr, opcode); return buf; case 0x19: vu_d_muly(s, ptr, opcode); return buf; case 0x1A: vu_d_mulz(s, ptr, opcode); return buf; case 0x1B: vu_d_mulw(s, ptr, opcode); return buf; case 0x1C: vu_d_mulq(s, ptr, opcode); return buf; case 0x1D: vu_d_maxi(s, ptr, opcode); return buf; case 0x1E: vu_d_muli(s, ptr, opcode); return buf; case 0x1F: vu_d_minii(s, ptr, opcode); return buf; case 0x20: vu_d_addq(s, ptr, opcode); return buf; case 0x21: vu_d_maddq(s, ptr, opcode); return buf; case 0x22: vu_d_addi(s, ptr, opcode); return buf; case 0x23: vu_d_maddi(s, ptr, opcode); return buf; case 0x24: vu_d_subq(s, ptr, opcode); return buf; case 0x25: vu_d_msubq(s, ptr, opcode); return buf; case 0x26: vu_d_subi(s, ptr, opcode); return buf; case 0x27: vu_d_msubi(s, ptr, opcode); return buf; case 0x28: vu_d_add(s, ptr, opcode); return buf; case 0x29: vu_d_madd(s, ptr, opcode); return buf; case 0x2A: vu_d_mul(s, ptr, opcode); return buf; case 0x2B: vu_d_max(s, ptr, opcode); return buf; case 0x2C: vu_d_sub(s, ptr, opcode); return buf; case 0x2D: vu_d_msub(s, ptr, opcode); return buf; case 0x2E: vu_d_opmsub(s, ptr, opcode); return buf; case 0x2F: vu_d_mini(s, ptr, opcode); return buf; } } vu_d_invalid(s, ptr, opcode); return buf; } char* vu_disassemble_lower(char* buf, uint64_t opcode, struct vu_dis_state* s) { char* ptr = buf; ptr = buf; if (s) if (s->print_address) ptr += sprintf(ptr, "%08x: ", s->addr); if (s) if (s->print_opcode) ptr += sprintf(ptr, "%08x ", opcode); switch ((opcode & 0xFE000000) >> 25) { case 0x00: vu_d_lq(s, ptr, opcode); return buf; case 0x01: vu_d_sq(s, ptr, opcode); return buf; case 0x04: vu_d_ilw(s, ptr, opcode); return buf; case 0x05: vu_d_isw(s, ptr, opcode); return buf; case 0x08: vu_d_iaddiu(s, ptr, opcode); return buf; case 0x09: vu_d_isubiu(s, ptr, opcode); return buf; case 0x10: vu_d_fceq(s, ptr, opcode); return buf; case 0x11: vu_d_fcset(s, ptr, opcode); return buf; case 0x12: vu_d_fcand(s, ptr, opcode); return buf; case 0x13: vu_d_fcor(s, ptr, opcode); return buf; case 0x14: vu_d_fseq(s, ptr, opcode); return buf; case 0x15: vu_d_fsset(s, ptr, opcode); return buf; case 0x16: vu_d_fsand(s, ptr, opcode); return buf; case 0x17: vu_d_fsor(s, ptr, opcode); return buf; case 0x18: vu_d_fmeq(s, ptr, opcode); return buf; case 0x1A: vu_d_fmand(s, ptr, opcode); return buf; case 0x1B: vu_d_fmor(s, ptr, opcode); return buf; case 0x1C: vu_d_fcget(s, ptr, opcode); return buf; case 0x20: vu_d_b(s, ptr, opcode); return buf; case 0x21: vu_d_bal(s, ptr, opcode); return buf; case 0x24: vu_d_jr(s, ptr, opcode); return buf; case 0x25: vu_d_jalr(s, ptr, opcode); return buf; case 0x28: vu_d_ibeq(s, ptr, opcode); return buf; case 0x29: vu_d_ibne(s, ptr, opcode); return buf; case 0x2C: vu_d_ibltz(s, ptr, opcode); return buf; case 0x2D: vu_d_ibgtz(s, ptr, opcode); return buf; case 0x2E: vu_d_iblez(s, ptr, opcode); return buf; case 0x2F: vu_d_ibgez(s, ptr, opcode); return buf; case 0x40: { if ((opcode & 0x3C) == 0x3C) { switch (((opcode & 0x7C0) >> 4) | (opcode & 3)) { case 0x30: vu_d_move(s, ptr, opcode); return buf; case 0x31: vu_d_mr32(s, ptr, opcode); return buf; case 0x34: vu_d_lqi(s, ptr, opcode); return buf; case 0x35: vu_d_sqi(s, ptr, opcode); return buf; case 0x36: vu_d_lqd(s, ptr, opcode); return buf; case 0x37: vu_d_sqd(s, ptr, opcode); return buf; case 0x38: vu_d_div(s, ptr, opcode); return buf; case 0x39: vu_d_sqrt(s, ptr, opcode); return buf; case 0x3A: vu_d_rsqrt(s, ptr, opcode); return buf; case 0x3B: vu_d_waitq(s, ptr, opcode); return buf; case 0x3C: vu_d_mtir(s, ptr, opcode); return buf; case 0x3D: vu_d_mfir(s, ptr, opcode); return buf; case 0x3E: vu_d_ilwr(s, ptr, opcode); return buf; case 0x3F: vu_d_iswr(s, ptr, opcode); return buf; case 0x40: vu_d_rnext(s, ptr, opcode); return buf; case 0x41: vu_d_rget(s, ptr, opcode); return buf; case 0x42: vu_d_rinit(s, ptr, opcode); return buf; case 0x43: vu_d_rxor(s, ptr, opcode); return buf; case 0x64: vu_d_mfp(s, ptr, opcode); return buf; case 0x68: vu_d_xtop(s, ptr, opcode); return buf; case 0x69: vu_d_xitop(s, ptr, opcode); return buf; case 0x6C: vu_d_xgkick(s, ptr, opcode); return buf; case 0x70: vu_d_esadd(s, ptr, opcode); return buf; case 0x71: vu_d_ersadd(s, ptr, opcode); return buf; case 0x72: vu_d_eleng(s, ptr, opcode); return buf; case 0x73: vu_d_erleng(s, ptr, opcode); return buf; case 0x74: vu_d_eatanxy(s, ptr, opcode); return buf; case 0x75: vu_d_eatanxz(s, ptr, opcode); return buf; case 0x76: vu_d_esum(s, ptr, opcode); return buf; case 0x78: vu_d_esqrt(s, ptr, opcode); return buf; case 0x79: vu_d_ersqrt(s, ptr, opcode); return buf; case 0x7A: vu_d_ercpr(s, ptr, opcode); return buf; case 0x7B: vu_d_waitp(s, ptr, opcode); return buf; case 0x7C: vu_d_esin(s, ptr, opcode); return buf; case 0x7D: vu_d_eatan(s, ptr, opcode); return buf; case 0x7E: vu_d_eexp(s, ptr, opcode); return buf; } } else { switch (opcode & 0x3F) { case 0x30: vu_d_iadd(s, ptr, opcode); return buf; case 0x31: vu_d_isub(s, ptr, opcode); return buf; case 0x32: vu_d_iaddi(s, ptr, opcode); return buf; case 0x34: vu_d_iand(s, ptr, opcode); return buf; case 0x35: vu_d_ior(s, ptr, opcode); return buf; } } } break; } vu_d_invalid(s, ptr, opcode); return buf; } ================================================ FILE: src/ee/vu_dis.h ================================================ #ifndef VU_DIS_H #define VU_DIS_H #ifdef __cplusplus extern "C" { #endif #include struct vu_dis_state { int print_address; int print_opcode; uint32_t addr; }; char* vu_disassemble_upper(char* buf, uint64_t opcode, struct vu_dis_state* s); char* vu_disassemble_lower(char* buf, uint64_t opcode, struct vu_dis_state* s); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ee/vu_uncached.c ================================================ #include #include #include #include #include #include "vu.h" #include "vu_dis.h" // #define printf(fmt, ...)(0) #define VU_LD_DI(i) (ins->ld_di[i]) #define VU_LD_D (ins->ld_d) #define VU_LD_S (ins->ld_s) #define VU_LD_T (ins->ld_t) #define VU_LD_SF (ins->ld_sf) #define VU_LD_TF (ins->ld_tf) #define VU_LD_IMM5 (ins->ld_imm5) #define VU_LD_IMM11 (ins->ld_imm11) #define VU_LD_IMM12 (ins->ld_imm12) #define VU_LD_IMM15 (ins->ld_imm15) #define VU_LD_IMM24 (ins->ld_imm24) #define VU_ID vu->vi[VU_LD_D] #define VU_IS vu->vi[VU_LD_S] #define VU_IT vu->vi[VU_LD_T] #define VU_UD_DI(i) (ins->ud_di[i]) #define VU_UD_D (ins->ud_d) #define VU_UD_S (ins->ud_s) #define VU_UD_T (ins->ud_t) struct vu_state* vu_create(void) { return (struct vu_state*)malloc(sizeof(struct vu_state)); } void vu_init(struct vu_state* vu, int id, struct ps2_gif* gif, struct ps2_vif* vif, struct vu_state* vu1) { memset(vu, 0, sizeof(struct vu_state)); vu->id = id; vu->vu1 = vu1; vu->vif = vif; vu->gif = gif; if (!id) { vu->micro_mem_size = 0x1ff; vu->vu_mem_size = 0xff; } else { vu->micro_mem_size = 0x7ff; vu->vu_mem_size = 0x3ff; } vu->vf[0].x = 0.0; vu->vf[0].y = 0.0; vu->vf[0].z = 0.0; vu->vf[0].w = 1.0; // VU uses round to zero by default fesetround(FE_TOWARDZERO); } void vu_destroy(struct vu_state* vu) { free(vu); } #define max(a, b) ((a) > (b) ? (a) : (b)) #define min(a, b) ((a) < (b) ? (a) : (b)) static inline uint32_t vu_max(int32_t a, int32_t b) { return (a < 0 && b < 0) ? min(a, b) : max(a, b); } static inline uint32_t vu_min(int32_t a, int32_t b) { return (a < 0 && b < 0) ? max(a, b) : min(a, b); } static inline float vu_atan(float t) { //In reality, VU1 uses an approximation to derive the result. This is shown here. const static float atan_const[] = { 0.999999344348907f, -0.333298563957214f, 0.199465364217758f, -0.139085337519646f, 0.096420042216778f, -0.055909886956215f, 0.021861229091883f, -0.004054057877511f }; float result = 0.785398185253143f; // pi/4 for (int i = 0; i < 8; i++) { result += atan_const[i] * powf(t, (i * 2) + 1); } return result; } static inline void vu_update_status(struct vu_state* vu) { vu->status &= ~0x3f; vu->status |= (vu->mac_pipeline[3] & 0x000f) ? 1 : 0; vu->status |= (vu->mac_pipeline[3] & 0x00f0) ? 2 : 0; vu->status |= (vu->mac_pipeline[3] & 0x0f00) ? 4 : 0; vu->status |= (vu->mac_pipeline[3] & 0xf000) ? 8 : 0; vu->status |= (vu->status & 0x3f) << 6; } static inline void vu_set_q(struct vu_state* vu, float value, int delay) { if (vu->q_delay == 0) { vu->prev_q.f = vu->q.f; } vu->q.f = value; vu->q_delay = delay; } static inline struct vu_reg32 vu_get_q(struct vu_state* vu) { if (!vu->q_delay) { return vu->q; } return vu->prev_q; } static inline float vu_update_flags(struct vu_state* vu, float value, int index) { uint32_t value_u = *(uint32_t*)&value; int flag_id = 3 - index; // Sign flag if (value_u & 0x80000000) vu->mac |= 0x10 << flag_id; else vu->mac &= ~(0x10 << flag_id); // Zero flag, clear under/overflow if ((value_u & 0x7FFFFFFF) == 0) { vu->mac |= 1 << flag_id; vu->mac &= ~(0x1100 << flag_id); return value; } switch ((value_u >> 23) & 0xFF) { //Underflow, set zero case 0: vu->mac |= 0x101 << flag_id; vu->mac &= ~(0x1000 << flag_id); value_u = value_u & 0x80000000; break; //Overflow case 255: vu->mac |= 0x1000 << flag_id; vu->mac &= ~(0x101 << flag_id); value_u = (value_u & 0x80000000) | 0x7F7FFFFF; break; //Clear all but sign default: vu->mac &= ~(0x1101 << flag_id); break; } return *(float*)&value_u; } static inline void vu_clear_flags(struct vu_state* vu, int index) { vu->mac &= ~(0x1111 << (3 - index)); } static inline float vu_cvtf(uint32_t value) { switch (value & 0x7f800000) { case 0x0: { value &= 0x80000000; return *(float*)&value; } break; case 0x7f800000: { uint32_t result = (value & 0x80000000) | 0x7f7fffff; return *(float*)&result; } } return *(float*)&value; } int32_t vu_cvti(float value) { if (value >= 2147483647.0) return 2147483647LL; if (value <= -2147483648.0) return -2147483648LL; return (int32_t)value; } static inline void vu_set_vf(struct vu_state* vu, int r, int f, float v) { if (r) vu->vf[r].f[f] = v; } static inline void vu_set_vfu(struct vu_state* vu, int r, int f, int32_t v) { if (r) vu->vf[r].s32[f] = v; } static inline void vu_set_vf_x(struct vu_state* vu, int r, float v) { if (r) vu->vf[r].x = v; } static inline void vu_set_vf_y(struct vu_state* vu, int r, float v) { if (r) vu->vf[r].y = v; } static inline void vu_set_vf_z(struct vu_state* vu, int r, float v) { if (r) vu->vf[r].z = v; } static inline void vu_set_vf_w(struct vu_state* vu, int r, float v) { if (r) vu->vf[r].w = v; } static inline void vu_set_vi(struct vu_state* vu, int r, uint16_t v) { if (r) vu->vi[r] = v; } static inline float vu_vf_i(struct vu_state* vu, int r, int i) { return vu_cvtf(vu->vf[r].u32[i]); } static inline float vu_vf_x(struct vu_state* vu, int r) { return vu_cvtf(vu->vf[r].u32[0]); } static inline float vu_vf_y(struct vu_state* vu, int r) { return vu_cvtf(vu->vf[r].u32[1]); } static inline float vu_vf_z(struct vu_state* vu, int r) { return vu_cvtf(vu->vf[r].u32[2]); } static inline float vu_vf_w(struct vu_state* vu, int r) { return vu_cvtf(vu->vf[r].u32[3]); } static inline float vu_acc_i(struct vu_state* vu, int i) { return vu_cvtf(vu->acc.u32[i]); } static inline void vu_mem_write(struct vu_state* vu, uint16_t addr, uint32_t data, int i) { if (!vu->id) { if (addr <= 0x3ff) { vu->vu_mem[addr & 0xff].u32[i] = data; } else { if ((addr >= 0x400) && (addr <= 0x41f)) { vu->vu1->vf[addr & 0x1f].u32[i] = data; } else if ((addr >= 0x420) && (addr <= 0x42f)) { vu->vu1->vi[addr & 0xf] = data; } else if (addr == 0x430) { vu->vu1->status = data; } else if (addr == 0x431) { vu->vu1->mac = data; } else if (addr == 0x432) { vu->vu1->clip = data; } else if (addr == 0x434) { vu->vu1->r.u32 = data; } else if (addr == 0x435) { vu->vu1->i.u32 = data; } else if (addr == 0x436) { vu->vu1->q.u32 = data; } else if (addr == 0x437) { vu->vu1->p.u32 = data; } else if (addr == 0x43a) { vu->vu1->tpc = data; } else { // printf("vu: oob write\n"); // exit(1); } } } else { // if (addr == 0x000001d3) *(int*)0 = 0; vu->vu_mem[addr & 0x3ff].u32[i] = data; } } static inline uint128_t vu_mem_read(struct vu_state* vu, uint32_t addr) { if (!vu->id) { if (addr <= 0x3ff) { return vu->vu_mem[addr & 0xff]; } else { if ((addr >= 0x400) && (addr <= 0x41f)) { return vu->vu1->vf[addr & 0x1f].u128; } else if ((addr >= 0x420) && (addr <= 0x42f)) { uint128_t result; result.u32[0] = vu->vu1->vi[addr & 0xf]; return result; } else if (addr == 0x430) { uint128_t result; result.u32[0] = vu->vu1->status; return result; } else if (addr == 0x431) { uint128_t result; result.u32[0] = vu->vu1->mac; return result; } else if (addr == 0x432) { uint128_t result; result.u32[0] = vu->vu1->clip; return result; } else if (addr == 0x434) { uint128_t result; result.u32[0] = vu->vu1->r.u32; return result; } else if (addr == 0x435) { uint128_t result; result.u32[0] = vu->vu1->i.u32; return result; } else if (addr == 0x436) { uint128_t result; result.u32[0] = vu->vu1->q.u32; return result; } else if (addr == 0x437) { uint128_t result; result.u32[0] = vu->vu1->p.u32; return result; } else if (addr == 0x43a) { uint128_t result; result.u32[0] = vu->vu1->tpc; return result; } } } return vu->vu_mem[addr & 0x3ff]; } static inline void vu_write_branch_pipeline(struct vu_state* vu, int dst) { if (!dst) return; //On repeat writes we need to remember the value from before the chain if (vu->vi_backup_cycles && dst == vu->vi_backup_reg) { vu->vi_backup_cycles = 2; return; } vu->vi_backup_cycles = 2; vu->vi_backup_reg = dst; vu->vi_backup_value = vu->vi[dst]; // printf("branch pipeline: dst=%d prev=%04x rw=%d\n", // vu->branch_pipeline_curr.reg, vu->branch_pipeline_curr.prev, // vu->branch_pipeline_curr.rw // ); } static inline uint16_t vu_get_branch_register(struct vu_state* vu, int reg) { if (vu->vi_backup_cycles && (vu->vi_backup_reg == reg)) { return vu->vi_backup_value; } return vu->vi[reg]; } void vu_xgkick(struct vu_state* vu) { uint16_t addr = vu->xgkick_addr; int eop = 1; do { uint128_t tag = vu_mem_read(vu, addr++); if ((tag.u64[0] | tag.u64[1]) == 0) break; // addr &= 0x3ff; // if (addr == 0) break; // printf("tag: addr=%08x %08x %08x %08x %08x\n", addr - 1, tag.u32[3], tag.u32[2], tag.u32[1], tag.u32[0]); ps2_gif_fifo_write(vu->gif, tag, GIF_PATH1); eop = (tag.u64[0] & 0x8000) != 0; int nloop = tag.u64[0] & 0x7fff; int flg = (tag.u64[0] >> 58) & 3; int nregs = (tag.u64[0] >> 60) & 0xf; if (!nloop) continue; // if (!nregs) // nregs = 16; int qwc = 0; switch (flg) { case 0: { qwc = nregs * nloop; } break; case 1: { qwc = (nregs * nloop + 1) / 2; // Round up for odd cases } break; case 2: case 3: { qwc = nloop; } break; } if (qwc >= 0x400) { fprintf(stderr, "vu: Weird xgkick tag nloop=%d nregs=%d eop=%d flg=%d qwc=%d\n", nloop, nregs, eop, flg, qwc ); exit(1); } for (int i = 0; i < qwc; i++) { // printf("vu: %08x: %08x %08x %08x %08x\n", // addr, // vu->vu_mem[addr].u32[3], // vu->vu_mem[addr].u32[2], // vu->vu_mem[addr].u32[1], // vu->vu_mem[addr].u32[0] // ); ps2_gif_fifo_write(vu->gif, vu_mem_read(vu, addr++), GIF_PATH1); addr &= 0x3ff; // if (addr == 0) { // eop = 1; // break; // } } } while (!eop); } // Upper pipeline void vu_i_abs(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) vu_set_vf(vu, t, i, fabsf(vu_vf_i(vu, s, i))); } } void vu_i_add(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + vu_vf_i(vu, t, i); vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_addi(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + vu->i.f; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_addq(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; float q = vu_get_q(vu).f; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + q; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_addx(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_addy(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_addz(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_addw(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_adda(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + vu_vf_i(vu, t, i); vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_addai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + vu->i.f; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_addaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; float q = vu_get_q(vu).f; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + q; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_addax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_adday(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_addaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_addaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) + bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_sub(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - vu_vf_i(vu, t, i); vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_subi(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - vu->i.f; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_subq(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; float q = vu_get_q(vu).f; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - q; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_subx(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_suby(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_subz(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_subw(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_suba(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - vu_vf_i(vu, t, i); vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_subai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - vu->i.f; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_subaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; float q = vu_get_q(vu).f; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - q; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_subax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_subay(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_subaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_subaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) - bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_mul(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i); vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_muli(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * vu->i.f; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_mulq(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; float q = vu_get_q(vu).f; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * q; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_mulx(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_muly(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_mulz(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_mulw(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_mula(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i); vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_mulai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * vu->i.f; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_mulaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; float q = vu_get_q(vu).f; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * q; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_mulax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_mulay(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_mulaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_mulaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_madd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i); vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_maddi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu->i.f; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_maddq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; float q = vu_get_q(vu).f; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * q; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_maddx(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_x(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_maddy(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_y(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_maddz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_z(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_maddw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_w(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_madda(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i); vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_maddai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * vu->i.f; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_maddaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; float q = vu_get_q(vu).f; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu->acc.f[i] + vu_vf_i(vu, s, i) * q; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_maddax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_madday(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_maddaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_maddaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) + vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msub(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i); vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msubi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu->i.f; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msubq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; float q = vu_get_q(vu).f; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - (vu_vf_i(vu, s, i) * q); vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msubx(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_x(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msuby(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_y(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msubz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_z(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msubw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; float bc = vu_vf_w(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu_set_vf(vu, d, i, vu_update_flags(vu, result, i)); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msuba(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu_vf_i(vu, t, i); vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msubai(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * vu->i.f; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msubaq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; float q = vu_get_q(vu).f; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu->acc.f[i] - vu_vf_i(vu, s, i) * q; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msubax(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_x(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msubay(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_y(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msubaz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_z(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_msubaw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; float bc = vu_vf_w(vu, t); for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float result = vu_acc_i(vu, i) - vu_vf_i(vu, s, i) * bc; vu->acc.f[i] = vu_update_flags(vu, result, i); } else { vu_clear_flags(vu, i); } } vu_update_status(vu); } void vu_i_max(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], vu->vf[t].s32[i]); } } } void vu_i_maxi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; if (!d) return; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], vu->i.s32); } } } void vu_i_maxx(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[0]; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float fs = vu_vf_i(vu, s, i); vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc); } } } void vu_i_maxy(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[1]; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float fs = vu_vf_i(vu, s, i); vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc); } } } void vu_i_maxz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[2]; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float fs = vu_vf_i(vu, s, i); vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc); } } } void vu_i_maxw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[3]; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { float fs = vu_vf_i(vu, s, i); vu->vf[d].u32[i] = vu_max(vu->vf[s].s32[i], bc); } } } void vu_i_mini(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], vu->vf[t].s32[i]); } } } void vu_i_minii(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int d = VU_UD_D; if (!d) return; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], vu->i.s32); } } } void vu_i_minix(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[0]; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc); } } } void vu_i_miniy(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[1]; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc); } } } void vu_i_miniz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[2]; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc); } } } void vu_i_miniw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; int d = VU_UD_D; if (!d) return; int32_t bc = vu->vf[t].s32[3]; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) { vu->vf[d].u32[i] = vu_min(vu->vf[s].s32[i], bc); } } } void vu_i_opmula(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; vu->acc.x = vu_vf_y(vu, s) * vu_vf_z(vu, t); vu->acc.y = vu_vf_z(vu, s) * vu_vf_x(vu, t); vu->acc.z = vu_vf_x(vu, s) * vu_vf_y(vu, t); vu->acc.x = vu_cvtf(vu->acc.u32[0]); vu->acc.y = vu_cvtf(vu->acc.u32[1]); vu->acc.z = vu_cvtf(vu->acc.u32[2]); vu->acc.x = vu_update_flags(vu, vu->acc.x, 0); vu->acc.y = vu_update_flags(vu, vu->acc.y, 1); vu->acc.z = vu_update_flags(vu, vu->acc.z, 2); // printf("s(%d)=%08x %08x %08x t(%d)=%08x %08x %08x acc=%08x %08x %08x prev=%08x %08x %08x\n", // s, // vu->vf[s].u32[0], // vu->vf[s].u32[1], // vu->vf[s].u32[2], // t, // vu->vf[t].u32[0], // vu->vf[t].u32[1], // vu->vf[t].u32[2], // vu->acc.u32[0], // vu->acc.u32[1], // vu->acc.u32[2], // acc.u32[0], // acc.u32[1], // acc.u32[2] // ); vu_clear_flags(vu, 3); vu_update_status(vu); } void vu_i_opmsub(struct vu_state* vu, const struct vu_instruction* ins) { int d = VU_UD_D; int s = VU_UD_S; int t = VU_UD_T; struct vu_reg128 tmp; tmp.f[0] = vu->acc.x - vu_vf_y(vu, s) * vu_vf_z(vu, t); tmp.f[1] = vu->acc.y - vu_vf_z(vu, s) * vu_vf_x(vu, t); tmp.f[2] = vu->acc.z - vu_vf_x(vu, s) * vu_vf_y(vu, t); vu_set_vf_x(vu, d, vu_update_flags(vu, tmp.f[0], 0)); vu_set_vf_y(vu, d, vu_update_flags(vu, tmp.f[1], 1)); vu_set_vf_z(vu, d, vu_update_flags(vu, tmp.f[2], 2)); // printf("s(%d)=%08x %08x %08x t(%d)=%08x %08x %08x d=%08x %08x %08x dp=%08x %08x %08x\n", // s, // vu->vf[s].u32[0], // vu->vf[s].u32[1], // vu->vf[s].u32[2], // t, // vu->vf[t].u32[0], // vu->vf[t].u32[1], // vu->vf[t].u32[2], // vu->vf[d].u32[0], // vu->vf[d].u32[1], // vu->vf[d].u32[2], // dv.u32[0], // dv.u32[1], // dv.u32[2] // ); vu_clear_flags(vu, 3); vu_update_status(vu); } void vu_i_nop(struct vu_state* vu, const struct vu_instruction* ins) { // No operation } void vu_i_ftoi0(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i))); } } void vu_i_ftoi4(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.0625f))); } } void vu_i_ftoi12(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.000244140625f))); } } void vu_i_ftoi15(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) vu_set_vfu(vu, t, i, vu_cvti(vu_vf_i(vu, s, i) * (1.0f / 0.000030517578125f))); } } void vu_i_itof0(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) vu_set_vf(vu, t, i, (float)vu->vf[s].s32[i]); } } void vu_i_itof4(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.0625f)); } } void vu_i_itof12(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.000244140625f)); } } void vu_i_itof15(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_UD_S; int t = VU_UD_T; for (int i = 0; i < 4; i++) { if (VU_UD_DI(i)) vu_set_vf(vu, t, i, (float)((float)(vu->vf[s].s32[i]) * 0.000030517578125f)); } } void vu_i_clip(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_UD_T; int s = VU_UD_S; vu->clip <<= 6; float w = fabsf(vu_vf_w(vu, t)); float x = vu_vf_x(vu, s); float y = vu_vf_y(vu, s); float z = vu_vf_z(vu, s); vu->clip |= (x > +w); vu->clip |= (x < -w) << 1; vu->clip |= (y > +w) << 2; vu->clip |= (y < -w) << 3; vu->clip |= (z > +w) << 4; vu->clip |= (z < -w) << 5; vu->clip &= 0xFFFFFF; } // Lower pipeline void vu_i_b(struct vu_state* vu, const struct vu_instruction* ins) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } void vu_i_bal(struct vu_state* vu, const struct vu_instruction* ins) { // Instruction next to the delay slot VU_IT = vu->tpc + 1; vu->next_tpc = vu->tpc + VU_LD_IMM11; } void vu_i_div(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; int s = VU_LD_S; int tf = VU_LD_TF; int sf = VU_LD_SF; struct vu_reg32 q; q.f = vu_vf_i(vu, s, sf) / vu_vf_i(vu, t, tf); q.f = vu_cvtf(q.u32); vu_set_q(vu, q.f, 7); } void vu_i_eatan(struct vu_state* vu, const struct vu_instruction* ins) { float x = vu_vf_i(vu, VU_LD_S, VU_LD_SF); if (x == -1.0f) { vu->p.u32 = 0xFF7FFFFF; } else { x = (x - 1.0f) / (x + 1.0f); vu->p.f = vu_atan(x); } } void vu_i_eatanxy(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x = vu_vf_x(vu, s); float y = vu_vf_y(vu, s); if (y + x == 0.0f) { vu->p.u32 = 0x7F7FFFFF | (vu->vf[s].u32[1] & 0x80000000); } else { x = (y - 1.0f) / (y + x); vu->p.f = vu_atan(x); } } void vu_i_eatanxz(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x = vu_vf_x(vu, s); float z = vu_vf_z(vu, s); //P = atan(z/x) if (z + x == 0.0f) { vu->p.u32 = 0x7F7FFFFF | (vu->vf[s].u32[2] & 0x80000000); } else { x = (z - x) / (z + x); vu->p.f = vu_atan(x); } } void vu_i_eexp(struct vu_state* vu, const struct vu_instruction* ins) { const static float coeffs[] = { 0.249998688697815f, 0.031257584691048f, 0.002591371303424f, 0.000171562001924f, 0.000005430199963f, 0.000000690600018f }; int s = VU_LD_S; int sf = VU_LD_SF; if (vu->vf[s].u32[sf] & 0x80000000) { vu->p.f = vu_vf_i(vu, s, sf); return; } float value = 1; float x = vu_vf_i(vu, s, sf); for (int exp = 1; exp <= 6; exp++) value += coeffs[exp - 1] * pow(x, exp); vu->p.f = 1.0 / value; } void vu_i_eleng(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s); float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s); float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s); vu->p.f = sqrtf(x2 + y2 + z2); } void vu_i_ercpr(struct vu_state* vu, const struct vu_instruction* ins) { vu->p.f = 1.0f / vu_vf_i(vu, VU_LD_S, VU_LD_SF); } void vu_i_erleng(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s); float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s); float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s); vu->p.f = 1.0f / sqrtf(x2 + y2 + z2); } void vu_i_ersadd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s); float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s); float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s); vu->p.f = 1.0f / (x2 + y2 + z2); } void vu_i_ersqrt(struct vu_state* vu, const struct vu_instruction* ins) { vu->p.f = 1.0f / sqrtf(vu_vf_i(vu, VU_LD_S, VU_LD_SF)); } void vu_i_esadd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; float x2 = vu_vf_x(vu, s) * vu_vf_x(vu, s); float y2 = vu_vf_y(vu, s) * vu_vf_y(vu, s); float z2 = vu_vf_z(vu, s) * vu_vf_z(vu, s); vu->p.f = x2 + y2 + z2; } void vu_i_esin(struct vu_state* vu, const struct vu_instruction* ins) { vu->p.f = sinf(vu_vf_i(vu, VU_LD_S, VU_LD_SF)); } void vu_i_esqrt(struct vu_state* vu, const struct vu_instruction* ins) { vu->p.f = sqrtf(vu_vf_i(vu, VU_LD_S, VU_LD_SF)); } void vu_i_esum(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; vu->p.f = vu_vf_x(vu, s) + vu_vf_y(vu, s) + vu_vf_z(vu, s) + vu_vf_w(vu, s); } void vu_i_fcand(struct vu_state* vu, const struct vu_instruction* ins) { vu->vi[1] = ((vu->clip & 0xffffff) & VU_LD_IMM24) != 0; } void vu_i_fceq(struct vu_state* vu, const struct vu_instruction* ins) { vu->vi[1] = (vu->clip & 0xffffff) == VU_LD_IMM24; } void vu_i_fcget(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; vu->vi[VU_LD_T] = vu->clip & 0xfff; } void vu_i_fcor(struct vu_state* vu, const struct vu_instruction* ins) { vu->vi[1] = ((vu->clip & 0xffffff) | VU_LD_IMM24) == 0xffffff; } void vu_i_fcset(struct vu_state* vu, const struct vu_instruction* ins) { vu->clip = VU_LD_IMM24; } void vu_i_fmand(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, vu->mac_pipeline[3] & VU_IS); } void vu_i_fmeq(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, (VU_IS & 0xffff) == (vu->mac_pipeline[3] & 0xffff)); } void vu_i_fmor(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, (VU_IS & 0xffff) | (vu->mac_pipeline[3] & 0xffff)); } void vu_i_fsand(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, vu->status & VU_LD_IMM12); } void vu_i_fseq(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, (vu->status & 0xfff) == VU_LD_IMM12); } void vu_i_fsor(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, (vu->status & 0xfff) | VU_LD_IMM12); } void vu_i_fsset(struct vu_state* vu, const struct vu_instruction* ins) { vu->status &= 0x3f; vu->status |= VU_LD_IMM12 & 0xfc0; } void vu_i_iadd(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_D); vu_set_vi(vu, VU_LD_D, VU_IS + VU_IT); } void vu_i_iaddi(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_T); vu_set_vi(vu, VU_LD_T, VU_IS + VU_LD_IMM5); } void vu_i_iaddiu(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_T); vu_set_vi(vu, VU_LD_T, VU_IS + VU_LD_IMM15); } void vu_i_iand(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_T); vu_set_vi(vu, VU_LD_D, VU_IS & VU_IT); } void vu_i_ibeq(struct vu_state* vu, const struct vu_instruction* ins) { uint16_t t = vu_get_branch_register(vu, VU_LD_T); uint16_t s = vu_get_branch_register(vu, VU_LD_S); if (t == s) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } void vu_i_ibgez(struct vu_state* vu, const struct vu_instruction* ins) { int16_t s = vu_get_branch_register(vu, VU_LD_S); if (s >= 0) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } void vu_i_ibgtz(struct vu_state* vu, const struct vu_instruction* ins) { int16_t s = vu_get_branch_register(vu, VU_LD_S); if (s > 0) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } void vu_i_iblez(struct vu_state* vu, const struct vu_instruction* ins) { int16_t s = vu_get_branch_register(vu, VU_LD_S); if (s <= 0) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } void vu_i_ibltz(struct vu_state* vu, const struct vu_instruction* ins) { int16_t s = vu_get_branch_register(vu, VU_LD_S); if (s < 0) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } void vu_i_ibne(struct vu_state* vu, const struct vu_instruction* ins) { uint16_t t = vu_get_branch_register(vu, VU_LD_T); uint16_t s = vu_get_branch_register(vu, VU_LD_S); // printf("ibne vi%02u (%04x), vi%02u (%04x), 0x%08x\n", VU_LD_T, t, VU_LD_S, s, vu->tpc + VU_LD_IMM11); if (t != s) { vu->next_tpc = vu->tpc + VU_LD_IMM11; } } void vu_i_ilw(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; uint32_t addr = VU_IS + VU_LD_IMM11; uint128_t data = vu_mem_read(vu, addr); for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu->vi[t] = data.u32[i]; } } void vu_i_ilwr(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; if (!t) return; uint32_t addr = vu->vi[s]; uint128_t data = vu_mem_read(vu, addr); for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu->vi[t] = data.u32[i]; } } void vu_i_ior(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_D); vu_set_vi(vu, VU_LD_D, VU_IS | VU_IT); } void vu_i_isub(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_D); vu_set_vi(vu, VU_LD_D, VU_IS - VU_IT); } void vu_i_isubiu(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_T); vu_set_vi(vu, VU_LD_T, VU_IS - VU_LD_IMM15); } void vu_i_isw(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; uint32_t addr = vu->vi[s] + VU_LD_IMM11; for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vi[t], i); } } void vu_i_iswr(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; uint32_t addr = vu->vi[s]; for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vi[t], i); } } void vu_i_jalr(struct vu_state* vu, const struct vu_instruction* ins) { uint16_t s = VU_IS; VU_IT = vu->tpc + 1; vu->next_tpc = s; } void vu_i_jr(struct vu_state* vu, const struct vu_instruction* ins) { vu->next_tpc = VU_IS; } void vu_i_lq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; uint32_t addr = vu->vi[s] + VU_LD_IMM11; uint128_t data = vu_mem_read(vu, addr); if (!t) return; for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = data.u32[i]; } } void vu_i_lqd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; vu_write_branch_pipeline(vu, s); vu_set_vi(vu, s, vu->vi[s] - 1); uint32_t addr = vu->vi[s]; uint128_t data = vu_mem_read(vu, addr); if (!t) return; for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = data.u32[i]; } } void vu_i_lqi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; vu_write_branch_pipeline(vu, s); if (t) { uint32_t addr = vu->vi[s]; uint128_t data = vu_mem_read(vu, addr); for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = data.u32[i]; } } vu_set_vi(vu, s, vu->vi[s] + 1); } void vu_i_mfir(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = (int32_t)(int16_t)VU_IS; } } void vu_i_mfp(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = vu->p.f; } } void vu_i_move(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; if (!t) return; for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = vu->vf[s].u32[i]; } } void vu_i_mr32(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; int s = VU_LD_S; uint32_t x = vu->vf[s].u32[0]; // for (int i = 0; i < 4; i++) { // if (VU_LD_DI(i)) vu->vf[t].u32[i] = rs.u32[(i + 1) & 3]; // } if (VU_LD_DI(0)) vu->vf[t].u32[0] = vu->vf[s].u32[1]; if (VU_LD_DI(1)) vu->vf[t].u32[1] = vu->vf[s].u32[2]; if (VU_LD_DI(2)) vu->vf[t].u32[2] = vu->vf[s].u32[3]; if (VU_LD_DI(3)) vu->vf[t].u32[3] = x; } void vu_i_mtir(struct vu_state* vu, const struct vu_instruction* ins) { vu_write_branch_pipeline(vu, VU_LD_T); vu_set_vi(vu, VU_LD_T, vu->vf[VU_LD_S].u32[VU_LD_SF] & 0xffff); } void vu_i_rget(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = vu->r.u32; } } void vu_i_rinit(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; vu->r.u32 = 0x3f800000; if (!s) return; vu->r.u32 |= vu->vf[s].u32[VU_LD_SF] & 0x007fffff; } void vu_i_rnext(struct vu_state* vu, const struct vu_instruction* ins) { int t = VU_LD_T; if (!t) return; int x = (vu->r.u32 >> 4) & 1; int y = (vu->r.u32 >> 22) & 1; vu->r.u32 <<= 1; vu->r.u32 ^= x ^ y; vu->r.u32 = (vu->r.u32 & 0x7FFFFF) | 0x3F800000; for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu->vf[t].u32[i] = vu->r.u32; } } void vu_i_rsqrt(struct vu_state* vu, const struct vu_instruction* ins) { struct vu_reg32 q; q.f = vu_vf_i(vu, VU_LD_S, VU_LD_SF) / sqrtf(vu_vf_i(vu, VU_LD_T, VU_LD_TF)); q.f = vu_cvtf(q.u32); vu_set_q(vu, q.f, 13); } void vu_i_rxor(struct vu_state* vu, const struct vu_instruction* ins) { vu->r.u32 = 0x3F800000 | ((vu->r.u32 ^ vu->vf[VU_LD_S].u32[VU_LD_SF]) & 0x007FFFFF); } void vu_i_sq(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; uint32_t addr = vu->vi[t] + VU_LD_IMM11; // 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]); for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vf[s].u32[i], i); } } void vu_i_sqd(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; vu_write_branch_pipeline(vu, t); vu_set_vi(vu, t, vu->vi[t] - 1); uint32_t addr = vu->vi[t]; for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vf[s].u32[i], i); } } void vu_i_sqi(struct vu_state* vu, const struct vu_instruction* ins) { int s = VU_LD_S; int t = VU_LD_T; vu_write_branch_pipeline(vu, t); uint32_t addr = vu->vi[t]; for (int i = 0; i < 4; i++) { if (VU_LD_DI(i)) vu_mem_write(vu, addr, vu->vf[s].u32[i], i); } vu_set_vi(vu, t, vu->vi[t] + 1); } void vu_i_sqrt(struct vu_state* vu, const struct vu_instruction* ins) { struct vu_reg32 q; q.f = sqrtf(vu_vf_i(vu, VU_LD_T, VU_LD_TF)); q.f = vu_cvtf(q.u32); vu_set_q(vu, q.f, 7); } void vu_i_waitp(struct vu_state* vu, const struct vu_instruction* ins) { // No operation } void vu_i_waitq(struct vu_state* vu, const struct vu_instruction* ins) { vu->q_delay = 0; } void vu_i_xgkick(struct vu_state* vu, const struct vu_instruction* ins) { // vu_xgkick(vu); // vu->xgkick_pending = 3; // vu->xgkick_addr = VU_IS; // return; uint16_t addr = VU_IS; int eop = 1; do { uint128_t tag = vu_mem_read(vu, addr++); if ((tag.u64[0] | tag.u64[1]) == 0) break; // addr &= 0x3ff; // if (addr == 0) break; // printf("tag: addr=%08x %08x %08x %08x %08x\n", addr - 1, tag.u32[3], tag.u32[2], tag.u32[1], tag.u32[0]); ps2_gif_fifo_write(vu->gif, tag, GIF_PATH1); eop = (tag.u64[0] & 0x8000) != 0; int nloop = tag.u64[0] & 0x7fff; int flg = (tag.u64[0] >> 58) & 3; int nregs = (tag.u64[0] >> 60) & 0xf; if (!nloop) continue; // if (!nregs) // nregs = 16; int qwc = 0; switch (flg) { case 0: { qwc = nregs * nloop; } break; case 1: { qwc = (nregs * nloop + 1) / 2; // Round up for odd cases } break; case 2: case 3: { qwc = nloop; } break; } if (qwc >= 0x400) { fprintf(stderr, "vu: Weird xgkick tag nloop=%d nregs=%d eop=%d flg=%d qwc=%d\n", nloop, nregs, eop, flg, qwc ); exit(1); } for (int i = 0; i < qwc; i++) { // printf("vu: %08x: %08x %08x %08x %08x\n", // addr, // vu->vu_mem[addr].u32[3], // vu->vu_mem[addr].u32[2], // vu->vu_mem[addr].u32[1], // vu->vu_mem[addr].u32[0] // ); ps2_gif_fifo_write(vu->gif, vu_mem_read(vu, addr++), GIF_PATH1); addr &= 0x3ff; // if (addr == 0) { // eop = 1; // break; // } } } while (!eop); } void vu_i_xitop(struct vu_state* vu, const struct vu_instruction* ins) { vu_set_vi(vu, VU_LD_T, vu->vif->itop); } void vu_i_xtop(struct vu_state* vu, const struct vu_instruction* ins) { if (vu->id == 0) { printf("vu: xtop used in VU0\n"); // exit(1); } vu_set_vi(vu, VU_LD_T, vu->vif->top); } uint64_t ps2_vu_read8(struct vu_state* vu, uint32_t addr) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; return *(uint8_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]); } uint8_t* ptr = (uint8_t*)vu->vu_mem; return *(uint8_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]); } uint64_t ps2_vu_read16(struct vu_state* vu, uint32_t addr) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; return *(uint16_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]); } uint8_t* ptr = (uint8_t*)vu->vu_mem; return *(uint16_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]); } uint64_t ps2_vu_read32(struct vu_state* vu, uint32_t addr) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; return *(uint32_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]); } uint8_t* ptr = (uint8_t*)vu->vu_mem; return *(uint32_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]); } uint64_t ps2_vu_read64(struct vu_state* vu, uint32_t addr) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; return *(uint64_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]); } uint8_t* ptr = (uint8_t*)vu->vu_mem; return *(uint64_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]); } uint128_t ps2_vu_read128(struct vu_state* vu, uint32_t addr) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; return *(uint128_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]); } uint8_t* ptr = (uint8_t*)vu->vu_mem; return *(uint128_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]); } void ps2_vu_write8(struct vu_state* vu, uint32_t addr, uint64_t data) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; *(uint8_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data; } else { uint8_t* ptr = (uint8_t*)vu->vu_mem; *(uint8_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data; } } void ps2_vu_write16(struct vu_state* vu, uint32_t addr, uint64_t data) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; *(uint16_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data; } else { uint8_t* ptr = (uint8_t*)vu->vu_mem; *(uint16_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data; } } void ps2_vu_write32(struct vu_state* vu, uint32_t addr, uint64_t data) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; *(uint32_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data; } else { uint8_t* ptr = (uint8_t*)vu->vu_mem; *(uint32_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data; } } void ps2_vu_write64(struct vu_state* vu, uint32_t addr, uint64_t data) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; *(uint64_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data; } else { uint8_t* ptr = (uint8_t*)vu->vu_mem; *(uint64_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data; } } void ps2_vu_write128(struct vu_state* vu, uint32_t addr, uint128_t data) { if (addr <= 0x3FFF) { uint8_t* ptr = (uint8_t*)vu->micro_mem; *(uint128_t*)(&ptr[addr & ((vu->micro_mem_size << 3) | 7)]) = data; } else { uint8_t* ptr = (uint8_t*)vu->vu_mem; *(uint128_t*)(&ptr[addr & ((vu->vu_mem_size << 4) | 0xf)]) = data; } } #define VU_FLD_X 1 #define VU_FLD_Y 2 #define VU_FLD_Z 4 #define VU_FLD_W 8 #define VU_DEC_UD_S_SRC_T_BROADCAST(bc, f) \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = (opcode >> 21) & 0xf; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = bc; \ vu->upper.func = f; #define VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(bc, f) \ vu->upper.dst.reg = vu->upper.ud_d; \ vu->upper.dst.field = (opcode >> 21) & 0xf; \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = vu->upper.dst.field; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = bc; \ vu->upper.func = f; #define VU_DEC_UD_D_DST_S_SRC_T_SRC(f) \ vu->upper.dst.reg = vu->upper.ud_d; \ vu->upper.dst.field = (opcode >> 21) & 0xf; \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = vu->upper.dst.field; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = vu->upper.dst.field; \ vu->upper.func = f; #define VU_DEC_UD_D_DST_S_SRC(f) \ vu->upper.dst.reg = vu->upper.ud_d; \ vu->upper.dst.field = (opcode >> 21) & 0xf; \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = vu->upper.dst.field; \ vu->upper.func = f; #define VU_DEC_UD_D_DST_S_SRC_Q_SRC(f) \ vu->upper.dst.reg = vu->upper.ud_d; \ vu->upper.dst.field = (opcode >> 21) & 0xf; \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = vu->upper.dst.field; \ vu->upper.src[1].reg = VU_REG_Q; \ vu->upper.func = f; #define VU_DEC_UD_T_DST_S_SRC(f) \ vu->upper.dst.reg = vu->upper.ud_t; \ vu->upper.dst.field = (opcode >> 21) & 0xf; \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = vu->upper.dst.field; \ vu->upper.func = f; #define VU_DEC_UD_S_SRC_T_SRC(f) \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = (opcode >> 21) & 0xf; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = vu->upper.src[0].field; \ vu->upper.func = f; #define VU_DEC_UD_S_SRC(f) \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = (opcode >> 21) & 0xf; \ vu->upper.func = f; #define VU_DEC_UD_S_SRC_Q_SRC(f) \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = (opcode >> 21) & 0xf; \ vu->upper.src[1].reg = VU_REG_Q; \ vu->upper.func = f; #define VU_DEC_OPMULA() \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = vu->upper.src[0].field; \ vu->upper.func = vu_i_opmula; #define VU_DEC_OPMSUB() \ vu->upper.dst.reg = vu->upper.ud_d; \ vu->upper.dst.field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = vu->upper.dst.field; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = vu->upper.dst.field; \ vu->upper.func = vu_i_opmsub; #define VU_DEC_CLIP() \ vu->upper.src[0].reg = vu->upper.ud_s; \ vu->upper.src[0].field = VU_FLD_X | VU_FLD_Y | VU_FLD_Z; \ vu->upper.src[1].reg = vu->upper.ud_t; \ vu->upper.src[1].field = VU_FLD_W; \ vu->upper.func = vu_i_clip; #define VU_DEC_LD_NONE(f) \ vu->lower.func = f; #define VU_DEC_UD_NONE(f) \ vu->upper.func = f; #define VU_DEC_LD_T_DST_S_VISRC(f) \ vu->lower.dst.reg = vu->lower.ld_t; \ vu->lower.dst.field = (opcode >> 21) & 0xf; \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.func = f; #define VU_DEC_LD_T_DST_S_VISRC_S_VIDST(f) \ vu->lower.dst.reg = vu->lower.ld_t; \ vu->lower.dst.field = (opcode >> 21) & 0xf; \ vu->lower.vi_dst = vu->lower.ld_s; \ vu->lower.vi_src[0] = vu->lower.vi_dst; \ vu->lower.func = f; #define VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(f) \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = (opcode >> 21) & 0xf; \ vu->lower.vi_dst = vu->lower.ld_t; \ vu->lower.vi_src[0] = vu->lower.vi_dst; \ vu->lower.func = f; #define VU_DEC_LD_S_SF_SRC_T_TF_SRC(f) \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = vu->lower.ld_sf; \ vu->lower.src[1].reg = vu->lower.ld_t; \ vu->lower.src[1].field = vu->lower.ld_tf; \ vu->lower.func = f; #define VU_DEC_LD_Q_DST_S_SF_SRC_T_TF_SRC(f) \ vu->lower.dst.reg = VU_REG_Q; \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = vu->lower.ld_sf; \ vu->lower.src[1].reg = vu->lower.ld_t; \ vu->lower.src[1].field = vu->lower.ld_tf; \ vu->lower.func = f; #define VU_DEC_LD_T_VIDST_S_SF_SRC(f) \ vu->lower.vi_dst = vu->lower.ld_t; \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = vu->lower.ld_sf; \ vu->lower.func = f; #define VU_DEC_LD_T_DST_S_VISRC(f) \ vu->lower.dst.reg = vu->lower.ld_t; \ vu->lower.dst.field = (opcode >> 21) & 0xf; \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.func = f; #define VU_DEC_LD_T_TF_SRC(f) \ vu->lower.src[0].reg = vu->lower.ld_t; \ vu->lower.src[0].field = vu->lower.ld_tf; \ vu->lower.func = f; #define VU_DEC_LD_Q_DST_T_TF_SRC(f) \ vu->lower.dst.reg = VU_REG_Q; \ vu->lower.src[0].reg = vu->lower.ld_t; \ vu->lower.src[0].field = vu->lower.ld_tf; \ vu->lower.func = f; #define VU_DEC_LD_S_SRC_T_VISRC(f) \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = (opcode >> 21) & 0xf; \ vu->lower.vi_src[0] = vu->lower.ld_t; \ vu->lower.func = f; #define VU_DEC_LD_T_VIDST_S_VISRC(f) \ vu->lower.vi_dst = vu->lower.ld_t; \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.func = f; #define VU_DEC_LD_S_VISRC_T_VISRC(f) \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.vi_src[1] = vu->lower.ld_t; \ vu->lower.func = f; #define VU_DEC_LD_T_VIDST_S_VISRC(f) \ vu->lower.vi_dst = vu->lower.ld_t; \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.func = f; #define VU_DEC_LD_T_VISRC_S_VISRC(f) \ vu->lower.vi_src[0] = vu->lower.ld_t; \ vu->lower.vi_src[1] = vu->lower.ld_s; \ vu->lower.func = f; #define VU_DEC_LD_VIDST(v, f) \ vu->lower.vi_dst = v; \ vu->lower.func = f; #define VU_DEC_LD_T_VIDST(f) \ vu->lower.vi_dst = vu->lower.ld_t; \ vu->lower.func = f; #define VU_DEC_LD_S_VISRC(f) \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.func = f; #define VU_DEC_LD_S_FLD_SRC(fld, f) \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = fld; \ vu->lower.func = f; #define VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(f) \ vu->lower.vi_dst = vu->lower.ld_d; \ vu->lower.vi_src[0] = vu->lower.ld_s; \ vu->lower.vi_src[1] = vu->lower.ld_t; \ vu->lower.func = f; #define VU_DEC_LD_T_DST_S_SRC(f) \ vu->lower.dst.reg = vu->lower.ld_t; \ vu->lower.dst.field = (opcode >> 21) & 0xf; \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = vu->lower.dst.field; \ vu->lower.func = f; #define VU_DEC_LD_T_DST(f) \ vu->lower.dst.reg = vu->lower.ld_t; \ vu->lower.dst.field = (opcode >> 21) & 0xf; \ vu->lower.func = f; #define VU_DEC_LD_S_SF_SRC(f) \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = vu->lower.ld_sf; \ vu->lower.func = f; #define VU_DEC_MR32() \ vu->lower.dst.reg = vu->lower.ld_t; \ vu->lower.dst.field = (opcode >> 21) & 0xf; \ vu->lower.src[0].reg = vu->lower.ld_s; \ vu->lower.src[0].field = (vu->lower.dst.field >> 1) | ((vu->lower.dst.field & 1) << 3); \ vu->lower.func = vu_i_mr32; void vu_decode_upper(struct vu_state* vu, uint32_t opcode) { vu->upper.ud_d = (opcode >> 6) & 0x1f; vu->upper.ud_s = (opcode >> 11) & 0x1f; vu->upper.ud_t = (opcode >> 16) & 0x1f; for (int i = 0; i < 4; i++) vu->upper.ud_di[i] = opcode & (1 << (24 - i)); vu->upper.func = NULL; vu->upper.dst.reg = 0; vu->upper.dst.field = 0; vu->upper.src[0].reg = 0; vu->upper.src[0].field = 0; vu->upper.src[1].reg = 0; vu->upper.src[1].field = 0; // Decode 000007FF style instruction if ((opcode & 0x3c) == 0x3c) { // 0EEEE 1111 EE // -0EE EE11 11EE // -------------- // bit 10 is always 0 // bits 2-5 are always 1 // -------------- // bits 0-1 and bits 6-9 (6 bits) are enough to decode // all of the following switch (((opcode & 0x3c0) >> 4) | (opcode & 3)) { case 0x00: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_addax); return; case 0x01: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_adday); return; case 0x02: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_addaz); return; case 0x03: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_addaw); return; case 0x04: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_subax); return; case 0x05: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_subay); return; case 0x06: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_subaz); return; case 0x07: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_subaw); return; case 0x08: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_maddax); return; case 0x09: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_madday); return; case 0x0A: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_maddaz); return; case 0x0B: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_maddaw); return; case 0x0C: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_msubax); return; case 0x0D: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_msubay); return; case 0x0E: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_msubaz); return; case 0x0F: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_msubaw); return; case 0x10: VU_DEC_UD_T_DST_S_SRC(vu_i_itof0); return; case 0x11: VU_DEC_UD_T_DST_S_SRC(vu_i_itof4); return; case 0x12: VU_DEC_UD_T_DST_S_SRC(vu_i_itof12); return; case 0x13: VU_DEC_UD_T_DST_S_SRC(vu_i_itof15); return; case 0x14: VU_DEC_UD_T_DST_S_SRC(vu_i_ftoi0); return; case 0x15: VU_DEC_UD_T_DST_S_SRC(vu_i_ftoi4); return; case 0x16: VU_DEC_UD_T_DST_S_SRC(vu_i_ftoi12); return; case 0x17: VU_DEC_UD_T_DST_S_SRC(vu_i_ftoi15); return; case 0x18: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_mulax); return; case 0x19: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_mulay); return; case 0x1A: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_mulaz); return; case 0x1B: VU_DEC_UD_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_mulaw); return; case 0x1C: VU_DEC_UD_S_SRC_Q_SRC(vu_i_mulaq); return; case 0x1D: VU_DEC_UD_T_DST_S_SRC(vu_i_abs); return; case 0x1E: VU_DEC_UD_S_SRC(vu_i_mulai); return; case 0x1F: VU_DEC_CLIP(); return; case 0x20: VU_DEC_UD_S_SRC_Q_SRC(vu_i_addaq); return; case 0x21: VU_DEC_UD_S_SRC_Q_SRC(vu_i_maddaq); return; case 0x22: VU_DEC_UD_S_SRC(vu_i_addai); return; case 0x23: VU_DEC_UD_S_SRC(vu_i_maddai); return; case 0x24: VU_DEC_UD_S_SRC_Q_SRC(vu_i_subaq); return; case 0x25: VU_DEC_UD_S_SRC_Q_SRC(vu_i_msubaq); return; case 0x26: VU_DEC_UD_S_SRC(vu_i_subai); return; case 0x27: VU_DEC_UD_S_SRC(vu_i_msubai); return; case 0x28: VU_DEC_UD_S_SRC_T_SRC(vu_i_adda); return; case 0x29: VU_DEC_UD_S_SRC_T_SRC(vu_i_madda); return; case 0x2A: VU_DEC_UD_S_SRC_T_SRC(vu_i_mula); return; case 0x2C: VU_DEC_UD_S_SRC_T_SRC(vu_i_suba); return; case 0x2D: VU_DEC_UD_S_SRC_T_SRC(vu_i_msuba); return; case 0x2E: VU_DEC_OPMULA(); return; case 0x2F: VU_DEC_UD_NONE(vu_i_nop); return; } } else { // Decode 0000003F style instruction switch (opcode & 0x3f) { case 0x00: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_addx); return; case 0x01: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_addy); return; case 0x02: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_addz); return; case 0x03: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_addw); return; case 0x04: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_subx); return; case 0x05: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_suby); return; case 0x06: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_subz); return; case 0x07: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_subw); return; case 0x08: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_maddx); return; case 0x09: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_maddy); return; case 0x0A: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_maddz); return; case 0x0B: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_maddw); return; case 0x0C: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_msubx); return; case 0x0D: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_msuby); return; case 0x0E: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_msubz); return; case 0x0F: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_msubw); return; case 0x10: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_maxx); return; case 0x11: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_maxy); return; case 0x12: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_maxz); return; case 0x13: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_maxw); return; case 0x14: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_minix); return; case 0x15: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_miniy); return; case 0x16: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_miniz); return; case 0x17: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_miniw); return; case 0x18: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_X, vu_i_mulx); return; case 0x19: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Y, vu_i_muly); return; case 0x1A: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_Z, vu_i_mulz); return; case 0x1B: VU_DEC_UD_D_DST_S_SRC_T_BROADCAST(VU_FLD_W, vu_i_mulw); return; case 0x1C: VU_DEC_UD_D_DST_S_SRC_Q_SRC(vu_i_mulq); return; case 0x1D: VU_DEC_UD_D_DST_S_SRC(vu_i_maxi); return; case 0x1E: VU_DEC_UD_D_DST_S_SRC(vu_i_muli); return; case 0x1F: VU_DEC_UD_D_DST_S_SRC(vu_i_minii); return; case 0x20: VU_DEC_UD_D_DST_S_SRC_Q_SRC(vu_i_addq); return; case 0x21: VU_DEC_UD_D_DST_S_SRC_Q_SRC(vu_i_maddq); return; case 0x22: VU_DEC_UD_D_DST_S_SRC(vu_i_addi); return; case 0x23: VU_DEC_UD_D_DST_S_SRC(vu_i_maddi); return; case 0x24: VU_DEC_UD_D_DST_S_SRC_Q_SRC(vu_i_subq); return; case 0x25: VU_DEC_UD_D_DST_S_SRC_Q_SRC(vu_i_msubq); return; case 0x26: VU_DEC_UD_D_DST_S_SRC(vu_i_subi); return; case 0x27: VU_DEC_UD_D_DST_S_SRC(vu_i_msubi); return; case 0x28: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_add); return; case 0x29: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_madd); return; case 0x2A: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_mul); return; case 0x2B: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_max); return; case 0x2C: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_sub); return; case 0x2D: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_msub); return; case 0x2E: VU_DEC_OPMSUB(); return; case 0x2F: VU_DEC_UD_D_DST_S_SRC_T_SRC(vu_i_mini); return; } } } void vu_decode_lower(struct vu_state* vu, uint32_t opcode) { vu->lower.ld_d = (opcode >> 6) & 0x1f; vu->lower.ld_s = (opcode >> 11) & 0x1f; vu->lower.ld_t = (opcode >> 16) & 0x1f; vu->lower.ld_sf = (opcode >> 21) & 3; vu->lower.ld_tf = (opcode >> 23) & 3; vu->lower.ld_imm5 = ((int32_t)(((opcode >> 6) & 0x1f) << 27)) >> 27; vu->lower.ld_imm11 = ((int32_t)((opcode & 0x7ff) << 21)) >> 21; vu->lower.ld_imm12 = (((opcode >> 21) & 1) << 11) | (opcode & 0x7ff); vu->lower.ld_imm15 = (opcode & 0x7ff) | ((opcode & 0x1e00000) >> 10); vu->lower.ld_imm24 = opcode & 0xffffff; for (int i = 0; i < 4; i++) vu->lower.ld_di[i] = opcode & (1 << (24 - i)); vu->lower.func = NULL; vu->lower.dst.reg = 0; vu->lower.dst.field = 0; vu->lower.src[0].reg = 0; vu->lower.src[0].field = 0; vu->lower.src[1].reg = 0; vu->lower.src[1].field = 0; vu->lower.vi_src[0] = 0; vu->lower.vi_src[1] = 0; vu->lower.vi_dst = 0; switch ((opcode & 0xFE000000) >> 25) { case 0x00: VU_DEC_LD_T_DST_S_VISRC(vu_i_lq); return; case 0x01: VU_DEC_LD_S_SRC_T_VISRC(vu_i_sq); return; case 0x04: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_ilw); return; case 0x05: VU_DEC_LD_S_VISRC_T_VISRC(vu_i_isw); return; case 0x08: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_iaddiu); return; case 0x09: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_isubiu); return; // Note: The flag check instructions clobber the destination register // "immediately", this means we don't actually need to generate // a dependency. case 0x10: VU_DEC_LD_NONE(vu_i_fceq); return; // VU_DEC_LD_VIDST(1, vu_i_fceq); return; case 0x11: VU_DEC_LD_NONE(vu_i_fcset); return; case 0x12: VU_DEC_LD_NONE(vu_i_fcand); return; // VU_DEC_LD_VIDST(1, vu_i_fcand); return; case 0x13: VU_DEC_LD_NONE(vu_i_fcor); return; // VU_DEC_LD_VIDST(1, vu_i_fcor); return; case 0x14: VU_DEC_LD_NONE(vu_i_fseq); return; // VU_DEC_LD_T_VIDST(vu_i_fseq); return; case 0x15: VU_DEC_LD_NONE(vu_i_fsset); return; case 0x16: VU_DEC_LD_NONE(vu_i_fsand); return; // VU_DEC_LD_T_VIDST(vu_i_fsand); return; case 0x17: VU_DEC_LD_NONE(vu_i_fsor); return; // VU_DEC_LD_T_VIDST(vu_i_fsor); return; case 0x18: VU_DEC_LD_S_VISRC(vu_i_fmeq); return; // VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmeq); return; case 0x1A: VU_DEC_LD_S_VISRC(vu_i_fmand); return; // VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmand); return; case 0x1B: VU_DEC_LD_S_VISRC(vu_i_fmor); return; // VU_DEC_LD_T_VIDST_S_VISRC(vu_i_fmor); return; case 0x1C: VU_DEC_LD_NONE(vu_i_fcget); return; // VU_DEC_LD_T_VIDST(vu_i_fcget); return; case 0x20: VU_DEC_LD_NONE(vu_i_b); return; case 0x21: VU_DEC_LD_T_VIDST(vu_i_bal); return; case 0x24: VU_DEC_LD_S_VISRC(vu_i_jr); return; case 0x25: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_jalr); return; case 0x28: VU_DEC_LD_S_VISRC_T_VISRC(vu_i_ibeq); return; case 0x29: VU_DEC_LD_S_VISRC_T_VISRC(vu_i_ibne); return; case 0x2C: VU_DEC_LD_S_VISRC(vu_i_ibltz); return; case 0x2D: VU_DEC_LD_S_VISRC(vu_i_ibgtz); return; case 0x2E: VU_DEC_LD_S_VISRC(vu_i_iblez); return; case 0x2F: VU_DEC_LD_S_VISRC(vu_i_ibgez); return; case 0x40: { if ((opcode & 0x3C) == 0x3C) { switch (((opcode & 0x7C0) >> 4) | (opcode & 3)) { case 0x30: VU_DEC_LD_T_DST_S_SRC(vu_i_move); return; case 0x31: VU_DEC_MR32(); return; case 0x34: VU_DEC_LD_T_DST_S_VISRC_S_VIDST(vu_i_lqi); return; case 0x35: VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(vu_i_sqi); return; case 0x36: VU_DEC_LD_T_DST_S_VISRC_S_VIDST(vu_i_lqd); return; case 0x37: VU_DEC_LD_S_SRC_T_VISRC_T_VIDST(vu_i_sqd); return; case 0x38: VU_DEC_LD_Q_DST_S_SF_SRC_T_TF_SRC(vu_i_div); return; case 0x39: VU_DEC_LD_Q_DST_T_TF_SRC(vu_i_sqrt); return; case 0x3A: VU_DEC_LD_Q_DST_S_SF_SRC_T_TF_SRC(vu_i_rsqrt); return; case 0x3B: VU_DEC_LD_NONE(vu_i_waitq); return; case 0x3C: VU_DEC_LD_T_VIDST_S_SF_SRC(vu_i_mtir); return; case 0x3D: VU_DEC_LD_T_DST_S_VISRC(vu_i_mfir); return; case 0x3E: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_ilwr); return; case 0x3F: VU_DEC_LD_T_VISRC_S_VISRC(vu_i_iswr); return; case 0x40: VU_DEC_LD_T_DST(vu_i_rnext); return; case 0x41: VU_DEC_LD_T_DST(vu_i_rget); return; case 0x42: VU_DEC_LD_S_SF_SRC(vu_i_rinit); return; case 0x43: VU_DEC_LD_S_SF_SRC(vu_i_rxor); return; case 0x64: VU_DEC_LD_T_DST(vu_i_mfp); return; case 0x68: VU_DEC_LD_T_VIDST(vu_i_xtop); return; case 0x69: VU_DEC_LD_T_VIDST(vu_i_xitop); return; case 0x6C: VU_DEC_LD_S_VISRC(vu_i_xgkick); return; case 0x70: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_esadd); return; case 0x71: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_ersadd); return; case 0x72: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_eleng); return; case 0x73: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z, vu_i_erleng); return; case 0x74: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y, vu_i_eatanxy); return; case 0x75: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Z, vu_i_eatanxz); return; case 0x76: VU_DEC_LD_S_FLD_SRC(VU_FLD_X | VU_FLD_Y | VU_FLD_Z | VU_FLD_W, vu_i_esum); return; case 0x78: VU_DEC_LD_S_SF_SRC(vu_i_esqrt); return; case 0x79: VU_DEC_LD_S_SF_SRC(vu_i_ersqrt); return; case 0x7A: VU_DEC_LD_S_SF_SRC(vu_i_ercpr); return; case 0x7B: VU_DEC_LD_NONE(vu_i_waitp); return; case 0x7C: VU_DEC_LD_S_SF_SRC(vu_i_esin); return; case 0x7D: VU_DEC_LD_S_SF_SRC(vu_i_eatan); return; case 0x7E: VU_DEC_LD_S_SF_SRC(vu_i_eexp); return; } } else { switch (opcode & 0x3F) { case 0x30: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_iadd); return; case 0x31: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_isub); return; case 0x32: VU_DEC_LD_T_VIDST_S_VISRC(vu_i_iaddi); return; case 0x34: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_iand); return; case 0x35: VU_DEC_LD_D_VIDST_S_VISRC_T_VISRC(vu_i_ior); return; } } } break; } } static inline void vu_advance_fmac_pipeline(struct vu_state* vu) { vu->upper_pipeline[3] = vu->upper_pipeline[2]; vu->upper_pipeline[2] = vu->upper_pipeline[1]; vu->upper_pipeline[1] = vu->upper_pipeline[0]; vu->upper_pipeline[0].dst.reg = vu->upper.dst.reg; vu->upper_pipeline[0].dst.field = vu->upper.dst.field; vu->lower_pipeline[3] = vu->lower_pipeline[2]; vu->lower_pipeline[2] = vu->lower_pipeline[1]; vu->lower_pipeline[1] = vu->lower_pipeline[0]; vu->lower_pipeline[0].dst.reg = vu->lower.dst.reg; vu->lower_pipeline[0].dst.field = vu->lower.dst.field; } static inline int vu_get_fmac_stall_cycles(struct vu_state* vu) { for (int i = 0; i < 4; i++) { for (int j = 0; j < 2; j++) { if (vu->upper.src[j].reg == vu->lower_pipeline[i].dst.reg) { if (vu->upper.src[j].field & vu->lower_pipeline[i].dst.field) { return 4 - i; } } } } return 0; } void vu_execute_program(struct vu_state* vu, uint32_t addr) { // printf("vu%d: Executing program at %08x (%08x) TOP=%08x\n", vu->id, addr, addr << 3, vu->vif->top); // Disable VU1 // if (vu->id == 1) // return; struct vu_dis_state ds; ds.print_address = 0; ds.print_opcode = 0; vu->tpc = addr; vu->next_tpc = addr + 1; vu->i_bit = 0; vu->e_bit = 0; vu->m_bit = 0; vu->d_bit = 0; vu->t_bit = 0; int delayed_e_bit = 0; while (!delayed_e_bit) { uint32_t tpc = vu->tpc; uint64_t liw = vu->micro_mem[vu->tpc]; vu->tpc = vu->next_tpc; vu->next_tpc = vu->tpc + 1; vu->tpc &= 0x7ff; vu->next_tpc &= 0x7ff; ds.addr = tpc; delayed_e_bit = vu->e_bit != 0; uint32_t upper = liw >> 32; uint32_t lower = liw & 0xffffffff; vu->i_bit = (upper & 0x80000000) != 0; vu->e_bit = (upper & 0x40000000) != 0; vu->m_bit = (upper & 0x20000000) != 0; vu->d_bit = (upper & 0x10000000) != 0; vu->t_bit = (upper & 0x08000000) != 0; if (vu->q_delay) vu->q_delay--; vu_update_status(vu); vu_decode_upper(vu, upper & 0x7ffffff); // char ubuf[512]; // printf("%04x: %08x %08x ", tpc, upper, lower); // printf("%-40s", vu_disassemble_upper(ubuf, upper & 0x7ffffff, &ds)); if (vu->i_bit) { // printf("%-12s0x%08x\n", "loi", lower); vu->upper.func(vu, &vu->upper); // LOI vu->i.u32 = lower; vu->lower.func = NULL; vu->lower.dst.reg = 0; vu->lower.dst.field = 0; vu->lower.src[0].reg = 0; vu->lower.src[0].field = 0; vu->lower.src[1].reg = 0; vu->lower.src[1].field = 0; vu->lower.vi_src[0] = 0; vu->lower.vi_src[1] = 0; vu->lower.vi_dst = 0; } else { vu_decode_lower(vu, lower); // char lbuf[512]; // printf("%-40s\n", vu_disassemble_lower(lbuf, lower & 0xffffffff, &ds)); int hazard0 = vu->upper.dst.reg == vu->lower.src[0].reg; int hazard1 = vu->upper.dst.reg == vu->lower.src[1].reg; int hazard2 = vu->upper.dst.reg == vu->lower.dst.reg; int hazard3 = (vu->lower.dst.reg == VU_REG_Q) && vu->q_delay; int waitq = vu->lower.func == vu_i_waitq; // If the lower instruction writes to Q and Q is not ready yet, // the VU stalls the pipeline until it is ready. if (hazard3) vu->q_delay = 0; // Note: This code checks hazards and stalls pipes when the FMAC pipe stalls. // It's absolutely disgusting, so I'm commenting it out for now. // Fixes: // - Raiden III /* int stall = vu_get_fmac_stall_cycles(vu); vu->q_delay -= stall; if (vu->q_delay < 0) vu->q_delay = 0; for (int k = 0; k < stall; k++) { vu_advance_fmac_pipeline(vu); } */ if (!vu->upper.dst.reg) { vu->upper.func(vu, &vu->upper); vu->lower.func(vu, &vu->lower); } else if (hazard0 || hazard1 || waitq) { // Upper instruction writes to a register that the lower // instruction reads from. In this case the lower instruction // gets the previous value of the register, executing the lower // instruction first does the trick. // We also execute WAITQ first, since it will stall the pipeline // if the upper instruction reads Q vu->lower.func(vu, &vu->lower); vu->upper.func(vu, &vu->upper); } else if (hazard2) { // Upper and lower instructions write to the same register. // In this case the upper instruction takes priority, so we // restore the value of the register after executing the lower // instruction. vu->upper.func(vu, &vu->upper); struct vu_reg128 tmp = vu->vf[vu->upper.dst.reg]; vu->lower.func(vu, &vu->lower); vu->vf[vu->upper.dst.reg] = tmp; } else { vu->upper.func(vu, &vu->upper); vu->lower.func(vu, &vu->lower); } } // if (vu_get_fmac_stall_cycles(vu)) { // 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); // exit(1); // } vu_advance_fmac_pipeline(vu); vu->mac_pipeline[3] = vu->mac_pipeline[2]; vu->mac_pipeline[2] = vu->mac_pipeline[1]; vu->mac_pipeline[1] = vu->mac_pipeline[0]; vu->mac_pipeline[0] = vu->mac; vu->clip_pipeline[3] = vu->clip_pipeline[2]; vu->clip_pipeline[2] = vu->clip_pipeline[1]; vu->clip_pipeline[1] = vu->clip_pipeline[0]; vu->clip_pipeline[0] = vu->clip; if (vu->vi_backup_cycles) { vu->vi_backup_cycles--; if (!vu->vi_backup_cycles) { vu->vi_backup_reg = 0; vu->vi_backup_value = 0; } } // if (vu->xgkick_pending) { // vu->xgkick_pending--; // if (!vu->xgkick_pending) { // vu_xgkick(vu); // } // } } } void ps2_vu_write_vi(struct vu_state* vu, int index, uint32_t value) { switch (index) { case 0: return; case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: { vu->vi[index] = value & 0xffff; } break; case 16: { vu->status &= ~0xfc0; vu->status |= value & 0xfc0; } break; case 17: return; // MAC flag register, read-only case 18: { vu->clip = value & 0xffffff; } break; case 19: return; // VU revision register? read-only case 20: { vu->r.u32 = value & 0x7fffff; } break; case 21: { vu->i.u32 = value; } break; case 22: { vu->q.u32 = value; } break; case 23: return; case 24: { vu->cr[8] = value & 0xc0c; } break; case 25: return; case 26: return; // VU TPC register, read-only case 27: { vu->cmsar0 = value & 0xffff; } break; case 28: { // To-do: Handle FBRST vu->fbrst = value & 0xc0c; if (value & 2) { // Reset VU0 ps2_vu_reset(vu); } if (value & 0x200) { // Reset VU1 ps2_vu_reset(vu->vu1); } } break; case 29: return; // VU VPU-STAT register, read-only case 30: return; // VU reserved register, read-only case 31: { vu->cmsar1 = value & 0xffff; vu_execute_program(vu->vu1, vu->cmsar1 >> 3); } break; } } uint32_t ps2_vu_read_vi(struct vu_state* vu, int index) { switch (index) { case 0: case 1: case 2: case 3: case 4: case 5: case 6: case 7: case 8: case 9: case 10: case 11: case 12: case 13: case 14: case 15: { return vu->vi[index]; } break; case 19: { // VU revision register return 0x2e30; } break; default: { return vu->cr[index - 16]; } break; } } void ps2_vu_reset(struct vu_state* vu) { for (int i = 0; i < 16; i++) vu->vi[i] = 0; for (int i = 0; i < 32; i++) { vu->vf[i].u32[0] = 0; vu->vf[i].u32[1] = 0; vu->vf[i].u32[2] = 0; vu->vf[i].u32[3] = 0; } vu->r.u32 = 0x3f800000; vu->i.u32 = 0; vu->q.u32 = 0; vu->clip = 0; vu->status = 0; vu->fbrst = 0; vu->cmsar0 = 0; vu->cmsar1 = 0; vu->mac = 0; vu->mac_pipeline[0] = 0; vu->mac_pipeline[1] = 0; vu->mac_pipeline[2] = 0; vu->mac_pipeline[3] = 0; vu->clip_pipeline[0] = 0; vu->clip_pipeline[1] = 0; vu->clip_pipeline[2] = 0; vu->clip_pipeline[3] = 0; vu->tpc = 0; vu->next_tpc = 1; vu->i_bit = 0; vu->e_bit = 0; vu->m_bit = 0; vu->d_bit = 0; vu->t_bit = 0; vu->q_delay = 0; vu->prev_q.u32 = 0; vu->vf[0].w = 1.0; } void ps2_vu_decode_upper(struct vu_state* vu, uint32_t opcode) { vu_decode_upper(vu, opcode); } void ps2_vu_decode_lower(struct vu_state* vu, uint32_t opcode) { vu_decode_lower(vu, opcode); } void vu_execute_program_tpc(struct vu_state* vu) { vu_execute_program(vu, vu->tpc); } uint128_t* vu_get_vu_mem_ptr(struct vu_state* vu, uint32_t addr) { return &vu->vu_mem[addr & vu->vu_mem_size]; } uint64_t* vu_get_micro_mem_ptr(struct vu_state* vu, uint32_t addr) { return &vu->micro_mem[addr & vu->micro_mem_size]; } uint32_t vu_get_tpc(struct vu_state* vu) { return vu->tpc; } // #undef printf ================================================ FILE: src/elf.h ================================================ /* This is the original elf.h file from the GNU C Library; I only removed the inclusion of feature.h and added definitions of __BEGIN_DECLS and __END_DECLS as documented in https://cmd.inp.nsk.su/old/cmd2/manuals/gnudocs/gnudocs/libtool/libtool_36.html On macOS, simply copy the file to /usr/local/include/. Mathias Lafeldt */ /* This file defines standard ELF types, structures, and macros. Copyright (C) 1995-2003,2004,2005,2006,2007,2008,2009,2010,2011 Free Software Foundation, Inc. This file is part of the GNU C Library. The GNU C Library is free software; you can redistribute it and/or modify it under the terms of the GNU Lesser General Public License as published by the Free Software Foundation; either version 2.1 of the License, or (at your option) any later version. The GNU C Library is distributed in the hope that it will be useful, but WITHOUT ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details. You should have received a copy of the GNU Lesser General Public License along with the GNU C Library; if not, write to the Free Software Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA. */ #ifndef _ELF_H #define _ELF_H 1 /* __BEGIN_DECLS should be used at the beginning of your declarations, so that C++ compilers don't mangle their names. Use __END_DECLS at the end of C declarations. */ #undef __BEGIN_DECLS #undef __END_DECLS #ifdef __cplusplus # define __BEGIN_DECLS extern "C" { # define __END_DECLS } #else # define __BEGIN_DECLS /* empty */ # define __END_DECLS /* empty */ #endif __BEGIN_DECLS /* Standard ELF types. */ #include /* Type for a 16-bit quantity. */ typedef uint16_t Elf32_Half; typedef uint16_t Elf64_Half; /* Types for signed and unsigned 32-bit quantities. */ typedef uint32_t Elf32_Word; typedef int32_t Elf32_Sword; typedef uint32_t Elf64_Word; typedef int32_t Elf64_Sword; /* Types for signed and unsigned 64-bit quantities. */ typedef uint64_t Elf32_Xword; typedef int64_t Elf32_Sxword; typedef uint64_t Elf64_Xword; typedef int64_t Elf64_Sxword; /* Type of addresses. */ typedef uint32_t Elf32_Addr; typedef uint64_t Elf64_Addr; /* Type of file offsets. */ typedef uint32_t Elf32_Off; typedef uint64_t Elf64_Off; /* Type for section indices, which are 16-bit quantities. */ typedef uint16_t Elf32_Section; typedef uint16_t Elf64_Section; /* Type for version symbol information. */ typedef Elf32_Half Elf32_Versym; typedef Elf64_Half Elf64_Versym; /* The ELF file header. This appears at the start of every ELF file. */ #define EI_NIDENT (16) typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ Elf32_Half e_type; /* Object file type */ Elf32_Half e_machine; /* Architecture */ Elf32_Word e_version; /* Object file version */ Elf32_Addr e_entry; /* Entry point virtual address */ Elf32_Off e_phoff; /* Program header table file offset */ Elf32_Off e_shoff; /* Section header table file offset */ Elf32_Word e_flags; /* Processor-specific flags */ Elf32_Half e_ehsize; /* ELF header size in bytes */ Elf32_Half e_phentsize; /* Program header table entry size */ Elf32_Half e_phnum; /* Program header table entry count */ Elf32_Half e_shentsize; /* Section header table entry size */ Elf32_Half e_shnum; /* Section header table entry count */ Elf32_Half e_shstrndx; /* Section header string table index */ } Elf32_Ehdr; typedef struct { unsigned char e_ident[EI_NIDENT]; /* Magic number and other info */ Elf64_Half e_type; /* Object file type */ Elf64_Half e_machine; /* Architecture */ Elf64_Word e_version; /* Object file version */ Elf64_Addr e_entry; /* Entry point virtual address */ Elf64_Off e_phoff; /* Program header table file offset */ Elf64_Off e_shoff; /* Section header table file offset */ Elf64_Word e_flags; /* Processor-specific flags */ Elf64_Half e_ehsize; /* ELF header size in bytes */ Elf64_Half e_phentsize; /* Program header table entry size */ Elf64_Half e_phnum; /* Program header table entry count */ Elf64_Half e_shentsize; /* Section header table entry size */ Elf64_Half e_shnum; /* Section header table entry count */ Elf64_Half e_shstrndx; /* Section header string table index */ } Elf64_Ehdr; /* Fields in the e_ident array. The EI_* macros are indices into the array. The macros under each EI_* macro are the values the byte may have. */ #define EI_MAG0 0 /* File identification byte 0 index */ #define ELFMAG0 0x7f /* Magic number byte 0 */ #define EI_MAG1 1 /* File identification byte 1 index */ #define ELFMAG1 'E' /* Magic number byte 1 */ #define EI_MAG2 2 /* File identification byte 2 index */ #define ELFMAG2 'L' /* Magic number byte 2 */ #define EI_MAG3 3 /* File identification byte 3 index */ #define ELFMAG3 'F' /* Magic number byte 3 */ /* Conglomeration of the identification bytes, for easy testing as a word. */ #define ELFMAG "\177ELF" #define SELFMAG 4 #define EI_CLASS 4 /* File class byte index */ #define ELFCLASSNONE 0 /* Invalid class */ #define ELFCLASS32 1 /* 32-bit objects */ #define ELFCLASS64 2 /* 64-bit objects */ #define ELFCLASSNUM 3 #define EI_DATA 5 /* Data encoding byte index */ #define ELFDATANONE 0 /* Invalid data encoding */ #define ELFDATA2LSB 1 /* 2's complement, little endian */ #define ELFDATA2MSB 2 /* 2's complement, big endian */ #define ELFDATANUM 3 #define EI_VERSION 6 /* File version byte index */ /* Value must be EV_CURRENT */ #define EI_OSABI 7 /* OS ABI identification */ #define ELFOSABI_NONE 0 /* UNIX System V ABI */ #define ELFOSABI_SYSV 0 /* Alias. */ #define ELFOSABI_HPUX 1 /* HP-UX */ #define ELFOSABI_NETBSD 2 /* NetBSD. */ #define ELFOSABI_GNU 3 /* Object uses GNU ELF extensions. */ #define ELFOSABI_LINUX ELFOSABI_GNU /* Compatibility alias. */ #define ELFOSABI_SOLARIS 6 /* Sun Solaris. */ #define ELFOSABI_AIX 7 /* IBM AIX. */ #define ELFOSABI_IRIX 8 /* SGI Irix. */ #define ELFOSABI_FREEBSD 9 /* FreeBSD. */ #define ELFOSABI_TRU64 10 /* Compaq TRU64 UNIX. */ #define ELFOSABI_MODESTO 11 /* Novell Modesto. */ #define ELFOSABI_OPENBSD 12 /* OpenBSD. */ #define ELFOSABI_ARM_AEABI 64 /* ARM EABI */ #define ELFOSABI_ARM 97 /* ARM */ #define ELFOSABI_STANDALONE 255 /* Standalone (embedded) application */ #define EI_ABIVERSION 8 /* ABI version */ #define EI_PAD 9 /* Byte index of padding bytes */ /* Legal values for e_type (object file type). */ #define ET_NONE 0 /* No file type */ #define ET_REL 1 /* Relocatable file */ #define ET_EXEC 2 /* Executable file */ #define ET_DYN 3 /* Shared object file */ #define ET_CORE 4 /* Core file */ #define ET_NUM 5 /* Number of defined types */ #define ET_LOOS 0xfe00 /* OS-specific range start */ #define ET_HIOS 0xfeff /* OS-specific range end */ #define ET_LOPROC 0xff00 /* Processor-specific range start */ #define ET_HIPROC 0xffff /* Processor-specific range end */ /* Legal values for e_machine (architecture). */ #define EM_NONE 0 /* No machine */ #define EM_M32 1 /* AT&T WE 32100 */ #define EM_SPARC 2 /* SUN SPARC */ #define EM_386 3 /* Intel 80386 */ #define EM_68K 4 /* Motorola m68k family */ #define EM_88K 5 /* Motorola m88k family */ #define EM_860 7 /* Intel 80860 */ #define EM_MIPS 8 /* MIPS R3000 big-endian */ #define EM_S370 9 /* IBM System/370 */ #define EM_MIPS_RS3_LE 10 /* MIPS R3000 little-endian */ #define EM_PARISC 15 /* HPPA */ #define EM_VPP500 17 /* Fujitsu VPP500 */ #define EM_SPARC32PLUS 18 /* Sun's "v8plus" */ #define EM_960 19 /* Intel 80960 */ #define EM_PPC 20 /* PowerPC */ #define EM_PPC64 21 /* PowerPC 64-bit */ #define EM_S390 22 /* IBM S390 */ #define EM_V800 36 /* NEC V800 series */ #define EM_FR20 37 /* Fujitsu FR20 */ #define EM_RH32 38 /* TRW RH-32 */ #define EM_RCE 39 /* Motorola RCE */ #define EM_ARM 40 /* ARM */ #define EM_FAKE_ALPHA 41 /* Digital Alpha */ #define EM_SH 42 /* Hitachi SH */ #define EM_SPARCV9 43 /* SPARC v9 64-bit */ #define EM_TRICORE 44 /* Siemens Tricore */ #define EM_ARC 45 /* Argonaut RISC Core */ #define EM_H8_300 46 /* Hitachi H8/300 */ #define EM_H8_300H 47 /* Hitachi H8/300H */ #define EM_H8S 48 /* Hitachi H8S */ #define EM_H8_500 49 /* Hitachi H8/500 */ #define EM_IA_64 50 /* Intel Merced */ #define EM_MIPS_X 51 /* Stanford MIPS-X */ #define EM_COLDFIRE 52 /* Motorola Coldfire */ #define EM_68HC12 53 /* Motorola M68HC12 */ #define EM_MMA 54 /* Fujitsu MMA Multimedia Accelerator*/ #define EM_PCP 55 /* Siemens PCP */ #define EM_NCPU 56 /* Sony nCPU embeeded RISC */ #define EM_NDR1 57 /* Denso NDR1 microprocessor */ #define EM_STARCORE 58 /* Motorola Start*Core processor */ #define EM_ME16 59 /* Toyota ME16 processor */ #define EM_ST100 60 /* STMicroelectronic ST100 processor */ #define EM_TINYJ 61 /* Advanced Logic Corp. Tinyj emb.fam*/ #define EM_X86_64 62 /* AMD x86-64 architecture */ #define EM_PDSP 63 /* Sony DSP Processor */ #define EM_FX66 66 /* Siemens FX66 microcontroller */ #define EM_ST9PLUS 67 /* STMicroelectronics ST9+ 8/16 mc */ #define EM_ST7 68 /* STmicroelectronics ST7 8 bit mc */ #define EM_68HC16 69 /* Motorola MC68HC16 microcontroller */ #define EM_68HC11 70 /* Motorola MC68HC11 microcontroller */ #define EM_68HC08 71 /* Motorola MC68HC08 microcontroller */ #define EM_68HC05 72 /* Motorola MC68HC05 microcontroller */ #define EM_SVX 73 /* Silicon Graphics SVx */ #define EM_ST19 74 /* STMicroelectronics ST19 8 bit mc */ #define EM_VAX 75 /* Digital VAX */ #define EM_CRIS 76 /* Axis Communications 32-bit embedded processor */ #define EM_JAVELIN 77 /* Infineon Technologies 32-bit embedded processor */ #define EM_FIREPATH 78 /* Element 14 64-bit DSP Processor */ #define EM_ZSP 79 /* LSI Logic 16-bit DSP Processor */ #define EM_MMIX 80 /* Donald Knuth's educational 64-bit processor */ #define EM_HUANY 81 /* Harvard University machine-independent object files */ #define EM_PRISM 82 /* SiTera Prism */ #define EM_AVR 83 /* Atmel AVR 8-bit microcontroller */ #define EM_FR30 84 /* Fujitsu FR30 */ #define EM_D10V 85 /* Mitsubishi D10V */ #define EM_D30V 86 /* Mitsubishi D30V */ #define EM_V850 87 /* NEC v850 */ #define EM_M32R 88 /* Mitsubishi M32R */ #define EM_MN10300 89 /* Matsushita MN10300 */ #define EM_MN10200 90 /* Matsushita MN10200 */ #define EM_PJ 91 /* picoJava */ #define EM_OPENRISC 92 /* OpenRISC 32-bit embedded processor */ #define EM_ARC_A5 93 /* ARC Cores Tangent-A5 */ #define EM_XTENSA 94 /* Tensilica Xtensa Architecture */ #define EM_NUM 95 /* If it is necessary to assign new unofficial EM_* values, please pick large random numbers (0x8523, 0xa7f2, etc.) to minimize the chances of collision with official or non-GNU unofficial values. */ #define EM_ALPHA 0x9026 /* Legal values for e_version (version). */ #define EV_NONE 0 /* Invalid ELF version */ #define EV_CURRENT 1 /* Current version */ #define EV_NUM 2 /* Section header. */ typedef struct { Elf32_Word sh_name; /* Section name (string tbl index) */ Elf32_Word sh_type; /* Section type */ Elf32_Word sh_flags; /* Section flags */ Elf32_Addr sh_addr; /* Section virtual addr at execution */ Elf32_Off sh_offset; /* Section file offset */ Elf32_Word sh_size; /* Section size in bytes */ Elf32_Word sh_link; /* Link to another section */ Elf32_Word sh_info; /* Additional section information */ Elf32_Word sh_addralign; /* Section alignment */ Elf32_Word sh_entsize; /* Entry size if section holds table */ } Elf32_Shdr; typedef struct { Elf64_Word sh_name; /* Section name (string tbl index) */ Elf64_Word sh_type; /* Section type */ Elf64_Xword sh_flags; /* Section flags */ Elf64_Addr sh_addr; /* Section virtual addr at execution */ Elf64_Off sh_offset; /* Section file offset */ Elf64_Xword sh_size; /* Section size in bytes */ Elf64_Word sh_link; /* Link to another section */ Elf64_Word sh_info; /* Additional section information */ Elf64_Xword sh_addralign; /* Section alignment */ Elf64_Xword sh_entsize; /* Entry size if section holds table */ } Elf64_Shdr; /* Special section indices. */ #define SHN_UNDEF 0 /* Undefined section */ #define SHN_LORESERVE 0xff00 /* Start of reserved indices */ #define SHN_LOPROC 0xff00 /* Start of processor-specific */ #define SHN_BEFORE 0xff00 /* Order section before all others (Solaris). */ #define SHN_AFTER 0xff01 /* Order section after all others (Solaris). */ #define SHN_HIPROC 0xff1f /* End of processor-specific */ #define SHN_LOOS 0xff20 /* Start of OS-specific */ #define SHN_HIOS 0xff3f /* End of OS-specific */ #define SHN_ABS 0xfff1 /* Associated symbol is absolute */ #define SHN_COMMON 0xfff2 /* Associated symbol is common */ #define SHN_XINDEX 0xffff /* Index is in extra table. */ #define SHN_HIRESERVE 0xffff /* End of reserved indices */ /* Legal values for sh_type (section type). */ #define SHT_NULL 0 /* Section header table entry unused */ #define SHT_PROGBITS 1 /* Program data */ #define SHT_SYMTAB 2 /* Symbol table */ #define SHT_STRTAB 3 /* String table */ #define SHT_RELA 4 /* Relocation entries with addends */ #define SHT_HASH 5 /* Symbol hash table */ #define SHT_DYNAMIC 6 /* Dynamic linking information */ #define SHT_NOTE 7 /* Notes */ #define SHT_NOBITS 8 /* Program space with no data (bss) */ #define SHT_REL 9 /* Relocation entries, no addends */ #define SHT_SHLIB 10 /* Reserved */ #define SHT_DYNSYM 11 /* Dynamic linker symbol table */ #define SHT_INIT_ARRAY 14 /* Array of constructors */ #define SHT_FINI_ARRAY 15 /* Array of destructors */ #define SHT_PREINIT_ARRAY 16 /* Array of pre-constructors */ #define SHT_GROUP 17 /* Section group */ #define SHT_SYMTAB_SHNDX 18 /* Extended section indeces */ #define SHT_NUM 19 /* Number of defined types. */ #define SHT_LOOS 0x60000000 /* Start OS-specific. */ #define SHT_GNU_ATTRIBUTES 0x6ffffff5 /* Object attributes. */ #define SHT_GNU_HASH 0x6ffffff6 /* GNU-style hash table. */ #define SHT_GNU_LIBLIST 0x6ffffff7 /* Prelink library list */ #define SHT_CHECKSUM 0x6ffffff8 /* Checksum for DSO content. */ #define SHT_LOSUNW 0x6ffffffa /* Sun-specific low bound. */ #define SHT_SUNW_move 0x6ffffffa #define SHT_SUNW_COMDAT 0x6ffffffb #define SHT_SUNW_syminfo 0x6ffffffc #define SHT_GNU_verdef 0x6ffffffd /* Version definition section. */ #define SHT_GNU_verneed 0x6ffffffe /* Version needs section. */ #define SHT_GNU_versym 0x6fffffff /* Version symbol table. */ #define SHT_HISUNW 0x6fffffff /* Sun-specific high bound. */ #define SHT_HIOS 0x6fffffff /* End OS-specific type */ #define SHT_LOPROC 0x70000000 /* Start of processor-specific */ #define SHT_HIPROC 0x7fffffff /* End of processor-specific */ #define SHT_LOUSER 0x80000000 /* Start of application-specific */ #define SHT_HIUSER 0x8fffffff /* End of application-specific */ /* Legal values for sh_flags (section flags). */ #define SHF_WRITE (1 << 0) /* Writable */ #define SHF_ALLOC (1 << 1) /* Occupies memory during execution */ #define SHF_EXECINSTR (1 << 2) /* Executable */ #define SHF_MERGE (1 << 4) /* Might be merged */ #define SHF_STRINGS (1 << 5) /* Contains nul-terminated strings */ #define SHF_INFO_LINK (1 << 6) /* `sh_info' contains SHT index */ #define SHF_LINK_ORDER (1 << 7) /* Preserve order after combining */ #define SHF_OS_NONCONFORMING (1 << 8) /* Non-standard OS specific handling required */ #define SHF_GROUP (1 << 9) /* Section is member of a group. */ #define SHF_TLS (1 << 10) /* Section hold thread-local data. */ #define SHF_MASKOS 0x0ff00000 /* OS-specific. */ #define SHF_MASKPROC 0xf0000000 /* Processor-specific */ #define SHF_ORDERED (1 << 30) /* Special ordering requirement (Solaris). */ #define SHF_EXCLUDE (1 << 31) /* Section is excluded unless referenced or allocated (Solaris).*/ /* Section group handling. */ #define GRP_COMDAT 0x1 /* Mark group as COMDAT. */ /* Symbol table entry. */ typedef struct { Elf32_Word st_name; /* Symbol name (string tbl index) */ Elf32_Addr st_value; /* Symbol value */ Elf32_Word st_size; /* Symbol size */ unsigned char st_info; /* Symbol type and binding */ unsigned char st_other; /* Symbol visibility */ Elf32_Section st_shndx; /* Section index */ } Elf32_Sym; typedef struct { Elf64_Word st_name; /* Symbol name (string tbl index) */ unsigned char st_info; /* Symbol type and binding */ unsigned char st_other; /* Symbol visibility */ Elf64_Section st_shndx; /* Section index */ Elf64_Addr st_value; /* Symbol value */ Elf64_Xword st_size; /* Symbol size */ } Elf64_Sym; /* The syminfo section if available contains additional information about every dynamic symbol. */ typedef struct { Elf32_Half si_boundto; /* Direct bindings, symbol bound to */ Elf32_Half si_flags; /* Per symbol flags */ } Elf32_Syminfo; typedef struct { Elf64_Half si_boundto; /* Direct bindings, symbol bound to */ Elf64_Half si_flags; /* Per symbol flags */ } Elf64_Syminfo; /* Possible values for si_boundto. */ #define SYMINFO_BT_SELF 0xffff /* Symbol bound to self */ #define SYMINFO_BT_PARENT 0xfffe /* Symbol bound to parent */ #define SYMINFO_BT_LOWRESERVE 0xff00 /* Beginning of reserved entries */ /* Possible bitmasks for si_flags. */ #define SYMINFO_FLG_DIRECT 0x0001 /* Direct bound symbol */ #define SYMINFO_FLG_PASSTHRU 0x0002 /* Pass-thru symbol for translator */ #define SYMINFO_FLG_COPY 0x0004 /* Symbol is a copy-reloc */ #define SYMINFO_FLG_LAZYLOAD 0x0008 /* Symbol bound to object to be lazy loaded */ /* Syminfo version values. */ #define SYMINFO_NONE 0 #define SYMINFO_CURRENT 1 #define SYMINFO_NUM 2 /* How to extract and insert information held in the st_info field. */ #define ELF32_ST_BIND(val) (((unsigned char) (val)) >> 4) #define ELF32_ST_TYPE(val) ((val) & 0xf) #define ELF32_ST_INFO(bind, type) (((bind) << 4) + ((type) & 0xf)) /* Both Elf32_Sym and Elf64_Sym use the same one-byte st_info field. */ #define ELF64_ST_BIND(val) ELF32_ST_BIND (val) #define ELF64_ST_TYPE(val) ELF32_ST_TYPE (val) #define ELF64_ST_INFO(bind, type) ELF32_ST_INFO ((bind), (type)) /* Legal values for ST_BIND subfield of st_info (symbol binding). */ #define STB_LOCAL 0 /* Local symbol */ #define STB_GLOBAL 1 /* Global symbol */ #define STB_WEAK 2 /* Weak symbol */ #define STB_NUM 3 /* Number of defined types. */ #define STB_LOOS 10 /* Start of OS-specific */ #define STB_GNU_UNIQUE 10 /* Unique symbol. */ #define STB_HIOS 12 /* End of OS-specific */ #define STB_LOPROC 13 /* Start of processor-specific */ #define STB_HIPROC 15 /* End of processor-specific */ /* Legal values for ST_TYPE subfield of st_info (symbol type). */ #define STT_NOTYPE 0 /* Symbol type is unspecified */ #define STT_OBJECT 1 /* Symbol is a data object */ #define STT_FUNC 2 /* Symbol is a code object */ #define STT_SECTION 3 /* Symbol associated with a section */ #define STT_FILE 4 /* Symbol's name is file name */ #define STT_COMMON 5 /* Symbol is a common data object */ #define STT_TLS 6 /* Symbol is thread-local data object*/ #define STT_NUM 7 /* Number of defined types. */ #define STT_LOOS 10 /* Start of OS-specific */ #define STT_GNU_IFUNC 10 /* Symbol is indirect code object */ #define STT_HIOS 12 /* End of OS-specific */ #define STT_LOPROC 13 /* Start of processor-specific */ #define STT_HIPROC 15 /* End of processor-specific */ /* Symbol table indices are found in the hash buckets and chain table of a symbol hash table section. This special index value indicates the end of a chain, meaning no further symbols are found in that bucket. */ #define STN_UNDEF 0 /* End of a chain. */ /* How to extract and insert information held in the st_other field. */ #define ELF32_ST_VISIBILITY(o) ((o) & 0x03) /* For ELF64 the definitions are the same. */ #define ELF64_ST_VISIBILITY(o) ELF32_ST_VISIBILITY (o) /* Symbol visibility specification encoded in the st_other field. */ #define STV_DEFAULT 0 /* Default symbol visibility rules */ #define STV_INTERNAL 1 /* Processor specific hidden class */ #define STV_HIDDEN 2 /* Sym unavailable in other modules */ #define STV_PROTECTED 3 /* Not preemptible, not exported */ /* Relocation table entry without addend (in section of type SHT_REL). */ typedef struct { Elf32_Addr r_offset; /* Address */ Elf32_Word r_info; /* Relocation type and symbol index */ } Elf32_Rel; /* I have seen two different definitions of the Elf64_Rel and Elf64_Rela structures, so we'll leave them out until Novell (or whoever) gets their act together. */ /* The following, at least, is used on Sparc v9, MIPS, and Alpha. */ typedef struct { Elf64_Addr r_offset; /* Address */ Elf64_Xword r_info; /* Relocation type and symbol index */ } Elf64_Rel; /* Relocation table entry with addend (in section of type SHT_RELA). */ typedef struct { Elf32_Addr r_offset; /* Address */ Elf32_Word r_info; /* Relocation type and symbol index */ Elf32_Sword r_addend; /* Addend */ } Elf32_Rela; typedef struct { Elf64_Addr r_offset; /* Address */ Elf64_Xword r_info; /* Relocation type and symbol index */ Elf64_Sxword r_addend; /* Addend */ } Elf64_Rela; /* How to extract and insert information held in the r_info field. */ #define ELF32_R_SYM(val) ((val) >> 8) #define ELF32_R_TYPE(val) ((val) & 0xff) #define ELF32_R_INFO(sym, type) (((sym) << 8) + ((type) & 0xff)) #define ELF64_R_SYM(i) ((i) >> 32) #define ELF64_R_TYPE(i) ((i) & 0xffffffff) #define ELF64_R_INFO(sym,type) ((((Elf64_Xword) (sym)) << 32) + (type)) /* Program segment header. */ typedef struct { Elf32_Word p_type; /* Segment type */ Elf32_Off p_offset; /* Segment file offset */ Elf32_Addr p_vaddr; /* Segment virtual address */ Elf32_Addr p_paddr; /* Segment physical address */ Elf32_Word p_filesz; /* Segment size in file */ Elf32_Word p_memsz; /* Segment size in memory */ Elf32_Word p_flags; /* Segment flags */ Elf32_Word p_align; /* Segment alignment */ } Elf32_Phdr; typedef struct { Elf64_Word p_type; /* Segment type */ Elf64_Word p_flags; /* Segment flags */ Elf64_Off p_offset; /* Segment file offset */ Elf64_Addr p_vaddr; /* Segment virtual address */ Elf64_Addr p_paddr; /* Segment physical address */ Elf64_Xword p_filesz; /* Segment size in file */ Elf64_Xword p_memsz; /* Segment size in memory */ Elf64_Xword p_align; /* Segment alignment */ } Elf64_Phdr; /* Special value for e_phnum. This indicates that the real number of program headers is too large to fit into e_phnum. Instead the real value is in the field sh_info of section 0. */ #define PN_XNUM 0xffff /* Legal values for p_type (segment type). */ #define PT_NULL 0 /* Program header table entry unused */ #define PT_LOAD 1 /* Loadable program segment */ #define PT_DYNAMIC 2 /* Dynamic linking information */ #define PT_INTERP 3 /* Program interpreter */ #define PT_NOTE 4 /* Auxiliary information */ #define PT_SHLIB 5 /* Reserved */ #define PT_PHDR 6 /* Entry for header table itself */ #define PT_TLS 7 /* Thread-local storage segment */ #define PT_NUM 8 /* Number of defined types */ #define PT_LOOS 0x60000000 /* Start of OS-specific */ #define PT_GNU_EH_FRAME 0x6474e550 /* GCC .eh_frame_hdr segment */ #define PT_GNU_STACK 0x6474e551 /* Indicates stack executability */ #define PT_GNU_RELRO 0x6474e552 /* Read-only after relocation */ #define PT_LOSUNW 0x6ffffffa #define PT_SUNWBSS 0x6ffffffa /* Sun Specific segment */ #define PT_SUNWSTACK 0x6ffffffb /* Stack segment */ #define PT_HISUNW 0x6fffffff #define PT_HIOS 0x6fffffff /* End of OS-specific */ #define PT_LOPROC 0x70000000 /* Start of processor-specific */ #define PT_HIPROC 0x7fffffff /* End of processor-specific */ /* Legal values for p_flags (segment flags). */ #define PF_X (1 << 0) /* Segment is executable */ #define PF_W (1 << 1) /* Segment is writable */ #define PF_R (1 << 2) /* Segment is readable */ #define PF_MASKOS 0x0ff00000 /* OS-specific */ #define PF_MASKPROC 0xf0000000 /* Processor-specific */ /* Legal values for note segment descriptor types for core files. */ #define NT_PRSTATUS 1 /* Contains copy of prstatus struct */ #define NT_FPREGSET 2 /* Contains copy of fpregset struct */ #define NT_PRPSINFO 3 /* Contains copy of prpsinfo struct */ #define NT_PRXREG 4 /* Contains copy of prxregset struct */ #define NT_TASKSTRUCT 4 /* Contains copy of task structure */ #define NT_PLATFORM 5 /* String from sysinfo(SI_PLATFORM) */ #define NT_AUXV 6 /* Contains copy of auxv array */ #define NT_GWINDOWS 7 /* Contains copy of gwindows struct */ #define NT_ASRS 8 /* Contains copy of asrset struct */ #define NT_PSTATUS 10 /* Contains copy of pstatus struct */ #define NT_PSINFO 13 /* Contains copy of psinfo struct */ #define NT_PRCRED 14 /* Contains copy of prcred struct */ #define NT_UTSNAME 15 /* Contains copy of utsname struct */ #define NT_LWPSTATUS 16 /* Contains copy of lwpstatus struct */ #define NT_LWPSINFO 17 /* Contains copy of lwpinfo struct */ #define NT_PRFPXREG 20 /* Contains copy of fprxregset struct */ #define NT_PRXFPREG 0x46e62b7f /* Contains copy of user_fxsr_struct */ #define NT_PPC_VMX 0x100 /* PowerPC Altivec/VMX registers */ #define NT_PPC_SPE 0x101 /* PowerPC SPE/EVR registers */ #define NT_PPC_VSX 0x102 /* PowerPC VSX registers */ #define NT_386_TLS 0x200 /* i386 TLS slots (struct user_desc) */ #define NT_386_IOPERM 0x201 /* x86 io permission bitmap (1=deny) */ #define NT_X86_XSTATE 0x202 /* x86 extended state using xsave */ /* Legal values for the note segment descriptor types for object files. */ #define NT_VERSION 1 /* Contains a version string. */ /* Dynamic section entry. */ typedef struct { Elf32_Sword d_tag; /* Dynamic entry type */ union { Elf32_Word d_val; /* Integer value */ Elf32_Addr d_ptr; /* Address value */ } d_un; } Elf32_Dyn; typedef struct { Elf64_Sxword d_tag; /* Dynamic entry type */ union { Elf64_Xword d_val; /* Integer value */ Elf64_Addr d_ptr; /* Address value */ } d_un; } Elf64_Dyn; /* Legal values for d_tag (dynamic entry type). */ #define DT_NULL 0 /* Marks end of dynamic section */ #define DT_NEEDED 1 /* Name of needed library */ #define DT_PLTRELSZ 2 /* Size in bytes of PLT relocs */ #define DT_PLTGOT 3 /* Processor defined value */ #define DT_HASH 4 /* Address of symbol hash table */ #define DT_STRTAB 5 /* Address of string table */ #define DT_SYMTAB 6 /* Address of symbol table */ #define DT_RELA 7 /* Address of Rela relocs */ #define DT_RELASZ 8 /* Total size of Rela relocs */ #define DT_RELAENT 9 /* Size of one Rela reloc */ #define DT_STRSZ 10 /* Size of string table */ #define DT_SYMENT 11 /* Size of one symbol table entry */ #define DT_INIT 12 /* Address of init function */ #define DT_FINI 13 /* Address of termination function */ #define DT_SONAME 14 /* Name of shared object */ #define DT_RPATH 15 /* Library search path (deprecated) */ #define DT_SYMBOLIC 16 /* Start symbol search here */ #define DT_REL 17 /* Address of Rel relocs */ #define DT_RELSZ 18 /* Total size of Rel relocs */ #define DT_RELENT 19 /* Size of one Rel reloc */ #define DT_PLTREL 20 /* Type of reloc in PLT */ #define DT_DEBUG 21 /* For debugging; unspecified */ #define DT_TEXTREL 22 /* Reloc might modify .text */ #define DT_JMPREL 23 /* Address of PLT relocs */ #define DT_BIND_NOW 24 /* Process relocations of object */ #define DT_INIT_ARRAY 25 /* Array with addresses of init fct */ #define DT_FINI_ARRAY 26 /* Array with addresses of fini fct */ #define DT_INIT_ARRAYSZ 27 /* Size in bytes of DT_INIT_ARRAY */ #define DT_FINI_ARRAYSZ 28 /* Size in bytes of DT_FINI_ARRAY */ #define DT_RUNPATH 29 /* Library search path */ #define DT_FLAGS 30 /* Flags for the object being loaded */ #define DT_ENCODING 32 /* Start of encoded range */ #define DT_PREINIT_ARRAY 32 /* Array with addresses of preinit fct*/ #define DT_PREINIT_ARRAYSZ 33 /* size in bytes of DT_PREINIT_ARRAY */ #define DT_NUM 34 /* Number used */ #define DT_LOOS 0x6000000d /* Start of OS-specific */ #define DT_HIOS 0x6ffff000 /* End of OS-specific */ #define DT_LOPROC 0x70000000 /* Start of processor-specific */ #define DT_HIPROC 0x7fffffff /* End of processor-specific */ #define DT_PROCNUM DT_MIPS_NUM /* Most used by any processor */ /* DT_* entries which fall between DT_VALRNGHI & DT_VALRNGLO use the Dyn.d_un.d_val field of the Elf*_Dyn structure. This follows Sun's approach. */ #define DT_VALRNGLO 0x6ffffd00 #define DT_GNU_PRELINKED 0x6ffffdf5 /* Prelinking timestamp */ #define DT_GNU_CONFLICTSZ 0x6ffffdf6 /* Size of conflict section */ #define DT_GNU_LIBLISTSZ 0x6ffffdf7 /* Size of library list */ #define DT_CHECKSUM 0x6ffffdf8 #define DT_PLTPADSZ 0x6ffffdf9 #define DT_MOVEENT 0x6ffffdfa #define DT_MOVESZ 0x6ffffdfb #define DT_FEATURE_1 0x6ffffdfc /* Feature selection (DTF_*). */ #define DT_POSFLAG_1 0x6ffffdfd /* Flags for DT_* entries, effecting the following DT_* entry. */ #define DT_SYMINSZ 0x6ffffdfe /* Size of syminfo table (in bytes) */ #define DT_SYMINENT 0x6ffffdff /* Entry size of syminfo */ #define DT_VALRNGHI 0x6ffffdff #define DT_VALTAGIDX(tag) (DT_VALRNGHI - (tag)) /* Reverse order! */ #define DT_VALNUM 12 /* DT_* entries which fall between DT_ADDRRNGHI & DT_ADDRRNGLO use the Dyn.d_un.d_ptr field of the Elf*_Dyn structure. If any adjustment is made to the ELF object after it has been built these entries will need to be adjusted. */ #define DT_ADDRRNGLO 0x6ffffe00 #define DT_GNU_HASH 0x6ffffef5 /* GNU-style hash table. */ #define DT_TLSDESC_PLT 0x6ffffef6 #define DT_TLSDESC_GOT 0x6ffffef7 #define DT_GNU_CONFLICT 0x6ffffef8 /* Start of conflict section */ #define DT_GNU_LIBLIST 0x6ffffef9 /* Library list */ #define DT_CONFIG 0x6ffffefa /* Configuration information. */ #define DT_DEPAUDIT 0x6ffffefb /* Dependency auditing. */ #define DT_AUDIT 0x6ffffefc /* Object auditing. */ #define DT_PLTPAD 0x6ffffefd /* PLT padding. */ #define DT_MOVETAB 0x6ffffefe /* Move table. */ #define DT_SYMINFO 0x6ffffeff /* Syminfo table. */ #define DT_ADDRRNGHI 0x6ffffeff #define DT_ADDRTAGIDX(tag) (DT_ADDRRNGHI - (tag)) /* Reverse order! */ #define DT_ADDRNUM 11 /* The versioning entry types. The next are defined as part of the GNU extension. */ #define DT_VERSYM 0x6ffffff0 #define DT_RELACOUNT 0x6ffffff9 #define DT_RELCOUNT 0x6ffffffa /* These were chosen by Sun. */ #define DT_FLAGS_1 0x6ffffffb /* State flags, see DF_1_* below. */ #define DT_VERDEF 0x6ffffffc /* Address of version definition table */ #define DT_VERDEFNUM 0x6ffffffd /* Number of version definitions */ #define DT_VERNEED 0x6ffffffe /* Address of table with needed versions */ #define DT_VERNEEDNUM 0x6fffffff /* Number of needed versions */ #define DT_VERSIONTAGIDX(tag) (DT_VERNEEDNUM - (tag)) /* Reverse order! */ #define DT_VERSIONTAGNUM 16 /* Sun added these machine-independent extensions in the "processor-specific" range. Be compatible. */ #define DT_AUXILIARY 0x7ffffffd /* Shared object to load before self */ #define DT_FILTER 0x7fffffff /* Shared object to get values from */ #define DT_EXTRATAGIDX(tag) ((Elf32_Word)-((Elf32_Sword) (tag) <<1>>1)-1) #define DT_EXTRANUM 3 /* Values of `d_un.d_val' in the DT_FLAGS entry. */ #define DF_ORIGIN 0x00000001 /* Object may use DF_ORIGIN */ #define DF_SYMBOLIC 0x00000002 /* Symbol resolutions starts here */ #define DF_TEXTREL 0x00000004 /* Object contains text relocations */ #define DF_BIND_NOW 0x00000008 /* No lazy binding for this object */ #define DF_STATIC_TLS 0x00000010 /* Module uses the static TLS model */ /* State flags selectable in the `d_un.d_val' element of the DT_FLAGS_1 entry in the dynamic section. */ #define DF_1_NOW 0x00000001 /* Set RTLD_NOW for this object. */ #define DF_1_GLOBAL 0x00000002 /* Set RTLD_GLOBAL for this object. */ #define DF_1_GROUP 0x00000004 /* Set RTLD_GROUP for this object. */ #define DF_1_NODELETE 0x00000008 /* Set RTLD_NODELETE for this object.*/ #define DF_1_LOADFLTR 0x00000010 /* Trigger filtee loading at runtime.*/ #define DF_1_INITFIRST 0x00000020 /* Set RTLD_INITFIRST for this object*/ #define DF_1_NOOPEN 0x00000040 /* Set RTLD_NOOPEN for this object. */ #define DF_1_ORIGIN 0x00000080 /* $ORIGIN must be handled. */ #define DF_1_DIRECT 0x00000100 /* Direct binding enabled. */ #define DF_1_TRANS 0x00000200 #define DF_1_INTERPOSE 0x00000400 /* Object is used to interpose. */ #define DF_1_NODEFLIB 0x00000800 /* Ignore default lib search path. */ #define DF_1_NODUMP 0x00001000 /* Object can't be dldump'ed. */ #define DF_1_CONFALT 0x00002000 /* Configuration alternative created.*/ #define DF_1_ENDFILTEE 0x00004000 /* Filtee terminates filters search. */ #define DF_1_DISPRELDNE 0x00008000 /* Disp reloc applied at build time. */ #define DF_1_DISPRELPND 0x00010000 /* Disp reloc applied at run-time. */ /* Flags for the feature selection in DT_FEATURE_1. */ #define DTF_1_PARINIT 0x00000001 #define DTF_1_CONFEXP 0x00000002 /* Flags in the DT_POSFLAG_1 entry effecting only the next DT_* entry. */ #define DF_P1_LAZYLOAD 0x00000001 /* Lazyload following object. */ #define DF_P1_GROUPPERM 0x00000002 /* Symbols from next object are not generally available. */ /* Version definition sections. */ typedef struct { Elf32_Half vd_version; /* Version revision */ Elf32_Half vd_flags; /* Version information */ Elf32_Half vd_ndx; /* Version Index */ Elf32_Half vd_cnt; /* Number of associated aux entries */ Elf32_Word vd_hash; /* Version name hash value */ Elf32_Word vd_aux; /* Offset in bytes to verdaux array */ Elf32_Word vd_next; /* Offset in bytes to next verdef entry */ } Elf32_Verdef; typedef struct { Elf64_Half vd_version; /* Version revision */ Elf64_Half vd_flags; /* Version information */ Elf64_Half vd_ndx; /* Version Index */ Elf64_Half vd_cnt; /* Number of associated aux entries */ Elf64_Word vd_hash; /* Version name hash value */ Elf64_Word vd_aux; /* Offset in bytes to verdaux array */ Elf64_Word vd_next; /* Offset in bytes to next verdef entry */ } Elf64_Verdef; /* Legal values for vd_version (version revision). */ #define VER_DEF_NONE 0 /* No version */ #define VER_DEF_CURRENT 1 /* Current version */ #define VER_DEF_NUM 2 /* Given version number */ /* Legal values for vd_flags (version information flags). */ #define VER_FLG_BASE 0x1 /* Version definition of file itself */ #define VER_FLG_WEAK 0x2 /* Weak version identifier */ /* Versym symbol index values. */ #define VER_NDX_LOCAL 0 /* Symbol is local. */ #define VER_NDX_GLOBAL 1 /* Symbol is global. */ #define VER_NDX_LORESERVE 0xff00 /* Beginning of reserved entries. */ #define VER_NDX_ELIMINATE 0xff01 /* Symbol is to be eliminated. */ /* Auxialiary version information. */ typedef struct { Elf32_Word vda_name; /* Version or dependency names */ Elf32_Word vda_next; /* Offset in bytes to next verdaux entry */ } Elf32_Verdaux; typedef struct { Elf64_Word vda_name; /* Version or dependency names */ Elf64_Word vda_next; /* Offset in bytes to next verdaux entry */ } Elf64_Verdaux; /* Version dependency section. */ typedef struct { Elf32_Half vn_version; /* Version of structure */ Elf32_Half vn_cnt; /* Number of associated aux entries */ Elf32_Word vn_file; /* Offset of filename for this dependency */ Elf32_Word vn_aux; /* Offset in bytes to vernaux array */ Elf32_Word vn_next; /* Offset in bytes to next verneed entry */ } Elf32_Verneed; typedef struct { Elf64_Half vn_version; /* Version of structure */ Elf64_Half vn_cnt; /* Number of associated aux entries */ Elf64_Word vn_file; /* Offset of filename for this dependency */ Elf64_Word vn_aux; /* Offset in bytes to vernaux array */ Elf64_Word vn_next; /* Offset in bytes to next verneed entry */ } Elf64_Verneed; /* Legal values for vn_version (version revision). */ #define VER_NEED_NONE 0 /* No version */ #define VER_NEED_CURRENT 1 /* Current version */ #define VER_NEED_NUM 2 /* Given version number */ /* Auxiliary needed version information. */ typedef struct { Elf32_Word vna_hash; /* Hash value of dependency name */ Elf32_Half vna_flags; /* Dependency specific information */ Elf32_Half vna_other; /* Unused */ Elf32_Word vna_name; /* Dependency name string offset */ Elf32_Word vna_next; /* Offset in bytes to next vernaux entry */ } Elf32_Vernaux; typedef struct { Elf64_Word vna_hash; /* Hash value of dependency name */ Elf64_Half vna_flags; /* Dependency specific information */ Elf64_Half vna_other; /* Unused */ Elf64_Word vna_name; /* Dependency name string offset */ Elf64_Word vna_next; /* Offset in bytes to next vernaux entry */ } Elf64_Vernaux; /* Legal values for vna_flags. */ #define VER_FLG_WEAK 0x2 /* Weak version identifier */ /* Auxiliary vector. */ /* This vector is normally only used by the program interpreter. The usual definition in an ABI supplement uses the name auxv_t. The vector is not usually defined in a standard file, but it can't hurt. We rename it to avoid conflicts. The sizes of these types are an arrangement between the exec server and the program interpreter, so we don't fully specify them here. */ typedef struct { uint32_t a_type; /* Entry type */ union { uint32_t a_val; /* Integer value */ /* We use to have pointer elements added here. We cannot do that, though, since it does not work when using 32-bit definitions on 64-bit platforms and vice versa. */ } a_un; } Elf32_auxv_t; typedef struct { uint64_t a_type; /* Entry type */ union { uint64_t a_val; /* Integer value */ /* We use to have pointer elements added here. We cannot do that, though, since it does not work when using 32-bit definitions on 64-bit platforms and vice versa. */ } a_un; } Elf64_auxv_t; /* Legal values for a_type (entry type). */ #define AT_NULL 0 /* End of vector */ #define AT_IGNORE 1 /* Entry should be ignored */ #define AT_EXECFD 2 /* File descriptor of program */ #define AT_PHDR 3 /* Program headers for program */ #define AT_PHENT 4 /* Size of program header entry */ #define AT_PHNUM 5 /* Number of program headers */ #define AT_PAGESZ 6 /* System page size */ #define AT_BASE 7 /* Base address of interpreter */ #define AT_FLAGS 8 /* Flags */ #define AT_ENTRY 9 /* Entry point of program */ #define AT_NOTELF 10 /* Program is not ELF */ #define AT_UID 11 /* Real uid */ #define AT_EUID 12 /* Effective uid */ #define AT_GID 13 /* Real gid */ #define AT_EGID 14 /* Effective gid */ #define AT_CLKTCK 17 /* Frequency of times() */ /* Some more special a_type values describing the hardware. */ #define AT_PLATFORM 15 /* String identifying platform. */ #define AT_HWCAP 16 /* Machine dependent hints about processor capabilities. */ /* This entry gives some information about the FPU initialization performed by the kernel. */ #define AT_FPUCW 18 /* Used FPU control word. */ /* Cache block sizes. */ #define AT_DCACHEBSIZE 19 /* Data cache block size. */ #define AT_ICACHEBSIZE 20 /* Instruction cache block size. */ #define AT_UCACHEBSIZE 21 /* Unified cache block size. */ /* A special ignored value for PPC, used by the kernel to control the interpretation of the AUXV. Must be > 16. */ #define AT_IGNOREPPC 22 /* Entry should be ignored. */ #define AT_SECURE 23 /* Boolean, was exec setuid-like? */ #define AT_BASE_PLATFORM 24 /* String identifying real platforms.*/ #define AT_RANDOM 25 /* Address of 16 random bytes. */ #define AT_EXECFN 31 /* Filename of executable. */ /* Pointer to the global system page used for system calls and other nice things. */ #define AT_SYSINFO 32 #define AT_SYSINFO_EHDR 33 /* Shapes of the caches. Bits 0-3 contains associativity; bits 4-7 contains log2 of line size; mask those to get cache size. */ #define AT_L1I_CACHESHAPE 34 #define AT_L1D_CACHESHAPE 35 #define AT_L2_CACHESHAPE 36 #define AT_L3_CACHESHAPE 37 /* Note section contents. Each entry in the note section begins with a header of a fixed form. */ typedef struct { Elf32_Word n_namesz; /* Length of the note's name. */ Elf32_Word n_descsz; /* Length of the note's descriptor. */ Elf32_Word n_type; /* Type of the note. */ } Elf32_Nhdr; typedef struct { Elf64_Word n_namesz; /* Length of the note's name. */ Elf64_Word n_descsz; /* Length of the note's descriptor. */ Elf64_Word n_type; /* Type of the note. */ } Elf64_Nhdr; /* Known names of notes. */ /* Solaris entries in the note section have this name. */ #define ELF_NOTE_SOLARIS "SUNW Solaris" /* Note entries for GNU systems have this name. */ #define ELF_NOTE_GNU "GNU" /* Defined types of notes for Solaris. */ /* Value of descriptor (one word) is desired pagesize for the binary. */ #define ELF_NOTE_PAGESIZE_HINT 1 /* Defined note types for GNU systems. */ /* ABI information. The descriptor consists of words: word 0: OS descriptor word 1: major version of the ABI word 2: minor version of the ABI word 3: subminor version of the ABI */ #define NT_GNU_ABI_TAG 1 #define ELF_NOTE_ABI NT_GNU_ABI_TAG /* Old name. */ /* Known OSes. These values can appear in word 0 of an NT_GNU_ABI_TAG note section entry. */ #define ELF_NOTE_OS_LINUX 0 #define ELF_NOTE_OS_GNU 1 #define ELF_NOTE_OS_SOLARIS2 2 #define ELF_NOTE_OS_FREEBSD 3 /* Synthetic hwcap information. The descriptor begins with two words: word 0: number of entries word 1: bitmask of enabled entries Then follow variable-length entries, one byte followed by a '\0'-terminated hwcap name string. The byte gives the bit number to test if enabled, (1U << bit) & bitmask. */ #define NT_GNU_HWCAP 2 /* Build ID bits as generated by ld --build-id. The descriptor consists of any nonzero number of bytes. */ #define NT_GNU_BUILD_ID 3 /* Version note generated by GNU gold containing a version string. */ #define NT_GNU_GOLD_VERSION 4 /* Move records. */ typedef struct { Elf32_Xword m_value; /* Symbol value. */ Elf32_Word m_info; /* Size and index. */ Elf32_Word m_poffset; /* Symbol offset. */ Elf32_Half m_repeat; /* Repeat count. */ Elf32_Half m_stride; /* Stride info. */ } Elf32_Move; typedef struct { Elf64_Xword m_value; /* Symbol value. */ Elf64_Xword m_info; /* Size and index. */ Elf64_Xword m_poffset; /* Symbol offset. */ Elf64_Half m_repeat; /* Repeat count. */ Elf64_Half m_stride; /* Stride info. */ } Elf64_Move; /* Macro to construct move records. */ #define ELF32_M_SYM(info) ((info) >> 8) #define ELF32_M_SIZE(info) ((unsigned char) (info)) #define ELF32_M_INFO(sym, size) (((sym) << 8) + (unsigned char) (size)) #define ELF64_M_SYM(info) ELF32_M_SYM (info) #define ELF64_M_SIZE(info) ELF32_M_SIZE (info) #define ELF64_M_INFO(sym, size) ELF32_M_INFO (sym, size) /* Motorola 68k specific definitions. */ /* Values for Elf32_Ehdr.e_flags. */ #define EF_CPU32 0x00810000 /* m68k relocs. */ #define R_68K_NONE 0 /* No reloc */ #define R_68K_32 1 /* Direct 32 bit */ #define R_68K_16 2 /* Direct 16 bit */ #define R_68K_8 3 /* Direct 8 bit */ #define R_68K_PC32 4 /* PC relative 32 bit */ #define R_68K_PC16 5 /* PC relative 16 bit */ #define R_68K_PC8 6 /* PC relative 8 bit */ #define R_68K_GOT32 7 /* 32 bit PC relative GOT entry */ #define R_68K_GOT16 8 /* 16 bit PC relative GOT entry */ #define R_68K_GOT8 9 /* 8 bit PC relative GOT entry */ #define R_68K_GOT32O 10 /* 32 bit GOT offset */ #define R_68K_GOT16O 11 /* 16 bit GOT offset */ #define R_68K_GOT8O 12 /* 8 bit GOT offset */ #define R_68K_PLT32 13 /* 32 bit PC relative PLT address */ #define R_68K_PLT16 14 /* 16 bit PC relative PLT address */ #define R_68K_PLT8 15 /* 8 bit PC relative PLT address */ #define R_68K_PLT32O 16 /* 32 bit PLT offset */ #define R_68K_PLT16O 17 /* 16 bit PLT offset */ #define R_68K_PLT8O 18 /* 8 bit PLT offset */ #define R_68K_COPY 19 /* Copy symbol at runtime */ #define R_68K_GLOB_DAT 20 /* Create GOT entry */ #define R_68K_JMP_SLOT 21 /* Create PLT entry */ #define R_68K_RELATIVE 22 /* Adjust by program base */ #define R_68K_TLS_GD32 25 /* 32 bit GOT offset for GD */ #define R_68K_TLS_GD16 26 /* 16 bit GOT offset for GD */ #define R_68K_TLS_GD8 27 /* 8 bit GOT offset for GD */ #define R_68K_TLS_LDM32 28 /* 32 bit GOT offset for LDM */ #define R_68K_TLS_LDM16 29 /* 16 bit GOT offset for LDM */ #define R_68K_TLS_LDM8 30 /* 8 bit GOT offset for LDM */ #define R_68K_TLS_LDO32 31 /* 32 bit module-relative offset */ #define R_68K_TLS_LDO16 32 /* 16 bit module-relative offset */ #define R_68K_TLS_LDO8 33 /* 8 bit module-relative offset */ #define R_68K_TLS_IE32 34 /* 32 bit GOT offset for IE */ #define R_68K_TLS_IE16 35 /* 16 bit GOT offset for IE */ #define R_68K_TLS_IE8 36 /* 8 bit GOT offset for IE */ #define R_68K_TLS_LE32 37 /* 32 bit offset relative to static TLS block */ #define R_68K_TLS_LE16 38 /* 16 bit offset relative to static TLS block */ #define R_68K_TLS_LE8 39 /* 8 bit offset relative to static TLS block */ #define R_68K_TLS_DTPMOD32 40 /* 32 bit module number */ #define R_68K_TLS_DTPREL32 41 /* 32 bit module-relative offset */ #define R_68K_TLS_TPREL32 42 /* 32 bit TP-relative offset */ /* Keep this the last entry. */ #define R_68K_NUM 43 /* Intel 80386 specific definitions. */ /* i386 relocs. */ #define R_386_NONE 0 /* No reloc */ #define R_386_32 1 /* Direct 32 bit */ #define R_386_PC32 2 /* PC relative 32 bit */ #define R_386_GOT32 3 /* 32 bit GOT entry */ #define R_386_PLT32 4 /* 32 bit PLT address */ #define R_386_COPY 5 /* Copy symbol at runtime */ #define R_386_GLOB_DAT 6 /* Create GOT entry */ #define R_386_JMP_SLOT 7 /* Create PLT entry */ #define R_386_RELATIVE 8 /* Adjust by program base */ #define R_386_GOTOFF 9 /* 32 bit offset to GOT */ #define R_386_GOTPC 10 /* 32 bit PC relative offset to GOT */ #define R_386_32PLT 11 #define R_386_TLS_TPOFF 14 /* Offset in static TLS block */ #define R_386_TLS_IE 15 /* Address of GOT entry for static TLS block offset */ #define R_386_TLS_GOTIE 16 /* GOT entry for static TLS block offset */ #define R_386_TLS_LE 17 /* Offset relative to static TLS block */ #define R_386_TLS_GD 18 /* Direct 32 bit for GNU version of general dynamic thread local data */ #define R_386_TLS_LDM 19 /* Direct 32 bit for GNU version of local dynamic thread local data in LE code */ #define R_386_16 20 #define R_386_PC16 21 #define R_386_8 22 #define R_386_PC8 23 #define R_386_TLS_GD_32 24 /* Direct 32 bit for general dynamic thread local data */ #define R_386_TLS_GD_PUSH 25 /* Tag for pushl in GD TLS code */ #define R_386_TLS_GD_CALL 26 /* Relocation for call to __tls_get_addr() */ #define R_386_TLS_GD_POP 27 /* Tag for popl in GD TLS code */ #define R_386_TLS_LDM_32 28 /* Direct 32 bit for local dynamic thread local data in LE code */ #define R_386_TLS_LDM_PUSH 29 /* Tag for pushl in LDM TLS code */ #define R_386_TLS_LDM_CALL 30 /* Relocation for call to __tls_get_addr() in LDM code */ #define R_386_TLS_LDM_POP 31 /* Tag for popl in LDM TLS code */ #define R_386_TLS_LDO_32 32 /* Offset relative to TLS block */ #define R_386_TLS_IE_32 33 /* GOT entry for negated static TLS block offset */ #define R_386_TLS_LE_32 34 /* Negated offset relative to static TLS block */ #define R_386_TLS_DTPMOD32 35 /* ID of module containing symbol */ #define R_386_TLS_DTPOFF32 36 /* Offset in TLS block */ #define R_386_TLS_TPOFF32 37 /* Negated offset in static TLS block */ /* 38? */ #define R_386_TLS_GOTDESC 39 /* GOT offset for TLS descriptor. */ #define R_386_TLS_DESC_CALL 40 /* Marker of call through TLS descriptor for relaxation. */ #define R_386_TLS_DESC 41 /* TLS descriptor containing pointer to code and to argument, returning the TLS offset for the symbol. */ #define R_386_IRELATIVE 42 /* Adjust indirectly by program base */ /* Keep this the last entry. */ #define R_386_NUM 43 /* SUN SPARC specific definitions. */ /* Legal values for ST_TYPE subfield of st_info (symbol type). */ #define STT_SPARC_REGISTER 13 /* Global register reserved to app. */ /* Values for Elf64_Ehdr.e_flags. */ #define EF_SPARCV9_MM 3 #define EF_SPARCV9_TSO 0 #define EF_SPARCV9_PSO 1 #define EF_SPARCV9_RMO 2 #define EF_SPARC_LEDATA 0x800000 /* little endian data */ #define EF_SPARC_EXT_MASK 0xFFFF00 #define EF_SPARC_32PLUS 0x000100 /* generic V8+ features */ #define EF_SPARC_SUN_US1 0x000200 /* Sun UltraSPARC1 extensions */ #define EF_SPARC_HAL_R1 0x000400 /* HAL R1 extensions */ #define EF_SPARC_SUN_US3 0x000800 /* Sun UltraSPARCIII extensions */ /* SPARC relocs. */ #define R_SPARC_NONE 0 /* No reloc */ #define R_SPARC_8 1 /* Direct 8 bit */ #define R_SPARC_16 2 /* Direct 16 bit */ #define R_SPARC_32 3 /* Direct 32 bit */ #define R_SPARC_DISP8 4 /* PC relative 8 bit */ #define R_SPARC_DISP16 5 /* PC relative 16 bit */ #define R_SPARC_DISP32 6 /* PC relative 32 bit */ #define R_SPARC_WDISP30 7 /* PC relative 30 bit shifted */ #define R_SPARC_WDISP22 8 /* PC relative 22 bit shifted */ #define R_SPARC_HI22 9 /* High 22 bit */ #define R_SPARC_22 10 /* Direct 22 bit */ #define R_SPARC_13 11 /* Direct 13 bit */ #define R_SPARC_LO10 12 /* Truncated 10 bit */ #define R_SPARC_GOT10 13 /* Truncated 10 bit GOT entry */ #define R_SPARC_GOT13 14 /* 13 bit GOT entry */ #define R_SPARC_GOT22 15 /* 22 bit GOT entry shifted */ #define R_SPARC_PC10 16 /* PC relative 10 bit truncated */ #define R_SPARC_PC22 17 /* PC relative 22 bit shifted */ #define R_SPARC_WPLT30 18 /* 30 bit PC relative PLT address */ #define R_SPARC_COPY 19 /* Copy symbol at runtime */ #define R_SPARC_GLOB_DAT 20 /* Create GOT entry */ #define R_SPARC_JMP_SLOT 21 /* Create PLT entry */ #define R_SPARC_RELATIVE 22 /* Adjust by program base */ #define R_SPARC_UA32 23 /* Direct 32 bit unaligned */ /* Additional Sparc64 relocs. */ #define R_SPARC_PLT32 24 /* Direct 32 bit ref to PLT entry */ #define R_SPARC_HIPLT22 25 /* High 22 bit PLT entry */ #define R_SPARC_LOPLT10 26 /* Truncated 10 bit PLT entry */ #define R_SPARC_PCPLT32 27 /* PC rel 32 bit ref to PLT entry */ #define R_SPARC_PCPLT22 28 /* PC rel high 22 bit PLT entry */ #define R_SPARC_PCPLT10 29 /* PC rel trunc 10 bit PLT entry */ #define R_SPARC_10 30 /* Direct 10 bit */ #define R_SPARC_11 31 /* Direct 11 bit */ #define R_SPARC_64 32 /* Direct 64 bit */ #define R_SPARC_OLO10 33 /* 10bit with secondary 13bit addend */ #define R_SPARC_HH22 34 /* Top 22 bits of direct 64 bit */ #define R_SPARC_HM10 35 /* High middle 10 bits of ... */ #define R_SPARC_LM22 36 /* Low middle 22 bits of ... */ #define R_SPARC_PC_HH22 37 /* Top 22 bits of pc rel 64 bit */ #define R_SPARC_PC_HM10 38 /* High middle 10 bit of ... */ #define R_SPARC_PC_LM22 39 /* Low miggle 22 bits of ... */ #define R_SPARC_WDISP16 40 /* PC relative 16 bit shifted */ #define R_SPARC_WDISP19 41 /* PC relative 19 bit shifted */ #define R_SPARC_GLOB_JMP 42 /* was part of v9 ABI but was removed */ #define R_SPARC_7 43 /* Direct 7 bit */ #define R_SPARC_5 44 /* Direct 5 bit */ #define R_SPARC_6 45 /* Direct 6 bit */ #define R_SPARC_DISP64 46 /* PC relative 64 bit */ #define R_SPARC_PLT64 47 /* Direct 64 bit ref to PLT entry */ #define R_SPARC_HIX22 48 /* High 22 bit complemented */ #define R_SPARC_LOX10 49 /* Truncated 11 bit complemented */ #define R_SPARC_H44 50 /* Direct high 12 of 44 bit */ #define R_SPARC_M44 51 /* Direct mid 22 of 44 bit */ #define R_SPARC_L44 52 /* Direct low 10 of 44 bit */ #define R_SPARC_REGISTER 53 /* Global register usage */ #define R_SPARC_UA64 54 /* Direct 64 bit unaligned */ #define R_SPARC_UA16 55 /* Direct 16 bit unaligned */ #define R_SPARC_TLS_GD_HI22 56 #define R_SPARC_TLS_GD_LO10 57 #define R_SPARC_TLS_GD_ADD 58 #define R_SPARC_TLS_GD_CALL 59 #define R_SPARC_TLS_LDM_HI22 60 #define R_SPARC_TLS_LDM_LO10 61 #define R_SPARC_TLS_LDM_ADD 62 #define R_SPARC_TLS_LDM_CALL 63 #define R_SPARC_TLS_LDO_HIX22 64 #define R_SPARC_TLS_LDO_LOX10 65 #define R_SPARC_TLS_LDO_ADD 66 #define R_SPARC_TLS_IE_HI22 67 #define R_SPARC_TLS_IE_LO10 68 #define R_SPARC_TLS_IE_LD 69 #define R_SPARC_TLS_IE_LDX 70 #define R_SPARC_TLS_IE_ADD 71 #define R_SPARC_TLS_LE_HIX22 72 #define R_SPARC_TLS_LE_LOX10 73 #define R_SPARC_TLS_DTPMOD32 74 #define R_SPARC_TLS_DTPMOD64 75 #define R_SPARC_TLS_DTPOFF32 76 #define R_SPARC_TLS_DTPOFF64 77 #define R_SPARC_TLS_TPOFF32 78 #define R_SPARC_TLS_TPOFF64 79 #define R_SPARC_GOTDATA_HIX22 80 #define R_SPARC_GOTDATA_LOX10 81 #define R_SPARC_GOTDATA_OP_HIX22 82 #define R_SPARC_GOTDATA_OP_LOX10 83 #define R_SPARC_GOTDATA_OP 84 #define R_SPARC_H34 85 #define R_SPARC_SIZE32 86 #define R_SPARC_SIZE64 87 #define R_SPARC_JMP_IREL 248 #define R_SPARC_IRELATIVE 249 #define R_SPARC_GNU_VTINHERIT 250 #define R_SPARC_GNU_VTENTRY 251 #define R_SPARC_REV32 252 /* Keep this the last entry. */ #define R_SPARC_NUM 253 /* For Sparc64, legal values for d_tag of Elf64_Dyn. */ #define DT_SPARC_REGISTER 0x70000001 #define DT_SPARC_NUM 2 /* MIPS R3000 specific definitions. */ /* Legal values for e_flags field of Elf32_Ehdr. */ #define EF_MIPS_NOREORDER 1 /* A .noreorder directive was used */ #define EF_MIPS_PIC 2 /* Contains PIC code */ #define EF_MIPS_CPIC 4 /* Uses PIC calling sequence */ #define EF_MIPS_XGOT 8 #define EF_MIPS_64BIT_WHIRL 16 #define EF_MIPS_ABI2 32 #define EF_MIPS_ABI_ON32 64 #define EF_MIPS_ARCH 0xf0000000 /* MIPS architecture level */ /* Legal values for MIPS architecture level. */ #define EF_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ #define EF_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ #define EF_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ #define EF_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ #define EF_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ #define EF_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ #define EF_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ /* The following are non-official names and should not be used. */ #define E_MIPS_ARCH_1 0x00000000 /* -mips1 code. */ #define E_MIPS_ARCH_2 0x10000000 /* -mips2 code. */ #define E_MIPS_ARCH_3 0x20000000 /* -mips3 code. */ #define E_MIPS_ARCH_4 0x30000000 /* -mips4 code. */ #define E_MIPS_ARCH_5 0x40000000 /* -mips5 code. */ #define E_MIPS_ARCH_32 0x60000000 /* MIPS32 code. */ #define E_MIPS_ARCH_64 0x70000000 /* MIPS64 code. */ /* Special section indices. */ #define SHN_MIPS_ACOMMON 0xff00 /* Allocated common symbols */ #define SHN_MIPS_TEXT 0xff01 /* Allocated test symbols. */ #define SHN_MIPS_DATA 0xff02 /* Allocated data symbols. */ #define SHN_MIPS_SCOMMON 0xff03 /* Small common symbols */ #define SHN_MIPS_SUNDEFINED 0xff04 /* Small undefined symbols */ /* Legal values for sh_type field of Elf32_Shdr. */ #define SHT_MIPS_LIBLIST 0x70000000 /* Shared objects used in link */ #define SHT_MIPS_MSYM 0x70000001 #define SHT_MIPS_CONFLICT 0x70000002 /* Conflicting symbols */ #define SHT_MIPS_GPTAB 0x70000003 /* Global data area sizes */ #define SHT_MIPS_UCODE 0x70000004 /* Reserved for SGI/MIPS compilers */ #define SHT_MIPS_DEBUG 0x70000005 /* MIPS ECOFF debugging information*/ #define SHT_MIPS_REGINFO 0x70000006 /* Register usage information */ #define SHT_MIPS_PACKAGE 0x70000007 #define SHT_MIPS_PACKSYM 0x70000008 #define SHT_MIPS_RELD 0x70000009 #define SHT_MIPS_IFACE 0x7000000b #define SHT_MIPS_CONTENT 0x7000000c #define SHT_MIPS_OPTIONS 0x7000000d /* Miscellaneous options. */ #define SHT_MIPS_SHDR 0x70000010 #define SHT_MIPS_FDESC 0x70000011 #define SHT_MIPS_EXTSYM 0x70000012 #define SHT_MIPS_DENSE 0x70000013 #define SHT_MIPS_PDESC 0x70000014 #define SHT_MIPS_LOCSYM 0x70000015 #define SHT_MIPS_AUXSYM 0x70000016 #define SHT_MIPS_OPTSYM 0x70000017 #define SHT_MIPS_LOCSTR 0x70000018 #define SHT_MIPS_LINE 0x70000019 #define SHT_MIPS_RFDESC 0x7000001a #define SHT_MIPS_DELTASYM 0x7000001b #define SHT_MIPS_DELTAINST 0x7000001c #define SHT_MIPS_DELTACLASS 0x7000001d #define SHT_MIPS_DWARF 0x7000001e /* DWARF debugging information. */ #define SHT_MIPS_DELTADECL 0x7000001f #define SHT_MIPS_SYMBOL_LIB 0x70000020 #define SHT_MIPS_EVENTS 0x70000021 /* Event section. */ #define SHT_MIPS_TRANSLATE 0x70000022 #define SHT_MIPS_PIXIE 0x70000023 #define SHT_MIPS_XLATE 0x70000024 #define SHT_MIPS_XLATE_DEBUG 0x70000025 #define SHT_MIPS_WHIRL 0x70000026 #define SHT_MIPS_EH_REGION 0x70000027 #define SHT_MIPS_XLATE_OLD 0x70000028 #define SHT_MIPS_PDR_EXCEPTION 0x70000029 /* Legal values for sh_flags field of Elf32_Shdr. */ #define SHF_MIPS_GPREL 0x10000000 /* Must be part of global data area */ #define SHF_MIPS_MERGE 0x20000000 #define SHF_MIPS_ADDR 0x40000000 #define SHF_MIPS_STRINGS 0x80000000 #define SHF_MIPS_NOSTRIP 0x08000000 #define SHF_MIPS_LOCAL 0x04000000 #define SHF_MIPS_NAMES 0x02000000 #define SHF_MIPS_NODUPE 0x01000000 /* Symbol tables. */ /* MIPS specific values for `st_other'. */ #define STO_MIPS_DEFAULT 0x0 #define STO_MIPS_INTERNAL 0x1 #define STO_MIPS_HIDDEN 0x2 #define STO_MIPS_PROTECTED 0x3 #define STO_MIPS_PLT 0x8 #define STO_MIPS_SC_ALIGN_UNUSED 0xff /* MIPS specific values for `st_info'. */ #define STB_MIPS_SPLIT_COMMON 13 /* Entries found in sections of type SHT_MIPS_GPTAB. */ typedef union { struct { Elf32_Word gt_current_g_value; /* -G value used for compilation */ Elf32_Word gt_unused; /* Not used */ } gt_header; /* First entry in section */ struct { Elf32_Word gt_g_value; /* If this value were used for -G */ Elf32_Word gt_bytes; /* This many bytes would be used */ } gt_entry; /* Subsequent entries in section */ } Elf32_gptab; /* Entry found in sections of type SHT_MIPS_REGINFO. */ typedef struct { Elf32_Word ri_gprmask; /* General registers used */ Elf32_Word ri_cprmask[4]; /* Coprocessor registers used */ Elf32_Sword ri_gp_value; /* $gp register value */ } Elf32_RegInfo; /* Entries found in sections of type SHT_MIPS_OPTIONS. */ typedef struct { unsigned char kind; /* Determines interpretation of the variable part of descriptor. */ unsigned char size; /* Size of descriptor, including header. */ Elf32_Section section; /* Section header index of section affected, 0 for global options. */ Elf32_Word info; /* Kind-specific information. */ } Elf_Options; /* Values for `kind' field in Elf_Options. */ #define ODK_NULL 0 /* Undefined. */ #define ODK_REGINFO 1 /* Register usage information. */ #define ODK_EXCEPTIONS 2 /* Exception processing options. */ #define ODK_PAD 3 /* Section padding options. */ #define ODK_HWPATCH 4 /* Hardware workarounds performed */ #define ODK_FILL 5 /* record the fill value used by the linker. */ #define ODK_TAGS 6 /* reserve space for desktop tools to write. */ #define ODK_HWAND 7 /* HW workarounds. 'AND' bits when merging. */ #define ODK_HWOR 8 /* HW workarounds. 'OR' bits when merging. */ /* Values for `info' in Elf_Options for ODK_EXCEPTIONS entries. */ #define OEX_FPU_MIN 0x1f /* FPE's which MUST be enabled. */ #define OEX_FPU_MAX 0x1f00 /* FPE's which MAY be enabled. */ #define OEX_PAGE0 0x10000 /* page zero must be mapped. */ #define OEX_SMM 0x20000 /* Force sequential memory mode? */ #define OEX_FPDBUG 0x40000 /* Force floating point debug mode? */ #define OEX_PRECISEFP OEX_FPDBUG #define OEX_DISMISS 0x80000 /* Dismiss invalid address faults? */ #define OEX_FPU_INVAL 0x10 #define OEX_FPU_DIV0 0x08 #define OEX_FPU_OFLO 0x04 #define OEX_FPU_UFLO 0x02 #define OEX_FPU_INEX 0x01 /* Masks for `info' in Elf_Options for an ODK_HWPATCH entry. */ #define OHW_R4KEOP 0x1 /* R4000 end-of-page patch. */ #define OHW_R8KPFETCH 0x2 /* may need R8000 prefetch patch. */ #define OHW_R5KEOP 0x4 /* R5000 end-of-page patch. */ #define OHW_R5KCVTL 0x8 /* R5000 cvt.[ds].l bug. clean=1. */ #define OPAD_PREFIX 0x1 #define OPAD_POSTFIX 0x2 #define OPAD_SYMBOL 0x4 /* Entry found in `.options' section. */ typedef struct { Elf32_Word hwp_flags1; /* Extra flags. */ Elf32_Word hwp_flags2; /* Extra flags. */ } Elf_Options_Hw; /* Masks for `info' in ElfOptions for ODK_HWAND and ODK_HWOR entries. */ #define OHWA0_R4KEOP_CHECKED 0x00000001 #define OHWA1_R4KEOP_CLEAN 0x00000002 /* MIPS relocs. */ #define R_MIPS_NONE 0 /* No reloc */ #define R_MIPS_16 1 /* Direct 16 bit */ #define R_MIPS_32 2 /* Direct 32 bit */ #define R_MIPS_REL32 3 /* PC relative 32 bit */ #define R_MIPS_26 4 /* Direct 26 bit shifted */ #define R_MIPS_HI16 5 /* High 16 bit */ #define R_MIPS_LO16 6 /* Low 16 bit */ #define R_MIPS_GPREL16 7 /* GP relative 16 bit */ #define R_MIPS_LITERAL 8 /* 16 bit literal entry */ #define R_MIPS_GOT16 9 /* 16 bit GOT entry */ #define R_MIPS_PC16 10 /* PC relative 16 bit */ #define R_MIPS_CALL16 11 /* 16 bit GOT entry for function */ #define R_MIPS_GPREL32 12 /* GP relative 32 bit */ #define R_MIPS_SHIFT5 16 #define R_MIPS_SHIFT6 17 #define R_MIPS_64 18 #define R_MIPS_GOT_DISP 19 #define R_MIPS_GOT_PAGE 20 #define R_MIPS_GOT_OFST 21 #define R_MIPS_GOT_HI16 22 #define R_MIPS_GOT_LO16 23 #define R_MIPS_SUB 24 #define R_MIPS_INSERT_A 25 #define R_MIPS_INSERT_B 26 #define R_MIPS_DELETE 27 #define R_MIPS_HIGHER 28 #define R_MIPS_HIGHEST 29 #define R_MIPS_CALL_HI16 30 #define R_MIPS_CALL_LO16 31 #define R_MIPS_SCN_DISP 32 #define R_MIPS_REL16 33 #define R_MIPS_ADD_IMMEDIATE 34 #define R_MIPS_PJUMP 35 #define R_MIPS_RELGOT 36 #define R_MIPS_JALR 37 #define R_MIPS_TLS_DTPMOD32 38 /* Module number 32 bit */ #define R_MIPS_TLS_DTPREL32 39 /* Module-relative offset 32 bit */ #define R_MIPS_TLS_DTPMOD64 40 /* Module number 64 bit */ #define R_MIPS_TLS_DTPREL64 41 /* Module-relative offset 64 bit */ #define R_MIPS_TLS_GD 42 /* 16 bit GOT offset for GD */ #define R_MIPS_TLS_LDM 43 /* 16 bit GOT offset for LDM */ #define R_MIPS_TLS_DTPREL_HI16 44 /* Module-relative offset, high 16 bits */ #define R_MIPS_TLS_DTPREL_LO16 45 /* Module-relative offset, low 16 bits */ #define R_MIPS_TLS_GOTTPREL 46 /* 16 bit GOT offset for IE */ #define R_MIPS_TLS_TPREL32 47 /* TP-relative offset, 32 bit */ #define R_MIPS_TLS_TPREL64 48 /* TP-relative offset, 64 bit */ #define R_MIPS_TLS_TPREL_HI16 49 /* TP-relative offset, high 16 bits */ #define R_MIPS_TLS_TPREL_LO16 50 /* TP-relative offset, low 16 bits */ #define R_MIPS_GLOB_DAT 51 #define R_MIPS_COPY 126 #define R_MIPS_JUMP_SLOT 127 /* Keep this the last entry. */ #define R_MIPS_NUM 128 /* Legal values for p_type field of Elf32_Phdr. */ #define PT_MIPS_REGINFO 0x70000000 /* Register usage information */ #define PT_MIPS_RTPROC 0x70000001 /* Runtime procedure table. */ #define PT_MIPS_OPTIONS 0x70000002 /* Special program header types. */ #define PF_MIPS_LOCAL 0x10000000 /* Legal values for d_tag field of Elf32_Dyn. */ #define DT_MIPS_RLD_VERSION 0x70000001 /* Runtime linker interface version */ #define DT_MIPS_TIME_STAMP 0x70000002 /* Timestamp */ #define DT_MIPS_ICHECKSUM 0x70000003 /* Checksum */ #define DT_MIPS_IVERSION 0x70000004 /* Version string (string tbl index) */ #define DT_MIPS_FLAGS 0x70000005 /* Flags */ #define DT_MIPS_BASE_ADDRESS 0x70000006 /* Base address */ #define DT_MIPS_MSYM 0x70000007 #define DT_MIPS_CONFLICT 0x70000008 /* Address of CONFLICT section */ #define DT_MIPS_LIBLIST 0x70000009 /* Address of LIBLIST section */ #define DT_MIPS_LOCAL_GOTNO 0x7000000a /* Number of local GOT entries */ #define DT_MIPS_CONFLICTNO 0x7000000b /* Number of CONFLICT entries */ #define DT_MIPS_LIBLISTNO 0x70000010 /* Number of LIBLIST entries */ #define DT_MIPS_SYMTABNO 0x70000011 /* Number of DYNSYM entries */ #define DT_MIPS_UNREFEXTNO 0x70000012 /* First external DYNSYM */ #define DT_MIPS_GOTSYM 0x70000013 /* First GOT entry in DYNSYM */ #define DT_MIPS_HIPAGENO 0x70000014 /* Number of GOT page table entries */ #define DT_MIPS_RLD_MAP 0x70000016 /* Address of run time loader map. */ #define DT_MIPS_DELTA_CLASS 0x70000017 /* Delta C++ class definition. */ #define DT_MIPS_DELTA_CLASS_NO 0x70000018 /* Number of entries in DT_MIPS_DELTA_CLASS. */ #define DT_MIPS_DELTA_INSTANCE 0x70000019 /* Delta C++ class instances. */ #define DT_MIPS_DELTA_INSTANCE_NO 0x7000001a /* Number of entries in DT_MIPS_DELTA_INSTANCE. */ #define DT_MIPS_DELTA_RELOC 0x7000001b /* Delta relocations. */ #define DT_MIPS_DELTA_RELOC_NO 0x7000001c /* Number of entries in DT_MIPS_DELTA_RELOC. */ #define DT_MIPS_DELTA_SYM 0x7000001d /* Delta symbols that Delta relocations refer to. */ #define DT_MIPS_DELTA_SYM_NO 0x7000001e /* Number of entries in DT_MIPS_DELTA_SYM. */ #define DT_MIPS_DELTA_CLASSSYM 0x70000020 /* Delta symbols that hold the class declaration. */ #define DT_MIPS_DELTA_CLASSSYM_NO 0x70000021 /* Number of entries in DT_MIPS_DELTA_CLASSSYM. */ #define DT_MIPS_CXX_FLAGS 0x70000022 /* Flags indicating for C++ flavor. */ #define DT_MIPS_PIXIE_INIT 0x70000023 #define DT_MIPS_SYMBOL_LIB 0x70000024 #define DT_MIPS_LOCALPAGE_GOTIDX 0x70000025 #define DT_MIPS_LOCAL_GOTIDX 0x70000026 #define DT_MIPS_HIDDEN_GOTIDX 0x70000027 #define DT_MIPS_PROTECTED_GOTIDX 0x70000028 #define DT_MIPS_OPTIONS 0x70000029 /* Address of .options. */ #define DT_MIPS_INTERFACE 0x7000002a /* Address of .interface. */ #define DT_MIPS_DYNSTR_ALIGN 0x7000002b #define DT_MIPS_INTERFACE_SIZE 0x7000002c /* Size of the .interface section. */ #define DT_MIPS_RLD_TEXT_RESOLVE_ADDR 0x7000002d /* Address of rld_text_rsolve function stored in GOT. */ #define DT_MIPS_PERF_SUFFIX 0x7000002e /* Default suffix of dso to be added by rld on dlopen() calls. */ #define DT_MIPS_COMPACT_SIZE 0x7000002f /* (O32)Size of compact rel section. */ #define DT_MIPS_GP_VALUE 0x70000030 /* GP value for aux GOTs. */ #define DT_MIPS_AUX_DYNAMIC 0x70000031 /* Address of aux .dynamic. */ /* The address of .got.plt in an executable using the new non-PIC ABI. */ #define DT_MIPS_PLTGOT 0x70000032 /* The base of the PLT in an executable using the new non-PIC ABI if that PLT is writable. For a non-writable PLT, this is omitted or has a zero value. */ #define DT_MIPS_RWPLT 0x70000034 #define DT_MIPS_NUM 0x35 /* Legal values for DT_MIPS_FLAGS Elf32_Dyn entry. */ #define RHF_NONE 0 /* No flags */ #define RHF_QUICKSTART (1 << 0) /* Use quickstart */ #define RHF_NOTPOT (1 << 1) /* Hash size not power of 2 */ #define RHF_NO_LIBRARY_REPLACEMENT (1 << 2) /* Ignore LD_LIBRARY_PATH */ #define RHF_NO_MOVE (1 << 3) #define RHF_SGI_ONLY (1 << 4) #define RHF_GUARANTEE_INIT (1 << 5) #define RHF_DELTA_C_PLUS_PLUS (1 << 6) #define RHF_GUARANTEE_START_INIT (1 << 7) #define RHF_PIXIE (1 << 8) #define RHF_DEFAULT_DELAY_LOAD (1 << 9) #define RHF_REQUICKSTART (1 << 10) #define RHF_REQUICKSTARTED (1 << 11) #define RHF_CORD (1 << 12) #define RHF_NO_UNRES_UNDEF (1 << 13) #define RHF_RLD_ORDER_SAFE (1 << 14) /* Entries found in sections of type SHT_MIPS_LIBLIST. */ typedef struct { Elf32_Word l_name; /* Name (string table index) */ Elf32_Word l_time_stamp; /* Timestamp */ Elf32_Word l_checksum; /* Checksum */ Elf32_Word l_version; /* Interface version */ Elf32_Word l_flags; /* Flags */ } Elf32_Lib; typedef struct { Elf64_Word l_name; /* Name (string table index) */ Elf64_Word l_time_stamp; /* Timestamp */ Elf64_Word l_checksum; /* Checksum */ Elf64_Word l_version; /* Interface version */ Elf64_Word l_flags; /* Flags */ } Elf64_Lib; /* Legal values for l_flags. */ #define LL_NONE 0 #define LL_EXACT_MATCH (1 << 0) /* Require exact match */ #define LL_IGNORE_INT_VER (1 << 1) /* Ignore interface version */ #define LL_REQUIRE_MINOR (1 << 2) #define LL_EXPORTS (1 << 3) #define LL_DELAY_LOAD (1 << 4) #define LL_DELTA (1 << 5) /* Entries found in sections of type SHT_MIPS_CONFLICT. */ typedef Elf32_Addr Elf32_Conflict; /* HPPA specific definitions. */ /* Legal values for e_flags field of Elf32_Ehdr. */ #define EF_PARISC_TRAPNIL 0x00010000 /* Trap nil pointer dereference. */ #define EF_PARISC_EXT 0x00020000 /* Program uses arch. extensions. */ #define EF_PARISC_LSB 0x00040000 /* Program expects little endian. */ #define EF_PARISC_WIDE 0x00080000 /* Program expects wide mode. */ #define EF_PARISC_NO_KABP 0x00100000 /* No kernel assisted branch prediction. */ #define EF_PARISC_LAZYSWAP 0x00400000 /* Allow lazy swapping. */ #define EF_PARISC_ARCH 0x0000ffff /* Architecture version. */ /* Defined values for `e_flags & EF_PARISC_ARCH' are: */ #define EFA_PARISC_1_0 0x020b /* PA-RISC 1.0 big-endian. */ #define EFA_PARISC_1_1 0x0210 /* PA-RISC 1.1 big-endian. */ #define EFA_PARISC_2_0 0x0214 /* PA-RISC 2.0 big-endian. */ /* Additional section indeces. */ #define SHN_PARISC_ANSI_COMMON 0xff00 /* Section for tenatively declared symbols in ANSI C. */ #define SHN_PARISC_HUGE_COMMON 0xff01 /* Common blocks in huge model. */ /* Legal values for sh_type field of Elf32_Shdr. */ #define SHT_PARISC_EXT 0x70000000 /* Contains product specific ext. */ #define SHT_PARISC_UNWIND 0x70000001 /* Unwind information. */ #define SHT_PARISC_DOC 0x70000002 /* Debug info for optimized code. */ /* Legal values for sh_flags field of Elf32_Shdr. */ #define SHF_PARISC_SHORT 0x20000000 /* Section with short addressing. */ #define SHF_PARISC_HUGE 0x40000000 /* Section far from gp. */ #define SHF_PARISC_SBP 0x80000000 /* Static branch prediction code. */ /* Legal values for ST_TYPE subfield of st_info (symbol type). */ #define STT_PARISC_MILLICODE 13 /* Millicode function entry point. */ #define STT_HP_OPAQUE (STT_LOOS + 0x1) #define STT_HP_STUB (STT_LOOS + 0x2) /* HPPA relocs. */ #define R_PARISC_NONE 0 /* No reloc. */ #define R_PARISC_DIR32 1 /* Direct 32-bit reference. */ #define R_PARISC_DIR21L 2 /* Left 21 bits of eff. address. */ #define R_PARISC_DIR17R 3 /* Right 17 bits of eff. address. */ #define R_PARISC_DIR17F 4 /* 17 bits of eff. address. */ #define R_PARISC_DIR14R 6 /* Right 14 bits of eff. address. */ #define R_PARISC_PCREL32 9 /* 32-bit rel. address. */ #define R_PARISC_PCREL21L 10 /* Left 21 bits of rel. address. */ #define R_PARISC_PCREL17R 11 /* Right 17 bits of rel. address. */ #define R_PARISC_PCREL17F 12 /* 17 bits of rel. address. */ #define R_PARISC_PCREL14R 14 /* Right 14 bits of rel. address. */ #define R_PARISC_DPREL21L 18 /* Left 21 bits of rel. address. */ #define R_PARISC_DPREL14R 22 /* Right 14 bits of rel. address. */ #define R_PARISC_GPREL21L 26 /* GP-relative, left 21 bits. */ #define R_PARISC_GPREL14R 30 /* GP-relative, right 14 bits. */ #define R_PARISC_LTOFF21L 34 /* LT-relative, left 21 bits. */ #define R_PARISC_LTOFF14R 38 /* LT-relative, right 14 bits. */ #define R_PARISC_SECREL32 41 /* 32 bits section rel. address. */ #define R_PARISC_SEGBASE 48 /* No relocation, set segment base. */ #define R_PARISC_SEGREL32 49 /* 32 bits segment rel. address. */ #define R_PARISC_PLTOFF21L 50 /* PLT rel. address, left 21 bits. */ #define R_PARISC_PLTOFF14R 54 /* PLT rel. address, right 14 bits. */ #define R_PARISC_LTOFF_FPTR32 57 /* 32 bits LT-rel. function pointer. */ #define R_PARISC_LTOFF_FPTR21L 58 /* LT-rel. fct ptr, left 21 bits. */ #define R_PARISC_LTOFF_FPTR14R 62 /* LT-rel. fct ptr, right 14 bits. */ #define R_PARISC_FPTR64 64 /* 64 bits function address. */ #define R_PARISC_PLABEL32 65 /* 32 bits function address. */ #define R_PARISC_PLABEL21L 66 /* Left 21 bits of fdesc address. */ #define R_PARISC_PLABEL14R 70 /* Right 14 bits of fdesc address. */ #define R_PARISC_PCREL64 72 /* 64 bits PC-rel. address. */ #define R_PARISC_PCREL22F 74 /* 22 bits PC-rel. address. */ #define R_PARISC_PCREL14WR 75 /* PC-rel. address, right 14 bits. */ #define R_PARISC_PCREL14DR 76 /* PC rel. address, right 14 bits. */ #define R_PARISC_PCREL16F 77 /* 16 bits PC-rel. address. */ #define R_PARISC_PCREL16WF 78 /* 16 bits PC-rel. address. */ #define R_PARISC_PCREL16DF 79 /* 16 bits PC-rel. address. */ #define R_PARISC_DIR64 80 /* 64 bits of eff. address. */ #define R_PARISC_DIR14WR 83 /* 14 bits of eff. address. */ #define R_PARISC_DIR14DR 84 /* 14 bits of eff. address. */ #define R_PARISC_DIR16F 85 /* 16 bits of eff. address. */ #define R_PARISC_DIR16WF 86 /* 16 bits of eff. address. */ #define R_PARISC_DIR16DF 87 /* 16 bits of eff. address. */ #define R_PARISC_GPREL64 88 /* 64 bits of GP-rel. address. */ #define R_PARISC_GPREL14WR 91 /* GP-rel. address, right 14 bits. */ #define R_PARISC_GPREL14DR 92 /* GP-rel. address, right 14 bits. */ #define R_PARISC_GPREL16F 93 /* 16 bits GP-rel. address. */ #define R_PARISC_GPREL16WF 94 /* 16 bits GP-rel. address. */ #define R_PARISC_GPREL16DF 95 /* 16 bits GP-rel. address. */ #define R_PARISC_LTOFF64 96 /* 64 bits LT-rel. address. */ #define R_PARISC_LTOFF14WR 99 /* LT-rel. address, right 14 bits. */ #define R_PARISC_LTOFF14DR 100 /* LT-rel. address, right 14 bits. */ #define R_PARISC_LTOFF16F 101 /* 16 bits LT-rel. address. */ #define R_PARISC_LTOFF16WF 102 /* 16 bits LT-rel. address. */ #define R_PARISC_LTOFF16DF 103 /* 16 bits LT-rel. address. */ #define R_PARISC_SECREL64 104 /* 64 bits section rel. address. */ #define R_PARISC_SEGREL64 112 /* 64 bits segment rel. address. */ #define R_PARISC_PLTOFF14WR 115 /* PLT-rel. address, right 14 bits. */ #define R_PARISC_PLTOFF14DR 116 /* PLT-rel. address, right 14 bits. */ #define R_PARISC_PLTOFF16F 117 /* 16 bits LT-rel. address. */ #define R_PARISC_PLTOFF16WF 118 /* 16 bits PLT-rel. address. */ #define R_PARISC_PLTOFF16DF 119 /* 16 bits PLT-rel. address. */ #define R_PARISC_LTOFF_FPTR64 120 /* 64 bits LT-rel. function ptr. */ #define R_PARISC_LTOFF_FPTR14WR 123 /* LT-rel. fct. ptr., right 14 bits. */ #define R_PARISC_LTOFF_FPTR14DR 124 /* LT-rel. fct. ptr., right 14 bits. */ #define R_PARISC_LTOFF_FPTR16F 125 /* 16 bits LT-rel. function ptr. */ #define R_PARISC_LTOFF_FPTR16WF 126 /* 16 bits LT-rel. function ptr. */ #define R_PARISC_LTOFF_FPTR16DF 127 /* 16 bits LT-rel. function ptr. */ #define R_PARISC_LORESERVE 128 #define R_PARISC_COPY 128 /* Copy relocation. */ #define R_PARISC_IPLT 129 /* Dynamic reloc, imported PLT */ #define R_PARISC_EPLT 130 /* Dynamic reloc, exported PLT */ #define R_PARISC_TPREL32 153 /* 32 bits TP-rel. address. */ #define R_PARISC_TPREL21L 154 /* TP-rel. address, left 21 bits. */ #define R_PARISC_TPREL14R 158 /* TP-rel. address, right 14 bits. */ #define R_PARISC_LTOFF_TP21L 162 /* LT-TP-rel. address, left 21 bits. */ #define R_PARISC_LTOFF_TP14R 166 /* LT-TP-rel. address, right 14 bits.*/ #define R_PARISC_LTOFF_TP14F 167 /* 14 bits LT-TP-rel. address. */ #define R_PARISC_TPREL64 216 /* 64 bits TP-rel. address. */ #define R_PARISC_TPREL14WR 219 /* TP-rel. address, right 14 bits. */ #define R_PARISC_TPREL14DR 220 /* TP-rel. address, right 14 bits. */ #define R_PARISC_TPREL16F 221 /* 16 bits TP-rel. address. */ #define R_PARISC_TPREL16WF 222 /* 16 bits TP-rel. address. */ #define R_PARISC_TPREL16DF 223 /* 16 bits TP-rel. address. */ #define R_PARISC_LTOFF_TP64 224 /* 64 bits LT-TP-rel. address. */ #define R_PARISC_LTOFF_TP14WR 227 /* LT-TP-rel. address, right 14 bits.*/ #define R_PARISC_LTOFF_TP14DR 228 /* LT-TP-rel. address, right 14 bits.*/ #define R_PARISC_LTOFF_TP16F 229 /* 16 bits LT-TP-rel. address. */ #define R_PARISC_LTOFF_TP16WF 230 /* 16 bits LT-TP-rel. address. */ #define R_PARISC_LTOFF_TP16DF 231 /* 16 bits LT-TP-rel. address. */ #define R_PARISC_GNU_VTENTRY 232 #define R_PARISC_GNU_VTINHERIT 233 #define R_PARISC_TLS_GD21L 234 /* GD 21-bit left. */ #define R_PARISC_TLS_GD14R 235 /* GD 14-bit right. */ #define R_PARISC_TLS_GDCALL 236 /* GD call to __t_g_a. */ #define R_PARISC_TLS_LDM21L 237 /* LD module 21-bit left. */ #define R_PARISC_TLS_LDM14R 238 /* LD module 14-bit right. */ #define R_PARISC_TLS_LDMCALL 239 /* LD module call to __t_g_a. */ #define R_PARISC_TLS_LDO21L 240 /* LD offset 21-bit left. */ #define R_PARISC_TLS_LDO14R 241 /* LD offset 14-bit right. */ #define R_PARISC_TLS_DTPMOD32 242 /* DTP module 32-bit. */ #define R_PARISC_TLS_DTPMOD64 243 /* DTP module 64-bit. */ #define R_PARISC_TLS_DTPOFF32 244 /* DTP offset 32-bit. */ #define R_PARISC_TLS_DTPOFF64 245 /* DTP offset 32-bit. */ #define R_PARISC_TLS_LE21L R_PARISC_TPREL21L #define R_PARISC_TLS_LE14R R_PARISC_TPREL14R #define R_PARISC_TLS_IE21L R_PARISC_LTOFF_TP21L #define R_PARISC_TLS_IE14R R_PARISC_LTOFF_TP14R #define R_PARISC_TLS_TPREL32 R_PARISC_TPREL32 #define R_PARISC_TLS_TPREL64 R_PARISC_TPREL64 #define R_PARISC_HIRESERVE 255 /* Legal values for p_type field of Elf32_Phdr/Elf64_Phdr. */ #define PT_HP_TLS (PT_LOOS + 0x0) #define PT_HP_CORE_NONE (PT_LOOS + 0x1) #define PT_HP_CORE_VERSION (PT_LOOS + 0x2) #define PT_HP_CORE_KERNEL (PT_LOOS + 0x3) #define PT_HP_CORE_COMM (PT_LOOS + 0x4) #define PT_HP_CORE_PROC (PT_LOOS + 0x5) #define PT_HP_CORE_LOADABLE (PT_LOOS + 0x6) #define PT_HP_CORE_STACK (PT_LOOS + 0x7) #define PT_HP_CORE_SHM (PT_LOOS + 0x8) #define PT_HP_CORE_MMF (PT_LOOS + 0x9) #define PT_HP_PARALLEL (PT_LOOS + 0x10) #define PT_HP_FASTBIND (PT_LOOS + 0x11) #define PT_HP_OPT_ANNOT (PT_LOOS + 0x12) #define PT_HP_HSL_ANNOT (PT_LOOS + 0x13) #define PT_HP_STACK (PT_LOOS + 0x14) #define PT_PARISC_ARCHEXT 0x70000000 #define PT_PARISC_UNWIND 0x70000001 /* Legal values for p_flags field of Elf32_Phdr/Elf64_Phdr. */ #define PF_PARISC_SBP 0x08000000 #define PF_HP_PAGE_SIZE 0x00100000 #define PF_HP_FAR_SHARED 0x00200000 #define PF_HP_NEAR_SHARED 0x00400000 #define PF_HP_CODE 0x01000000 #define PF_HP_MODIFY 0x02000000 #define PF_HP_LAZYSWAP 0x04000000 #define PF_HP_SBP 0x08000000 /* Alpha specific definitions. */ /* Legal values for e_flags field of Elf64_Ehdr. */ #define EF_ALPHA_32BIT 1 /* All addresses must be < 2GB. */ #define EF_ALPHA_CANRELAX 2 /* Relocations for relaxing exist. */ /* Legal values for sh_type field of Elf64_Shdr. */ /* These two are primerily concerned with ECOFF debugging info. */ #define SHT_ALPHA_DEBUG 0x70000001 #define SHT_ALPHA_REGINFO 0x70000002 /* Legal values for sh_flags field of Elf64_Shdr. */ #define SHF_ALPHA_GPREL 0x10000000 /* Legal values for st_other field of Elf64_Sym. */ #define STO_ALPHA_NOPV 0x80 /* No PV required. */ #define STO_ALPHA_STD_GPLOAD 0x88 /* PV only used for initial ldgp. */ /* Alpha relocs. */ #define R_ALPHA_NONE 0 /* No reloc */ #define R_ALPHA_REFLONG 1 /* Direct 32 bit */ #define R_ALPHA_REFQUAD 2 /* Direct 64 bit */ #define R_ALPHA_GPREL32 3 /* GP relative 32 bit */ #define R_ALPHA_LITERAL 4 /* GP relative 16 bit w/optimization */ #define R_ALPHA_LITUSE 5 /* Optimization hint for LITERAL */ #define R_ALPHA_GPDISP 6 /* Add displacement to GP */ #define R_ALPHA_BRADDR 7 /* PC+4 relative 23 bit shifted */ #define R_ALPHA_HINT 8 /* PC+4 relative 16 bit shifted */ #define R_ALPHA_SREL16 9 /* PC relative 16 bit */ #define R_ALPHA_SREL32 10 /* PC relative 32 bit */ #define R_ALPHA_SREL64 11 /* PC relative 64 bit */ #define R_ALPHA_GPRELHIGH 17 /* GP relative 32 bit, high 16 bits */ #define R_ALPHA_GPRELLOW 18 /* GP relative 32 bit, low 16 bits */ #define R_ALPHA_GPREL16 19 /* GP relative 16 bit */ #define R_ALPHA_COPY 24 /* Copy symbol at runtime */ #define R_ALPHA_GLOB_DAT 25 /* Create GOT entry */ #define R_ALPHA_JMP_SLOT 26 /* Create PLT entry */ #define R_ALPHA_RELATIVE 27 /* Adjust by program base */ #define R_ALPHA_TLS_GD_HI 28 #define R_ALPHA_TLSGD 29 #define R_ALPHA_TLS_LDM 30 #define R_ALPHA_DTPMOD64 31 #define R_ALPHA_GOTDTPREL 32 #define R_ALPHA_DTPREL64 33 #define R_ALPHA_DTPRELHI 34 #define R_ALPHA_DTPRELLO 35 #define R_ALPHA_DTPREL16 36 #define R_ALPHA_GOTTPREL 37 #define R_ALPHA_TPREL64 38 #define R_ALPHA_TPRELHI 39 #define R_ALPHA_TPRELLO 40 #define R_ALPHA_TPREL16 41 /* Keep this the last entry. */ #define R_ALPHA_NUM 46 /* Magic values of the LITUSE relocation addend. */ #define LITUSE_ALPHA_ADDR 0 #define LITUSE_ALPHA_BASE 1 #define LITUSE_ALPHA_BYTOFF 2 #define LITUSE_ALPHA_JSR 3 #define LITUSE_ALPHA_TLS_GD 4 #define LITUSE_ALPHA_TLS_LDM 5 /* Legal values for d_tag of Elf64_Dyn. */ #define DT_ALPHA_PLTRO (DT_LOPROC + 0) #define DT_ALPHA_NUM 1 /* PowerPC specific declarations */ /* Values for Elf32/64_Ehdr.e_flags. */ #define EF_PPC_EMB 0x80000000 /* PowerPC embedded flag */ /* Cygnus local bits below */ #define EF_PPC_RELOCATABLE 0x00010000 /* PowerPC -mrelocatable flag*/ #define EF_PPC_RELOCATABLE_LIB 0x00008000 /* PowerPC -mrelocatable-lib flag */ /* PowerPC relocations defined by the ABIs */ #define R_PPC_NONE 0 #define R_PPC_ADDR32 1 /* 32bit absolute address */ #define R_PPC_ADDR24 2 /* 26bit address, 2 bits ignored. */ #define R_PPC_ADDR16 3 /* 16bit absolute address */ #define R_PPC_ADDR16_LO 4 /* lower 16bit of absolute address */ #define R_PPC_ADDR16_HI 5 /* high 16bit of absolute address */ #define R_PPC_ADDR16_HA 6 /* adjusted high 16bit */ #define R_PPC_ADDR14 7 /* 16bit address, 2 bits ignored */ #define R_PPC_ADDR14_BRTAKEN 8 #define R_PPC_ADDR14_BRNTAKEN 9 #define R_PPC_REL24 10 /* PC relative 26 bit */ #define R_PPC_REL14 11 /* PC relative 16 bit */ #define R_PPC_REL14_BRTAKEN 12 #define R_PPC_REL14_BRNTAKEN 13 #define R_PPC_GOT16 14 #define R_PPC_GOT16_LO 15 #define R_PPC_GOT16_HI 16 #define R_PPC_GOT16_HA 17 #define R_PPC_PLTREL24 18 #define R_PPC_COPY 19 #define R_PPC_GLOB_DAT 20 #define R_PPC_JMP_SLOT 21 #define R_PPC_RELATIVE 22 #define R_PPC_LOCAL24PC 23 #define R_PPC_UADDR32 24 #define R_PPC_UADDR16 25 #define R_PPC_REL32 26 #define R_PPC_PLT32 27 #define R_PPC_PLTREL32 28 #define R_PPC_PLT16_LO 29 #define R_PPC_PLT16_HI 30 #define R_PPC_PLT16_HA 31 #define R_PPC_SDAREL16 32 #define R_PPC_SECTOFF 33 #define R_PPC_SECTOFF_LO 34 #define R_PPC_SECTOFF_HI 35 #define R_PPC_SECTOFF_HA 36 /* PowerPC relocations defined for the TLS access ABI. */ #define R_PPC_TLS 67 /* none (sym+add)@tls */ #define R_PPC_DTPMOD32 68 /* word32 (sym+add)@dtpmod */ #define R_PPC_TPREL16 69 /* half16* (sym+add)@tprel */ #define R_PPC_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ #define R_PPC_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ #define R_PPC_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ #define R_PPC_TPREL32 73 /* word32 (sym+add)@tprel */ #define R_PPC_DTPREL16 74 /* half16* (sym+add)@dtprel */ #define R_PPC_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ #define R_PPC_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ #define R_PPC_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ #define R_PPC_DTPREL32 78 /* word32 (sym+add)@dtprel */ #define R_PPC_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ #define R_PPC_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ #define R_PPC_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ #define R_PPC_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ #define R_PPC_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ #define R_PPC_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ #define R_PPC_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ #define R_PPC_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ #define R_PPC_GOT_TPREL16 87 /* half16* (sym+add)@got@tprel */ #define R_PPC_GOT_TPREL16_LO 88 /* half16 (sym+add)@got@tprel@l */ #define R_PPC_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ #define R_PPC_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ #define R_PPC_GOT_DTPREL16 91 /* half16* (sym+add)@got@dtprel */ #define R_PPC_GOT_DTPREL16_LO 92 /* half16* (sym+add)@got@dtprel@l */ #define R_PPC_GOT_DTPREL16_HI 93 /* half16* (sym+add)@got@dtprel@h */ #define R_PPC_GOT_DTPREL16_HA 94 /* half16* (sym+add)@got@dtprel@ha */ /* The remaining relocs are from the Embedded ELF ABI, and are not in the SVR4 ELF ABI. */ #define R_PPC_EMB_NADDR32 101 #define R_PPC_EMB_NADDR16 102 #define R_PPC_EMB_NADDR16_LO 103 #define R_PPC_EMB_NADDR16_HI 104 #define R_PPC_EMB_NADDR16_HA 105 #define R_PPC_EMB_SDAI16 106 #define R_PPC_EMB_SDA2I16 107 #define R_PPC_EMB_SDA2REL 108 #define R_PPC_EMB_SDA21 109 /* 16 bit offset in SDA */ #define R_PPC_EMB_MRKREF 110 #define R_PPC_EMB_RELSEC16 111 #define R_PPC_EMB_RELST_LO 112 #define R_PPC_EMB_RELST_HI 113 #define R_PPC_EMB_RELST_HA 114 #define R_PPC_EMB_BIT_FLD 115 #define R_PPC_EMB_RELSDA 116 /* 16 bit relative offset in SDA */ /* Diab tool relocations. */ #define R_PPC_DIAB_SDA21_LO 180 /* like EMB_SDA21, but lower 16 bit */ #define R_PPC_DIAB_SDA21_HI 181 /* like EMB_SDA21, but high 16 bit */ #define R_PPC_DIAB_SDA21_HA 182 /* like EMB_SDA21, adjusted high 16 */ #define R_PPC_DIAB_RELSDA_LO 183 /* like EMB_RELSDA, but lower 16 bit */ #define R_PPC_DIAB_RELSDA_HI 184 /* like EMB_RELSDA, but high 16 bit */ #define R_PPC_DIAB_RELSDA_HA 185 /* like EMB_RELSDA, adjusted high 16 */ /* GNU extension to support local ifunc. */ #define R_PPC_IRELATIVE 248 /* GNU relocs used in PIC code sequences. */ #define R_PPC_REL16 249 /* half16 (sym+add-.) */ #define R_PPC_REL16_LO 250 /* half16 (sym+add-.)@l */ #define R_PPC_REL16_HI 251 /* half16 (sym+add-.)@h */ #define R_PPC_REL16_HA 252 /* half16 (sym+add-.)@ha */ /* This is a phony reloc to handle any old fashioned TOC16 references that may still be in object files. */ #define R_PPC_TOC16 255 /* PowerPC specific values for the Dyn d_tag field. */ #define DT_PPC_GOT (DT_LOPROC + 0) #define DT_PPC_NUM 1 /* PowerPC64 relocations defined by the ABIs */ #define R_PPC64_NONE R_PPC_NONE #define R_PPC64_ADDR32 R_PPC_ADDR32 /* 32bit absolute address */ #define R_PPC64_ADDR24 R_PPC_ADDR24 /* 26bit address, word aligned */ #define R_PPC64_ADDR16 R_PPC_ADDR16 /* 16bit absolute address */ #define R_PPC64_ADDR16_LO R_PPC_ADDR16_LO /* lower 16bits of address */ #define R_PPC64_ADDR16_HI R_PPC_ADDR16_HI /* high 16bits of address. */ #define R_PPC64_ADDR16_HA R_PPC_ADDR16_HA /* adjusted high 16bits. */ #define R_PPC64_ADDR14 R_PPC_ADDR14 /* 16bit address, word aligned */ #define R_PPC64_ADDR14_BRTAKEN R_PPC_ADDR14_BRTAKEN #define R_PPC64_ADDR14_BRNTAKEN R_PPC_ADDR14_BRNTAKEN #define R_PPC64_REL24 R_PPC_REL24 /* PC-rel. 26 bit, word aligned */ #define R_PPC64_REL14 R_PPC_REL14 /* PC relative 16 bit */ #define R_PPC64_REL14_BRTAKEN R_PPC_REL14_BRTAKEN #define R_PPC64_REL14_BRNTAKEN R_PPC_REL14_BRNTAKEN #define R_PPC64_GOT16 R_PPC_GOT16 #define R_PPC64_GOT16_LO R_PPC_GOT16_LO #define R_PPC64_GOT16_HI R_PPC_GOT16_HI #define R_PPC64_GOT16_HA R_PPC_GOT16_HA #define R_PPC64_COPY R_PPC_COPY #define R_PPC64_GLOB_DAT R_PPC_GLOB_DAT #define R_PPC64_JMP_SLOT R_PPC_JMP_SLOT #define R_PPC64_RELATIVE R_PPC_RELATIVE #define R_PPC64_UADDR32 R_PPC_UADDR32 #define R_PPC64_UADDR16 R_PPC_UADDR16 #define R_PPC64_REL32 R_PPC_REL32 #define R_PPC64_PLT32 R_PPC_PLT32 #define R_PPC64_PLTREL32 R_PPC_PLTREL32 #define R_PPC64_PLT16_LO R_PPC_PLT16_LO #define R_PPC64_PLT16_HI R_PPC_PLT16_HI #define R_PPC64_PLT16_HA R_PPC_PLT16_HA #define R_PPC64_SECTOFF R_PPC_SECTOFF #define R_PPC64_SECTOFF_LO R_PPC_SECTOFF_LO #define R_PPC64_SECTOFF_HI R_PPC_SECTOFF_HI #define R_PPC64_SECTOFF_HA R_PPC_SECTOFF_HA #define R_PPC64_ADDR30 37 /* word30 (S + A - P) >> 2 */ #define R_PPC64_ADDR64 38 /* doubleword64 S + A */ #define R_PPC64_ADDR16_HIGHER 39 /* half16 #higher(S + A) */ #define R_PPC64_ADDR16_HIGHERA 40 /* half16 #highera(S + A) */ #define R_PPC64_ADDR16_HIGHEST 41 /* half16 #highest(S + A) */ #define R_PPC64_ADDR16_HIGHESTA 42 /* half16 #highesta(S + A) */ #define R_PPC64_UADDR64 43 /* doubleword64 S + A */ #define R_PPC64_REL64 44 /* doubleword64 S + A - P */ #define R_PPC64_PLT64 45 /* doubleword64 L + A */ #define R_PPC64_PLTREL64 46 /* doubleword64 L + A - P */ #define R_PPC64_TOC16 47 /* half16* S + A - .TOC */ #define R_PPC64_TOC16_LO 48 /* half16 #lo(S + A - .TOC.) */ #define R_PPC64_TOC16_HI 49 /* half16 #hi(S + A - .TOC.) */ #define R_PPC64_TOC16_HA 50 /* half16 #ha(S + A - .TOC.) */ #define R_PPC64_TOC 51 /* doubleword64 .TOC */ #define R_PPC64_PLTGOT16 52 /* half16* M + A */ #define R_PPC64_PLTGOT16_LO 53 /* half16 #lo(M + A) */ #define R_PPC64_PLTGOT16_HI 54 /* half16 #hi(M + A) */ #define R_PPC64_PLTGOT16_HA 55 /* half16 #ha(M + A) */ #define R_PPC64_ADDR16_DS 56 /* half16ds* (S + A) >> 2 */ #define R_PPC64_ADDR16_LO_DS 57 /* half16ds #lo(S + A) >> 2 */ #define R_PPC64_GOT16_DS 58 /* half16ds* (G + A) >> 2 */ #define R_PPC64_GOT16_LO_DS 59 /* half16ds #lo(G + A) >> 2 */ #define R_PPC64_PLT16_LO_DS 60 /* half16ds #lo(L + A) >> 2 */ #define R_PPC64_SECTOFF_DS 61 /* half16ds* (R + A) >> 2 */ #define R_PPC64_SECTOFF_LO_DS 62 /* half16ds #lo(R + A) >> 2 */ #define R_PPC64_TOC16_DS 63 /* half16ds* (S + A - .TOC.) >> 2 */ #define R_PPC64_TOC16_LO_DS 64 /* half16ds #lo(S + A - .TOC.) >> 2 */ #define R_PPC64_PLTGOT16_DS 65 /* half16ds* (M + A) >> 2 */ #define R_PPC64_PLTGOT16_LO_DS 66 /* half16ds #lo(M + A) >> 2 */ /* PowerPC64 relocations defined for the TLS access ABI. */ #define R_PPC64_TLS 67 /* none (sym+add)@tls */ #define R_PPC64_DTPMOD64 68 /* doubleword64 (sym+add)@dtpmod */ #define R_PPC64_TPREL16 69 /* half16* (sym+add)@tprel */ #define R_PPC64_TPREL16_LO 70 /* half16 (sym+add)@tprel@l */ #define R_PPC64_TPREL16_HI 71 /* half16 (sym+add)@tprel@h */ #define R_PPC64_TPREL16_HA 72 /* half16 (sym+add)@tprel@ha */ #define R_PPC64_TPREL64 73 /* doubleword64 (sym+add)@tprel */ #define R_PPC64_DTPREL16 74 /* half16* (sym+add)@dtprel */ #define R_PPC64_DTPREL16_LO 75 /* half16 (sym+add)@dtprel@l */ #define R_PPC64_DTPREL16_HI 76 /* half16 (sym+add)@dtprel@h */ #define R_PPC64_DTPREL16_HA 77 /* half16 (sym+add)@dtprel@ha */ #define R_PPC64_DTPREL64 78 /* doubleword64 (sym+add)@dtprel */ #define R_PPC64_GOT_TLSGD16 79 /* half16* (sym+add)@got@tlsgd */ #define R_PPC64_GOT_TLSGD16_LO 80 /* half16 (sym+add)@got@tlsgd@l */ #define R_PPC64_GOT_TLSGD16_HI 81 /* half16 (sym+add)@got@tlsgd@h */ #define R_PPC64_GOT_TLSGD16_HA 82 /* half16 (sym+add)@got@tlsgd@ha */ #define R_PPC64_GOT_TLSLD16 83 /* half16* (sym+add)@got@tlsld */ #define R_PPC64_GOT_TLSLD16_LO 84 /* half16 (sym+add)@got@tlsld@l */ #define R_PPC64_GOT_TLSLD16_HI 85 /* half16 (sym+add)@got@tlsld@h */ #define R_PPC64_GOT_TLSLD16_HA 86 /* half16 (sym+add)@got@tlsld@ha */ #define R_PPC64_GOT_TPREL16_DS 87 /* half16ds* (sym+add)@got@tprel */ #define R_PPC64_GOT_TPREL16_LO_DS 88 /* half16ds (sym+add)@got@tprel@l */ #define R_PPC64_GOT_TPREL16_HI 89 /* half16 (sym+add)@got@tprel@h */ #define R_PPC64_GOT_TPREL16_HA 90 /* half16 (sym+add)@got@tprel@ha */ #define R_PPC64_GOT_DTPREL16_DS 91 /* half16ds* (sym+add)@got@dtprel */ #define R_PPC64_GOT_DTPREL16_LO_DS 92 /* half16ds (sym+add)@got@dtprel@l */ #define R_PPC64_GOT_DTPREL16_HI 93 /* half16 (sym+add)@got@dtprel@h */ #define R_PPC64_GOT_DTPREL16_HA 94 /* half16 (sym+add)@got@dtprel@ha */ #define R_PPC64_TPREL16_DS 95 /* half16ds* (sym+add)@tprel */ #define R_PPC64_TPREL16_LO_DS 96 /* half16ds (sym+add)@tprel@l */ #define R_PPC64_TPREL16_HIGHER 97 /* half16 (sym+add)@tprel@higher */ #define R_PPC64_TPREL16_HIGHERA 98 /* half16 (sym+add)@tprel@highera */ #define R_PPC64_TPREL16_HIGHEST 99 /* half16 (sym+add)@tprel@highest */ #define R_PPC64_TPREL16_HIGHESTA 100 /* half16 (sym+add)@tprel@highesta */ #define R_PPC64_DTPREL16_DS 101 /* half16ds* (sym+add)@dtprel */ #define R_PPC64_DTPREL16_LO_DS 102 /* half16ds (sym+add)@dtprel@l */ #define R_PPC64_DTPREL16_HIGHER 103 /* half16 (sym+add)@dtprel@higher */ #define R_PPC64_DTPREL16_HIGHERA 104 /* half16 (sym+add)@dtprel@highera */ #define R_PPC64_DTPREL16_HIGHEST 105 /* half16 (sym+add)@dtprel@highest */ #define R_PPC64_DTPREL16_HIGHESTA 106 /* half16 (sym+add)@dtprel@highesta */ /* GNU extension to support local ifunc. */ #define R_PPC64_JMP_IREL 247 #define R_PPC64_IRELATIVE 248 #define R_PPC64_REL16 249 /* half16 (sym+add-.) */ #define R_PPC64_REL16_LO 250 /* half16 (sym+add-.)@l */ #define R_PPC64_REL16_HI 251 /* half16 (sym+add-.)@h */ #define R_PPC64_REL16_HA 252 /* half16 (sym+add-.)@ha */ /* PowerPC64 specific values for the Dyn d_tag field. */ #define DT_PPC64_GLINK (DT_LOPROC + 0) #define DT_PPC64_OPD (DT_LOPROC + 1) #define DT_PPC64_OPDSZ (DT_LOPROC + 2) #define DT_PPC64_NUM 3 /* ARM specific declarations */ /* Processor specific flags for the ELF header e_flags field. */ #define EF_ARM_RELEXEC 0x01 #define EF_ARM_HASENTRY 0x02 #define EF_ARM_INTERWORK 0x04 #define EF_ARM_APCS_26 0x08 #define EF_ARM_APCS_FLOAT 0x10 #define EF_ARM_PIC 0x20 #define EF_ARM_ALIGN8 0x40 /* 8-bit structure alignment is in use */ #define EF_ARM_NEW_ABI 0x80 #define EF_ARM_OLD_ABI 0x100 #define EF_ARM_SOFT_FLOAT 0x200 #define EF_ARM_VFP_FLOAT 0x400 #define EF_ARM_MAVERICK_FLOAT 0x800 /* Other constants defined in the ARM ELF spec. version B-01. */ /* NB. These conflict with values defined above. */ #define EF_ARM_SYMSARESORTED 0x04 #define EF_ARM_DYNSYMSUSESEGIDX 0x08 #define EF_ARM_MAPSYMSFIRST 0x10 #define EF_ARM_EABIMASK 0XFF000000 /* Constants defined in AAELF. */ #define EF_ARM_BE8 0x00800000 #define EF_ARM_LE8 0x00400000 #define EF_ARM_EABI_VERSION(flags) ((flags) & EF_ARM_EABIMASK) #define EF_ARM_EABI_UNKNOWN 0x00000000 #define EF_ARM_EABI_VER1 0x01000000 #define EF_ARM_EABI_VER2 0x02000000 #define EF_ARM_EABI_VER3 0x03000000 #define EF_ARM_EABI_VER4 0x04000000 #define EF_ARM_EABI_VER5 0x05000000 /* Additional symbol types for Thumb. */ #define STT_ARM_TFUNC STT_LOPROC /* A Thumb function. */ #define STT_ARM_16BIT STT_HIPROC /* A Thumb label. */ /* ARM-specific values for sh_flags */ #define SHF_ARM_ENTRYSECT 0x10000000 /* Section contains an entry point */ #define SHF_ARM_COMDEF 0x80000000 /* Section may be multiply defined in the input to a link step. */ /* ARM-specific program header flags */ #define PF_ARM_SB 0x10000000 /* Segment contains the location addressed by the static base. */ #define PF_ARM_PI 0x20000000 /* Position-independent segment. */ #define PF_ARM_ABS 0x40000000 /* Absolute segment. */ /* Processor specific values for the Phdr p_type field. */ #define PT_ARM_EXIDX (PT_LOPROC + 1) /* ARM unwind segment. */ /* Processor specific values for the Shdr sh_type field. */ #define SHT_ARM_EXIDX (SHT_LOPROC + 1) /* ARM unwind section. */ #define SHT_ARM_PREEMPTMAP (SHT_LOPROC + 2) /* Preemption details. */ #define SHT_ARM_ATTRIBUTES (SHT_LOPROC + 3) /* ARM attributes section. */ /* ARM relocs. */ #define R_ARM_NONE 0 /* No reloc */ #define R_ARM_PC24 1 /* PC relative 26 bit branch */ #define R_ARM_ABS32 2 /* Direct 32 bit */ #define R_ARM_REL32 3 /* PC relative 32 bit */ #define R_ARM_PC13 4 #define R_ARM_ABS16 5 /* Direct 16 bit */ #define R_ARM_ABS12 6 /* Direct 12 bit */ #define R_ARM_THM_ABS5 7 #define R_ARM_ABS8 8 /* Direct 8 bit */ #define R_ARM_SBREL32 9 #define R_ARM_THM_PC22 10 #define R_ARM_THM_PC8 11 #define R_ARM_AMP_VCALL9 12 #define R_ARM_SWI24 13 /* Obsolete static relocation. */ #define R_ARM_TLS_DESC 13 /* Dynamic relocation. */ #define R_ARM_THM_SWI8 14 #define R_ARM_XPC25 15 #define R_ARM_THM_XPC22 16 #define R_ARM_TLS_DTPMOD32 17 /* ID of module containing symbol */ #define R_ARM_TLS_DTPOFF32 18 /* Offset in TLS block */ #define R_ARM_TLS_TPOFF32 19 /* Offset in static TLS block */ #define R_ARM_COPY 20 /* Copy symbol at runtime */ #define R_ARM_GLOB_DAT 21 /* Create GOT entry */ #define R_ARM_JUMP_SLOT 22 /* Create PLT entry */ #define R_ARM_RELATIVE 23 /* Adjust by program base */ #define R_ARM_GOTOFF 24 /* 32 bit offset to GOT */ #define R_ARM_GOTPC 25 /* 32 bit PC relative offset to GOT */ #define R_ARM_GOT32 26 /* 32 bit GOT entry */ #define R_ARM_PLT32 27 /* 32 bit PLT address */ #define R_ARM_ALU_PCREL_7_0 32 #define R_ARM_ALU_PCREL_15_8 33 #define R_ARM_ALU_PCREL_23_15 34 #define R_ARM_LDR_SBREL_11_0 35 #define R_ARM_ALU_SBREL_19_12 36 #define R_ARM_ALU_SBREL_27_20 37 #define R_ARM_TLS_GOTDESC 90 #define R_ARM_TLS_CALL 91 #define R_ARM_TLS_DESCSEQ 92 #define R_ARM_THM_TLS_CALL 93 #define R_ARM_GNU_VTENTRY 100 #define R_ARM_GNU_VTINHERIT 101 #define R_ARM_THM_PC11 102 /* thumb unconditional branch */ #define R_ARM_THM_PC9 103 /* thumb conditional branch */ #define R_ARM_TLS_GD32 104 /* PC-rel 32 bit for global dynamic thread local data */ #define R_ARM_TLS_LDM32 105 /* PC-rel 32 bit for local dynamic thread local data */ #define R_ARM_TLS_LDO32 106 /* 32 bit offset relative to TLS block */ #define R_ARM_TLS_IE32 107 /* PC-rel 32 bit for GOT entry of static TLS block offset */ #define R_ARM_TLS_LE32 108 /* 32 bit offset relative to static TLS block */ #define R_ARM_THM_TLS_DESCSEQ 129 #define R_ARM_IRELATIVE 160 #define R_ARM_RXPC25 249 #define R_ARM_RSBREL32 250 #define R_ARM_THM_RPC22 251 #define R_ARM_RREL32 252 #define R_ARM_RABS22 253 #define R_ARM_RPC24 254 #define R_ARM_RBASE 255 /* Keep this the last entry. */ #define R_ARM_NUM 256 /* IA-64 specific declarations. */ /* Processor specific flags for the Ehdr e_flags field. */ #define EF_IA_64_MASKOS 0x0000000f /* os-specific flags */ #define EF_IA_64_ABI64 0x00000010 /* 64-bit ABI */ #define EF_IA_64_ARCH 0xff000000 /* arch. version mask */ /* Processor specific values for the Phdr p_type field. */ #define PT_IA_64_ARCHEXT (PT_LOPROC + 0) /* arch extension bits */ #define PT_IA_64_UNWIND (PT_LOPROC + 1) /* ia64 unwind bits */ #define PT_IA_64_HP_OPT_ANOT (PT_LOOS + 0x12) #define PT_IA_64_HP_HSL_ANOT (PT_LOOS + 0x13) #define PT_IA_64_HP_STACK (PT_LOOS + 0x14) /* Processor specific flags for the Phdr p_flags field. */ #define PF_IA_64_NORECOV 0x80000000 /* spec insns w/o recovery */ /* Processor specific values for the Shdr sh_type field. */ #define SHT_IA_64_EXT (SHT_LOPROC + 0) /* extension bits */ #define SHT_IA_64_UNWIND (SHT_LOPROC + 1) /* unwind bits */ /* Processor specific flags for the Shdr sh_flags field. */ #define SHF_IA_64_SHORT 0x10000000 /* section near gp */ #define SHF_IA_64_NORECOV 0x20000000 /* spec insns w/o recovery */ /* Processor specific values for the Dyn d_tag field. */ #define DT_IA_64_PLT_RESERVE (DT_LOPROC + 0) #define DT_IA_64_NUM 1 /* IA-64 relocations. */ #define R_IA64_NONE 0x00 /* none */ #define R_IA64_IMM14 0x21 /* symbol + addend, add imm14 */ #define R_IA64_IMM22 0x22 /* symbol + addend, add imm22 */ #define R_IA64_IMM64 0x23 /* symbol + addend, mov imm64 */ #define R_IA64_DIR32MSB 0x24 /* symbol + addend, data4 MSB */ #define R_IA64_DIR32LSB 0x25 /* symbol + addend, data4 LSB */ #define R_IA64_DIR64MSB 0x26 /* symbol + addend, data8 MSB */ #define R_IA64_DIR64LSB 0x27 /* symbol + addend, data8 LSB */ #define R_IA64_GPREL22 0x2a /* @gprel(sym + add), add imm22 */ #define R_IA64_GPREL64I 0x2b /* @gprel(sym + add), mov imm64 */ #define R_IA64_GPREL32MSB 0x2c /* @gprel(sym + add), data4 MSB */ #define R_IA64_GPREL32LSB 0x2d /* @gprel(sym + add), data4 LSB */ #define R_IA64_GPREL64MSB 0x2e /* @gprel(sym + add), data8 MSB */ #define R_IA64_GPREL64LSB 0x2f /* @gprel(sym + add), data8 LSB */ #define R_IA64_LTOFF22 0x32 /* @ltoff(sym + add), add imm22 */ #define R_IA64_LTOFF64I 0x33 /* @ltoff(sym + add), mov imm64 */ #define R_IA64_PLTOFF22 0x3a /* @pltoff(sym + add), add imm22 */ #define R_IA64_PLTOFF64I 0x3b /* @pltoff(sym + add), mov imm64 */ #define R_IA64_PLTOFF64MSB 0x3e /* @pltoff(sym + add), data8 MSB */ #define R_IA64_PLTOFF64LSB 0x3f /* @pltoff(sym + add), data8 LSB */ #define R_IA64_FPTR64I 0x43 /* @fptr(sym + add), mov imm64 */ #define R_IA64_FPTR32MSB 0x44 /* @fptr(sym + add), data4 MSB */ #define R_IA64_FPTR32LSB 0x45 /* @fptr(sym + add), data4 LSB */ #define R_IA64_FPTR64MSB 0x46 /* @fptr(sym + add), data8 MSB */ #define R_IA64_FPTR64LSB 0x47 /* @fptr(sym + add), data8 LSB */ #define R_IA64_PCREL60B 0x48 /* @pcrel(sym + add), brl */ #define R_IA64_PCREL21B 0x49 /* @pcrel(sym + add), ptb, call */ #define R_IA64_PCREL21M 0x4a /* @pcrel(sym + add), chk.s */ #define R_IA64_PCREL21F 0x4b /* @pcrel(sym + add), fchkf */ #define R_IA64_PCREL32MSB 0x4c /* @pcrel(sym + add), data4 MSB */ #define R_IA64_PCREL32LSB 0x4d /* @pcrel(sym + add), data4 LSB */ #define R_IA64_PCREL64MSB 0x4e /* @pcrel(sym + add), data8 MSB */ #define R_IA64_PCREL64LSB 0x4f /* @pcrel(sym + add), data8 LSB */ #define R_IA64_LTOFF_FPTR22 0x52 /* @ltoff(@fptr(s+a)), imm22 */ #define R_IA64_LTOFF_FPTR64I 0x53 /* @ltoff(@fptr(s+a)), imm64 */ #define R_IA64_LTOFF_FPTR32MSB 0x54 /* @ltoff(@fptr(s+a)), data4 MSB */ #define R_IA64_LTOFF_FPTR32LSB 0x55 /* @ltoff(@fptr(s+a)), data4 LSB */ #define R_IA64_LTOFF_FPTR64MSB 0x56 /* @ltoff(@fptr(s+a)), data8 MSB */ #define R_IA64_LTOFF_FPTR64LSB 0x57 /* @ltoff(@fptr(s+a)), data8 LSB */ #define R_IA64_SEGREL32MSB 0x5c /* @segrel(sym + add), data4 MSB */ #define R_IA64_SEGREL32LSB 0x5d /* @segrel(sym + add), data4 LSB */ #define R_IA64_SEGREL64MSB 0x5e /* @segrel(sym + add), data8 MSB */ #define R_IA64_SEGREL64LSB 0x5f /* @segrel(sym + add), data8 LSB */ #define R_IA64_SECREL32MSB 0x64 /* @secrel(sym + add), data4 MSB */ #define R_IA64_SECREL32LSB 0x65 /* @secrel(sym + add), data4 LSB */ #define R_IA64_SECREL64MSB 0x66 /* @secrel(sym + add), data8 MSB */ #define R_IA64_SECREL64LSB 0x67 /* @secrel(sym + add), data8 LSB */ #define R_IA64_REL32MSB 0x6c /* data 4 + REL */ #define R_IA64_REL32LSB 0x6d /* data 4 + REL */ #define R_IA64_REL64MSB 0x6e /* data 8 + REL */ #define R_IA64_REL64LSB 0x6f /* data 8 + REL */ #define R_IA64_LTV32MSB 0x74 /* symbol + addend, data4 MSB */ #define R_IA64_LTV32LSB 0x75 /* symbol + addend, data4 LSB */ #define R_IA64_LTV64MSB 0x76 /* symbol + addend, data8 MSB */ #define R_IA64_LTV64LSB 0x77 /* symbol + addend, data8 LSB */ #define R_IA64_PCREL21BI 0x79 /* @pcrel(sym + add), 21bit inst */ #define R_IA64_PCREL22 0x7a /* @pcrel(sym + add), 22bit inst */ #define R_IA64_PCREL64I 0x7b /* @pcrel(sym + add), 64bit inst */ #define R_IA64_IPLTMSB 0x80 /* dynamic reloc, imported PLT, MSB */ #define R_IA64_IPLTLSB 0x81 /* dynamic reloc, imported PLT, LSB */ #define R_IA64_COPY 0x84 /* copy relocation */ #define R_IA64_SUB 0x85 /* Addend and symbol difference */ #define R_IA64_LTOFF22X 0x86 /* LTOFF22, relaxable. */ #define R_IA64_LDXMOV 0x87 /* Use of LTOFF22X. */ #define R_IA64_TPREL14 0x91 /* @tprel(sym + add), imm14 */ #define R_IA64_TPREL22 0x92 /* @tprel(sym + add), imm22 */ #define R_IA64_TPREL64I 0x93 /* @tprel(sym + add), imm64 */ #define R_IA64_TPREL64MSB 0x96 /* @tprel(sym + add), data8 MSB */ #define R_IA64_TPREL64LSB 0x97 /* @tprel(sym + add), data8 LSB */ #define R_IA64_LTOFF_TPREL22 0x9a /* @ltoff(@tprel(s+a)), imm2 */ #define R_IA64_DTPMOD64MSB 0xa6 /* @dtpmod(sym + add), data8 MSB */ #define R_IA64_DTPMOD64LSB 0xa7 /* @dtpmod(sym + add), data8 LSB */ #define R_IA64_LTOFF_DTPMOD22 0xaa /* @ltoff(@dtpmod(sym + add)), imm22 */ #define R_IA64_DTPREL14 0xb1 /* @dtprel(sym + add), imm14 */ #define R_IA64_DTPREL22 0xb2 /* @dtprel(sym + add), imm22 */ #define R_IA64_DTPREL64I 0xb3 /* @dtprel(sym + add), imm64 */ #define R_IA64_DTPREL32MSB 0xb4 /* @dtprel(sym + add), data4 MSB */ #define R_IA64_DTPREL32LSB 0xb5 /* @dtprel(sym + add), data4 LSB */ #define R_IA64_DTPREL64MSB 0xb6 /* @dtprel(sym + add), data8 MSB */ #define R_IA64_DTPREL64LSB 0xb7 /* @dtprel(sym + add), data8 LSB */ #define R_IA64_LTOFF_DTPREL22 0xba /* @ltoff(@dtprel(s+a)), imm22 */ /* SH specific declarations */ /* Processor specific flags for the ELF header e_flags field. */ #define EF_SH_MACH_MASK 0x1f #define EF_SH_UNKNOWN 0x0 #define EF_SH1 0x1 #define EF_SH2 0x2 #define EF_SH3 0x3 #define EF_SH_DSP 0x4 #define EF_SH3_DSP 0x5 #define EF_SH4AL_DSP 0x6 #define EF_SH3E 0x8 #define EF_SH4 0x9 #define EF_SH2E 0xb #define EF_SH4A 0xc #define EF_SH2A 0xd #define EF_SH4_NOFPU 0x10 #define EF_SH4A_NOFPU 0x11 #define EF_SH4_NOMMU_NOFPU 0x12 #define EF_SH2A_NOFPU 0x13 #define EF_SH3_NOMMU 0x14 #define EF_SH2A_SH4_NOFPU 0x15 #define EF_SH2A_SH3_NOFPU 0x16 #define EF_SH2A_SH4 0x17 #define EF_SH2A_SH3E 0x18 /* SH relocs. */ #define R_SH_NONE 0 #define R_SH_DIR32 1 #define R_SH_REL32 2 #define R_SH_DIR8WPN 3 #define R_SH_IND12W 4 #define R_SH_DIR8WPL 5 #define R_SH_DIR8WPZ 6 #define R_SH_DIR8BP 7 #define R_SH_DIR8W 8 #define R_SH_DIR8L 9 #define R_SH_SWITCH16 25 #define R_SH_SWITCH32 26 #define R_SH_USES 27 #define R_SH_COUNT 28 #define R_SH_ALIGN 29 #define R_SH_CODE 30 #define R_SH_DATA 31 #define R_SH_LABEL 32 #define R_SH_SWITCH8 33 #define R_SH_GNU_VTINHERIT 34 #define R_SH_GNU_VTENTRY 35 #define R_SH_TLS_GD_32 144 #define R_SH_TLS_LD_32 145 #define R_SH_TLS_LDO_32 146 #define R_SH_TLS_IE_32 147 #define R_SH_TLS_LE_32 148 #define R_SH_TLS_DTPMOD32 149 #define R_SH_TLS_DTPOFF32 150 #define R_SH_TLS_TPOFF32 151 #define R_SH_GOT32 160 #define R_SH_PLT32 161 #define R_SH_COPY 162 #define R_SH_GLOB_DAT 163 #define R_SH_JMP_SLOT 164 #define R_SH_RELATIVE 165 #define R_SH_GOTOFF 166 #define R_SH_GOTPC 167 /* Keep this the last entry. */ #define R_SH_NUM 256 /* S/390 specific definitions. */ /* Valid values for the e_flags field. */ #define EF_S390_HIGH_GPRS 0x00000001 /* High GPRs kernel facility needed. */ /* Additional s390 relocs */ #define R_390_NONE 0 /* No reloc. */ #define R_390_8 1 /* Direct 8 bit. */ #define R_390_12 2 /* Direct 12 bit. */ #define R_390_16 3 /* Direct 16 bit. */ #define R_390_32 4 /* Direct 32 bit. */ #define R_390_PC32 5 /* PC relative 32 bit. */ #define R_390_GOT12 6 /* 12 bit GOT offset. */ #define R_390_GOT32 7 /* 32 bit GOT offset. */ #define R_390_PLT32 8 /* 32 bit PC relative PLT address. */ #define R_390_COPY 9 /* Copy symbol at runtime. */ #define R_390_GLOB_DAT 10 /* Create GOT entry. */ #define R_390_JMP_SLOT 11 /* Create PLT entry. */ #define R_390_RELATIVE 12 /* Adjust by program base. */ #define R_390_GOTOFF32 13 /* 32 bit offset to GOT. */ #define R_390_GOTPC 14 /* 32 bit PC relative offset to GOT. */ #define R_390_GOT16 15 /* 16 bit GOT offset. */ #define R_390_PC16 16 /* PC relative 16 bit. */ #define R_390_PC16DBL 17 /* PC relative 16 bit shifted by 1. */ #define R_390_PLT16DBL 18 /* 16 bit PC rel. PLT shifted by 1. */ #define R_390_PC32DBL 19 /* PC relative 32 bit shifted by 1. */ #define R_390_PLT32DBL 20 /* 32 bit PC rel. PLT shifted by 1. */ #define R_390_GOTPCDBL 21 /* 32 bit PC rel. GOT shifted by 1. */ #define R_390_64 22 /* Direct 64 bit. */ #define R_390_PC64 23 /* PC relative 64 bit. */ #define R_390_GOT64 24 /* 64 bit GOT offset. */ #define R_390_PLT64 25 /* 64 bit PC relative PLT address. */ #define R_390_GOTENT 26 /* 32 bit PC rel. to GOT entry >> 1. */ #define R_390_GOTOFF16 27 /* 16 bit offset to GOT. */ #define R_390_GOTOFF64 28 /* 64 bit offset to GOT. */ #define R_390_GOTPLT12 29 /* 12 bit offset to jump slot. */ #define R_390_GOTPLT16 30 /* 16 bit offset to jump slot. */ #define R_390_GOTPLT32 31 /* 32 bit offset to jump slot. */ #define R_390_GOTPLT64 32 /* 64 bit offset to jump slot. */ #define R_390_GOTPLTENT 33 /* 32 bit rel. offset to jump slot. */ #define R_390_PLTOFF16 34 /* 16 bit offset from GOT to PLT. */ #define R_390_PLTOFF32 35 /* 32 bit offset from GOT to PLT. */ #define R_390_PLTOFF64 36 /* 16 bit offset from GOT to PLT. */ #define R_390_TLS_LOAD 37 /* Tag for load insn in TLS code. */ #define R_390_TLS_GDCALL 38 /* Tag for function call in general dynamic TLS code. */ #define R_390_TLS_LDCALL 39 /* Tag for function call in local dynamic TLS code. */ #define R_390_TLS_GD32 40 /* Direct 32 bit for general dynamic thread local data. */ #define R_390_TLS_GD64 41 /* Direct 64 bit for general dynamic thread local data. */ #define R_390_TLS_GOTIE12 42 /* 12 bit GOT offset for static TLS block offset. */ #define R_390_TLS_GOTIE32 43 /* 32 bit GOT offset for static TLS block offset. */ #define R_390_TLS_GOTIE64 44 /* 64 bit GOT offset for static TLS block offset. */ #define R_390_TLS_LDM32 45 /* Direct 32 bit for local dynamic thread local data in LE code. */ #define R_390_TLS_LDM64 46 /* Direct 64 bit for local dynamic thread local data in LE code. */ #define R_390_TLS_IE32 47 /* 32 bit address of GOT entry for negated static TLS block offset. */ #define R_390_TLS_IE64 48 /* 64 bit address of GOT entry for negated static TLS block offset. */ #define R_390_TLS_IEENT 49 /* 32 bit rel. offset to GOT entry for negated static TLS block offset. */ #define R_390_TLS_LE32 50 /* 32 bit negated offset relative to static TLS block. */ #define R_390_TLS_LE64 51 /* 64 bit negated offset relative to static TLS block. */ #define R_390_TLS_LDO32 52 /* 32 bit offset relative to TLS block. */ #define R_390_TLS_LDO64 53 /* 64 bit offset relative to TLS block. */ #define R_390_TLS_DTPMOD 54 /* ID of module containing symbol. */ #define R_390_TLS_DTPOFF 55 /* Offset in TLS block. */ #define R_390_TLS_TPOFF 56 /* Negated offset in static TLS block. */ #define R_390_20 57 /* Direct 20 bit. */ #define R_390_GOT20 58 /* 20 bit GOT offset. */ #define R_390_GOTPLT20 59 /* 20 bit offset to jump slot. */ #define R_390_TLS_GOTIE20 60 /* 20 bit GOT offset for static TLS block offset. */ /* Keep this the last entry. */ #define R_390_NUM 61 /* CRIS relocations. */ #define R_CRIS_NONE 0 #define R_CRIS_8 1 #define R_CRIS_16 2 #define R_CRIS_32 3 #define R_CRIS_8_PCREL 4 #define R_CRIS_16_PCREL 5 #define R_CRIS_32_PCREL 6 #define R_CRIS_GNU_VTINHERIT 7 #define R_CRIS_GNU_VTENTRY 8 #define R_CRIS_COPY 9 #define R_CRIS_GLOB_DAT 10 #define R_CRIS_JUMP_SLOT 11 #define R_CRIS_RELATIVE 12 #define R_CRIS_16_GOT 13 #define R_CRIS_32_GOT 14 #define R_CRIS_16_GOTPLT 15 #define R_CRIS_32_GOTPLT 16 #define R_CRIS_32_GOTREL 17 #define R_CRIS_32_PLT_GOTREL 18 #define R_CRIS_32_PLT_PCREL 19 #define R_CRIS_NUM 20 /* AMD x86-64 relocations. */ #define R_X86_64_NONE 0 /* No reloc */ #define R_X86_64_64 1 /* Direct 64 bit */ #define R_X86_64_PC32 2 /* PC relative 32 bit signed */ #define R_X86_64_GOT32 3 /* 32 bit GOT entry */ #define R_X86_64_PLT32 4 /* 32 bit PLT address */ #define R_X86_64_COPY 5 /* Copy symbol at runtime */ #define R_X86_64_GLOB_DAT 6 /* Create GOT entry */ #define R_X86_64_JUMP_SLOT 7 /* Create PLT entry */ #define R_X86_64_RELATIVE 8 /* Adjust by program base */ #define R_X86_64_GOTPCREL 9 /* 32 bit signed PC relative offset to GOT */ #define R_X86_64_32 10 /* Direct 32 bit zero extended */ #define R_X86_64_32S 11 /* Direct 32 bit sign extended */ #define R_X86_64_16 12 /* Direct 16 bit zero extended */ #define R_X86_64_PC16 13 /* 16 bit sign extended pc relative */ #define R_X86_64_8 14 /* Direct 8 bit sign extended */ #define R_X86_64_PC8 15 /* 8 bit sign extended pc relative */ #define R_X86_64_DTPMOD64 16 /* ID of module containing symbol */ #define R_X86_64_DTPOFF64 17 /* Offset in module's TLS block */ #define R_X86_64_TPOFF64 18 /* Offset in initial TLS block */ #define R_X86_64_TLSGD 19 /* 32 bit signed PC relative offset to two GOT entries for GD symbol */ #define R_X86_64_TLSLD 20 /* 32 bit signed PC relative offset to two GOT entries for LD symbol */ #define R_X86_64_DTPOFF32 21 /* Offset in TLS block */ #define R_X86_64_GOTTPOFF 22 /* 32 bit signed PC relative offset to GOT entry for IE symbol */ #define R_X86_64_TPOFF32 23 /* Offset in initial TLS block */ #define R_X86_64_PC64 24 /* PC relative 64 bit */ #define R_X86_64_GOTOFF64 25 /* 64 bit offset to GOT */ #define R_X86_64_GOTPC32 26 /* 32 bit signed pc relative offset to GOT */ #define R_X86_64_GOT64 27 /* 64-bit GOT entry offset */ #define R_X86_64_GOTPCREL64 28 /* 64-bit PC relative offset to GOT entry */ #define R_X86_64_GOTPC64 29 /* 64-bit PC relative offset to GOT */ #define R_X86_64_GOTPLT64 30 /* like GOT64, says PLT entry needed */ #define R_X86_64_PLTOFF64 31 /* 64-bit GOT relative offset to PLT entry */ #define R_X86_64_SIZE32 32 /* Size of symbol plus 32-bit addend */ #define R_X86_64_SIZE64 33 /* Size of symbol plus 64-bit addend */ #define R_X86_64_GOTPC32_TLSDESC 34 /* GOT offset for TLS descriptor. */ #define R_X86_64_TLSDESC_CALL 35 /* Marker for call through TLS descriptor. */ #define R_X86_64_TLSDESC 36 /* TLS descriptor. */ #define R_X86_64_IRELATIVE 37 /* Adjust indirectly by program base */ #define R_X86_64_NUM 38 /* AM33 relocations. */ #define R_MN10300_NONE 0 /* No reloc. */ #define R_MN10300_32 1 /* Direct 32 bit. */ #define R_MN10300_16 2 /* Direct 16 bit. */ #define R_MN10300_8 3 /* Direct 8 bit. */ #define R_MN10300_PCREL32 4 /* PC-relative 32-bit. */ #define R_MN10300_PCREL16 5 /* PC-relative 16-bit signed. */ #define R_MN10300_PCREL8 6 /* PC-relative 8-bit signed. */ #define R_MN10300_GNU_VTINHERIT 7 /* Ancient C++ vtable garbage... */ #define R_MN10300_GNU_VTENTRY 8 /* ... collection annotation. */ #define R_MN10300_24 9 /* Direct 24 bit. */ #define R_MN10300_GOTPC32 10 /* 32-bit PCrel offset to GOT. */ #define R_MN10300_GOTPC16 11 /* 16-bit PCrel offset to GOT. */ #define R_MN10300_GOTOFF32 12 /* 32-bit offset from GOT. */ #define R_MN10300_GOTOFF24 13 /* 24-bit offset from GOT. */ #define R_MN10300_GOTOFF16 14 /* 16-bit offset from GOT. */ #define R_MN10300_PLT32 15 /* 32-bit PCrel to PLT entry. */ #define R_MN10300_PLT16 16 /* 16-bit PCrel to PLT entry. */ #define R_MN10300_GOT32 17 /* 32-bit offset to GOT entry. */ #define R_MN10300_GOT24 18 /* 24-bit offset to GOT entry. */ #define R_MN10300_GOT16 19 /* 16-bit offset to GOT entry. */ #define R_MN10300_COPY 20 /* Copy symbol at runtime. */ #define R_MN10300_GLOB_DAT 21 /* Create GOT entry. */ #define R_MN10300_JMP_SLOT 22 /* Create PLT entry. */ #define R_MN10300_RELATIVE 23 /* Adjust by program base. */ #define R_MN10300_NUM 24 /* M32R relocs. */ #define R_M32R_NONE 0 /* No reloc. */ #define R_M32R_16 1 /* Direct 16 bit. */ #define R_M32R_32 2 /* Direct 32 bit. */ #define R_M32R_24 3 /* Direct 24 bit. */ #define R_M32R_10_PCREL 4 /* PC relative 10 bit shifted. */ #define R_M32R_18_PCREL 5 /* PC relative 18 bit shifted. */ #define R_M32R_26_PCREL 6 /* PC relative 26 bit shifted. */ #define R_M32R_HI16_ULO 7 /* High 16 bit with unsigned low. */ #define R_M32R_HI16_SLO 8 /* High 16 bit with signed low. */ #define R_M32R_LO16 9 /* Low 16 bit. */ #define R_M32R_SDA16 10 /* 16 bit offset in SDA. */ #define R_M32R_GNU_VTINHERIT 11 #define R_M32R_GNU_VTENTRY 12 /* M32R relocs use SHT_RELA. */ #define R_M32R_16_RELA 33 /* Direct 16 bit. */ #define R_M32R_32_RELA 34 /* Direct 32 bit. */ #define R_M32R_24_RELA 35 /* Direct 24 bit. */ #define R_M32R_10_PCREL_RELA 36 /* PC relative 10 bit shifted. */ #define R_M32R_18_PCREL_RELA 37 /* PC relative 18 bit shifted. */ #define R_M32R_26_PCREL_RELA 38 /* PC relative 26 bit shifted. */ #define R_M32R_HI16_ULO_RELA 39 /* High 16 bit with unsigned low */ #define R_M32R_HI16_SLO_RELA 40 /* High 16 bit with signed low */ #define R_M32R_LO16_RELA 41 /* Low 16 bit */ #define R_M32R_SDA16_RELA 42 /* 16 bit offset in SDA */ #define R_M32R_RELA_GNU_VTINHERIT 43 #define R_M32R_RELA_GNU_VTENTRY 44 #define R_M32R_REL32 45 /* PC relative 32 bit. */ #define R_M32R_GOT24 48 /* 24 bit GOT entry */ #define R_M32R_26_PLTREL 49 /* 26 bit PC relative to PLT shifted */ #define R_M32R_COPY 50 /* Copy symbol at runtime */ #define R_M32R_GLOB_DAT 51 /* Create GOT entry */ #define R_M32R_JMP_SLOT 52 /* Create PLT entry */ #define R_M32R_RELATIVE 53 /* Adjust by program base */ #define R_M32R_GOTOFF 54 /* 24 bit offset to GOT */ #define R_M32R_GOTPC24 55 /* 24 bit PC relative offset to GOT */ #define R_M32R_GOT16_HI_ULO 56 /* High 16 bit GOT entry with unsigned low */ #define R_M32R_GOT16_HI_SLO 57 /* High 16 bit GOT entry with signed low */ #define R_M32R_GOT16_LO 58 /* Low 16 bit GOT entry */ #define R_M32R_GOTPC_HI_ULO 59 /* High 16 bit PC relative offset to GOT with unsigned low */ #define R_M32R_GOTPC_HI_SLO 60 /* High 16 bit PC relative offset to GOT with signed low */ #define R_M32R_GOTPC_LO 61 /* Low 16 bit PC relative offset to GOT */ #define R_M32R_GOTOFF_HI_ULO 62 /* High 16 bit offset to GOT with unsigned low */ #define R_M32R_GOTOFF_HI_SLO 63 /* High 16 bit offset to GOT with signed low */ #define R_M32R_GOTOFF_LO 64 /* Low 16 bit offset to GOT */ #define R_M32R_NUM 256 /* Keep this the last entry. */ __END_DECLS #endif /* elf.h */ ================================================ FILE: src/gs/gs.c ================================================ #include #include #include #include #include "gs.h" #include "ee/intc.h" #include "iop/intc.h" struct ps2_gs* ps2_gs_create(void) { return malloc(sizeof(struct ps2_gs)); } static inline void gs_test_gs_irq(struct ps2_gs* gs) { uint32_t mask = (gs->imr >> 8) & 0x1f; uint32_t stat = gs->csr & 0x1f; if (stat & (~mask)) { // printf("gs: IRQ triggered! stat=%02x mask=%02x\n", stat, mask); ps2_intc_irq(gs->ee_intc, EE_INTC_GS); } } static inline int gs_assert_vblank(struct ps2_gs* gs) { if ((gs->csr & 8) == 0) { gs->csr |= 8; return ((gs->imr >> 8) & 8) == 0; } return 0; } static inline int gs_assert_hblank(struct ps2_gs* gs) { if ((gs->csr & 4) == 0) { if (gs->csr_enable & 4) gs->csr |= 4; // printf("gs: Asserting Hblank imr.hsync=%d\n", (gs->imr >> 8) & 4); return ((gs->imr >> 8) & 4) == 0; } return 0; } void gs_handle_vblank_in(void* udata, int overshoot); void gs_handle_hblank(void* udata, int overshoot); void gs_handle_vblank_out(void* udata, int overshoot) { struct ps2_gs* gs = (struct ps2_gs*)udata; struct sched_event vblank_in_event; vblank_in_event.callback = gs_handle_vblank_in; vblank_in_event.cycles = GS_FRAME_NTSC; vblank_in_event.name = "Vblank in event"; vblank_in_event.udata = gs; sched_schedule(gs->sched, vblank_in_event); // Send Vblank IRQs through INTC ps2_intc_irq(gs->ee_intc, EE_INTC_VBLANK_OUT); ps2_iop_intc_irq(gs->iop_intc, IOP_INTC_VBLANK_OUT); gs->vblank = 0; } void gs_flip_field(void* udata, int overshoot) { struct ps2_gs* gs = (struct ps2_gs*)udata; // Toggle field gs->csr ^= (1 << 13) | (1 << 14); } void gs_handle_vblank_in(void* udata, int overshoot) { struct ps2_gs* gs = (struct ps2_gs*)udata; struct sched_event vblank_out_event; vblank_out_event.callback = gs_handle_vblank_out; vblank_out_event.cycles = GS_VBLANK_NTSC; vblank_out_event.name = "Vblank out event"; vblank_out_event.udata = gs; struct sched_event field_flip_event; field_flip_event.callback = gs_flip_field; field_flip_event.cycles = 65622; field_flip_event.name = "Field flip event"; field_flip_event.udata = gs; // Set Vblank and Hblank flag if (gs_assert_vblank(gs)) { ps2_intc_irq(gs->ee_intc, EE_INTC_GS); } // Send Vblank IRQ through INTC ps2_intc_irq(gs->ee_intc, EE_INTC_VBLANK_IN); ps2_iop_intc_irq(gs->iop_intc, IOP_INTC_VBLANK_IN); // uint32_t mask = (gs->imr >> 8) & 0x1f; // uint32_t stat = gs->csr & 0x1f; // if (stat & (~mask)) { // ps2_intc_irq(gs->ee_intc, EE_INTC_GS); // } sched_schedule(gs->sched, vblank_out_event); sched_schedule(gs->sched, field_flip_event); gs->vblank = 1; } void gs_handle_hblank(void* udata, int overshoot) { struct ps2_gs* gs = (struct ps2_gs*)udata; struct sched_event hblank_event; hblank_event.callback = gs_handle_hblank; hblank_event.cycles = GS_SCANLINE_NTSC; hblank_event.name = "Hblank event"; hblank_event.udata = gs; if (gs_assert_hblank(gs)) { ps2_intc_irq(gs->ee_intc, EE_INTC_GS); } gs->csr ^= 1 << 13 | 1 << 14; sched_schedule(gs->sched, hblank_event); } void 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) { memset(gs, 0, sizeof(struct ps2_gs)); gs->sched = sched; gs->ee_intc = ee_intc; gs->iop_intc = iop_intc; gs->ee_timers = ee_timers; gs->iop_timers = iop_timers; gs->vram = malloc(0x400000); // 4 MB // Schedule Vblank event struct sched_event vblank_event; vblank_event.callback = gs_handle_vblank_in; vblank_event.cycles = GS_FRAME_NTSC; vblank_event.name = "Vblank in event"; vblank_event.udata = gs; sched_schedule(gs->sched, vblank_event); // struct sched_event hblank_event; // hblank_event.callback = gs_handle_hblank; // hblank_event.cycles = GS_SCANLINE_NTSC; // hblank_event.name = "Hblank event"; // hblank_event.udata = gs; // sched_schedule(gs->sched, hblank_event); gs->ctx = &gs->context[0]; gs->imr = 0x00007f00; } void ps2_gs_reset(struct ps2_gs* gs) { gs->ctx = &gs->context[0]; gs->csr |= 2; gs->imr = 0x00007f00; // Schedule Vblank event struct sched_event vblank_event; vblank_event.callback = gs_handle_vblank_in; vblank_event.cycles = GS_FRAME_NTSC; vblank_event.name = "Vblank in event"; vblank_event.udata = gs; sched_schedule(gs->sched, vblank_event); // struct sched_event hblank_event; // hblank_event.callback = gs_handle_hblank; // hblank_event.cycles = GS_SCANLINE_NTSC; // hblank_event.name = "Hblank event"; // hblank_event.udata = gs; // sched_schedule(gs->sched, hblank_event); memset(gs->vram, 0, 0x400000); } // void gs_switch_context(struct ps2_gs* gs, int c) { // gs->ctx = &gs->context[c]; // } void ps2_gs_destroy(struct ps2_gs* gs) { free(gs->vram); free(gs); } // void gs_start_primitive(struct ps2_gs* gs) { // if (gs->prim & ~0x7ff) { // // printf("gs: Invalid prim value %016x\n", gs->prim); // // exit(1); // } // gs->vqi = 0; // } // static inline void gs_unpack_vertex(struct ps2_gs* gs, struct gs_vertex* v) { // v->x = v->xyz & 0xffff; // v->y = (v->xyz >> 16) & 0xffff; // v->z = v->xyz >> 32; // v->r = v->rgbaq & 0xff; // v->g = (v->rgbaq >> 8) & 0xff; // v->b = (v->rgbaq >> 16) & 0xff; // v->a = (v->rgbaq >> 24) & 0xff; // union { // uint32_t u32; // float f; // } s, t, q; // s.u32 = v->st & 0xffffffff; // t.u32 = v->st >> 32; // q.u32 = v->rgbaq >> 32; // v->s = s.f; // v->t = t.f; // v->q = q.f; // v->u = v->uv & 0x3fff; // v->v = (v->uv >> 16) & 0x3fff; // } // void gs_write_vertex(struct ps2_gs* gs, uint64_t data, int discard) { // gs->vq[gs->vqi].xyz = data; // gs->vq[gs->vqi].st = gs->st; // gs->vq[gs->vqi].uv = gs->uv; // gs->vq[gs->vqi].rgbaq = gs->rgbaq; // gs->attr = (gs->prmodecont & 1) ? gs->prim : gs->prmode; // // Cache PRIM/PRMODE fields // gs->iip = (gs->attr >> 3) & 1; // gs->tme = (gs->attr >> 4) & 1; // gs->fge = (gs->attr >> 5) & 1; // gs->abe = (gs->attr >> 6) & 1; // gs->aa1 = (gs->attr >> 7) & 1; // gs->fst = (gs->attr >> 8) & 1; // gs->ctxt = (gs->attr >> 9) & 1; // gs->fix = (gs->attr >> 10) & 1; // gs_unpack_vertex(gs, &gs->vq[gs->vqi]); // // printf("gs: Pushing vertex (%04x,%04x) to VQ[%d] discard=%d\n", // // gs->vq[gs->vqi].x, gs->vq[gs->vqi].y, gs->vqi, discard // // ); // gs->vqi++; // // for (int c = 0; c < 2; c++) { // // uint32_t fbp = (gs->context[c].frame & 0x1ff) << 11; // // uint32_t fbw = ((gs->context[c].frame >> 16) & 0x3f) << 6; // // uint32_t xoff = (gs->context[c].xyoffset & 0xffff); // // uint32_t yoff = ((gs->context[c].xyoffset >> 32) & 0xffff); // // int scax0 = gs->context[c].scissor & 0x3ff; // // int scay0 = (gs->context[c].scissor >> 32) & 0x3ff; // // int scax1 = (gs->context[c].scissor >> 16) & 0x3ff; // // int scay1 = (gs->context[c].scissor >> 48) & 0x3ff; // // printf("context %d: fbp=%08x fbw=%d xyoffset=(%d,%d) scissor=(%d,%d-%d,%d) prmodecont=%016lx prmode=%016lx prim=%016lx\n", // // c, // // fbp, fbw, // // xoff, yoff, // // scax0, scay0, // // scax1, scay1, // // gs->prmodecont, // // gs->prmode, // // gs->prim // // ); // // } // gs_switch_context(gs, (gs->attr & GS_CTXT) ? 1 : 0); // switch (gs->prim & 7) { // case 0: if (gs->vqi == 1) { gs->backend.render_point(gs, gs->backend.udata); gs->vqi = 0; } break; // case 1: if (gs->vqi == 2) { gs->backend.render_line(gs, gs->backend.udata); gs->vqi = 0; } break; // case 2: { // if (gs->vqi == 2) { // if (!discard) // gs->backend.render_line(gs, gs->backend.udata); // } else if (gs->vqi == 3) { // gs->vq[0] = gs->vq[1]; // gs->vq[1] = gs->vq[2]; // if (!discard) // gs->backend.render_line(gs, gs->backend.udata); // gs->vqi = 2; // } // } break; // case 3: if (gs->vqi == 3) { if (!discard) gs->backend.render_triangle(gs, gs->backend.udata); gs->vqi = 0; } break; // case 4: { // if (gs->vqi == 3) { // if (!discard) gs->backend.render_triangle(gs, gs->backend.udata); // } else if (gs->vqi == 4) { // gs->vq[0] = gs->vq[1]; // gs->vq[1] = gs->vq[2]; // gs->vq[2] = gs->vq[3]; // if (!discard) gs->backend.render_triangle(gs, gs->backend.udata); // gs->vqi = 3; // } // } break; // case 5: { // if (gs->vqi == 3) { // if (!discard) gs->backend.render_triangle(gs, gs->backend.udata); // } else if (gs->vqi == 4) { // gs->vq[1] = gs->vq[2]; // gs->vq[2] = gs->vq[3]; // if (!discard) gs->backend.render_triangle(gs, gs->backend.udata); // gs->vqi = 3; // } // } break; // case 6: if (gs->vqi == 2) { if (!discard) gs->backend.render_sprite(gs, gs->backend.udata); gs->vqi = 0; } break; // case 7: if (gs->vqi == 2) { if (!discard) gs->backend.render_sprite(gs, gs->backend.udata); gs->vqi = 0; } break; // default: { // printf("gs: Reserved primitive %ld\n", gs->prim & 7); // } break; // } // } // void gs_write_vertex_fog(struct ps2_gs* gs, uint64_t data, int discard) { // gs->vq[gs->vqi].fog = data >> 56; // gs_write_vertex(gs, data & 0xffffffffffffffull, discard); // } // void gs_write_vertex_no_fog(struct ps2_gs* gs, uint64_t data, int discard) { // gs->vq[gs->vqi].fog = gs->fog; // gs_write_vertex(gs, data, discard); // } uint64_t ps2_gs_read64(struct ps2_gs* gs, uint32_t addr) { // Hack toggle between FIFO empty and FIFO "Neither Empty nor Almost Full" gs->csr ^= 0x4000; addr = (addr & 0xfffff000) | (addr & 0x3ff); switch (addr) { case 0x12000000: case 0x12000010: case 0x12000020: case 0x12000030: case 0x12000040: case 0x12000050: case 0x12000060: case 0x12000070: case 0x12000080: case 0x12000090: case 0x120000A0: case 0x120000B0: case 0x120000C0: case 0x120000D0: case 0x120000E0: case 0x12001000: case 0x12001010: case 0x12001040: { return gs->csr | 0x551b0000; } case 0x12001080: return gs->siglblid; case 0x12001001: return (gs->csr >> 8) & 0xff; } printf("gs: Unhandled read from %08x\n", addr); return 0; } static inline void gs_unpack_dispfb1(struct ps2_gs* gs) { gs->dfbp1 = (gs->dispfb1 & 0x1ff) << 5; gs->dfbw1 = (gs->dispfb1 >> 9) & 0x3f; gs->dfbpsm1 = (gs->dispfb1 >> 15) & 0x1f; } static inline void gs_unpack_dispfb2(struct ps2_gs* gs) { gs->dfbp2 = (gs->dispfb2 & 0x1ff) << 5; gs->dfbw2 = (gs->dispfb2 >> 9) & 0x3f; gs->dfbpsm2 = (gs->dispfb2 >> 15) & 0x1f; } void ps2_gs_write64(struct ps2_gs* gs, uint32_t addr, uint64_t data) { switch (addr) { case 0x12000000: gs->pmode = data; return; case 0x12000010: gs->smode1 = data; return; case 0x12000020: gs->smode2 = data; return; case 0x12000030: gs->srfsh = data; return; case 0x12000040: gs->synch1 = data; return; case 0x12000050: gs->synch2 = data; return; case 0x12000060: gs->syncv = data; return; case 0x12000070: gs->dispfb1 = data; gs_unpack_dispfb1(gs); return; case 0x12000080: gs->display1 = data; return; case 0x12000090: gs->dispfb2 = data; gs_unpack_dispfb2(gs); return; case 0x120000A0: gs->display2 = data; return; case 0x120000B0: gs->extbuf = data; return; case 0x120000C0: gs->extdata = data; return; case 0x120000D0: gs->extwrite = data; return; case 0x120000E0: gs->bgcolor = data; return; case 0x12001000: { if (data & 8) { // Game is requesting vsync // gs->vblank |= 1; } gs->csr = (gs->csr & 0xfffffe00) | (gs->csr & ~(data & 0xf)); gs->csr_enable = data; if (data & 1) { if (gs->signal_pending) { gs->siglblid &= ~0xffffffffull; gs->siglblid |= gs->stall_sigid; } } } return; case 0x12001010: { int prev_signal = (gs->imr >> 8) & 1; int new_signal = (data >> 8) & 1; gs->imr = data; if (gs->signal_pending && (prev_signal && !new_signal)) { gs->signal_pending--; ps2_intc_irq(gs->ee_intc, EE_INTC_GS); } } return; case 0x12001040: gs->busdir = data; return; case 0x12001080: gs->siglblid = data; return; } fprintf(stderr, "gs: Unhandled write to %08x with data %016lx\n", addr, data); exit(1); } // static inline void gs_load_clut_cache(struct ps2_gs* gs, int i) { // // printf("tbpsm=%x cbpsm=%x csm=%d csa=%x (%d) cld=%d\n", // // gs->context[i].tbpsm, // // gs->context[i].cbpsm, // // gs->context[i].csm, // // gs->context[i].csa, // // gs->context[i].csa, // // gs->context[i].cld // // ); // switch (gs->context[i].cld) { // case 1: break; // case 2: gs->cbp0 = gs->context[i].cbp; break; // case 3: gs->cbp1 = gs->context[i].cbp; break; // case 4: if (gs->context[i].cbp == gs->cbp0) return; break; // case 5: if (gs->context[i].cbp == gs->cbp1) return; break; // default: return; // } // switch (gs->context[i].tbpsm) { // case GS_PSMT8H: // case GS_PSMT8: { // switch (gs->context[i].cbpsm) { // case GS_PSMCT32: { // for (int y = 0; y < 16; y++) { // for (int x = 0; x < 16; x++) { // uint32_t cache_addr = (gs->context[i].csa * 16) + (x + (y * 16)); // uint32_t vram_addr = gs->context[i].cbp + (x + (y * 64)); // gs->clut_cache[cache_addr] = gs->vram[vram_addr]; // } // } // } break; // case GS_PSMCT16: // case GS_PSMCT16S: { // printf("16bpp 8-bit CLUT\n"); // exit(1); // } break; // } // } break; // case GS_PSMT4HH: // case GS_PSMT4HL: // case GS_PSMT4: { // switch (gs->context[i].cbpsm) { // case GS_PSMCT32: { // for (int y = 0; y < 2; y++) { // for (int x = 0; x < 8; x++) { // uint32_t cache_addr = (gs->context[i].csa * 16) + (x + (y * 8)); // uint32_t vram_addr = gs->context[i].cbp + (x + (y * 64)); // gs->clut_cache[cache_addr] = gs->vram[vram_addr]; // } // } // } break; // case GS_PSMCT16: // case GS_PSMCT16S: { // printf("16bpp 4-bit CLUT\n"); // exit(1); // } break; // } // } break; // } // } // static inline void gs_unpack_tex0(struct ps2_gs* gs, int i) { // gs->context[i].tbp0 = gs->context[i].tex0 & 0x3fff; // gs->context[i].tbw = (gs->context[i].tex0 >> 14) & 0x3f; // gs->context[i].tbpsm = (gs->context[i].tex0 >> 20) & 0x3f; // gs->context[i].usize = 1 << ((gs->context[i].tex0 >> 26) & 0xf); // tw // gs->context[i].vsize = 1 << ((gs->context[i].tex0 >> 30) & 0xf); // th // gs->context[i].tcc = (gs->context[i].tex0 >> 34) & 1; // gs->context[i].tfx = (gs->context[i].tex0 >> 35) & 3; // gs->context[i].cbp = (gs->context[i].tex0 >> 37) & 0x3fff; // gs->context[i].cbpsm = (gs->context[i].tex0 >> 51) & 0xf; // gs->context[i].csm = (gs->context[i].tex0 >> 55) & 1; // gs->context[i].csa = (gs->context[i].tex0 >> 56) & 0x1f; // gs->context[i].cld = (gs->context[i].tex0 >> 61) & 7; // gs->context[i].usize = (gs->context[i].usize > 1024) ? 1024 : gs->context[i].usize; // gs->context[i].vsize = (gs->context[i].vsize > 1024) ? 1024 : gs->context[i].vsize; // if (gs->context[i].cld) { // // printf("gs: CLUT cache load (mode %d, dbp=%08x)\n", gs->context[i].cld, gs->context[i].cbp); // // gs_load_clut_cache(gs, i); // } // } // static inline void gs_unpack_clamp(struct ps2_gs* gs, int i) { // gs->context[i].wms = gs->context[i].clamp & 3; // gs->context[i].wmt = (gs->context[i].clamp >> 2) & 3; // gs->context[i].minu = (gs->context[i].clamp >> 4) & 0x3ff; // gs->context[i].maxu = (gs->context[i].clamp >> 14) & 0x3ff; // gs->context[i].minv = (gs->context[i].clamp >> 24) & 0x3ff; // gs->context[i].maxv = (gs->context[i].clamp >> 34) & 0x3ff; // } // static inline void gs_unpack_tex1(struct ps2_gs* gs, int i) { // gs->context[i].lcm = gs->context[i].tex1 & 1; // gs->context[i].mxl = (gs->context[i].tex1 >> 2) & 7; // gs->context[i].mmag = (gs->context[i].tex1 >> 5) & 1; // gs->context[i].mmin = (gs->context[i].tex1 >> 6) & 7; // gs->context[i].mtba = (gs->context[i].tex1 >> 9) & 1; // gs->context[i].l = (gs->context[i].tex1 >> 19) & 3; // gs->context[i].k = gs->context[i].tex1 >> 32; // } // static inline void gs_unpack_tex2(struct ps2_gs* gs, int i) { // gs->context[i].tbpsm = (gs->context[i].tex2 >> 20) & 0x3f; // gs->context[i].cbp = (gs->context[i].tex2 >> 37) & 0x3fff; // gs->context[i].cbpsm = (gs->context[i].tex2 >> 51) & 0xf; // gs->context[i].csm = (gs->context[i].tex2 >> 55) & 1; // gs->context[i].csa = (gs->context[i].tex2 >> 56) & 0x1f; // gs->context[i].cld = (gs->context[i].tex2 >> 61) & 7; // if (gs->context[i].cld) { // // printf("gs: CLUT cache load (mode %d, dbp=%08x)\n", gs->context[i].cld, gs->context[i].cbp); // // gs_load_clut_cache(gs, i); // } // // if (gs->context[i].cld) { // // gs_load_clut_cache(gs, i); // // } // } // static inline void gs_unpack_xyoffset(struct ps2_gs* gs, int i) { // gs->context[i].ofx = gs->context[i].xyoffset & 0xffff; // gs->context[i].ofy = (gs->context[i].xyoffset >> 32) & 0xffff; // } // static inline void gs_unpack_miptbp1(struct ps2_gs* gs, int i) { // gs->context[i].mmtbp[0] = gs->context[i].miptbp1 & 0x3fff; // gs->context[i].mmtbw[0] = (gs->context[i].miptbp1 >> 14) & 0x3f; // gs->context[i].mmtbp[1] = (gs->context[i].miptbp1 >> 20) & 0x3fff; // gs->context[i].mmtbw[1] = (gs->context[i].miptbp1 >> 34) & 0x3f; // gs->context[i].mmtbp[2] = (gs->context[i].miptbp1 >> 40) & 0x3fff; // gs->context[i].mmtbw[2] = (gs->context[i].miptbp1 >> 54) & 0x3f; // } // static inline void gs_unpack_miptbp2(struct ps2_gs* gs, int i) { // gs->context[i].mmtbp[3] = gs->context[i].miptbp2 & 0x3fff; // gs->context[i].mmtbw[3] = (gs->context[i].miptbp2 >> 14) & 0x3f; // gs->context[i].mmtbp[4] = (gs->context[i].miptbp2 >> 20) & 0x3fff; // gs->context[i].mmtbw[4] = (gs->context[i].miptbp2 >> 34) & 0x3f; // gs->context[i].mmtbp[5] = (gs->context[i].miptbp2 >> 40) & 0x3fff; // gs->context[i].mmtbw[5] = (gs->context[i].miptbp2 >> 54) & 0x3f; // } // static inline void gs_unpack_scissor(struct ps2_gs* gs, int i) { // gs->context[i].scax0 = gs->context[i].scissor & 0x3ff; // gs->context[i].scay0 = (gs->context[i].scissor >> 32) & 0x3ff; // gs->context[i].scax1 = (gs->context[i].scissor >> 16) & 0x3ff; // gs->context[i].scay1 = (gs->context[i].scissor >> 48) & 0x3ff; // } // static inline void gs_unpack_alpha(struct ps2_gs* gs, int i) { // gs->context[i].a = gs->context[i].alpha & 3; // gs->context[i].b = (gs->context[i].alpha >> 2) & 3; // gs->context[i].c = (gs->context[i].alpha >> 4) & 3; // gs->context[i].d = (gs->context[i].alpha >> 6) & 3; // gs->context[i].fix = (gs->context[i].alpha >> 32) & 0xff; // } // static inline void gs_unpack_test(struct ps2_gs* gs, int i) { // gs->context[i].ate = gs->context[i].test & 1; // gs->context[i].atst = (gs->context[i].test >> 1) & 7; // gs->context[i].aref = (gs->context[i].test >> 4) & 0xff; // gs->context[i].afail = (gs->context[i].test >> 12) & 3; // gs->context[i].date = (gs->context[i].test >> 14) & 1; // gs->context[i].datm = (gs->context[i].test >> 15) & 1; // gs->context[i].zte = (gs->context[i].test >> 16) & 1; // gs->context[i].ztst = (gs->context[i].test >> 17) & 3; // } // static inline void gs_unpack_frame(struct ps2_gs* gs, int i) { // gs->context[i].fbp = (gs->context[i].frame & 0x1ff) << 11; // gs->context[i].fbw = ((gs->context[i].frame >> 16) & 0x3f) << 6; // gs->context[i].fbpsm = (gs->context[i].frame >> 24) & 0x3f; // gs->context[i].fbmsk = gs->context[i].frame >> 32; // } // static inline void gs_unpack_zbuf(struct ps2_gs* gs, int i) { // gs->context[i].zbp = (gs->context[i].zbuf & 0x1ff) << 11; // gs->context[i].zbpsm = (gs->context[i].zbuf >> 24) & 0xf; // gs->context[i].zbmsk = (gs->context[i].zbuf >> 32) & 1; // } // static inline void gs_unpack_texclut(struct ps2_gs* gs) { // gs->cbw = gs->texclut & 0x3f; // gs->cou = ((gs->texclut >> 6) & 0x3f) << 4; // gs->cov = (gs->texclut >> 12) & 0x3ff; // } // static inline void gs_unpack_texa(struct ps2_gs* gs) { // gs->ta0 = gs->texa & 0xff; // gs->aem = (gs->texa >> 15) & 1; // gs->ta1 = (gs->texa >> 32) & 0xff; // } // static inline void gs_unpack_dimx(struct ps2_gs* gs) { // gs->dither[0][0] = ((int32_t)(((gs->dimx >> 0 ) & 7) << 29)) >> 29; // gs->dither[0][1] = ((int32_t)(((gs->dimx >> 4 ) & 7) << 29)) >> 29; // gs->dither[0][2] = ((int32_t)(((gs->dimx >> 8 ) & 7) << 29)) >> 29; // gs->dither[0][3] = ((int32_t)(((gs->dimx >> 12) & 7) << 29)) >> 29; // gs->dither[1][0] = ((int32_t)(((gs->dimx >> 16) & 7) << 29)) >> 29; // gs->dither[1][1] = ((int32_t)(((gs->dimx >> 20) & 7) << 29)) >> 29; // gs->dither[1][2] = ((int32_t)(((gs->dimx >> 24) & 7) << 29)) >> 29; // gs->dither[1][3] = ((int32_t)(((gs->dimx >> 28) & 7) << 29)) >> 29; // gs->dither[2][0] = ((int32_t)(((gs->dimx >> 32) & 7) << 29)) >> 29; // gs->dither[2][1] = ((int32_t)(((gs->dimx >> 36) & 7) << 29)) >> 29; // gs->dither[2][2] = ((int32_t)(((gs->dimx >> 40) & 7) << 29)) >> 29; // gs->dither[2][3] = ((int32_t)(((gs->dimx >> 44) & 7) << 29)) >> 29; // gs->dither[3][0] = ((int32_t)(((gs->dimx >> 48) & 7) << 29)) >> 29; // gs->dither[3][1] = ((int32_t)(((gs->dimx >> 52) & 7) << 29)) >> 29; // gs->dither[3][2] = ((int32_t)(((gs->dimx >> 56) & 7) << 29)) >> 29; // gs->dither[3][3] = ((int32_t)(((gs->dimx >> 60) & 7) << 29)) >> 29; // } // void ps2_gs_write_internal(struct ps2_gs* gs, int reg, uint64_t data) { // switch (reg) { // case 0x00: /* printf("gs: PRIM <- %016lx\n", data); */ gs->prim = data; gs_start_primitive(gs); return; // case 0x01: /* printf("gs: RGBAQ <- %016lx\n", data); */ gs->rgbaq = data; return; // case 0x02: /* printf("gs: ST <- %016lx\n", data); */ gs->st = data; return; // case 0x03: /* printf("gs: UV <- %016lx\n", data); */ gs->uv = data; return; // case 0x04: /* printf("gs: XYZF2 <- %016lx\n", data); */ gs->xyzf2 = data; gs_write_vertex_fog(gs, gs->xyzf2, 0); return; // case 0x05: /* printf("gs: XYZ2 <- %016lx\n", data); */ gs->xyz2 = data; gs_write_vertex_no_fog(gs, gs->xyz2, 0); return; // case 0x06: /* printf("gs: TEX0_1 <- %016lx\n", data); */ gs->context[0].tex0 = data; gs_unpack_tex0(gs, 0); return; // case 0x07: /* printf("gs: TEX0_2 <- %016lx\n", data); */ gs->context[1].tex0 = data; gs_unpack_tex0(gs, 1); return; // case 0x08: /* printf("gs: CLAMP_1 <- %016lx\n", data); */ gs->context[0].clamp = data; gs_unpack_clamp(gs, 0); return; // case 0x09: /* printf("gs: CLAMP_2 <- %016lx\n", data); */ gs->context[1].clamp = data; gs_unpack_clamp(gs, 1); return; // case 0x0A: /* printf("gs: FOG <- %016lx\n", data); */ gs->fog = data; return; // case 0x0C: /* printf("gs: XYZF3 <- %016lx\n", data); */ gs->xyzf3 = data; gs_write_vertex_fog(gs, gs->xyzf3, 1); return; // case 0x0D: /* printf("gs: XYZ3 <- %016lx\n", data); */ gs->xyz3 = data; gs_write_vertex_no_fog(gs, gs->xyz3, 1); return; // case 0x14: /* printf("gs: TEX1_1 <- %016lx\n", data); */ gs->context[0].tex1 = data; gs_unpack_tex1(gs, 0); return; // case 0x15: /* printf("gs: TEX1_2 <- %016lx\n", data); */ gs->context[1].tex1 = data; gs_unpack_tex1(gs, 1); return; // case 0x16: /* printf("gs: TEX2_1 <- %016lx\n", data); */ gs->context[0].tex2 = data; gs_unpack_tex2(gs, 0); return; // case 0x17: /* printf("gs: TEX2_2 <- %016lx\n", data); */ gs->context[1].tex2 = data; gs_unpack_tex2(gs, 1); return; // case 0x18: /* printf("gs: XYOFFSET_1 <- %016lx\n", data); */ gs->context[0].xyoffset = data; gs_unpack_xyoffset(gs, 0); return; // case 0x19: /* printf("gs: XYOFFSET_2 <- %016lx\n", data); */ gs->context[1].xyoffset = data; gs_unpack_xyoffset(gs, 1); return; // case 0x1A: /* printf("gs: PRMODECONT <- %016lx\n", data); */ gs->prmodecont = data; return; // case 0x1B: /* printf("gs: PRMODE <- %016lx\n", data); */ gs->prmode = data; return; // case 0x1C: /* printf("gs: TEXCLUT <- %016lx\n", data); */ gs->texclut = data; gs_unpack_texclut(gs); return; // case 0x22: /* printf("gs: SCANMSK <- %016lx\n", data); */ gs->scanmsk = data; return; // case 0x34: /* printf("gs: MIPTBP1_1 <- %016lx\n", data); */ gs->context[0].miptbp1 = data; gs_unpack_miptbp1(gs, 0); return; // case 0x35: /* printf("gs: MIPTBP1_2 <- %016lx\n", data); */ gs->context[1].miptbp1 = data; gs_unpack_miptbp1(gs, 1); return; // case 0x36: /* printf("gs: MIPTBP2_1 <- %016lx\n", data); */ gs->context[0].miptbp2 = data; gs_unpack_miptbp2(gs, 0); return; // case 0x37: /* printf("gs: MIPTBP2_2 <- %016lx\n", data); */ gs->context[1].miptbp2 = data; gs_unpack_miptbp2(gs, 1); return; // case 0x3B: /* printf("gs: TEXA <- %016lx\n", data); */ gs->texa = data; gs_unpack_texa(gs); return; // case 0x3D: /* printf("gs: FOGCOL <- %016lx\n", data); */ gs->fogcol = data; return; // case 0x3F: /* printf("gs: TEXFLUSH <- %016lx\n", data); */ gs->texflush = data; return; // 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; // 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; // case 0x42: /* printf("gs: ALPHA_1 <- %016lx\n", data); */ gs->context[0].alpha = data; gs_unpack_alpha(gs, 0); return; // case 0x43: /* printf("gs: ALPHA_2 <- %016lx\n", data); */ gs->context[1].alpha = data; gs_unpack_alpha(gs, 1); return; // case 0x44: /* printf("gs: DIMX <- %016lx\n", data); */ gs->dimx = data; gs_unpack_dimx(gs); return; // case 0x45: /* printf("gs: DTHE <- %016lx\n", data); */ gs->dthe = data; return; // case 0x46: /* printf("gs: COLCLAMP <- %016lx\n", data); */ gs->colclamp = data; return; // case 0x47: /* printf("gs: TEST_1 <- %016lx\n", data); */ gs->context[0].test = data; gs_unpack_test(gs, 0); return; // case 0x48: /* printf("gs: TEST_2 <- %016lx\n", data); */ gs->context[1].test = data; gs_unpack_test(gs, 1); return; // case 0x49: /* printf("gs: PABE <- %016lx\n", data); */ gs->pabe = data; return; // case 0x4A: /* printf("gs: FBA_1 <- %016lx\n", data); */ gs->context[0].fba = data; return; // case 0x4B: /* printf("gs: FBA_2 <- %016lx\n", data); */ gs->context[1].fba = data; return; // case 0x4C: /* printf("gs: FRAME_1 <- %016lx\n", data); */ gs->context[0].frame = data; gs_unpack_frame(gs, 0); return; // case 0x4D: /* printf("gs: FRAME_2 <- %016lx\n", data); */ gs->context[1].frame = data; gs_unpack_frame(gs, 1); return; // case 0x4E: /* printf("gs: ZBUF_1 <- %016lx\n", data); */ gs->context[0].zbuf = data; gs_unpack_zbuf(gs, 0); return; // case 0x4F: /* printf("gs: ZBUF_2 <- %016lx\n", data); */ gs->context[1].zbuf = data; gs_unpack_zbuf(gs, 1); return; // case 0x50: /* printf("gs: BITBLTBUF <- %016lx\n", data); */ gs->bitbltbuf = data; return; // case 0x51: /* printf("gs: TRXPOS <- %016lx\n", data); */ gs->trxpos = data; return; // case 0x52: /* printf("gs: TRXREG <- %016lx\n", data); */ gs->trxreg = data; return; // case 0x53: /* printf("gs: TRXDIR <- %016lx\n", data); */ gs->trxdir = data; gs->backend.transfer_start(gs, gs->backend.udata); return; // case 0x54: gs->hwreg = data; gs->backend.transfer_write(gs, gs->backend.udata); return; // case 0x60: /* printf("gs: SIGNAL <- %016lx\n", data); */ { // uint64_t mask = data >> 32; // uint64_t value = data & mask; // if (gs->csr & 1) { // gs->signal_pending++; // gs->stall_sigid = gs->siglblid & 0xffffffff; // gs->stall_sigid &= ~mask; // gs->stall_sigid |= value; // return; // } // gs->signal_pending++; // gs->signal = data; // gs->csr |= 1; // gs->siglblid &= ~mask; // gs->siglblid |= value; // gs_test_gs_irq(gs); // } return; // case 0x61: /* printf("gs: FINISH <- %016lx\n", data); */ { // // Trigger FINISH event // gs->csr |= 2; // gs_test_gs_irq(gs); // } return; // case 0x62: /* printf("gs: LABEL <- %016lx\n", data); */ { // gs->label = data; // uint64_t mask = data >> 32; // gs->siglblid &= (~mask) << 32; // gs->siglblid |= (data & mask) << 32; // } break; // default: { // // printf("gs: Invalid privileged register %02x write %016lx\n", reg, data); // return; // } // } // } // uint64_t ps2_gs_read_internal(struct ps2_gs* gs, int reg) { // switch (reg) { // case 0x00: return gs->prim; // case 0x01: return gs->rgbaq; // case 0x02: return gs->st; // case 0x03: return gs->uv; // case 0x04: return gs->xyzf2; // case 0x05: return gs->xyz2; // case 0x06: return gs->context[0].tex0; // case 0x07: return gs->context[1].tex0; // case 0x08: return gs->context[0].clamp; // case 0x09: return gs->context[1].clamp; // case 0x0A: return gs->fog; // case 0x0C: return gs->xyzf3; // case 0x0D: return gs->xyz3; // case 0x14: return gs->context[0].tex1; // case 0x15: return gs->context[1].tex1; // case 0x16: return gs->context[0].tex2; // case 0x17: return gs->context[1].tex2; // case 0x18: return gs->context[0].xyoffset; // case 0x19: return gs->context[1].xyoffset; // case 0x1A: return gs->prmodecont; // case 0x1B: return gs->prmode; // case 0x1C: return gs->texclut; // case 0x22: return gs->scanmsk; // case 0x34: return gs->context[0].miptbp1; // case 0x35: return gs->context[1].miptbp1; // case 0x36: return gs->context[0].miptbp2; // case 0x37: return gs->context[1].miptbp2; // case 0x3B: return gs->texa; // case 0x3D: return gs->fogcol; // case 0x3F: return gs->texflush; // case 0x40: return gs->context[0].scissor; // case 0x41: return gs->context[1].scissor; // case 0x42: return gs->context[0].alpha; // case 0x43: return gs->context[1].alpha; // case 0x44: return gs->dimx; // case 0x45: return gs->dthe; // case 0x46: return gs->colclamp; // case 0x47: return gs->context[0].test; // case 0x48: return gs->context[1].test; // case 0x49: return gs->pabe; // case 0x4A: return gs->context[0].fba; // case 0x4B: return gs->context[1].fba; // case 0x4C: return gs->context[0].frame; // case 0x4D: return gs->context[1].frame; // case 0x4E: return gs->context[0].zbuf; // case 0x4F: return gs->context[1].zbuf; // case 0x50: return gs->bitbltbuf; // case 0x51: return gs->trxpos; // case 0x52: return gs->trxreg; // case 0x53: return gs->trxdir; // case 0x54: gs->backend.transfer_read(gs, gs->backend.udata); return gs->hwreg; // case 0x60: return gs->signal; // case 0x61: return gs->finish; // case 0x62: return gs->label; // default: { // fprintf(stderr, "gs: Invalid privileged register %02x read\n", reg); // exit(1); // } // } // return 0; // } void gs_get_privileged_state(struct ps2_gs* gs, struct gs_privileged_state* state) { state->pmode = gs->pmode; state->smode1 = gs->smode1; state->smode2 = gs->smode2; state->srfsh = gs->srfsh; state->synch1 = gs->synch1; state->synch2 = gs->synch2; state->syncv = gs->syncv; state->dispfb1 = gs->dispfb1; state->display1 = gs->display1; state->dispfb2 = gs->dispfb2; state->display2 = gs->display2; state->extbuf = gs->extbuf; state->extdata = gs->extdata; state->extwrite = gs->extwrite; state->bgcolor = gs->bgcolor; state->csr = gs->csr; state->imr = gs->imr; state->busdir = gs->busdir; state->siglblid = gs->siglblid; } int ps2_gs_is_vblank(struct ps2_gs* gs) { return gs->vblank; } int ps2_gs_write_signal(struct ps2_gs* gs, uint64_t data) { uint64_t mask = data >> 32; uint64_t value = data & mask; if (gs->csr & 1) { gs->signal_pending++; gs->stall_sigid = gs->siglblid & 0xffffffff; gs->stall_sigid &= ~mask; gs->stall_sigid |= value; return 1; } gs->signal_pending++; gs->signal = data; gs->csr |= 1; gs->siglblid &= ~mask; gs->siglblid |= value; gs_test_gs_irq(gs); return 0; } int ps2_gs_write_finish(struct ps2_gs* gs, uint64_t data) { // Trigger FINISH event gs->csr |= 2; gs_test_gs_irq(gs); return 1; } int ps2_gs_write_label(struct ps2_gs* gs, uint64_t data) { gs->label = data; uint64_t mask = data >> 32; gs->siglblid &= (~mask) << 32; gs->siglblid |= (data & mask) << 32; return 0; } ================================================ FILE: src/gs/gs.h ================================================ #ifndef GS_H #define GS_H #ifdef __cplusplus extern "C" { #endif #include #include "u128.h" #include "scheduler.h" #include "ee/timers.h" #include "iop/timers.h" #define GS_PRIM 0x00 #define GS_RGBAQ 0x01 #define GS_ST 0x02 #define GS_UV 0x03 #define GS_XYZF2 0x04 #define GS_XYZ2 0x05 #define GS_TEX0_1 0x06 #define GS_TEX0_2 0x07 #define GS_CLAMP_1 0x08 #define GS_CLAMP_2 0x09 #define GS_FOG 0x0A #define GS_XYZF3 0x0C #define GS_XYZ3 0x0D #define GS_TEX1_1 0x14 #define GS_TEX1_2 0x15 #define GS_TEX2_1 0x16 #define GS_TEX2_2 0x17 #define GS_XYOFFSET_1 0x18 #define GS_XYOFFSET_2 0x19 #define GS_PRMODECONT 0x1A #define GS_PRMODE 0x1B #define GS_TEXCLUT 0x1C #define GS_SCANMSK 0x22 #define GS_MIPTBP1_1 0x34 #define GS_MIPTBP1_2 0x35 #define GS_MIPTBP2_1 0x36 #define GS_MIPTBP2_2 0x37 #define GS_TEXA 0x3B #define GS_FOGCOL 0x3D #define GS_TEXFLUSH 0x3F #define GS_SCISSOR_1 0x40 #define GS_SCISSOR_2 0x41 #define GS_ALPHA_1 0x42 #define GS_ALPHA_2 0x43 #define GS_DIMX 0x44 #define GS_DTHE 0x45 #define GS_COLCLAMP 0x46 #define GS_TEST_1 0x47 #define GS_TEST_2 0x48 #define GS_PABE 0x49 #define GS_FBA_1 0x4A #define GS_FBA_2 0x4B #define GS_FRAME_1 0x4C #define GS_FRAME_2 0x4D #define GS_ZBUF_1 0x4E #define GS_ZBUF_2 0x4F #define GS_BITBLTBUF 0x50 #define GS_TRXPOS 0x51 #define GS_TRXREG 0x52 #define GS_TRXDIR 0x53 #define GS_HWREG 0x54 #define GS_SIGNAL 0x60 #define GS_FINISH 0x61 #define GS_LABEL 0x62 // TCC #define GS_RGB 0 #define GS_RGBA 1 #define GS_IIP (1 << 3) // int0:1:0 Shading Method #define GS_TME (1 << 4) // int0:1:0 Texture Mapping #define GS_FGE (1 << 5) // int0:1:0 Fogging #define GS_ABE (1 << 6) // int0:1:0 Alpha Blending #define GS_AA1 (1 << 7) // int0:1:0 1 Pass Antialiasing (*1) #define GS_FST (1 << 8) // int0:1:0 Method of Specifying Texture Coordinates (*2) #define GS_CTXT (1 << 9) // int0:1:0 Context #define GS_FIX (1 << 10) // int0:1:0 Fragment Value Control (RGBAFSTQ Change by DDA) // Framebuffer/Pixel formats #define GS_PSMCT32 0x00 #define GS_PSMCT24 0x01 #define GS_PSMCT16 0x02 #define GS_PSMCT16S 0x0a #define GS_PSMZ32 0x30 #define GS_PSMZ24 0x31 #define GS_PSMZ16 0x32 #define GS_PSMZ16S 0x3a #define GS_PSMT8 0x13 #define GS_PSMT4 0x14 #define GS_PSMT8H 0x1b #define GS_PSMT4HL 0x24 #define GS_PSMT4HH 0x2c // Z buffer formats #define GS_ZSMZ32 0x00 #define GS_ZSMZ24 0x01 #define GS_ZSMZ16 0x02 #define GS_ZSMZ16S 0x0a // Texture function #define GS_MODULATE 0 #define GS_DECAL 1 #define GS_HIGHLIGHT 2 #define GS_HIGHLIGHT2 3 // Timings #define GS_FRAME_SCANS_NTSC 240 #define GS_VBLANK_SCANS_NTSC 22 #define GS_SCANLINE_NTSC 9370 #define GS_FRAME_SCANS_PAL 286 #define GS_VBLANK_SCANS_PAL 26 #define GS_SCANLINE_PAL 9476 // EE clock: 294.912 MHz, 294912000 clocks/s // 294912000/60=4915200 clocks/frame // #define GS_FRAME_NTSC (4497600) // (240 * 9370) // #define GS_VBLANK_NTSC (417600) // (22 * 9370) #define GS_FRAME_NTSC 4489019 // (240 * 9370) #define GS_VBLANK_NTSC 431096 // (22 * 9370) #define GS_FRAME_PAL (286 * 9476) #define GS_VBLANK_PAL (26 * 9476) struct ps2_gs; struct gs_vertex { uint64_t rgbaq; uint64_t xyz; uint64_t st; uint64_t uv; uint64_t fog; // Cached fields uint32_t r; uint32_t g; uint32_t b; uint32_t a; int32_t x; int32_t y; uint32_t z; uint32_t u; uint32_t v; float s; float t; float q; }; struct gs_callback { void (*func)(void*); void* udata; }; struct gs_context { uint64_t frame; // (FRAME_1, FRAME_2) uint64_t zbuf; // (ZBUF_1, ZBUF_2) uint64_t tex0; // (TEX0_1, TEX0_2) uint64_t tex1; // (TEX1_1, TEX1_2) uint64_t tex2; // (TEX2_1, TEX2_2) uint64_t miptbp1; // (MIPTBP1_1, MIPTBP1_2) uint64_t miptbp2; // (MIPTBP2_1, MIPTBP2_2) uint64_t clamp; // (CLAMP_1, CLAMP_2) uint64_t test; // (TEST_1, TEST_2) uint64_t alpha; // (ALPHA_1, ALPHA_2) uint64_t xyoffset; // (XYOFFSET_1, XYOFFSET_2) uint64_t scissor; // (SCISSOR_1, SCISSOR_2) uint64_t fba; // (FBA_1, FBA_2) // Cached fields // FRAME uint32_t fbp; uint32_t fbw; uint32_t fbpsm; uint32_t fbmsk; // ZBUF uint32_t zbp; uint32_t zbpsm; uint32_t zbmsk; // TEX0 uint32_t tbp0; uint32_t tbw; uint32_t tbpsm; uint32_t usize; uint32_t vsize; uint32_t tcc; uint32_t tfx; uint32_t cbp; uint32_t cbpsm; uint32_t csm; uint32_t csa; uint32_t cld; // TEX1 uint32_t lcm; uint32_t mxl; uint32_t mmag; uint32_t mmin; uint32_t mtba; uint32_t l; uint32_t k; // MIPTBP1/2 uint32_t mmtbp[6]; uint32_t mmtbw[6]; // CLAMP uint32_t wms; uint32_t wmt; uint32_t minu; uint32_t maxu; uint32_t minv; uint32_t maxv; // TEST uint32_t ate; uint32_t atst; uint32_t aref; uint32_t afail; uint32_t date; uint32_t datm; uint32_t zte; uint32_t ztst; // ALPHA uint32_t a; uint32_t b; uint32_t c; uint32_t d; uint32_t fix; // XYOFFSET uint32_t ofx; uint32_t ofy; // SCISSOR uint32_t scax0; uint32_t scax1; uint32_t scay0; uint32_t scay1; }; #define GS_EVENT_VBLANK 0 #define GS_EVENT_SCISSOR 1 struct ps2_gs { uint32_t* vram; int vblank; // SIGNAL stuff int signal_pending; int signal_stall; uint32_t stall_sigid; // 1KB CLUT cache uint32_t clut_cache[0x100]; uint32_t cbp0; uint32_t cbp1; uint32_t attr; struct gs_context context[2]; struct gs_context* ctx; // Privileged registers uint64_t pmode; uint64_t smode1; uint64_t smode2; uint64_t srfsh; uint64_t synch1; uint64_t synch2; uint64_t syncv; uint64_t dispfb1; uint64_t display1; uint64_t dispfb2; uint64_t display2; uint64_t extbuf; uint64_t extdata; uint64_t extwrite; uint64_t bgcolor; uint64_t csr; uint64_t imr; uint64_t busdir; uint64_t siglblid; uint64_t csr_enable; // Internal registers uint64_t prim; uint64_t rgbaq; uint64_t st; uint64_t uv; uint64_t xyzf2; uint64_t xyz2; uint64_t fog; uint64_t xyzf3; uint64_t xyz3; uint64_t prmodecont; uint64_t prmode; uint64_t texclut; uint64_t scanmsk; uint64_t texa; uint64_t fogcol; uint64_t texflush; uint64_t dimx; uint64_t dthe; uint64_t colclamp; uint64_t pabe; uint64_t bitbltbuf; uint64_t trxpos; uint64_t trxreg; uint64_t trxdir; uint64_t hwreg; uint64_t signal; uint64_t finish; uint64_t label; // Drawing data struct gs_vertex vq[4]; unsigned int vqi; // Cached fields int iip; int tme; int fge; int abe; int aa1; int fst; int ctxt; int fix; // TEXCLUT uint32_t cbw; uint32_t cou; uint32_t cov; // TEXA int aem; uint32_t ta0; uint32_t ta1; // DISPFB1/2 uint32_t dfbp1; uint32_t dfbw1; uint32_t dfbpsm1; uint32_t dfbp2; uint32_t dfbw2; uint32_t dfbpsm2; // DIMX int dither[4][4]; struct sched_state* sched; struct ps2_intc* ee_intc; struct ps2_iop_intc* iop_intc; struct ps2_ee_timers* ee_timers; struct ps2_iop_timers* iop_timers; }; struct ps2_gs* ps2_gs_create(void); void 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); void ps2_gs_reset(struct ps2_gs* gs); void ps2_gs_destroy(struct ps2_gs* gs); uint64_t ps2_gs_read64(struct ps2_gs* gs, uint32_t addr); void ps2_gs_write64(struct ps2_gs* gs, uint32_t addr, uint64_t data); int ps2_gs_is_vblank(struct ps2_gs* gs); struct gs_privileged_state { uint64_t pmode; uint64_t smode1; uint64_t smode2; uint64_t srfsh; uint64_t synch1; uint64_t synch2; uint64_t syncv; uint64_t dispfb1; uint64_t display1; uint64_t dispfb2; uint64_t display2; uint64_t extbuf; uint64_t extdata; uint64_t extwrite; uint64_t bgcolor; uint64_t csr; uint64_t imr; uint64_t busdir; uint64_t siglblid; }; void gs_get_privileged_state(struct ps2_gs* gs, struct gs_privileged_state* state); int ps2_gs_write_signal(struct ps2_gs* gs, uint64_t data); int ps2_gs_write_finish(struct ps2_gs* gs, uint64_t data); int ps2_gs_write_label(struct ps2_gs* gs, uint64_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/gs/renderer/config.hpp ================================================ #pragma once struct hardware_config { int super_sampling = 0; bool force_progressive = false; bool overscan = false; bool crtc_offsets = false; bool disable_mipmaps = false; bool unsynced_readbacks = false; bool backbuffer_promotion = false; bool allow_blend_demote = false; }; ================================================ FILE: src/gs/renderer/hardware.cpp ================================================ #include "hardware.hpp" void* hardware_create() { return new hardware_state(); } bool hardware_init(void* udata, const renderer_create_info& info) { hardware_state* ctx = static_cast(udata); if (!Context::init_loader(nullptr)) return false; ctx->gs = info.gs; ctx->gif = info.gif; ctx->instance = new ExternallyManagedInstance(info.instance, info.instance_create_info); ctx->device = new ExternallyManagedDevice(info.device, info.device_create_info); ctx->signal_handler = new RendererSignalHandler(ctx->gs); ctx->granite_ctx.set_instance_factory(ctx->instance); ctx->granite_ctx.set_device_factory(ctx->device); ctx->granite_ctx.set_num_thread_indices(1); // We don't need to pass any extensions or layers because the // VkInstance is managed/created externally. if (!ctx->granite_ctx.init_instance(nullptr, 0)) { fprintf(stderr, "renderer: Failed to initialize Granite instance\n"); return false; } if (!ctx->granite_ctx.init_device(info.physical_device, VK_NULL_HANDLE, nullptr, 0)) { fprintf(stderr, "renderer: Failed to initialize Granite device\n"); return false; } ctx->granite_device.set_context(ctx->granite_ctx); ctx->granite_device.init_frame_contexts(4); GSOptions opts = {}; opts.super_sampling = (SuperSampling)ctx->config.super_sampling; opts.ordered_super_sampling = true; opts.super_sampled_textures = true; opts.dynamic_super_sampling = true; if (!ctx->interface.init(&ctx->granite_device, opts)) { return false; } ctx->interface.reset_context_state(); ctx->interface.set_signal_interface(ctx->signal_handler); ctx->config = *(hardware_config*)info.config; Hacks hacks = {}; hacks.allow_blend_demote = ctx->config.allow_blend_demote; hacks.backbuffer_promotion = ctx->config.backbuffer_promotion; hacks.disable_mipmaps = ctx->config.disable_mipmaps; hacks.unsynced_readbacks = ctx->config.unsynced_readbacks; ctx->interface.set_hacks(hacks); if (ctx->config.super_sampling == 0) { ctx->interface.set_super_sampling_rate((SuperSampling)0, false, false); } else { SuperSampling super_sampling; switch (ctx->config.super_sampling) { case 1: super_sampling = SuperSampling::X2; break; case 2: super_sampling = SuperSampling::X4; break; case 3: super_sampling = SuperSampling::X8; break; case 4: super_sampling = SuperSampling::X16; break; default: super_sampling = (SuperSampling)0; break; } ctx->interface.set_super_sampling_rate(super_sampling, true, true); } return true; } void hardware_reset(void* udata) { hardware_state* ctx = static_cast(udata); // ctx->interface.flush(); ctx->interface.reset_context_state(); // Clear VRAM void* ptr = ctx->interface.map_vram_write(0, 0x400000); memset(ptr, 0, 0x400000); ctx->interface.end_vram_write(0, 0x400000); } void hardware_destroy(void* udata) { hardware_state* ctx = static_cast(udata); delete ctx->instance; delete ctx->device; delete ctx->signal_handler; delete ctx; } renderer_image hardware_get_frame(void* udata) { hardware_state* ctx = static_cast(udata); struct gs_privileged_state state; gs_get_privileged_state(ctx->gs, &state); if (!state.pmode) { // No display enabled. renderer_image image = {}; image.image = VK_NULL_HANDLE; image.view = VK_NULL_HANDLE; return image; } auto& priv = ctx->interface.get_priv_register_state(); *((uint64_t*)&priv.pmode) = state.pmode; *((uint64_t*)&priv.smode1) = state.smode1; *((uint64_t*)&priv.smode2) = state.smode2; *((uint64_t*)&priv.srfsh) = state.srfsh; *((uint64_t*)&priv.synch1) = state.synch1; *((uint64_t*)&priv.synch2) = state.synch2; *((uint64_t*)&priv.syncv) = state.syncv; *((uint64_t*)&priv.dispfb1) = state.dispfb1; *((uint64_t*)&priv.display1) = state.display1; *((uint64_t*)&priv.dispfb2) = state.dispfb2; *((uint64_t*)&priv.display2) = state.display2; *((uint64_t*)&priv.extbuf) = state.extbuf; *((uint64_t*)&priv.extdata) = state.extdata; *((uint64_t*)&priv.extwrite) = state.extwrite; *((uint64_t*)&priv.bgcolor) = state.bgcolor; *((uint64_t*)&priv.csr) = state.csr; *((uint64_t*)&priv.imr) = state.imr; *((uint64_t*)&priv.busdir) = state.busdir; *((uint64_t*)&priv.siglblid) = state.siglblid; ctx->interface.flush(); VSyncInfo info = {}; info.phase = ctx->gs->csr & (1 << 13) ? 0 : 1; if (ctx->config.super_sampling) { info.raw_circuit_scanout = true; info.high_resolution_scanout = true; info.anti_blur = true; } else { info.raw_circuit_scanout = false; info.high_resolution_scanout = false; info.anti_blur = false; } info.force_progressive = ctx->config.force_progressive; info.overscan = ctx->config.overscan; info.crtc_offsets = ctx->config.crtc_offsets; info.dst_access = VK_ACCESS_2_SHADER_SAMPLED_READ_BIT; info.dst_stage = VK_PIPELINE_STAGE_FRAGMENT_SHADER_BIT; info.dst_layout = VK_IMAGE_LAYOUT_SHADER_READ_ONLY_OPTIMAL; info.adapt_to_internal_horizontal_resolution = false; ScanoutResult scanout = ctx->interface.vsync(info); Image* granite_image = scanout.image.get(); renderer_image image; image.image = granite_image->get_image(); image.width = granite_image->get_width(); image.height = granite_image->get_height(); image.format = granite_image->get_format(); image.view = granite_image->get_view().get_view().view; return image; } extern "C" void hardware_transfer(void* udata, int path, const void* data, size_t size) { hardware_state* ctx = static_cast(udata); ctx->interface.gif_transfer(path, data, size); } void hardware_set_config(void* udata, void* config) { hardware_state* ctx = (hardware_state*)udata; ctx->config = *(hardware_config*)config; Hacks hacks = {}; hacks.allow_blend_demote = ctx->config.allow_blend_demote; hacks.backbuffer_promotion = ctx->config.backbuffer_promotion; hacks.disable_mipmaps = ctx->config.disable_mipmaps; hacks.unsynced_readbacks = ctx->config.unsynced_readbacks; ctx->interface.set_hacks(hacks); if (ctx->config.super_sampling == 0) { ctx->interface.set_super_sampling_rate((SuperSampling)0, false, false); } else { SuperSampling super_sampling; switch (ctx->config.super_sampling) { case 1: super_sampling = SuperSampling::X2; break; case 2: super_sampling = SuperSampling::X4; break; case 3: super_sampling = SuperSampling::X8; break; case 4: super_sampling = SuperSampling::X16; break; default: super_sampling = (SuperSampling)0; break; } ctx->interface.set_super_sampling_rate(super_sampling, true, true); } } ================================================ FILE: src/gs/renderer/hardware.hpp ================================================ #pragma once #include #include #include #include "renderer.hpp" #include "Granite/vulkan/device.hpp" #include "Granite/vulkan/context.hpp" #include "gs_renderer.hpp" #include "gs_interface.hpp" using namespace Vulkan; using namespace ParallelGS; class ExternallyManagedDevice : public DeviceFactory { VkDevice m_device = nullptr; VkDeviceCreateInfo m_create_info; public: // Device is externally managed ExternallyManagedDevice(VkDevice device, VkDeviceCreateInfo create_info) : m_device(device), m_create_info(create_info) {}; // Create a device ourselves (unused) ExternallyManagedDevice() = delete; virtual ~ExternallyManagedDevice() override = default; VkDevice create_device(VkPhysicalDevice gpu, const VkDeviceCreateInfo *info) override { return m_device; } const VkDeviceCreateInfo *get_existing_create_info() override { return &m_create_info; } bool factory_owns_created_device() override { return true; } }; class ExternallyManagedInstance : public InstanceFactory { VkInstanceCreateInfo m_create_info = {}; VkInstance m_instance = nullptr; public: // Instance is externally managed ExternallyManagedInstance(VkInstance instance, VkInstanceCreateInfo create_info) : m_instance(instance), m_create_info(create_info) {}; // Create an instance ourselves (unused) ExternallyManagedInstance() = delete; ~ExternallyManagedInstance() override = default; VkInstance create_instance(const VkInstanceCreateInfo *info) override { if (m_instance) return m_instance; // Create a new instance, I won't implement this return m_instance; } const VkInstanceCreateInfo* get_existing_create_info() override { return &m_create_info; } bool factory_owns_created_instance() override { return true; } }; class RendererSignalHandler : public SignalInterface { struct ps2_gs* m_gs; public: virtual ~RendererSignalHandler() override = default; RendererSignalHandler(struct ps2_gs* gs) : m_gs(gs) {} virtual bool on_signal(uint64_t payload) override { return ps2_gs_write_signal(m_gs, payload); } virtual bool on_finish(uint64_t payload) override { return ps2_gs_write_finish(m_gs, payload); } virtual bool on_label(uint64_t payload) override { return ps2_gs_write_label(m_gs, payload);; } }; struct hardware_state { Vulkan::Context granite_ctx; Vulkan::Device granite_device; GSInterface interface; ExternallyManagedDevice* device; ExternallyManagedInstance* instance; RendererSignalHandler* signal_handler; hardware_config config; struct ps2_gs* gs; struct ps2_gif* gif; }; void* hardware_create(); bool hardware_init(void* udata, const renderer_create_info& info); void hardware_reset(void* udata); void hardware_destroy(void* udata); void hardware_set_config(void* udata, void* config); renderer_image hardware_get_frame(void* udata); extern "C" { void hardware_transfer(void* udata, int path, const void* data, size_t size); } ================================================ FILE: src/gs/renderer/null.cpp ================================================ #include "renderer.hpp" #include "null.hpp" void* null_create() { return nullptr; } bool null_init(void* udata, const renderer_create_info& info) { return true; } void null_reset(void* udata) { // Nothing } void null_destroy(void* udata) { // Nothing } renderer_image null_get_frame(void* udata) { renderer_image image = {}; image.image = VK_NULL_HANDLE; image.view = VK_NULL_HANDLE; return image; } void null_set_config(void* udata, void* config) { // Nothing } extern "C" { void null_transfer(void* udata, int path, const void* data, size_t size) { // Do nothing } } ================================================ FILE: src/gs/renderer/null.hpp ================================================ #pragma once #include #include #include #include "renderer.hpp" void* null_create(); bool null_init(void* udata, const renderer_create_info& info); void null_reset(void* udata); void null_destroy(void* udata); void null_set_config(void* udata, void* config); renderer_image null_get_frame(void* udata); extern "C" { void null_transfer(void* udata, int path, const void* data, size_t size); } ================================================ FILE: src/gs/renderer/renderer.cpp ================================================ #include "renderer.hpp" #include "null.hpp" #include "hardware.hpp" renderer_state* renderer_create(void) { return new renderer_state; } bool renderer_init(renderer_state* renderer, const renderer_create_info& info) { renderer->info = info; switch (info.backend) { case RENDERER_BACKEND_NULL: { renderer->create = null_create; renderer->init = null_init; renderer->reset = null_reset; renderer->destroy = null_destroy; renderer->get_frame = null_get_frame; renderer->set_config = null_set_config; renderer->transfer = null_transfer; } break; case RENDERER_BACKEND_SOFTWARE: { // To-do: Software renderer renderer->create = null_create; renderer->init = null_init; renderer->reset = null_reset; renderer->destroy = null_destroy; renderer->get_frame = null_get_frame; renderer->set_config = null_set_config; renderer->transfer = null_transfer; } break; case RENDERER_BACKEND_HARDWARE: { renderer->create = hardware_create; renderer->init = hardware_init; renderer->reset = hardware_reset; renderer->destroy = hardware_destroy; renderer->get_frame = hardware_get_frame; renderer->set_config = hardware_set_config; renderer->transfer = hardware_transfer; } break; } renderer->udata = renderer->create(); ps2_gif_set_backend(info.gif, renderer->udata, renderer->transfer); return renderer->init(renderer->udata, info); } bool renderer_switch(renderer_state* renderer, int backend, void* config) { if (backend == renderer->info.backend) return true; renderer->destroy(renderer->udata); renderer_create_info info = renderer->info; info.backend = backend; info.config = config; return renderer_init(renderer, info); } void renderer_destroy(renderer_state* renderer) { renderer->destroy(renderer->udata); delete renderer; } void renderer_reset(renderer_state* renderer) { renderer->reset(renderer->udata); } renderer_image renderer_get_frame(renderer_state* renderer) { return renderer->get_frame(renderer->udata); } void renderer_set_config(renderer_state* renderer, void* config) { renderer->set_config(renderer->udata, config); } ================================================ FILE: src/gs/renderer/renderer.hpp ================================================ #pragma once #include "Granite/vulkan/vulkan_headers.hpp" #include #include "gs/gs.h" #include "config.hpp" // enum : int { // // Keeps aspect ratio by native resolution // RENDERER_ASPECT_NATIVE, // // Stretch to window (disregard aspect, disregard scale) // RENDERER_ASPECT_STRETCH, // // Stretch to window (keep aspect, disregard scale) // RENDERER_ASPECT_STRETCH_KEEP, // // Force 4:3 // RENDERER_ASPECT_4_3, // // Force 16:9 // RENDERER_ASPECT_16_9, // // Force 5:4 (PAL) // RENDERER_ASPECT_5_4, // // Use NVRAM settings (same as SOFTWARE_ASPECT_STRETCH_KEEP for now) // RENDERER_ASPECT_AUTO // }; enum : int { RENDERER_BACKEND_NULL = 0, RENDERER_BACKEND_SOFTWARE, RENDERER_BACKEND_HARDWARE }; struct renderer_stats { unsigned int primitives = 0; unsigned int triangles = 0; unsigned int lines = 0; unsigned int points = 0; unsigned int sprites = 0; unsigned int texture_uploads = 0; unsigned int texture_blits = 0; unsigned int frames_rendered = 0; }; /* An Iris renderer consists of two APIs, a backend API that receives GIF transfers from the emulation core, and a frontend API that serves frames to our frontend Backend API - render_back_transfer() Frontend API - render_front_get_frame() */ struct renderer_create_info { struct ps2_gif* gif; struct ps2_gs* gs; VkInstance instance; VkInstanceCreateInfo instance_create_info; VkDevice device; VkDeviceCreateInfo device_create_info; VkPhysicalDevice physical_device; void* config; int backend; }; struct renderer_image { VkImage image; VkImageView view; VkFormat format; unsigned int width; unsigned int height; }; struct renderer_state { struct ps2_gif* gif = nullptr; void* udata = nullptr; renderer_create_info info = {}; void* (*create)(); bool (*init)(void* udata, const renderer_create_info& info); void (*reset)(void* udata); void (*destroy)(void* udata); renderer_image (*get_frame)(void* udata); void (*transfer)(void* udata, int path, const void* data, size_t size); void (*set_config)(void* udata, void* config); }; renderer_state* renderer_create(void); bool renderer_init(renderer_state* renderer, const renderer_create_info& info); bool renderer_switch(renderer_state* renderer, int backend, void* config); void renderer_reset(renderer_state* renderer); void renderer_destroy(renderer_state* renderer); void renderer_set_config(renderer_state* renderer, void* config); renderer_image renderer_get_frame(renderer_state* renderer); ================================================ FILE: src/gs/renderer/software_thread.cpp ================================================ #include #include #include #include #include "gs/gs.h" #include "software_thread.hpp" // INCBIN stuff #define INCBIN_PREFIX g_ #define INCBIN_STYLE INCBIN_STYLE_SNAKE #include "incbin.h" #ifdef __APPLE__ #define SHADER_FORMAT SDL_GPU_SHADERFORMAT_MSL #define SHADER_ENTRYPOINT "main0" INCBIN(vertex_shader, "../shaders/vertex.msl"); INCBIN(fragment_shader, "../shaders/fragment.msl"); INCBIN_EXTERN(vertex_shader); INCBIN_EXTERN(fragment_shader); #else #define SHADER_FORMAT SDL_GPU_SHADERFORMAT_SPIRV #define SHADER_ENTRYPOINT "main" INCBIN(vertex_shader, "../shaders/vertex.spv"); INCBIN(fragment_shader, "../shaders/fragment.spv"); INCBIN_EXTERN(vertex_shader); INCBIN_EXTERN(fragment_shader); #endif #include int psmct32_block[] = { 0 , 1 , 4 , 5 , 16, 17, 20, 21, 2 , 3 , 6 , 7 , 18, 19, 22, 23, 8 , 9 , 12, 13, 24, 25, 28, 29, 10, 11, 14, 15, 26, 27, 30, 31 }; int psmct32_column[] = { 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 2 , 3 , 6 , 7 , 10, 11, 14, 15 }; static inline int psmct32_addr(int base, int width, int x, int y) { // base expressed in blocks // 1 page = 64x32 = 8 KiB = 2048 words // 1 block = 8x8 = 256 B = 64 words // 1 column = 8x2 = 64 B = 16 words int page = (x >> 6) + ((y >> 5) * width); int blkx = (x >> 3) & 7; int blky = (y >> 3) & 3; int blk = blkx + (blky * 8); int col = (y >> 1) & 3; int idx = (x & 7) + ((y & 1) * 8); // Unswizzle block blk = psmct32_block[blk]; // Unswizzle column idx = psmct32_column[idx]; // printf("(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\n", // base, width, x, y, // page, blk, cl, // (page * 2048) + (base * 64) + (blk * 64) + cl // ); return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx; } int psmz32_block[] = { 24, 25, 28, 29, 8 , 9 , 12, 13, 26, 27, 30, 31, 10, 11, 14, 15, 16, 17, 20, 21, 0 , 1 , 4 , 5 , 18, 19, 22, 23, 2 , 3 , 6 , 7 }; static inline int psmz32_addr(int base, int width, int x, int y) { // base expressed in blocks // 1 page = 64x32 = 8 KiB = 2048 words // 1 block = 8x8 = 256 B = 64 words // 1 column = 8x2 = 64 B = 16 words int page = (x >> 6) + ((y >> 5) * width); int blkx = (x >> 3) & 7; int blky = (y >> 3) & 3; int blk = blkx + (blky * 8); int col = (y >> 1) & 3; int idx = (x & 7) + ((y & 1) * 8); // Unswizzle block blk = psmz32_block[blk]; // Unswizzle column idx = psmct32_column[idx]; // printf("(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\n", // base, width, x, y, // page, blk, cl, // (page * 2048) + (base * 64) + (blk * 64) + cl // ); return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx; } int psmct16_block[] = { 0 , 2 , 8 , 10, 1 , 3 , 9 , 11, 4 , 6 , 12, 14, 5 , 7 , 13, 15, 16, 18, 24, 26, 17, 19, 25, 27, 20, 22, 28, 30, 21, 23, 29, 31 }; int psmz16_block[] = { 24, 26, 16, 18, 25, 27, 17, 19, 28, 30, 20, 22, 29, 31, 21, 23, 8 , 10, 0 , 2 , 9 , 11, 1 , 3 , 12, 14, 4 , 6 , 13, 15, 5 , 7 }; int psmct16_column[] = { 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15 }; int psmct16_shift[] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 }; static inline int psmct16_addr(int base, int width, int x, int y) { // page block column // 64 x 64 pixels 16 x 8 pixels 16 x 2 pixels // base expressed in blocks // 1 page = 64x32 = 8 KiB = 2048 words // 1 block = 8x8 = 256 B = 64 words // 1 column = 8x2 = 64 B = 16 words int page = (x >> 6) + ((y >> 6) * width); int blkx = (x >> 4) & 3; int blky = (y >> 3) & 7; int blk = blkx + (blky * 4); int col = (y >> 1) & 3; int idx = (x & 15) + ((y & 1) * 16); // Unswizzle block blk = psmct16_block[blk]; // Unswizzle column idx = psmct16_column[idx]; // printf("(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\n", // base, width, x, y, // page, blk, cl, // (page * 2048) + (base * 64) + (blk * 64) + cl // ); return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx; } static inline int psmz16_addr(int base, int width, int x, int y) { // page block column // 64 x 64 pixels 16 x 8 pixels 16 x 2 pixels // base expressed in blocks // 1 page = 64x32 = 8 KiB = 2048 words // 1 block = 8x8 = 256 B = 64 words // 1 column = 8x2 = 64 B = 16 words int page = (x >> 6) + ((y >> 6) * width); int blkx = (x >> 4) & 3; int blky = (y >> 3) & 7; int blk = blkx + (blky * 4); int col = (y >> 1) & 3; int idx = (x & 15) + ((y & 1) * 16); // Unswizzle block blk = psmz16_block[blk]; // Unswizzle column idx = psmct16_column[idx]; // printf("(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\n", // base, width, x, y, // page, blk, cl, // (page * 2048) + (base * 64) + (blk * 64) + cl // ); return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx; } int psmct16s_block[] = { 0 , 2 , 16, 18, 1 , 3 , 17, 19, 8 , 10, 24, 26, 9 , 11, 25, 27, 4 , 6 , 20, 22, 5 , 7 , 21, 23, 12, 14, 28, 30, 13, 15, 29, 31 }; int psmz16s_block[] = { 24, 26, 8 , 10, 25, 27, 9 , 11, 16, 18, 0 , 2 , 17, 19, 1 , 3 , 28, 30, 12, 14, 29, 31, 13, 15, 20, 22, 4 , 6 , 21, 23, 5 , 7 }; static inline int psmct16s_addr(int base, int width, int x, int y) { // page block column // 64 x 64 pixels 16 x 8 pixels 16 x 2 pixels // base expressed in blocks // 1 page = 64x32 = 8 KiB = 2048 words // 1 block = 8x8 = 256 B = 64 words // 1 column = 8x2 = 64 B = 16 words int page = (x >> 6) + ((y >> 6) * width); int blkx = (x >> 4) & 3; int blky = (y >> 3) & 7; int blk = blkx + (blky * 4); int col = (y >> 1) & 3; int idx = (x & 15) + ((y & 1) * 16); // Unswizzle block blk = psmct16s_block[blk]; // Unswizzle column idx = psmct16_column[idx]; // printf("(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\n", // base, width, x, y, // page, blk, cl, // (page * 2048) + (base * 64) + (blk * 64) + cl // ); return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx; } static inline int psmz16s_addr(int base, int width, int x, int y) { // page block column // 64 x 64 pixels 16 x 8 pixels 16 x 2 pixels // base expressed in blocks // 1 page = 64x32 = 8 KiB = 2048 words // 1 block = 8x8 = 256 B = 64 words // 1 column = 8x2 = 64 B = 16 words int page = (x >> 6) + ((y >> 6) * width); int blkx = (x >> 4) & 3; int blky = (y >> 3) & 7; int blk = blkx + (blky * 4); int col = (y >> 1) & 3; int idx = (x & 15) + ((y & 1) * 16); // Unswizzle block blk = psmct16s_block[blk]; // Unswizzle column idx = psmct16_column[idx]; // printf("(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\n", // base, width, x, y, // page, blk, cl, // (page * 2048) + (base * 64) + (blk * 64) + cl // ); return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx; } int psmt8_column_02[] = { 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 }; int psmt8_column_13[] = { 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15 }; int psmt8_shift[] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 2 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 1 , 3 , 3 , 3 , 3 , 3 , 3 , 3 , 3 }; static inline int psmt8_addr(int base, int width, int x, int y) { // page block column // 128 x 64 pixels 16 x 16 pixels 16 x 4 pixels // base expressed in blocks // 1 page = 128x64 = 8 KiB = 2048 words // 1 block = 16x16 = 256 B = 64 words // 1 column = 16x4 = 64 B = 16 words // 1 page = 128/4x64 = 32x64 words // 1 block = 16/4x16 = 4x16 words // 1 column = 16/4x4 = 4x4 words int page = (x >> 7) + ((y >> 6) * (width >> 1)); int blkx = (x >> 4) & 7; int blky = (y >> 4) & 3; int blk = blkx + (blky * 8); int col = (y >> 2) & 3; int idx = (x & 15) + ((y & 3) * 16); // int shift = (x & 3) * 16; // Unswizzle block blk = psmct32_block[blk]; // Unswizzle column idx = (col & 1) ? psmt8_column_13[idx] : psmt8_column_02[idx]; // printf("(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\n", // base, width, x, y, // page, blk, cl, // (page * 2048) + (base * 64) + (blk * 64) + cl // ); return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx; } int psmt4_block[] = { 0 , 2 , 8 , 10, 1 , 3 , 9 , 11, 4 , 6 , 12, 14, 5 , 7 , 13, 15, 16, 18, 24, 26, 17, 19, 25, 27, 20, 22, 28, 30, 21, 23, 29, 31 }; int psmt4_column_02[] = { 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 }; int psmt4_column_13[] = { 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 0 , 1 , 4 , 5 , 8 , 9 , 12, 13, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15, 2 , 3 , 6 , 7 , 10, 11, 14, 15 }; int psmt4_shift[] = { 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, 0 , 0 , 0 , 0 , 0 , 0 , 0 , 0 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 8 , 16, 16, 16, 16, 16, 16, 16, 16, 24, 24, 24, 24, 24, 24, 24, 24, 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 12, 12, 12, 12, 12, 12, 12, 12, 20, 20, 20, 20, 20, 20, 20, 20, 28, 28, 28, 28, 28, 28, 28, 28, 4 , 4 , 4 , 4 , 4 , 4 , 4 , 4 , 12, 12, 12, 12, 12, 12, 12, 12, 20, 20, 20, 20, 20, 20, 20, 20, 28, 28, 28, 28, 28, 28, 28, 28 }; static inline int psmt4_addr(int base, int width, int x, int y) { // page block column // 128 x 128 pixels 32 x 16 pixels 32 x 4 pixels int page = (x >> 7) + ((y >> 7) * (width >> 1)); int blkx = (x >> 5) & 3; int blky = (y >> 4) & 7; int blk = blkx + (blky * 4); int col = (y >> 2) & 3; int idx = (x & 31) + ((y & 3) * 32); // int shift = (x & 3) * 16; // Unswizzle block blk = psmt4_block[blk]; // Unswizzle column idx = (col & 1) ? psmt4_column_13[idx] : psmt4_column_02[idx]; // printf("(%x, %d, %d, %d) -> p=%d b=%d c=%d addr=%08x\n", // base, width, x, y, // page, blk, cl, // (page * 2048) + (base * 64) + (blk * 64) + cl // ); return (page * 2048) + ((base + blk) * 64) + (col * 16) + idx; } static const int psmt8_clut_block[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x08, 0x09, 0x0a, 0x0b, 0x0c, 0x0d, 0x0e, 0x0f, 0x18, 0x19, 0x1a, 0x1b, 0x1c, 0x1d, 0x1e, 0x1f, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x28, 0x29, 0x2a, 0x2b, 0x2c, 0x2d, 0x2e, 0x2f, 0x38, 0x39, 0x3a, 0x3b, 0x3c, 0x3d, 0x3e, 0x3f, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x48, 0x49, 0x4a, 0x4b, 0x4c, 0x4d, 0x4e, 0x4f, 0x58, 0x59, 0x5a, 0x5b, 0x5c, 0x5d, 0x5e, 0x5f, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x68, 0x69, 0x6a, 0x6b, 0x6c, 0x6d, 0x6e, 0x6f, 0x78, 0x79, 0x7a, 0x7b, 0x7c, 0x7d, 0x7e, 0x7f, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x88, 0x89, 0x8a, 0x8b, 0x8c, 0x8d, 0x8e, 0x8f, 0x98, 0x99, 0x9a, 0x9b, 0x9c, 0x9d, 0x9e, 0x9f, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xa8, 0xa9, 0xaa, 0xab, 0xac, 0xad, 0xae, 0xaf, 0xb8, 0xb9, 0xba, 0xbb, 0xbc, 0xbd, 0xbe, 0xbf, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xc8, 0xc9, 0xca, 0xcb, 0xcc, 0xcd, 0xce, 0xcf, 0xd8, 0xd9, 0xda, 0xdb, 0xdc, 0xdd, 0xde, 0xdf, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xe8, 0xe9, 0xea, 0xeb, 0xec, 0xed, 0xee, 0xef, 0xf8, 0xf9, 0xfa, 0xfb, 0xfc, 0xfd, 0xfe, 0xff }; void render_point(struct ps2_gs* gs, void* udata); void render_line(struct ps2_gs* gs, void* udata); void render_triangle(struct ps2_gs* gs, void* udata); void render_sprite(struct ps2_gs* gs, void* udata); void transfer_start(struct ps2_gs* gs, void* udata); void transfer_write(struct ps2_gs* gs, void* udata); void transfer_read(struct ps2_gs* gs, void* udata); void software_thread_render_thread(software_thread_state* ctx) { while (!ctx->end_signal) { while (true) { ctx->queue_mtx.lock(); if (ctx->render_queue.empty()) { ctx->queue_mtx.unlock(); break; } // Explicitly copy data from the queue render_data rdata = render_data(ctx->render_queue.front()); // Assign context pointer to the copied context rdata.gs.ctx = &rdata.gs.context[(rdata.gs.attr & GS_CTXT) ? 1 : 0]; ctx->render_queue.pop(); ctx->queue_mtx.unlock(); ctx->render_mtx.lock(); switch (rdata.prim) { case 0: render_point(&rdata.gs, nullptr); break; case 1: render_line(&rdata.gs, nullptr); break; case 2: render_triangle(&rdata.gs, nullptr); break; case 3: render_sprite(&rdata.gs, nullptr); break; } ctx->render_mtx.unlock(); } // std::this_thread::yield(); } } void software_thread_destroy(void* udata) { software_thread_state* ctx = (software_thread_state*)udata; // Send end signal to rendering thread ctx->end_signal = true; // Clear rendering queue ctx->queue_mtx.lock(); while (!ctx->render_queue.empty()) ctx->render_queue.pop(); ctx->queue_mtx.unlock(); if (ctx->buf) free(ctx->buf); // release buffers SDL_ReleaseGPUBuffer(ctx->device, ctx->vertex_buffer); SDL_ReleaseGPUBuffer(ctx->device, ctx->index_buffer); SDL_ReleaseGPUSampler(ctx->device, ctx->sampler[0]); SDL_ReleaseGPUSampler(ctx->device, ctx->sampler[1]); if (ctx->texture) SDL_ReleaseGPUTexture(ctx->device, ctx->texture); // release the pipeline SDL_ReleaseGPUGraphicsPipeline(ctx->device, ctx->pipeline); // Should call destructors for our mutex, thread and queue delete ctx; } void software_thread_set_size(void* udata, int width, int height) { software_thread_state* ctx = (software_thread_state*)udata; int en1 = ctx->gs->pmode & 1; int en2 = (ctx->gs->pmode >> 1) & 1; uint64_t display = 0, dispfb = 0; if (en1 && en2) { display = ctx->gs->display1 > ctx->gs->display2 ? ctx->gs->display1 : ctx->gs->display2; dispfb = ctx->gs->dispfb1 > ctx->gs->dispfb2 ? ctx->gs->dispfb1 : ctx->gs->dispfb2; } else if (en1) { display = ctx->gs->display1; dispfb = ctx->gs->dispfb1; } else if (en2) { display = ctx->gs->display2; dispfb = ctx->gs->dispfb2; } uint32_t tex_fmt = (dispfb >> 15) & 0x1f; int magh = ((display >> 23) & 0xf) + 1; int magv = ((display >> 27) & 3) + 1; if (((display >> 32) & 0xfff) == 0) { ctx->tex_w = 0; ctx->tex_h = 0; return; } if (((display >> 44) & 0x7ff) == 0) { ctx->tex_w = 0; ctx->tex_h = 0; return; } uint32_t tex_w = (((display >> 32) & 0xfff) / magh) + 1; uint32_t tex_h = (((display >> 44) & 0x7ff) / magv) + 1; // SMODE2 INT=1 FFMD=1 // if ((ctx->gs->smode2 & 3) == 3) { // tex_h /= 2; // } // Weird dobiestation hack, should fix Silent Hill 2, Choro Q HG, etc. if (tex_h >= (tex_w * 1.3)) tex_h = tex_h / 2; // 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, // (int)((display >> 32) & 0xfff) + 1, // (int)((display >> 44) & 0x7ff) + 1, // (int)(display & 0xfff), // (int)((display >> 12) & 0xfff) // ); // Do nothing if the size hasn't changed if (tex_w == ctx->tex_w && tex_h == ctx->tex_h && tex_fmt == ctx->disp_fmt) { return; } ctx->tex_w = tex_w; ctx->tex_h = tex_h; ctx->disp_fmt = tex_fmt; if (ctx->buf) free(ctx->buf); ctx->buf = (uint32_t*)malloc((ctx->tex_w * sizeof(uint32_t)) * ctx->tex_h); SDL_GPUTextureFormat fmt; switch (ctx->disp_fmt) { case GS_PSMCT32: case GS_PSMCT24: { fmt = SDL_GPU_TEXTUREFORMAT_B8G8R8A8_UNORM; } break; case GS_PSMCT16: case GS_PSMCT16S: { fmt = SDL_GPU_TEXTUREFORMAT_B5G5R5A1_UNORM; } break; default: { // printf("gsr: Unknown framebuffer format %02x\n", ctx->disp_fmt); } break; } if (ctx->texture) { SDL_ReleaseGPUTexture(ctx->device, ctx->texture); } // Create a texture to send in our frame data SDL_GPUTextureCreateInfo tci = { .type = SDL_GPU_TEXTURETYPE_2D, .format = fmt, .usage = SDL_GPU_TEXTUREUSAGE_SAMPLER, .width = tex_w, .height = tex_h, .layer_count_or_depth = 1, .num_levels = 1, .sample_count = SDL_GPU_SAMPLECOUNT_1, }; ctx->texture = SDL_CreateGPUTexture(ctx->device, &tci); } void software_thread_set_scale(void* udata, float scale) { software_thread_state* ctx = (software_thread_state*)udata; ctx->scale = scale; } void software_thread_set_aspect_mode(void* udata, int aspect_mode) { software_thread_state* ctx = (software_thread_state*)udata; ctx->aspect_mode = aspect_mode; } void software_thread_set_integer_scaling(void* udata, bool integer_scaling) { software_thread_state* ctx = (software_thread_state*)udata; ctx->integer_scaling = integer_scaling; } void software_thread_set_bilinear(void* udata, bool bilinear) { software_thread_state* ctx = (software_thread_state*)udata; ctx->bilinear = bilinear; } void software_thread_get_viewport_size(void* udata, int* w, int* h) { software_thread_state* ctx = (software_thread_state*)udata; *w = ctx->tex_w; *h = ctx->tex_h; } void software_thread_get_display_size(void* udata, int* w, int* h) { software_thread_state* ctx = (software_thread_state*)udata; *w = ctx->disp_w; *h = ctx->disp_h; } void software_thread_get_display_format(void* udata, int* fmt) { software_thread_state* ctx = (software_thread_state*)udata; *fmt = ctx->disp_fmt; } void software_thread_set_window_rect(void* udata, int x, int y, int w, int h) { software_thread_state* ctx = (software_thread_state*)udata; ctx->window_x = x; ctx->window_y = y; ctx->window_w = w; ctx->window_h = h; } void* software_thread_get_buffer_data(void* udata, int* w, int* h, int* bpp) { software_thread_state* ctx = (software_thread_state*)udata; if (!ctx->tex_w) return nullptr; if (!ctx->buf) return nullptr; *w = ctx->tex_w; *h = ctx->tex_h; switch (ctx->disp_fmt) { case GS_PSMCT32: case GS_PSMCT24: { *bpp = 4; } break; case GS_PSMCT16: case GS_PSMCT16S: { *bpp = 2; } break; } return ctx->buf; } void software_thread_get_interlace_mode(void* udata, int* interlace) { software_thread_state* ctx = (software_thread_state*)udata; *interlace = ctx->gs->smode2 & 3; } const char* software_thread_get_name(void* udata) { return "Software (Threaded)"; } #define CLAMP(v, l, u) (((v) > (u)) ? (u) : (((v) < (l)) ? (l) : (v))) static inline uint32_t gs_apply_function(struct ps2_gs* gs, uint32_t t, uint32_t f) { int pr, pg, pb, pa; int tr = t & 0xff; int tg = (t >> 8) & 0xff; int tb = (t >> 16) & 0xff; int ta = (t >> 24) & 0xff; int fr = f & 0xff; int fg = (f >> 8) & 0xff; int fb = (f >> 16) & 0xff; int fa = (f >> 24) & 0xff; switch (gs->ctx->tfx) { case GS_MODULATE: { pr = CLAMP((tr * fr) >> 7, 0, 255); pg = CLAMP((tg * fg) >> 7, 0, 255); pb = CLAMP((tb * fb) >> 7, 0, 255); if (gs->ctx->tcc) { pa = CLAMP((ta * fa) >> 7, 0, 255); } else { pa = fa; } } break; case GS_DECAL: { pr = tr; pg = tg; pb = tb; if (gs->ctx->tcc) { pa = ta; } else { pa = fa; } } break; case GS_HIGHLIGHT: { pr = CLAMP(((tr * fr) >> 7) + fa, 0, 255); pg = CLAMP(((tg * fg) >> 7) + fa, 0, 255); pb = CLAMP(((tb * fb) >> 7) + fa, 0, 255); if (gs->ctx->tcc) { pa = CLAMP(fa + ta, 0, 255); } else { pa = fa; } } break; case GS_HIGHLIGHT2: { pr = CLAMP(((tr * fr) >> 7) + fa, 0, 255); pg = CLAMP(((tg * fg) >> 7) + fa, 0, 255); pb = CLAMP(((tb * fb) >> 7) + fa, 0, 255); if (gs->ctx->tcc) { pa = ta; } else { pa = fa; } } break; } return (pr & 0xff) | ((pg & 0xff) << 8) | ((pb & 0xff) << 16) | ((pa & 0xff) << 24); } static inline int gs_clamp_u(struct ps2_gs* gs, int u) { switch (gs->ctx->wms) { case 0: { u %= gs->ctx->usize; } break; case 1: { u = (u < 0) ? 0 : ((u > (int)gs->ctx->usize) ? gs->ctx->usize : u); } break; case 2: { u = (u < (int)gs->ctx->minu) ? gs->ctx->minu : ((u > (int)gs->ctx->maxu) ? gs->ctx->maxu : u); } break; case 3: { int umsk = gs->ctx->minu; int ufix = gs->ctx->maxu; u = (u & umsk) | ufix; } break; } return u; } static inline int gs_clamp_v(struct ps2_gs* gs, int v) { switch (gs->ctx->wmt) { case 0: { v %= gs->ctx->vsize; } break; case 1: { v = (v < 0) ? 0 : ((v > (int)gs->ctx->vsize) ? gs->ctx->vsize : v); } break; case 2: { v = (v < (int)gs->ctx->minv) ? gs->ctx->minv : ((v > (int)gs->ctx->maxv) ? gs->ctx->maxv : v); } break; case 3: { int vmsk = gs->ctx->minv; int vfix = gs->ctx->maxv; v = (v & vmsk) | vfix; } break; } return v; } enum : int { TR_FAIL, TR_PASS, TR_FB_ONLY, TR_ZB_ONLY, TR_RGB_ONLY }; static inline uint32_t gs_read_fb(struct ps2_gs* gs, int x, int y) { switch (gs->ctx->fbpsm) { case GS_PSMCT32: { uint32_t addr = psmct32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); return gs->vram[addr & 0xfffff]; } case GS_PSMCT24: { uint32_t addr = psmct32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); return (gs->vram[addr & 0xfffff] & 0xffffff) | 0x80000000; } case GS_PSMCT16: { uint32_t addr = psmct16_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; case GS_PSMCT16S: { uint32_t addr = psmct16s_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; case GS_PSMZ32: { uint32_t addr = psmz32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); return gs->vram[addr & 0xfffff]; } case GS_PSMZ24: { uint32_t addr = psmz32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); return (gs->vram[addr & 0xfffff] & 0xffffff) | 0x80000000; } case GS_PSMZ16: { uint32_t addr = psmz16_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; case GS_PSMZ16S: { uint32_t addr = psmz16s_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; default: { // printf("Unsupported PSMT %02x for fb read\n", gs->ctx->fbpsm); // exit(1); } break; } return 0; } static inline uint32_t gs_read_dispfb(struct ps2_gs* gs, int x, int y, int dfb) { uint32_t dfbp = dfb ? gs->dfbp2 : gs->dfbp1; uint32_t dfbw = dfb ? gs->dfbw2 : gs->dfbw1; uint32_t dfbpsm = dfb ? gs->dfbpsm2 : gs->dfbpsm1; switch (dfbpsm) { case GS_PSMCT32: { uint32_t addr = psmct32_addr(dfbp, dfbw, x, y); return gs->vram[addr & 0xfffff]; } case GS_PSMCT24: { uint32_t addr = psmct32_addr(dfbp, dfbw, x, y); return gs->vram[addr & 0xfffff] & 0xffffff; } case GS_PSMCT16: { uint32_t addr = psmct16_addr(dfbp, dfbw, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; case GS_PSMCT16S: { uint32_t addr = psmct16s_addr(dfbp, dfbw, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; default: { fprintf(stderr, "Unsupported PSMT %02x for dispfb read\n", dfbpsm); exit(1); } break; } return 0; } static inline uint32_t gs_read_zb(struct ps2_gs* gs, int x, int y) { switch (gs->ctx->zbpsm) { case GS_PSMCT32: { uint32_t addr = psmct32_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y); return gs->vram[addr & 0xfffff]; } case GS_PSMCT24: { uint32_t addr = psmct32_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y); return gs->vram[addr & 0xfffff] & 0xffffff; } case GS_PSMCT16: { uint32_t addr = psmct16_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); return vram[psmct16_shift[idx]]; } break; case GS_PSMCT16S: { uint32_t addr = psmct16s_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); return vram[psmct16_shift[idx]]; } break; case GS_PSMZ32: { uint32_t addr = psmz32_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y); return gs->vram[addr & 0xfffff]; } case GS_PSMZ24: { uint32_t addr = psmz32_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y); return gs->vram[addr & 0xfffff] & 0xffffff; } case GS_PSMZ16: { uint32_t addr = psmz16_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); return vram[psmct16_shift[idx]]; } break; case GS_PSMZ16S: { uint32_t addr = psmz16s_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); return vram[psmct16_shift[idx]]; } break; default: { // printf("Unsupported PSMT %02x for zb read\n", gs->ctx->zbpsm); // exit(1); } break; } return 0; } static inline uint32_t gs_read_cb_csm2(struct ps2_gs* gs, int i) { int x = i + gs->cou; uint32_t addr = psmct16_addr(gs->ctx->cbp, gs->cbw, x, gs->cov); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((gs->cov & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; // switch (gs->ctx->tbpsm) { // case GS_PSMT8H: // case GS_PSMT8: { // uint32_t addr = psmct16_addr(gs->ctx->cbp, gs->cbw, x, gs->cov); // uint16_t* vram = (uint16_t*)(&gs->vram[addr]); // int idx = (x & 15) + ((gs->cov & 1) * 16); // return gs->vram[psmct16_shift[idx]]; // } break; // case GS_PSMT4HL: // case GS_PSMT4HH: // case GS_PSMT4: { // uint32_t addr = psmct16_addr(gs->ctx->cbp, gs->ctx->tbw, i, 0); // uint16_t* vram = (uint16_t*)(&gs->vram[addr]); // return gs->vram[psmct16_shift[i & 15]]; // } break; // } } static inline uint32_t gs_read_cb(struct ps2_gs* gs, int i) { // Note: CSM2 should be the same? // not sure if the different arrangement actually // changes how address calculation is performed // maybe it only applies to the CLUT cache // - Edit: CSM2 uses the TEXCLUT reg, and it's // basically just like a normal buffer // Note: CLUT buffers are always 64 pixels wide if (gs->ctx->csm == 1) return gs_read_cb_csm2(gs, i); switch (gs->ctx->tbpsm) { case GS_PSMT8H: case GS_PSMT8: { int p = psmt8_clut_block[i]; int x = p & 0xf; int y = p >> 4; switch (gs->ctx->cbpsm) { case GS_PSMCT32: { return gs->vram[psmct32_addr(gs->ctx->cbp, 1, x, y) & 0xfffff]; } break; case GS_PSMCT16: case GS_PSMCT16S: { uint32_t addr = psmct16_addr(gs->ctx->cbp, 2, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; default: { // printf("Unsupported PSMT %02x for 8-bit cb read\n", gs->ctx->cbpsm); // exit(1); } break; } } break; case GS_PSMT4HH: case GS_PSMT4HL: case GS_PSMT4: { int x = i & 0x7; int y = i >> 3; switch (gs->ctx->cbpsm) { case GS_PSMCT32: { // return gs->clut_cache[gs->ctx->csa + x + (y * 8)]; return gs->vram[psmct32_addr(gs->ctx->cbp, 1, x, y) & 0xfffff]; } break; case GS_PSMCT16: case GS_PSMCT16S: { uint32_t addr = psmct16_addr(gs->ctx->cbp, 2, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; default: { // printf("Unsupported PSMT %02x for 4-bit cb read\n", gs->ctx->cbpsm); // exit(1); } break; } } break; } return 0; } static inline uint32_t gs_to_rgba32(struct ps2_gs* gs, uint32_t c, int fmt) { switch (fmt) { case GS_PSMCT32: case GS_PSMZ32: { return c; } case GS_PSMCT24: case GS_PSMZ24: { uint32_t a = 0; if (gs->aem) { if (c & 0xffffff) { a = gs->ta0; } else { a = 0; } } else { a = gs->ta0; } return c | (a << 24); } case GS_PSMCT16: case GS_PSMCT16S: case GS_PSMZ16: case GS_PSMZ16S: { int ia = c & 0x8000; uint32_t oa = 0; if (ia) { oa = gs->ta1; } else { if (gs->aem) { if (c & 0x7fff) { oa = gs->ta0; } else { oa = 0; } } else { oa = gs->ta0; } } return ((c & 0x001f) << 3) | ((c & 0x03e0) << 6) | ((c & 0x7c00) << 9) | (oa << 24); } break; case GS_PSMT8: case GS_PSMT8H: case GS_PSMT4: case GS_PSMT4HL: case GS_PSMT4HH: { return gs_to_rgba32(gs, c, gs->ctx->cbpsm); } break; default: { // printf("Unsupported PSMT %02x for to_rgba32\n", fmt); // exit(1); } break; } return 0; } static inline uint32_t gs_from_rgba32(struct ps2_gs* gs, uint32_t c, int fmt, int x = 0, int y = 0, int dither = 0) { switch (fmt) { case GS_PSMCT32: case GS_PSMZ32: { return c; } case GS_PSMCT24: case GS_PSMZ24: { // To-do: Use TEXA return c & 0xffffff; } case GS_PSMCT16: case GS_PSMCT16S: { if (dither) { int dv = gs->dither[y & 3][x & 3]; int r = c & 0xff; int g = (c >> 8) & 0xff; int b = (c >> 16) & 0xff; r += dv; g += dv; b += dv; r = CLAMP(r, 0, 255); g = CLAMP(g, 0, 255); b = CLAMP(b, 0, 255); r = (r >> 3) & 0x1f; g = (g >> 3) & 0x1f; b = (b >> 3) & 0x1f; return r | (g << 5) | (b << 10) | ((c & 0x80000000) ? 0x8000 : 0); } // To-do: Use TEXA return ((c & 0x0000f8) >> 3) | ((c & 0x00f800) >> 6) | ((c & 0xf80000) >> 9) | ((c & 0x80000000) ? 0x8000 : 0); } break; case GS_PSMZ16: case GS_PSMZ16S: { return c & 0xffff; } break; case GS_PSMT8: case GS_PSMT8H: case GS_PSMT4: case GS_PSMT4HL: case GS_PSMT4HH: { return gs_from_rgba32(gs, c, gs->ctx->cbpsm); } break; default: { // printf("Unsupported PSMT %02x for from_rgba32\n", fmt); // exit(1); } break; } return 0; } static inline uint32_t gs_read_tb_impl(struct ps2_gs* gs, int u, int v) { u = gs_clamp_u(gs, u); v = gs_clamp_v(gs, v); switch (gs->ctx->tbpsm) { case GS_PSMCT32: return gs->vram[psmct32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v) & 0xfffff]; case GS_PSMCT24: return gs->vram[psmct32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v) & 0xfffff] & 0xffffff; case GS_PSMCT16: { uint32_t addr = psmct16_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 1) * 16); return vram[psmct16_shift[idx]]; } break; case GS_PSMCT16S: { uint32_t addr = psmct16s_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 1) * 16); return vram[psmct16_shift[idx]]; } break; case GS_PSMT8: { uint32_t addr = psmt8_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v); uint8_t* vram = (uint8_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 3) * 16); return gs_read_cb(gs, vram[psmt8_shift[idx]]); } break; case GS_PSMT8H: { uint32_t data = gs->vram[psmct32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v) & 0xfffff]; return gs_read_cb(gs, data >> 24); } break; case GS_PSMT4: { uint32_t addr = psmt4_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v); int idx = (u & 31) + ((v & 3) * 32); int shift = psmt4_shift[idx]; uint32_t mask = 0xful << shift; return gs_read_cb(gs, (gs->vram[addr & 0xfffff] & mask) >> shift); } break; case GS_PSMT4HL: { uint32_t data = gs->vram[psmct32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v) & 0xfffff]; return gs_read_cb(gs, (data >> 24) & 0xf); } break; case GS_PSMT4HH: { uint32_t data = gs->vram[psmct32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v) & 0xfffff]; return gs_read_cb(gs, data >> 28); } break; case GS_PSMZ32: { uint32_t addr = psmz32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v); return gs->vram[addr & 0xfffff]; } break; case GS_PSMZ24: { uint32_t addr = psmz32_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v); return gs->vram[addr & 0xfffff] & 0xffffff; } break; case GS_PSMZ16: { uint32_t addr = psmz16_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; case GS_PSMZ16S: { uint32_t addr = psmz16s_addr(gs->ctx->tbp0, gs->ctx->tbw, u, v); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; default: { // printf("Unsupported PSMT %02x for tb read\n", gs->ctx->tbpsm); // exit(1); } break; } return 0; } static inline uint32_t gs_read_tb(struct ps2_gs* gs, int u, int v) { if (gs->ctx->mmag) { float a = ((u - 8) & 0xf) * 0.0625; float b = ((v - 8) & 0xf) * 0.0625; int iu0 = (u - 8) >> 4; int iv0 = (v - 8) >> 4; int iu1 = iu0 + 1; int iv1 = iv0 + 1; uint32_t s0 = gs_read_tb_impl(gs, iu0, iv0); uint32_t s1 = gs_read_tb_impl(gs, iu1, iv0); uint32_t s2 = gs_read_tb_impl(gs, iu0, iv1); uint32_t s3 = gs_read_tb_impl(gs, iu1, iv1); s0 = gs_to_rgba32(gs, s0, gs->ctx->tbpsm); s1 = gs_to_rgba32(gs, s1, gs->ctx->tbpsm); s2 = gs_to_rgba32(gs, s2, gs->ctx->tbpsm); s3 = gs_to_rgba32(gs, s3, gs->ctx->tbpsm); int r0 = s0 & 0xff; int g0 = (s0 >> 8) & 0xff; int b0 = (s0 >> 16) & 0xff; int a0 = (s0 >> 24) & 0xff; int r1 = s1 & 0xff; int g1 = (s1 >> 8) & 0xff; int b1 = (s1 >> 16) & 0xff; int a1 = (s1 >> 24) & 0xff; int r2 = s2 & 0xff; int g2 = (s2 >> 8) & 0xff; int b2 = (s2 >> 16) & 0xff; int a2 = (s2 >> 24) & 0xff; int r3 = s3 & 0xff; int g3 = (s3 >> 8) & 0xff; int b3 = (s3 >> 16) & 0xff; int a3 = (s3 >> 24) & 0xff; uint32_t rr = ((1.0-a) * (1.0-b) * r0) + (a * (1.0-b) * r1) + ((1.0-a) * b * r2) + (a * b * r3); uint32_t gg = ((1.0-a) * (1.0-b) * g0) + (a * (1.0-b) * g1) + ((1.0-a) * b * g2) + (a * b * g3); uint32_t bb = ((1.0-a) * (1.0-b) * b0) + (a * (1.0-b) * b1) + ((1.0-a) * b * b2) + (a * b * b3); uint32_t aa = ((1.0-a) * (1.0-b) * a0) + (a * (1.0-b) * a1) + ((1.0-a) * b * a2) + (a * b * a3); rr = CLAMP(rr, 0, 255); gg = CLAMP(gg, 0, 255); bb = CLAMP(bb, 0, 255); aa = CLAMP(aa, 0, 255); return gs_from_rgba32(gs, rr | (gg << 8) | (bb << 16) | (aa << 24), gs->ctx->tbpsm); } return gs_read_tb_impl(gs, u >> 4, v >> 4); } static inline void gs_write_fb(struct ps2_gs* gs, int x, int y, uint32_t c) { uint32_t f = gs_from_rgba32(gs, c, gs->ctx->fbpsm, x, y, gs->dthe); // To-do: Implement FBMSK switch (gs->ctx->fbpsm) { case GS_PSMCT32: { uint32_t addr = psmct32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); gs->vram[addr & 0xfffff] = f; // gs->vram[(gs->ctx->fbp + x + (y * gs->ctx->fbw)) & 0xfffff] = f; } break; case GS_PSMCT24: { uint32_t addr = psmct32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint32_t v = gs->vram[addr]; gs->vram[addr] = (v & 0xff000000) | (f & 0xffffff); // gs->vram[(gs->ctx->fbp + x + (y * gs->ctx->fbw)) & 0xfffff] = f; } break; case GS_PSMCT16: { uint32_t addr = psmct16_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr]); int idx = (x & 15) + ((y & 1) * 16); vram[psmct16_shift[idx]] = f; } break; case GS_PSMCT16S: { uint32_t addr = psmct16s_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr]); int idx = (x & 15) + ((y & 1) * 16); vram[psmct16_shift[idx]] = f; } break; case GS_PSMZ32: { uint32_t addr = psmz32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); gs->vram[addr & 0xfffff] = f; // gs->vram[(gs->ctx->fbp + x + (y * gs->ctx->fbw)) & 0xfffff] = f; } break; case GS_PSMZ24: { uint32_t addr = psmz32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint32_t v = gs->vram[addr]; gs->vram[addr] = (v & 0xff000000) | (f & 0xffffff); // gs->vram[(gs->ctx->fbp + x + (y * gs->ctx->fbw)) & 0xfffff] = f; } break; case GS_PSMZ16: { uint32_t addr = psmz16_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr]); int idx = (x & 15) + ((y & 1) * 16); vram[psmct16_shift[idx]] = f; } break; case GS_PSMZ16S: { uint32_t addr = psmz16s_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr]); int idx = (x & 15) + ((y & 1) * 16); vram[psmct16_shift[idx]] = f; } break; default: { // printf("Unsupported PSMT %02x for fb write\n", gs->ctx->fbpsm); // exit(1); } break; } } static inline void gs_write_fb_no_alpha(struct ps2_gs* gs, int x, int y, uint32_t c) { uint32_t f = gs_from_rgba32(gs, c, gs->ctx->fbpsm); // To-do: Implement FBMSK switch (gs->ctx->fbpsm) { case GS_PSMCT32: { uint32_t addr = psmct32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint32_t p = gs->vram[addr & 0xfffff]; gs->vram[addr & 0xfffff] = (f & 0xffffff) | (p & 0xff000000); } break; case GS_PSMCT24: { uint32_t addr = psmct32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint32_t p = gs->vram[addr]; gs->vram[addr] = (f & 0xffffff) | (p & 0xff000000); } break; case GS_PSMCT16: { uint32_t addr = psmct16_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr]); uint16_t p = gs->vram[addr]; int idx = (x & 15) + ((y & 1) * 16); vram[psmct16_shift[idx]] = (f & 0x7fff) | (p & 0x8000); } break; case GS_PSMCT16S: { uint32_t addr = psmct16s_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr]); uint16_t p = gs->vram[addr]; int idx = (x & 15) + ((y & 1) * 16); vram[psmct16_shift[idx]] = (f & 0x7fff) | (p & 0x8000); } break; case GS_PSMZ32: { uint32_t addr = psmz32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint32_t p = gs->vram[addr & 0xfffff]; gs->vram[addr & 0xfffff] = (f & 0xffffff) | (p & 0xff000000); } break; case GS_PSMZ24: { uint32_t addr = psmz32_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint32_t p = gs->vram[addr]; gs->vram[addr] = (f & 0xffffff) | (p & 0xff000000); } break; case GS_PSMZ16: { uint32_t addr = psmz16_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr]); uint16_t p = gs->vram[addr]; int idx = (x & 15) + ((y & 1) * 16); vram[psmct16_shift[idx]] = (f & 0x7fff) | (p & 0x8000); } break; case GS_PSMZ16S: { uint32_t addr = psmz16s_addr(gs->ctx->fbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr]); uint16_t p = gs->vram[addr]; int idx = (x & 15) + ((y & 1) * 16); vram[psmct16_shift[idx]] = (f & 0x7fff) | (p & 0x8000); } break; default: { // printf("Unsupported PSMT %02x for fb write\n", gs->ctx->fbpsm); // exit(1); } break; } } static inline void gs_write_zb(struct ps2_gs* gs, int x, int y, uint32_t z) { if (gs->ctx->zbmsk || !gs->ctx->zte) return; switch (gs->ctx->zbpsm) { case 0x01: z = std::min(z, 0xffffffu); break; case 0x02: z = std::min(z, 0xffffu); break; case 0x0A: z = std::min(z, 0xffffu); break; case 0x31: z = std::min(z, 0xffffffu); break; case 0x32: z = std::min(z, 0xffffu); break; case 0x3A: z = std::min(z, 0xffffu); break; } switch (gs->ctx->zbpsm) { case GS_PSMCT32: case GS_PSMCT24: { gs->vram[psmct32_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y) & 0xfffff] = z; } break; case GS_PSMCT16: { uint32_t addr = psmct16_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); vram[psmct16_shift[idx]] = z; } break; case GS_PSMCT16S: { uint32_t addr = psmct16s_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); vram[psmct16_shift[idx]] = z; } break; case GS_PSMZ32: case GS_PSMZ24: { gs->vram[psmz32_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y) & 0xfffff] = z; } break; case GS_PSMZ16: { uint32_t addr = psmz16_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); vram[psmct16_shift[idx]] = z; } break; case GS_PSMZ16S: { uint32_t addr = psmz16s_addr(gs->ctx->zbp >> 6, gs->ctx->fbw >> 6, x, y); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (x & 15) + ((y & 1) * 16); vram[psmct16_shift[idx]] = z; } break; default: { // printf("Unsupported PSMT %02x for zb write\n", gs->ctx->zbpsm); // exit(1); } break; } } static inline int gs_test_scissor(struct ps2_gs* gs, int x, int y) { if (x < (int)gs->ctx->scax0 || y < (int)gs->ctx->scay0 || x > (int)gs->ctx->scax1 || y > (int)gs->ctx->scay1) { return TR_FAIL; } return TR_PASS; } static inline int gs_test_pixel(struct ps2_gs* gs, int x, int y, uint32_t z, uint8_t a) { int tr = TR_PASS; // Alpha test if (gs->ctx->ate) { switch (gs->ctx->atst) { case 0: tr = TR_FAIL; break; case 2: tr = a < gs->ctx->aref; break; case 3: tr = a <= gs->ctx->aref; break; case 4: tr = a == gs->ctx->aref; break; case 5: tr = a >= gs->ctx->aref; break; case 6: tr = a > gs->ctx->aref; break; case 7: tr = a != gs->ctx->aref; break; } if (tr == TR_FAIL) { switch (gs->ctx->afail) { case 0: return TR_FAIL; case 1: tr = TR_FB_ONLY; break; case 2: tr = TR_ZB_ONLY; break; case 3: tr = TR_RGB_ONLY; break; } } } // Destination alpha test if (gs->ctx->date) { // Thanks to @refraction // Quote: "When the format is 24bit (Z or C), DATE ceases to function. // It was believed that in 24bit mode all pixels pass because alpha // doesn't exist however after testing this on a PS2 it turns out // nothing passes, it ignores the draw." if ((gs->ctx->fbpsm & 0xf) == GS_PSMCT24) return TR_FAIL; uint32_t s = gs_read_fb(gs, x, y); switch (gs->ctx->fbpsm) { case GS_PSMCT16: case GS_PSMCT16S: if (((s >> 15) & 1) != gs->ctx->datm) return TR_FAIL; break; case GS_PSMCT32: if (((s >> 31) & 1) != gs->ctx->datm) return TR_FAIL; break; default: { fprintf(stderr, "Unsupported PSMT %02x for destination alpha test\n", gs->ctx->fbpsm); exit(1); } break; } } // Depth test if (gs->ctx->zte) { uint32_t zb = gs_read_zb(gs, x, y); switch (gs->ctx->zbpsm) { case 0x01: z = std::min(z, 0xffffffu); break; case 0x02: z = std::min(z, 0xffffu); break; case 0x0A: z = std::min(z, 0xffffu); break; case 0x31: z = std::min(z, 0xffffffu); break; case 0x32: z = std::min(z, 0xffffu); break; case 0x3A: z = std::min(z, 0xffffu); break; } switch (gs->ctx->ztst) { case 0: return TR_FAIL; case 2: { if (z < zb) return TR_FAIL; } break; case 3: { if (z <= zb) return TR_FAIL; } break; } } return tr; } static inline uint32_t gs_alpha_blend(struct ps2_gs* gs, int x, int y, uint32_t s) { uint32_t d = gs_read_fb(gs, x, y); switch (gs->ctx->fbpsm) { case GS_PSMCT32: break; case GS_PSMCT24: d |= 0x80000000; break; case GS_PSMCT16: case GS_PSMCT16S: { int a = d & 0x8000; d = ((d & 0x001f) << 3) | ((d & 0x03e0) << 6) | ((d & 0x7c00) << 9); d |= a ? 0x80000000 : 0; } break; default: { fprintf(stderr, "Unsupported PSMT %02x for alpha blend\n", gs->ctx->fbpsm); exit(1); } break; } uint32_t av = (gs->ctx->a == 0) ? s : ((gs->ctx->a == 1) ? d : 0); uint32_t bv = (gs->ctx->b == 0) ? s : ((gs->ctx->b == 1) ? d : 0); uint32_t cv = (gs->ctx->c == 0) ? (s >> 24) : ((gs->ctx->c == 1) ? (d >> 24) : gs->ctx->fix); uint32_t dv = (gs->ctx->d == 0) ? s : ((gs->ctx->d == 1) ? d : 0); // cv = CLAMP(cv, 0, 127); // if (!gs->tme) // printf("Cd=%08x Cs=%08x a=%08x (%d) b=%08x (%d) c=%08x (%d) d=%08x (%d)\n", d, s, // av, gs->ctx->a, // bv, gs->ctx->b, // cv, gs->ctx->c, // dv, gs->ctx->d // ); int ar = (av >> 0 ) & 0xff; int ag = (av >> 8 ) & 0xff; int ab = (av >> 16) & 0xff; // uint32_t aa = (av >> 24) & 0xff; int br = (bv >> 0 ) & 0xff; int bg = (bv >> 8 ) & 0xff; int bb = (bv >> 16) & 0xff; // uint32_t ba = (bv >> 24) & 0xff; int dr = (dv >> 0 ) & 0xff; int dg = (dv >> 8 ) & 0xff; int db = (dv >> 16) & 0xff; // uint32_t da = (dv >> 24) & 0xff; int rr = ar - br; int rg = ag - bg; int rb = ab - bb; rr = ((rr * (int)cv) >> 7) + dr; rg = ((rg * (int)cv) >> 7) + dg; rb = ((rb * (int)cv) >> 7) + db; // uint32_t ra = (((aa - ba) * cv) >> 7) + da; rr = CLAMP(rr, 0, 255); rg = CLAMP(rg, 0, 255); rb = CLAMP(rb, 0, 255); return (rr & 0xff) | ((rg & 0xff) << 8) | ((rb & 0xff) << 16) | (d & 0xff000000); } static inline float lerpf(int32_t x, float u1, int32_t x1, float u2, int32_t x2) { float b = u1 * (x2 - x); b += u2 * (x - x1); if (!(x2 - x1)) return u1; return b / (x2 - x1); } static inline int32_t lerp(int32_t x, int32_t u1, int32_t x1, int32_t u2, int32_t x2) { int64_t b = (int64_t)u1 * (x2 - x); b += (int64_t)u2 * (x - x1); if (!(x2 - x1)) return u1; return b / (x2 - x1); } int32_t stepsize(int32_t u1, int32_t x1, int32_t u2, int32_t x2, int64_t mult) { if (!(x2 - x1)) return ((u2 - u1) * mult); return ((u2 - u1) * mult)/(x2 - x1); } static inline void gs_draw_pixel(struct ps2_gs* gs, int x, int y, uint32_t z, uint32_t c) { int a = c >> 24; int tr = gs_test_pixel(gs, x, y, z, a); if (tr == TR_FAIL) return; if (gs->abe) c = gs_alpha_blend(gs, x, y, c); switch (tr) { case TR_FB_ONLY: gs_write_fb(gs, x, y, c); break; case TR_ZB_ONLY: gs_write_zb(gs, x, y, z); break; case TR_RGB_ONLY: gs_write_fb_no_alpha(gs, x, y, c); break; case TR_PASS: { gs_write_zb(gs, x, y, z); gs_write_fb(gs, x, y, c); } break; } } void software_thread_init(void* udata, struct ps2_gs* gs, SDL_Window* window, SDL_GPUDevice* device) { software_thread_state* ctx = (software_thread_state*)udata; ctx->window = window; ctx->device = device; ctx->gs = gs; ctx->scale = 1.5f; ctx->end_signal = false; ctx->render_thr = std::thread(software_thread_render_thread, ctx); ctx->render_thr.detach(); // create the vertex shader SDL_GPUShaderCreateInfo vsci = { .code_size = g_vertex_shader_size, .code = g_vertex_shader_data, .entrypoint = SHADER_ENTRYPOINT, .format = SHADER_FORMAT, .stage = SDL_GPU_SHADERSTAGE_VERTEX, .num_samplers = 0, .num_storage_textures = 0, .num_storage_buffers = 0, .num_uniform_buffers = 0, }; SDL_GPUShader* vertex_shader = SDL_CreateGPUShader(device, &vsci); // create the fragment shader SDL_GPUShaderCreateInfo fsci = { .code_size = g_fragment_shader_size, .code = g_fragment_shader_data, .entrypoint = SHADER_ENTRYPOINT, .format = SHADER_FORMAT, .stage = SDL_GPU_SHADERSTAGE_FRAGMENT, .num_samplers = 1, .num_storage_textures = 0, .num_storage_buffers = 0, .num_uniform_buffers = 0, }; SDL_GPUShader* fragment_shader = SDL_CreateGPUShader(device, &fsci); // Create vertex buffer description and attributes SDL_GPUVertexBufferDescription vbd[1] = {{ .slot = 0, .pitch = sizeof(gpu_vertex), .input_rate = SDL_GPU_VERTEXINPUTRATE_VERTEX, .instance_step_rate = 0, }}; SDL_GPUVertexAttribute va[2] = {{ .location = 0, .buffer_slot = 0, .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, .offset = 0, }, { .location = 1, .buffer_slot = 0, .format = SDL_GPU_VERTEXELEMENTFORMAT_FLOAT2, .offset = sizeof(float) * 2, }}; SDL_GPUColorTargetDescription ctd[1] = {{ .format = SDL_GetGPUSwapchainTextureFormat(device, window), .blend_state = { .src_color_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA, .dst_color_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, .color_blend_op = SDL_GPU_BLENDOP_ADD, .src_alpha_blendfactor = SDL_GPU_BLENDFACTOR_SRC_ALPHA, .dst_alpha_blendfactor = SDL_GPU_BLENDFACTOR_ONE_MINUS_SRC_ALPHA, .alpha_blend_op = SDL_GPU_BLENDOP_ADD, .color_write_mask = SDL_GPU_COLORCOMPONENT_A, .enable_blend = false, .enable_color_write_mask = false, }, }}; // Create graphics pipeline SDL_GPUGraphicsPipelineCreateInfo gpci = { .vertex_shader = vertex_shader, .fragment_shader = fragment_shader, .vertex_input_state = { .vertex_buffer_descriptions = vbd, .num_vertex_buffers = 1, .vertex_attributes = va, .num_vertex_attributes = 2, }, .primitive_type = SDL_GPU_PRIMITIVETYPE_TRIANGLELIST, .target_info = { .color_target_descriptions = ctd, .num_color_targets = 1, }, }; ctx->pipeline = SDL_CreateGPUGraphicsPipeline(device, &gpci); SDL_assert(ctx->pipeline); // we don't need to store the shaders after creating the pipeline SDL_ReleaseGPUShader(device, vertex_shader); SDL_ReleaseGPUShader(device, fragment_shader); // create the vertex and index buffers SDL_GPUBufferCreateInfo vbci = { .usage = SDL_GPU_BUFFERUSAGE_VERTEX, .size = sizeof(gpu_vertex) * 4, }; ctx->vertex_buffer = SDL_CreateGPUBuffer(device, &vbci); // We're sending two triangles to make a single quad so we need 6 indices SDL_GPUBufferCreateInfo ibci = { .usage = SDL_GPU_BUFFERUSAGE_INDEX, .size = sizeof(Uint16) * 6, }; ctx->index_buffer = SDL_CreateGPUBuffer(device, &ibci); SDL_GPUSamplerCreateInfo nearest_sci = { .min_filter = SDL_GPU_FILTER_NEAREST, .mag_filter = SDL_GPU_FILTER_NEAREST, .mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST, .address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, .address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, .address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, }; SDL_GPUSamplerCreateInfo linear_sci = { .min_filter = SDL_GPU_FILTER_LINEAR, .mag_filter = SDL_GPU_FILTER_LINEAR, .mipmap_mode = SDL_GPU_SAMPLERMIPMAPMODE_NEAREST, .address_mode_u = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, .address_mode_v = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, .address_mode_w = SDL_GPU_SAMPLERADDRESSMODE_CLAMP_TO_EDGE, }; ctx->sampler[0] = SDL_CreateGPUSampler(ctx->device, &nearest_sci); ctx->sampler[1] = SDL_CreateGPUSampler(ctx->device, &linear_sci); } static 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) { switch (bpsm) { case GS_PSMCT32: return gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff]; case GS_PSMCT24: return gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff]; case GS_PSMCT16: { uint32_t addr = psmct16_addr(bp, bw, u, v); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; case GS_PSMCT16S: { uint32_t addr = psmct16s_addr(bp, bw, u, v); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; case GS_PSMT8: { uint32_t addr = psmt8_addr(bp, bw, u, v); uint8_t* vram = (uint8_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 3) * 16); return vram[psmt8_shift[idx] & 0xfffff]; } break; case GS_PSMT8H: { uint32_t data = gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff]; return data >> 24; } break; case GS_PSMT4: { uint32_t addr = psmt4_addr(bp, bw, u, v); int idx = (u & 31) + ((v & 3) * 32); int shift = psmt4_shift[idx]; uint32_t mask = 0xful << shift; return (gs->vram[addr & 0xfffff] & mask) >> shift; } break; case GS_PSMT4HL: { uint32_t data = gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff]; return (data >> 24) & 0xf; } break; case GS_PSMT4HH: { uint32_t data = gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff]; return data >> 28; } break; case GS_PSMZ32: { return gs->vram[psmz32_addr(bp, bw, u, v) & 0xfffff]; } break; case GS_PSMZ24: { return gs->vram[psmz32_addr(bp, bw, u, v) & 0xfffff] & 0xffffff; } break; case GS_PSMZ16: { uint32_t addr = psmz16_addr(bp, bw, u, v); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; case GS_PSMZ16S: { uint32_t addr = psmz16s_addr(bp, bw, u, v); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 1) * 16); return vram[psmct16_shift[idx] & 0xfffff]; } break; default: { // printf("Unsupported PSMT %02x for generic read\n", gs->ctx->tbpsm); // exit(1); } break; } } static 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) { switch (bpsm) { case GS_PSMCT32: gs->vram[psmct32_addr(bp, bw, u, v) & 0xfffff] = data; break; case GS_PSMCT24: { uint32_t addr = psmct32_addr(bp, bw, u, v) & 0xfffff; gs->vram[addr] = (gs->vram[addr] & 0xff000000) | (data & 0xffffff); } break; break; case GS_PSMCT16: { uint32_t addr = psmct16_addr(bp, bw, u, v); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 1) * 16); vram[psmct16_shift[idx] & 0xfffff] = data; } break; case GS_PSMCT16S: { uint32_t addr = psmct16s_addr(bp, bw, u, v); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 1) * 16); vram[psmct16_shift[idx] & 0xfffff] = data; } break; case GS_PSMT8: { uint32_t addr = psmt8_addr(bp, bw, u, v); uint8_t* vram = (uint8_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 3) * 16); vram[psmt8_shift[idx] & 0xfffff] = data; } break; case GS_PSMT8H: { uint32_t addr = psmct32_addr(bp, bw, u, v) & 0xfffff; gs->vram[addr] = (gs->vram[addr] & 0x00ffffff) | (data << 24); } break; case GS_PSMT4: { uint32_t addr = psmt4_addr(bp, bw, u, v) & 0xfffff; int idx = (u & 31) + ((v & 3) * 32); int shift = psmt4_shift[idx]; uint32_t mask = 0xful << shift; gs->vram[addr] = (gs->vram[addr] & ~mask) | (data << shift); } break; case GS_PSMT4HL: { uint32_t addr = psmct32_addr(bp, bw, u, v) & 0xfffff; gs->vram[addr] = (gs->vram[addr] & 0xf0ffffff) | ((data & 0xf) << 24); } break; case GS_PSMT4HH: { uint32_t addr = psmct32_addr(bp, bw, u, v) & 0xfffff; gs->vram[addr] = (gs->vram[addr] & 0x0fffffff) | ((data & 0xf) << 28); } break; case GS_PSMZ32: gs->vram[psmz32_addr(bp, bw, u, v) & 0xfffff] = data; break; case GS_PSMZ24: { uint32_t addr = psmz32_addr(bp, bw, u, v) & 0xfffff; gs->vram[addr] = (gs->vram[addr] & 0xff000000) | (data & 0xffffff); } break; break; case GS_PSMZ16: { uint32_t addr = psmz16_addr(bp, bw, u, v); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 1) * 16); vram[psmct16_shift[idx] & 0xfffff] = data; } break; case GS_PSMZ16S: { uint32_t addr = psmz16s_addr(bp, bw, u, v); uint16_t* vram = (uint16_t*)(&gs->vram[addr & 0xfffff]); int idx = (u & 15) + ((v & 1) * 16); vram[psmct16_shift[idx] & 0xfffff] = data; } break; default: { // printf("Unsupported PSMT %02x for write\n", gs->ctx->tbpsm); // exit(1); } break; } } static inline void software_thread_vram_blit(struct ps2_gs* gs, software_thread_state* ctx) { // 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", // ctx->dbp, ctx->dbp, // ctx->dbw, ctx->dbw, // ctx->dpsm, // ctx->dsax, // ctx->dsay, // ctx->sbp, ctx->sbp, // ctx->sbw, ctx->sbw, // ctx->spsm, // ctx->ssax, // ctx->ssay, // ctx->rrw, // ctx->rrh, // ctx->xdir // ); for (int y = 0; y < ctx->rrh; y++) { for (int x = 0; x < ctx->rrw; x++) { uint32_t s = gs_generic_read(gs, ctx->sbp, ctx->sbw, ctx->spsm, ctx->ssax + x, ctx->ssay + y); gs_generic_write(gs, ctx->dbp, ctx->dbw, ctx->dpsm, ctx->dsax + x, ctx->dsay + y, s); } } } void render_point(struct ps2_gs* gs, void* udata) { struct gs_vertex vert = gs->vq[0]; vert.x -= gs->ctx->ofx; vert.y -= gs->ctx->ofy; if (!gs_test_scissor(gs, vert.x >> 4, vert.y >> 4)) return; gs_draw_pixel(gs, vert.x >> 4, vert.y >> 4, vert.z, vert.rgbaq & 0xffffffff); } void render_line(struct ps2_gs* gs, void* udata) { struct gs_vertex v0 = gs->vq[0]; struct gs_vertex v1 = gs->vq[1]; // printf("v0=(%d,%d) v1=(%d,%d)\n", v0.x, v0.y, v1.x, v1.y); v0.x -= gs->ctx->ofx; v0.y -= gs->ctx->ofy; v1.x -= gs->ctx->ofx; v1.y -= gs->ctx->ofy; int dx = abs(v1.x - v0.x); int sx = v0.x < v1.x ? 1 : -1; int dy = -abs(v1.y - v0.y); int sy = v0.y < v1.y ? 1 : -1; int error = dx + dy; while (1) { if (gs_test_scissor(gs, v0.x >> 4, v0.y >> 4)) gs_draw_pixel(gs, v0.x >> 4, v0.y >> 4, v0.z, v1.rgbaq & 0xffffffff); int e2 = error << 1; if (e2 >= dy) { if (v0.x == v1.x) break; error = error + dy; v0.x = v0.x + sx; } if (e2 <= dx) { if (v0.y == v1.y) break; error = error + dx; v0.y = v0.y + sy; } } } void gs_draw_wireframe(struct ps2_gs* gs, struct gs_vertex v0, struct gs_vertex v1) { v0.x -= gs->ctx->ofx; v0.y -= gs->ctx->ofy; v1.x -= gs->ctx->ofx; v1.y -= gs->ctx->ofy; int dx = abs(v1.x - v0.x); int sx = v0.x < v1.x ? 1 : -1; int dy = -abs(v1.y - v0.y); int sy = v0.y < v1.y ? 1 : -1; int error = dx + dy; v0.x >>= 4; v0.y >>= 4; v1.x >>= 4; v1.y >>= 4; while (1) { if (gs_test_scissor(gs, v0.x, v0.y)) gs_write_fb(gs, v0.x, v0.y, 0xff0000ff); int e2 = error << 1; if (e2 >= dy) { if (v0.x == v1.x) break; error = error + dy; v0.x = v0.x + sx; } if (e2 <= dx) { if (v0.y == v1.y) break; error = error + dx; v0.y = v0.y + sy; } } } #define EDGE(a, b, c) ((b.x-a.x)*(c.y-a.y)-(b.y-a.y)*(c.x-a.x)) #define MIN2(a, b) ((((int)a) < ((int)b)) ? ((int)a) : ((int)b)) #define MIN3(a, b, c) (MIN2(MIN2(a, b), c)) #define MAX2(a, b) ((((int)a) > ((int)b)) ? ((int)a) : ((int)b)) #define MAX3(a, b, c) (MAX2(MAX2(a, b), c)) #define IS_TOPLEFT(a, b) ((b.y > a.y) || ((a.y == b.y) && (b.x < a.x))) void render_triangle(struct ps2_gs* gs, void* udata) { struct gs_vertex v0 = gs->vq[0]; struct gs_vertex v1 = gs->vq[1]; struct gs_vertex v2 = gs->vq[2]; int area = EDGE(v0, v1, v2); if (area < 0) { v1 = gs->vq[2]; v2 = gs->vq[1]; area = EDGE(v0, v1, v2); } // 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", // v0.x, v0.y, // v1.x, v1.y, // v2.x, v2.y, // gs->ctx->ate, gs->ctx->date, gs->ctx->zte, gs->ctx->ztst, // gs->ctx->zbpsm, gs->ctx->zbp, gs->ctx->fbp // ); v0.x -= gs->ctx->ofx; v1.x -= gs->ctx->ofx; v2.x -= gs->ctx->ofx; v0.y -= gs->ctx->ofy; v1.y -= gs->ctx->ofy; v2.y -= gs->ctx->ofy; int32_t scax0 = gs->ctx->scax0 << 4; int32_t scay0 = gs->ctx->scay0 << 4; int32_t scax1 = gs->ctx->scax1 << 4; int32_t scay1 = gs->ctx->scay1 << 4; int32_t xmin = ((MAX2(MIN3(v0.x, v1.x, v2.x), scax0) + 0) >> 4) << 4; int32_t ymin = ((MAX2(MIN3(v0.y, v1.y, v2.y), scay0) + 0) >> 4) << 4; int32_t xmax = ((MIN2(MAX3(v0.x, v1.x, v2.x), scax1) + 16) >> 4) << 4; int32_t ymax = ((MIN2(MAX3(v0.y, v1.y, v2.y), scay1) + 16) >> 4) << 4; int a01 = (v0.y - v1.y) * 16, b01 = (v1.x - v0.x) * 16; int a12 = (v1.y - v2.y) * 16, b12 = (v2.x - v1.x) * 16; int a20 = (v2.y - v0.y) * 16, b20 = (v0.x - v2.x) * 16; int fogr = gs->fogcol & 0xff; int fogg = (gs->fogcol >> 8) & 0xff; int fogb = (gs->fogcol >> 16) & 0xff; struct { int x, y; } p; p.x = xmin; p.y = ymin; int bias0 = IS_TOPLEFT(v1, v2) ? -1 : 0; int bias1 = IS_TOPLEFT(v2, v0) ? -1 : 0; int bias2 = IS_TOPLEFT(v0, v1) ? -1 : 0; int w0_row = EDGE(v1, v2, p); int w1_row = EDGE(v2, v0, p); int w2_row = EDGE(v0, v1, p); // printf("triangle: v0=(%d.%d,%d.%d) v1=(%d.%d,%d.%d) v2=(%d.%d,%d.%d) v3=(%d.%d,%d.%d)\n", // v0.x >> 4, (v0.x & 0xf) * 625, v0.y >> 4, (v0.y & 0xf) * 625, // v1.x >> 4, (v1.x & 0xf) * 625, v1.y >> 4, (v1.y & 0xf) * 625, // v2.x >> 4, (v2.x & 0xf) * 625, v2.y >> 4, (v2.y & 0xf) * 625, // gs->vq[3].x >> 4, (gs->vq[3].x & 0xf) * 625, gs->vq[3].y >> 4, (gs->vq[3].y & 0xf) * 625 // ); for (p.y = ymin; p.y < ymax; p.y += 16) { // Barycentric coordinates at start of row int w0 = w0_row; int w1 = w1_row; int w2 = w2_row; for (p.x = xmin; p.x < xmax; p.x += 16) { // If p is on or inside all edges, render pixel if (((w0 + bias0) | (w1 + bias1) | (w2 + bias2)) < 0) { w0 += a12; w1 += a20; w2 += a01; continue; } // Calculate interpolation weights double iw0 = (double)w0 / (double)area; double iw1 = (double)w1 / (double)area; double iw2 = (double)w2 / (double)area; uint32_t fr, fg, fb, fa; if (gs->iip) { fr = roundf(v0.r * iw0 + v1.r * iw1 + v2.r * iw2); fg = roundf(v0.g * iw0 + v1.g * iw1 + v2.g * iw2); fb = roundf(v0.b * iw0 + v1.b * iw1 + v2.b * iw2); fa = roundf(v0.a * iw0 + v1.a * iw1 + v2.a * iw2); } else { fr = v2.r; fg = v2.g; fb = v2.b; fa = v2.a; } if (gs->tme) { // Fixed-point 12:4 int u, v; if (gs->fst) { u = v0.u * iw0 + v1.u * iw1 + v2.u * iw2; v = v0.v * iw0 + v1.v * iw1 + v2.v * iw2; } else { float s = v0.s * iw0 + v1.s * iw1 + v2.s * iw2; float t = v0.t * iw0 + v1.t * iw1 + v2.t * iw2; float q = v0.q * iw0 + v1.q * iw1 + v2.q * iw2; float uf = (s / q) * gs->ctx->usize; float vf = (t / q) * gs->ctx->vsize; // Convert to 12:4 fixed-point u = uf * (1 << 4); v = vf * (1 << 4); } uint32_t f = fr | (fg << 8) | (fb << 16) | (fa << 24); uint32_t t = gs_read_tb(gs, u, v); t = gs_to_rgba32(gs, t, gs->ctx->tbpsm); t = gs_apply_function(gs, t, f); fr = t & 0xff; fg = (t >> 8) & 0xff; fb = (t >> 16) & 0xff; fa = t >> 24; } if (gs->fge) { int f = roundf(v0.fog * iw0 + v1.fog * iw1 + v2.fog * iw2); fr = ((f * fr) >> 8) + (((255 - f) * fogr) >> 8); fg = ((f * fg) >> 8) + (((255 - f) * fogg) >> 8); fb = ((f * fb) >> 8) + (((255 - f) * fogb) >> 8); } uint32_t fz = roundf(v0.z * iw0 + v1.z * iw1 + v2.z * iw2); uint32_t fc = fr | (fg << 8) | (fb << 16) | (fa << 24); gs_draw_pixel(gs, p.x >> 4, p.y >> 4, fz, fc); // One step to the right w0 += a12; w1 += a20; w2 += a01; } // One row step w0_row += b12; w1_row += b20; w2_row += b01; } // gs_draw_wireframe(gs, gs->vq[0], gs->vq[1]); // gs_draw_wireframe(gs, gs->vq[1], gs->vq[2]); // gs_draw_wireframe(gs, gs->vq[2], gs->vq[0]); } int32_t gs_lerp(int32_t x, int32_t u1, int32_t x1, int32_t u2, int32_t x2) { if (!(x2 - x1)) return u1; int64_t b = (int64_t)u1 * (x2 - x); b += (int64_t)u2 * (x - x1); return b / (x2 - x1); } int32_t gs_step(int32_t u1, int32_t x1, int32_t u2, int32_t x2, int64_t mult) { if (!(x2 - x1)) return ((u2 - u1) * mult); return ((u2 - u1) * mult)/(x2 - x1); } float gs_lerpf(int32_t x, float u1, int32_t x1, float u2, int32_t x2) { if (x2 < x1) { int temp = x1; x1 = x2; x2 = temp; } float b = u1 * (x2 - x); b += u2 * (x - x1); if (!(x2 - x1)) return u1; return b / (x2 - x1); } float gs_stepf(float u1, int32_t x1, float u2, int32_t x2, int64_t mult) { if (!(x2 - x1)) return ((u2 - u1) * mult); return ((u2 - u1) * mult)/(x2 - x1); } void render_sprite(struct ps2_gs* gs, void* udata) { struct gs_vertex v0 = gs->vq[0]; struct gs_vertex v1 = gs->vq[1]; v0.x -= gs->ctx->ofx; v1.x -= gs->ctx->ofx; v0.y -= gs->ctx->ofy; v1.y -= gs->ctx->ofy; int32_t scax0 = gs->ctx->scax0 << 4; int32_t scay0 = gs->ctx->scay0 << 4; int32_t scax1 = (gs->ctx->scax1 + 1) << 4; int32_t scay1 = (gs->ctx->scay1 + 1) << 4; int32_t xmin = ((std::max(std::min(v0.x, v1.x), scax0) + 8) >> 4) << 4; int32_t ymin = ((std::max(std::min(v0.y, v1.y), scay0) + 8) >> 4) << 4; int32_t xmax = ((std::min(std::max(v0.x, v1.x), scax1) + 8) >> 4) << 4; int32_t ymax = ((std::min(std::max(v0.y, v1.y), scay1) + 8) >> 4) << 4; // printf("sprite: v0=(%d,%d) v1=(%d,%d) sca1=(%04x,%04x) rsca1=(%04x,%04x)\n", // v0.x >> 4, v0.y >> 4, // v1.x >> 4, v1.y >> 4, // scax1, scay1, // gs->ctx->scax1 << 4, // gs->ctx->scay1 << 4 // ); // if (gs->tme) // 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", // v0.x, v0.y, // v1.x, v1.y, // gs->ctx->tbpsm, // gs->ctx->tbw, // gs->ctx->tbp0, // gs->ctx->fbp >> 6, // gs->ctx->fbpsm, // gs->ctx->zbp >> 6, // gs->ctx->zbpsm, // gs->ctx->zte, // gs->ctx->zbmsk, // gs->ctx->cbp >> 6, // gs->ctx->cbpsm // ); // U and S values at the start of a row float row_u = gs_lerpf(xmin, v0.u, v0.x, v1.u, v1.x); float row_s = gs_lerpf(xmin, v0.s, v0.x, v1.s, v1.x); // V and T values at the start of the entire quad float v = gs_lerpf(ymin, v0.v, v0.y, v1.v, v1.y); float t = gs_lerpf(ymin, v0.t, v0.y, v1.t, v1.y); // Step values for U and V float u_step = gs_stepf(v0.u, v0.x, v1.u, v1.x, 16); float v_step = gs_stepf(v0.v, v0.y, v1.v, v1.y, 16); float s_step = gs_stepf(v0.s, v0.x, v1.s, v1.x, 16); float t_step = gs_stepf(v0.t, v0.y, v1.t, v1.y, 16); // Static values const float q = v1.q; const int z = v1.z; int a = v1.a; for (int y = ymin; y < ymax; y += 16) { float u = row_u; float s = row_s; for (int x = xmin; x < xmax; x += 16) { uint32_t c = v1.rgbaq & 0xffffffff; if (gs->tme) { if (!gs->fst) { u = ((s / q) * gs->ctx->usize) * 16.0; v = ((t / q) * gs->ctx->vsize) * 16.0; } c = gs_read_tb(gs, u, v); c = gs_to_rgba32(gs, c, gs->ctx->tbpsm); c = gs_apply_function(gs, c, v1.rgbaq & 0xffffffff); a = c >> 24; } gs_draw_pixel(gs, x >> 4, y >> 4, z, c); u += u_step; s += s_step; } v += v_step; t += t_step; } // struct gs_vertex dv0 = gs->vq[0]; // struct gs_vertex dv1 = gs->vq[0]; // struct gs_vertex dv2 = gs->vq[1]; // struct gs_vertex dv3 = gs->vq[1]; // dv1.x = gs->vq[1].x; // dv1.y = gs->vq[0].y; // dv3.x = gs->vq[0].x; // dv3.y = gs->vq[1].y; // gs_draw_wireframe(gs, dv0, dv1); // gs_draw_wireframe(gs, dv1, dv2); // gs_draw_wireframe(gs, dv2, dv3); // gs_draw_wireframe(gs, dv3, dv0); } static inline int gs_pixels_to_size(int fmt, int px) { switch (fmt) { case GS_PSMCT32: case GS_PSMCT24: case GS_PSMT8H: case GS_PSMT4HL: case GS_PSMT4HH: return px; case GS_PSMCT16: case GS_PSMCT16S: return px >> 1; case GS_PSMT8: return px >> 2; case GS_PSMT4: return px >> 3; } return px; } static inline void gs_write_psmct32(struct ps2_gs* gs, software_thread_state* ctx, uint32_t data) { uint32_t addr = psmct32_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy); ctx->dx++; gs->vram[addr & 0xfffff] = data; if (ctx->dx == (ctx->rrw + ctx->dsax)) { ctx->dx = ctx->dsax; ctx->dy++; } } static inline void gs_write_psmt4hh(struct ps2_gs* gs, software_thread_state* ctx, uint32_t data) { uint32_t addr = psmct32_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy); ctx->dx++; gs->vram[addr] = (gs->vram[addr] & 0x0fffffff) | (data << 28); if (ctx->dx == (ctx->rrw + ctx->dsax)) { ctx->dx = ctx->dsax; ctx->dy++; } } static inline void gs_write_psmt4hl(struct ps2_gs* gs, software_thread_state* ctx, uint32_t data) { uint32_t addr = psmct32_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy); ctx->dx++; gs->vram[addr] = (gs->vram[addr] & 0xf0ffffff) | (data << 24); if (ctx->dx == (ctx->rrw + ctx->dsax)) { ctx->dx = ctx->dsax; ctx->dy++; } } static inline void gs_write_psmt4(struct ps2_gs* gs, software_thread_state* ctx, uint32_t index) { uint32_t addr = psmt4_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy) & 0xfffff; int idx = (ctx->dx & 31) + ((ctx->dy & 3) * 32); int shift = psmt4_shift[idx]; uint32_t mask = 0xful << shift; gs->vram[addr] = (gs->vram[addr] & ~mask) | (index << shift); ctx->dx++; if (ctx->dx == (ctx->rrw + ctx->dsax)) { ctx->dx = ctx->dsax; ctx->dy++; } } static inline void gs_write_psmt8(struct ps2_gs* gs, software_thread_state* ctx, uint32_t index) { uint32_t addr = psmt8_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy) & 0xfffff; uint8_t* vram = (uint8_t*)(&gs->vram[addr]); int idx = (ctx->dx & 15) + ((ctx->dy & 3) * 16); vram[psmt8_shift[idx]] = index; ctx->dx++; if (ctx->dx == (ctx->rrw + ctx->dsax)) { ctx->dx = ctx->dsax; ctx->dy++; } } static inline void gs_write_psmt8h(struct ps2_gs* gs, software_thread_state* ctx, uint32_t data) { uint32_t addr = psmct32_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy); ctx->dx++; gs->vram[addr] = (gs->vram[addr] & 0x00ffffff) | (data << 24); if (ctx->dx == (ctx->rrw + ctx->dsax)) { ctx->dx = ctx->dsax; ctx->dy++; } } static inline void gs_store_hwreg_psmt4(struct ps2_gs* gs, software_thread_state* ctx) { for (int i = 0; i < 16; i++) { uint64_t index = (gs->hwreg >> (i * 4)) & 0xf; gs_write_psmt4(gs, ctx, index); } } static inline void gs_store_hwreg_psmt4hh(struct ps2_gs* gs, software_thread_state* ctx) { for (int i = 0; i < 16; i++) { uint64_t index = (gs->hwreg >> (i * 4)) & 0xf; gs_write_psmt4hh(gs, ctx, index); } } static inline void gs_store_hwreg_psmt4hl(struct ps2_gs* gs, software_thread_state* ctx) { for (int i = 0; i < 16; i++) { uint64_t index = (gs->hwreg >> (i * 4)) & 0xf; gs_write_psmt4hl(gs, ctx, index); } } static inline void gs_store_hwreg_psmt8(struct ps2_gs* gs, software_thread_state* ctx) { for (int i = 0; i < 8; i++) { uint64_t index = (gs->hwreg >> (i * 8)) & 0xff; gs_write_psmt8(gs, ctx, index); } } static inline void gs_store_hwreg_psmt8h(struct ps2_gs* gs, software_thread_state* ctx) { for (int i = 0; i < 8; i++) { uint64_t index = (gs->hwreg >> (i * 8)) & 0xff; gs_write_psmt8h(gs, ctx, index); } } static inline void gs_store_hwreg_psmct32(struct ps2_gs* gs, software_thread_state* ctx) { uint64_t data[2] = { gs->hwreg & 0xffffffff, gs->hwreg >> 32 }; for (int i = 0; i < 2; i++) { gs_write_psmct32(gs, ctx, data[i]); } } static inline void gs_write_psmct24(struct ps2_gs* gs, software_thread_state* ctx, uint32_t data) { uint32_t addr = psmct32_addr(ctx->dbp, ctx->dbw, ctx->dx++, ctx->dy) & 0xfffff; gs->vram[addr] = (gs->vram[addr] & 0xff000000) | (data & 0xffffff); if (ctx->dx == (ctx->rrw + ctx->dsax)) { ctx->dx = ctx->dsax; ctx->dy++; } } static inline void gs_store_hwreg_psmct24(struct ps2_gs* gs, software_thread_state* ctx) { switch (ctx->psmct24_shift++) { case 0: { gs_write_psmct24(gs, ctx, gs->hwreg); gs_write_psmct24(gs, ctx, gs->hwreg >> 24); ctx->psmct24_data = gs->hwreg >> 48; } break; case 1: { gs_write_psmct24(gs, ctx, ctx->psmct24_data | ((gs->hwreg & 0xff) << 16)); gs_write_psmct24(gs, ctx, gs->hwreg >> 8); gs_write_psmct24(gs, ctx, gs->hwreg >> 32); ctx->psmct24_data = gs->hwreg >> 56; } break; case 2: { gs_write_psmct24(gs, ctx, ctx->psmct24_data | ((gs->hwreg & 0xffff) << 8)); gs_write_psmct24(gs, ctx, gs->hwreg >> 16); gs_write_psmct24(gs, ctx, gs->hwreg >> 40); ctx->psmct24_shift = 0; } break; } } static inline void gs_write_psmct16(struct ps2_gs* gs, software_thread_state* ctx, uint32_t index) { uint32_t addr = psmct16_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy) & 0xfffff; uint16_t* vram = (uint16_t*)(&gs->vram[addr]); int idx = (ctx->dx & 15) + ((ctx->dy & 1) * 16); vram[psmct16_shift[idx]] = index; ctx->dx++; if (ctx->dx == (ctx->rrw + ctx->dsax)) { ctx->dx = ctx->dsax; ctx->dy++; } } static inline void gs_write_psmct16s(struct ps2_gs* gs, software_thread_state* ctx, uint32_t index) { uint32_t addr = psmct16s_addr(ctx->dbp, ctx->dbw, ctx->dx, ctx->dy) & 0xfffff; uint16_t* vram = (uint16_t*)(&gs->vram[addr]); int idx = (ctx->dx & 15) + ((ctx->dy & 1) * 16); vram[psmct16_shift[idx]] = index; ctx->dx++; if (ctx->dx == (ctx->rrw + ctx->dsax)) { ctx->dx = ctx->dsax; ctx->dy++; } } static inline void gs_store_hwreg_psmct16(struct ps2_gs* gs, software_thread_state* ctx) { for (int i = 0; i < 4; i++) { uint64_t p = (gs->hwreg >> (i * 16)) & 0xffff; gs_write_psmct16(gs, ctx, p); } } static inline void gs_store_hwreg_psmct16s(struct ps2_gs* gs, software_thread_state* ctx) { for (int i = 0; i < 4; i++) { uint64_t p = (gs->hwreg >> (i * 16)) & 0xffff; gs_write_psmct16s(gs, ctx, p); } } void transfer_flush_buffer(software_thread_state* ctx) { // printf("gs: Flushing transfer... %d (%d)\n", ctx->transfer_buffer.size(), ctx->transfer_size); ctx->render_mtx.lock(); for (int i = 0; i < ctx->transfer_buffer.size(); i++) { ctx->gs->hwreg = ctx->transfer_buffer[i]; switch (ctx->dpsm) { case GS_PSMCT32: { gs_store_hwreg_psmct32(ctx->gs, ctx); } break; case GS_PSMCT24: { gs_store_hwreg_psmct24(ctx->gs, ctx); } break; case GS_PSMCT16: { gs_store_hwreg_psmct16(ctx->gs, ctx); } break; case GS_PSMCT16S: { gs_store_hwreg_psmct16s(ctx->gs, ctx); } break; case GS_PSMT8: { gs_store_hwreg_psmt8(ctx->gs, ctx); } break; case GS_PSMT8H: { gs_store_hwreg_psmt8h(ctx->gs, ctx); } break; case GS_PSMT4: { gs_store_hwreg_psmt4(ctx->gs, ctx); } break; case GS_PSMT4HH: { gs_store_hwreg_psmt4hh(ctx->gs, ctx); } break; case GS_PSMT4HL: { gs_store_hwreg_psmt4hl(ctx->gs, ctx); } break; default: { gs_store_hwreg_psmct32(ctx->gs, ctx); } break; } } ctx->render_mtx.unlock(); } void transfer_start(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; if (ctx->transfer_size != 0) { transfer_flush_buffer(ctx); } ctx->dbp = (gs->bitbltbuf >> 32) & 0x3fff; ctx->dbw = (gs->bitbltbuf >> 48) & 0x3f; ctx->dpsm = (gs->bitbltbuf >> 56) & 0x3f; ctx->sbp = (gs->bitbltbuf >> 0) & 0x3fff; ctx->sbw = (gs->bitbltbuf >> 16) & 0x3f; ctx->spsm = (gs->bitbltbuf >> 24) & 0x3f; ctx->dsax = (gs->trxpos >> 32) & 0x7ff; ctx->dsay = (gs->trxpos >> 48) & 0x7ff; ctx->ssax = (gs->trxpos >> 0) & 0x7ff; ctx->ssay = (gs->trxpos >> 16) & 0x7ff; ctx->dir = (gs->trxpos >> 59) & 3; ctx->rrw = (gs->trxreg >> 0) & 0xfff; ctx->rrh = (gs->trxreg >> 32) & 0xfff; ctx->xdir = gs->trxdir & 3; ctx->dx = ctx->dsax; ctx->dy = ctx->dsay; ctx->sx = ctx->ssax; ctx->sy = ctx->ssay; ctx->px = 0; unsigned int pixels = ctx->rrw * ctx->rrh; // size in pixels = rrw*rrh // size in dwords (psmct32): pixels / 2 // size in dwords (psmct24): floor(pixels / 3) + 1 // size in dwords (psmct16/s): pixels / 4 // size in dwords (psmt8/h): pixels / 8 // size in dwords (psmt4/h/hl/hh): pixels / 16 switch (ctx->dpsm) { case GS_PSMZ32: case GS_PSMCT32: { ctx->transfer_size = pixels >> 1; } break; case GS_PSMCT24: { ctx->transfer_size = (pixels / 3) + 1; } break; case GS_PSMCT16: case GS_PSMCT16S: { ctx->transfer_size = pixels >> 2; } break; case GS_PSMT8: case GS_PSMT8H: { ctx->transfer_size = pixels >> 3; } break; case GS_PSMT4: case GS_PSMT4HL: case GS_PSMT4HH: { ctx->transfer_size = pixels >> 4; } break; // Assume 32-bit (Viewtiful Joe, Viewtiful Joe 2, etc.) default: { ctx->transfer_size = pixels >> 1; // printf("gs: Unknown transfer format %02x\n", ctx->dpsm); // exit(1); } break; } ctx->transfer_buffer.clear(); ctx->transfer_buffer.reserve(ctx->transfer_size); ctx->psmct24_data = 0; ctx->psmct24_shift = 0; // printf("dbp=%x (%x) dbw=%d (%d) dpsm=%02x dsa=(%d,%d) rr=(%d,%d) xdir=%d transfer_size=%d\n", // ctx->dbp, ctx->dbp, // ctx->dbw, ctx->dbw, // ctx->dpsm, // ctx->dsax, // ctx->dsay, // ctx->rrw, // ctx->rrh, // ctx->xdir, // ctx->transfer_size // ); if (ctx->xdir == 2) { ctx->render_mtx.lock(); ctx->stats.texture_blits++; software_thread_vram_blit(gs, ctx); ctx->render_mtx.unlock(); } else if (ctx->xdir == 1) { printf("gs: Read transfer requested\n"); // exit(1); } else { ctx->stats.texture_uploads++; } } void transfer_write(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; ctx->transfer_buffer.push_back(gs->hwreg); ctx->transfer_size--; if (ctx->transfer_size == 0) { transfer_flush_buffer(ctx); } } void transfer_read(struct ps2_gs* gs, void* udata) { gs->hwreg = 0; } extern "C" void software_thread_render_point(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; ctx->stats.points++; ctx->stats.primitives++; ctx->queue_mtx.lock(); ctx->render_queue.push(render_data()); render_data& rdata = ctx->render_queue.back(); rdata.gs = *gs; rdata.prim = 0; ctx->queue_mtx.unlock(); } extern "C" void software_thread_render_line(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; ctx->stats.lines++; ctx->stats.primitives++; ctx->queue_mtx.lock(); ctx->render_queue.push(render_data()); render_data& rdata = ctx->render_queue.back(); rdata.gs = *gs; rdata.prim = 1; ctx->queue_mtx.unlock(); } extern "C" void software_thread_render_triangle(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; ctx->stats.triangles++; ctx->stats.primitives++; ctx->queue_mtx.lock(); ctx->render_queue.push(render_data()); render_data& rdata = ctx->render_queue.back(); rdata.gs = *gs; rdata.prim = 2; ctx->queue_mtx.unlock(); } extern "C" void software_thread_render_sprite(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; ctx->stats.sprites++; ctx->stats.primitives++; ctx->queue_mtx.lock(); ctx->render_queue.push(render_data()); render_data& rdata = ctx->render_queue.back(); rdata.gs = *gs; rdata.prim = 3; ctx->queue_mtx.unlock(); } extern "C" void software_thread_transfer_start(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; // Block until queue is empty while (true) { ctx->queue_mtx.lock(); if (ctx->render_queue.empty()) { ctx->queue_mtx.unlock(); break; } ctx->queue_mtx.unlock(); } transfer_start(gs, ctx); } extern "C" void software_thread_transfer_write(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; transfer_write(gs, ctx); } extern "C" void software_thread_transfer_read(struct ps2_gs* gs, void* udata) { software_thread_state* ctx = (software_thread_state*)udata; // Block until queue is empty while (true) { ctx->queue_mtx.lock(); if (ctx->render_queue.empty()) { ctx->queue_mtx.unlock(); break; } ctx->queue_mtx.unlock(); } // ctx->render_mtx.lock(); transfer_read(gs, ctx); // ctx->render_mtx.unlock(); } void gs_blit_dispfb_deinterlace_frame(software_thread_state* ctx, int dfb) { // Get current field int odd = ((ctx->gs->csr >> 13) & 1) == 0; switch (ctx->disp_fmt) { case GS_PSMCT32: case GS_PSMCT24: { for (int y = 0; y < ctx->tex_h / 2; y++) { for (int x = 0; x < ctx->tex_w; x++) { uint32_t* dst = ctx->buf + x + (((y * 2) + odd) * ctx->tex_w); *dst = gs_read_dispfb(ctx->gs, x, y, dfb); } } } break; case GS_PSMCT16: case GS_PSMCT16S: { for (int y = 0; y < ctx->tex_h / 2; y++) { for (int x = 0; x < ctx->tex_w; x++) { uint16_t* dst = ((uint16_t*)ctx->buf) + x + (((y * 2) + odd) * ctx->tex_w); *dst = gs_read_dispfb(ctx->gs, x, y, dfb); } } } break; } } void gs_blit_dispfb_deinterlace_field(software_thread_state* ctx, int dfb) { // Get current field int odd = ((ctx->gs->csr >> 13) & 1) == 0; switch (ctx->disp_fmt) { case GS_PSMCT32: case GS_PSMCT24: { for (int y = 0; y < ctx->tex_h / 2; y++) { for (int x = 0; x < ctx->tex_w; x++) { uint32_t* dst = ctx->buf + x + (((y * 2) + odd) * ctx->tex_w); *dst = gs_read_dispfb(ctx->gs, x, (y * 2) + odd, dfb); } } } break; case GS_PSMCT16: case GS_PSMCT16S: { for (int y = 0; y < ctx->tex_h / 2; y++) { for (int x = 0; x < ctx->tex_w; x++) { uint16_t* dst = ((uint16_t*)ctx->buf) + x + (((y * 2) + odd) * ctx->tex_w); *dst = gs_read_dispfb(ctx->gs, x, (y * 2) + odd, dfb); } } } break; } } void gs_blit_dispfb_no_deinterlace(software_thread_state* ctx, int dfb) { switch (ctx->disp_fmt) { case GS_PSMCT32: case GS_PSMCT24: { for (int y = 0; y < ctx->tex_h; y++) { for (int x = 0; x < ctx->tex_w; x++) { uint32_t* dst = ctx->buf + x + (y * ctx->tex_w); *dst = gs_read_dispfb(ctx->gs, x, y, dfb); } } } break; case GS_PSMCT16: case GS_PSMCT16S: { for (int y = 0; y < ctx->tex_h; y++) { for (int x = 0; x < ctx->tex_w; x++) { uint16_t* dst = ((uint16_t*)ctx->buf) + x + (y * ctx->tex_w); *dst = gs_read_dispfb(ctx->gs, x, y, dfb); } } } break; } } // This starts a copy pass and transfers our vertex/index buffers and our // display frame void software_thread_begin_render(void* udata, SDL_GPUCommandBuffer* command_buffer) { software_thread_state* ctx = (software_thread_state*)udata; // Block until queue is empty while (true) { ctx->queue_mtx.lock(); if (ctx->render_queue.empty()) { ctx->queue_mtx.unlock(); break; } ctx->queue_mtx.unlock(); } int en1 = ctx->gs->pmode & 1; int en2 = (ctx->gs->pmode >> 1) & 1; uint32_t dfbp = 0; int dfb = 0; if (en1) { dfb = 0; dfbp = ctx->gs->dispfb1; } else if (en2) { dfb = 1; dfbp = ctx->gs->dispfb2; } if (!ctx->tex_w) { return; } // 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); if (ctx->gs->smode2 & 1) { // Need to deinterlace if (ctx->gs->smode2 & 2) { gs_blit_dispfb_deinterlace_frame(ctx, dfb); } else { gs_blit_dispfb_no_deinterlace(ctx, dfb); } } else { gs_blit_dispfb_no_deinterlace(ctx, dfb); } uint32_t* ptr = ctx->buf; SDL_Rect size, rect; rect.w = ctx->tex_w; rect.h = ctx->tex_h; SDL_GetWindowSize(ctx->window, &size.w, &size.h); float scale = ctx->integer_scaling ? floorf(ctx->scale) : ctx->scale; switch (ctx->aspect_mode) { case RENDERER_ASPECT_NATIVE: { rect.w *= scale; rect.h *= scale; } break; case RENDERER_ASPECT_4_3: { rect.w *= scale; rect.h = (float)rect.w * (3.0f / 4.0f); } break; case RENDERER_ASPECT_16_9: { rect.w *= scale; rect.h = (float)rect.w * (9.0f / 16.0f); } break; case RENDERER_ASPECT_5_4: { rect.w *= scale; rect.h = (float)rect.w * (4.0f / 5.0f); } break; case RENDERER_ASPECT_STRETCH: { rect.w = size.w; rect.h = size.h; } break; case RENDERER_ASPECT_AUTO: case RENDERER_ASPECT_STRETCH_KEEP: { rect.h = size.h; rect.w = (float)rect.h * (4.0f / 3.0f); } break; } ctx->disp_w = rect.w; ctx->disp_h = rect.h; rect.x = (size.w / 2) - (rect.w / 2); rect.y = (size.h / 2) - (rect.h / 2); float x0 = (rect.x / ((float)size.w / 2.0f)) - 1.0f; float y0 = (rect.y / ((float)size.h / 2.0f)) - 1.0f; float x1 = ((rect.x + rect.w) / ((float)size.w / 2.0f)) - 1.0f; float y1 = ((rect.y + rect.h) / ((float)size.h / 2.0f)) - 1.0f; // Create a transfer buffer to upload our vertex buffer and index buffer SDL_GPUTransferBufferCreateInfo btbci = { .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = sizeof(gpu_vertex) * 4 + sizeof(Uint16) * 6, }; SDL_GPUTransferBuffer* buffer_tb = SDL_CreateGPUTransferBuffer(ctx->device, &btbci); // fill the transfer buffer gpu_vertex* vertices = (gpu_vertex*)SDL_MapGPUTransferBuffer(ctx->device, buffer_tb, false); vertices[0] = { x0, y1, 0.0, 1.0 }; // Top left vertices[1] = { x1, y1, 1.0, 1.0 }; // Top right vertices[2] = { x1, y0, 1.0, 0.0 }; // Bottom right vertices[3] = { x0, y0, 0.0, 0.0 }; // Bottom left Uint16* indices = (Uint16*)&vertices[4]; indices[0] = 0; indices[1] = 1; indices[2] = 2; indices[3] = 0; indices[4] = 2; indices[5] = 3; SDL_UnmapGPUTransferBuffer(ctx->device, buffer_tb); // start a copy pass SDL_GPUCopyPass* cp = SDL_BeginGPUCopyPass(command_buffer); // Upload vertex buffer (at offset 0) SDL_GPUTransferBufferLocation tbl = { .transfer_buffer = buffer_tb, .offset = 0, }; SDL_GPUBufferRegion br = { .buffer = ctx->vertex_buffer, .offset = 0, .size = sizeof(gpu_vertex) * 4, }; SDL_UploadToGPUBuffer(cp, &tbl, &br, false); tbl = { .transfer_buffer = buffer_tb, .offset = sizeof(gpu_vertex) * 4, }; br = { .buffer = ctx->index_buffer, .offset = 0, .size = sizeof(Uint16) * 6, }; // Upload index buffer (right after our vertex data) SDL_UploadToGPUBuffer(cp, &tbl, &br, false); int stride = 4; switch (ctx->disp_fmt) { case GS_PSMCT32: case GS_PSMCT24: { stride = 4; } break; case GS_PSMCT16: case GS_PSMCT16S: { stride = 2; } break; // Ignore unknown formats // default: { // printf("Unsupported display format %d\n", ctx->disp_fmt); // exit(1); // } break; } SDL_GPUTransferBufferCreateInfo ttbci = { .usage = SDL_GPU_TRANSFERBUFFERUSAGE_UPLOAD, .size = static_cast(ctx->tex_w) * static_cast(ctx->tex_h) * stride }; SDL_GPUTransferBuffer* texture_tb = SDL_CreateGPUTransferBuffer(ctx->device, &ttbci); // fill the transfer buffer void* data = SDL_MapGPUTransferBuffer(ctx->device, texture_tb, false); SDL_memcpy(data, ptr, (ctx->tex_w * stride) * ctx->tex_h); SDL_UnmapGPUTransferBuffer(ctx->device, texture_tb); SDL_GPUTextureTransferInfo tti = { .transfer_buffer = texture_tb, .offset = 0, }; SDL_GPUTextureRegion tr = { .texture = ctx->texture, .w = static_cast(ctx->tex_w), .h = static_cast(ctx->tex_h), .d = 1, }; SDL_UploadToGPUTexture(cp, &tti, &tr, false); // end the copy pass SDL_EndGPUCopyPass(cp); SDL_ReleaseGPUTransferBuffer(ctx->device, buffer_tb); SDL_ReleaseGPUTransferBuffer(ctx->device, texture_tb); } void software_thread_render(void* udata, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass) { software_thread_state* ctx = (software_thread_state*)udata; if (!ctx->texture) return; ctx->stats.frames_rendered++; ctx->last_frame_stats = ctx->stats; ctx->stats.lines = 0; ctx->stats.points = 0; ctx->stats.triangles = 0; ctx->stats.sprites = 0; ctx->stats.primitives = 0; ctx->stats.texture_uploads = 0; ctx->stats.texture_blits = 0; SDL_BindGPUGraphicsPipeline(render_pass, ctx->pipeline); // bind the vertex buffer SDL_GPUBufferBinding bb[1]{}; bb[0].buffer = ctx->vertex_buffer; bb[0].offset = 0; SDL_BindGPUVertexBuffers(render_pass, 0, bb, 1); bb[0].buffer = ctx->index_buffer; bb[0].offset = 0; SDL_BindGPUIndexBuffer(render_pass, bb, SDL_GPU_INDEXELEMENTSIZE_16BIT); SDL_GPUTextureSamplerBinding tsb = { .texture = ctx->texture, .sampler = ctx->bilinear ? ctx->sampler[1] : ctx->sampler[0], }; SDL_BindGPUFragmentSamplers(render_pass, 0, &tsb, 1); // issue a draw call SDL_DrawGPUIndexedPrimitives(render_pass, 6, 1, 0, 0, 0); } void software_thread_end_render(void* udata, SDL_GPUCommandBuffer* command_buffer) { // Nothing for now } renderer_stats* software_thread_get_debug_stats(void* udata) { software_thread_state* ctx = (software_thread_state*)udata; return &ctx->last_frame_stats; } ================================================ FILE: src/gs/renderer/software_thread.hpp ================================================ #pragma once #include #include #include #include #include #include #include #include #include "gs/gs.h" #include "renderer.hpp" struct render_data { int prim; struct ps2_gs gs; }; struct gpu_vertex { float x, y, u, v; }; struct software_thread_state { std::queue render_queue; std::thread render_thr; std::mutex queue_mtx; std::mutex render_mtx; std::vector transfer_buffer; int transfer_index = 0; int transfer_size = 0; std::atomic end_signal; unsigned int sbp = 0, dbp = 0; unsigned int sbw = 0, dbw = 0; unsigned int spsm = 0, dpsm = 0; unsigned int ssax = 0, ssay = 0, dsax = 0, dsay = 0; unsigned int rrh = 0, rrw = 0; unsigned int dir = 0, xdir = 0; unsigned int sx = 0, sy = 0; unsigned int dx = 0, dy = 0, px = 0; uint32_t psmct24_data = 0; uint32_t psmct24_shift = 0; // SDL stuff SDL_Window* window = nullptr; SDL_GPUDevice* device = nullptr; SDL_GPUBuffer* vertex_buffer = nullptr; SDL_GPUBuffer* index_buffer = nullptr; SDL_GPUTexture* texture = nullptr; SDL_GPUSampler* sampler[2] = { nullptr }; SDL_GPUGraphicsPipeline* pipeline = nullptr; // Context SDL_GPUCommandBuffer* cb = nullptr; SDL_GPURenderPass* rp = nullptr; struct ps2_gs* gs = nullptr; unsigned int window_x = 0, window_y = 0; unsigned int window_w = 0, window_h = 0; int tex_w = 0; int tex_h = 0; int disp_w = 0; int disp_h = 0; int disp_fmt = 0; int aspect_mode = 0; // Native bool integer_scaling = false; float scale = 1.5; bool bilinear = true; unsigned int frame = 0; uint32_t* buf = nullptr; renderer_stats stats = { 0 }; renderer_stats last_frame_stats = { 0 }; }; void software_thread_init(void* udata, struct ps2_gs* gs, SDL_Window* window, SDL_GPUDevice* device); void software_thread_destroy(void* udata); void software_thread_set_size(void* udata, int width, int height); void software_thread_set_scale(void* udata, float scale); void software_thread_set_aspect_mode(void* udata, int aspect_mode); void software_thread_set_integer_scaling(void* udata, bool integer_scaling); void software_thread_set_bilinear(void* udata, bool bilinear); void software_thread_get_viewport_size(void* udata, int* w, int* h); void software_thread_get_display_size(void* udata, int* w, int* h); void software_thread_get_display_format(void* udata, int* fmt); void software_thread_get_interlace_mode(void* udata, int* mode); void software_thread_set_window_rect(void* udata, int x, int y, int w, int h); void* software_thread_get_buffer_data(void* udata, int* w, int* h, int* bpp); renderer_stats* software_thread_get_debug_stats(void* udata); const char* software_thread_get_name(void* udata); void software_thread_begin_render(void* udata, SDL_GPUCommandBuffer* command_buffer); void software_thread_render(void* udata, SDL_GPUCommandBuffer* command_buffer, SDL_GPURenderPass* render_pass); void software_thread_end_render(void* udata, SDL_GPUCommandBuffer* command_buffer); extern "C" { void software_thread_render_point(struct ps2_gs* gs, void* udata); void software_thread_render_line(struct ps2_gs* gs, void* udata); void software_thread_render_triangle(struct ps2_gs* gs, void* udata); void software_thread_render_sprite(struct ps2_gs* gs, void* udata); void software_thread_transfer_start(struct ps2_gs* gs, void* udata); void software_thread_transfer_write(struct ps2_gs* gs, void* udata); void software_thread_transfer_read(struct ps2_gs* gs, void* udata); } ================================================ FILE: src/iop/bus.c ================================================ #include #include #include #include "bus.h" #include "bus_decl.h" #define printf(fmt, ...)(0) struct iop_bus* iop_bus_create(void) { return malloc(sizeof(struct iop_bus)); } void iop_bus_init(struct iop_bus* bus, const char* bios_path) { memset(bus, 0, sizeof(struct iop_bus)); for (int i = 0; i < 0x10000; i++) { bus->fastmem_r_table[i] = NULL; bus->fastmem_w_table[i] = NULL; } } #define RAM_MAX_SIZE 0x1000000 void iop_bus_init_fastmem(struct iop_bus* bus, int ram_size) { memset(bus->fastmem_r_table, 0, sizeof(bus->fastmem_r_table)); memset(bus->fastmem_w_table, 0, sizeof(bus->fastmem_w_table)); // BIOS for (int i = 0; i < 0x200; i++) { bus->fastmem_r_table[i+0xfe00] = bus->bios->buf + (i * 0x2000); } // IOP RAM int mask = ram_size - 1; for (int i = 0; i < (RAM_MAX_SIZE / 0x2000); i++) { bus->fastmem_r_table[i+0x0000] = bus->iop_ram->buf + ((i * 0x2000) & mask); bus->fastmem_w_table[i+0x0000] = bus->iop_ram->buf + ((i * 0x2000) & mask); } } void iop_bus_init_bios(struct iop_bus* bus, struct ps2_bios* bios) { bus->bios = bios; } void iop_bus_init_rom1(struct iop_bus* bus, struct ps2_bios* rom1) { bus->rom1 = rom1; } void iop_bus_init_rom2(struct iop_bus* bus, struct ps2_bios* rom2) { bus->rom2 = rom2; } void iop_bus_init_iop_ram(struct iop_bus* bus, struct ps2_ram* iop_ram) { bus->iop_ram = iop_ram; } void iop_bus_init_iop_spr(struct iop_bus* bus, struct ps2_ram* iop_spr) { bus->iop_spr = iop_spr; } void iop_bus_init_sif(struct iop_bus* bus, struct ps2_sif* sif) { bus->sif = sif; } void iop_bus_init_dma(struct iop_bus* bus, struct ps2_iop_dma* dma) { bus->dma = dma; } void iop_bus_init_intc(struct iop_bus* bus, struct ps2_iop_intc* intc) { bus->intc = intc; } void iop_bus_init_timers(struct iop_bus* bus, struct ps2_iop_timers* timers) { bus->timers = timers; } void iop_bus_init_cdvd(struct iop_bus* bus, struct ps2_cdvd* cdvd) { bus->cdvd = cdvd; } void iop_bus_init_sio2(struct iop_bus* bus, struct ps2_sio2* sio2) { bus->sio2 = sio2; } void iop_bus_init_spu2(struct iop_bus* bus, struct ps2_spu2* spu2) { bus->spu2 = spu2; } void iop_bus_init_usb(struct iop_bus* bus, struct ps2_usb* usb) { bus->usb = usb; } void iop_bus_init_fw(struct iop_bus* bus, struct ps2_fw* fw) { bus->fw = fw; } void iop_bus_init_sbus(struct iop_bus* bus, struct ps2_sbus* sbus) { bus->sbus = sbus; } void iop_bus_init_dev9(struct iop_bus* bus, struct ps2_dev9* dev9) { bus->dev9 = dev9; } void iop_bus_init_speed(struct iop_bus* bus, struct ps2_speed* speed) { bus->speed = speed; } void iop_bus_init_s14x_nand(struct iop_bus* bus, struct s14x_nand* nand) { bus->s14x_nand = nand; } void iop_bus_init_s14x_syscon(struct iop_bus* bus, struct s14x_syscon* syscon) { bus->s14x_syscon = syscon; } void iop_bus_init_s14x_sram(struct iop_bus* bus, struct s14x_sram* sram) { bus->s14x_sram = sram; } void iop_bus_init_s14x_link(struct iop_bus* bus, struct s14x_link* link) { bus->s14x_link = link; } void iop_bus_destroy(struct iop_bus* bus) { free(bus); } #define MAP_MEM_READ(b, l, u, d, n) \ if ((addr >= l) && (addr <= u)) return ps2_ ## d ## _read ## b (bus->n, addr - l); #define MAP_REG_READ(b, l, u, d, n) \ if ((addr >= l) && (addr <= u)) return ps2_ ## d ## _read ## b (bus->n, addr); #define MAP_MEM_WRITE(b, l, u, d, n) \ if ((addr >= l) && (addr <= u)) { ps2_ ## d ## _write ## b (bus->n, addr - l, data); return; } #define MAP_REG_WRITE(b, l, u, d, n) \ if ((addr >= l) && (addr <= u)) { ps2_ ## d ## _write ## b (bus->n, addr, data); return; } #define MAP_REG_READ_S14X(b, l, u, d, n) \ if (bus->n && (addr >= l) && (addr <= u)) return d ## _read (bus->n, addr - l); #define MAP_REG_WRITE_S14X(b, l, u, d, n) \ if (bus->n && (addr >= l) && (addr <= u)) { d ## _write (bus->n, addr - l, data); return; } #define MAP_MEM_READ_S14X(b, l, u, d, n) \ if (bus->n && (addr >= l) && (addr <= u)) return d ## _read ## b (bus->n, addr - l); #define MAP_MEM_WRITE_S14X(b, l, u, d, n) \ if (bus->n && (addr >= l) && (addr <= u)) { d ## _write ## b (bus->n, addr - l, data); return; } uint32_t iop_bus_read8(void* udata, uint32_t addr) { struct iop_bus* bus = (struct iop_bus*)udata; void* ptr = bus->fastmem_r_table[(addr & 0x1fffffff) >> 13]; if (ptr) return *((uint8_t*)(((uint8_t*)ptr) + (addr & 0x1fff))); // MAP_MEM_READ(8, 0x00000000, 0x001FFFFF, ram, iop_ram); // MAP_MEM_READ(8, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_MEM_READ(8, 0x1F800000, 0x1F8003FF, ram, iop_spr); MAP_REG_READ(8, 0x1F801070, 0x1F80107B, iop_intc, intc); MAP_REG_READ(8, 0x1F402004, 0x1F4020FF, cdvd, cdvd); MAP_REG_READ(8, 0x1F808200, 0x1F808280, sio2, sio2); MAP_MEM_READ(8, 0x1E000000, 0x1E3FFFFF, bios, rom1); MAP_MEM_READ(8, 0x1E400000, 0x1E7FFFFF, bios, rom2); MAP_REG_READ(8, 0x1F801460, 0x1F80147F, dev9, dev9); // System 147/148 mappings MAP_REG_READ_S14X(8, 0x10000000, 0x1000000F, s14x_syscon, s14x_syscon); MAP_REG_READ_S14X(8, 0x10800000, 0x108000FF, s14x_link, s14x_link); MAP_MEM_READ_S14X(8, 0x10C00000, 0x10C07FFF, s14x_sram, s14x_sram); MAP_REG_READ_S14X(8, 0x14000000, 0x1400000F, s14x_nand, s14x_nand); // System 147/148 syscon overlays retail SPEED MAP_REG_READ(8, 0x10000000, 0x1000FFFF, speed, speed); switch (addr) { // Required for T10000 TOOL BIOS // Otherwise the IOP hangs during initialization if the RAM size // is 8 MB or hangs with a stack overflow after init if the RAM // size is 16 MB case 0x1f803204: return 0x7c; } printf("iop_bus: Unhandled 8-bit read from physical address 0x%08x\n", addr); return 0; } uint32_t iop_bus_read16(void* udata, uint32_t addr) { struct iop_bus* bus = (struct iop_bus*)udata; void* ptr = bus->fastmem_r_table[(addr & 0x1fffffff) >> 13]; if (ptr) return *((uint16_t*)(((uint8_t*)ptr) + (addr & 0x1fff))); // MAP_MEM_READ(16, 0x1FC00000, 0x1FFFFFFF, bios, bios); // MAP_MEM_READ(16, 0x00000000, 0x001FFFFF, ram, iop_ram); MAP_MEM_READ(16, 0x1F800000, 0x1F8003FF, ram, iop_spr); MAP_REG_READ(16, 0x1F801070, 0x1F80107B, iop_intc, intc); MAP_REG_READ(32, 0x1F801100, 0x1F80112F, iop_timers, timers); MAP_REG_READ(32, 0x1F801480, 0x1F8014AF, iop_timers, timers); MAP_REG_READ(16, 0x1F801080, 0x1F8010EF, iop_dma, dma); MAP_REG_READ(16, 0x1F801500, 0x1F80155F, iop_dma, dma); MAP_REG_READ(16, 0x1F801570, 0x1F80157F, iop_dma, dma); MAP_REG_READ(16, 0x1F8010F0, 0x1F8010F8, iop_dma, dma); MAP_REG_READ(16, 0x1F900000, 0x1F9007FF, spu2, spu2); MAP_MEM_READ(16, 0x1E000000, 0x1E3FFFFF, bios, rom1); MAP_MEM_READ(16, 0x1E400000, 0x1E7FFFFF, bios, rom2); MAP_REG_READ(16, 0x1F801460, 0x1F80147F, dev9, dev9); MAP_REG_READ(16, 0x10000000, 0x1000FFFF, speed, speed); // System 147/148 mappings MAP_MEM_READ_S14X(16, 0x10C00000, 0x10C07FFF, s14x_sram, s14x_sram); // PSX DESR if (addr == 0x1000480c) return 0xffff; // 0x20 - PCMCIA (CXD9566) // 0x30 - Expansion bay if (addr == 0x1f80146e) { return 0x30; } // SPEED rev3 (Capabilities) // bit 0 - SMAP // bit 1 - ATA // bit 3 - UART // bit 4 - DVR // bit 5 - FLASH // if (addr == 0x10000004) { return 0x03; } // if (addr == 0x1000205c) { return 0xffff; } // if (addr == 0x1000205e) { return 0xffff; } if (addr == 0x1241c000) return 0xffff; printf("iop_bus: Unhandled 16-bit read from physical address 0x%08x\n", addr); return 0; } uint32_t iop_bus_read32(void* udata, uint32_t addr) { struct iop_bus* bus = (struct iop_bus*)udata; if (addr == 0xfffe0130) return 0xffffffff; void* ptr = bus->fastmem_r_table[(addr & 0x1fffffff) >> 13]; if (ptr) return *((uint32_t*)(((uint8_t*)ptr) + (addr & 0x1fff))); // MAP_MEM_READ(32, 0x00000000, 0x001FFFFF, ram, iop_ram); // MAP_MEM_READ(32, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_MEM_READ(32, 0x1F800000, 0x1F8003FF, ram, iop_spr); MAP_REG_READ(32, 0x1D000000, 0x1D00006F, sif, sif); MAP_REG_READ(32, 0x1F801070, 0x1F80107B, iop_intc, intc); MAP_REG_READ(32, 0x1F801080, 0x1F8010EF, iop_dma, dma); MAP_REG_READ(32, 0x1F801500, 0x1F80155F, iop_dma, dma); MAP_REG_READ(32, 0x1F801570, 0x1F80157F, iop_dma, dma); MAP_REG_READ(32, 0x1F8010F0, 0x1F8010F8, iop_dma, dma); MAP_REG_READ(32, 0x1F801100, 0x1F80112F, iop_timers, timers); MAP_REG_READ(32, 0x1F801480, 0x1F8014AF, iop_timers, timers); MAP_REG_READ(32, 0x1F808200, 0x1F808280, sio2, sio2); MAP_REG_READ(32, 0x1F801600, 0x1F8016FF, usb, usb); MAP_REG_READ(32, 0x1F808400, 0x1F80854F, fw, fw); MAP_MEM_READ(32, 0x1E000000, 0x1E3FFFFF, bios, rom1); MAP_MEM_READ(32, 0x1E400000, 0x1E7FFFFF, bios, rom2); MAP_REG_READ(32, 0x1F801460, 0x1F80147F, dev9, dev9); MAP_REG_READ(32, 0x10000000, 0x1000FFFF, speed, speed); // System 147/148 mappings MAP_MEM_READ_S14X(32, 0x10C00000, 0x10C07FFF, s14x_sram, s14x_sram); if (addr == 0x1f801450) return 0; if (addr == 0x1f801414) return 1; if (addr == 0x1f801560) return 0; if ((addr & 0xff000000) == 0x1e000000) return 0; if (addr == 0xfffe0130) return 0xffffffff; // Bloody Roar 4 Wrong IOP CDVD DMA // if ((addr & 0xff000000) == 0x0c000000) { *(uint8_t*)0 = 0; } printf("iop_bus: Unhandled 32-bit read from physical address 0x%08x\n", addr); return 0; } void iop_bus_write8(void* udata, uint32_t addr, uint32_t data) { struct iop_bus* bus = (struct iop_bus*)udata; void* ptr = bus->fastmem_w_table[(addr & 0x1fffffff) >> 13]; if (ptr) { *((uint8_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data; return; } // MAP_MEM_WRITE(8, 0x00000000, 0x001FFFFF, ram, iop_ram); // MAP_MEM_WRITE(8, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_MEM_WRITE(8, 0x1F800000, 0x1F8003FF, ram, iop_spr); MAP_REG_WRITE(8, 0x1F402004, 0x1F4020FF, cdvd, cdvd); MAP_REG_WRITE(8, 0x1F801070, 0x1F80107B, iop_intc, intc); MAP_REG_WRITE(32, 0x1F801080, 0x1F8010EF, iop_dma, dma); MAP_REG_WRITE(32, 0x1F801500, 0x1F80155F, iop_dma, dma); MAP_REG_WRITE(32, 0x1F801570, 0x1F80157F, iop_dma, dma); MAP_REG_WRITE(32, 0x1F8010F0, 0x1F8010F8, iop_dma, dma); MAP_REG_WRITE(8, 0x1F808200, 0x1F808280, sio2, sio2); MAP_REG_WRITE(8, 0x1F801460, 0x1F80147F, dev9, dev9); // System 147/148 mappings MAP_REG_WRITE_S14X(8, 0x10000000, 0x1000000F, s14x_syscon, s14x_syscon); MAP_REG_WRITE_S14X(8, 0x10800000, 0x108000FF, s14x_link, s14x_link); MAP_MEM_WRITE_S14X(8, 0x10C00000, 0x10C07FFF, s14x_sram, s14x_sram); MAP_REG_WRITE_S14X(8, 0x14000000, 0x1400000F, s14x_nand, s14x_nand); // System 147/148 syscon overlays retail SPEED MAP_REG_WRITE(8, 0x10000000, 0x1000FFFF, speed, speed); printf("iop_bus: Unhandled 8-bit write to physical address 0x%08x (0x%02x)\n", addr, data); } void iop_bus_write16(void* udata, uint32_t addr, uint32_t data) { struct iop_bus* bus = (struct iop_bus*)udata; void* ptr = bus->fastmem_w_table[(addr & 0x1fffffff) >> 13]; if (ptr) { *((uint16_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data; return; } // MAP_MEM_WRITE(16, 0x00000000, 0x001FFFFF, ram, iop_ram); // MAP_MEM_WRITE(16, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_MEM_WRITE(16, 0x1F800000, 0x1F8003FF, ram, iop_spr); MAP_REG_WRITE(32, 0x1F801100, 0x1F80112F, iop_timers, timers); MAP_REG_WRITE(32, 0x1F801480, 0x1F8014AF, iop_timers, timers); MAP_REG_WRITE(16, 0x1F801070, 0x1F80107B, iop_intc, intc); MAP_REG_WRITE(16, 0x1F801080, 0x1F8010EF, iop_dma, dma); MAP_REG_WRITE(16, 0x1F801500, 0x1F80155F, iop_dma, dma); MAP_REG_WRITE(16, 0x1F801570, 0x1F80157F, iop_dma, dma); MAP_REG_WRITE(16, 0x1F8010F0, 0x1F8010F8, iop_dma, dma); MAP_REG_WRITE(16, 0x1F900000, 0x1F9007FF, spu2, spu2); MAP_REG_WRITE(16, 0x1F801460, 0x1F80147F, dev9, dev9); MAP_REG_WRITE(16, 0x10000000, 0x1000FFFF, speed, speed); // System 147/148 mappings MAP_MEM_WRITE_S14X(16, 0x10C00000, 0x10C07FFF, s14x_sram, s14x_sram); // printf("iop_bus: Unhandled 16-bit write to physical address 0x%08x (0x%04x)\n", addr, data); } void iop_bus_write32(void* udata, uint32_t addr, uint32_t data) { struct iop_bus* bus = (struct iop_bus*)udata; void* ptr = bus->fastmem_w_table[(addr & 0x1fffffff) >> 13]; if (ptr) { *((uint32_t*)(((uint8_t*)ptr) + (addr & 0x1fff))) = data; return; } // BIU config if (addr == 0xfffe0130) return; // MAP_MEM_WRITE(32, 0x00000000, 0x001FFFFF, ram, iop_ram); // MAP_MEM_WRITE(32, 0x1FC00000, 0x1FFFFFFF, bios, bios); MAP_MEM_WRITE(32, 0x1F800000, 0x1F8003FF, ram, iop_spr); MAP_REG_WRITE(32, 0x1D000000, 0x1D00006F, sif, sif); MAP_REG_WRITE(32, 0x1F801450, 0x1F801453, sbus, sbus); MAP_REG_WRITE(32, 0x1F801070, 0x1F80107B, iop_intc, intc); MAP_REG_WRITE(32, 0x1F801080, 0x1F8010EF, iop_dma, dma); MAP_REG_WRITE(32, 0x1F801500, 0x1F80155F, iop_dma, dma); MAP_REG_WRITE(32, 0x1F801570, 0x1F80157F, iop_dma, dma); MAP_REG_WRITE(32, 0x1F8010F0, 0x1F8010F8, iop_dma, dma); MAP_REG_WRITE(32, 0x1F801100, 0x1F80112F, iop_timers, timers); MAP_REG_WRITE(32, 0x1F801480, 0x1F8014AF, iop_timers, timers); MAP_REG_WRITE(32, 0x1F808200, 0x1F808280, sio2, sio2); MAP_REG_WRITE(32, 0x1F801600, 0x1F8016FF, usb, usb); MAP_REG_WRITE(32, 0x1F808400, 0x1F80854F, fw, fw); MAP_REG_WRITE(32, 0x1F801460, 0x1F80147F, dev9, dev9); MAP_REG_WRITE(32, 0x10000000, 0x1000FFFF, speed, speed); // System 147/148 mappings MAP_MEM_WRITE_S14X(32, 0x10C00000, 0x10C07FFF, s14x_sram, s14x_sram); // printf("iop_bus: Unhandled 32-bit write to physical address 0x%08x (0x%08x)\n", addr, data); } #undef MAP_READ #undef MAP_WRITE ================================================ FILE: src/iop/bus.h ================================================ #ifndef IOP_BUS_H #define IOP_BUS_H #ifdef __cplusplus extern "C" { #endif #include "u128.h" // Shared IOP/EE hardware #include "shared/ram.h" #include "shared/sif.h" #include "shared/bios.h" #include "shared/sbus.h" #include "shared/dev9.h" #include "shared/speed.h" // IOP-only hardware #include "dma.h" #include "intc.h" #include "timers.h" #include "cdvd.h" #include "sio2.h" #include "spu2.h" #include "usb.h" #include "fw.h" // Arcade hardware #include "s14x/nand.h" #include "s14x/syscon.h" #include "s14x/sram.h" #include "s14x/link.h" #define FASTMEM_BLKSIZE 0x2000 #define FASTMEM_TBLSIZE (0x20000000 / FASTMEM_BLKSIZE) struct iop_bus { struct ps2_bios* bios; struct ps2_bios* rom1; struct ps2_bios* rom2; struct ps2_ram* iop_ram; struct ps2_ram* iop_spr; struct ps2_sif* sif; struct ps2_iop_dma* dma; struct ps2_iop_intc* intc; struct ps2_iop_timers* timers; struct ps2_cdvd* cdvd; struct ps2_sio2* sio2; struct ps2_spu2* spu2; struct ps2_usb* usb; struct ps2_fw* fw; struct ps2_sbus* sbus; struct ps2_dev9* dev9; struct ps2_speed* speed; // Arcade hardware struct s14x_nand* s14x_nand; struct s14x_syscon* s14x_syscon; struct s14x_sram* s14x_sram; struct s14x_link* s14x_link; void* fastmem_r_table[0x10000]; void* fastmem_w_table[0x10000]; }; void iop_bus_init_bios(struct iop_bus* bus, struct ps2_bios* bios); void iop_bus_init_rom1(struct iop_bus* bus, struct ps2_bios* rom1); void iop_bus_init_rom2(struct iop_bus* bus, struct ps2_bios* rom2); void iop_bus_init_iop_ram(struct iop_bus* bus, struct ps2_ram* iop_ram); void iop_bus_init_iop_spr(struct iop_bus* bus, struct ps2_ram* iop_spr); void iop_bus_init_sif(struct iop_bus* bus, struct ps2_sif* sif); void iop_bus_init_dma(struct iop_bus* bus, struct ps2_iop_dma* dma); void iop_bus_init_intc(struct iop_bus* bus, struct ps2_iop_intc* intc); void iop_bus_init_timers(struct iop_bus* bus, struct ps2_iop_timers* timers); void iop_bus_init_cdvd(struct iop_bus* bus, struct ps2_cdvd* cdvd); void iop_bus_init_sio2(struct iop_bus* bus, struct ps2_sio2* sio2); void iop_bus_init_spu2(struct iop_bus* bus, struct ps2_spu2* spu2); void iop_bus_init_usb(struct iop_bus* bus, struct ps2_usb* usb); void iop_bus_init_fw(struct iop_bus* bus, struct ps2_fw* fw); void iop_bus_init_sbus(struct iop_bus* bus, struct ps2_sbus* sbus); void iop_bus_init_dev9(struct iop_bus* bus, struct ps2_dev9* dev9); void iop_bus_init_speed(struct iop_bus* bus, struct ps2_speed* speed); void iop_bus_init_s14x_nand(struct iop_bus* bus, struct s14x_nand* nand); void iop_bus_init_s14x_syscon(struct iop_bus* bus, struct s14x_syscon* syscon); void iop_bus_init_s14x_sram(struct iop_bus* bus, struct s14x_sram* sram); void iop_bus_init_s14x_link(struct iop_bus* bus, struct s14x_link* link); void iop_bus_init_fastmem(struct iop_bus* bus, int ram_size); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/bus_decl.h ================================================ #ifndef IOP_BUS_DECL_H #define IOP_BUS_DECL_H #include struct iop_bus; struct iop_bus* iop_bus_create(void); void iop_bus_init(struct iop_bus* bus, const char* bios_path); void iop_bus_destroy(struct iop_bus* bus); uint32_t iop_bus_read8(void* udata, uint32_t addr); uint32_t iop_bus_read16(void* udata, uint32_t addr); uint32_t iop_bus_read32(void* udata, uint32_t addr); void iop_bus_write8(void* udata, uint32_t addr, uint32_t data); void iop_bus_write16(void* udata, uint32_t addr, uint32_t data); void iop_bus_write32(void* udata, uint32_t addr, uint32_t data); #endif ================================================ FILE: src/iop/cdvd.c ================================================ #include #include #include #include #include #include #include #include "cdvd.h" #define printf(fmt,...)(0) struct nvram_layout g_spc970_layout = { .bios_version = 0x00000000, .config0_offset = 0x00000280, .config1_offset = 0x00000300, .config2_offset = 0x00000200, .console_id_offset = 0x000001C8, .ilink_id_offset = 0x000001C0, .modelnum_offset = 0x000001A0, .regparams_offset = 0x00000180, .mac_offset = 0x00000198 }; struct nvram_layout g_dragon_layout = { .bios_version = 0x00000146, .config0_offset = 0x00000270, .config1_offset = 0x000002B0, .config2_offset = 0x00000200, .console_id_offset = 0x000001F0, .ilink_id_offset = 0x000001E0, .modelnum_offset = 0x000001B0, .regparams_offset = 0x00000180, .mac_offset = 0x00000198 }; static const uint8_t itob_table[] = { 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0xa0, 0xa1, 0xa2, 0xa3, 0xa4, 0xa5, 0xa6, 0xa7, 0xa8, 0xa9, 0xb0, 0xb1, 0xb2, 0xb3, 0xb4, 0xb5, 0xb6, 0xb7, 0xb8, 0xb9, 0xc0, 0xc1, 0xc2, 0xc3, 0xc4, 0xc5, 0xc6, 0xc7, 0xc8, 0xc9, 0xd0, 0xd1, 0xd2, 0xd3, 0xd4, 0xd5, 0xd6, 0xd7, 0xd8, 0xd9, 0xe0, 0xe1, 0xe2, 0xe3, 0xe4, 0xe5, 0xe6, 0xe7, 0xe8, 0xe9, 0xf0, 0xf1, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7, 0xf8, 0xf9, 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16, 0x17, 0x18, 0x19, 0x20, 0x21, 0x22, 0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46, 0x47, 0x48, 0x49, 0x50, 0x51, 0x52, 0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76, 0x77, 0x78, 0x79, 0x80, 0x81, 0x82, 0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, }; static inline const char* cdvd_get_n_command_name(uint8_t cmd) { switch (cmd) { case 0x00: return "nop"; case 0x01: return "nop_sync"; case 0x02: return "standby"; case 0x03: return "stop"; case 0x04: return "pause"; case 0x05: return "seek"; case 0x06: return "read_cd"; case 0x07: return "read_cdda"; case 0x08: return "read_dvd"; case 0x09: return "get_toc"; case 0x0c: return "read_key"; } return ""; } static inline const char* cdvd_get_type_name(int type) { switch (type) { case CDVD_DISC_NO_DISC: return "No disc"; case CDVD_DISC_DETECTING: return "Detecting"; case CDVD_DISC_DETECTING_CD: return "Detecting CD"; case CDVD_DISC_DETECTING_DVD: return "Detecting DVD"; case CDVD_DISC_DETECTING_DL_DVD: return "Detecting Dual-layer DVD"; case CDVD_DISC_PSX_CD: return "PlayStation CD"; case CDVD_DISC_PSX_CDDA: return "PlayStation CDDA"; case CDVD_DISC_PS2_CD: return "PlayStation 2 CD"; case CDVD_DISC_PS2_CDDA: return "PlayStation 2 CDDA"; case CDVD_DISC_PS2_DVD: return "PlayStation 2 DVD"; case CDVD_DISC_CDDA: return "CD Audio"; case CDVD_DISC_DVD_VIDEO: return "DVD Video"; case CDVD_DISC_INVALID: return "Invalid"; } return "Unknown"; } static inline int cdvd_is_dual_layer(struct ps2_cdvd* cdvd) { return disc_get_volume_lba(cdvd->disc, 1); } static inline void cdvd_set_busy(struct ps2_cdvd* cdvd) { cdvd->n_stat |= CDVD_N_STATUS_BUSY; cdvd->n_stat &= ~CDVD_N_STATUS_READY; } static inline void cdvd_set_ready(struct ps2_cdvd* cdvd) { cdvd->n_stat &= ~CDVD_N_STATUS_BUSY; cdvd->n_stat |= CDVD_N_STATUS_READY; } static inline void cdvd_init_s_fifo(struct ps2_cdvd* cdvd, int size) { if (cdvd->s_fifo) free(cdvd->s_fifo); cdvd->s_fifo_size = size; cdvd->s_fifo_index = 0; cdvd->s_fifo = malloc(cdvd->s_fifo_size); cdvd->s_stat &= ~0x40; } static inline void cdvd_s_read_subq(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 11); int track = disc_get_track_number(cdvd->disc, cdvd->read_lba); struct track_info info; disc_get_track_info(cdvd->disc, track, &info); int track_lba = (cdvd->read_lba + 150) - info.lba; int track_mm = track_lba / (60 * 75); int track_ss = (track_lba % (60 * 75)) / 75; int track_ff = (track_lba % (60 * 75)) % 75; int abs_mm = (cdvd->read_lba + 150) / (60 * 75); int abs_ss = ((cdvd->read_lba + 150) % (60 * 75)) / 75; int abs_ff = ((cdvd->read_lba + 150) % (60 * 75)) % 75; fprintf(stdout, "cdvd: S subq read: track=%02d trk %02d:%02d:%02d abs %02d:%02d:%02d\n", track, track_mm, track_ss, track_ff, abs_mm, abs_ss, abs_ff ); // Note: From PCSX2 // the formatted subq command returns: // control/adr, track, index, trk min, trk sec, trk frm, 0x00, abs min, abs sec, abs frm memset(&cdvd->s_fifo[0], 0, 11); cdvd->s_fifo[0] = 0x41; // control/adr cdvd->s_fifo[1] = track; // track cdvd->s_fifo[2] = 0x01; // index cdvd->s_fifo[3] = itob_table[track_mm]; // trk min cdvd->s_fifo[4] = itob_table[track_ss]; // trk sec cdvd->s_fifo[5] = itob_table[track_ff]; // trk frm cdvd->s_fifo[6] = 0x00; cdvd->s_fifo[7] = itob_table[abs_mm]; // abs min cdvd->s_fifo[8] = itob_table[abs_ss]; // abs sec cdvd->s_fifo[9] = itob_table[abs_ff]; // abs frm // To-do: This doesn't work for whatever reason, even though // we're returning correct data, the CD player just // won't actually display the current time. } static inline void cdvd_s_mechacon_cmd(struct ps2_cdvd* cdvd) { switch (cdvd->s_params[0]) { case 0x00: { cdvd_init_s_fifo(cdvd, 4); cdvd->s_fifo[0] = 0x03; cdvd->s_fifo[1] = 0x06; cdvd->s_fifo[2] = 0x02; cdvd->s_fifo[3] = 0x00; } break; case 0x90: { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0x00; } break; case 0xef: { cdvd_init_s_fifo(cdvd, 3); cdvd->s_fifo[0] = 0x00; cdvd->s_fifo[1] = 0x0f; cdvd->s_fifo[2] = 0x05; } break; // sceCdReadRenewalDate (sent by PSX DESR BIOS) case 0xfd: { cdvd_init_s_fifo(cdvd, 6); cdvd->s_fifo[0] = 0; cdvd->s_fifo[1] = 0x04; //year cdvd->s_fifo[2] = 0x12; //month cdvd->s_fifo[3] = 0x10; //day cdvd->s_fifo[4] = 0x01; //hour cdvd->s_fifo[5] = 0x30; //min } break; default: { fprintf(stderr, "cdvd: Unknown S subcommand %02x\n", cdvd->s_params[0]); exit(1); } break; } } static inline void cdvd_s_update_sticky_flags(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; cdvd->sticky_status = cdvd->status; } static inline void cdvd_s_read_rtc(struct ps2_cdvd* cdvd) { time_t t = time(NULL); struct tm tm = *localtime(&t); cdvd_init_s_fifo(cdvd, 8); cdvd->s_fifo[0] = 0; cdvd->s_fifo[1] = itob_table[tm.tm_sec]; cdvd->s_fifo[2] = itob_table[tm.tm_min]; cdvd->s_fifo[3] = itob_table[tm.tm_hour]; cdvd->s_fifo[4] = 0; cdvd->s_fifo[5] = itob_table[tm.tm_mday]; cdvd->s_fifo[6] = itob_table[tm.tm_mon + 1]; cdvd->s_fifo[7] = itob_table[tm.tm_year - 100]; } static inline void cdvd_s_write_rtc(struct ps2_cdvd* cdvd) { fprintf(stderr, "cdvd: write_rtc\n"); exit(1); } static inline void cdvd_s_read_nvram(struct ps2_cdvd* cdvd) { uint16_t addr = *(uint16_t*)&cdvd->s_params[0]; cdvd_init_s_fifo(cdvd, 2); cdvd->s_fifo[0] = cdvd->nvram[(addr << 1) + 0]; cdvd->s_fifo[1] = cdvd->nvram[(addr << 1) + 1]; } static inline void cdvd_s_write_nvram(struct ps2_cdvd* cdvd) { fprintf(stderr, "cdvd: write_nvram\n"); exit(1); } static inline void cdvd_s_read_ilink_id(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 9); // uint8_t id[9] = { // 0x00, 0xac, 0xff, 0xff, // 0xff, 0xff, 0xb9, 0x86, // 0x00 // }; // for (int i = 0; i < 9; i++) { // cdvd->s_fifo[i] = id[i]; // } int offset = cdvd->layout.ilink_id_offset; memcpy(&cdvd->s_fifo[1], &cdvd->nvram[offset], 8); } static inline void cdvd_s_ctrl_audio_digital_out(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_forbid_dvd(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 5; } static inline void cdvd_s_read_model(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 9); int offset = cdvd->layout.modelnum_offset + cdvd->s_params[0]; memcpy(&cdvd->s_fifo[1], &cdvd->nvram[offset], 8); } static inline void cdvd_s_certify_boot(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 1; } static inline void cdvd_s_cancel_pwoff_ready(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_blue_led_ctl(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_read_wakeup_time(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 10); for (int i = 0; i < 10; i++) cdvd->s_fifo[i] = 0; } static inline void cdvd_s_rc_bypass_ctrl(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_open_config(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->config_rw = cdvd->s_params[0]; cdvd->config_offset = cdvd->s_params[1]; cdvd->config_numblocks = cdvd->s_params[2]; cdvd->config_block_index = 0; cdvd->s_fifo[0] = 0; } static inline void cdvd_s_read_config(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 16); int offset = 0; switch (cdvd->config_offset) { case 0: offset = cdvd->layout.config0_offset; break; case 1: offset = cdvd->layout.config1_offset; break; case 2: offset = cdvd->layout.config2_offset; break; default: offset = cdvd->layout.config1_offset; break; } offset += (cdvd->config_block_index++) * 16; memcpy(cdvd->s_fifo, &cdvd->nvram[offset], 16); } static inline void cdvd_s_write_config(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_close_config(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_mechacon_auth_80(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_mechacon_auth_81(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_mechacon_auth_82(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_mechacon_auth_83(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_mechacon_auth_84(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1+8+4); cdvd->s_fifo[0] = 0; cdvd->s_fifo[1] = 0x21; cdvd->s_fifo[2] = 0xdc; cdvd->s_fifo[3] = 0x31; cdvd->s_fifo[4] = 0x96; cdvd->s_fifo[5] = 0xce; cdvd->s_fifo[6] = 0x72; cdvd->s_fifo[7] = 0xe0; cdvd->s_fifo[8] = 0xc8; cdvd->s_fifo[9] = 0x69; cdvd->s_fifo[10] = 0xda; cdvd->s_fifo[11] = 0x34; cdvd->s_fifo[12] = 0x9b; } static inline void cdvd_s_mechacon_auth_85(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1+4+8); cdvd->s_fifo[0] = 0; cdvd->s_fifo[1] = 0xeb; cdvd->s_fifo[2] = 0x01; cdvd->s_fifo[3] = 0xc7; cdvd->s_fifo[4] = 0xa9; cdvd->s_fifo[5] = 0x3f; cdvd->s_fifo[6] = 0x9c; cdvd->s_fifo[7] = 0x5b; cdvd->s_fifo[8] = 0x19; cdvd->s_fifo[9] = 0x31; cdvd->s_fifo[10] = 0xa0; cdvd->s_fifo[11] = 0xb3; cdvd->s_fifo[12] = 0xa3; } static inline void cdvd_s_mechacon_auth_86(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_mechacon_auth_87(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_mechacon_auth_88(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_mg_write_data(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; printf("mg: Write KELF data params="); for (int i = 0; i < cdvd->s_param_index; i++) printf("%02x ", cdvd->s_params[i]); printf("\n"); } static inline void cdvd_s_mechacon_auth_8f(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_mg_write_hdr_start(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); printf("mg: Write KELF header params="); for (int i = 0; i < cdvd->s_param_index; i++) printf("%02x ", cdvd->s_params[i]); printf("\n"); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_mg_read_bit_length(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 3); for (int i = 0; i < 3; i++) cdvd->s_fifo[i] = 0; } static inline void cdvd_s_get_region_params(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 15); for (int i = 5; i < 15; i++) cdvd->s_fifo[i] = 0; int offset = cdvd->layout.regparams_offset; cdvd->s_fifo[1] = 1 << 0; // MechaCon encryption zone cdvd->s_fifo[2] = 0; memcpy(&cdvd->s_fifo[3], &cdvd->nvram[offset], 8); fprintf(stdout, "cdvd: Region: \'%c\' Language set: %c%c%c%c\n", cdvd->s_fifo[3], cdvd->s_fifo[4], cdvd->s_fifo[5], cdvd->s_fifo[6], cdvd->s_fifo[7] ); //This is basically what PCSX2 returns on a blank NVM/MEC file // cdvd->s_fifo[0] = 0; // cdvd->s_fifo[1] = 1 << 0x3; //MEC encryption zone // cdvd->s_fifo[2] = 0; // cdvd->s_fifo[3] = 0x80; //Region Params // cdvd->s_fifo[4] = 0x1; } static inline void cdvd_s_remote2_read(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 5); cdvd->s_fifo[0] = 0x00; cdvd->s_fifo[1] = 0x14; cdvd->s_fifo[2] = 0x00; cdvd->s_fifo[3] = 0x00; cdvd->s_fifo[4] = 0x00; } static inline void cdvd_s_remote2_6(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 3); cdvd->s_fifo[0] = 0; cdvd->s_fifo[1] = 1; cdvd->s_fifo[2] = 0; } static inline void cdvd_s_auto_adjust_ctrl(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_notice_game_start(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 1); cdvd->s_fifo[0] = 0; } static inline void cdvd_s_get_medium_removal(struct ps2_cdvd* cdvd) { cdvd_init_s_fifo(cdvd, 2); cdvd->s_fifo[0] = 0; } void cdvd_handle_s_command(struct ps2_cdvd* cdvd, uint8_t cmd) { cdvd->s_cmd = cmd; switch (cmd) { // Note: Used by CD player to get current playing position case 0x02: printf("cdvd: read_subq\n"); cdvd_s_read_subq(cdvd); break; case 0x03: printf("cdvd: mechacon_cmd(%02x)\n", cdvd->s_params[0]); cdvd_s_mechacon_cmd(cdvd); break; case 0x05: printf("cdvd: update_sticky_flags\n"); cdvd_s_update_sticky_flags(cdvd); break; // case 0x06: printf("cdvd: tray_ctrl\n"); cdvd_s_tray_ctrl(cdvd); break; case 0x08: printf("cdvd: read_rtc\n"); cdvd_s_read_rtc(cdvd); break; case 0x09: printf("cdvd: write_rtc\n"); cdvd_s_write_rtc(cdvd); break; case 0x0a: printf("cdvd: read_nvram\n"); cdvd_s_read_nvram(cdvd); break; case 0x0b: printf("cdvd: write_nvram\n"); cdvd_s_write_nvram(cdvd); break; // case 0x0f: printf("cdvd: power_off\n"); cdvd_s_power_off(cdvd); break; case 0x12: printf("cdvd: read_ilink_id\n"); cdvd_s_read_ilink_id(cdvd); break; // Note: Used by CD player case 0x14: printf("cdvd: ctrl_audio_digital_out\n"); cdvd_s_ctrl_audio_digital_out(cdvd); break; case 0x15: printf("cdvd: forbid_dvd\n"); cdvd_s_forbid_dvd(cdvd); break; case 0x16: printf("cdvd: auto_adjust_ctrl\n"); cdvd_s_auto_adjust_ctrl(cdvd); break; case 0x17: printf("cdvd: read_model\n"); cdvd_s_read_model(cdvd); break; case 0x1a: printf("cdvd: certify_boot\n"); cdvd_s_certify_boot(cdvd); break; case 0x1b: printf("cdvd: cancel_pwoff_ready\n"); cdvd_s_cancel_pwoff_ready(cdvd); break; // Used by Namco System 246 at boot case 0x1c: printf("cdvd: blue_led_ctl\n"); cdvd_s_blue_led_ctl(cdvd); break; case 0x1e: printf("cdvd: remote2_read\n"); cdvd_s_remote2_read(cdvd); break; // Used by the PS2 DVD player case 0x20: printf("cdvd: remote2_6\n"); cdvd_s_remote2_6(cdvd); break; case 0x22: printf("cdvd: read_wakeup_time\n"); cdvd_s_read_wakeup_time(cdvd); break; case 0x24: printf("cdvd: rc_bypass_ctrl\n"); cdvd_s_rc_bypass_ctrl(cdvd); break; case 0x29: printf("cdvd: notice_game_start\n"); cdvd_s_notice_game_start(cdvd); break; case 0x32: printf("cdvd: get_medium_removal\n"); cdvd_s_get_medium_removal(cdvd); break; case 0x36: printf("cdvd: get_region_params\n"); cdvd_s_get_region_params(cdvd); break; case 0x40: printf("cdvd: open_config\n"); cdvd_s_open_config(cdvd); break; case 0x41: printf("cdvd: read_config\n"); cdvd_s_read_config(cdvd); break; case 0x42: printf("cdvd: write_config\n"); cdvd_s_write_config(cdvd); break; case 0x43: printf("cdvd: close_config\n"); cdvd_s_close_config(cdvd); break; case 0x80: printf("cdvd: mechacon_auth_80\n"); cdvd_s_mechacon_auth_80(cdvd); break; case 0x81: printf("cdvd: mechacon_auth_81\n"); cdvd_s_mechacon_auth_81(cdvd); break; case 0x82: printf("cdvd: mechacon_auth_82\n"); cdvd_s_mechacon_auth_82(cdvd); break; case 0x83: printf("cdvd: mechacon_auth_83\n"); cdvd_s_mechacon_auth_83(cdvd); break; case 0x84: printf("cdvd: mechacon_auth_84\n"); cdvd_s_mechacon_auth_84(cdvd); break; case 0x85: printf("cdvd: mechacon_auth_85\n"); cdvd_s_mechacon_auth_85(cdvd); break; case 0x86: printf("cdvd: mechacon_auth_86\n"); cdvd_s_mechacon_auth_86(cdvd); break; case 0x87: printf("cdvd: mechacon_auth_87\n"); cdvd_s_mechacon_auth_87(cdvd); break; case 0x88: printf("cdvd: mechacon_auth_88\n"); cdvd_s_mechacon_auth_88(cdvd); break; case 0x8d: printf("cdvd: mg_write_data\n"); cdvd_s_mg_write_data(cdvd); break; case 0x8f: printf("cdvd: mechacon_auth_8f\n"); cdvd_s_mechacon_auth_8f(cdvd); break; case 0x90: printf("cdvd: mg_write_hdr_start\n"); cdvd_s_mg_write_hdr_start(cdvd); break; case 0x91: printf("cdvd: mg_read_bit_length\n"); cdvd_s_mg_read_bit_length(cdvd); break; default: { fprintf(stderr, "cdvd: Unknown S command %02xh\n", cmd); exit(1); } break; } cdvd->s_param_index = 0; } static inline void cdvd_handle_s_param(struct ps2_cdvd* cdvd, uint8_t param) { if (cdvd->s_param_index == 16) { printf("cdvd: S parameter FIFO overflow\n"); } cdvd->s_params[cdvd->s_param_index++] = param; } static inline uint8_t cdvd_read_s_response(struct ps2_cdvd* cdvd) { if (cdvd->s_fifo_index == cdvd->s_fifo_size) { return 0; } uint8_t data = cdvd->s_fifo[cdvd->s_fifo_index++]; if (cdvd->s_fifo_index == cdvd->s_fifo_size) cdvd->s_stat |= 0x40; return data; } static inline long cdvd_get_cd_read_timing(struct ps2_cdvd* cdvd, int from) { long block_timing = (36864000L * cdvd->read_size) / (24 * 153600); long delta = cdvd->read_lba - from; long contiguous_cycles = block_timing * cdvd->read_count; if (!delta) return 1000; if (delta < 0) { delta = -delta; } // Small delta if (delta < 8) return ((block_timing * delta) + contiguous_cycles) / 4; // Fast seek: ~30ms if (delta < 4371) return (((36864000 / 1000) * 30) + contiguous_cycles) / 4; // Full seek: ~100ms return (((36864000 / 1000) * 100) + contiguous_cycles) / 4; } static inline void cdvd_set_status(struct ps2_cdvd* cdvd, uint8_t data) { cdvd->status = data; cdvd->sticky_status |= data; } static inline void cdvd_send_irq(struct ps2_cdvd* cdvd) { ps2_iop_intc_irq(cdvd->intc, IOP_INTC_CDVD); cdvd->i_stat |= 2; } void cdvd_fetch_sector(struct ps2_cdvd* cdvd) { memset(cdvd->buf, 0, 2352); switch (cdvd->read_size) { case CDVD_CD_SS_2048: case CDVD_CD_SS_2328: { disc_read_sector(cdvd->disc, cdvd->buf, cdvd->read_lba++, DISC_SS_DATA); } break; case CDVD_CD_SS_2352: { disc_read_sector(cdvd->disc, cdvd->buf, cdvd->read_lba++, DISC_SS_RAW); } break; case CDVD_CD_SS_2340: { // LBA -> MSF uint64_t a = cdvd->read_lba + 150; uint32_t m = a / 4500; a -= m * 4500; uint32_t s = a / 75; uint32_t f = a - (s * 75); // Fill in header cdvd->buf[0] = itob_table[m]; cdvd->buf[1] = itob_table[s]; cdvd->buf[2] = itob_table[f]; cdvd->buf[3] = 1; // Write raw data at offset 12 disc_read_sector(cdvd->disc, cdvd->buf + 12, cdvd->read_lba++, DISC_SS_DATA); } break; case CDVD_DVD_SS: { memset(cdvd->buf, 0, 2340); uint32_t lba, layer; if (cdvd->layer2_lba && (cdvd->read_lba >= cdvd->layer2_lba)) { layer = cdvd->read_lba >= cdvd->layer2_lba; lba = cdvd->read_lba - cdvd->layer2_lba + 0x30000; } else { layer = 0; lba = cdvd->read_lba + 0x30000; } cdvd->buf[0] = 0x20 | layer; cdvd->buf[1] = (lba >> 16) & 0xFF; cdvd->buf[2] = (lba >> 8) & 0xFF; cdvd->buf[3] = lba & 0xff; disc_read_sector(cdvd->disc, cdvd->buf + 12, cdvd->read_lba++, DISC_SS_DATA); // for (int i = 0; i < 2064;) { // for (int x = 0; x < 16; x++) { // printf("%02x ", cdvd->buf[i+x]); // } // putchar('|'); // for (int x = 0; x < 16; x++) { // printf("%c", isprint(cdvd->buf[i+x]) ? cdvd->buf[i+x] : '.'); // } // puts("|"); // i += 16; // } } break; } if (!cdvd->mecha_decode) return; uint8_t shift_amount = (cdvd->mecha_decode >> 4) & 7; int do_xor = (cdvd->mecha_decode) & 1; int do_shift = (cdvd->mecha_decode) & 2; for (int i = 0; i < cdvd->read_size; ++i) { if (do_xor) cdvd->buf[i] ^= cdvd->cdkey[4]; if (do_shift) cdvd->buf[i] = (cdvd->buf[i] >> shift_amount) | (cdvd->buf[i] << (8 - shift_amount)); } } void cdvd_do_read(void* udata, int overshoot) { struct ps2_cdvd* cdvd = (struct ps2_cdvd*)udata; // Ugly hack!! // Some games will send if (!(cdvd->dma->cdvd.chcr & 0x1000000)) { // printf("cdvd: CDVD DMA not yet ready\n"); struct sched_event event; event.name = "CDVD Read"; event.udata = cdvd; event.callback = cdvd_do_read; event.cycles = 1000; sched_schedule(cdvd->sched, event); cdvd_set_status(cdvd, CDVD_STATUS_READING); return; } // Fetch a sector cdvd_fetch_sector(cdvd); // Send sector to DMA cdvd->buf_size = cdvd->read_size; cdvd->read_count--; // printf("cdvd: Sending a sector to DMA (left=%d)\n", cdvd->read_count); iop_dma_handle_cdvd_transfer(cdvd->dma); if (cdvd->read_count) { struct sched_event event; event.name = "CDVD Read"; event.udata = cdvd; event.callback = cdvd_do_read; event.cycles = 1000; sched_schedule(cdvd->sched, event); cdvd_set_status(cdvd, CDVD_STATUS_READING); return; } cdvd->n_stat = 0x4e; cdvd->n_cmd = 0; cdvd_set_ready(cdvd); cdvd_set_status(cdvd, CDVD_STATUS_PAUSED); cdvd_send_irq(cdvd); // I_STAT needs to be set to 3? cdvd->i_stat |= 2; } static inline void cdvd_n_nop(struct ps2_cdvd* cdvd) { printf("cdvd: nop\n"); cdvd_set_ready(cdvd); cdvd_send_irq(cdvd); } static inline void cdvd_n_nop_sync(struct ps2_cdvd* cdvd) { fprintf(stderr, "cdvd: nop_sync\n"); exit(1); } static inline void cdvd_n_standby(struct ps2_cdvd* cdvd) { printf("cdvd: standby\n"); cdvd_set_ready(cdvd); cdvd_set_status(cdvd, CDVD_STATUS_PAUSED); cdvd_send_irq(cdvd); } static inline void cdvd_n_stop(struct ps2_cdvd* cdvd) { printf("cdvd: stop\n"); cdvd_set_ready(cdvd); cdvd_set_status(cdvd, CDVD_STATUS_STOPPED); cdvd_send_irq(cdvd); } static inline void cdvd_n_pause(struct ps2_cdvd* cdvd) { printf("cdvd: pause\n"); cdvd_set_ready(cdvd); cdvd_set_status(cdvd, CDVD_STATUS_PAUSED); cdvd_send_irq(cdvd); cdvd->n_cmd = 0; } static inline void cdvd_n_seek(struct ps2_cdvd* cdvd) { printf("cdvd: seek\n"); cdvd->read_lba = *(uint32_t*)(cdvd->n_params); cdvd_set_ready(cdvd); cdvd_set_status(cdvd, CDVD_STATUS_SEEKING); cdvd_send_irq(cdvd); } static inline void cdvd_n_read_cd(struct ps2_cdvd* cdvd) { /* Params: 0-3 Sector position 4-7 Sectors to read 10 Block size (1=2328 bytes, 2=2340 bytes, all others=2048 bytes) Performs a CD-style read. Seems to raise bit 0 of CDVD I_STAT upon completion? */ int prev_lba = cdvd->read_lba; cdvd->read_lba = *(uint32_t*)(cdvd->n_params); cdvd->read_count = *(uint32_t*)(cdvd->n_params + 4); cdvd->read_speed = cdvd->n_params[9]; cdvd->read_size = CDVD_CD_SS_2048; switch (cdvd->n_params[10]) { case 1: cdvd->read_size = CDVD_CD_SS_2328; break; case 2: cdvd->read_size = CDVD_CD_SS_2340; break; } struct sched_event event; event.name = "CDVD ReadCd"; event.udata = cdvd; event.callback = cdvd_do_read; event.cycles = cdvd_get_cd_read_timing(cdvd, prev_lba); sched_schedule(cdvd->sched, event); cdvd_set_status(cdvd, CDVD_STATUS_READING); // printf("cdvd: ReadCd lba=%08x count=%08x size=%d cycles=%ld speed=%02x (%p)\n", // cdvd->read_lba, // cdvd->read_count, // cdvd->read_size, // event.cycles, // cdvd->n_params[9], // cdvd->disc->read_sector // ); } static inline void cdvd_n_read_cdda(struct ps2_cdvd* cdvd) { int prev_lba = cdvd->read_lba; cdvd->read_lba = *(uint32_t*)(cdvd->n_params); cdvd->read_count = *(uint32_t*)(cdvd->n_params + 4); cdvd->read_speed = cdvd->n_params[9]; cdvd->read_size = CDVD_CD_SS_2352; // fprintf(stderr, "cdvd: ReadCdda lba=%d count=%d decode=%d\n", cdvd->read_lba, cdvd->read_count, cdvd->mecha_decode); struct sched_event event; event.name = "CDVD ReadCdda"; event.udata = cdvd; event.callback = cdvd_do_read; event.cycles = ((2352.f / 2.f) / 44100.f) * (36864000.f * 8); cdvd_set_status(cdvd, CDVD_STATUS_READING); sched_schedule(cdvd->sched, event); } static inline void cdvd_n_read_dvd(struct ps2_cdvd* cdvd) { /* Params: 0-3 Sector position 4-7 Sectors to read Performs a DVD-style read, with a block size of 2064 bytes. The format of the data is as follows: 0 1 Volume number + 0x20 1 3 Sector number - volume start + 0x30000, in big-endian. 4 8 ? (all zeroes) 12 2048 Raw sector data 2060 4 ? (all zeroes) */ int prev_lba = cdvd->read_lba; cdvd->read_lba = *(uint32_t*)(cdvd->n_params); cdvd->read_count = *(uint32_t*)(cdvd->n_params + 4); cdvd->read_speed = cdvd->n_params[9]; cdvd->read_size = CDVD_DVD_SS; struct sched_event event; event.name = "CDVD ReadDvd"; event.udata = cdvd; event.callback = cdvd_do_read; event.cycles = cdvd_get_cd_read_timing(cdvd, prev_lba); sched_schedule(cdvd->sched, event); cdvd_set_status(cdvd, CDVD_STATUS_READING); } static inline void cdvd_n_get_toc(struct ps2_cdvd* cdvd) { fprintf(stderr, "cdvd: get_toc\n"); memset(cdvd->buf, 0, 2064); if (cdvd->disc_type == CDVD_DISC_CDDA) { int track_count = disc_get_track_count(cdvd->disc); int disc_size = disc_get_size(cdvd->disc) / 2352; int size_mm = disc_size / (60 * 75); int size_ss = (disc_size % (60 * 75)) / 75; int size_ff = (disc_size % (60 * 75)) % 75; cdvd->buf[0] = 0x41; cdvd->buf[1] = 0x00; //Number of FirstTrack cdvd->buf[2] = 0xa0; cdvd->buf[7] = 0x01; //Number of LastTrack cdvd->buf[12] = 0xa1; cdvd->buf[17] = itob_table[track_count]; //DiskLength cdvd->buf[22] = 0xa2; cdvd->buf[27] = itob_table[size_mm]; // mm cdvd->buf[28] = itob_table[size_ss]; // ss cdvd->buf[29] = itob_table[size_ff]; // ff for (int i = 0; i < track_count; i++) { int num = i + 1; struct track_info info; disc_get_track_info(cdvd->disc, num, &info); int track_mm = info.lba / (60 * 75); int track_ss = (info.lba % (60 * 75)) / 75; int track_ff = (info.lba % (60 * 75)) % 75; // fprintf(stderr, "cdvd: track %d %02d:%02d:%02d\n", i + 1, track_mm, track_ss, track_ff); cdvd->buf[30 + (i * 10)] = info.type; cdvd->buf[32 + (i * 10)] = itob_table[num]; // Track number cdvd->buf[37 + (i * 10)] = itob_table[track_mm]; // mm cdvd->buf[38 + (i * 10)] = itob_table[track_ss]; // ss cdvd->buf[39 + (i * 10)] = itob_table[track_ff]; // ff } } else if (!cdvd_is_dual_layer(cdvd)) { cdvd->buf[0] = 0x04; cdvd->buf[1] = 0x02; cdvd->buf[2] = 0xF2; cdvd->buf[3] = 0x00; cdvd->buf[4] = 0x86; cdvd->buf[5] = 0x72; cdvd->buf[17] = 0x03; } else { cdvd->buf[0] = 0x24; cdvd->buf[1] = 0x02; cdvd->buf[2] = 0xF2; cdvd->buf[3] = 0x00; cdvd->buf[4] = 0x41; cdvd->buf[5] = 0x95; cdvd->buf[14] = 0x60; cdvd->buf[16] = 0x00; cdvd->buf[17] = 0x03; cdvd->buf[18] = 0x00; cdvd->buf[19] = 0x00; int32_t start = cdvd->layer2_lba + 0x30000 - 1; cdvd->buf[20] = start >> 24; cdvd->buf[21] = (start >> 16) & 0xff; cdvd->buf[22] = (start >> 8) & 0xff; cdvd->buf[23] = start & 0xFF; } cdvd->buf_size = 2064; cdvd->n_stat = 0x40; iop_dma_handle_cdvd_transfer(cdvd->dma); cdvd_set_ready(cdvd); cdvd_set_status(cdvd, CDVD_STATUS_READING); cdvd_send_irq(cdvd); cdvd->n_cmd = 0; } static inline void cdvd_n_read_key(struct ps2_cdvd* cdvd) { uint32_t b0 = cdvd->n_params[3]; uint32_t b1 = cdvd->n_params[4]; uint32_t b2 = cdvd->n_params[5]; uint32_t b3 = cdvd->n_params[6]; uint32_t arg = b0 | (b1 << 8) | (b2 << 16) | (b3 << 24); // Code referenced/taken from PCSX2 // This performs some kind of encryption/checksum with the game's serial? memset(cdvd->cdkey, 0, 16); char serial[16]; if (!disc_get_serial(cdvd->disc, serial)) { printf("cdvd: Couldn't find game serial, can't get cdkey\n"); } else { printf("cdvd: \'%s\'\n", serial); } int32_t letters = (int32_t)((serial[3] & 0x7F) << 0) | (int32_t)((serial[2] & 0x7F) << 7) | (int32_t)((serial[1] & 0x7F) << 14) | (int32_t)((serial[0] & 0x7F) << 21); char m[6]; for (int i = 0; i < 3; i++) m[i] = serial[i+5]; for (int i = 0; i < 2; i++) m[i+3] = serial[i+9]; m[5] = '\0'; int32_t code = strtoul(m, NULL, 10); uint32_t key_0_3 = ((code & 0x1FC00) >> 10) | ((0x01FFFFFF & letters) << 7); uint32_t key_4 = ((code & 0x0001F) << 3) | ((0x0E000000 & letters) >> 25); uint32_t key_14 = ((code & 0x003E0) >> 2) | 0x04; cdvd->cdkey[0] = (key_0_3 & 0x000000FF) >> 0; cdvd->cdkey[1] = (key_0_3 & 0x0000FF00) >> 8; cdvd->cdkey[2] = (key_0_3 & 0x00FF0000) >> 16; cdvd->cdkey[3] = (key_0_3 & 0xFF000000) >> 24; cdvd->cdkey[4] = key_4; switch (arg) { case 75: { cdvd->cdkey[14] = key_14; cdvd->cdkey[15] = 0x05; } break; case 4246: { cdvd->cdkey[0] = 0x07; cdvd->cdkey[1] = 0xF7; cdvd->cdkey[2] = 0xF2; cdvd->cdkey[3] = 0x01; cdvd->cdkey[4] = 0x00; cdvd->cdkey[15] = 0x01; } break; default: { cdvd->cdkey[15] = 0x01; } break; } cdvd_set_ready(cdvd); cdvd_send_irq(cdvd); } static inline void cdvd_handle_n_command(struct ps2_cdvd* cdvd, uint8_t cmd) { cdvd->n_cmd = cmd; // fprintf(stdout, "cdvd: N command %s (%02x)\n", cdvd_get_n_command_name(cmd), cmd); cdvd_set_busy(cdvd); switch (cdvd->n_cmd) { case 0x00: cdvd_n_nop(cdvd); break; case 0x01: cdvd_n_nop_sync(cdvd); break; case 0x02: cdvd_n_standby(cdvd); break; case 0x03: cdvd_n_stop(cdvd); break; case 0x04: cdvd_n_pause(cdvd); break; case 0x05: cdvd_n_seek(cdvd); break; case 0x06: cdvd_n_read_cd(cdvd); break; case 0x07: cdvd_n_read_cdda(cdvd); break; case 0x08: cdvd_n_read_dvd(cdvd); break; case 0x09: cdvd_n_get_toc(cdvd); break; case 0x0c: cdvd_n_read_key(cdvd); break; default: { fprintf(stderr, "cdvd: Unhandled N command %02x\n", cdvd->n_cmd); exit(1); } break; } // Reset N param FIFO cdvd->n_param_index = 0; } static inline void cdvd_handle_n_param(struct ps2_cdvd* cdvd, uint8_t param) { cdvd->n_params[cdvd->n_param_index++] = param; if (cdvd->n_param_index > 15) { fprintf(stderr, "cdvd: N parameter FIFO overflow\n"); exit(1); } } struct ps2_cdvd* ps2_cdvd_create(void) { return malloc(sizeof(struct ps2_cdvd)); } void ps2_cdvd_init(struct ps2_cdvd* cdvd, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched) { memset(cdvd, 0, sizeof(struct ps2_cdvd)); // 00:02:00 cdvd->read_lba = 0x150; cdvd->n_stat = 0x4e; cdvd->s_stat = CDVD_S_STATUS_NO_DATA; cdvd->sticky_status = 0x1e; cdvd->sched = sched; cdvd->dma = dma; cdvd->intc = intc; } void ps2_cdvd_destroy(struct ps2_cdvd* cdvd) { ps2_cdvd_close(cdvd); free(cdvd->s_fifo); free(cdvd); } void cdvd_set_detected_type(void* udata, int overshoot) { struct ps2_cdvd* cdvd = (struct ps2_cdvd*)udata; cdvd_set_status(cdvd, CDVD_STATUS_PAUSED); cdvd->disc_type = cdvd->detected_disc_type; } int ps2_cdvd_open(struct ps2_cdvd* cdvd, const char* path, int delay) { ps2_cdvd_close(cdvd); cdvd->layer2_lba = 0; cdvd->disc = disc_open(path); if (!cdvd->disc) { printf("cdvd: Couldn't open disc \'%s\'\n", path); return 1; } cdvd->detected_disc_type = disc_get_type(cdvd->disc); cdvd->layer2_lba = disc_get_volume_lba(cdvd->disc, 1); fprintf(stdout, "cdvd: Opened \'%s\' (%s)\n", path, cdvd_get_type_name(cdvd->detected_disc_type)); if (!delay) { cdvd_set_status(cdvd, CDVD_STATUS_SPINNING); cdvd->disc_type = cdvd->detected_disc_type; return 0; } switch (cdvd->detected_disc_type) { case CDVD_DISC_PS2_CD: case CDVD_DISC_PS2_CDDA: case CDVD_DISC_CDDA: case CDVD_DISC_PSX_CD: case CDVD_DISC_PSX_CDDA: { cdvd->disc_type = CDVD_DISC_DETECTING_CD; } break; case CDVD_DISC_PS2_DVD: case CDVD_DISC_DVD_VIDEO: { cdvd->disc_type = cdvd->layer2_lba ? CDVD_DISC_DETECTING_DL_DVD : CDVD_DISC_DETECTING_DVD; } break; } cdvd_set_status(cdvd, CDVD_STATUS_TRAY_OPEN_BIT); struct sched_event event; event.cycles = delay; // IOP clock * 2 = 2s event.udata = cdvd; event.name = "CDVD disc detect"; event.callback = cdvd_set_detected_type; sched_schedule(cdvd->sched, event); return 0; } void ps2_cdvd_close(struct ps2_cdvd* cdvd) { if (cdvd->disc) { disc_close(cdvd->disc); cdvd->disc = NULL; } cdvd->disc_type = CDVD_DISC_NO_DISC; cdvd_set_status(cdvd, CDVD_STATUS_TRAY_OPEN_BIT); // Send disc ejected IRQ cdvd->i_stat = CDVD_IRQ_DISC_EJECTED; ps2_iop_intc_irq(cdvd->intc, IOP_INTC_CDVD); } void ps2_cdvd_power_off(struct ps2_cdvd* cdvd) { // Send poweroff IRQ cdvd->i_stat = CDVD_IRQ_POWER_OFF; ps2_iop_intc_irq(cdvd->intc, IOP_INTC_CDVD); } uint8_t cdvd_read_speed(struct ps2_cdvd* cdvd) { uint8_t speed = cdvd->read_speed & 0x3F; if (!speed) speed = (cdvd->disc_type == CDVD_DISC_PS2_DVD) ? 3 : 5; if (cdvd->disc_type == CDVD_DISC_PS2_DVD) speed += 0xF; else speed--; return speed; } uint64_t ps2_cdvd_read8(struct ps2_cdvd* cdvd, uint32_t addr) { // printf("cdvd: read %08x\n", addr); switch (addr) { case 0x1F402004: printf("cdvd: read n_cmd %x\n", cdvd->n_cmd); return cdvd->n_cmd; case 0x1F402005: printf("cdvd: read n_stat %x\n", cdvd->n_stat); return cdvd->n_stat; // case 0x1F402005: (W) case 0x1F402006: printf("cdvd: read error %x\n", 0); return 0; //cdvd->error; // case 0x1F402007: (W) case 0x1F402008: printf("cdvd: read i_stat %x\n", cdvd->i_stat); return cdvd->i_stat; case 0x1F40200A: printf("cdvd: read status %x\n", cdvd->status); return cdvd->status; case 0x1F40200B: printf("cdvd: read sticky_status %x\n", cdvd->sticky_status); return cdvd->sticky_status; case 0x1F40200F: printf("cdvd: read disc_type %x\n", cdvd->disc_type); return cdvd->disc_type; case 0x1F402013: printf("cdvd: read speed %x\n", cdvd_read_speed(cdvd)); return cdvd_read_speed(cdvd); case 0x1F402015: return 0xff; case 0x1F402016: printf("cdvd: read s_cmd %x\n", cdvd->s_cmd); return cdvd->s_cmd; case 0x1F402017: printf("cdvd: read s_stat %x\n", cdvd->s_stat); return cdvd->s_stat; // case 0x1F402017: (W); case 0x1F402018: { int r = cdvd_read_s_response(cdvd); printf("cdvd: read s_response %x\n", r); return r; } case 0x1F402020: case 0x1F402021: case 0x1F402022: case 0x1F402023: case 0x1F402024: printf("cdvd: ReadKey %08x (%d) -> %02x\n", addr, addr - 0x1f402020, cdvd->cdkey[addr - 0x1F402020]); return cdvd->cdkey[addr - 0x1F402020]; case 0x1F402028: case 0x1F402029: case 0x1F40202A: case 0x1F40202B: case 0x1F40202C: printf("cdvd: ReadKey %08x (%d) -> %02x\n", addr, addr - 0x1f402023, cdvd->cdkey[addr - 0x1F402023]); return cdvd->cdkey[addr - 0x1F402023]; case 0x1F402030: case 0x1F402031: case 0x1F402032: case 0x1F402033: case 0x1F402034: printf("cdvd: ReadKey %08x (%d) -> %02x\n", addr, addr - 0x1f402026, cdvd->cdkey[addr - 0x1F402026]); return cdvd->cdkey[addr - 0x1F402026]; case 0x1F402038: printf("cdvd: ReadKey %08x (%d) -> %02x\n", addr, addr - 0x1f402038, cdvd->cdkey[15]); return cdvd->cdkey[15]; } printf("cdvd: unknown read %08x\n", addr); return 0; } void ps2_cdvd_write8(struct ps2_cdvd* cdvd, uint32_t addr, uint64_t data) { // printf("cdvd: write %08x %02x\n", addr, data); switch (addr) { case 0x1F402004: cdvd_handle_n_command(cdvd, data); return; case 0x1F402005: cdvd_handle_n_param(cdvd, data); return; case 0x1F402006: /* Read-only */ return; case 0x1F402007: printf("cdvd: break\n"); /* To-do: BREAK */ return; case 0x1F402008: cdvd->i_stat &= ~data; return; case 0x1F40200A: return; case 0x1F40200B: return; case 0x1F40200F: return; case 0x1F402016: cdvd_handle_s_command(cdvd, data); return; case 0x1F402017: cdvd_handle_s_param(cdvd, data); return; // case 0x1F402017: (W); case 0x1F40203A: cdvd->mecha_decode = data; return; case 0x1F402018: return; } return; } void ps2_cdvd_reset(struct ps2_cdvd* cdvd) { cdvd->n_stat = 0x4c; cdvd->read_lba = 0x150; cdvd->read_count = 0; cdvd->read_size = 0; cdvd->read_speed = 0; cdvd->config_rw = 0; cdvd->config_offset = 0; cdvd->config_numblocks = 0; cdvd->config_block_index = 0; cdvd->s_cmd = 0; cdvd->buf_size = 0; } void ps2_cdvd_set_mechacon_model(struct ps2_cdvd* cdvd, int model) { cdvd->mechacon_model = model; switch (model) { case CDVD_MECHACON_SPC970: { cdvd->layout = g_spc970_layout; } break; case CDVD_MECHACON_DRAGON: { cdvd->layout = g_dragon_layout; } break; } } int ps2_cdvd_load_nvram(struct ps2_cdvd* cdvd, const char* path) { FILE* file = fopen(path, "rb"); if (!file) { printf("cdvd: Couldn't open NVRAM file \'%s\'\n", path); return 0; } fread(cdvd->nvram, 1, 1024, file); fclose(file); return 1; } ================================================ FILE: src/iop/cdvd.h ================================================ #ifndef CDVD_H #define CDVD_H #ifdef __cplusplus extern "C" { #endif #include #include "scheduler.h" #include "dma.h" #include "disc.h" /* 0 Tray status (1=open) 1 Spindle spinning (1=spinning) 2 Read status (1=reading data sectors) 3 Paused 4 Seek status (1=seeking) 5 Error (1=error occurred) 6-7 Unknown */ #define CDVD_STATUS_TRAY_OPEN_BIT 1 #define CDVD_STATUS_SPINNING_BIT 2 #define CDVD_STATUS_READING_BIT 4 #define CDVD_STATUS_PAUSED_BIT 8 #define CDVD_STATUS_SEEKING_BIT 16 #define CDVD_STATUS_ERROR_BIT 32 #define CDVD_STATUS_STOPPED 0x00 #define CDVD_STATUS_SPINNING 0x02 #define CDVD_STATUS_READING 0x06 #define CDVD_STATUS_PAUSED 0x0A #define CDVD_STATUS_SEEKING 0x12 /* 0 Error (1=error occurred) 1 Unknown/unused 2 DEV9 device connected (1=HDD/network adapter connected) 3 Unknown/unused 4 Test mode 5 Power off ready 6 Drive status (1=ready) 7 Busy executing NCMD */ #define CDVD_N_STATUS_ERROR 1 #define CDVD_N_STATUS_DEV9_CONNECTED 4 #define CDVD_N_STATUS_TEST_MODE 16 #define CDVD_N_STATUS_POWER_OFF 32 #define CDVD_N_STATUS_READY 64 #define CDVD_N_STATUS_BUSY 128 /* 0 Data ready? 1 (N?) Command complete 2 Power off pressed 3 Disk ejected 4 BS_Power DET? 5-7 Unused */ #define CDVD_IRQ_DATA_READY 1 #define CDVD_IRQ_NCMD_DONE 2 #define CDVD_IRQ_POWER_OFF 4 #define CDVD_IRQ_DISC_EJECTED 8 #define CDVD_IRQ_BS_POWER 16 /* 0-5 Unknown 6 Result data available (0=available, 1=no data) 7 Busy */ #define CDVD_S_STATUS_NO_DATA 64 #define CDVD_S_STATUS_BUSY 128 #define CDVD_CD_SS_2328 2328 #define CDVD_CD_SS_2340 2340 #define CDVD_CD_SS_2048 2048 #define CDVD_CD_SS_2352 2352 #define CDVD_DVD_SS 2064 struct nvram_layout { uint32_t bios_version; // bios version that this eeprom layout is for int32_t config0_offset; // offset of 1st config block int32_t config1_offset; // offset of 2nd config block int32_t config2_offset; // offset of 3rd config block int32_t console_id_offset; // offset of console id (?) int32_t ilink_id_offset; // offset of ilink id (ilink mac address) int32_t modelnum_offset; // offset of ps2 model number (eg "SCPH-70002") int32_t regparams_offset; // offset of RegionParams for PStwo int32_t mac_offset; // offset of MAC address on PStwo }; enum { CDVD_MECHACON_SPC970, CDVD_MECHACON_DRAGON }; struct ps2_cdvd { uint8_t n_cmd; uint8_t n_stat; uint8_t error; uint8_t i_stat; uint8_t status; uint8_t sticky_status; uint8_t disc_type; uint8_t s_cmd; uint8_t s_stat; #ifdef _MSC_VER __declspec(align(4)) uint8_t n_params[16]; __declspec(align(4)) uint8_t s_params[16]; #else uint8_t n_params[16] __attribute__((aligned(4))); uint8_t s_params[16] __attribute__((aligned(4))); #endif uint8_t* s_fifo; int n_param_index; int s_param_index; int s_fifo_index; int s_fifo_size; uint8_t detected_disc_type; uint8_t mecha_decode; uint8_t cdkey[16]; struct disc_state* disc; uint8_t buf[2352]; int buf_size; // Pending read uint32_t read_lba; uint32_t read_count; uint32_t read_size; uint8_t read_speed; uint8_t nvram[1024]; struct ps2_iop_dma* dma; struct ps2_iop_intc* intc; struct sched_state* sched; uint64_t layer2_lba; uint32_t config_rw; uint32_t config_offset; uint32_t config_numblocks; uint32_t config_block_index; int mechacon_model; struct nvram_layout layout; // To-do: // void (*poweroff_handler)(void* udata) // void (*trayctrl_handler)(void* udata, uint8_t ctrl) }; struct ps2_cdvd* ps2_cdvd_create(void); void ps2_cdvd_init(struct ps2_cdvd* cdvd, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched); void ps2_cdvd_destroy(struct ps2_cdvd* cdvd); int ps2_cdvd_open(struct ps2_cdvd* cdvd, const char* path, int delay); void ps2_cdvd_close(struct ps2_cdvd* cdvd); void ps2_cdvd_power_off(struct ps2_cdvd* cdvd); int ps2_cdvd_load_nvram(struct ps2_cdvd* cdvd, const char* path); void ps2_cdvd_set_mechacon_model(struct ps2_cdvd* cdvd, int model); uint64_t ps2_cdvd_read8(struct ps2_cdvd* cdvd, uint32_t addr); void ps2_cdvd_write8(struct ps2_cdvd* cdvd, uint32_t addr, uint64_t data); void ps2_cdvd_reset(struct ps2_cdvd* cdvd); #undef ALIGNED_U32 #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/disc/bin.c ================================================ #include #include "../disc.h" #include "bin.h" struct disc_bin* bin_create(void) { return malloc(sizeof(struct disc_bin)); } int bin_init(struct disc_bin* bin, const char* path) { bin->file = fopen(path, "rb"); if (!bin->file) { free(bin); return 0; } return 1; } void bin_destroy(struct disc_bin* bin) { fclose(bin->file); free(bin); } // Disc IF int bin_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size) { struct disc_bin* bin = (struct disc_bin*)udata; int s, r; if (size == DISC_SS_DATA) { s = fseek64(bin->file, (lba * 2352) + 0x18, SEEK_SET); r = fread(buf, 1, 2048, bin->file); } else { s = fseek64(bin->file, lba * 2352, SEEK_SET); r = fread(buf, 1, 2352, bin->file); } return r && !s; } uint64_t bin_get_size(void* udata) { struct disc_bin* bin = (struct disc_bin*)udata; fseek64(bin->file, 0, SEEK_END); return ftell64(bin->file); } int bin_get_sector_size(void* udata) { return 2352; } int bin_get_track_count(void* udata) { return 1; } int bin_get_track_info(void* udata, int track, struct track_info* info) { return 0; } int bin_get_track_number(void* udata, uint64_t lba) { return 1; } #undef fseek64 #undef ftell64 ================================================ FILE: src/iop/disc/bin.h ================================================ #ifndef BIN_H #define BIN_H #ifdef __cplusplus extern "C" { #endif #include #include #include "../disc.h" struct disc_bin { FILE* file; }; struct disc_bin* bin_create(void); int bin_init(struct disc_bin* bin, const char* path); void bin_destroy(struct disc_bin* bin); // Disc IF int bin_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size); uint64_t bin_get_size(void* udata); int bin_get_sector_size(void* udata); int bin_get_track_count(void* udata); int bin_get_track_info(void* udata, int track, struct track_info* info); int bin_get_track_number(void* udata, uint64_t lba); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/disc/chd.c ================================================ #include #include #include #include "chd.h" int get_sector_type_size(const char* type) { if (strncmp(type, "MODE1", 64) == 0) { return 2048; } else if (strncmp(type, "MODE1_RAW", 64) == 0) { return 2352; } else if (strncmp(type, "MODE2", 64) == 0) { return 2336; } else if (strncmp(type, "MODE2_FORM1", 64) == 0) { return 2048; } else if (strncmp(type, "MODE2_FORM2", 64) == 0) { return 2324; } else if (strncmp(type, "MODE2_FORM_MIX", 64) == 0) { return 2336; } else if (strncmp(type, "MODE2_RAW", 64) == 0) { return 2352; } return 0; } struct disc_chd* chd_create(void) { return malloc(sizeof(struct disc_chd)); } int chd_init(struct disc_chd* chd, const char* path) { if (chd_open(path, CHD_OPEN_READ, NULL, &chd->file)) { chd_close(chd->file); return 0; } chd->header = chd_get_header(chd->file); chd->buffer = malloc(chd->header->hunkbytes); // Get sector size from metadata char buf[512], type[64]; uint32_t len, tag; uint8_t flags; if (chd_get_metadata(chd->file, CDROM_TRACK_METADATA2_TAG, 0, buf, 512, &len, &tag, &flags)) { printf("chd: Failed to get metadata\n"); return 0; } if (tag != CDROM_TRACK_METADATA2_TAG) { printf("chd: Failed to get track metadata\n"); return 0; } // Parse track type char* c = buf; char* t = type; while (*c != ':') c++; c++; while (*c != ':') c++; c++; while (((t - type) < 64) && !isspace(*c)) { *t++ = *c++; } *t = '\0'; chd->sector_size = get_sector_type_size(type); if (!chd->sector_size) { printf("chd: Unsupported sector type \'%s\'\n", type); return 0; } return 1; } void chd_destroy(struct disc_chd* chd) { chd_close(chd->file); free(chd->buffer); free(chd); } static inline size_t find_hunk_number(struct disc_chd* chd, uint64_t offset) { return offset / chd->header->hunkbytes; } // Disc IF int chd_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size) { struct disc_chd* chd = (struct disc_chd*)udata; uint64_t offset = lba * chd->header->unitbytes; size_t hunknum = find_hunk_number(chd, offset); if (chd->cached_hunknum != hunknum) { chd->cached_hunknum = hunknum; // Cache hunk chd_read(chd->file, hunknum, chd->buffer); } else { uint8_t* base = chd->buffer + (offset % chd->header->hunkbytes); // Hunk is cached if (chd->sector_size == 2048) { memcpy(buf, base, 2048); } else { if (size == DISC_SS_DATA) { memcpy(buf, base + 0x18, 2048); } else { memcpy(buf, base, 2352); } } return 1; } // Read cached hunk return chd_read_sector(udata, buf, lba, size); } uint64_t chd_get_size(void* udata) { struct disc_chd* chd = (struct disc_chd*)udata; return chd->sector_size * chd->header->hunkcount; } int chd_get_sector_size(void* udata) { struct disc_chd* chd = (struct disc_chd*)udata; return chd->sector_size; } int chd_get_track_count(void* udata) { return 1; } int chd_get_track_info(void* udata, int track, struct track_info* info) { return 0; } int chd_get_track_number(void* udata, uint64_t lba) { return 1; } #undef fseek64 #undef ftell64 ================================================ FILE: src/iop/disc/chd.h ================================================ #ifndef CHD_H #define CHD_H #ifdef __cplusplus extern "C" { #endif #include "../disc.h" #include #include #include struct disc_chd { const chd_header* header; chd_file* file; uint8_t* buffer; size_t cached_hunknum; int sector_size; }; struct disc_chd* chd_create(void); int chd_init(struct disc_chd* chd, const char* path); void chd_destroy(struct disc_chd* chd); // Disc IF int chd_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size); uint64_t chd_get_size(void* udata); int chd_get_sector_size(void* udata); int chd_get_track_count(void* udata); int chd_get_track_info(void* udata, int track, struct track_info* info); int chd_get_track_number(void* udata, uint64_t lba); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/disc/ciso.c ================================================ #include #include #include #include "ciso.h" #define CISO_DEFLATE 0 #define CISO_LZ4 1 int get_compression_type(const char* path) { char* ext = strrchr(path, '.'); if (!ext) { return CISO_DEFLATE; } if (tolower(ext[1]) == 'c') { return CISO_DEFLATE; } else if (tolower(ext[1]) == 'z') { return CISO_LZ4; } return CISO_DEFLATE; } int realloc_comp_buf(struct disc_ciso* ciso, size_t size) { if (size > ciso->comp_buf_cap) { uint8_t* new_buf = realloc(ciso->comp_buf, size); if (!new_buf) { return 0; } ciso->comp_buf = new_buf; ciso->comp_buf_cap = size; } return 1; } struct disc_ciso* ciso_create(void) { return malloc(sizeof(struct disc_ciso)); } int ciso_init(struct disc_ciso* ciso, const char* path) { memset(ciso, 0, sizeof(struct disc_ciso)); ciso->file = fopen(path, "rb"); if (!ciso->file) { free(ciso); return 0; } fread(&ciso->header, sizeof(struct ciso_header), 1, ciso->file); if (ciso->header.magic != 0x4f534943 && ciso->header.magic != 0x4f53495a) { fprintf(stderr, "ciso: Invalid CISO/ZISO magic\n"); ciso_destroy(ciso); return 0; } if (ciso->header.version > 1) { fprintf(stderr, "ciso: Unsupported CISO version %u\n", ciso->header.version); ciso_destroy(ciso); return 0; } if (ciso->header.block_size != 2048) { fprintf(stderr, "ciso: Unsupported CISO header size %u\n", ciso->header.header_size); ciso_destroy(ciso); return 0; } ciso->compression_type = get_compression_type(path); ciso->decompressor = libdeflate_alloc_decompressor(); if (!ciso->decompressor) { fprintf(stderr, "ciso: Failed to allocate decompressor\n"); ciso_destroy(ciso); return 0; } ciso->index_count = (ciso->header.uncompressed_size / (uint64_t)ciso->header.block_size) + 1; ciso->index_table = malloc(ciso->index_count * sizeof(uint32_t)); if (!ciso->index_table) { fprintf(stderr, "ciso: Failed to allocate index table\n"); ciso_destroy(ciso); return 0; } fread(ciso->index_table, ciso->index_count * sizeof(uint32_t), 1, ciso->file); return 1; } void ciso_destroy(struct disc_ciso* ciso) { if (ciso->file) fclose(ciso->file); if (ciso->decompressor) libdeflate_free_decompressor(ciso->decompressor); if (ciso->index_table) free(ciso->index_table); if (ciso->comp_buf) free(ciso->comp_buf); free(ciso); } // Disc IF int ciso_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size) { struct disc_ciso* ciso = (struct disc_ciso*)udata; if (lba * 2048 >= ciso->header.uncompressed_size) return 0; size = 2048; uint32_t index_entry = ciso->index_table[lba]; uint32_t next_index_entry = ciso->index_table[lba + 1]; uint32_t offset = index_entry & 0x7fffffff; uint32_t next_offset = next_index_entry & 0x7fffffff; uint32_t buf_size = next_offset - offset; int is_compressed = !(index_entry & 0x80000000); fseek(ciso->file, offset, SEEK_SET); if (is_compressed) { realloc_comp_buf(ciso, buf_size); fseek(ciso->file, offset, SEEK_SET); fread(ciso->comp_buf, 1, buf_size, ciso->file); size_t actual_decomp_size = 0; if (ciso->compression_type == CISO_LZ4) { actual_decomp_size = LZ4_decompress_safe( (const char*)ciso->comp_buf, (char*)buf, buf_size, size ); } else { libdeflate_deflate_decompress( ciso->decompressor, ciso->comp_buf, buf_size, buf, size, &actual_decomp_size ); } if (actual_decomp_size != 2048) { fprintf(stderr, "ciso: Decompression failed for sector %llu\n", lba); return 0; } return 1; } if (buf_size != 2048) { fprintf(stderr, "ciso: Invalid uncompressed sector size %u for sector %llu\n", buf_size, lba); return 0; } fread(buf, 1, buf_size, ciso->file); return 1; } uint64_t ciso_get_size(void* udata) { struct disc_ciso* ciso = (struct disc_ciso*)udata; return ciso->header.uncompressed_size; } int ciso_get_sector_size(void* udata) { return 2048; } int ciso_get_track_count(void* udata) { return 1; } int ciso_get_track_info(void* udata, int track, struct track_info* info) { return 0; } int ciso_get_track_number(void* udata, uint64_t lba) { return 1; } ================================================ FILE: src/iop/disc/ciso.h ================================================ #ifndef CISO_H #define CISO_H #ifdef __cplusplus extern "C" { #endif #include "../disc.h" #include #include #include #include struct ciso_header { uint32_t magic; uint32_t header_size; uint64_t uncompressed_size; uint32_t block_size; uint8_t version; uint8_t alignment; uint8_t reserved[2]; }; struct disc_ciso { struct ciso_header header; struct libdeflate_decompressor* decompressor; FILE* file; uint32_t* index_table; size_t index_count; int compression_type; uint8_t* comp_buf; size_t comp_buf_cap; }; struct disc_ciso* ciso_create(void); int ciso_init(struct disc_ciso* ciso, const char* path); void ciso_destroy(struct disc_ciso* ciso); // Disc IF int ciso_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size); uint64_t ciso_get_size(void* udata); int ciso_get_sector_size(void* udata); int ciso_get_track_count(void* udata); int ciso_get_track_info(void* udata, int track, struct track_info* info); int ciso_get_track_number(void* udata, uint64_t lba); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/disc/cue.c ================================================ #include #include #include #include #include #include "cue.h" #include "../disc.h" static const char* cue_keywords[] = { "4CH", "AIFF", "AUDIO", "BINARY", "CATALOG", "CDG", "CDI/2336", "CDI/2352", "CDTEXTFILE", "DCP", "FILE", "FLAGS", "INDEX", "ISRC", "MODE1/2048", "MODE1/2352", "MODE2/2336", "MODE2/2352", "MOTOROLA", "MP3", "PERFORMER", "POSTGAP", "PRE", "PREGAP", "REM", "SCMS", "SONGWRITER", "TITLE", "TRACK", "WAVE", 0 }; char* strapp(char* dst, const char* a, const char* b) { char* d = dst; while (*a) *dst++ = *a++; while (*b) *dst++ = *b++; *dst = '\0'; return d; } const char* find_last_slash(const char* a) { if (!a) return NULL; const char* b = a; while (*a) { if (*a == '/' || *a == '\\') b = a + 1; ++a; } return b; } char* get_root_path(char* dst, const char* a) { if (!a) { *dst = '\0'; return dst; } const char* b = a; const char* c = a; char* d = dst; while (*a) { if (*a == '/' || *a == '\\') b = a + 1; ++a; } while (c != b) *dst++ = *c++; *dst = '\0'; return d; } int cue_parse_keyword(struct disc_cue* cue) { char buf[256]; char* ptr = buf; while (isalpha(cue->c) || isdigit(cue->c) || cue->c == '/') { *ptr++ = cue->c; cue->c = fgetc(cue->file); } *ptr = '\0'; int i = 0; const char* keyword = cue_keywords[i]; while (keyword) { if (!strcmp(keyword, buf)) { return i; } else { keyword = cue_keywords[++i]; } } return -1; } int cue_parse_number(struct disc_cue* cue) { if (!isdigit(cue->c)) return 0; char buf[4]; char* ptr = buf; while (isdigit(cue->c)) { *ptr++ = cue->c; cue->c = fgetc(cue->file); } *ptr = '\0'; return atoi(buf); } uint64_t cue_parse_msf(struct disc_cue* cue) { int m = 0; int s = 0; int f = 0; if (!isdigit(cue->c)) return 0; m = cue_parse_number(cue); if (cue->c != ':') return 0; cue->c = fgetc(cue->file); s = cue_parse_number(cue); if (cue->c != ':') return 0; cue->c = fgetc(cue->file); f = cue_parse_number(cue); // 1 second = 75 frames (sectors) // 1 minute = 60 seconds = 4500 frames return f + (s * 75) + (m * 4500); } void cue_parse_index(struct disc_cue* cue) { cue_track_t* track = list_back(cue->tracks)->data; while (isspace(cue->c)) cue->c = fgetc(cue->file); if (!isdigit(cue->c)) return; int i = cue_parse_number(cue); while (isspace(cue->c)) cue->c = fgetc(cue->file); if (i > 1) return; track->index[i] = cue_parse_msf(cue); } cue_track_t* cue_parse_track(struct disc_cue* cue) { while (isspace(cue->c)) cue->c = fgetc(cue->file); if (!isdigit(cue->c)) return NULL; cue_track_t* track = malloc(sizeof(cue_track_t)); track->end = 0; track->start = 0; track->pregap = 0; track->index[0] = -1; track->index[1] = -1; track->file = list_back(cue->files)->data; track->number = cue_parse_number(cue); while (isspace(cue->c)) cue->c = fgetc(cue->file); track->mode = cue_parse_keyword(cue); return track; } cue_file_t* cue_parse_file(struct disc_cue* cue, const char* p, const char* s) { while (isspace(cue->c)) cue->c = fgetc(cue->file); if (cue->c != '\"') return NULL; cue_file_t* file = malloc(sizeof(cue_file_t)); file->tracks = list_create(); file->name = malloc(512); // Append root path to track file path char* ptr = file->name; while (p != s) *ptr++ = *p++; cue->c = fgetc(cue->file); while (cue->c != '\"') { *ptr++ = cue->c; cue->c = fgetc(cue->file); } *ptr = '\0'; cue->c = fgetc(cue->file); // Ignore file type while (isspace(cue->c)) cue->c = fgetc(cue->file); while (isalpha(cue->c)) cue->c = fgetc(cue->file); return file; } struct disc_cue* cue_create(void) { return malloc(sizeof(struct disc_cue)); } int cue_parse(struct disc_cue* cue, const char* path) { cue->file = fopen(path, "rb"); if (!cue->file) return CUE_FILE_NOT_FOUND; const char* s = find_last_slash(path); cue->c = fgetc(cue->file); while (isspace(cue->c)) cue->c = fgetc(cue->file); while (!feof(cue->file)) { int kw = cue_parse_keyword(cue); switch (kw) { case CUE_FILE: { list_push_back(cue->files, cue_parse_file(cue, path, s)); } break; case CUE_TRACK: { cue_track_t* track = cue_parse_track(cue); cue_file_t* file = list_back(cue->files)->data; list_push_back(cue->tracks, track); list_push_back(file->tracks, track); } break; case CUE_INDEX: { cue_parse_index(cue); } break; case CUE_REM: case CUE_PREGAP: case CUE_FLAGS: case CUE_POSTGAP: { // Ignore everything until a newline (handle CRLF and LF) while ((cue->c != '\n') && (cue->c != '\r')) cue->c = fgetc(cue->file); while ((cue->c == '\n') && (cue->c == '\r')) cue->c = fgetc(cue->file); } break; default: { printf("Unknown keyword: %s (%u)\n", cue_keywords[kw], kw); return 1; } break; } while (isspace(cue->c)) cue->c = fgetc(cue->file); } return 0; } size_t get_file_size(FILE* file) { fseek64(file, 0, SEEK_END); size_t size = ftell64(file); fseek64(file, 0, SEEK_SET); return size; } /* (0 - 0 ) = 150 (0 - 0 ) + 150 + 315000 = 315150 (0 - 0 ) + 315150 + 375 = 315525 (150 - 0 ) + 315525 + 390 = 316065 (195 - 150) + 316065 + 390 = 316500 (155 - 190) + 316500 + 390 = */ int prev_pregap = 0; int init_tracks(cue_file_t* file, uint64_t* lba) { node_t* node = list_front(file->tracks); // 1 track per file case if (file->tracks->size == 1) { cue_track_t* data = node->data; data->pregap = 0; if ((data->index[0] != -1) && (data->index[1] != -1)) data->pregap = data->index[1]; data->start = *lba + data->pregap; data->end = data->start + (file->size / 0x930); *lba = data->end; return 0; } // Multiple tracks per file while (node) { cue_track_t* data = node->data; // If this is the last track if (!node->next) { data->pregap = 0; data->start = data->index[1] + 150; data->end = file->size / 0x930; return 0; } cue_track_t* next = node->next->data; data->start = data->index[1] + 150; data->end = (next->index[1] + 150) - 1; data->pregap = 0; node = node->next; } return 0; } int cue_load(struct disc_cue* cue, int mode) { node_t* node = list_front(cue->files); // 00:02:00 uint64_t lba = 2 * 75; while (node) { cue_file_t* data = node->data; FILE* file = fopen(data->name, "rb"); if (!file) return CUE_TRACK_FILE_NOT_FOUND; data->buf_mode = mode; data->size = get_file_size(file); // printf("Loaded \'%s\': size=%llx, sectors=%llu\n", // data->name, // data->size, // data->size / 0x930 // ); if (data->buf_mode == LD_BUFFERED) { data->buf = malloc(data->size); fseek64(file, 0, SEEK_SET); if (!fread(data->buf, 1, data->size, file)) return CUE_TRACK_READ_ERROR; fclose(file); } else { data->buf = file; } data->start = lba; init_tracks(data, &lba); node = node->next; } return CUE_OK; } void cue_destroy(struct disc_cue* cue) { node_t* node = list_front(cue->files); while (node) { cue_file_t* file = node->data; if (file->buf_mode == LD_BUFFERED) { free(file->buf); } else { fclose((FILE*)file->buf); } list_destroy(file->tracks); free(file->name); free(file); node = node->next; } list_destroy(cue->files); node = list_front(cue->tracks); while (node) { free(node->data); node = node->next; } list_destroy(cue->tracks); free(cue); } cue_track_t* get_sector_track(struct disc_cue* cue, uint64_t lba) { node_t* node = list_front(cue->tracks); while (node) { cue_track_t* track = node->data; if ((lba >= track->start) && (lba < track->end)) return track; node = node->next; } return NULL; } cue_track_t* get_sector_track_in_pregap(struct disc_cue* cue, uint64_t lba) { node_t* node = list_front(cue->tracks); while (node) { cue_track_t* track = node->data; if (!node->next) return track; cue_track_t* next = node->next->data; // Ignore sector number int curr_start = track->start - (track->start % 75); int next_start = next->start - (next->start % 75); if ((lba >= curr_start) && (lba < next_start)) return track; node = node->next; } return NULL; } int cue_query(struct disc_cue* cue, uint64_t lba) { if (lba >= ((cue_track_t*)list_back(cue->tracks)->data)->end) return TS_FAR; cue_track_t* track = get_sector_track(cue, lba); // If the LBA isn't too far but the track wasn't found // then we are being requested a pregap sector. Clear buffer // and initialize sync data (not actually needed) if (!track) return TS_PREGAP; return (track->mode != CUE_AUDIO) ? TS_DATA : TS_AUDIO; } int cue_read(struct disc_cue* cue, uint64_t lba, void* buf, int* sector_size) { if (lba >= ((cue_track_t*)list_back(cue->tracks)->data)->end) return TS_FAR; cue_track_t* track = get_sector_track(cue, lba); *sector_size = 2352; switch (track->mode) { case CUE_MODE1_2048: *sector_size = 2048; break; case CUE_MODE2_2336: *sector_size = 2336; break; case CUE_MODE2_2352: *sector_size = 2352; break; } // If the LBA isn't too far but the track wasn't found // then we are being requested a pregap sector. Clear buffer // and initialize sync data (not actually needed) if (!track) { memset((uint8_t*)buf, 0, *sector_size); memset((uint8_t*)buf + 1, 255, 10); return TS_PREGAP; } cue_file_t* file = track->file; // printf("Reading sector %u at track %u, file=%s (%u), offset=%u (%08x)\n", // lba, // track->number, // track->file->name, // file->start, // lba - file->start, // (lba - file->start) * 2352 // ); if (file->buf_mode == LD_BUFFERED) { uint8_t* ptr = (uint8_t*)file->buf + ((lba - file->start) * (*sector_size)); memcpy(buf, ptr, *sector_size); } else { fseek64(file->buf, (lba - file->start) * (*sector_size), SEEK_SET); // Should always succeed, ignore result for speed (void)fread(buf, 1, *sector_size, file->buf); } return (track->mode != CUE_AUDIO) ? TS_DATA : TS_AUDIO; } int cue_get_track_number_impl(struct disc_cue* cue, uint64_t lba) { cue_track_t* track = get_sector_track_in_pregap(cue, lba); return track->number; } int cue_get_track_count_impl(struct disc_cue* cue) { return cue->tracks->size; } int cue_get_track_lba(struct disc_cue* cue, int track) { if (!track) return ((cue_track_t*)list_back(cue->tracks)->data)->end; if (track > cue->tracks->size) return TS_FAR; cue_track_t* data = list_at(cue->tracks, track - 1)->data; return data->start; } int cue_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size) { struct disc_cue* cue = (struct disc_cue*)udata; // Adjust for lead-in lba += 150; char temp[2352]; int sector_size; if (cue_read(cue, lba, temp, §or_size) == TS_FAR) { if (size == DISC_SS_DATA) { memset(buf, 0, 2048); } else { memset(buf, 0, 2352); } return 0; } if (size == DISC_SS_DATA) { if (sector_size == 2048) { memcpy(buf, temp, 2048); } else { memcpy(buf, temp + 0x18, 2048); } } else { memcpy(buf, temp, sector_size); } return 1; } uint64_t cue_get_size(void* udata) { struct disc_cue* cue = (struct disc_cue*)udata; unsigned int size = 0; for (int i = 0; i < cue->files->size; i++) { cue_file_t* file = list_at(cue->files, i)->data; size += file->size; } return size; } int cue_get_sector_size(void* udata) { return 2352; } int cue_init(struct disc_cue* cue, const char* path) { cue->files = list_create(); cue->tracks = list_create(); if (cue_parse(cue, path) != CUE_OK) { printf("cue: Failed to parse CUE file '%s'\n", path); return 0; } if (cue_load(cue, LD_FILE) != CUE_OK) { printf("cue: Failed to load CUE file '%s'\n", path); return 0; } return 1; } int cue_get_track_count(void* udata) { struct disc_cue* cue = (struct disc_cue*)udata; return cue_get_track_count_impl(cue); } int cue_get_track_info(void* udata, int track, struct track_info* info) { struct disc_cue* cue = (struct disc_cue*)udata; if (track > cue->tracks->size) return 0; cue_track_t* data = list_at(cue->tracks, track - 1)->data; info->number = data->number; info->type = (data->mode != CUE_AUDIO) ? TS_DATA : TS_AUDIO; info->lba = data->start; return 1; } int cue_get_track_number(void* udata, uint64_t lba) { struct disc_cue* cue = (struct disc_cue*)udata; return cue_get_track_number_impl(cue, lba + 150); } ================================================ FILE: src/iop/disc/cue.h ================================================ #ifndef CUE_H #define CUE_H #include "list.h" #include "../disc.h" #include #include #include enum { TS_FAR = 0, TS_DATA, TS_AUDIO, TS_PREGAP }; enum { CUE_OK = 0, CUE_FILE_NOT_FOUND, CUE_TRACK_FILE_NOT_FOUND, CUE_TRACK_READ_ERROR }; enum { CUE_4CH = 0, CUE_AIFF, CUE_AUDIO, CUE_BINARY, CUE_CATALOG, CUE_CDG, CUE_CDI_2336, CUE_CDI_2352, CUE_CDTEXTFILE, CUE_DCP, CUE_FILE, CUE_FLAGS, CUE_INDEX, CUE_ISRC, CUE_MODE1_2048, CUE_MODE1_2352, CUE_MODE2_2336, CUE_MODE2_2352, CUE_MOTOROLA, CUE_MP3, CUE_PERFORMER, CUE_POSTGAP, CUE_PRE, CUE_PREGAP, CUE_REM, CUE_SCMS, CUE_SONGWRITER, CUE_TITLE, CUE_TRACK, CUE_WAVE, CUE_NONE = 255 }; enum { LD_BUFFERED, LD_FILE }; typedef struct { char* name; int buf_mode; void* buf; size_t size; uint64_t start; list_t* tracks; } cue_file_t; typedef struct { int number; int mode; int32_t index[2]; uint64_t pregap; uint64_t start; uint64_t end; cue_file_t* file; } cue_track_t; struct disc_cue { list_t* files; list_t* tracks; char c; FILE* file; }; struct disc_cue* cue_create(void); int cue_init(struct disc_cue* cue, const char* path); void cue_destroy(struct disc_cue* cue); // Disc interface int cue_read(struct disc_cue* cue, uint64_t lba, void* buf, int* sector_size); int cue_query(struct disc_cue* cue, uint64_t lba); int cue_get_track_number_impl(struct disc_cue* cue, uint64_t lba); int cue_get_track_count_impl(struct disc_cue* cue); int cue_get_track_lba(struct disc_cue* cue, int track); int cue_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size); uint64_t cue_get_size(void* udata); int cue_get_sector_size(void* udata); int cue_get_track_count(void* udata); int cue_get_track_info(void* udata, int track, struct track_info* info); int cue_get_track_number(void* udata, uint64_t lba); #endif ================================================ FILE: src/iop/disc/iso.c ================================================ #include #include "iso.h" struct disc_iso* iso_create(void) { return malloc(sizeof(struct disc_iso)); } int iso_init(struct disc_iso* iso, const char* path) { iso->file = fopen(path, "rb"); if (!iso->file) { free(iso); return 0; } return 1; } void iso_destroy(struct disc_iso* iso) { fclose(iso->file); free(iso); } // Disc IF int iso_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size) { struct disc_iso* iso = (struct disc_iso*)udata; int s, r; s = fseek64(iso->file, lba * 0x800, SEEK_SET); r = fread(buf, 1, 0x800, iso->file); return r && !s; } uint64_t iso_get_size(void* udata) { struct disc_iso* iso = (struct disc_iso*)udata; fseek64(iso->file, 0, SEEK_END); return ftell64(iso->file); } int iso_get_sector_size(void* udata) { return 2048; } int iso_get_track_count(void* udata) { return 1; } int iso_get_track_info(void* udata, int track, struct track_info* info) { return 0; } int iso_get_track_number(void* udata, uint64_t lba) { return 1; } #undef fseek64 #undef ftell64 ================================================ FILE: src/iop/disc/iso.h ================================================ #ifndef ISO_H #define ISO_H #ifdef __cplusplus extern "C" { #endif #include "../disc.h" #include #include struct disc_iso { FILE* file; }; struct disc_iso* iso_create(void); int iso_init(struct disc_iso* iso, const char* path); void iso_destroy(struct disc_iso* iso); // Disc IF int iso_read_sector(void* udata, unsigned char* buf, uint64_t lba, int size); uint64_t iso_get_size(void* udata); int iso_get_sector_size(void* udata); int iso_get_track_count(void* udata); int iso_get_track_info(void* udata, int track, struct track_info* info); int iso_get_track_number(void* udata, uint64_t lba); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/disc.c ================================================ #include #include #include #include #include "disc.h" #include "disc/iso.h" #include "disc/cue.h" #include "disc/chd.h" #include "disc/ciso.h" #include "disc/bin.h" #ifdef _MSC_VER __pragma(pack(push, 1)) #define PACKED #else #define PACKED __attribute__((packed)) #endif struct PACKED iso9660_pvd { char id[8]; char system_id[32]; char volume_id[32]; char zero[8]; uint32_t total_sector_le, total_sect_be; char zero2[32]; uint16_t volume_set_size_le, volume_set_size_be; uint16_t volume_seq_nr_le, volume_seq_nr_be; uint16_t sector_size_le, sector_size_be; uint32_t path_table_len_le, path_table_len_be; uint32_t path_table_le, path_table_2nd_le; uint32_t path_table_be, path_table_2nd_be; uint8_t root[34]; char volume_set_id[128], publisher_id[128], data_preparer_id[128], application_id[128]; char copyright_file_id[37], abstract_file_id[37], bibliographical_file_id[37]; }; struct PACKED iso9660_dirent { uint8_t dr_len; uint8_t ext_dr_len; uint32_t lba_le, lba_be; uint32_t size_le, size_be; uint8_t date[7]; uint8_t flags; uint8_t file_unit_size; uint8_t interleave_gap_size; uint16_t volume_seq_nr_le, volume_seq_nr_be; uint8_t id_len; uint8_t id; }; #ifdef _MSC_VER __pragma(pack(pop)) #endif static const char* disc_extensions[] = { "iso", "bin", "cue", "chd", "cso", "zso", NULL }; static inline int disc_fetch_pvd(struct disc_state* disc) { if (disc->pvd_cached) return 1; if (!disc_read_sector(disc, disc->pvd, 16, DISC_SS_DATA)) return 0; disc->pvd_cached = 1; return 1; } static inline int disc_fetch_root(struct disc_state* disc) { if (disc->root_cached) return 1; if (!disc_fetch_pvd(disc)) return 0; struct iso9660_pvd pvd = *(struct iso9660_pvd*)disc->pvd; struct iso9660_dirent* root = (struct iso9660_dirent*)pvd.root; // Root points to unreadable sector if (!disc_read_sector(disc, disc->root, root->lba_le, DISC_SS_DATA)) return 0; disc->root_cached = 1; return 1; } static inline int disc_fetch_system_cnf(struct disc_state* disc) { if (disc->system_cnf_cached) return 1; if (!disc_fetch_root(disc)) return 0; struct iso9660_dirent* dir = (struct iso9660_dirent*)disc->root; while (dir->dr_len) { if (dir->id_len == 12) { if (!strcmp((char*)&dir->id, "SYSTEM.CNF;1")) { break; } } uint8_t* ptr = (uint8_t*)dir; dir = (struct iso9660_dirent*)(ptr + dir->dr_len); } // Couldn't find SYSTEM.CNF file, non-playstation disc if (!dir->dr_len) return 0; // SYSTEM.CNF points to unreadable sector, invalid if (!disc_read_sector(disc, disc->system_cnf, dir->lba_le, DISC_SS_DATA)) return 0; disc->system_cnf_cached = 1; return 1; } int disc_get_extension(const char* path) { if (!path) return DISC_EXT_UNSUPPORTED; const char* ptr = strrchr(path, '.'); if (!ptr) { return DISC_EXT_NONE; } char* lc = strdup(ptr + 1); for (char* p = lc; *p; p++) { *p = tolower(*p); } for (int i = 0; disc_extensions[i]; i++) { if (!strcmp(lc, disc_extensions[i])) { free(lc); return i; } } free(lc); return DISC_EXT_UNSUPPORTED; } struct disc_state* disc_open(const char* path) { int ext = disc_get_extension(path); if (ext == DISC_EXT_UNSUPPORTED) return NULL; struct disc_state* s = malloc(sizeof(struct disc_state)); memset(s, 0, sizeof(struct disc_state)); s->layer2_lba = 0; s->ext = ext; int r; switch (ext) { // Standard raw 2048-byte sector ISO 9660 image // usually used for DVDs case DISC_EXT_ISO: { struct disc_iso* iso = iso_create(); s->udata = iso; s->read_sector = iso_read_sector; s->get_size = iso_get_size; s->get_sector_size = iso_get_sector_size; s->get_track_count = iso_get_track_count; s->get_track_info = iso_get_track_info; s->get_track_number = iso_get_track_number; // To-do: Check if path exists r = iso_init(iso, path); } break; // Raw 2352-byte sector disc image (CD) case DISC_EXT_NONE: case DISC_EXT_BIN: { struct disc_bin* bin = bin_create(); s->udata = bin; s->read_sector = bin_read_sector; s->get_size = bin_get_size; s->get_sector_size = bin_get_sector_size; s->get_track_count = bin_get_track_count; s->get_track_info = bin_get_track_info; s->get_track_number = bin_get_track_number; r = bin_init(bin, path); } break; // CUE+BIN disc image (contains track information) case DISC_EXT_CUE: { struct disc_cue* cue = cue_create(); s->udata = cue; s->read_sector = cue_read_sector; s->get_size = cue_get_size; s->get_sector_size = cue_get_sector_size; s->get_track_count = cue_get_track_count; s->get_track_info = cue_get_track_info; s->get_track_number = cue_get_track_number; r = cue_init(cue, path); } break; // MAME CHD disc image (Compressed Hunks of Data) case DISC_EXT_CHD: { struct disc_chd* chd = chd_create(); s->udata = chd; s->read_sector = chd_read_sector; s->get_size = chd_get_size; s->get_sector_size = chd_get_sector_size; s->get_track_count = chd_get_track_count; s->get_track_info = chd_get_track_info; s->get_track_number = chd_get_track_number; r = chd_init(chd, path); } break; case DISC_EXT_CSO: case DISC_EXT_ZSO: { struct disc_ciso* ciso = ciso_create(); s->udata = ciso; s->read_sector = ciso_read_sector; s->get_size = ciso_get_size; s->get_sector_size = ciso_get_sector_size; s->get_track_count = ciso_get_track_count; s->get_track_info = ciso_get_track_info; s->get_track_number = ciso_get_track_number; r = ciso_init(ciso, path); } break; default: { free(s); return NULL; } break; } if (!r) { free(s); return NULL; } return s; } #define CD_EXTRA_SIZE 800000000 #define CDX_MAX_SIZE 734003200 #define CD_MAX_SIZE 681574400 int disc_detect_media(struct disc_state* disc) { uint64_t size = disc_get_size(disc); disc_fetch_pvd(disc); uint64_t sector_size = disc_get_sector_size(disc); uint64_t volume_size = *(uint32_t*)&disc->pvd[0x50]; uint64_t path_table_lba = *(uint32_t*)&disc->pvd[0x8c]; printf("sector_size=%lx volume_size=%lx (%ld) in bytes=%lx (%ld) path_table_lba=%lx (%ld) size=%lx (%ld)\n", sector_size, volume_size, volume_size, volume_size * sector_size, volume_size * sector_size, path_table_lba, path_table_lba, size, size ); // DVD is dual-layer if ((volume_size * sector_size) < size) { disc->layer2_lba = volume_size; return DISC_MEDIA_DVD; } if (((volume_size * sector_size) <= CD_EXTRA_SIZE) && (path_table_lba != 257)) { return DISC_MEDIA_CD; } return DISC_MEDIA_DVD; } static inline int disc_detect_type(struct disc_state* disc) { if (!disc_fetch_pvd(disc)) return DISC_TYPE_INVALID; struct iso9660_pvd pvd = *(struct iso9660_pvd*)disc->pvd; // Not ISO 9660 format disc if (strncmp(pvd.id, "\1CD001\1", 8)) { // If the disc doesn't contain an ISO filesystem // and it's a CUE image, it's probably a CD audio image if (disc->ext == DISC_EXT_CUE) { return DISC_TYPE_CDDA; } // Otherwise it's invalid return DISC_TYPE_INVALID; } // Check for the "PLAYSTATION" string at PVD offset 08h // Patch 20 byte so comparison is done correctly disc->pvd[0x13] = 0; // Disc contains a "valid" ISO filesystem, but it's not a // PlayStation disc, it might be a DVD video disc or something // else entirely, either way don't outright reject it just yet if (strncmp(&disc->pvd[0x8], "PLAYSTATION", 12)) return DISC_TYPE_UNKNOWN; // Disc contains a valid ISO filesystem and the PlayStation string // is present, so this is most likely a game disc. return DISC_TYPE_GAME; } int disc_get_type(struct disc_state* disc) { int media = disc_detect_media(disc); int type = disc_detect_type(disc); if (type == DISC_TYPE_INVALID) { return CDVD_DISC_INVALID; } if (type == DISC_TYPE_CDDA) { return CDVD_DISC_CDDA; } // Start final detection char buf[2048]; if (!disc_read_sector(disc, buf, 16, DISC_SS_DATA)) { return CDVD_DISC_INVALID; } struct iso9660_pvd pvd = *(struct iso9660_pvd*)buf; struct iso9660_dirent* root = (struct iso9660_dirent*)pvd.root; // Root points to unreadable sector if (!disc_read_sector(disc, buf, root->lba_le, DISC_SS_DATA)) { return CDVD_DISC_INVALID; } struct iso9660_dirent* dir = (struct iso9660_dirent*)buf; while (dir->dr_len) { if (dir->id_len == 12) { if (!strcmp((char*)&dir->id, "SYSTEM.CNF;1")) { break; } } // printf("dir=\'%s\'\n", &dir->id); uint8_t* ptr = (uint8_t*)dir; dir = (struct iso9660_dirent*)(ptr + dir->dr_len); } // Couldn't find SYSTEM.CNF file, non-playstation disc if (!dir->dr_len) { // Might be a DVD Video disc // Look for VIDEO_TS in the root dir dir = (struct iso9660_dirent*)buf; while (dir->dr_len) { // Search directories if ((dir->flags & 2) == 0) goto next; if (dir->id_len != 8) goto next; if (!strncmp((char*)&dir->id, "VIDEO_TS", dir->id_len)) { return CDVD_DISC_DVD_VIDEO; } // printf("dir=\'%s\' (%d)\n", (char*)&dir->id, dir->id_len); next:; uint8_t* ptr = (uint8_t*)dir; dir = (struct iso9660_dirent*)(ptr + dir->dr_len); } // SYSTEM.CNF not found and VIDEO_TS not found // If the PLAYSTATION string is in the PVD, then this might actually // be part of a multi-disc set, return as a PlayStation disc // Otherwise it's probably something else entirely (like an Xbox disc) // The PS2 wouldn't handle it anyways, return as invalid. // The Linux for PlayStation 2 install disc is an example of this. // Disc 2 contains a valid ISO filesystem and the PLAYSTATION string, // but no SYSTEM.CNF file. if (media == DISC_MEDIA_DVD) { return type == DISC_TYPE_GAME ? CDVD_DISC_PS2_DVD : CDVD_DISC_INVALID; } return type == DISC_TYPE_GAME ? CDVD_DISC_PS2_CD : CDVD_DISC_INVALID; } // SYSTEM.CNF points to unreadable sector, invalid if (!disc_read_sector(disc, buf, dir->lba_le, DISC_SS_DATA)) { return CDVD_DISC_INVALID; } // Parse SYSTEM.CNF char* p = buf; char key[64]; while (*p) { char* kptr = key; while (isspace(*p)) ++p; while (isalnum(*p)) *kptr++ = *p++; *kptr = '\0'; // printf("key: %s\n", key); // BOOT entry found, PlayStation CD if (!strncmp(key, "BOOT", 64)) return CDVD_DISC_PSX_CD; // BOOT2 entry found, PlayStation 2 disc if (!strncmp(key, "BOOT2", 64)) { return media == DISC_MEDIA_CD ? CDVD_DISC_PS2_CD : CDVD_DISC_PS2_DVD; } } // Couldn't find BOOT or BOOT2 entry, invalid/bootleg PlayStation disc? return DISC_TYPE_INVALID; } int disc_read_sector(struct disc_state* disc, unsigned char* buf, uint64_t lba, int size) { if (!disc) return 0; if (!disc->read_sector) return 0; return disc->read_sector(disc->udata, buf, lba, size); } uint64_t disc_get_size(struct disc_state* disc) { if (!disc) return 0; if (!disc->read_sector) return 0; return disc->get_size(disc->udata); } uint64_t disc_get_volume_lba(struct disc_state* disc, int vol) { if (!disc) return 0; if (!disc->read_sector) return 0; if (!vol) return 0; if (!disc->layer2_lba) { disc_detect_media(disc); } return disc->layer2_lba; } int disc_get_sector_size(struct disc_state* disc) { if (!disc) return 0; if (!disc->get_sector_size) return 0; return disc->get_sector_size(disc->udata); } int disc_get_track_count(struct disc_state* disc) { if (!disc) return 0; if (!disc->get_track_count) return 0; return disc->get_track_count(disc->udata); } int disc_get_track_info(struct disc_state* disc, int track, struct track_info* info) { if (!disc) return 0; if (!disc->get_track_info) return 0; return disc->get_track_info(disc->udata, track, info); } int disc_get_track_number(struct disc_state* disc, uint64_t lba) { if (!disc) return 0; if (!disc->get_track_number) return 0; return disc->get_track_number(disc->udata, lba); } void disc_close(struct disc_state* disc) { switch (disc->ext) { // Standard raw 2048-byte sector ISO 9660 image // usually used for DVDs case DISC_EXT_ISO: { iso_destroy(disc->udata); } break; case DISC_EXT_CUE: { cue_destroy(disc->udata); } break; // Raw 2352-byte sector disc image (CD) case DISC_EXT_BIN: { bin_destroy(disc->udata); } break; } free(disc); } char* disc_get_serial(struct disc_state* disc, char* serial) { if (!disc) return NULL; // No game serial if (!disc_fetch_system_cnf(disc)) return NULL; // Parse SYSTEM.CNF char* p = disc->system_cnf; char key[64]; while (*p) { char* kptr = key; while (isspace(*p)) ++p; while (isalnum(*p)) *kptr++ = *p++; *kptr = '\0'; if (!strncmp(key, "BOOT2", 64)) { while (isspace(*p)) ++p; if (*p != '=') { printf("iso: Expected =\n"); return NULL; } ++p; while (isspace(*p)) ++p; while (*p != ':') ++p; ++p; if (*p == '\\' || *p == '/') ++p; int i; for (i = 0; i < 16; i++) { if (*p == ';' || *p == '\n' || *p == '\r') break; serial[i] = *p++; } serial[i] = '\0'; return serial; } else { while ((*p != '\n') && (*p != '\0') && (*p != '\r')) ++p; while ((*p == '\n') || (*p == '\r')) ++p; } } printf("iso: Couldn't find BOOT2 entry in SYSTEM.CNF (PlayStation disc?)\n"); return NULL; } char* disc_get_boot_path(struct disc_state* disc) { if (disc->boot_path_cached) return disc->boot_path; // No boot path if (!disc_fetch_system_cnf(disc)) return NULL; // Parse SYSTEM.CNF char* p = disc->system_cnf; char key[64]; while (*p) { char* kptr = key; while (isspace(*p)) ++p; while (isalnum(*p)) *kptr++ = *p++; *kptr = '\0'; // printf("key: %s\n", key); if (!strncmp(key, "BOOT2", 64)) { while (isspace(*p)) ++p; if (*p != '=') { printf("iso: Expected =\n"); return NULL; } ++p; while (isspace(*p)) ++p; int i; for (i = 0; i < 255; i++) { if (*p == '\n' || *p == '\r') break; disc->boot_path[i] = *p++; } disc->boot_path[i] = '\0'; return disc->boot_path; } else { while ((*p != '\n') && (*p != '\0') && (*p != '\r')) ++p; while ((*p == '\n') || (*p == '\r')) ++p; } } printf("iso: Couldn't find BOOT2 entry in SYSTEM.CNF (PlayStation disc?)\n"); return NULL; } char* disc_read_boot_elf(struct disc_state* disc, int s) { char* boot_path = disc_get_boot_path(disc); printf("iso: Reading boot ELF from disc at path \'%s\'...\n", boot_path); if (!boot_path) { printf("iso: No boot path found in SYSTEM.CNF\n"); return NULL; } if (!disc_fetch_root(disc)) { printf("iso: Couldn't fetch root directory\n"); return NULL; } char path[256]; path[0] = '\0'; char* ptr = boot_path; // Go to end of boot path while (*ptr++); // Reverse search for a path separator while (*ptr != '/' && *ptr != '\\' && *ptr != ':') { if (ptr == boot_path) break; --ptr; } // Skip the path separator ptr += 1; // Copy the path to our buffer int i; for (i = 0; *ptr; i++) { path[i] = *ptr++; } path[i] = '\0'; struct iso9660_dirent* dir = (struct iso9660_dirent*)disc->root; while (dir->dr_len) { if (!strncmp((char*)&dir->id, path, dir->id_len)) { int32_t size = dir->size_le; uint32_t lba = dir->lba_le; char* buf = malloc(((size >> 11) + 2) << 11); char* ptr = buf; printf("iso: Boot ELF found at lba=%08x size=%08x\n", lba, dir->size_le, dir->size_le); while (size > 0) { if (!disc_read_sector(disc, ptr, lba++, DISC_SS_DATA)) { printf("iso: Couldn't read boot ELF sector %d\n", lba - 1); return NULL; } ptr += 2048; size -= 2048; } if (size != 0) { // Read the last sector, which might be smaller than 2048 bytes if (!disc_read_sector(disc, ptr, lba, DISC_SS_DATA)) { printf("iso: Couldn't read boot ELF sector %d\n", lba); return NULL; } } return buf; } uint8_t* ptr = (uint8_t*)dir; dir = (struct iso9660_dirent*)(ptr + dir->dr_len); } // Couldn't find the boot ELF file in the root directory return NULL; } #undef PACKED ================================================ FILE: src/iop/disc.h ================================================ #ifndef DISC_H #define DISC_H #ifdef __cplusplus extern "C" { #endif #include #ifdef _MSC_VER #define fseek64 _fseeki64 #define ftell64 _ftelli64 #elif defined(_WIN32) #define fseek64 fseeko64 #define ftell64 ftello64 #else #define fseek64 fseek #define ftell64 ftell #endif #define DISC_ERR_CANT_OPEN 0 #define DISC_ERR_ISO_INVALID 1 #define DISC_ERR_UNSUPPORTED_SS 2 #define DISC_EXT_ISO 0 #define DISC_EXT_BIN 1 #define DISC_EXT_CUE 2 #define DISC_EXT_CHD 3 #define DISC_EXT_CSO 4 #define DISC_EXT_ZSO 5 #define DISC_EXT_NONE 6 #define DISC_EXT_UNSUPPORTED 7 #define DISC_MEDIA_CD 0 #define DISC_MEDIA_DVD 1 #define DISC_TYPE_INVALID 0 #define DISC_TYPE_CDDA 1 #define DISC_TYPE_GAME 2 #define DISC_TYPE_UNKNOWN 3 /* 00h No disc 01h Detecting 02h Detecting CD 03h Detecting DVD 04h Detecting dual-layer DVD 05h Unknown 10h PSX CD 11h PSX CDDA 12h PS2 CD 13h PS2 CDDA 14h PS2 DVD FDh CDDA (Music) FEh DVDV (Movie disc) FFh Illegal */ #define CDVD_DISC_NO_DISC 0 #define CDVD_DISC_DETECTING 1 #define CDVD_DISC_DETECTING_CD 2 #define CDVD_DISC_DETECTING_DVD 3 #define CDVD_DISC_DETECTING_DL_DVD 4 #define CDVD_DISC_PSX_CD 16 #define CDVD_DISC_PSX_CDDA 17 #define CDVD_DISC_PS2_CD 18 #define CDVD_DISC_PS2_CDDA 19 #define CDVD_DISC_PS2_DVD 20 #define CDVD_DISC_CDDA 253 #define CDVD_DISC_DVD_VIDEO 254 #define CDVD_DISC_INVALID 255 #define DISC_SS_DATA 0 #define DISC_SS_RAW 1 #define CDVD_TRACK_AUDIO 0x01 #define CDVD_TRACK_MODE1 0x41 #define CDVD_TRACK_MODE2 0x61 struct track_info { int number; int type; uint32_t lba; }; struct disc_state { int (*read_sector)(void* udata, unsigned char* buf, uint64_t lba, int size); uint64_t (*get_size)(void* udata); int (*get_sector_size)(void* udata); int (*get_track_count)(void* udata); int (*get_track_info)(void* udata, int track, struct track_info* info); int (*get_track_number)(void* udata, uint64_t lba); void* udata; uint64_t layer2_lba; int ext; int pvd_cached, system_cnf_cached, root_cached, boot_path_cached; char pvd[2048]; char root[2048]; char system_cnf[2048]; char boot_path[256]; }; struct disc_state* disc_open(const char* path); int disc_read_sector(struct disc_state* disc, unsigned char* buf, uint64_t lba, int size); int disc_get_type(struct disc_state* disc); uint64_t disc_get_size(struct disc_state* disc); uint64_t disc_get_volume_lba(struct disc_state* disc, int vol); int disc_get_sector_size(struct disc_state* disc); int disc_get_track_count(struct disc_state* disc); int disc_get_track_info(struct disc_state* disc, int track, struct track_info* info); int disc_get_track_number(struct disc_state* disc, uint64_t lba); char* disc_get_serial(struct disc_state* disc, char* buf); char* disc_get_boot_path(struct disc_state* disc); char* disc_read_boot_elf(struct disc_state* disc, int size); void disc_close(struct disc_state* disc); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/dma.c ================================================ #include #include #include #include #include #include #include #include "dma.h" static inline void iop_dma_set_dicr(struct ps2_iop_dma* dma, uint32_t v) { dma->dicr &= ~0xffffff; dma->dicr |= v & 0xffffff; dma->dicr &= ~(v & 0x7f000000); } static inline void iop_dma_set_dicr2(struct ps2_iop_dma* dma, uint32_t v) { dma->dicr2 &= ~0xffffff; dma->dicr2 |= v & 0xffffff; dma->dicr2 &= ~(v & 0x3f000000); } inline static void iop_dma_set_dicr_flag(struct ps2_iop_dma* dma, uint32_t ch) { if (ch < 7) { uint32_t m = 0x10000 << ch; if (dma->dicr & m) dma->dicr |= 0x1000000 << ch; return; } uint32_t m = 0x10000 << (ch - 7); if (dma->dicr2 & m) dma->dicr2 |= 0x1000000 << (ch - 7); } inline static void iop_dma_check_irq(struct ps2_iop_dma* dma) { int be = dma->dicr & 0x8000; int mcien = dma->dicr & 0x800000; uint32_t dicr_flags = dma->dicr & 0x7f000000; uint32_t dicr2_flags = dma->dicr2 & 0x3f000000; int cinten = !!(dma->dmacinten & 1); int minten = !(dma->dmacinten & 2); int mif = be || (mcien && (dicr_flags || dicr2_flags)); mif = mif && cinten; dma->dicr &= 0x7fffffff; dma->dicr |= mif ? 0x80000000 : 0; if (mif && minten) { ps2_iop_intc_irq(dma->intc, IOP_INTC_DMA); } } struct ps2_iop_dma* ps2_iop_dma_create(void) { return malloc(sizeof(struct ps2_iop_dma)); } void 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) { memset(dma, 0, sizeof(struct ps2_iop_dma)); dma->intc = intc; dma->bus = bus; dma->sif = sif; dma->drive = cdvd; dma->sched = sched; dma->ee_dma = ee_dma; dma->sio2 = sio2; dma->spu = spu; dma->dmacinten = 0x01; } void ps2_iop_dma_destroy(struct ps2_iop_dma* dma) { free(dma); } static inline struct iop_dma_channel* iop_dma_get_channel(struct ps2_iop_dma* dma, uint32_t addr) { switch (addr & 0xff0) { case 0x080: return &dma->mdec_in; case 0x090: return &dma->mdec_out; case 0x0a0: return &dma->sif2; case 0x0b0: return &dma->cdvd; case 0x0c0: return &dma->spu1; case 0x0d0: return &dma->pio; case 0x0e0: return &dma->otc; case 0x500: return &dma->spu2; case 0x510: return &dma->dev9; case 0x520: return &dma->sif0; case 0x530: return &dma->sif1; case 0x540: return &dma->sio2_in; case 0x550: return &dma->sio2_out; } return NULL; } static inline const char* iop_dma_get_channel_name(uint32_t addr) { switch (addr & 0xff0) { case 0x080: return "mdec_in"; case 0x090: return "mdec_out"; case 0x0a0: return "sif2"; case 0x0b0: return "cdvd"; case 0x0c0: return "spu1"; case 0x0d0: return "pio"; case 0x0e0: return "otc"; case 0x500: return "spu2"; case 0x510: return "dev9"; case 0x520: return "sif0"; case 0x530: return "sif1"; case 0x540: return "sio2_in"; case 0x550: return "sio2_out"; } return NULL; } static inline void dma_fetch_tag(struct ps2_iop_dma* dma, struct iop_dma_channel* c) { c->tag = iop_bus_read32(dma->bus, c->tadr); c->tag |= (uint64_t)iop_bus_read32(dma->bus, c->tadr + 4) << 32; c->addr = c->tag & 0x7fffff; c->size = (c->tag >> 32) & 0xffffff; c->irq = !!(c->tag & 0x40000000); c->eot = !!(c->tag & 0x80000000); c->extra = !!(c->chcr & 0x100); // Round to 8 words c->size = (c->size + 3) & ~3; } void iop_dma_handle_mdec_in_transfer(struct ps2_iop_dma* dma) { fprintf(stderr, "iop: MDEC in channel unimplemented\n"); exit(1); } void iop_dma_handle_mdec_out_transfer(struct ps2_iop_dma* dma) { fprintf(stderr, "iop: MDEC out channel unimplemented\n"); exit(1); } void iop_dma_handle_sif2_transfer(struct ps2_iop_dma* dma) { fprintf(stderr, "iop: SIF2 channel unimplemented\n"); exit(1); } void iop_dma_handle_cdvd_transfer(struct ps2_iop_dma* dma) { // No data in CDVD buffer yet if (!dma->drive->buf_size) return; // Channel not yet started if (!(dma->cdvd.chcr & 0x1000000)) { printf("iop: CDVD transfer incoming, channel not yet started (%08x)\n", dma->cdvd.chcr); // exit(1); return; } // printf("iop: Writing %d bytes of sector data to %08x (%08x)\n", dma->drive->buf_size, dma->cdvd.madr, dma->cdvd.bcr); // uint32_t addr = dma->cdvd.madr; int i = 0; while (dma->cdvd.transfer_size && dma->drive->buf_size) { iop_bus_write8(dma->bus, dma->cdvd.madr++, dma->drive->buf[i++]); dma->drive->buf_size--; dma->cdvd.transfer_size--; } // printf("dma: buf_size=%d transfer_size=%d\n", // dma->drive->buf_size, // dma->cdvd.transfer_size // ); // int size = dma->drive->buf_size; // while (size > 0) { // printf("%08x: ", addr); // for (int i = 0; i < 16; i++) { // printf("%02x ", iop_bus_read8(dma->bus, addr + i)); // } // putchar('|'); // for (int i = 0; i < 16; i++) { // uint8_t b = iop_bus_read8(dma->bus, addr + i); // printf("%c", isprint(b) ? b : '.'); // } // puts("|"); // addr += 16; // size -= 16; // } // Only end the transfer when there aren't any // blocks left to copy if (dma->cdvd.transfer_size) return; // printf("dma: Sending IRQ to IOP\n"); iop_dma_set_dicr_flag(dma, IOP_DMA_CDVD); iop_dma_check_irq(dma); // printf("cdvd: Ending transfer\n"); dma->cdvd.chcr &= ~0x1000000; dma->cdvd.bcr = 0; } void spu1_dma_irq_event_handler(void* udata, int overshoot) { struct ps2_iop_dma* dma = (struct ps2_iop_dma*)udata; iop_dma_set_dicr_flag(dma, IOP_DMA_SPU1); iop_dma_check_irq(dma); dma->spu1.chcr &= ~0x1000000; } void iop_dma_handle_spu1_transfer(struct ps2_iop_dma* dma) { // printf("spu2 core0: chcr=%08x madr=%08x bcr=%08x bytes=%d (%08x) adma=%d\n", dma->spu2.chcr, dma->spu2.madr, dma->spu2.bcr, // (dma->spu2.bcr & 0xffff) * (dma->spu2.bcr >> 16) * 4, (dma->spu2.bcr & 0xffff) * (dma->spu2.bcr >> 16) * 4, dma->spu->c[1].admas // ); // If ADMA is off, then transfer all the data at once and trigger // an IRQ event to signal the end of the transfer if (!(dma->spu->c[0].admas & 1)) { unsigned int size = (dma->spu1.bcr & 0xffff) * (dma->spu1.bcr >> 16); for (int i = 0; i < size; i++) { uint32_t d = iop_bus_read32(dma->bus, dma->spu1.madr); iop_bus_write16(dma->bus, 0x1f9001ac, d & 0xffff); iop_bus_write16(dma->bus, 0x1f9001ac, d >> 16); dma->spu1.madr += 4; } struct sched_event spu1_dma_irq_event; spu1_dma_irq_event.callback = spu1_dma_irq_event_handler; spu1_dma_irq_event.cycles = 1000000; spu1_dma_irq_event.name = "SPU1 DMA IRQ event"; spu1_dma_irq_event.udata = dma; sched_schedule(dma->sched, spu1_dma_irq_event); return; } if ((dma->spu1.chcr & 0x1000000) == 0) return; // else we need to do an ADMA transfer spu2_start_adma(dma->spu, 0); // Transfer data as long as the SPU2 isn't streaming ADMA // samples and we still have data to transfer while (dma->spu1.transfer_size && !spu2_is_adma_active(dma->spu, 0)) { uint32_t d = iop_bus_read32(dma->bus, dma->spu1.madr); iop_bus_write16(dma->bus, 0x1f9001ac, d & 0xffff); iop_bus_write16(dma->bus, 0x1f9001ac, d >> 16); dma->spu1.madr += 4; dma->spu1.transfer_size -= 4; } // if (!dma->spu1.transfer_size) { // // If we have no more data to transfer, then we can end the transfer // // and trigger an IRQ event // iop_dma_set_dicr_flag(dma, IOP_DMA_SPU1); // iop_dma_check_irq(dma); // dma->spu1.chcr &= ~0x1000000; // } } void iop_dma_handle_pio_transfer(struct ps2_iop_dma* dma) { fprintf(stderr, "iop: PIO channel unimplemented\n"); exit(1); } void iop_dma_handle_otc_transfer(struct ps2_iop_dma* dma) { fprintf(stderr, "iop: OTC channel unimplemented\n"); exit(1); } void spu2_dma_irq_event_handler(void* udata, int overshoot) { struct ps2_iop_dma* dma = (struct ps2_iop_dma*)udata; iop_dma_set_dicr_flag(dma, IOP_DMA_SPU2); iop_dma_check_irq(dma); dma->spu2.chcr &= ~0x1000000; } void iop_dma_handle_spu2_transfer(struct ps2_iop_dma* dma) { if ((dma->spu2.chcr & 0x1000000) == 0) return; // printf("spu2 core1: chcr=%08x madr=%08x bcr=%08x bytes=%d (%08x) adma=%d\n", dma->spu2.chcr, dma->spu2.madr, dma->spu2.bcr, // (dma->spu2.bcr & 0xffff) * (dma->spu2.bcr >> 16) * 4, (dma->spu2.bcr & 0xffff) * (dma->spu2.bcr >> 16) * 4, dma->spu->c[1].admas // ); if (!dma->spu2.transfer_size) return; // If ADMA is off, then transfer all the data at once and trigger // an IRQ event to signal the end of the transfer if (!(dma->spu->c[1].admas & 2)) { unsigned int size = (dma->spu2.bcr & 0xffff) * (dma->spu2.bcr >> 16); for (int i = 0; i < size; i++) { uint32_t d = iop_bus_read32(dma->bus, dma->spu2.madr); iop_bus_write16(dma->bus, 0x1f9005ac, d & 0xffff); iop_bus_write16(dma->bus, 0x1f9005ac, d >> 16); dma->spu2.madr += 4; } struct sched_event spu2_dma_irq_event; spu2_dma_irq_event.callback = spu2_dma_irq_event_handler; spu2_dma_irq_event.cycles = 1000000; spu2_dma_irq_event.name = "SPU2 DMA IRQ event"; spu2_dma_irq_event.udata = dma; sched_schedule(dma->sched, spu2_dma_irq_event); return; } if ((dma->spu2.chcr & 0x01000000) == 0) return; // printf("spu2 core1: transfer start (%d bytes pending)\n", dma->spu2.transfer_size); // else we need to do an ADMA transfer spu2_start_adma(dma->spu, 1); // Transfer data as long as the SPU2 isn't streaming ADMA // samples and we still have data to transfer while (dma->spu2.transfer_size && !spu2_is_adma_active(dma->spu, 1)) { uint32_t d = iop_bus_read32(dma->bus, dma->spu2.madr); // int transfer_size = dma->spu2.transfer_size; iop_bus_write16(dma->bus, 0x1f9005ac, d & 0xffff); iop_bus_write16(dma->bus, 0x1f9005ac, d >> 16); // if (dma->spu2.transfer_size != transfer_size) { // printf("iop: SPU2 transfer size changed from %d to %d (loop=%d)\n", transfer_size, dma->spu2.transfer_size, loop); // *(int*)0 = 0; // dma->spu2.transfer_size = transfer_size; // } dma->spu2.madr += 4; dma->spu2.transfer_size -= 4; } // if (!dma->spu2.transfer_size) { // // If we have no more data to transfer, then we can end the transfer // // and trigger an IRQ event // printf("spu2 core1: ending transfer\n"); // iop_dma_set_dicr_flag(dma, IOP_DMA_SPU2); // iop_dma_check_irq(dma); // dma->spu2.chcr &= ~0x1000000; // } } void iop_dma_handle_dev9_transfer(struct ps2_iop_dma* dma) { // Note: DEV9 DMA serves different purposes based on the system. // On retail hardware, DEV9 DMA is used to transfer data in and out // of the HDD. On the Namco Syste 147/148 arcade hardware, DEV9 DMA // is used to transfer data to and from the Samsung NAND flash // storage chip. // We'll have to account for this once we implement HDD support, for // now we're defaulting to the System 147/148 behavior, since it // won't be used by any retail games anyways unless the HDD is present. while (dma->dev9.transfer_size) { uint32_t d = iop_bus_read8(dma->bus, 0x14000008); iop_bus_write8(dma->bus, dma->dev9.madr++, d); dma->dev9.transfer_size--; } iop_dma_set_dicr_flag(dma, IOP_DMA_DEV9); iop_dma_check_irq(dma); dma->dev9.chcr &= ~0x1000000; } void iop_dma_handle_sif0_transfer(struct ps2_iop_dma* dma) { // if (!ps2_sif0_is_empty(dma->sif)) { // printf("iopdma: SIF FIFO not empty\n"); // exit(1); // } // ps2_sif0_reset(dma->sif); dma->sif0.eot = 0; do { dma_fetch_tag(dma, &dma->sif0); // printf("iop: SIF0 tag at %08x extra=%u addr=%08x size=%08x irq=%d eot=%d\n", // dma->sif0.tadr, dma->sif0.extra, dma->sif0.addr, dma->sif0.size, dma->sif0.irq, dma->sif0.eot // ); uint128_t q; if (dma->sif0.extra) { q.u32[0] = iop_bus_read32(dma->bus, dma->sif0.tadr + 8); q.u32[1] = iop_bus_read32(dma->bus, dma->sif0.tadr + 12); q.u32[2] = iop_bus_read32(dma->bus, dma->sif0.tadr + 0); q.u32[3] = iop_bus_read32(dma->bus, dma->sif0.tadr + 4); ps2_sif0_write(dma->sif, q); } while (dma->sif0.size) { q.u32[0] = iop_bus_read32(dma->bus, dma->sif0.addr); q.u32[1] = iop_bus_read32(dma->bus, dma->sif0.addr + 4); q.u32[2] = iop_bus_read32(dma->bus, dma->sif0.addr + 8); q.u32[3] = iop_bus_read32(dma->bus, dma->sif0.addr + 12); ps2_sif0_write(dma->sif, q); dma->sif0.addr += 16; dma->sif0.size -= 4; } dma->sif0.tadr += (dma->sif0.extra ? 4 : 2) * 4; } while (!dma->sif0.eot); iop_dma_set_dicr_flag(dma, IOP_DMA_SIF0); iop_dma_check_irq(dma); dmac_handle_sif0_transfer(dma->ee_dma); dma->sif0.tadr += dma->sif0.extra ? 4 : 2; dma->sif0.chcr &= ~0x1000000; } #include "rpc.h" void iop_dma_handle_sif1_transfer(struct ps2_iop_dma* dma) { int madr_increment = ((dma->sif1.chcr >> 1) & 1) ? -4 : 4; // No data in the SIF FIFO yet if (ps2_sif1_is_empty(dma->sif)) return; // Data ready but channel isn't ready yet, keep waiting if (!(dma->sif1.chcr & 0x1000000)) { // printf("iop: EE sent SIF1 but channel isn't ready\n"); return; } // Data ready and channel is started, do transfer int eot; do { uint128_t q = ps2_sif1_read(dma->sif); uint64_t tag = q.u64[0]; uint32_t addr = tag & 0x7fffff; int size = (tag >> 32) & 0xffffff; int irq = !!(tag & 0x40000000); eot = !!(tag & 0x80000000); // printf("iop: SIF1 tag read_index=%\n", dma->sif->sif1.read_index); char buf[128]; if (rpc_decode_packet(dma->intc->iop, buf, ((void*)dma->sif->sif1.data) + (dma->sif->sif1.read_index * 16))) { // printf("%s\n", buf); } while (size) { uint128_t q = ps2_sif1_read(dma->sif); for (int i = 0; i < 4; i++) { iop_bus_write32(dma->bus, addr, q.u32[i]); addr += madr_increment; --size; } } // Send interrupt on tag IRQ (regardless of channel IRQ enable) if ((dma->dicr2 & 0x400) && irq) { ps2_iop_intc_irq(dma->intc, IOP_INTC_DMA); } if (ps2_sif1_is_empty(dma->sif)) break; } while (!eot); iop_dma_set_dicr_flag(dma, IOP_DMA_SIF1); iop_dma_check_irq(dma); // ps2_sif1_reset(dma->sif); dma->sif1.chcr &= ~0x1000000; } void iop_dma_handle_sio2_in_transfer(struct ps2_iop_dma* dma) { uint32_t size = (dma->sio2_in.bcr & 0xffff) * (dma->sio2_in.bcr >> 16); // printf("dma: SIO2 in transfer size=%d\n", size); sio2_dma_reset(dma->sio2); for (int i = 0; i < size; i++) { uint32_t w = iop_bus_read32(dma->bus, dma->sio2_in.madr); iop_bus_write8(dma->bus, 0x1F808260, (w >> 0) & 0xff); iop_bus_write8(dma->bus, 0x1F808260, (w >> 8) & 0xff); iop_bus_write8(dma->bus, 0x1F808260, (w >> 16) & 0xff); iop_bus_write8(dma->bus, 0x1F808260, (w >> 24) & 0xff); // printf("%02x %02x %02x %02x\n", // (w >> 0) & 0xff, // (w >> 8) & 0xff, // (w >> 16) & 0xff, // (w >> 24) & 0xff // ); dma->sio2_in.madr += 4; } iop_dma_set_dicr_flag(dma, IOP_DMA_SIO2_IN); iop_dma_check_irq(dma); dma->sio2_in.chcr &= ~0x1000000; } void dma_handle_sio2_out_irq_event(void* udata, int overshoot) { struct ps2_iop_dma* dma = (struct ps2_iop_dma*)udata; iop_dma_set_dicr_flag(dma, IOP_DMA_SIO2_OUT); iop_dma_check_irq(dma); dma->sio2_out.chcr &= ~0x1000000; } void iop_dma_handle_sio2_out_transfer(struct ps2_iop_dma* dma) { if ((dma->sio2_out.chcr & 0x1000000) == 0) { fprintf(stderr, "dma: SIO2_out not requested\n"); exit(1); return; } if (queue_is_empty(dma->sio2->out)) { // 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); return; } 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); exit(1); for (int b = 0; b < (dma->sio2_out.bcr >> 16); b++) { for (int i = 0; i < (dma->sio2_out.bcr & 0xffff); i++) { for (int j = 0; j < 4; j++) { uint8_t b = iop_bus_read8(dma->bus, 0x1F808264); iop_bus_write8(dma->bus, dma->sio2_out.madr++, b); } // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out)); // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out)); // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out)); // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out)); } } // for (int b = 0; b < (dma->sio2_out.bcr >> 16); b++) { // for (int i = 0; i < (dma->sio2_out.bcr & 0xffff); i++) { // iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0); // iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0); // iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0); // iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0); // } // } // struct sched_event event; // event.callback = dma_handle_sio2_out_irq_event; // event.cycles = 10000; // event.name = "SIO2 out DMA IRQ"; // event.udata = dma; // sched_schedule(dma->sched, event); iop_dma_set_dicr_flag(dma, IOP_DMA_SIO2_OUT); iop_dma_check_irq(dma); dma->sio2_out.chcr &= ~0x1000000; } void iop_dma_end_sio2_out_transfer(struct ps2_iop_dma* dma) { if ((dma->sio2_out.chcr & 0x1000000) == 0) { // printf("dma: SIO2_out not requested\n"); return; } if (queue_is_empty(dma->sio2->out)) { // printf("dma: SIO2 command put nothing in the fifo, ending transfer\n"); iop_dma_set_dicr_flag(dma, IOP_DMA_SIO2_OUT); iop_dma_check_irq(dma); dma->sio2_out.chcr &= ~0x1000000; // 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); return; } // 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); for (int b = 0; b < (dma->sio2_out.bcr >> 16); b++) { for (int i = 0; i < (dma->sio2_out.bcr & 0xffff); i++) { for (int j = 0; j < 4; j++) { uint8_t b = iop_bus_read8(dma->bus, 0x1F808264); iop_bus_write8(dma->bus, dma->sio2_out.madr++, b); } // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out)); // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out)); // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out)); // iop_bus_write8(dma->bus, dma->sio2_out.madr++, queue_pop(dma->sio2->out)); } } // for (int b = 0; b < (dma->sio2_out.bcr >> 16); b++) { // for (int i = 0; i < (dma->sio2_out.bcr & 0xffff); i++) { // iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0); // iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0); // iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0); // iop_bus_write8(dma->bus, dma->sio2_out.madr++, 0); // } // } // struct sched_event event; // event.callback = dma_handle_sio2_out_irq_event; // event.cycles = 10000; // event.name = "SIO2 out DMA IRQ"; // event.udata = dma; // sched_schedule(dma->sched, event); iop_dma_set_dicr_flag(dma, IOP_DMA_SIO2_OUT); iop_dma_check_irq(dma); dma->sio2_out.chcr &= ~0x1000000; } uint64_t ps2_iop_dma_read32(struct ps2_iop_dma* dma, uint32_t addr) { struct iop_dma_channel* c = iop_dma_get_channel(dma, addr); if (c) { switch (addr & 0xf) { case 0x0: return c->madr; case 0x4: return c->bcr; case 0x8: return c->chcr; case 0xc: return c->tadr; } const char* name = iop_dma_get_channel_name(addr); printf("iop_dma: Unknown %s register read %08x\n", name, addr); return 0; } switch (addr) { case 0x1f8010f0: return dma->dpcr; case 0x1f801570: return dma->dpcr2; case 0x1f8010f4: return dma->dicr; case 0x1f801574: return dma->dicr2; case 0x1f801578: return dma->dmacen; case 0x1f80157c: return dma->dmacinten; } printf("iop_dma: Unknown DMA register read %08x\n", addr); return 0; } void ps2_iop_dma_write32(struct ps2_iop_dma* dma, uint32_t addr, uint64_t data) { struct iop_dma_channel* c = iop_dma_get_channel(dma, addr); if (c) { switch (addr & 0xf) { case 0x0: c->madr = data; return; case 0x4: c->bcr = data; return; case 0x8: { if ((c->chcr & 0x1000000) && (data & 0x1000000)) { printf("iop: SPU2 channel already started, ignoring\n"); return; } c->chcr = data; if (!(c->chcr & 0x1000000)) { return; } c->transfer_size = (c->bcr >> 16) * ((c->bcr & 0xffff) * 4); // if ((addr & 0xff0) == 0x0b0) { // FILE* file = fopen("cdvd.dump", "a"); // 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); // fclose(file); // } // if ((addr & 0xff0) == 0x0b0) // 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); // 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); // Check negative MADR increments // if ((c->chcr & 2) == 0) { // fprintf(stderr, "iop: Negative MADR increments not supported on IOP DMA channels\n"); // // exit(1); // } // // Check for burst transfers // if (((c->chcr >> 9) & 3) != 0) { // fprintf(stderr, "iop: Burst transfers not supported on IOP DMA channels\n"); // // exit(1); // } // // Check for 0-sized blocks // if ((c->bcr & 0xffff) == 0) { // fprintf(stderr, "iop: 0-sized blocks not supported on IOP DMA channels\n"); // exit(1); // } switch (addr & 0xff0) { case 0x080: iop_dma_handle_mdec_in_transfer(dma); break; case 0x090: iop_dma_handle_mdec_out_transfer(dma); break; case 0x0a0: iop_dma_handle_sif2_transfer(dma); break; case 0x0b0: iop_dma_handle_cdvd_transfer(dma); break; case 0x0c0: iop_dma_handle_spu1_transfer(dma); break; case 0x0d0: iop_dma_handle_pio_transfer(dma); break; case 0x0e0: iop_dma_handle_otc_transfer(dma); break; case 0x500: iop_dma_handle_spu2_transfer(dma); break; case 0x510: iop_dma_handle_dev9_transfer(dma); break; case 0x520: iop_dma_handle_sif0_transfer(dma); break; case 0x530: iop_dma_handle_sif1_transfer(dma); break; case 0x540: iop_dma_handle_sio2_in_transfer(dma); break; case 0x550: iop_dma_handle_sio2_out_transfer(dma); break; } return; } case 0xc: c->tadr = data; return; } const char* name = iop_dma_get_channel_name(addr); printf("iop_dma: Unknown %s register write %08x %08lx\n", name, addr, data); return; } switch (addr) { case 0x1f8010f0: dma->dpcr = data; return; case 0x1f801570: dma->dpcr2 = data; return; case 0x1f8010f4: iop_dma_set_dicr(dma, data); iop_dma_check_irq(dma); return; case 0x1f801574: iop_dma_set_dicr2(dma, data); iop_dma_check_irq(dma); return; case 0x1f801578: dma->dmacen = data; return; case 0x1f80157c: dma->dmacinten = data; iop_dma_check_irq(dma); return; } printf("iop_dma: Unknown DMA register write %08x %08lx\n", addr, data); } uint64_t ps2_iop_dma_read16(struct ps2_iop_dma* dma, uint32_t addr) { struct iop_dma_channel* c = iop_dma_get_channel(dma, addr); if (c) { switch (addr & 0xf) { case 0x0: return c->madr; case 0x4: return c->bcr; case 0x8: return c->chcr; case 0xc: return c->tadr; } const char* name = iop_dma_get_channel_name(addr); printf("iop_dma: Unknown %s register read %08x\n", name, addr); return 0; } switch (addr) { case 0x1f8010f0: return dma->dpcr; case 0x1f801570: return dma->dpcr2; case 0x1f8010f4: return dma->dicr; case 0x1f801574: return dma->dicr2; case 0x1f801578: return dma->dmacen; case 0x1f80157c: return dma->dmacinten; } printf("iop_dma: Unknown DMA register read %08x\n", addr); return 0; } void ps2_iop_dma_write16(struct ps2_iop_dma* dma, uint32_t addr, uint64_t data) { struct iop_dma_channel* c = iop_dma_get_channel(dma, addr); if (c) { switch (addr & 0xf) { case 0x4: if ((addr & 0xff0) == 0x0b0) printf("iop: bcrlo write16 %08x\n", data); c->bcr &= ~0xffff; c->bcr |= data; return; case 0x6: if ((addr & 0xff0) == 0x0b0) printf("iop: bcrhi write16 %08x\n", data); c->bcr &= 0xffff; c->bcr |= data << 16; return; } const char* name = iop_dma_get_channel_name(addr); fprintf(stderr, "iop_dma: Unknown 16-bit %s register write %08x %08lx\n", name, addr, data); exit(1); return; } fprintf(stderr, "iop_dma: Unknown DMA register write %08x %08lx\n", addr, data); exit(1); } void iop_dma_end_spu1_transfer(struct ps2_iop_dma* dma) { iop_dma_set_dicr_flag(dma, IOP_DMA_SPU1); iop_dma_check_irq(dma); dma->spu1.chcr &= ~0x1000000; } void iop_dma_end_spu2_transfer(struct ps2_iop_dma* dma) { iop_dma_set_dicr_flag(dma, IOP_DMA_SPU2); iop_dma_check_irq(dma); dma->spu2.chcr &= ~0x1000000; } void iop_dma_handle_spu1_adma(struct ps2_iop_dma* dma) { if (!dma->spu1.transfer_size) { // If we have no more data to transfer, then we can end the transfer // and trigger an IRQ event iop_dma_set_dicr_flag(dma, IOP_DMA_SPU1); iop_dma_check_irq(dma); dma->spu1.chcr &= ~0x1000000; // printf("spu2 core0: transfer done (chcr=%08x)\n", dma->spu1.chcr); return; } // printf("spu2 core0: transfer update (%d bytes pending)\n", dma->spu1.transfer_size); // Transfer data as long as the spu1 isn't streaming ADMA // samples and we still have data to transfer while (dma->spu1.transfer_size && !spu2_is_adma_active(dma->spu, 0)) { uint32_t d = iop_bus_read32(dma->bus, dma->spu1.madr); iop_bus_write16(dma->bus, 0x1f9001ac, d & 0xffff); iop_bus_write16(dma->bus, 0x1f9001ac, d >> 16); dma->spu1.madr += 4; dma->spu1.transfer_size -= 4; } } void iop_dma_handle_spu2_adma(struct ps2_iop_dma* dma) { if (!dma->spu2.transfer_size) { // If we have no more data to transfer, then we can end the transfer // and trigger an IRQ event iop_dma_set_dicr_flag(dma, IOP_DMA_SPU2); iop_dma_check_irq(dma); dma->spu2.chcr &= ~0x1000000; // printf("spu2 core1: transfer done (chcr=%08x)\n", dma->spu2.chcr); return; } // printf("spu2 core1: transfer update (%d bytes pending)\n", dma->spu2.transfer_size); // Transfer data as long as the SPU2 isn't streaming ADMA // samples and we still have data to transfer while (dma->spu2.transfer_size && !spu2_is_adma_active(dma->spu, 1)) { uint32_t d = iop_bus_read32(dma->bus, dma->spu2.madr); iop_bus_write16(dma->bus, 0x1f9005ac, d & 0xffff); iop_bus_write16(dma->bus, 0x1f9005ac, d >> 16); dma->spu2.madr += 4; dma->spu2.transfer_size -= 4; } } ================================================ FILE: src/iop/dma.h ================================================ #ifndef IOP_DMA_H #define IOP_DMA_H #ifdef __cplusplus extern "C" { #endif #include #include "u128.h" #include "shared/sif.h" #include "intc.h" #include "cdvd.h" #include "scheduler.h" #include "sio2.h" #include "spu2.h" #include "bus_decl.h" #include "ee/dmac.h" #define IOP_DMA_MDEC_IN 0 #define IOP_DMA_MDEC_OUT 1 #define IOP_DMA_SIF2 2 #define IOP_DMA_CDVD 3 #define IOP_DMA_SPU1 4 #define IOP_DMA_PIO 5 #define IOP_DMA_OTC 6 #define IOP_DMA_SPU2 7 #define IOP_DMA_DEV9 8 #define IOP_DMA_SIF0 9 #define IOP_DMA_SIF1 10 #define IOP_DMA_SIO2_IN 11 #define IOP_DMA_SIO2_OUT 12 struct iop_dma_channel { uint32_t madr; uint32_t bcr; uint32_t chcr; uint32_t tadr; int transfer_pending; // Tag uint64_t tag; uint32_t addr; uint32_t size; int irq; int eot; int extra; int32_t transfer_size; }; struct ps2_iop_dma { struct iop_bus* bus; struct iop_dma_channel mdec_in; struct iop_dma_channel mdec_out; struct iop_dma_channel sif2; struct iop_dma_channel cdvd; struct iop_dma_channel spu1; struct iop_dma_channel pio; struct iop_dma_channel otc; struct iop_dma_channel spu2; struct iop_dma_channel dev9; struct iop_dma_channel sif0; struct iop_dma_channel sif1; struct iop_dma_channel sio2_in; struct iop_dma_channel sio2_out; uint32_t dpcr; uint32_t dpcr2; uint32_t dicr; uint32_t dicr2; uint32_t dmacen; uint32_t dmacinten; struct ps2_iop_intc* intc; struct ps2_sif* sif; struct ps2_cdvd* drive; struct ps2_dmac* ee_dma; struct ps2_sio2* sio2; struct ps2_spu2* spu; struct sched_state* sched; }; struct ps2_iop_dma* ps2_iop_dma_create(void); void 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); void ps2_iop_dma_destroy(struct ps2_iop_dma* dma); uint64_t ps2_iop_dma_read16(struct ps2_iop_dma* dma, uint32_t addr); void ps2_iop_dma_write16(struct ps2_iop_dma* dma, uint32_t addr, uint64_t data); uint64_t ps2_iop_dma_read32(struct ps2_iop_dma* dma, uint32_t addr); void ps2_iop_dma_write32(struct ps2_iop_dma* dma, uint32_t addr, uint64_t data); void iop_dma_handle_mdec_in_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_mdec_out_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_sif2_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_cdvd_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_spu1_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_pio_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_otc_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_spu2_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_dev9_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_sif0_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_sif1_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_sio2_in_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_sio2_out_transfer(struct ps2_iop_dma* dma); void iop_dma_end_sio2_out_transfer(struct ps2_iop_dma* dma); void iop_dma_handle_spu1_adma(struct ps2_iop_dma* dma); void iop_dma_handle_spu2_adma(struct ps2_iop_dma* dma); void iop_dma_end_spu1_transfer(struct ps2_iop_dma* dma); void iop_dma_end_spu2_transfer(struct ps2_iop_dma* dma); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/fw.c ================================================ #include #include #include #include "fw.h" struct ps2_fw* ps2_fw_create(void) { return malloc(sizeof(struct ps2_fw)); } void ps2_fw_init(struct ps2_fw* fw, struct ps2_iop_intc* intc) { memset(fw, 0, sizeof(struct ps2_fw)); fw->intc = intc; } void ps2_fw_destroy(struct ps2_fw* fw) { free(fw); } void fw_read_phy(struct ps2_fw* fw) { uint8_t reg = (fw->phy_access >> 24) & 0xF; fw->phy_access &= ~0x80000000; //Cancel read request fw->phy_access |= fw->phy_r[reg] | ((uint16_t)reg << 8); if (fw->intr0mask & 0x40000000) { fw->intr0 |= 0x40000000; ps2_iop_intc_irq(fw->intc, IOP_INTC_FWRE); } // printf("fw: PHY read from reg %d (%08x)\n", reg, fw->phy_access & 0xff); } void fw_write_phy(struct ps2_fw* fw) { uint8_t reg = (fw->phy_access >> 8) & 0xF; uint8_t value = fw->phy_access & 0xFF; fw->phy_r[reg] = value; fw->phy_access &= ~0x4000ffff; // printf("fw: PHY write to reg %d (%08x)\n", reg, value); } uint64_t ps2_fw_read32(struct ps2_fw* fw, uint32_t addr) { uint32_t reg = addr & 0x1ff; switch (reg) { case 0x0: return 0xffc00001; case 0x8: return fw->ctrl0; case 0x10: return fw->ctrl2; case 0x14: return fw->phy_access; case 0x20: return fw->intr0; case 0x24: return fw->intr0mask; case 0x28: return fw->intr1; case 0x2C: return fw->intr1mask; case 0x30: return fw->intr2; case 0x34: return fw->intr2mask; case 0x7C: return 0x10000001; //Value related to NodeID somehow } printf("fw: Unhandled 32-bit read from %08x\n", addr); return 0; } void ps2_fw_write32(struct ps2_fw* fw, uint32_t addr, uint64_t data) { uint32_t reg = addr & 0x1ff; switch (reg) { case 0x8: { fw->ctrl0 = data; fw->ctrl0 &= ~0x3800000; } return; case 0x10: { if (data & 0x2) //Power On fw->ctrl2 |= 0x8; //SCLK OK } return; case 0x14: { fw->phy_access = data; if (fw->phy_access & 0x40000000) { fw_write_phy(fw); } else if (fw->phy_access & 0x80000000) { fw_read_phy(fw); } } return; case 0x20: { fw->intr0 &= ~data; } return; case 0x24: { fw->intr0mask = data; } return; case 0x28: { fw->intr1 &= ~data; } return; case 0x2C: { fw->intr1mask = data; } return; case 0x30: { fw->intr2 &= ~data; } return; case 0x34: { fw->intr2mask = data; } return; case 0xB8: { fw->dma_ctrl_sr0 = data; } return; case 0x138: { fw->dma_ctrl_sr1 = data; } return; } printf("fw: Unhandled 32-bit write to %08x (%08lx)\n", addr, data); } ================================================ FILE: src/iop/fw.h ================================================ #ifndef FW_H #define FW_H #ifdef __cplusplus extern "C" { #endif #include #include "intc.h" struct ps2_fw { uint32_t intr0; uint32_t intr1; uint32_t intr2; uint32_t intr0mask; uint32_t intr1mask; uint32_t intr2mask; uint32_t ctrl0; uint32_t ctrl1; uint32_t ctrl2; uint32_t dma_ctrl_sr0; uint32_t dma_ctrl_sr1; uint32_t phy_access; uint8_t phy_r[16]; struct ps2_iop_intc* intc; }; struct ps2_fw* ps2_fw_create(void); void ps2_fw_init(struct ps2_fw* fw, struct ps2_iop_intc* intc); void ps2_fw_destroy(struct ps2_fw* fw); uint64_t ps2_fw_read32(struct ps2_fw* fw, uint32_t addr); void ps2_fw_write32(struct ps2_fw* fw, uint32_t addr, uint64_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/hle/ioman.cpp ================================================ #include #include #include #include #include "ioman.h" #include "../iop.h" #include "../bus.h" #include "../iop_export.h" #define IOMAN_MAX_OPEN_FILES 128 /** Format mask */ #define FIO_SO_IFMT 0x0038 /** Symbolic link */ #define FIO_SO_IFLNK 0x0008 /** Regular file */ #define FIO_SO_IFREG 0x0010 /** Directory */ #define FIO_SO_IFDIR 0x0020 /** read */ #define FIO_SO_IROTH 0x0004 /** write */ #define FIO_SO_IWOTH 0x0002 /** execute */ #define FIO_SO_IXOTH 0x0001 #define FIO_O_RDONLY 0x0001 #define FIO_O_WRONLY 0x0002 #define FIO_O_RDWR 0x0003 #define FIO_O_DIROPEN 0x0008 // Internal use for dopen #define FIO_O_NBLOCK 0x0010 #define FIO_O_APPEND 0x0100 #define FIO_O_CREAT 0x0200 #define FIO_O_TRUNC 0x0400 #define FIO_O_EXCL 0x0800 #define FIO_O_NOWAIT 0x8000 #define FIO_SEEK_SET 0 #define FIO_SEEK_CUR 1 #define FIO_SEEK_END 2 std::string ioman_read_string(struct iop_state* iop, uint32_t addr) { std::string str; for (int i = 0; i < 256; i++) { uint8_t d = iop_read8(iop, addr + i); if (!d) break; str += d; } return str; } void ioman_read_ptr(struct iop_state* iop, uint32_t addr, void* buf, int size) { unsigned char* ptr = (unsigned char*)buf; for (int i = 0; i < size; i++) { ptr[i] = iop_read8(iop, addr + i); } } struct iomanx_stat { unsigned int mode; unsigned int attr; unsigned int size; unsigned char ctime[8]; unsigned char atime[8]; unsigned char mtime[8]; unsigned int hisize; /** Number of subs (main) / subpart number (sub) */ unsigned int private_0; unsigned int private_1; unsigned int private_2; unsigned int private_3; unsigned int private_4; /** Sector start. */ unsigned int private_5; }; struct iomanx_dirent { iomanx_stat stat; char name[256]; uint32_t privdata; }; struct ioman_dirent { std::filesystem::path* path; int index; }; struct ioman_hle_state { FILE* files[IOMAN_MAX_OPEN_FILES] = { nullptr }; ioman_dirent directories[IOMAN_MAX_OPEN_FILES] = { nullptr }; } state; static inline int ioman_allocate_file(FILE* file) { for (int i = 0; i < IOMAN_MAX_OPEN_FILES; i++) { if (!state.files[i]) { state.files[i] = file; return i; } } // No free file slots return -1; } static inline int ioman_allocate_directory(std::filesystem::path path) { for (int i = 0; i < IOMAN_MAX_OPEN_FILES; i++) { if (!state.directories[i].path) { state.directories[i].path = new std::filesystem::path(path); return i; } } // No free directory slots return -1; } static inline int ioman_get_device(std::string path) { auto p = path.find_first_of(':'); if (p == std::string::npos) return 0; std::string device = path.substr(0, p); if (device == "rom0") { return IOMAN_DEV_ROM0; } else if (device == "rom1") { return IOMAN_DEV_ROM1; } else if (device == "cdrom0") { return IOMAN_DEV_CDROM0; } else if (device == "host") { return IOMAN_DEV_HOST; } else if (device == "host0") { return IOMAN_DEV_HOST; } else if (device == "mc0") { return IOMAN_DEV_MC0; } else if (device == "mc1") { return IOMAN_DEV_MC1; } else if (device == "mass") { return IOMAN_DEV_MASS; } // To-do: There's probably some ATA/DEV9/HDD device // but I have no idea how it's used return IOMAN_DEV_UNKNOWN; } const char* ioman_get_mode_string(int mode) { if ((mode & FIO_O_RDWR) == FIO_O_RDWR) { return "r+b"; } else if ((mode & FIO_O_RDWR) == FIO_O_WRONLY) { return "wb"; } else { return "rb"; } } std::string ioman_get_host_path(std::string path) { auto p = path.find_first_of(':'); if (p == std::string::npos) return ""; std::string str = path.substr(p + 1); if (!str.size()) return ""; if (str[0] == '/' || str[0] == '\\') str = str.substr(1); p = str.find_first_not_of(' '); if (p != std::string::npos) str = str.substr(p); return str; } extern "C" int ioman_open(struct iop_state* iop, int iomanx) { std::string path = ioman_read_string(iop, iop->r[4]); int mode = iop->r[5]; int device = ioman_get_device(path); // printf("%s: path=%s mode=%d\n", iomanx ? "iomanx" : "ioman", path.c_str(), mode); // Only hook host files if (device != IOMAN_DEV_HOST && device != IOMAN_DEV_MASS) return 0; // Get access path std::string str = ioman_get_host_path(path); if (!str.size()) return 0; std::filesystem::path absolute = std::filesystem::absolute(str); FILE* file = NULL; if (mode & FIO_O_TRUNC && mode & FIO_O_CREAT) { // Truncate file if it exists, otherwise create new file if (!(file = fopen(absolute.string().c_str(), "w+b"))) { return 0; } } else if (mode & FIO_O_CREAT) { // Only create file if (!(file = fopen(absolute.string().c_str(), "a+b"))) { return 0; } } else if (mode & FIO_O_TRUNC) { // Check if file exists before truncating if (!(file = fopen(absolute.string().c_str(), "r+b"))) { return 0; } if (!(file = fopen(absolute.string().c_str(), "w+b"))) { return 0; } } fclose(file); file = fopen(absolute.string().c_str(), ioman_get_mode_string(mode)); if (!file) return 0; printf("%s: Opened \'%s\'\n", iomanx ? "iomanx" : "ioman", absolute.string().c_str()); int slot = ioman_allocate_file(file); // Return file handle iop_return(iop, 0x100 + slot); return 1; } extern "C" int ioman_close(struct iop_state* iop, int iomanx) { uint32_t fd = iop->r[4]; if (!(fd >= 0x100 && fd < 0x140)) return 0; fd -= 0x100; if (state.files[fd]) fclose(state.files[fd]); state.files[fd] = nullptr; iop_return(iop, 0); return 1; } extern "C" int ioman_read(struct iop_state* iop, int iomanx) { uint32_t fd = iop->r[4]; if (!(fd >= 0x100 && fd < 0x140)) return 0; fd -= 0x100; if (!state.files[fd]) return 0; uint32_t ptr = iop->r[5]; uint32_t size = iop->r[6]; uint8_t* buf = (uint8_t*)malloc(size); int ret = fread(buf, 1, size, state.files[fd]); for (int i = 0; i < size; i++) { iop_write8(iop, ptr + i, buf[i]); } free(buf); iop_return(iop, ret); return 1; } extern "C" int ioman_write(struct iop_state* iop, int iomanx) { uint32_t fd = iop->r[4]; // We only use this to HLE IOMAN stdout writes // if (fd != 1) // return 0; // printf("%s: write fd=%d\n", iomanx ? "iomanx" : "ioman", fd); if (fd >= 0x100 && fd < 0x140) { fd -= 0x100; if (!state.files[fd]) return 0; uint32_t ptr = iop->r[5]; uint32_t size = iop->r[6]; uint8_t* buf = (uint8_t*)malloc(size); for (int i = 0; i < size; i++) { buf[i] = iop_read8(iop, ptr + i); } int ret = fwrite(buf, 1, size, state.files[fd]); free(buf); iop_return(iop, ret); return 1; } else if (fd == 1) { // HLE IOMAN stdout writes uint32_t ptr = iop->r[5]; uint32_t size = iop->r[6] & 0xfff; char c = iop_read8(iop, ptr++); int cnt = 0; while (c && ((cnt++) != size)) { iop->kputchar(iop->kputchar_udata, c); c = iop_read8(iop, ptr++); } fflush(stdout); iop_return(iop, size); return 1; } return 0; } extern "C" int ioman_lseek(struct iop_state* iop, int iomanx) { uint32_t fd = iop->r[4]; if (!(fd >= 0x100 && fd < 0x140)) return 0; fd -= 0x100; if (!state.files[fd]) return 0; int32_t off = iop->r[5]; uint32_t whence = iop->r[6]; switch (whence) { case 0: fseek(state.files[fd], off, SEEK_SET); break; case 1: fseek(state.files[fd], off, SEEK_CUR); break; case 2: fseek(state.files[fd], off, SEEK_END); break; } int ret = ftell(state.files[fd]); iop_return(iop, ret); return 1; } extern "C" int ioman_ioctl(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_remove(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_mkdir(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_rmdir(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_dopen(struct iop_state* iop, int iomanx) { std::string path = ioman_read_string(iop, iop->r[4]); int mode = iop->r[5]; int device = ioman_get_device(path); if (device != IOMAN_DEV_HOST && device != IOMAN_DEV_MASS) return 0; // printf("%s: dopen path=%s mode=%d\n", iomanx ? "iomanx" : "ioman", path.c_str(), mode); // Get access path std::string str = ioman_get_host_path(path); if (!str.size()) return 0; std::filesystem::path absolute = std::filesystem::absolute(str); if (!std::filesystem::exists(absolute) || !std::filesystem::is_directory(absolute)) { fprintf(stderr, "ioman: Directory \'%s\' does not exist!\n", absolute.string().c_str()); return 0; } int slot = ioman_allocate_directory(absolute); if (slot == -1) return 0; // printf("%s: Opened directory \'%s\' (fd=%x)\n", iomanx ? "iomanx" : "ioman", absolute.string().c_str(), 0x140 + slot); iop_return(iop, 0x140 + slot); return 1; } extern "C" int ioman_dclose(struct iop_state* iop, int iomanx) { uint32_t fd = iop->r[4]; if (!(fd >= 0x140 && fd < 0x180)) return 0; fd -= 0x140; if (state.directories[fd].path) delete state.directories[fd].path; state.directories[fd].path = nullptr; state.directories[fd].index = 0; iop_return(iop, 0); return 1; } extern "C" int ioman_dread(struct iop_state* iop, int iomanx) { uint32_t fd = iop->r[4]; uint32_t ptr = iop->r[5]; if (!(fd >= 0x140 && fd < 0x180)) return 0; fd -= 0x140; if (!state.directories[fd].path) return 0; ioman_dirent* dir = &state.directories[fd]; std::filesystem::directory_entry entry; bool found = false; int i = 0; for (const auto& e : std::filesystem::directory_iterator(*dir->path)) { if (i == dir->index) { entry = e; found = true; break; } i++; } if (!found) { iop_return(iop, 0); return 1; } iomanx_dirent dirent; dirent.stat.mode = entry.is_directory() ? FIO_SO_IFDIR : FIO_SO_IFREG; strncpy(dirent.name, entry.path().filename().string().c_str(), 256); dirent.name[255] = '\0'; for (int i = 0; i < sizeof(dirent); i++) { iop_write8(iop, ptr + i, ((uint8_t*)&dirent)[i]); } printf("%s: dread index=%d name=%s\n", iomanx ? "iomanx" : "ioman", dir->index, dirent.name); dir->index++; iop_return(iop, fd + 0x140); return 1; } extern "C" int ioman_getstat(struct iop_state* iop, int iomanx) { char buf[256]; for (int i = 0; i < 256; i++) { uint8_t d = iop_read8(iop, iop->r[4] + i); buf[i] = d; if (!d) break; } // fprintf(stderr, "%s: getstat(%s)\n", iomanx ? "iomanx" : "ioman", buf); iop_return(iop, 0); return 1; } extern "C" int ioman_chstat(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_format(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_adddrv(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_deldrv(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_stdioinit(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_rename(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_chdir(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_sync(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_mount(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_umount(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_lseek64(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_devctl(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_symlink(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_readlink(struct iop_state* iop, int iomanx) { return 0; } extern "C" int ioman_ioctl2(struct iop_state* iop, int iomanx) { return 0; } ================================================ FILE: src/iop/hle/ioman.h ================================================ #ifndef IOMAN_H #define IOMAN_H #include "../iop.h" #include "../iop_export.h" #define IOMAN_DEV_UNKNOWN 0 // BIOS ROM // DVD ROM // CDVD drive // Host machine // Memory card slot 1 // Memory card slot 2 // USB drive #define IOMAN_DEV_ROM0 1 #define IOMAN_DEV_ROM1 2 #define IOMAN_DEV_CDROM0 3 #define IOMAN_DEV_HOST 4 #define IOMAN_DEV_MC0 5 #define IOMAN_DEV_MC1 6 #define IOMAN_DEV_MASS 7 #ifdef __cplusplus extern "C" { #endif int ioman_open(struct iop_state* iop, int iomanx); int ioman_close(struct iop_state* iop, int iomanx); int ioman_read(struct iop_state* iop, int iomanx); int ioman_write(struct iop_state* iop, int iomanx); int ioman_lseek(struct iop_state* iop, int iomanx); int ioman_ioctl(struct iop_state* iop, int iomanx); int ioman_remove(struct iop_state* iop, int iomanx); int ioman_mkdir(struct iop_state* iop, int iomanx); int ioman_rmdir(struct iop_state* iop, int iomanx); int ioman_dopen(struct iop_state* iop, int iomanx); int ioman_dclose(struct iop_state* iop, int iomanx); int ioman_dread(struct iop_state* iop, int iomanx); int ioman_getstat(struct iop_state* iop, int iomanx); int ioman_chstat(struct iop_state* iop, int iomanx); int ioman_format(struct iop_state* iop, int iomanx); int ioman_adddrv(struct iop_state* iop, int iomanx); int ioman_deldrv(struct iop_state* iop, int iomanx); int ioman_stdioinit(struct iop_state* iop, int iomanx); int ioman_rename(struct iop_state* iop, int iomanx); int ioman_chdir(struct iop_state* iop, int iomanx); int ioman_sync(struct iop_state* iop, int iomanx); int ioman_mount(struct iop_state* iop, int iomanx); int ioman_umount(struct iop_state* iop, int iomanx); int ioman_lseek64(struct iop_state* iop, int iomanx); int ioman_devctl(struct iop_state* iop, int iomanx); int ioman_symlink(struct iop_state* iop, int iomanx); int ioman_readlink(struct iop_state* iop, int iomanx); int ioman_ioctl2(struct iop_state* iop, int iomanx); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/hle/loadcore.c ================================================ #include "loadcore.h" #include "../iop.h" #include #include static unsigned get_module_list(struct iop_state* iop) { /* Loadcore puts a pointer at 0x3f0 to an array in its data section */ unsigned bootmodes_ptr = iop_read32(iop, 0x3f0); unsigned p = bootmodes_ptr - 0x60; unsigned found = 0; /* see if the string starting with PsIIload is there*/ while (p < bootmodes_ptr) { if (iop_read32(iop, p) == 0x49497350 && iop_read32(iop, p + 4) == 0x64616F6C) { found = p; break; } p += 4; } /* This seems to have held true for all the versions i've seen */ unsigned lc_struct; if (!found) { lc_struct = bootmodes_ptr - 0x20; } else { lc_struct = p + 0x18; } return lc_struct + 0x10; } static void iop_strncpy(struct iop_state* iop, char* dest, unsigned src, int n) { char c; while ((c = iop_read8(iop, src)) && n) { *dest = c; dest++; src++; n--; } } static void cache_loaded_modules(struct iop_state* iop, unsigned list) { unsigned ent = iop_read32(iop, list); struct iop_module* mod; int count = 0; while (ent) { ent = iop_read32(iop, ent); count++; } mod = calloc(count, sizeof(*mod)); ent = iop_read32(iop, list); int i = 0; while (ent != 0) { if (iop_read32(iop, ent + 4)) { iop_strncpy(iop, mod[i].name, iop_read32(iop, ent + 4), sizeof(mod[i].name)); } else { strcpy(mod[i].name, "-- MISSING --"); } mod[i].version = iop_read16(iop, ent + 8); mod[i].entry = iop_read32(iop, ent + 0x10); mod[i].gp = iop_read32(iop, ent + 0x14); mod[i].text_addr = iop_read32(iop, ent + 0x18); mod[i].text_size = iop_read32(iop, ent + 0x1c); mod[i].data_size = iop_read32(iop, ent + 0x20); mod[i].bss_size = iop_read32(iop, ent + 0x24); ent = iop_read32(iop, ent); i++; } iop->module_count = count; iop->module_list = mod; } void refresh_module_list(struct iop_state* iop) { struct iop_module* mod = iop->module_list; iop->module_count = 0; iop->module_list = NULL; free(mod); cache_loaded_modules(iop, iop->module_list_addr); } int loadcore_reg_lib_ent(struct iop_state* iop) { unsigned list = get_module_list(iop); iop->module_list_addr = list; refresh_module_list(iop); return 0; } ================================================ FILE: src/iop/hle/loadcore.h ================================================ #ifndef LOADCORE_H_ #define LOADCORE_H_ #include "../iop.h" #ifdef __cplusplus extern "C" { #endif struct iop_module { char name[64]; uint16_t version; uint32_t text_addr; uint32_t entry; uint32_t gp; uint32_t text_size; uint32_t data_size; uint32_t bss_size; }; int loadcore_reg_lib_ent(struct iop_state* iop); void refresh_module_list(struct iop_state* iop); #ifdef __cplusplus } #endif #endif // LOADCORE_H_ ================================================ FILE: src/iop/hle/sysmem.c ================================================ #include "sysmem.h" #define SM_PUTCHAR(c) \ iop->sm_putchar(iop->sm_putchar_udata, c); int reg_index = 0; uint32_t fetch_next_param(struct iop_state* iop) { return iop->r[5 + reg_index++]; } int sysmem_kprintf(struct iop_state* iop) { if (!iop->sm_putchar) return 0; int ptr = iop->r[4]; reg_index = 5; char c = iop_read8(iop, ptr++); while (c != 0) { switch (c) { case '%': { int zero_pad = 0; int digits = 0; parse: c = iop_read8(iop, ptr++); switch (c) { case 'c': { char ch = fetch_next_param(iop) & 0xff; SM_PUTCHAR(ch); } break; case 's': { uint32_t str_addr = fetch_next_param(iop); char ch = iop_read8(iop, str_addr++); while (ch != 0) { SM_PUTCHAR(ch); ch = iop_read8(iop, str_addr++); } } break; case '0': { zero_pad = 1; goto parse; } break; case '1': case '2': case '3': case '4': case '5': case '6': case '7': case '8': case '9': { digits = (digits * 10) + (c - '0'); goto parse; } break; case 'd': case 'u': case 'i': case 'x': case 'X':{ uint32_t val = fetch_next_param(iop); char fmt_buf[8]; char* fmt = fmt_buf; *fmt++ = '%'; if (zero_pad) { *fmt++ = '0'; } if (digits) { fmt += sprintf(fmt, "%d", digits); } *fmt++ = c; *fmt = '\0'; char buf[16]; sprintf(buf, fmt_buf, val); for (char* p = buf; *p != 0; p++) { SM_PUTCHAR(*p); } } break; case '%': { SM_PUTCHAR('%'); } break; default: { printf("sysmem_kprintf: Unknown format specifier %c\n", c); // Unknown format specifier, just print it as is SM_PUTCHAR('%'); SM_PUTCHAR(c); } break; } } break; default: SM_PUTCHAR(c); break; } c = iop_read8(iop, ptr++); } return 0; } ================================================ FILE: src/iop/hle/sysmem.h ================================================ #ifndef SYSMEM_H #define SYSMEM_H #ifdef __cplusplus extern "C" { #endif #include "../iop.h" #include "../iop_export.h" int sysmem_kprintf(struct iop_state* iop); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/intc.c ================================================ #include #include #include #include #include "intc.h" struct ps2_iop_intc* ps2_iop_intc_create(void) { return malloc(sizeof(struct ps2_iop_intc)); } void ps2_iop_intc_init(struct ps2_iop_intc* intc, struct iop_state* iop) { memset(intc, 0, sizeof(struct ps2_iop_intc)); intc->iop = iop; intc->ctrl = 1; } void ps2_iop_intc_irq(struct ps2_iop_intc* intc, int dev) { intc->stat |= dev; if (intc->ctrl && (intc->stat & intc->mask)) { iop_set_irq_pending(intc->iop); } else { intc->iop->cop0_r[COP0_CAUSE] &= ~SR_IM2; } } void ps2_iop_intc_destroy(struct ps2_iop_intc* intc) { free(intc); } uint64_t ps2_iop_intc_read8(struct ps2_iop_intc* intc, uint32_t addr) { printf("intc: IOP intc 8-bit read from address %08x\n", addr); exit(1); } uint64_t ps2_iop_intc_read16(struct ps2_iop_intc* intc, uint32_t addr) { printf("intc: IOP intc 16-bit read from address %08x\n", addr); exit(1); } uint64_t ps2_iop_intc_read32(struct ps2_iop_intc* intc, uint32_t addr) { uint32_t ctrl = intc->ctrl; switch (addr) { case 0x1f801070: return intc->stat; case 0x1f801074: return intc->mask; case 0x1f801078: intc->ctrl = 0; break; } int n = intc->ctrl && (intc->stat & intc->mask); if (!n) { intc->iop->cop0_r[COP0_CAUSE] &= ~SR_IM2; } else { iop_set_irq_pending(intc->iop); } return ctrl; } void ps2_iop_intc_write8(struct ps2_iop_intc* intc, uint32_t addr, uint64_t data) { printf("iop: IOP INTC 8-bit write to address %08x (%02lx)\n", addr, data); exit(1); } void ps2_iop_intc_write16(struct ps2_iop_intc* intc, uint32_t addr, uint64_t data) { printf("iop: IOP INTC 8-bit write to address %08x (%04lx)\n", addr, data); exit(1); } void ps2_iop_intc_write32(struct ps2_iop_intc* intc, uint32_t addr, uint64_t data) { switch (addr) { case 0x1f801070: intc->stat &= data; break; case 0x1f801074: intc->mask = data; break; case 0x1f801078: intc->ctrl = data; break; } int n = intc->ctrl && (intc->stat & intc->mask); if (!n) { intc->iop->cop0_r[COP0_CAUSE] &= ~SR_IM2; } else { iop_set_irq_pending(intc->iop); } } ================================================ FILE: src/iop/intc.h ================================================ #ifndef IOP_INTC_H #define IOP_INTC_H #ifdef __cplusplus extern "C" { #endif #include #include "iop.h" /* 0 IRQ0 VBLANK start 1 IRQ1 GPU (used in PSX mode) 2 IRQ2 CDVD Drive 3 IRQ3 DMA 4 IRQ4 Timer 0 5 IRQ5 Timer 1 6 IRQ6 Timer 2 7 IRQ7 SIO0 8 IRQ8 SIO1 9 IRQ9 SPU2 10 IRQ10 PIO 11 IRQ11 VBLANK end 12 IRQ12 DVD? (unknown purpose) 13 IRQ13 DEV9 14 IRQ14 Timer 3 15 IRQ15 Timer 4 16 IRQ16 Timer 5 17 IRQ17 SIO2 18 IRQ18 HTR0? (unknown purpose) 19 IRQ19 HTR1? 20 IRQ20 HTR2? 21 IRQ21 HTR3? 22 IRQ22 USB 23 IRQ23 EXTR? (unknown purpose) 24 IRQ24 FWRE (related to FireWire) 25 IRQ25 FDMA? (FireWire DMA?) */ #define IOP_INTC_VBLANK_IN 0x00000001 // Bit 1 in PS2 mode is mapped to the SBUS IRQ, in PS1 mode // it's mapped to the PS1 GPU #define IOP_INTC_SBUS 0x00000002 #define IOP_INTC_GPU 0x00000002 #define IOP_INTC_CDVD 0x00000004 #define IOP_INTC_DMA 0x00000008 #define IOP_INTC_TIMER0 0x00000010 #define IOP_INTC_TIMER1 0x00000020 #define IOP_INTC_TIMER2 0x00000040 #define IOP_INTC_SIO0 0x00000080 #define IOP_INTC_SIO1 0x00000100 #define IOP_INTC_SPU2 0x00000200 #define IOP_INTC_PIO 0x00000400 #define IOP_INTC_VBLANK_OUT 0x00000800 #define IOP_INTC_DVD 0x00001000 #define IOP_INTC_DEV9 0x00002000 #define IOP_INTC_TIMER3 0x00004000 #define IOP_INTC_TIMER4 0x00008000 #define IOP_INTC_TIMER5 0x00010000 #define IOP_INTC_SIO2 0x00020000 #define IOP_INTC_HTR0 0x00040000 #define IOP_INTC_HTR1 0x00080000 #define IOP_INTC_HTR2 0x00100000 #define IOP_INTC_HTR3 0x00200000 #define IOP_INTC_USB 0x00400000 #define IOP_INTC_EXTR 0x00800000 #define IOP_INTC_FWRE 0x01000000 #define IOP_INTC_FDMA 0x02000000 struct ps2_iop_intc { uint32_t stat; uint32_t mask; uint32_t ctrl; struct iop_state* iop; }; struct ps2_iop_intc* ps2_iop_intc_create(void); void ps2_iop_intc_init(struct ps2_iop_intc* intc, struct iop_state* iop); void ps2_iop_intc_irq(struct ps2_iop_intc* intc, int dev); void ps2_iop_intc_destroy(struct ps2_iop_intc* intc); uint64_t ps2_iop_intc_read8(struct ps2_iop_intc* intc, uint32_t addr); uint64_t ps2_iop_intc_read16(struct ps2_iop_intc* intc, uint32_t addr); uint64_t ps2_iop_intc_read32(struct ps2_iop_intc* intc, uint32_t addr); void ps2_iop_intc_write8(struct ps2_iop_intc* intc, uint32_t addr, uint64_t data); void ps2_iop_intc_write16(struct ps2_iop_intc* intc, uint32_t addr, uint64_t data); void ps2_iop_intc_write32(struct ps2_iop_intc* intc, uint32_t addr, uint64_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/iop.c ================================================ #include #include #include "iop.h" #include "iop_dis.h" #include "iop_export.h" // static int p = 0; const uint32_t iop_bus_region_mask_table[] = { 0xffffffff, 0xffffffff, 0xffffffff, 0xffffffff, 0x7fffffff, 0x1fffffff, 0xffffffff, 0xffffffff }; static inline uint32_t iop_translate_addr(uint32_t addr) { //KSEG0 if (addr >= 0x80000000 && addr < 0xA0000000) return addr - 0x80000000; //KSEG1 if (addr >= 0xA0000000 && addr < 0xC0000000) return addr - 0xA0000000; //KUSEG, KSEG2 return addr; } static inline uint32_t iop_bus_read8(struct iop_state* iop, uint32_t addr) { return iop->bus.read8(iop->bus.udata, iop_translate_addr(addr)); } static inline uint32_t iop_bus_read16(struct iop_state* iop, uint32_t addr) { return iop->bus.read16(iop->bus.udata, iop_translate_addr(addr)); } static inline uint32_t iop_bus_read32(struct iop_state* iop, uint32_t addr) { return iop->bus.read32(iop->bus.udata, iop_translate_addr(addr)); } static inline void iop_bus_write8(struct iop_state* iop, uint32_t addr, uint32_t data) { iop->bus.write8(iop->bus.udata, iop_translate_addr(addr), data); } static inline void iop_bus_write16(struct iop_state* iop, uint32_t addr, uint32_t data) { iop->bus.write16(iop->bus.udata, iop_translate_addr(addr), data); } static inline void iop_bus_write32(struct iop_state* iop, uint32_t addr, uint32_t data) { iop->bus.write32(iop->bus.udata, iop_translate_addr(addr), data); } // External functions uint32_t iop_read8(struct iop_state* iop, uint32_t addr) { return iop->bus.read8(iop->bus.udata, iop_translate_addr(addr)); } uint32_t iop_read16(struct iop_state* iop, uint32_t addr) { return iop->bus.read16(iop->bus.udata, iop_translate_addr(addr)); } uint32_t iop_read32(struct iop_state* iop, uint32_t addr) { return iop->bus.read32(iop->bus.udata, iop_translate_addr(addr)); } void iop_write8(struct iop_state* iop, uint32_t addr, uint32_t data) { iop->bus.write8(iop->bus.udata, iop_translate_addr(addr), data); } void iop_write16(struct iop_state* iop, uint32_t addr, uint32_t data) { iop->bus.write16(iop->bus.udata, iop_translate_addr(addr), data); } void iop_write32(struct iop_state* iop, uint32_t addr, uint32_t data) { iop->bus.write32(iop->bus.udata, iop_translate_addr(addr), data); } static const uint32_t g_iop_cop0_write_mask_table[] = { 0x00000000, // cop0r0 - N/A 0x00000000, // cop0r1 - N/A 0x00000000, // cop0r2 - N/A 0xffffffff, // BPC - Breakpoint on execute (R/W) 0x00000000, // cop0r4 - N/A 0xffffffff, // BDA - Breakpoint on data access (R/W) 0x00000000, // JUMPDEST - Randomly memorized jump address (R) 0xffc0f03f, // DCIC - Breakpoint control (R/W) 0x00000000, // BadVaddr - Bad Virtual Address (R) 0xffffffff, // BDAM - Data Access breakpoint mask (R/W) 0x00000000, // cop0r10 - N/A 0xffffffff, // BPCM - Execute breakpoint mask (R/W) 0xffffffff, // SR - System status register (R/W) 0x00000300, // CAUSE - Describes the most recently recognised exception (R) 0x00000000, // EPC - Return Address from Trap (R) 0x00000000 // PRID - Processor ID (R) }; #define OP ((iop->opcode >> 26) & 0x3f) #define S ((iop->opcode >> 21) & 0x1f) #define T ((iop->opcode >> 16) & 0x1f) #define D ((iop->opcode >> 11) & 0x1f) #define IMM5 ((iop->opcode >> 6) & 0x1f) #define CMT ((iop->opcode >> 6) & 0xfffff) #define SOP (iop->opcode & 0x3f) #define IMM26 (iop->opcode & 0x3ffffff) #define IMM16 (iop->opcode & 0xffff) #define IMM16S ((int32_t)((int16_t)IMM16)) #define R_R0 (iop->r[0]) #define R_A0 (iop->r[4]) #define R_RA (iop->r[31]) #define DO_PENDING_LOAD { \ iop->r[iop->load_d] = iop->load_v; \ R_R0 = 0; \ iop->load_v = 0xffffffff; \ iop->load_d = 0; } #define SE8(v) ((int32_t)((int8_t)v)) #define SE16(v) ((int32_t)((int16_t)v)) #define BRANCH(offset) { \ iop->next_pc = iop->next_pc + (offset); \ iop->next_pc = iop->next_pc - 4; \ iop->branch = 1; \ iop->branch_taken = 1; } struct iop_state* iop_create(void) { return (struct iop_state*)malloc(sizeof(struct iop_state)); } void iop_destroy(struct iop_state* iop) { free(iop); } void iop_init(struct iop_state* iop, struct iop_bus_s bus) { memset(iop, 0, sizeof(struct iop_state)); iop->bus = bus; iop->pc = 0xbfc00000; iop->next_pc = iop->pc + 4; iop->cop0_r[COP0_SR] = 0x10900000; iop->cop0_r[COP0_PRID] = 0x0000001f; } void iop_init_kputchar(struct iop_state* iop, void (*kputchar)(void*, char), void* udata) { iop->kputchar = kputchar; iop->kputchar_udata = udata; } void iop_init_sm_putchar(struct iop_state* iop, void (*sm_putchar)(void*, char), void* udata) { iop->sm_putchar = sm_putchar; iop->sm_putchar_udata = udata; } static inline int iop_check_irq(struct iop_state* iop) { return (iop->cop0_r[COP0_SR] & SR_IEC) && (iop->cop0_r[COP0_SR] & iop->cop0_r[COP0_CAUSE] & 0x00000400); } static inline void iop_print_disassembly(struct iop_state* iop) { char buf[128]; struct iop_dis_state state; state.print_address = 1; state.print_opcode = 1; state.addr = iop->pc; puts(iop_disassemble(buf, iop->opcode, &state)); } static inline void iop_exception(struct iop_state* iop, uint32_t cause) { if ((cause != CAUSE_SYSCALL) && (cause != CAUSE_INT)) printf("iop: Crashed with cause %02x at pc=%08x next=%08x saved=%08x\n", cause >> 2, iop->pc, iop->saved_pc, iop->saved_pc); // Set excode and clear 3 LSBs iop->cop0_r[COP0_CAUSE] &= 0xffffff80; iop->cop0_r[COP0_CAUSE] |= cause; iop->cop0_r[COP0_EPC] = iop->saved_pc; if (iop->delay_slot) { iop->cop0_r[COP0_EPC] -= 4; iop->cop0_r[COP0_CAUSE] |= 0x80000000; } // Do exception stack push uint32_t mode = iop->cop0_r[COP0_SR] & 0x3f; iop->cop0_r[COP0_SR] &= 0xffffffc0; iop->cop0_r[COP0_SR] |= (mode << 2) & 0x3f; // Set PC to the vector selected on BEV iop->pc = (iop->cop0_r[COP0_SR] & SR_BEV) ? 0xbfc00180 : 0x80000080; iop->next_pc = iop->pc + 4; } void iop_cycle(struct iop_state* iop) { iop->last_cycles = 0; iop->saved_pc = iop->pc; iop->delay_slot = iop->branch; iop->branch = 0; iop->branch_taken = 0; if (iop->saved_pc & 3) iop_exception(iop, CAUSE_ADEL); iop->opcode = iop_bus_read32(iop, iop->pc); iop->last_cycles = 0; if (iop->p) { iop_print_disassembly(iop); --iop->p; } iop->pc = iop->next_pc; iop->next_pc += 4; if (iop_check_irq(iop)) { iop->r[0] = 0; // printf("iop: irq pc=%08x next_pc=%08x saved_pc=%08x\n", iop->pc, iop->next_pc, iop->saved_pc); iop_exception(iop, CAUSE_INT); return; } int cyc = iop_execute(iop); if (!cyc) { printf("iop: Illegal instruction %08x at %08x (next=%08x, saved=%08x)\n", iop->opcode, iop->pc, iop->next_pc, iop->saved_pc); iop_exception(iop, CAUSE_RI); } iop->last_cycles += cyc; iop->total_cycles += iop->last_cycles; iop->r[0] = 0; } void iop_reset(struct iop_state* iop) { for (int i = 0; i < 32; i++) iop->r[i] = 0; for (int i = 0; i < 16; i++) iop->cop0_r[i] = 0; iop->pc = 0xbfc00000; iop->next_pc = iop->pc + 4; iop->cop0_r[COP0_SR] = 0x10900000; iop->cop0_r[COP0_PRID] = 0x0000001f; iop->opcode = 0; iop->hi = 0; iop->lo = 0; iop->load_d = 0; iop->load_v = 0; iop->last_cycles = 0; iop->total_cycles = 0; iop->biu_config = 0; iop->branch = 0; iop->delay_slot = 0; iop->branch_taken = 0; } void iop_set_irq_pending(struct iop_state* iop) { iop->cop0_r[COP0_CAUSE] |= SR_IM2; } static inline void iop_i_invalid(struct iop_state* iop) { printf("%08x: Illegal instruction %08x", iop->pc - 8, iop->opcode); iop_exception(iop, CAUSE_RI); } static inline void iop_i_bltz(struct iop_state* iop) { int32_t s = (int32_t)iop->r[S]; DO_PENDING_LOAD; if ((int32_t)s < (int32_t)0) BRANCH(IMM16S << 2); } static inline void iop_i_bgez(struct iop_state* iop) { int32_t s = (int32_t)iop->r[S]; DO_PENDING_LOAD; if ((int32_t)s >= (int32_t)0) BRANCH(IMM16S << 2); } static inline void iop_i_bltzal(struct iop_state* iop) { int32_t s = (int32_t)iop->r[S]; DO_PENDING_LOAD; R_RA = iop->next_pc; if ((int32_t)s < (int32_t)0) BRANCH(IMM16S << 2); } static inline void iop_i_bgezal(struct iop_state* iop) { int32_t s = (int32_t)iop->r[S]; DO_PENDING_LOAD; R_RA = iop->next_pc; if ((int32_t)s >= (int32_t)0) BRANCH(IMM16S << 2); } static inline void iop_i_j(struct iop_state* iop) { iop->branch = 1; DO_PENDING_LOAD; // If we get a 1 that means the call has been HLE'd if (iop_test_module_hooks(iop)) return; iop->next_pc = (iop->next_pc & 0xf0000000) | (IMM26 << 2); } static inline void iop_i_jal(struct iop_state* iop) { iop->branch = 1; DO_PENDING_LOAD; R_RA = iop->next_pc; iop->next_pc = (iop->next_pc & 0xf0000000) | (IMM26 << 2); } static inline void iop_i_beq(struct iop_state* iop) { iop->branch = 1; iop->branch_taken = 0; uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; if (s == t) BRANCH(IMM16S << 2); } static inline void iop_i_bne(struct iop_state* iop) { iop->branch = 1; iop->branch_taken = 0; uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; if (s != t) BRANCH(IMM16S << 2); } static inline void iop_i_blez(struct iop_state* iop) { iop->branch = 1; iop->branch_taken = 0; int32_t s = (int32_t)iop->r[S]; DO_PENDING_LOAD; if ((int32_t)s <= (int32_t)0) BRANCH(IMM16S << 2); } static inline void iop_i_bgtz(struct iop_state* iop) { iop->branch = 1; iop->branch_taken = 0; int32_t s = (int32_t)iop->r[S]; DO_PENDING_LOAD; if ((int32_t)s > (int32_t)0) BRANCH(IMM16S << 2); } static inline void iop_i_addi(struct iop_state* iop) { uint32_t s = iop->r[S]; DO_PENDING_LOAD; uint32_t i = IMM16S; uint32_t r = s + i; uint32_t o = (s ^ r) & (i ^ r); if (o & 0x80000000) { iop_exception(iop, CAUSE_OV); } else { iop->r[T] = r; } } static inline void iop_i_addiu(struct iop_state* iop) { uint32_t s = iop->r[S]; DO_PENDING_LOAD; iop->r[T] = s + IMM16S; } static inline void iop_i_slti(struct iop_state* iop) { int32_t s = (int32_t)iop->r[S]; DO_PENDING_LOAD; iop->r[T] = s < IMM16S; } static inline void iop_i_sltiu(struct iop_state* iop) { uint32_t s = iop->r[S]; DO_PENDING_LOAD; iop->r[T] = s < IMM16S; } static inline void iop_i_andi(struct iop_state* iop) { uint32_t s = iop->r[S]; DO_PENDING_LOAD; iop->r[T] = s & IMM16; } static inline void iop_i_ori(struct iop_state* iop) { uint32_t s = iop->r[S]; DO_PENDING_LOAD; iop->r[T] = s | IMM16; } static inline void iop_i_xori(struct iop_state* iop) { uint32_t s = iop->r[S]; DO_PENDING_LOAD; iop->r[T] = s ^ IMM16; } static inline void iop_i_lui(struct iop_state* iop) { DO_PENDING_LOAD; iop->r[T] = IMM16 << 16; } static inline void iop_i_lb(struct iop_state* iop) { uint32_t s = iop->r[S]; if (iop->load_d != T) DO_PENDING_LOAD; iop->load_d = T; iop->load_v = SE8(iop_bus_read8(iop, s + IMM16S)); } static inline void iop_i_lh(struct iop_state* iop) { uint32_t s = iop->r[S]; if (iop->load_d != T) DO_PENDING_LOAD; uint32_t addr = s + IMM16S; if (addr & 0x1) { iop_exception(iop, CAUSE_ADEL); } else { iop->load_d = T; iop->load_v = SE16(iop_bus_read16(iop, addr)); } } static inline void iop_i_lwl(struct iop_state* iop) { uint32_t rt = T; uint32_t s = iop->r[S]; uint32_t t = iop->r[rt]; uint32_t addr = s + IMM16S; uint32_t load = iop_bus_read32(iop, addr & 0xfffffffc); if (rt == iop->load_d) { t = iop->load_v; } else { DO_PENDING_LOAD; } int shift = (int)((addr & 0x3) << 3); uint32_t mask = (uint32_t)0x00FFFFFF >> shift; uint32_t value = (t & mask) | (load << (24 - shift)); iop->load_d = rt; iop->load_v = value; // printf("lwl rt=%u s=%08x t=%08x addr=%08x load=%08x (%08x) shift=%u mask=%08x value=%08x\n", // rt, s, t, addr, load, addr & 0xfffffffc, shift, mask, value // ); } static inline void iop_i_lw(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t addr = s + IMM16S; if (iop->load_d != T) DO_PENDING_LOAD; if (addr & 0x3) { iop_exception(iop, CAUSE_ADEL); } else { iop->load_d = T; iop->load_v = iop_bus_read32(iop, addr); } } static inline void iop_i_lbu(struct iop_state* iop) { uint32_t s = iop->r[S]; if (iop->load_d != T) DO_PENDING_LOAD; iop->load_d = T; iop->load_v = iop_bus_read8(iop, s + IMM16S); } static inline void iop_i_lhu(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t addr = s + IMM16S; if (iop->load_d != T) DO_PENDING_LOAD; if (addr & 0x1) { iop_exception(iop, CAUSE_ADEL); } else { iop->load_d = T; iop->load_v = iop_bus_read16(iop, addr); } } static inline void iop_i_lwr(struct iop_state* iop) { uint32_t rt = T; uint32_t s = iop->r[S]; uint32_t t = iop->r[rt]; uint32_t addr = s + IMM16S; uint32_t load = iop_bus_read32(iop, addr & 0xfffffffc); if (rt == iop->load_d) { t = iop->load_v; } else { DO_PENDING_LOAD; } int shift = (int)((addr & 0x3) << 3); uint32_t mask = 0xFFFFFF00 << (24 - shift); uint32_t value = (t & mask) | (load >> shift); iop->load_d = rt; iop->load_v = value; // printf("lwr rt=%u s=%08x t=%08x addr=%08x load=%08x (%08x) shift=%u mask=%08x value=%08x\n", // rt, s, t, addr, load, addr & 0xfffffffc, shift, mask, value // ); } static inline void iop_i_sb(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; // Cache isolated if (iop->cop0_r[COP0_SR] & SR_ISC) { return; } iop_bus_write8(iop, s + IMM16S, t); } static inline void iop_i_sh(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; uint32_t addr = s + IMM16S; DO_PENDING_LOAD; // Cache isolated if (iop->cop0_r[COP0_SR] & SR_ISC) { return; } if (addr & 0x1) { iop_exception(iop, CAUSE_ADES); } else { iop_bus_write16(iop, addr, t); } } static inline void iop_i_swl(struct iop_state* iop) { uint32_t s = iop->r[S]; DO_PENDING_LOAD; uint32_t addr = s + IMM16S; uint32_t aligned = addr & 0xfffffffc; uint32_t v = iop_bus_read32(iop, aligned); switch (addr & 0x3) { case 0: v = (v & 0xffffff00) | (iop->r[T] >> 24); break; case 1: v = (v & 0xffff0000) | (iop->r[T] >> 16); break; case 2: v = (v & 0xff000000) | (iop->r[T] >> 8 ); break; case 3: v = iop->r[T] ; break; } iop_bus_write32(iop, aligned, v); } static inline void iop_i_sw(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; uint32_t addr = s + IMM16S; DO_PENDING_LOAD; // Cache isolated if (iop->cop0_r[COP0_SR] & SR_ISC) { return; } if (addr & 0x3) { iop_exception(iop, CAUSE_ADES); } else { if (addr == 0xfffe0130) { iop->biu_config = t; return; } iop_bus_write32(iop, addr, t); } } static inline void iop_i_swr(struct iop_state* iop) { uint32_t s = iop->r[S]; DO_PENDING_LOAD; uint32_t addr = s + IMM16S; uint32_t aligned = addr & 0xfffffffc; uint32_t v = iop_bus_read32(iop, aligned); switch (addr & 0x3) { case 0: v = iop->r[T] ; break; case 1: v = (v & 0x000000ff) | (iop->r[T] << 8 ); break; case 2: v = (v & 0x0000ffff) | (iop->r[T] << 16); break; case 3: v = (v & 0x00ffffff) | (iop->r[T] << 24); break; } iop_bus_write32(iop, aligned, v); } static inline void iop_i_lwc0(struct iop_state* iop) { iop_exception(iop, CAUSE_CPU); } static inline void iop_i_lwc1(struct iop_state* iop) { iop_exception(iop, CAUSE_CPU); } static inline void iop_i_lwc2(struct iop_state* iop) { iop_exception(iop, CAUSE_CPU); } static inline void iop_i_lwc3(struct iop_state* iop) { iop_exception(iop, CAUSE_CPU); } static inline void iop_i_swc0(struct iop_state* iop) { iop_exception(iop, CAUSE_CPU); } static inline void iop_i_swc1(struct iop_state* iop) { iop_exception(iop, CAUSE_CPU); } static inline void iop_i_swc2(struct iop_state* iop) { iop_exception(iop, CAUSE_CPU); } static inline void iop_i_swc3(struct iop_state* iop) { iop_exception(iop, CAUSE_CPU); } // Secondary static inline void iop_i_sll(struct iop_state* iop) { uint32_t t = iop->r[T]; DO_PENDING_LOAD; iop->r[D] = t << IMM5; } static inline void iop_i_srl(struct iop_state* iop) { uint32_t t = iop->r[T]; DO_PENDING_LOAD; iop->r[D] = t >> IMM5; } static inline void iop_i_sra(struct iop_state* iop) { int32_t t = (int32_t)iop->r[T]; DO_PENDING_LOAD; iop->r[D] = t >> IMM5; } static inline void iop_i_sllv(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; iop->r[D] = t << (s & 0x1f); } static inline void iop_i_srlv(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; iop->r[D] = t >> (s & 0x1f); } static inline void iop_i_srav(struct iop_state* iop) { uint32_t s = iop->r[S]; int32_t t = (int32_t)iop->r[T]; DO_PENDING_LOAD; iop->r[D] = t >> (s & 0x1f); } static inline void iop_i_jr(struct iop_state* iop) { iop->branch = 1; uint32_t s = iop->r[S]; DO_PENDING_LOAD; iop->next_pc = s; } static inline void iop_i_jalr(struct iop_state* iop) { iop->branch = 1; uint32_t s = iop->r[S]; DO_PENDING_LOAD; iop->r[D] = iop->next_pc; iop->next_pc = s; } static inline void iop_i_syscall(struct iop_state* iop) { DO_PENDING_LOAD; iop_exception(iop, CAUSE_SYSCALL); } static inline void iop_i_break(struct iop_state* iop) { DO_PENDING_LOAD; // iop_exception(iop, CAUSE_BP); } static inline void iop_i_mfhi(struct iop_state* iop) { DO_PENDING_LOAD; iop->r[D] = iop->hi; } static inline void iop_i_mthi(struct iop_state* iop) { DO_PENDING_LOAD; iop->hi = iop->r[S]; } static inline void iop_i_mflo(struct iop_state* iop) { DO_PENDING_LOAD; iop->r[D] = iop->lo; } static inline void iop_i_mtlo(struct iop_state* iop) { DO_PENDING_LOAD; iop->lo = iop->r[S]; } static inline void iop_i_mult(struct iop_state* iop) { int64_t s = (int64_t)((int32_t)iop->r[S]); int64_t t = (int64_t)((int32_t)iop->r[T]); DO_PENDING_LOAD; uint64_t r = s * t; iop->hi = r >> 32; iop->lo = r & 0xffffffff; } static inline void iop_i_multu(struct iop_state* iop) { uint64_t s = (uint64_t)iop->r[S]; uint64_t t = (uint64_t)iop->r[T]; DO_PENDING_LOAD; uint64_t r = s * t; iop->hi = r >> 32; iop->lo = r & 0xffffffff; } static inline void iop_i_div(struct iop_state* iop) { int32_t s = (int32_t)iop->r[S]; int32_t t = (int32_t)iop->r[T]; DO_PENDING_LOAD; if (!t) { iop->hi = s; iop->lo = (s >= 0) ? 0xffffffff : 1; } else if ((((uint32_t)s) == 0x80000000) && (t == -1)) { iop->hi = 0; iop->lo = 0x80000000; } else { iop->hi = (uint32_t)(s % t); iop->lo = (uint32_t)(s / t); } } static inline void iop_i_divu(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; if (!t) { iop->hi = s; iop->lo = 0xffffffff; } else { iop->hi = s % t; iop->lo = s / t; } } static inline void iop_i_add(struct iop_state* iop) { int32_t s = iop->r[S]; int32_t t = iop->r[T]; DO_PENDING_LOAD; int32_t r = s + t; uint32_t o = (s ^ r) & (t ^ r); if (o & 0x80000000) { iop_exception(iop, CAUSE_OV); } else { iop->r[D] = (uint32_t)r; } } static inline void iop_i_addu(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; iop->r[D] = s + t; } static inline void iop_i_sub(struct iop_state* iop) { int32_t s = (int32_t)iop->r[S]; int32_t t = (int32_t)iop->r[T]; int32_t r; DO_PENDING_LOAD; int o = __builtin_ssub_overflow(s, t, &r); if (o) { iop_exception(iop, CAUSE_OV); } else { iop->r[D] = r; } } static inline void iop_i_subu(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; iop->r[D] = s - t; } static inline void iop_i_and(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; iop->r[D] = s & t; } static inline void iop_i_or(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; iop->r[D] = s | t; } static inline void iop_i_xor(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; iop->r[D] = (s ^ t); } static inline void iop_i_nor(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; iop->r[D] = ~(s | t); } static inline void iop_i_slt(struct iop_state* iop) { int32_t s = (int32_t)iop->r[S]; int32_t t = (int32_t)iop->r[T]; DO_PENDING_LOAD; iop->r[D] = s < t; } static inline void iop_i_sltu(struct iop_state* iop) { uint32_t s = iop->r[S]; uint32_t t = iop->r[T]; DO_PENDING_LOAD; iop->r[D] = s < t; } // COP0 static inline void iop_i_mfc0(struct iop_state* iop) { DO_PENDING_LOAD; iop->load_v = iop->cop0_r[D]; iop->load_d = T; } static inline void iop_i_mtc0(struct iop_state* iop) { uint32_t t = iop->r[T]; DO_PENDING_LOAD; iop->cop0_r[D] = t & g_iop_cop0_write_mask_table[D]; } static inline void iop_i_rfe(struct iop_state* iop) { DO_PENDING_LOAD; uint32_t mode = iop->cop0_r[COP0_SR] & 0x3f; iop->cop0_r[COP0_SR] &= 0xfffffff0; iop->cop0_r[COP0_SR] |= mode >> 2; } int iop_execute(struct iop_state* iop) { switch ((iop->opcode & 0xfc000000) >> 26) { case 0x00000000 >> 26: { switch (iop->opcode & 0x0000003f) { case 0x00000000: iop_i_sll(iop); return 2; case 0x00000002: iop_i_srl(iop); return 2; case 0x00000003: iop_i_sra(iop); return 2; case 0x00000004: iop_i_sllv(iop); return 2; case 0x00000006: iop_i_srlv(iop); return 2; case 0x00000007: iop_i_srav(iop); return 2; case 0x00000008: iop_i_jr(iop); return 2; case 0x00000009: iop_i_jalr(iop); return 2; case 0x0000000c: iop_i_syscall(iop); return 2; case 0x0000000d: iop_i_break(iop); return 2; case 0x00000010: iop_i_mfhi(iop); return 2; case 0x00000011: iop_i_mthi(iop); return 2; case 0x00000012: iop_i_mflo(iop); return 2; case 0x00000013: iop_i_mtlo(iop); return 2; case 0x00000018: iop_i_mult(iop); return 2; case 0x00000019: iop_i_multu(iop); return 2; case 0x0000001a: iop_i_div(iop); return 2; case 0x0000001b: iop_i_divu(iop); return 2; case 0x00000020: iop_i_add(iop); return 2; case 0x00000021: iop_i_addu(iop); return 2; case 0x00000022: iop_i_sub(iop); return 2; case 0x00000023: iop_i_subu(iop); return 2; case 0x00000024: iop_i_and(iop); return 2; case 0x00000025: iop_i_or(iop); return 2; case 0x00000026: iop_i_xor(iop); return 2; case 0x00000027: iop_i_nor(iop); return 2; case 0x0000002a: iop_i_slt(iop); return 2; case 0x0000002b: iop_i_sltu(iop); return 2; } break; } break; case 0x04000000 >> 26: { iop->branch = 1; iop->branch_taken = 0; switch ((iop->opcode & 0x001f0000) >> 16) { case 0x00000000 >> 16: iop_i_bltz(iop); return 2; case 0x00010000 >> 16: iop_i_bgez(iop); return 2; case 0x00100000 >> 16: iop_i_bltzal(iop); return 2; case 0x00110000 >> 16: iop_i_bgezal(iop); return 2; // bltz/bgez dupes default: { if (iop->opcode & 0x00010000) { iop_i_bgez(iop); } else { iop_i_bltz(iop); } } return 2; } break; } break; case 0x08000000 >> 26: iop_i_j(iop); return 2; case 0x0c000000 >> 26: iop_i_jal(iop); return 2; case 0x10000000 >> 26: iop_i_beq(iop); return 2; case 0x14000000 >> 26: iop_i_bne(iop); return 2; case 0x18000000 >> 26: iop_i_blez(iop); return 2; case 0x1c000000 >> 26: iop_i_bgtz(iop); return 2; case 0x20000000 >> 26: iop_i_addi(iop); return 2; case 0x24000000 >> 26: iop_i_addiu(iop); return 2; case 0x28000000 >> 26: iop_i_slti(iop); return 2; case 0x2c000000 >> 26: iop_i_sltiu(iop); return 2; case 0x30000000 >> 26: iop_i_andi(iop); return 2; case 0x34000000 >> 26: iop_i_ori(iop); return 2; case 0x38000000 >> 26: iop_i_xori(iop); return 2; case 0x3c000000 >> 26: iop_i_lui(iop); return 2; case 0x40000000 >> 26: { switch ((iop->opcode & 0x03e00000) >> 21) { case 0x00000000 >> 21: iop_i_mfc0(iop); return 2; case 0x00800000 >> 21: iop_i_mtc0(iop); return 2; case 0x02000000 >> 21: iop_i_rfe(iop); return 2; } } break; case 0x48000000 >> 26: iop_i_invalid(iop); return 2; case 0x80000000 >> 26: iop_i_lb(iop); return 2; case 0x84000000 >> 26: iop_i_lh(iop); return 2; case 0x88000000 >> 26: iop_i_lwl(iop); return 2; case 0x8c000000 >> 26: iop_i_lw(iop); return 2; case 0x90000000 >> 26: iop_i_lbu(iop); return 2; case 0x94000000 >> 26: iop_i_lhu(iop); return 2; case 0x98000000 >> 26: iop_i_lwr(iop); return 2; case 0xa0000000 >> 26: iop_i_sb(iop); return 2; case 0xa4000000 >> 26: iop_i_sh(iop); return 2; case 0xa8000000 >> 26: iop_i_swl(iop); return 2; case 0xac000000 >> 26: iop_i_sw(iop); return 2; case 0xb8000000 >> 26: iop_i_swr(iop); return 2; case 0xc0000000 >> 26: iop_i_lwc0(iop); return 2; case 0xc4000000 >> 26: iop_i_lwc1(iop); return 2; case 0xc8000000 >> 26: iop_i_lwc2(iop); return 2; case 0xcc000000 >> 26: iop_i_lwc3(iop); return 2; case 0xe0000000 >> 26: iop_i_swc0(iop); return 2; case 0xe4000000 >> 26: iop_i_swc1(iop); return 2; case 0xe8000000 >> 26: iop_i_swc2(iop); return 2; case 0xec000000 >> 26: iop_i_swc3(iop); return 2; } return 0; } #undef R_R0 #undef R_A0 #undef R_RA #undef OP #undef S #undef T #undef D #undef IMM5 #undef CMT #undef SOP #undef IMM26 #undef IMM16 #undef IMM16S #undef DO_PENDING_LOAD #undef DEBUG_ALL #undef SE8 #undef SE16 ================================================ FILE: src/iop/iop.h ================================================ #ifndef IOP_H #define IOP_H #ifdef __cplusplus extern "C" { #endif #include #include #define COP0_BPC 3 #define COP0_BDA 5 #define COP0_JUMPDEST 6 #define COP0_DCIC 7 #define COP0_BADVADDR 8 #define COP0_BDAM 9 #define COP0_BPCM 11 #define COP0_SR 12 #define COP0_CAUSE 13 #define COP0_EPC 14 #define COP0_PRID 15 /* Name Alias Common Usage R0 zero Constant (always 0) R1 at Assembler temporary (destroyed by some assembler pseudoinstructions!) R2-R3 v0-v1 Subroutine return values, may be changed by subroutines R4-R7 a0-a3 Subroutine arguments, may be changed by subroutines R8-R15 t0-t7 Temporaries, may be changed by subroutines R16-R23 s0-s7 Static variables, must be saved by subs R24-R25 t8-t9 Temporaries, may be changed by subroutines R26-R27 k0-k1 Reserved for kernel (destroyed by some IRQ handlers!) R28 gp Global pointer (rarely used) R29 sp Stack pointer R30 fp(s8) Frame Pointer, or 9th Static variable, must be saved R31 ra Return address (used so by JAL,BLTZAL,BGEZAL opcodes) - pc Program counter - hi,lo Multiply/divide results, may be changed by subroutines */ struct iop_bus_s { void* udata; uint32_t (*read8)(void* udata, uint32_t addr); uint32_t (*read16)(void* udata, uint32_t addr); uint32_t (*read32)(void* udata, uint32_t addr); void (*write8)(void* udata, uint32_t addr, uint32_t data); void (*write16)(void* udata, uint32_t addr, uint32_t data); void (*write32)(void* udata, uint32_t addr, uint32_t data); }; struct iop_state { struct iop_bus_s bus; uint32_t r[32]; uint32_t opcode; uint32_t pc, next_pc, saved_pc; uint32_t hi, lo; uint32_t load_d, load_v; uint32_t last_cycles; uint32_t total_cycles; uint32_t biu_config; int branch, delay_slot, branch_taken; void (*kputchar)(void*, char); void* kputchar_udata; void (*sm_putchar)(void*, char); void* sm_putchar_udata; uint32_t cop0_r[16]; int p; uint32_t module_list_addr; /* cache module list */ int module_count; struct iop_module *module_list; }; /* 0 IEc Current Interrupt Enable (0=Disable, 1=Enable) ;rfe pops IUp here 1 KUc Current Kernel/User Mode (0=Kernel, 1=User) ;rfe pops KUp here 2 IEp Previous Interrupt Disable ;rfe pops IUo here 3 KUp Previous Kernel/User Mode ;rfe pops KUo here 4 IEo Old Interrupt Disable ;left unchanged by rfe 5 KUo Old Kernel/User Mode ;left unchanged by rfe 6-7 - Not used (zero) 8-15 Im 8 bit interrupt mask fields. When set the corresponding interrupts are allowed to cause an exception. 16 Isc Isolate Cache (0=No, 1=Isolate) When isolated, all load and store operations are targetted to the Data cache, and never the main memory. (Used by PSX Kernel, in combination with Port FFFE0130h) 17 Swc Swapped cache mode (0=Normal, 1=Swapped) Instruction cache will act as Data cache and vice versa. Use only with Isc to access & invalidate Instr. cache entries. (Not used by PSX Kernel) 18 PZ When set cache parity bits are written as 0. 19 CM Shows the result of the last load operation with the D-cache isolated. It gets set if the cache really contained data for the addressed memory location. 20 PE Cache parity error (Does not cause exception) 21 TS TLB shutdown. Gets set if a programm address simultaneously matches 2 TLB entries. (initial value on reset allows to detect extended CPU version?) 22 BEV Boot exception vectors in RAM/ROM (0=RAM/KSEG0, 1=ROM/KSEG1) 23-24 - Not used (zero) 25 RE Reverse endianness (0=Normal endianness, 1=Reverse endianness) Reverses the byte order in which data is stored in memory. (lo-hi -> hi-lo) (Affects only user mode, not kernel mode) (?) (The bit doesn't exist in PSX ?) 26-27 - Not used (zero) 28 CU0 COP0 Enable (0=Enable only in Kernel Mode, 1=Kernel and User Mode) 29 CU1 COP1 Enable (0=Disable, 1=Enable) (none in PSX) 30 CU2 COP2 Enable (0=Disable, 1=Enable) (GTE in PSX) 31 CU3 COP3 Enable (0=Disable, 1=Enable) (none in PSX) */ #define SR_IEC 0x00000001 #define SR_KUC 0x00000002 #define SR_IEP 0x00000004 #define SR_KUP 0x00000008 #define SR_IEO 0x00000010 #define SR_KUO 0x00000020 #define SR_IM 0x0000ff00 #define SR_IM0 0x00000100 #define SR_IM1 0x00000200 #define SR_IM2 0x00000400 #define SR_IM3 0x00000800 #define SR_IM4 0x00001000 #define SR_IM5 0x00002000 #define SR_IM6 0x00004000 #define SR_IM7 0x00008000 #define SR_ISC 0x00010000 #define SR_SWC 0x00020000 #define SR_PZ 0x00040000 #define SR_CM 0x00080000 #define SR_PE 0x00100000 #define SR_TS 0x00200000 #define SR_BEV 0x00400000 #define SR_RE 0x02000000 #define SR_CU0 0x10000000 #define SR_CU1 0x20000000 #define SR_CU2 0x40000000 #define SR_CU3 0x80000000 struct iop_state* iop_create(void); void iop_init(struct iop_state* iop, struct iop_bus_s bus); void iop_init_kputchar(struct iop_state* iop, void (*kputchar)(void*, char), void* udata); void iop_init_sm_putchar(struct iop_state* iop, void (*sm_putchar)(void*, char), void* udata); void iop_destroy(struct iop_state* iop); void iop_cycle(struct iop_state* iop); void iop_reset(struct iop_state* iop); void iop_set_irq_pending(struct iop_state* iop); void iop_fetch(struct iop_state* iop); int iop_execute(struct iop_state* iop); // External bus access functions uint32_t iop_read8(struct iop_state* iop, uint32_t addr); uint32_t iop_read16(struct iop_state* iop, uint32_t addr); uint32_t iop_read32(struct iop_state* iop, uint32_t addr); void iop_write8(struct iop_state* iop, uint32_t addr, uint32_t data); void iop_write16(struct iop_state* iop, uint32_t addr, uint32_t data); void iop_write32(struct iop_state* iop, uint32_t addr, uint32_t data); /* 00h INT Interrupt 01h MOD TLB modification (none such in PSX) 02h TLBL TLB load (none such in PSX) 03h TLBS TLB store (none such in PSX) 04h AdEL Address error, Data load or Instruction fetch 05h AdES Address error, Data store The address errors occur when attempting to read outside of KUseg in user mode and when the address is misaligned. (See also: BadVaddr register) 06h IBE Bus error on Instruction fetch 07h DBE Bus error on Data load/store 08h Syscall Generated unconditionally by syscall instruction 09h BP Breakpoint - break instruction 0Ah RI Reserved instruction 0Bh CpU Coprocessor unusable 0Ch Ov Arithmetic overflow */ #define CAUSE_INT (0x00 << 2) #define CAUSE_MOD (0x01 << 2) #define CAUSE_TLBL (0x02 << 2) #define CAUSE_TLBS (0x03 << 2) #define CAUSE_ADEL (0x04 << 2) #define CAUSE_ADES (0x05 << 2) #define CAUSE_IBE (0x06 << 2) #define CAUSE_DBE (0x07 << 2) #define CAUSE_SYSCALL (0x08 << 2) #define CAUSE_BP (0x09 << 2) #define CAUSE_RI (0x0a << 2) #define CAUSE_CPU (0x0b << 2) #define CAUSE_OV (0x0c << 2) #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/iop_dis.c ================================================ // license:MIT // copyright-holders:Lisandro Alarcon (Allkern) /** * @file r3000d.c * @brief Disassembler for MIPS R3000A compatible code * @author Allkern (https://github.com/allkern) */ #include #include #include "iop_dis.h" #define OP ((opcode >> 26) & 0x3f) #define S ((opcode >> 21) & 0x1f) #define T ((opcode >> 16) & 0x1f) #define D ((opcode >> 11) & 0x1f) #define IMM5 ((opcode >> 6) & 0x1f) #define CMT ((opcode >> 6) & 0xfffff) #define SOP (opcode & 0x3f) #define IMM26 (opcode & 0x3ffffff) #define IMM16 (opcode & 0xffff) #define IMM16S ((int32_t)((int16_t)IMM16)) const char* invalid = ""; static const char* r3000_secondary_table[] = { "sll" , "invalid", "srl" , "sra" , "sllv" , "invalid", "srlv" , "srav" , "jr" , "jalr" , "invalid", "invalid", "syscall", "break" , "invalid", "invalid", "mfhi" , "mthi" , "mflo" , "mtlo" , "invalid", "invalid", "invalid", "invalid", "mult" , "multu" , "div" , "divu" , "invalid", "invalid", "invalid", "invalid", "add" , "addu" , "sub" , "subu" , "and" , "or" , "xor" , "nor" , "invalid", "invalid", "slt" , "sltu" , "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid" }; static const char* r3000_primary_table[] = { "special", "bxx" , "j" , "jal" , "beq" , "bne" , "blez" , "bgtz" , "addi" , "addiu" , "slti" , "sltiu" , "andi" , "ori" , "xori" , "lui" , "cop0" , "cop1" , "cop2" , "cop3" , "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "lb" , "lh" , "lwl" , "lw" , "lbu" , "lhu" , "lwr" , "invalid", "sb" , "sh" , "swl" , "sw" , "invalid", "invalid", "swr" , "invalid", "lwc0" , "lwc1" , "lwc2" , "lwc3" , "invalid", "invalid", "invalid", "invalid", "swc0" , "swc1" , "swc2" , "swc3" , "invalid", "invalid", "invalid", "invalid" }; static const char* r3000_cop0_table[] = { "mfc0" , "invalid", "invalid", "invalid", "mtc0" , "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "rfe" , "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid", "invalid" }; // static const char* r3000_cop2_table[] = { // "mfc2" , "invalid", "cfc2" , "invalid", // "mtc2" , "invalid", "ctc2" , "invalid", // "invalid", "invalid", "invalid", "invalid", // "invalid", "invalid", "invalid", "invalid", // "gte" , "gte" , "gte" , "gte" , // "gte" , "gte" , "gte" , "gte" , // "gte" , "gte" , "gte" , "gte" , // "gte" , "gte" , "gte" , "gte" // }; static const char* r3000_bxx_table[] = { "bltz" , "bgez" , "bltz" , "bgez" , "bltz" , "bgez" , "bltz" , "bgez" , "bltz" , "bgez" , "bltz" , "bgez" , "bltz" , "bgez" , "bltz" , "bgez" , "bltzal" , "bgezal" , "bltz" , "bgez" , "bltz" , "bgez" , "bltz" , "bgez" , "bltz" , "bgez" , "bltz" , "bgez" , "bltz" , "bgez" , "bltz" , "bgez" }; static const char* r3000_register_names[] = { "r0", "at", "v0", "v1", "a0", "a1", "a2", "a3", "t0", "t1", "t2", "t3", "t4", "t5", "t6", "t7", "s0", "s1", "s2", "s3", "s4", "s5", "s6", "s7", "t8", "t9", "k0", "k1", "gp", "sp", "fp", "ra" }; static const char* r3000_cop0_register_names[] = { "Index", "Random", "EntryLo", "BPC", "Context", "BDA", "JUMPDEST", "DCIC", "BadVAddr", "BDAM", "EntryHi", "BPCM", "SR", "Cause", "EPC", "PRId", "r16", "r17", "r18", "r19", "r20", "r21", "r22", "r23", "r24", "r25", "r26", "r27", "r28", "r29", "r30", "r31", "r32", "r33", "r34", "r35", "r36", "r37", "r38", "r39", "r40", "r41", "r42", "r43", "r44", "r45", "r46", "r47" }; void disassemble_secondary(char* buf, uint32_t opcode, struct iop_dis_state* state) { char* ptr = buf; int sop = SOP; switch (sop) { case 0: case 2: case 3: sprintf(ptr, "%-8s $%s, $%s, %u", r3000_secondary_table[sop], r3000_register_names[D], r3000_register_names[T], IMM5 ); break; case 4: case 6: case 7: sprintf(ptr, "%-8s $%s, $%s, $%s", r3000_secondary_table[sop], r3000_register_names[D], r3000_register_names[T], r3000_register_names[S] ); break; case 9: sprintf(ptr, "%-8s $%s, $%s", r3000_secondary_table[sop], r3000_register_names[S], r3000_register_names[D] ); break; case 12: case 13: sprintf(ptr, "%-8s 0x%x", r3000_secondary_table[sop], CMT ); break; case 16: sprintf(ptr, "%-8s 0x%x", r3000_cop0_table[sop], CMT ); break; case 18: sprintf(ptr, "%-8s $%s", r3000_secondary_table[sop], r3000_register_names[D] ); break; case 8: case 17: case 19: sprintf(ptr, "%-8s $%s", r3000_secondary_table[sop], r3000_register_names[S] ); break; case 24: case 25: case 26: case 27: sprintf(ptr, "%-8s $%s, $%s", r3000_secondary_table[sop], r3000_register_names[S], r3000_register_names[T] ); break; case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 39: case 42: case 43: sprintf(ptr, "%-8s $%s, $%s, $%s", r3000_secondary_table[sop], r3000_register_names[D], r3000_register_names[S], r3000_register_names[T] ); break; default: sprintf(ptr, ""); break; } } char* iop_disassemble(char* buf, uint32_t opcode, struct iop_dis_state* state) { char* ptr = buf; if (state) if (state->print_address) ptr += sprintf(ptr, "%08x: ", state->addr); if (state) if (state->print_opcode) ptr += sprintf(ptr, "%08x ", opcode); int op = OP; switch (op) { case 0: disassemble_secondary(ptr, opcode, state); break; case 1: sprintf(ptr, (IMM16S >= 0) ? "%-8s $%s, 0x%04x" : "%-8s $%s, -0x%04x", r3000_bxx_table[T], r3000_register_names[S], (IMM16S >= 0) ? IMM16S : ((~IMM16S) + 1) ); break; case 2: case 3: sprintf(ptr, "%-8s 0x%08x", r3000_primary_table[op], (state ? (state->addr & 0xf0000000) : 0) | (IMM26 << 2) ); break; case 4: case 5: sprintf(ptr, (IMM16S >= 0) ? "%-8s $%s, $%s, 0x%04x" : "%-8s $%s, $%s, -0x%04x", r3000_primary_table[op], r3000_register_names[S], r3000_register_names[T], (IMM16S >= 0 ? IMM16S : ~IMM16S) << 2 ); break; case 6: case 7: sprintf(ptr, (IMM16S >= 0) ? "%-8s $%s, 0x%04x" : "%-8s $%s, -0x%04x", r3000_primary_table[op], r3000_register_names[S], (IMM16S >= 0) ? IMM16S : ((~IMM16S) + 1) ); break; case 8: case 9: case 10: case 11: case 12: case 13: case 14: sprintf(ptr, "%-8s $%s, $%s, 0x%x", r3000_primary_table[op], r3000_register_names[T], r3000_register_names[S], IMM16S ); break; case 15: sprintf(ptr, "%-8s $%s, 0x%04x", r3000_primary_table[op], r3000_register_names[T], IMM16 ); break; case 16: { int s = S; sprintf(ptr, s == 16 ? "%-8s" : "%-8s $%s, $Cop0_%s", r3000_cop0_table[s], r3000_register_names[T], r3000_cop0_register_names[D] ); } break; /* To-do: case 18 (COP2, GTE) */ case 18: sprintf(ptr, ""); break; case 32: case 33: case 34: case 35: case 36: case 37: case 38: case 40: case 41: case 42: case 43: case 46: case 48: case 49: case 50: case 51: case 56: case 57: case 58: case 59: { const char* fmt = "%-8s $%s, %i($%s)"; if (state) if (state->hex_memory_offset) fmt = "%-8s $%s, 0x%04x($%s)"; sprintf(ptr, fmt, r3000_primary_table[op], r3000_register_names[T], IMM16S, r3000_register_names[S] ); } break; default: sprintf(ptr, ""); break; } return buf; } ================================================ FILE: src/iop/iop_dis.h ================================================ // license:MIT // copyright-holders:Lisandro Alarcon (Allkern) /** * @file iop_dis.h * @brief Disassembler for MIPS R3000A (IOP) compatible code * @author Allkern (https://github.com/allkern) */ #ifndef IOP_DIS_H #define IOP_DIS_H #ifdef __cplusplus extern "C" { #endif #include struct iop_dis_state { uint32_t addr; int print_address; int print_opcode; int hex_memory_offset; }; /** @brief Disassemble a single opcode, printing to a buffer and * optionally taking in a pointer to a disassembler state * struct * * @param buf pointer to a char buffer * @param opcode opcode to disassemble * @param state optional pointer to disassembler state struct * (pass NULL if not required) * @returns `buf` */ char* iop_disassemble(char* buf, uint32_t opcode, struct iop_dis_state* state); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/iop_export.c ================================================ #include "iop_export.h" static inline uint32_t irx_import_table_addr(struct iop_state* iop, int entry) { uint32_t i = entry - 0x18; while ((entry - i) < 0x2000) { if (iop_read32(iop, i) == 0x41e00000) return i; i -= 4; } return 0; } static inline int iop_get_module(struct iop_state* iop, int itable) { char buf[9]; for (int i = 0; i < 8; i++) buf[i] = iop_read8(iop, itable + 12 + i); if (!strncmp(buf, "ioman", 8)) return MODULE_IOMAN; if (!strncmp(buf, "iomanx", 8)) return MODULE_IOMANX; if (!strncmp(buf, "loadcore", 8)) return MODULE_LOADCORE; if (!strncmp(buf, "sysmem", 8)) return MODULE_SYSMEM; return MODULE_UNKNOWN; } static inline int iop_delegate_ioman(struct iop_state* iop, int slot, int iomanx) { switch (slot & 0xffff) { case IOMAN_OPEN: return ioman_open(iop, iomanx); case IOMAN_CLOSE: return ioman_close(iop, iomanx); case IOMAN_READ: return ioman_read(iop, iomanx); case IOMAN_WRITE: return ioman_write(iop, iomanx); case IOMAN_LSEEK: return ioman_lseek(iop, iomanx); case IOMAN_IOCTL: return ioman_ioctl(iop, iomanx); case IOMAN_REMOVE: return ioman_remove(iop, iomanx); case IOMAN_MKDIR: return ioman_mkdir(iop, iomanx); case IOMAN_RMDIR: return ioman_rmdir(iop, iomanx); case IOMAN_DOPEN: return ioman_dopen(iop, iomanx); case IOMAN_DCLOSE: return ioman_dclose(iop, iomanx); case IOMAN_DREAD: return ioman_dread(iop, iomanx); case IOMAN_GETSTAT: return ioman_getstat(iop, iomanx); case IOMAN_CHSTAT: return ioman_chstat(iop, iomanx); case IOMAN_FORMAT: return ioman_format(iop, iomanx); case IOMAN_ADDDRV: return ioman_adddrv(iop, iomanx); case IOMAN_DELDRV: return ioman_deldrv(iop, iomanx); case IOMAN_STDIOINIT: return ioman_stdioinit(iop, iomanx); case IOMAN_RENAME: return ioman_rename(iop, iomanx); case IOMAN_CHDIR: return ioman_chdir(iop, iomanx); case IOMAN_SYNC: return ioman_sync(iop, iomanx); case IOMAN_MOUNT: return ioman_mount(iop, iomanx); case IOMAN_UMOUNT: return ioman_umount(iop, iomanx); case IOMAN_LSEEK64: return ioman_lseek64(iop, iomanx); case IOMAN_DEVCTL: return ioman_devctl(iop, iomanx); case IOMAN_SYMLINK: return ioman_symlink(iop, iomanx); case IOMAN_READLINK: return ioman_readlink(iop, iomanx); case IOMAN_IOCTL2: return ioman_ioctl2(iop, iomanx); } return 0; } static inline int iop_delegate_loadcore(struct iop_state* iop, int slot) { switch (slot & 0xffff) { case LOADCORE_REG_LIB_ENT: return loadcore_reg_lib_ent(iop); } return 0; } static inline int iop_delegate_sysmem(struct iop_state* iop, int slot) { switch (slot & 0xffff) { // SYSMEM kprintf case 14: return sysmem_kprintf(iop); } return 0; } int iop_test_module_hooks(struct iop_state* iop) { uint32_t slot = iop_read32(iop, iop->pc); if ((slot >> 16) != 0x2400) return 0; uint32_t itable = irx_import_table_addr(iop, iop->pc); if (!itable) return 0; int module = iop_get_module(iop, itable); if (!module) return 0; switch (module) { case MODULE_IOMAN: return iop_delegate_ioman(iop, slot, 0); case MODULE_IOMANX: return iop_delegate_ioman(iop, slot, 1); case MODULE_LOADCORE: return iop_delegate_loadcore(iop, slot); case MODULE_SYSMEM: return iop_delegate_sysmem(iop, slot); } return 0; } void iop_return(struct iop_state* iop, int ret) { // Set v0 (return register) to ret iop->r[2] = ret; // Emulate jal ra iop->pc = iop->r[31]; iop->next_pc = iop->pc + 4; } ================================================ FILE: src/iop/iop_export.h ================================================ #ifndef IOP_EXPORT_H #define IOP_EXPORT_H #ifdef __cplusplus extern "C" { #endif #include #include "iop.h" #include "hle/ioman.h" #include "hle/loadcore.h" #include "hle/sysmem.h" #define MODULE_UNKNOWN 0 #define MODULE_IOMAN 1 #define MODULE_IOMANX 2 #define MODULE_LOADCORE 3 #define MODULE_SYSMEM 4 #define IOMAN_OPEN 4 #define IOMAN_CLOSE 5 #define IOMAN_READ 6 #define IOMAN_WRITE 7 #define IOMAN_LSEEK 8 #define IOMAN_IOCTL 9 #define IOMAN_REMOVE 10 #define IOMAN_MKDIR 11 #define IOMAN_RMDIR 12 #define IOMAN_DOPEN 13 #define IOMAN_DCLOSE 14 #define IOMAN_DREAD 15 #define IOMAN_GETSTAT 16 #define IOMAN_CHSTAT 17 #define IOMAN_FORMAT 18 #define IOMAN_ADDDRV 20 #define IOMAN_DELDRV 21 #define IOMAN_STDIOINIT 23 #define IOMAN_RENAME 25 #define IOMAN_CHDIR 26 #define IOMAN_SYNC 27 #define IOMAN_MOUNT 28 #define IOMAN_UMOUNT 29 #define IOMAN_LSEEK64 30 #define IOMAN_DEVCTL 31 #define IOMAN_SYMLINK 32 #define IOMAN_READLINK 33 #define IOMAN_IOCTL2 34 #define LOADCORE_REG_LIB_ENT 6 int iop_test_module_hooks(struct iop_state* iop); void iop_return(struct iop_state* iop, int ret); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/rpc.c ================================================ #include #include #include #include "rpc.h" #define printf(fmt, ...)(0) const char* rpc_get_server(uint32_t id) { switch (id) { case 0x80000001: return "FILEIO"; case 0x80000003: return "IOP heap alloc (FILEIO)"; case 0x80000006: return "LOADFILE"; case 0x80000100: return "PADMAN"; case 0x80000101: return "PADMAN ext"; case 0x80000400: return "MCSERV"; case 0x80000592: return "CDVD Init (CDVDFSV)"; case 0x80000593: return "CDVD S commands (CDVDFSV)"; case 0x80000595: return "CDVD N commands (CDVDFSV)"; case 0x80000597: return "CDVD SearchFile (CDVDFSV)"; case 0x8000059A: return "CDVD Disk Ready (CDVDFSV)"; case 0x80000701: return "LIBSD Remote (SDRDRV)"; case 0x80000901: return "MTAP Port Open (MTAPMAN)"; case 0x80000902: return "MTAP Port Close (MTAPMAN)"; case 0x80000903: return "MTAP Get Connection (MTAPMAN)"; case 0x80000904: return "MTAP Unknown (MTAPMAN)"; case 0x80000905: return "MTAP Unknown (MTAPMAN)"; case 0x80001400: return "EYETOY"; } return ""; } char* rpc_decode_packet(struct iop_state* iop, char* buf, uint32_t* data) { char* ptr = buf; struct sif_cmd_header* hdr = (struct sif_cmd_header*)data; // ptr += sprintf(ptr, "rpc: "); switch (hdr->cid) { case 0x80000000: ptr += sprintf(ptr, "rpc: ChangeSaddr: "); break; case 0x80000001: ptr += sprintf(ptr, "rpc: SetSreg: "); break; case 0x80000002: { struct sif_init_pkt* init = (struct sif_init_pkt*)data; ptr += sprintf(ptr, "rpc: Init (opt=%d)", init->header.opt); } break; case 0x80000003: ptr += sprintf(ptr, "rpc: Reboot: "); break; case 0x80000008: ptr += sprintf(ptr, "rpc: RequestEnd: "); break; case 0x80000009: { struct sif_rpc_bind_pkt* bind = (struct sif_rpc_bind_pkt*)data; const char* server = rpc_get_server(bind->sid); ptr += sprintf(ptr, "rpc: Bind (%s)", server); break; } break; case 0x8000000A: { struct sif_rpc_call_pkt* call = (struct sif_rpc_call_pkt*)data; uint32_t sid = iop_read32(iop, call->server); ptr += sprintf(ptr, "rpc: Call Server=%08x Func=%08x", sid, call->rpc_number); if (sid == 0x01470201) { printf(ptr, "rpc: ReadBackupRam Func=%08x SendSize=%d PktAddr=%08x\n", call->rpc_number, call->send_size, call->pkt_addr ); } if (sid == 0x01470200) { printf(ptr, "rpc: WriteBackupRam Func=%08x SendSize=%d PktAddr=%08x\n", call->rpc_number, call->send_size, call->pkt_addr ); } if (sid == 0x14799) { struct MODULE_99_PACKET { uint8_t type; uint8_t unknown[4]; uint8_t command; uint8_t data[0x39]; uint8_t checksum; }; switch (call->rpc_number) { case 0x02000000: printf("s14x_link: RPC ReceiveData\n"); break; case 0x03004002: { uint8_t* buf = malloc(call->send_size); for (int i = 0; i < call->send_size; i++) buf[i] = iop_read8(iop, call->pkt_addr + i); struct MODULE_99_PACKET* packet = (struct MODULE_99_PACKET*)buf; printf("s14x_link: RPC SendData(type=%d, command=%02x)\n", packet->type, packet->command ); } break; case 0x08000000: printf("s14x_link: RPC CheckOnline\n"); break; default: printf("s14x_link: RPC LINK_%08X\n", call->rpc_number); break; } } } break; case 0x8000000C: ptr += sprintf(ptr, "rpc: GetOtherData"); break; // default: ptr += sprintf(ptr, "Unknown CID %08x", hdr->cid); break; default: return NULL; } *ptr++ = '\0'; return buf; } // 80000000h Change SADDR // 80000001h Set SREG // 80000002h SIFCMD Init // 80000003h Reboot IOP // 80000008h Request End // 80000009h Bind // 8000000Ah Call // 8000000Ch Get other data // 0x80000001 "FILEIO" // 0x80000003 "IOP heap alloc (FILEIO)" // 0x80000006 "LOADFILE" // 0x80000100 "PADMAN" // 0x80000101 "PADMAN ext" // 0x80000400 "MCSERV" // 0x80000592 "CDVD Init (CDVDFSV)" // 0x80000593 "CDVD S commands (CDVDFSV)" // 0x80000595 "CDVD N commands (CDVDFSV)" // 0x80000597 "CDVD SearchFile (CDVDFSV)" // 0x8000059A "CDVD Disk Ready (CDVDFSV)" // 0x80000701 "LIBSD Remote (SDRDRV)" // 0x80000901 "MTAP Port Open (MTAPMAN)" // 0x80000902 "MTAP Port Close (MTAPMAN)" // 0x80000903 "MTAP Get Connection (MTAPMAN)" // 0x80000904 "MTAP Unknown (MTAPMAN)" // 0x80000905 "MTAP Unknown (MTAPMAN)" // 0x80001400 "EYETOY" ================================================ FILE: src/iop/rpc.h ================================================ #ifndef RPC_H #define RPC_H #ifdef __cplusplus extern "C" { #endif #include #include "iop.h" struct __attribute__((packed)) sif_cmd_header { unsigned int psize : 8; unsigned int dsize : 24; unsigned int dest; unsigned int cid; unsigned int opt; }; struct sif_rpc_packet_header { struct sif_cmd_header sif_cmd; unsigned int rec_id; unsigned int pkt_addr; unsigned int rpc_id; }; struct sif_rpc_client_data { struct sif_rpc_packet_header hdr; unsigned int command; unsigned int buff, *cbuff; unsigned int end_function; unsigned int end_param; unsigned int server; }; struct sif_rpc_server_data { unsigned int sid; unsigned int func; unsigned int buff; unsigned int size; unsigned int cfunc; unsigned int cbuff; unsigned int size2; unsigned int client; unsigned int pkt_addr; unsigned int rpc_number; unsigned int recv; unsigned int rsize; unsigned int rmode; unsigned int rid; unsigned int link; unsigned int next; unsigned int base; }; struct sif_rpc_data_queue { unsigned int thread_id, active; unsigned int link, start, end; unsigned int next; }; struct sif_dma_transfer { unsigned int src, dest; unsigned int size; unsigned int attr; }; struct sif_saddr_pkt { struct sif_cmd_header header; unsigned int buf; }; struct sif_cmd_set_sreg_pkt { struct sif_cmd_header header; unsigned int index; unsigned int value; }; struct sif_init_pkt { struct sif_cmd_header header; unsigned int buff; }; struct sif_iop_reset_pkt { struct sif_cmd_header header; unsigned int arglen; unsigned int mode; char arg[80]; }; struct sif_rpc_rend_pkt { struct sif_cmd_header sifcmd; unsigned int rec_id; unsigned int pkt_addr; unsigned int rpc_id; unsigned int client; unsigned int cid; unsigned int server; unsigned int buff; unsigned int cbuff; }; struct sif_rpc_bind_pkt { struct sif_cmd_header sifcmd; unsigned int rec_id; unsigned int pkt_addr; unsigned int rpc_id; unsigned int client; unsigned int sid; }; struct sif_rpc_call_pkt { struct sif_cmd_header sifcmd; unsigned int rec_id; unsigned int pkt_addr; unsigned int rpc_id; unsigned int client; unsigned int rpc_number; unsigned int send_size; unsigned int receive; unsigned int recv_size; unsigned int rmode; unsigned int server; }; struct sif_rpc_other_data_pkt { struct sif_cmd_header sifcmd; unsigned int rec_id; unsigned int pkt_addr; unsigned int rpc_id; unsigned int receive; unsigned int src; unsigned int dest; unsigned int size; }; char* rpc_decode_packet(struct iop_state* iop, char* buf, uint32_t* data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/sio2.c ================================================ #include #include #include #include "sio2.h" static inline void sio2_reset(struct ps2_sio2* sio2) { queue_clear(sio2->in); sio2->send3_index = 0; } struct ps2_sio2* ps2_sio2_create(void) { return malloc(sizeof(struct ps2_sio2)); } void sio2_send_irq(void* udata, int overshoot) { struct ps2_sio2* sio2 = (struct ps2_sio2*)udata; sio2->istat |= 1; ps2_iop_intc_irq(sio2->intc, IOP_INTC_SIO2); } void sio2_dma_reset(struct ps2_sio2* sio2) { queue_clear(sio2->in); } void ps2_sio2_init(struct ps2_sio2* sio2, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched) { memset(sio2, 0, sizeof(struct ps2_sio2)); sio2->intc = intc; sio2->dma = dma; sio2->in = queue_create(); sio2->out = queue_create(); queue_init(sio2->in); queue_init(sio2->out); sio2->sched = sched; sio2->recv1 = 0x1d100; } void ps2_sio2_destroy(struct ps2_sio2* sio2) { // Detach all devices for (int i = 0; i < 4; i++) ps2_sio2_detach_device(sio2, i); queue_destroy(sio2->in); queue_destroy(sio2->out); free(sio2); } static inline int sio2_handle_command(struct ps2_sio2* sio2, int idx) { int devid = queue_at(sio2->in, 0); int cmd = queue_at(sio2->in, 1); int p = sio2->send3[idx] & 3; int len = (sio2->send3[idx] >> 18) & 0xff; // printf("sio2: Trying command %02x in SEND3[%d] port %d devid %02x (in: %d (%d), len: %d)\n", cmd, // idx, p, devid, queue_size(sio2->in), queue_size(sio2->in) + 2, len // ); // If we're sending a pad command, make sure it's only for ports // 0-1, and if it's a memory card command, make sure it's only // for ports 2-3 int pad = devid == SIO2_DEV_PAD; int mcd = devid == SIO2_DEV_MCD; int mtap = devid == SIO2_DEV_MTAP; if (!(pad || mcd)) return 0; // Check if the port is actually connected if (!sio2->port[p].handle_command) return 0; // printf("sio2: Sending command %02x to port %d with devid %02x (in: %d (%d), len: %d)\n", // cmd, p, devid, queue_size(sio2->in), queue_size(sio2->in) + 2, len // ); // Send command sio2->port[p].handle_command(sio2, sio2->port[p].udata, cmd); // Clear current command parameters for (int i = 0; i < len; i++) queue_pop(sio2->in); // Weird behavior, DMA MCD commands are aligned to a 36-word boundary if (mcd) { while (sio2->out->size % 0x90) queue_push(sio2->out, 0); if (!queue_is_empty(sio2->in)) { // Remove input padding while (sio2->in->index % 0x90) queue_pop(sio2->in); } } // printf("sio2: FIFOOUT size: %d (%x)\n", sio2->out->size, sio2->out->size); // if (cmd == 0x81) exit(1); return 1; } static inline void sio2_write_ctrl(struct ps2_sio2* sio2, uint32_t data) { sio2->ctrl = data & ~1; if (data & 0xc) { // printf("sio2: Controller reset\n"); queue_clear(sio2->out); } // Send command bit if ((data & 1) == 0) return; // Disconnected by default sio2->recv1 = 0x1d100; // Send IRQ no matter what sio2->istat |= 1; ps2_iop_intc_irq(sio2->intc, IOP_INTC_SIO2); // printf("sio2: Sending command %02x to port %d with devid %02x (in: %d, len: %d)\n", // cmd, p, devid, queue_size(sio2->in), len // ); for (int i = 0; i < 16; i++) { // Break when we find a null command if (!sio2->send3[i]) break; // If any of the commands were handled, set RECV1 to 0x1100 if (sio2_handle_command(sio2, i)) { if (sio2->recv1 & 0x2000) { sio2->recv1 = 0x1d100; } else { sio2->recv1 = 0x1100; } } } // Complete SIO2_out transfer after all commands have executed // For commands that use DMA (mostly MCD commands), this will handle // reading from the output FIFO. Will do nothing on commands // that don't use DMA, because the IOP needs to start a transfer // before sending a DMA command, and if a command executed, but // didn't actually put anything in the FIFO, the DMA request // will be cleared. (e.g. when a memory card isn't connected) iop_dma_end_sio2_out_transfer(sio2->dma); } uint64_t ps2_sio2_read8(struct ps2_sio2* sio2, uint32_t addr) { switch (addr) { // case 0x1F808260: /* printf("8-bit SIO2_FIFOIN read\n"); */ return 0; case 0x1F808264: { if (queue_is_empty(sio2->out)) { // printf("sio2: SIO2_FIFOOUT read %02x\n", 0x00); return 0x00; } uint8_t b = queue_pop(sio2->out); // printf("sio2: SIO2_FIFOOUT read %02x\n", b); return b; } break; // case 0x1F808268: /* printf("8-bit SIO2_CTRL read\n"); */ return 0; // case 0x1F80826C: /* printf("8-bit SIO2_RECV1 read\n"); */ return 0; // case 0x1F808270: /* printf("8-bit SIO2_RECV2 read\n"); */ return 0; // case 0x1F808274: /* printf("8-bit SIO2_RECV3 read\n"); */ return 0; // case 0x1F808280: /* printf("8-bit SIO2_ISTAT read\n"); */ return 0; // default: { // if (addr >= 0x1F808200 && addr <= 0x1F80823F) { // Memcard or non-controller access // // printf("8-bit SIO2_SEND3 read\n"); // } // if (addr >= 0x1F808240 && addr <= 0x1F80825F) { // // printf("8-bit SIO2_SEND%d read\n", (addr & 4) ? 2 : 1); // } // } break; } printf("sio2: Unhandled 8-bit read from address %08x\n", addr); exit(1); return 0; } uint64_t ps2_sio2_read32(struct ps2_sio2* sio2, uint32_t addr) { switch (addr) { // case 0x1F808260: /* printf("32-bit SIO2_FIFOIN read\n"); */ return 0; // case 0x1F808264: /* printf("32-bit SIO2_FIFOOUT read\n"); */ return 0; case 0x1F808268: /* printf("32-bit SIO2_CTRL read\n"); */ return 0; case 0x1F80826C: /* printf("32-bit SIO2_RECV1 read\n"); */ return sio2->recv1; case 0x1F808270: /* printf("32-bit SIO2_RECV2 read\n"); */ return 0xf; case 0x1F808274: /* printf("32-bit SIO2_RECV3 read\n"); */ return 0; case 0x1F808280: /* printf("32-bit SIO2_ISTAT read\n"); */ return sio2->istat; default: { if (addr >= 0x1F808200 && addr <= 0x1F80823F) { // printf("32-bit SIO2_SEND3 read\n"); return sio2->send3[(addr & 0x3f) >> 2]; } // if (addr >= 0x1F808240 && addr <= 0x1F80825F) { // // printf("32-bit SIO2_SEND%d read\n", (addr & 4) ? 2 : 1); // } } break; } printf("sio2: Unhandled 32-bit read from address %08x\n", addr); exit(1); return 0; } void ps2_sio2_write8(struct ps2_sio2* sio2, uint32_t addr, uint64_t data) { switch (addr) { case 0x1F808260: /* printf("sio2: FIFOIN write %02x\n", data); */ queue_push(sio2->in, data); return; // case 0x1F808264: /* printf("8-bit SIO2_FIFOOUT write %02x\n", data); */ return; case 0x1F808268: sio2_write_ctrl(sio2, data); return; // case 0x1F80826C: /* printf("8-bit SIO2_RECV1 write %02x\n", data); */ return; // case 0x1F808270: /* printf("8-bit SIO2_RECV2 write %02x\n", data); */ return; // case 0x1F808274: /* printf("8-bit SIO2_RECV3 write %02x\n", data); */ return; // case 0x1F808280: /* printf("8-bit SIO2_ISTAT write %02x\n", data); */ return; // default: { // if (addr >= 0x1F808200 && addr <= 0x1F80823F) { // // printf("8-bit SIO2_SEND3 write %02x\n", data); // } // if (addr >= 0x1F808240 && addr <= 0x1F80825F) { // // printf("8-bit SIO2_SEND%d write %02x\n", (addr & 4) ? 2 : 1, data); // } // } break; } printf("sio2: Unhandled 8-bit write to address %08x\n", addr); exit(1); } void ps2_sio2_write32(struct ps2_sio2* sio2, uint32_t addr, uint64_t data) { switch (addr) { // case 0x1F808260: /* printf("32-bit SIO2_FIFOIN write %08x\n", data); */ return; // case 0x1F808264: /* printf("32-bit SIO2_FIFOOUT write %08x\n", data); */ return; case 0x1F808268: sio2_write_ctrl(sio2, data); return; // case 0x1F80826C: /* printf("32-bit SIO2_RECV1 write %08x\n", data); */ return; // case 0x1F808270: /* printf("32-bit SIO2_RECV2 write %08x\n", data); */ return; // case 0x1F808274: /* printf("32-bit SIO2_RECV3 write %08x\n", data); */ return; case 0x1F808280: /* printf("32-bit SIO2_ISTAT write %08x\n", data); */ sio2->istat &= ~(data & 3); return; default: { if (addr >= 0x1F808200 && addr <= 0x1F80823F) { int index = (addr & 0x3f) >> 2; sio2->send3[index] = data; if (!index) sio2_reset(sio2); // printf("sio2: 32-bit SEND3 write %08x\n", data); return; } if (addr >= 0x1F808240 && addr <= 0x1F80825F) { // printf("32-bit SIO2_SEND%d write %08x\n", (addr & 4) ? 2 : 1, data); return; } } break; } printf("sio2: Unhandled 32-bit write to address %08x (%08x)\n", addr, data); // exit(1); } void ps2_sio2_attach_device(struct ps2_sio2* sio2, struct sio2_device dev, int port) { sio2->port[port] = dev; } void ps2_sio2_detach_device(struct ps2_sio2* sio2, int port) { struct sio2_device* dev = &sio2->port[port]; if (dev->detach) dev->detach(dev->udata); dev->handle_command = 0; dev->detach = 0; dev->udata = NULL; } ================================================ FILE: src/iop/sio2.h ================================================ #ifndef SIO2_H #define SIO2_H #ifdef __cplusplus extern "C" { #endif #include #include "queue.h" #include "intc.h" #include "scheduler.h" #include "dma.h" #define SIO2_DEV_PAD 0x01 #define SIO2_DEV_PS1PAD 0x42 // ? #define SIO2_DEV_MTAP 0x21 #define SIO2_DEV_IR 0x61 #define SIO2_DEV_MCD 0x81 struct ps2_sio2; struct sio2_device { void (*handle_command)(struct ps2_sio2*, void*, int); void (*detach)(void*); void* udata; }; struct ps2_sio2 { struct sio2_device port[4]; uint32_t ctrl; uint32_t send3[16]; uint32_t send1[8]; uint32_t send2[8]; struct queue_state* in; struct queue_state* out; uint32_t recv1; uint32_t recv2; uint32_t recv3; uint32_t istat; int send3_index; struct ps2_iop_dma* dma; struct ps2_iop_intc* intc; struct sched_state* sched; }; struct ps2_sio2* ps2_sio2_create(void); void ps2_sio2_init(struct ps2_sio2* sio2, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched); void ps2_sio2_destroy(struct ps2_sio2* sio2); uint64_t ps2_sio2_read8(struct ps2_sio2* sio2, uint32_t addr); uint64_t ps2_sio2_read32(struct ps2_sio2* sio2, uint32_t addr); void ps2_sio2_write8(struct ps2_sio2* sio2, uint32_t addr, uint64_t data); void ps2_sio2_write32(struct ps2_sio2* sio2, uint32_t addr, uint64_t data); void ps2_sio2_attach_device(struct ps2_sio2* sio2, struct sio2_device dev, int port); void ps2_sio2_detach_device(struct ps2_sio2* sio2, int port); void sio2_dma_reset(struct ps2_sio2* sio2); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/spu2.c ================================================ #include #include #include #include #include "spu2.h" FILE* output = NULL; uint32_t chunk_size = 0; static const int16_t g_spu_gauss_table[] = { -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, -0x001, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0001, 0x0001, 0x0001, 0x0001, 0x0002, 0x0002, 0x0002, 0x0003, 0x0003, 0x0003, 0x0004, 0x0004, 0x0005, 0x0005, 0x0006, 0x0007, 0x0007, 0x0008, 0x0009, 0x0009, 0x000A, 0x000B, 0x000C, 0x000D, 0x000E, 0x000F, 0x0010, 0x0011, 0x0012, 0x0013, 0x0015, 0x0016, 0x0018, 0x0019, 0x001B, 0x001C, 0x001E, 0x0020, 0x0021, 0x0023, 0x0025, 0x0027, 0x0029, 0x002C, 0x002E, 0x0030, 0x0033, 0x0035, 0x0038, 0x003A, 0x003D, 0x0040, 0x0043, 0x0046, 0x0049, 0x004D, 0x0050, 0x0054, 0x0057, 0x005B, 0x005F, 0x0063, 0x0067, 0x006B, 0x006F, 0x0074, 0x0078, 0x007D, 0x0082, 0x0087, 0x008C, 0x0091, 0x0096, 0x009C, 0x00A1, 0x00A7, 0x00AD, 0x00B3, 0x00BA, 0x00C0, 0x00C7, 0x00CD, 0x00D4, 0x00DB, 0x00E3, 0x00EA, 0x00F2, 0x00FA, 0x0101, 0x010A, 0x0112, 0x011B, 0x0123, 0x012C, 0x0135, 0x013F, 0x0148, 0x0152, 0x015C, 0x0166, 0x0171, 0x017B, 0x0186, 0x0191, 0x019C, 0x01A8, 0x01B4, 0x01C0, 0x01CC, 0x01D9, 0x01E5, 0x01F2, 0x0200, 0x020D, 0x021B, 0x0229, 0x0237, 0x0246, 0x0255, 0x0264, 0x0273, 0x0283, 0x0293, 0x02A3, 0x02B4, 0x02C4, 0x02D6, 0x02E7, 0x02F9, 0x030B, 0x031D, 0x0330, 0x0343, 0x0356, 0x036A, 0x037E, 0x0392, 0x03A7, 0x03BC, 0x03D1, 0x03E7, 0x03FC, 0x0413, 0x042A, 0x0441, 0x0458, 0x0470, 0x0488, 0x04A0, 0x04B9, 0x04D2, 0x04EC, 0x0506, 0x0520, 0x053B, 0x0556, 0x0572, 0x058E, 0x05AA, 0x05C7, 0x05E4, 0x0601, 0x061F, 0x063E, 0x065C, 0x067C, 0x069B, 0x06BB, 0x06DC, 0x06FD, 0x071E, 0x0740, 0x0762, 0x0784, 0x07A7, 0x07CB, 0x07EF, 0x0813, 0x0838, 0x085D, 0x0883, 0x08A9, 0x08D0, 0x08F7, 0x091E, 0x0946, 0x096F, 0x0998, 0x09C1, 0x09EB, 0x0A16, 0x0A40, 0x0A6C, 0x0A98, 0x0AC4, 0x0AF1, 0x0B1E, 0x0B4C, 0x0B7A, 0x0BA9, 0x0BD8, 0x0C07, 0x0C38, 0x0C68, 0x0C99, 0x0CCB, 0x0CFD, 0x0D30, 0x0D63, 0x0D97, 0x0DCB, 0x0E00, 0x0E35, 0x0E6B, 0x0EA1, 0x0ED7, 0x0F0F, 0x0F46, 0x0F7F, 0x0FB7, 0x0FF1, 0x102A, 0x1065, 0x109F, 0x10DB, 0x1116, 0x1153, 0x118F, 0x11CD, 0x120B, 0x1249, 0x1288, 0x12C7, 0x1307, 0x1347, 0x1388, 0x13C9, 0x140B, 0x144D, 0x1490, 0x14D4, 0x1517, 0x155C, 0x15A0, 0x15E6, 0x162C, 0x1672, 0x16B9, 0x1700, 0x1747, 0x1790, 0x17D8, 0x1821, 0x186B, 0x18B5, 0x1900, 0x194B, 0x1996, 0x19E2, 0x1A2E, 0x1A7B, 0x1AC8, 0x1B16, 0x1B64, 0x1BB3, 0x1C02, 0x1C51, 0x1CA1, 0x1CF1, 0x1D42, 0x1D93, 0x1DE5, 0x1E37, 0x1E89, 0x1EDC, 0x1F2F, 0x1F82, 0x1FD6, 0x202A, 0x207F, 0x20D4, 0x2129, 0x217F, 0x21D5, 0x222C, 0x2282, 0x22DA, 0x2331, 0x2389, 0x23E1, 0x2439, 0x2492, 0x24EB, 0x2545, 0x259E, 0x25F8, 0x2653, 0x26AD, 0x2708, 0x2763, 0x27BE, 0x281A, 0x2876, 0x28D2, 0x292E, 0x298B, 0x29E7, 0x2A44, 0x2AA1, 0x2AFF, 0x2B5C, 0x2BBA, 0x2C18, 0x2C76, 0x2CD4, 0x2D33, 0x2D91, 0x2DF0, 0x2E4F, 0x2EAE, 0x2F0D, 0x2F6C, 0x2FCC, 0x302B, 0x308B, 0x30EA, 0x314A, 0x31AA, 0x3209, 0x3269, 0x32C9, 0x3329, 0x3389, 0x33E9, 0x3449, 0x34A9, 0x3509, 0x3569, 0x35C9, 0x3629, 0x3689, 0x36E8, 0x3748, 0x37A8, 0x3807, 0x3867, 0x38C6, 0x3926, 0x3985, 0x39E4, 0x3A43, 0x3AA2, 0x3B00, 0x3B5F, 0x3BBD, 0x3C1B, 0x3C79, 0x3CD7, 0x3D35, 0x3D92, 0x3DEF, 0x3E4C, 0x3EA9, 0x3F05, 0x3F62, 0x3FBD, 0x4019, 0x4074, 0x40D0, 0x412A, 0x4185, 0x41DF, 0x4239, 0x4292, 0x42EB, 0x4344, 0x439C, 0x43F4, 0x444C, 0x44A3, 0x44FA, 0x4550, 0x45A6, 0x45FC, 0x4651, 0x46A6, 0x46FA, 0x474E, 0x47A1, 0x47F4, 0x4846, 0x4898, 0x48E9, 0x493A, 0x498A, 0x49D9, 0x4A29, 0x4A77, 0x4AC5, 0x4B13, 0x4B5F, 0x4BAC, 0x4BF7, 0x4C42, 0x4C8D, 0x4CD7, 0x4D20, 0x4D68, 0x4DB0, 0x4DF7, 0x4E3E, 0x4E84, 0x4EC9, 0x4F0E, 0x4F52, 0x4F95, 0x4FD7, 0x5019, 0x505A, 0x509A, 0x50DA, 0x5118, 0x5156, 0x5194, 0x51D0, 0x520C, 0x5247, 0x5281, 0x52BA, 0x52F3, 0x532A, 0x5361, 0x5397, 0x53CC, 0x5401, 0x5434, 0x5467, 0x5499, 0x54CA, 0x54FA, 0x5529, 0x5558, 0x5585, 0x55B2, 0x55DE, 0x5609, 0x5632, 0x565B, 0x5684, 0x56AB, 0x56D1, 0x56F6, 0x571B, 0x573E, 0x5761, 0x5782, 0x57A3, 0x57C3, 0x57E2, 0x57FF, 0x581C, 0x5838, 0x5853, 0x586D, 0x5886, 0x589E, 0x58B5, 0x58CB, 0x58E0, 0x58F4, 0x5907, 0x5919, 0x592A, 0x593A, 0x5949, 0x5958, 0x5965, 0x5971, 0x597C, 0x5986, 0x598F, 0x5997, 0x599E, 0x59A4, 0x59A9, 0x59AD, 0x59B0, 0x59B2, 0x59B3 }; struct ps2_spu2* ps2_spu2_create(void) { return (struct ps2_spu2*)malloc(sizeof(struct ps2_spu2)); } struct wav_hdr { char riff[4]; uint32_t size; char wave[4]; char fmt[4]; uint32_t block_size; uint16_t audio_format; uint16_t num_channels; uint32_t samplerate; uint32_t bytes_per_sec; uint16_t bytes_per_block; uint16_t bits_per_sample; }; struct wav_chunk { char id[4]; // "data" uint32_t size; }; // FormatBlocID (4 bytes) : Identifier « fmt␣ » (0x66, 0x6D, 0x74, 0x20) // BlocSize (4 bytes) : Chunk size minus 8 bytes, which is 16 bytes here (0x10) // AudioFormat (2 bytes) : Audio format (1: PCM integer, 3: IEEE 754 float) // NbrChannels (2 bytes) : Number of channels // Frequency (4 bytes) : Sample rate (in hertz) // BytePerSec (4 bytes) : Number of bytes to read per second (Frequency * BytePerBloc). // BytePerBloc (2 bytes) : Number of bytes per block (NbrChannels * BitsPerSample / 8). // BitsPerSample (2 bytes) : Number of bits per sample // void spu2_adma_cb(void* udata, int overshoot) { // struct ps2_spu2* spu2 = (struct ps2_spu2*)udata; // // Request more data // if (spu2->dma->spu1.transfer_size) // iop_dma_handle_spu1_adma(spu2->dma); // struct sched_event event; // event.name = "spu2 adma callback"; // event.callback = spu2_adma_cb; // event.cycles = 49152; // event.udata = spu2; // sched_schedule(spu2->sched, event); // } void ps2_spu2_init(struct ps2_spu2* spu2, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched) { memset(spu2, 0, sizeof(struct ps2_spu2)); spu2->dma = dma; spu2->intc = intc; spu2->sched = sched; // CORE0/1 DMA status (ready) spu2->c[0].stat = 0x80; spu2->c[1].stat = 0x80; spu2->c[0].endx = 0x00ffffff; spu2->c[1].endx = 0x00ffffff; // output = fopen("adma.wav", "wb"); // fseek(output, sizeof(struct wav_hdr) + sizeof(struct wav_chunk), SEEK_SET); // struct sched_event event; // event.name = "spu2 adma callback"; // event.callback = spu2_adma_cb; // event.cycles = 49152; // event.udata = spu2; // sched_schedule(spu2->sched, event); } void spu2_irq(struct ps2_spu2* spu2, int c) { if (spu2->spdif_irq & (4 << c)) return; spu2->spdif_irq |= 4 << c; // printf("spu2: IRQ fired\n"); ps2_iop_intc_irq(spu2->intc, IOP_INTC_SPU2); } void spu2_check_irq(struct ps2_spu2* spu2, uint32_t addr) { for (int i = 0; i < 2; i++) { if ((addr == spu2->c[i].irqa) && (spu2->c[i].attr & (1 << 6))) { spu2_irq(spu2, i); } } } void spu2_decode_adpcm_block(struct ps2_spu2* spu2, struct spu2_voice* v); void adsr_load_attack(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v); void adsr_load_release(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v, int i); void spu2_write_kon(struct ps2_spu2* spu2, int c, int h, uint64_t data) { // printf("spu2: core%d kon%c %04x\n", c, h ? 'h' : 'l', data); for (int i = 0; i < 16; i++) { if (!(data & (1 << i))) continue; int idx = i+h*16; if (idx >= 24) break; struct spu2_core* cr = &spu2->c[c]; struct spu2_voice* v = &cr->v[idx]; // if (v->playing) // continue; // Make sure to clear the internal state of a voice // before playing v->playing = 1; v->counter = 0; v->h[0] = 0; v->h[1] = 0; for (int i = 0; i < 28; i++) v->buf[i] = 0; v->loop_start = 0; v->loop = 0; v->loop_end = 0; v->prev_sample_index = 0; for (int i = 0; i < 4; i++) v->s[i] = 0; v->adsr_cycles_left = 0; v->adsr_phase = 0; v->adsr_cycles_reload = 0; v->adsr_cycles = 0; v->adsr_mode = 0; v->adsr_dir = 0; v->adsr_shift = 0; v->adsr_step = 0; v->adsr_pending_step = 0; v->adsr_sustain_level = 0; v->nax = v->ssa; v->adsr_sustain_level = ((v->adsr1 & 0xf) + 1) * 0x800; cr->endx &= ~(1u << idx); // 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); adsr_load_attack(spu2, cr, v); spu2_decode_adpcm_block(spu2, v); v->envx = 0x7fff; } } void spu2_write_koff(struct ps2_spu2* spu2, int c, int h, uint64_t data) { // printf("spu2: core%d koff%c %04x\n", c, h ? 'h' : 'l', data); for (int i = 0; i < 16; i++) { if (!(data & (1 << i))) continue; int v = i+h*16; if (v >= 24) break; // spu2->c[c].v[i+h*16].playing = 0; // printf("spu2: core %d voice %d koff\n", c, v); if (!spu2->c[c].v[v].playing) continue; adsr_load_release(spu2, &spu2->c[c], &spu2->c[c].v[v], v); } } void adma_write_data(struct ps2_spu2* spu2, int c, uint64_t data) { spu2->ram[(c ? 0x2400 : 0x2000) + ((spu2->c[c].memin_write_addr++) & 0x3ff)] = data; } void spu2_write_data(struct ps2_spu2* spu2, int c, uint64_t data) { // printf("spu2: core%d data=%04x tsa=%08x\n", c, data, spu2->c[c].tsa); if (spu2->c[c].admas & (1 << c)) { adma_write_data(spu2, c, data); return; } spu2_check_irq(spu2, spu2->c[c].tsa); spu2->ram[spu2->c[c].tsa++] = data; spu2->c[c].tsa &= 0xfffff; } void spu2_core0_reset_handler(void* udata, int overshoot) { struct ps2_spu2* spu2 = (struct ps2_spu2*)udata; printf("spu2: core0 Reset\n"); spu2->c[0].stat |= 0x80; } void spu2_core1_reset_handler(void* udata, int overshoot) { struct ps2_spu2* spu2 = (struct ps2_spu2*)udata; printf("spu2: core1 Reset\n"); spu2->c[1].stat |= 0x80; } void spu2_write_attr(struct ps2_spu2* spu2, int c, uint64_t data) { if (spu2->c[c].attr & (1 << 6)) { if (!(data & (1 << 6))) { spu2->spdif_irq &= ~(4 << c); } } spu2->c[c].attr = data & 0x7fff; if (data & 0x8000) { struct sched_event event; printf("spu2: Resetting core%d\n", c); event.callback = c ? spu2_core1_reset_handler : spu2_core0_reset_handler; event.cycles = 10000; event.name = "SPU2 Reset"; event.udata = spu2; sched_schedule(spu2->sched, event); spu2->c[c].stat = 0; } } uint64_t ps2_spu2_read16(struct ps2_spu2* spu2, uint32_t addr) { addr &= 0x7ff; if ((addr >= 0x000 && addr <= 0x17f) || (addr >= 0x400 && addr <= 0x57f)) { int core = (addr >> 10) & 1; int voice = (addr >> 4) & 0x1f; switch (addr & 0xf) { case 0x0: return spu2->c[core].v[voice].voll; case 0x2: return spu2->c[core].v[voice].volr; case 0x4: return spu2->c[core].v[voice].pitch; case 0x6: return spu2->c[core].v[voice].adsr1; case 0x8: return spu2->c[core].v[voice].adsr2; case 0xA: return spu2->c[core].v[voice].envx; case 0xC: return spu2->c[core].v[voice].volxl; case 0xE: return spu2->c[core].v[voice].volxr; } } if ((addr >= 0x180 && addr <= 0x1bf) || (addr >= 0x580 && addr <= 0x5bf)) { int core = (addr >> 10) & 1; switch (addr & 0x3ff) { case 0x180: return spu2->c[core].pmon & 0xffff; case 0x182: return spu2->c[core].pmon >> 16; case 0x184: return spu2->c[core].non & 0xffff; case 0x186: return spu2->c[core].non >> 16; case 0x188: return spu2->c[core].vmixl & 0xffff; case 0x18A: return spu2->c[core].vmixl >> 16; case 0x18C: return spu2->c[core].vmixel & 0xffff; case 0x18E: return spu2->c[core].vmixel >> 16; case 0x190: return spu2->c[core].vmixr & 0xffff; case 0x192: return spu2->c[core].vmixr >> 16; case 0x194: return spu2->c[core].vmixer & 0xffff; case 0x196: return spu2->c[core].vmixer >> 16; case 0x198: return spu2->c[core].mmix; case 0x19A: return spu2->c[core].attr; case 0x19C: return spu2->c[core].irqa >> 16; case 0x19E: return spu2->c[core].irqa & 0xffff; case 0x1A0: return spu2->c[core].kon & 0xffff; case 0x1A2: return spu2->c[core].kon >> 16; case 0x1A4: return spu2->c[core].koff & 0xffff; case 0x1A6: return spu2->c[core].koff >> 16; case 0x1A8: return spu2->c[core].tsa >> 16; case 0x1AA: return spu2->c[core].tsa & 0xffff; case 0x1AC: return spu2->c[core].data; case 0x1AE: return spu2->c[core].ctrl; case 0x1B0: return spu2->c[core].admas; case 0x2E0: return spu2->c[core].esa >> 16; case 0x2E2: return spu2->c[core].esa & 0xffff; } } if ((addr >= 0x1c0 && addr <= 0x2df) || (addr >= 0x5c0 && addr <= 0x6df)) { int core = (addr >> 10) & 1; addr -= 0x1c0 + 0x400 * core; int voice = addr / 12; int reg = addr % 12; switch (reg) { case 0x0: return spu2->c[core].v[voice].ssa >> 16; case 0x2: return spu2->c[core].v[voice].ssa & 0xffff; case 0x4: return spu2->c[core].v[voice].lsax >> 16; case 0x6: return spu2->c[core].v[voice].lsax & 0xffff; case 0x8: return spu2->c[core].v[voice].nax >> 16; case 0xA: return spu2->c[core].v[voice].nax & 0xffff; } } if ((addr >= 0x2e0 && addr <= 0x347) || (addr >= 0x6e0 && addr <= 0x747)) { int core = (addr >> 10) & 1; switch (addr & 0x3ff) { case 0x2E0: return spu2->c[core].esa >> 16; case 0x2E2: return spu2->c[core].esa & 0xffff; case 0x2E4: return spu2->c[core].fb_src_a >> 16; case 0x2E6: return spu2->c[core].fb_src_a & 0xffff; case 0x2E8: return spu2->c[core].fb_src_b >> 16; case 0x2EA: return spu2->c[core].fb_src_b & 0xffff; case 0x2EC: return spu2->c[core].iir_dest_a0 >> 16; case 0x2EE: return spu2->c[core].iir_dest_a0 & 0xffff; case 0x2F0: return spu2->c[core].iir_dest_a1 >> 16; case 0x2F2: return spu2->c[core].iir_dest_a1 & 0xffff; case 0x2F4: return spu2->c[core].acc_src_a0 >> 16; case 0x2F6: return spu2->c[core].acc_src_a0 & 0xffff; case 0x2F8: return spu2->c[core].acc_src_a1 >> 16; case 0x2FA: return spu2->c[core].acc_src_a1 & 0xffff; case 0x2FC: return spu2->c[core].acc_src_b0 >> 16; case 0x2FE: return spu2->c[core].acc_src_b0 & 0xffff; case 0x300: return spu2->c[core].acc_src_b1 >> 16; case 0x302: return spu2->c[core].acc_src_b1 & 0xffff; case 0x304: return spu2->c[core].iir_src_a0 >> 16; case 0x306: return spu2->c[core].iir_src_a0 & 0xffff; case 0x308: return spu2->c[core].iir_src_a1 >> 16; case 0x30A: return spu2->c[core].iir_src_a1 & 0xffff; case 0x30C: return spu2->c[core].iir_dest_b0 >> 16; case 0x30E: return spu2->c[core].iir_dest_b0 & 0xffff; case 0x310: return spu2->c[core].iir_dest_b1 >> 16; case 0x312: return spu2->c[core].iir_dest_b1 & 0xffff; case 0x314: return spu2->c[core].acc_src_c0 >> 16; case 0x316: return spu2->c[core].acc_src_c0 & 0xffff; case 0x318: return spu2->c[core].acc_src_c1 >> 16; case 0x31A: return spu2->c[core].acc_src_c1 & 0xffff; case 0x31C: return spu2->c[core].acc_src_d0 >> 16; case 0x31E: return spu2->c[core].acc_src_d0 & 0xffff; case 0x320: return spu2->c[core].acc_src_d1 >> 16; case 0x322: return spu2->c[core].acc_src_d1 & 0xffff; case 0x324: return spu2->c[core].iir_src_b1 >> 16; case 0x326: return spu2->c[core].iir_src_b1 & 0xffff; case 0x328: return spu2->c[core].iir_src_b0 >> 16; case 0x32A: return spu2->c[core].iir_src_b0 & 0xffff; case 0x32C: return spu2->c[core].mix_dest_a0 >> 16; case 0x32E: return spu2->c[core].mix_dest_a0 & 0xffff; case 0x330: return spu2->c[core].mix_dest_a1 >> 16; case 0x332: return spu2->c[core].mix_dest_a1 & 0xffff; case 0x334: return spu2->c[core].mix_dest_b0 >> 16; case 0x336: return spu2->c[core].mix_dest_b0 & 0xffff; case 0x338: return spu2->c[core].mix_dest_b1 >> 16; case 0x33A: return spu2->c[core].mix_dest_b1 & 0xffff; case 0x33C: return spu2->c[core].eea >> 16; case 0x33E: return spu2->c[core].eea & 0xffff; case 0x340: return spu2->c[core].endx >> 16; case 0x342: return spu2->c[core].endx & 0xffff; case 0x344: return spu2->c[core].stat; case 0x346: return spu2->c[core].ends; } } // Misc. switch (addr & 0x7ff) { case 0x760: return spu2->c[0].mvoll; case 0x762: return spu2->c[0].mvolr; case 0x764: return spu2->c[0].evoll; case 0x766: return spu2->c[0].evolr; case 0x768: return spu2->c[0].avoll; case 0x76A: return spu2->c[0].avolr; case 0x76C: return spu2->c[0].bvoll; case 0x76E: return spu2->c[0].bvolr; case 0x770: return spu2->c[0].mvolxl; case 0x772: return spu2->c[0].mvolxr; case 0x774: return spu2->c[0].iir_alpha; case 0x776: return spu2->c[0].acc_coef_a; case 0x778: return spu2->c[0].acc_coef_b; case 0x77A: return spu2->c[0].acc_coef_c; case 0x77C: return spu2->c[0].acc_coef_d; case 0x77E: return spu2->c[0].iir_coef; case 0x780: return spu2->c[0].fb_alpha; case 0x782: return spu2->c[0].fb_x; case 0x784: return spu2->c[0].in_coef_l; case 0x786: return spu2->c[0].in_coef_r; case 0x788: return spu2->c[1].mvoll; case 0x78a: return spu2->c[1].mvolr; case 0x78c: return spu2->c[1].evoll; case 0x78e: return spu2->c[1].evolr; case 0x790: return spu2->c[1].avoll; case 0x792: return spu2->c[1].avolr; case 0x794: return spu2->c[1].bvoll; case 0x796: return spu2->c[1].bvolr; case 0x798: return spu2->c[1].mvolxl; case 0x79a: return spu2->c[1].mvolxr; case 0x79c: return spu2->c[1].iir_alpha; case 0x79e: return spu2->c[1].acc_coef_a; case 0x7a0: return spu2->c[1].acc_coef_b; case 0x7a2: return spu2->c[1].acc_coef_c; case 0x7a4: return spu2->c[1].acc_coef_d; case 0x7a6: return spu2->c[1].iir_coef; case 0x7a8: return spu2->c[1].fb_alpha; case 0x7aa: return spu2->c[1].fb_x; case 0x7ac: return spu2->c[1].in_coef_l; case 0x7ae: return spu2->c[1].in_coef_r; case 0x7C0: return spu2->spdif_out; case 0x7C2: return spu2->spdif_irq; case 0x7C6: return spu2->spdif_mode; case 0x7C8: return spu2->spdif_media; case 0x7CA: return spu2->spdif_copy; } printf("spu2: Unhandled register %x read\n", addr & 0x7ff); return 0; } #define SPU2_WRITEL(cr, r) { spu2->c[cr].r &= 0xffff0000; spu2->c[cr].r |= data; } #define SPU2_WRITEH(cr, r) { spu2->c[cr].r &= 0x0000ffff; spu2->c[cr].r |= data << 16; } #define SPU2_WRITEL_V(cr, vc, r) { spu2->c[cr].v[vc].r &= 0xffff0000; spu2->c[cr].v[vc].r |= data; } #define SPU2_WRITEH_V(cr, vc, r) { spu2->c[cr].v[vc].r &= 0x0000ffff; spu2->c[cr].v[vc].r |= data << 16; } void ps2_spu2_write16(struct ps2_spu2* spu2, uint32_t addr, uint64_t data) { addr &= 0x7ff; if ((addr >= 0x000 && addr <= 0x17f) || (addr >= 0x400 && addr <= 0x57f)) { int core = (addr >> 10) & 1; int voice = (addr >> 4) & 0x1f; switch (addr & 0xf) { case 0x0: spu2->c[core].v[voice].voll = data; return; case 0x2: spu2->c[core].v[voice].volr = data; return; case 0x4: spu2->c[core].v[voice].pitch = data; return; case 0x6: spu2->c[core].v[voice].adsr1 = data; return; case 0x8: spu2->c[core].v[voice].adsr2 = data; return; case 0xA: spu2->c[core].v[voice].envx = data; return; case 0xC: spu2->c[core].v[voice].volxl = data; return; case 0xE: spu2->c[core].v[voice].volxr = data; return; } } if ((addr >= 0x180 && addr <= 0x1bf) || (addr >= 0x580 && addr <= 0x5bf)) { int core = (addr >> 10) & 1; switch (addr & 0x3ff) { case 0x180: SPU2_WRITEL(core, pmon); return; case 0x182: SPU2_WRITEH(core, pmon); return; case 0x184: SPU2_WRITEL(core, non); return; case 0x186: SPU2_WRITEH(core, non); return; case 0x188: SPU2_WRITEL(core, vmixl); return; case 0x18A: SPU2_WRITEH(core, vmixl); return; case 0x18C: SPU2_WRITEL(core, vmixel); return; case 0x18E: SPU2_WRITEH(core, vmixel); return; case 0x190: SPU2_WRITEL(core, vmixr); return; case 0x192: SPU2_WRITEH(core, vmixr); return; case 0x194: SPU2_WRITEL(core, vmixer); return; case 0x196: SPU2_WRITEH(core, vmixer); return; case 0x198: spu2->c[core].mmix = data; return; case 0x19A: spu2_write_attr(spu2, core, data); return; case 0x19C: SPU2_WRITEH(core, irqa); return; case 0x19E: SPU2_WRITEL(core, irqa); return; case 0x1A0: spu2_write_kon(spu2, core, 0, data); return; case 0x1A2: spu2_write_kon(spu2, core, 1, data); return; case 0x1A4: spu2_write_koff(spu2, core, 0, data); return; case 0x1A6: spu2_write_koff(spu2, core, 1, data); return; case 0x1A8: SPU2_WRITEH(core, tsa); return; case 0x1AA: SPU2_WRITEL(core, tsa); return; case 0x1AC: spu2_write_data(spu2, core, data); return; case 0x1AE: spu2->c[core].ctrl = data; return; case 0x1B0: spu2->c[core].admas = data; return; } } if ((addr >= 0x1c0 && addr <= 0x2df) || (addr >= 0x5c0 && addr <= 0x6df)) { int core = (addr >> 10) & 1; addr -= (core ? 0x5c0 : 0x1c0); int voice = addr / 12; switch (addr % 12) { case 0x0: SPU2_WRITEH_V(core, voice, ssa); spu2->c[core].v[voice].ssa &= 0xfffff; return; case 0x2: SPU2_WRITEL_V(core, voice, ssa); spu2->c[core].v[voice].ssa &= 0xfffff; return; case 0x4: SPU2_WRITEH_V(core, voice, lsax); spu2->c[core].v[voice].lsax &= 0xfffff; return; case 0x6: SPU2_WRITEL_V(core, voice, lsax); spu2->c[core].v[voice].lsax &= 0xfffff; return; case 0x8: SPU2_WRITEH_V(core, voice, nax); spu2->c[core].v[voice].nax &= 0xfffff; return; case 0xA: SPU2_WRITEL_V(core, voice, nax); spu2->c[core].v[voice].nax &= 0xfffff; return; } } if ((addr >= 0x2e0 && addr <= 0x347) || (addr >= 0x6e0 && addr <= 0x747)) { int core = (addr >> 10) & 1; switch (addr & 0x3ff) { case 0x2E0: SPU2_WRITEH(core, esa); /* To-do: Reverb spu2->c[core].eaddr = 0; */ return; case 0x2E2: SPU2_WRITEL(core, esa); /* To-do: Reverb spu2->c[core].eaddr = 0; */ return; case 0x2E4: SPU2_WRITEH(core, fb_src_a); return; case 0x2E6: SPU2_WRITEL(core, fb_src_a); return; case 0x2E8: SPU2_WRITEH(core, fb_src_b); return; case 0x2EA: SPU2_WRITEL(core, fb_src_b); return; case 0x2EC: SPU2_WRITEH(core, iir_dest_a0); return; case 0x2EE: SPU2_WRITEL(core, iir_dest_a0); return; case 0x2F0: SPU2_WRITEH(core, iir_dest_a1); return; case 0x2F2: SPU2_WRITEL(core, iir_dest_a1); return; case 0x2F4: SPU2_WRITEH(core, acc_src_a0); return; case 0x2F6: SPU2_WRITEL(core, acc_src_a0); return; case 0x2F8: SPU2_WRITEH(core, acc_src_a1); return; case 0x2FA: SPU2_WRITEL(core, acc_src_a1); return; case 0x2FC: SPU2_WRITEH(core, acc_src_b0); return; case 0x2FE: SPU2_WRITEL(core, acc_src_b0); return; case 0x300: SPU2_WRITEH(core, acc_src_b1); return; case 0x302: SPU2_WRITEL(core, acc_src_b1); return; case 0x304: SPU2_WRITEH(core, iir_src_a0); return; case 0x306: SPU2_WRITEL(core, iir_src_a0); return; case 0x308: SPU2_WRITEH(core, iir_src_a1); return; case 0x30A: SPU2_WRITEL(core, iir_src_a1); return; case 0x30C: SPU2_WRITEH(core, iir_dest_b0); return; case 0x30E: SPU2_WRITEL(core, iir_dest_b0); return; case 0x310: SPU2_WRITEH(core, iir_dest_b1); return; case 0x312: SPU2_WRITEL(core, iir_dest_b1); return; case 0x314: SPU2_WRITEH(core, acc_src_c0); return; case 0x316: SPU2_WRITEL(core, acc_src_c0); return; case 0x318: SPU2_WRITEH(core, acc_src_c1); return; case 0x31A: SPU2_WRITEL(core, acc_src_c1); return; case 0x31C: SPU2_WRITEH(core, acc_src_d0); return; case 0x31E: SPU2_WRITEL(core, acc_src_d0); return; case 0x320: SPU2_WRITEH(core, acc_src_d1); return; case 0x322: SPU2_WRITEL(core, acc_src_d1); return; case 0x324: SPU2_WRITEH(core, iir_src_b1); return; case 0x326: SPU2_WRITEL(core, iir_src_b1); return; case 0x328: SPU2_WRITEH(core, iir_src_b0); return; case 0x32A: SPU2_WRITEL(core, iir_src_b0); return; case 0x32C: SPU2_WRITEH(core, mix_dest_a0); return; case 0x32E: SPU2_WRITEL(core, mix_dest_a0); return; case 0x330: SPU2_WRITEH(core, mix_dest_a1); return; case 0x332: SPU2_WRITEL(core, mix_dest_a1); return; case 0x334: SPU2_WRITEH(core, mix_dest_b0); return; case 0x336: SPU2_WRITEL(core, mix_dest_b0); return; case 0x338: SPU2_WRITEH(core, mix_dest_b1); return; case 0x33A: SPU2_WRITEL(core, mix_dest_b1); return; case 0x33C: SPU2_WRITEH(core, eea); /* To-do: Reverb spu2->c[core].eaddr = 0; */ return; case 0x33E: SPU2_WRITEL(core, eea); /* To-do: Reverb spu2->c[core].eaddr = 0; */ return; case 0x340: SPU2_WRITEH(core, endx); return; case 0x342: SPU2_WRITEL(core, endx); return; case 0x344: spu2->c[core].stat = data; return; case 0x346: spu2->c[core].ends = data; return; } } // Misc. switch (addr & 0x7ff) { case 0x760: spu2->c[0].mvoll = data; return; case 0x762: spu2->c[0].mvolr = data; return; case 0x764: spu2->c[0].evoll = data; return; case 0x766: spu2->c[0].evolr = data; return; case 0x768: spu2->c[0].avoll = data; return; case 0x76A: spu2->c[0].avolr = data; return; case 0x76C: spu2->c[0].bvoll = data; return; case 0x76E: spu2->c[0].bvolr = data; return; case 0x770: spu2->c[0].mvolxl = data; return; case 0x772: spu2->c[0].mvolxr = data; return; case 0x774: spu2->c[0].iir_alpha = data; return; case 0x776: spu2->c[0].acc_coef_a = data; return; case 0x778: spu2->c[0].acc_coef_b = data; return; case 0x77A: spu2->c[0].acc_coef_c = data; return; case 0x77C: spu2->c[0].acc_coef_d = data; return; case 0x77E: spu2->c[0].iir_coef = data; return; case 0x780: spu2->c[0].fb_alpha = data; return; case 0x782: spu2->c[0].fb_x = data; return; case 0x784: spu2->c[0].in_coef_l = data; return; case 0x786: spu2->c[0].in_coef_r = data; return; case 0x788: spu2->c[1].mvoll = data; return; case 0x78a: spu2->c[1].mvolr = data; return; case 0x78c: spu2->c[1].evoll = data; return; case 0x78e: spu2->c[1].evolr = data; return; case 0x790: spu2->c[1].avoll = data; return; case 0x792: spu2->c[1].avolr = data; return; case 0x794: spu2->c[1].bvoll = data; return; case 0x796: spu2->c[1].bvolr = data; return; case 0x798: spu2->c[1].mvolxl = data; return; case 0x79a: spu2->c[1].mvolxr = data; return; case 0x79c: spu2->c[1].iir_alpha = data; return; case 0x79e: spu2->c[1].acc_coef_a = data; return; case 0x7a0: spu2->c[1].acc_coef_b = data; return; case 0x7a2: spu2->c[1].acc_coef_c = data; return; case 0x7a4: spu2->c[1].acc_coef_d = data; return; case 0x7a6: spu2->c[1].iir_coef = data; return; case 0x7a8: spu2->c[1].fb_alpha = data; return; case 0x7aa: spu2->c[1].fb_x = data; return; case 0x7ac: spu2->c[1].in_coef_l = data; return; case 0x7ae: spu2->c[1].in_coef_r = data; return; case 0x7C0: spu2->spdif_out = data; return; case 0x7C2: spu2->spdif_irq = data; return; case 0x7C6: spu2->spdif_mode = data; return; case 0x7C8: spu2->spdif_media = data; return; case 0x7CA: spu2->spdif_copy = data; return; } printf("spu2: Unhandled register %x write (%04lx)\n", addr & 0x7ff, data); } void ps2_spu2_destroy(struct ps2_spu2* spu2) { // uint32_t size = ftell(output) - 8; // struct wav_hdr hdr; // hdr.riff[0] = 'R'; // hdr.riff[1] = 'I'; // hdr.riff[2] = 'F'; // hdr.riff[3] = 'F'; // hdr.size = size; // hdr.wave[0] = 'W'; // hdr.wave[1] = 'A'; // hdr.wave[2] = 'V'; // hdr.wave[3] = 'E'; // hdr.fmt[0] = 'f'; // hdr.fmt[1] = 'm'; // hdr.fmt[2] = 't'; // hdr.fmt[3] = ' '; // hdr.block_size = 16; // hdr.audio_format = 1; // hdr.num_channels = 2; // hdr.samplerate = 48000; // hdr.bits_per_sample = 16; // hdr.bytes_per_block = 4; // hdr.bytes_per_sec = 48000 * 4; // struct wav_chunk chunk; // chunk.id[0] = 'd'; // chunk.id[1] = 'a'; // chunk.id[2] = 't'; // chunk.id[3] = 'a'; // chunk.size = chunk_size; // fseek(output, 0, SEEK_SET); // fwrite(&hdr, sizeof(struct wav_hdr), 1, output); // fwrite(&chunk, sizeof(struct wav_chunk), 1, output); // fflush(output); // fclose(output); free(spu2); } static const struct spu2_sample silence = { .u32 = 0 }; static const int ps_adpcm_coefs_i[5][2] = { { 0 , 0 }, { 60 , 0 }, { 115 , -52 }, { 98 , -55 }, { 122 , -60 }, }; void spu2_decode_adpcm_block(struct ps2_spu2* spu2, struct spu2_voice* v) { uint16_t hdr = spu2->ram[v->nax]; // if (v->nax == spu2->c[0].irqa || v->nax == spu2->c[1].irqa) // ps2_iop_intc_irq(spu2->intc, IOP_INTC_SPU2); v->loop_end = (hdr >> 8) & 1; v->loop = (hdr >> 9) & 1; v->loop_start = (hdr >> 10) & 1; // printf("spu2: start=%d loop=%d end=%d\n", v->loop_start, v->loop, v->loop_end); int shift_factor = (hdr & 0xf); int coef_index = ((hdr >> 4) & 0x7); if (coef_index > 4) { // printf("spu2: Invalid ADPCM coefficient index %d\n", coef_index); coef_index = 4; } int32_t f0 = ps_adpcm_coefs_i[coef_index][0]; int32_t f1 = ps_adpcm_coefs_i[coef_index][1]; for (int i = 0; i < 28; i++) { int sh = (i & 3) * 4; uint32_t addr = v->nax + 1 + (i >> 2); uint16_t n = (spu2->ram[addr] & (0xf << sh)) >> sh; // Don't send SPU2 IRQs // if (addr == spu2->c[0].irqa || addr == spu2->c[1].irqa) // ps2_iop_intc_irq(spu2->intc, IOP_INTC_SPU2); // Sign extend t int32_t t = (int16_t)((n << 12) & 0xf000) >> shift_factor; t += (f0 * v->h[0] + f1 * v->h[1]) >> 6; t = (t < INT16_MIN) ? INT16_MIN : ((t > INT16_MAX) ? INT16_MAX : t); v->h[1] = v->h[0]; v->h[0] = t; v->buf[i] = t; // printf("spu2: buf[%d]=%04x n=%04x nax=%08x\n", i, s, n, v->nax); } } #define PHASE v->adsr_phase #define CYCLES v->adsr_cycles #define EXPONENTIAL v->adsr_mode #define DECREASE v->adsr_dir #define SHIFT v->adsr_shift #define STEP v->adsr_step #define LEVEL_STEP v->adsr_pending_step #define LEVEL v->envx #define CLAMP(v, l, h) (((v) <= (l)) ? (l) : (((v) >= (h)) ? (h) : (v))) #define MAX(a, b) ((a) > (b) ? (a) : (b)) enum { ADSR_ATTACK, ADSR_DECAY, ADSR_SUSTAIN, ADSR_RELEASE, ADSR_END }; void adsr_calculate_values(struct ps2_spu2* spu2, struct spu2_voice* v) { CYCLES = 1 << MAX(0, SHIFT - 11); LEVEL_STEP = STEP << MAX(0, 11 - SHIFT); if (EXPONENTIAL && (LEVEL > 0x6000) && !DECREASE) CYCLES *= 4; if (EXPONENTIAL && DECREASE) LEVEL_STEP = (LEVEL_STEP * LEVEL) >> 15; v->adsr_cycles_reload = CYCLES; } void adsr_load_attack(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v) { v->adsr_phase = ADSR_ATTACK; v->adsr_mode = (v->adsr1 >> 15) & 1; v->adsr_shift = (v->adsr1 >> 10) & 0x1f; v->adsr_step = 7 - ((v->adsr1 >> 8) & 3); v->adsr_dir = 0; v->envx = 0; adsr_calculate_values(spu2, v); // printf("adsr: attack mode=%d shift=%d step=%d dir=%d envx=%d cycles=%d level_step=%d\n", // v->adsr_mode, // v->adsr_shift, // v->adsr_step, // v->adsr_dir, // v->envx, // CYCLES, // LEVEL_STEP // ); } void adsr_load_decay(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v) { v->adsr_phase = ADSR_DECAY; v->adsr_mode = 1; v->adsr_dir = 1; v->adsr_shift = (v->adsr1 >> 4) & 0xf; v->adsr_step = -8; v->envx = 0x7fff; adsr_calculate_values(spu2, v); } void adsr_load_sustain(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v) { v->adsr_phase = ADSR_SUSTAIN; v->adsr_mode = (v->adsr2 >> 15) & 1; v->adsr_dir = (v->adsr2 >> 14) & 1; v->adsr_shift = (v->adsr2 >> 8) & 0x1f; v->adsr_step = (v->adsr2 >> 6) & 3; v->adsr_step = v->adsr_dir ? (-8 + v->adsr_step) : (7 - v->adsr_step); v->envx = v->adsr_sustain_level; adsr_calculate_values(spu2, v); } void adsr_load_release(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v, int i) { v->adsr_phase = ADSR_RELEASE; v->adsr_mode = (v->adsr2 >> 5) & 1; v->adsr_dir = 1; v->adsr_shift = v->adsr2 & 0x1f; v->adsr_step = -8; c->endx |= 1u << i; adsr_calculate_values(spu2, v); } void spu2_handle_adsr(struct ps2_spu2* spu2, struct spu2_core* c, struct spu2_voice* v) { if (CYCLES) { CYCLES -= 1; return; } int level = v->envx; level += LEVEL_STEP; switch (v->adsr_phase) { case ADSR_ATTACK: { level = CLAMP(level, 0x0000, 0x7fff); if (level == 0x7fff) adsr_load_decay(spu2, c, v); } break; case ADSR_DECAY: { level = CLAMP(level, 0x0000, 0x7fff); if (level <= v->adsr_sustain_level) adsr_load_sustain(spu2, c, v); } break; case ADSR_SUSTAIN: { level = CLAMP(level, 0x0000, 0x7fff); /* Not stopped automatically, need to KOFF */ } break; case ADSR_RELEASE: { level = CLAMP(level, 0x0000, 0x7fff); if (!level) { PHASE = ADSR_END; CYCLES = 0; // v->nax = 0; // v->ssa = 0; // v->lsax = 0; v->envx = 0; v->playing = 0; } } break; case ADSR_END: { level = 0; v->envx = 0; v->playing = 0; } break; } v->envx = level; adsr_calculate_values(spu2, v); CYCLES = v->adsr_cycles_reload; } #undef PHASE #undef CYCLES #undef MODE #undef DIR #undef SHIFT #undef STEP #undef PENDING_STEP #undef CLAMP #undef MAX struct spu2_sample spu2_get_voice_sample(struct ps2_spu2* spu2, int cr, int vc) { // if (!spu2->c[cr].v[vc].playing) // return silence; struct spu2_core* c = &spu2->c[cr]; struct spu2_voice* v = &c->v[vc]; struct spu2_sample s; int sample_index = v->counter >> 12; spu2_handle_adsr(spu2, c, v); if (sample_index > 27) { sample_index -= 28; v->counter &= 0xfff; v->counter |= sample_index << 12; if (v->loop_start) v->lsax = v->nax; v->nax += 8; v->nax &= 0xfffff; spu2_check_irq(spu2, v->nax); if (v->loop_end) { if (!v->loop) { v->envx = 0; adsr_load_release(spu2, c, v, vc); } else { v->nax = v->lsax; spu2_check_irq(spu2, v->nax); } } spu2_decode_adpcm_block(spu2, v); } if (v->prev_sample_index != sample_index) { v->s[3] = v->s[2]; v->s[2] = v->s[1]; v->s[1] = v->s[0]; } v->s[0] = v->buf[sample_index]; // Apply 4-point Gaussian interpolation uint8_t gauss_index = (v->counter >> 4) & 0xff; int32_t g0 = g_spu_gauss_table[0x0ff - gauss_index]; int32_t g1 = g_spu_gauss_table[0x1ff - gauss_index]; int32_t g2 = g_spu_gauss_table[0x100 + gauss_index]; int32_t g3 = g_spu_gauss_table[0x000 + gauss_index]; int32_t out; out = (g0 * v->s[3]) >> 15; out += (g1 * v->s[2]) >> 15; out += (g2 * v->s[1]) >> 15; out += (g3 * v->s[0]) >> 15; // Output voice 1 and 3 to the capture buffers if (vc == 1) { uint16_t addr = c->cb_out1_addr + (cr ? 0xc00 : 0x400); c->cb_out1_addr = (c->cb_out1_addr + 1) & 0x1ff; spu2->ram[addr] = out; spu2_check_irq(spu2, addr); } if (vc == 3) { uint16_t addr = c->cb_out3_addr + (cr ? 0xe00 : 0x600); c->cb_out3_addr = (c->cb_out3_addr + 1) & 0x1ff; spu2->ram[addr] = out; spu2_check_irq(spu2, addr); } s.s16[0] = (out * v->voll) >> 15; s.s16[1] = (out * v->volr) >> 15; s.s16[0] = ((int32_t)s.s16[0] * v->envx) >> 15; s.s16[1] = ((int32_t)s.s16[1] * v->envx) >> 15; v->counter += v->pitch; v->prev_sample_index = sample_index; return s; } static inline struct spu2_sample spu2_get_adma_sample(struct ps2_spu2* spu2, int c) { if (spu2->c[c].memin_write_addr < 0x200) { return silence; } spu2->c[c].adma_playing = 1; struct spu2_sample s = silence; // 32-bit HIFI PCM mode if (spu2->spdif_out & 4) { int32_t left = *(int32_t*)(&spu2->ram[0x2400 + spu2->c[c].memin_read_addr]); int32_t right = *(int32_t*)(&spu2->ram[0x2600 + spu2->c[c].memin_read_addr]); s.s16[0] = (int16_t)((left >> 16) & 0xffff); s.s16[1] = (int16_t)((right >> 16) & 0xffff); spu2->c[c].memin_read_addr++; spu2->c[c].memin_read_addr++; } else { s.s16[0] = spu2->ram[(c ? 0x2400 : 0x2000) + spu2->c[c].memin_read_addr]; s.s16[1] = spu2->ram[(c ? 0x2600 : 0x2200) + spu2->c[c].memin_read_addr]; spu2->c[c].memin_read_addr++; } if (spu2->c[c].memin_read_addr == 0x100) { spu2->c[c].memin_read_addr = 0; spu2->c[c].memin_write_addr = 0; spu2->c[c].adma_playing = 0; // Request more data if (c == 0) { iop_dma_handle_spu1_adma(spu2->dma); } else { iop_dma_handle_spu2_adma(spu2->dma); } } return s; } struct spu2_sample ps2_spu2_get_adma_sample(struct ps2_spu2* spu2, int c) { return spu2_get_adma_sample(spu2, c); } struct spu2_sample ps2_spu2_get_sample(struct ps2_spu2* spu2, int adma_enable) { struct spu2_sample s = silence; s.u16[0] = 0; s.u16[1] = 0; // ADMA struct spu2_sample c0_adma = spu2_get_adma_sample(spu2, 0); struct spu2_sample c1_adma = spu2_get_adma_sample(spu2, 1); // if (output) { // if (spu2->c[0].adma_playing) { // chunk_size += sizeof(int16_t) * 2; // fwrite(&c0_adma.s16, sizeof(int16_t), 2, output); // } // if (spu2->c[1].adma_playing) { // chunk_size += sizeof(int16_t) * 2; // fwrite(&c1_adma.s16, sizeof(int16_t), 2, output); // } // } if (adma_enable) { s.s16[0] += c0_adma.s16[0]; s.s16[1] += c0_adma.s16[1]; s.s16[0] += c1_adma.s16[0]; s.s16[1] += c1_adma.s16[1]; } for (int i = 0; i < 24; i++) { struct spu2_sample c0 = spu2_get_voice_sample(spu2, 0, i); struct spu2_sample c1 = spu2_get_voice_sample(spu2, 1, i); s.s16[0] += c0.s16[0] << 1; s.s16[1] += c0.s16[1] << 1; s.s16[0] += c1.s16[0] << 1; s.s16[1] += c1.s16[1] << 1; } return s; } struct spu2_sample ps2_spu2_get_voice_sample(struct ps2_spu2* spu2, int c, int v) { return spu2_get_voice_sample(spu2, c, v); } int spu2_is_adma_active(struct ps2_spu2* spu2, int c) { return spu2->c[c].memin_write_addr >= 0x400; } void spu2_start_adma(struct ps2_spu2* spu2, int c) { spu2->c[c].adma_playing = 0; spu2->c[c].memin_read_addr = 0; spu2->c[c].memin_write_addr = 0; } ================================================ FILE: src/iop/spu2.h ================================================ #ifndef SPU2_H #define SPU2_H #ifdef __cplusplus extern "C" { #endif #include #include "scheduler.h" #include "intc.h" #include "dma.h" #define SPU2_RAM_SIZE 0x100000 // 2 MB /* Memory ranges: 1f900000-1f90017f CORE0 Voice settings 1f900180-1f9001b1 CORE0 Common settings 1f9001c0-1f9002df CORE0 Voice address settings 1f9002e0-1f900347 CORE0 Misc. settings 1f900400-1f90057f CORE1 Voice settings 1f900580-1f9005b1 CORE1 Common settings 1f9005c0-1f9006df CORE1 Voice address settings 1f9006e0-1f900747 CORE1 Reverb/Misc. settings 1f900760-1f900787 CORE0 Volume/Reverb settings 1f900788-1f9007af CORE1 Volume/Reverb settings 1f9007c0-1f9007cf S/PDIF settings Breakdown: 1f900000-1f90017f CORE0 Voice settings: 1f900XX0 Voice X (0-23) VOLL (S) voice volume (left) 1f900XX2 Voice X (0-23) VOLR (S) voice volume (right) 1f900XX4 Voice X (0-23) PITCH (S) voice pitch 1f900XX6 Voice X (0-23) ADSR1 (S) voice envelope (AR, DR, SL) 1f900XX8 Voice X (0-23) ADSR2 (S) voice envelope (SR, RR) 1f900XXa Voice X (0-23) ENVX (S) voice envelope (current value) 1f900XXc Voice X (0-23) VOLXL (S) voice volume (current value left) 1f900XXe Voice X (0-23) VOLXR (S) voice volume (current value right) 1f900180-1f9001b1 CORE0 Common settings 1f900180 PMON (P) pitch modulation on 1f900184 NON (P) noise generator on 1f900188 VMIXL (P) voice output mixing (dry left) 1f90018C VMIXEL (P) voice output mixing (wet left) 1f900190 VMIXR (P) voice output mixing (dry right) 1f900194 VMIXER (P) voice output mixing (wet right) 1f900198 MMIX (P) output type after voice mixing 1f90019A ATTR (P) core attributes 1f90019C IRQA (P) IRQ address 1f9001A0 KON (P) key on (start voice sound generation) 1f9001A4 KOFF (P) key off (end voice sound generation) 1f9001A8 TSA (P) DMA transfer start address 1f9001AC DATA (S) DMA data register 1f9001AE CTRL (S) DMA control register 1f9001B0 ADMAS (S) AutoDMA (ADMA) status 1f9001c0-1f9002df CORE0 Voice address settings 1f9002e0-1f900347 CORE0 Misc. settings 1f900400-1f90057f CORE1 Voice settings 1f900580-1f9005b1 CORE1 Common settings 1f9005c0-1f9006df CORE1 Voice address settings 1f9006e0-1f900747 CORE1 Reverb/Misc. settings 1f900760-1f900787 CORE0 Volume/Reverb settings 1f900788-1f9007af CORE1 Volume/Reverb settings 1f9007c0-1f9007cf S/PDIF settings */ struct spu2_voice { uint16_t voll; uint16_t volr; uint16_t pitch; uint16_t adsr1; uint16_t adsr2; uint16_t envx; uint16_t volxl; uint16_t volxr; uint32_t ssa; uint32_t lsax; uint32_t nax; // Internal stuff int playing; unsigned int counter; int32_t h[2]; int16_t buf[28]; int loop_start; int loop; int loop_end; int prev_sample_index; int loop_addr_specified; int16_t s[4]; // Envelope int adsr_cycles_left; int adsr_phase; int adsr_cycles_reload; int adsr_cycles; int adsr_mode; int adsr_dir; int adsr_shift; int adsr_step; int adsr_pending_step; int adsr_sustain_level; }; struct spu2_core { struct spu2_voice v[24]; int words_written; uint32_t pmon; uint32_t non; uint32_t vmixl; uint32_t vmixel; uint32_t vmixr; uint32_t vmixer; uint16_t mmix; uint16_t attr; uint32_t irqa; uint32_t kon; uint32_t koff; uint32_t tsa; uint16_t data; uint16_t ctrl; uint16_t admas; uint32_t esa; uint32_t fb_src_a; uint32_t fb_src_b; uint32_t iir_dest_a0; uint32_t iir_dest_a1; uint32_t acc_src_a0; uint32_t acc_src_a1; uint32_t acc_src_b0; uint32_t acc_src_b1; uint32_t iir_src_a0; uint32_t iir_src_a1; uint32_t iir_dest_b0; uint32_t iir_dest_b1; uint32_t acc_src_c0; uint32_t acc_src_c1; uint32_t acc_src_d0; uint32_t acc_src_d1; uint32_t iir_src_b1; uint32_t iir_src_b0; uint32_t mix_dest_a0; uint32_t mix_dest_a1; uint32_t mix_dest_b0; uint32_t mix_dest_b1; uint32_t eea; uint32_t endx; uint16_t stat; uint16_t ends; uint16_t mvoll; uint16_t mvolr; uint16_t evoll; uint16_t evolr; uint16_t avoll; uint16_t avolr; uint16_t bvoll; uint16_t bvolr; uint16_t mvolxl; uint16_t mvolxr; uint16_t iir_alpha; uint16_t acc_coef_a; uint16_t acc_coef_b; uint16_t acc_coef_c; uint16_t acc_coef_d; uint16_t iir_coef; uint16_t fb_alpha; uint16_t fb_x; uint16_t in_coef_l; uint16_t in_coef_r; // ADMA uint32_t memin_write_addr; uint32_t memin_read_addr; int adma_playing; int16_t adma_ringbuf[4096]; uint32_t adma_ringbuf_write_idx; uint32_t adma_ringbuf_read_idx; int adma_ringbuf_full; // Capture buffers uint16_t cb_out1_addr; uint16_t cb_out3_addr; }; struct ps2_spu2 { // 2 MB uint16_t ram[0x100000]; struct spu2_core c[2]; // CORE1 S/PDIF settings uint32_t spdif_out; uint32_t spdif_mode; uint32_t spdif_media; uint32_t spdif_copy; int spdif_irq; struct ps2_iop_dma* dma; struct ps2_iop_intc* intc; struct sched_state* sched; }; struct spu2_sample { union { uint32_t u32; uint16_t u16[2]; int16_t s16[2]; }; }; struct ps2_spu2* ps2_spu2_create(void); void ps2_spu2_init(struct ps2_spu2* spu2, struct ps2_iop_dma* dma, struct ps2_iop_intc* intc, struct sched_state* sched); uint64_t ps2_spu2_read16(struct ps2_spu2* spu2, uint32_t addr); void ps2_spu2_write16(struct ps2_spu2* spu2, uint32_t addr, uint64_t data); void ps2_spu2_destroy(struct ps2_spu2* spu2); struct spu2_sample ps2_spu2_get_sample(struct ps2_spu2* spu, int adma_enable); struct spu2_sample ps2_spu2_get_voice_sample(struct ps2_spu2* spu2, int c, int v); struct spu2_sample ps2_spu2_get_adma_sample(struct ps2_spu2* spu2, int c); void spu2_start_adma(struct ps2_spu2* spu2, int c); int spu2_is_adma_active(struct ps2_spu2* spu2, int c); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/timers.c ================================================ #include #include #include #include "timers.h" #include "intc.h" #include "scheduler.h" struct ps2_iop_timers* ps2_iop_timers_create(void) { return malloc(sizeof(struct ps2_iop_timers)); } void ps2_iop_timers_init(struct ps2_iop_timers* timers, struct ps2_iop_intc* intc, struct sched_state* sched) { memset(timers, 0, sizeof(struct ps2_iop_timers)); timers->intc = intc; timers->sched = sched; } void ps2_iop_timers_destroy(struct ps2_iop_timers* timers) { free(timers); } static inline uint32_t timer_get_irq_mask(int t) { switch (t) { case 0: return IOP_INTC_TIMER0; case 1: return IOP_INTC_TIMER1; case 2: return IOP_INTC_TIMER2; case 3: return IOP_INTC_TIMER3; case 4: return IOP_INTC_TIMER4; case 5: return IOP_INTC_TIMER5; } return 0; } void iop_timer_tick(struct ps2_iop_timers* timers, int i) { struct iop_timer* t = &timers->timer[i]; uint32_t prev = t->counter; // To-do: Breaks Crazy Taxi (USA) if (i == 1) { if (t->use_ext) { if (t->internal != 90) { ++t->internal; } else { ++t->counter; t->internal = 0; } } else { t->counter += 2; } } else { if (i == 4) { switch (t->t4_prescaler) { case 0: t->counter += 2; break; case 1: { if (t->internal != 128) { ++t->internal; } else { t->counter += 1; t->internal = 0; } } break; } } else { t->counter += 2; } } if (t->counter >= t->target && prev < t->target) { // printf("iop: Timer 5 reached target %08x <= %08x\n", t->counter, t->target); t->cmp_irq_set = 1; if (t->cmp_irq && t->irq_en) { // printf(" timer %d irq\n", i); ps2_iop_intc_irq(timers->intc, timer_get_irq_mask(i)); if (!t->rep_irq) { t->irq_en = 0; } else { if (t->levl) { t->irq_en = !t->irq_en; } } if (t->irq_reset) { t->counter = 0; } // printf("iop: Timer %d compare IRQ cnt=%d tgt=%d rep=%d levl=%d irq_en=%d irq_reset=%d\n", i, // t->counter, // t->target, // t->rep_irq, // t->levl, // t->irq_en, // t->irq_reset // ); } } uint64_t ovf = (i < 3) ? 0xffff : 0xffffffff; if (t->counter > ovf) { t->ovf_irq_set = 1; if (t->ovf_irq && t->irq_en) { // printf("iop: Timer %d overflow IRQ rep=%d levl=%d irq_en=%d irq_reset=%d\n", i, // t->rep_irq, // t->levl, // t->irq_en, // t->irq_reset // ); ps2_iop_intc_irq(timers->intc, timer_get_irq_mask(i)); if (!t->rep_irq) { t->irq_en = 0; } else { if (t->levl) { t->irq_en = !t->irq_en; } } if (t->irq_reset) { t->counter = 0; } } t->counter -= ovf; } } void ps2_iop_timers_tick(struct ps2_iop_timers* timers) { for (int i = 0; i < 6; i++) { iop_timer_tick(timers, i); } } uint32_t iop_timer_handle_mode_read(struct ps2_iop_timers* timers, int i) { uint32_t r = timers->timer[i].mode; timers->timer[i].cmp_irq_set = 0; timers->timer[i].ovf_irq_set = 0; // timers->timer[i].irq_en = 1; // if (i == 5) // printf("iop: Timer %d mode read %08x -> %08x\n", i, r, timers->timer[i].mode); return r; } uint64_t ps2_iop_timers_read32(struct ps2_iop_timers* timers, uint32_t addr) { switch (addr & 0xfff) { case 0x100: return timers->timer[0].counter; case 0x110: return timers->timer[1].counter; case 0x120: return timers->timer[2].counter; case 0x480: return timers->timer[3].counter; case 0x490: return timers->timer[4].counter; case 0x4a0: return timers->timer[5].counter; case 0x108: return timers->timer[0].target; case 0x118: return timers->timer[1].target; case 0x128: return timers->timer[2].target; case 0x488: return timers->timer[3].target; case 0x498: return timers->timer[4].target; case 0x4a8: return timers->timer[5].target; case 0x104: return iop_timer_handle_mode_read(timers, 0); case 0x114: return iop_timer_handle_mode_read(timers, 1); case 0x124: return iop_timer_handle_mode_read(timers, 2); case 0x484: return iop_timer_handle_mode_read(timers, 3); case 0x494: return iop_timer_handle_mode_read(timers, 4); case 0x4a4: return iop_timer_handle_mode_read(timers, 5); } return 0; } void iop_timer_handle_mode_write(struct ps2_iop_timers* timers, int t, uint64_t data) { struct iop_timer* timer = &timers->timer[t]; timer->counter = 0; timer->mode |= 0x400; timer->mode &= 0x1c00; timer->mode |= data & 0xe3ff; if (timer->counter >= timer->target) { timer->cmp_irq_set = 1; // ps2_iop_intc_irq(timers->intc, timer_get_irq_mask(t)); } // 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", // t, timers->timer[t].mode, // data, // timers->timer[t].gate_en, // timers->timer[t].gate_mode, // timers->timer[t].irq_reset, // timers->timer[t].cmp_irq, // timers->timer[t].ovf_irq, // timers->timer[t].rep_irq, // timers->timer[t].levl, // timers->timer[t].use_ext, // timers->timer[t].irq_en, // timers->timer[t].t4_prescaler // ); /* To-do: Schedule timer interrupts for better performance */ } void iop_timer_handle_target_write(struct ps2_iop_timers* timers, int t, uint64_t data) { struct iop_timer* timer = &timers->timer[t]; timer->target = data; if (!timer->levl) { timer->irq_en = 1; } if (t != 5) return; // printf("iop: Timer %d target write %08x levl=%d mode=%08x counter=%08x\n", t, data, timer->levl, timer->mode, timer->counter); } void ps2_iop_timers_write32(struct ps2_iop_timers* timers, uint32_t addr, uint64_t data) { switch (addr & 0xfff) { case 0x100: /* printf("iop: Timer 0 counter write %08x prev=%08x\n", data, timers->timer[0].counter); */ timers->timer[0].counter = data; break; case 0x110: /* printf("iop: Timer 1 counter write %08x prev=%08x\n", data, timers->timer[1].counter); */ timers->timer[1].counter = data; break; case 0x120: /* printf("iop: Timer 2 counter write %08x prev=%08x\n", data, timers->timer[2].counter); */ timers->timer[2].counter = data; break; case 0x480: /* printf("iop: Timer 3 counter write %08x prev=%08x\n", data, timers->timer[3].counter); */ timers->timer[3].counter = data; break; case 0x490: /* printf("iop: Timer 4 counter write %08x prev=%08x\n", data, timers->timer[4].counter); */ timers->timer[4].counter = data; break; case 0x4a0: /* printf("iop: Timer 5 counter write %08x prev=%08x\n", data, timers->timer[5].counter); */ timers->timer[5].counter = data; break; case 0x108: iop_timer_handle_target_write(timers, 0, data); break; case 0x118: iop_timer_handle_target_write(timers, 1, data); break; case 0x128: iop_timer_handle_target_write(timers, 2, data); break; case 0x488: iop_timer_handle_target_write(timers, 3, data); break; case 0x498: iop_timer_handle_target_write(timers, 4, data); break; case 0x4a8: iop_timer_handle_target_write(timers, 5, data); break; case 0x104: iop_timer_handle_mode_write(timers, 0, data); break; case 0x114: iop_timer_handle_mode_write(timers, 1, data); break; case 0x124: iop_timer_handle_mode_write(timers, 2, data); break; case 0x484: iop_timer_handle_mode_write(timers, 3, data); break; case 0x494: iop_timer_handle_mode_write(timers, 4, data); break; case 0x4a4: iop_timer_handle_mode_write(timers, 5, data); break; } } ================================================ FILE: src/iop/timers.h ================================================ #ifndef IOP_TIMER_H #define IOP_TIMER_H #ifdef __cplusplus extern "C" { #endif #include struct iop_timer { int64_t counter; union { uint32_t mode; struct { unsigned int gate_en : 1; unsigned int gate_mode : 2; unsigned int irq_reset : 1; unsigned int cmp_irq : 1; unsigned int ovf_irq : 1; unsigned int rep_irq : 1; unsigned int levl : 1; unsigned int use_ext : 1; unsigned int t2_prescaler : 1; unsigned int irq_en : 1; unsigned int cmp_irq_set : 1; unsigned int ovf_irq_set : 1; unsigned int t4_prescaler : 1; unsigned int t5_prescaler : 1; unsigned int unused : 17; }; }; uint32_t target; int64_t internal; }; struct ps2_iop_timers { struct iop_timer timer[6]; struct ps2_iop_intc* intc; struct sched_state* sched; }; struct ps2_iop_timers* ps2_iop_timers_create(void); void ps2_iop_timers_init(struct ps2_iop_timers* timers, struct ps2_iop_intc* intc, struct sched_state* sched); void ps2_iop_timers_destroy(struct ps2_iop_timers* timers); void ps2_iop_timers_tick(struct ps2_iop_timers* timers); uint64_t ps2_iop_timers_read32(struct ps2_iop_timers* timers, uint32_t addr); void ps2_iop_timers_write32(struct ps2_iop_timers* timers, uint32_t addr, uint64_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/iop/usb.c ================================================ #include #include #include #include "usb.h" struct ps2_usb* ps2_usb_create(void) { return malloc(sizeof(struct ps2_usb)); } void ps2_usb_init(struct ps2_usb* usb) { memset(usb, 0, sizeof(struct ps2_usb)); } void ps2_usb_destroy(struct ps2_usb* usb) { free(usb); } uint64_t ps2_usb_read32(struct ps2_usb* usb, uint32_t addr) { addr &= 0xff; switch (addr) { case 0x00: printf("usb: read USB_HC_REVISION\n"); return 0; case 0x04: printf("usb: read USB_HC_CONTROL\n"); return 0; case 0x08: printf("usb: read USB_HC_COMMANDSTATUS\n"); return 0; case 0x0c: printf("usb: read USB_HC_INTERRUPTSTATUS\n"); return 0xffffffff; case 0x10: printf("usb: read USB_HC_INTERRUPTENABLE\n"); return 0xffffffff; case 0x14: printf("usb: read USB_HC_INTERRUPTDISABLE\n"); return 0; case 0x18: printf("usb: read USB_HC_HCCA\n"); return 0; case 0x1c: printf("usb: read USB_HC_PERIODCURRENTED\n"); return 0; case 0x20: printf("usb: read USB_HC_CONTROLHEADED\n"); return 0; case 0x24: printf("usb: read USB_HC_CONTROLCURRENTED\n"); return 0; case 0x28: printf("usb: read USB_HC_BULKHEADED\n"); return 0; case 0x2c: printf("usb: read USB_HC_BULKCURRENTED\n"); return 0; case 0x30: printf("usb: read USB_HC_DONEHEAD\n"); return 0; case 0x34: printf("usb: read USB_HC_FMINTERVAL\n"); return 0; case 0x38: printf("usb: read USB_HC_FMREMAINING\n"); return 0; case 0x3c: printf("usb: read USB_HC_FMNUMBER\n"); return 0; case 0x40: printf("usb: read USB_HC_PERIODICSTART\n"); return 0; case 0x44: printf("usb: read USB_HC_LSTHRESHOLD\n"); return 0; case 0x48: printf("usb: read USB_HC_RHDESCRIPTORA\n"); return 0x3F000202; case 0x4c: printf("usb: read USB_HC_RHDESCRIPTORB\n"); return 0xffff; case 0x50: printf("usb: read USB_HC_RHSTATUS\n"); return 0xffffffff; } if (addr >= 0x54 && addr <= 0x8c) { printf("usb: Read USB_HC_RHPORT%dSTATUS\n", (addr - 0x54) >> 2); return 0x150303; } printf("usb: Unhandled read at %08x\n", addr); return 0; } void ps2_usb_write32(struct ps2_usb* usb, uint32_t addr, uint64_t data) { addr &= 0xff; switch (addr) { case 0x00: printf("usb: write %08x to USB_HC_REVISION\n", data); return; case 0x04: printf("usb: write %08x to USB_HC_CONTROL\n", data); return; case 0x08: printf("usb: write %08x to USB_HC_COMMANDSTATUS\n", data); return; case 0x0c: printf("usb: write %08x to USB_HC_INTERRUPTSTATUS\n", data); return; case 0x10: printf("usb: write %08x to USB_HC_INTERRUPTENABLE\n", data); return; case 0x14: printf("usb: write %08x to USB_HC_INTERRUPTDISABLE\n", data); return; case 0x18: printf("usb: write %08x to USB_HC_HCCA\n", data); return; case 0x1c: printf("usb: write %08x to USB_HC_PERIODCURRENTED\n", data); return; case 0x20: printf("usb: write %08x to USB_HC_CONTROLHEADED\n", data); return; case 0x24: printf("usb: write %08x to USB_HC_CONTROLCURRENTED\n", data); return; case 0x28: printf("usb: write %08x to USB_HC_BULKHEADED\n", data); return; case 0x2c: printf("usb: write %08x to USB_HC_BULKCURRENTED\n", data); return; case 0x30: printf("usb: write %08x to USB_HC_DONEHEAD\n", data); return; case 0x34: printf("usb: write %08x to USB_HC_FMINTERVAL\n", data); return; case 0x38: printf("usb: write %08x to USB_HC_FMREMAINING\n", data); return; case 0x3c: printf("usb: write %08x to USB_HC_FMNUMBER\n", data); return; case 0x40: printf("usb: write %08x to USB_HC_PERIODICSTART\n", data); return; case 0x44: printf("usb: write %08x to USB_HC_LSTHRESHOLD\n", data); return; case 0x48: printf("usb: write %08x to USB_HC_RHDESCRIPTORA\n", data); return; case 0x4c: printf("usb: write %08x to USB_HC_RHDESCRIPTORB\n", data); return; case 0x50: printf("usb: write %08x to USB_HC_RHSTATUS\n", data); return; } if (addr >= 0x54 && addr <= 0x8c) { printf("usb: write %08x to USB_HC_RHPORT%dSTATUS\n", data, (addr - 0x54) >> 2); return; } printf("usb: Unhandled write at %08x (%08x)\n", addr, data); return; } ================================================ FILE: src/iop/usb.h ================================================ #ifndef USB_H #define USB_H #ifdef __cplusplus extern "C" { #endif #include #define OHCI_BASE 0x1f801600 #define OHCI_MAX_PORTS 15 #define USB_HC_REVISION 0x00 #define USB_HC_CONTROL 0x04 #define USB_HC_COMMANDSTATUS 0x08 #define USB_HC_INTERRUPTSTATUS 0x0c #define USB_HC_INTERRUPTENABLE 0x10 #define USB_HC_INTERRUPTDISABLE 0x14 #define USB_HC_HCCA 0x18 #define USB_HC_PERIODCURRENTED 0x1c #define USB_HC_CONTROLHEADED 0x20 #define USB_HC_CONTROLCURRENTED 0x24 #define USB_HC_BULKHEADED 0x28 #define USB_HC_BULKCURRENTED 0x2c #define USB_HC_DONEHEAD 0x30 #define USB_HC_FMINTERVAL 0x34 #define USB_HC_FMREMAINING 0x38 #define USB_HC_FMNUMBER 0x3c #define USB_HC_PERIODICSTART 0x40 #define USB_HC_LSTHRESHOLD 0x44 #define USB_HC_RHDESCRIPTORA 0x48 #define USB_HC_RHDESCRIPTORB 0x4c #define USB_HC_RHSTATUS 0x50 struct ps2_usb { int dummy; }; struct ps2_usb* ps2_usb_create(void); void ps2_usb_init(struct ps2_usb* usb); void ps2_usb_destroy(struct ps2_usb* usb); uint64_t ps2_usb_read32(struct ps2_usb* usb, uint32_t addr); void ps2_usb_write32(struct ps2_usb* usb, uint32_t addr, uint64_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ipu/chromtable.cpp ================================================ #include "chromtable.hpp" VLC_Entry ChromTable::table[] = { {0x0, 0, 2}, {0x1, 1, 2}, {0x2, 2, 2}, {0x6, 3, 3}, {0xE, 4, 4}, {0x1E, 5, 5}, {0x3E, 6, 6}, {0x7E, 7, 7}, {0xFE, 8, 8}, {0x1FE, 9, 9}, {0x3FE, 10, 10}, {0x3FF, 11, 10} }; unsigned int ChromTable::index_table[10] = { 0, 0, 3, 4, 5, 6, 7, 8, 9, 10, }; ChromTable::ChromTable() : VLC_Table(table, SIZE, 10, index_table) { } ================================================ FILE: src/ipu/chromtable.hpp ================================================ #ifndef CHROMTABLE_HPP #define CHROMTABLE_HPP #include "vlc_table.hpp" class ChromTable : public VLC_Table { private: static VLC_Entry table[]; static unsigned int index_table[]; constexpr static int SIZE = 12; public: ChromTable(); }; #endif // CHROMTABLE_HPP ================================================ FILE: src/ipu/codedblockpattern.cpp ================================================ #include "codedblockpattern.hpp" VLC_Entry CodedBlockPattern::table[] = { {0x7, 60, 3}, {0xD, 4, 4}, {0xC, 8, 4}, {0xB, 16, 4}, {0xA, 32, 4}, {0x13, 12, 5}, {0x12, 48, 5}, {0x11, 20, 5}, {0x10, 40, 5}, {0xF, 28, 5}, {0xE, 44, 5}, {0xD, 52, 5}, //16 {0xC, 56, 5}, {0xB, 1, 5}, {0xA, 61, 5}, {0x9, 2, 5}, //20 {0x8, 62, 5}, {0xF, 24, 6}, {0xE, 36, 6}, {0xD, 3, 6}, //24 {0xC, 63, 6}, {0x17, 5, 7}, {0x16, 9, 7}, {0x15, 17, 7}, //28 {0x14, 33, 7}, {0x13, 6, 7}, {0x12, 10, 7}, {0x11, 18, 7}, //32 {0x10, 34, 7}, {0x1F, 7, 8}, {0x1E, 11, 8}, {0x1D, 19, 8}, //36 {0x1C, 35, 8}, {0x1B, 13, 8}, {0x1A, 49, 8}, {0x19, 21, 8}, //40 {0x18, 41, 8}, {0x17, 14, 8}, {0x16, 50, 8}, {0x15, 22, 8}, //44 {0x14, 42, 8}, {0x13, 15, 8}, {0x12, 51, 8}, {0x11, 23, 8}, //48 {0x10, 43, 8}, {0xF, 25, 8}, {0xE, 37, 8}, {0xD, 26, 8}, //52 {0xC, 38, 8}, {0xB, 29, 8}, {0xA, 45, 8}, {0x9, 53, 8}, //56 {0x8, 57, 8}, {0x7, 30, 8}, {0x6, 46, 8}, {0x5, 54, 8}, //60 {0x4, 58, 8}, {0x7, 31, 9}, {0x6, 47, 9}, {0x5, 55, 9}, //64 {0x4, 59, 9}, {0x3, 27, 9}, {0x2, 39, 9}, {0x1, 0, 9} }; unsigned int CodedBlockPattern::index_table[9] = { 0, 0, 0, 1, 5, 17, 21, 29, 57, }; CodedBlockPattern::CodedBlockPattern() : VLC_Table(table, SIZE, 9, index_table) { } ================================================ FILE: src/ipu/codedblockpattern.hpp ================================================ #ifndef CODEDBLOCKPATTERN_HPP #define CODEDBLOCKPATTERN_HPP #include "vlc_table.hpp" class CodedBlockPattern : public VLC_Table { private: static VLC_Entry table[]; static unsigned int index_table[]; constexpr static int SIZE = 64; public: CodedBlockPattern(); }; #endif // CODEDBLOCKPATTERN_HPP ================================================ FILE: src/ipu/dct_coeff.cpp ================================================ #include #include #include "dct_coeff.hpp" DCT_Coeff::DCT_Coeff(VLC_Entry* table, int table_size, int max_bits, unsigned int* index_table) : VLC_Table(table, table_size, max_bits, index_table) { } bool DCT_Coeff::peek_value(IPU_FIFO &FIFO, int bits, int &bit_count, uint32_t &result) { if (bits + bit_count > 32) { } if (!FIFO.get_bits(result, bits + bit_count)) return false; result &= ~(0xFFFFFFFF << bits); bit_count += bits; return true; } ================================================ FILE: src/ipu/dct_coeff.hpp ================================================ #ifndef DCT_COEFF_HPP #define DCT_COEFF_HPP #include "vlc_table.hpp" struct RunLevelPair { int run, level; }; class DCT_Coeff : public VLC_Table { protected: constexpr static int RUN_ESCAPE = 102; public: DCT_Coeff(VLC_Entry* table, int table_size, int max_bits, unsigned int* index_table); virtual bool get_end_of_block(IPU_FIFO& FIFO, uint32_t& result) = 0; virtual bool get_skip_block(IPU_FIFO& FIFO) = 0; virtual bool get_runlevel_pair(IPU_FIFO& FIFO, RunLevelPair& pair, bool MPEG1) = 0; virtual bool get_runlevel_pair_dc(IPU_FIFO& FIFO, RunLevelPair& pair, bool MPEG1) = 0; bool peek_value(IPU_FIFO& FIFO, int bits, int& bit_count, uint32_t& result); }; #endif // DCT_COEFF_HPP ================================================ FILE: src/ipu/dct_coeff_table0.cpp ================================================ #include #include #include "dct_coeff_table0.hpp" #define printf(fmt, ...)(0) VLC_Entry DCT_Coeff_Table0::table[] = { {0x3, 0, 2}, {0x3, 1, 3}, {0x4, 2, 4}, {0x5, 3, 4}, {0x5, 4, 5}, {0x7, 5, 5}, {0x6, 6, 5}, {0x6, 7, 6}, {0x7, 8, 6}, {0x5, 9, 6}, {0x4, 10, 6}, {0x1, 15, 6}, {0x6, 11, 7}, {0x4, 12, 7}, {0x7, 13, 7}, {0x5, 14, 7}, //20 {0x26, 16, 8}, {0x21, 17, 8}, {0x25, 18, 8}, {0x24, 19, 8}, //24 {0x27, 20, 8}, {0x23, 21, 8}, {0x22, 22, 8}, {0x20, 23, 8}, //28 {0xA, 24, 10}, {0xC, 25, 10}, {0xB, 26, 10}, {0xF, 27, 10}, //32 {0x9, 28, 10}, {0xE, 29, 10}, {0xD, 30, 10}, {0x8, 31, 10}, //36 {0x1D, 32, 12}, {0x18, 33, 12}, {0x13, 34, 12}, {0x10, 35, 12}, //40 {0x1B, 36, 12}, {0x14, 37, 12}, {0x1C, 38, 12}, {0x12, 39, 12}, //44 {0x1E, 40, 12}, {0x15, 41, 12}, {0x11, 42, 12}, {0x1F, 43, 12}, //48 {0x1A, 44, 12}, {0x19, 45, 12}, {0x17, 46, 12}, {0x16, 47, 12}, //52 {0x1A, 48, 13}, {0x19, 49, 13}, {0x18, 50, 13}, {0x17, 51, 13}, //56 {0x16, 52, 13}, {0x15, 53, 13}, {0x14, 54, 13}, {0x13, 55, 13}, //60 {0x12, 56, 13}, {0x11, 57, 13}, {0x10, 58, 13}, {0x1F, 59, 13}, //64 {0x1E, 60, 13}, {0x1D, 61, 13}, {0x1C, 62, 13}, {0x1B, 63, 13}, //68 {0x1F, 64, 14}, {0x1E, 65, 14}, {0x1D, 66, 14}, {0x1C, 67, 14}, //72 {0x1B, 68, 14}, {0x1A, 69, 14}, {0x19, 70, 14}, {0x18, 71, 14}, //76 {0x17, 72, 14}, {0x16, 73, 14}, {0x15, 74, 14}, {0x14, 75, 14}, //80 {0x13, 76, 14}, {0x12, 77, 14}, {0x11, 78, 14}, {0x10, 79, 14}, //84 {0x18, 80, 15}, {0x17, 81, 15}, {0x16, 82, 15}, {0x15, 83, 15}, //88 {0x14, 84, 15}, {0x13, 85, 15}, {0x12, 86, 15}, {0x11, 87, 15}, //92 {0x10, 88, 15}, {0x1F, 89, 15}, {0x1E, 90, 15}, {0x1D, 91, 15}, //96 {0x1C, 92, 15}, {0x1B, 93, 15}, {0x1A, 94, 15}, {0x19, 95, 15}, //100 {0x13, 96, 16}, {0x12, 97, 16}, {0x11, 98, 16}, {0x10, 99, 16}, //104 {0x14, 100, 16}, {0x1A, 101, 16}, {0x19, 102, 16}, {0x18, 103, 16}, //108 {0x17, 104, 16}, {0x16, 105, 16}, {0x15, 106, 16}, {0x1F, 107, 16}, //112 {0x1E, 108, 16}, {0x1D, 109, 16}, {0x1C, 110, 16}, {0x1B, 111, 16}, }; RunLevelPair DCT_Coeff_Table0::runlevel_table[] = { { 0, 1 }, { 1, 1 }, { 0, 2 }, { 2, 1 }, { 0, 3 }, { 3, 1 }, { 4, 1 }, { 1, 2 }, { 5, 1 }, { 6, 1 }, { 7, 1 }, { 0, 4 }, { 2, 2 }, { 8, 1 }, { 9, 1 }, { RUN_ESCAPE, 0 }, { 0, 5 }, { 0, 6 }, { 1, 3 }, { 3, 2 }, { 10, 1 }, { 11, 1 }, { 12, 1 }, { 13, 1 }, { 0, 7 }, { 1, 4 }, { 2, 3 }, { 4, 2 }, { 5, 2 }, { 14, 1 }, { 15, 1 }, { 16, 1 }, { 0, 8 }, { 0, 9 }, { 0, 10 }, { 0, 11 }, { 1, 5 }, { 2, 4 }, { 3, 3 }, { 4, 3 }, { 6, 2 }, { 7, 2 }, { 8, 2 }, { 17, 1 }, { 18, 1 }, { 19, 1 }, { 20, 1 }, { 21, 1 }, { 0, 12 }, { 0, 13 }, { 0, 14 }, { 0, 15 }, { 1, 6 }, { 1, 7 }, { 2, 5 }, { 3, 4 }, { 5, 3 }, { 9, 2 }, { 10, 2 }, { 22, 1 }, { 23, 1 }, { 24, 1 }, { 25, 1 }, { 26, 1 }, { 0, 16 }, { 0, 17 }, { 0, 18 }, { 0, 19 }, { 0, 20 }, { 0, 21 }, { 0, 22 }, { 0, 23 }, { 0, 24 }, { 0, 25 }, { 0, 26 }, { 0, 27 }, { 0, 28 }, { 0, 29 }, { 0, 30 }, { 0, 31 }, { 0, 32 }, { 0, 33 }, { 0, 34 }, { 0, 35 }, { 0, 36 }, { 0, 37 }, { 0, 38 }, { 0, 39 }, { 0, 40 }, { 1, 8 }, { 1, 9 }, { 1, 10 }, { 1, 11 }, { 1, 12 }, { 1, 13 }, { 1, 14 }, { 1, 15 }, { 1, 16 }, { 1, 17 }, { 1, 18 }, { 6, 3 }, { 11, 2 }, { 12, 2 }, { 13, 2 }, { 14, 2 }, { 15, 2 }, { 16, 2 }, { 27, 1 }, { 28, 1 }, { 29, 1 }, { 30, 1 }, { 31, 1 }, }; unsigned int DCT_Coeff_Table0::index_table[16] = { 0, 0, 1, 2, 4, 7, 12, 16, 24, 24, 32, 32, 48, 64, 80, 96, }; DCT_Coeff_Table0::DCT_Coeff_Table0() : DCT_Coeff(table, SIZE, 16, index_table) { } bool DCT_Coeff_Table0::get_end_of_block(IPU_FIFO &FIFO, uint32_t &result) { if (!FIFO.get_bits(result, 2)) return false; printf("[DCT_Coeff_Table0] EOB: $%08X\n", result); result = (result == 2); return true; } bool DCT_Coeff_Table0::get_skip_block(IPU_FIFO &FIFO) { uint32_t blorp; if (!FIFO.get_bits(blorp, 2)) return false; FIFO.advance_stream(2); return true; } bool DCT_Coeff_Table0::get_runlevel_pair(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1) { VLC_Entry entry; if (!peek_symbol(FIFO, entry)) return false; int bit_count = entry.bits; RunLevelPair cur_pair = runlevel_table[entry.value]; printf("Run level pair index: %d (Key: $%02X)\n", entry.value, entry.key); if (cur_pair.run == RUN_ESCAPE) { uint32_t run; if (!peek_value(FIFO, 6, bit_count, run)) return false; pair.run = run; uint32_t level; if (MPEG1) { if (!peek_value(FIFO, 8, bit_count, level)) return false; if (!level) { if (!peek_value(FIFO, 8, bit_count, level)) return false; } else if ((uint8_t)level == 128) { if (!peek_value(FIFO, 8, bit_count, level)) return false; level -= 256; } else if (level > 128) level -= 256; } else { if (!peek_value(FIFO, 12, bit_count, level)) return false; if (level & 0x800) { level |= 0xF000; level = (int16_t)level; } } pair.level = level; } else { uint32_t sign = 0; if (!peek_value(FIFO, 1, bit_count, sign)) return false; pair.run = cur_pair.run; if (sign) pair.level = 0 - cur_pair.level; else pair.level = cur_pair.level; } FIFO.advance_stream(bit_count); return true; } bool DCT_Coeff_Table0::get_runlevel_pair_dc(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1) { uint32_t special; if (!FIFO.get_bits(special, 1)) return false; if (special) { uint32_t sign; if (!FIFO.get_bits(sign, 2)) return false; FIFO.advance_stream(2); pair.run = 0; if (sign & 0x1) pair.level = 0 - 1; else pair.level = 1; return true; } else return get_runlevel_pair(FIFO, pair, MPEG1); } ================================================ FILE: src/ipu/dct_coeff_table0.hpp ================================================ #ifndef DCT_COEFF_TABLE0_HPP #define DCT_COEFF_TABLE0_HPP #include "dct_coeff.hpp" class DCT_Coeff_Table0 : public DCT_Coeff { private: static VLC_Entry table[]; static RunLevelPair runlevel_table[]; static unsigned int index_table[]; constexpr static int SIZE = 112; public: DCT_Coeff_Table0(); bool get_end_of_block(IPU_FIFO &FIFO, uint32_t &result); bool get_skip_block(IPU_FIFO &FIFO); bool get_runlevel_pair(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1); bool get_runlevel_pair_dc(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1); }; #endif // DCT_COEFF_TABLE0_HPP ================================================ FILE: src/ipu/dct_coeff_table1.cpp ================================================ #include #include #include "dct_coeff_table1.hpp" #define printf(fmt, ...)(0) VLC_Entry DCT_Coeff_Table1::table[] = { {0x2, 0, 2}, {0x2, 1, 3}, {0x6, 2, 3}, {0x7, 4, 4}, {0x5, 3, 5}, {0x7, 5, 5}, {0x6, 7, 5}, {0x1C, 11, 5}, {0x1D, 16, 5}, {0x6, 6, 6}, {0x7, 8, 6}, {0x5, 17, 6}, {0x4, 24, 6}, {0x1, 15, 6}, {0x79, 18, 7}, {0x7A, 20, 7}, {0x7, 12, 7}, {0x5, 13, 7}, {0x78, 14, 7}, {0x6, 9, 7}, {0x4, 10, 7}, {0x7B, 32, 7}, {0x7C, 33, 7}, {0x26, 19, 8}, {0x21, 21, 8}, {0x25, 22, 8}, {0x24, 23, 8}, {0x27, 25, 8}, {0xFC, 26, 8}, {0xFD, 27, 8}, {0xFA, 48, 8}, {0xFB, 49, 8}, {0xFE, 50, 8}, {0xFF, 51, 8}, {0x23, 34, 8}, {0x22, 35, 8}, {0x20, 36, 8}, {0x4, 28, 9}, {0x5, 29, 9}, {0x7, 30, 9}, //40 {0xD, 31, 10}, {0xC, 37, 10}, {0x1C, 38, 12}, {0x12, 39, 12}, //44 {0x1E, 40, 12}, {0x15, 41, 12}, {0x11, 42, 12}, {0x1F, 43, 12}, //48 {0x1A, 44, 12}, {0x19, 45, 12}, {0x17, 46, 12}, {0x16, 47, 12}, //52 {0x16, 52, 13}, {0x15, 53, 13}, {0x14, 54, 13}, {0x13, 55, 13}, //56 {0x12, 56, 13}, {0x11, 57, 13}, {0x10, 58, 13}, {0x1F, 59, 13}, //60 {0x1E, 60, 13}, {0x1D, 61, 13}, {0x1C, 62, 13}, {0x1B, 63, 13}, //64 {0x1F, 64, 14}, {0x1E, 65, 14}, {0x1D, 66, 14}, {0x1C, 67, 14}, //68 {0x1B, 68, 14}, {0x1A, 69, 14}, {0x19, 70, 14}, {0x18, 71, 14}, //72 {0x17, 72, 14}, {0x16, 73, 14}, {0x15, 74, 14}, {0x14, 75, 14}, //76 {0x13, 76, 14}, {0x12, 77, 14}, {0x11, 78, 14}, {0x10, 79, 14}, //80 {0x18, 80, 15}, {0x17, 81, 15}, {0x16, 82, 15}, {0x15, 83, 15}, //84 {0x14, 84, 15}, {0x13, 85, 15}, {0x12, 86, 15}, {0x11, 87, 15}, //88 {0x10, 88, 15}, {0x1F, 89, 15}, {0x1E, 90, 15}, {0x1D, 91, 15}, //92 {0x1C, 92, 15}, {0x1B, 93, 15}, {0x1A, 94, 15}, {0x19, 95, 15}, //96 {0x13, 96, 16}, {0x12, 97, 16}, {0x11, 98, 16}, {0x10, 99, 16}, //100 {0x14, 100, 16}, {0x1A, 101, 16}, {0x19, 102, 16}, {0x18, 103, 16}, //104 {0x17, 104, 16}, {0x16, 105, 16}, {0x15, 106, 16}, {0x1F, 107, 16}, //108 {0x1E, 108, 16}, {0x1D, 109, 16}, {0x1C, 110, 16}, {0x1B, 111, 16} //112 }; RunLevelPair DCT_Coeff_Table1::runlevel_table[] = { //Page 134 { 0, 1 }, { 1, 1 }, { 0, 2 }, { 2, 1 }, { 0, 3 }, { 3, 1 }, { 4, 1 }, { 1, 2 }, { 5, 1 }, { 6, 1 }, { 7, 1 }, { 0, 4 }, { 2, 2 }, { 8, 1 }, { 9, 1 }, { RUN_ESCAPE, 0 }, { 0, 5 }, { 0, 6 }, { 1, 3 }, { 3, 2 }, { 10, 1 }, { 11, 1 }, { 12, 1 }, { 13, 1 }, { 0, 7 }, { 1, 4 }, { 2, 3 }, { 4, 2 }, { 5, 2 }, { 14, 1 }, { 15, 1 }, { 16, 1 }, //Page 135 { 0, 8 }, { 0, 9 }, { 0, 10 }, { 0, 11 }, { 1, 5 }, { 2, 4 }, { 3, 3 }, { 4, 3 }, { 6, 2 }, { 7, 2 }, { 8, 2 }, { 17, 1 }, { 18, 1 }, { 19, 1 }, { 20, 1 }, { 21, 1 }, { 0, 12 }, { 0, 13 }, { 0, 14 }, { 0, 15 }, { 1, 6 }, { 1, 7 }, { 2, 5 }, { 3, 4 }, { 5, 3 }, { 9, 2 }, { 10, 2 }, { 22, 1 }, { 23, 1 }, { 24, 1 }, { 25, 1 }, { 26, 1 }, //Page 132 { 0, 16 }, { 0, 17 }, { 0, 18 }, { 0, 19 }, { 0, 20 }, { 0, 21 }, { 0, 22 }, { 0, 23 }, { 0, 24 }, { 0, 25 }, { 0, 26 }, { 0, 27 }, { 0, 28 }, { 0, 29 }, { 0, 30 }, { 0, 31 }, { 0, 32 }, { 0, 33 }, { 0, 34 }, { 0, 35 }, { 0, 36 }, { 0, 37 }, { 0, 38 }, { 0, 39 }, { 0, 40 }, { 1, 8 }, { 1, 9 }, { 1, 10 }, { 1, 11 }, { 1, 12 }, { 1, 13 }, { 1, 14 }, { 1, 15 }, { 1, 16 }, { 1, 17 }, { 1, 18 }, { 6, 3 }, { 11, 2 }, { 12, 2 }, { 13, 2 }, { 14, 2 }, { 15, 2 }, { 16, 2 }, { 27, 1 }, { 28, 1 }, { 29, 1 }, { 30, 1 }, { 31, 1 }, }; unsigned int DCT_Coeff_Table1::index_table[16] = { 0, 0, 1, 3, //4 4, //5 9, //6 14, //7 23, //8 37, //9 40, //10 40, //11 42, //12 52, //13 64, //14 80, //15 96, //16 }; DCT_Coeff_Table1::DCT_Coeff_Table1() : DCT_Coeff(table, SIZE, 16, index_table) { } bool DCT_Coeff_Table1::get_end_of_block(IPU_FIFO &FIFO, uint32_t &result) { if (!FIFO.get_bits(result, 4)) return false; printf("[DCT_Coeff_Table1] EOB: $%08X\n", result); result = (result == 6); return true; } bool DCT_Coeff_Table1::get_skip_block(IPU_FIFO &FIFO) { uint32_t blorp; if (!FIFO.get_bits(blorp, 4)) return false; FIFO.advance_stream(4); return true; } bool DCT_Coeff_Table1::get_runlevel_pair(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1) { VLC_Entry entry; if (!peek_symbol(FIFO, entry)) return false; printf("Key: $%08X Value: $%08X Bits: %d\n", entry.key, entry.value, entry.bits); int bit_count = entry.bits; RunLevelPair cur_pair = runlevel_table[entry.value]; if (cur_pair.run == RUN_ESCAPE) { printf("[DCT_Coeff_Table1] RUN_ESCAPE\n"); uint32_t run = 0; if (!peek_value(FIFO, 6, bit_count, run)) return false; pair.run = run; uint32_t level = 0; if (MPEG1) { if (!peek_value(FIFO, 8, bit_count, level)) return false; if (!level) { if (!peek_value(FIFO, 8, bit_count, level)) return false; } else if ((uint8_t)level == 128) { if (!peek_value(FIFO, 8, bit_count, level)) return false; level -= 256; } else if (level > 128) { level -= 256; } } else { if (!peek_value(FIFO, 12, bit_count, level)) return false; if (level & 0x800) { level |= 0xF000; level = (int16_t)level; } } pair.level = level; } else { uint32_t sign = 0; if (!peek_value(FIFO, 1, bit_count, sign)) return false; pair.run = cur_pair.run; if (sign) pair.level = -cur_pair.level; else pair.level = cur_pair.level; } FIFO.advance_stream(bit_count); return true; } bool DCT_Coeff_Table1::get_runlevel_pair_dc(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1) { //DCT_Coeff_Table1 only gets called by intra macroblocks, so this should never happen return false; } ================================================ FILE: src/ipu/dct_coeff_table1.hpp ================================================ #ifndef DCT_COEFF_TABLE1_HPP #define DCT_COEFF_TABLE1_HPP #include "dct_coeff.hpp" class DCT_Coeff_Table1 : public DCT_Coeff { private: static VLC_Entry table[]; static RunLevelPair runlevel_table[]; static unsigned int index_table[]; constexpr static int SIZE = 112; public: DCT_Coeff_Table1(); bool get_end_of_block(IPU_FIFO &FIFO, uint32_t &result); bool get_skip_block(IPU_FIFO &FIFO); bool get_runlevel_pair(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1); bool get_runlevel_pair_dc(IPU_FIFO &FIFO, RunLevelPair &pair, bool MPEG1); }; #endif // DCT_COEFF_TABLE1_HPP ================================================ FILE: src/ipu/ipu.cpp ================================================ #include #include #include #include #include #include "ipu.hpp" #include "ee/dmac.h" #include "ee/intc.h" #if defined(IRIS_IPU_TRACE) #define printf(...) std::printf(__VA_ARGS__) #else #define printf(...) ((void)0) #endif /** * The majority of this code is based upon Play!'s implementation of the IPU. * All the relevant files are located in the following links: * * https://github.com/jpd002/Play-/tree/master/Source/ee (IPU base, some tables) * https://github.com/jpd002/Play--Framework/tree/master/include/mpeg2 (Table includes) * https://github.com/jpd002/Play--Framework/tree/master/src/mpeg2 (Tables) * https://github.com/jpd002/Play--Framework/blob/master/src/idct/IEEE1180.cpp (IDCT transformation) */ uint32_t ImageProcessingUnit::inverse_scan_zigzag[0x40] = { 0, 1, 5, 6, 14, 15, 27, 28, 2, 4, 7, 13, 16, 26, 29, 42, 3, 8, 12, 17, 25, 30, 41, 43, 9, 11, 18, 24, 31, 40, 44, 53, 10, 19, 23, 32, 39, 45, 52, 54, 20, 22, 33, 38, 46, 51, 55, 60, 21, 34, 37, 47, 50, 56, 59, 61, 35, 36, 48, 49, 57, 58, 62, 63 }; uint32_t ImageProcessingUnit::inverse_scan_alternate[0x40] = { 0, 4, 6, 20, 22, 36, 38, 52, 1, 5, 7, 21, 23, 37, 39, 53, 2, 8, 19, 24, 34, 40, 50, 54, 3, 9, 18, 25, 35, 41, 51, 55, 10, 17, 26, 30, 42, 46, 56, 60, 11, 16, 27, 31, 43, 47, 57, 61, 12, 15, 28, 32, 44, 48, 58, 62, 13, 14, 29, 33, 45, 49, 59, 63 }; uint32_t ImageProcessingUnit::quantizer_linear[0x20] = { 0, 2, 4, 6, 8, 10, 12, 14, 16, 18, 20, 22, 24, 26, 28, 30, 32, 34, 36, 38, 40, 42, 44, 46, 48, 50, 52, 54, 56, 58, 60, 62 }; uint32_t ImageProcessingUnit::quantizer_nonlinear[0x20] = { 0, 1, 2, 3, 4, 5, 6, 7, 8, 10, 12, 14, 16, 18, 20, 22, 24, 28, 32, 36, 40, 44, 48, 52, 56, 64, 72, 80, 88, 96, 104, 112, }; static const uint8_t default_intra_IQ[0x40] = { 8, 16, 19, 22, 26, 27, 29, 34, 16, 16, 22, 24, 27, 29, 34, 37, 19, 22, 26, 27, 29, 34, 34, 38, 22, 22, 26, 27, 29, 34, 37, 40, 22, 26, 27, 29, 32, 35, 40, 48, 26, 27, 29, 32, 35, 40, 48, 58, 26, 27, 29, 34, 38, 46, 56, 69, 27, 29, 35, 38, 46, 56, 69, 83, }; static const uint8_t default_nonintra_IQ[0x40] = { 16, 17, 18, 19, 20, 21, 22, 23, 17, 18, 19, 20, 21, 22, 23, 24, 18, 19, 20, 21, 22, 23, 24, 25, 19, 20, 21, 22, 23, 24, 26, 27, 20, 21, 22, 23, 25, 26, 27, 28, 21, 22, 23, 24, 26, 27, 28, 30, 22, 23, 24, 26, 27, 28, 30, 31, 23, 24, 25, 27, 28, 30, 31, 33, }; ImageProcessingUnit::ImageProcessingUnit(struct ps2_intc* intc, struct ps2_dmac* dmac) : intc(intc), dmac(dmac) { //Generate CrCb->RGB conversion map for (unsigned int i = 0; i < 0x40; i += 0x8) { for (unsigned int j = 0; j < 0x10; j += 2) { int index = j + (i * 4); crcb_map[index + 0x00] = (j / 2) + i; crcb_map[index + 0x01] = (j / 2) + i; crcb_map[index + 0x10] = (j / 2) + i; crcb_map[index + 0x11] = (j / 2) + i; } } //I'm assuming the dithering process rounds down so I've rounded the matrix values down dither_mtx[0][0] = -4; dither_mtx[0][1] = 0; dither_mtx[0][2] = -3; dither_mtx[0][3] = 1; dither_mtx[1][0] = 2; dither_mtx[1][1] = -2; dither_mtx[1][2] = 3; dither_mtx[1][3] = -1; dither_mtx[2][0] = -3; dither_mtx[2][1] = 1; dither_mtx[2][2] = -4; dither_mtx[2][3] = 0; dither_mtx[3][0] = 3; dither_mtx[3][1] = -1; dither_mtx[3][2] = 2; dither_mtx[3][3] = -2; } void ImageProcessingUnit::reset() { dct_coeff = nullptr; VDEC_table = nullptr; in_FIFO.reset(); out_FIFO.reset(); prepare_IDCT(); memcpy(intra_IQ, default_intra_IQ, sizeof(intra_IQ)); memcpy(nonintra_IQ, default_nonintra_IQ, sizeof(nonintra_IQ)); ctrl.error_code = false; ctrl.start_code = false; ctrl.intra_DC_precision = 0; ctrl.MPEG1 = false; ctrl.picture_type = 0; ctrl.busy = false; ctrl.coded_block_pattern = 0; command = 0; command_option = 0; bytes_left = 0; command_decoding = false; } void ImageProcessingUnit::run() { if (ctrl.busy) { try { switch (command) { case 0x01: if (in_FIFO.f.size()) { if (process_IDEC()) finish_command(); } break; case 0x02: if (in_FIFO.f.size()) { if (process_BDEC()) finish_command(); } break; case 0x03: if (in_FIFO.f.size()) process_VDEC(); break; case 0x04: if (in_FIFO.f.size()) process_FDEC(); break; case 0x05: if (setiq_state == SETIQ_STATE::ADVANCE) { if (!in_FIFO.advance_stream(command_option & 0x3F)) break; setiq_state = SETIQ_STATE::POPULATE_TABLE; } while (bytes_left && in_FIFO.f.size()) { uint32_t value; if (!in_FIFO.get_bits(value, 8)) break; in_FIFO.advance_stream(8); int index = 64 - bytes_left; if (command_option & (1 << 27)) nonintra_IQ[index] = value & 0xFF; else intra_IQ[index] = value & 0xFF; bytes_left--; } if (bytes_left <= 0) ctrl.busy = false; break; case 0x06: while (bytes_left && in_FIFO.f.size()) { uint128_t quad = in_FIFO.f.front(); in_FIFO.f.pop_front(); for (int i = 0; i < 8; i++) { int index = (32 - bytes_left) >> 1; VQCLUT[index] = quad.u16[i]; bytes_left -= 2; } } if (bytes_left <= 0) ctrl.busy = false; break; case 0x07: if (in_FIFO.f.size()) { if (process_CSC()) finish_command(); } break; case 0x08: if (in_FIFO.f.size()) { if (process_PACK()) finish_command(); } break; } } catch (VLC_Error& e) { std::fprintf(stderr, "ipu: VLC error: %s\n", e.what()); ctrl.error_code = true; finish_command(); } } if (can_write_FIFO()) { dmac->ipu_to.dreq = 1; // printf("ipu: set ipu_to dreq\n"); dmac_handle_ipu_to_transfer(dmac); } if (can_read_FIFO()) { dmac->ipu_from.dreq = 1; printf("ipu: Output FIFO ready\n"); // printf("ipu: set ipu_from dreq out=%x\n", out_FIFO.f.size()); dmac_handle_ipu_from_transfer(dmac); } } void ImageProcessingUnit::finish_command() { ctrl.busy = false; command_decoding = false; ps2_intc_irq(intc, EE_INTC_IPU); } bool ImageProcessingUnit::process_IDEC() { while (true) { switch (idec.state) { case IDEC_STATE::DELAY: //Play delays IDEC execution before consuming FIFO data. idec.state = IDEC_STATE::ADVANCE; return false; case IDEC_STATE::ADVANCE: printf("ipu: Advance stream\n"); if (!in_FIFO.advance_stream(command_option & 0x3F)) return false; idec.state = IDEC_STATE::MACRO_I_TYPE; break; case IDEC_STATE::MACRO_I_TYPE: printf("ipu: Decode macroblock I type\n"); if (!macroblock_I_pic.get_symbol(in_FIFO, idec.macro_type)) return false; idec.state = IDEC_STATE::DCT_TYPE; break; case IDEC_STATE::DCT_TYPE: printf("ipu: Decode DCT\n"); if (idec.decodes_dct) { uint32_t value; if (!in_FIFO.get_bits(value, 1)) return false; in_FIFO.advance_stream(1); //Play expects this to be zero for IDEC intra path. if (value != 0) throw VLC_Error("IDEC unsupported DCT type"); } idec.state = IDEC_STATE::QSC; break; case IDEC_STATE::QSC: printf("ipu: Decode QSC\n"); if (idec.macro_type & 0x10) { if (!in_FIFO.get_bits(idec.qsc, 5)) return false; in_FIFO.advance_stream(5); } idec.state = IDEC_STATE::INIT_BDEC; break; case IDEC_STATE::INIT_BDEC: //We don't need to advance, and the macroblock is always intra so no need to check for a CBP. printf("ipu: Init BDEC\n"); bdec.state = BDEC_STATE::RESET_DC; bdec.intra = true; bdec.quantizer_step = idec.qsc; bdec.out_fifo = &idec.temp_fifo; ctrl.coded_block_pattern = 0x3F; bdec.block_index = 0; bdec.cur_channel = 0; bdec.reset_dc = idec.blocks_decoded == 0; bdec.check_start_code = false; idec.state = IDEC_STATE::READ_BLOCK; break; case IDEC_STATE::READ_BLOCK: printf("ipu: Read macroblock\n"); if (!process_BDEC()) return false; idec.blocks_decoded++; idec.state = IDEC_STATE::INIT_CSC; break; case IDEC_STATE::INIT_CSC: //BDEC outputs in RAW16. CSC works in RAW8, so we need to convert appropriately. printf("ipu: Init CSC\n"); for (int i = 0; i < RAW_BLOCK_SIZE / 8; i++) { uint128_t quad = idec.temp_fifo.f.front(); idec.temp_fifo.f.pop_front(); int offset = i * 8; for (int j = 0; j < 8; j++) { int16_t data = (int16_t)quad.u16[j]; if (data < 0) data = 0; if (data > 255) data = 255; csc.block[offset + j] = (uint8_t)data; } } csc.state = CSC_STATE::CONVERT; csc.block_index = 0; csc.macroblocks = 1; idec.state = IDEC_STATE::EXEC_CSC; break; case IDEC_STATE::EXEC_CSC: printf("ipu: Exec CSC\n"); if (!process_CSC()) return false; idec.state = IDEC_STATE::CHECK_START_CODE; break; case IDEC_STATE::CHECK_START_CODE: { printf("ipu: Check start code\n"); uint32_t code; if (!in_FIFO.get_bits(code, 8)) return false; if (!code) { idec.state = IDEC_STATE::VALID_START_CODE; in_FIFO.byte_align(); } else idec.state = IDEC_STATE::MACRO_INC; } break; case IDEC_STATE::VALID_START_CODE: { printf("ipu: Validate start code\n"); uint32_t code; if (!in_FIFO.get_bits(code, 24)) { //If we already detected 8 zero bits in CHECK_START_CODE but don't have //enough bits for a full 24-bit validation code, treat this as a valid //start-code boundary and finish the command. idec.state = IDEC_STATE::DONE; break; } if (code == 0) { //Consume one byte of zero padding and keep searching for 0x000001. if (!in_FIFO.advance_stream(8)) return false; } else if (code == 1) { idec.state = IDEC_STATE::DONE; } else { throw VLC_Error("IDEC start code invalid"); } } break; case IDEC_STATE::MACRO_INC: { printf("ipu: Macroblock increment\n"); uint32_t inc; if (!macroblock_increment.get_symbol(in_FIFO, inc)) return false; if ((inc & 0xFFFF) != 1) throw VLC_Error("IDEC invalid macroblock increment"); idec.state = IDEC_STATE::MACRO_I_TYPE; } break; case IDEC_STATE::DONE: printf("ipu: IDEC done!\n"); return true; } } } bool ImageProcessingUnit::process_BDEC() { while (true) { switch (bdec.state) { case BDEC_STATE::ADVANCE: if (!in_FIFO.advance_stream(command_option & 0x3F)) return false; bdec.state = BDEC_STATE::GET_CBP; break; case BDEC_STATE::GET_CBP: printf("ipu: Get CBP!\n"); if (!bdec.intra) { uint32_t pattern; if (!cbp.get_symbol(in_FIFO, pattern)) return false; ctrl.coded_block_pattern = pattern; printf("CBP: %d\n", ctrl.coded_block_pattern); } else ctrl.coded_block_pattern = 0x3F; bdec.state = BDEC_STATE::RESET_DC; break; case BDEC_STATE::RESET_DC: if (bdec.reset_dc) { printf("ipu: Reset DC!\n"); int16_t value; switch (ctrl.intra_DC_precision) { case 0: value = 128; break; case 1: value = 256; break; case 2: value = 512; break; } for (int i = 0; i < 3; i++) bdec.dc_predictor[i] = value; } bdec.state = BDEC_STATE::BEGIN_DECODING; break; case BDEC_STATE::BEGIN_DECODING: printf("ipu: Begin decoding block %d!\n", bdec.block_index); bdec.cur_block = bdec.blocks[bdec.block_index]; memset(bdec.cur_block, 0, sizeof(int16_t) * 64); if (ctrl.coded_block_pattern & (1 << (5 - bdec.block_index))) { //If block index is Cb/Cr, set the channel to the relevant chromance one if (bdec.block_index > 3) bdec.cur_channel = bdec.block_index - 3; else bdec.cur_channel = 0; if (bdec.intra && ctrl.intra_VLC_table) { printf("ipu: Use DCT coefficient table 1\n"); dct_coeff = &dct_coeff1; } else { printf("ipu: Use DCT coefficient table 0\n"); dct_coeff = &dct_coeff0; } bdec.read_coeff_state = BDEC_Command::READ_COEFF::INIT; bdec.state = BDEC_STATE::READ_COEFFS; } else bdec.state = BDEC_STATE::LOAD_NEXT_BLOCK; break; case BDEC_STATE::READ_COEFFS: { printf("ipu: Read coeffs!\n"); if (!BDEC_read_coeffs()) return false; printf("ipu: Dequantize!\n"); dequantize(bdec.cur_block); printf("ipu: Inverse scan!\n"); inverse_scan(bdec.cur_block); printf("ipu: IDCT!\n"); int16_t temp[0x40]; memcpy(temp, bdec.cur_block, 0x40 * sizeof(int16_t)); perform_IDCT(temp, bdec.cur_block); bdec.state = BDEC_STATE::LOAD_NEXT_BLOCK; } break; case BDEC_STATE::LOAD_NEXT_BLOCK: printf("ipu: Load next block!\n"); bdec.block_index++; if (bdec.block_index == 6) bdec.state = BDEC_STATE::DONE; else bdec.state = BDEC_STATE::BEGIN_DECODING; break; case BDEC_STATE::DONE: { printf("ipu: BDEC done!\n"); uint128_t quad; for (int i = 0; i < 8; i++) { memcpy(quad.u8, bdec.blocks[0] + (i * 8), sizeof(int16_t) * 8); bdec.out_fifo->f.push_back(quad); memcpy(quad.u8, bdec.blocks[1] + (i * 8), sizeof(int16_t) * 8); bdec.out_fifo->f.push_back(quad); } for (int i = 0; i < 8; i++) { memcpy(quad.u8, bdec.blocks[2] + (i * 8), sizeof(int16_t) * 8); bdec.out_fifo->f.push_back(quad); memcpy(quad.u8, bdec.blocks[3] + (i * 8), sizeof(int16_t) * 8); bdec.out_fifo->f.push_back(quad); } for (int i = 0; i < 8; i++) { memcpy(quad.u8, bdec.blocks[4] + (i * 8), sizeof(int16_t) * 8); bdec.out_fifo->f.push_back(quad); } for (int i = 0; i < 8; i++) { memcpy(quad.u8, bdec.blocks[5] + (i * 8), sizeof(int16_t) * 8); bdec.out_fifo->f.push_back(quad); } if (bdec.check_start_code) bdec.state = BDEC_STATE::CHECK_START_CODE; else return true; } break; case BDEC_STATE::CHECK_START_CODE: { uint32_t bits; if (in_FIFO.get_bits(bits, 8)) { if (!bits) { ctrl.start_code = true; printf("ipu: Start code detected!\n"); } return true; } else return false; } } } } void ImageProcessingUnit::inverse_scan(int16_t *block) { int16_t temp[0x40]; memcpy(temp, block, 0x40 * sizeof(int16_t)); int id; for (int i = 0; i < 0x40; i++) { if (ctrl.alternate_scan) id = inverse_scan_alternate[i]; else id = inverse_scan_zigzag[i]; block[i] = temp[id]; } } void ImageProcessingUnit::dequantize(int16_t *block) { int q_scale; if (ctrl.nonlinear_Q_step) q_scale = quantizer_nonlinear[bdec.quantizer_step]; else q_scale = quantizer_linear[bdec.quantizer_step]; if (bdec.intra) { switch (ctrl.intra_DC_precision) { case 0: block[0] *= 8; break; case 1: block[0] *= 4; break; case 2: block[0] *= 2; break; default: printf("ipu: Dequantize: Intra DC precision == 3!\n"); block[0] = 0; break; } for (int i = 1; i < 0x40; i++) { int16_t sign; if (!block[i]) sign = 0; else { if (block[i] > 0) sign = 1; else sign = (int16_t)0xFFFF; } int32_t scaled = (int32_t)block[i] * (int32_t)intra_IQ[i] * q_scale * 2; block[i] = (int16_t)(scaled / 32); if (sign) { if (!(block[i] & 0x1)) { block[i] -= sign; block[i] |= 1; } } } } else { for (int i = 0; i < 64; i++) { int16_t sign; if (!block[i]) sign = 0; else { if (block[i] > 0) sign = 1; else sign = 0xFFFF; } int32_t scaled = (((int32_t)block[i] * 2) + sign) * (int32_t)nonintra_IQ[i] * q_scale; block[i] = (int16_t)(scaled / 32); if (sign) { if (!(block[i] & 0x1)) { block[i] -= sign; block[i] |= 1; } } } } //Saturation step for (int i = 0; i < 64; i++) { if (block[i] > 2047) block[i] = 2047; if (block[i] < -2048) block[i] = -2048; } } //IDCT code here taken from mpeg2decode //Copyright (C) 1996, MPEG Software Simulation Group. All Rights Reserved. #ifndef PI # ifdef M_PI # define PI M_PI # else # define PI 3.14159265358979323846 # endif #endif void ImageProcessingUnit::prepare_IDCT() { int freq, time; double scale; for (freq=0; freq < 8; freq++) { scale = (freq == 0) ? sqrt(0.125) : 0.5; for (time=0; time<8; time++) { IDCT_table[freq][time] = scale*cos((PI/8.0)*freq*(time + 0.5)); } } } void ImageProcessingUnit::perform_IDCT(const int16_t* pUV, int16_t* pXY) { int i, j, k, v; double partial_product; double tmp[64]; for (i=0; i<8; i++) { for (j=0; j<8; j++) { partial_product = 0.0; for (k=0; k<8; k++) { partial_product+= IDCT_table[k][j]*pUV[8*i+k]; } tmp[8*i+j] = partial_product; } } /* Transpose operation is integrated into address mapping by switching loop order of i and j */ for (j=0; j<8; j++) { for (i=0; i<8; i++) { partial_product = 0.0; for (k=0; k<8; k++) { partial_product+= IDCT_table[k][i]*tmp[8*k+j]; } v = (int) floor(partial_product+0.5); pXY[8*i+j] = v; } } } //End IDCT code bool ImageProcessingUnit::BDEC_read_coeffs() { while (true) { switch (bdec.read_coeff_state) { case BDEC_Command::READ_COEFF::INIT: printf("ipu: READ_COEFF Init!\n"); bdec.read_diff_state = BDEC_Command::READ_DIFF::SIZE; bdec.subblock_index = 0; if (bdec.intra) { bdec.subblock_index = 1; bdec.read_coeff_state = BDEC_Command::READ_COEFF::READ_DC_DIFF; } else bdec.read_coeff_state = BDEC_Command::READ_COEFF::CHECK_END; break; case BDEC_Command::READ_COEFF::READ_DC_DIFF: printf("ipu: READ_COEFF Read DC diffs!\n"); if (!BDEC_read_diff()) return false; bdec.cur_block[0] = (int16_t)(bdec.dc_predictor[bdec.cur_channel] + bdec.dc_diff); bdec.dc_predictor[bdec.cur_channel] = bdec.cur_block[0]; bdec.read_coeff_state = BDEC_Command::READ_COEFF::CHECK_END; break; case BDEC_Command::READ_COEFF::CHECK_END: printf("ipu: READ_COEFF Check end of block!\n"); { uint32_t end = 0; if (!dct_coeff->get_end_of_block(in_FIFO, end)) return false; if (bdec.subblock_index && end) bdec.read_coeff_state = BDEC_Command::READ_COEFF::SKIP_END; else bdec.read_coeff_state = BDEC_Command::READ_COEFF::COEFF; } break; case BDEC_Command::READ_COEFF::COEFF: printf("ipu: READ_COEFF Read coeffs!\n"); { RunLevelPair pair; if (!bdec.subblock_index) { if (!dct_coeff->get_runlevel_pair_dc(in_FIFO, pair, ctrl.MPEG1)) return false; } else { if (!dct_coeff->get_runlevel_pair(in_FIFO, pair, ctrl.MPEG1)) return false; } printf("ipu: Run: %d Level: %d\n", pair.run, pair.level); bdec.subblock_index += pair.run; if (bdec.subblock_index < 0x40) { bdec.cur_block[bdec.subblock_index] = (int16_t)pair.level; } else { throw VLC_Error("BDEC coefficient index overflow"); } bdec.subblock_index++; bdec.read_coeff_state = BDEC_Command::READ_COEFF::CHECK_END; } break; case BDEC_Command::READ_COEFF::SKIP_END: printf("ipu: READ_COEFF Skip end!\n"); if (!dct_coeff->get_skip_block(in_FIFO)) return false; return true; } } } bool ImageProcessingUnit::BDEC_read_diff() { while (true) { switch (bdec.read_diff_state) { case BDEC_Command::READ_DIFF::SIZE: printf("ipu: READ_DIFF SIZE!\n"); if (bdec.cur_channel == 0) { if (!lum_table.get_symbol(in_FIFO, bdec.dc_size)) return false; } else { if (!chrom_table.get_symbol(in_FIFO, bdec.dc_size)) return false; } bdec.read_diff_state = BDEC_Command::READ_DIFF::DIFF; break; case BDEC_Command::READ_DIFF::DIFF: printf("ipu: READ_DIFF DIFF!\n"); if (!bdec.dc_size) bdec.dc_diff = 0; else { uint32_t result = 0; if (!in_FIFO.get_bits(result, bdec.dc_size)) return false; if (!in_FIFO.advance_stream(bdec.dc_size)) return false; int16_t half_range = 1 << (bdec.dc_size - 1); bdec.dc_diff = (int16_t)result; if (bdec.dc_diff < half_range) bdec.dc_diff += 1 - (2 * half_range); } return true; } } } void ImageProcessingUnit::convert_RGB32_to_RGB16(const uint8_t* rgb32, uint16_t* rgb16, bool dithering) { for (int i = 0; i < 16; ++i) { for (int j = 0; j < 16; ++j) { //It's worth noting that bit 30 is the alpha bit for RGB16, not bit 31. const int index = j + (i * 16); const int dither = dithering ? dither_mtx[i & 3][j & 3] : 0; const int r = std::max(0, std::min(rgb32[4 * index] + dither, 255)) >> 3; const int g = std::max(0, std::min(rgb32[4 * index + 1] + dither, 255)) >> 3; const int b = std::max(0, std::min(rgb32[4 * index + 2] + dither, 255)) >> 3; const int a = rgb32[4 * index + 3] == 0x40; rgb16[index] = r | g << 5 | b << 10 | a << 15; } } } void ImageProcessingUnit::process_VDEC() { int table = command_option >> 26; switch (table) { case 0: printf("ipu: MBAI\n"); VDEC_table = ¯oblock_increment; break; case 1: printf("ipu: MBT\n"); switch (ctrl.picture_type) { case 0x1: printf("ipu: I pic\n"); VDEC_table = ¯oblock_I_pic; break; case 0x2: printf("ipu: P pic\n"); VDEC_table = ¯oblock_P_pic; break; case 0x3: printf("ipu: B pic\n"); VDEC_table = ¯oblock_B_pic; break; } break; case 2: printf("ipu: MC\n"); VDEC_table = &motioncode; break; } while (true) { switch (vdec_state) { case VDEC_STATE::ADVANCE: if (!in_FIFO.advance_stream(command_option & 0x3F)) return; vdec_state = VDEC_STATE::DECODE; break; case VDEC_STATE::DECODE: if (!VDEC_table->get_symbol(in_FIFO, command_output)) return; else vdec_state = VDEC_STATE::DONE; break; case VDEC_STATE::DONE: printf("ipu: VDEC done! Output: $%08X infifo=%d\n", command_output, in_FIFO.f.size()); finish_command(); return; } } } void ImageProcessingUnit::process_FDEC() { while (true) { switch (fdec_state) { case VDEC_STATE::ADVANCE: if (!in_FIFO.advance_stream(command_option & 0x3F)) return; fdec_state = VDEC_STATE::DECODE; break; case VDEC_STATE::DECODE: if (!in_FIFO.get_bits(command_output, 32)) return; fdec_state = VDEC_STATE::DONE; break; case VDEC_STATE::DONE: finish_command(); printf("ipu: FDEC result: $%08X\n", command_output); return; } } } bool ImageProcessingUnit::process_CSC() { while (true) { switch (csc.state) { case CSC_STATE::BEGIN: if (csc.macroblocks) { csc.state = CSC_STATE::READ; csc.block_index = 0; } else csc.state = CSC_STATE::DONE; break; case CSC_STATE::READ: if (csc.block_index == RAW_BLOCK_SIZE) csc.state = CSC_STATE::CONVERT; else { uint32_t value; if (!in_FIFO.get_bits(value, 8)) return false; in_FIFO.advance_stream(8); csc.block[csc.block_index] = value & 0xFF; csc.block_index++; } break; case CSC_STATE::CONVERT: { uint8_t rgb32[4 * RGB_BLOCK_SIZE]; uint8_t* lum_block = csc.block; uint8_t* cb_block = csc.block + 0x100; uint8_t* cr_block = csc.block + 0x140; uint16_t alphaTh0 = (TH0 & 0x1FF); uint16_t alphaTh1 = (TH1 & 0x1FF); for (int i = 0; i < 16; i++) { for (int j = 0; j < 16; j++) { int index = j + (i * 16); float lum = lum_block[index]; float cb = cb_block[crcb_map[index]]; float cr = cr_block[crcb_map[index]]; float r = lum + 1.402f * (cr - 128); float g = lum - 0.34414f * (cb - 128) - 0.71414f * (cr - 128); float b = lum + 1.772f * (cb - 128); if (r < 0) r = 0; if (r > 255) r = 255; if (g < 0) g = 0; if (g > 255) g = 255; if (b < 0) b = 0; if (b > 255) b = 255; uint8_t alpha; if (r < alphaTh0 && g < alphaTh0 && b < alphaTh0) alpha = 0; else if (r < alphaTh1 && g < alphaTh1 && b < alphaTh1) alpha = 0x40; else alpha = 0x80; rgb32[4 * index] = (uint8_t)r; rgb32[4 * index + 1] = (uint8_t)g; rgb32[4 * index + 2] = (uint8_t)b; rgb32[4 * index + 3] = alpha; } } uint128_t quad; if (csc.use_RGB16) { uint16_t rgb16[RGB_BLOCK_SIZE]; convert_RGB32_to_RGB16(rgb32, rgb16, csc.use_dithering); for (int i = 0; i < RGB_BLOCK_SIZE / 8; i++) { for (int j = 0; j < 8; j++) { quad.u16[j] = rgb16[j + (i * 8)]; } out_FIFO.f.push_back(quad); } } else { for (int i = 0; i < RGB_BLOCK_SIZE / 4; i++) { for (int j = 0; j < 4; j++) { int index = 4 * (j + (i * 4)); uint8_t r = rgb32[index]; uint8_t g = rgb32[index + 1]; uint8_t b = rgb32[index + 2]; uint8_t a = rgb32[index + 3]; uint32_t color = r | g << 8 | b << 16 | a << 24; quad.u32[j] = color; } out_FIFO.f.push_back(quad); } } csc.macroblocks--; csc.state = CSC_STATE::BEGIN; dmac->ipu_from.dreq = 1; printf("ipu: set ipu_from dreq out=%x\n", out_FIFO.f.size()); dmac_handle_ipu_from_transfer(dmac); } break; case CSC_STATE::DONE: printf("ipu: CSC done!\n"); return true; } } } bool ImageProcessingUnit::process_PACK() { while (true) { switch (pack.state) { case PACK_STATE::BEGIN: if (pack.macroblocks) { pack.state = PACK_STATE::READ; pack.block_index = 0; } else pack.state = PACK_STATE::DONE; break; case PACK_STATE::READ: if (pack.block_index == 4 * RGB_BLOCK_SIZE) pack.state = PACK_STATE::CONVERT; else { uint32_t value; if (!in_FIFO.get_bits(value, 8)) return false; in_FIFO.advance_stream(8); pack.block[pack.block_index] = value & 0xFF; pack.block_index++; } break; case PACK_STATE::CONVERT: { uint16_t rgb16[RGB_BLOCK_SIZE]; convert_RGB32_to_RGB16(pack.block, rgb16, pack.use_dithering); uint128_t quad; if (pack.use_RGB16) { for (int i = 0; i < RGB_BLOCK_SIZE / 8; ++i) { for (int j = 0; j < 8; ++j) { quad.u16[j] = rgb16[j + (i * 8)]; } out_FIFO.f.push_back(quad); } } else { int clut_r[16]; int clut_g[16]; int clut_b[16]; for (int i = 0; i < 16; ++i) { clut_r[i] = VQCLUT[i] & 0x1F; clut_g[i] = (VQCLUT[i] >> 5) & 0x1F; clut_b[i] = (VQCLUT[i] >> 10) & 0x1F; } auto closest_index = [&](uint16_t color) { const int r = color & 0x1F; const int g = (color >> 5) & 0x1F; const int b = (color >> 10) & 0x1F; uint8_t index = 0; int min_distance = std::numeric_limits::max(); for (uint8_t i = 0; i < 16; ++i) { const int dr = r - clut_r[i]; const int dg = g - clut_g[i]; const int db = b - clut_b[i]; const int distance = dr * dr + dg * dg + db * db; // TODO: If two distances are the same which index is used? if (min_distance > distance) { index = i; min_distance = distance; } } return index; }; for (int i = 0; i < RGB_BLOCK_SIZE / 32; ++i) { for (int j = 0; j < 16; ++j) { int index = 2 * j + (i * 32); const uint16_t color16_low = rgb16[index]; const uint16_t color16_high = rgb16[index + 1]; quad.u8[j] = closest_index(color16_high) << 4 | closest_index(color16_low); } out_FIFO.f.push_back(quad); } } pack.macroblocks--; pack.state = PACK_STATE::BEGIN; dmac->ipu_from.dreq = 1; printf("ipu: set ipu_from dreq out=%x\n", out_FIFO.f.size()); dmac_handle_ipu_from_transfer(dmac); } break; case PACK_STATE::DONE: printf("ipu: PACK done!\n"); return true; } } } uint64_t ImageProcessingUnit::read_command() { uint64_t reg = 0; reg |= command_output; reg |= (uint64_t)command_decoding << 63UL; printf("ipu: Read command: $%08X\n", command_output); return reg; } uint32_t ImageProcessingUnit::read_control() { uint32_t reg = 0; reg |= in_FIFO.f.size(); reg |= (ctrl.coded_block_pattern & 0x3F) << 8; reg |= ctrl.error_code << 14; reg |= ctrl.start_code << 15; reg |= ctrl.intra_DC_precision << 16; reg |= ctrl.alternate_scan << 20; reg |= ctrl.intra_VLC_table << 21; reg |= ctrl.nonlinear_Q_step << 22; reg |= ctrl.MPEG1 << 23; reg |= ctrl.picture_type << 24; reg |= ctrl.busy << 31; return reg; } uint32_t ImageProcessingUnit::read_BP() { uint32_t reg = 0; uint8_t fifo_size = in_FIFO.f.size(); //Check for FP bit if (in_FIFO.bit_pointer && fifo_size) { reg |= 1 << 16; fifo_size--; } reg |= in_FIFO.bit_pointer; reg |= fifo_size << 8; printf("ipu: Read BP: $%08X\n", reg); return reg; } uint64_t ImageProcessingUnit::read_top() { uint64_t reg = 0; int max_bits = (in_FIFO.f.size() * 128) - in_FIFO.bit_pointer; if (max_bits > 32) max_bits = 32; uint32_t next_data; in_FIFO.get_bits(next_data, max_bits); reg |= next_data << (32 - max_bits); /** Note on max_bits * This seems to be undocumented behavior. FMV libraries use this bit to determine how much data is left * in the input FIFO, for the purposes of flushing their bitstream cache. * If this bit is set, this means that there are less than 32 bits left in the FIFO, and BP is then used. * If this is not set, at least 32 bits are available. * * This is needed for rare cases where games peek in the FIFO when less than 32 bits are available. */ reg |= ((uint64_t)command_decoding | (uint64_t)(max_bits < 32)) << 63UL; return reg; } void ImageProcessingUnit::write_command(uint32_t value) { printf("ipu: Write command: $%08X\n", value); if (!ctrl.busy) { ctrl.busy = true; command = (value >> 28); command_option = value & ~0xF0000000; ctrl.error_code = false; ctrl.start_code = false; switch (command) { case 0x00: printf("ipu: BCLR\n"); in_FIFO.reset(); in_FIFO.bit_pointer = command_option & 0x7F; finish_command(); break; case 0x01: printf("ipu: IDEC\n"); idec.state = IDEC_STATE::DELAY; idec.macro_type = 0; idec.qsc = (command_option >> 16) & 0x1F; idec.decodes_dct = command_option & (1 << 24); idec.blocks_decoded = 0; csc.use_RGB16 = command_option & (1 << 27); break; case 0x02: printf("ipu: BDEC\n"); bdec.state = BDEC_STATE::ADVANCE; bdec.out_fifo = &out_FIFO; ctrl.coded_block_pattern = 0x3F; bdec.block_index = 0; bdec.cur_channel = 0; bdec.quantizer_step = (command_option >> 16) & 0x1F; bdec.reset_dc = command_option & (1 << 26); bdec.intra = command_option & (1 << 27); bdec.check_start_code = true; break; case 0x03: printf("ipu: VDEC\n"); command_decoding = true; vdec_state = VDEC_STATE::ADVANCE; process_VDEC(); break; case 0x04: printf("ipu: FDEC\n"); command_decoding = true; fdec_state = VDEC_STATE::ADVANCE; process_FDEC(); break; case 0x05: printf("ipu: SETIQ\n"); bytes_left = 64; setiq_state = SETIQ_STATE::ADVANCE; break; case 0x06: printf("ipu: SETVQ\n"); bytes_left = 32; break; case 0x07: printf("ipu: CSC\n"); csc.state = CSC_STATE::BEGIN; csc.macroblocks = command_option & 0x7FF; csc.use_RGB16 = command_option & (1 << 27); csc.use_dithering = command_option & (1 << 26); break; case 0x08: printf("ipu: PACK\n"); pack.state = PACK_STATE::BEGIN; pack.macroblocks = command_option & 0x7FF; pack.use_RGB16 = command_option & (1 << 27); pack.use_dithering = command_option & (1 << 26); break; case 0x09: printf("ipu: SETTH\n"); TH0 = command_option & 0x1FF; TH1 = (command_option >> 16) & 0x1FF; finish_command(); break; } } } void ImageProcessingUnit::write_control(uint32_t value) { printf("ipu: Write control: $%08X\n", value); ctrl.intra_DC_precision = (value >> 16) & 0x3; ctrl.alternate_scan = value & (1 << 20); ctrl.intra_VLC_table = value & (1 << 21); ctrl.nonlinear_Q_step = value & (1 << 22); ctrl.MPEG1 = value & (1 << 23); ctrl.picture_type = (value >> 24) & 0x7; if (value & (1 << 30)) { command = 0; in_FIFO.reset(); out_FIFO.reset(); // Note: A control reset does a forced command end, meaning it will // force the procedure of a command stopping even if there is // no command currently active, causing an interrupt to the core. // Fightbox relies on this behaviour to boot and play its first // two videos. finish_command(); } } bool ImageProcessingUnit::can_read_FIFO() { return out_FIFO.f.size() > 0; } bool ImageProcessingUnit::can_write_FIFO() { return in_FIFO.f.size() < 8; } uint128_t ImageProcessingUnit::read_FIFO() { uint128_t quad = out_FIFO.f.front(); out_FIFO.f.pop_front(); if (!out_FIFO.f.size()) { printf("ipu: clear ipu_from dreq\n"); dmac->ipu_from.dreq = 0; } return quad; } void ImageProcessingUnit::write_FIFO(uint128_t quad) { printf("ipu: Write FIFO: $%08X_%08X_%08X_%08X\n", quad.u32[3], quad.u32[2], quad.u32[1], quad.u32[0]); //Certain games (Theme Park, Neo Contra, etc) read command output without sending a command. //They expect to read the first word of a newly started IPU_TO transfer. if (in_FIFO.f.size() == 0 && !ctrl.busy) { command_output = quad.u32[0]; command_output = (command_output >> 24) | (((command_output >> 16) & 0xFF) << 8) | (((command_output >> 8) & 0xFF) << 16) | (command_output << 24); } if (in_FIFO.f.size() == 7) { dmac->ipu_to.dreq = 0; } if (in_FIFO.f.size() >= 8) { } in_FIFO.f.push_back(quad); in_FIFO.bit_cache_dirty = true; } struct ps2_ipu { ImageProcessingUnit* ipu; }; extern "C" struct ps2_ipu* ps2_ipu_create(void) { return (struct ps2_ipu*)malloc(sizeof(struct ps2_ipu)); } extern "C" void ps2_ipu_init(struct ps2_ipu* ipu, struct ps2_dmac* dmac, struct ps2_intc* intc) { ipu->ipu = new ImageProcessingUnit(intc, dmac); } extern "C" void ps2_ipu_reset(struct ps2_ipu* ipu) { ipu->ipu->reset(); } extern "C" uint64_t ps2_ipu_read64(struct ps2_ipu* ipu, uint32_t addr) { switch (addr) { case 0x10002000: return ipu->ipu->read_command(); case 0x10002010: return ipu->ipu->read_control(); case 0x10002020: return ipu->ipu->read_BP(); case 0x10002030: return ipu->ipu->read_top(); } std::fprintf(stderr, "ipu: Unhandled IPU read address %08x\n", addr); return 0; } extern "C" void ps2_ipu_write64(struct ps2_ipu* ipu, uint32_t addr, uint64_t data) { switch (addr) { case 0x10002000: ipu->ipu->write_command(data); return; case 0x10002010: ipu->ipu->write_control(data); return; case 0x10002020: return; // (W) ipu->ipu->write_BP(); return; case 0x10002030: return; // (W) ipu->ipu->write_top(); return; } fprintf(stderr, "ipu: Unhandled IPU write address %08x\n", addr); exit(1); } extern "C" uint128_t ps2_ipu_read128(struct ps2_ipu* ipu, uint32_t addr) { switch (addr) { case 0x10007000: return ipu->ipu->read_FIFO(); case 0x10007010: break; // (W) ipu->ipu->write_FIFO(); } fprintf(stderr, "ipu: Unhandled IPU read address %08x\n", addr); return { 0 }; // Return a zeroed quad if the address is unhandled } extern "C" void ps2_ipu_write128(struct ps2_ipu* ipu, uint32_t addr, uint128_t data) { switch (addr) { case 0x10007000: break; // (R) ipu->ipu->read_FIFO(); case 0x10007010: ipu->ipu->write_FIFO(data); return; } std::fprintf(stderr, "ipu: Unhandled IPU write address %08x\n", addr); exit(1); } int ps2_ipu_is_busy(struct ps2_ipu* ipu) { return ipu->ipu->ctrl.busy; } void ps2_ipu_run(struct ps2_ipu* ipu) { ipu->ipu->run(); } extern "C" void ps2_ipu_destroy(struct ps2_ipu* ipu) { delete ipu->ipu; free(ipu); } ================================================ FILE: src/ipu/ipu.h ================================================ #ifndef IPU_H #define IPU_H #ifdef __cplusplus extern "C" { #endif #include "ee/dmac.h" #include "ee/intc.h" #include "u128.h" #include struct ipu_fifo { uint128_t buf[8]; int bp; int wq; int rq; }; // struct ps2_ipu { // uint8_t iq[64]; // uint8_t vq[32]; // struct ipu_fifo in; // struct ipu_fifo out; // uint64_t result; // uint64_t cmd; // uint32_t ctrl; // uint32_t bp; // uint64_t top; // struct ps2_dmac* dmac; // struct ps2_intc* intc; // }; struct ps2_ipu; struct ps2_ipu* ps2_ipu_create(void); void ps2_ipu_init(struct ps2_ipu* ipu, struct ps2_dmac* dmac, struct ps2_intc* intc); void ps2_ipu_reset(struct ps2_ipu* ipu); int ps2_ipu_is_busy(struct ps2_ipu* ipu); uint64_t ps2_ipu_read64(struct ps2_ipu* ipu, uint32_t addr); uint128_t ps2_ipu_read128(struct ps2_ipu* ipu, uint32_t addr); void ps2_ipu_write64(struct ps2_ipu* ipu, uint32_t addr, uint64_t data); void ps2_ipu_write128(struct ps2_ipu* ipu, uint32_t addr, uint128_t data); void ps2_ipu_run(struct ps2_ipu* ipu); void ps2_ipu_destroy(struct ps2_ipu* ipu); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ipu/ipu.hpp ================================================ #ifndef IPU_HPP #define IPU_HPP #include #include #include "u128.h" #include "chromtable.hpp" #include "codedblockpattern.hpp" #include "dct_coeff_table0.hpp" #include "dct_coeff_table1.hpp" #include "lumtable.hpp" #include "mac_addr_inc.hpp" #include "mac_i_pic.hpp" #include "mac_p_pic.hpp" #include "mac_b_pic.hpp" #include "motioncode.hpp" // eegs includes #include "ee/dmac.h" #include "ee/intc.h" constexpr int RAW_BLOCK_SIZE = 0x180; constexpr int RGB_BLOCK_SIZE = 0x100; struct IPU_CTRL { uint8_t coded_block_pattern; bool error_code; bool start_code; uint8_t intra_DC_precision; bool alternate_scan; bool intra_VLC_table; bool nonlinear_Q_step; bool MPEG1; uint8_t picture_type; bool busy; }; enum class VDEC_STATE { ADVANCE, DECODE, DONE }; enum class BDEC_STATE { ADVANCE, GET_CBP, RESET_DC, BEGIN_DECODING, READ_COEFFS, LOAD_NEXT_BLOCK, DONE, CHECK_START_CODE }; enum class IDEC_STATE { DELAY, ADVANCE, MACRO_I_TYPE, DCT_TYPE, QSC, INIT_BDEC, READ_BLOCK, INIT_CSC, EXEC_CSC, CHECK_START_CODE, VALID_START_CODE, MACRO_INC, DONE }; enum class SETIQ_STATE { ADVANCE, POPULATE_TABLE }; struct IDEC_Command { IDEC_STATE state; uint32_t macro_type; bool decodes_dct; uint32_t qsc; IPU_FIFO temp_fifo; int blocks_decoded; }; struct BDEC_Command { BDEC_STATE state; IPU_FIFO* out_fifo; bool intra; bool reset_dc; bool check_start_code; int quantizer_step; int block_index; int subblock_index; //Four Y blocks, a Cb block, and a Cr block int16_t blocks[6][64]; int16_t* cur_block; int cur_channel; //0 = Y, 1 = Cb, 2 = Cr enum class READ_COEFF { INIT, READ_DC_DIFF, CHECK_END, COEFF, SKIP_END }; enum class READ_DIFF { SIZE, DIFF, }; uint32_t dc_size; int16_t dc_diff; int16_t dc_predictor[3]; READ_COEFF read_coeff_state; READ_DIFF read_diff_state; }; enum class CSC_STATE { BEGIN, READ, CONVERT, DONE }; struct CSC_Command { CSC_STATE state; int macroblocks; bool use_RGB16; bool use_dithering; uint8_t block[RAW_BLOCK_SIZE]; int block_index; }; enum class PACK_STATE { BEGIN, READ, CONVERT, DONE }; struct PACK_Command { PACK_STATE state; int macroblocks; bool use_RGB16; bool use_dithering; uint8_t block[4 * RGB_BLOCK_SIZE]; int block_index; }; struct ImageProcessingUnit { private: struct ps2_intc* intc; struct ps2_dmac* dmac; DCT_Coeff_Table0 dct_coeff0; DCT_Coeff_Table1 dct_coeff1; DCT_Coeff* dct_coeff; ChromTable chrom_table; CodedBlockPattern cbp; LumTable lum_table; MacroblockAddrInc macroblock_increment; Macroblock_IPic macroblock_I_pic; Macroblock_PPic macroblock_P_pic; Macroblock_BPic macroblock_B_pic; MotionCode motioncode; VLC_Table* VDEC_table; IPU_FIFO in_FIFO, out_FIFO; int8_t dither_mtx[4][4]; uint8_t intra_IQ[0x40], nonintra_IQ[0x40]; uint16_t VQCLUT[16]; uint32_t TH0, TH1; unsigned int crcb_map[0x100]; static uint32_t inverse_scan_zigzag[0x40]; static uint32_t inverse_scan_alternate[0x40]; static uint32_t quantizer_linear[0x20]; static uint32_t quantizer_nonlinear[0x20]; bool command_decoding; uint8_t command; uint32_t command_option; uint32_t command_output; int bytes_left; IDEC_Command idec; BDEC_Command bdec; VDEC_STATE vdec_state, fdec_state; CSC_Command csc; SETIQ_STATE setiq_state; PACK_Command pack; double IDCT_table[8][8]; void finish_command(); bool process_IDEC(); bool process_BDEC(); void inverse_scan(int16_t* block); void dequantize(int16_t* block); void prepare_IDCT(); void perform_IDCT(const int16_t* pUV, int16_t* pXY); bool BDEC_read_coeffs(); bool BDEC_read_diff(); void convert_RGB32_to_RGB16(const uint8_t* rgb32, uint16_t *rgb16, bool dithering); void process_VDEC(); void process_FDEC(); bool process_CSC(); bool process_PACK(); public: IPU_CTRL ctrl; ImageProcessingUnit(struct ps2_intc* intc, struct ps2_dmac* dmac); void reset(); void run(); uint64_t read_command(); uint32_t read_control(); uint32_t read_BP(); uint64_t read_top(); void write_command(uint32_t value); void write_control(uint32_t value); bool can_read_FIFO(); bool can_write_FIFO(); uint128_t read_FIFO(); void write_FIFO(uint128_t quad); }; #endif // IPU_HPP ================================================ FILE: src/ipu/ipu_fifo.cpp ================================================ #include #include #include "ipu_fifo.hpp" bool IPU_FIFO::get_bits(uint32_t &data, int bits) { int bits_available = (f.size() * 128) - bit_pointer; if (bits_available < bits || bits_available == 0) { data = 0; return false; } if (bit_cache_dirty) { uint8_t temp[32]; *(uint128_t*)&temp[0] = f[0]; if (f.size() > 1) *(uint128_t*)&temp[16] = f[1]; uint8_t blorp[8]; int index = (bit_pointer & ~0x1F) / 8; //MPEG is big-endian... for (int i = 0; i < 8; i++) { int fifo_index = index + i; blorp[7 - i] = temp[fifo_index]; } bit_cache_dirty = false; cached_bits = *(uint64_t*)&blorp[0]; } int shift = 64 - (bit_pointer % 32) - bits; uint64_t mask = ~0x0ULL >> (64 - bits); data = (cached_bits >> shift) & mask; return true; } bool IPU_FIFO::advance_stream(uint8_t amount) { if (amount > 32) amount = 32; //printf("Advance stream: %d + %d = %d\n", bit_pointer - amount, amount, bit_pointer); if ((bit_pointer + amount) > (f.size() * 128)) { return false; } uint32_t old_words = bit_pointer / 32; uint32_t new_words = (bit_pointer + amount) / 32; bit_cache_dirty |= (old_words != new_words); bit_pointer += amount; while (bit_pointer >= 128) { bit_pointer -= 128; f.pop_front(); bit_cache_dirty = true; } return true; } void IPU_FIFO::reset() { std::deque empty; f.swap(empty); bit_pointer = 0; cached_bits = 0; bit_cache_dirty = true; } void IPU_FIFO::byte_align() { int bits = bit_pointer & 0x7; if (bits) advance_stream(8 - bits); } ================================================ FILE: src/ipu/ipu_fifo.hpp ================================================ #ifndef IPU_FIFO_HPP #define IPU_FIFO_HPP #include #include #include "u128.h" struct IPU_FIFO { std::deque f; int bit_pointer; uint64_t cached_bits; bool bit_cache_dirty; bool get_bits(uint32_t& data, int bits); bool advance_stream(uint8_t amount); void reset(); void byte_align(); }; #endif // IPU_FIFO_HPP ================================================ FILE: src/ipu/lumtable.cpp ================================================ #include "lumtable.hpp" VLC_Entry LumTable::table[] = { {0x0000, 1, 2}, {0x0001, 2, 2}, {0x0004, 0, 3}, {0x0005, 3, 3}, {0x0006, 4, 3}, {0x000E, 5, 4}, {0x001E, 6, 5}, {0x003E, 7, 6}, {0x007E, 8, 7}, {0x00FE, 9, 8}, {0x01FE, 10, 9}, {0x01FF, 11, 9} }; unsigned int LumTable::index_table[9] = { 0, 0, 2, 5, 6, 7, 8, 9, 10, }; LumTable::LumTable() : VLC_Table(table, SIZE, 9, index_table) { } ================================================ FILE: src/ipu/lumtable.hpp ================================================ #ifndef LUMTABLE_HPP #define LUMTABLE_HPP #include "vlc_table.hpp" class LumTable : public VLC_Table { private: static VLC_Entry table[]; static unsigned int index_table[]; constexpr static int SIZE = 12; public: LumTable(); }; #endif // LUMTABLE_HPP ================================================ FILE: src/ipu/mac_addr_inc.cpp ================================================ #include "mac_addr_inc.hpp" VLC_Entry MacroblockAddrInc::table[] = { {0x1, 0x10001, 1}, {0x3, 0x30002, 3}, {0x2, 0x30003, 3}, {0x3, 0x40004, 4}, {0x2, 0x40005, 4}, {0x3, 0x50006, 5}, {0x2, 0x50007, 5}, {0x7, 0x70008, 7}, {0x6, 0x70009, 7}, {0xB, 0x8000A, 8}, {0xA, 0x8000B, 8}, {0x9, 0x8000C, 8}, {0x8, 0x8000D, 8}, {0x7, 0x8000E, 8}, {0x6, 0x8000F, 8}, {0x17, 0xA0010, 10}, //16 {0x16, 0xA0011, 10}, {0x15, 0xA0012, 10}, {0x14, 0xA0013, 10}, {0x13, 0xA0014, 10}, //20 {0x12, 0xA0015, 10}, {0x23, 0xB0016, 11}, {0x22, 0xB0017, 11}, {0x21, 0xB0018, 11}, //24 {0x20, 0xB0019, 11}, {0x1F, 0xB001A, 11}, {0x1E, 0xB001B, 11}, {0x1D, 0xB001C, 11}, //28 {0x1C, 0xB001D, 11}, {0x1B, 0xB001E, 11}, {0x1A, 0xB001F, 11}, {0x19, 0xB0020, 11}, {0x18, 0xB0021, 11}, {0xF, 0xB0022, 11}, {0x8, 0xB0023, 11} }; unsigned int MacroblockAddrInc::index_table[11] = { 0, 1, 1, 3, 5, 5, 7, 9, 9, 15, 21, }; MacroblockAddrInc::MacroblockAddrInc() : VLC_Table(table, SIZE, 11, index_table) { } ================================================ FILE: src/ipu/mac_addr_inc.hpp ================================================ #ifndef MAC_ADDR_INC_H #define MAC_ADDR_INC_H #include "vlc_table.hpp" class MacroblockAddrInc : public VLC_Table { private: static VLC_Entry table[]; static unsigned int index_table[]; constexpr static int SIZE = 35; public: MacroblockAddrInc(); }; #endif // MAC_ADDR_INC_H ================================================ FILE: src/ipu/mac_b_pic.cpp ================================================ #include "mac_b_pic.hpp" VLC_Entry Macroblock_BPic::table[] = { {0x2, 0x2000C, 2}, {0x3, 0x2000E, 2}, {0x2, 0x30004, 3}, {0x3, 0x30006, 3}, {0x2, 0x40008, 4}, {0x3, 0x4000A, 4}, {0x3, 0x50001, 5}, {0x2, 0x5001E, 5}, {0x3, 0x6001A, 6}, {0x2, 0x60016, 6}, {0x1, 0x60011, 6} }; unsigned int Macroblock_BPic::index_table[6] = { 0, 0, 2, 4, 6, 8, }; Macroblock_BPic::Macroblock_BPic() : VLC_Table(table, SIZE, 6, index_table) { } ================================================ FILE: src/ipu/mac_b_pic.hpp ================================================ #ifndef MAC_B_PIC_HPP #define MAC_B_PIC_HPP #include "vlc_table.hpp" class Macroblock_BPic : public VLC_Table { private: static VLC_Entry table[]; static unsigned int index_table[]; constexpr static int SIZE = 11; public: Macroblock_BPic(); }; #endif // MAC_B_PIC_HPP ================================================ FILE: src/ipu/mac_i_pic.cpp ================================================ #include "mac_i_pic.hpp" VLC_Entry Macroblock_IPic::table[] = { {0x1, 0x10001, 1}, {0x1, 0x20011, 2} }; unsigned int Macroblock_IPic::index_table[2] = { 0, 1, }; Macroblock_IPic::Macroblock_IPic() : VLC_Table(table, SIZE, 2, index_table) { } ================================================ FILE: src/ipu/mac_i_pic.hpp ================================================ #ifndef MAC_I_PIC_HPP #define MAC_I_PIC_HPP #include "vlc_table.hpp" class Macroblock_IPic : public VLC_Table { private: static VLC_Entry table[]; static unsigned int index_table[]; constexpr static int SIZE = 2; public: Macroblock_IPic(); }; #endif // MAC_I_PIC_HPP ================================================ FILE: src/ipu/mac_p_pic.cpp ================================================ #include "mac_p_pic.hpp" VLC_Entry Macroblock_PPic::table[] = { {0x1, 0x1000A, 1}, {0x1, 0x20002, 2}, {0x1, 0x30008, 3}, {0x3, 0x50001, 5}, {0x2, 0x5001A, 5}, {0x1, 0x50012, 5}, {0x1, 0x60011, 6} }; unsigned int Macroblock_PPic::index_table[6] = { 0, 1, 2, 2, 3, 6, }; Macroblock_PPic::Macroblock_PPic() : VLC_Table(table, SIZE, 6, index_table) { } ================================================ FILE: src/ipu/mac_p_pic.hpp ================================================ #ifndef MAC_P_PIC_HPP #define MAC_P_PIC_HPP #include "vlc_table.hpp" class Macroblock_PPic : public VLC_Table { private: static VLC_Entry table[]; static unsigned int index_table[]; constexpr static int SIZE = 7; public: Macroblock_PPic(); }; #endif // MAC_P_PIC_HPP ================================================ FILE: src/ipu/motioncode.cpp ================================================ #include "motioncode.hpp" VLC_Entry MotionCode::table[] = { {0x1, 0x10000, 1}, {0x2, 0x30001, 3}, {0x3, 0x3FFFF, 3}, {0x2, 0x40002, 4}, {0x3, 0x4FFFE, 4}, {0x2, 0x50003, 5}, {0x3, 0x5FFFD, 5}, {0x6, 0x70004, 7}, {0x7, 0x7FFFC, 7}, {0xA, 0x80005, 8}, {0xB, 0x8FFFB, 8}, {0x8, 0x80006, 8}, {0x9, 0x8FFFA, 8}, {0x6, 0x80007, 8}, {0x7, 0x8FFF9, 8}, {0x16, 0xA0008, 10}, {0x17, 0xAFFF8, 10}, {0x14, 0xA0009, 10}, {0x15, 0xAFFF7, 10}, {0x12, 0xA000A, 10}, {0x13, 0xAFFF6, 10}, {0x22, 0xB000B, 11}, {0x23, 0xBFFF5, 11}, {0x20, 0xB000C, 11}, {0x21, 0xBFFF4, 11}, {0x1E, 0xB000D, 11}, {0x1F, 0xBFFF3, 11}, {0x1C, 0xB000E, 11}, {0x1D, 0xBFFF2, 11}, {0x1A, 0xB000F, 11}, {0x1B, 0xBFFF1, 11}, {0x18, 0xB0010, 11}, {0x19, 0xBFFF0, 11} }; unsigned int MotionCode::index_table[11] = { 0, 1, 1, 3, 5, 7, 7, 9, 15, 15, 21, }; MotionCode::MotionCode() : VLC_Table(table, SIZE, 11, index_table) { } ================================================ FILE: src/ipu/motioncode.hpp ================================================ #ifndef MOTIONCODE_HPP #define MOTIONCODE_HPP #include "vlc_table.hpp" class MotionCode : public VLC_Table { private: static VLC_Entry table[]; static unsigned int index_table[]; constexpr static int SIZE = 33; public: MotionCode(); }; #endif // MOTIONCODE_HPP ================================================ FILE: src/ipu/vlc_table.cpp ================================================ #include #include #include "vlc_table.hpp" VLC_Table::VLC_Table(VLC_Entry* table, int table_size, int max_bits, unsigned int* index_table) : table(table), table_size(table_size), max_bits(max_bits), index_table(index_table) { } bool VLC_Table::peek_symbol(IPU_FIFO &FIFO, VLC_Entry &entry) { uint32_t key; for (int i = 0; i < max_bits; i++) { int bits = i + 1; if (!FIFO.get_bits(key, bits)) return false; for (int j = index_table[i]; j < table_size; j++) { if (bits != table[j].bits) break; if (key == table[j].key) { entry = table[j]; return true; } } } throw VLC_Error("VLC symbol not found"); return false; } bool VLC_Table::get_symbol(IPU_FIFO& FIFO, uint32_t &result) { VLC_Entry entry; if (!peek_symbol(FIFO, entry)) return false; FIFO.advance_stream(entry.bits); result = entry.value; return true; } ================================================ FILE: src/ipu/vlc_table.hpp ================================================ #ifndef VLC_TABLE_HPP #define VLC_TABLE_HPP #include #include #include #include "ipu_fifo.hpp" struct VLC_Entry { uint32_t key; uint32_t value; uint8_t bits; }; class VLC_Error : public std::runtime_error { using std::runtime_error::runtime_error; }; class VLC_Table { private: VLC_Entry* table; int table_size, max_bits; unsigned int* index_table; protected: VLC_Table(VLC_Entry* table, int table_size, int max_bits, unsigned int* index_table); public: bool peek_symbol(IPU_FIFO& FIFO, VLC_Entry& entry); bool get_symbol(IPU_FIFO& FIFO, uint32_t& result); }; #endif // VLC_TABLE_HPP ================================================ FILE: src/list.c ================================================ #include "list.h" #include #include list_t* list_create(void) { list_t* list = malloc(sizeof(list_t)); list_init(list); return list; } void list_init(list_t* list) { list->first = NULL; list->last = NULL; list->size = 0; } void list_push_front(list_t* list, void* data) { node_t* node = malloc(sizeof(node_t)); node->data = data; node->next = list->first; list->first = node; if (!list->last) list->last = list->first; ++list->size; } void list_push_back(list_t* list, void* data) { node_t* node = malloc(sizeof(node_t)); node->data = data; node->next = NULL; if (!list->last) { list->first = node; list->last = node; } else { list->last->next = node; list->last = node; } ++list->size; } void list_pop_front(list_t* list) { if (!list->first) return; node_t* next = list->first->next; free(list->first); list->first = next; --list->size; } void list_pop_back(list_t* list) { if (!list->last) return; node_t* node = list->first; while (node->next != list->last) node = node->next; free(node->next); node->next = NULL; list->last = node; } node_t* list_front(list_t* list) { return list->first; } node_t* list_back(list_t* list) { return list->last; } node_t* list_at(list_t* list, int index) { if (index > list->size) return NULL; node_t* node = list->first; for (int i = 0; i < index; i++) node = node->next; return node; } void list_iterate(list_t* list, void (*func)(void*)) { node_t* node = list->first; while (node) { func(node->data); node = node->next; } } void list_destroy(list_t* list) { node_t* node = list->first; while (node) { node_t* next = node->next; free(node); node = next; } free(list); } ================================================ FILE: src/list.h ================================================ #ifndef LIST_H #define LIST_H #include typedef struct node_t node_t; typedef struct node_t { node_t* next; void* data; } node_t; typedef struct { node_t* first; node_t* last; size_t size; } list_t; list_t* list_create(void); void list_init(list_t* list); void list_push_front(list_t* list, void* data); void list_push_back(list_t* list, void* data); void list_pop_front(list_t* list); void list_pop_back(list_t* list); node_t* list_front(list_t* list); node_t* list_back(list_t* list); node_t* list_at(list_t* list, int index); void list_iterate(list_t* list, void (*func)(void*)); void list_destroy(list_t* list); #endif ================================================ FILE: src/md5.c ================================================ /* * Derived from the RSA Data Security, Inc. MD5 Message-Digest Algorithm * and modified slightly to be functionally identical but condensed into control structures. */ #include "md5.h" /* * Constants defined by the MD5 algorithm */ #define A 0x67452301 #define B 0xefcdab89 #define C 0x98badcfe #define D 0x10325476 static uint32_t S[] = {7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 7, 12, 17, 22, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 5, 9, 14, 20, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 4, 11, 16, 23, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21, 6, 10, 15, 21}; static uint32_t K[] = {0xd76aa478, 0xe8c7b756, 0x242070db, 0xc1bdceee, 0xf57c0faf, 0x4787c62a, 0xa8304613, 0xfd469501, 0x698098d8, 0x8b44f7af, 0xffff5bb1, 0x895cd7be, 0x6b901122, 0xfd987193, 0xa679438e, 0x49b40821, 0xf61e2562, 0xc040b340, 0x265e5a51, 0xe9b6c7aa, 0xd62f105d, 0x02441453, 0xd8a1e681, 0xe7d3fbc8, 0x21e1cde6, 0xc33707d6, 0xf4d50d87, 0x455a14ed, 0xa9e3e905, 0xfcefa3f8, 0x676f02d9, 0x8d2a4c8a, 0xfffa3942, 0x8771f681, 0x6d9d6122, 0xfde5380c, 0xa4beea44, 0x4bdecfa9, 0xf6bb4b60, 0xbebfbc70, 0x289b7ec6, 0xeaa127fa, 0xd4ef3085, 0x04881d05, 0xd9d4d039, 0xe6db99e5, 0x1fa27cf8, 0xc4ac5665, 0xf4292244, 0x432aff97, 0xab9423a7, 0xfc93a039, 0x655b59c3, 0x8f0ccc92, 0xffeff47d, 0x85845dd1, 0x6fa87e4f, 0xfe2ce6e0, 0xa3014314, 0x4e0811a1, 0xf7537e82, 0xbd3af235, 0x2ad7d2bb, 0xeb86d391}; /* * Padding used to make the size (in bits) of the input congruent to 448 mod 512 */ static uint8_t PADDING[] = {0x80, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00}; /* * Bit-manipulation functions defined by the MD5 algorithm */ #define F(X, Y, Z) ((X & Y) | (~X & Z)) #define G(X, Y, Z) ((X & Z) | (Y & ~Z)) #define H(X, Y, Z) (X ^ Y ^ Z) #define I(X, Y, Z) (Y ^ (X | ~Z)) /* * Rotates a 32-bit word left by n bits */ uint32_t rotateLeft(uint32_t x, uint32_t n){ return (x << n) | (x >> (32 - n)); } /* * Initialize a context */ void md5_init(struct md5_context* ctx){ ctx->size = (uint64_t)0; ctx->buffer[0] = (uint32_t)A; ctx->buffer[1] = (uint32_t)B; ctx->buffer[2] = (uint32_t)C; ctx->buffer[3] = (uint32_t)D; } /* * Add some amount of input to the context * * If the input fills out a block of 512 bits, apply the algorithm (md5_step) * and save the result in the buffer. Also updates the overall size. */ void md5_update(struct md5_context* ctx, uint8_t* input_buffer, size_t input_len){ uint32_t input[16]; unsigned int offset = ctx->size % 64; ctx->size += (uint64_t)input_len; // Copy each byte in input_buffer into the next space in our context input for(unsigned int i = 0; i < input_len; ++i){ ctx->input[offset++] = (uint8_t)*(input_buffer + i); // If we've filled our context input, copy it into our local array input // then reset the offset to 0 and fill in a new buffer. // Every time we fill out a chunk, we run it through the algorithm // to enable some back and forth between cpu and i/o if(offset % 64 == 0){ for(unsigned int j = 0; j < 16; ++j){ // Convert to little-endian // The local variable `input` our 512-bit chunk separated into 32-bit words // we can use in calculations input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 | (uint32_t)(ctx->input[(j * 4) + 2]) << 16 | (uint32_t)(ctx->input[(j * 4) + 1]) << 8 | (uint32_t)(ctx->input[(j * 4)]); } md5_step(ctx->buffer, input); offset = 0; } } } /* * Pad the current input to get to 448 bytes, append the size in bits to the very end, * and save the result of the final iteration into digest. */ void md5_finalize(struct md5_context* ctx){ uint32_t input[16]; unsigned int offset = ctx->size % 64; unsigned int padding_length = offset < 56 ? 56 - offset : (56 + 64) - offset; // Fill in the padding and undo the changes to size that resulted from the update md5_update(ctx, PADDING, padding_length); ctx->size -= (uint64_t)padding_length; // Do a final update (internal to this function) // Last two 32-bit words are the two halves of the size (converted from bytes to bits) for(unsigned int j = 0; j < 14; ++j){ input[j] = (uint32_t)(ctx->input[(j * 4) + 3]) << 24 | (uint32_t)(ctx->input[(j * 4) + 2]) << 16 | (uint32_t)(ctx->input[(j * 4) + 1]) << 8 | (uint32_t)(ctx->input[(j * 4)]); } input[14] = (uint32_t)(ctx->size * 8); input[15] = (uint32_t)((ctx->size * 8) >> 32); md5_step(ctx->buffer, input); // Move the result into digest (convert from little-endian) for(unsigned int i = 0; i < 4; ++i){ ctx->digest[(i * 4) + 0] = (uint8_t)((ctx->buffer[i] & 0x000000FF)); ctx->digest[(i * 4) + 1] = (uint8_t)((ctx->buffer[i] & 0x0000FF00) >> 8); ctx->digest[(i * 4) + 2] = (uint8_t)((ctx->buffer[i] & 0x00FF0000) >> 16); ctx->digest[(i * 4) + 3] = (uint8_t)((ctx->buffer[i] & 0xFF000000) >> 24); } } /* * Step on 512 bits of input with the main MD5 algorithm. */ void md5_step(uint32_t *buffer, uint32_t *input){ uint32_t AA = buffer[0]; uint32_t BB = buffer[1]; uint32_t CC = buffer[2]; uint32_t DD = buffer[3]; uint32_t E; unsigned int j; for(unsigned int i = 0; i < 64; ++i){ switch(i / 16){ case 0: E = F(BB, CC, DD); j = i; break; case 1: E = G(BB, CC, DD); j = ((i * 5) + 1) % 16; break; case 2: E = H(BB, CC, DD); j = ((i * 3) + 5) % 16; break; default: E = I(BB, CC, DD); j = (i * 7) % 16; break; } uint32_t temp = DD; DD = CC; CC = BB; BB = BB + rotateLeft(AA + E + K[i] + input[j], S[i]); AA = temp; } buffer[0] += AA; buffer[1] += BB; buffer[2] += CC; buffer[3] += DD; } /* * Functions that run the algorithm on the provided input and put the digest into result. * result should be able to store 16 bytes. */ void md5_hash_string(char *input, uint8_t* result){ struct md5_context ctx; md5_init(&ctx); md5_update(&ctx, (uint8_t* )input, strlen(input)); md5_finalize(&ctx); memcpy(result, ctx.digest, 16); } void md5_hash_file(FILE *file, uint8_t* result){ char *input_buffer = malloc(1024); size_t input_size = 0; struct md5_context ctx; md5_init(&ctx); while((input_size = fread(input_buffer, 1, 1024, file)) > 0){ md5_update(&ctx, (uint8_t* )input_buffer, input_size); } md5_finalize(&ctx); free(input_buffer); memcpy(result, ctx.digest, 16); } ================================================ FILE: src/md5.h ================================================ #ifndef MD5_H #define MD5_H #include #include #include #include struct md5_context { uint64_t size; // Size of input in bytes uint32_t buffer[4]; // Current accumulation of hash uint8_t input[64]; // Input to be used in the next step uint8_t digest[16]; // Result of algorithm }; void md5_init(struct md5_context* ctx); void md5_update(struct md5_context* ctx, uint8_t* input, size_t input_len); void md5_finalize(struct md5_context* ctx); void md5_step(uint32_t* buffer, uint32_t* input); void md5_hash_string(char* input, uint8_t* result); void md5_hash_file(FILE* file, uint8_t* result); #endif ================================================ FILE: src/ps2.c ================================================ #include #include #include #include #include "ps2.h" #include "rom.h" struct ps2_state* ps2_create(void) { return malloc(sizeof(struct ps2_state)); } void ps2_init(struct ps2_state* ps2) { memset(ps2, 0, sizeof(struct ps2_state)); ps2->sched = sched_create(); sched_init(ps2->sched); ps2->ee = ee_create(); ps2->vu0 = vu_create(); ps2->vu1 = vu_create(); ps2->iop = iop_create(); ps2->ee_bus = ee_bus_create(); ps2->iop_bus = iop_bus_create(); ps2->gif = ps2_gif_create(); ps2->vif0 = ps2_vif_create(); ps2->vif1 = ps2_vif_create(); ps2->gs = ps2_gs_create(); ps2->ipu = ps2_ipu_create(); ps2->ee_dma = ps2_dmac_create(); ps2->ee_ram = ps2_ram_create(); ps2->ee_intc = ps2_intc_create(); ps2->ee_timers = ps2_ee_timers_create(); ps2->iop_dma = ps2_iop_dma_create(); ps2->iop_spr = ps2_ram_create(); ps2->iop_intc = ps2_iop_intc_create(); ps2->iop_timers = ps2_iop_timers_create(); ps2->iop_ram = ps2_ram_create(); ps2->bios = ps2_bios_create(); ps2->rom1 = ps2_bios_create(); ps2->rom2 = ps2_bios_create(); ps2->sif = ps2_sif_create(); ps2->cdvd = ps2_cdvd_create(); ps2->sio2 = ps2_sio2_create(); ps2->spu2 = ps2_spu2_create(); ps2->usb = ps2_usb_create(); ps2->fw = ps2_fw_create(); ps2->sbus = ps2_sbus_create(); ps2->dev9 = ps2_dev9_create(); ps2->speed = ps2_speed_create(); // Initialize EE ee_bus_init(ps2->ee_bus, NULL); struct ee_bus_s ee_bus_data; ee_bus_data.read8 = ee_bus_read8; ee_bus_data.read16 = ee_bus_read16; ee_bus_data.read32 = ee_bus_read32; ee_bus_data.read64 = ee_bus_read64; ee_bus_data.read128 = ee_bus_read128; ee_bus_data.write8 = ee_bus_write8; ee_bus_data.write16 = ee_bus_write16; ee_bus_data.write32 = ee_bus_write32; ee_bus_data.write64 = ee_bus_write64; ee_bus_data.write128 = ee_bus_write128; ee_bus_data.udata = ps2->ee_bus; ee_init(ps2->ee, ps2->vu0, ps2->vu1, RAM_SIZE_32MB, ee_bus_data); vu_init(ps2->vu0, 0, ps2->gif, ps2->vif0, ps2->vu1); vu_init(ps2->vu1, 1, ps2->gif, ps2->vif1, ps2->vu1); // Initialize IOP iop_bus_init(ps2->iop_bus, NULL); struct iop_bus_s iop_bus_data; iop_bus_data.read8 = iop_bus_read8; iop_bus_data.read16 = iop_bus_read16; iop_bus_data.read32 = iop_bus_read32; iop_bus_data.write8 = iop_bus_write8; iop_bus_data.write16 = iop_bus_write16; iop_bus_data.write32 = iop_bus_write32; iop_bus_data.udata = ps2->iop_bus; iop_init(ps2->iop, iop_bus_data); // Initialize devices ps2_dmac_init(ps2->ee_dma, ps2->sif, ps2->iop_dma, ee_get_spr(ps2->ee), ps2->ee, ps2->sched, ps2->ee_bus); ps2_ram_init(ps2->ee_ram, RAM_SIZE_32MB); ps2_gif_init(ps2->gif, ps2->vu1, ps2->gs); ps2_vif_init(ps2->vif0, 0, ps2->vu0, ps2->gif, ps2->ee_intc, ps2->sched, ps2->ee_bus); ps2_vif_init(ps2->vif1, 1, ps2->vu1, ps2->gif, ps2->ee_intc, ps2->sched, ps2->ee_bus); ps2_gs_init(ps2->gs, ps2->ee_intc, ps2->iop_intc, ps2->ee_timers, ps2->iop_timers, ps2->sched); ps2_ipu_init(ps2->ipu, ps2->ee_dma, ps2->ee_intc); ps2_intc_init(ps2->ee_intc, ps2->ee, ps2->sched); ps2_ee_timers_init(ps2->ee_timers, ps2->ee_intc, ps2->sched); ps2_ram_init(ps2->iop_ram, RAM_SIZE_2MB); 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); ps2_ram_init(ps2->iop_spr, RAM_SIZE_1KB); ps2_iop_intc_init(ps2->iop_intc, ps2->iop); ps2_iop_timers_init(ps2->iop_timers, ps2->iop_intc, ps2->sched); ps2_cdvd_init(ps2->cdvd, ps2->iop_dma, ps2->iop_intc, ps2->sched); ps2_sio2_init(ps2->sio2, ps2->iop_dma, ps2->iop_intc, ps2->sched); ps2_spu2_init(ps2->spu2, ps2->iop_dma, ps2->iop_intc, ps2->sched); ps2_usb_init(ps2->usb); ps2_fw_init(ps2->fw, ps2->iop_intc); ps2_sbus_init(ps2->sbus, ps2->ee_intc, ps2->iop_intc, ps2->sched); ps2_dev9_init(ps2->dev9, DEV9_TYPE_EXPBAY); ps2_speed_init(ps2->speed, ps2->iop_intc); ps2_bios_init(ps2->bios); ps2_bios_init(ps2->rom1); ps2_bios_init(ps2->rom2); ps2_sif_init(ps2->sif, ps2->iop_intc); // Initialize bus pointers iop_bus_init_bios(ps2->iop_bus, ps2->bios); iop_bus_init_rom1(ps2->iop_bus, ps2->rom1); iop_bus_init_rom2(ps2->iop_bus, ps2->rom2); iop_bus_init_iop_ram(ps2->iop_bus, ps2->iop_ram); iop_bus_init_iop_spr(ps2->iop_bus, ps2->iop_spr); iop_bus_init_sif(ps2->iop_bus, ps2->sif); iop_bus_init_dma(ps2->iop_bus, ps2->iop_dma); iop_bus_init_intc(ps2->iop_bus, ps2->iop_intc); iop_bus_init_timers(ps2->iop_bus, ps2->iop_timers); iop_bus_init_cdvd(ps2->iop_bus, ps2->cdvd); iop_bus_init_sio2(ps2->iop_bus, ps2->sio2); iop_bus_init_spu2(ps2->iop_bus, ps2->spu2); iop_bus_init_usb(ps2->iop_bus, ps2->usb); iop_bus_init_fw(ps2->iop_bus, ps2->fw); iop_bus_init_sbus(ps2->iop_bus, ps2->sbus); iop_bus_init_dev9(ps2->iop_bus, ps2->dev9); iop_bus_init_speed(ps2->iop_bus, ps2->speed); ee_bus_init_bios(ps2->ee_bus, ps2->bios); ee_bus_init_rom1(ps2->ee_bus, ps2->rom1); ee_bus_init_rom2(ps2->ee_bus, ps2->rom2); ee_bus_init_iop_ram(ps2->ee_bus, ps2->iop_ram); ee_bus_init_sif(ps2->ee_bus, ps2->sif); ee_bus_init_dmac(ps2->ee_bus, ps2->ee_dma); ee_bus_init_intc(ps2->ee_bus, ps2->ee_intc); ee_bus_init_timers(ps2->ee_bus, ps2->ee_timers); ee_bus_init_gif(ps2->ee_bus, ps2->gif); ee_bus_init_vif0(ps2->ee_bus, ps2->vif0); ee_bus_init_vif1(ps2->ee_bus, ps2->vif1); ee_bus_init_gs(ps2->ee_bus, ps2->gs); ee_bus_init_ipu(ps2->ee_bus, ps2->ipu); ee_bus_init_vu0(ps2->ee_bus, ps2->vu0); ee_bus_init_vu1(ps2->ee_bus, ps2->vu1); ee_bus_init_cdvd(ps2->ee_bus, ps2->cdvd); ee_bus_init_usb(ps2->ee_bus, ps2->usb); ee_bus_init_sbus(ps2->ee_bus, ps2->sbus); ee_bus_init_dev9(ps2->ee_bus, ps2->dev9); ee_bus_init_speed(ps2->ee_bus, ps2->speed); ee_bus_init_ram(ps2->ee_bus, ps2->ee_ram); ps2_ipu_reset(ps2->ipu); ps2->ee_cycles = 0; ps2->timescale = 1; } void ps2_init_tty_handler(struct ps2_state* ps2, int tty, void (*handler)(void*, char), void* udata) { switch (tty) { case PS2_TTY_EE: ee_bus_init_kputchar(ps2->ee_bus, handler, udata); break; case PS2_TTY_IOP: iop_init_kputchar(ps2->iop, handler, udata); break; case PS2_TTY_SYSMEM: iop_init_sm_putchar(ps2->iop, handler, udata); break; } } void ps2_boot_file(struct ps2_state* ps2, const char* path) { ps2_reset(ps2); while (ee_get_pc(ps2->ee) != 0x00082000) ps2_cycle(ps2); uint32_t i; // Find rom0:OSDSYS string for (i = 0; i < RAM_SIZE_32MB; i += 0x10) { char* ptr = (char*)&ps2->ee_ram->buf[i]; if (!strncmp(ptr, "rom0:OSDSYS", 12)) { printf("eegs: Found OSDSYS path at 0x%08x\n", i); sprintf(ptr, "%s", path); } } } int ps2_load_bios(struct ps2_state* ps2, const char* path) { if (ps2_bios_load(ps2->bios, path)) { return 0; } ee_bus_init_fastmem(ps2->ee_bus, ps2->ee_ram->size, ps2->iop_ram->size); iop_bus_init_fastmem(ps2->iop_bus, ps2->iop_ram->size); if (ps2->system == PS2_SYSTEM_AUTO) { ps2->rom0_info = ps2_rom0_search(ps2->bios->buf, ps2->bios->size + 1); ps2_set_system(ps2, ps2->rom0_info.system); ps2->detected_system = ps2->rom0_info.system; } return 1; } int ps2_load_rom1(struct ps2_state* ps2, const char* path) { if (ps2_bios_load(ps2->rom1, path)) { return 0; } ps2->rom1_info = ps2_rom1_search(ps2->rom1->buf, ps2->rom1->size + 1); return 1; } int ps2_load_rom2(struct ps2_state* ps2, const char* path) { if (!ps2_bios_load(ps2->rom2, path)) { return 0; } return 1; } void ps2_reset(struct ps2_state* ps2) { sched_reset(ps2->sched); ee_reset(ps2->ee); iop_reset(ps2->iop); vu_init(ps2->vu0, 0, ps2->gif, ps2->vif0, ps2->vu1); vu_init(ps2->vu1, 1, ps2->gif, ps2->vif1, ps2->vu1); ps2_dmac_init(ps2->ee_dma, ps2->sif, ps2->iop_dma, ee_get_spr(ps2->ee), ps2->ee, ps2->sched, ps2->ee_bus); ps2_vif_init(ps2->vif0, 0, ps2->vu0, ps2->gif, ps2->ee_intc, ps2->sched, ps2->ee_bus); ps2_vif_init(ps2->vif1, 1, ps2->vu1, ps2->gif, ps2->ee_intc, ps2->sched, ps2->ee_bus); ps2_intc_init(ps2->ee_intc, ps2->ee, ps2->sched); ps2_ee_timers_init(ps2->ee_timers, ps2->ee_intc, ps2->sched); 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); ps2_iop_intc_init(ps2->iop_intc, ps2->iop); ps2_iop_timers_init(ps2->iop_timers, ps2->iop_intc, ps2->sched); ps2_spu2_init(ps2->spu2, ps2->iop_dma, ps2->iop_intc, ps2->sched); ps2_usb_init(ps2->usb); ps2_fw_init(ps2->fw, ps2->iop_intc); ps2_sbus_init(ps2->sbus, ps2->ee_intc, ps2->iop_intc, ps2->sched); ps2_cdvd_reset(ps2->cdvd); ps2_gif_reset(ps2->gif); ps2_gs_reset(ps2->gs); ps2_ram_reset(ps2->ee_ram); ps2_ram_reset(ps2->iop_ram); ps2_ipu_reset(ps2->ipu); ps2->ee_cycles = 0; } // To-do: This will soon be useless, need to integrate // the tracer into our debugging UI // static int depth = 0; // static inline void ps2_trace(struct ps2_state* ps2) { // for (int i = 0; i < ps2->nfuncs; i++) { // if (ps2->ee->pc == ps2->func[i].addr) { // printf("trace: "); // for (int i = 0; i < depth; i++) // putchar(' '); // printf("%s @ 0x%08x\n", ps2->func[i].name, ps2->func[i].addr); // ++depth; // break; // } // } // if (ps2->ee->opcode == 0x03e00008) // if (depth > 0) --depth; // } void ps2_cycle(struct ps2_state* ps2) { int cycles = ee_run_block(ps2->ee, 128); ps2->ee_cycles += cycles; sched_tick(ps2->sched, ps2->timescale * cycles); ps2_ipu_run(ps2->ipu); ps2_ee_timers_tick_cycles(ps2->ee_timers, cycles); while (ps2->ee_cycles > 8) { iop_cycle(ps2->iop); ps2_iop_timers_tick(ps2->iop_timers); ps2->ee_cycles -= 8; } } void ps2_step_ee(struct ps2_state* ps2) { ee_step(ps2->ee); sched_tick(ps2->sched, 1); ps2_ee_timers_tick(ps2->ee_timers); ps2_ipu_run(ps2->ipu); ps2->ee_cycles++; if (ps2->ee_cycles == 8) { iop_cycle(ps2->iop); ps2_iop_timers_tick(ps2->iop_timers); ps2->ee_cycles = 0; } } void ps2_step_iop(struct ps2_state* ps2) { for (int i = 0; i < 8; i++) { ps2_ee_timers_tick(ps2->ee_timers); ee_step(ps2->ee); } sched_tick(ps2->sched, 8); iop_cycle(ps2->iop); ps2_iop_timers_tick(ps2->iop_timers); ps2_ipu_run(ps2->ipu); } void ps2_iop_cycle(struct ps2_state* ps2) { // while (ps2->ee_cycles) { // sched_tick(ps2->sched, 1); // ee_cycle(ps2->ee); // ps2_ee_timers_tick(ps2->ee_timers); // --ps2->ee_cycles; // } // iop_cycle(ps2->iop); // ps2_iop_timers_tick(ps2->iop_timers); // ps2->ee_cycles = 7; } void ps2_set_timescale(struct ps2_state* ps2, int timescale) { ps2->timescale = timescale; } void ps2_destroy(struct ps2_state* ps2) { free(ps2->strtab); free(ps2->func); ps2_cdvd_destroy(ps2->cdvd); sched_destroy(ps2->sched); ee_destroy(ps2->ee); vu_destroy(ps2->vu0); vu_destroy(ps2->vu1); iop_destroy(ps2->iop); ee_bus_destroy(ps2->ee_bus); iop_bus_destroy(ps2->iop_bus); ps2_gif_destroy(ps2->gif); ps2_gs_destroy(ps2->gs); ps2_ipu_destroy(ps2->ipu); ps2_vif_destroy(ps2->vif0); ps2_vif_destroy(ps2->vif1); ps2_dmac_destroy(ps2->ee_dma); ps2_ram_destroy(ps2->ee_ram); ps2_intc_destroy(ps2->ee_intc); ps2_ee_timers_destroy(ps2->ee_timers); ps2_iop_dma_destroy(ps2->iop_dma); ps2_ram_destroy(ps2->iop_spr); ps2_iop_intc_destroy(ps2->iop_intc); ps2_iop_timers_destroy(ps2->iop_timers); ps2_ram_destroy(ps2->iop_ram); ps2_sio2_destroy(ps2->sio2); ps2_spu2_destroy(ps2->spu2); ps2_usb_destroy(ps2->usb); ps2_fw_destroy(ps2->fw); ps2_sbus_destroy(ps2->sbus); ps2_dev9_destroy(ps2->dev9); ps2_speed_destroy(ps2->speed); ps2_bios_destroy(ps2->bios); ps2_bios_destroy(ps2->rom1); ps2_bios_destroy(ps2->rom2); ps2_sif_destroy(ps2->sif); // Destroy optional hardware if (ps2->s14x_nand) s14x_nand_destroy(ps2->s14x_nand); if (ps2->s14x_syscon) s14x_syscon_destroy(ps2->s14x_syscon); if (ps2->s14x_sram) s14x_sram_destroy(ps2->s14x_sram); if (ps2->s14x_link) s14x_link_destroy(ps2->s14x_link); if (ps2->s14x_ioboard) s14x_ioboard_destroy(ps2->s14x_ioboard); if (ps2->s14x_aiboard) s14x_aiboard_destroy(ps2->s14x_aiboard); free(ps2); } void ps2_set_system(struct ps2_state* ps2, int system) { int ee_ram_size, iop_ram_size, mechacon_model; // Destroy optional hardware if (ps2->s14x_nand) { s14x_nand_destroy(ps2->s14x_nand); ps2->s14x_nand = NULL; } if (ps2->s14x_syscon) { s14x_syscon_destroy(ps2->s14x_syscon); ps2->s14x_syscon = NULL; } if (ps2->s14x_sram) { s14x_sram_destroy(ps2->s14x_sram); ps2->s14x_sram = NULL; } if (ps2->s14x_link) { s14x_link_destroy(ps2->s14x_link); ps2->s14x_link = NULL; } if (ps2->s14x_ioboard) { s14x_ioboard_destroy(ps2->s14x_ioboard); ps2->s14x_ioboard = NULL; } if (ps2->s14x_aiboard) { s14x_aiboard_destroy(ps2->s14x_aiboard); ps2->s14x_aiboard = NULL; } switch (system) { case PS2_SYSTEM_AUTO: { ps2->rom0_info = ps2_rom0_search(ps2->bios->buf, ps2->bios->size + 1); ps2_set_system(ps2, ps2->rom0_info.system); ps2->detected_system = ps2->rom0_info.system; return; } break; case PS2_SYSTEM_RETAIL: { ee_ram_size = RAM_SIZE_32MB; iop_ram_size = RAM_SIZE_2MB; mechacon_model = CDVD_MECHACON_SPC970; } break; case PS2_SYSTEM_RETAIL_DECKARD: { ee_ram_size = RAM_SIZE_32MB; iop_ram_size = RAM_SIZE_2MB; mechacon_model = CDVD_MECHACON_DRAGON; } break; case PS2_SYSTEM_DESR: { ee_ram_size = RAM_SIZE_64MB; iop_ram_size = RAM_SIZE_8MB; mechacon_model = CDVD_MECHACON_DRAGON; } break; case PS2_SYSTEM_TEST: case PS2_SYSTEM_TOOL: { ee_ram_size = RAM_SIZE_128MB; iop_ram_size = RAM_SIZE_8MB; // To-do: Separate mechacon model for TOOL/TEST mechacon_model = CDVD_MECHACON_DRAGON; } break; case PS2_SYSTEM_KONAMI_PYTHON: { ee_ram_size = RAM_SIZE_32MB; iop_ram_size = RAM_SIZE_2MB; mechacon_model = CDVD_MECHACON_SPC970; } break; case PS2_SYSTEM_KONAMI_PYTHON2: { ee_ram_size = RAM_SIZE_32MB; iop_ram_size = RAM_SIZE_2MB; mechacon_model = CDVD_MECHACON_DRAGON; } break; case PS2_SYSTEM_NAMCO_S147: { ee_ram_size = RAM_SIZE_32MB; iop_ram_size = RAM_SIZE_2MB; // This board actually has no MechaCon mechacon_model = CDVD_MECHACON_DRAGON; // Wire up System 147/148 hardware ps2->s14x_nand = s14x_nand_create(); ps2->s14x_syscon = s14x_syscon_create(); ps2->s14x_sram = s14x_sram_create(); ps2->s14x_link = s14x_link_create(); ps2->s14x_ioboard = s14x_ioboard_create(); ps2->s14x_aiboard = s14x_aiboard_create(); s14x_nand_init(ps2->s14x_nand); s14x_syscon_init(ps2->s14x_syscon); s14x_sram_init(ps2->s14x_sram, &ps2->s14x_syscon->sram_write_flag); s14x_link_init(ps2->s14x_link, ps2->iop_intc, ps2->sched); s14x_ioboard_init(ps2->s14x_ioboard, 0); s14x_aiboard_init(ps2->s14x_aiboard); iop_bus_init_s14x_nand(ps2->iop_bus, ps2->s14x_nand); iop_bus_init_s14x_syscon(ps2->iop_bus, ps2->s14x_syscon); iop_bus_init_s14x_sram(ps2->iop_bus, ps2->s14x_sram); iop_bus_init_s14x_link(ps2->iop_bus, ps2->s14x_link); s14x_link_register_node(ps2->s14x_link, 2, s14x_ioboard_handle_packet, ps2->s14x_ioboard); s14x_link_register_node(ps2->s14x_link, 3, s14x_aiboard_handle_packet, ps2->s14x_aiboard); } break; case PS2_SYSTEM_NAMCO_S148: { ee_ram_size = RAM_SIZE_64MB; iop_ram_size = RAM_SIZE_2MB; // This board actually has no MechaCon mechacon_model = CDVD_MECHACON_DRAGON; // Wire up System 147/148 hardware ps2->s14x_nand = s14x_nand_create(); ps2->s14x_syscon = s14x_syscon_create(); ps2->s14x_sram = s14x_sram_create(); ps2->s14x_link = s14x_link_create(); ps2->s14x_ioboard = s14x_ioboard_create(); ps2->s14x_aiboard = s14x_aiboard_create(); s14x_nand_init(ps2->s14x_nand); s14x_syscon_init(ps2->s14x_syscon); s14x_sram_init(ps2->s14x_sram, &ps2->s14x_syscon->sram_write_flag); s14x_link_init(ps2->s14x_link, ps2->iop_intc, ps2->sched); s14x_ioboard_init(ps2->s14x_ioboard, 0); s14x_aiboard_init(ps2->s14x_aiboard); iop_bus_init_s14x_nand(ps2->iop_bus, ps2->s14x_nand); iop_bus_init_s14x_syscon(ps2->iop_bus, ps2->s14x_syscon); iop_bus_init_s14x_sram(ps2->iop_bus, ps2->s14x_sram); iop_bus_init_s14x_link(ps2->iop_bus, ps2->s14x_link); s14x_link_register_node(ps2->s14x_link, 2, s14x_ioboard_handle_packet, ps2->s14x_ioboard); s14x_link_register_node(ps2->s14x_link, 3, s14x_aiboard_handle_packet, ps2->s14x_aiboard); } break; case PS2_SYSTEM_NAMCO_S246: { ee_ram_size = RAM_SIZE_32MB; iop_ram_size = RAM_SIZE_2MB; mechacon_model = CDVD_MECHACON_DRAGON; } break; case PS2_SYSTEM_NAMCO_S256: { ee_ram_size = RAM_SIZE_64MB; iop_ram_size = RAM_SIZE_4MB; mechacon_model = CDVD_MECHACON_DRAGON; } break; default: { ee_ram_size = RAM_SIZE_32MB; iop_ram_size = RAM_SIZE_2MB; mechacon_model = CDVD_MECHACON_DRAGON; } break; } ps2->detected_system = system; ps2_ram_destroy(ps2->ee_ram); ps2_ram_destroy(ps2->iop_ram); ps2->ee_ram = ps2_ram_create(); ps2->iop_ram = ps2_ram_create(); ps2_ram_init(ps2->ee_ram, ee_ram_size); ps2_ram_init(ps2->iop_ram, iop_ram_size); ps2_cdvd_set_mechacon_model(ps2->cdvd, mechacon_model); struct ee_bus_s ee_bus_data; ee_bus_data.read8 = ee_bus_read8; ee_bus_data.read16 = ee_bus_read16; ee_bus_data.read32 = ee_bus_read32; ee_bus_data.read64 = ee_bus_read64; ee_bus_data.read128 = ee_bus_read128; ee_bus_data.write8 = ee_bus_write8; ee_bus_data.write16 = ee_bus_write16; ee_bus_data.write32 = ee_bus_write32; ee_bus_data.write64 = ee_bus_write64; ee_bus_data.write128 = ee_bus_write128; ee_bus_data.udata = ps2->ee_bus; ee_set_ram_size(ps2->ee, ee_ram_size); ee_bus_init_ram(ps2->ee_bus, ps2->ee_ram); ee_bus_init_iop_ram(ps2->ee_bus, ps2->iop_ram); iop_bus_init_iop_ram(ps2->iop_bus, ps2->iop_ram); ee_bus_init_fastmem(ps2->ee_bus, ps2->ee_ram->size, ps2->iop_ram->size); iop_bus_init_fastmem(ps2->iop_bus, ps2->iop_ram->size); } void ps2_set_mac_address(struct ps2_state* ps2, const uint8_t* mac) { ps2_speed_set_mac_address(ps2->speed, mac); } ================================================ FILE: src/ps2.h ================================================ #ifndef PS2_H #define PS2_H #ifdef __cplusplus extern "C" { #endif #include "ee/bus.h" #include "ee/ee.h" #include "ee/gif.h" #include "ee/vif.h" #include "ee/dmac.h" #include "ee/intc.h" #include "ee/timers.h" #include "ee/vu.h" #include "iop/bus.h" #include "iop/bus_decl.h" #include "iop/iop.h" #include "iop/dma.h" #include "iop/intc.h" #include "iop/timers.h" #include "iop/cdvd.h" #include "iop/sio2.h" #include "iop/spu2.h" #include "iop/usb.h" #include "iop/fw.h" #include "shared/bios.h" #include "shared/ram.h" #include "shared/sif.h" #include "shared/sbus.h" #include "shared/dev9.h" #include "shared/speed.h" #include "gs/gs.h" #include "ipu/ipu.h" // Arcade hardware // Namco System 147/148 #include "s14x/nand.h" #include "s14x/syscon.h" #include "s14x/sram.h" #include "s14x/link.h" #include "s14x/ioboard.h" #include "s14x/aiboard.h" // SIO2 devices (controllers, memory cards, etc.) #include "dev/ds.h" #include "dev/guncon.h" #include "dev/mcd.h" #include "dev/ps1_mcd.h" #include "dev/mtap.h" #include "scheduler.h" #include "rom.h" #define PS2_TTY_EE 0 #define PS2_TTY_IOP 1 #define PS2_TTY_SYSMEM 2 enum { PS2_SYSTEM_AUTO = 0, PS2_SYSTEM_RETAIL, PS2_SYSTEM_RETAIL_DECKARD, PS2_SYSTEM_DESR, PS2_SYSTEM_TEST, PS2_SYSTEM_TOOL, PS2_SYSTEM_KONAMI_PYTHON, PS2_SYSTEM_KONAMI_PYTHON2, PS2_SYSTEM_NAMCO_S147, PS2_SYSTEM_NAMCO_S148, PS2_SYSTEM_NAMCO_S246, PS2_SYSTEM_NAMCO_S256 }; struct ps2_elf_function { char* name; uint32_t addr; }; struct ps2_state { // CPUs struct ee_state* ee; struct iop_state* iop; struct vu_state* vu0; struct vu_state* vu1; // EE-only struct ee_bus* ee_bus; struct ps2_gif* gif; struct ps2_vif* vif0; struct ps2_vif* vif1; struct ps2_gs* gs; struct ps2_ipu* ipu; struct ps2_dmac* ee_dma; struct ps2_ram* ee_ram; struct ps2_intc* ee_intc; struct ps2_ee_timers* ee_timers; // IOP-only struct iop_bus* iop_bus; struct ps2_ram* iop_spr; struct ps2_iop_dma* iop_dma; struct ps2_iop_intc* iop_intc; struct ps2_iop_timers* iop_timers; struct ps2_sio2* sio2; struct ps2_spu2* spu2; struct ps2_fw* fw; // Shared struct ps2_ram* iop_ram; struct ps2_bios* bios; struct ps2_bios* rom1; // Mapped to 1E000000-1E3FFFFF (DVD firmware) struct ps2_bios* rom2; // Mapped to 1E400000-1E7FFFFF (Chinese exts) struct ps2_cdvd* cdvd; struct ps2_sif* sif; struct ps2_usb* usb; struct ps2_sbus* sbus; struct ps2_dev9* dev9; struct ps2_speed* speed; // Namco System 147/148 struct s14x_nand* s14x_nand; struct s14x_syscon* s14x_syscon; struct s14x_sram* s14x_sram; struct s14x_link* s14x_link; struct s14x_ioboard* s14x_ioboard; struct s14x_aiboard* s14x_aiboard; struct sched_state* sched; int ee_cycles; int timescale; int system, detected_system; struct ps2_rom_info rom0_info; struct ps2_rom_info rom1_info; // Debug struct ps2_elf_function* func; unsigned int nfuncs; char* strtab; }; struct ps2_state* ps2_create(void); void ps2_init(struct ps2_state* ps2); void ps2_init_tty_handler(struct ps2_state* ps2, int tty, void (*handler)(void*, char), void* udata); void ps2_boot_file(struct ps2_state* ps2, const char* path); void ps2_reset(struct ps2_state* ps2); int ps2_load_bios(struct ps2_state* ps2, const char* path); int ps2_load_rom1(struct ps2_state* ps2, const char* path); int ps2_load_rom2(struct ps2_state* ps2, const char* path); void ps2_cycle(struct ps2_state* ps2); void ps2_step_ee(struct ps2_state* ps2); void ps2_step_iop(struct ps2_state* ps2); void ps2_set_timescale(struct ps2_state* ps2, int timescale); void ps2_iop_cycle(struct ps2_state* ps2); void ps2_destroy(struct ps2_state* ps2); void ps2_set_system(struct ps2_state* ps2, int system); void ps2_set_mac_address(struct ps2_state* ps2, const uint8_t* mac); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ps2_elf.c ================================================ #include #include #include #include "ps2_elf.h" int ps2_elf_load(struct ps2_state* ps2, const char* path) { ps2_reset(ps2); while (ee_get_pc(ps2->ee) != 0x00082000) ps2_cycle(ps2); Elf32_Ehdr ehdr; FILE* file = fopen(path, "rb"); if (!fread(&ehdr, sizeof(Elf32_Ehdr), 1, file)) { printf("elf: Couldn't read ELF header\n"); return 1; } // ps2->ee->pc = ehdr.e_entry; // ps2->ee->next_pc = ps2->ee->pc + 4; Elf32_Phdr phdr; puts(" Type Offset VirtAddr PhysAddr FileSiz MemSiz Flg Align"); for (int i = 0; i < ehdr.e_phnum; i++) { fseek(file, ehdr.e_phoff + (i * ehdr.e_phentsize), SEEK_SET); if (!fread(&phdr, sizeof(Elf32_Phdr), 1, file)) { printf("elf: Couldn't read program header\n"); return 1; } if (phdr.p_type != PT_LOAD) continue; printf(" LOAD 0x%06x 0x%08x 0x%08x 0x%05x 0x%05x %c%c%c 0x%x\n", phdr.p_offset, phdr.p_vaddr, phdr.p_paddr, phdr.p_filesz, phdr.p_memsz, (phdr.p_flags & 1) ? 'R' : ' ', (phdr.p_flags & 2) ? 'W' : ' ', (phdr.p_flags & 4) ? 'X' : ' ', phdr.p_align ); // Clear p_memsz bytes of EE RAM memset(ps2->ee_ram->buf + phdr.p_vaddr, 0, phdr.p_memsz); // Read segment binary fseek(file, phdr.p_offset, SEEK_SET); if (!fread(ps2->ee_ram->buf + phdr.p_vaddr, 1, phdr.p_filesz, file)) { printf("elf: Couldn't read segment binary\n"); } } printf("Entry: 0x%08x\n", ehdr.e_entry); // Read symbol table header Elf32_Shdr symtab; memset(&symtab, 0, sizeof(Elf32_Shdr)); for (int i = 0; i < ehdr.e_shnum; i++) { Elf32_Shdr shdr; fseek(file, ehdr.e_shoff + (i * ehdr.e_shentsize), SEEK_SET); if (!fread(&shdr, sizeof(Elf32_Shdr), 1, file)) { printf("elf: Couldn't read section header\n"); return 1; } if ((shdr.sh_type == SHT_STRTAB) && (i != ehdr.e_shstrndx)) { printf("elf: Loading string table size=%x offset=%x\n", shdr.sh_size, shdr.sh_offset); ps2->strtab = malloc(shdr.sh_size); fseek(file, shdr.sh_offset, SEEK_SET); if (!fread(ps2->strtab, 1, shdr.sh_size, file)) { printf("elf: Couldn't read string table\n"); free(ps2->strtab); return 1; } } if (shdr.sh_type == SHT_SYMTAB) symtab = shdr; } // No symbol table present if (symtab.sh_type != SHT_SYMTAB) { fclose(file); return 0; } if (!symtab.sh_entsize) { fclose(file); return 0; } if (!symtab.sh_size) { fclose(file); return 0; } printf("elf: Got symbol table\n"); size_t nsyms = symtab.sh_size / symtab.sh_entsize; // Read symbol table Elf32_Sym sym; for (int i = 0; i < nsyms; i++) { fseek(file, symtab.sh_offset + (i * symtab.sh_entsize), SEEK_SET); if (!fread(&sym, sizeof(Elf32_Sym), 1, file)) { printf("elf: Couldn't read symbol table\n"); free(ps2->strtab); return 1; } if (ELF32_ST_TYPE(sym.st_info) != STT_FUNC) continue; ++ps2->nfuncs; } ps2->func = malloc(ps2->nfuncs * sizeof(struct ps2_elf_function)); int index = 0; for (int i = 0; i < nsyms; i++) { fseek(file, symtab.sh_offset + (i * symtab.sh_entsize), SEEK_SET); if (!fread(&sym, sizeof(Elf32_Sym), 1, file)) { printf("elf: Couldn't read symbols\n"); free(ps2->strtab); free(ps2->func); return 1; } if (ELF32_ST_TYPE(sym.st_info) != STT_FUNC) continue; ps2->func[index].addr = sym.st_value; ps2->func[index++].name = ps2->strtab + sym.st_name; } fclose(file); return 0; } ================================================ FILE: src/ps2_elf.h ================================================ #ifndef PS2_ELF_H #define PS2_ELF_H #ifdef __cplusplus extern "C" { #endif #ifdef __linux__ #include #else #include "elf.h" #endif #include "ps2.h" int ps2_elf_load(struct ps2_state* ps2, const char* path); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/ps2_iso9660.c ================================================ #include #include #include #include #include #include "ps2_iso9660.h" #ifdef _MSC_VER #define fseek64 _fseeki64 #define ftell64 _ftelli64 #elif defined(_WIN32) #define fseek64 fseeko64 #define ftell64 ftello64 #else #define fseek64 fseek #define ftell64 ftell #endif #define IGNORE_RETURN ((void)!) struct iso9660_state* iso9660_open(const char* path) { FILE* file = fopen(path, "rb"); if (!file) return NULL; struct iso9660_state* iso = malloc(sizeof(struct iso9660_state)); memset(iso, 0, sizeof(struct iso9660_state)); iso->file = file; return iso; } char* iso9660_get_boot_path(struct iso9660_state* iso) { // Cache the PVD (Primary Volume Descriptor) fseek64(iso->file, 16 * 0x800, SEEK_SET); if (!fread(&iso->pvd, sizeof(struct iso9660_pvd), 1, iso->file)) { printf("iso9660: Couldn't read PVD\n"); return NULL; } if (strncmp(iso->pvd.id, "\1CD001\1", 8)) { printf("iso: Unknown format disc %d\n", iso->pvd.id[0]); return NULL; } struct iso9660_dirent* root = (struct iso9660_dirent*)iso->pvd.root; fseek64(iso->file, (uint64_t)root->lba_le * 0x800, SEEK_SET); if (!fread(iso->buf, 0x800, 1, iso->file)) { printf("iso9660: Couldn't read root sector\n"); return NULL; } struct iso9660_dirent* dir = (struct iso9660_dirent*)iso->buf; while (dir->dr_len) { // printf("Entry: lba=%d len=%d size=%d name_len=%d name=\"", dir->lba_le, dir->dr_len, dir->size_le, dir->id_len); // if (dir->id == '\0') { // putchar('.'); // } else if (dir->id == '\1') { // putchar('.'); // putchar('.'); // } else { // for (int j = 0; j < dir->id_len; j++) { // putchar(*(((char*)&dir->id) + j)); // } // } // puts("\""); if (dir->id_len == 12) { if (!strcmp((char*)&dir->id, "SYSTEM.CNF;1")) { break; } } uint8_t* ptr = (uint8_t*)dir; dir = (struct iso9660_dirent*)(ptr + dir->dr_len); } if (!dir->dr_len) { printf("iso: SYSTEM.CNF not found! (non-PlayStation disc?)\n"); return NULL; } fseek64(iso->file, (uint64_t)dir->lba_le * 0x800, SEEK_SET); if (!fread(iso->buf, 0x800, 1, iso->file)) { printf("iso9660: Couldn't read SYSTEM.CNF\n"); return NULL; } printf("isobuf: %s\n", iso->buf); // Parse SYSTEM.CNF char* p = iso->buf; char key[64]; while (*p) { char* kptr = key; while (isspace(*p)) ++p; while (isalnum(*p)) *kptr++ = *p++; *kptr = '\0'; // printf("key: %s\n", key); if (!strncmp(key, "BOOT2", 64)) { while (isspace(*p)) ++p; if (*p != '=') { printf("iso: Expected =\n"); return NULL; } ++p; while (isspace(*p)) ++p; int i; for (i = 0; i < 255; i++) { if (*p == '\n' || *p == '\r') break; iso->boot_file[i] = *p++; } iso->boot_file[i] = '\0'; return iso->boot_file; } else { while ((*p != '\n') && (*p != '\0') && (*p != '\r')) ++p; while ((*p == '\n') || (*p == '\r')) ++p; } } printf("iso: Couldn't find BOOT2 entry in SYSTEM.CNF (PlayStation disc?)\n"); return NULL; } // void iso9660_load_boot_elf(struct iso9660_state* iso, char* buf); void iso9660_close(struct iso9660_state* iso) { if (iso->file) fclose(iso->file); free(iso); } #undef fseek64 #undef ftell64 ================================================ FILE: src/ps2_iso9660.h ================================================ #ifndef PS2_ISO9660_H #define PS2_ISO9660_H #ifdef __cplusplus extern "C" { #endif #include #include #include "ps2.h" #ifdef _MSC_VER #pragma pack(push, 1) #define PACKED #else #define PACKED __attribute__((packed)) #endif struct PACKED iso9660_pvd { char id[8]; char system_id[32]; char volume_id[32]; char zero[8]; uint32_t total_sector_le, total_sect_be; char zero2[32]; uint16_t volume_set_size_le, volume_set_size_be; uint16_t volume_seq_nr_le, volume_seq_nr_be; uint16_t sector_size_le, sector_size_be; uint32_t path_table_len_le, path_table_len_be; uint32_t path_table_le, path_table_2nd_le; uint32_t path_table_be, path_table_2nd_be; uint8_t root[34]; char volume_set_id[128], publisher_id[128], data_preparer_id[128], application_id[128]; char copyright_file_id[37], abstract_file_id[37], bibliographical_file_id[37]; }; struct PACKED iso9660_dirent { uint8_t dr_len; uint8_t ext_dr_len; uint32_t lba_le, lba_be; uint32_t size_le, size_be; uint8_t date[7]; uint8_t flags; uint8_t file_unit_size; uint8_t interleave_gap_size; uint16_t volume_seq_nr_le, volume_seq_nr_be; uint8_t id_len; uint8_t id; }; #ifdef _MSC_VER #pragma pack(pop) #endif struct iso9660_state { char buf[0x800]; char boot_file[256]; struct iso9660_pvd pvd; FILE* file; }; struct iso9660_state* iso9660_open(const char* path); char* iso9660_get_boot_path(struct iso9660_state* iso); // void iso9660_load_boot_elf(struct iso9660_state* iso, char* buf); void iso9660_close(struct iso9660_state* iso); #undef PACKED #ifdef __cplusplus } #endif #endif ================================================ FILE: src/queue.c ================================================ #include #include #include #include #include "queue.h" struct queue_state* queue_create(void) { return malloc(sizeof(struct queue_state)); } void queue_init(struct queue_state* queue) { memset(queue, 0, sizeof(struct queue_state)); queue->cap = 4; queue->buf = malloc(queue->cap * sizeof(uint32_t)); } void queue_push(struct queue_state* queue, uint32_t value) { if (queue->size == queue->cap) { queue->cap *= 2; uint32_t* buf = realloc(queue->buf, queue->cap * sizeof(uint32_t)); if (!buf) { printf("queue: Couldn't allocate memory\n"); exit(1); } queue->buf = buf; } queue->buf[queue->size++] = value; } uint32_t queue_pop(struct queue_state* queue) { if (queue->index == queue->size) return 0; return queue->buf[queue->index++]; } uint32_t queue_peek(struct queue_state* queue) { if (queue->index == queue->size) return 0; return queue->buf[queue->index]; } uint32_t queue_at(struct queue_state* queue, int idx) { return queue->buf[queue->index + idx]; } int queue_is_empty(struct queue_state* queue) { return queue->index == queue->size; } int queue_size(struct queue_state* queue) { return queue->size; } void queue_clear(struct queue_state* queue) { queue->size = 0; queue->index = 0; } void queue_destroy(struct queue_state* queue) { free(queue->buf); free(queue); } ================================================ FILE: src/queue.h ================================================ #ifndef QUEUE_H #define QUEUE_H #ifdef __cplusplus extern "C" { #endif #include struct queue_state { uint32_t* buf; unsigned int cap; unsigned int size; unsigned int index; }; struct queue_state* queue_create(void); void queue_init(struct queue_state* queue); void queue_push(struct queue_state* queue, uint32_t value); uint32_t queue_pop(struct queue_state* queue); uint32_t queue_peek(struct queue_state* queue); uint32_t queue_at(struct queue_state* queue, int idx); int queue_is_empty(struct queue_state* queue); int queue_size(struct queue_state* queue); void queue_clear(struct queue_state* queue); void queue_destroy(struct queue_state* queue); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/rom.c ================================================ #include "rom.h" #include "md5.h" static const struct ps2_rom_info rom0_info_table[] = { { "32f2e4d5ff5ee11072a6bc45530f5765", "5.0 01/17/00 T", "N/A", "DTL-H10000", 4 }, { "acf4730ceb38ac9d8c7d8e21f2614600", "5.0 01/17/00 T", "NTSC-J", "SCPH-10000", 1 }, { "acf9968c8f596d2b15f42272082513d1", "5.0 02/17/00 T", "N/A", "DTL-H10000", 4 }, { "b1459d7446c69e3e97e6ace3ae23dd1c", "5.0 02/17/00 T", "NTSC-J", "SCPH-10000/SCPH-15000", 1 }, { "d3f1853a16c2ec18f3cd1ae655213308", "5.0 02/24/00 T", "N/A", "DTL-H10000", 4 }, { "63e6fd9b3c72e0d7b920e80cf76645cd", "5.0 07/27/00 A", "N/A", "DTL-H30001", 4 }, { "a20c97c02210f16678ca3010127caf36", "5.0 07/27/00 A", "NTSC-U/C", "SCPH-30001", 1 }, { "a925e84ac0ed2711af36e28772793be2", "5.0 09/01/00 ?", "N/A", "COH-H31000 (Konami Python)", 6 }, { "8db2fbbac7413bf3e7154c1e0715e565", "5.0 09/02/00 A", "NTSC-U/C", "SCPH-30001", 1 }, { "91c87cb2f2eb6ce529a2360f80ce2457", "5.0 09/02/00 E", "N/A", "DTL-H30002", 4 }, { "3016b3dd42148a67e2c048595ca4d7ce", "5.0 09/02/00 E", "N/A", "DTL-H30102", 4 }, { "b7fa11e87d51752a98b38e3e691cbf17", "5.0 09/02/00 E", "PAL-E", "SCPH-30002/SCPH-30003/SCPH-30004", 1 }, { "f63bc530bd7ad7c026fcd6f7bd0d9525", "5.0 10/27/00 J", "NTSC-J", "SCPH-18000 (GH-003)", 1 }, { "cee06bd68c333fc5768244eae77e4495", "5.0 10/27/00 J", "NTSC-J", "SCPH-18000 (GH-008)", 1 }, { "0bf988e9c7aaa4c051805b0fa6eb3387", "5.0 12/28/00 A", "N/A", "DTL-H30101", 4 }, { "8accc3c49ac45f5ae2c5db0adc854633", "5.0 12/28/00 A", "NTSC-U/C", "SCPH-30001/SCPH-35001", 1 }, { "6f9a6feb749f0533aaae2cc45090b0ed", "5.0 12/28/00 E", "N/A", "DTL-H30102", 4 }, { "838544f12de9b0abc90811279ee223c8", "5.0 12/28/00 E", "PAL-E", "SCPH-30002/SCPH-30003/SCPH-30004/SCPH-35002/SCPH-35003/SCPH-35004", 1 }, { "bb6bbc850458fff08af30e969ffd0175", "5.0 01/18/01 J", "N/A", "DTL-H30000", 4 }, { "815ac991d8bc3b364696bead3457de7d", "5.0 01/18/01 J", "NTSC-J", "SCPH-30000/SCPH-35000", 1 }, { "b107b5710042abe887c0f6175f6e94bb", "5.0 04/27/01 A", "NTSC-U/C", "SCPH-30001R", 1 }, { "ab55cceea548303c22c72570cfd4dd71", "5.0 04/27/01 J", "NTSC-J", "SCPH-30000", 1 }, { "18bcaadb9ff74ed3add26cdf709fff2e", "5.0 07/04/01 A", "NTSC-U/C", "SCPH-30001R", 1 }, { "491209dd815ceee9de02dbbc408c06d6", "5.0 07/04/01 E", "PAL-E", "SCPH-30002R/SCPH-30003R/SCPH-30004R", 1 }, { "7200a03d51cacc4c14fcdfdbc4898431", "5.0 10/04/01 A", "NTSC-U/C", "SCPH-30001R", 1 }, { "8359638e857c8bc18c3c18ac17d9cc3c", "5.0 10/04/01 E", "PAL-E", "SCPH-30002R/SCPH-30003R/SCPH-30004R", 1 }, { "28922c703cc7d2cf856f177f2985b3a9", "5.0 10/04/01 E", "PAL-E", "SCPH-30002R/SCPH-30003R/SCPH-30004R", 1 }, { "352d2ff9b3f68be7e6fa7e6dd8389346", "5.0 07/30/01 J", "NTSC-J", "SCPH-30005R/SCPH-30006R/SCPH-30007R", 1 }, { "d5ce2c7d119f563ce04bc04dbc3a323e", "5.0 02/07/02 A", "NTSC-U", "SCPH-39001", 1 }, { "0d2228e6fd4fb639c9c39d077a9ec10c", "5.0 03/19/02 E", "PAL-E", "SCPH-39002/SCPH-39003/SCPH-39004", 1 }, { "72da56fccb8fcd77bba16d1b6f479914", "5.0 04/26/02 J", "NTSC-J", "SCPH-37000/SCPH-39000", 1 }, { "5b1f47fbeb277c6be2fccdd6344ff2fd", "5.0 04/26/02 E", "PAL-E", "SCPH-39008", 1 }, { "315a4003535dfda689752cb25f24785c", "5.0 04/26/02 J", "NTSC-J", "SCPH-39005/SCPH-39006/SCPH-39007", 1 }, { "52cca0058626569c7a9699838baab2d8", "5.0 11/19/02 ?", "N/A", "Namco System 246", 10 }, { "312ad4816c232a9606e56f946bc0678a", "5.0 02/06/03 J", "NTSC-J", "SCPH-50000/SCPH-55000", 2 }, { "666018ffec65c5c7e04796081295c6c7", "5.0 02/27/03 E", "N/A", "DTL-H50002", 4 }, { "6e69920fa6eef8522a1d688a11e41bc6", "5.0 02/27/03 E", "PAL-E", "SCPH-50002/SCPH-50003/SCPH-50004", 2 }, { "eb960de68f0c0f7f9fa083e9f79d0360", "5.0 03/25/03 A", "N/A", "DTL-H50001", 4 }, { "8aa12ce243210128c5074552d3b86251", "5.0 03/25/03 A", "NTSC-U/C", "SCPH-50001", 2 }, { "240d4c5ddd4b54069bdc4a3cd2faf99d", "5.0 02/24/03 J", "N/A", "DTL-H50009", 4 }, { "1c6cd089e6c83da618fbf2a081eb4888", "5.0 10/28/03 J", "NTSC-J", "DESR-5000/DESR-5100/DESR-7000/DESR-7100 (PSX1)", 3 }, { "1999f40e409756522f9e70fea308a020", "5.0 10/31/03 T", "N/A", "DTL-T10000", 5 }, { "463d87789c555a4a7604e97d7db545d1", "5.0 06/23/03 J", "NTSC-J", "SCPH-55000", 2 }, { "35461cecaa51712b300b2d6798825048", "5.0 06/23/03 A", "NTSC-U/C", "SCPH-50001/SCPH-50010", 2 }, { "bd6415094e1ce9e05daabe85de807666", "5.0 06/23/03 E", "PAL-E", "SCPH-50002/SCPH-50003/SCPH-50004", 2 }, { "2e70ad008d4ec8549aada8002fdf42fb", "5.0 06/23/03 J", "NTSC-J", "SCPH-50005/SCPH-50006/SCPH-50007", 2 }, { "b53d51edc7fc086685e31b811dc32aad", "5.0 06/23/03 E", "PAL-E", "SCPH-50008", 2 }, { "1b6e631b536247756287b916f9396872", "5.0 06/23/03 J", "NTSC-J", "SCPH-50009", 2 }, { "00da1b177096cfd2532c8fa22b43e667", "5.0 08/22/03 J", "NTSC-J", "SCPH-50000/Konami Python 2", 2 }, { "afde410bd026c16be605a1ae4bd651fd", "5.0 08/22/03 E", "PAL-E", "SCPH-50004", 2 }, { "81f4336c1de607dd0865011c0447052e", "5.0 03/29/04 A", "NTSC-U/C", "SCPH-50011", 2 }, { "a58676c6bd79229bda967d07b4ec2e16", "5.0 05/19/04 ?", "N/A", "Namco System 256", 11 }, { "0eee5d1c779aa50e94edd168b4ebf42e", "5.0 06/14/04 J", "NTSC-J", "SCPH-70000", 2 }, { "d333558cc14561c1fdc334c75d5f37b7", "5.0 06/14/04 A", "NTSC-U/C", "SCPH-70001/SCPH-70011/SCPH-70012", 2 }, { "dc752f160044f2ed5fc1f4964db2a095", "5.0 06/14/04 E", "PAL-E", "SCPH-70002/SCPH-70003/SCPH-70004/SCPH-70008", 2 }, { "63ead1d74893bf7f36880af81f68a82d", "5.0 06/14/04 E", "N/A", "DTL-H70002", 4 }, { "3e3e030c0f600442fa05b94f87a1e238", "5.0 06/14/04 J", "NTSC-J", "SCPH-70005/SCPH-70006/SCPH-70007", 2 }, { "1ad977bb539fc9448a08ab276a836bbc", "5.0 09/17/04 J", "NTSC-J", "DESR-5500/DESR-5700/DESR-7500/DESR-7700 (PSX2)", 3 }, { "eb4f40fcf4911ede39c1bbfe91e7a89a", "5.0 06/20/05 J", "NTSC-J", "SCPH-75000", 2 }, { "9959ad7a8685cad66206e7752ca23f8b", "5.0 06/20/05 A", "N/A", "DTL-H75000A", 4 }, { "929a14baca1776b00869f983aa6e14d2", "5.0 06/20/05 A", "NTSC-U/C", "SCPH-75001/SCPH-75010", 2 }, { "573f7d4a430c32b3cc0fd0c41e104bbd", "5.0 06/20/05 E", "PAL-E", "SCPH-75002/SCPH-75003/SCPH-75004/SCPH-75008", 2 }, { "df63a604e8bff5b0599bd1a6c2721bd0", "5.0 06/20/05 J", "NTSC-J", "SCPH-75006", 2 }, { "5b1ba4bb914406fae75ab8e38901684d", "5.0 02/10/06 J", "NTSC-J", "SCPH-77000", 2 }, { "cb801b7920a7d536ba07b6534d2433ca", "5.0 02/10/06 A", "NTSC-U/C", "SCPH-77001/SCPH-77010", 2 }, { "af60e6d1a939019d55e5b330d24b1c25", "5.0 02/10/06 E", "PAL-E", "SCPH-77002/SCPH-77003/SCPH-77004/SCPH-77008", 2 }, { "549a66d0c698635ca9fa3ab012da7129", "5.0 02/10/06 J", "NTSC-J", "SCPH-77006/SCPH-77007", 2 }, { "5de9d0d730ff1e7ad122806335332524", "5.0 09/05/06 J", "NTSC-J", "SCPH-79000/SCPH-90000", 2 }, { "21fe4cad111f7dc0f9af29477057f88d", "5.0 09/05/06 A", "N/A", "DTL-H90000", 4 }, { "40c11c063b3b9409aa5e4058e984e30c", "5.0 09/05/06 A", "NTSC-U/C", "SCPH-79001/SCPH-79010/SCPH-90001", 2 }, { "80bbb237a6af9c611df43b16b930b683", "5.0 09/05/06 E", "PAL-E", "SCPH-79002/SCPH-79003/SCPH-79004/SCPH-79008/SCPH-90002/SCPH-90003/SCPH-90004", 2 }, { "c37bce95d32b2be480f87dd32704e664", "5.0 09/05/06 J", "NTSC-J", "SCPH-79006/SCPH-79007/SCPH-90006/SCPH-90007", 2 }, { "80ac46fa7e77b8ab4366e86948e54f83", "5.0 02/20/08 J", "NTSC-J", "SCPH-90000", 2 }, { "21038400dc633070a78ad53090c53017", "5.0 02/20/08 A", "NTSC-U/C", "SCPH-90001/SCPH-90010", 2 }, { "dc69f0643a3030aaa4797501b483d6c4", "5.0 02/20/08 E", "PAL-E", "SCPH-90002/SCPH-90003/SCPH-90004/SCPH-90008", 2 }, { "30d56e79d89fbddf10938fa67fe3f34e", "5.0 02/20/08 J", "NTSC-J", "SCPH-90005/SCPH-90006/SCPH-90007", 2 }, { "93ea3bcee4252627919175ff1b16a1d9", "5.0 04/15/10 E", "PAL-E", "KDL-22PX300 (Sony Bravia TV) (Europe)", 2 }, { "d3e81e95db25f5a86a7b7474550a2155", "5.0 04/15/10 J", "NTSC-J", "KDL-22PX300 (Sony Bravia TV)", 2 }, { "cc4b9cea0fdb3d2506173668a2a88305", "?.? \?\?/\?\?/?? ?", "N/A", "Namco System 147", 8 }, { "73d4ba0f0a6fb84a151b89fde468aa74", "?.? \?\?/\?\?/?? ?", "N/A", "Namco System 147B", 8 }, { "860c13259a548c7ff07b67157928b076", "?.? \?\?/\?\?/?? ?", "N/A", "Namco System 148", 9 } }; static const struct ps2_rom_info rom1_info_table[] = { { "31a671627b9bf2d88f5e3f6680941fa6", "1.10U" }, { "22080eed26576f4e2282c905dc6e0a4b", "1.20E" }, { "20a4e401b9e7885e25f1c31f6bfcbe0c", "1.20U" }, { "a1a15b62cef142575faaea17fb23dbd1", "1.30E" }, { "567fe068711a9e5914e836cb650600af", "1.30U" }, { "8afc4544e572842a6bb301ad92dc0c02", "2.00J" }, { "d5118e3979eb2a3814ebbfa32825e7b0", "2.10E" }, { "f0b2e6b7f6d06561e230a5b85e5d1bf9", "2.10J" }, { "d0f79251699fdeff073a7c2365d0c526", "2.10U" }, { "32abbe7ab7c1b72d5ffc24d4963bd6c6", "2.12G" }, { "4499f6303d05d4caeb289c2344ea3469", "2.12U" }, { "6bdb45a952f697f367cd1646cdf09235", "2.13E" }, { "d609f69d9e3ef236f6e2bf0a80762b6f", "2.15G" }, { "f40466438d83a02c1eecba3efff20b6a", "3.00E" }, { "e414c981647883e33f17642da827a739", "3.00U" }, { "6bbd2f348c585fdd645900f6ec75f2c7", "3.02C" }, { "503115717429b64e19fa6103e3fa5a35", "3.02E" }, { "2ac40eec790adecf4bc5ee1090a27676", "3.02U" }, { "a2b55c44a3c3eec0abe5647cfb6a6493", "3.10" }, { "79b4880006769a1af9b0a6c7302cc18d", "3.11" } }; static const struct ps2_rom_info unknown = { "00000000000000000000000000000000", "Unknown", "Unknown", "Unknown", 2 }; struct ps2_rom_info ps2_rom0_search(uint8_t* rom, size_t size) { struct md5_context ctx; char buf[33]; md5_init(&ctx); md5_update(&ctx, rom, size); md5_finalize(&ctx); for (int i = 0; i < 16; i++) { sprintf(&buf[i * 2], "%02x", ctx.digest[i]); } for (size_t i = 0; i < sizeof(rom0_info_table) / sizeof(rom0_info_table[0]); i++) { if (strncmp(buf, rom0_info_table[i].md5hash, 32) == 0) { return rom0_info_table[i]; } } struct ps2_rom_info info; info = unknown; memcpy(info.md5hash, buf, 33); return info; } struct ps2_rom_info ps2_rom1_search(uint8_t* rom, size_t size) { struct md5_context ctx; char buf[33]; md5_init(&ctx); md5_update(&ctx, rom, size); md5_finalize(&ctx); for (int i = 0; i < 16; i++) { sprintf(&buf[i * 2], "%02x", ctx.digest[i]); } for (size_t i = 0; i < sizeof(rom1_info_table) / sizeof(rom1_info_table[0]); i++) { if (strncmp(buf, rom1_info_table[i].md5hash, 32) == 0) { return rom1_info_table[i]; } } struct ps2_rom_info info; info = unknown; memcpy(info.md5hash, buf, 33); return info; } int ps2_rom0_is_valid(uint8_t* rom, size_t size) { struct ps2_rom_info info = ps2_rom0_search(rom, size); return strcmp(info.version, "Unknown") != 0; } int ps2_rom1_is_valid(uint8_t* rom, size_t size) { struct ps2_rom_info info = ps2_rom1_search(rom, size); return strcmp(info.version, "Unknown") != 0; } ================================================ FILE: src/rom.h ================================================ #ifndef ROM_H #define ROM_H #ifdef __cplusplus extern "C" { #endif #include #include struct ps2_rom_info { char md5hash[33]; const char* version; const char* region; const char* model; int system; }; struct ps2_rom_info ps2_rom0_search(uint8_t* rom, size_t size); struct ps2_rom_info ps2_rom1_search(uint8_t* rom, size_t size); int ps2_rom0_is_valid(uint8_t* rom, size_t size); int ps2_rom1_is_valid(uint8_t* rom, size_t size); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/s14x/aiboard.c ================================================ #include #include #include #include "aiboard.h" struct s14x_aiboard* s14x_aiboard_create(void) { return malloc(sizeof(struct s14x_aiboard)); } int s14x_aiboard_init(struct s14x_aiboard* aiboard) { memset(aiboard, 0, sizeof(struct s14x_aiboard)); aiboard->version = 0x0104; } void s14x_aiboard_destroy(struct s14x_aiboard* aiboard) { free(aiboard); } void s14x_aiboard_handle_packet(void* udata, struct link_packet* in, struct link_packet* out) { struct s14x_aiboard* aiboard = (struct s14x_aiboard*)udata; // if (cp == 0x38) { // link->ram[addr+0] = node; // link->ram[addr+1] = 0; // Broadcast // link->ram[addr+2] = 0x38; // link->ram[addr+3] = 0; // link->ram[addr+0x38] = 0x20; // // link->ram[addr+0x39] = link->ram[0x79]; // // link->ram[addr+0x3a] = link->ram[0x7a]; // // link->ram[addr+0x3b] = link->ram[0x7b]; // // link->ram[addr+0x3c] = link->ram[0x7c]; // // link->ram[addr+0x3d] = link->ram[0x7d]; // // link->ram[addr+0x3e] = link->ram[0x7e]; // link->ram[addr+0x3f] = 0; // // for (int i = 0; i < 7; i++) // // link->ram[addr+0x3f] += link->ram[addr+0x38+i]; // ps2_iop_intc_irq(link->intc, IOP_INTC_DEV9); // return; // } } ================================================ FILE: src/s14x/aiboard.h ================================================ #ifndef S14X_AIBOARD_H #define S14X_AIBOARD_H #ifdef __cplusplus extern "C" { #endif #include #include #include "link.h" struct s14x_aiboard { uint16_t version; }; struct s14x_aiboard* s14x_aiboard_create(void); int s14x_aiboard_init(struct s14x_aiboard* aiboard); void s14x_aiboard_destroy(struct s14x_aiboard* aiboard); void s14x_aiboard_handle_packet(void* udata, struct link_packet* in, struct link_packet* out); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/s14x/ioboard.c ================================================ #include #include #include #include "ioboard.h" struct s14x_ioboard* s14x_ioboard_create(void) { return malloc(sizeof(struct s14x_ioboard)); } int s14x_ioboard_init(struct s14x_ioboard* ioboard, int mode) { memset(ioboard, 0, sizeof(struct s14x_ioboard)); ioboard->version = 0x0104; ioboard->switches = 0xffff; ioboard->mode = mode; } void s14x_ioboard_destroy(struct s14x_ioboard* ioboard) { free(ioboard); } uint8_t link_calculate_checksum(struct link_packet* packet) { uint8_t checksum = 0; for (int i = 0; i < sizeof(packet->data); i++) { checksum += packet->data[i]; } return checksum; } void ioboard_process_ic_card(struct link_packet* in, struct link_packet* out) { uint8_t channel = in->data[0]; uint8_t size = in->data[1]; uint8_t cmd = in->data[5]; const uint8_t base = 4; out->data[base + 0] = 0x02; out->data[base + 1] = cmd; // Command ID int out_size = 0; switch (cmd) { case 0x78: { out_size = 6; out->data[base + 3] = 0x00; // Command result? out->data[base + 4] = 0x00; out->data[base + 5] = 0xFF; } break; case 0x7A: { out_size = 4; out->data[base + 3] = 0x00; // Command result? } break; case 0x7B: { out_size = 6; out->data[base + 3] = 0x00; // Command result? (0x03 -> No IC card) out->data[base + 4] = 0x00; // ?? out->data[base + 5] = 0x00; // ?? } break; case 0x80: { out_size = 4; out->data[base + 3] = 0x0D; // Command result? } break; case 0x9F: { out_size = 4; out->data[base + 3] = 0x00; // Command result? } break; case 0xA7: { out_size = 4; out->data[base + 3] = 0x00; // Command result? } break; case 0xAC: { // Some key stuff (parity)? (game expects 8 bytes in payload) out_size = 12; out->data[base + 3] = 0x00; // Command result? out->data[base + 4] = 0xAA; out->data[base + 5] = 0xAA; out->data[base + 6] = 0xAA; out->data[base + 7] = 0xAA; out->data[base + 8] = 0x55; out->data[base + 9] = 0x55; out->data[base + 10] = 0x55; out->data[base + 11] = 0x55; } break; case 0xAF: { // More key stuff (checksum)? (game expects 16 bytes in payload) out_size = 20; out->data[base + 3] = 0x00; //Command result? } break; default: { printf("ioboard: Unknown IC card command %02x", cmd); } break; } uint8_t out_size_with_parity = out_size + 1; out->data[0] = channel; out->data[1] = out_size_with_parity; // This needs to be at least 5 out->data[base + 2] = out_size_with_parity - 5; uint8_t parity = 0; for (int i = 1; i < out_size; i++) { parity ^= out->data[base + i]; } out->data[base + out_size] = parity; } void s14x_ioboard_handle_packet(void* udata, struct link_packet* in, struct link_packet* out) { struct s14x_ioboard* ioboard = (struct s14x_ioboard*)udata; // Setup header (cmd, cp, unk03, seq_number) *out = *in; out->src_node = in->dst_node; out->dst_node = in->src_node; switch (in->cmd) { // GetPCBInformation case 0x0f: { out->data[0] = 0; out->data[1] = 0; out->data[2] = ioboard->version >> 8; out->data[3] = ioboard->version & 0xff; } break; // GetSystemSwitches case 0x10: { if (ioboard->mode == 0) { out->data[0] = ioboard->switches & 0xff; out->data[1] = ioboard->switches >> 8; } else { out->data[0] = in->data[0]; } } break; case 0x11: { out->data[0x32] = 0x20; out->data[0x36] = ioboard->buttons & 0xff; out->data[0x37] = ioboard->buttons >> 8; } break; case 0x31: { uint8_t channel = in->data[0]; uint8_t size = in->data[1]; if (channel == 0) { // Barcode Reader out->data[0] = channel; out->data[1] = 0x6; out->data[4 + 0] = 0x02; out->data[4 + 1] = 0x02; out->data[4 + 2] = 0x80; out->data[4 + 3] = 0x02; out->data[4 + 4] = 0x03; out->data[4 + 5] = 0x03; } else if (channel == 2) { ioboard_process_ic_card(in, out); } } break; // Used by Animal Kaiser case 0x41: { out->data[0] = 0; } break; // Used by Animal Kaiser case 0x51: { out->data[0] = 0; } break; case 0x0d: // ? case 0x18: // Switch case 0x38: // SCI case 0x39: // GetSwitches case 0x48: // CoinSensor case 0x58: // MechanicalSensor case 0xa5: { // Reset out->data[0] = in->data[0]; } break; default: { printf("s14x_ioboard: Unhandled command %02x\n", in->cmd); out->data[0] = in->data[0]; } break; } out->checksum = link_calculate_checksum(out); } void s14x_ioboard_press_switch(struct s14x_ioboard* ioboard, uint16_t mask) { ioboard->switches &= ~mask; } void s14x_ioboard_release_switch(struct s14x_ioboard* ioboard, uint16_t mask) { ioboard->switches |= mask; } void s14x_ioboard_press_button(struct s14x_ioboard* ioboard, uint16_t mask) { ioboard->buttons |= mask; } void s14x_ioboard_release_button(struct s14x_ioboard* ioboard, uint16_t mask) { ioboard->buttons &= ~mask; } ================================================ FILE: src/s14x/ioboard.h ================================================ /* s14x/ioboard.h - Namco System 147/148 I/O board emulation Notes: The I/O board is connected to the main board via the CircLink network at node 2. The main board sends ARCNET packets with commands through CircLink and the I/O board responds with data. Packet format ------------- Offset Description 00h Source node (e.g. 1 for the main board) 01h Destination node 02h Unknown (driver will only accept 04h) 03h Unknown (driver will only accept 00h) 04h Sequence number Note: This byte needs to contain the same sequence number that was sent by the main board. 05h Command 06h-3Eh Data 3Fh Checksum (sum of 06h-3Eh) I/O board commands ------------------ Command Description 0Dh Unknown (?) 0Fh Get PCB information (returns version 0000:0104h) 10h Depends on the board, on pacmanap this returns the state of the switches 18h Switch (?) 38h SCI (?) 39h Get switches (?) 48h Coin sensor (?) 58h Mechanical sensor (?) A5h Reset There are actually more commands but we stub most of them by returning the same data that was sent by the main board. */ #ifndef S14X_IOBOARD_H #define S14X_IOBOARD_H #ifdef __cplusplus extern "C" { #endif #include #include #include "link.h" #define S14X_IOBOARD_SW_DOWN 0x0001 #define S14X_IOBOARD_SW_UP 0x0002 #define S14X_IOBOARD_SW_ENTER 0x0004 #define S14X_IOBOARD_SW_TEST 0x0008 #define S14X_IOBOARD_SW_SERVICE 0x0020 #define S14X_IOBOARD_SW_P4_START 0x0100 #define S14X_IOBOARD_SW_P3_START 0x0200 #define S14X_IOBOARD_SW_P2_START 0x0400 #define S14X_IOBOARD_SW_P1_START 0x0800 #define S14X_IOBOARD_BT_P4_UP 0x0001 #define S14X_IOBOARD_BT_P4_DOWN 0x0002 #define S14X_IOBOARD_BT_P4_RIGHT 0x0004 #define S14X_IOBOARD_BT_P4_LEFT 0x0008 #define S14X_IOBOARD_BT_P2_UP 0x0010 #define S14X_IOBOARD_BT_P2_DOWN 0x0020 #define S14X_IOBOARD_BT_P2_RIGHT 0x0040 #define S14X_IOBOARD_BT_P2_LEFT 0x0080 #define S14X_IOBOARD_BT_P3_UP 0x0100 #define S14X_IOBOARD_BT_P3_DOWN 0x0200 #define S14X_IOBOARD_BT_P3_RIGHT 0x0400 #define S14X_IOBOARD_BT_P3_LEFT 0x0800 #define S14X_IOBOARD_BT_P1_UP 0x1000 #define S14X_IOBOARD_BT_P1_DOWN 0x2000 #define S14X_IOBOARD_BT_P1_RIGHT 0x4000 #define S14X_IOBOARD_BT_P1_LEFT 0x8000 struct s14x_ioboard { uint16_t version; uint16_t switches; uint16_t buttons; int mode; }; struct s14x_ioboard* s14x_ioboard_create(void); int s14x_ioboard_init(struct s14x_ioboard* ioboard, int mode); void s14x_ioboard_destroy(struct s14x_ioboard* ioboard); void s14x_ioboard_press_switch(struct s14x_ioboard* ioboard, uint16_t mask); void s14x_ioboard_release_switch(struct s14x_ioboard* ioboard, uint16_t mask); void s14x_ioboard_press_button(struct s14x_ioboard* ioboard, uint16_t mask); void s14x_ioboard_release_button(struct s14x_ioboard* ioboard, uint16_t mask); void s14x_ioboard_handle_packet(void* udata, struct link_packet* in, struct link_packet* out); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/s14x/link.c ================================================ #include "link.h" #include #include #include #include #define printf(fmt, ...)(0) const char* g_reg_names[] = { "S14X_LINK_PAD00", "S14X_LINK_COMR0", "S14X_LINK_PAD02", "S14X_LINK_COMR1", "S14X_LINK_PAD04", "S14X_LINK_COMR2", "S14X_LINK_PAD06", "S14X_LINK_COMR3", "S14X_LINK_PAD08", "S14X_LINK_COMR4", "S14X_LINK_PAD0A", "S14X_LINK_COMR5", "S14X_LINK_PAD0C", "S14X_LINK_COMR6", "S14X_LINK_PAD0E", "S14X_LINK_COMR7", "S14X_LINK_NSTH", "S14X_LINK_NSTL", "S14X_LINK_STSH", "S14X_LINK_STSL", "S14X_LINK_MSKH", "S14X_LINK_MSKL", "S14X_LINK_PAD16", "S14X_LINK_ECCMD", "S14X_LINK_MRSID", "S14X_LINK_RSID", "S14X_LINK_PAD1A", "S14X_LINK_SSID", "S14X_LINK_RXFHH", "S14X_LINK_RXFHL", "S14X_LINK_RXFLH", "S14X_LINK_RXFLL", "S14X_LINK_PAD20", "S14X_LINK_CMID", "S14X_LINK_MODEH", "S14X_LINK_MODEL", "S14X_LINK_CARRYH", "S14X_LINK_CARRYL", "S14X_LINK_RXMHH", "S14X_LINK_RXMHL", "S14X_LINK_RXMLH", "S14X_LINK_RXMLL", "S14X_LINK_PAD2A", "S14X_LINK_MAXID", "S14X_LINK_PAD2C", "S14X_LINK_NID", "S14X_LINK_PAD2E", "S14X_LINK_PS", "S14X_LINK_PAD30", "S14X_LINK_CKP", "S14X_LINK_NSTDIFH", "S14X_LINK_NSTDIFL", "S14X_LINK_WATCHDOG_FLAG" }; struct s14x_link* s14x_link_create(void) { return malloc(sizeof(struct s14x_link)); } void s14x_link_init(struct s14x_link* link, struct ps2_iop_intc* intc, struct sched_state* sched) { memset(link, 0, sizeof(struct s14x_link)); // ARCNET CORE interrupt (possibly sent after INIMODE was set?) // TA bit set link->stsl = 1; // RECON bit set link->comr0 = 4; link->intc = intc; link->sched = sched; } void link_send_irq(struct s14x_link* link, uint16_t irq) { link->stsl |= irq & 0xff; link->stsh |= (irq >> 8) & 0xff; ps2_iop_intc_irq(link->intc, IOP_INTC_DEV9); } uint64_t s14x_link_read(struct s14x_link* link, uint32_t addr) { uint32_t r = 0; switch (addr) { case S14X_LINK_PAD00: r = link->pad00; break; case S14X_LINK_COMR0: r = link->comr0; break; case S14X_LINK_PAD02: r = link->pad02; break; case S14X_LINK_COMR1: r = link->comr1; break; case S14X_LINK_PAD04: r = link->pad04; break; case S14X_LINK_COMR2: r = link->comr2; break; case S14X_LINK_PAD06: r = link->pad06; break; case S14X_LINK_COMR3: r = link->comr3; break; case S14X_LINK_PAD08: r = link->pad08; break; case S14X_LINK_COMR4: { uint32_t addr = link->ramadr; if (link->comr2 & S14X_LINK_COMR2_AUTOINC) { if (link->comr2 & S14X_LINK_COMR2_WRAPAR) { link->ramadr = (link->ramadr & 0x3c0) | ((link->ramadr + 1) & 0x3f); } else { link->ramadr = (link->ramadr + 1) & 0x3ff; } } // printf("s14x_link: Read RAM[%04x] = %02x\n", addr, link->ram[addr]); r = link->ram[addr]; } break; case S14X_LINK_PAD0A: r = link->pad0a; break; case S14X_LINK_COMR5: r = link->comr5; break; case S14X_LINK_PAD0C: r = link->pad0c; break; case S14X_LINK_COMR6: r = link->comr6; break; case S14X_LINK_PAD0E: r = link->pad0e; break; case S14X_LINK_COMR7: r = link->comr7; break; case S14X_LINK_NSTH: r = link->nsth; break; case S14X_LINK_NSTL: r = link->nstl; break; case S14X_LINK_STSH: r = link->stsh; break; case S14X_LINK_STSL: r = link->stsl; break; case S14X_LINK_MSKH: r = link->mskh; break; case S14X_LINK_MSKL: r = link->mskl; break; case S14X_LINK_PAD16: r = link->pad16; break; case S14X_LINK_ECCMD: r = link->eccmd; break; case S14X_LINK_MRSID: r = link->mrsid; break; case S14X_LINK_RSID: r = link->rsid; break; case S14X_LINK_PAD1A: r = link->pad1a; break; case S14X_LINK_SSID: r = link->ssid; break; case S14X_LINK_RXFHH: r = link->rxfhh; break; case S14X_LINK_RXFHL: r = link->rxfhl; break; case S14X_LINK_RXFLH: r = link->rxflh; break; case S14X_LINK_RXFLL: r = link->rxfll; break; case S14X_LINK_PAD20: r = link->pad20; break; case S14X_LINK_CMID: r = link->cmid; break; case S14X_LINK_MODEH: r = link->modeh; break; case S14X_LINK_MODEL: r = link->model; break; case S14X_LINK_CARRYH: r = link->carryh; break; case S14X_LINK_CARRYL: r = link->carryl; break; case S14X_LINK_RXMHH: r = link->rxmhh; break; case S14X_LINK_RXMHL: r = link->rxmhl; break; case S14X_LINK_RXMLH: r = link->rxmlh; break; case S14X_LINK_RXMLL: r = link->rxmll; break; case S14X_LINK_PAD2A: r = link->pad2a; break; case S14X_LINK_MAXID: r = link->maxid; break; case S14X_LINK_PAD2C: r = link->pad2c; break; case S14X_LINK_NID: r = link->nid; break; case S14X_LINK_PAD2E: r = link->pad2e; break; case S14X_LINK_PS: r = link->ps; break; case S14X_LINK_PAD30: r = link->pad30; break; case S14X_LINK_CKP: r = link->ckp; break; case S14X_LINK_NSTDIFH: r = link->nstdifh; break; case S14X_LINK_NSTDIFL: r = link->nstdifl; break; case S14X_LINK_WATCHDOG_FLAG: r = link->watchdog_flag; break; } if (addr != S14X_LINK_WATCHDOG_FLAG) { printf("s14x_link: Read %s (%08x) %08x\n", g_reg_names[addr], addr, r); } return r; } void s14x_link_register_node(struct s14x_link* link, int node, link_packet_handler handler, void* udata) { link->nodes[node].handler = handler; link->nodes[node].udata = udata; } // Note: pacmanap reverse engineering // mynode = 1, maxnodes = 2 // Main board is at node 1 // I/O board is at node 2 // pacmanbr // node = 1, maxnodes = 3 // Main board is at node 1 // I/O board is at node 2 // A.I./Standalone board is at node 3 void link_recv_reply(void* udata, int overshoot) { struct s14x_link* link = (struct s14x_link*)udata; struct link_packet* tx = (struct link_packet*)&link->ram[0x40]; struct link_packet* rx = (struct link_packet*)&link->ram[tx->dst_node * 0x40]; memset(rx, 0, sizeof(struct link_packet)); // Set TA and TMA bits (transmitter available) link->stsl |= S14X_LINK_STSL_TA; link->comr0 |= S14X_LINK_COMR0_R_TA | S14X_LINK_COMR0_R_TMA; struct link_node* node = &link->nodes[tx->dst_node]; if (!node->handler) { fprintf(stdout, "s14x_link: Packet sent to disconnected node %d (cmd=%02x cp=%02x)\n", tx->dst_node, tx->cmd, tx->cp ); return; } // Set packet pending flag link->rxfll = 1 << tx->dst_node; // Get response from the requested node node->handler(node->udata, tx, rx); // Send DEV9 IRQ to IOP ps2_iop_intc_irq(link->intc, IOP_INTC_DEV9); } void s14x_link_write(struct s14x_link* link, uint32_t addr, uint64_t data) { if (addr != S14X_LINK_WATCHDOG_FLAG) { printf(stdout, "s14x_link: Write %s (%08x) %08x\n", g_reg_names[addr], addr, data); } switch (addr) { case S14X_LINK_PAD00: link->pad00 = data; return; case S14X_LINK_COMR0: { link->comr0 &= ~(S14X_LINK_COMR0_R_RECON | S14X_LINK_COMR0_W_TA); link->comr0 |= data & (S14X_LINK_COMR0_R_RECON | S14X_LINK_COMR0_W_TA); if (data & 0xf) { link_send_irq(link, S14X_LINK_IRQ_COM); } } return; case S14X_LINK_PAD02: link->pad02 = data; return; case S14X_LINK_COMR1: link->comr1 = data; return; case S14X_LINK_PAD04: link->pad04 = data; return; case S14X_LINK_COMR2: { link->comr2 = data; link->ramadr = (link->ramadr & 0x3f) | ((data & 0xf) << 6); } return; case S14X_LINK_PAD06: link->pad06 = data; return; case S14X_LINK_COMR3: { link->comr3 = data; link->ramadr = (link->ramadr & 0x3c0) | (data & 0x3f); } return; case S14X_LINK_PAD08: link->pad08 = data; return; case S14X_LINK_COMR4: { uint32_t addr = link->ramadr; if (link->comr2 & S14X_LINK_COMR2_AUTOINC) { if (link->comr2 & S14X_LINK_COMR2_WRAPAR) { link->ramadr = (link->ramadr + 1) & 0x3ff; } else { link->ramadr = (link->ramadr & 0x3c0) | ((link->ramadr + 1) & 0x3f); } } // printf("s14x_link: Write RAM[%04x] = %02x\n", addr, data); link->ram[addr] = data; } return; case S14X_LINK_PAD0A: link->pad0a = data; return; case S14X_LINK_COMR5: link->comr5 = data; return; case S14X_LINK_PAD0C: link->pad0c = data; return; case S14X_LINK_COMR6: link->comr6 = data; return; case S14X_LINK_PAD0E: link->pad0e = data; return; case S14X_LINK_COMR7: link->comr7 = data; return; case S14X_LINK_NSTH: link->nsth = data; return; case S14X_LINK_NSTL: link->nstl = data; return; case S14X_LINK_STSH: { link->stsh &= 0x30; link->stsh |= data & 0xcf; } return; case S14X_LINK_STSL: { link->stsl &= 0x09; link->stsl |= data & 0xf6; } return; case S14X_LINK_MSKH: { link->mskh = data; } return; case S14X_LINK_MSKL: { link->mskl = data; } return; case S14X_LINK_PAD16: link->pad16 = data; return; case S14X_LINK_ECCMD: { link->eccmd = data; switch (link->eccmd) { case 0x03: { struct sched_event event; event.callback = link_recv_reply; event.udata = link; event.cycles = 10000; event.name = "Link reply"; link->stsl &= ~S14X_LINK_STSL_TA; link->comr0 &= ~(S14X_LINK_COMR0_R_TA | S14X_LINK_COMR0_R_TMA); sched_schedule(link->sched, event); } break; case 0x16: { // Clear RECON bit link->comr0 &= ~S14X_LINK_COMR0_W_RECON; } break; default: { printf("s14x_link: Unhandled EC command %02x\n", link->eccmd); } break; } } return; case S14X_LINK_MRSID: link->mrsid = data; return; case S14X_LINK_RSID: link->rsid = data; return; case S14X_LINK_PAD1A: link->pad1a = data; return; case S14X_LINK_SSID: link->ssid = data; return; // Writing clears receive flags case S14X_LINK_RXFHH: link->rxfhh &= ~data; return; case S14X_LINK_RXFHL: link->rxfhl &= ~data; return; case S14X_LINK_RXFLH: link->rxflh &= ~data; return; case S14X_LINK_RXFLL: link->rxfll &= ~data; return; case S14X_LINK_PAD20: link->pad20 = data; return; case S14X_LINK_CMID: link->cmid = data; return; case S14X_LINK_MODEH: { // Software reset if (link->modeh & S14X_LINK_MODEH_INIMODE != data & S14X_LINK_MODEH_INIMODE) { link->stsl = 0; link->stsh = 0; link->mskl = 0; link->mskh = 0; link->comr0 = 0; link->comr1 = 0; } link->modeh = data; } break; case S14X_LINK_MODEL: link->model = data; return; case S14X_LINK_CARRYH: link->carryh = data; return; case S14X_LINK_CARRYL: link->carryl = data; return; case S14X_LINK_RXMHH: link->rxmhh = data; return; case S14X_LINK_RXMHL: link->rxmhl = data; return; case S14X_LINK_RXMLH: link->rxmlh = data; return; case S14X_LINK_RXMLL: link->rxmll = data; return; case S14X_LINK_PAD2A: link->pad2a = data; return; case S14X_LINK_MAXID: link->maxid = data; return; case S14X_LINK_PAD2C: link->pad2c = data; return; case S14X_LINK_NID: link->nid = data; return; case S14X_LINK_PAD2E: link->pad2e = data; return; case S14X_LINK_PS: link->ps = data; return; case S14X_LINK_PAD30: link->pad30 = data; return; case S14X_LINK_CKP: link->ckp = data; return; case S14X_LINK_NSTDIFH: link->nstdifh = data; return; case S14X_LINK_NSTDIFL: link->nstdifl = data; return; case S14X_LINK_WATCHDOG_FLAG: link->watchdog_flag = data; return; } } void s14x_link_destroy(struct s14x_link* link) { free(link); } ================================================ FILE: src/s14x/link.h ================================================ struct s14x_link; #ifndef S14X_LINK_H #define S14X_LINK_H #ifdef __cplusplus extern "C" { #endif #include #include #include "iop/intc.h" #include "scheduler.h" #define S14X_LINK_PAD00 0x00 #define S14X_LINK_COMR0 0x01 #define S14X_LINK_PAD02 0x02 #define S14X_LINK_COMR1 0x03 #define S14X_LINK_PAD04 0x04 #define S14X_LINK_COMR2 0x05 #define S14X_LINK_PAD06 0x06 #define S14X_LINK_COMR3 0x07 #define S14X_LINK_PAD08 0x08 #define S14X_LINK_COMR4 0x09 #define S14X_LINK_PAD0A 0x0a #define S14X_LINK_COMR5 0x0b #define S14X_LINK_PAD0C 0x0c #define S14X_LINK_COMR6 0x0d #define S14X_LINK_PAD0E 0x0e #define S14X_LINK_COMR7 0x0f #define S14X_LINK_NSTH 0x10 #define S14X_LINK_NSTL 0x11 #define S14X_LINK_STSH 0x12 #define S14X_LINK_STSL 0x13 #define S14X_LINK_MSKH 0x14 #define S14X_LINK_MSKL 0x15 #define S14X_LINK_PAD16 0x16 #define S14X_LINK_ECCMD 0x17 #define S14X_LINK_MRSID 0x18 #define S14X_LINK_RSID 0x19 #define S14X_LINK_PAD1A 0x1a #define S14X_LINK_SSID 0x1b #define S14X_LINK_RXFHH 0x1c #define S14X_LINK_RXFHL 0x1d #define S14X_LINK_RXFLH 0x1e #define S14X_LINK_RXFLL 0x1f #define S14X_LINK_PAD20 0x20 #define S14X_LINK_CMID 0x21 #define S14X_LINK_MODEH 0x22 #define S14X_LINK_MODEL 0x23 #define S14X_LINK_CARRYH 0x24 #define S14X_LINK_CARRYL 0x25 #define S14X_LINK_RXMHH 0x26 #define S14X_LINK_RXMHL 0x27 #define S14X_LINK_RXMLH 0x28 #define S14X_LINK_RXMLL 0x29 #define S14X_LINK_PAD2A 0x2a #define S14X_LINK_MAXID 0x2b #define S14X_LINK_PAD2C 0x2c #define S14X_LINK_NID 0x2d #define S14X_LINK_PAD2E 0x2e #define S14X_LINK_PS 0x2f #define S14X_LINK_PAD30 0x30 #define S14X_LINK_CKP 0x31 #define S14X_LINK_NSTDIFH 0x32 #define S14X_LINK_NSTDIFL 0x33 #define S14X_LINK_WATCHDOG_FLAG 0x34 #define S14X_LINK_COMR2_RDDATA 0x80 #define S14X_LINK_COMR2_AUTOINC 0x40 #define S14X_LINK_COMR2_WRAPAR 0x20 #define S14X_LINK_COMR2_PAGE 0x1f #define S14X_LINK_COMR0_W_EXCNAK 8 #define S14X_LINK_COMR0_W_RECON 4 #define S14X_LINK_COMR0_W_NXTIDERR 2 #define S14X_LINK_COMR0_W_TA 1 #define S14X_LINK_COMR0_R_POR 0x10 #define S14X_LINK_COMR0_R_RECON 4 #define S14X_LINK_COMR0_R_TMA 2 #define S14X_LINK_COMR0_R_TA 1 #define S14X_LINK_IRQ_RXERR 0x8000 #define S14X_LINK_IRQ_CMIECC 0x4000 #define S14X_LINK_IRQ_NSTUNLOC 0x2000 #define S14X_LINK_IRQ_WARTERR 0x1000 #define S14X_LINK_IRQ_FRCV 0x0800 #define S14X_LINK_IRQ_RRCV 0x0300 #define S14X_LINK_IRQ_MRCV 0x0200 #define S14X_LINK_IRQ_SIDF 0x0100 #define S14X_LINK_IRQ_TKNRETF 0x0080 #define S14X_LINK_IRQ_ACKNAKF 0x0040 #define S14X_LINK_IRQ_HUBWDTO 0x0020 #define S14X_LINK_IRQ_CPERR 0x0010 #define S14X_LINK_IRQ_COM 0x0008 #define S14X_LINK_IRQ_FBENR 0x0003 #define S14X_LINK_IRQ_TXERR 0x0002 #define S14X_LINK_IRQ_TA 0x0001 #define S14X_LINK_MODEH_CMIERRMD 0x10 #define S14X_LINK_MODEH_NSTSEND 0x08 #define S14X_LINK_MODEH_NSTSTOP 0x04 #define S14X_LINK_MODEH_INIMODE 0x02 #define S14X_LINK_MODEH_TXEN 0x01 #define S14X_LINK_MODEL_ECRI 0x80 #define S14X_LINK_MODEL_BRE 0x40 #define S14X_LINK_MODEL_TXM 0x20 #define S14X_LINK_MODEL_RTO 0x10 #define S14X_LINK_MODEL_WDMD 0x08 #define S14X_LINK_MODEL_NTKNRTY 0x04 #define S14X_LINK_MODEL_NACKNAK 0x02 #define S14X_LINK_MODEL_NACLR 0x01 #define S14X_LINK_MSKH_RXERR 0x80 #define S14X_LINK_MSKH_CMIECC 0x40 #define S14X_LINK_MSKH_NSTUNLOC 0x20 #define S14X_LINK_MSKH_WARTERR 0x10 #define S14X_LINK_MSKH_FRCV 0x08 #define S14X_LINK_MSKH_RRCV 0x03 #define S14X_LINK_MSKH_MRCV 0x02 #define S14X_LINK_MSKH_SIDF 0x01 #define S14X_LINK_MSKL_TKNRETF 0x80 #define S14X_LINK_MSKL_ACKNAKF 0x40 #define S14X_LINK_MSKL_HUBWDTO 0x20 #define S14X_LINK_MSKL_CPERR 0x10 #define S14X_LINK_MSKL_COM 0x08 #define S14X_LINK_MSKL_FBENR 0x03 #define S14X_LINK_MSKL_TXERR 0x02 #define S14X_LINK_MSKL_TA 0x01 #define S14X_LINK_STSH_RXERR 0x80 #define S14X_LINK_STSH_CMIECC 0x40 #define S14X_LINK_STSH_NSTUNLOC 0x20 #define S14X_LINK_STSH_WARTERR 0x10 #define S14X_LINK_STSH_FRCV 0x08 #define S14X_LINK_STSH_RRCV 0x03 #define S14X_LINK_STSH_MRCV 0x02 #define S14X_LINK_STSH_SIDF 0x01 #define S14X_LINK_STSL_TKNRETF 0x80 #define S14X_LINK_STSL_ACKNAKF 0x40 #define S14X_LINK_STSL_HUBWDTO 0x20 #define S14X_LINK_STSL_CPERR 0x10 #define S14X_LINK_STSL_COM 0x08 #define S14X_LINK_STSL_FBENR 0x03 #define S14X_LINK_STSL_TXERR 0x02 #define S14X_LINK_STSL_TA 0x01 #define S14X_LINK_RAMSIZE 1024 // ARCNET packets are 64 bytes long struct link_packet { union { uint8_t raw[64]; struct { // Offset 00h - Node of the sender uint8_t src_node; // Offset 01h - Node of the receiver uint8_t dst_node; // Offset 02h - Continuation Pointer (data offset within packet) uint8_t cp; // Offset 03h - Unknown (must be 00h) uint8_t unk03; // Offset 04h - Packet number in a sequence uint8_t seq_number; // Offset 05h - I/O board command uint8_t cmd; // Offset 06h-3Eh - Command payload/data uint8_t data[0x39]; // Offsset 3Fh - Checksum of all the data bytes uint8_t checksum; }; }; }; typedef void (*link_packet_handler)(void*, struct link_packet*, struct link_packet*); struct link_node { link_packet_handler handler; void* udata; }; static uint8_t link_calculate_checksum(struct link_packet* packet); struct s14x_link { uint8_t pad00; uint8_t comr0; uint8_t pad02; uint8_t comr1; uint8_t pad04; uint8_t comr2; uint8_t pad06; uint8_t comr3; uint8_t pad08; uint8_t comr4; uint8_t pad0a; uint8_t comr5; uint8_t pad0c; uint8_t comr6; uint8_t pad0e; uint8_t comr7; uint8_t nsth; uint8_t nstl; uint8_t stsh; uint8_t stsl; uint8_t mskh; uint8_t mskl; uint8_t pad16; uint8_t eccmd; uint8_t mrsid; uint8_t rsid; uint8_t pad1a; uint8_t ssid; uint8_t rxfhh; uint8_t rxfhl; uint8_t rxflh; uint8_t rxfll; uint8_t pad20; uint8_t cmid; uint8_t modeh; uint8_t model; uint8_t carryh; uint8_t carryl; uint8_t rxmhh; uint8_t rxmhl; uint8_t rxmlh; uint8_t rxmll; uint8_t pad2a; uint8_t maxid; uint8_t pad2c; uint8_t nid; uint8_t pad2e; uint8_t ps; uint8_t pad30; uint8_t ckp; uint8_t nstdifh; uint8_t nstdifl; uint8_t watchdog_flag; uint8_t ram[S14X_LINK_RAMSIZE]; uint32_t ramadr; struct link_node nodes[32]; struct ps2_iop_intc* intc; struct sched_state* sched; }; struct s14x_link* s14x_link_create(void); void s14x_link_init(struct s14x_link* link, struct ps2_iop_intc* intc, struct sched_state* sched); uint64_t s14x_link_read(struct s14x_link* link, uint32_t addr); void s14x_link_write(struct s14x_link* link, uint32_t addr, uint64_t data); void s14x_link_register_node(struct s14x_link* link, int node, link_packet_handler handler, void* udata); void s14x_link_send_packet(struct s14x_link* link, struct link_packet packet); void s14x_link_destroy(struct s14x_link* link); uint8_t link_calculate_checksum(struct link_packet* packet); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/s14x/nand.c ================================================ #include "nand.h" #include #include #include #include struct s14x_nand* s14x_nand_create(void) { return malloc(sizeof(struct s14x_nand)); } int s14x_nand_init(struct s14x_nand* nand) { memset(nand, 0, sizeof(struct s14x_nand)); nand->buf = malloc(S14X_NAND_PAGE_SIZE_ECC); nand->state = S14X_NAND_STATE_READ_BYTE0; return 1; } int s14x_nand_load(struct s14x_nand* nand, const char* path) { nand->file = fopen(path, "rb"); if (!nand->file) { return 0; } return 1; } void nand_handle_offset_write(struct s14x_nand* nand, uint8_t data) { switch (nand->state) { case S14X_NAND_STATE_READ_BYTE0: { nand->byte_offset = (nand->byte_offset & 0xff00) | data; nand->state++; } break; case S14X_NAND_STATE_READ_BYTE1: { nand->byte_offset = (nand->byte_offset & 0x00ff) | (data << 8); nand->state++; } break; case S14X_NAND_STATE_READ_PAGE0: { nand->page_offset = (nand->page_offset & 0xffff00) | data; nand->state++; } break; case S14X_NAND_STATE_READ_PAGE1: { nand->page_offset = (nand->page_offset & 0xff00ff) | (data << 8); nand->state++; } break; case S14X_NAND_STATE_READ_PAGE2: { nand->page_offset = (nand->page_offset & 0x00ffff) | ((uint32_t)data << 16); nand->state = S14X_NAND_STATE_READ_BYTE0; } break; } } void nand_handle_cmd_read(struct s14x_nand* nand) { nand->size = S14X_NAND_PAGE_SIZE_ECC; nand->index = nand->byte_offset; if (!nand->file) { memset(nand->buf, 0, S14X_NAND_PAGE_SIZE_ECC); return; } fseek(nand->file, nand->page_offset * S14X_NAND_PAGE_SIZE_ECC, SEEK_SET); fread(nand->buf, 1, S14X_NAND_PAGE_SIZE_ECC, nand->file); } uint64_t s14x_nand_read(struct s14x_nand* nand, uint32_t addr) { switch (addr) { case S14X_NAND_REG_OUTBYTE: { int index = nand->index++ % nand->size; return nand->buf[index]; } break; default: { // printf("s14x_nand: Unhandled register read %02x\n", addr); } break; } } void s14x_nand_write(struct s14x_nand* nand, uint32_t addr, uint64_t data) { switch (addr) { case S14X_NAND_REG_CMD: { nand->cmd = data; switch (nand->cmd) { case 0: { // NOP } break; case S14X_NAND_CMD_READ: { nand_handle_cmd_read(nand); } break; default: { printf("s14x_nand: Unhandled command %02x\n", nand->cmd); exit(1); } break; } } break; case S14X_NAND_REG_OFFSET: { nand_handle_offset_write(nand, data); } break; default: { // printf("s14x_nand: Unhandled register write %02x = %02x\n", addr, (uint8_t)data); } break; } } void s14x_nand_destroy(struct s14x_nand* nand) { if (nand->buf) free(nand->buf); free(nand); } ================================================ FILE: src/s14x/nand.h ================================================ #ifndef S14X_NAND_H #define S14X_NAND_H #ifdef __cplusplus extern "C" { #endif #include #include #define S14X_NAND_CMD_READ 0x30 #define S14X_NAND_CMD_ERASE 0x60 #define S14X_NAND_CMD_WRITE 0x80 #define S14X_NAND_CMD_READID 0x90 #define S14X_NAND_PAGE_SIZE_NOECC 0x800 #define S14X_NAND_PAGE_SIZE_ECC 0x840 #define S14X_NAND_PAGES_PER_BLOCK 0x40 #define S14X_NAND_STATE_READ_BYTE0 0 #define S14X_NAND_STATE_READ_BYTE1 1 #define S14X_NAND_STATE_READ_PAGE0 2 #define S14X_NAND_STATE_READ_PAGE1 3 #define S14X_NAND_STATE_READ_PAGE2 4 #define S14X_NAND_REG_WAITFLAG 0 #define S14X_NAND_REG_ENABLE 1 #define S14X_NAND_REG_CMD 2 #define S14X_NAND_REG_OFFSET 3 #define S14X_NAND_REG_WRITE_UNLOCK 4 #define S14X_NAND_REG_OUTBYTE 8 struct s14x_nand { FILE* file; int enable; uint8_t cmd; uint8_t* buf; int index; int size; uint16_t byte_offset; uint32_t page_offset; int state; }; struct s14x_nand* s14x_nand_create(void); int s14x_nand_init(struct s14x_nand* nand); int s14x_nand_load(struct s14x_nand* nand, const char* path); uint64_t s14x_nand_read(struct s14x_nand* nand, uint32_t addr); void s14x_nand_write(struct s14x_nand* nand, uint32_t addr, uint64_t data); void s14x_nand_destroy(struct s14x_nand* nand); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/s14x/sram.c ================================================ #include "sram.h" #include #include #include struct s14x_sram* s14x_sram_create(void) { return malloc(sizeof(struct s14x_sram)); } int s14x_sram_init(struct s14x_sram* sram, int* write_flag) { memset(sram, 0, sizeof(struct s14x_sram)); sram->write_flag = write_flag; return 0; } int s14x_sram_load(struct s14x_sram* sram, const char* path) { FILE* file = fopen(path, "rb"); sram->path = path; // If the file doesn't exist then we'll create it if (!file) { fclose(file); file = fopen(path, "wb"); fwrite(sram->buf, 1, S14X_SRAM_SIZE, file); fclose(file); return 0; } fread(sram->buf, 1, S14X_SRAM_SIZE, file); fclose(file); return 1; } uint64_t s14x_sram_read8(struct s14x_sram* sram, uint32_t addr) { return sram->buf[addr & 0x7fff]; } uint64_t s14x_sram_read16(struct s14x_sram* sram, uint32_t addr) { return *(uint16_t*)(sram->buf + (addr & 0x7fff)); } uint64_t s14x_sram_read32(struct s14x_sram* sram, uint32_t addr) { return *(uint32_t*)(sram->buf + (addr & 0x7fff)); } void s14x_sram_write8(struct s14x_sram* sram, uint32_t addr, uint64_t data) { if (sram->write_flag && !*sram->write_flag) return; sram->buf[addr & 0x7fff] = data & 0xff; } void s14x_sram_write16(struct s14x_sram* sram, uint32_t addr, uint64_t data) { if (sram->write_flag && !*sram->write_flag) return; *(uint16_t*)(sram->buf + (addr & 0x7fff)) = data & 0xffff; } void s14x_sram_write32(struct s14x_sram* sram, uint32_t addr, uint64_t data) { if (sram->write_flag && !*sram->write_flag) return; *(uint32_t*)(sram->buf + (addr & 0x7fff)) = data & 0xffffffff; } void s14x_sram_destroy(struct s14x_sram* sram) { if (sram->path) { FILE* file = fopen(sram->path, "wb"); fwrite(sram->buf, 1, S14X_SRAM_SIZE, file); fclose(file); } free(sram); } ================================================ FILE: src/s14x/sram.h ================================================ #ifndef S14X_SRAM_H #define S14X_SRAM_H #ifdef __cplusplus extern "C" { #endif #include #include #define S14X_SRAM_SIZE 0x8000 struct s14x_sram { const char* path; int* write_flag; uint8_t buf[S14X_SRAM_SIZE]; }; struct s14x_sram* s14x_sram_create(void); int s14x_sram_init(struct s14x_sram* sram, int* write_flag); int s14x_sram_load(struct s14x_sram* sram, const char* path); uint64_t s14x_sram_read8(struct s14x_sram* sram, uint32_t addr); uint64_t s14x_sram_read16(struct s14x_sram* sram, uint32_t addr); uint64_t s14x_sram_read32(struct s14x_sram* sram, uint32_t addr); void s14x_sram_write8(struct s14x_sram* sram, uint32_t addr, uint64_t data); void s14x_sram_write16(struct s14x_sram* sram, uint32_t addr, uint64_t data); void s14x_sram_write32(struct s14x_sram* sram, uint32_t addr, uint64_t data); void s14x_sram_destroy(struct s14x_sram* sram); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/s14x/syscon.c ================================================ #include "syscon.h" #include #include #include struct s14x_syscon* s14x_syscon_create(void) { return malloc(sizeof(struct s14x_syscon)); } void s14x_syscon_init(struct s14x_syscon* syscon) { memset(syscon, 0, sizeof(struct s14x_syscon)); syscon->battery_level = 0x0; // 0 - OK, non-zero - NG } uint64_t s14x_syscon_read(struct s14x_syscon* syscon, uint32_t addr) { switch (addr) { case S14X_SYSCON_REG_LED: return syscon->led; case S14X_SYSCON_REG_SECURITY_UNLOCK: return syscon->security_unlock; case S14X_SYSCON_REG_RTC_FLAG: { switch (syscon->rtc_state) { case S14X_RTC_STATE_READ_YEAR: syscon->rtc_flag = 23; break; case S14X_RTC_STATE_READ_MONTH: syscon->rtc_flag = 6; break; case S14X_RTC_STATE_READ_DAY: syscon->rtc_flag = 15; break; case S14X_RTC_STATE_READ_DOW: syscon->rtc_flag = 4; break; case S14X_RTC_STATE_READ_HOURS: syscon->rtc_flag = 12; break; case S14X_RTC_STATE_READ_MINUTES: syscon->rtc_flag = 34; break; case S14X_RTC_STATE_READ_SECONDS: syscon->rtc_flag = 56; break; } int b = (syscon->rtc_flag >> syscon->rtc_bit++) & 1; int bits = syscon->rtc_state == S14X_RTC_STATE_READ_DOW ? 4 : 8; if (syscon->rtc_bit >= bits) { syscon->rtc_bit = 0; syscon->rtc_state++; if (syscon->rtc_state > S14X_RTC_STATE_READ_SECONDS) { syscon->rtc_state = S14X_RTC_STATE_READ_YEAR; } } // printf("s14x_rtc: read RTC_FLAG %d\n", syscon->rtc_flag); return b; } case S14X_SYSCON_REG_WATCHDOG_FLAG2: return syscon->watchdog_flag2; case S14X_SYSCON_REG_BATTERY_LEVEL: return syscon->battery_level; case S14X_SYSCON_REG_SRAM_WRITE_FLAG: return syscon->sram_write_flag; case S14X_SYSCON_REG_SECURITY_UNLOCK_SET1: return syscon->security_unlock_set1; case S14X_SYSCON_REG_SECURITY_UNLOCK_SET2: return syscon->security_unlock_set2; default: printf("s14x_syscon: Unknown register read %08x\n", addr); return 0; } return 0; } void s14x_syscon_write(struct s14x_syscon* syscon, uint32_t addr, uint64_t data) { switch (addr) { case S14X_SYSCON_REG_LED: syscon->led = data; return; case S14X_SYSCON_REG_SECURITY_UNLOCK: syscon->security_unlock = data; return; // case S14X_SYSCON_REG_RTC_FLAG: syscon->rtc_flag = data; return; case S14X_SYSCON_REG_WATCHDOG_FLAG2: syscon->watchdog_flag2 = data; return; case S14X_SYSCON_REG_BATTERY_LEVEL: syscon->battery_level = data; return; case S14X_SYSCON_REG_SRAM_WRITE_FLAG: syscon->sram_write_flag = data; return; case S14X_SYSCON_REG_SECURITY_UNLOCK_SET1: syscon->security_unlock_set1 = data; return; case S14X_SYSCON_REG_SECURITY_UNLOCK_SET2: syscon->security_unlock_set2 = data; return; default: return; // printf("s14x_syscon: Unknown register write %08x %08lx\n", addr, data); return; } } void s14x_syscon_destroy(struct s14x_syscon* syscon) { free(syscon); } ================================================ FILE: src/s14x/syscon.h ================================================ // 10C00000 - 10C07FFF: S14X SRAM (32 KB) #ifndef S14X_SYSCON_H #define S14X_SYSCON_H #ifdef __cplusplus extern "C" { #endif #include #include #define S14X_SYSCON_REG_LED 1 #define S14X_SYSCON_REG_SECURITY_UNLOCK 2 #define S14X_SYSCON_REG_RTC_FLAG 4 #define S14X_SYSCON_REG_WATCHDOG_FLAG2 5 #define S14X_SYSCON_REG_BATTERY_LEVEL 6 #define S14X_SYSCON_REG_SRAM_WRITE_FLAG 7 #define S14X_SYSCON_REG_SECURITY_UNLOCK_SET1 12 #define S14X_SYSCON_REG_SECURITY_UNLOCK_SET2 13 #define S14X_RTC_STATE_READ_YEAR 0 #define S14X_RTC_STATE_READ_MONTH 1 #define S14X_RTC_STATE_READ_DAY 2 #define S14X_RTC_STATE_READ_DOW 3 #define S14X_RTC_STATE_READ_HOURS 4 #define S14X_RTC_STATE_READ_MINUTES 5 #define S14X_RTC_STATE_READ_SECONDS 6 struct s14x_syscon { uint8_t led; uint8_t security_unlock; uint8_t rtc_flag; uint8_t battery_level; uint8_t watchdog_flag2; uint8_t security_unlock_set1; uint8_t security_unlock_set2; int sram_write_flag; int rtc_state; int rtc_bit; }; struct s14x_syscon* s14x_syscon_create(void); void s14x_syscon_init(struct s14x_syscon* syscon); uint64_t s14x_syscon_read(struct s14x_syscon* syscon, uint32_t addr); void s14x_syscon_write(struct s14x_syscon* syscon, uint32_t addr, uint64_t data); void s14x_syscon_destroy(struct s14x_syscon* syscon); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/scheduler.c ================================================ #include #include #include #include #include #include "scheduler.h" struct sched_state* sched_create(void) { return malloc(sizeof(struct sched_state)); } void sched_init(struct sched_state* sched) { memset(sched, 0, sizeof(struct sched_state)); if (sched->events) free(sched->events); sched->nevents = 0; sched->cap = 0; sched->events = NULL; sched->offset = 0; } int event_compare(const void* a, const void* b) { return ((struct sched_event*)a)->cycles - ((struct sched_event*)b)->cycles; } void sched_schedule(struct sched_state* sched, struct sched_event event) { if (!sched->nevents) { sched->events = realloc(sched->events, sizeof(struct sched_event) * 32); if (!sched->events) { printf("sched: Failed to allocate new event\n"); exit(1); } sched->cap = 32; sched->nevents = 1; sched->events[0] = event; } else if (sched->nevents == 1) { if (sched->events[0].cycles > event.cycles) { sched->events[1] = sched->events[0]; sched->events[0] = event; } else { sched->events[1] = event; } // Clear offset, if there were more events in the // queue, then we should subtract the offset from all of // those. sched->offset = 0; sched->nevents = 2; } else { if (sched->nevents == sched->cap) { sched->cap <<= 1; sched->events = realloc(sched->events, sizeof(struct sched_event) * sched->cap); } // Need to sync events // Don't apply offset to the current nearest event (offset is applied by ticking) for (int i = 1; i < sched->nevents; i++) { sched->events[i].cycles -= sched->offset; } sched->offset = 0; for (int i = 0; i < sched->nevents; i++) { sched->events[sched->nevents - i] = sched->events[sched->nevents - i - 1]; } ++sched->nevents; sched->events[0] = event; if (sched->events[0].cycles > sched->events[1].cycles) { qsort(sched->events, sched->nevents, sizeof(struct sched_event), event_compare); } } } int sched_tick(struct sched_state* sched, int cycles) { if (!sched->nevents) return 0; sched->events[0].cycles -= cycles; sched->offset += cycles; if (sched->events[0].cycles > 0) return 0; --sched->nevents; struct sched_event event = sched->events[0]; for (int i = 0; i < sched->nevents; i++) { sched->events[i] = sched->events[i + 1]; sched->events[i].cycles -= sched->offset; } // Provide callback with overshot cycles event.callback(event.udata, event.cycles); sched->offset = 0; return 1; } const struct sched_event* sched_next_event(struct sched_state* sched) { return &sched->events[0]; } void sched_reset(struct sched_state* sched) { sched->nevents = 0; sched->offset = 0; } void sched_destroy(struct sched_state* sched) { free(sched->events); free(sched); } ================================================ FILE: src/scheduler.h ================================================ #ifndef SCHED_H #define SCHED_H #ifdef __cplusplus extern "C" { #endif #include struct sched_event { long cycles; void (*callback)(void*, int); const char* name; void* udata; }; struct sched_state { struct sched_event* events; int nevents; int cap; uint64_t offset; }; struct sched_state* sched_create(void); void sched_init(struct sched_state* sched); void sched_schedule(struct sched_state* sched, struct sched_event event); void sched_reset(struct sched_state* sched); int sched_tick(struct sched_state* sched, int cycles); const struct sched_event* sched_next_event(struct sched_state* sched); void sched_destroy(struct sched_state* sched); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/shared/bios.c ================================================ #include #include #include #include "bios.h" struct ps2_bios* ps2_bios_create(void) { return malloc(sizeof(struct ps2_bios)); } static int get_file_size(FILE* file) { int prev = ftell(file); int size; fseek(file, 0, SEEK_END); size = ftell(file); fseek(file, 0, SEEK_SET); return size; } void ps2_bios_init(struct ps2_bios* bios) { memset(bios, 0, sizeof(struct ps2_bios)); // Initialize dummy data bios->buf = malloc(0x400000); bios->size = 0x3fffff; memset(bios->buf, 0, 0x400000); uint32_t* ptr = (uint32_t*)bios->buf; ptr[0] = 0x1000fffe; // b 0x00000000 } int ps2_bios_load(struct ps2_bios* bios, const char* path) { if (!path) return 1; FILE* file = fopen(path, "rb"); if (!file) return 1; // Free dummy data free(bios->buf); // "size" is actually a mask bios->size = get_file_size(file) - 1; bios->buf = malloc(bios->size + 1); if (!fread(bios->buf, 1, bios->size + 1, file)) { printf("bios: Couldn't read binary from \'%s\'\n", path); return 1; } return 0; } void ps2_bios_destroy(struct ps2_bios* bios) { free(bios->buf); free(bios); } uint64_t ps2_bios_read8(struct ps2_bios* bios, uint32_t addr) { return *(uint8_t*)(bios->buf + (addr & bios->size)); } uint64_t ps2_bios_read16(struct ps2_bios* bios, uint32_t addr) { return *(uint16_t*)(bios->buf + (addr & bios->size)); } uint64_t ps2_bios_read32(struct ps2_bios* bios, uint32_t addr) { return *(uint32_t*)(bios->buf + (addr & bios->size)); } uint64_t ps2_bios_read64(struct ps2_bios* bios, uint32_t addr) { return *(uint64_t*)(bios->buf + (addr & bios->size)); } uint128_t ps2_bios_read128(struct ps2_bios* bios, uint32_t addr) { return *(uint128_t*)(bios->buf + (addr & bios->size)); } void ps2_bios_write8(struct ps2_bios* bios, uint32_t addr, uint64_t data) { *(uint8_t*)(bios->buf + (addr & bios->size)) = data; } void ps2_bios_write16(struct ps2_bios* bios, uint32_t addr, uint64_t data) { *(uint16_t*)(bios->buf + (addr & bios->size)) = data; } void ps2_bios_write32(struct ps2_bios* bios, uint32_t addr, uint64_t data) { *(uint32_t*)(bios->buf + (addr & bios->size)) = data; } void ps2_bios_write64(struct ps2_bios* bios, uint32_t addr, uint64_t data) { *(uint64_t*)(bios->buf + (addr & bios->size)) = data; } void ps2_bios_write128(struct ps2_bios* bios, uint32_t addr, uint128_t data) { *(uint128_t*)(bios->buf + (addr & bios->size)) = data; } ================================================ FILE: src/shared/bios.h ================================================ #ifndef BIOS_H #define BIOS_H #ifdef __cplusplus extern "C" { #endif #include "u128.h" struct ps2_bios { uint8_t* buf; size_t size; }; struct ps2_bios* ps2_bios_create(void); void ps2_bios_init(struct ps2_bios* bios); int ps2_bios_load(struct ps2_bios* bios, const char* path); void ps2_bios_destroy(struct ps2_bios* bios); uint64_t ps2_bios_read8(struct ps2_bios* bios, uint32_t addr); uint64_t ps2_bios_read16(struct ps2_bios* bios, uint32_t addr); uint64_t ps2_bios_read32(struct ps2_bios* bios, uint32_t addr); uint64_t ps2_bios_read64(struct ps2_bios* bios, uint32_t addr); uint128_t ps2_bios_read128(struct ps2_bios* bios, uint32_t addr); void ps2_bios_write8(struct ps2_bios* bios, uint32_t addr, uint64_t data); void ps2_bios_write16(struct ps2_bios* bios, uint32_t addr, uint64_t data); void ps2_bios_write32(struct ps2_bios* bios, uint32_t addr, uint64_t data); void ps2_bios_write64(struct ps2_bios* bios, uint32_t addr, uint64_t data); void ps2_bios_write128(struct ps2_bios* bios, uint32_t addr, uint128_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/shared/dev9.c ================================================ #include #include #include #include "dev9.h" struct ps2_dev9* ps2_dev9_create(void) { return malloc(sizeof(struct ps2_dev9)); } void ps2_dev9_init(struct ps2_dev9* dev9, int model) { memset(dev9, 0, sizeof(struct ps2_dev9)); dev9->rev = model; } void ps2_dev9_destroy(struct ps2_dev9* dev9) { free(dev9); } uint64_t ps2_dev9_read8(struct ps2_dev9* dev9, uint32_t addr) { switch (addr) { case 0x1f80146e: return dev9->rev; } printf("dev9: Unknown 8-bit read at address %08x\n", addr); return 0; } uint64_t ps2_dev9_read16(struct ps2_dev9* dev9, uint32_t addr) { switch (addr) { case 0x1f80146e: return dev9->rev; } printf("dev9: Unknown 16-bit read at address %08x\n", addr); return 0; } uint64_t ps2_dev9_read32(struct ps2_dev9* dev9, uint32_t addr) { switch (addr) { case 0x1f80146e: return dev9->rev; } printf("dev9: Unknown 32-bit read at address %08x\n", addr); return 0; } void ps2_dev9_write8(struct ps2_dev9* dev9, uint32_t addr, uint64_t data) { printf("dev9: Unknown 8-bit write at address %08x (%04lx)\n", addr, data); return; } void ps2_dev9_write16(struct ps2_dev9* dev9, uint32_t addr, uint64_t data) { printf("dev9: Unknown 16-bit write at address %08x (%04lx)\n", addr, data); return; } void ps2_dev9_write32(struct ps2_dev9* dev9, uint32_t addr, uint64_t data) { printf("dev9: Unknown 32-bit write at address %08x (%04lx)\n", addr, data); return; } ================================================ FILE: src/shared/dev9.h ================================================ #ifndef DEV9_H #define DEV9_H #ifdef __cplusplus extern "C" { #endif #include #include #define DEV9_TYPE_PCMCIA 0x20 // CXD9566 #define DEV9_TYPE_EXPBAY 0x30 // CXD9611 struct ps2_dev9 { uint16_t r_1460; uint16_t r_1462; uint16_t r_1464; uint16_t r_1466; uint16_t r_1468; uint16_t r_146a; uint16_t power; uint16_t rev; uint16_t r_1470; uint16_t r_1472; uint16_t r_1474; uint16_t r_1476; uint16_t r_1478; uint16_t r_147a; uint16_t r_147c; uint16_t r_147e; }; struct ps2_dev9* ps2_dev9_create(void); void ps2_dev9_init(struct ps2_dev9* dev9, int model); void ps2_dev9_destroy(struct ps2_dev9* dev9); uint64_t ps2_dev9_read8(struct ps2_dev9* dev9, uint32_t addr); uint64_t ps2_dev9_read16(struct ps2_dev9* dev9, uint32_t addr); uint64_t ps2_dev9_read32(struct ps2_dev9* dev9, uint32_t addr); void ps2_dev9_write8(struct ps2_dev9* dev9, uint32_t addr, uint64_t data); void ps2_dev9_write16(struct ps2_dev9* dev9, uint32_t addr, uint64_t data); void ps2_dev9_write32(struct ps2_dev9* dev9, uint32_t addr, uint64_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/shared/ram.c ================================================ #include #include #include "ram.h" struct ps2_ram* ps2_ram_create(void) { return malloc(sizeof(struct ps2_ram)); } void ps2_ram_init(struct ps2_ram* ram, int size) { memset(ram, 0, sizeof(struct ps2_ram)); ram->buf = malloc(size); ram->size = size; memset(ram->buf, 0, ram->size); } void ps2_ram_reset(struct ps2_ram* ram) { memset(ram->buf, 0, ram->size); } void ps2_ram_destroy(struct ps2_ram* ram) { free(ram->buf); free(ram); } uint64_t ps2_ram_read8(struct ps2_ram* ram, uint32_t addr) { return *(uint8_t*)(ram->buf + (addr & (ram->size - 1))); } uint64_t ps2_ram_read16(struct ps2_ram* ram, uint32_t addr) { return *(uint16_t*)(ram->buf + (addr & (ram->size - 1))); } uint64_t ps2_ram_read32(struct ps2_ram* ram, uint32_t addr) { return *(uint32_t*)(ram->buf + (addr & (ram->size - 1))); } uint64_t ps2_ram_read64(struct ps2_ram* ram, uint32_t addr) { return *(uint64_t*)(ram->buf + (addr & (ram->size - 1))); } uint128_t ps2_ram_read128(struct ps2_ram* ram, uint32_t addr) { return *(uint128_t*)(ram->buf + (addr & (ram->size - 1))); } void ps2_ram_write8(struct ps2_ram* ram, uint32_t addr, uint64_t data) { *(uint8_t*)(ram->buf + (addr & (ram->size - 1))) = data; } void ps2_ram_write16(struct ps2_ram* ram, uint32_t addr, uint64_t data) { *(uint16_t*)(ram->buf + (addr & (ram->size - 1))) = data; } void ps2_ram_write32(struct ps2_ram* ram, uint32_t addr, uint64_t data) { *(uint32_t*)(ram->buf + (addr & (ram->size - 1))) = data; } void ps2_ram_write64(struct ps2_ram* ram, uint32_t addr, uint64_t data) { *(uint64_t*)(ram->buf + (addr & (ram->size - 1))) = data; } void ps2_ram_write128(struct ps2_ram* ram, uint32_t addr, uint128_t data) { *(uint128_t*)(ram->buf + (addr & (ram->size - 1))) = data; } ================================================ FILE: src/shared/ram.h ================================================ #ifndef RAM_H #define RAM_H #ifdef __cplusplus extern "C" { #endif #include #include "u128.h" struct ps2_ram { uint8_t* buf; size_t size; }; #define RAM_SIZE_1KB 0x400 #define RAM_SIZE_2MB 0x200000 #define RAM_SIZE_4MB 0x400000 #define RAM_SIZE_8MB 0x800000 #define RAM_SIZE_16MB 0x1000000 #define RAM_SIZE_32MB 0x2000000 #define RAM_SIZE_64MB 0x4000000 #define RAM_SIZE_128MB 0x8000000 #define RAM_SIZE_256MB 0x10000000 struct ps2_ram* ps2_ram_create(void); void ps2_ram_init(struct ps2_ram* ram, int size); void ps2_ram_reset(struct ps2_ram* ram); void ps2_ram_destroy(struct ps2_ram* ram); uint64_t ps2_ram_read8(struct ps2_ram* ram, uint32_t addr); uint64_t ps2_ram_read16(struct ps2_ram* ram, uint32_t addr); uint64_t ps2_ram_read32(struct ps2_ram* ram, uint32_t addr); uint64_t ps2_ram_read64(struct ps2_ram* ram, uint32_t addr); uint128_t ps2_ram_read128(struct ps2_ram* ram, uint32_t addr); void ps2_ram_write8(struct ps2_ram* ram, uint32_t addr, uint64_t data); void ps2_ram_write16(struct ps2_ram* ram, uint32_t addr, uint64_t data); void ps2_ram_write32(struct ps2_ram* ram, uint32_t addr, uint64_t data); void ps2_ram_write64(struct ps2_ram* ram, uint32_t addr, uint64_t data); void ps2_ram_write128(struct ps2_ram* ram, uint32_t addr, uint128_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/shared/sbus.c ================================================ #include #include #include "sbus.h" struct ps2_sbus* ps2_sbus_create(void) { return malloc(sizeof(struct ps2_sbus)); } void ps2_sbus_init(struct ps2_sbus* sbus, struct ps2_intc* ee_intc, struct ps2_iop_intc* iop_intc, struct sched_state* sched) { memset(sbus, 0, sizeof(struct ps2_sbus)); sbus->ee_intc = ee_intc; sbus->iop_intc = iop_intc; sbus->sched = sched; } void ps2_sbus_destroy(struct ps2_sbus* sbus) { free(sbus); } uint64_t ps2_sbus_read8(struct ps2_sbus* sbus, uint32_t addr) { printf("sbus: 8-bit read %08x\n", addr); exit(1); } uint64_t ps2_sbus_read16(struct ps2_sbus* sbus, uint32_t addr) { printf("sbus: 16-bit read %08x\n", addr); exit(1); } void sbus_trigger_iop_irq(void* udata, int overshoot) { struct ps2_sbus* sbus = (struct ps2_sbus*)udata; ps2_iop_intc_irq(sbus->iop_intc, IOP_INTC_SBUS); } void ps2_sbus_write8(struct ps2_sbus* sbus, uint32_t addr, uint64_t data) { printf("sbus: 8-bit write %08x <- %02lx\n", addr, data); exit(1); } void ps2_sbus_write16(struct ps2_sbus* sbus, uint32_t addr, uint64_t data) { printf("sbus: 16-bit write %08x <- %04lx\n", addr, data); exit(1); } void ps2_sbus_write32(struct ps2_sbus* sbus, uint32_t addr, uint64_t data) { switch (addr) { case 0x1f801450: { if (data & 2) { ps2_intc_irq(sbus->ee_intc, EE_INTC_SBUS); } } return; } } ================================================ FILE: src/shared/sbus.h ================================================ #ifndef SBUS_H #define SBUS_H #ifdef __cplusplus extern "C" { #endif #include "ee/intc.h" #include "iop/intc.h" #include "scheduler.h" struct ps2_sbus { struct ps2_intc* ee_intc; struct ps2_iop_intc* iop_intc; struct sched_state* sched; }; struct ps2_sbus* ps2_sbus_create(void); void ps2_sbus_init(struct ps2_sbus* sbus, struct ps2_intc* ee_intc, struct ps2_iop_intc* iop_intc, struct sched_state* sched); void ps2_sbus_destroy(struct ps2_sbus* sbus); uint64_t ps2_sbus_read8(struct ps2_sbus* sbus, uint32_t addr); uint64_t ps2_sbus_read16(struct ps2_sbus* sbus, uint32_t addr); uint64_t ps2_sbus_read32(struct ps2_sbus* sbus, uint32_t addr); void ps2_sbus_write8(struct ps2_sbus* sbus, uint32_t addr, uint64_t data); void ps2_sbus_write16(struct ps2_sbus* sbus, uint32_t addr, uint64_t data); void ps2_sbus_write32(struct ps2_sbus* sbus, uint32_t addr, uint64_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/shared/sif.c ================================================ #include #include #include #include "sif.h" struct ps2_sif* ps2_sif_create(void) { return malloc(sizeof(struct ps2_sif)); } void ps2_sif_init(struct ps2_sif* sif, struct ps2_iop_intc* iop_intc) { memset(sif, 0, sizeof(struct ps2_sif)); sif->ctrl = 0xf0000012; sif->iop_intc = iop_intc; } void ps2_sif_destroy(struct ps2_sif* sif) { free(sif->sif0.data); free(sif->sif1.data); free(sif); } uint64_t ps2_sif_read32(struct ps2_sif* sif, uint32_t addr) { // IOP read switch (addr) { case 0x1d000000: return sif->mscom; case 0x1d000010: return sif->smcom; case 0x1d000020: return sif->msflg; case 0x1d000030: return sif->smflg; case 0x1d000040: return sif->ctrl | 0xf0000001; case 0x1d000060: return sif->bd6; } // EE read switch (addr) { case 0x1000f200: return sif->mscom; case 0x1000f210: return sif->smcom; case 0x1000f220: return sif->msflg; case 0x1000f230: return sif->smflg; case 0x1000f240: return sif->ctrl | 0xf0000101; case 0x1000f260: return sif->bd6; } return 0; } void ps2_sif_write32(struct ps2_sif* sif, uint32_t addr, uint64_t data) { // IOP write switch (addr) { case 0x1d000000: sif->mscom = data; return; case 0x1d000010: sif->smcom = data; return; case 0x1d000020: sif->msflg &= ~data; return; case 0x1d000030: sif->smflg |= data; return; case 0x1d000040: sif->ctrl = data; return; case 0x1d000060: sif->bd6 = data; return; } // EE write switch (addr) { case 0x1000f200: sif->mscom = data; return; case 0x1000f210: sif->smcom = data; return; case 0x1000f220: sif->msflg |= data; return; case 0x1000f230: sif->smflg &= ~data; return; case 0x1000f240: { if (data & 0x40000) { ps2_iop_intc_irq(sif->iop_intc, IOP_INTC_SBUS); } sif->ctrl = data & ~0x40000; }return; case 0x1000f260: sif->bd6 = data; return; } } void ps2_sif0_write(struct ps2_sif* sif, uint128_t data) { // printf("writing %016lx %016lx to SIF FIFO\n", data.u64[1], data.u64[0]); if (!sif->sif0.capacity) { sif->sif0.capacity = 4; sif->sif0.data = malloc(sizeof(uint128_t) * 4); } else if (sif->sif0.write_index == sif->sif0.capacity) { sif->sif0.capacity *= 2; uint128_t* ptr = realloc(sif->sif0.data, sizeof(uint128_t) * sif->sif0.capacity); if (!ptr) { fprintf(stderr, "sif: Couldn't resize SIF0 buffer\n"); exit(1); } sif->sif0.data = ptr; } sif->sif0.data[sif->sif0.write_index++] = data; } uint128_t ps2_sif0_read(struct ps2_sif* sif) { // If EE requests more data than the IOP produced, then return the last // QW that was actually transferred. // This happens during SIF initialization. The IOP triggers a SIF0 transfer // that sends 8 words of data (2 QW), the first QW is an EE tag that starts // a 2 QW transfer from the SIF FIFO, but the IOP only ever wrote 2 QWs. if (sif->sif0.read_index == sif->sif0.write_index) return sif->sif0.data[sif->sif0.read_index - 1]; uint128_t q = sif->sif0.data[sif->sif0.read_index++]; return q; } void ps2_sif0_reset(struct ps2_sif* sif) { sif->sif0.read_index = 0; sif->sif0.write_index = 0; } int ps2_sif0_is_empty(struct ps2_sif* sif) { return sif->sif0.read_index == sif->sif0.write_index; } void ps2_sif1_write(struct ps2_sif* sif, uint128_t data) { // printf("writing %016lx %016lx to SIF FIFO\n", data.u64[1], data.u64[0]); if (!sif->sif1.capacity) { sif->sif1.capacity = 4; sif->sif1.data = malloc(sizeof(uint128_t) * 4); } else if (sif->sif1.write_index == sif->sif1.capacity) { sif->sif1.capacity *= 2; uint128_t* ptr = realloc(sif->sif1.data, sizeof(uint128_t) * sif->sif1.capacity); if (!ptr) { fprintf(stderr, "sif: Couldn't resize SIF1 buffer\n"); exit(1); } sif->sif1.data = ptr; } sif->sif1.data[sif->sif1.write_index++] = data; } uint128_t ps2_sif1_read(struct ps2_sif* sif) { // If EE requests more data than the IOP produced, then return the last // QW that was actually transferred. // This happens during SIF initialization. The IOP triggers a SIF0 transfer // that sends 8 words of data (2 QW), the first QW is an EE tag that starts // a 2 QW transfer from the SIF FIFO, but the IOP only ever wrote 2 QWs. if (sif->sif1.read_index == sif->sif1.write_index) return sif->sif1.data[sif->sif1.read_index - 1]; uint128_t q = sif->sif1.data[sif->sif1.read_index++]; return q; } void ps2_sif1_reset(struct ps2_sif* sif) { sif->sif1.read_index = 0; sif->sif1.write_index = 0; } int ps2_sif1_is_empty(struct ps2_sif* sif) { return sif->sif1.read_index == sif->sif1.write_index; } ================================================ FILE: src/shared/sif.h ================================================ #ifndef SIF_H #define SIF_H #ifdef __cplusplus extern "C" { #endif #include #include "iop/intc.h" #include "u128.h" #define SIF_EE_SIDE 0 #define SIF_IOP_SIDE 1 struct sif_fifo { int read_index; int write_index; int ready; uint128_t* data; int capacity; }; struct ps2_sif { uint32_t mscom; uint32_t smcom; uint32_t msflg; uint32_t smflg; uint32_t ctrl; uint32_t bd6; struct ps2_iop_intc* iop_intc; struct sif_fifo sif0; struct sif_fifo sif1; }; struct ps2_sif* ps2_sif_create(void); void ps2_sif_init(struct ps2_sif* sif, struct ps2_iop_intc* iop_intc); void ps2_sif_destroy(struct ps2_sif* sif); uint64_t ps2_sif_read32(struct ps2_sif* sif, uint32_t addr); void ps2_sif_write32(struct ps2_sif* sif, uint32_t addr, uint64_t data); // DMA stuff void ps2_sif0_write(struct ps2_sif* sif, uint128_t data); uint128_t ps2_sif0_read(struct ps2_sif* sif); void ps2_sif0_reset(struct ps2_sif* sif); int ps2_sif0_is_empty(struct ps2_sif* sif); void ps2_sif1_write(struct ps2_sif* sif, uint128_t data); uint128_t ps2_sif1_read(struct ps2_sif* sif); void ps2_sif1_reset(struct ps2_sif* sif); int ps2_sif1_is_empty(struct ps2_sif* sif); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/shared/speed/ata.c ================================================ #include #include #include "ata.h" struct ps2_ata* ps2_ata_create(void) { return malloc(sizeof(struct ps2_ata)); } void ps2_ata_init(struct ps2_ata* ata, struct ps2_speed* speed) { memset(ata, 0, sizeof(struct ps2_ata)); ata->speed = speed; ata->nsector = 1; ata->sector = 1; } int ps2_ata_load(struct ps2_ata* ata, const char* path) { // To-do: Load HDD image return 1; } void ps2_ata_destroy(struct ps2_ata* ata) { free(ata); } uint64_t ps2_ata_read16(struct ps2_ata* ata, uint32_t addr) { printf("ata: read16 %08x\n", addr); switch (addr) { case 0x0040: return ata->data; case 0x0042: return ata->error; case 0x0044: return ata->nsector; case 0x0046: return ata->sector; case 0x0048: return ata->lcyl; case 0x004a: return ata->hcyl; case 0x004c: return ata->select; case 0x004e: return 0x48; // RDY | DRQ case 0x005c: return 0x40; } } uint64_t ps2_ata_read32(struct ps2_ata* ata, uint32_t addr) { printf("ata: read32 %08x\n", addr); } void ps2_ata_write16(struct ps2_ata* ata, uint32_t addr, uint64_t data) { printf("ata: write16 %08x %08lx\n", addr, data); switch (addr) { // case 0x0040: return ata->data; // case 0x0042: return ata->error; // case 0x0044: return ata->nsector; // case 0x0046: return ata->sector; case 0x0048: ata->lcyl = data; return; case 0x004a: ata->hcyl = data; return; case 0x004c: ata->select = data; return; case 0x004e: { // COMMAND ps2_speed_send_irq(ata->speed, SPD_INTR_ATA0); return; } break; // case 0x005c: return ata->control; } } void ps2_ata_write32(struct ps2_ata* ata, uint32_t addr, uint64_t data) { printf("ata: write32 %08x %08lx\n", addr, data); } ================================================ FILE: src/shared/speed/ata.h ================================================ #ifndef ATA_H #define ATA_H #ifdef __cplusplus extern "C" { #endif /* SPEED ATA docs -------------- This is basically just a standard ATA interface connected to the SPEED chip. It's mapped starting at 10000040 (EE side 14000040). Registers (X=0 IOP side, X=4 EE side): 1X000040 - DATA 1X000042 - ERROR - FEATURE 1X000044 - NSECTOR 1X000046 - SECTOR 1X000048 - LCYL 1X00004a - HCYL 1X00004c - SELECT 1X00004e - STATUS - COMMAND 1X000050 - 1X00004B - unused 1X00005c - CONTROL */ #include #include #include #include "../speed.h" struct ps2_ata { uint16_t data; uint16_t error; uint16_t feature; uint16_t nsector; uint16_t sector; uint16_t lcyl; uint16_t hcyl; uint16_t select; uint16_t status; uint16_t command; uint16_t control; struct ps2_speed* speed; }; struct ps2_ata* ps2_ata_create(void); void ps2_ata_init(struct ps2_ata* ata, struct ps2_speed* speed); int ps2_ata_load(struct ps2_ata* ata, const char* path); void ps2_ata_destroy(struct ps2_ata* ata); uint64_t ps2_ata_read16(struct ps2_ata* ata, uint32_t addr); uint64_t ps2_ata_read32(struct ps2_ata* ata, uint32_t addr); void ps2_ata_write16(struct ps2_ata* ata, uint32_t addr, uint64_t data); void ps2_ata_write32(struct ps2_ata* ata, uint32_t addr, uint64_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/shared/speed/eeprom.c ================================================ #include #include #include "eeprom.h" uint16_t default_data[32] = { 0x6D76, 0x6361, 0x3130, 0x0207, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0010, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; struct ps2_eeprom* ps2_eeprom_create(void) { return malloc(sizeof(struct ps2_eeprom)); } void ps2_eeprom_init(struct ps2_eeprom* eeprom) { memset(eeprom, 0, sizeof(struct ps2_eeprom)); memcpy(eeprom->buf, default_data, 32 * sizeof(uint16_t)); } void ps2_eeprom_load(struct ps2_eeprom* eeprom, const uint16_t* data) { memcpy(eeprom->buf, data, 32 * sizeof(uint16_t)); } void ps2_eeprom_destroy(struct ps2_eeprom* eeprom) { free(eeprom); } uint64_t ps2_eeprom_read(struct ps2_eeprom* eeprom) { return eeprom->dout << PP_DOUT; } void eeprom_step(struct ps2_eeprom* eeprom) { switch (eeprom->state) { case EEPROM_S_CMD_START: { eeprom->sequence = 0; if (eeprom->din) { eeprom->state = EEPROM_S_CMD_READ; } } break; case EEPROM_S_CMD_READ: { eeprom->cmd |= eeprom->din << (1 - eeprom->sequence); eeprom->sequence++; if (eeprom->sequence == 2) { eeprom->sequence = 0; eeprom->state = EEPROM_S_ADDR_READ; } } break; case EEPROM_S_ADDR_READ: { // Address read in from highest bit to lowest bit eeprom->addr |= eeprom->din << (5 - eeprom->sequence); // Don't know what the behaviour would be but lets prevent oob. eeprom->addr = eeprom->addr & 31; eeprom->sequence++; if (eeprom->sequence == 6) { eeprom->state = EEPROM_S_TRANSMIT; eeprom->sequence = 0; } } break; // fire away, bit position increments every pulse case EEPROM_S_TRANSMIT: { if (eeprom->cmd == PP_OP_READ) { eeprom->dout = (eeprom->buf[eeprom->addr] >> (15 - eeprom->sequence)) & 1; } if (eeprom->cmd == PP_OP_WRITE) { eeprom->buf[eeprom->addr] = eeprom->buf[eeprom->addr] | (eeprom->din << (15 - eeprom->sequence)); } eeprom->sequence++; if (eeprom->sequence == 16) { eeprom->sequence = 0; // Let's prevent oob here too eeprom->addr = (eeprom->addr + 1) & 31; } } break; } return; } void ps2_eeprom_write(struct ps2_eeprom* eeprom, uint64_t data) { uint8_t csel = (data >> PP_CSEL) & 1; uint8_t sclk = (data >> PP_SCLK) & 1; uint8_t din = (data >> PP_DIN) & 1; if (!csel) { eeprom->sequence = 0; eeprom->addr = 0; eeprom->state = EEPROM_S_CMD_START; eeprom->clk = 0; return; } eeprom->din = din; if (sclk && !eeprom->clk) { eeprom_step(eeprom); } eeprom->clk = sclk; } ================================================ FILE: src/shared/speed/eeprom.h ================================================ #ifndef EEPROM_H #define EEPROM_H #ifdef __cplusplus extern "C" { #endif #include #include #define PP_DOUT 4 #define PP_DIN 5 #define PP_SCLK 6 #define PP_CSEL 7 #define PP_OP_READ 2 #define PP_OP_WRITE 1 #define PP_OP_EWEN 0 #define PP_OP_EWDS 0 enum { EEPROM_S_CMD_START, EEPROM_S_CMD_READ, EEPROM_S_ADDR_READ, EEPROM_S_TRANSMIT }; struct ps2_eeprom { int state; // Pins uint8_t clk; uint8_t din; uint8_t dout; uint8_t cmd; uint8_t sequence; uint8_t addr; uint16_t buf[32]; }; struct ps2_eeprom* ps2_eeprom_create(void); void ps2_eeprom_init(struct ps2_eeprom* eeprom); void ps2_eeprom_load(struct ps2_eeprom* eeprom, const uint16_t* data); void ps2_eeprom_destroy(struct ps2_eeprom* eeprom); uint64_t ps2_eeprom_read(struct ps2_eeprom* eeprom); void ps2_eeprom_write(struct ps2_eeprom* eeprom, uint64_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/shared/speed/flash.c ================================================ #include #include "flash.h" static unsigned char xor_table[256] = { 0x00, 0x87, 0x96, 0x11, 0xA5, 0x22, 0x33, 0xB4, 0xB4, 0x33, 0x22, 0xA5, 0x11, 0x96, 0x87, 0x00, 0xC3, 0x44, 0x55, 0xD2, 0x66, 0xE1, 0xF0, 0x77, 0x77, 0xF0, 0xE1, 0x66, 0xD2, 0x55, 0x44, 0xC3, 0xD2, 0x55, 0x44, 0xC3, 0x77, 0xF0, 0xE1, 0x66, 0x66, 0xE1, 0xF0, 0x77, 0xC3, 0x44, 0x55, 0xD2, 0x11, 0x96, 0x87, 0x00, 0xB4, 0x33, 0x22, 0xA5, 0xA5, 0x22, 0x33, 0xB4, 0x00, 0x87, 0x96, 0x11, 0xE1, 0x66, 0x77, 0xF0, 0x44, 0xC3, 0xD2, 0x55, 0x55, 0xD2, 0xC3, 0x44, 0xF0, 0x77, 0x66, 0xE1, 0x22, 0xA5, 0xB4, 0x33, 0x87, 0x00, 0x11, 0x96, 0x96, 0x11, 0x00, 0x87, 0x33, 0xB4, 0xA5, 0x22, 0x33, 0xB4, 0xA5, 0x22, 0x96, 0x11, 0x00, 0x87, 0x87, 0x00, 0x11, 0x96, 0x22, 0xA5, 0xB4, 0x33, 0xF0, 0x77, 0x66, 0xE1, 0x55, 0xD2, 0xC3, 0x44, 0x44, 0xC3, 0xD2, 0x55, 0xE1, 0x66, 0x77, 0xF0, 0xF0, 0x77, 0x66, 0xE1, 0x55, 0xD2, 0xC3, 0x44, 0x44, 0xC3, 0xD2, 0x55, 0xE1, 0x66, 0x77, 0xF0, 0x33, 0xB4, 0xA5, 0x22, 0x96, 0x11, 0x00, 0x87, 0x87, 0x00, 0x11, 0x96, 0x22, 0xA5, 0xB4, 0x33, 0x22, 0xA5, 0xB4, 0x33, 0x87, 0x00, 0x11, 0x96, 0x96, 0x11, 0x00, 0x87, 0x33, 0xB4, 0xA5, 0x22, 0xE1, 0x66, 0x77, 0xF0, 0x44, 0xC3, 0xD2, 0x55, 0x55, 0xD2, 0xC3, 0x44, 0xF0, 0x77, 0x66, 0xE1, 0x11, 0x96, 0x87, 0x00, 0xB4, 0x33, 0x22, 0xA5, 0xA5, 0x22, 0x33, 0xB4, 0x00, 0x87, 0x96, 0x11, 0xD2, 0x55, 0x44, 0xC3, 0x77, 0xF0, 0xE1, 0x66, 0x66, 0xE1, 0xF0, 0x77, 0xC3, 0x44, 0x55, 0xD2, 0xC3, 0x44, 0x55, 0xD2, 0x66, 0xE1, 0xF0, 0x77, 0x77, 0xF0, 0xE1, 0x66, 0xD2, 0x55, 0x44, 0xC3, 0x00, 0x87, 0x96, 0x11, 0xA5, 0x22, 0x33, 0xB4, 0xB4, 0x33, 0x22, 0xA5, 0x11, 0x96, 0x87, 0x00 }; static void flash_calculate_xor(unsigned char buffer[128], unsigned char blah[4]) { unsigned char a = 0, b = 0, c = 0, i; for (i = 0; i < 128; i++) { a ^= xor_table[buffer[i]]; if (xor_table[buffer[i]] & 0x80) { b ^= ~i; c ^= i; } } blah[0] = (~a) & 0x77; blah[1] = (~b) & 0x7F; blah[2] = (~c) & 0x7F; } static void flash_calculate_ecc(uint8_t page[FLASH_PAGE_SIZE_ECC]) { memset(page + FLASH_PAGE_SIZE, 0, FLASH_ECC_SIZE); flash_calculate_xor(page + 0 * (FLASH_PAGE_SIZE >> 2), page + FLASH_PAGE_SIZE + 0 * 3); //(ECC_SIZE>>2)); flash_calculate_xor(page + 1 * (FLASH_PAGE_SIZE >> 2), page + FLASH_PAGE_SIZE + 1 * 3); //(ECC_SIZE>>2)); flash_calculate_xor(page + 2 * (FLASH_PAGE_SIZE >> 2), page + FLASH_PAGE_SIZE + 2 * 3); //(ECC_SIZE>>2)); flash_calculate_xor(page + 3 * (FLASH_PAGE_SIZE >> 2), page + FLASH_PAGE_SIZE + 3 * 3); //(ECC_SIZE>>2)); } static const char* flash_get_cmd_name(uint32_t cmd) { switch (cmd) { case SM_CMD_READ1: return "READ1"; case SM_CMD_READ2: return "READ2"; case SM_CMD_READ3: return "READ3"; case SM_CMD_RESET: return "RESET"; case SM_CMD_WRITEDATA: return "WRITEDATA"; case SM_CMD_PROGRAMPAGE: return "PROGRAMPAGE"; case SM_CMD_ERASEBLOCK: return "ERASEBLOCK"; case SM_CMD_ERASECONFIRM: return "ERASECONFIRM"; case SM_CMD_GETSTATUS: return "GETSTATUS"; case SM_CMD_READID: return "READID"; } return ""; } struct ps2_flash* ps2_flash_create(void) { return malloc(sizeof(struct ps2_flash)); } void ps2_flash_init(struct ps2_flash* flash) { memset(flash, 0, sizeof(struct ps2_flash)); flash->counter = 0; flash->addrbyte = 0; flash->address = 0; flash->ctrl = FLASH_CTRL_READY; memset(flash->data, 0xff, FLASH_PAGE_SIZE); memset(flash->file, 0xff, FLASH_CARD_SIZE_ECC); flash_calculate_ecc(flash->data); } int ps2_flash_load(struct ps2_flash* flash, const char* path) { FILE* fd = fopen(path, "rb"); if (!fd) { memset(flash->file, 0xff, FLASH_CARD_SIZE_ECC); return 0; } size_t size = fread(flash->file, 1, FLASH_CARD_SIZE_ECC, fd); if (size != FLASH_CARD_SIZE_ECC) { printf("flash: Flash file size incorrect (%zu bytes)\n", size); return 0; } fclose(fd); flash->id = FLASH_ID_64MBIT; printf("flash: Dump \'%s\' loaded (%zu bytes)\n", path, size); return 1; } void ps2_flash_destroy(struct ps2_flash* flash) { // To-do: Flush to file free(flash); } void ps2_flash_reset(struct ps2_flash* flash) { flash->cmd = 0; flash->ctrl = FLASH_CTRL_READY; flash->counter = 0; flash->addrbyte = 0; flash->address = 0; memset(flash->data, 0xff, FLASH_PAGE_SIZE); flash_calculate_ecc(flash->data); } uint32_t flash_read_data(struct ps2_flash* flash, int size) { uint32_t value, refill = 0; memcpy(&value, &flash->data[flash->counter], size); flash->counter += size; if (flash->cmd == SM_CMD_READ3) { if (flash->counter >= FLASH_PAGE_SIZE_ECC) { flash->counter = FLASH_PAGE_SIZE; refill = 1; } } else { if ((flash->ctrl & FLASH_CTRL_NOECC) && (flash->counter >= FLASH_PAGE_SIZE)) { flash->counter %= FLASH_PAGE_SIZE; refill = 1; } else if (!(flash->ctrl & FLASH_CTRL_NOECC) && (flash->counter >= FLASH_PAGE_SIZE_ECC)) { flash->counter %= FLASH_PAGE_SIZE_ECC; refill = 1; } } if (refill) { flash->address += FLASH_PAGE_SIZE; flash->address %= FLASH_CARD_SIZE; memcpy(flash->data, flash->file + (flash->address >> FLASH_PAGE_SIZE_BITS) * FLASH_PAGE_SIZE_ECC, FLASH_PAGE_SIZE); flash_calculate_ecc(flash->data); // calculate ECC; should be in the file already flash->ctrl |= FLASH_CTRL_READY; } return value; } uint32_t flash_read_id(struct ps2_flash* flash) { switch (flash->cmd) { case SM_CMD_READID: { return flash->id; } break; case SM_CMD_GETSTATUS: { return 0x80 | ((flash->ctrl & FLASH_CTRL_READY) ? 0x40 : 0x00); } break; } return 0; } void flash_write_data(struct ps2_flash* flash, int size, uint32_t data) { memcpy(&flash->data[flash->counter], &flash->data, size); flash->counter += size; flash->counter %= FLASH_PAGE_SIZE_ECC; //should not get past the last byte, but at the end } void flash_write_cmd(struct ps2_flash* flash, uint16_t value) { // printf("flash: Command %02x (%s)\n", value, flash_get_cmd_name(value)); if (!(flash->ctrl & FLASH_CTRL_READY)) { if ((value != SM_CMD_GETSTATUS) && (value != SM_CMD_RESET)) { return; } } if (flash->cmd == SM_CMD_WRITEDATA) { if ((value != SM_CMD_PROGRAMPAGE) && (value != SM_CMD_RESET)) { flash->ctrl &= ~FLASH_CTRL_READY; //go busy, reset is needed } } switch (value) { // A8 bit is encoded in READ cmd;) case SM_CMD_READ1: { flash->counter = 0; if (flash->cmd != SM_CMD_GETSTATUS) flash->address = flash->counter; flash->addrbyte = 0; } break; case SM_CMD_READ2: { flash->counter = FLASH_PAGE_SIZE / 2; if (flash->cmd != SM_CMD_GETSTATUS) flash->address = flash->counter; flash->addrbyte = 0; } break; case SM_CMD_READ3: { flash->counter = FLASH_PAGE_SIZE; if (flash->cmd != SM_CMD_GETSTATUS) flash->address = flash->counter; flash->addrbyte = 0; } break; case SM_CMD_RESET: { ps2_flash_reset(flash); } break; case SM_CMD_WRITEDATA: { flash->counter = 0; flash->address = flash->counter; flash->addrbyte = 0; } break; case SM_CMD_ERASEBLOCK: { flash->counter = 0; memset(flash->data, 0xff, FLASH_PAGE_SIZE); flash->address = flash->counter; flash->addrbyte = 1; } break; case SM_CMD_PROGRAMPAGE: //fall case SM_CMD_ERASECONFIRM: { flash_calculate_ecc(flash->data); memcpy(flash->file + (flash->address / FLASH_PAGE_SIZE) * FLASH_PAGE_SIZE_ECC, flash->data, FLASH_PAGE_SIZE_ECC); /*write2file*/ flash->ctrl |= FLASH_CTRL_READY; } break; case SM_CMD_GETSTATUS: break; case SM_CMD_READID: { flash->counter = 0; flash->address = flash->counter; flash->addrbyte = 0; } break; default: { flash->ctrl &= ~FLASH_CTRL_READY; return; } break; } flash->cmd = value; } void flash_write_addr(struct ps2_flash* flash, uint16_t value) { flash->address |= (value & 0xff) << (flash->addrbyte == 0 ? 0 : (1 + 8 * flash->addrbyte)); flash->addrbyte++; if (!(value & 0x100)) { // address is complete if ((flash->cmd == SM_CMD_READ1) || (flash->cmd == SM_CMD_READ2) || (flash->cmd == SM_CMD_READ3)) { memcpy(flash->data, flash->file + (flash->address >> FLASH_PAGE_SIZE_BITS) * FLASH_PAGE_SIZE_ECC, FLASH_PAGE_SIZE); flash_calculate_ecc(flash->data); // calculate ECC; should be in the file already flash->ctrl |= FLASH_CTRL_READY; } flash->addrbyte = 0; // address reset } } void flash_write_ctrl(struct ps2_flash* flash, uint16_t value) { flash->ctrl = (flash->ctrl & FLASH_CTRL_READY) | (value & ~FLASH_CTRL_READY); } uint64_t ps2_flash_read16(struct ps2_flash* flash, uint32_t addr) { switch (addr) { case 0x4800: return flash_read_data(flash, 2); case 0x480c: return flash->ctrl; case 0x4814: return flash_read_id(flash); } printf("flash: Unknown 16-bit read at address %08x\n", addr); return 0; } uint64_t ps2_flash_read32(struct ps2_flash* flash, uint32_t addr) { switch (addr) { case 0x4800: return flash_read_data(flash, 4); case 0x480c: return flash->ctrl; case 0x4814: return flash_read_id(flash); } printf("flash: Unknown 32-bit read at address %08x\n", addr); return 0; } void ps2_flash_write16(struct ps2_flash* flash, uint32_t addr, uint64_t data) { switch (addr) { case 0x4800: flash_write_data(flash, 2, data); return; case 0x4804: flash_write_cmd(flash, data); return; case 0x4808: flash_write_addr(flash, data); return; case 0x480c: flash_write_ctrl(flash, data); return; } printf("flash: Unknown 16-bit write at address %08x (%08lx)\n", addr, data); } void ps2_flash_write32(struct ps2_flash* flash, uint32_t addr, uint64_t data) { switch (addr) { case 0x4800: flash_write_data(flash, 4, data); return; case 0x4804: flash_write_cmd(flash, data); return; case 0x4808: flash_write_addr(flash, data); return; case 0x480c: flash_write_ctrl(flash, data); return; } printf("flash: Unknown 32-bit write at address %08x (%08lx)\n", addr, data); } ================================================ FILE: src/shared/speed/flash.h ================================================ #ifndef FLASH_H #define FLASH_H #ifdef __cplusplus extern "C" { #endif /* SPEED FLASH docs ---------------- Also known as XFROM (eXternal Flash ROM), this flash memory is connected to the SPEED chip, and is accessed through the registers listed below. The actual chip seems to be the same chip used for the memory cards, but bigger. The chip is controlled using "SmartMedia" commands sent through the COMMAND register. Registers (X=0 IOP side, X=4 EE side): 1X004800 - DATA register Used by WRITEDATA/PROGRAMPAGE/READ3/READ1, this is where data is written or read from 1X004804 - CMD register Write to this register to send a command 1X004808 - ADDR register Probably used to set page offsets 1X00480c - CTRL register bit 0 seems to be some kind of READY bit 1X004810 - unused? 1X004814 - ID/STATUS? (used by READID/GETSTATUS) Contains the size (or ID) of the flash chip after sending READID and some kind of status after sending GETSTATUS. SmartMedia commands list: 0x00 - READ1 0x01 - READ2 0x50 - READ3 0xff - RESET 0x80 - WRITEDATA 0x10 - PROGRAMPAGE 0x60 - ERASEBLOCK 0xd0 - ERASECONFIRM 0x70 - GETSTATUS 0x90 - READID */ #include #include #include #define FLASH_ID_64MBIT 0xe6 #define FLASH_ID_128MBIT 0x73 #define FLASH_ID_256MBIT 0x75 #define FLASH_ID_512MBIT 0x76 #define FLASH_ID_1024MBIT 0x79 /* SmartMedia commands. */ #define SM_CMD_READ1 0x00 #define SM_CMD_READ2 0x01 #define SM_CMD_READ3 0x50 #define SM_CMD_RESET 0xff #define SM_CMD_WRITEDATA 0x80 #define SM_CMD_PROGRAMPAGE 0x10 #define SM_CMD_ERASEBLOCK 0x60 #define SM_CMD_ERASECONFIRM 0xd0 #define SM_CMD_GETSTATUS 0x70 #define SM_CMD_READID 0x90 #define FLASH_CTRL_READY (1 << 0) // r/w /BUSY #define FLASH_CTRL_WRITE (1 << 7) // -/w WRITE data #define FLASH_CTRL_CSEL (1 << 8) // -/w CS #define FLASH_CTRL_READ (1 << 11) // -/w READ data #define FLASH_CTRL_NOECC (1 << 12) // -/w ECC disabled #define FLASH_PAGE_SIZE_BITS 9 #define FLASH_PAGE_SIZE (1 << FLASH_PAGE_SIZE_BITS) #define FLASH_ECC_SIZE (16) #define FLASH_PAGE_SIZE_ECC (FLASH_PAGE_SIZE + FLASH_ECC_SIZE) #define FLASH_BLOCK_SIZE (16 * FLASH_PAGE_SIZE) #define FLASH_BLOCK_SIZE_ECC (16 * FLASH_PAGE_SIZE_ECC) #define FLASH_CARD_SIZE (1024 * FLASH_BLOCK_SIZE) #define FLASH_CARD_SIZE_ECC (1024 * FLASH_BLOCK_SIZE_ECC) struct ps2_flash { uint16_t cmd; // 10004804 uint16_t addr; // 10004808 uint16_t ctrl; // 1000480c uint16_t id; // 10004814 int counter; int addrbyte; int address; uint8_t data[FLASH_PAGE_SIZE_ECC]; uint8_t file[FLASH_CARD_SIZE_ECC]; }; struct ps2_flash* ps2_flash_create(void); void ps2_flash_init(struct ps2_flash* flash); int ps2_flash_load(struct ps2_flash* flash, const char* path); void ps2_flash_destroy(struct ps2_flash* flash); uint64_t ps2_flash_read16(struct ps2_flash* flash, uint32_t addr); uint64_t ps2_flash_read32(struct ps2_flash* flash, uint32_t addr); void ps2_flash_write16(struct ps2_flash* flash, uint32_t addr, uint64_t data); void ps2_flash_write32(struct ps2_flash* flash, uint32_t addr, uint64_t data); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/shared/speed.c ================================================ #include #include #include #include "speed.h" struct ps2_speed* ps2_speed_create(void) { return malloc(sizeof(struct ps2_speed)); } void ps2_speed_init(struct ps2_speed* speed, struct ps2_iop_intc* iop_intc) { memset(speed, 0, sizeof(struct ps2_speed)); speed->iop_intc = iop_intc; speed->flash = ps2_flash_create(); speed->ata = ps2_ata_create(); speed->eeprom = ps2_eeprom_create(); ps2_flash_init(speed->flash); ps2_ata_init(speed->ata, speed); ps2_eeprom_init(speed->eeprom); speed->rev8 |= 2; } void ps2_speed_destroy(struct ps2_speed* speed) { ps2_flash_destroy(speed->flash); ps2_ata_destroy(speed->ata); ps2_eeprom_destroy(speed->eeprom); free(speed); } uint64_t ps2_speed_read8(struct ps2_speed* speed, uint32_t addr) { addr &= 0xffff; printf("speed: read8 %08x %08x\n", addr); switch (addr) { case 0x002e: return ps2_eeprom_read(speed->eeprom); } return 0; // exit(1); } uint64_t ps2_speed_read16(struct ps2_speed* speed, uint32_t addr) { addr &= 0xffff; if (addr >= 0x4800 && addr < 0x4820) { return ps2_flash_read16(speed->flash, addr); } if (addr >= 0x0040 && addr < 0x0060) { return ps2_ata_read16(speed->ata, addr); } switch (addr) { case 0x0000: return speed->rev; case 0x0002: return speed->rev1; case 0x0004: return speed->rev3; case 0x000e: return speed->rev8; case 0x0024: return speed->dma_ctrl; case 0x0028: return speed->intr_stat; case 0x002a: return speed->intr_mask; case 0x0032: return speed->xfr_ctrl; case 0x0038: return speed->unknown38; case 0x0064: return speed->if_ctrl; } printf("speed: read16 %08x\n", addr); // exit(1); return 0; } uint64_t ps2_speed_read32(struct ps2_speed* speed, uint32_t addr) { addr &= 0xffff; if (addr >= 0x4800 && addr < 0x4820) { return ps2_flash_read32(speed->flash, addr); } printf("speed: read32 %08x\n", addr); // exit(1); return 0; } void ps2_speed_write8(struct ps2_speed* speed, uint32_t addr, uint64_t data) { addr &= 0xffff; printf("speed: write8 %08x %08x\n", addr, data); switch (addr) { case 0x002c: speed->pio_dir = data; return; case 0x002e: ps2_eeprom_write(speed->eeprom, data); return; } // exit(1); } void ps2_speed_write16(struct ps2_speed* speed, uint32_t addr, uint64_t data) { addr &= 0xffff; if (addr >= 0x4800 && addr < 0x4820) { ps2_flash_write16(speed->flash, addr, data); return; } if (addr >= 0x0040 && addr < 0x0060) { ps2_ata_write16(speed->ata, addr, data); return; } switch (addr) { case 0x0024: speed->dma_ctrl = data; return; case 0x0032: speed->xfr_ctrl = data; return; case 0x0038: speed->unknown38 = data; /* ??? */ return; case 0x0064: speed->if_ctrl = data; return; case 0x0070: speed->pio_mode = data; return; case 0x0072: speed->mwdma_mode = data; return; case 0x0074: speed->udma_mode = data; return; case 0x002a: speed->intr_mask = data; return; } printf("speed: write16 %08x %08x\n", addr, data); // exit(1); } void ps2_speed_write32(struct ps2_speed* speed, uint32_t addr, uint64_t data) { addr &= 0xffff; if (addr >= 0x4800 && addr < 0x4820) { ps2_flash_write32(speed->flash, addr, data); return; } printf("speed: write32 %08x %08x\n", addr, data); // exit(1); } void ps2_speed_send_irq(struct ps2_speed* speed, uint16_t irq) { speed->intr_stat |= irq; if (speed->intr_stat & speed->intr_mask) { ps2_iop_intc_irq(speed->iop_intc, IOP_INTC_DEV9); } } int ps2_speed_load_flash(struct ps2_speed* speed, const char* path) { int ret = ps2_flash_load(speed->flash, path); if (ret) { speed->rev3 |= SPD_CAPS_FLASH; } return ret; } void ps2_speed_set_mac_address(struct ps2_speed* speed, const uint8_t* mac) { uint16_t data[32] = { 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0010, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x1000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000, 0x0000 }; data[0] = (mac[1] << 8) | mac[0]; data[1] = (mac[3] << 8) | mac[2]; data[2] = (mac[5] << 8) | mac[4]; data[3] = data[0] + data[1] + data[2]; ps2_eeprom_load(speed->eeprom, data); } ================================================ FILE: src/shared/speed.h ================================================ #ifndef SPEED_H #define SPEED_H #ifdef __cplusplus extern "C" { #endif #include #include "iop/intc.h" #include "speed/ata.h" #include "speed/flash.h" #include "speed/eeprom.h" // SPEED is a chip presented as a register interface to speed devices. // This includes: // - SMAP (Ethernet?) at 0x10000100 // - ATA (HDD) at 0x10000040 // - UART (Maxim MAX232 UART, used in arcade units?) at ? // - DVR (PSX DESR Digital Video Recorder) // - Flash (also known as EROM, used in PSX DESR units) at 0x10004800 // It is mapped to phys 0x10000000 on the IOP side, and phys 0x14000000 // on the EE side. // Software won't even try to access SPEED if it isn't detected through // the speed_REV (1f80146e) register first. // Note: SMAP also provides access to the unit's MAC address (used by the BIOS) // Notes: USB and i.Link (FireWire) are completely separate // and not connected to the SPEED interface at all. // // USB is presented through an OHCI interface at 0x1F801600 // i.Link is presented through a FireWire interface at 0x1F808400 #define SPD_INTR_ATA0 0x0001 #define SPD_INTR_ATA1 0x0002 #define SPD_INTR_DVR 0x0200 // mask=0x0200 #define SPD_INTR_UART 0x1000 // mask=0x1000 #define SPD_INTR_ATA (SPD_INTR_ATA0 | SPD_INTR_ATA1) // mask=0x0003 #define SPD_CAPS_SMAP (1 << 0) #define SPD_CAPS_ATA (1 << 1) #define SPD_CAPS_UART (1 << 3) #define SPD_CAPS_DVR (1 << 4) #define SPD_CAPS_FLASH (1 << 5) struct ps2_speed { uint16_t rev; // 10000000 uint16_t rev1; // 10000002 uint16_t rev3; // 10000004 uint16_t rev8; // 1000000e uint32_t dma_ctrl; // 10000024 uint16_t intr_stat; // 10000028 uint16_t intr_mask; // 1000002a uint16_t pio_dir; // 1000002c uint16_t pio_data; // 1000002e uint32_t xfr_ctrl; // 10000032 uint32_t unknown38; // 10000038 uint32_t if_ctrl; // 10000064 uint32_t pio_mode; // 10000070 uint32_t mwdma_mode; // 10000072 uint32_t udma_mode; // 10000074 struct ps2_ata* ata; struct ps2_flash* flash; struct ps2_eeprom* eeprom; struct ps2_iop_intc* iop_intc; }; struct ps2_speed* ps2_speed_create(void); void ps2_speed_init(struct ps2_speed* speed, struct ps2_iop_intc* iop_intc); void ps2_speed_destroy(struct ps2_speed* speed); uint64_t ps2_speed_read8(struct ps2_speed* speed, uint32_t addr); uint64_t ps2_speed_read16(struct ps2_speed* speed, uint32_t addr); uint64_t ps2_speed_read32(struct ps2_speed* speed, uint32_t addr); void ps2_speed_write8(struct ps2_speed* speed, uint32_t addr, uint64_t data); void ps2_speed_write16(struct ps2_speed* speed, uint32_t addr, uint64_t data); void ps2_speed_write32(struct ps2_speed* speed, uint32_t addr, uint64_t data); void ps2_speed_send_irq(struct ps2_speed* speed, uint16_t irq); int ps2_speed_load_flash(struct ps2_speed* speed, const char* path); void ps2_speed_set_mac_address(struct ps2_speed* speed, const uint8_t* mac); #ifdef __cplusplus } #endif #endif ================================================ FILE: src/u128.h ================================================ #ifndef U128_H #define U128_H #ifdef __cplusplus extern "C" { #endif #include #include typedef union { // unsigned __int128 u128; uint64_t u64[2]; uint32_t u32[4]; uint16_t u16[8]; uint8_t u8[16]; int64_t s64[2]; int32_t s32[4]; int16_t s16[8]; int8_t s8[16]; uint64_t ul64; uint32_t ul32; uint16_t ul16; uint8_t ul8; int64_t sl64; int32_t sl32; int16_t sl16; int8_t sl8; } uint128_t; #ifdef __cplusplus } #endif #endif