├── .devcontainer ├── devcontainer.json └── install-dependencies.sh ├── .github ├── ISSUE_TEMPLATE │ ├── config.yml │ └── report.yml └── workflows │ └── build_nightly.yml ├── .gitignore ├── .gitmodules ├── CMakeLists.txt ├── LICENSE.txt ├── README.md ├── database ├── CMakeLists.txt ├── db_obj_builder.cmake ├── get_and_parse_hdldb.py ├── parse_GameDB.py ├── parse_arcade.py └── parse_db.py ├── doc └── Logo.png ├── ext ├── CMakeLists.txt ├── ESP8266SdFatWrapper │ ├── include │ │ └── sd.h │ └── src │ │ ├── Arduino.h │ │ ├── SPI.cpp │ │ ├── SPI.h │ │ └── sd.cpp ├── fnv │ ├── Makefile │ ├── README │ ├── fnv.h │ ├── fnv32.c │ ├── fnv64.c │ ├── hash_32.c │ ├── hash_32a.c │ ├── hash_64.c │ ├── hash_64a.c │ ├── have_ulong64.c │ ├── longlong.h │ ├── qmk_fnv_type_validation.c │ └── test_fnv.c ├── inih │ ├── LICENSE.txt │ ├── README.md │ ├── ini.c │ └── ini.h ├── mcfat │ ├── CMakeLists.txt │ ├── inc │ │ ├── mcfat.h │ │ └── mcio.h │ └── src │ │ ├── mcfat.c │ │ ├── mcfat_internal.h │ │ ├── mcio.c │ │ ├── util.c │ │ └── util.h └── ssd1306 │ ├── LICENSE │ ├── README.md │ ├── ssd1306.c │ └── ssd1306.h ├── memmap_custom.ld ├── misc └── variants.cmake ├── pico_sdk_import.cmake └── src ├── bigmem.c ├── bigmem.h ├── blit16.h ├── card_config.c ├── card_config.h ├── config.h ├── debug.c ├── debug.h ├── des.c ├── des.h ├── flashmap.h ├── game_db ├── game_db.c └── game_db.h ├── gui.c ├── gui.h ├── input.c ├── input.h ├── keystore.c ├── keystore.h ├── led ├── CMakeLists.txt ├── led.c ├── led.h └── ws2812.pio ├── lv_conf.h ├── main.c ├── oled.c ├── oled.h ├── ps1.c ├── ps1.h ├── ps1 ├── CMakeLists.txt ├── ps1_cardman.c ├── ps1_cardman.h ├── ps1_dirty.c ├── ps1_dirty.h ├── ps1_empty_card.c ├── ps1_empty_card.h ├── ps1_mc_data_interface.c ├── ps1_mc_data_interface.h ├── ps1_mc_spi.pio ├── ps1_memory_card.c ├── ps1_memory_card.h ├── ps1_mmce.c └── ps1_mmce.h ├── ps2.c ├── ps2.h ├── ps2 ├── CMakeLists.txt ├── card_emu │ ├── ps2_mc_auth.c │ ├── ps2_mc_auth.h │ ├── ps2_mc_commands.c │ ├── ps2_mc_commands.h │ ├── ps2_mc_data_interface.c │ ├── ps2_mc_data_interface.h │ ├── ps2_mc_internal.h │ ├── ps2_mc_spi.pio │ ├── ps2_memory_card.c │ └── ps2_memory_card.h ├── history_tracker │ ├── CMakeLists.txt │ ├── ps2_history_tracker.c │ └── ps2_history_tracker.h ├── mmceman │ ├── ps2_mmceman.c │ ├── ps2_mmceman.h │ ├── ps2_mmceman_commands.c │ ├── ps2_mmceman_commands.h │ ├── ps2_mmceman_debug.c │ ├── ps2_mmceman_debug.h │ ├── ps2_mmceman_fs.c │ └── ps2_mmceman_fs.h ├── ps2_cardman.c ├── ps2_cardman.h ├── ps2_dirty.c └── ps2_dirty.h ├── psram ├── CMakeLists.txt ├── pio_qspi.c ├── pio_qspi.h ├── psram.c ├── psram.h └── qspi.pio ├── settings.c ├── settings.h ├── ui_menu.c ├── ui_menu.h ├── ui_theme_mono.c ├── ui_theme_mono.h ├── util.c ├── util.h ├── version ├── CMakeLists.txt ├── script.cmake ├── template │ └── version.c └── version.h └── wear_leveling ├── wear_leveling.c ├── wear_leveling.h ├── wear_leveling_internal.h ├── wear_leveling_rp2040_flash.c └── wear_leveling_rp2040_flash_config.h /.devcontainer/devcontainer.json: -------------------------------------------------------------------------------- 1 | // For format details, see https://aka.ms/devcontainer.json. For config options, see the 2 | // README at: https://github.com/devcontainers/templates/tree/main/src/alpine 3 | { 4 | "name": "SD2PSX Ubuntu", 5 | // Or use a Dockerfile or Docker Compose file. More info: https://containers.dev/guide/dockerfile 6 | "image": "mcr.microsoft.com/devcontainers/cpp:ubuntu", 7 | "features": { 8 | "ghcr.io/devcontainers/features/python:1": { 9 | "installTools": true, 10 | "version": "latest" 11 | } 12 | }, 13 | "containerEnv": { 14 | "PICO_SDK_PATH": "/opt/pico-sdk", 15 | "PICO_TOOLCHAIN_PATH": "/usr/bin" 16 | }, 17 | 18 | // Features to add to the dev container. More info: https://containers.dev/features. 19 | // "features": {}, 20 | 21 | // Use 'forwardPorts' to make a list of ports inside the container available locally. 22 | // "forwardPorts": [], 23 | 24 | // Use 'postCreateCommand' to run commands after the container is created. 25 | "postCreateCommand": "sudo bash .devcontainer/install-dependencies.sh", 26 | 27 | // Configure tool-specific properties. 28 | "customizations": { 29 | "vscode": { 30 | "extensions": 31 | ["twxs.cmake", 32 | "ms-vscode.cmake-tools", 33 | "go2sh.cmake-integration-vscode", 34 | "llvm-vs-code-extensions.vscode-clangd", 35 | "xaver.clang-format", 36 | "IBM.output-colorizer" 37 | ], 38 | "settings": {"clangd.path": "/usr/bin/clangd", 39 | "cmake.cmakePath": "cmake", 40 | "clangd.arguments": ["--query-driver", "/usr/bin/arm-none-eabi-gcc"]} 41 | 42 | }, 43 | 44 | }, 45 | 46 | // Uncomment to connect as root instead. More info: https://aka.ms/dev-containers-non-root. 47 | // "remoteUser": "root" 48 | } 49 | -------------------------------------------------------------------------------- /.devcontainer/install-dependencies.sh: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | apt-get update 3 | apt-get install -y cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib clangd git python3-venv 4 | #git clone https://github.com/raspberrypi/pico-sdk $PICO_SDK_PATH 5 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/config.yml: -------------------------------------------------------------------------------- 1 | blank_issues_enabled: true 2 | contact_links: 3 | - name: PS2 Space discord server 4 | url: https://discord.gg/JryKTfs275 5 | about: Almost everyone that works on this project is there! check the "#sd2psx-ps2xmc2" channel 6 | 7 | -------------------------------------------------------------------------------- /.github/ISSUE_TEMPLATE/report.yml: -------------------------------------------------------------------------------- 1 | name: General issue report 2 | description: Report an issue 3 | title: "[REPORT]: " 4 | labels: [report] 5 | body: 6 | 7 | - type: checkboxes 8 | id: terms 9 | attributes: 10 | label: Checks 11 | description: "Check before posting" 12 | options: 13 | - label: I have checked if a report of the same problem, in the same environment, is already listed [here](https://github.com/sd2psXtd/firmware/issues) 14 | 15 | - type: markdown 16 | attributes: 17 | value: | 18 | ##### please complete the following information: 19 | 20 | - type: dropdown 21 | id: firmwaretype 22 | attributes: 23 | label: "firmware" 24 | description: "Wich firmware are you using?" 25 | multiple: false 26 | options: 27 | - "pmc+" 28 | - "pmczero" 29 | - "psxmemcard" 30 | - "sd2psx" 31 | - "sd2psxlite" 32 | validations: 33 | required: true 34 | 35 | - type: dropdown 36 | id: ps1or2 37 | attributes: 38 | label: "PS1 or PS2?" 39 | description: "wich of the following consoles is being used with the device?" 40 | multiple: false 41 | options: 42 | - "PS1" 43 | - "PS2" 44 | validations: 45 | required: true 46 | 47 | - type: dropdown 48 | id: ps2mg 49 | attributes: 50 | label: "[PS2 ONLY]: special consoles" 51 | description: "If you are using the device on non retail PS2, please specify wich. to tag the appropiate developers" 52 | multiple: false 53 | options: 54 | - "DEVELOPER (DTL-H or DTL-T models)" 55 | - "ARCADE (namco system 246/256 and konami python1)" 56 | - "CONQUEST (SoulCalibur2 Conquest card)" 57 | - "PROTOTYPE" 58 | validations: 59 | required: false 60 | 61 | - type: input 62 | id: console 63 | attributes: 64 | label: Console model 65 | description: you may see it on the console sticker or inside OSDSYS (press triangle on main menú) 66 | placeholder: "SCPH-????? / DTL-H????? / DESR-???? / COH-H?????" 67 | validations: 68 | required: true 69 | 70 | - type: input 71 | id: firmver 72 | attributes: 73 | label: "Firmware version" 74 | description: "Firmware version being used (if your device has OLED, you can check it on the settings" 75 | placeholder: "eg: 1.0.0-beta11" 76 | validations: 77 | required: true 78 | 79 | - type: input 80 | id: game 81 | attributes: 82 | label: "related game" 83 | description: "if the issue is related to a PS2 game, please mention it with region code" 84 | placeholder: "eg: Kingdom Hearts 2 Final mix [SLPM_666.75]" 85 | validations: 86 | required: false 87 | 88 | - type: textarea 89 | id: explanation 90 | attributes: 91 | label: Explain your issue 92 | description: Information about what and how it is failing 93 | placeholder: "ie: the device hangs when I try to do ... and ..." 94 | validations: 95 | required: false 96 | 97 | - type: textarea 98 | id: logs 99 | attributes: 100 | label: Debug Logs 101 | description: If by any chance you were using debug firmware and you have any log related to the issue, paste it here 102 | render: shell 103 | validations: 104 | required: false 105 | -------------------------------------------------------------------------------- /.github/workflows/build_nightly.yml: -------------------------------------------------------------------------------- 1 | name: Build Nightly 2 | 3 | on: 4 | push: 5 | tags: 6 | - '*' 7 | branches: 8 | - '*' 9 | 10 | env: 11 | # Customize the CMake build type here (Release, Debug, RelWithDebInfo, etc.) 12 | BUILD_TYPE: Release 13 | 14 | jobs: 15 | build: 16 | # The CMake configure and build commands are platform agnostic and should work equally well on Windows or Mac. 17 | # You can convert this to a matrix build if you need cross-platform coverage. 18 | # See: https://docs.github.com/en/free-pro-team@latest/actions/learn-github-actions/managing-complex-workflows#using-a-build-matrix 19 | runs-on: ubuntu-latest 20 | #needs: create_release 21 | strategy: 22 | matrix: 23 | target: [SD2PSX, SD2PSXlite, PMC+, PMCZero, PSXMemCard] 24 | debug: [ON, OFF] 25 | include: 26 | - debug: ON 27 | ext: -debug.uf2 28 | delay: 3 29 | - debug: OFF 30 | ext: .uf2 31 | delay: 0 32 | - target: SD2PSX 33 | filename: sd2psx 34 | - target: SD2PSXlite 35 | filename: sd2psxlite 36 | - target: PMC+ 37 | filename: pmc+ 38 | - target: PMCZero 39 | filename: pmczero 40 | - target: PSXMemCard 41 | filename: psxmemcard 42 | steps: 43 | - uses: actions/checkout@v4 44 | with: 45 | filter: 'blob:none' 46 | #submodules: 'recursive' 47 | #fetch-depth: '0' 48 | - run: | 49 | git submodule update --init --recursive --filter=blob:none 50 | git fetch origin --tags --force 51 | TAG=$(git describe --tags --exact-match HEAD --exclude=latest --exclude=nightly) || true 52 | if [ $TAG ] 53 | then 54 | echo "SD2PSX_VERSION=${TAG}" >> $GITHUB_ENV 55 | echo "SD2PSX_RLS_TAG=latest" >> $GITHUB_ENV 56 | echo "${TAG}" 57 | else 58 | echo "SD2PSX_VERSION=nightly-$(git rev-parse --short HEAD)" >> $GITHUB_ENV 59 | echo "SD2PSX_RLS_TAG=nightly" >> $GITHUB_ENV 60 | echo "nightly" 61 | fi 62 | 63 | - name: add build essential 64 | run: sudo apt-get update && sudo apt-get install -y cmake gcc-arm-none-eabi libnewlib-arm-none-eabi libstdc++-arm-none-eabi-newlib clangd git python3-venv 65 | 66 | - name: Build 67 | # Configure CMake in a 'build' subdirectory. `CMAKE_BUILD_TYPE` is only required if you are using a single-configuration generator such as make. 68 | # See https://cmake.org/cmake/help/latest/variable/CMAKE_BUILD_TYPE.html?highlight=cmake_build_type 69 | run: | 70 | cmake -B ${{ github.workspace }}/build -DCMAKE_BUILD_TYPE=${{ env.BUILD_TYPE }} -DDEBUG_USB_UART:BOOL=${{ matrix.debug }} -DVARIANT:STRING=${{ matrix.target }} -DDEBUG_STARTUP_DELAY=${{ matrix.delay }} -G "Unix Makefiles" 71 | cmake --build ${{github.workspace}}/build 72 | 73 | - name: Upload variants artifact 74 | uses: actions/upload-artifact@v4 75 | with: 76 | name: ${{ matrix.filename }}${{ matrix.ext }} 77 | path: build/${{ matrix.filename }}${{ matrix.ext }} 78 | 79 | create_release: 80 | if: startsWith(github.ref, 'refs/tags/') || github.ref == 'refs/heads/develop' || github.ref == 'refs/heads/main' 81 | needs: [build] 82 | runs-on: ubuntu-latest 83 | steps: 84 | - name: Download all artifacts 85 | uses: actions/download-artifact@v4 86 | 87 | - name: Create Nightly 88 | if: (!startsWith(github.ref, 'refs/tags/')) 89 | uses: mathieucarbou/marvinpinto-action-automatic-releases@latest 90 | with: 91 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 92 | prerelease: true 93 | automatic_release_tag: "nightly" 94 | title: "${{ env.SD2PSX_VERSION }}" 95 | files: | 96 | */*.uf2 97 | 98 | - name: Create release 99 | if: startsWith(github.ref, 'refs/tags/') 100 | uses: mathieucarbou/marvinpinto-action-automatic-releases@latest 101 | with: 102 | repo_token: "${{ secrets.GITHUB_TOKEN }}" 103 | prerelease: "${{ contains(github.ref, '-rc') || contains(github.ref, '-beta')}}" 104 | title: "${{ env.SD2PSX_VERSION }}" 105 | files: | 106 | */*.uf2 107 | -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | build 2 | .vscode 3 | keys.c 4 | .cache 5 | gamedbps*.dat 6 | src/version/version.c 7 | *.uf2 8 | -------------------------------------------------------------------------------- /.gitmodules: -------------------------------------------------------------------------------- 1 | [submodule "ext/pico-sdk"] 2 | path = ext/pico-sdk 3 | url = https://github.com/raspberrypi/pico-sdk.git 4 | [submodule "ext/lvgl"] 5 | path = ext/lvgl 6 | url = https://github.com/lvgl/lvgl.git 7 | [submodule "ext/ESP8266SdFat"] 8 | path = ext/ESP8266SdFat 9 | url = https://github.com/sd2psx/ESP8266SdFat.git 10 | -------------------------------------------------------------------------------- /CMakeLists.txt: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | # Pico SDK 3 | set(PICO_SDK_PATH ${CMAKE_CURRENT_SOURCE_DIR}/ext/pico-sdk) 4 | include(pico_sdk_import.cmake) 5 | 6 | project(SD2PSXTD LANGUAGES C CXX ASM) 7 | 8 | pico_sdk_init() 9 | 10 | set(CMAKE_C_STANDARD 11) 11 | set(CMAKE_CXX_STANDARD 17) 12 | 13 | option(SD2PSX_WITH_PSRAM "Build SD2PSX with PSRAM support" ON) 14 | option(SD2PSX_WITH_GUI "Build SD2PSX with GUI support" ON) 15 | option(SD2PSX_WITH_LED "Build SD2PSX with LED support" OFF) 16 | option(DEBUG_USB_UART "Activate UART over USB for debugging" OFF) 17 | 18 | # variants 19 | include(misc/variants.cmake) 20 | 21 | # set proper target name 22 | set(TARGET_NAME) 23 | if (DEBUG_USB_UART) 24 | string(TOLOWER "${VARIANT}-debug" TARGET_NAME PARENT_SCOPE) 25 | else() 26 | string(TOLOWER "${VARIANT}" TARGET_NAME PARENT_SCOPE) 27 | endif() 28 | 29 | message(STATUS "Building ${TARGET_NAME}") 30 | 31 | 32 | # Add all subdirectories for sub-targets 33 | 34 | add_subdirectory(database) 35 | add_subdirectory(src/version) 36 | add_subdirectory(src/ps2) 37 | add_subdirectory(src/ps1) 38 | if (SD2PSX_WITH_PSRAM) 39 | add_subdirectory(src/psram) 40 | endif() 41 | add_subdirectory(ext/) 42 | 43 | # SD2PSX Main Lib 44 | 45 | add_executable(${TARGET_NAME} 46 | src/main.c 47 | src/ps2.c 48 | src/ps1.c 49 | 50 | src/game_db/game_db.c 51 | src/wear_leveling/wear_leveling.c 52 | src/wear_leveling/wear_leveling_rp2040_flash.c 53 | 54 | ext/fnv/hash_64a.c 55 | ) 56 | 57 | target_compile_definitions( 58 | ${TARGET_NAME} PUBLIC 59 | PICO_XOSC_STARTUP_DELAY_MULTIPLIER=64 60 | ) 61 | 62 | target_include_directories(${TARGET_NAME} PUBLIC 63 | ext/fnv 64 | ) 65 | 66 | target_link_libraries(${TARGET_NAME} 67 | PRIVATE 68 | pico_stdlib 69 | pico_multicore 70 | hardware_pio 71 | hardware_i2c 72 | hardware_flash 73 | gamedb 74 | sd2psx_version 75 | sd2psx_common 76 | ps1_card 77 | ps2_card 78 | sd_fat 79 | ) 80 | 81 | add_dependencies(${TARGET_NAME} gamedb) 82 | set_target_properties(${TARGET_NAME} PROPERTIES PICO_TARGET_LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/memmap_custom.ld) 83 | 84 | # Common Lib 85 | 86 | add_library(sd2psx_common STATIC 87 | ${CMAKE_CURRENT_SOURCE_DIR}/src/util.c 88 | ${CMAKE_CURRENT_SOURCE_DIR}/src/debug.c 89 | ${CMAKE_CURRENT_SOURCE_DIR}/src/input.c 90 | ${CMAKE_CURRENT_SOURCE_DIR}/src/des.c 91 | ${CMAKE_CURRENT_SOURCE_DIR}/src/keystore.c 92 | ${CMAKE_CURRENT_SOURCE_DIR}/src/settings.c 93 | ${CMAKE_CURRENT_SOURCE_DIR}/src/bigmem.c 94 | ${CMAKE_CURRENT_SOURCE_DIR}/src/card_config.c) 95 | 96 | target_include_directories(sd2psx_common 97 | PUBLIC 98 | ${CMAKE_CURRENT_SOURCE_DIR}/src 99 | PRIVATE ) 100 | 101 | target_link_libraries(sd2psx_common 102 | PRIVATE 103 | sd_fat 104 | pico_platform_headers 105 | hardware_flash 106 | hardware_gpio 107 | hardware_watchdog 108 | pico_multicore 109 | inih) 110 | 111 | target_compile_options(sd2psx_common 112 | PUBLIC 113 | -Wall -Wextra 114 | -fno-jump-tables) 115 | 116 | target_compile_definitions(sd2psx_common PUBLIC 117 | USE_SPI_ARRAY_TRANSFER=1 118 | -DFEAT_PS2_CARDSIZE=1 119 | -DFEAT_PS2_MMCE=1) 120 | if (SD2PSX_WITH_PSRAM) 121 | target_compile_definitions(sd2psx_common PUBLIC "WITH_PSRAM=1") 122 | endif() 123 | 124 | if (SD2PSX_WITH_GUI) 125 | target_compile_definitions(sd2psx_common PUBLIC "WITH_GUI=1") 126 | target_sources(sd2psx_common PRIVATE 127 | ${CMAKE_CURRENT_SOURCE_DIR}/src/gui.c 128 | ${CMAKE_CURRENT_SOURCE_DIR}/src/oled.c 129 | ${CMAKE_CURRENT_SOURCE_DIR}/src/ui_menu.c 130 | ${CMAKE_CURRENT_SOURCE_DIR}/src/ui_theme_mono.c) 131 | target_link_libraries(sd2psx_common 132 | PUBLIC 133 | lvgl::lvgl 134 | PRIVATE 135 | ssd1306) 136 | endif() 137 | 138 | if (SD2PSX_WITH_LED) 139 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/src/led) 140 | target_link_libraries(sd2psx_common 141 | PUBLIC 142 | sd2psx_led) 143 | 144 | 145 | endif() 146 | 147 | set_target_properties(sd2psx_common PROPERTIES PICO_TARGET_LINKER_SCRIPT ${CMAKE_SOURCE_DIR}/memmap_custom.ld) 148 | 149 | pico_add_extra_outputs(${TARGET_NAME}) 150 | set_property(TARGET ${TARGET_NAME} APPEND_STRING PROPERTY LINK_FLAGS "-Wl,--print-memory-usage") 151 | 152 | 153 | 154 | if(DEBUG_USB_UART) 155 | set(DEBUG_STARTUP_DELAY 0 CACHE STRING "Startup Delay for Debug Output") 156 | if (NOT ${DEBUG_STARTUP_DELAY} MATCHES "[0-9]+") 157 | message(FATAL_ERROR "Misconfigured Debug Startup Delay: ${DEBUG_STARTUP_DELAY}") 158 | endif() 159 | target_compile_definitions(sd2psx_common PUBLIC -DDEBUG_USB_UART -DMMCE_DEBUG -DPICO_STDIO_USB_CONNECT_WAIT_TIMEOUT_MS=${DEBUG_STARTUP_DELAY}*1000) 160 | pico_enable_stdio_usb(${TARGET_NAME} 1) 161 | else() 162 | pico_enable_stdio_usb(${TARGET_NAME} 0) 163 | endif() 164 | -------------------------------------------------------------------------------- /database/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | # PS1 2 | set(GAMEDB_PS1_OBJ "${CMAKE_CURRENT_BINARY_DIR}/gamedbps1.o") 3 | 4 | add_custom_target(gamedbobjs_ps1 ALL 5 | COMMAND ${CMAKE_COMMAND} 6 | -D OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR} 7 | -D PYTHON_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/parse_GameDB.py 8 | -D CMAKE_OBJCOPY=${CMAKE_OBJCOPY} 9 | -D SYSTEM=ps1 10 | -P ${CMAKE_CURRENT_SOURCE_DIR}/db_obj_builder.cmake 11 | VERBATIM 12 | BYPRODUCTS ${GAMEDB_PS1_OBJ}) 13 | 14 | add_library(gamedb INTERFACE) 15 | add_dependencies(gamedb gamedbobjs_ps1) 16 | 17 | target_link_libraries(gamedb INTERFACE ${GAMEDB_PS1_OBJ}) 18 | 19 | # PS2 20 | 21 | set(GAMEDB_PS2_OBJ "${CMAKE_CURRENT_BINARY_DIR}/gamedbps2.o") 22 | 23 | add_custom_target(gamedbobjs_ps2 ALL 24 | COMMAND ${CMAKE_COMMAND} 25 | -D OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR} 26 | -D PYTHON_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/get_and_parse_hdldb.py 27 | -D CMAKE_OBJCOPY=${CMAKE_OBJCOPY} 28 | -D SYSTEM=ps2 29 | -P ${CMAKE_CURRENT_SOURCE_DIR}/db_obj_builder.cmake 30 | VERBATIM 31 | BYPRODUCTS ${GAMEDB_PS2_OBJ}) 32 | 33 | add_dependencies(gamedb gamedbobjs_ps2) 34 | 35 | target_link_libraries(gamedb INTERFACE ${GAMEDB_PS2_OBJ}) 36 | 37 | # PS2 38 | 39 | set(GAMEDB_COH_OBJ "${CMAKE_CURRENT_BINARY_DIR}/gamedbcoh.o") 40 | 41 | add_custom_target(gamedbobjs_coh ALL 42 | COMMAND ${CMAKE_COMMAND} 43 | -D OUTPUT_DIR=${CMAKE_CURRENT_BINARY_DIR} 44 | -D PYTHON_SCRIPT=${CMAKE_CURRENT_SOURCE_DIR}/parse_arcade.py 45 | -D CMAKE_OBJCOPY=${CMAKE_OBJCOPY} 46 | -D SYSTEM=coh 47 | -P ${CMAKE_CURRENT_SOURCE_DIR}/db_obj_builder.cmake 48 | VERBATIM 49 | BYPRODUCTS ${GAMEDB_COH_OBJ}) 50 | 51 | add_dependencies(gamedb gamedbobjs_coh) 52 | 53 | target_link_libraries(gamedb INTERFACE ${GAMEDB_COH_OBJ}) 54 | -------------------------------------------------------------------------------- /database/db_obj_builder.cmake: -------------------------------------------------------------------------------- 1 | string(TIMESTAMP date "%Y%m%d") 2 | 3 | set(GAMEDB_${SYSTEM}_BIN "gamedb${SYSTEM}.dat") 4 | set(GAMEDB_${SYSTEM}_OBJ "${OUTPUT_DIR}/gamedb${SYSTEM}_${date}.o") 5 | 6 | if(NOT EXISTS "${GAMEDB_${SYSTEM}_OBJ}") 7 | 8 | find_package (Python3 COMPONENTS Interpreter) 9 | execute_process (COMMAND "${Python3_EXECUTABLE}" -m venv "${OUTPUT_DIR}/db_builder") 10 | 11 | # Here is the trick 12 | ## update the environment with VIRTUAL_ENV variable (mimic the activate script) 13 | set (ENV{VIRTUAL_ENV} "${OUTPUT_DIR}/db_builder") 14 | ## change the context of the search 15 | set (Python3_FIND_VIRTUALENV FIRST) 16 | ## unset Python3_EXECUTABLE because it is also an input variable (see documentation, Artifacts Specification section) 17 | unset (Python3_EXECUTABLE) 18 | ## Launch a new search 19 | find_package (Python3 COMPONENTS Interpreter Development) 20 | 21 | file(GLOB files "${OUTPUT_DIR}/gamedb${SYSTEM}_*") 22 | foreach(file ${files}) 23 | file(REMOVE "${file}") 24 | endforeach() 25 | 26 | execute_process( 27 | COMMAND ${Python3_EXECUTABLE} -m pip install requests unidecode 28 | WORKING_DIRECTORY ${OUTPUT_DIR} 29 | OUTPUT_QUIET 30 | ) 31 | execute_process( 32 | COMMAND ${Python3_EXECUTABLE} ${PYTHON_SCRIPT} ${SYSTEM} ${OUTPUT_DIR} 33 | WORKING_DIRECTORY ${OUTPUT_DIR} 34 | OUTPUT_QUIET 35 | ) 36 | execute_process( 37 | COMMAND ${CMAKE_OBJCOPY} --input-target=binary --output-target=elf32-littlearm --binary-architecture arm --rename-section .data=.rodata "${GAMEDB_${SYSTEM}_BIN}" "${GAMEDB_${SYSTEM}_OBJ}" 38 | WORKING_DIRECTORY ${OUTPUT_DIR} 39 | OUTPUT_QUIET 40 | ) 41 | 42 | file(REMOVE_RECURSE "${OUTPUT_DIR}/${SYSTEM}") 43 | 44 | endif() 45 | 46 | file(CREATE_LINK ${GAMEDB_${SYSTEM}_OBJ} "${OUTPUT_DIR}/gamedb${SYSTEM}.o" SYMBOLIC) -------------------------------------------------------------------------------- /database/get_and_parse_hdldb.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import csv 3 | from unidecode import unidecode 4 | 5 | class GameId: 6 | name = "" 7 | id = "" 8 | prefix = "" 9 | parent_id = "" 10 | def __init__(self, name, id, parent_id=None): 11 | self.name = unidecode(name) 12 | separator = "_" 13 | if "-" in id: 14 | separator = "-" 15 | self.id = id.split(separator)[1].replace(".", "") 16 | self.prefix = id.split(separator)[0] 17 | if parent_id: 18 | self.parent_id = parent_id.split(separator)[1] 19 | else: 20 | self.parent_id = self.id 21 | def __str__(self): 22 | return "Prefix " + self.prefix + " Id " + self.id + " Name " + self.name + " Parent " + self.parent_id 23 | 24 | def __lt__(self, o): 25 | return self.name < o.name 26 | 27 | 28 | def writeSortedGameList(outfile, prefixes, games_count, games_sorted, gamenames): 29 | term = 0 30 | # Calculate general offsets 31 | game_ids_offset = (len(prefixes) + 1) * 8 32 | game_names_base_offset = game_ids_offset + (games_count * 12) + (len(prefixes) * 12) 33 | prefix_offset = game_ids_offset 34 | 35 | offset = game_names_base_offset 36 | game_name_to_offset = {} 37 | # Calculate offset for each game name 38 | for gamename in gamenames: 39 | game_name_to_offset[gamename] = offset 40 | offset = offset + len(gamename) + 1 41 | # First: write prefix Indices in the format 42 | # 4 Byte: Index Chars, padded with ws in the end 43 | # 4 Byte: Index Offset within dat 44 | for prefix in games_sorted: 45 | adjustedPrefix = prefix 46 | if len(prefix) < 4: 47 | adjustedPrefix = prefix + (4 - len(prefix) ) * " " 48 | outfile.write(adjustedPrefix.encode('ascii')) 49 | outfile.write(prefix_offset.to_bytes(4, 'big')) 50 | prefix_offset = prefix_offset + (len(games_sorted[prefix]) + 1) * 12 51 | outfile.write(term.to_bytes(8, 'big')) 52 | # Next: write game entries for each index in the format: 53 | # 4 Byte: Game ID without prefix, Big Endian 54 | # 4 Byte: Offset to game name, Big Endian 55 | # 4 Byte: Parent Game ID - if multi disc this is equal to Game ID 56 | for prefix in games_sorted: 57 | for game in games_sorted[prefix]: 58 | #print(game) 59 | outfile.write(int(game.id).to_bytes(4, 'big')) 60 | outfile.write(game_name_to_offset[game.name].to_bytes(4, 'big')) 61 | outfile.write(int(game.parent_id).to_bytes(4, 'big')) 62 | outfile.write(term.to_bytes(12, 'big')) 63 | # Last: write null terminated game names 64 | for game in game_name_to_offset: 65 | outfile.write(game.encode('ascii')) 66 | outfile.write(term.to_bytes(1, 'big')) 67 | 68 | def getGamesHDLBatchInstaller() -> ([], [], {}, int): 69 | prefixes = [] 70 | gamenames = [] 71 | games_sorted = {} 72 | games_count = 0 73 | 74 | url = "https://github.com/israpps/HDL-Batch-installer/raw/main/Database/gamename.csv" 75 | 76 | r = requests.get(url, allow_redirects=True) 77 | 78 | 79 | if r.status_code == 200: 80 | lines = r.text.split("\n") 81 | csv_reader = csv.reader(lines, delimiter=";") 82 | for row in csv_reader: 83 | if len(row) == 2: 84 | id = row[0] 85 | title = row[1] 86 | game = GameId(row[1], row[0]) 87 | try: 88 | if int(game.id) > 0: 89 | # Create Prefix list and game name list 90 | # Create dict that contains all games sorted by prefix 91 | if game.prefix not in prefixes: 92 | prefixes.append(game.prefix) 93 | if game.name not in gamenames: 94 | gamenames.append(game.name) 95 | if not game.prefix in games_sorted: 96 | games_sorted[game.prefix] = [] 97 | games_sorted[game.prefix].append(game) 98 | games_count += 1 99 | except ValueError: 100 | print(f"{game} not parsed") 101 | continue 102 | return (prefixes, gamenames, games_sorted, games_count) 103 | 104 | 105 | prefixes = [] 106 | gamenames = [] 107 | games_sorted = {} 108 | games_count = 0 109 | 110 | 111 | with open("gamedbps2.dat", "wb") as out: 112 | (prefixes, gamenames, games_sorted, games_count) = getGamesHDLBatchInstaller() 113 | writeSortedGameList(out, prefixes, games_count, games_sorted, gamenames) 114 | 115 | -------------------------------------------------------------------------------- /database/parse_GameDB.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | import re 4 | from unidecode import unidecode 5 | 6 | 7 | disc_pattern = r'\(Disc (\d)\)' 8 | replacer = r'\((.*)\)' 9 | 10 | class GameId: 11 | name = "" 12 | id = "" 13 | prefix = "" 14 | parent_id = "" 15 | def __init__(self, name, id, parent_id=None): 16 | self.name = unidecode(name) 17 | separator = "_" 18 | if "-" in id: 19 | separator = "-" 20 | self.id = id.split(separator)[1].replace(".", "") 21 | self.prefix = id.split(separator)[0] 22 | if parent_id: 23 | self.parent_id = parent_id.split(separator)[1] 24 | else: 25 | self.parent_id = self.id 26 | def __str__(self): 27 | return "Prefix " + self.prefix + " Id " + self.id + " Name " + self.name + " Parent " + self.parent_id 28 | 29 | def __lt__(self, o): 30 | return self.name < o.name 31 | 32 | 33 | def writeSortedGameList(outfile, prefixes, games_count, games_sorted, gamenames): 34 | term = 0 35 | # Calculate general offsets 36 | game_ids_offset = (len(prefixes) + 1) * 8 37 | game_names_base_offset = game_ids_offset + (games_count * 12) + (len(prefixes) * 12) 38 | prefix_offset = game_ids_offset 39 | 40 | offset = game_names_base_offset 41 | game_name_to_offset = {} 42 | # Calculate offset for each game name 43 | for gamename in gamenames: 44 | game_name_to_offset[gamename] = offset 45 | offset = offset + len(gamename) + 1 46 | # First: write prefix Indices in the format 47 | # 4 Byte: Index Chars, padded with ws in the end 48 | # 4 Byte: Index Offset within dat 49 | for prefix in games_sorted: 50 | adjustedPrefix = prefix 51 | if len(prefix) < 4: 52 | adjustedPrefix = prefix + (4 - len(prefix) ) * " " 53 | outfile.write(adjustedPrefix.encode('ascii')) 54 | outfile.write(prefix_offset.to_bytes(4, 'big')) 55 | prefix_offset = prefix_offset + (len(games_sorted[prefix]) + 1) * 12 56 | outfile.write(term.to_bytes(8, 'big')) 57 | # Next: write game entries for each index in the format: 58 | # 4 Byte: Game ID without prefix, Big Endian 59 | # 4 Byte: Offset to game name, Big Endian 60 | # 4 Byte: Parent Game ID - if multi disc this is equal to Game ID 61 | for prefix in games_sorted: 62 | for game in games_sorted[prefix]: 63 | #print(game) 64 | outfile.write(int(game.id).to_bytes(4, 'big')) 65 | outfile.write(game_name_to_offset[game.name].to_bytes(4, 'big')) 66 | outfile.write(int(game.parent_id).to_bytes(4, 'big')) 67 | outfile.write(term.to_bytes(12, 'big')) 68 | # Last: write null terminated game names 69 | for game in game_name_to_offset: 70 | outfile.write(game.encode('ascii')) 71 | outfile.write(term.to_bytes(1, 'big')) 72 | 73 | def getGamesGameDB() -> ([], [], {}, int): 74 | prefixes = [] 75 | gamenames = [] 76 | games_sorted = {} 77 | parent_serials = {} 78 | games_count = 0 79 | name_to_serials = {} 80 | 81 | url = "https://github.com/niemasd/GameDB-PSX/releases/latest/download/PSX.data.json" 82 | 83 | r = requests.get(url, allow_redirects=True) 84 | 85 | 86 | if r.status_code == 200: 87 | input = json.loads(r.text) 88 | for id in input: 89 | try: 90 | if "redump_name" in input[id]: 91 | raw_name = input[id]["redump_name"] 92 | clean_name = re.sub(replacer, "", input[id]["redump_name"]).strip() 93 | else: 94 | raw_name = input[id]["title"] 95 | clean_name = re.sub(replacer, "", input[id]["title"]).strip() 96 | parent_id = id 97 | game = GameId(clean_name, id) 98 | if len(game.prefix) > 4: 99 | raise ValueError 100 | if int(game.id) > 0: 101 | # Create Prefix list and game name list 102 | # Create dict that contains all games sorted by prefix 103 | if game.prefix not in prefixes: 104 | prefixes.append(game.prefix) 105 | if game.name not in gamenames: 106 | gamenames.append(game.name) 107 | if not game.prefix in games_sorted: 108 | games_sorted[game.prefix] = [] 109 | name_to_serials[f"{game.prefix}{raw_name}"] = id 110 | match = re.search(disc_pattern, raw_name) 111 | if match and match[0] != "(Disc 1)": 112 | parent_name = raw_name.replace(match[0], "(Disc 1)") 113 | 114 | if f"{game.prefix}{parent_name}" in name_to_serials: 115 | parent_id = name_to_serials[f"{game.prefix}{parent_name}"] 116 | game.parent_id = parent_id.split("-")[1] 117 | games_sorted[game.prefix].append(game) 118 | games_count += 1 119 | except ValueError: 120 | #print(f"{game} not parsed - wrong value") 121 | continue 122 | except IndexError: 123 | #print(f"{game} not parsed - wrong gameid format") 124 | continue 125 | 126 | print(f"Parsed {games_count} games in {len(prefixes)} Prefixes") 127 | return (prefixes, gamenames, games_sorted, games_count) 128 | 129 | 130 | prefixes = [] 131 | gamenames = [] 132 | games_sorted = {} 133 | games_count = 0 134 | 135 | 136 | with open("gamedbps1.dat", "wb") as out: 137 | (prefixes, gamenames, games_sorted, games_count) = getGamesGameDB() 138 | writeSortedGameList(out, prefixes, games_count, games_sorted, gamenames) 139 | 140 | -------------------------------------------------------------------------------- /database/parse_arcade.py: -------------------------------------------------------------------------------- 1 | import requests 2 | import json 3 | 4 | def getGamesGameDB(system) -> dict[str, str]: 5 | url = f"https://raw.githubusercontent.com/israpps/AthenaEnv/refs/heads/aclauncher/bin/aclauncher/{system}.json" 6 | 7 | r = requests.get(url, allow_redirects=True) 8 | games = {} 9 | 10 | if r.status_code == 200: 11 | input = json.loads(r.text) 12 | for game in input["games"]: 13 | id = game["gameid"] 14 | if "NM" in id: 15 | id = int(game["gameid"].replace("NM", "")) 16 | if id in games: 17 | common_prefix = [] 18 | for x, y in zip(games[id].split(), game["title"].split()): 19 | if x == y: 20 | common_prefix.append(x) 21 | else: 22 | break 23 | if len(common_prefix) > 0: 24 | games[id] = " ".join(common_prefix) 25 | else: 26 | games[id] = f"{games[id]} / {game['title']}" 27 | else: 28 | games[id] = game["title"] 29 | 30 | 31 | return games 32 | 33 | import argparse 34 | parser = argparse.ArgumentParser() 35 | parser.add_argument("dirname") 36 | parser.add_argument("outputdir") 37 | args = parser.parse_args() 38 | 39 | filename = "gamedbcoh.dat" 40 | 41 | # Get games from GameDB: 42 | 43 | games_dict = getGamesGameDB("246") | getGamesGameDB("256") 44 | 45 | 46 | # Assemble Arcade DB: 47 | 48 | game_ids_section_length = (len(games_dict) +1 ) * 8 49 | game_names_section_length = 0 50 | name_offsets = { } 51 | with open(filename, "wb") as out: 52 | term = 0 53 | for id in games_dict: 54 | name_offsets[id] = game_ids_section_length + game_names_section_length 55 | game_names_section_length += len(games_dict[id]) + 1 56 | out.write(int(id).to_bytes(4, 'big')) 57 | out.write(name_offsets[id].to_bytes(4, 'big')) 58 | out.write(term.to_bytes(8, 'big')) 59 | 60 | for id in games_dict: 61 | out.write(games_dict[id].encode("ascii")) 62 | out.write(b"\x00") 63 | 64 | -------------------------------------------------------------------------------- /doc/Logo.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/sd2psXtd/firmware/53afa498723c87622e38dff73342f248f38e889e/doc/Logo.png -------------------------------------------------------------------------------- /ext/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(sd_fat STATIC 3 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/FmtNumber.cpp 4 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/FsCache.cpp 5 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/FsDateTime.cpp 6 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/FsName.cpp 7 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/FsStructs.cpp 8 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/FsUtf.cpp 9 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/common/upcase.cpp 10 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatDbg.cpp 11 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatFile.cpp 12 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatFilePrint.cpp 13 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatFileWrite.cpp 14 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatFormatter.cpp 15 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatName.cpp 16 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatPartition.cpp 17 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/ExFatLib/ExFatVolume.cpp 18 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatDbg.cpp 19 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatFile.cpp 20 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatFileLFN.cpp 21 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatFilePrint.cpp 22 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatFileSFN.cpp 23 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatFormatter.cpp 24 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatName.cpp 25 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatPartition.cpp 26 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FatLib/FatVolume.cpp 27 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FreeStack.cpp 28 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FsLib/FsFile.cpp 29 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FsLib/FsNew.cpp 30 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/FsLib/FsVolume.cpp 31 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/iostream/istream.cpp 32 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/iostream/ostream.cpp 33 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/iostream/StdioStream.cpp 34 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/iostream/StreamBaseClass.cpp 35 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/MinimumSerial.cpp 36 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/SdCard/SdCardInfo.cpp 37 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src/SdCard/SdSpiCard.cpp 38 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFatWrapper/src/sd.cpp 39 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFatWrapper/src/SPI.cpp 40 | ) 41 | 42 | target_include_directories(sd_fat 43 | PRIVATE 44 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFatWrapper/src 45 | PUBLIC 46 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFatWrapper/include 47 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/src 48 | ${CMAKE_CURRENT_SOURCE_DIR}/ESP8266SdFat/extras/attic) 49 | 50 | target_link_libraries(sd_fat 51 | PUBLIC 52 | pico_stdlib 53 | hardware_spi 54 | ) 55 | 56 | target_link_libraries(sd_fat PRIVATE sd2psx_common) 57 | 58 | if (WITH_PSRAM) 59 | target_compile_definitions(sd_fat PRIVATE NUM_FILES=16) 60 | else() 61 | target_compile_definitions(sd_fat PRIVATE NUM_FILES=8) 62 | endif() 63 | 64 | 65 | add_library(ssd1306 STATIC ${CMAKE_CURRENT_SOURCE_DIR}/ssd1306/ssd1306.c) 66 | target_link_libraries(ssd1306 pico_stdlib hardware_i2c hardware_dma) 67 | target_include_directories(ssd1306 PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/ssd1306) 68 | 69 | set(LV_CONF_PATH ${PROJECT_SOURCE_DIR}/src/lv_conf.h CACHE STRING "" FORCE) 70 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/lvgl EXCLUDE_FROM_ALL) 71 | add_subdirectory(${CMAKE_CURRENT_SOURCE_DIR}/mcfat) 72 | 73 | target_compile_definitions(mcfat PRIVATE MAX_CACHEENTRY=0x1) 74 | target_compile_definitions(mcfat PRIVATE MAX_FDHANDLES=0x1) 75 | 76 | add_library(inih STATIC ${CMAKE_CURRENT_SOURCE_DIR}/inih/ini.c) 77 | target_link_libraries(inih PRIVATE sd_fat) 78 | target_include_directories(inih PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}/inih) 79 | -------------------------------------------------------------------------------- /ext/ESP8266SdFatWrapper/include/sd.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define SEEK_SET 0 8 | #define SEEK_CUR 1 9 | #define SEEK_END 2 10 | 11 | /** Symbolic link */ 12 | #define FIO_S_IFLNK 0x4000 13 | /** Regular file */ 14 | #define FIO_S_IFREG 0x2000 15 | /** Directory */ 16 | #define FIO_S_IFDIR 0x1000 17 | /** Others read permission */ 18 | #define FIO_S_IROTH 0x0004 19 | /** Others write permission */ 20 | #define FIO_S_IWOTH 0x0002 21 | 22 | typedef struct ps2_fileio_stat_t 23 | { 24 | unsigned int mode; 25 | unsigned int attr; 26 | unsigned int size; 27 | unsigned char ctime[8]; 28 | unsigned char atime[8]; 29 | unsigned char mtime[8]; 30 | unsigned int hisize; 31 | } ps2_fileio_stat_t; 32 | 33 | typedef struct sd_file_stat_t { 34 | size_t size; 35 | uint16_t adate; 36 | uint16_t atime; 37 | uint16_t cdate; 38 | uint16_t ctime; 39 | uint16_t mdate; 40 | uint16_t mtime; 41 | bool writable; 42 | } sd_file_stat_t; 43 | 44 | void sd_init(void); 45 | int sd_open(const char *path, int oflag); 46 | int sd_close(int fd); 47 | void sd_flush(int fd); 48 | int sd_read(int fd, void *buf, size_t count); 49 | int sd_write(int fd, void *buf, size_t count); 50 | int sd_seek(int fd, int32_t offset, int whence); 51 | uint32_t sd_tell(int fd); 52 | int sd_getStat(int fd, sd_file_stat_t* const sd_stat); 53 | 54 | int sd_filesize(int fd); 55 | int sd_mkdir(const char *path); 56 | int sd_exists(const char *path); 57 | 58 | int sd_remove(const char* path); 59 | int sd_rmdir(const char* path); 60 | 61 | int sd_get_stat(int fd, ps2_fileio_stat_t* const ps2_fileio_stat); 62 | 63 | int sd_iterate_dir(int dir, int it); 64 | size_t sd_get_name(int fd, char* name, size_t size); 65 | bool sd_is_dir(int fd); 66 | int sd_fd_is_open(int fd); 67 | 68 | uint64_t sd_filesize64(int fd); 69 | int sd_seek64(int fd, int64_t offset, int whence); 70 | uint64_t sd_tell64(int fd); -------------------------------------------------------------------------------- /ext/ESP8266SdFatWrapper/src/Arduino.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #include "pico/time.h" 7 | 8 | typedef uint8_t byte; 9 | typedef uint32_t pin_size_t; 10 | class __FlashStringHelper; 11 | 12 | #define DEC 10 13 | #define HEX 16 14 | #define OCT 8 15 | #define BIN 2 16 | 17 | typedef enum { 18 | LSBFIRST = 0, 19 | MSBFIRST = 1, 20 | } BitOrder; 21 | 22 | static inline uint32_t micros() { 23 | return to_us_since_boot(get_absolute_time()); 24 | } 25 | 26 | static inline uint32_t millis() { 27 | return to_ms_since_boot(get_absolute_time()); 28 | } 29 | 30 | class Print 31 | { 32 | private: 33 | int write_error; 34 | size_t printNumber(unsigned long, uint8_t); 35 | size_t printULLNumber(unsigned long long, uint8_t); 36 | size_t printFloat(double, int); 37 | protected: 38 | void setWriteError(int err = 1) { write_error = err; } 39 | public: 40 | Print() : write_error(0) {} 41 | 42 | int getWriteError() { return write_error; } 43 | void clearWriteError() { setWriteError(0); } 44 | 45 | virtual size_t write(uint8_t) = 0; 46 | size_t write(const char *str) { 47 | if (str == NULL) return 0; 48 | return write((const uint8_t *)str, strlen(str)); 49 | } 50 | virtual size_t write(const uint8_t *buffer, size_t size); 51 | size_t write(const char *buffer, size_t size) { 52 | return write((const uint8_t *)buffer, size); 53 | } 54 | 55 | // default to zero, meaning "a single write may block" 56 | // should be overriden by subclasses with buffering 57 | virtual int availableForWrite() { return 0; } 58 | 59 | size_t print(const __FlashStringHelper *); 60 | size_t print(const char[]); 61 | size_t print(char); 62 | size_t print(unsigned char, int = DEC); 63 | size_t print(int, int = DEC); 64 | size_t print(unsigned int, int = DEC); 65 | size_t print(long, int = DEC); 66 | size_t print(unsigned long, int = DEC); 67 | size_t print(long long, int = DEC); 68 | size_t print(unsigned long long, int = DEC); 69 | size_t print(double, int = 2); 70 | 71 | size_t println(const __FlashStringHelper *); 72 | size_t println(const char[]); 73 | size_t println(char); 74 | size_t println(unsigned char, int = DEC); 75 | size_t println(int, int = DEC); 76 | size_t println(unsigned int, int = DEC); 77 | size_t println(long, int = DEC); 78 | size_t println(unsigned long, int = DEC); 79 | size_t println(long long, int = DEC); 80 | size_t println(unsigned long long, int = DEC); 81 | size_t println(double, int = 2); 82 | size_t println(void); 83 | 84 | // EFP3 - Add printf() to make life so much easier... 85 | size_t printf(const char *format, ...); 86 | size_t printf_P(const char *format, ...); 87 | 88 | virtual void flush() { /* Empty implementation for backward compatibility */ } 89 | }; 90 | 91 | #define SS 0 92 | 93 | // Template which will evaluate at *compile time* to a single 32b number 94 | // with the specified bits set. 95 | template 96 | constexpr uint32_t __bitset(const int (&a)[N], size_t i = 0U) { 97 | return i < N ? (1L << a[i]) | __bitset(a, i + 1) : 0; 98 | } 99 | -------------------------------------------------------------------------------- /ext/ESP8266SdFatWrapper/src/SPI.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "Arduino.h" 4 | 5 | #include 6 | 7 | #include "hardware/spi.h" 8 | 9 | #define DEBUGSPI(...) do { } while(0) 10 | 11 | typedef enum { 12 | SPI_MODE0 = 0, 13 | SPI_MODE1 = 1, 14 | SPI_MODE2 = 2, 15 | SPI_MODE3 = 3, 16 | } SPIMode; 17 | 18 | class SPISettings { 19 | public: 20 | SPISettings(uint32_t clock, BitOrder bitOrder, SPIMode dataMode) { 21 | if (__builtin_constant_p(clock)) { 22 | init_AlwaysInline(clock, bitOrder, dataMode); 23 | } else { 24 | init_MightInline(clock, bitOrder, dataMode); 25 | } 26 | } 27 | 28 | SPISettings(uint32_t clock, BitOrder bitOrder, int dataMode) { 29 | if (__builtin_constant_p(clock)) { 30 | init_AlwaysInline(clock, bitOrder, (SPIMode)dataMode); 31 | } else { 32 | init_MightInline(clock, bitOrder, (SPIMode)dataMode); 33 | } 34 | } 35 | 36 | // Default speed set to 4MHz, SPI mode set to MODE 0 and Bit order set to MSB first. 37 | SPISettings() { init_AlwaysInline(4000000, MSBFIRST, SPI_MODE0); } 38 | 39 | bool operator==(const SPISettings& rhs) const 40 | { 41 | if ((this->clockFreq == rhs.clockFreq) && 42 | (this->bitOrder == rhs.bitOrder) && 43 | (this->dataMode == rhs.dataMode)) { 44 | return true; 45 | } 46 | return false; 47 | } 48 | 49 | bool operator!=(const SPISettings& rhs) const 50 | { 51 | return !(*this == rhs); 52 | } 53 | 54 | uint32_t getClockFreq() const { 55 | return clockFreq; 56 | } 57 | SPIMode getDataMode() const { 58 | return dataMode; 59 | } 60 | BitOrder getBitOrder() const { 61 | return (bitOrder); 62 | } 63 | 64 | private: 65 | void init_MightInline(uint32_t clock, BitOrder bitOrder, SPIMode dataMode) { 66 | init_AlwaysInline(clock, bitOrder, dataMode); 67 | } 68 | 69 | // Core developer MUST use an helper function in beginTransaction() to use this data 70 | void init_AlwaysInline(uint32_t clock, BitOrder bitOrder, SPIMode dataMode) __attribute__((__always_inline__)) { 71 | this->clockFreq = clock; 72 | this->dataMode = dataMode; 73 | this->bitOrder = bitOrder; 74 | } 75 | 76 | uint32_t clockFreq; 77 | SPIMode dataMode; 78 | BitOrder bitOrder; 79 | }; 80 | 81 | class HardwareSPI 82 | { 83 | public: 84 | virtual ~HardwareSPI() { } 85 | 86 | virtual uint8_t transfer(uint8_t data) = 0; 87 | virtual uint16_t transfer16(uint16_t data) = 0; 88 | virtual void transfer(void *buf, size_t count) = 0; 89 | 90 | // New transfer API. If either send or recv == nullptr then ignore it 91 | virtual void transfer(const void *send, void *recv, size_t count) { 92 | const uint8_t *out = (const uint8_t *)send; 93 | uint8_t *in = (uint8_t *)recv; 94 | for (size_t i = 0; i < count; i++) { 95 | uint8_t t = out ? *(out++) : 0xff; 96 | t = transfer(t); 97 | if (in) { 98 | *(in++) = t; 99 | } 100 | } 101 | } 102 | 103 | // Transaction Functions 104 | virtual void usingInterrupt(int interruptNumber) = 0; 105 | virtual void notUsingInterrupt(int interruptNumber) = 0; 106 | virtual void beginTransaction(SPISettings settings) = 0; 107 | virtual void endTransaction(void) = 0; 108 | 109 | // SPI Configuration methods 110 | virtual void attachInterrupt() = 0; 111 | virtual void detachInterrupt() = 0; 112 | 113 | virtual void begin() = 0; 114 | virtual void end() = 0; 115 | }; 116 | 117 | class SPIClassRP2040 : public HardwareSPI { 118 | public: 119 | SPIClassRP2040(spi_inst_t *spi, pin_size_t rx, pin_size_t cs, pin_size_t sck, pin_size_t tx); 120 | 121 | // Send or receive 8- or 16-bit data. Returns read back value 122 | byte transfer(uint8_t data) override; 123 | uint16_t transfer16(uint16_t data) override; 124 | 125 | // Sends buffer in 8 bit chunks. Overwrites buffer with read data 126 | void transfer(void *buf, size_t count) override; 127 | 128 | // Sends one buffer and receives into another, much faster! can set rx or txbuf to nullptr 129 | void transfer(const void *txbuf, void *rxbuf, size_t count) override; 130 | 131 | // Call before/after every complete transaction 132 | void beginTransaction(SPISettings settings) override; 133 | void endTransaction(void) override; 134 | 135 | // Assign pins, call before begin() 136 | bool setRX(pin_size_t pin); 137 | bool setCS(pin_size_t pin); 138 | bool setSCK(pin_size_t pin); 139 | bool setTX(pin_size_t pin); 140 | 141 | // Call once to init/deinit SPI class, select pins, etc. 142 | virtual void begin() override { 143 | begin(false); 144 | } 145 | void begin(bool hwCS); 146 | void end() override; 147 | 148 | // Deprecated - do not use! 149 | void setBitOrder(BitOrder order) __attribute__((deprecated)); 150 | void setDataMode(uint8_t uc_mode) __attribute__((deprecated)); 151 | void setClockDivider(uint8_t uc_div) __attribute__((deprecated)); 152 | 153 | // Unimplemented 154 | virtual void usingInterrupt(int interruptNumber) override { 155 | (void) interruptNumber; 156 | } 157 | virtual void notUsingInterrupt(int interruptNumber) override { 158 | (void) interruptNumber; 159 | } 160 | virtual void attachInterrupt() override { /* noop */ } 161 | virtual void detachInterrupt() override { /* noop */ } 162 | 163 | private: 164 | spi_cpol_t cpol(); 165 | spi_cpha_t cpha(); 166 | uint8_t reverseByte(uint8_t b); 167 | uint16_t reverse16Bit(uint16_t w); 168 | void adjustBuffer(const void *s, void *d, size_t cnt, bool by16); 169 | 170 | spi_inst_t *_spi; 171 | SPISettings _spis; 172 | pin_size_t _RX, _TX, _SCK, _CS; 173 | bool _hwCS; 174 | bool _running; // SPI port active 175 | bool _initted; // Transaction begun 176 | }; 177 | 178 | typedef SPIClassRP2040 SPIClass; 179 | 180 | extern SPIClassRP2040 SPI; 181 | extern SPIClassRP2040 SPI1; 182 | 183 | // SPI 184 | #define PIN_SPI0_MISO (16u) 185 | #define PIN_SPI0_MOSI (19u) 186 | #define PIN_SPI0_SCK (18u) 187 | #define PIN_SPI0_SS (17u) 188 | 189 | #define PIN_SPI1_MISO (12u) 190 | #define PIN_SPI1_MOSI (15u) 191 | #define PIN_SPI1_SCK (14u) 192 | #define PIN_SPI1_SS (13u) 193 | -------------------------------------------------------------------------------- /ext/fnv/README: -------------------------------------------------------------------------------- 1 | #=====================# 2 | # Fowler/Noll/Vo hash # 3 | #=====================# 4 | 5 | The basis of this hash algorithm was taken from an idea sent 6 | as reviewer comments to the IEEE POSIX P1003.2 committee by: 7 | 8 | Phong Vo (http://www.research.att.com/info/kpv) 9 | Glenn Fowler (http://www.research.att.com/~gsf/) 10 | 11 | In a subsequent ballot round: 12 | 13 | Landon Curt Noll (http://www.isthe.com/chongo) 14 | 15 | improved on their algorithm. Some people tried this hash 16 | and found that it worked rather well. In an EMail message 17 | to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash. 18 | 19 | FNV hashes are designed to be fast while maintaining a low 20 | collision rate. The FNV speed allows one to quickly hash lots 21 | of data while maintaining a reasonable collision rate. See: 22 | 23 | http://www.isthe.com/chongo/tech/comp/fnv/index.html 24 | 25 | for more details as well as other forms of the FNV hash. 26 | Comments, questions, bug fixes and suggestions welcome at 27 | the address given in the above URL. 28 | 29 | 30 | #==================# 31 | # FNV hash utility # 32 | #==================# 33 | 34 | Two hash utilities (32 bit and 64 bit) are provided: 35 | 36 | fnv032 [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...] 37 | fnv132 [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...] 38 | fnv1a32 [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...] 39 | 40 | fnv064 [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...] 41 | fnv164 [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...] 42 | fnv1a64 [-b bcnt] [-m] [-s arg] [-t code] [-v] [arg ...] 43 | 44 | -b bcnt mask off all but the lower bcnt bits (default: 32) 45 | -m multiple hashes, one per line for each arg 46 | -s hash arg as a string (ignoring terminating NUL bytes) 47 | -t code 0 ==> generate test vectors, 1 ==> test FNV hash 48 | -v verbose mode, print arg after hash (implies -m) 49 | arg string (if -s was given) or filename (default stdin) 50 | 51 | The fnv032, fnv064 implement the historic FNV-0 hash. 52 | The fnv132, fnv164 implement the recommended FNV-1 hash. 53 | The fnv1a32, fnv1a64 implement the recommended FNV-1a hash. 54 | 55 | This is the original historic FNV algorithm with a 0 offset basis. 56 | It is recommended that FNV-1, with a non-0 offset basis be used instead. 57 | 58 | To test FNV hashes, try: 59 | 60 | fnv032 -t 1 -v 61 | fnv132 -t 1 -v 62 | fnv1a32 -t 1 -v 63 | 64 | fnv064 -t 1 -v 65 | fnv164 -t 1 -v 66 | fnv1a64 -t 1 -v 67 | 68 | If you are compiling, try: 69 | 70 | make check 71 | 72 | 73 | #==================# 74 | # FNV hash library # 75 | #==================# 76 | 77 | The libfnv.a library implements both a 32 bit and a 64 bit FNV hash 78 | on collections of bytes, a NUL terminated strings or on an open file 79 | descriptor. 80 | 81 | Here is the 32 bit FNV 1 hash: 82 | 83 | Fnv32_t fnv_32_buf(void *buf, int len, Fnv32_t hval); /* byte buf */ 84 | Fnv32_t fnv_32_str(char *string, Fnv32_t hval); /* string */ 85 | 86 | Here is the 32 bit FNV 1a hash: 87 | 88 | Fnv32_t fnv_32a_buf(void *buf, int len, Fnv32_t hval); /* byte buf */ 89 | Fnv32_t fnv_32a_str(char *string, Fnv32_t hval); /* string */ 90 | 91 | Here is the 64 bit FNV 1 hash: 92 | 93 | Fnv64_t fnv_64_buf(void *buf, int len, Fnv64_t hval); /* byte buf */ 94 | Fnv64_t fnv_64_str(char *string, Fnv64_t hval); /* string */ 95 | 96 | Here is the 64 bit FNV 1a hash: 97 | 98 | Fnv64_t fnv_64a_buf(void *buf, int len, Fnv64_t hval); /* byte buf */ 99 | Fnv64_t fnv_64a_str(char *string, Fnv64_t hval); /* string */ 100 | 101 | On the first call to a hash function, one must supply the initial basis 102 | that is appropriate for the hash in question: 103 | 104 | FNV-0: (not recommended) 105 | 106 | FNV0_32_INIT /* 32 bit FNV-0 initial basis */ 107 | FNV0_64_INIT /* 64 bit FNV-0 initial basis */ 108 | 109 | FNV-1: 110 | 111 | FNV1_32_INIT /* 32 bit FNV-1 initial basis */ 112 | FNV1_64_INIT /* 64 bit FNV-1 initial basis */ 113 | 114 | FNV-1a: 115 | 116 | FNV1A_32_INIT /* 32 bit FNV-1a initial basis */ 117 | FNV1A_64_INIT /* 64 bit FNV-1a initial basis */ 118 | 119 | For example to perform a 64 bit FNV-1 hash: 120 | 121 | #include "fnv.h" 122 | 123 | Fnv64_t hash_val; 124 | 125 | hash_val = fnv_64_str("a string", FNV1_64_INIT); 126 | hash_val = fnv_64_str("more string", hash_val); 127 | 128 | produces the same final hash value as: 129 | 130 | hash_val = fnv_64_str("a stringmore string", FNV1_64_INIT); 131 | 132 | NOTE: If one used 'FNV0_64_INIT' instead of 'FNV1_64_INIT' one would get the 133 | historic FNV-0 hash instead recommended FNV-1 hash. 134 | 135 | To perform a 32 bit FNV-1 hash: 136 | 137 | #include "fnv.h" 138 | 139 | Fnv32_t hash_val; 140 | 141 | hash_val = fnv_32_buf(buf, length_of_buf, FNV1_32_INIT); 142 | hash_val = fnv_32_str("more data", hash_val); 143 | 144 | To perform a 64 bit FNV-1a hash: 145 | 146 | #include "fnv.h" 147 | 148 | Fnv64_t hash_val; 149 | 150 | hash_val = fnv_64a_buf(buf, length_of_buf, FNV1_64_INIT); 151 | hash_val = fnv_64a_str("more data", hash_val); 152 | 153 | =-= 154 | 155 | chongo /\oo/\ 156 | http://www.isthe.com/chongo 157 | 158 | Share and Enjoy! 159 | -------------------------------------------------------------------------------- /ext/fnv/hash_32.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hash_32 - 32 bit Fowler/Noll/Vo hash code 3 | * 4 | * @(#) $Revision: 5.1 $ 5 | * @(#) $Id: hash_32.c,v 5.1 2009/06/30 09:13:32 chongo Exp $ 6 | * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_32.c,v $ 7 | * 8 | *** 9 | * 10 | * Fowler/Noll/Vo hash 11 | * 12 | * The basis of this hash algorithm was taken from an idea sent 13 | * as reviewer comments to the IEEE POSIX P1003.2 committee by: 14 | * 15 | * Phong Vo (http://www.research.att.com/info/kpv/) 16 | * Glenn Fowler (http://www.research.att.com/~gsf/) 17 | * 18 | * In a subsequent ballot round: 19 | * 20 | * Landon Curt Noll (http://www.isthe.com/chongo/) 21 | * 22 | * improved on their algorithm. Some people tried this hash 23 | * and found that it worked rather well. In an EMail message 24 | * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash. 25 | * 26 | * FNV hashes are designed to be fast while maintaining a low 27 | * collision rate. The FNV speed allows one to quickly hash lots 28 | * of data while maintaining a reasonable collision rate. See: 29 | * 30 | * http://www.isthe.com/chongo/tech/comp/fnv/index.html 31 | * 32 | * for more details as well as other forms of the FNV hash. 33 | *** 34 | * 35 | * NOTE: The FNV-0 historic hash is not recommended. One should use 36 | * the FNV-1 hash instead. 37 | * 38 | * To use the 32 bit FNV-0 historic hash, pass FNV0_32_INIT as the 39 | * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str(). 40 | * 41 | * To use the recommended 32 bit FNV-1 hash, pass FNV1_32_INIT as the 42 | * Fnv32_t hashval argument to fnv_32_buf() or fnv_32_str(). 43 | * 44 | *** 45 | * 46 | * Please do not copyright this code. This code is in the public domain. 47 | * 48 | * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 49 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO 50 | * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR 51 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 52 | * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 53 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 54 | * PERFORMANCE OF THIS SOFTWARE. 55 | * 56 | * By: 57 | * chongo /\oo/\ 58 | * http://www.isthe.com/chongo/ 59 | * 60 | * Share and Enjoy! :-) 61 | */ 62 | 63 | #include 64 | #include "fnv.h" 65 | 66 | 67 | /* 68 | * 32 bit magic FNV-0 and FNV-1 prime 69 | */ 70 | #define FNV_32_PRIME ((Fnv32_t)0x01000193) 71 | 72 | 73 | /* 74 | * fnv_32_buf - perform a 32 bit Fowler/Noll/Vo hash on a buffer 75 | * 76 | * input: 77 | * buf - start of buffer to hash 78 | * len - length of buffer in octets 79 | * hval - previous hash value or 0 if first call 80 | * 81 | * returns: 82 | * 32 bit hash as a static hash type 83 | * 84 | * NOTE: To use the 32 bit FNV-0 historic hash, use FNV0_32_INIT as the hval 85 | * argument on the first call to either fnv_32_buf() or fnv_32_str(). 86 | * 87 | * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval 88 | * argument on the first call to either fnv_32_buf() or fnv_32_str(). 89 | */ 90 | Fnv32_t 91 | fnv_32_buf(void *buf, size_t len, Fnv32_t hval) 92 | { 93 | unsigned char *bp = (unsigned char *)buf; /* start of buffer */ 94 | unsigned char *be = bp + len; /* beyond end of buffer */ 95 | 96 | /* 97 | * FNV-1 hash each octet in the buffer 98 | */ 99 | while (bp < be) { 100 | 101 | /* multiply by the 32 bit FNV magic prime mod 2^32 */ 102 | #if defined(NO_FNV_GCC_OPTIMIZATION) 103 | hval *= FNV_32_PRIME; 104 | #else 105 | hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); 106 | #endif 107 | 108 | /* xor the bottom with the current octet */ 109 | hval ^= (Fnv32_t)*bp++; 110 | } 111 | 112 | /* return our new hash value */ 113 | return hval; 114 | } 115 | 116 | 117 | /* 118 | * fnv_32_str - perform a 32 bit Fowler/Noll/Vo hash on a string 119 | * 120 | * input: 121 | * str - string to hash 122 | * hval - previous hash value or 0 if first call 123 | * 124 | * returns: 125 | * 32 bit hash as a static hash type 126 | * 127 | * NOTE: To use the 32 bit FNV-0 historic hash, use FNV0_32_INIT as the hval 128 | * argument on the first call to either fnv_32_buf() or fnv_32_str(). 129 | * 130 | * NOTE: To use the recommended 32 bit FNV-1 hash, use FNV1_32_INIT as the hval 131 | * argument on the first call to either fnv_32_buf() or fnv_32_str(). 132 | */ 133 | Fnv32_t 134 | fnv_32_str(char *str, Fnv32_t hval) 135 | { 136 | unsigned char *s = (unsigned char *)str; /* unsigned string */ 137 | 138 | /* 139 | * FNV-1 hash each octet in the buffer 140 | */ 141 | while (*s) { 142 | 143 | /* multiply by the 32 bit FNV magic prime mod 2^32 */ 144 | #if defined(NO_FNV_GCC_OPTIMIZATION) 145 | hval *= FNV_32_PRIME; 146 | #else 147 | hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); 148 | #endif 149 | 150 | /* xor the bottom with the current octet */ 151 | hval ^= (Fnv32_t)*s++; 152 | } 153 | 154 | /* return our new hash value */ 155 | return hval; 156 | } 157 | -------------------------------------------------------------------------------- /ext/fnv/hash_32a.c: -------------------------------------------------------------------------------- 1 | /* 2 | * hash_32 - 32 bit Fowler/Noll/Vo FNV-1a hash code 3 | * 4 | * @(#) $Revision: 5.1 $ 5 | * @(#) $Id: hash_32a.c,v 5.1 2009/06/30 09:13:32 chongo Exp $ 6 | * @(#) $Source: /usr/local/src/cmd/fnv/RCS/hash_32a.c,v $ 7 | * 8 | *** 9 | * 10 | * Fowler/Noll/Vo hash 11 | * 12 | * The basis of this hash algorithm was taken from an idea sent 13 | * as reviewer comments to the IEEE POSIX P1003.2 committee by: 14 | * 15 | * Phong Vo (http://www.research.att.com/info/kpv/) 16 | * Glenn Fowler (http://www.research.att.com/~gsf/) 17 | * 18 | * In a subsequent ballot round: 19 | * 20 | * Landon Curt Noll (http://www.isthe.com/chongo/) 21 | * 22 | * improved on their algorithm. Some people tried this hash 23 | * and found that it worked rather well. In an EMail message 24 | * to Landon, they named it the ``Fowler/Noll/Vo'' or FNV hash. 25 | * 26 | * FNV hashes are designed to be fast while maintaining a low 27 | * collision rate. The FNV speed allows one to quickly hash lots 28 | * of data while maintaining a reasonable collision rate. See: 29 | * 30 | * http://www.isthe.com/chongo/tech/comp/fnv/index.html 31 | * 32 | * for more details as well as other forms of the FNV hash. 33 | *** 34 | * 35 | * To use the recommended 32 bit FNV-1a hash, pass FNV1_32A_INIT as the 36 | * Fnv32_t hashval argument to fnv_32a_buf() or fnv_32a_str(). 37 | * 38 | *** 39 | * 40 | * Please do not copyright this code. This code is in the public domain. 41 | * 42 | * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 43 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO 44 | * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR 45 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 46 | * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 47 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 48 | * PERFORMANCE OF THIS SOFTWARE. 49 | * 50 | * By: 51 | * chongo /\oo/\ 52 | * http://www.isthe.com/chongo/ 53 | * 54 | * Share and Enjoy! :-) 55 | */ 56 | 57 | #include 58 | #include "fnv.h" 59 | 60 | 61 | /* 62 | * 32 bit magic FNV-1a prime 63 | */ 64 | #define FNV_32_PRIME ((Fnv32_t)0x01000193) 65 | 66 | 67 | /* 68 | * fnv_32a_buf - perform a 32 bit Fowler/Noll/Vo FNV-1a hash on a buffer 69 | * 70 | * input: 71 | * buf - start of buffer to hash 72 | * len - length of buffer in octets 73 | * hval - previous hash value or 0 if first call 74 | * 75 | * returns: 76 | * 32 bit hash as a static hash type 77 | * 78 | * NOTE: To use the recommended 32 bit FNV-1a hash, use FNV1_32A_INIT as the 79 | * hval arg on the first call to either fnv_32a_buf() or fnv_32a_str(). 80 | */ 81 | Fnv32_t 82 | fnv_32a_buf(void *buf, size_t len, Fnv32_t hval) 83 | { 84 | unsigned char *bp = (unsigned char *)buf; /* start of buffer */ 85 | unsigned char *be = bp + len; /* beyond end of buffer */ 86 | 87 | /* 88 | * FNV-1a hash each octet in the buffer 89 | */ 90 | while (bp < be) { 91 | 92 | /* xor the bottom with the current octet */ 93 | hval ^= (Fnv32_t)*bp++; 94 | 95 | /* multiply by the 32 bit FNV magic prime mod 2^32 */ 96 | #if defined(NO_FNV_GCC_OPTIMIZATION) 97 | hval *= FNV_32_PRIME; 98 | #else 99 | hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); 100 | #endif 101 | } 102 | 103 | /* return our new hash value */ 104 | return hval; 105 | } 106 | 107 | 108 | /* 109 | * fnv_32a_str - perform a 32 bit Fowler/Noll/Vo FNV-1a hash on a string 110 | * 111 | * input: 112 | * str - string to hash 113 | * hval - previous hash value or 0 if first call 114 | * 115 | * returns: 116 | * 32 bit hash as a static hash type 117 | * 118 | * NOTE: To use the recommended 32 bit FNV-1a hash, use FNV1_32A_INIT as the 119 | * hval arg on the first call to either fnv_32a_buf() or fnv_32a_str(). 120 | */ 121 | Fnv32_t 122 | fnv_32a_str(char *str, Fnv32_t hval) 123 | { 124 | unsigned char *s = (unsigned char *)str; /* unsigned string */ 125 | 126 | /* 127 | * FNV-1a hash each octet in the buffer 128 | */ 129 | while (*s) { 130 | 131 | /* xor the bottom with the current octet */ 132 | hval ^= (Fnv32_t)*s++; 133 | 134 | /* multiply by the 32 bit FNV magic prime mod 2^32 */ 135 | #if defined(NO_FNV_GCC_OPTIMIZATION) 136 | hval *= FNV_32_PRIME; 137 | #else 138 | hval += (hval<<1) + (hval<<4) + (hval<<7) + (hval<<8) + (hval<<24); 139 | #endif 140 | } 141 | 142 | /* return our new hash value */ 143 | return hval; 144 | } 145 | -------------------------------------------------------------------------------- /ext/fnv/have_ulong64.c: -------------------------------------------------------------------------------- 1 | /* 2 | * have_ulong64 - Determine if we have a 64 bit unsigned long long 3 | * 4 | * usage: 5 | * have_ulong64 > longlong.h 6 | * 7 | * Not all systems have a 'long long type' so this may not compile on 8 | * your system. 9 | * 10 | * This prog outputs the define: 11 | * 12 | * HAVE_64BIT_LONG_LONG 13 | * defined ==> we have a 64 bit unsigned long long 14 | * undefined ==> we must simulate a 64 bit unsigned long long 15 | */ 16 | /* 17 | * 18 | * Please do not copyright this code. This code is in the public domain. 19 | * 20 | * LANDON CURT NOLL DISCLAIMS ALL WARRANTIES WITH REGARD TO THIS SOFTWARE, 21 | * INCLUDING ALL IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS. IN NO 22 | * EVENT SHALL LANDON CURT NOLL BE LIABLE FOR ANY SPECIAL, INDIRECT OR 23 | * CONSEQUENTIAL DAMAGES OR ANY DAMAGES WHATSOEVER RESULTING FROM LOSS OF 24 | * USE, DATA OR PROFITS, WHETHER IN AN ACTION OF CONTRACT, NEGLIGENCE OR 25 | * OTHER TORTIOUS ACTION, ARISING OUT OF OR IN CONNECTION WITH THE USE OR 26 | * PERFORMANCE OF THIS SOFTWARE. 27 | * 28 | * By: 29 | * chongo /\oo/\ 30 | * http://www.isthe.com/chongo/ 31 | * 32 | * Share and Enjoy! :-) 33 | */ 34 | 35 | /* 36 | * have the compiler try its hand with unsigned and signed long longs 37 | */ 38 | #if ! defined(NO64BIT_LONG_LONG) 39 | unsigned long long val = 1099511628211ULL; 40 | #endif /* NO64BIT_LONG_LONG */ 41 | 42 | int 43 | main(void) 44 | { 45 | /* 46 | * ensure that the length of long long val is what we expect 47 | */ 48 | #if defined(NO64BIT_LONG_LONG) 49 | printf("#undef HAVE_64BIT_LONG_LONG\t/* no */\n"); 50 | #else /* NO64BIT_LONG_LONG */ 51 | if (val == 1099511628211ULL && sizeof(val) == 8) { 52 | printf("#define HAVE_64BIT_LONG_LONG\t/* yes */\n"); 53 | } 54 | #endif /* NO64BIT_LONG_LONG */ 55 | 56 | /* exit(0); */ 57 | return 0; 58 | } 59 | -------------------------------------------------------------------------------- /ext/fnv/longlong.h: -------------------------------------------------------------------------------- 1 | /* 2 | * DO NOT EDIT -- generated by the Makefile 3 | */ 4 | 5 | #if !defined(__LONGLONG_H__) 6 | #define __LONGLONG_H__ 7 | 8 | /* do we have/want to use a long long type? */ 9 | #define HAVE_64BIT_LONG_LONG /* yes */ 10 | 11 | /* 12 | * NO64BIT_LONG_LONG undef HAVE_64BIT_LONG_LONG 13 | */ 14 | #if defined(NO64BIT_LONG_LONG) 15 | #undef HAVE_64BIT_LONG_LONG 16 | #endif /* NO64BIT_LONG_LONG */ 17 | 18 | #endif /* !__LONGLONG_H__ */ 19 | -------------------------------------------------------------------------------- /ext/fnv/qmk_fnv_type_validation.c: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Nick Brassel (@tzarc) 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | #include "fnv.h" 4 | 5 | // This library was originally sourced from: 6 | // http://www.isthe.com/chongo/tech/comp/fnv/index.html 7 | // 8 | // Version at the time of retrieval on 2022-06-26: v5.0.3 9 | 10 | _Static_assert(sizeof(long long) == 8, "long long should be 64 bits"); 11 | _Static_assert(sizeof(unsigned long long) == 8, "unsigned long long should be 64 bits"); 12 | 13 | _Static_assert(sizeof(Fnv32_t) == 4, "Fnv32_t should be 32 bits"); 14 | _Static_assert(sizeof(Fnv64_t) == 8, "Fnv64_t should be 64 bits"); 15 | -------------------------------------------------------------------------------- /ext/inih/LICENSE.txt: -------------------------------------------------------------------------------- 1 | 2 | The "inih" library is distributed under the New BSD license: 3 | 4 | Copyright (c) 2009, Ben Hoyt 5 | All rights reserved. 6 | 7 | Redistribution and use in source and binary forms, with or without 8 | modification, are permitted provided that the following conditions are met: 9 | * Redistributions of source code must retain the above copyright 10 | notice, this list of conditions and the following disclaimer. 11 | * Redistributions in binary form must reproduce the above copyright 12 | notice, this list of conditions and the following disclaimer in the 13 | documentation and/or other materials provided with the distribution. 14 | * Neither the name of Ben Hoyt nor the names of its contributors 15 | may be used to endorse or promote products derived from this software 16 | without specific prior written permission. 17 | 18 | THIS SOFTWARE IS PROVIDED BY BEN HOYT ''AS IS'' AND ANY 19 | EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED 20 | WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE 21 | DISCLAIMED. IN NO EVENT SHALL BEN HOYT BE LIABLE FOR ANY 22 | DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES 23 | (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; 24 | LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND 25 | ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT 26 | (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS 27 | SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE. 28 | -------------------------------------------------------------------------------- /ext/inih/ini.h: -------------------------------------------------------------------------------- 1 | /* inih -- simple .INI file parser 2 | 3 | SPDX-License-Identifier: BSD-3-Clause 4 | 5 | Copyright (C) 2009-2020, Ben Hoyt 6 | 7 | inih is released under the New BSD license (see LICENSE.txt). Go to the project 8 | home page for more info: 9 | 10 | https://github.com/benhoyt/inih 11 | 12 | */ 13 | 14 | #ifndef INI_H 15 | #define INI_H 16 | 17 | /* Make this header file easier to include in C++ code */ 18 | #ifdef __cplusplus 19 | extern "C" { 20 | #endif 21 | 22 | #include 23 | 24 | /* Nonzero if ini_handler callback should accept lineno parameter. */ 25 | #ifndef INI_HANDLER_LINENO 26 | #define INI_HANDLER_LINENO 0 27 | #endif 28 | 29 | /* Visibility symbols, required for Windows DLLs */ 30 | #ifndef INI_API 31 | #if defined _WIN32 || defined __CYGWIN__ 32 | # ifdef INI_SHARED_LIB 33 | # ifdef INI_SHARED_LIB_BUILDING 34 | # define INI_API __declspec(dllexport) 35 | # else 36 | # define INI_API __declspec(dllimport) 37 | # endif 38 | # else 39 | # define INI_API 40 | # endif 41 | #else 42 | # if defined(__GNUC__) && __GNUC__ >= 4 43 | # define INI_API __attribute__ ((visibility ("default"))) 44 | # else 45 | # define INI_API 46 | # endif 47 | #endif 48 | #endif 49 | 50 | /* Typedef for prototype of handler function. */ 51 | #if INI_HANDLER_LINENO 52 | typedef int (*ini_handler)(void* user, const char* section, 53 | const char* name, const char* value, 54 | int lineno); 55 | #else 56 | typedef int (*ini_handler)(void* user, const char* section, 57 | const char* name, const char* value); 58 | #endif 59 | 60 | /* Typedef for prototype of fgets-style reader function. */ 61 | typedef char* (*ini_reader)(char* str, int num, void* stream); 62 | 63 | /* Parse given INI-style file. May have [section]s, name=value pairs 64 | (whitespace stripped), and comments starting with ';' (semicolon). Section 65 | is "" if name=value pair parsed before any section heading. name:value 66 | pairs are also supported as a concession to Python's configparser. 67 | 68 | For each name=value pair parsed, call handler function with given user 69 | pointer as well as section, name, and value (data only valid for duration 70 | of handler call). Handler should return nonzero on success, zero on error. 71 | 72 | Returns 0 on success, line number of first error on parse error (doesn't 73 | stop on first error), -1 on file open error, or -2 on memory allocation 74 | error (only when INI_USE_STACK is zero). 75 | */ 76 | INI_API int ini_parse(const char* filename, ini_handler handler, void* user); 77 | 78 | /* Same as ini_parse(), but takes a FILE* instead of filename. This doesn't 79 | close the file when it's finished -- the caller must do that. */ 80 | INI_API int ini_parse_file(FILE* file, ini_handler handler, void* user); 81 | 82 | /* Same as ini_parse(), but takes an ini_reader function pointer instead of 83 | filename. Used for implementing custom or string-based I/O (see also 84 | ini_parse_string). */ 85 | INI_API int ini_parse_stream(ini_reader reader, void* stream, ini_handler handler, 86 | void* user); 87 | 88 | /* Same as ini_parse(), but takes a zero-terminated string with the INI data 89 | instead of a file. Useful for parsing INI data from a network socket or 90 | already in memory. */ 91 | INI_API int ini_parse_string(const char* string, ini_handler handler, void* user); 92 | 93 | INI_API int ini_parse_sd_file(int fd, ini_handler handler, void* user); 94 | 95 | 96 | /* Nonzero to allow multi-line value parsing, in the style of Python's 97 | configparser. If allowed, ini_parse() will call the handler with the same 98 | name for each subsequent line parsed. */ 99 | #ifndef INI_ALLOW_MULTILINE 100 | #define INI_ALLOW_MULTILINE 1 101 | #endif 102 | 103 | /* Nonzero to allow a UTF-8 BOM sequence (0xEF 0xBB 0xBF) at the start of 104 | the file. See https://github.com/benhoyt/inih/issues/21 */ 105 | #ifndef INI_ALLOW_BOM 106 | #define INI_ALLOW_BOM 1 107 | #endif 108 | 109 | /* Chars that begin a start-of-line comment. Per Python configparser, allow 110 | both ; and # comments at the start of a line by default. */ 111 | #ifndef INI_START_COMMENT_PREFIXES 112 | #define INI_START_COMMENT_PREFIXES ";#" 113 | #endif 114 | 115 | /* Nonzero to allow inline comments (with valid inline comment characters 116 | specified by INI_INLINE_COMMENT_PREFIXES). Set to 0 to turn off and match 117 | Python 3.2+ configparser behaviour. */ 118 | #ifndef INI_ALLOW_INLINE_COMMENTS 119 | #define INI_ALLOW_INLINE_COMMENTS 1 120 | #endif 121 | #ifndef INI_INLINE_COMMENT_PREFIXES 122 | #define INI_INLINE_COMMENT_PREFIXES ";" 123 | #endif 124 | 125 | /* Nonzero to use stack for line buffer, zero to use heap (malloc/free). */ 126 | #ifndef INI_USE_STACK 127 | #define INI_USE_STACK 1 128 | #endif 129 | 130 | /* Maximum line length for any line in INI file (stack or heap). Note that 131 | this must be 3 more than the longest line (due to '\r', '\n', and '\0'). */ 132 | #ifndef INI_MAX_LINE 133 | #define INI_MAX_LINE 200 134 | #endif 135 | 136 | /* Nonzero to allow heap line buffer to grow via realloc(), zero for a 137 | fixed-size buffer of INI_MAX_LINE bytes. Only applies if INI_USE_STACK is 138 | zero. */ 139 | #ifndef INI_ALLOW_REALLOC 140 | #define INI_ALLOW_REALLOC 0 141 | #endif 142 | 143 | /* Initial size in bytes for heap line buffer. Only applies if INI_USE_STACK 144 | is zero. */ 145 | #ifndef INI_INITIAL_ALLOC 146 | #define INI_INITIAL_ALLOC 200 147 | #endif 148 | 149 | /* Stop parsing on first error (default is to keep parsing). */ 150 | #ifndef INI_STOP_ON_FIRST_ERROR 151 | #define INI_STOP_ON_FIRST_ERROR 0 152 | #endif 153 | 154 | /* Nonzero to call the handler at the start of each new section (with 155 | name and value NULL). Default is to only call the handler on 156 | each name=value pair. */ 157 | #ifndef INI_CALL_HANDLER_ON_NEW_SECTION 158 | #define INI_CALL_HANDLER_ON_NEW_SECTION 0 159 | #endif 160 | 161 | /* Nonzero to allow a name without a value (no '=' or ':' on the line) and 162 | call the handler with value NULL in this case. Default is to treat 163 | no-value lines as an error. */ 164 | #ifndef INI_ALLOW_NO_VALUE 165 | #define INI_ALLOW_NO_VALUE 0 166 | #endif 167 | 168 | /* Nonzero to use custom ini_malloc, ini_free, and ini_realloc memory 169 | allocation functions (INI_USE_STACK must also be 0). These functions must 170 | have the same signatures as malloc/free/realloc and behave in a similar 171 | way. ini_realloc is only needed if INI_ALLOW_REALLOC is set. */ 172 | #ifndef INI_CUSTOM_ALLOCATOR 173 | #define INI_CUSTOM_ALLOCATOR 0 174 | #endif 175 | 176 | 177 | #ifdef __cplusplus 178 | } 179 | #endif 180 | 181 | #endif /* INI_H */ 182 | -------------------------------------------------------------------------------- /ext/mcfat/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(mcfat STATIC 2 | src/mcio.c 3 | src/util.c 4 | src/mcfat.c) 5 | 6 | target_include_directories(mcfat PUBLIC inc) 7 | -------------------------------------------------------------------------------- /ext/mcfat/inc/mcfat.h: -------------------------------------------------------------------------------- 1 | #ifndef _MCFAT_H 2 | #define _MCFAT_H 3 | 4 | #include 5 | #include 6 | 7 | typedef struct _mcfat_cardspecs 8 | { 9 | uint16_t pagesize; 10 | uint16_t blocksize; 11 | int32_t cardsize; 12 | uint8_t flags; 13 | } mcfat_cardspecs_t; 14 | 15 | typedef struct _mc_ops 16 | { 17 | int (*page_erase)(mcfat_cardspecs_t*, uint32_t); 18 | int (*page_write)(mcfat_cardspecs_t*, uint32_t, void*); 19 | int (*page_read)(mcfat_cardspecs_t*, uint32_t, uint32_t, void*); 20 | int (*ecc_write)(mcfat_cardspecs_t*, uint32_t, void*); 21 | int (*ecc_read)(mcfat_cardspecs_t*, uint32_t, uint32_t, void*); 22 | } mcfat_mcops_t; 23 | 24 | 25 | void mcfat_setConfig( const mcfat_mcops_t mcops, const mcfat_cardspecs_t cardspecs ); 26 | void mcfat_setCardChanged( bool changed ); 27 | 28 | #endif -------------------------------------------------------------------------------- /ext/mcfat/inc/mcio.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps3mca-tool - PlayStation 3 Memory Card Adaptor Software 3 | * Copyright (C) 2011 - jimmikaelkael 4 | * Copyright (C) 2011 - "someone who wants to stay anonymous" 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef __MCIO_H__ 21 | #define __MCIO_H__ 22 | 23 | #include 24 | 25 | struct sceMcStDateTime { 26 | uint8_t Resv2; 27 | uint8_t Sec; 28 | uint8_t Min; 29 | uint8_t Hour; 30 | uint8_t Day; 31 | uint8_t Month; 32 | uint16_t Year; 33 | } __attribute__((packed)); 34 | 35 | struct io_stat { 36 | uint32_t mode; 37 | uint32_t attr; 38 | uint32_t size; 39 | struct sceMcStDateTime ctime; 40 | struct sceMcStDateTime mtime; 41 | } __attribute__((packed)); 42 | 43 | struct io_dirent { 44 | struct io_stat stat; 45 | char name[256]; 46 | uint32_t unknown; 47 | } __attribute__((packed)); 48 | 49 | void Card_DataChecksum(uint8_t *pagebuf, uint8_t *ecc); 50 | int mcio_init(void); 51 | int mcio_mcDetect(void); 52 | int mcio_mcGetInfo(int *pagesize, int *blocksize, int *cardsize, int *cardflags); 53 | int mcio_mcGetAvailableSpace(int *cardfree); 54 | int mcio_mcOpen(char *filename, int flag); 55 | int mcio_mcClose(int fd); 56 | int mcio_mcRead(int fd, void *buf, int length); 57 | int mcio_mcWrite(int fd, void *buf, int length); 58 | int mcio_mcSeek(int fd, int offset, int origin); 59 | int mcio_mcGetCluster(int fd); 60 | int mcio_mcCreateCrossLinkedFile(char *real_filename, char *dummy_filename); 61 | int mcio_mcDopen(char *dirname); 62 | int mcio_mcDclose(int fd); 63 | int mcio_mcDread(int fd, struct io_dirent *dirent); 64 | int mcio_mcMkDir(char *dirname); 65 | int mcio_mcReadPage(int pagenum, void *buf); 66 | int mcio_mcUnformat(void); 67 | int mcio_mcFormat(void); 68 | int mcio_mcRemove(char *filename); 69 | int mcio_mcRmDir(char *dirname); 70 | 71 | /* MC error codes */ 72 | #define sceMcResSucceed 0 73 | #define sceMcResChangedCard -1 74 | #define sceMcResNoFormat -2 75 | #define sceMcResFullDevice -3 76 | #define sceMcResNoEntry -4 77 | #define sceMcResDeniedPermit -5 78 | #define sceMcResNotEmpty -6 79 | #define sceMcResUpLimitHandle -7 80 | #define sceMcResFailReplace -8 81 | #define sceMcResFailResetAuth -11 82 | #define sceMcResFailDetect -12 83 | #define sceMcResFailDetect2 -13 84 | #define sceMcResFailReadCluster -21 85 | #define sceMcResFailCheckBackupBlocks -47 86 | #define sceMcResFailIO -48 87 | #define sceMcResFailSetDeviceSpecs -49 88 | #define sceMcResDeniedPS1Permit -51 89 | #define sceMcResFailAuth -90 90 | #define sceMcResNotDir -100 91 | #define sceMcResNotFile -101 92 | 93 | /* file attributes */ 94 | #define sceMcFileAttrReadable 0x0001 95 | #define sceMcFileAttrWriteable 0x0002 96 | #define sceMcFileAttrExecutable 0x0004 97 | #define sceMcFileAttrDupProhibit 0x0008 98 | #define sceMcFileAttrFile 0x0010 99 | #define sceMcFileAttrSubdir 0x0020 100 | #define sceMcFileCreateDir 0x0040 101 | #define sceMcFileAttrClosed 0x0080 102 | #define sceMcFileCreateFile 0x0200 103 | #define sceMcFile0400 0x0400 104 | #define sceMcFileAttrPDAExec 0x0800 105 | #define sceMcFileAttrPS1 0x1000 106 | #define sceMcFileAttrHidden 0x2000 107 | #define sceMcFileAttrExists 0x8000 108 | 109 | #endif 110 | 111 | -------------------------------------------------------------------------------- /ext/mcfat/src/mcfat.c: -------------------------------------------------------------------------------- 1 | #include "mcfat.h" 2 | #include "mcfat_internal.h" 3 | 4 | bool mcfat_cardchanged = false; 5 | mcfat_cardspecs_t mcfat_cardspecs; 6 | mcfat_mcops_t mcfat_bdoperations; 7 | 8 | void mcfat_setConfig( const mcfat_mcops_t mcops, const mcfat_cardspecs_t cardspecs ) 9 | { 10 | mcfat_cardspecs = cardspecs; 11 | mcfat_bdoperations = mcops; 12 | } 13 | 14 | void mcfat_setCardChanged(bool changed) 15 | { 16 | mcfat_cardchanged = changed; 17 | } -------------------------------------------------------------------------------- /ext/mcfat/src/mcfat_internal.h: -------------------------------------------------------------------------------- 1 | #ifndef _MCFAT_INTERNAL_H 2 | #define _MCFAT_INTERNAL_H 3 | 4 | #include "mcfat.h" 5 | 6 | 7 | extern bool mcfat_cardchanged; 8 | extern mcfat_cardspecs_t mcfat_cardspecs; 9 | extern mcfat_mcops_t mcfat_bdoperations; 10 | 11 | #endif -------------------------------------------------------------------------------- /ext/mcfat/src/util.c: -------------------------------------------------------------------------------- 1 | /* 2 | * ps3mca-tool - PlayStation 3 Memory Card Adaptor Software 3 | * Copyright (C) 2011 - jimmikaelkael 4 | * Copyright (C) 2011 - "someone who wants to stay anonymous" 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #include "util.h" 21 | 22 | void memrcpy(void *dst, void *src, size_t len) 23 | { 24 | size_t i; 25 | for (i = 0; i < len; i++) { 26 | ((uint8_t *)dst)[i] = ((uint8_t *)src)[len-1-i]; 27 | } 28 | } 29 | 30 | /* 31 | * memxor: perform exclusive-or of memory buffers. 32 | */ 33 | void memxor(const void *a, const void *b, void *Result, size_t Length) 34 | { 35 | size_t i; 36 | for (i = 0; i < Length; i++) { 37 | ((uint8_t *)Result)[i] = ((uint8_t *)a)[i] ^ ((uint8_t *)b)[i]; 38 | } 39 | } 40 | 41 | /* 42 | * append_le_uint16: append an unsigned 16 bits Little Endian 43 | * value to a buffer 44 | */ 45 | void append_le_uint16(uint8_t *buf, uint16_t val) 46 | { 47 | buf[0] = (uint8_t)val; 48 | buf[1] = (uint8_t)(val >> 8); 49 | } 50 | 51 | /* 52 | * append_le_uint32: append an unsigned 32 bits Little Endian 53 | * value to a buffer 54 | */ 55 | void append_le_uint32(uint8_t *buf, uint32_t val) 56 | { 57 | buf[0] = (uint8_t)val; 58 | buf[1] = (uint8_t)(val >> 8); 59 | buf[2] = (uint8_t)(val >> 16); 60 | buf[3] = (uint8_t)(val >> 24); 61 | } 62 | 63 | /* 64 | * append_le_uint64: append an unsigned 64 bits Little Endian 65 | * value to a buffer 66 | */ 67 | void append_le_uint64(uint8_t *buf, uint64_t val) 68 | { 69 | int i; 70 | for (i = 7; i >= 0; i--, val >>= 8) { 71 | buf[i] = (uint8_t)val; 72 | } 73 | } 74 | 75 | /* 76 | * read_le_uint16: read an unsigned 16 bits Little Endian 77 | * value from a buffer 78 | */ 79 | uint16_t read_le_uint16(const uint8_t *buf) 80 | { 81 | register uint16_t val; 82 | 83 | val = buf[0]; 84 | val |= (buf[1] << 8); 85 | 86 | return val; 87 | } 88 | 89 | /* 90 | * read_le_uint32: read an unsigned 32 bits Little Endian 91 | * value from a buffer 92 | */ 93 | uint32_t read_le_uint32(const uint8_t *buf) 94 | { 95 | register uint32_t val; 96 | 97 | val = buf[0]; 98 | val |= (buf[1] << 8); 99 | val |= (buf[2] << 16); 100 | val |= (buf[3] << 24); 101 | 102 | return val; 103 | } 104 | 105 | /* 106 | * read_le_uint64: read an unsigned 64 bits Little Endian 107 | * value from a buffer 108 | */ 109 | uint64_t read_le_uint64(const uint8_t *buf) 110 | { 111 | uint64_t val = 0; 112 | int i; 113 | for (i = 0; i < 8; i++) { 114 | val = (val << 8) | buf[i]; 115 | } 116 | 117 | return val; 118 | } 119 | 120 | -------------------------------------------------------------------------------- /ext/mcfat/src/util.h: -------------------------------------------------------------------------------- 1 | /* 2 | * ps3mca-tool - PlayStation 3 Memory Card Adaptor Software 3 | * Copyright (C) 2011 - jimmikaelkael 4 | * Copyright (C) 2011 - "someone who wants to stay anonymous" 5 | * 6 | * This program is free software: you can redistribute it and/or modify 7 | * it under the terms of the GNU General Public License as published by 8 | * the Free Software Foundation, either version 3 of the License, or 9 | * (at your option) any later version. 10 | * 11 | * This program is distributed in the hope that it will be useful, 12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 14 | * GNU General Public License for more details. 15 | * 16 | * You should have received a copy of the GNU General Public License 17 | * along with this program. If not, see . 18 | */ 19 | 20 | #ifndef __UTIL_H__ 21 | #define __UTIL_H__ 22 | 23 | #include 24 | #include 25 | #include 26 | 27 | void memrcpy(void *dst, void *src, size_t len); 28 | void memxor(const void *a, const void *b, void *Result, size_t Length); 29 | void append_le_uint16(uint8_t *buf, uint16_t val); 30 | void append_le_uint32(uint8_t *buf, uint32_t val); 31 | void append_le_uint64(uint8_t *buf, uint64_t val); 32 | uint16_t read_le_uint16(const uint8_t *buf); 33 | uint32_t read_le_uint32(const uint8_t *buf); 34 | uint64_t read_le_uint64(const uint8_t *buf); 35 | 36 | #endif 37 | 38 | -------------------------------------------------------------------------------- /ext/ssd1306/LICENSE: -------------------------------------------------------------------------------- 1 | MIT License 2 | 3 | Copyright (c) 2021 David Schramm 4 | 5 | Permission is hereby granted, free of charge, to any person obtaining a copy 6 | of this software and associated documentation files (the "Software"), to deal 7 | in the Software without restriction, including without limitation the rights 8 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 9 | copies of the Software, and to permit persons to whom the Software is 10 | furnished to do so, subject to the following conditions: 11 | 12 | The above copyright notice and this permission notice shall be included in all 13 | copies or substantial portions of the Software. 14 | 15 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 16 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 17 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 18 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 19 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 20 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 21 | SOFTWARE. 22 | -------------------------------------------------------------------------------- /ext/ssd1306/README.md: -------------------------------------------------------------------------------- 1 | [![example vid](ssd1306-example.gif)]() 2 | 3 | # pico-ssd1306 4 | Simple library for using ssd1306 displays with the Raspberry Pi Pico and the pico-sdk. 5 | 6 | ## Usage 7 | * copy `font.h`, `ssd1306.c` and `ssd1306.h` to your project 8 | * see example 9 | 10 | ## Documentation 11 | * online [documentation](https://daschr.github.io/pico-ssd1306/) 12 | * see `ssd1306.h` and example 13 | 14 | ## Example 15 | * set `PICO_SDK_PATH` 16 | * goto `example` 17 | * `mkdir build && cd build && cmake .. && make` 18 | * copy the `ssd1306-example.uf2` to your Pico 19 | -------------------------------------------------------------------------------- /ext/ssd1306/ssd1306.h: -------------------------------------------------------------------------------- 1 | /* 2 | MIT License 3 | 4 | Copyright (c) 2021 David Schramm 5 | 6 | Permission is hereby granted, free of charge, to any person obtaining a copy 7 | of this software and associated documentation files (the "Software"), to deal 8 | in the Software without restriction, including without limitation the rights 9 | to use, copy, modify, merge, publish, distribute, sublicense, and/or sell 10 | copies of the Software, and to permit persons to whom the Software is 11 | furnished to do so, subject to the following conditions: 12 | 13 | The above copyright notice and this permission notice shall be included in all 14 | copies or substantial portions of the Software. 15 | 16 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR 17 | IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, 18 | FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE 19 | AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER 20 | LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, 21 | OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE 22 | SOFTWARE. 23 | */ 24 | 25 | /** 26 | * @file ssd1306.h 27 | * 28 | * simple driver for ssd1306 displays 29 | */ 30 | 31 | #ifndef _inc_ssd1306 32 | #define _inc_ssd1306 33 | #include 34 | #include 35 | 36 | /** 37 | * @brief defines commands used in ssd1306 38 | */ 39 | typedef enum { 40 | SET_CONTRAST = 0x81, 41 | SET_ENTIRE_ON = 0xA4, 42 | SET_NORM_INV = 0xA6, 43 | SET_DISP = 0xAE, 44 | SET_MEM_ADDR = 0x20, 45 | SET_COL_ADDR = 0x21, 46 | SET_PAGE_ADDR = 0x22, 47 | SET_DISP_START_LINE = 0x40, 48 | SET_SEG_REMAP = 0xA0, 49 | SET_MUX_RATIO = 0xA8, 50 | SET_COM_OUT_DIR = 0xC0, 51 | SET_DISP_OFFSET = 0xD3, 52 | SET_COM_PIN_CFG = 0xDA, 53 | SET_DISP_CLK_DIV = 0xD5, 54 | SET_PRECHARGE = 0xD9, 55 | SET_VCOM_DESEL = 0xDB, 56 | SET_CHARGE_PUMP = 0x8D 57 | } ssd1306_command_t; 58 | 59 | /** 60 | * @brief holds the configuration 61 | */ 62 | typedef struct { 63 | uint8_t width; /**< width of display */ 64 | uint8_t height; /**< height of display */ 65 | uint8_t pages; /**< stores pages of display (calculated on initialization*/ 66 | uint8_t address; /**< i2c address of display*/ 67 | i2c_inst_t *i2c_i; /**< i2c connection instance */ 68 | bool external_vcc; /**< whether display uses external vcc */ 69 | uint16_t *buffer; /**< display buffer */ 70 | size_t bufsize; /**< buffer size */ 71 | } ssd1306_t; 72 | 73 | /** 74 | * @brief initialize display 75 | * 76 | * @param[in] p : pointer to instance of ssd1306_t 77 | * @param[in] width : width of display 78 | * @param[in] height : heigth of display 79 | * @param[in] address : i2c address of display 80 | * @param[in] i2c_instance : instance of i2c connection 81 | * 82 | * @return bool. 83 | * @retval true for Success 84 | * @retval false if initialization failed 85 | */ 86 | bool ssd1306_init(ssd1306_t *p, uint16_t width, uint16_t height, uint8_t address, i2c_inst_t *i2c_instance, uint8_t contrast, uint8_t vcomh, bool flipped); 87 | 88 | /** 89 | * @brief turn off display 90 | * 91 | * @param[in] p : instance of display 92 | * 93 | */ 94 | void ssd1306_poweroff(ssd1306_t *p); 95 | 96 | /** 97 | @brief turn on display 98 | 99 | @param[in] p : instance of display 100 | 101 | */ 102 | void ssd1306_poweron(ssd1306_t *p); 103 | 104 | /** 105 | @brief set contrast of display 106 | 107 | @param[in] p : instance of display 108 | @param[in] val : contrast 109 | 110 | */ 111 | void ssd1306_contrast(ssd1306_t *p, uint8_t val); 112 | 113 | /** 114 | @brief set VCOMH deselect level of display 115 | 116 | @param[in] p : instance of display 117 | @param[in] val : vcomh 118 | 119 | */ 120 | void ssd1306_set_vcomh(ssd1306_t *p, uint8_t val); 121 | 122 | /** 123 | @brief flip screen 124 | @param[in] p : instance of display 125 | @param[in] flip : flip screen 126 | */ 127 | void ssd1306_flip_display(ssd1306_t *p, bool flip); 128 | 129 | /** 130 | @brief display buffer, should be called on change 131 | 132 | @param[in] p : instance of display 133 | 134 | */ 135 | void ssd1306_show(ssd1306_t *p); 136 | 137 | /** 138 | @brief clear display buffer 139 | 140 | @param[in] p : instance of display 141 | 142 | */ 143 | void ssd1306_clear(ssd1306_t *p); 144 | 145 | /** 146 | @brief draw pixel on buffer 147 | 148 | @param[in] p : instance of display 149 | @param[in] x : x position 150 | @param[in] y : y position 151 | */ 152 | void ssd1306_draw_pixel(ssd1306_t *p, uint32_t x, uint32_t y); 153 | 154 | #endif 155 | -------------------------------------------------------------------------------- /misc/variants.cmake: -------------------------------------------------------------------------------- 1 | 2 | # Variants... 3 | set(VARIANT "SD2PSX" CACHE STRING "Firmware variant to build") 4 | set_property(CACHE VARIANT PROPERTY STRINGS 5 | "SD2PSX" 6 | "PMC+" 7 | "PSXMemCard" 8 | "SD2PSXlite" 9 | "PMCZero" 10 | "Other") 11 | message(STATUS "Building for ${VARIANT}") 12 | if (VARIANT STREQUAL "SD2PSX") 13 | set(PIN_PSX_ACK 16) 14 | set(PIN_PSX_SEL 17) 15 | set(PIN_PSX_CLK 18) 16 | set(PIN_PSX_CMD 19) 17 | set(PIN_PSX_DAT 20) 18 | set(PIN_PSX_SPD_SEL 10) 19 | add_compile_definitions("UART_TX=8" 20 | "UART_RX=9" 21 | "UART_PERIPH=uart1" 22 | "UART_BAUD=3000000" 23 | "SD_PERIPH=SPI1" 24 | "SD_MISO=24" 25 | "SD_MOSI=27" 26 | "SD_SCK=26" 27 | "SD_CS=29" 28 | "FLASH_OFF_CIV=0x7fb000" 29 | "FLASH_OFF_EEPROM=0x7fc000" 30 | "MMCE_PRODUCT_ID=0x1" 31 | ) 32 | set(SD2PSX_WITH_GUI TRUE) 33 | set(SD2PSX_WITH_PSRAM TRUE) 34 | set(SD2PSX_WITH_LED FALSE) 35 | add_compile_definitions(PICO_FLASH_SIZE_BYTES=16777216) 36 | elseif( VARIANT STREQUAL "PMC+") 37 | set(PIN_PSX_ACK 9) 38 | set(PIN_PSX_SEL 7) 39 | set(PIN_PSX_CLK 8) 40 | set(PIN_PSX_CMD 6) 41 | set(PIN_PSX_DAT 5) 42 | set(PIN_PSX_SPD_SEL 10) 43 | add_compile_definitions("UART_TX=0" 44 | "UART_RX=1" 45 | "UART_PERIPH=uart1" 46 | "UART_BAUD=115200" 47 | "SD_PERIPH=SPI" 48 | "SD_MISO=16" 49 | "SD_MOSI=19" 50 | "SD_SCK=18" 51 | "SD_CS=17" 52 | "FLASH_OFF_CIV=0x1fb000" 53 | "FLASH_OFF_EEPROM=0x1fc000" 54 | "MMCE_PRODUCT_ID=0x3" 55 | "PMC_BUTTONS=1" 56 | "PIN_BTN_LEFT=27" 57 | "PIN_BTN_RIGHT=28" 58 | "PIN_BTN_BOOT=26" 59 | ) 60 | set(SD2PSX_WITH_GUI FALSE) 61 | set(SD2PSX_WITH_PSRAM FALSE) 62 | set(SD2PSX_WITH_LED FALSE) 63 | add_compile_definitions(PICO_FLASH_SIZE_BYTES=2097152) 64 | elseif( VARIANT STREQUAL "PSXMemCard") 65 | set(PIN_PSX_ACK 9) 66 | set(PIN_PSX_SEL 7) 67 | set(PIN_PSX_CLK 8) 68 | set(PIN_PSX_CMD 6) 69 | set(PIN_PSX_DAT 5) 70 | set(PIN_PSX_SPD_SEL 10) 71 | add_compile_definitions("UART_TX=0" 72 | "UART_RX=1" 73 | "UART_PERIPH=uart1" 74 | "UART_BAUD=115200" 75 | "SD_PERIPH=SPI" 76 | "SD_MISO=16" 77 | "SD_MOSI=19" 78 | "SD_SCK=18" 79 | "SD_CS=17" 80 | "FLASH_OFF_CIV=0x1fb000" 81 | "FLASH_OFF_EEPROM=0x1fc000" 82 | "MMCE_PRODUCT_ID=0x3" 83 | ) 84 | set(SD2PSX_WITH_GUI FALSE) 85 | set(SD2PSX_WITH_PSRAM FALSE) 86 | set(SD2PSX_WITH_LED TRUE) 87 | add_compile_definitions(PICO_FLASH_SIZE_BYTES=2097152) 88 | elseif( VARIANT STREQUAL "PMCZero") 89 | set(PIN_PSX_ACK 13) 90 | set(PIN_PSX_SEL 11) 91 | set(PIN_PSX_CLK 12) 92 | set(PIN_PSX_CMD 10) 93 | set(PIN_PSX_DAT 9) 94 | set(PIN_PSX_SPD_SEL 11) 95 | add_compile_definitions("UART_TX=7" 96 | "UART_RX=8" 97 | "UART_PERIPH=uart1" 98 | "UART_BAUD=115200" 99 | "SD_PERIPH=SPI" 100 | "SD_MISO=0" 101 | "SD_MOSI=3" 102 | "SD_SCK=2" 103 | "SD_CS=1" 104 | "FLASH_OFF_CIV=0x1fb000" 105 | "FLASH_OFF_EEPROM=0x1fc000" 106 | "MMCE_PRODUCT_ID=0x4" 107 | "PMC_BUTTONS=1" 108 | "PIN_BTN_LEFT=27" 109 | "PIN_BTN_RIGHT=28" 110 | "PIN_BTN_BOOT=26" 111 | "WS2812=1" 112 | ) 113 | set(SD2PSX_WITH_GUI FALSE) 114 | set(SD2PSX_WITH_PSRAM FALSE) 115 | set(SD2PSX_WITH_LED TRUE) 116 | add_compile_definitions(PICO_FLASH_SIZE_BYTES=2097152) 117 | elseif( VARIANT STREQUAL "SD2PSXlite") 118 | set(PIN_PSX_ACK 16) 119 | set(PIN_PSX_SEL 17) 120 | set(PIN_PSX_CLK 18) 121 | set(PIN_PSX_CMD 19) 122 | set(PIN_PSX_DAT 20) 123 | set(PIN_PSX_SPD_SEL 10) 124 | add_compile_definitions("UART_TX=8" 125 | "UART_RX=9" 126 | "UART_PERIPH=uart1" 127 | "UART_BAUD=3000000" 128 | "SD_PERIPH=SPI1" 129 | "SD_MISO=24" 130 | "SD_MOSI=27" 131 | "SD_SCK=26" 132 | "SD_CS=29" 133 | "FLASH_OFF_CIV=0x1fb000" 134 | "FLASH_OFF_EEPROM=0x1fc000" 135 | "MMCE_PRODUCT_ID=0x1" 136 | PARENT_DIRECTORY) 137 | set(SD2PSX_WITH_GUI FALSE) 138 | set(SD2PSX_WITH_PSRAM FALSE) 139 | set(SD2PSX_WITH_LED FALSE) 140 | add_compile_definitions(PICO_FLASH_SIZE_BYTES=16777216) 141 | else() 142 | set(PIN_PSX_ACK 16) 143 | set(PIN_PSX_SEL 17) 144 | set(PIN_PSX_CLK 18) 145 | set(PIN_PSX_CMD 19) 146 | set(PIN_PSX_DAT 20) 147 | set(PIN_PSX_SPD_SEL 10) 148 | endif() 149 | 150 | -------------------------------------------------------------------------------- /pico_sdk_import.cmake: -------------------------------------------------------------------------------- 1 | # This is a copy of /external/pico_sdk_import.cmake 2 | 3 | # This can be dropped into an external project to help locate this SDK 4 | # It should be include()ed prior to project() 5 | 6 | if (DEFINED ENV{PICO_SDK_PATH} AND (NOT PICO_SDK_PATH)) 7 | set(PICO_SDK_PATH $ENV{PICO_SDK_PATH}) 8 | message("Using PICO_SDK_PATH from environment ('${PICO_SDK_PATH}')") 9 | endif () 10 | 11 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT} AND (NOT PICO_SDK_FETCH_FROM_GIT)) 12 | set(PICO_SDK_FETCH_FROM_GIT $ENV{PICO_SDK_FETCH_FROM_GIT}) 13 | message("Using PICO_SDK_FETCH_FROM_GIT from environment ('${PICO_SDK_FETCH_FROM_GIT}')") 14 | endif () 15 | 16 | if (DEFINED ENV{PICO_SDK_FETCH_FROM_GIT_PATH} AND (NOT PICO_SDK_FETCH_FROM_GIT_PATH)) 17 | set(PICO_SDK_FETCH_FROM_GIT_PATH $ENV{PICO_SDK_FETCH_FROM_GIT_PATH}) 18 | message("Using PICO_SDK_FETCH_FROM_GIT_PATH from environment ('${PICO_SDK_FETCH_FROM_GIT_PATH}')") 19 | endif () 20 | 21 | set(PICO_SDK_PATH "${PICO_SDK_PATH}" CACHE PATH "Path to the Raspberry Pi Pico SDK") 22 | set(PICO_SDK_FETCH_FROM_GIT "${PICO_SDK_FETCH_FROM_GIT}" CACHE BOOL "Set to ON to fetch copy of SDK from git if not otherwise locatable") 23 | set(PICO_SDK_FETCH_FROM_GIT_PATH "${PICO_SDK_FETCH_FROM_GIT_PATH}" CACHE FILEPATH "location to download SDK") 24 | 25 | if (NOT PICO_SDK_PATH) 26 | if (PICO_SDK_FETCH_FROM_GIT) 27 | include(FetchContent) 28 | set(FETCHCONTENT_BASE_DIR_SAVE ${FETCHCONTENT_BASE_DIR}) 29 | if (PICO_SDK_FETCH_FROM_GIT_PATH) 30 | get_filename_component(FETCHCONTENT_BASE_DIR "${PICO_SDK_FETCH_FROM_GIT_PATH}" REALPATH BASE_DIR "${CMAKE_SOURCE_DIR}") 31 | endif () 32 | # GIT_SUBMODULES_RECURSE was added in 3.17 33 | if (${CMAKE_VERSION} VERSION_GREATER_EQUAL "3.17.0") 34 | FetchContent_Declare( 35 | pico_sdk 36 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 37 | GIT_TAG master 38 | GIT_SUBMODULES_RECURSE FALSE 39 | ) 40 | else () 41 | FetchContent_Declare( 42 | pico_sdk 43 | GIT_REPOSITORY https://github.com/raspberrypi/pico-sdk 44 | GIT_TAG master 45 | ) 46 | endif () 47 | 48 | if (NOT pico_sdk) 49 | message("Downloading Raspberry Pi Pico SDK") 50 | FetchContent_Populate(pico_sdk) 51 | set(PICO_SDK_PATH ${pico_sdk_SOURCE_DIR}) 52 | endif () 53 | set(FETCHCONTENT_BASE_DIR ${FETCHCONTENT_BASE_DIR_SAVE}) 54 | else () 55 | message(FATAL_ERROR 56 | "SDK location was not specified. Please set PICO_SDK_PATH or set PICO_SDK_FETCH_FROM_GIT to on to fetch from git." 57 | ) 58 | endif () 59 | endif () 60 | 61 | get_filename_component(PICO_SDK_PATH "${PICO_SDK_PATH}" REALPATH BASE_DIR "${CMAKE_BINARY_DIR}") 62 | if (NOT EXISTS ${PICO_SDK_PATH}) 63 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' not found") 64 | endif () 65 | 66 | set(PICO_SDK_INIT_CMAKE_FILE ${PICO_SDK_PATH}/pico_sdk_init.cmake) 67 | if (NOT EXISTS ${PICO_SDK_INIT_CMAKE_FILE}) 68 | message(FATAL_ERROR "Directory '${PICO_SDK_PATH}' does not appear to contain the Raspberry Pi Pico SDK") 69 | endif () 70 | 71 | set(PICO_SDK_PATH ${PICO_SDK_PATH} CACHE PATH "Path to the Raspberry Pi Pico SDK" FORCE) 72 | 73 | include(${PICO_SDK_INIT_CMAKE_FILE}) 74 | -------------------------------------------------------------------------------- /src/bigmem.c: -------------------------------------------------------------------------------- 1 | #include "bigmem.h" 2 | 3 | bigmem_t bigmem; 4 | uint8_t cache[CACHE_SIZE]; -------------------------------------------------------------------------------- /src/bigmem.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #if WITH_PSRAM 5 | #define CACHE_SIZE 512 * 45 6 | #else 7 | #define CACHE_SIZE 1024 * 128 8 | #endif 9 | typedef union { 10 | struct { 11 | uint16_t dirty_heap[1024]; 12 | uint8_t dirty_map[1024]; /* every 128 byte block */ 13 | } ps1; 14 | #ifdef WITH_PSRAM 15 | struct { 16 | uint16_t dirty_heap[8 * 1024 * 1024 / 512]; 17 | uint8_t dirty_map[8 * 1024 * 1024 / 512 / 8]; 18 | } ps2; 19 | #endif 20 | 21 | } bigmem_t; 22 | 23 | 24 | extern bigmem_t bigmem; 25 | extern uint8_t cache[CACHE_SIZE]; 26 | -------------------------------------------------------------------------------- /src/card_config.h: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | 4 | #define MAX_FOLDER_NAME_LENGTH ( 32 ) 5 | 6 | void card_config_read_channel_name(const char* card_folder, const char* card_base, const char* channel_number, char* name, size_t name_max_len); 7 | uint8_t card_config_get_max_channels(const char* card_folder, const char* card_base); 8 | uint8_t card_config_get_ps2_cardsize(const char* card_folder, const char* card_base); 9 | void card_config_get_card_folder(const char* game_id, char* card_folder, size_t card_folder_max_len); 10 | -------------------------------------------------------------------------------- /src/config.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #ifndef PIN_BTN_LEFT 4 | #define PIN_BTN_LEFT 23 5 | #endif 6 | #ifndef PIN_BTN_RIGHT 7 | #define PIN_BTN_RIGHT 21 8 | #endif 9 | #ifndef PIN_BTN_BOOT 10 | #define PIN_BTN_BOOT 11 11 | #endif 12 | 13 | // SD2PSX 14 | #ifndef UART_TX 15 | #define UART_TX 8 16 | #endif 17 | #ifndef UART_RX 18 | #define UART_RX 9 19 | #endif 20 | #ifndef UART_PERIPH 21 | #define UART_PERIPH uart1 22 | #endif 23 | #ifndef UART_BAUD 24 | #define UART_BAUD 3000000 25 | #endif 26 | 27 | 28 | #define OLED_I2C_SDA 28 29 | #define OLED_I2C_SCL 25 30 | #define OLED_I2C_ADDR 0x3C 31 | #define OLED_I2C_PERIPH i2c0 32 | #define OLED_I2C_CLOCK 400000 33 | 34 | #define PSRAM_CS 2 35 | #define PSRAM_CLK 3 36 | #define PSRAM_DAT 4 /* IO0-IO3 must be sequential! */ 37 | #define PSRAM_CLKDIV 2 38 | 39 | // SD2PSX 40 | #ifndef SD_PERIPH 41 | #define SD_PERIPH SPI1 42 | #endif 43 | #ifndef SD_MISO 44 | #define SD_MISO 24 45 | #endif 46 | #ifndef SD_MOSI 47 | #define SD_MOSI 27 48 | #endif 49 | #ifndef SD_SCK 50 | #define SD_SCK 26 51 | #endif 52 | #ifndef SD_CS 53 | #define SD_CS 29 54 | #endif 55 | 56 | #define SD_BAUD 45 * 1000000 57 | 58 | #define DISPLAY_WIDTH 128 59 | #define DISPLAY_HEIGHT 64 60 | 61 | #define DEBOUNCE_MS 5 62 | 63 | #ifndef MMCE_PRODUCT_ID 64 | #define MMCE_PRODUCT_ID 0x1 65 | #endif 66 | 67 | #ifndef MMCE_REVISION 68 | #define MMCE_REVISION 0x1 69 | #endif 70 | 71 | #ifndef MMCE_PROTOCOL_VER 72 | #define MMCE_PROTOCOL_VER 0x1 73 | #endif 74 | -------------------------------------------------------------------------------- /src/debug.c: -------------------------------------------------------------------------------- 1 | #include "debug.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #if WITH_LED 8 | #include "led.h" 9 | #endif 10 | 11 | #if WITH_GUI 12 | #include "oled.h" 13 | #endif 14 | #include "pico/platform.h" 15 | 16 | const char *log_level_str[] = { 17 | " ", 18 | "[ERROR]", 19 | "[WARN] ", 20 | "[INFO] ", 21 | "[TRACE]" 22 | }; 23 | 24 | static char debug_queue[1024]; 25 | static size_t debug_read_pos, debug_write_pos; 26 | 27 | void debug_put(char c) { 28 | debug_queue[debug_write_pos] = c; 29 | debug_write_pos = (debug_write_pos + 1) % sizeof(debug_queue); 30 | } 31 | 32 | char debug_get(void) { 33 | char ret = debug_queue[debug_read_pos]; 34 | if (ret) { 35 | debug_queue[debug_read_pos] = 0; 36 | debug_read_pos = (debug_read_pos + 1) % sizeof(debug_queue); 37 | } 38 | return ret; 39 | } 40 | 41 | void __time_critical_func(buffered_printf)(const char *format, ...) { 42 | char buf[128]; 43 | 44 | va_list args; 45 | va_start(args, format); 46 | vsnprintf(buf, sizeof(buf), format, args); 47 | va_end(args); 48 | 49 | for (char *c = buf; *c; ++c) 50 | debug_put(*c); 51 | } 52 | 53 | void fatal(int err, const char *format, ...) { 54 | char buf[128]; 55 | 56 | va_list args; 57 | va_start(args, format); 58 | vsnprintf(buf, sizeof(buf), format, args); 59 | va_end(args); 60 | 61 | printf("%s\n", buf); 62 | #if WITH_GUI 63 | static int fatal_reentry; 64 | if (!fatal_reentry) { 65 | fatal_reentry = 1; 66 | oled_init(); 67 | oled_clear(); 68 | oled_draw_text("FATAL ERROR\n\n"); 69 | oled_draw_text(buf); 70 | oled_show(); 71 | } 72 | #endif 73 | while (true) { 74 | #if WITH_LED 75 | for (int i = 0; i < err; i++) { 76 | led_fatal(); 77 | sleep_ms(250); 78 | led_clear(); 79 | sleep_ms(250); 80 | } 81 | sleep_ms(1000); 82 | #endif 83 | } 84 | 85 | } 86 | 87 | void hexdump(const uint8_t *buf, size_t sz) { 88 | for (size_t i = 0; i < sz; ++i) 89 | printf("%02X ", buf[i]); 90 | printf("\n"); 91 | } 92 | -------------------------------------------------------------------------------- /src/debug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | #include 7 | 8 | #define LOG_LEVEL_MC_DATA 2 9 | #define LOG_LEVEL_MMCEMAN 2 10 | #define LOG_LEVEL_MMCEMAN_FS 2 11 | #define LOG_LEVEL_MC_AUTH 2 12 | #define LOG_LEVEL_PS2_CM 2 13 | #define LOG_LEVEL_PS2_MAIN 2 14 | #define LOG_LEVEL_PS2_MC 2 15 | #define LOG_LEVEL_PS2_HT 2 16 | #define LOG_LEVEL_PS2_S2M 2 17 | #define LOG_LEVEL_GUI 2 18 | #define LOG_LEVEL_CARD_CONF 2 19 | #define LOG_LEVEL_PS1_MC 2 20 | #define LOG_LEVEL_PS1_CM 2 21 | #define LOG_LEVEL_PS1_MMCE 2 22 | 23 | #define LOG_ERROR 1 24 | #define LOG_WARN 2 25 | #define LOG_INFO 3 26 | #define LOG_TRACE 4 27 | 28 | #define ERR_SDCARD 0x01 29 | #define ERR_CARDMAN 0x02 30 | #define ERR_PSRAM 0x03 31 | #define ERR_SETTINGS 0x04 32 | #define ERR_CIV 0x05 33 | #define ERR_MC_DATA 0x06 34 | #define ERR_MC_AUTH_UNK 0x07 35 | 36 | extern const char *log_level_str[]; 37 | 38 | #ifdef DEBUG_USB_UART 39 | #define LOG_PRINT(file_level, level, fmt, x...) \ 40 | do { \ 41 | if (level <= file_level) { \ 42 | printf("%s C%i: "fmt, log_level_str[level], get_core_num(), ##x); \ 43 | } \ 44 | } while (0); 45 | #else 46 | #define LOG_PRINT(file_level, level, fmt, x...) 47 | 48 | #endif 49 | 50 | #define TIME_PROFILE(f) do {\ 51 | uint64_t time = time_us_64();\ 52 | f();\ 53 | printf("%s took %u us\n", #f, (uint32_t)(time_us_64() - time));\ 54 | } while (0); 55 | 56 | #ifdef DEBUG_USB_UART 57 | #define DPRINTF(fmt, x...) buffered_printf(fmt, ##x) 58 | #define QPRINTF(fmt, x...) printf(fmt, ##x) 59 | #else 60 | #define DPRINTF(fmt, x...) 61 | #define QPRINTF(fmt, x...) 62 | #endif 63 | 64 | #define DPRINTFFLT() QPRINTF("%s:%u - %lu\n", __func__, __LINE__, (uint32_t)time_us_64()/1000U) 65 | 66 | 67 | void debug_put(char c); 68 | char debug_get(void); 69 | void buffered_printf(const char *format, ...); 70 | void fatal(int err, const char *format, ...); 71 | void hexdump(const uint8_t *buf, size_t sz); 72 | -------------------------------------------------------------------------------- /src/des.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file des.h 3 | * @brief DES (Data Encryption Standard) 4 | * 5 | * @section License 6 | * 7 | * SPDX-License-Identifier: GPL-2.0-or-later 8 | * 9 | * Copyright (C) 2010-2021 Oryx Embedded SARL. All rights reserved. 10 | * 11 | * This file is part of CycloneCRYPTO Open. 12 | * 13 | * This program is free software; you can redistribute it and/or 14 | * modify it under the terms of the GNU General Public License 15 | * as published by the Free Software Foundation; either version 2 16 | * of the License, or (at your option) any later version. 17 | * 18 | * This program is distributed in the hope that it will be useful, 19 | * but WITHOUT ANY WARRANTY; without even the implied warranty of 20 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the 21 | * GNU General Public License for more details. 22 | * 23 | * You should have received a copy of the GNU General Public License 24 | * along with this program; if not, write to the Free Software Foundation, 25 | * Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. 26 | * 27 | * @author Oryx Embedded SARL (www.oryx-embedded.com) 28 | * @version 2.0.2 29 | **/ 30 | 31 | #ifndef _DES_H 32 | #define _DES_H 33 | 34 | #include 35 | 36 | //DES block size 37 | #define DES_BLOCK_SIZE 8 38 | 39 | //C++ guard 40 | #ifdef __cplusplus 41 | extern "C" { 42 | #endif 43 | 44 | 45 | /** 46 | * @brief DES algorithm context 47 | **/ 48 | 49 | typedef struct 50 | { 51 | uint32_t ks[32]; 52 | } DesContext; 53 | 54 | 55 | //DES related functions 56 | void desInit(DesContext *context, const uint8_t *key, uint32_t keyLen); 57 | void desEncryptBlock(DesContext *context, const uint8_t *input, uint8_t *output); 58 | void desDecryptBlock(DesContext *context, const uint8_t *input, uint8_t *output); 59 | 60 | //C++ guard 61 | #ifdef __cplusplus 62 | } 63 | #endif 64 | 65 | #endif 66 | -------------------------------------------------------------------------------- /src/flashmap.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | /* application, including rp2040 bootloader */ 4 | #define FLASH_OFF_APP (0x0) 5 | 6 | /* 4k before eeprom starts */ 7 | // SD2PSX 8 | #ifndef FLASH_OFF_CIV 9 | #define FLASH_OFF_CIV (0x7fb000) 10 | #endif 11 | // BitFunX 12 | //#define FLASH_OFF_CIV (0x1fb000) 13 | 14 | /* 16k space before 8MB boundary */ 15 | // SD2PSX 16 | #ifndef FLASH_OFF_EEPROM 17 | #define FLASH_OFF_EEPROM (0x7fc000) 18 | #endif 19 | // BitFunX 20 | //#define FLASH_OFF_EEPROM (0x1fc000) 21 | 22 | /* at the 8MB boundary */ 23 | #define FLASH_OFF_PS2EXP (0x800000) 24 | -------------------------------------------------------------------------------- /src/game_db/game_db.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | #define MAX_GAME_ID_LENGTH (16) 9 | 10 | void game_db_extract_title_id(const uint8_t* const in_title_id, char* const out_title_id, const size_t in_title_id_length, const size_t out_buffer_size); 11 | bool game_db_sanity_check_title_id(const char* const title_id); 12 | 13 | 14 | void game_db_get_current_name(char* const game_name); 15 | int game_db_get_current_parent(char* const parent_id); 16 | int game_db_update_game(const char* const game_id); 17 | int game_db_update_arcade(const char* const game_id); 18 | void game_db_get_game_name(const char* game_id, char* game_name); 19 | 20 | void game_db_init(void); -------------------------------------------------------------------------------- /src/gui.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "lvgl.h" 4 | #include "ui_menu.h" 5 | 6 | void gui_init(void); 7 | void gui_task(void); 8 | void gui_request_refresh(void); 9 | void gui_do_ps1_card_switch(void); 10 | void gui_do_ps2_card_switch(void); 11 | 12 | void evt_menu_page(lv_event_t *event); 13 | 14 | #define UI_GOTO_SCREEN(scr) do { \ 15 | lv_scr_load(scr); \ 16 | lv_group_focus_obj(scr); \ 17 | } while (0); 18 | -------------------------------------------------------------------------------- /src/input.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #if WITH_GUI 4 | #include "lvgl.h" 5 | #endif 6 | 7 | void input_flip(void); 8 | void input_init(void); 9 | void input_task(void); 10 | #if WITH_GUI 11 | void input_update_display(lv_obj_t *line); 12 | #endif 13 | int input_get_pressed(void); 14 | int input_is_down_raw(int idx); 15 | void input_flush(void); 16 | int input_is_any_down(void); 17 | 18 | enum { 19 | INPUT_KEY_NEXT = 50, 20 | INPUT_KEY_PREV = 51, 21 | INPUT_KEY_ENTER = 52, 22 | INPUT_KEY_BACK = 53, 23 | INPUT_KEY_MENU = 54, 24 | INPUT_KEY_BOOT = 55 25 | }; 26 | -------------------------------------------------------------------------------- /src/keystore.c: -------------------------------------------------------------------------------- 1 | #include "keystore.h" 2 | 3 | #include 4 | #include 5 | #include 6 | #include "debug.h" 7 | #include "hardware/regs/addressmap.h" 8 | #include "hardware/flash.h" 9 | #include "pico/multicore.h" 10 | 11 | #include "flashmap.h" 12 | #include "pico/platform.h" 13 | #include "sd.h" 14 | #include "settings.h" 15 | 16 | uint8_t ps2_civ[8]; 17 | const char civ_path[] = "civ.bin"; 18 | const char civ_path_backup[] = ".sd2psx/civ.bin"; 19 | int ps2_magicgate; 20 | static bool is_confirmed = false; 21 | 22 | 23 | void __not_in_flash_func(keystore_backup)(void) { 24 | if (!sd_exists(civ_path_backup)) { 25 | int fd = sd_open(civ_path_backup, O_CREAT | O_WRONLY); 26 | sd_write(fd, ps2_civ, 8); 27 | sd_close(fd); 28 | } 29 | } 30 | 31 | void keystore_init(void) { 32 | keystore_read(); 33 | if (ps2_magicgate == 0) { 34 | printf("Deploying keys...\n"); 35 | keystore_deploy(); 36 | #if WITH_GUI==0 37 | if (!ps2_magicgate) 38 | fatal(ERR_CIV, "Cannot find civ!\n"); 39 | #endif 40 | } 41 | } 42 | 43 | void keystore_read(void) { 44 | uint8_t buf[17]; 45 | memcpy(buf, (uint8_t*)XIP_BASE + FLASH_OFF_CIV, sizeof(buf)); 46 | for (int i = 0; i < 8; ++i) { 47 | uint8_t chk = ~buf[i + 8]; 48 | if (buf[i] != chk) { 49 | printf("keystore - No CIV key deployed\n"); 50 | return; 51 | } 52 | } 53 | 54 | printf("keystore - Found valid CIV : %02X %02X ... - activating magicgate\n", buf[0], buf[1]); 55 | printf("keystore - MagicGate is %s\n", (buf[16] == 0x01) ? "confirmed" : "unconfirmed"); 56 | memcpy(ps2_civ, buf, sizeof(ps2_civ)); 57 | is_confirmed = (buf[16] == 0x01); 58 | ps2_magicgate = 1; 59 | } 60 | 61 | char *keystore_error(int rc) { 62 | switch (rc) { 63 | case 0: 64 | return "No error"; 65 | case KEYSTORE_DEPLOY_NOFILE: 66 | return "civ.bin not\nfound\n"; 67 | case KEYSTORE_DEPLOY_OPEN: 68 | return "Cannot open\nciv.bin\n"; 69 | case KEYSTORE_DEPLOY_READ: 70 | return "Cannot read\nciv.bin\n"; 71 | default: 72 | return "Unknown error"; 73 | } 74 | } 75 | 76 | int __not_in_flash_func(keystore_deploy)(void) { 77 | uint8_t civbuf[8] = { 0 }; 78 | uint8_t chkbuf[256] = { 0 }; 79 | const char* path; 80 | 81 | sd_init(); 82 | 83 | if (sd_exists(civ_path)) 84 | path = civ_path; 85 | else if (sd_exists(civ_path_backup)) 86 | path = civ_path_backup; 87 | else 88 | return KEYSTORE_DEPLOY_NOFILE; 89 | 90 | int fd = sd_open(path, O_RDONLY); 91 | if (fd < 0) 92 | return KEYSTORE_DEPLOY_OPEN; 93 | 94 | if (sd_read(fd, civbuf, sizeof(civbuf)) != sizeof(civbuf)) { 95 | sd_close(fd); 96 | return KEYSTORE_DEPLOY_READ; 97 | } 98 | sd_close(fd); 99 | 100 | memcpy(chkbuf, civbuf, sizeof(civbuf)); 101 | for (int i = 0; i < 8; ++i) 102 | chkbuf[i + 8] = ~chkbuf[i]; 103 | chkbuf[16] = 0x00; // confirmation byte 104 | 105 | if (memcmp(chkbuf, (uint8_t*)XIP_BASE + FLASH_OFF_CIV, sizeof(chkbuf)) != 0) { 106 | if (multicore_lockout_victim_is_initialized(1)) 107 | multicore_lockout_start_blocking(); 108 | flash_range_erase(FLASH_OFF_CIV, 4096); 109 | flash_range_program(FLASH_OFF_CIV, chkbuf, sizeof(chkbuf)); 110 | if (multicore_lockout_victim_is_initialized(1)) 111 | multicore_lockout_end_blocking(); 112 | } else { 113 | printf("keystore - skipping CIV flash because data is unchanged\n"); 114 | } 115 | 116 | sd_remove(path); 117 | 118 | keystore_read(); 119 | 120 | keystore_backup(); 121 | 122 | return 0; 123 | } 124 | 125 | void __not_in_flash_func(keystore_confirm)(void) { 126 | if (!is_confirmed) { 127 | printf("keystore - Confirming CIV\n"); 128 | uint8_t chkbuf[256] = { 0 }; 129 | memcpy(chkbuf, ps2_civ, sizeof(ps2_civ)); 130 | for (int i = 0; i < 8; ++i) 131 | chkbuf[i + 8] = ~chkbuf[i]; 132 | chkbuf[16] = 0x01; // confirmation byte 133 | if (multicore_lockout_victim_is_initialized(1)) 134 | multicore_lockout_start_blocking(); 135 | uint32_t ints = save_and_disable_interrupts(); 136 | flash_range_erase(FLASH_OFF_CIV, 4096); 137 | flash_range_program(FLASH_OFF_CIV, chkbuf, sizeof(chkbuf)); 138 | restore_interrupts (ints); 139 | if (multicore_lockout_victim_is_initialized(1)) 140 | multicore_lockout_end_blocking(); 141 | is_confirmed = true; 142 | } 143 | } 144 | 145 | 146 | void __not_in_flash_func(keystore_reset)(void) { 147 | if (!is_confirmed) { 148 | printf("keystore - Resetting unconfirmed CIV\n"); 149 | uint8_t chkbuf[256] = { 0 }; 150 | printf("keystore - Resetting CIV\n"); 151 | ps2_magicgate = 0; 152 | 153 | if (multicore_lockout_victim_is_initialized(1)) 154 | multicore_lockout_start_blocking(); 155 | uint32_t ints = save_and_disable_interrupts(); 156 | flash_range_erase(FLASH_OFF_CIV, 4096); 157 | flash_range_program(FLASH_OFF_CIV, chkbuf, sizeof(chkbuf)); 158 | restore_interrupts (ints); 159 | if (multicore_lockout_victim_is_initialized(1)) 160 | multicore_lockout_end_blocking(); 161 | if (sd_exists(civ_path_backup)) 162 | sd_remove(civ_path_backup); 163 | keystore_read(); 164 | } 165 | } 166 | -------------------------------------------------------------------------------- /src/keystore.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | void keystore_init(void); 6 | void keystore_read(void); 7 | char *keystore_error(int rc); 8 | int keystore_deploy(void); 9 | void keystore_reset(void); 10 | void keystore_confirm(void); 11 | 12 | enum { 13 | KEYSTORE_DEPLOY_NOFILE = 1, 14 | KEYSTORE_DEPLOY_OPEN, 15 | KEYSTORE_DEPLOY_READ, 16 | }; 17 | 18 | extern uint8_t ps2_civ[8]; 19 | extern int ps2_magicgate; 20 | -------------------------------------------------------------------------------- /src/led/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(sd2psx_led STATIC 3 | ${CMAKE_CURRENT_SOURCE_DIR}/led.c 4 | ) 5 | 6 | target_include_directories(sd2psx_led 7 | PUBLIC 8 | ${CMAKE_CURRENT_SOURCE_DIR} 9 | PRIVATE 10 | ) 11 | 12 | target_link_libraries(sd2psx_led 13 | PRIVATE 14 | sd2psx_common 15 | hardware_gpio 16 | hardware_pio 17 | ps1_card 18 | ps2_card 19 | ) 20 | 21 | pico_generate_pio_header(sd2psx_led ${CMAKE_CURRENT_SOURCE_DIR}/ws2812.pio) 22 | 23 | 24 | target_compile_definitions(sd2psx_led PUBLIC "WITH_LED=1") -------------------------------------------------------------------------------- /src/led/led.c: -------------------------------------------------------------------------------- 1 | #include "led.h" 2 | #include 3 | #include 4 | #include "hardware/gpio.h" 5 | #include "hardware/timer.h" 6 | #include "ps1/ps1_mc_data_interface.h" 7 | #include "ps2/card_emu/ps2_mc_data_interface.h" 8 | #include "ps1/ps1_dirty.h" 9 | #include "ps2/mmceman/ps2_mmceman_fs.h" 10 | #include "ps2/ps2_cardman.h" 11 | #include "ps1/ps1_cardman.h" 12 | #include "settings.h" 13 | #include "ws2812.pio.h" 14 | 15 | #define LED_REFRESH_US (250 * 1000) // 250 ms 16 | 17 | #ifdef WS2812 18 | #define WS2812_PIN (16U) 19 | uint offsetWs2813; 20 | uint smWs2813; 21 | 22 | void ws2812_put_pixel(uint32_t pixel_grb) { 23 | sleep_ms(1); // delay to ensure LED latch will hold data 24 | pio_sm_put(pio1, smWs2813, pixel_grb << 8u); 25 | } 26 | 27 | void ws2812_put_rgb(uint8_t red, uint8_t green, uint8_t blue) { 28 | uint32_t mask = (green << 16) | (red << 8) | (blue << 0); 29 | ws2812_put_pixel(mask); 30 | } 31 | #else 32 | #define LED_R (25U) 33 | #define LED_G (24U) 34 | #define LED_B (23U) 35 | #endif 36 | 37 | static uint64_t last_refresh = 0; 38 | 39 | void led_init(void) { 40 | #if WS2812 41 | offsetWs2813 = pio_add_program(pio1, &ws2812_program); 42 | smWs2813 = pio_claim_unused_sm(pio1, true); 43 | ws2812_program_init(pio1, smWs2813, offsetWs2813, 16, 800000, true); 44 | #else 45 | gpio_init(LED_R); 46 | gpio_init(LED_G); 47 | gpio_init(LED_B); 48 | gpio_set_dir(LED_R, true); 49 | gpio_set_dir(LED_G, true); 50 | gpio_set_dir(LED_B, true); 51 | #endif 52 | } 53 | 54 | void led_fatal(void) { 55 | #if WS2812 56 | ws2812_put_rgb(0xFF, 0x00, 0x00); 57 | #else 58 | gpio_put(LED_R, true); 59 | #endif 60 | } 61 | 62 | void led_clear(void) { 63 | #if WS2812 64 | ws2812_put_rgb(0x00, 0x00, 0x00); 65 | #else 66 | gpio_put(LED_R, false); 67 | gpio_put(LED_G, false); 68 | gpio_put(LED_B, false); 69 | #endif 70 | } 71 | 72 | void led_task(void) { 73 | uint64_t time = time_us_64(); 74 | static bool red_active = false, green_active = false, blue_active = false; 75 | static int last_idx = 0, last_ch = 0; 76 | 77 | int idx = (settings_get_mode(true) == MODE_PS2) ? ps2_cardman_get_idx() : ps1_cardman_get_idx(); 78 | int ch = (settings_get_mode(true) == MODE_PS2) ? ps2_cardman_get_channel() : ps1_cardman_get_channel(); 79 | 80 | if ((idx != last_idx) || (ch != last_ch)) { 81 | red_active |= true; 82 | green_active |= true; 83 | blue_active |= true; 84 | last_idx = idx; 85 | last_ch = ch; 86 | } else { 87 | green_active |= !ps2_mmceman_fs_idle(); 88 | blue_active |= (settings_get_mode(true) == MODE_PS2) ? ps2_mc_data_interface_write_occured() : ps1_mc_data_interface_write_occured(); 89 | } 90 | 91 | if (time - last_refresh > LED_REFRESH_US) { 92 | #if WS2812 93 | ws2812_put_rgb(red_active ? 0xFF : 0x00 , 94 | green_active ? 0xFF : 0x00, 95 | blue_active ? 0xFF : 0x00); 96 | #else 97 | gpio_put(LED_R, red_active); 98 | gpio_put(LED_B, blue_active); 99 | gpio_put(LED_G, green_active); 100 | #endif 101 | 102 | red_active = false; 103 | green_active = false; 104 | blue_active = false; 105 | last_refresh = time; 106 | } 107 | } 108 | -------------------------------------------------------------------------------- /src/led/led.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | void led_init(void); 4 | void led_fatal(void); 5 | void led_clear(void); 6 | void led_task(void); -------------------------------------------------------------------------------- /src/led/ws2812.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | ;.pio_version 0 // only requires PIO version 0 7 | 8 | .program ws2812 9 | .side_set 1 10 | 11 | ; The following constants are selected for broad compatibility with WS2812, 12 | ; WS2812B, and SK6812 LEDs. Other constants may support higher bandwidths for 13 | ; specific LEDs, such as (7,10,8) for WS2812B LEDs. 14 | 15 | .define public T1 3 16 | .define public T2 3 17 | .define public T3 4 18 | 19 | 20 | .wrap_target 21 | bitloop: 22 | out x, 1 side 0 [T3 - 1] ; Side-set still takes place when instruction stalls 23 | jmp !x do_zero side 1 [T1 - 1] ; Branch on the bit we shifted out. Positive pulse 24 | do_one: 25 | jmp bitloop side 1 [T2 - 1] ; Continue driving high, for a long pulse 26 | do_zero: 27 | nop side 0 [T2 - 1] ; Or drive low, for a short pulse 28 | .wrap 29 | 30 | % c-sdk { 31 | #include "hardware/clocks.h" 32 | 33 | static inline void ws2812_program_init(PIO pio, uint sm, uint offset, uint pin, float freq, bool rgbw) { 34 | 35 | pio_gpio_init(pio, pin); 36 | pio_sm_set_consecutive_pindirs(pio, sm, pin, 1, true); 37 | 38 | pio_sm_config c = ws2812_program_get_default_config(offset); 39 | sm_config_set_sideset_pins(&c, pin); 40 | sm_config_set_out_shift(&c, false, true, rgbw ? 32 : 24); 41 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 42 | 43 | int cycles_per_bit = ws2812_T1 + ws2812_T2 + ws2812_T3; 44 | float div = clock_get_hz(clk_sys) / (freq * cycles_per_bit); 45 | sm_config_set_clkdiv(&c, div); 46 | 47 | pio_sm_init(pio, sm, offset, &c); 48 | pio_sm_set_enabled(pio, sm, true); 49 | } 50 | %} 51 | -------------------------------------------------------------------------------- /src/main.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include "hardware/watchdog.h" 3 | #if WITH_LED 4 | #include "led.h" 5 | #endif 6 | #include "pico/stdio.h" 7 | #include "pico/stdlib.h" 8 | #include "pico/bootrom.h" 9 | #include "pico/multicore.h" 10 | #include "hardware/clocks.h" 11 | #include "hardware/structs/bus_ctrl.h" 12 | 13 | #if WITH_GUI 14 | #include "oled.h" 15 | #include "gui.h" 16 | #endif 17 | #include "input.h" 18 | #include "config.h" 19 | #include "debug.h" 20 | #include "pico/time.h" 21 | #include "sd.h" 22 | #include "settings.h" 23 | #include "version/version.h" 24 | #if WITH_PSRAM 25 | #include "psram/psram.h" 26 | #endif 27 | 28 | #include "ps1/ps1_memory_card.h" 29 | #include "ps1/ps1_cardman.h" 30 | 31 | #include "ps2.h" 32 | #include "ps1.h" 33 | 34 | #include "card_emu/ps2_memory_card.h" 35 | #include "mmceman/ps2_mmceman.h" 36 | #include "mmceman/ps2_mmceman_commands.h" 37 | #include "ps2_cardman.h" 38 | 39 | 40 | #include "game_db/game_db.h" 41 | 42 | /* reboot to bootloader if either button is held on startup 43 | to make the device easier to flash when assembled inside case */ 44 | static void check_bootloader_reset(void) { 45 | /* make sure at least DEBOUNCE interval passes or we won't get inputs */ 46 | for (int i = 0; i < 2 * DEBOUNCE_MS; ++i) { 47 | input_task(); 48 | sleep_ms(1); 49 | } 50 | 51 | if (input_is_down_raw(0) || input_is_down_raw(1)) 52 | reset_usb_boot(0, 0); 53 | } 54 | 55 | static void debug_task(void) { 56 | 57 | for (int i = 0; i < 10; ++i) { 58 | char ch = debug_get(); 59 | if (ch) { 60 | #if DEBUG_USB_UART 61 | putchar(ch); 62 | #else 63 | if (ch == '\n') 64 | uart_putc_raw(UART_PERIPH, '\r'); 65 | uart_putc_raw(UART_PERIPH, ch); 66 | #endif 67 | } else { 68 | break; 69 | } 70 | } 71 | #if DEBUG_USB_UART 72 | int charin = getchar_timeout_us(0); 73 | if ((charin != PICO_ERROR_TIMEOUT) && (charin > 0x20) && (charin < 0x7A)) { 74 | QPRINTF("Got %c Input\n", charin); 75 | 76 | char in[3] = {0}; 77 | in[0] = charin; 78 | in[1] = getchar_timeout_us(1000*1000*3); 79 | in[2] = getchar_timeout_us(1000*1000*3); 80 | if (in[0] == 'b') { 81 | if ((in[1] == 'l') && (in[2] == 'r')) { 82 | QPRINTF("Resetting to Bootloader"); 83 | reset_usb_boot(0, 0); 84 | } 85 | } else if (in[0] == 'r') { 86 | if ((in[1] == 'r') && (in[2] == 'r')) { 87 | QPRINTF("Resetting"); 88 | watchdog_reboot(0, 0, 0); 89 | } 90 | } else if (in[0] == 'c') { 91 | if ((in[1] == 'h') && (in[2] == '+')) { 92 | DPRINTF("Received Channel Up!\n"); 93 | if (settings_get_mode(true) == MODE_PS2) { 94 | mmceman_mode = MMCEMAN_MODE_NEXT; 95 | mmceman_cmd = MMCEMAN_SET_CHANNEL; 96 | } else { 97 | ps1_memory_card_exit(); 98 | ps1_cardman_close(); 99 | ps1_cardman_next_channel(); 100 | ps1_cardman_open(); 101 | ps1_memory_card_enter(); 102 | } 103 | } else if ((in[1] == 'h') && (in[2] == '-')) { 104 | DPRINTF("Received Channel Down!\n"); 105 | if (settings_get_mode(true) == MODE_PS2) { 106 | mmceman_mode = MMCEMAN_MODE_PREV; 107 | mmceman_cmd = MMCEMAN_SET_CHANNEL; 108 | } else { 109 | ps1_memory_card_exit(); 110 | ps1_cardman_close(); 111 | ps1_cardman_prev_channel(); 112 | ps1_cardman_open(); 113 | ps1_memory_card_enter(); 114 | } 115 | } else if (in[1] == '+') { 116 | DPRINTF("Received Card Up!\n"); 117 | if (settings_get_mode(true) == MODE_PS2) { 118 | mmceman_mode = MMCEMAN_MODE_NEXT; 119 | mmceman_cmd = MMCEMAN_SET_CARD; 120 | } else { 121 | ps1_memory_card_exit(); 122 | ps1_cardman_close(); 123 | ps1_cardman_next_idx(); 124 | ps1_cardman_open(); 125 | ps1_memory_card_enter(); 126 | } 127 | } else if (in[1] == '-') { 128 | DPRINTF("Received Card Down!\n"); 129 | if (settings_get_mode(true) == MODE_PS2) { 130 | mmceman_mode = MMCEMAN_MODE_PREV; 131 | mmceman_cmd = MMCEMAN_SET_CARD; 132 | } else { 133 | ps1_memory_card_exit(); 134 | ps1_cardman_close(); 135 | ps1_cardman_prev_idx(); 136 | ps1_cardman_open(); 137 | ps1_memory_card_enter(); 138 | } 139 | } 140 | } 141 | } 142 | #endif 143 | } 144 | 145 | 146 | 147 | int main() { 148 | input_init(); 149 | check_bootloader_reset(); 150 | 151 | printf("prepare...\n"); 152 | int mhz = 240; 153 | set_sys_clock_khz(mhz * 1000, true); 154 | clock_configure(clk_peri, 0, CLOCKS_CLK_PERI_CTRL_AUXSRC_VALUE_CLK_SYS, mhz * 1000000, mhz * 1000000); 155 | 156 | #if DEBUG_USB_UART 157 | stdio_usb_init(); 158 | #else 159 | stdio_uart_init_full(UART_PERIPH, UART_BAUD, UART_TX, UART_RX); 160 | #endif 161 | 162 | /* set up core1 as high priority bus access */ 163 | bus_ctrl_hw->priority |= BUSCTRL_BUS_PRIORITY_PROC1_BITS; 164 | while (!bus_ctrl_hw->priority_ack) {} 165 | 166 | printf("\n\n\nStarted! Clock %d; bus priority 0x%X\n", (int)clock_get_hz(clk_sys), (unsigned)bus_ctrl_hw->priority); 167 | printf("SD2PSX Version %s\n", sd2psx_version); 168 | printf("SD2PSX HW Variant: %s\n", sd2psx_variant); 169 | 170 | settings_init(); 171 | #if WITH_PSRAM 172 | psram_init(); 173 | #endif 174 | game_db_init(); 175 | 176 | #if WITH_LED 177 | led_init(); 178 | #endif 179 | 180 | while (1) { 181 | if (settings_get_mode(true) == MODE_PS2) { 182 | printf("Starting PS2 mode...\n"); 183 | ps2_init(); 184 | settings_load_sd(); 185 | do { 186 | debug_task(); 187 | } while(ps2_task()); 188 | ps2_deinit(); 189 | 190 | } else { 191 | printf("Starting PS1 mode...\n"); 192 | ps1_init(); 193 | settings_load_sd(); 194 | do { 195 | debug_task(); 196 | } while(ps1_task()); 197 | ps1_deinit(); 198 | } 199 | } 200 | } 201 | -------------------------------------------------------------------------------- /src/oled.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "settings.h" 3 | #include "ssd1306.h" 4 | 5 | #define blit16_ARRAY_ONLY 6 | #define blit16_NO_HELPERS 7 | #include "blit16.h" 8 | 9 | 10 | static ssd1306_t oled_disp = { .external_vcc = 0 }; 11 | static int oled_init_done, have_oled; 12 | static uint64_t last_action_time_us; 13 | 14 | void oled_update_last_action_time() { 15 | last_action_time_us = time_us_64(); 16 | } 17 | 18 | int oled_init(void) { 19 | if (oled_init_done) 20 | return have_oled; 21 | oled_init_done = 1; 22 | 23 | i2c_init(OLED_I2C_PERIPH, OLED_I2C_CLOCK); 24 | gpio_set_function(OLED_I2C_SDA, GPIO_FUNC_I2C); 25 | gpio_set_function(OLED_I2C_SCL, GPIO_FUNC_I2C); 26 | gpio_pull_up(OLED_I2C_SDA); 27 | gpio_pull_up(OLED_I2C_SCL); 28 | oled_update_last_action_time(); 29 | 30 | have_oled = ssd1306_init( 31 | &oled_disp, DISPLAY_WIDTH, DISPLAY_HEIGHT, OLED_I2C_ADDR, OLED_I2C_PERIPH, 32 | settings_get_display_contrast(), settings_get_display_vcomh(), settings_get_display_flipped() 33 | ); 34 | 35 | return have_oled; 36 | } 37 | 38 | void oled_clear(void) { 39 | ssd1306_clear(&oled_disp); 40 | } 41 | 42 | void oled_draw_pixel(int x, int y) { 43 | ssd1306_draw_pixel(&oled_disp, x, y); 44 | } 45 | 46 | void oled_show(void) { 47 | ssd1306_show(&oled_disp); 48 | } 49 | 50 | void oled_set_contrast(uint8_t v) { 51 | ssd1306_contrast(&oled_disp, v); 52 | } 53 | 54 | void oled_set_vcomh(uint8_t v) { 55 | ssd1306_set_vcomh(&oled_disp, v); 56 | } 57 | 58 | static int text_x, text_y; 59 | 60 | static void draw_char(char c) { 61 | if (c == '\n') { 62 | text_x = 0; 63 | text_y += blit16_HEIGHT + 1; 64 | return; 65 | } 66 | 67 | if (c < 32 || c >= 32 + blit_NUM_GLYPHS) 68 | c = ' '; 69 | 70 | blit16_glyph g = blit16_Glyphs[c - 32]; 71 | for (int y = 0; y < blit16_HEIGHT; ++y) 72 | for (int x = 0; x < blit16_WIDTH; ++x) 73 | if (g & (1 << (x + y * blit16_WIDTH))) 74 | oled_draw_pixel(text_x + x, text_y + y); 75 | 76 | text_x += blit16_WIDTH + 1; 77 | if (text_x >= DISPLAY_WIDTH) { 78 | text_x = 0; 79 | text_y += blit16_HEIGHT + 1; 80 | } 81 | } 82 | 83 | void oled_draw_text(const char *s) { 84 | for (; *s; s++) 85 | draw_char(*s); 86 | } 87 | 88 | bool oled_is_powered_on(void) { 89 | return have_oled; 90 | } 91 | 92 | static void oled_power_off(void) { 93 | if (!oled_is_powered_on()) 94 | return; 95 | 96 | ssd1306_poweroff(&oled_disp); 97 | have_oled = 0; 98 | } 99 | 100 | static void oled_power_on(void) { 101 | if (oled_is_powered_on()) 102 | return; 103 | 104 | ssd1306_poweron(&oled_disp); 105 | have_oled = 1; 106 | } 107 | 108 | 109 | void oled_flip(bool flip) { 110 | ssd1306_flip_display(&oled_disp, flip); 111 | } 112 | 113 | void oled_task(void) { 114 | uint8_t display_timeout = settings_get_display_timeout(); 115 | if (!display_timeout) 116 | return; 117 | 118 | uint64_t now_us = time_us_64(); 119 | uint64_t diff_s = (now_us - last_action_time_us) / 1000000; 120 | 121 | if (diff_s > display_timeout) { 122 | oled_power_off(); 123 | } else { 124 | oled_power_on(); 125 | } 126 | } 127 | -------------------------------------------------------------------------------- /src/oled.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | void oled_update_last_action_time(void); 7 | int oled_init(void); 8 | void oled_clear(void); 9 | void oled_draw_pixel(int x, int y); 10 | void oled_show(void); 11 | void oled_set_contrast(uint8_t v); 12 | void oled_set_vcomh(uint8_t v); 13 | void oled_draw_text(const char *s); 14 | bool oled_is_powered_on(void); 15 | void oled_flip(bool flip); 16 | void oled_task(void); 17 | -------------------------------------------------------------------------------- /src/ps1.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include 4 | #include 5 | #include "pico/multicore.h" 6 | #include "ps1_mc_data_interface.h" 7 | 8 | #if WITH_LED 9 | #include "led.h" 10 | #endif 11 | #if WITH_GUI 12 | #include "gui.h" 13 | #include "input.h" 14 | #include "oled.h" 15 | #elif PMC_BUTTONS 16 | #include "input.h" 17 | #endif 18 | #include "ps1.h" 19 | #include "ps1_cardman.h" 20 | #include "ps1_dirty.h" 21 | #include "ps1_memory_card.h" 22 | #include "ps1_mmce.h" 23 | 24 | 25 | #ifdef PMC_BUTTONS 26 | static void ps1_update_buttons(void) { 27 | int button = input_get_pressed(); 28 | switch (button) { 29 | case INPUT_KEY_BACK: 30 | ps1_mmce_prev_idx(false); 31 | break; 32 | case INPUT_KEY_PREV: 33 | ps1_mmce_prev_ch(false); 34 | break; 35 | case INPUT_KEY_NEXT: 36 | ps1_mmce_next_ch(false); 37 | break; 38 | case INPUT_KEY_ENTER: 39 | ps1_mmce_next_idx(false); 40 | break; 41 | case INPUT_KEY_BOOT: 42 | ps1_mmce_switch_bootcard(false); 43 | break; 44 | default: 45 | break; 46 | } 47 | } 48 | #endif 49 | 50 | void ps1_init() { 51 | printf("starting in PS1 mode\n"); 52 | 53 | ps1_cardman_init(); 54 | ps1_dirty_init(); 55 | 56 | #if WITH_GUI 57 | gui_init(); 58 | #endif 59 | 60 | multicore_launch_core1(ps1_memory_card_main); 61 | 62 | printf("Starting memory card... "); 63 | uint64_t start = time_us_64(); 64 | #if WITH_GUI 65 | gui_do_ps1_card_switch(); 66 | #endif 67 | ps1_cardman_open(); 68 | ps1_memory_card_enter(); 69 | uint64_t end = time_us_64(); 70 | printf("DONE! (%d us)\n", (int)(end - start)); 71 | } 72 | 73 | bool ps1_task() { 74 | ps1_mmce_task(); 75 | 76 | #if WITH_GUI 77 | gui_task(); 78 | input_task(); 79 | oled_task(); 80 | #elif PMC_BUTTONS 81 | input_task(); 82 | ps1_update_buttons(); 83 | #endif 84 | #if WITH_LED 85 | led_task(); 86 | #endif 87 | ps1_mc_data_interface_task(); 88 | if ((settings_get_mode(true) == MODE_PS2)) 89 | return false; 90 | 91 | return true; 92 | } 93 | 94 | void ps1_deinit(void) { 95 | 96 | ps1_memory_card_exit(); 97 | 98 | while(ps1_dirty_activity) 99 | ps1_dirty_task(); 100 | ps1_cardman_close(); 101 | multicore_reset_core1(); 102 | ps1_memory_card_unload(); 103 | } -------------------------------------------------------------------------------- /src/ps1.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | void ps1_init(); 5 | bool ps1_task(); 6 | void ps1_deinit(); -------------------------------------------------------------------------------- /src/ps1/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(ps1_card STATIC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/ps1_cardman.c 3 | ${CMAKE_CURRENT_SOURCE_DIR}/ps1_dirty.c 4 | ${CMAKE_CURRENT_SOURCE_DIR}/ps1_memory_card.c 5 | ${CMAKE_CURRENT_SOURCE_DIR}/ps1_empty_card.c 6 | ${CMAKE_CURRENT_SOURCE_DIR}/ps1_mc_data_interface.c 7 | ${CMAKE_CURRENT_SOURCE_DIR}/ps1_mmce.c 8 | ) 9 | 10 | target_include_directories(ps1_card PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 11 | 12 | target_link_libraries(ps1_card 13 | PRIVATE 14 | sd2psx_common 15 | pico_platform_headers 16 | pico_stdlib 17 | pico_multicore 18 | hardware_pio 19 | sd_fat 20 | ) 21 | 22 | if (SD2PSX_WITH_PSRAM) 23 | target_link_libraries(ps1_card PRIVATE 24 | psram) 25 | endif() 26 | 27 | configure_file(${CMAKE_CURRENT_LIST_DIR}/ps1_mc_spi.pio ${CMAKE_CURRENT_BINARY_DIR}/ps1_mc_spi.pio) 28 | 29 | pico_generate_pio_header(ps1_card ${CMAKE_CURRENT_BINARY_DIR}/ps1_mc_spi.pio) 30 | -------------------------------------------------------------------------------- /src/ps1/ps1_cardman.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | #define PS1_CARD_IDX_SPECIAL 0 7 | 8 | typedef enum { 9 | PS1_CM_STATE_NAMED, 10 | PS1_CM_STATE_BOOT, 11 | PS1_CM_STATE_GAMEID, 12 | PS1_CM_STATE_NORMAL 13 | } ps1_cardman_state_t; 14 | 15 | void ps1_cardman_init(void); 16 | int ps1_cardman_read_sector(int sector, void *buf128); 17 | int ps1_cardman_write_sector(int sector, void *buf512); 18 | void ps1_cardman_flush(void); 19 | void ps1_cardman_open(void); 20 | bool ps1_cardman_needs_update(void); 21 | void ps1_cardman_close(void); 22 | int ps1_cardman_get_idx(void); 23 | int ps1_cardman_get_channel(void); 24 | const char* ps1_cardman_get_folder_name(void); 25 | ps1_cardman_state_t ps1_cardman_get_state(void); 26 | 27 | void ps1_cardman_next_channel(void); 28 | void ps1_cardman_prev_channel(void); 29 | void ps1_cardman_next_idx(void); 30 | void ps1_cardman_prev_idx(void); 31 | void ps1_cardman_set_game_id(const char* card_game_id); 32 | void ps1_cardman_switch_bootcard(void); 33 | void ps1_cardman_switch_default(void); 34 | -------------------------------------------------------------------------------- /src/ps1/ps1_dirty.c: -------------------------------------------------------------------------------- 1 | #include "ps1_dirty.h" 2 | #include "ps1_cardman.h" 3 | #include "ps1_mc_data_interface.h" 4 | #ifdef WITH_PSRAM 5 | #include 6 | #endif 7 | 8 | #include "bigmem.h" 9 | #define dirty_heap bigmem.ps1.dirty_heap 10 | #define dirty_map bigmem.ps1.dirty_map 11 | 12 | #include 13 | #include 14 | 15 | spin_lock_t *ps1_dirty_spin_lock; 16 | volatile uint32_t ps1_dirty_lockout; 17 | int ps1_dirty_activity; 18 | 19 | static int num_dirty; 20 | 21 | #define SWAP(a, b) do { \ 22 | uint16_t tmp = a; \ 23 | a = b; \ 24 | b = tmp; \ 25 | } while (0); 26 | 27 | void ps1_dirty_init(void) { 28 | if (!ps1_dirty_spin_lock) 29 | ps1_dirty_spin_lock = spin_lock_init(spin_lock_claim_unused(1)); 30 | } 31 | 32 | void __time_critical_func(ps1_dirty_mark)(uint32_t sector) { 33 | if (sector < sizeof(dirty_map)) { 34 | /* already marked? */ 35 | if (dirty_map[sector]) 36 | return; 37 | 38 | /* update map */ 39 | dirty_map[sector] = 1; 40 | 41 | /* update heap */ 42 | int cur = num_dirty++; 43 | dirty_heap[cur] = sector; 44 | while (dirty_heap[cur] < dirty_heap[(cur-1)/2]) { 45 | SWAP(dirty_heap[cur], dirty_heap[(cur-1)/2]); 46 | cur = (cur-1)/2; 47 | } 48 | } 49 | } 50 | 51 | static void heapify(int i) { 52 | int l = i * 2 + 1; 53 | int r = i * 2 + 2; 54 | int best = i; 55 | if (l < num_dirty && dirty_heap[l] < dirty_heap[best]) 56 | best = l; 57 | if (r < num_dirty && dirty_heap[r] < dirty_heap[best]) 58 | best = r; 59 | if (best != i) { 60 | SWAP(dirty_heap[i], dirty_heap[best]); 61 | heapify(best); 62 | } 63 | } 64 | 65 | int ps1_dirty_get_marked(void) { 66 | if (num_dirty == 0) 67 | return -1; 68 | 69 | uint16_t ret = dirty_heap[0]; 70 | 71 | /* update heap */ 72 | dirty_heap[0] = dirty_heap[--num_dirty]; 73 | heapify(0); 74 | 75 | /* update map */ 76 | dirty_map[ret] = 0; 77 | 78 | return ret; 79 | } 80 | 81 | void ps1_dirty_task(void) { 82 | static uint8_t flushbuf[128]; 83 | 84 | int num_after = 0; 85 | int hit = 0; 86 | uint64_t start = time_us_64(); 87 | while (1) { 88 | if (!ps1_dirty_lockout_expired()) 89 | break; 90 | /* do up to 100ms of work per call to dirty_taks */ 91 | if ((time_us_64() - start) > 100 * 1000) 92 | break; 93 | 94 | ps1_dirty_lock(); 95 | int sector = ps1_dirty_get_marked(); 96 | num_after = num_dirty; 97 | if (sector == -1) { 98 | ps1_dirty_unlock(); 99 | break; 100 | } 101 | #if WITH_PSRAM 102 | psram_read_dma(sector * 128, flushbuf, 128, NULL); 103 | psram_wait_for_dma(); 104 | #else 105 | uint8_t* page = ps1_mc_data_interface_get_page(sector); 106 | memcpy(flushbuf, page, PS1_PAGE_SIZE); 107 | #endif 108 | ps1_dirty_unlock(); 109 | 110 | ++hit; 111 | 112 | printf("ps1 - write sector %d\n", sector); 113 | 114 | if (ps1_cardman_write_sector(sector, flushbuf) != 0) { 115 | // TODO: do something if we get too many errors? 116 | // for now lets push it back into the heap and try again later 117 | printf("!! writing sector 0x%x failed\n", sector); 118 | 119 | ps1_dirty_lock(); 120 | ps1_dirty_mark(sector); 121 | ps1_dirty_unlock(); 122 | } 123 | } 124 | /* to make sure writes hit the storage medium */ 125 | ps1_cardman_flush(); 126 | 127 | uint64_t end = time_us_64(); 128 | 129 | if (hit) 130 | printf("remain to flush - %d - this one flushed %d and took %d ms\n", num_after, hit, (int)((end - start) / 1000)); 131 | 132 | if (num_after || !ps1_dirty_lockout_expired()) 133 | ps1_dirty_activity = 1; 134 | else 135 | ps1_dirty_activity = 0; 136 | } 137 | -------------------------------------------------------------------------------- /src/ps1/ps1_dirty.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "hardware/sync.h" 5 | #include "hardware/timer.h" 6 | #include "pico/platform.h" 7 | 8 | #include "util.h" 9 | 10 | extern spin_lock_t *ps1_dirty_spin_lock; 11 | extern volatile uint32_t ps1_dirty_lockout; 12 | 13 | static inline void __time_critical_func(ps1_dirty_lock)(void) { 14 | spin_lock_unsafe_blocking(ps1_dirty_spin_lock); 15 | } 16 | 17 | static inline void __time_critical_func(ps1_dirty_unlock)(void) { 18 | spin_unlock_unsafe(ps1_dirty_spin_lock); 19 | } 20 | 21 | static inline void __time_critical_func(ps1_dirty_lockout_renew)(void) { 22 | /* lockout for 100ms, store time in ms */ 23 | ps1_dirty_lockout = (uint32_t)(RAM_time_us_64() / 1000) + 100; 24 | } 25 | 26 | static inline int __time_critical_func(ps1_dirty_lockout_expired)(void) { 27 | return ps1_dirty_lockout < (uint32_t)(RAM_time_us_64() / 1000); 28 | } 29 | 30 | void ps1_dirty_init(void); 31 | int ps1_dirty_get_marked(void); 32 | void ps1_dirty_mark(uint32_t sector); 33 | void ps1_dirty_task(void); 34 | 35 | extern int ps1_dirty_activity; 36 | -------------------------------------------------------------------------------- /src/ps1/ps1_empty_card.h: -------------------------------------------------------------------------------- 1 | #include 2 | 3 | extern uint8_t ps1_empty_card[8192]; 4 | -------------------------------------------------------------------------------- /src/ps1/ps1_mc_data_interface.c: -------------------------------------------------------------------------------- 1 | #include 2 | #include 3 | #include 4 | 5 | 6 | #include "pico/multicore.h" 7 | #include "pico/time.h" 8 | #include "ps1_mc_data_interface.h" 9 | #include "bigmem.h" 10 | #if WITH_PSRAM 11 | #include "psram.h" 12 | #endif 13 | #include "ps1_cardman.h" 14 | #include "ps1_dirty.h" 15 | 16 | #include "debug.h" 17 | 18 | 19 | #define QPRINTF(fmt, x...) printf(fmt, ##x) 20 | 21 | #define PAGE_CACHE_SIZE 40 22 | #define MAX_READ_AHEAD 0 23 | #define PS1_CARD_SIZE 128 * 1024 24 | 25 | static bool dma_in_progress = false; 26 | static bool write_occured = false; 27 | 28 | 29 | #define card cache 30 | 31 | 32 | #if WITH_PSRAM 33 | static void __time_critical_func(ps1_mc_data_interface_rx_done)() { 34 | dma_in_progress = false; 35 | 36 | ps1_dirty_unlock(); 37 | } 38 | 39 | void __time_critical_func(ps1_mc_data_interface_start_dma)(uint32_t page) { 40 | ps1_dirty_lockout_renew(); 41 | /* the spinlock will be unlocked by the DMA irq once all data is tx'd */ 42 | ps1_dirty_lock(); 43 | dma_in_progress = true; 44 | psram_read_dma(page * PS1_PAGE_SIZE, card, PS1_PAGE_SIZE, ps1_mc_data_interface_rx_done); 45 | } 46 | #endif 47 | 48 | 49 | 50 | void __time_critical_func(ps1_mc_data_interface_setup_read_page)(uint32_t page) { 51 | #if WITH_PSRAM 52 | ps1_mc_data_interface_start_dma(page); 53 | #endif 54 | } 55 | 56 | uint8_t* __time_critical_func(ps1_mc_data_interface_get_page)(uint32_t page) { 57 | uint8_t* ret = NULL; 58 | 59 | #ifdef WITH_PSRAM 60 | ret = card; 61 | #else 62 | ret = &card[page*PS1_PAGE_SIZE]; 63 | #endif 64 | 65 | return ret; 66 | } 67 | 68 | void __time_critical_func(ps1_mc_data_interface_write_byte)(uint32_t address, uint8_t byte) { 69 | #if WITH_PSRAM 70 | card[address%PS1_PAGE_SIZE] = byte; 71 | ps1_dirty_lockout_renew(); 72 | ps1_dirty_lock(); 73 | psram_write_dma(address, &card[address%PS1_PAGE_SIZE], 1, NULL); 74 | psram_wait_for_dma(); 75 | 76 | ps1_dirty_unlock(); 77 | #else 78 | card[address] = byte; 79 | #endif 80 | write_occured = true; 81 | } 82 | 83 | void __time_critical_func(ps1_mc_data_interface_write_mc)(uint32_t page) { 84 | ps1_dirty_mark(page); 85 | } 86 | 87 | void __time_critical_func(ps1_mc_data_interface_wait_for_byte)(uint32_t offset) { 88 | #if WITH_PSRAM 89 | while (dma_in_progress && psram_read_dma_remaining() >= (PS1_PAGE_SIZE - offset)) {}; 90 | #endif 91 | } 92 | 93 | // Core 0 94 | 95 | void ps1_mc_data_interface_card_changed(void) { 96 | #if WITH_PSRAM != 1 97 | QPRINTF("Card changed\n"); 98 | 99 | for (int i = 0; i < 1024; i++) { 100 | if (ps1_cardman_read_sector(i, &card[i*PS1_PAGE_SIZE]) < 0) 101 | fatal(ERR_MC_DATA, "Sector %i not read!!!\n", i); 102 | } 103 | #endif 104 | } 105 | 106 | bool ps1_mc_data_interface_write_occured(void) { 107 | return write_occured; 108 | } 109 | 110 | void ps1_mc_data_interface_task(void) { 111 | write_occured = false; 112 | 113 | ps1_dirty_task(); 114 | } 115 | 116 | void ps1_mc_data_interface_flush(void) { 117 | while ( ps1_dirty_activity > 0 118 | ) { 119 | ps1_mc_data_interface_task(); 120 | } 121 | } -------------------------------------------------------------------------------- /src/ps1/ps1_mc_data_interface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define PS1_PAGE_SIZE 128 8 | 9 | // Core 1 10 | 11 | void ps1_mc_data_interface_setup_read_page(uint32_t page); 12 | void ps1_mc_data_interface_write_byte(uint32_t address, uint8_t byte); 13 | void ps1_mc_data_interface_write_mc(uint32_t page); 14 | void ps1_mc_data_interface_erase(uint32_t page); 15 | uint8_t* ps1_mc_data_interface_get_page(uint32_t page); 16 | void ps1_mc_data_interface_commit_write(uint32_t page, uint8_t *buf); 17 | void ps1_mc_data_interface_wait_for_byte(uint32_t offset); 18 | 19 | // Core 0 20 | 21 | void ps1_mc_data_interface_card_changed(void); 22 | bool ps1_mc_data_interface_write_occured(void); 23 | void ps1_mc_data_interface_task(void); 24 | void ps1_mc_data_interface_flush(void); 25 | -------------------------------------------------------------------------------- /src/ps1/ps1_mc_spi.pio: -------------------------------------------------------------------------------- 1 | ; largely based on https://github.com/dangiu/PicoMemcard/blob/pmc%2B/release/psxSPI.pio 2 | 3 | ; INJECTION 4 | .define PUBLIC PIN_PSX_ACK ${PIN_PSX_ACK} 5 | .define PUBLIC PIN_PSX_SEL ${PIN_PSX_SEL} 6 | .define PUBLIC PIN_PSX_CLK ${PIN_PSX_CLK} 7 | .define PUBLIC PIN_PSX_CMD ${PIN_PSX_CMD} 8 | .define PUBLIC PIN_PSX_DAT ${PIN_PSX_DAT} 9 | 10 | ; SD2PSX 11 | ;.define PUBLIC PIN_PSX_ACK 16 12 | ;.define PUBLIC PIN_PSX_SEL 17 13 | ;.define PUBLIC PIN_PSX_CLK 18 14 | ;.define PUBLIC PIN_PSX_CMD 19 15 | ;.define PUBLIC PIN_PSX_DAT 20 16 | 17 | ; BitFunX (?) 18 | ;.define PUBLIC PIN_PSX_ACK 9 19 | ;.define PUBLIC PIN_PSX_SEL 7 20 | ;.define PUBLIC PIN_PSX_CLK 8 21 | ;.define PUBLIC PIN_PSX_CMD 6 22 | ;.define PUBLIC PIN_PSX_DAT 5 23 | 24 | .program cmd_reader 25 | wait 0 gpio PIN_PSX_SEL ; wait for SEL 26 | .wrap_target 27 | wait 0 gpio PIN_PSX_CLK ; wait for clock to fall 28 | wait 1 gpio PIN_PSX_CLK ; wait for rising clock edge 29 | in pins 1 ; sample 1 bit from the CMD line 30 | .wrap 31 | 32 | .program dat_writer 33 | .side_set 1 opt pindirs 34 | set pindirs, 0 ; release DAT line (set pin as input = Hi-Z) 35 | wait 0 gpio PIN_PSX_SEL ; wait for SEL 36 | .wrap_target 37 | pull block [3] ; pull a byte to send, blocking 38 | ack: 39 | nop side 1 [4] 40 | set x, 7 side 0 [5] 41 | sendbit: 42 | wait 1 gpio PIN_PSX_CLK ; wait for clock to rise 43 | wait 0 gpio PIN_PSX_CLK ; wait for falling clock edge 44 | out pindirs 1 ; output 1 bit (by changing pin direction) 45 | jmp x-- sendbit 46 | nop [7] 47 | jmp !osre ack 48 | .wrap 49 | 50 | 51 | % c-sdk { 52 | 53 | #define PS1_CLKDIV 96 54 | 55 | static inline void cmd_reader_program_init(PIO pio, uint sm, uint offset) { 56 | pio_sm_config c = cmd_reader_program_get_default_config(offset); 57 | 58 | sm_config_set_in_pins(&c, PIN_PSX_CMD); 59 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_CMD, 1, false); 60 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_SEL, 1, false); 61 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_CLK, 1, false); 62 | 63 | /* shift ISR to right, autopush every 8 bits */ 64 | sm_config_set_in_shift(&c, true, true, 8); 65 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); 66 | 67 | sm_config_set_clkdiv_int_frac(&c, PS1_CLKDIV, 0x00); 68 | 69 | pio_sm_init(pio, sm, offset, &c); 70 | } 71 | 72 | static inline void controller_program_init(PIO pio, uint sm, uint offset) { 73 | pio_sm_config c = cmd_reader_program_get_default_config(offset); 74 | 75 | sm_config_set_in_pins(&c, PIN_PSX_DAT); 76 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_DAT, 1, false); 77 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_SEL, 1, false); 78 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_CLK, 1, false); 79 | 80 | /* shift ISR to right, autopush every 8 bits */ 81 | sm_config_set_in_shift(&c, true, true, 8); 82 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); 83 | 84 | sm_config_set_clkdiv_int_frac(&c, PS1_CLKDIV, 0x00); 85 | 86 | pio_sm_init(pio, sm, offset, &c); 87 | } 88 | 89 | static inline void dat_writer_program_init(PIO pio, uint sm, uint offset) { 90 | pio_sm_config c = dat_writer_program_get_default_config(offset); 91 | 92 | sm_config_set_out_pins(&c, PIN_PSX_DAT, 1); 93 | sm_config_set_set_pins(&c, PIN_PSX_DAT, 1); 94 | sm_config_set_sideset_pins(&c, PIN_PSX_ACK); 95 | 96 | /* configure ACK pin for open drain */ 97 | pio_sm_set_pins_with_mask(pio, sm, 0x00000000, 1 << PIN_PSX_ACK); 98 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_ACK, 1, false); 99 | pio_gpio_init(pio, PIN_PSX_ACK); 100 | 101 | /* configure DAT pin for open drain */ 102 | pio_sm_set_pins_with_mask(pio, sm, 0x00000000, 1 << PIN_PSX_DAT); 103 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_DAT, 1, false); 104 | pio_gpio_init(pio, PIN_PSX_DAT); 105 | 106 | /* SEL and CLK used as "wait" inputs only */ 107 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_SEL, 1, false); 108 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_CLK, 1, false); 109 | 110 | /* shift OSR to right, autopull every 8 bits */ 111 | sm_config_set_out_shift(&c, true, true, 8); 112 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 113 | 114 | sm_config_set_clkdiv_int_frac(&c, PS1_CLKDIV, 0x00); 115 | 116 | pio_sm_init(pio, sm, offset, &c); 117 | } 118 | 119 | %} 120 | -------------------------------------------------------------------------------- /src/ps1/ps1_memory_card.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | 4 | #include 5 | 6 | #define MCP_GAME_ID (1U) 7 | #define MCP_NXT_CH (2U) 8 | #define MCP_PRV_CH (3U) 9 | #define MCP_NXT_CARD (4U) 10 | #define MCP_PRV_CARD (5U) 11 | #define MCP_SWITCH_BOOTCARD (6U) 12 | #define MCP_SWITCH_DEFAULT (7U) 13 | 14 | 15 | void ps1_memory_card_main(void); 16 | void ps1_memory_card_enter(void); 17 | void ps1_memory_card_exit(void); 18 | void ps1_memory_card_unload(void); 19 | 20 | uint8_t ps1_memory_card_get_ode_command(void); 21 | void ps1_memory_card_reset_ode_command(void); 22 | const char* ps1_memory_card_get_game_id(void); -------------------------------------------------------------------------------- /src/ps1/ps1_mmce.c: -------------------------------------------------------------------------------- 1 | 2 | #include 3 | #include "pico/time.h" 4 | #include "ps1_mc_data_interface.h" 5 | #include "ps1_memory_card.h" 6 | #include "ps1_cardman.h" 7 | #include "ps1_mmce.h" 8 | #include "debug.h" 9 | #include "game_db/game_db.h" 10 | 11 | #if WITH_GUI 12 | #include 13 | #endif 14 | #include 15 | 16 | #define CARD_SWITCH_DELAY_MS (250) 17 | #define MAX_GAME_ID_LENGTH (16) 18 | 19 | #if LOG_LEVEL_PS1_MMCE == 0 20 | #define log(x...) 21 | #else 22 | #define log(level, fmt, x...) LOG_PRINT(LOG_LEVEL_PS1_MMCE, level, fmt, ##x) 23 | #endif 24 | static volatile uint8_t mmce_command; 25 | static uint64_t mmce_switching_timeout = 0; 26 | 27 | static char received_game_id[MAX_GAME_ID_LENGTH]; 28 | 29 | 30 | void ps1_mmce_task(void) { 31 | if (mmce_command != 0U) { 32 | 33 | switch (mmce_command) { 34 | case MCP_GAME_ID: { 35 | DPRINTF("Received Game ID: %s\n", received_game_id); 36 | game_db_update_game(received_game_id); 37 | game_db_get_current_parent(received_game_id); 38 | ps1_cardman_set_game_id(received_game_id); 39 | break; 40 | } 41 | case MCP_NXT_CARD: 42 | DPRINTF("Received next card.\n"); 43 | ps1_cardman_next_idx(); 44 | break; 45 | case MCP_PRV_CARD: 46 | DPRINTF("Received prev card.\n"); 47 | ps1_cardman_prev_idx(); 48 | break; 49 | case MCP_NXT_CH: 50 | DPRINTF("Received next chan.\n"); 51 | ps1_cardman_next_channel(); 52 | break; 53 | case MCP_PRV_CH: 54 | DPRINTF("Received prev chan.\n"); 55 | ps1_cardman_prev_channel(); 56 | break; 57 | case MCP_SWITCH_BOOTCARD: 58 | DPRINTF("Received switch boot card.\n"); 59 | ps1_cardman_switch_bootcard(); 60 | break; 61 | case MCP_SWITCH_DEFAULT: 62 | DPRINTF("Received switch default card.\n"); 63 | ps1_cardman_switch_default(); 64 | break; 65 | default: 66 | DPRINTF("Invalid ODE Command received."); 67 | break; 68 | } 69 | 70 | mmce_command = 0; 71 | } 72 | 73 | if ((mmce_switching_timeout < time_us_64()) 74 | && !input_is_any_down() 75 | && (ps1_cardman_needs_update())) { 76 | 77 | ps1_memory_card_exit(); 78 | ps1_mc_data_interface_flush(); 79 | ps1_cardman_close(); 80 | #ifdef WITH_GUI 81 | gui_do_ps1_card_switch(); 82 | #endif 83 | 84 | sleep_ms(CARD_SWITCH_DELAY_MS); // This delay is required, so ODE can register the card change 85 | 86 | ps1_cardman_open(); 87 | ps1_memory_card_enter(); 88 | #ifdef WITH_GUI 89 | gui_request_refresh(); 90 | #endif 91 | } 92 | } 93 | 94 | 95 | bool __time_critical_func(ps1_mmce_set_gameid)(const uint8_t* const game_id) { 96 | char sanitized_game_id[11] = {0}; 97 | bool ret = false; 98 | log(LOG_INFO, "Raw Game ID: %s\n", game_id); 99 | 100 | game_db_extract_title_id(game_id, sanitized_game_id, UINT8_MAX, sizeof(sanitized_game_id)); 101 | log(LOG_INFO, "Game ID: %s\n", sanitized_game_id); 102 | if (game_db_sanity_check_title_id(sanitized_game_id)) { 103 | snprintf(received_game_id, sizeof(received_game_id), "%s", sanitized_game_id); 104 | mmce_command = MCP_GAME_ID; 105 | ret = true; 106 | } else if ((game_id[0] != 0x00) && (ps1_cardman_get_idx() == PS1_CARD_IDX_SPECIAL)) { 107 | mmce_command = MCP_SWITCH_DEFAULT; 108 | } 109 | return ret; 110 | } 111 | 112 | const char* ps1_mmce_get_gameid(void) { 113 | return received_game_id; 114 | } 115 | 116 | void ps1_mmce_next_ch(bool delay) { 117 | mmce_switching_timeout = time_us_64() + (delay ? 1500 * 1000 : 0); 118 | mmce_command = MCP_NXT_CH; 119 | } 120 | 121 | void ps1_mmce_prev_ch(bool delay) { 122 | mmce_switching_timeout = time_us_64() + (delay ? 1500 * 1000 : 0); 123 | mmce_command = MCP_PRV_CH; 124 | } 125 | 126 | void ps1_mmce_next_idx(bool delay) { 127 | mmce_switching_timeout = time_us_64() + (delay ? 1500 * 1000 : 0); 128 | mmce_command = MCP_NXT_CARD; 129 | } 130 | 131 | void ps1_mmce_prev_idx(bool delay) { 132 | mmce_switching_timeout = time_us_64() + (delay ? 1500 * 1000 : 0); 133 | mmce_command = MCP_PRV_CARD; 134 | } 135 | 136 | void ps1_mmce_switch_bootcard(bool delay) { 137 | mmce_switching_timeout = time_us_64() + (delay ? 1500 * 1000 : 0); 138 | mmce_command = MCP_SWITCH_BOOTCARD; 139 | } 140 | -------------------------------------------------------------------------------- /src/ps1/ps1_mmce.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | #include 4 | #include 5 | 6 | void ps1_mmce_init(void); 7 | void ps1_mmce_task(void); 8 | void ps1_mmce_next_ch(bool delay); 9 | void ps1_mmce_prev_ch(bool delay); 10 | void ps1_mmce_next_idx(bool delay); 11 | void ps1_mmce_prev_idx(bool delay); 12 | void ps1_mmce_switch_bootcard(bool delay); 13 | bool ps1_mmce_set_gameid(const uint8_t* const game_id); 14 | -------------------------------------------------------------------------------- /src/ps2.c: -------------------------------------------------------------------------------- 1 | #include "card_emu/ps2_mc_auth.h" 2 | #include "keystore.h" 3 | #if WITH_LED 4 | #include "led.h" 5 | #endif 6 | #if WITH_GUI 7 | #include "gui.h" 8 | #include "input.h" 9 | #include "oled.h" 10 | #elif PMC_BUTTONS 11 | #include "input.h" 12 | #endif 13 | #include "settings.h" 14 | #include "card_emu/ps2_mc_data_interface.h" 15 | #include "mmceman/ps2_mmceman.h" 16 | #include "mmceman/ps2_mmceman_fs.h" 17 | #include "card_emu/ps2_memory_card.h" 18 | #include "ps2_dirty.h" 19 | #include "history_tracker/ps2_history_tracker.h" 20 | #include "ps2_cardman.h" 21 | #include "debug.h" 22 | 23 | #include 24 | 25 | #if LOG_LEVEL_PS2_MAIN == 0 26 | #define log(x...) 27 | #else 28 | #define log(level, fmt, x...) LOG_PRINT(LOG_LEVEL_PS2_MAIN, level, fmt, ##x) 29 | #endif 30 | 31 | #ifdef PMC_BUTTONS 32 | static void ps2_update_buttons(void) { 33 | int button = input_get_pressed(); 34 | switch (button) { 35 | case INPUT_KEY_BACK: 36 | ps2_mmceman_prev_ch(false); 37 | break; 38 | case INPUT_KEY_PREV: 39 | ps2_mmceman_prev_idx(false); 40 | break; 41 | case INPUT_KEY_NEXT: 42 | ps2_mmceman_next_ch(false); 43 | break; 44 | case INPUT_KEY_ENTER: 45 | ps2_mmceman_next_idx(false); 46 | break; 47 | case INPUT_KEY_BOOT: 48 | ps2_mmceman_set_bootcard(false); 49 | break; 50 | default: 51 | break; 52 | } 53 | } 54 | #endif 55 | 56 | void ps2_init(void) { 57 | log(LOG_INFO, "starting in PS2 mode\n"); 58 | mmceman_mcman_retry_counter = 0; 59 | keystore_init(); 60 | 61 | multicore_launch_core1(ps2_memory_card_main); 62 | 63 | ps2_history_tracker_init(); 64 | 65 | ps2_memory_card_enter(); 66 | 67 | ps2_mc_data_interface_init(); 68 | 69 | ps2_cardman_init(); 70 | 71 | log(LOG_INFO, "Starting memory card... "); 72 | ps2_cardman_open(); 73 | 74 | #ifdef FEAT_PS2_MMCE 75 | ps2_mmceman_fs_init(); 76 | #endif 77 | 78 | uint64_t start = time_us_64(); 79 | 80 | #if WITH_GUI 81 | gui_init(); 82 | 83 | gui_do_ps2_card_switch(); 84 | #endif 85 | uint64_t end = time_us_64(); 86 | log(LOG_INFO, "DONE! (%d us)\n", (int)(end - start)); 87 | } 88 | 89 | bool ps2_task(void) { 90 | ps2_mmceman_task(); 91 | ps2_cardman_task(); 92 | #if WITH_GUI 93 | gui_task(); 94 | input_task(); 95 | oled_task(); 96 | #elif PMC_BUTTONS 97 | input_task(); 98 | if (ps2_cardman_is_idle()) 99 | ps2_update_buttons(); 100 | #endif 101 | #if WITH_LED 102 | led_task(); 103 | #endif 104 | log(LOG_TRACE, "%s after GUI\n", __func__); 105 | #ifdef FEAT_PS2_MMCE 106 | ps2_mmceman_fs_run(); 107 | log(LOG_TRACE, "%s mmcefs\n", __func__); 108 | #endif 109 | 110 | if (ps2_cardman_is_accessible()) 111 | ps2_history_tracker_task(); 112 | 113 | if (ps2_cardman_is_idle()) 114 | ps2_mc_data_interface_task(); 115 | 116 | if (ps2_mc_auth_keyStoreResetRequired() 117 | && ps2_cardman_is_idle()) { 118 | keystore_reset(); 119 | ps2_mc_auth_keyStoreResetAck(); 120 | #if WITH_GUI 121 | gui_request_refresh(); 122 | #endif 123 | } else if (ps2_mc_auth_isValid()) { 124 | keystore_confirm(); 125 | } 126 | 127 | if ((settings_get_mode(true) == MODE_PS1) 128 | && (ps2_cardman_is_idle()) 129 | && !ps2_history_tracker_needs_refresh()) 130 | return false; 131 | 132 | return true; 133 | } 134 | 135 | void ps2_deinit(void) { 136 | 137 | ps2_memory_card_exit(); 138 | while (ps2_mc_data_interface_write_occured()) 139 | ps2_mc_data_interface_task(); 140 | multicore_reset_core1(); 141 | ps2_cardman_close(); 142 | ps2_memory_card_unload(); 143 | } -------------------------------------------------------------------------------- /src/ps2.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | 4 | void ps2_switch_card(void); 5 | void ps2_init(void); 6 | bool ps2_task(void); 7 | void ps2_deinit(void); 8 | -------------------------------------------------------------------------------- /src/ps2/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | 2 | add_library(ps2_card STATIC 3 | ${CMAKE_CURRENT_SOURCE_DIR}/mmceman/ps2_mmceman.c 4 | ${CMAKE_CURRENT_SOURCE_DIR}/mmceman/ps2_mmceman_commands.c 5 | ${CMAKE_CURRENT_SOURCE_DIR}/mmceman/ps2_mmceman_debug.c 6 | ${CMAKE_CURRENT_SOURCE_DIR}/mmceman/ps2_mmceman_fs.c 7 | ${CMAKE_CURRENT_SOURCE_DIR}/card_emu/ps2_memory_card.c 8 | ${CMAKE_CURRENT_SOURCE_DIR}/card_emu/ps2_mc_commands.c 9 | ${CMAKE_CURRENT_SOURCE_DIR}/card_emu/ps2_mc_auth.c 10 | ${CMAKE_CURRENT_SOURCE_DIR}/card_emu/ps2_mc_data_interface.c 11 | ${CMAKE_CURRENT_SOURCE_DIR}/history_tracker/ps2_history_tracker.c 12 | 13 | ${CMAKE_CURRENT_SOURCE_DIR}/ps2_cardman.c 14 | ) 15 | 16 | target_include_directories(ps2_card 17 | PUBLIC 18 | ${CMAKE_CURRENT_SOURCE_DIR} 19 | ) 20 | 21 | target_link_libraries(ps2_card PRIVATE 22 | sd2psx_common 23 | pico_stdlib 24 | hardware_sync 25 | hardware_pio 26 | hardware_flash 27 | hardware_dma 28 | PUBLIC 29 | pico_multicore 30 | mcfat 31 | sd_fat 32 | ) 33 | 34 | if (SD2PSX_WITH_PSRAM) 35 | target_link_libraries(ps2_card PRIVATE 36 | psram) 37 | target_sources(ps2_card PRIVATE 38 | ${CMAKE_CURRENT_SOURCE_DIR}/ps2_dirty.c) 39 | pico_generate_pio_header(ps2_card ${CMAKE_CURRENT_LIST_DIR}/../psram/qspi.pio) 40 | endif() 41 | 42 | 43 | configure_file(${CMAKE_CURRENT_LIST_DIR}/card_emu/ps2_mc_spi.pio ${CMAKE_CURRENT_BINARY_DIR}/ps2_mc_spi.pio) 44 | 45 | pico_generate_pio_header(ps2_card ${CMAKE_CURRENT_BINARY_DIR}/ps2_mc_spi.pio) 46 | -------------------------------------------------------------------------------- /src/ps2/card_emu/ps2_mc_auth.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "pico.h" 4 | 5 | extern void ps2_mc_auth(void); 6 | extern void ps2_mc_sessionKeyEncr(void); 7 | extern void ps2_mc_auth_keySelect(void); 8 | extern void ps2_mc_auth_reset(void); 9 | 10 | void __time_critical_func(generateIvSeedNonce)(void); 11 | 12 | 13 | extern bool ps2_mc_auth_keyStoreResetRequired(); 14 | extern void ps2_mc_auth_keyStoreResetAck(); 15 | extern bool ps2_mc_auth_isValid(); 16 | -------------------------------------------------------------------------------- /src/ps2/card_emu/ps2_mc_commands.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PS2_SIO2_CMD_IDENTIFIER 0x81 4 | #define PS1_SIO2_CMD_IDENTIFIER 0x01 5 | 6 | #define PS2_SIO2_CMD_0x11 0x11 7 | #define PS2_SIO2_CMD_0x12 0x12 8 | #define PS2_SIO2_CMD_SET_ERASE_ADDRESS 0x21 9 | #define PS2_SIO2_CMD_SET_WRITE_ADDRESS 0x22 10 | #define PS2_SIO2_CMD_SET_READ_ADDRESS 0x23 11 | #define PS2_SIO2_CMD_GET_SPECS 0x26 12 | #define PS2_SIO2_CMD_SET_TERMINATOR 0x27 13 | #define PS2_SIO2_CMD_GET_TERMINATOR 0x28 14 | #define PS2_SIO2_CMD_WRITE_DATA 0x42 15 | #define PS2_SIO2_CMD_READ_DATA 0x43 16 | #define PS2_SIO2_CMD_COMMIT_DATA 0x81 17 | #define PS2_SIO2_CMD_ERASE 0x82 18 | #define PS2_SIO2_CMD_BF 0xBF 19 | #define PS2_SIO2_CMD_AUTH_RESET 0xF3 20 | #define PS2_SIO2_CMD_KEY_SELECT 0xF7 21 | #define PS2_SIO2_CMD_AUTH 0xF0 22 | #define PS2_SIO2_CMD_SESSION_KEY_0 0xF1 23 | #define PS2_SIO2_CMD_SESSION_KEY_1 0xF2 24 | 25 | 26 | extern void ps2_mc_cmd_0x11(void); 27 | extern void ps2_mc_cmd_0x12(void); 28 | extern void ps2_mc_cmd_setEraseAddress(void); 29 | extern void ps2_mc_cmd_setWriteAddress(void); 30 | extern void ps2_mc_cmd_setReadAddress(void); 31 | extern void ps2_mc_cmd_getSpecs(void); 32 | extern void ps2_mc_cmd_setTerminator(void); 33 | extern void ps2_mc_cmd_getTerminator(void); 34 | extern void ps2_mc_cmd_writeData(void); 35 | extern void ps2_mc_cmd_readData(void); 36 | extern void ps2_mc_cmd_commitData(void); 37 | extern void ps2_mc_cmd_erase(void); 38 | extern void ps2_mc_cmd_0xBF(void); 39 | -------------------------------------------------------------------------------- /src/ps2/card_emu/ps2_mc_data_interface.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define PS2_PAGE_SIZE 512 8 | 9 | 10 | typedef struct { 11 | size_t page; 12 | enum { 13 | PAGE_EMPTY = 0, 14 | PAGE_READ_REQ = 1, 15 | PAGE_READ_AHEAD_REQ = 2, 16 | PAGE_WRITE_REQ = 3, 17 | PAGE_ERASE_REQ = 4, 18 | PAGE_DATA_AVAILABLE = 5, 19 | PAGE_READ_AHEAD_AVAILABLE = 6, 20 | } page_state; 21 | uint8_t* data; 22 | } ps2_mcdi_page_t; 23 | 24 | 25 | // Core 1 26 | 27 | void ps2_mc_data_interface_setup_read_page(uint32_t page, bool readahead, bool wait); 28 | void ps2_mc_data_interface_write_mc(uint32_t page, void *buf); 29 | void ps2_mc_data_interface_erase(uint32_t page); 30 | volatile ps2_mcdi_page_t* ps2_mc_data_interface_get_page(uint32_t page); 31 | void ps2_mc_data_interface_commit_write(uint32_t page, uint8_t *buf); 32 | bool ps2_mc_data_interface_write_busy(void); 33 | bool ps2_mc_data_interface_data_available(void); 34 | void ps2_mc_data_interface_wait_for_byte(uint32_t offset); 35 | bool ps2_mc_data_interface_delay_required(void); 36 | 37 | 38 | // Core 0 39 | void ps2_mc_data_interface_card_changed(void); 40 | void ps2_mc_data_interface_read_core0(uint32_t page, void* buff512); 41 | bool ps2_mc_data_interface_write_occured(void); 42 | void ps2_mc_data_interface_set_sdmode(bool mode); 43 | bool ps2_mc_data_interface_get_sdmode(void); 44 | void ps2_mc_data_interface_task(void); 45 | void ps2_mc_data_interface_init(void); 46 | void ps2_mc_data_interface_flush(void); 47 | -------------------------------------------------------------------------------- /src/ps2/card_emu/ps2_mc_internal.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include "../ps2_dirty.h" 4 | #include "psram/psram.h" 5 | #include "debug.h" 6 | 7 | 8 | #include 9 | #include 10 | 11 | 12 | #define ERASE_SECTORS 16 13 | #define CARD_SIZE (8 * 1024 * 1024) 14 | 15 | #define XOR8(a) (a[0] ^ a[1] ^ a[2] ^ a[3] ^ a[4] ^ a[5] ^ a[6] ^ a[7]) 16 | #define ARG8(a) a[0], a[1], a[2], a[3], a[4], a[5], a[6], a[7] 17 | 18 | enum { RECEIVE_RESET, RECEIVE_EXIT, RECEIVE_OK }; 19 | 20 | extern uint8_t term; 21 | extern uint32_t read_sector, write_sector, erase_sector; 22 | extern uint8_t writetmp[528]; 23 | extern int is_write, is_dma_read; 24 | extern uint32_t readptr, writeptr; 25 | extern uint8_t *eccptr; 26 | extern volatile bool card_active; 27 | 28 | extern const uint8_t EccTable[]; 29 | 30 | extern uint8_t receive(uint8_t *cmd); 31 | extern uint8_t receiveFirst(uint8_t *cmd); 32 | extern void __time_critical_func(mc_respond)(uint8_t ch); 33 | extern void __time_critical_func(read_mc)(uint32_t addr, void *buf, size_t sz, void (*cb)(void)); 34 | extern void __time_critical_func(write_mc)(uint32_t addr, void *buf, size_t sz); 35 | 36 | #define receiveOrNextCmd(cmd) \ 37 | if (receive(cmd) == RECEIVE_RESET) {\ 38 | DPRINTF("Reset at %s:%u", __func__, __LINE__); \ 39 | return;} 40 | -------------------------------------------------------------------------------- /src/ps2/card_emu/ps2_mc_spi.pio: -------------------------------------------------------------------------------- 1 | ; largely based on https://github.com/dangiu/PicoMemcard/blob/pmc%2B/release/psxSPI.pio 2 | 3 | ; INJECTION (${VARIANT}) 4 | .define PUBLIC PIN_PSX_ACK ${PIN_PSX_ACK} 5 | .define PUBLIC PIN_PSX_SEL ${PIN_PSX_SEL} 6 | .define PUBLIC PIN_PSX_CLK ${PIN_PSX_CLK} 7 | .define PUBLIC PIN_PSX_CMD ${PIN_PSX_CMD} 8 | .define PUBLIC PIN_PSX_DAT ${PIN_PSX_DAT} 9 | 10 | ; SD2PSX 11 | ;.define PUBLIC PIN_PSX_ACK 16 12 | ;.define PUBLIC PIN_PSX_SEL 17 13 | ;.define PUBLIC PIN_PSX_CLK 18 14 | ;.define PUBLIC PIN_PSX_CMD 19 15 | ;.define PUBLIC PIN_PSX_DAT 20 16 | ;.define PUBLIC PIN_PSX_SPD_SEL 10 17 | 18 | ; BitFunX (?) 19 | ;.define PUBLIC PIN_PSX_ACK 9 20 | ;.define PUBLIC PIN_PSX_SEL 7 21 | ;.define PUBLIC PIN_PSX_CLK 8 22 | ;.define PUBLIC PIN_PSX_CMD 6 23 | ;.define PUBLIC PIN_PSX_DAT 5 24 | ;.define PUBLIC PIN_PSX_SPD_SEL 25 25 | 26 | .program cmd_reader 27 | wait 0 gpio PIN_PSX_SEL ; wait for SEL 28 | .wrap_target 29 | wait 0 gpio PIN_PSX_CLK ; wait for clock to fall 30 | wait 1 gpio PIN_PSX_CLK ; wait for rising clock edge 31 | in pins 1 ; sample 1 bit from the CMD line 32 | .wrap 33 | 34 | .program dat_writer 35 | ; wait for SEL 36 | wait 0 gpio PIN_PSX_SEL 37 | .wrap_target 38 | ; wait for the arm core to give us a byte to send 39 | pull block ; Pull Byte for sending 40 | 41 | ack: 42 | ; Set ACK to 0 for whole transfer to speed up bus 43 | set pins, 0 44 | 45 | set x 6 ; Send 8 Bits in total 46 | out pins 1 47 | wait 0 gpio PIN_PSX_CLK 48 | sendbit: 49 | wait 1 gpio PIN_PSX_CLK 50 | out pins 1 51 | wait 0 gpio PIN_PSX_CLK 52 | jmp x-- sendbit ; Repeat until byte is sent 53 | set pins, 1 ; Set ACK to 1 54 | wait 1 gpio PIN_PSX_CLK 55 | jmp !osre ack ; If next byte already available, continue sending 56 | .wrap ; Wait for next byte 57 | 58 | 59 | % c-sdk { 60 | 61 | static inline void cmd_reader_program_init(PIO pio, uint sm, uint offset) { 62 | pio_sm_config c = cmd_reader_program_get_default_config(offset); 63 | 64 | sm_config_set_in_pins(&c, PIN_PSX_CMD); 65 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_CMD, 1, false); 66 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_SEL, 1, false); 67 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_CLK, 1, false); 68 | 69 | /* shift ISR to right, autopush every 8 bits */ 70 | sm_config_set_in_shift(&c, true, true, 8); 71 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_RX); 72 | 73 | pio_sm_init(pio, sm, offset, &c); 74 | } 75 | 76 | static inline void dat_writer_program_init(PIO pio, uint sm, uint offset) { 77 | pio_sm_config c = dat_writer_program_get_default_config(offset); 78 | 79 | sm_config_set_out_pins(&c, PIN_PSX_DAT, 1); 80 | sm_config_set_set_pins(&c, PIN_PSX_ACK, 1); 81 | 82 | /* configure ACK pin for output */ 83 | pio_sm_set_pins_with_mask(pio, sm, 1 << PIN_PSX_ACK, 1 << PIN_PSX_ACK); 84 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_ACK, 1, true); 85 | pio_gpio_init(pio, PIN_PSX_ACK); 86 | 87 | /* configure DAT pin for output */ 88 | pio_sm_set_pins_with_mask(pio, sm, 1 << PIN_PSX_DAT, 1 << PIN_PSX_DAT); 89 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_DAT, 1, true); 90 | pio_gpio_init(pio, PIN_PSX_DAT); 91 | 92 | /* SEL and CLK used as "wait" inputs only */ 93 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_SEL, 1, false); 94 | pio_sm_set_consecutive_pindirs(pio, sm, PIN_PSX_CLK, 1, false); 95 | 96 | /* shift OSR to right, autopull every 8 bits */ 97 | sm_config_set_out_shift(&c, true, true, 8); 98 | sm_config_set_fifo_join(&c, PIO_FIFO_JOIN_TX); 99 | 100 | pio_sm_init(pio, sm, offset, &c); 101 | } 102 | 103 | 104 | %} 105 | -------------------------------------------------------------------------------- /src/ps2/card_emu/ps2_memory_card.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | void ps2_memory_card_main(void); 7 | void ps2_memory_card_enter(void); 8 | void ps2_memory_card_exit(void); 9 | void ps2_memory_card_unload(void); 10 | bool ps2_memory_card_running(void); -------------------------------------------------------------------------------- /src/ps2/history_tracker/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(ps2_history_tracker STATIC 2 | ps2_history_tracker.c) 3 | 4 | target_link_libraries(ps2_history_tracker PRIVATE mcFat) -------------------------------------------------------------------------------- /src/ps2/history_tracker/ps2_history_tracker.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | #include 3 | #include 4 | 5 | 6 | void ps2_history_tracker_registerPageWrite(uint32_t page); 7 | void ps2_history_tracker_init(void); 8 | void ps2_history_tracker_task(void); 9 | void ps2_history_tracker_card_changed(void); 10 | bool ps2_history_tracker_needs_refresh(void); 11 | 12 | void ps2_history_tracker_format(); -------------------------------------------------------------------------------- /src/ps2/mmceman/ps2_mmceman.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | extern void (*mmceman_callback)(void); 7 | extern int mmceman_transfer_stage; 8 | 9 | extern volatile bool mmceman_tx_queued; 10 | extern volatile uint8_t mmceman_tx_byte; 11 | 12 | /* NOTE: Used to prevent mcman flushing old cache 13 | * data to the new memcard after a memcard switch. 14 | * mcman will invalidate handles and clear cache if 15 | * it cannot detect the memcard after 5 retries. */ 16 | extern volatile uint8_t mmceman_mcman_retry_counter; 17 | extern volatile bool mmceman_op_in_progress; 18 | extern volatile bool mmceman_timeout_detected; 19 | extern volatile bool mmceman_fs_abort_read; 20 | 21 | extern volatile uint8_t mmceman_cmd; 22 | extern volatile uint8_t mmceman_mode; 23 | extern volatile uint16_t mmceman_cnum; 24 | extern char mmceman_gameid[251]; 25 | 26 | void ps2_mmceman_task(void); 27 | 28 | void ps2_mmceman_set_cb(void (*cb)(void)); 29 | void ps2_mmceman_queue_tx(uint8_t byte); 30 | 31 | bool ps2_mmceman_set_gameid(const uint8_t* game_id); 32 | const char* ps2_mmceman_get_gameid(void); 33 | 34 | void ps2_mmceman_next_ch(bool delay); 35 | void ps2_mmceman_prev_ch(bool delay); 36 | void ps2_mmceman_next_idx(bool delay); 37 | void ps2_mmceman_prev_idx(bool delay); 38 | void ps2_mmceman_set_bootcard(bool delay); 39 | -------------------------------------------------------------------------------- /src/ps2/mmceman/ps2_mmceman_commands.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #define PS2_MMCEMAN_CMD_IDENTIFIER 0x8B 4 | 5 | #define MMCEMAN_PING 0x1 6 | #define MMCEMAN_GET_STATUS 0x2 7 | #define MMCEMAN_GET_CARD 0x3 8 | #define MMCEMAN_SET_CARD 0x4 9 | #define MMCEMAN_GET_CHANNEL 0x5 10 | #define MMCEMAN_SET_CHANNEL 0x6 11 | #define MMCEMAN_GET_GAMEID 0x7 12 | #define MMCEMAN_SET_GAMEID 0x8 13 | #define MMCEMAN_RESET 0x9 14 | 15 | //TEMP 16 | #define MMCEMAN_SWITCH_BOOTCARD 0x20 17 | #define MMCEMAN_UNMOUNT_BOOTCARD 0x30 18 | 19 | #define MMCEMAN_CMD_FS_OPEN 0x40 20 | #define MMCEMAN_CMD_FS_CLOSE 0x41 21 | #define MMCEMAN_CMD_FS_READ 0x42 22 | #define MMCEMAN_CMD_FS_WRITE 0x43 23 | #define MMCEMAN_CMD_FS_LSEEK 0x44 24 | #define MMCEMAN_CMD_FS_REMOVE 0x46 25 | #define MMCEMAN_CMD_FS_MKDIR 0x47 26 | #define MMCEMAN_CMD_FS_RMDIR 0x48 27 | #define MMCEMAN_CMD_FS_DOPEN 0x49 28 | #define MMCEMAN_CMD_FS_DCLOSE 0x4a 29 | #define MMCEMAN_CMD_FS_DREAD 0x4b 30 | #define MMCEMAN_CMD_FS_GETSTAT 0x4c 31 | 32 | #define MMCEMAN_CMD_FS_LSEEK64 0x53 33 | 34 | #define MMCEMAN_CMD_FS_READ_SECTOR 0x58 35 | 36 | #define MMCEMAN_MODE_NUM 0x0 37 | #define MMCEMAN_MODE_NEXT 0x1 38 | #define MMCEMAN_MODE_PREV 0x2 39 | 40 | extern void ps2_mmceman_cmd_ping(void); 41 | extern void ps2_mmceman_cmd_get_status(void); 42 | extern void ps2_mmceman_cmd_get_card(void); 43 | extern void ps2_mmceman_cmd_set_card(void); 44 | extern void ps2_mmceman_cmd_get_channel(void); 45 | extern void ps2_mmceman_cmd_set_channel(void); 46 | extern void ps2_mmceman_cmd_get_gameid(void); 47 | extern void ps2_mmceman_cmd_set_gameid(void); 48 | extern void ps2_mmceman_cmd_unmount_bootcard(void); 49 | extern void ps2_mmceman_cmd_reset(void); 50 | 51 | extern void ps2_mmceman_cmd_fs_open(void); 52 | extern void ps2_mmceman_cmd_fs_close(void); 53 | extern void ps2_mmceman_cmd_fs_read(void); 54 | extern void ps2_mmceman_cmd_fs_write(void); 55 | extern void ps2_mmceman_cmd_fs_lseek(void); 56 | extern void ps2_mmceman_cmd_fs_remove(void); 57 | extern void ps2_mmceman_cmd_fs_mkdir(void); 58 | extern void ps2_mmceman_cmd_fs_rmdir(void); 59 | extern void ps2_mmceman_cmd_fs_dclose(void); 60 | extern void ps2_mmceman_cmd_fs_dopen(void); 61 | extern void ps2_mmceman_cmd_fs_dread(void); 62 | extern void ps2_mmceman_cmd_fs_getstat(void); 63 | extern void ps2_mmceman_cmd_fs_lseek64(void); 64 | 65 | extern void ps2_mmceman_cmd_fs_read_sector(void); -------------------------------------------------------------------------------- /src/ps2/mmceman/ps2_mmceman_debug.c: -------------------------------------------------------------------------------- 1 | #include "ps2_mmceman_debug.h" 2 | #include "pico.h" 3 | 4 | #include 5 | 6 | struct timeval tv_start_cmd, tv_end_cmd, tv_signal_mmce_fs, tv_start_mmce_fs, tv_end_mmce_fs; 7 | 8 | //TODO: fix 9 | void mmce_profiling_stat() 10 | { 11 | long cmd_elasped = (tv_end_cmd.tv_sec - tv_start_cmd.tv_sec) * 1000000 + tv_end_cmd.tv_usec - tv_start_cmd.tv_usec; 12 | //long mmce_fs_elasped = (tv_end_mmce_fs.tv_sec - tv_start_mmce_fs.tv_sec) * 1000000 + tv_end_mmce_fs.tv_usec - tv_start_mmce_fs.tv_usec; 13 | //long cmd_mmce_fs_diff = (tv_start_mmce_fs.tv_sec - tv_signal_mmce_fs.tv_sec) * 1000000 + tv_start_mmce_fs.tv_usec - tv_signal_mmce_fs.tv_usec; 14 | 15 | printf("[STAT] Command completed in: %liuS\n", cmd_elasped); 16 | //printf("MMCE FS time: %liuS\n", mmce_fs_elasped); 17 | //printf("Signal op -> start of op: %liuS\n", cmd_mmce_fs_diff); 18 | printf("\n"); 19 | } 20 | -------------------------------------------------------------------------------- /src/ps2/mmceman/ps2_mmceman_debug.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #define MMCEMAN_PROFILING 0 8 | 9 | #if MMCEMAN_PROFILING == 0 10 | #define MP_CMD_START(x...) 11 | #define MP_CMD_END(x...) 12 | #define MP_SIGNAL_OP(x...) 13 | #define MP_OP_START(x...) 14 | #define MP_OP_END(x...) 15 | #else 16 | extern struct timeval tv_start_cmd, tv_end_cmd, tv_signal_mmce_fs, tv_start_mmce_fs, tv_end_mmce_fs; 17 | extern void mmce_profiling_stat(); 18 | #define MP_CMD_START() gettimeofday(&tv_start_cmd, 0) 19 | #define MP_SIGNAL_OP() gettimeofday(&tv_signal_mmce_fs, 0) 20 | #define MP_OP_START() gettimeofday(&tv_start_mmce_fs, 0) 21 | #define MP_OP_END() gettimeofday(&tv_end_mmce_fs, 0) 22 | #define MP_CMD_END() do { \ 23 | gettimeofday(&tv_end_cmd, 0); \ 24 | mmce_profiling_stat(); \ 25 | } while (0) 26 | #endif 27 | 28 | 29 | 30 | 31 | -------------------------------------------------------------------------------- /src/ps2/mmceman/ps2_mmceman_fs.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "pico/multicore.h" 8 | #include "pico/critical_section.h" 9 | #include "pico/mutex.h" 10 | 11 | #include "sd.h" 12 | 13 | //TODO: enumify 14 | #define MMCEMAN_FS_NONE 0x0 15 | #define MMCEMAN_FS_OPEN 0x1 16 | #define MMCEMAN_FS_CLOSE 0x2 17 | #define MMCEMAN_FS_READ 0x3 18 | #define MMCEMAN_FS_WRITE 0x4 19 | #define MMCEMAN_FS_LSEEK 0x5 20 | #define MMCEMAN_FS_IOCTL 0x6 21 | #define MMCEMAN_FS_REMOVE 0x7 22 | #define MMCEMAN_FS_MKDIR 0x8 23 | #define MMCEMAN_FS_RMDIR 0x9 24 | #define MMCEMAN_FS_DOPEN 0xA 25 | #define MMCEMAN_FS_DCLOSE 0xB 26 | #define MMCEMAN_FS_DREAD 0xC 27 | #define MMCEMAN_FS_GETSTAT 0xD 28 | #define MMCEMAN_FS_VALIDATE_FD 0xE 29 | #define MMCEMAN_FS_READ_AHEAD 0xF 30 | #define MMCEMAN_FS_LSEEK64 0x10 31 | 32 | #define MMCEMAN_FS_RESET 0x11 33 | 34 | #define CHUNK_SIZE 256 35 | #define CHUNK_COUNT 15 36 | 37 | #define CHUNK_STATE_NOT_READY 0x0 38 | #define CHUNK_STATE_READY 0x1 39 | #define CHUNK_STATE_INVALID 0x2 40 | 41 | //Single chunk read ahead on open, lseek, and after read 42 | typedef struct ps2_mmceman_fs_read_ahead_t { 43 | int fd; 44 | int valid; 45 | uint64_t pos; 46 | uint8_t buffer[CHUNK_SIZE]; 47 | } ps2_mmceman_fs_read_ahead_t; 48 | 49 | typedef struct ps2_mmceman_fs_op_data_t { 50 | int rv; 51 | int fd; 52 | int flags; //file flags 53 | int it_fd[17]; //iterator dir 54 | 55 | uint64_t filesize; 56 | 57 | int offset; 58 | int position; 59 | uint8_t whence; 60 | 61 | int64_t offset64; 62 | int64_t position64; 63 | uint8_t whence64; 64 | 65 | uint32_t length; //length of transfer, read only 66 | uint32_t bytes_read; //stop reading when == length 67 | uint32_t bytes_written; // 68 | uint32_t bytes_transferred; //stop sending when == length 69 | 70 | uint8_t tail_idx; //read ring tail idx 71 | uint8_t head_idx; //read ring head idx 72 | 73 | uint8_t buffer[CHUNK_COUNT + 1][CHUNK_SIZE]; 74 | volatile uint8_t chunk_state[CHUNK_COUNT + 1]; //written to by both cores, writes encased in critical section 75 | 76 | uint8_t transfer_failed; 77 | int use_read_ahead; 78 | ps2_mmceman_fs_read_ahead_t read_ahead; 79 | 80 | ps2_fileio_stat_t fileio_stat; 81 | } ps2_mmceman_fs_op_data_t; 82 | 83 | extern critical_section_t mmceman_fs_crit; //used to lock writes to chunk_state (mmceman_commands <-> ps2_mmceman_fs) 84 | 85 | /* Flow (Core 1): 86 | * enter cmd handler function 87 | * ps2_mmceman_fs_wait_ready(); core1 waits for core0 to finish any ops (shouldn't be any) 88 | * write necessary data to mmce_fs_data_t 89 | * ps2_mmceman_fs_signal_op(int op); signal core0 to perform op 90 | * ps2_mmceman_fs_wait_ready(); wait for op to be completed 91 | * OR 92 | * ps2_mmceman_fs_is_ready(); polled by PS2 ready packet (used only with writes) 93 | * OR 94 | * poll chunk state or other status var (read and dread do this) 95 | * access data 96 | * repeat 97 | */ 98 | 99 | //Core 0 100 | bool ps2_mmceman_fs_idle(void); 101 | void ps2_mmceman_fs_init(void); 102 | void ps2_mmceman_fs_run(void); 103 | 104 | //Core 1 105 | void ps2_mmceman_fs_wait_ready(); 106 | void ps2_mmceman_fs_signal_operation(int op); 107 | int ps2_mmceman_fs_get_operation(); 108 | 109 | 110 | ps2_mmceman_fs_op_data_t *ps2_mmceman_fs_get_op_data(void); 111 | -------------------------------------------------------------------------------- /src/ps2/ps2_cardman.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | 8 | #define PS2_CARD_SIZE_128M (128 * 1024 * 1024) 9 | #define PS2_CARD_SIZE_64M (64 * 1024 * 1024) 10 | #define PS2_CARD_SIZE_32M (32 * 1024 * 1024) 11 | #define PS2_CARD_SIZE_16M (16 * 1024 * 1024) 12 | #define PS2_CARD_SIZE_8M (8 * 1024 * 1024) 13 | #define PS2_CARD_SIZE_4M (4 * 1024 * 1024) 14 | #define PS2_CARD_SIZE_2M (2 * 1024 * 1024) 15 | #define PS2_CARD_SIZE_1M (1024 * 1024) 16 | #define PS2_CARD_SIZE_512K (512 * 1024) 17 | 18 | #define PS2_CARD_IDX_SPECIAL 0 19 | 20 | typedef enum { 21 | PS2_CM_STATE_NAMED, 22 | PS2_CM_STATE_BOOT, 23 | PS2_CM_STATE_GAMEID, 24 | PS2_CM_STATE_NORMAL 25 | } ps2_cardman_state_t; 26 | 27 | extern int cardman_fd; 28 | 29 | void ps2_cardman_init(void); 30 | void ps2_cardman_task(void); 31 | int ps2_cardman_read_sector(int sector, void *buf512); 32 | int ps2_cardman_write_sector(int sector, void *buf512); 33 | bool ps2_cardman_is_sector_available(int sector); 34 | void ps2_cardman_mark_sector_available(int sector); 35 | void ps2_cardman_set_priority_sector(int page_idx); 36 | void ps2_cardman_flush(void); 37 | void ps2_cardman_open(void); 38 | void ps2_cardman_close(void); 39 | int ps2_cardman_get_idx(void); 40 | int ps2_cardman_get_channel(void); 41 | uint32_t ps2_cardman_get_card_size(void); 42 | 43 | void ps2_cardman_set_channel(uint16_t num); 44 | void ps2_cardman_next_channel(void); 45 | void ps2_cardman_prev_channel(void); 46 | 47 | void ps2_cardman_switch_bootcard(void); 48 | 49 | void ps2_cardman_set_idx(uint16_t num); 50 | void ps2_cardman_next_idx(void); 51 | void ps2_cardman_prev_idx(void); 52 | 53 | typedef void (*cardman_cb_t)(int, bool); 54 | 55 | void ps2_cardman_set_progress_cb(cardman_cb_t func); 56 | char *ps2_cardman_get_progress_text(void); 57 | 58 | void ps2_cardman_set_gameid(const char* game_id); 59 | const char* ps2_cardman_get_folder_name(void); 60 | ps2_cardman_state_t ps2_cardman_get_state(void); 61 | 62 | void ps2_cardman_set_variant(int variant); 63 | 64 | bool ps2_cardman_needs_update(void); 65 | bool ps2_cardman_is_accessible(void); 66 | bool ps2_cardman_is_idle(void); 67 | -------------------------------------------------------------------------------- /src/ps2/ps2_dirty.c: -------------------------------------------------------------------------------- 1 | #include "ps2_dirty.h" 2 | #include "history_tracker/ps2_history_tracker.h" 3 | #include "psram.h" 4 | #include "ps2_cardman.h" 5 | #include "debug.h" 6 | 7 | #include "bigmem.h" 8 | #define dirty_heap bigmem.ps2.dirty_heap 9 | #define dirty_map bigmem.ps2.dirty_map 10 | 11 | #include 12 | #include 13 | #include 14 | 15 | spin_lock_t *ps2_dirty_spin_lock; 16 | volatile uint32_t ps2_dirty_lockout; 17 | int ps2_dirty_activity = 0; 18 | 19 | static int num_dirty; 20 | 21 | #define SWAP(a, b) do { \ 22 | uint16_t tmp = a; \ 23 | a = b; \ 24 | b = tmp; \ 25 | } while (0); 26 | 27 | static inline bool dirty_map_is_marked(uint32_t sector) { 28 | return dirty_map[sector / 8] & (1 << (sector % 8)); 29 | } 30 | 31 | static inline void dirty_map_mark_sector(uint32_t sector) { 32 | dirty_map[sector / 8] |= (1 << (sector % 8)); 33 | } 34 | 35 | static inline void dirty_map_unmark_sector(uint32_t sector) { 36 | dirty_map[sector / 8] &= ~(1 << (sector % 8)); 37 | } 38 | 39 | void ps2_dirty_init(void) { 40 | if (!ps2_dirty_spin_lock) 41 | ps2_dirty_spin_lock = spin_lock_init(spin_lock_claim_unused(1)); 42 | } 43 | 44 | void __time_critical_func(ps2_dirty_mark)(uint32_t sector) { 45 | if (sector < (sizeof(dirty_map) * 8)) { 46 | /* already marked? */ 47 | if (dirty_map_is_marked(sector)) 48 | return; 49 | 50 | /* update map */ 51 | dirty_map_mark_sector(sector); 52 | 53 | /* update heap */ 54 | int cur = num_dirty++; 55 | dirty_heap[cur] = sector; 56 | while (dirty_heap[cur] < dirty_heap[(cur-1)/2]) { 57 | SWAP(dirty_heap[cur], dirty_heap[(cur-1)/2]); 58 | cur = (cur-1)/2; 59 | } 60 | } 61 | } 62 | 63 | static void heapify(int i) { 64 | int l = i * 2 + 1; 65 | int r = i * 2 + 2; 66 | int best = i; 67 | if (l < num_dirty && dirty_heap[l] < dirty_heap[best]) 68 | best = l; 69 | if (r < num_dirty && dirty_heap[r] < dirty_heap[best]) 70 | best = r; 71 | if (best != i) { 72 | SWAP(dirty_heap[i], dirty_heap[best]); 73 | heapify(best); 74 | } 75 | } 76 | 77 | int ps2_dirty_get_marked(void) { 78 | if (num_dirty == 0) 79 | return -1; 80 | 81 | uint16_t ret = dirty_heap[0]; 82 | 83 | /* update heap */ 84 | dirty_heap[0] = dirty_heap[--num_dirty]; 85 | heapify(0); 86 | 87 | /* update map */ 88 | dirty_map_unmark_sector(ret); 89 | 90 | return ret; 91 | } 92 | 93 | /* this goes through blocks in psram marked as dirty and flushes them to sd */ 94 | void ps2_dirty_task(void) { 95 | static uint8_t flushbuf[512]; 96 | 97 | int num_after = 0; 98 | int hit = 0; 99 | uint64_t start = time_us_64(); 100 | while (1) { 101 | if (!ps2_dirty_lockout_expired()) 102 | break; 103 | /* do up to 100ms of work per call to dirty_taks */ 104 | if ((time_us_64() - start) > 100 * 1000) 105 | break; 106 | 107 | ps2_dirty_lock(); 108 | int sector = ps2_dirty_get_marked(); 109 | num_after = num_dirty; 110 | if (sector == -1) { 111 | ps2_dirty_unlock(); 112 | break; 113 | } 114 | psram_read_dma(sector * 512, flushbuf, 512, NULL); 115 | psram_wait_for_dma(); 116 | ps2_dirty_unlock(); 117 | 118 | ++hit; 119 | 120 | if (ps2_cardman_write_sector(sector, flushbuf) != 0) { 121 | // TODO: do something if we get too many errors? 122 | // for now lets push it back into the heap and try again later 123 | DPRINTF("!! writing sector 0x%x failed\n", sector); 124 | 125 | ps2_dirty_lock(); 126 | ps2_dirty_mark(sector); 127 | ps2_dirty_unlock(); 128 | } 129 | //DPRINTF("Writing %u\n", sector); 130 | ps2_history_tracker_registerPageWrite(sector); 131 | } 132 | /* to make sure writes hit the storage medium */ 133 | ps2_cardman_flush(); 134 | 135 | uint64_t end = time_us_64(); 136 | 137 | if (hit) 138 | DPRINTF("remain to flush - %d - this one flushed %d and took %d ms\n", num_after, hit, (int)((end - start) / 1000)); 139 | 140 | if (num_after || !ps2_dirty_lockout_expired()) 141 | ps2_dirty_activity = 1; 142 | else 143 | ps2_dirty_activity = 0; 144 | } 145 | -------------------------------------------------------------------------------- /src/ps2/ps2_dirty.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include "hardware/sync.h" 5 | #include "hardware/timer.h" 6 | #include "pico/platform.h" 7 | 8 | #include "util.h" 9 | 10 | extern spin_lock_t *ps2_dirty_spin_lock; 11 | extern volatile uint32_t ps2_dirty_lockout; 12 | 13 | static inline void __time_critical_func(ps2_dirty_lock)(void) { 14 | spin_lock_unsafe_blocking(ps2_dirty_spin_lock); 15 | } 16 | 17 | static inline void __time_critical_func(ps2_dirty_unlock)(void) { 18 | spin_unlock_unsafe(ps2_dirty_spin_lock); 19 | } 20 | 21 | static inline void __time_critical_func(ps2_dirty_lockout_renew)(void) { 22 | /* lockout for 100ms, store time in ms */ 23 | ps2_dirty_lockout = (uint32_t)(RAM_time_us_64() / 1000) + 100; 24 | } 25 | 26 | static inline int __time_critical_func(ps2_dirty_lockout_expired)(void) { 27 | return ps2_dirty_lockout < (uint32_t)(RAM_time_us_64() / 1000); 28 | } 29 | 30 | void ps2_dirty_init(void); 31 | int ps2_dirty_get_marked(void); 32 | void ps2_dirty_mark(uint32_t sector); 33 | void ps2_dirty_task(void); 34 | 35 | extern int ps2_dirty_activity; 36 | -------------------------------------------------------------------------------- /src/psram/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_library(psram STATIC 2 | ${CMAKE_CURRENT_SOURCE_DIR}/psram.c 3 | ${CMAKE_CURRENT_SOURCE_DIR}/pio_qspi.c) 4 | 5 | target_include_directories(psram PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 6 | 7 | target_link_libraries(psram PRIVATE 8 | hardware_pio 9 | hardware_dma 10 | sd2psx_common 11 | ) 12 | 13 | pico_generate_pio_header(psram ${CMAKE_CURRENT_LIST_DIR}/qspi.pio) 14 | -------------------------------------------------------------------------------- /src/psram/pio_qspi.h: -------------------------------------------------------------------------------- 1 | /** 2 | * Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | * 4 | * SPDX-License-Identifier: BSD-3-Clause 5 | */ 6 | #ifndef _PIO_SPI_H 7 | #define _PIO_SPI_H 8 | 9 | #include "hardware/pio.h" 10 | #include "qspi.pio.h" 11 | 12 | extern int PIO_SPI_DMA_TX_CMD_CHAN; 13 | extern int PIO_SPI_DMA_RX_CMD_CHAN; 14 | extern int PIO_SPI_DMA_TX_DATA_CHAN; 15 | extern int PIO_SPI_DMA_RX_DATA_CHAN; 16 | 17 | typedef struct pio_spi_inst { 18 | PIO pio; 19 | uint sm; 20 | uint cs_pin; 21 | } pio_spi_inst_t; 22 | 23 | void pio_spi_write8_blocking(const pio_spi_inst_t *spi, const uint8_t *src, size_t len); 24 | 25 | void pio_spi_read8_blocking(const pio_spi_inst_t *spi, uint8_t *dst, size_t len); 26 | 27 | void pio_spi_write8_read8_blocking(const pio_spi_inst_t *spi, uint8_t *src, size_t srclen, uint8_t *dst, size_t dstlen); 28 | 29 | void pio_qspi_write8_read8_blocking(const pio_spi_inst_t *spi, uint8_t *cmd, uint8_t *src, size_t srclen, uint8_t *dst, size_t dstlen); 30 | 31 | void pio_qspi_write8_dma(const pio_spi_inst_t *spi, uint32_t addr, uint8_t *dst, size_t dstlen, void (*cb)(void)); 32 | 33 | void pio_qspi_read8_dma(const pio_spi_inst_t *spi, uint32_t addr, uint8_t *src, size_t srclen, void (*cb)(void)); 34 | 35 | void pio_qspi_dma_init(const pio_spi_inst_t *spi); 36 | 37 | #define pio_qspi_write8_blocking(spi, addr, buf, buf_len) do { \ 38 | uint8_t cmd_write[4] = { 0x38, (addr & 0xFF0000) >> 16, (addr & 0xFF00) >> 8, (addr & 0xFF) }; \ 39 | pio_qspi_write8_read8_blocking(spi, cmd_write, buf, buf_len, NULL, 0); \ 40 | } while (0); 41 | 42 | #define pio_qspi_read8_blocking(spi, addr, buf, buf_len) do { \ 43 | uint8_t cmd_read[4] = { 0xEB, (addr & 0xFF0000) >> 16, (addr & 0xFF00) >> 8, (addr & 0xFF) }; \ 44 | pio_qspi_write8_read8_blocking(spi, cmd_read, NULL, 0, buf, buf_len); \ 45 | } while (0); 46 | 47 | bool pio_qspi_dma_active(); 48 | 49 | #endif 50 | -------------------------------------------------------------------------------- /src/psram/psram.c: -------------------------------------------------------------------------------- 1 | #include "config.h" 2 | #include "pico.h" 3 | #include "psram.h" 4 | 5 | #include "pico/critical_section.h" 6 | #include "pio_qspi.h" 7 | #include "hardware/timer.h" 8 | #include "hardware/dma.h" 9 | 10 | #include 11 | #include 12 | 13 | #include "debug.h" 14 | 15 | static pio_spi_inst_t spi = { 16 | .pio = pio1, 17 | .sm = 0, 18 | .cs_pin = PSRAM_CS 19 | }; 20 | 21 | static critical_section_t crit_psram; 22 | 23 | 24 | #define SPI_OP(stmt) \ 25 | do { \ 26 | gpio_put(spi.cs_pin, 0); \ 27 | stmt; \ 28 | gpio_put(spi.cs_pin, 1); \ 29 | } while (0); 30 | 31 | #define TEST_CYCLES 30 32 | #define TEST_BLOCK_SIZE 1024 33 | 34 | typedef void (*test_t)(uint8_t *dst); 35 | 36 | static void prepare_test_0(uint8_t *dst) { 37 | for (size_t i = 0; i < TEST_BLOCK_SIZE; ++i) 38 | dst[i] = (i % 2 == 0) ? 0x55 : 0xAA; 39 | } 40 | 41 | static void prepare_test_1(uint8_t *dst) { 42 | for (size_t i = 0; i < TEST_BLOCK_SIZE; ++i) 43 | dst[i] = (i % 2 == 0) ? 0xFF : 0x00; 44 | } 45 | 46 | static void prepare_test_2(uint8_t *dst) { 47 | for (size_t i = 0; i < TEST_BLOCK_SIZE; ++i) 48 | dst[i] = i % 256; 49 | } 50 | 51 | static void prepare_test_3(uint8_t *dst) { 52 | uint32_t rng = 1; 53 | 54 | for (size_t i = 0; i < TEST_BLOCK_SIZE; ++i) { 55 | rng = rng * 1103515245 + 12345; 56 | dst[i] = rng % 256; 57 | } 58 | } 59 | 60 | static test_t psram_tests[] = { 61 | prepare_test_0, 62 | prepare_test_1, 63 | prepare_test_2, 64 | prepare_test_3, 65 | }; 66 | 67 | #define NUM_TESTS (sizeof(psram_tests)/sizeof(*psram_tests)) 68 | 69 | void __time_critical_func(psram_read_dma)(uint32_t addr, void *vbuf, size_t sz, void (*cb)(void)) { 70 | uint8_t *buf = vbuf; 71 | critical_section_enter_blocking(&crit_psram); 72 | gpio_put(spi.cs_pin, 0); 73 | pio_qspi_read8_dma(&spi, addr, buf, sz, cb); 74 | critical_section_exit(&crit_psram); 75 | } 76 | 77 | void __time_critical_func(psram_write_dma)(uint32_t addr, void *vbuf, size_t sz, void (*cb)(void)) { 78 | uint8_t *buf = vbuf; 79 | critical_section_enter_blocking(&crit_psram); 80 | gpio_put(spi.cs_pin, 0); 81 | pio_qspi_write8_dma(&spi, addr, buf, sz, cb); 82 | critical_section_exit(&crit_psram); 83 | } 84 | 85 | void __time_critical_func(psram_write)(uint32_t addr, void *vbuf, size_t sz) { 86 | uint8_t *buf = vbuf; 87 | critical_section_enter_blocking(&crit_psram); 88 | gpio_put(spi.cs_pin, 0); 89 | pio_qspi_write8_blocking(&spi, addr, buf, sz); 90 | critical_section_exit(&crit_psram); 91 | } 92 | 93 | uint32_t psram_write_dma_remaining() { 94 | return dma_channel_hw_addr(PIO_SPI_DMA_TX_DATA_CHAN)->transfer_count; 95 | } 96 | uint32_t psram_read_dma_remaining() { 97 | return dma_channel_hw_addr(PIO_SPI_DMA_RX_DATA_CHAN)->transfer_count; 98 | } 99 | 100 | inline void psram_wait_for_dma() { 101 | //while(pio_qspi_dma_active()) {tight_loop_contents(); 102 | //printf("Rd: %u Wr: %u Reg: %08x\n", psram_read_dma_remaining(), psram_write_dma_remaining(), dma_channel_hw_addr(PIO_SPI_DMA_RX_DATA_CHAN)->ctrl_trig); if (cnt-- == 0) fatal("Took too long"); 103 | //}; 104 | dma_channel_wait_for_finish_blocking(PIO_SPI_DMA_TX_CMD_CHAN); 105 | dma_channel_wait_for_finish_blocking(PIO_SPI_DMA_RX_CMD_CHAN); 106 | dma_channel_wait_for_finish_blocking(PIO_SPI_DMA_TX_DATA_CHAN); 107 | dma_channel_wait_for_finish_blocking(PIO_SPI_DMA_RX_DATA_CHAN); 108 | } 109 | 110 | static void psram_run_tests(void) { 111 | uint8_t buf_write[TEST_BLOCK_SIZE] = { 0 }; 112 | uint8_t buf_read[TEST_BLOCK_SIZE] = { 0 }; 113 | 114 | uint64_t start = time_us_64(); 115 | 116 | for (size_t test = 0; test < NUM_TESTS; ++test) { 117 | printf("Start PSRAM test %d\n", test); 118 | psram_tests[test](buf_write); 119 | 120 | uint32_t addr = 0; 121 | for (size_t i = 0; i < TEST_CYCLES; ++i) { 122 | memset(buf_read, 0, sizeof(buf_read)); 123 | 124 | psram_write_dma(addr, buf_write, sizeof(buf_write), NULL); 125 | psram_wait_for_dma(); 126 | 127 | psram_read_dma(addr, buf_read, sizeof(buf_read), NULL); 128 | psram_wait_for_dma(); 129 | 130 | if (memcmp(buf_write, buf_read, TEST_BLOCK_SIZE) != 0) { 131 | printf("test %d cycle %d\n", test, i); 132 | fatal(ERR_PSRAM, "PSRAM failed test"); 133 | } 134 | 135 | addr += TEST_BLOCK_SIZE; 136 | } 137 | } 138 | 139 | uint64_t end = time_us_64(); 140 | printf("PSRAM passed all tests -- took %.2f ms -- avg speed %.2f kB/s\n", 141 | (end - start) / 1000.0, 142 | 1000000.0 * (NUM_TESTS * TEST_CYCLES * TEST_BLOCK_SIZE * 2) / (end - start) / 1024); 143 | } 144 | 145 | void psram_init(void) { 146 | uint32_t offset; 147 | 148 | gpio_init(spi.cs_pin); 149 | gpio_put(spi.cs_pin, 1); 150 | gpio_set_dir(spi.cs_pin, GPIO_OUT); 151 | 152 | /* start in SPI mode */ 153 | offset = pio_add_program(spi.pio, &spi_cpha0_program); 154 | pio_spi_init(spi.pio, spi.sm, offset, 8, PSRAM_CLKDIV, 0, 0, PSRAM_CLK, PSRAM_DAT, PSRAM_DAT+1); 155 | 156 | /* make sure PSRAM chip is marked as Known Good Die as per datasheet */ 157 | uint8_t read_id[] = { 0x9F, 0x00, 0x00, 0x00 }; 158 | uint8_t read_id_rsp[32] = { 0 }; 159 | SPI_OP(pio_spi_write8_read8_blocking(&spi, read_id, sizeof(read_id), read_id_rsp, sizeof(read_id_rsp))); 160 | 161 | if (read_id_rsp[3] != 0x0D || read_id_rsp[4] != 0x5D) { 162 | fatal("PSRAM ID is not Known Good Die\nexpected 0D 5D got %02X %02X\n\ntry to power-cycle it if PSRAM\nis stuck in the QPI mode", 163 | read_id_rsp[3], read_id_rsp[4]); 164 | } 165 | 166 | /* switch from SPI to Quad-SPI mode */ 167 | uint8_t enter_qspi[] = { 0x35 }; 168 | SPI_OP(pio_spi_write8_read8_blocking(&spi, enter_qspi, sizeof(enter_qspi), NULL, 0)); 169 | 170 | pio_remove_program(spi.pio, &spi_cpha0_program, offset); 171 | offset = pio_add_program(spi.pio, &qspi_cpha0_program); 172 | pio_qspi_init(spi.pio, spi.sm, offset, 8, PSRAM_CLKDIV, 0, 0, PSRAM_CLK, PSRAM_DAT); 173 | pio_qspi_dma_init(&spi); 174 | 175 | critical_section_init(&crit_psram); 176 | 177 | /* validate PSRAM is working properly */ 178 | psram_run_tests(); 179 | 180 | /* and erase everything to 0xFF */ 181 | uint8_t erasebuf[512]; 182 | memset(erasebuf, 0xFF, sizeof(erasebuf)); 183 | for (int i = 0; i < 8 * 1024 * 1024; i += 512) { 184 | psram_write_dma(i, erasebuf, sizeof(erasebuf), NULL); 185 | psram_wait_for_dma(); 186 | } 187 | } 188 | -------------------------------------------------------------------------------- /src/psram/psram.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | void psram_init(void); 7 | void psram_read(uint32_t addr, void *buf, size_t sz); 8 | void psram_write(uint32_t addr, void *buf, size_t sz); 9 | void psram_read_dma(uint32_t addr, void *buf, size_t sz, void (*cb)(void)); 10 | void psram_write_dma(uint32_t addr, void *buf, size_t sz, void (*cb)(void)); 11 | uint32_t psram_write_dma_remaining(); 12 | uint32_t psram_read_dma_remaining(); 13 | void psram_wait_for_dma(); 14 | -------------------------------------------------------------------------------- /src/psram/qspi.pio: -------------------------------------------------------------------------------- 1 | ; 2 | ; Copyright (c) 2020 Raspberry Pi (Trading) Ltd. 3 | ; 4 | ; SPDX-License-Identifier: BSD-3-Clause 5 | ; 6 | 7 | ; These programs implement full-duplex SPI, with a SCK period of 4 clock 8 | ; cycles. A different program is provided for each value of CPHA, and CPOL is 9 | ; achieved using the hardware GPIO inversion available in the IO controls. 10 | ; 11 | ; Transmit-only SPI can go twice as fast -- see the ST7789 example! 12 | 13 | 14 | .program spi_cpha0 15 | .side_set 1 16 | 17 | ; Pin assignments: 18 | ; - SCK is side-set pin 0 19 | ; - MOSI is OUT pin 0 20 | ; - MISO is IN pin 0 21 | ; 22 | ; Autopush and autopull must be enabled, and the serial frame size is set by 23 | ; configuring the push/pull threshold. Shift left/right is fine, but you must 24 | ; justify the data yourself. This is done most conveniently for frame sizes of 25 | ; 8 or 16 bits by using the narrow store replication and narrow load byte 26 | ; picking behaviour of RP2040's IO fabric. 27 | 28 | ; Clock phase = 0: data is captured on the leading edge of each SCK pulse, and 29 | ; transitions on the trailing edge, or some time before the first leading edge. 30 | 31 | out pins, 1 side 0 [1] ; Stall here on empty (sideset proceeds even if 32 | in pins, 1 side 1 [1] ; instruction stalls, so we stall with SCK low) 33 | 34 | .program qspi_cpha0 35 | .side_set 1 36 | out pins, 4 side 0 [1] 37 | in pins, 4 side 1 [1] 38 | 39 | % c-sdk { 40 | #include "hardware/gpio.h" 41 | static inline void pio_spi_init(PIO pio, uint sm, uint prog_offs, uint n_bits, 42 | float clkdiv, bool cpha, bool cpol, uint pin_sck, uint pin_mosi, uint pin_miso) { 43 | (void)cpha; 44 | pio_sm_config c = spi_cpha0_program_get_default_config(prog_offs); 45 | sm_config_set_out_pins(&c, pin_mosi, 1); 46 | sm_config_set_in_pins(&c, pin_miso); 47 | sm_config_set_sideset_pins(&c, pin_sck); 48 | // Only support MSB-first in this example code (shift to left, auto push/pull, threshold=nbits) 49 | sm_config_set_out_shift(&c, false, true, n_bits); 50 | sm_config_set_in_shift(&c, false, true, n_bits); 51 | sm_config_set_clkdiv(&c, clkdiv); 52 | 53 | // MOSI, SCK output are low, MISO is input 54 | pio_sm_set_pins_with_mask(pio, sm, 0, (1u << pin_sck) | (1u << pin_mosi)); 55 | pio_sm_set_pindirs_with_mask(pio, sm, (1u << pin_sck) | (1u << pin_mosi), (1u << pin_sck) | (1u << pin_mosi) | (1u << pin_miso)); 56 | pio_gpio_init(pio, pin_mosi); 57 | pio_gpio_init(pio, pin_miso); 58 | pio_gpio_init(pio, pin_sck); 59 | 60 | // The pin muxes can be configured to invert the output (among other things 61 | // and this is a cheesy way to get CPOL=1 62 | gpio_set_outover(pin_sck, cpol ? GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL); 63 | // SPI is synchronous, so bypass input synchroniser to reduce input delay. 64 | hw_set_bits(&pio->input_sync_bypass, 1u << pin_miso); 65 | 66 | pio_sm_init(pio, sm, prog_offs, &c); 67 | pio_sm_set_enabled(pio, sm, true); 68 | } 69 | 70 | static inline void pio_qspi_init(PIO pio, uint sm, uint prog_offs, uint n_bits, 71 | float clkdiv, bool cpha, bool cpol, uint pin_sck, uint pin_dat) { 72 | (void)cpha; 73 | pio_sm_config c = qspi_cpha0_program_get_default_config(prog_offs); 74 | sm_config_set_out_pins(&c, pin_dat, 4); 75 | sm_config_set_in_pins(&c, pin_dat); 76 | sm_config_set_sideset_pins(&c, pin_sck); 77 | // Only support MSB-first in this example code (shift to left, auto push/pull, threshold=nbits) 78 | sm_config_set_out_shift(&c, false, true, n_bits); 79 | sm_config_set_in_shift(&c, false, true, n_bits); 80 | sm_config_set_clkdiv(&c, clkdiv); 81 | 82 | // MOSI, SCK output are low, MISO is input 83 | pio_sm_set_pins_with_mask(pio, sm, 0, (1u << pin_sck)); 84 | pio_sm_set_pindirs_with_mask(pio, sm, (1u << pin_sck), (1u << pin_sck) | (1u << pin_dat) | (1u << (pin_dat+1)) | (1u << (pin_dat+2)) | (1u << (pin_dat+3))); 85 | pio_gpio_init(pio, pin_dat); 86 | pio_gpio_init(pio, pin_dat+1); 87 | pio_gpio_init(pio, pin_dat+2); 88 | pio_gpio_init(pio, pin_dat+3); 89 | pio_gpio_init(pio, pin_sck); 90 | 91 | // The pin muxes can be configured to invert the output (among other things 92 | // and this is a cheesy way to get CPOL=1 93 | gpio_set_outover(pin_sck, cpol ? GPIO_OVERRIDE_INVERT : GPIO_OVERRIDE_NORMAL); 94 | // SPI is synchronous, so bypass input synchroniser to reduce input delay. 95 | hw_set_bits(&pio->input_sync_bypass, (1u << pin_dat) | (1u << (pin_dat+1)) | (1u << (pin_dat+2)) | (1u << (pin_dat+3))); 96 | 97 | pio_sm_init(pio, sm, prog_offs, &c); 98 | pio_sm_set_enabled(pio, sm, true); 99 | } 100 | %} 101 | -------------------------------------------------------------------------------- /src/settings.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | #include 5 | 6 | void settings_load_sd(void); 7 | void settings_init(void); 8 | 9 | int settings_get_ps1_card(void); 10 | int settings_get_ps1_channel(void); 11 | int settings_get_ps1_boot_channel(void); 12 | void settings_set_ps1_card(int x); 13 | void settings_set_ps1_channel(int x); 14 | void settings_set_ps1_boot_channel(int x); 15 | 16 | int settings_get_ps2_card(void); 17 | int settings_get_ps2_channel(void); 18 | int settings_get_ps2_boot_channel(void); 19 | uint8_t settings_get_ps2_cardsize(void); 20 | int settings_get_ps2_variant(void); 21 | void settings_set_ps2_card(int x); 22 | void settings_set_ps2_channel(int x); 23 | void settings_set_ps2_boot_channel(int x); 24 | void settings_set_ps2_cardsize(uint8_t size); 25 | void settings_set_ps2_variant(int x); 26 | 27 | enum { 28 | MODE_PS1 = 0, 29 | MODE_PS2 = 1, 30 | MODE_TEMP_PS1 = 2 31 | }; 32 | 33 | enum { 34 | PS2_VARIANT_RETAIL = 0, // Retail 35 | PS2_VARIANT_COH = 1, // Arcade. Port 1 36 | PS2_VARIANT_PROTO = 2, // Prototype 37 | PS2_VARIANT_SC2 = 3, // Arcade. port 2 38 | }; 39 | 40 | int settings_get_mode(bool current); 41 | void settings_set_mode(int mode); 42 | bool settings_get_ps1_autoboot(void); 43 | void settings_set_ps1_autoboot(bool autoboot); 44 | bool settings_get_ps1_game_id(void); 45 | void settings_set_ps1_game_id(bool enabled); 46 | bool settings_get_ps2_autoboot(void); 47 | void settings_set_ps2_autoboot(bool autoboot); 48 | bool settings_get_ps2_game_id(void); 49 | void settings_set_ps2_game_id(bool enabled); 50 | 51 | #define IDX_MIN 1 52 | #define IDX_BOOT 0 53 | #define CHAN_MIN 1 54 | 55 | uint8_t settings_get_display_timeout(void); 56 | uint8_t settings_get_display_contrast(void); 57 | uint8_t settings_get_display_vcomh(void); 58 | bool settings_get_display_flipped(void); 59 | void settings_set_display_timeout(uint8_t display_timeout); 60 | void settings_set_display_contrast(uint8_t display_contrast); 61 | void settings_set_display_vcomh(uint8_t display_vcomh); 62 | void settings_set_display_flipped(bool flipped); 63 | -------------------------------------------------------------------------------- /src/ui_theme_mono.h: -------------------------------------------------------------------------------- 1 | /** 2 | * @file lv_theme_mono.h 3 | * 4 | */ 5 | 6 | #ifndef UI_USE_THEME_MONO_H 7 | #define UI_USE_THEME_MONO_H 8 | 9 | #ifdef __cplusplus 10 | extern "C" { 11 | #endif 12 | 13 | /********************* 14 | * INCLUDES 15 | *********************/ 16 | #include "lvgl.h" 17 | 18 | /********************* 19 | * DEFINES 20 | *********************/ 21 | 22 | /********************** 23 | * TYPEDEFS 24 | **********************/ 25 | 26 | /********************** 27 | * GLOBAL PROTOTYPES 28 | **********************/ 29 | 30 | /** 31 | * Initialize the theme 32 | * @param color_primary the primary color of the theme 33 | * @param color_secondary the secondary color for the theme 34 | * @param font pointer to a font to use. 35 | * @return a pointer to reference this theme later 36 | */ 37 | lv_theme_t * ui_theme_mono_init(lv_disp_t * disp, bool dark_bg, const lv_font_t * font); 38 | 39 | /** 40 | * Check if the theme is initialized 41 | * @return true if default theme is initialized, false otherwise 42 | */ 43 | bool ui_theme_mono_is_inited(void); 44 | 45 | /********************** 46 | * MACROS 47 | **********************/ 48 | 49 | #endif 50 | 51 | #ifdef __cplusplus 52 | } /*extern "C"*/ 53 | #endif 54 | -------------------------------------------------------------------------------- /src/util.c: -------------------------------------------------------------------------------- 1 | #include "util.h" 2 | 3 | #include 4 | #include 5 | #include 6 | 7 | #include "sd.h" 8 | #include "game_db/game_db.h" 9 | 10 | bool str_is_integer(const char *str) { 11 | int i = 0; 12 | 13 | while (true) { 14 | if (!str[i]) 15 | break; 16 | if (isdigit((unsigned char)str[i]) == 0) 17 | return false; 18 | i++; 19 | } 20 | 21 | return true; 22 | } 23 | 24 | bool try_set_named_card_folder(const char *cards_dir, int it_idx, char *folder_name, size_t folder_name_size) { 25 | bool ret = false; 26 | int dir_fd, it_fd = -1; 27 | char filename[MAX_GAME_ID_LENGTH + 1] = {}; // +1 byte to be able to tell whether the name was truncated or not 28 | 29 | dir_fd = sd_open(cards_dir, O_RDONLY); 30 | if (dir_fd < 0) 31 | return false; 32 | 33 | it_fd = sd_iterate_dir(dir_fd, it_fd); 34 | while (it_fd != -1) { 35 | if (!sd_is_dir(it_fd) || !sd_get_name(it_fd, filename, sizeof(filename))) { 36 | it_fd = sd_iterate_dir(dir_fd, it_fd); 37 | continue; 38 | } 39 | 40 | // Skip boot card, normal cards, and cards with names longer than 15 characters 41 | if (strcmp(filename, "BOOT") == 0 || 42 | (strncmp(filename, "Card", 4) == 0 && str_is_integer(filename + 4)) || 43 | (strlen(filename) >= MAX_GAME_ID_LENGTH)) { 44 | it_fd = sd_iterate_dir(dir_fd, it_fd); 45 | continue; 46 | } 47 | 48 | if (it_idx > 0) { 49 | it_idx--; 50 | it_fd = sd_iterate_dir(dir_fd, it_fd); 51 | continue; 52 | } 53 | 54 | // This is the valid folder name 55 | snprintf(folder_name, folder_name_size, "%s", filename); 56 | ret = true; 57 | break; 58 | } 59 | 60 | 61 | if (it_fd != -1) 62 | sd_close(it_fd); 63 | 64 | sd_close(dir_fd); 65 | 66 | return ret; 67 | } -------------------------------------------------------------------------------- /src/util.h: -------------------------------------------------------------------------------- 1 | #pragma once 2 | 3 | #include 4 | 5 | #include "hardware/timer.h" 6 | 7 | static inline uint64_t __time_critical_func(RAM_time_us_64)() { 8 | // Need to make sure that the upper 32 bits of the timer 9 | // don't change, so read that first 10 | uint32_t hi = timer_hw->timerawh; 11 | uint32_t lo; 12 | do { 13 | // Read the lower 32 bits 14 | lo = timer_hw->timerawl; 15 | // Now read the upper 32 bits again and 16 | // check that it hasn't incremented. If it has loop around 17 | // and read the lower 32 bits again to get an accurate value 18 | uint32_t next_hi = timer_hw->timerawh; 19 | if (hi == next_hi) break; 20 | hi = next_hi; 21 | } while (true); 22 | return ((uint64_t) hi << 32u) | lo; 23 | } 24 | 25 | bool try_set_named_card_folder(const char *cards_dir, int it_idx, char *folder_name, size_t folder_name_size); 26 | -------------------------------------------------------------------------------- /src/version/CMakeLists.txt: -------------------------------------------------------------------------------- 1 | add_custom_target(sd2psx_version_generator ALL 2 | COMMAND ${CMAKE_COMMAND} 3 | -D SCRIPT_TEMPLATE=${CMAKE_CURRENT_SOURCE_DIR}/template/version.c 4 | -D SCRIPT_WORKING_DIR=${CMAKE_CURRENT_SOURCE_DIR} 5 | -D SCRIPT_OUTPUT_FILE=${CMAKE_CURRENT_BINARY_DIR}/version.c 6 | -D VARIANT=${VARIANT} 7 | -P ${CMAKE_CURRENT_SOURCE_DIR}/script.cmake 8 | BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/version.c) 9 | 10 | add_library(sd2psx_version STATIC ${CMAKE_CURRENT_BINARY_DIR}/version.c) 11 | target_include_directories(sd2psx_version PUBLIC ${CMAKE_CURRENT_SOURCE_DIR}) 12 | -------------------------------------------------------------------------------- /src/version/script.cmake: -------------------------------------------------------------------------------- 1 | cmake_minimum_required(VERSION 3.12) 2 | 3 | find_package(Git QUIET) 4 | 5 | if(Git_FOUND AND EXISTS "${SCRIPT_WORKING_DIR}/../../.git") 6 | execute_process(COMMAND ${GIT_EXECUTABLE} fetch origin --tags --force WORKING_DIRECTORY ${SCRIPT_WORKING_DIR}) 7 | execute_process(COMMAND ${GIT_EXECUTABLE} describe --tags --exact-match HEAD --exclude=latest --exclude=nightly 8 | OUTPUT_VARIABLE SD2PSX_VERSION WORKING_DIRECTORY ${SCRIPT_WORKING_DIR} 9 | OUTPUT_STRIP_TRAILING_WHITESPACE) 10 | execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --short HEAD 11 | OUTPUT_VARIABLE SD2PSX_COMMIT WORKING_DIRECTORY ${SCRIPT_WORKING_DIR} 12 | OUTPUT_STRIP_TRAILING_WHITESPACE) 13 | execute_process(COMMAND ${GIT_EXECUTABLE} rev-parse --abbrev-ref HEAD 14 | OUTPUT_VARIABLE SD2PSX_BRANCH WORKING_DIRECTORY ${SCRIPT_WORKING_DIR} 15 | OUTPUT_STRIP_TRAILING_WHITESPACE) 16 | string(REGEX REPLACE "\n$" "" SD2PSX_VERSION "${SD2PSX_VERSION}") 17 | string(REGEX REPLACE "\n$" "" SD2PSX_COMMIT "${SD2PSX_COMMIT}") 18 | string(REGEX REPLACE "\n$" "" SD2PSX_BRANCH "${SD2PSX_BRANCH}") 19 | if("${SD2PSX_VERSION}" STREQUAL "") 20 | set(SD2PSX_VERSION "nightly") 21 | endif() 22 | else() 23 | set(SD2PSX_VERSION "None") 24 | set(SD2PSX_COMMIT "None") 25 | set(SD2PSX_BRANCH "None") 26 | endif() 27 | 28 | file(READ ${SCRIPT_TEMPLATE} template_file) 29 | 30 | string(REPLACE "@SD2PSX_VERSION@" ${SD2PSX_VERSION} template_file "${template_file}") 31 | string(REPLACE "@SD2PSX_COMMIT@" ${SD2PSX_COMMIT} template_file "${template_file}") 32 | string(REPLACE "@SD2PSX_BRANCH@" ${SD2PSX_BRANCH} template_file "${template_file}") 33 | string(REPLACE "@VARIANT@" ${VARIANT} template_file "${template_file}") 34 | 35 | file(WRITE ${SCRIPT_OUTPUT_FILE} "${template_file}") 36 | -------------------------------------------------------------------------------- /src/version/template/version.c: -------------------------------------------------------------------------------- 1 | #include "version.h" 2 | 3 | const char* sd2psx_version = "@SD2PSX_VERSION@"; 4 | const char* sd2psx_commit = "@SD2PSX_COMMIT@"; 5 | const char* sd2psx_branch = "@SD2PSX_BRANCH@"; 6 | const char* sd2psx_variant = "@VARIANT@"; 7 | -------------------------------------------------------------------------------- /src/version/version.h: -------------------------------------------------------------------------------- 1 | 2 | #pragma once 3 | 4 | extern const char* sd2psx_version; 5 | extern const char* sd2psx_commit; 6 | extern const char* sd2psx_branch; 7 | extern const char* sd2psx_variant; -------------------------------------------------------------------------------- /src/wear_leveling/wear_leveling.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Nick Brassel (@tzarc) 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | #pragma once 4 | #include "wear_leveling_rp2040_flash_config.h" 5 | #include 6 | #include 7 | 8 | /** 9 | * @typedef Status returned from any wear-leveling API. 10 | */ 11 | typedef enum wear_leveling_status_t { 12 | WEAR_LEVELING_FAILED, //< Invocation failed 13 | WEAR_LEVELING_SUCCESS, //< Invocation succeeded 14 | WEAR_LEVELING_CONSOLIDATED //< Invocation succeeded, consolidation occurred 15 | } wear_leveling_status_t; 16 | 17 | /** 18 | * Wear-leveling initialization 19 | * 20 | * @return Status of the request 21 | */ 22 | wear_leveling_status_t wear_leveling_init(void); 23 | 24 | /** 25 | * Wear-leveling erasure. 26 | * 27 | * Clears the wear-leveling area, with the definition that the "reset state" of all data is zero. 28 | * 29 | * @return Status of the request 30 | */ 31 | wear_leveling_status_t wear_leveling_erase(void); 32 | 33 | /** 34 | * Writes logical data into the backing store. 35 | * 36 | * Skips writes if there are no changes to written values. The entire written block is considered when attempting to 37 | * determine if an overwrite should occur -- if there is any data mismatch the entire block will be written to the log, 38 | * not just the changed bytes. 39 | * 40 | * @param address[in] the logical address to write data 41 | * @param value[in] pointer to the source buffer 42 | * @param length[in] length of the data 43 | * @return Status of the request 44 | */ 45 | wear_leveling_status_t wear_leveling_write(uint32_t address, const void* value, size_t length); 46 | 47 | /** 48 | * Reads logical data from the cache. 49 | * 50 | * @param address[in] the logical address to read data 51 | * @param value[out] pointer to the destination buffer 52 | * @param length[in] length of the data 53 | * @return Status of the request 54 | */ 55 | wear_leveling_status_t wear_leveling_read(uint32_t address, void* value, size_t length); 56 | -------------------------------------------------------------------------------- /src/wear_leveling/wear_leveling_rp2040_flash_config.h: -------------------------------------------------------------------------------- 1 | // Copyright 2022 Nick Brassel (@tzarc) 2 | // SPDX-License-Identifier: GPL-2.0-or-later 3 | #pragma once 4 | 5 | #ifndef __ASSEMBLER__ 6 | # include "hardware/flash.h" 7 | # include "flashmap.h" 8 | #endif 9 | 10 | // 2-byte writes 11 | #ifndef BACKING_STORE_WRITE_SIZE 12 | # define BACKING_STORE_WRITE_SIZE 2 13 | #endif 14 | 15 | // 16k backing space allocated 16 | #ifndef WEAR_LEVELING_BACKING_SIZE 17 | # define WEAR_LEVELING_BACKING_SIZE (16 * 1024) 18 | #endif // WEAR_LEVELING_BACKING_SIZE 19 | 20 | // 512b logical EEPROM 21 | #ifndef WEAR_LEVELING_LOGICAL_SIZE 22 | # define WEAR_LEVELING_LOGICAL_SIZE (512) 23 | #endif // WEAR_LEVELING_LOGICAL_SIZE 24 | 25 | // Define how much flash space we have (defaults to lib/pico-sdk/src/boards/include/boards/***) 26 | #ifndef WEAR_LEVELING_RP2040_FLASH_SIZE 27 | # define WEAR_LEVELING_RP2040_FLASH_SIZE (PICO_FLASH_SIZE_BYTES) 28 | #endif 29 | 30 | // Define the location of emulated EEPROM 31 | #define WEAR_LEVELING_RP2040_FLASH_BASE FLASH_OFF_EEPROM 32 | --------------------------------------------------------------------------------