diff options
| author | John "Lameguy" Wilbert Villamor <lameguy64@gmail.com> | 2021-11-22 14:40:59 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2021-11-22 14:40:59 +0800 |
| commit | 45123e1b968d1883fed9b8526157ce2c4bffc4a7 (patch) | |
| tree | d20c80fbd4f5a5d1d3972669625972cea6b3684d | |
| parent | 538f28cfbbbb8163ab8a96de77d6887123856c81 (diff) | |
| parent | 9b00e5f7ff163a8fc6f341dbf237d90c61dadddc (diff) | |
| download | psn00bsdk-45123e1b968d1883fed9b8526157ce2c4bffc4a7.tar.gz | |
Merge pull request #43 from spicyjpeg/cmake
Even more CMake fixes, submodules, pads example
71 files changed, 1220 insertions, 349 deletions
diff --git a/.gitmodules b/.gitmodules new file mode 100644 index 0000000..734dafd --- /dev/null +++ b/.gitmodules @@ -0,0 +1,7 @@ +[submodule "tools/tinyxml2"] + path = tools/tinyxml2 + url = https://github.com/leethomason/tinyxml2 +[submodule "tools/mkpsxiso"] + path = tools/mkpsxiso + url = https://github.com/spicyjpeg/mkpsxiso + branch = cmake diff --git a/CMakeLists.txt b/CMakeLists.txt index 431e95b..87980a5 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,13 +6,12 @@ # workaround is to use ExternalProject_Add() to launch multiple independent # CMake instances, creating what's known as a "superbuild". -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) include(ExternalProject) project( PSn00bSDK LANGUAGES NONE - # IMPORTANT TODO: set a version number VERSION 0.1.0 DESCRIPTION "Open source PlayStation 1 SDK" HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk" @@ -36,14 +35,6 @@ set( ) set( - SKIP_TINYXML2 OFF - CACHE BOOL "Skip downloading and building tinyxml2 (if already installed)" -) -set( - SKIP_MKPSXISO OFF - CACHE BOOL "Skip downloading and building mkpsxiso (if already installed)" -) -set( SKIP_EXAMPLES OFF CACHE BOOL "Skip building SDK examples (not required for installation)" ) @@ -61,12 +52,6 @@ set( -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} ) set( - EXT_LIBRARY_ARGS - -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE} - -DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/install_temp - -DCMAKE_MSVC_RUNTIME_LIBRARY:STRING=MultiThreaded$<$<CONFIG:Debug>:Debug> -) -set( SUBPROJECT_ARGS -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE} -DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/install_tree @@ -77,50 +62,30 @@ set( -DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/examples ) -## External dependencies - -if(NOT SKIP_TINYXML2) - list(APPEND SUBPROJECT_ARGS -Dtinyxml2_ROOT:PATH=${PROJECT_BINARY_DIR}/install_temp) - - ExternalProject_Add( - tinyxml2 - GIT_REPOSITORY "https://github.com/leethomason/tinyxml2" - CMAKE_CACHE_ARGS ${COMMON_ARGS} ${EXT_LIBRARY_ARGS} - INSTALL_DIR install_temp - ) -else() - list(APPEND SUBPROJECT_ARGS -Dtinyxml2_ROOT:PATH=${tinyxml2_ROOT}) - - # Create a dummy target so CMake doesn't throw missing dependency errors. - add_library(tinyxml2 INTERFACE) -endif() +## Subprojects -if(NOT SKIP_MKPSXISO) - ExternalProject_Add( - mkpsxiso - GIT_REPOSITORY "https://github.com/Lameguy64/mkpsxiso" - CMAKE_CACHE_ARGS ${COMMON_ARGS} ${SUBPROJECT_ARGS} - INSTALL_DIR install_tree - DEPENDS tinyxml2 - ) -else() - add_library(mkpsxiso INTERFACE) +if(NOT EXISTS ${PROJECT_SOURCE_DIR}/tools/mkpsxiso/CMakeLists.txt) + message(FATAL_ERROR "The mkpsxiso directory is empty. Run 'git submodule update --init --recursive' to populate it.") endif() -## Subprojects - ExternalProject_Add( - libpsn00b - SOURCE_DIR ${PROJECT_SOURCE_DIR}/libpsn00b + tools + SOURCE_DIR ${PROJECT_SOURCE_DIR}/tools CMAKE_CACHE_ARGS ${COMMON_ARGS} ${SUBPROJECT_ARGS} INSTALL_DIR install_tree ) ExternalProject_Add( - tools - SOURCE_DIR ${PROJECT_SOURCE_DIR}/tools + mkpsxiso + SOURCE_DIR ${PROJECT_SOURCE_DIR}/tools/mkpsxiso CMAKE_CACHE_ARGS ${COMMON_ARGS} ${SUBPROJECT_ARGS} INSTALL_DIR install_tree - DEPENDS tinyxml2 +) +ExternalProject_Add( + libpsn00b + SOURCE_DIR ${PROJECT_SOURCE_DIR}/libpsn00b + CMAKE_CACHE_ARGS ${COMMON_ARGS} ${SUBPROJECT_ARGS} + INSTALL_DIR install_tree + #DEPENDS tools ) ExternalProject_Add( examples @@ -133,12 +98,21 @@ ExternalProject_Add( # Install all files in the temporary installation tree, as well as static files # from the source tree, when "cmake --install" is invoked. -install( - DIRECTORY ${PROJECT_BINARY_DIR}/install_tree/ # THE TRAILING SLASH IS IMPORTANT - DESTINATION . - COMPONENT sdk - USE_SOURCE_PERMISSIONS +foreach( + _subdir IN ITEMS + ${CMAKE_INSTALL_BINDIR} + ${CMAKE_INSTALL_LIBDIR} + ${CMAKE_INSTALL_DATADIR} ) + install( + # THE TRAILING SLASH IS IMPORTANT + DIRECTORY ${PROJECT_BINARY_DIR}/install_tree/${_subdir}/ + DESTINATION ${_subdir} + COMPONENT sdk + USE_SOURCE_PERMISSIONS + ) +endforeach() + install( DIRECTORY doc template DESTINATION ${CMAKE_INSTALL_DATADIR}/psn00bsdk diff --git a/CMakePresets.json b/CMakePresets.json new file mode 100644 index 0000000..f550e82 --- /dev/null +++ b/CMakePresets.json @@ -0,0 +1,29 @@ +{ + "version": 2, + "cmakeMinimumRequired": { + "major": 3, + "minor": 20, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "displayName": "Default configuration", + "description": "Use this preset when building the SDK for local installation.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build" + }, + { + "name": "package", + "displayName": "Installer package", + "description": "Use this preset to build installer packages via CPack.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "SKIP_EXAMPLES": "ON", + "BUNDLE_TOOLCHAIN": "ON" + } + } + ] +} @@ -4,35 +4,53 @@ ## Building and installing The instructions below are for Windows and Linux. Building on macOS hasn't been -tested but should work. +tested extensively yet, however it should work once the GCC toolchain is built +and installed properly. -1. Set up a host compiler. Most Linux distros provide a `build-essential`, - `base-devel` or similar all-in-one package. You'll also need to install the - [Ninja](https://ninja-build.org) build engine (it's usually in a package - called `ninja-build`). On Windows install [MSys2](https://www.msys2.org), - then run the following command in the MSys2 shell to install MinGW and Ninja: +1. Install prerequisites and a host compiler toolchain. On Linux (most distros) + install the following packages from your distro's package manager: + + - `git` + - `build-essential`, `base-devel` or similar + - `make` or `ninja-build` + - `cmake` (3.20+ is required, download it from + [here](https://cmake.org/download) if your package manager only provides + older versions) + + On Windows install [MSys2](https://www.msys2.org), then open the "MSys2 + MSYS" shell and run this command: ```bash - pacman -Syu mingw-w64-x86_64-gcc mingw-w64-x86_64-ninja + pacman -Syu git mingw-w64-x86_64-make mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc ``` - Add `C:\msys64\mingw64\bin` (replace `C:\msys64` if you installed MSys2 to a + If you are prompted to close the shell, you may have to reopen it afterwards + and rerun the command to finish installation. + **Do not use the MSys2 shell for the next steps**, use a regular command + prompt or PowerShell instead. + + Add these directories (replace `C:\msys64` if you installed MSys2 to a different location) to the `PATH` environment variable using System - Properties. + Properties: -2. Install Git and CMake. Note that MSys2 and some Linux distros ship relatively - old versions (PSn00bSDK requires 3.21+), so grab the latest CMake release - from [here](https://cmake.org) instead of through your package manager. + - `C:\msys64\mingw64\bin` + - `C:\msys64\usr\bin` -3. Build and install a GCC toolchain for `mipsel-unknown-elf`, as detailed in +2. Build and install a GCC toolchain for `mipsel-unknown-elf`, as detailed in [TOOLCHAIN.md](TOOLCHAIN.md). On Windows, you may download a precompiled version from [Lameguy64's website](http://lameguy64.net?page=psn00bsdk) and extract it into one of the directories listed below instead. -4. If you chose a non-standard install location for the toolchain, add the `bin` - subfolder (inside the top-level toolchain directory) to the `PATH` - environment variable. This step is unnecessary if you installed/extracted the - toolchain into any of these directories: + **NOTE**: PSn00bSDK is also compatible with toolchains that target + `mipsel-none-elf`. If you already have such a toolchain (e.g. because you + have another PS1 SDK installed) you can skip this step. Make sure you pass + `-DPSN00BSDK_TARGET=mipsel-none-elf` to CMake when configuring the SDK + though (see step 5). + +3. If you chose a non-standard install location for the toolchain, add the + `bin` subfolder (inside the top-level toolchain directory) to the `PATH` + environment variable. This step is unnecessary if you installed/extracted + the toolchain into any of these directories: - `C:\Program Files\mipsel-unknown-elf` - `C:\Program Files (x86)\mipsel-unknown-elf` @@ -41,25 +59,33 @@ tested but should work. - `/usr/mipsel-unknown-elf` - `/opt/mipsel-unknown-elf` -5. Clone/download the PSn00bSDK repo and run the following commands from its - directory: +4. Clone the PSn00bSDK repo, then run the following command from the PSn00bSDK + repository to download additional dependencies: ```bash - cmake -S . -B ./build -G Ninja + git submodule update --init --recursive --remote + ``` + +5. Compile the libraries, tools and examples using CMake: + + ```bash + cmake --preset default . cmake --build ./build ``` If you want to install the SDK to a custom location rather than the default one (`C:\Program Files\PSn00bSDK` or `/usr/local` depending on your OS), add - `--install-prefix <INSTALL_PATH>` to the first command. Remove `-G Ninja` to - use `make` instead of Ninja (slower, not recommended). + `--install-prefix <INSTALL_PATH>` to the first command. Add + `-DPSN00BSDK_TARGET=mipsel-none-elf` if your toolchain targets + `mipsel-none-elf` rather than `mipsel-unknown-elf`. - If you run into errors, try passing `-DSKIP_TINYXML2=ON` to the first command - after installing `tinyxml2` manually. [See below](#advanced-build-options) - for more details. + **NOTE**: Ninja is used by default to build the SDK. If you can't get it to + work or don't have it installed, pass `-G "Unix Makefiles"` (or + `-G "MSYS Makefiles"` on Windows) to the first command to build using `make` + instead. -6. Install the SDK to the path you chose by running this command (add `sudo` if - necessary): +6. Install the SDK to the path you chose (add `sudo` or run it from a command + prompt with admin privileges if necessary): ```bash cmake --install ./build @@ -81,23 +107,7 @@ with debugging capabilities such as [no$psx](https://problemkaputt.de/psx.htm) [pcsx-redux](https://github.com/grumpycoders/pcsx-redux). **Avoid ePSXe and anything based on MAME** as they are inaccurate. -## Advanced build options - -### Skipping external dependency downloads - -By default [mkpsxiso](https://github.com/Lameguy64/mkpsxiso) (required for -building CD images) and [tinyxml2](https://github.com/leethomason/tinyxml2) -(required to build mkpsxiso and other SDK tools) are automatically cloned from -their respective repos and built as part of the PSn00bSDK build process, -*even if they are already installed*. - -If you wish to disable this behavior (e.g. because it leads to errors, or to -perform an offline build), invoke CMake with the `-DSKIP_MKPSXISO=ON` and/or -`-DSKIP_TINYXML2=ON` options when configuring the SDK. Note that you must have -`mkpsxiso` and/or `tinyxml2` already installed (either manually or via vcpkg or -your distro's package manager) to be able to skip them. - -### Building installer packages +## Building installer packages CPack can be used to build NSIS-based installers, DEB/RPM packages and zipped releases that include built SDK libraries, headers as well as the GCC toolchain. @@ -108,27 +118,25 @@ far from being feature-complete. [NSIS](https://nsis.sourceforge.io/Download) on Windows or `dpkg` and `rpm` on Linux. -2. Run the following commands from the PSn00bSDK directory: +2. Run the following commands from the PSn00bSDK directory (pass the + appropriate options to the first command if necessary): ```bash - cmake -S . -B ./build -G Ninja -DBUNDLE_TOOLCHAIN=ON + cmake --preset package . cmake --build ./build -t package ``` All built packages will be copied to the `build/packages` folder. - **NOTE**: do not use `-DSKIP_MKPSXISO=ON`, otherwise the mkpsxiso binary will - not be included in the packages. - ## Creating a project -1. Copy the contents of `INSTALL_PATH/share/psn00bsdk/template` (or the +1. Copy the contents of `<INSTALL_PATH>/share/psn00bsdk/template` (or the `template` folder within the repo) to your new project's root directory. 2. Configure and build the template by running: ```bash - cmake -S . -B ./build -G Ninja + cmake -S . -B ./build cmake --build ./build ``` @@ -138,11 +146,11 @@ far from being feature-complete. Note that, even though the template relies on the `PSN00BSDK_LIBS` environment variable to locate the SDK by default, you can also specify the path directly on the CMake command line by adding -`-DCMAKE_TOOLCHAIN_FILE=INSTALL_PATH/lib/libpsn00b/cmake/sdk.cmake` (replace -`INSTALL_PATH`) to the first command. +`-DCMAKE_TOOLCHAIN_FILE=<INSTALL_PATH>/lib/libpsn00b/cmake/sdk.cmake` to the +CMake command line. The toolchain script defines a few CMake macros to create PS1 executables, DLLs and CD images. See the [reference](doc/cmake_reference.md) for details. ----------------------------------------- -_Last updated on 2021-10-18 by spicyjpeg_ +_Last updated on 2021-11-19 by spicyjpeg_ diff --git a/TOOLCHAIN.md b/TOOLCHAIN.md index 8ecee38..2a78ccb 100644 --- a/TOOLCHAIN.md +++ b/TOOLCHAIN.md @@ -25,7 +25,8 @@ the latest stable release of GCC and binutils. If for some reason you are having problems you may try building one of the following versions, which have been tested extensively: -- GCC **7.4.0** with binutils **2.31** +- ~~GCC 7.4.0 with binutils 2.31~~ (the linker fails to build PS1 DLLs) +- GCC **11.1.0** with binutils **2.36** - GCC **11.2.0** with binutils **2.37** If you wish to pick an older GCC release but don't know which binutils version @@ -220,4 +221,4 @@ implemented due to bloat concerns that it may introduce. Besides, the official SDK lacks full C++ support as well. ----------------------------------------- -_Last updated on 2021-10-18 by spicyjpeg_ +_Last updated on 2021-10-31 by spicyjpeg_ diff --git a/changelog.txt b/changelog.txt index a7861b9..3efa7a0 100644 --- a/changelog.txt +++ b/changelog.txt @@ -2,6 +2,34 @@ PSn00bSDK changelog Items that are lower in the log are more recently implemented. +11-19-2021 by spicyjpeg: + +* libc: Removed STACK_MAX_SIZE and added _mem_init() back. RAM and stack size + can now be set by calling _mem_init() manually before allocating any memory. + +* libc: sprintf() now supports fixed padding when using %@ (binary integer). + +* psxapi: Added wrapper around BIOS function GetSystemInfo(). + +* examples: Added pads example. Also added CMake build script to cartrom, which + is now built alongside all other examples. + +* Deprecated malloc.h and removed all references to it (stdlib.h should be used + instead). Moved int*_t and uint*_t types to stdint.h. + +* Fixed file permission errors when attempting to install the SDK on macOS. + Made some small updates to INSTALL.md. + + +10-31-2021 by spicyjpeg: + +* Added tinyxml2 and mkpsxiso as git submodules and removed the (admittedly + short-lived) SKIP_* options completely. CMake's ExternalProject is no longer + used to download dependencies at build time. + +* Added CMake presets file with presets for building installer packages. + + 10-25-2021 by Lameguy64: * Made a very tiny change in the readme file. diff --git a/cpack/fakeroot_fix.cmake b/cpack/fakeroot_fix.cmake index e34baa0..1c1430d 100644 --- a/cpack/fakeroot_fix.cmake +++ b/cpack/fakeroot_fix.cmake @@ -7,7 +7,7 @@ # script does is simply finding and deleting all directories that do not match # the installation prefix before CPack generates the package. -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) set(_prefix ${CPACK_TEMPORARY_INSTALL_DIRECTORY}${CPACK_PACKAGING_INSTALL_PREFIX}) diff --git a/cpack/setup.cmake b/cpack/setup.cmake index a483462..33f127e 100644 --- a/cpack/setup.cmake +++ b/cpack/setup.cmake @@ -2,7 +2,7 @@ # rules to bundle the GCC toolchain and CMake in packages. It is included by # the main CMakeLists.txt script. -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) ## Settings @@ -111,11 +111,11 @@ set(CPACK_PACKAGE_INSTALL_DIRECTORY PSn00bSDK) ## DEB/RPM variables -set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.28), cmake (>= 3.21)") +set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.28), cmake (>= 3.20)") set(CPACK_DEBIAN_PACKAGE_RECOMMENDS "ninja-build (>= 1.10)") set(CPACK_DEBIAN_PACKAGE_SUGGESTS "git (>= 2.25)") set(CPACK_DEBIAN_PACKAGE_SECTION devel) -set(CPACK_RPM_PACKAGE_REQUIRES "cmake >= 3.21") +set(CPACK_RPM_PACKAGE_REQUIRES "cmake >= 3.20") set(CPACK_RPM_PACKAGE_SUGGESTS "ninja-build >= 1.10, git >= 2.25") #set(CPACK_RPM_PACKAGE_RELOCATABLE ON) diff --git a/doc/dev notes.txt b/doc/dev notes.txt index 1bc9ccd..3aa2db5 100644 --- a/doc/dev notes.txt +++ b/doc/dev notes.txt @@ -53,6 +53,44 @@ IRQs are only triggered on transfer completion. Additional notes by spicyjpeg: +* The controller/memory card SPI interface is poorly implemented in most +emulators, making custom controller polling code insanely hard to write and +debug. The only emulator that comes close to real hardware is no$psx, which +seems to correctly implement all features of the SPI port (even those not used +by the BIOS pad driver, such as TX/RX interrupts). DuckStation only emulates +the bare minimum required by the BIOS and Sony libraries, and pcsx-redux has +major bugs that break most custom polling implementations. This pretty much +means TX/RX IRQs and many flags in the JOY_* registers should not be used +unless you are willing to break compatibility with emulators. + +* As if communicating with controllers wasn't difficult enough already, +DualShock pads also have a built-in watchdog timer that gets enabled when first +putting them in configuration mode (and is NOT disabled after exiting config +mode). If no polling commands are sent to the controller for about 1 second, +vibration motors are switched off and analog mode is disabled; the same happens +if the analog button is pressed while in analog mode. In order to always keep +the pad in analog mode you must: + + * Poll both controller ports at least once per frame by sending command 42h. + Polling at a higher rate might be desirable in some cases (such as rhythm + games) to increase timing accuracy. + * If a digital pad response (type = 4) is received from a port that hasn't + previously been flagged as digital-only, attempt to put the pad into config + mode using command 43h *twice* (as the proper response is delayed). + * If the pad doesn't recognize the config command, flag it as digital-only + and treat all further digital pad responses from it as valid. + * If the pad recognizes the command, it will reply by identifying as an + analog pad in config mode (type = 15). Send command 44h immediately + (without sending 42h first, otherwise the pad will exit config mode) to + enable analog mode and turn on the LED. + * Pressing the analog button will result in the controller identifying as + digital even though it is not flagged as such, thus re-triggering the + configuration process and putting it back into analog mode. + * All analog pad responses (type = 7) can be treated as valid, as they will + come from controllers that have already been configured. + * If no valid response is received, assume no controller is connected and + reset the port's digital-only flag. + * The SDK provides no support yet for replacing the BIOS exception handler with a custom one, however it can be done (if you are ok with losing all BIOS controller, memory card and file functionality). In order not to break anything @@ -128,6 +166,38 @@ cache or something like that (?). The only workaround I know of is to use CPACK_PRE_BUILD_SCRIPTS to trigger a custom script that deletes anything other than the actual files to be packaged (see cpack/fakeroot_fix.cmake). +* Project installation might fail on macOS (and possibly some Linux distros) if +CMake attempts to set permissions on system directories such as /usr/local. +This is usually caused by install() commands that copy files to the root +installation directory rather than to a subfolder, like this: + + install( + DIRECTORY install_tree/ + DESTINATION . + USE_SOURCE_PERMISSIONS + ) + +If the USE_SOURCE_PERMISSIONS flag is specified CMake will attempt to set +permissions on the DESTINATION folder, which in this case would be the root +prefix (/usr/local by default on macOS), to match the source directory. This +will however fail as macOS restricts top-level system directories from having +their permissions changed. The simplest workaround is to avoid using +"DESTINATION ." and install each subdirectory explicitly instead, like this: + + foreach( + _dir IN ITEMS + ${CMAKE_INSTALL_BINDIR} + ${CMAKE_INSTALL_LIBDIR} + ${CMAKE_INSTALL_INCLUDEDIR} + ${CMAKE_INSTALL_DATADIR} + ) + install( + DIRECTORY install_tree/${_dir}/ + DESTINATION ${_dir} + USE_SOURCE_PERMISSIONS + ) + endforeach() + * Depending on how you find external dependencies (find_package(), vcpkg, pkg-config...), CMake may end up outputting an executable that relies on a DLL installed system-wide. To correctly install the DLL alongside the executable diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 7cd7e98..15212c8 100644 --- a/examples/CMakeLists.txt +++ b/examples/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK examples build script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) project( PSn00bSDK-examples @@ -10,16 +10,32 @@ project( HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk" ) +include(GNUInstallDirs) + +# Find all subdirectories that contain a CMake build script. This includes the +# top-level examples directory and this file as well, however we're going to +# skip it. file( GLOB_RECURSE _examples + RELATIVE ${PROJECT_SOURCE_DIR} ${PROJECT_SOURCE_DIR}/CMakeLists.txt ) foreach(_script IN LISTS _examples) - cmake_path(GET _script PARENT_PATH _dir) - if(_dir STREQUAL PROJECT_SOURCE_DIR) + if(_script STREQUAL "CMakeLists.txt") continue() endif() - add_subdirectory(${_dir}) + # CMake provides no way to override the install prefix of a subdirectory, + # as it "imports" its targets into the main project rather than treating it + # as a separate project. However, as the example subdirectories use + # install(... TYPE BIN) to install executables, it is possible to override + # CMAKE_INSTALL_BINDIR temporarily to place them in any directory within + # the install prefix. This is a hack, but it allows us to preserve the + # examples' folder hierarchy in the installation directory (it is already + # preserved by CMake in the build tree!) with minimal effort. + cmake_path(GET _script PARENT_PATH _dir) + cmake_path(GET _dir PARENT_PATH CMAKE_INSTALL_BINDIR) + + add_subdirectory(${PROJECT_SOURCE_DIR}/${_dir}) endforeach() diff --git a/examples/beginner/cppdemo/CMakeLists.txt b/examples/beginner/cppdemo/CMakeLists.txt index 83f0f0a..becf464 100644 --- a/examples/beginner/cppdemo/CMakeLists.txt +++ b/examples/beginner/cppdemo/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -19,4 +19,4 @@ file(GLOB _sources *.cpp) psn00bsdk_add_executable(cppdemo STATIC ${_sources}) #psn00bsdk_add_cd_image(cppdemo_iso cppdemo iso.xml DEPENDS cppdemo) -install(FILES ${PROJECT_BINARY_DIR}/cppdemo.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/cppdemo.exe TYPE BIN) diff --git a/examples/beginner/cppdemo/main.cpp b/examples/beginner/cppdemo/main.cpp index 58bfcda..fd2e3a8 100644 --- a/examples/beginner/cppdemo/main.cpp +++ b/examples/beginner/cppdemo/main.cpp @@ -13,7 +13,7 @@ #include <sys/types.h> #include <stdio.h> -#include <malloc.h> +#include <stdlib.h> #include <psxgte.h> #include <psxgpu.h> diff --git a/examples/beginner/hello/CMakeLists.txt b/examples/beginner/hello/CMakeLists.txt index 40e30b0..7fb7c22 100644 --- a/examples/beginner/hello/CMakeLists.txt +++ b/examples/beginner/hello/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -19,4 +19,4 @@ file(GLOB _sources *.c) psn00bsdk_add_executable(hello STATIC ${_sources}) #psn00bsdk_add_cd_image(hello_iso hello iso.xml DEPENDS hello) -install(FILES ${PROJECT_BINARY_DIR}/hello.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/hello.exe TYPE BIN) diff --git a/examples/cdrom/cdbrowse/CMakeLists.txt b/examples/cdrom/cdbrowse/CMakeLists.txt index 6eb4cbb..e5ec759 100644 --- a/examples/cdrom/cdbrowse/CMakeLists.txt +++ b/examples/cdrom/cdbrowse/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -23,5 +23,5 @@ install( FILES ${PROJECT_BINARY_DIR}/cdbrowse.bin ${PROJECT_BINARY_DIR}/cdbrowse.cue - DESTINATION . + TYPE BIN ) diff --git a/examples/cdrom/cdbrowse/main.c b/examples/cdrom/cdbrowse/main.c index ead2df0..9a1dbd0 100644 --- a/examples/cdrom/cdbrowse/main.c +++ b/examples/cdrom/cdbrowse/main.c @@ -67,7 +67,6 @@ #include <psxspu.h> #include <psxcd.h> -#include <malloc.h> #include "ball16c.h" diff --git a/examples/cdrom/cdxa/CMakeLists.txt b/examples/cdrom/cdxa/CMakeLists.txt index b0c8d90..18dcc69 100644 --- a/examples/cdrom/cdxa/CMakeLists.txt +++ b/examples/cdrom/cdxa/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -25,5 +25,5 @@ install( #${PROJECT_BINARY_DIR}/cdxa.bin #${PROJECT_BINARY_DIR}/cdxa.cue ${PROJECT_BINARY_DIR}/cdxa.exe - DESTINATION . + TYPE BIN ) diff --git a/examples/cdrom/cdxa/main.c b/examples/cdrom/cdxa/main.c index 16f1c82..5f11d8d 100644 --- a/examples/cdrom/cdxa/main.c +++ b/examples/cdrom/cdxa/main.c @@ -129,7 +129,6 @@ #include <psxspu.h> #include <psxcd.h> -#include <malloc.h> #include "ball16c.h" diff --git a/examples/demos/n00bdemo/CMakeLists.txt b/examples/demos/n00bdemo/CMakeLists.txt index 0b51beb..c62c4ef 100644 --- a/examples/demos/n00bdemo/CMakeLists.txt +++ b/examples/demos/n00bdemo/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -47,4 +47,4 @@ target_include_directories(n00bdemo PRIVATE ${PROJECT_SOURCE_DIR}) add_custom_target(n00bdemo_data DEPENDS data.lzp) add_dependencies(n00bdemo n00bdemo_data) -install(FILES ${PROJECT_BINARY_DIR}/n00bdemo.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/n00bdemo.exe TYPE BIN) diff --git a/examples/demos/n00bdemo/logo.c b/examples/demos/n00bdemo/logo.c index d10b5b4..40160e7 100644 --- a/examples/demos/n00bdemo/logo.c +++ b/examples/demos/n00bdemo/logo.c @@ -5,7 +5,6 @@ #include <psxgte.h> #include <psxgpu.h> #include <inline_c.h> -#include "malloc.h" #include "smd.h" #include <lzp/lzp.h> diff --git a/examples/demos/n00bdemo/main.c b/examples/demos/n00bdemo/main.c index ba21d88..d2fe317 100644 --- a/examples/demos/n00bdemo/main.c +++ b/examples/demos/n00bdemo/main.c @@ -32,7 +32,6 @@ #include <lzp/lzp.h> #include <lzp/lzqlp.h> -#include "malloc.h" #include "smd.h" #include "data.h" #include "disp.h" @@ -617,7 +616,13 @@ void transition() { if( comp >= 16 ) break; - + + // FIXME: for some reason this loop glitches out and hangs indefinitely + // in no$psx, *unless* there's a function somewhere that gets called + // with a pointer/string as first argument... wtf. It works fine in + // other emulators. If you are reading this, please help and enlighten + // me. -- spicyjpeg + puts("."); } DrawSync(0); diff --git a/examples/graphics/balls/CMakeLists.txt b/examples/graphics/balls/CMakeLists.txt index b063425..5886484 100644 --- a/examples/graphics/balls/CMakeLists.txt +++ b/examples/graphics/balls/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -19,4 +19,4 @@ file(GLOB _sources *.c) psn00bsdk_add_executable(balls STATIC ${_sources}) #psn00bsdk_add_cd_image(balls_iso balls iso.xml DEPENDS balls) -install(FILES ${PROJECT_BINARY_DIR}/balls.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/balls.exe TYPE BIN) diff --git a/examples/graphics/billboard/CMakeLists.txt b/examples/graphics/billboard/CMakeLists.txt index bf73297..8cd31a9 100644 --- a/examples/graphics/billboard/CMakeLists.txt +++ b/examples/graphics/billboard/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -25,4 +25,4 @@ psn00bsdk_add_executable( ) #psn00bsdk_add_cd_image(billboard_iso billboard iso.xml DEPENDS billboard) -install(FILES ${PROJECT_BINARY_DIR}/billboard.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/billboard.exe TYPE BIN) diff --git a/examples/graphics/fpscam/CMakeLists.txt b/examples/graphics/fpscam/CMakeLists.txt index 8fa66f2..791f6c2 100644 --- a/examples/graphics/fpscam/CMakeLists.txt +++ b/examples/graphics/fpscam/CMakeLists.txt @@ -1,14 +1,14 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) endif() project( - hello + fpscam LANGUAGES C VERSION 1.0.0 DESCRIPTION "PSn00bSDK 3D camera controls example" @@ -19,4 +19,4 @@ file(GLOB _sources *.c) psn00bsdk_add_executable(fpscam STATIC ${_sources}) #psn00bsdk_add_cd_image(fpscam_iso fpscam iso.xml DEPENDS fpscam) -install(FILES ${PROJECT_BINARY_DIR}/fpscam.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/fpscam.exe TYPE BIN) diff --git a/examples/graphics/gte/CMakeLists.txt b/examples/graphics/gte/CMakeLists.txt index 3cdf2ff..85b2942 100644 --- a/examples/graphics/gte/CMakeLists.txt +++ b/examples/graphics/gte/CMakeLists.txt @@ -1,14 +1,14 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) endif() project( - hello + gte LANGUAGES C VERSION 1.0.0 DESCRIPTION "PSn00bSDK GTE 3D cube example" @@ -19,4 +19,4 @@ file(GLOB _sources *.c) psn00bsdk_add_executable(gte STATIC ${_sources}) #psn00bsdk_add_cd_image(gte_iso gte iso.xml DEPENDS gte) -install(FILES ${PROJECT_BINARY_DIR}/gte.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/gte.exe TYPE BIN) diff --git a/examples/graphics/hdtv/CMakeLists.txt b/examples/graphics/hdtv/CMakeLists.txt index 98a0b3f..f92faeb 100644 --- a/examples/graphics/hdtv/CMakeLists.txt +++ b/examples/graphics/hdtv/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -19,4 +19,4 @@ file(GLOB _sources *.c) psn00bsdk_add_executable(hdtv STATIC ${_sources}) #psn00bsdk_add_cd_image(hdtv_iso hdtv iso.xml DEPENDS hdtv) -install(FILES ${PROJECT_BINARY_DIR}/hdtv.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/hdtv.exe TYPE BIN) diff --git a/examples/graphics/render2tex/CMakeLists.txt b/examples/graphics/render2tex/CMakeLists.txt index 42a063b..360840d 100644 --- a/examples/graphics/render2tex/CMakeLists.txt +++ b/examples/graphics/render2tex/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -25,4 +25,4 @@ psn00bsdk_add_executable( ) #psn00bsdk_add_cd_image(render2tex_iso render2tex iso.xml DEPENDS render2tex) -install(FILES ${PROJECT_BINARY_DIR}/render2tex.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/render2tex.exe TYPE BIN) diff --git a/examples/graphics/rgb24/CMakeLists.txt b/examples/graphics/rgb24/CMakeLists.txt index c66ae69..bf8a8fa 100644 --- a/examples/graphics/rgb24/CMakeLists.txt +++ b/examples/graphics/rgb24/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -25,4 +25,4 @@ psn00bsdk_add_executable( ) #psn00bsdk_add_cd_image(rgb24_iso rgb24 iso.xml DEPENDS rgb24) -install(FILES ${PROJECT_BINARY_DIR}/rgb24.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/rgb24.exe TYPE BIN) diff --git a/examples/io/pads/CMakeLists.txt b/examples/io/pads/CMakeLists.txt new file mode 100644 index 0000000..5bd7f5d --- /dev/null +++ b/examples/io/pads/CMakeLists.txt @@ -0,0 +1,22 @@ +# PSn00bSDK example CMake script +# (C) 2021 spicyjpeg - MPL licensed + +cmake_minimum_required(VERSION 3.20) + +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) + set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) +endif() + +project( + pads + LANGUAGES C ASM + VERSION 1.0.0 + DESCRIPTION "PSn00bSDK controller polling example" + HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk" +) + +file(GLOB _sources *.c *.s) +psn00bsdk_add_executable(pads STATIC ${_sources}) +#psn00bsdk_add_cd_image(pads_iso pads iso.xml DEPENDS pads) + +install(FILES ${PROJECT_BINARY_DIR}/pads.exe TYPE BIN) diff --git a/examples/io/pads/main.c b/examples/io/pads/main.c new file mode 100644 index 0000000..92beb1c --- /dev/null +++ b/examples/io/pads/main.c @@ -0,0 +1,279 @@ +/* + * PSn00bSDK controller polling example + * (C) 2021 spicyjpeg - MPL licensed + * + * This example shows how to poll controllers at high speeds (250 Hz) by using + * timer interrupts and communicating with devices on the SPI controller bus + * manually rather than relying on the BIOS pad driver, which is limited to + * 50/60 Hz and does not support custom commands. The example also demonstrates + * using configuration mode commands to force DualShock pads into analog mode + * and enable button pressure sensing on DualShock 2 (PS2) controllers. + * + * See spi.c for details on how the low-level SPI communication driver works. + * The DualShock handshaking logic is implemented here (see poll_cb() and + * dualshock_init_cb()). There is no support for memory cards in this example, + * but the code in spi.c can be used to read/write sectors on a memory card and + * combined with a higher-level filesystem driver for full support. + * + * IMPORTANT: this example hasn't yet been tested on real hardware and/or with + * unofficial controllers, which might behave differently at higher poll rates. + * Also keep in mind that many emulators emulate controllers and memory cards + * inaccurately. It is thus recommended to test controller I/O code extensively + * and handle as many edge cases as possible (e.g. partial but valid responses, + * zerofilled responses, slow replies) for maximum compatibility. + */ + +#include <stdint.h> +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <psxetc.h> +#include <psxgpu.h> +#include <psxpad.h> + +#include "spi.h" + +static const char *const PAD_TYPEIDS[] = { + "[UNKNOWN]", + "MOUSE", + "NEGCON", + "IRQ10_GUN", + "DIGITAL", + "ANALOG_STICK", + "GUNCON", + "ANALOG", + "MULTITAP", + "[UNKNOWN]", + "[UNKNOWN]", + "[UNKNOWN]", + "[UNKNOWN]", + "[UNKNOWN]", + "JOGCON", + "CONFIG_MODE" +}; + +/* Display/GPU context utilities */ + +#define SCREEN_XRES 320 +#define SCREEN_YRES 240 + +#define BGCOLOR_R 48 +#define BGCOLOR_G 24 +#define BGCOLOR_B 0 + +typedef struct { + DISPENV disp; + DRAWENV draw; +} DB; + +typedef struct { + DB db[2]; + uint32_t db_active; +} CONTEXT; + +void init_context(CONTEXT *ctx) { + DB *db; + + ResetGraph(0); + ctx->db_active = 0; + + db = &(ctx->db[0]); + SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); + setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); + db->draw.isbg = 1; + db->draw.dtd = 1; + + db = &(ctx->db[1]); + SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES); + setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); + db->draw.isbg = 1; + db->draw.dtd = 1; + + PutDrawEnv(&(db->draw)); + //PutDispEnv(&(db->disp)); + + // Create a text stream at the top of the screen. + FntLoad(960, 0); + FntOpen(8, 16, 304, 208, 2, 512); +} + +void display(CONTEXT *ctx) { + DB *db; + + DrawSync(0); + VSync(0); + ctx->db_active ^= 1; + + db = &(ctx->db[ctx->db_active]); + PutDrawEnv(&(db->draw)); + PutDispEnv(&(db->disp)); + SetDispMask(1); +} + +/* Pad buffers and callback */ + +static volatile uint8_t pad_buff[2][34]; +static volatile size_t pad_buff_len[2]; +static volatile uint32_t pad_digital_only[2] = { 0, 0 }; + +// Just a wrapper around spi_new_request(). This does not send the command +// immediately but adds it to the driver's request queue. +void send_pad_cmd( + uint32_t port, + PAD_COMMAND cmd, + uint8_t arg1, + uint8_t arg2, + SPICALLBACK callback +) { + SPIREQUEST *req = spi_new_request(); + + req->len = 9; + req->port = port; + req->callback = callback; + req->pad_req.addr = 0x01; + req->pad_req.cmd = cmd; + req->pad_req.tap_mode = 0x00; + req->pad_req.motor_r = arg1; + req->pad_req.motor_l = arg2; + + // The padding bytes must be 0xff when unlocking vibration motors. + memset( + req->pad_req.dummy, + (cmd == PAD_CMD_REQUEST_CONFIG) ? 0xff : 0x00, + 4 + ); +} + +// This callback determines whether a pad that identified as digital is +// actually a DualShock in digital mode by checking if it started identifying +// as CONFIG_MODE after receiving a configuration command. +void dualshock_init_cb(uint32_t port, const volatile uint8_t *buff, size_t rx_len) { + PADTYPE *pad = (PADTYPE *) buff; + + if ( + (rx_len < 2) || + (pad->raw.prefix != 0x5a) || + (pad->raw.type != PAD_ID_CONFIG_MODE) + ) { + printf("no, pad is digital-only (len = %d)\n", rx_len); + + pad_digital_only[port] = 1; + return; + } + + printf("yes, forcing analog mode (len = %d)\n", rx_len); + + // Issue further commands to force analog mode on, unlock rumble (not used + // in this example) and enable longer responses containing button pressure + // readings. + // TODO: find out if passing 0x03 instead of 0x02 in PAD_CMD_SET_ANALOG + // locks the analog button, as emulated by DuckStation... + // https://gist.github.com/scanlime/5042071 + send_pad_cmd(port, PAD_CMD_SET_ANALOG, 0x01, 0x02, 0); + send_pad_cmd(port, PAD_CMD_INIT_PRESSURE, 0x00, 0x00, 0); // Ignored by DualShock 1 + send_pad_cmd(port, PAD_CMD_REQUEST_CONFIG, 0x00, 0x01, 0); + send_pad_cmd(port, PAD_CMD_RESPONSE_CONFIG, 0xff, 0xff, 0); // Ignored by DualShock 1 + send_pad_cmd(port, PAD_CMD_CONFIG_MODE, 0x00, 0x00, 0); +} + +// This function is called by the pad timer ISR each time a pad is polled and a +// response (even an invalid/incomplete one) is received. +void poll_cb(uint32_t port, const volatile uint8_t *buff, size_t rx_len) { + // Copy the response to a persistent buffer so it can be accessed from the + // main loop and displayed on screen. + pad_buff_len[port] = rx_len; + if (rx_len) + memcpy((void *) pad_buff[port], (void *) buff, rx_len); + + PADTYPE *pad = (PADTYPE *) buff; + + // If this pad identifies as a digital pad and hasn't been flagged as a + // digital-only pad already, attempt to put it into analog mode by entering + // configuration mode. It this fails, it will be flagged as digital-only. + // The digital-only flag is reset when the controller is unplugged or stops + // returning digital pad responses. + if ( + rx_len && + (pad->raw.prefix == 0x5a) && + (pad->raw.type == PAD_ID_DIGITAL) + ) { + if (!pad_digital_only[port]) { + printf("Detecting if pad %d supports config mode... ", port + 1); + + // The pad only identifies as CONFIG_MODE after at least another + // command is sent. + send_pad_cmd(port, PAD_CMD_CONFIG_MODE, 0x01, 0x00, 0); + send_pad_cmd(port, PAD_CMD_CONFIG_MODE, 0x01, 0x00, &dualshock_init_cb); + } + + } else { + //printf("Clearing digital-only flag for pad %d\n", port + 1); + + pad_digital_only[port] = 0; + } +} + +/* Main */ + +static CONTEXT ctx; + +int main(int argc, const char* argv[]) { + init_context(&ctx); + spi_init(&poll_cb); + + uint32_t counter = 0; + + while (1) { + FntPrint(-1, "COUNTER=%d", counter++); + + for (uint32_t port = 0; port < 2; port++) { + // TODO. + if (!pad_buff_len[port]) { + FntPrint(-1, "\n\nPORT %d: NO DEVICE FOUND\n", port + 1); + if ((counter % 64) < 32) + FntPrint(-1, " CONNECT PAD NOW..."); + + continue; + } + + PADTYPE *pad = (PADTYPE *) pad_buff[port]; + PAD_TYPEID type = pad->raw.type; + + // According to nocash docs, there is a hardware bug in DualShock + // controllers that causes the prefix byte (normally 0x5a) to turn + // into 0x00 if the analog button is pressed after configuration + // commands have been used. Thus making sure the prefix is 0x5a + // isn't enough to reliably detect pads. + /*if ((pad->raw.prefix != 0x5a) && (type != PAD_ID_ANALOG)) { + FntPrint(-1, "\n\nPORT %d: INVALID RESPONSE\n", port + 1); + if ((counter % 64) < 32) + FntPrint(-1, " CHECK CONNECTION..."); + + continue; + }*/ + + FntPrint( + -1, + "\n\nPORT %d: %s (TYPE=%d)\n", + port + 1, + PAD_TYPEIDS[type], + type + ); + + // Print a hexdump of the payload returned by the pad. + for (uint32_t i = 0; i < pad_buff_len[port]; i++) + FntPrint( + -1, + ((i - 2) % 8) ? " %02x" : "\n %02x", + pad_buff[port][i] + ); + } + + FntFlush(-1); + display(&ctx); + } + + return 0; +} diff --git a/examples/io/pads/spi.c b/examples/io/pads/spi.c new file mode 100644 index 0000000..e01b3f6 --- /dev/null +++ b/examples/io/pads/spi.c @@ -0,0 +1,218 @@ +/* + * PSn00bSDK controller polling example (SPI driver) + * (C) 2021 spicyjpeg - MPL licensed + * + * This is a fairly complete timer driven, asynchronous high-speed SPI driver, + * with support for sending custom commands (including memory card access), in + * about 200 lines of code. Feel free to copypaste and adapt it. + * + * The way this works is by maintaining a queue of requests to send, each with + * its own payload and callback. Timer 2 is configured to trigger an IRQ at + * regular intervals. On each tick, the next request in the queue (or a poll + * command if no request is pending) is prepared and the first byte is + * sent; if the controller asks for more data by pulling /ACK low, the next + * byte is sent and the received byte is placed into a buffer. This goes on + * until the last byte is exchanged and the controller stops asserting /ACK. + * + * On the next tick, the response buffer is passed to the request's callback + * and reset, and the next request in the queue is sent. This blindly assumes + * it only takes one tick for a request/response to be sent, which is the case + * for controllers' very small packets but not for memory cards. It is + * advisable to call spi_set_poll_rate() to temporarily reduce poll rate while + * accessing memory cards. + * + * Note that this driver completely takes over the SPI bus, so you won't be + * able to use any BIOS functions that rely on SPI access (i.e. pad and memory + * card APIs) alongside it. + */ + +#include <stdint.h> +#include <string.h> +#include <stdlib.h> +#include <psxetc.h> +#include <psxapi.h> +#include <psxpad.h> + +#include "spi.h" + +/* Register definitions */ + +#define F_CPU 33868800UL + +#define TIM_VALUE(N) *((volatile uint32_t *) 0x1f801100 + 4 * (N)) +#define TIM_CTRL(N) *((volatile uint32_t *) 0x1f801104 + 4 * (N)) +#define TIM_RELOAD(N) *((volatile uint32_t *) 0x1f801108 + 4 * (N)) + +// IMPORTANT: even though JOY_TXRX is a 32-bit register, it should only be +// accessed as 8-bit. Reading it as 16 or 32-bit works fine on real hardware, +// but leads to problems in some emulators. +#define JOY_TXRX *((volatile uint8_t *) 0x1f801040) +#define JOY_STAT *((volatile uint16_t *) 0x1f801044) +#define JOY_MODE *((volatile uint16_t *) 0x1f801048) +#define JOY_CTRL *((volatile uint16_t *) 0x1f80104a) +#define JOY_BAUD *((volatile uint16_t *) 0x1f80104e) + +/* Internal structures and globals */ + +typedef struct _SPICONTEXT { + uint8_t tx_buff[SPI_BUFF_LEN]; + uint8_t rx_buff[SPI_BUFF_LEN]; + uint32_t tx_len, rx_len, port; + SPICALLBACK callback; +} SPICONTEXT; + +static volatile SPICONTEXT ctx; +static volatile SPIREQUEST volatile *current_req; +static SPICALLBACK default_cb; + +/* Request queue management */ + +static void prepare_poll_req(void) { + PADREQUEST *req = (PADREQUEST *) ctx.tx_buff; + + req->addr = 0x01; + req->cmd = PAD_CMD_READ; + req->tap_mode = 0x00; // 0x01 to enable extended multitap response + req->motor_l = 0x00; + req->motor_r = 0x00; + + ctx.tx_len = 4; + ctx.rx_len = 0; + ctx.port ^= 1; + ctx.callback = default_cb; +} + +static void prepare_next_req(void) { + // Copy the contents of the first request in the queue into the TX buffer. + memcpy((void *) ctx.tx_buff, (void *) current_req->data, current_req->len); + + ctx.tx_len = current_req->len; + ctx.rx_len = 0; + ctx.port = current_req->port; + ctx.callback = current_req->callback; + + // Pop the first request from the queue by deallocating it and adjusting + // the pointer to the first queue item. + SPIREQUEST *next = current_req->next; + + free((void *) current_req); + current_req = next; +} + +/* Interrupt handlers */ + +static void poll_timer_tick(void) { + // Fetch the last response byte, which wasn't followed by a pulse on /ACK, + // from the RX FIFO. + if (JOY_STAT & 0x0002) + ctx.rx_buff[ctx.rx_len - 1] = (uint8_t) JOY_TXRX; + + if (ctx.callback) + ctx.callback(ctx.port, ctx.rx_buff, ctx.rx_len); + + // If the request queue is empty, create a pad polling request. + if (current_req) + prepare_next_req(); + else + prepare_poll_req(); + + // Prepare the SPI port by clearing any pending IRQ, pulling /CS high and + // enabling the /ACK IRQ. In order to communicate with controllers, /CS has + // to be driven low again for about 20 us before sending the first byte. + JOY_CTRL = 0x0010; + for (uint32_t i = 0; i < 50; i++) + __asm__("nop"); + + JOY_CTRL = 0x1003 | (ctx.port << 13); + for (uint32_t i = 0; i < 500; i++) + __asm__("nop"); + + // Send the first byte indicating which device to address. If the matching + // device is connected, it will reply by triggering the /ACK IRQ. + JOY_TXRX = ctx.tx_buff[0]; +} + +static void spi_ack_handler(void) { + // Wait until /ACK is pulled up by the controller before sending the next + // byte. According to nocash docs, this has to be done before resetting the + // IRQ. + while (JOY_STAT & 0x0080) + __asm__("nop"); + + // Keep /CS pulled low and acknowledge the IRQ (bit 4) to ensure it can be + // triggered again. + JOY_CTRL = 0x1013 | (ctx.port << 13); + + if (!ctx.rx_len) { + // We just sent the first address byte. Obviously the response we + // received was read from an open bus, so the SPI port's internal FIFO + // must be flushed (by performing dummy reads) to ensure we are only + // going to read valid data from now on. + JOY_TXRX; + + } else if (ctx.rx_len <= SPI_BUFF_LEN) { + // If this is not the first byte, put it in the RX buffer. + ctx.rx_buff[ctx.rx_len - 1] = (uint8_t) JOY_TXRX; + } + + // Send the next byte, or a null byte if there is no more data to send and + // we're just reading a response. + ctx.rx_len++; + if (ctx.rx_len < ctx.tx_len) + JOY_TXRX = (uint32_t) ctx.tx_buff[ctx.rx_len]; + else + JOY_TXRX = 0x00; +} + +/* Public API */ + +SPIREQUEST *spi_new_request(void) { + SPIREQUEST *req = malloc(sizeof(SPIREQUEST)); + + req->len = 0; + req->port = 0; + req->callback = 0; + req->next = 0; + + // Find the last queued request by traversing the linked list and append a + // pointer to the new request. + if (!current_req) { + current_req = req; + } else { + volatile SPIREQUEST *volatile last = current_req; + while (last->next) + last = last->next; + + last->next = req; + } + + return req; +} + +void spi_set_poll_rate(uint32_t value) { + TIM_CTRL(2) = 0x0258; // CLK/8 input, IRQ on reload, disable one-shot IRQ + + if (value < 65) + TIM_RELOAD(2) = 0xffff; + else + TIM_RELOAD(2) = (F_CPU / 8) / value; +} + +void spi_init(SPICALLBACK callback) { + // Disable the BIOS timer handler (which for some stupid reason is enabled + // by default, even though it does nothing) and set up custom interrupt + // handlers. + EnterCriticalSection(); + ChangeClearRCnt(2, 0); + InterruptCallback(6, &poll_timer_tick); + InterruptCallback(7, &spi_ack_handler); + ExitCriticalSection(); + + JOY_CTRL = 0x0040; // Reset all registers + JOY_MODE = 0x000d; // 1x multiplier, 8 data bits, no parity + JOY_BAUD = 0x0088; // 250000 bps + + spi_set_poll_rate(250); + current_req = 0; + default_cb = callback; +} diff --git a/examples/io/pads/spi.h b/examples/io/pads/spi.h new file mode 100644 index 0000000..1c473cd --- /dev/null +++ b/examples/io/pads/spi.h @@ -0,0 +1,61 @@ +/* + * PSn00bSDK controller polling example (SPI driver) + * (C) 2021 spicyjpeg - MPL licensed + */ + +#ifndef __SPI_H +#define __SPI_H + +#include <stdint.h> +#include <psxpad.h> + +//#define SPI_BUFF_LEN 34 +#define SPI_BUFF_LEN 140 + +/* Request structures */ + +typedef void (*SPICALLBACK)(uint32_t port, const volatile uint8_t *buff, size_t rx_len); + +typedef struct _SPIREQUEST { + union { + uint8_t data[SPI_BUFF_LEN]; + PADREQUEST pad_req; + MCDREQUEST mcd_req; + }; + uint32_t len, port; + SPICALLBACK callback; + struct _SPIREQUEST *next; +} SPIREQUEST; + +/* Public API */ + +/** + * @brief Allocates a new request object and adds it to the request queue. The + * object must be populated afterwards by setting the length, callback and + * filling in the TX data buffer. + */ +SPIREQUEST *spi_new_request(void); + +/** + * @brief Changes the controller polling rate. The lowest supported rate is 65 + * Hz (requests sent every 1/65th of a second, each port polled at 32.5 Hz when + * no request is pending). + * + * @param value + */ +void spi_set_poll_rate(uint32_t value); + +/** + * @brief Installs the SPI and timer 2 interrupt handlers and starts the poll + * timer. By default the polling rate is set to 250 Hz (125 Hz per port), + * however it can be changed at any time by calling spi_set_poll_rate(). + * + * The provided callback (if any) is called to report the result of poll + * requests, which are issued automatically when no other request is in the + * queue. Passing NULL as callback does not disable auto-polling. + * + * @param callback + */ +void spi_init(SPICALLBACK callback); + +#endif diff --git a/examples/lowlevel/cartrom/CMakeLists.txt b/examples/lowlevel/cartrom/CMakeLists.txt new file mode 100644 index 0000000..3e807a3 --- /dev/null +++ b/examples/lowlevel/cartrom/CMakeLists.txt @@ -0,0 +1,36 @@ +# PSn00bSDK example CMake script +# (C) 2021 spicyjpeg - MPL licensed + +cmake_minimum_required(VERSION 3.21) + +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) + set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) +endif() + +project( + cartrom + LANGUAGES C ASM + VERSION 1.0.0 + DESCRIPTION "PSn00bSDK expansion port ROM example" + HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk" +) + +file(GLOB _sources *.c *.s) + +# This example only uses the toolchain (without the rest of the SDK), so the +# executable has to be created manually and converted into raw binary format +# (for testing on emulators or flashing to a cheat cartridge). +add_executable (cartrom ${_sources}) +target_link_libraries(cartrom psn00bsdk_static_exe) +set_target_properties(cartrom PROPERTIES PREFIX "" SUFFIX ".elf") +target_link_options (cartrom PRIVATE -T${PROJECT_SOURCE_DIR}/rom.ld) + +target_include_directories(cartrom PRIVATE ${PROJECT_SOURCE_DIR}) + +add_custom_command( + TARGET cartrom POST_BUILD + COMMAND ${CMAKE_OBJCOPY} -O binary cartrom.elf cartrom.bin + BYPRODUCTS cartrom.bin +) + +install(FILES ${PROJECT_BINARY_DIR}/cartrom.bin TYPE BIN) diff --git a/examples/lowlevel/cartrom/makefile b/examples/lowlevel/cartrom/makefile deleted file mode 100644 index 2434685..0000000 --- a/examples/lowlevel/cartrom/makefile +++ /dev/null @@ -1,14 +0,0 @@ -PREFIX = mipsel-unknown-elf- - -CC = $(PREFIX)gcc -AS = $(PREFIX)as -LD = $(PREFIX)ld - -all: rom.o - $(LD) --oformat binary -T rom.ld -o cartrom.rom rom.o - -%.o: %.s - $(AS) -msoft-float --warn $< -o $@ - -clean: - rm -f rom.o cartrom.rom
\ No newline at end of file diff --git a/examples/sound/vagsample/CMakeLists.txt b/examples/sound/vagsample/CMakeLists.txt index 1f42608..1a15d9c 100644 --- a/examples/sound/vagsample/CMakeLists.txt +++ b/examples/sound/vagsample/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -19,4 +19,4 @@ file(GLOB _sources *.c) psn00bsdk_add_executable(vagsample STATIC ${_sources}) #psn00bsdk_add_cd_image(vagsample_iso vagsample iso.xml DEPENDS vagsample) -install(FILES ${PROJECT_BINARY_DIR}/vagsample.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/vagsample.exe TYPE BIN) diff --git a/examples/system/childexec/CMakeLists.txt b/examples/system/childexec/CMakeLists.txt index c9983c4..88168e0 100644 --- a/examples/system/childexec/CMakeLists.txt +++ b/examples/system/childexec/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -38,4 +38,4 @@ target_link_options(child PRIVATE -Ttext=0x80030000) # embedded via child_exe.s). add_dependencies(parent child) -install(FILES ${PROJECT_BINARY_DIR}/parent.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/parent.exe TYPE BIN) diff --git a/examples/system/console/CMakeLists.txt b/examples/system/console/CMakeLists.txt index acae08f..6dc6154 100644 --- a/examples/system/console/CMakeLists.txt +++ b/examples/system/console/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -19,4 +19,4 @@ file(GLOB _sources *.c) psn00bsdk_add_executable(console STATIC ${_sources}) #psn00bsdk_add_cd_image(console_iso console iso.xml DEPENDS console) -install(FILES ${PROJECT_BINARY_DIR}/console.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/console.exe TYPE BIN) diff --git a/examples/system/dynlink/CMakeLists.txt b/examples/system/dynlink/CMakeLists.txt index 3af5d4b..5834647 100644 --- a/examples/system/dynlink/CMakeLists.txt +++ b/examples/system/dynlink/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -28,5 +28,5 @@ install( FILES ${PROJECT_BINARY_DIR}/dynlink.bin ${PROJECT_BINARY_DIR}/dynlink.cue - DESTINATION . + TYPE BIN ) diff --git a/examples/system/dynlink/display.c b/examples/system/dynlink/display.c index d8ad3ed..573f17c 100644 --- a/examples/system/dynlink/display.c +++ b/examples/system/dynlink/display.c @@ -10,6 +10,10 @@ #define SCREEN_XRES 320 #define SCREEN_YRES 240 +#define BGCOLOR_R 48 +#define BGCOLOR_G 24 +#define BGCOLOR_B 0 + /* Display/GPU context utilities */ void init_context(CONTEXT *ctx) { @@ -23,14 +27,14 @@ void init_context(CONTEXT *ctx) { db = &(ctx->db[0]); SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES); SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); - setRGB0(&(db->draw), 63, 0, 127); + setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); db->draw.isbg = 1; db->draw.dtd = 1; db = &(ctx->db[1]); SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES); - setRGB0(&(db->draw), 63, 0, 127); + setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); db->draw.isbg = 1; db->draw.dtd = 1; diff --git a/examples/system/dynlink/library/balls.c b/examples/system/dynlink/library/balls.c index 2a4d9f4..ef6993e 100644 --- a/examples/system/dynlink/library/balls.c +++ b/examples/system/dynlink/library/balls.c @@ -3,7 +3,7 @@ * (C) 2021 spicyjpeg - MPL licensed */ -#include <sys/types.h> +#include <stdint.h> #include <stdio.h> #include <stdlib.h> #include <psxgpu.h> diff --git a/examples/system/dynlink/library/cube.c b/examples/system/dynlink/library/cube.c index 57f3e56..84fe552 100644 --- a/examples/system/dynlink/library/cube.c +++ b/examples/system/dynlink/library/cube.c @@ -3,7 +3,7 @@ * (C) 2021 spicyjpeg - MPL licensed */ -#include <sys/types.h> +#include <stdint.h> #include <stdio.h> #include <psxgpu.h> #include <psxgte.h> diff --git a/examples/system/dynlink/library/dll_common.h b/examples/system/dynlink/library/dll_common.h index 4f9314b..315a993 100644 --- a/examples/system/dynlink/library/dll_common.h +++ b/examples/system/dynlink/library/dll_common.h @@ -6,6 +6,7 @@ #ifndef __DLL_COMMON_H #define __DLL_COMMON_H +#include <stdint.h> #include <psxgpu.h> /* Common structures shared by the main executable and DLLs */ diff --git a/examples/system/dynlink/main.c b/examples/system/dynlink/main.c index 33f6f44..6d93e71 100644 --- a/examples/system/dynlink/main.c +++ b/examples/system/dynlink/main.c @@ -36,12 +36,11 @@ * as plugins/mods/patches stored on a memory card. */ -#include <sys/types.h> +#include <stdint.h> #include <stdarg.h> #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <malloc.h> #include <dlfcn.h> #include <psxapi.h> #include <psxetc.h> diff --git a/examples/system/timer/CMakeLists.txt b/examples/system/timer/CMakeLists.txt index 189bf87..58daf9b 100644 --- a/examples/system/timer/CMakeLists.txt +++ b/examples/system/timer/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -19,4 +19,4 @@ file(GLOB _sources *.c) psn00bsdk_add_executable(timer STATIC ${_sources}) #psn00bsdk_add_cd_image(timer_iso timer iso.xml DEPENDS timer) -install(FILES ${PROJECT_BINARY_DIR}/timer.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/timer.exe TYPE BIN) diff --git a/examples/system/tty/CMakeLists.txt b/examples/system/tty/CMakeLists.txt index 024ccd4..4e0ca36 100644 --- a/examples/system/tty/CMakeLists.txt +++ b/examples/system/tty/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) @@ -19,4 +19,4 @@ file(GLOB _sources *.c) psn00bsdk_add_executable(tty STATIC ${_sources}) #psn00bsdk_add_cd_image(tty_iso tty iso.xml DEPENDS tty) -install(FILES ${PROJECT_BINARY_DIR}/tty.exe DESTINATION .) +install(FILES ${PROJECT_BINARY_DIR}/tty.exe TYPE BIN) diff --git a/indev/README.md b/indev/README.md index 2064a36..8263487 100644 --- a/indev/README.md +++ b/indev/README.md @@ -23,6 +23,10 @@ also go into this directory. The Github release of this work-in-progress component includes delay
corrections for PAL consoles.
+ **NOTE**: the `io/pads` example also shows how to poll controllers manually
+ in a slightly different way (using a timer), and includes a reusable
+ low-level pad driver.
+
Work-in-progress components such as psxcd, interlace-exp, xptest and partest
are not included, as the former was completed while the remaining latter are
merely scrap test programs.
\ No newline at end of file diff --git a/indev/psxmdec/main.c b/indev/psxmdec/main.c index c9fd678..f4f0f51 100644 --- a/indev/psxmdec/main.c +++ b/indev/psxmdec/main.c @@ -1,7 +1,7 @@ #include <sys/types.h>
#include <stdio.h>
#include <string.h>
-#include <malloc.h>
+#include <stdlib.h>
#include <psxgte.h>
#include <psxgpu.h>
diff --git a/indev/psxpad/main.c b/indev/psxpad/main.c index e8a6181..afd801e 100644 --- a/indev/psxpad/main.c +++ b/indev/psxpad/main.c @@ -1,7 +1,6 @@ #include <stdio.h> #include <stdlib.h> #include <string.h> -#include <malloc.h> #include <psxetc.h> #include <psxgte.h> #include <psxgpu.h> diff --git a/libpsn00b/CMakeLists.txt b/libpsn00b/CMakeLists.txt index 083208a..7b5f7a5 100644 --- a/libpsn00b/CMakeLists.txt +++ b/libpsn00b/CMakeLists.txt @@ -1,7 +1,7 @@ # libpsn00b build script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) # ${PROJECT_SOURCE_DIR} is not available until project() is called. set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/cmake/sdk.cmake) diff --git a/libpsn00b/cmake/internal_setup.cmake b/libpsn00b/cmake/internal_setup.cmake index 377afbc..b0c4591 100644 --- a/libpsn00b/cmake/internal_setup.cmake +++ b/libpsn00b/cmake/internal_setup.cmake @@ -4,7 +4,7 @@ # This script is included automatically when using the toolchain file and # defines helper functions. -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) include(GNUInstallDirs) # IMPORTANT TODO: set a version number @@ -86,16 +86,14 @@ function(psn00bsdk_add_executable name type) endif() add_executable (${name} ${ARGN}) - target_link_libraries(${name} psn00bsdk_${_type}_exe) + target_link_libraries(${name} psn00bsdk_${_type}_exe ${PSN00BSDK_LIBRARIES}) set_target_properties(${name} PROPERTIES PREFIX "" SUFFIX ".elf") target_link_options (${name} PRIVATE -T${PSN00BSDK_LDSCRIPTS}/exe.ld) target_include_directories(${name} PRIVATE ${PSN00BSDK_INCLUDE}) # Add post-build steps to generate the .exe and symbol map once the - # executable is built. CMake 3.21 added support for target-dependent - # generator expressions (catchy name lol) in add_custom_command(), so I'm - # making heavy use of those here. + # executable is built. add_custom_command( TARGET ${name} POST_BUILD COMMAND ${ELF2X} -q ${name}.elf ${name}${PSN00BSDK_EXECUTABLE_SUFFIX} diff --git a/libpsn00b/cmake/sdk.cmake b/libpsn00b/cmake/sdk.cmake index 4c2f330..ae23a06 100644 --- a/libpsn00b/cmake/sdk.cmake +++ b/libpsn00b/cmake/sdk.cmake @@ -1,7 +1,7 @@ # PSn00bSDK toolchain setup file for CMake # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) set( PSN00BSDK_TC $ENV{PSN00BSDK_TC} diff --git a/libpsn00b/cmake/virtual_targets.cmake b/libpsn00b/cmake/virtual_targets.cmake index df1df79..9afcebd 100644 --- a/libpsn00b/cmake/virtual_targets.cmake +++ b/libpsn00b/cmake/virtual_targets.cmake @@ -72,7 +72,7 @@ set(_cflags -mno-abicalls -mgpopt -mno-extern-sdata) set(_cxxflags) set(_ldflags -G8 -static) -_add_interface_target(psn00bsdk_static_exe psn00bsdk_common ${PSN00BSDK_LIBRARIES}) +_add_interface_target(psn00bsdk_static_exe psn00bsdk_common) # Options for executables with support for dynamic linking: # - Position-independent code disabled @@ -83,7 +83,7 @@ set(_cflags -mno-abicalls -mno-gpopt) set(_cxxflags) set(_ldflags -G0 -static) -_add_interface_target(psn00bsdk_dynamic_exe psn00bsdk_common ${PSN00BSDK_LIBRARIES}) +_add_interface_target(psn00bsdk_dynamic_exe psn00bsdk_common) # Options for static libraries: # - GP-relative addressing disabled diff --git a/libpsn00b/include/malloc.h b/libpsn00b/include/malloc.h index d94823f..75c3711 100644 --- a/libpsn00b/include/malloc.h +++ b/libpsn00b/include/malloc.h @@ -1,18 +1,8 @@ #ifndef _MALLOC_H #define _MALLOC_H -#ifdef __cplusplus -extern "C" { -#endif +#warning "<malloc.h> is deprecated, include <stdlib.h> instead" -unsigned int *GetBSSend(); -void InitHeap(unsigned int *addr, int size); -int SetHeapSize(int size); -void *malloc(int size); -void free(void *ptr); - -#ifdef __cplusplus -} -#endif +#include <stdlib.h> #endif // _MALLOC_H
\ No newline at end of file diff --git a/libpsn00b/include/psxapi.h b/libpsn00b/include/psxapi.h index 9e92568..ec0dfea 100644 --- a/libpsn00b/include/psxapi.h +++ b/libpsn00b/include/psxapi.h @@ -32,6 +32,35 @@ #define RCntMdFR 0x0000 #define RCntMdGATE 0x0010 +typedef struct { // Thread control block + int status; + int mode; + union { + unsigned int reg[37]; + struct { + unsigned int zero, at; + unsigned int v0, v1; + unsigned int a0, a1, a2, a3; + unsigned int t0, t1, t2, t3, t4, t5, t6, t7; + unsigned int s0, s1, s2, s3, s4, s5, s6, s7; + unsigned int t8, t9; + unsigned int k0, k1; + unsigned int gp, sp, fp, ra; + + unsigned int cop0r14; + unsigned int hi; + unsigned int lo; + unsigned int cop0r12; + unsigned int cop0r13; + }; + }; + int _reserved[9]; +} TCB; + +typedef struct { // Process control block + TCB *thread; +} PCB; + typedef struct { // Device control block char *name; int flags; @@ -182,6 +211,9 @@ void ChangeClearRCnt(int t, int m); int Exec(struct EXEC *exec, int argc, char **argv); void FlushCache(void); +// Misc functions +int GetSystemInfo(int index); + void _boot(void); #ifdef __cplusplus diff --git a/libpsn00b/include/psxpad.h b/libpsn00b/include/psxpad.h index 01aff06..d152896 100644 --- a/libpsn00b/include/psxpad.h +++ b/libpsn00b/include/psxpad.h @@ -3,10 +3,12 @@ * 2019 Lameguy64 / Meido-Tek Productions * * Currently only provides a bunch of definitions and a few structs but no - * handling functions yet. Use the code in pad.s in one of the sample - * programs for the meantime instead. + * handling functions yet. See the io/pads example for a simple manual pollling + * implementation (with support for DualShock 2 controllers). * * Work in progress, subject to change significantly in future releases. + * + * Reference: https://gist.github.com/scanlime/5042071 */ #ifndef _PSXPAD_H @@ -50,58 +52,153 @@ #define GCON_TRIGGER 8192 #define GCON_B 16384 -// Struct for digital, joystick, dual analog and Dualshock controllers -typedef struct { - unsigned char stat; // Status - unsigned char len:4; // Data length (in halfwords) - unsigned char type:4; // Device type: - // 0x4 - digital pad - // 0x5 - analog joystick - // 0x7 - dual analog & Dualshock - unsigned short btn; // Button states - unsigned char rs_x,rs_y; // Right stick coordinates - unsigned char ls_x,ls_y; // Left stick coordinates +typedef enum { + PAD_ID_MOUSE = 0x1, // Sony PS1 mouse + PAD_ID_NEGCON = 0x2, // Namco neGcon + PAD_ID_IRQ10_GUN = 0x3, // "Konami" lightgun without composite video passthrough + PAD_ID_DIGITAL = 0x4, // Digital pad or Dual Analog/DualShock in digital mode + PAD_ID_ANALOG_STICK = 0x5, // Flight stick or Dual Analog in green LED mode + PAD_ID_GUNCON = 0x6, // Namco Guncon (lightgun with composite video passthrough) + PAD_ID_ANALOG = 0x7, // Dual Analog/DualShock in analog (red LED) mode + PAD_ID_MULTITAP = 0x8, // Multitap adapter (when tap_mode == 1) + PAD_ID_JOGCON = 0xe, // Namco Jogcon + PAD_ID_CONFIG_MODE = 0xf, // Dual Analog/DualShock in config mode (if len == 0x3) + PAD_ID_NONE = 0xf // No pad connected (if len == 0xf) +} PAD_TYPEID; + +// Controller command definitions +typedef enum { + PAD_CMD_INIT_PRESSURE = '@', // Initialize DS2 button pressure sensors (in config mode) + PAD_CMD_READ = 'B', // Read pad state and set rumble + PAD_CMD_CONFIG_MODE = 'C', // Toggle DualShock configuration mode + PAD_CMD_SET_ANALOG = 'D', // Set analog mode/LED state (in config mode) + PAD_CMD_GET_ANALOG = 'E', // Get analog mode/LED state (in config mode) + PAD_CMD_REQUEST_CONFIG = 'M', // Configure request/unlock vibration (in config mode) + PAD_CMD_RESPONSE_CONFIG = 'O' // Configure response/unlock DS2 pressure (in config mode) +} PAD_COMMAND; + +// Memory card command/response definitions +typedef enum { + MCD_CMD_READ = 'R', // Read sector + MCD_CMD_IDENTIFY = 'S', // Retrieve ID and card size information + MCD_CMD_WRITE = 'W' // Write sector +} MCD_COMMAND; + +typedef enum { + MCD_STAT_OK = 'G', + MCD_STAT_BAD_CHECKSUM = 'N', + MCD_STAT_BAD_SECTOR = 0xff +} MCD_STATUS; + +#define MCD_CMD_READ_LEN 139 +#define MCD_CMD_IDENTIFY_LEN 9 +#define MCD_CMD_WRITE_LEN 137 + +// Memory card status flags +#define MCD_FLAG_WRITE_ERROR 4 // Last write command failed +#define MCD_FLAG_NOT_WRITTEN 8 // No writes have been issued yet +#define MCD_FLAG_UNKNOWN 16 // Might be set on third-party cards + +// Struct for data returned by controllers +typedef struct _PADTYPE { + union { // Header: + struct __attribute__((packed)) { // When parsing data returned by BIOS: + unsigned char stat; // Status + unsigned char len:4; // Payload length / 2, 0 for multitap + unsigned char type:4; // Device type (PAD_TYPEID) + }; + struct __attribute__((packed)) { // When parsing raw controller response: + unsigned char len:4; // Payload length / 2, 0 for multitap + unsigned char type:4; // Device type (PAD_TYPEID) + unsigned char prefix; // Must be 0x5a + } raw; + }; + struct { // Payload: + unsigned short btn; // Button states + union { + struct { // Analog controller: + unsigned char rs_x,rs_y; // Right stick coordinates + unsigned char ls_x,ls_y; // Left stick coordinates + unsigned char press[12]; // Button pressure (DualShock 2 only) + }; + struct { // Mouse: + char x_mov; // X movement of mouse + char y_mov; // Y movement of mouse + }; + struct { // neGcon: + unsigned char twist; // Controller twist + unsigned char btn_i; // I button value + unsigned char btn_ii; // II button value + unsigned char trg_l; // L trigger value + }; + struct { // Jogcon: + unsigned short jog_rot; // Jog rotation + }; + struct { // Guncon: + unsigned short gun_x; // Gun X position in dotclocks + unsigned short gun_y; // Gun Y position in scanlines + }; + }; + }; } PADTYPE; -// Struct for a mouse controller -typedef struct { - unsigned char stat; - unsigned char len:4; - unsigned char type:4; // Device type (0x1) - unsigned short btn; - char x_mov; // X movement of mouse - char y_mov; // Y movement of mouse -} MOUSETYPE; - -// Struct for a neGcon controller (for Namco neGcon) -typedef struct { - unsigned char stat; - unsigned char len:4; - unsigned char type:4; // (0x2) - unsigned short btn; - unsigned char twist; // Controller twist - unsigned char btn_i; // I button value - unsigned char btn_ii; // II button value - unsigned char trg_l; // L trigger value -} NCONTYPE; - -// Struct for a Jogcon controller (for Namco Jogcon) -typedef struct { - unsigned char stat; - unsigned char len:4; - unsigned char type:4; // (0xE) - unsigned short btn; - unsigned short jog_rot; // Jog rotation -} JCONTYPE; - -// Struct for a Gun-Con controller (for Namco Gun-Con) -typedef struct { - unsigned char status; - unsigned char len:4; - unsigned char type:4; // (0x6) - unsigned short btn; - unsigned short gun_x; // Gun X position in dotclocks - unsigned short gun_y; // Gun Y position in scanlines -} GCONTYPE; +typedef struct _MCDRESPONSE { + unsigned char flags; // Status flags + unsigned char type1; // Must be 0x5a + unsigned char type2; // Must be 0x5d + union { + struct { // MCD_CMD_READ response: + unsigned char dummy[2]; + unsigned char ack1; // Must be 0x5c + unsigned char ack2; // Must be 0x5d + unsigned char lba_h; + unsigned char lba_l; + unsigned char data[128]; + unsigned char checksum; // = lba_h ^ lba_l ^ data + unsigned char stat; // Status (MCD_STATUS) + } read; + struct { // MCD_CMD_IDENTIFY response: + unsigned char ack1; // Must be 0x5c + unsigned char ack2; // Must be 0x5d + unsigned char size_h; // Card capacity bits 8-15 (0x04 = 128KB) + unsigned char size_l; // Card capacity bits 0-7 (0x00 = 128KB) + unsigned char blksize_h; // Sector size bits 8-15 (must be 0x00) + unsigned char blksize_l; // Sector size bits 0-7 (must be 0x80) + } identify; + struct { // MCD_CMD_WRITE response: + unsigned char dummy[131]; + unsigned char ack1; // Must be 0x5c + unsigned char ack2; // Must be 0x5d + unsigned char stat; // Status (MCD_STATUS) + } write; + }; +} MCDRESPONSE; + +//typedef PADTYPE MOUSETYPE; +//typedef PADTYPE NCONTYPE; +//typedef PADTYPE JCONTYPE; +//typedef PADTYPE GCONTYPE; + +// Structs for raw controller request +typedef struct _PADREQUEST { + unsigned char addr; // Must be 0x01 (or 02/03/04 for multitap pads) + unsigned char cmd; // Command (PAD_COMMAND) + unsigned char tap_mode; // 0x01 to enable multitap response + unsigned char motor_r; // Right motor control (on/off) + unsigned char motor_l; // Left motor control (PWM) + unsigned char dummy[4]; +} PADREQUEST; + +// Structs for raw memory card request +typedef struct _MCDREQUEST { + unsigned char addr; // Must be 0x81 (or 02/03/04 for multitap cards) + unsigned char cmd; // Command (MCD_COMMAND) + unsigned char dummy[2]; + unsigned char lba_h; // Sector address bits 8-15 (dummy for CMD_IDENTIFY) + unsigned char lba_l; // Sector address bits 0-7 (dummy for CMD_IDENTIFY) + unsigned char data[128]; // Sector payload (dummy for CMD_READ/CMD_IDENTIFY) + unsigned char checksum; // = lba_h ^ lba_l ^ data (CMD_WRITE only) + unsigned char dummy2[3]; +} MCDREQUEST; #endif
\ No newline at end of file diff --git a/libpsn00b/include/stdint.h b/libpsn00b/include/stdint.h new file mode 100644 index 0000000..83acb00 --- /dev/null +++ b/libpsn00b/include/stdint.h @@ -0,0 +1,16 @@ +#ifndef _STDINT_H +#define _STDINT_H + +typedef unsigned int size_t; + +typedef char int8_t; +typedef short int16_t; +typedef int int32_t; +typedef long long int64_t; + +typedef unsigned char uint8_t; +typedef unsigned short uint16_t; +typedef unsigned int uint32_t; +typedef unsigned long long uint64_t; + +#endif // _STDINT_H
\ No newline at end of file diff --git a/libpsn00b/include/stdlib.h b/libpsn00b/include/stdlib.h index 474eba6..de3ab47 100644 --- a/libpsn00b/include/stdlib.h +++ b/libpsn00b/include/stdlib.h @@ -19,34 +19,16 @@ extern long atol(char *s); extern char atob(char *s); // Is this right? */ -// Random number functions (not yet implemented) - -/* -int rand(); -void srand(unsigned int seed); -*/ - // Quick sort (not yet implemented) //void qsort(void *base , int nel , int width , int (*cmp)(const void *,const void *)); -// Memory allocation functions (not yet implemented, avoid using BIOS as they are reportedly buggy) - -/* -#warning "malloc() family of functions NEEDS MORE TESTING" - -void *malloc(int size); -void free(void *buf); -void *calloc(int number, int size); -void *realloc(void *buf , int n); -*/ - #ifdef __cplusplus extern "C" { #endif extern int __argc; -extern char __argv[]; +extern const char **__argv; int rand(); void srand(unsigned long seed); @@ -64,6 +46,14 @@ long atol(const char *s); double strtod(const char *nptr, char **endptr); float strtof(const char *nptr, char **endptr); +// Memory allocation functions +unsigned int *GetBSSend(); +void InitHeap(unsigned int *addr, int size); +int SetHeapSize(int size); +void *malloc(int size); +void *calloc(int number, int size); +void free(void *ptr); + #ifdef __cplusplus } #endif diff --git a/libpsn00b/include/sys/types.h b/libpsn00b/include/sys/types.h index aee197e..da43590 100644 --- a/libpsn00b/include/sys/types.h +++ b/libpsn00b/include/sys/types.h @@ -1,21 +1,13 @@ #ifndef _TYPES_H #define _TYPES_H +//#warning "<sys/types.h> and u_* types are deprecated, include <stdint.h> instead" + +//#include <stdint.h> + typedef unsigned char u_char; typedef unsigned short u_short; typedef unsigned int u_int; typedef unsigned long u_long; -typedef unsigned int size_t; - -typedef char int8_t; -typedef short int16_t; -typedef int int32_t; -typedef long long int64_t; - -typedef unsigned char uint8_t; -typedef unsigned short uint16_t; -typedef unsigned int uint32_t; -typedef unsigned long long uint64_t; - #endif // _TYPES_H
\ No newline at end of file diff --git a/libpsn00b/libc/c++-support.cxx b/libpsn00b/libc/c++-support.cxx index fcf7cfc..d169fdb 100644 --- a/libpsn00b/libc/c++-support.cxx +++ b/libpsn00b/libc/c++-support.cxx @@ -1,7 +1,6 @@ #include <assert.h> -#include <sys/types.h> +#include <stdint.h> #include <stdlib.h> -#include <malloc.h> extern "C" diff --git a/libpsn00b/libc/start.c b/libpsn00b/libc/start.c index c5872df..f190794 100644 --- a/libpsn00b/libc/start.c +++ b/libpsn00b/libc/start.c @@ -3,9 +3,9 @@ * (C) 2021 Lameguy64, spicyjpeg - MPL licensed */ -#include <sys/types.h> +#include <stdint.h> #include <string.h> -#include <malloc.h> +#include <stdlib.h> #define KERNEL_ARG_STRING ((const char *) 0x80000180) #define KERNEL_RETURN_VALUE ((volatile int *) 0x8000dffc) @@ -47,13 +47,7 @@ static void _parse_kernel_args() { } } -/* Main */ - -// How much space at the end of RAM to leave for the stack (instead of using it -// as heap). By default 128 KB are reserved for the stack, but this constant -// can be overridden in main.c (or anywhere else) simply by redeclaring it -// without the weak attribute. -const int32_t __attribute__((weak)) STACK_MAX_SIZE = 0x20000; +/* Heap initialization */ // These are defined by the linker script. Note that these are *NOT* pointers, // they are virtual symbols whose location matches their value. The simplest @@ -63,6 +57,20 @@ extern uint8_t __bss_start[]; extern uint8_t _end[]; //extern uint8_t _gp[]; +// This function should not be called manually in most cases. It might be +// useful though to change the stack size and/or reinitialize the heap on +// systems that have more than 2 MB of RAM (e.g. emulators, devkits, PS1-based +// arcade boards). +void _mem_init(size_t ram_size, size_t stack_max_size) { + void *exe_end = _end + 4; + size_t exe_size = (size_t) exe_end - (size_t) __text_start; + size_t ram_used = (0x10000 + exe_size + stack_max_size) & 0xfffffffc; + + InitHeap(exe_end, ram_size - ram_used); +} + +/* Main */ + extern void (*__CTOR_LIST__[])(void); extern void (*__DTOR_LIST__[])(void); @@ -73,24 +81,16 @@ extern int32_t main(int32_t argc, const char* argv[]); // to overwrite the arg strings in kernel RAM. void _start(int32_t override_argc, const char **override_argv) { __asm__ volatile("la $gp, _gp;"); - - // Mem init assembly function (clears BSS and InitHeap to _end which is - // not possible to do purely in C because the linker complains about - // relocation truncated to fit: R_MIPS_GPREL16 against `_end' - // Workaround is to do it in assembly because la pseudo-op doesn't use - // stupid gp relative addressing - //_mem_init(); // Clear BSS 4 bytes at a time. BSS is always aligned to 4 bytes by the // linker script. for (uint32_t *i = (uint32_t *) __bss_start; i < (uint32_t *) _end; i++) *i = 0; - // Calculate how much RAM is available after the loaded executable and - // initialize heap accordingly. - void *exe_end = _end + 4; - unsigned int exe_size = (unsigned int) exe_end - (unsigned int) __text_start; - InitHeap(exe_end, 0x200000 - (exe_size + STACK_MAX_SIZE)); + // Initialize the heap, assuming 2 MB of RAM and reserving 128 KB for the + // stack. Note that _mem_init() can be called again in main() to change + // these values. + _mem_init(0x200000, 0x20000); if (override_argv) { __argc = override_argc; diff --git a/libpsn00b/libc/string.c b/libpsn00b/libc/string.c index e11ed67..445b227 100644 --- a/libpsn00b/libc/string.c +++ b/libpsn00b/libc/string.c @@ -6,7 +6,7 @@ #include <stdio.h> #include <string.h> -#include <malloc.h> +#include <stdlib.h> int tolower(int chr) { diff --git a/libpsn00b/libc/vsprintf.c b/libpsn00b/libc/vsprintf.c index 361b24e..0a99dcc 100644 --- a/libpsn00b/libc/vsprintf.c +++ b/libpsn00b/libc/vsprintf.c @@ -58,6 +58,15 @@ pad_quantity = (pad_quantity - 1) - last; \ if(pad_quantity < 0) pad_quantity = 0; +#define calculate_real_padding_bin() \ + last = 0; \ + for (x = 0; x < 32; x++) \ + if((arg >> x) & 1) \ + last = x; \ + \ + pad_quantity = (pad_quantity - 1) - last; \ + if(pad_quantity < 0) pad_quantity = 0; + #define write_padding() \ if(!(flags & SPRINTF_NEGFIELD_FLAG)) \ for(x = 0; x < pad_quantity; x++) \ @@ -703,6 +712,9 @@ int vsnprintf(char *string, unsigned int size, const char *fmt, va_list ap) //else // arg = va_arg(ap, unsigned long long); + calculate_real_padding_bin(); + write_padding(); + for(x=31;x>=0;x--) { y = (arg >> x); @@ -715,6 +727,8 @@ int vsnprintf(char *string, unsigned int size, const char *fmt, va_list ap) put_in_string(string, ssz, y + '0', string_pos++); } + write_neg_padding(); + directive_coming = 0; break; diff --git a/libpsn00b/lzp/compress.c b/libpsn00b/lzp/compress.c index 5969dd6..9cfc64d 100644 --- a/libpsn00b/lzp/compress.c +++ b/libpsn00b/lzp/compress.c @@ -3,7 +3,6 @@ #include <string.h> #if LZP_USE_MALLOC == TRUE #include <stdlib.h> -#include <malloc.h> #endif #include "lzconfig.h" diff --git a/libpsn00b/psxapi/sys/getsysteminfo.s b/libpsn00b/psxapi/sys/getsysteminfo.s new file mode 100644 index 0000000..60c1d43 --- /dev/null +++ b/libpsn00b/psxapi/sys/getsysteminfo.s @@ -0,0 +1,10 @@ +.set noreorder + +.section .text + +.global GetSystemInfo +.type GetSystemInfo, @function +GetSystemInfo: + addiu $t2, $0, 0xa0 + jr $t2 + addiu $t1, $0, 0xb4
\ No newline at end of file diff --git a/libpsn00b/psxcd/isofs.c b/libpsn00b/psxcd/isofs.c index dc0c5ca..40a40af 100644 --- a/libpsn00b/psxcd/isofs.c +++ b/libpsn00b/psxcd/isofs.c @@ -1,6 +1,6 @@ #include <sys/types.h> #include <stdio.h> -#include <malloc.h> +#include <stdlib.h> #include <string.h> #include <psxgpu.h> #include <psxsio.h> diff --git a/libpsn00b/psxetc/dl.c b/libpsn00b/psxetc/dl.c index 586e7e1..e43374f 100644 --- a/libpsn00b/psxetc/dl.c +++ b/libpsn00b/psxetc/dl.c @@ -23,9 +23,9 @@ * of entries */ -#include <sys/types.h> +#include <stdint.h> #include <stdio.h> -#include <malloc.h> +#include <stdlib.h> #include <ctype.h> #include <elf.h> #include <dlfcn.h> diff --git a/libpsn00b/psxgpu/font.c b/libpsn00b/psxgpu/font.c index 7d0dd89..4c715a9 100644 --- a/libpsn00b/psxgpu/font.c +++ b/libpsn00b/psxgpu/font.c @@ -1,7 +1,7 @@ #include <sys/types.h> #include <stdio.h> #include <string.h> -#include <malloc.h> +#include <stdlib.h> #include <ctype.h> #include <psxgpu.h> diff --git a/template/CMakeLists.txt b/template/CMakeLists.txt index c2d0d9d..c0c05d0 100644 --- a/template/CMakeLists.txt +++ b/template/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) # CMAKE_TOOLCHAIN_FILE must be set to point to cmake/sdk.cmake in the PSn00bSDK # installation directory *before* calling project(). You don't have to hardcode diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index dd90109..af99046 100644 --- a/tools/CMakeLists.txt +++ b/tools/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK tools build script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.21) +cmake_minimum_required(VERSION 3.20) project( PSn00bSDK-tools @@ -12,22 +12,22 @@ project( include(GNUInstallDirs) -## External dependencies - -# Let CMake attempt to find tinyxml2 on its own first. This avoids invoking -# pkg-config where it might not be installed, and allows usage of package -# managers like vcpkg. The path to tinyxml2 can also be specified manually by -# passing -Dtinyxml2_ROOT. -find_package(tinyxml2 CONFIG) +set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") +set(CMAKE_C_STANDARD 11) +set(CMAKE_CXX_STANDARD 11) -if(NOT tinyxml2_FOUND) - find_package(PkgConfig REQUIRED) - pkg_search_module(_tinyxml2 REQUIRED IMPORTED_TARGET tinyxml2) +## Dependencies - add_library(tinyxml2::tinyxml2 ALIAS PkgConfig::_tinyxml2) +if(NOT EXISTS ${PROJECT_SOURCE_DIR}/tinyxml2/tinyxml2.cpp) + message(FATAL_ERROR "The tinyxml2 directory is empty. Run 'git submodule update --init --recursive' to populate it.") endif() -## Internal dependencies +# Build tinyxml2. I didn't bother with tinyxml2's actual CMake integration +# because it's far easier do do this. It is a single-file library after all. +add_library (tinyxml2 STATIC tinyxml2/tinyxml2.cpp) +target_include_directories(tinyxml2 PUBLIC tinyxml2) + +#add_subdirectory(tinyxml2) # Build liblzp using sources from the libpsn00b tree. Hacky but it works. set(LIBPSN00B_PATH ${PROJECT_SOURCE_DIR}/../libpsn00b) @@ -46,28 +46,18 @@ target_include_directories( ## Executables -# This is required in order to properly link against tinyxml2 under MSVC. -set(CMAKE_MSVC_RUNTIME_LIBRARY "MultiThreaded$<$<CONFIG:Debug>:Debug>") -set(CMAKE_C_STANDARD 11) -set(CMAKE_CXX_STANDARD 11) - add_executable(elf2x util/elf2x.c) add_executable(elf2cpe util/elf2cpe.c) add_executable(smxlink smxlink/main.cpp smxlink/timreader.cpp) add_executable(lzpack lzpack/main.cpp lzpack/filelist.cpp) -target_link_libraries(smxlink tinyxml2::tinyxml2) -target_link_libraries(lzpack tinyxml2::tinyxml2 lzp) +target_link_libraries(smxlink tinyxml2) +target_link_libraries(lzpack tinyxml2 lzp) ## Installation -# Install the executables alongside the tinyxml2 DLL (if any) and copy the -# Blender SMX export plugin to the data directory (for manual installation). -install( - TARGETS elf2x elf2cpe smxlink lzpack - RUNTIME_DEPENDENCIES - PRE_EXCLUDE_REGEXES ".*" - PRE_INCLUDE_REGEXES "tinyxml2" -) +# Install the executables and copy the Blender SMX export plugin to the data +# directory (for manual installation). +install(TARGETS elf2x elf2cpe smxlink lzpack) install( DIRECTORY plugin DESTINATION ${CMAKE_INSTALL_DATADIR}/psn00bsdk diff --git a/tools/mkpsxiso b/tools/mkpsxiso new file mode 160000 +Subproject fe14e95d07a0da5727a8c2fb3ff2aaa892e465c diff --git a/tools/tinyxml2 b/tools/tinyxml2 new file mode 160000 +Subproject a9773976845b19e89020c1215781e71116477ef diff --git a/tools/util/elf2cpe.c b/tools/util/elf2cpe.c index 46b0a37..210f25b 100644 --- a/tools/util/elf2cpe.c +++ b/tools/util/elf2cpe.c @@ -1,6 +1,6 @@ #include <stdio.h> #include <string.h> -#include <malloc.h> +#include <stdlib.h> #include "elf.h" #ifdef WIN32 |
