diff options
| author | John "Lameguy" Wilbert Villamor <lameguy64@gmail.com> | 2022-10-19 17:57:06 +0800 |
|---|---|---|
| committer | GitHub <noreply@github.com> | 2022-10-19 17:57:06 +0800 |
| commit | e08a3d9366f8ca14a76b3dd569dac1fb9f569748 (patch) | |
| tree | 33654513b0b184c27f8035dbc405640fcbeb44ab | |
| parent | c4a2533d21dfd05cde841ea48c67b05e0e6a853f (diff) | |
| parent | 9b2ffc6078a850b7d354855cca7622090b41f30c (diff) | |
| download | psn00bsdk-e08a3d9366f8ca14a76b3dd569dac1fb9f569748.tar.gz | |
Merge pull request #59 from spicyjpeg/psxmdec
IRQ handler fix, .STR playback example, multiple library builds (v0.21)
92 files changed, 2233 insertions, 1417 deletions
diff --git a/CHANGELOG.md b/CHANGELOG.md index f7f4f93..461d2b8 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -19,6 +19,59 @@ to ensure the changelog can be parsed correctly. ------------------------------------------------------------------------------- +## 2022-10-16: 0.21 + +spicyjpeg: + +- psxetc: Fixed (another) critical bug in the IRQ callback dispatcher. This + also fixed some examples that were broken ever since the library was + rewritten in C. Made the dynamic linker less verbose, improving DLL loading + speed in debug mode. + +- psxcd: Added `CdDataSync()`. Renamed `CdlModeSize0` and `CdlModeSize1` to + `CdlModeIgnore` and `CdlModeSize` respectively. + +- psxpress: Fixed a bug in the Huffman decompression API that would make it + crash if the bitstream header didn't contain a valid decompressed length, + even if the bitstream was properly encoded. This fix makes the API fully + usable for video playback (as demonstrated by the new example). + +- examples: Added `mdec/strvideo` FMV playback example. Fixed + `system/childexec` not properly uninstalling BIOS callbacks before launching + the child executable. Added pause/resume code to `sound/spustream`. + +## 2022-10-11 + +spicyjpeg: + +- libpsn00b: Completely rewritten CMake scripts. Six copies of each library are + now built and installed, one for each combination of configuration (debug and + release) and target type (executable with/without $gp-relative addressing and + DLL). Library debug logging is now completely disabled when a project is + built in release mode (using `-DCMAKE_BUILD_TYPE=Release`). + +- libc: Replaced `memset()` with a much faster optimized implementation that + makes use of Duff's device. Added `GetHeapUsage()` and `TrackHeapUsage()`. + Removed `_mem_init()`. + +- psxetc: Fixed a critical bug in `DMACallback()` that would lead to the DMA + interrupt handler being disabled entirely in some edge cases. + +- psxgpu: Replaced the debug font with an improved one. No changes have been + made to the API. + +- psxcd: Added `CdGetSector2()` (asynchronous variant of `CdGetSector()`). + `CdControl()` can now take a `CdlLOC` as second argument when issuing a seek + command (previously the argument was ignored if the command was not `ReadN` + or `ReadS`). + +- Replaced the `DEBUG` macro with the standard C `NDEBUG` macro, which is only + defined if the project is being built in release mode. + +- Added `PSN00BSDK_*_LINK_LIBRARIES` CMake variables to control which SDK + libraries are linked to newly created executables and DLLs. Updated + `cmake_reference.md` to reflect the changes. + ## 2022-09-22 spicyjpeg: diff --git a/CMakeLists.txt b/CMakeLists.txt index 4f255a2..3ce6573 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,13 +6,13 @@ # workaround is to use ExternalProject_Add() to launch multiple independent # CMake instances, creating what's known as a "superbuild". -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) include(ExternalProject) project( PSn00bSDK LANGUAGES NONE - VERSION 0.20 + VERSION 0.21 DESCRIPTION "Open source PlayStation 1 SDK" HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk" ) @@ -89,11 +89,17 @@ set( -DPSN00BSDK_BUILD_DATE:STRING=${PSN00BSDK_BUILD_DATE} -DPSN00BSDK_GIT_TAG:STRING=${PSN00BSDK_GIT_TAG} -DPSN00BSDK_GIT_COMMIT:STRING=${PSN00BSDK_GIT_COMMIT} - -DMKPSXISO_NO_LIBFLAC:BOOL=${MKPSXISO_NO_LIBFLAC} +) +set( + _tools_args + ${_common_args} + -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE} + -DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/tree -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} + -DMKPSXISO_NO_LIBFLAC:BOOL=${MKPSXISO_NO_LIBFLAC} ) set( - _sdk_args + _libpsn00b_args ${_common_args} -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE} -DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/tree @@ -103,6 +109,7 @@ set( ${_common_args} -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${PROJECT_BINARY_DIR}/tree/${CMAKE_INSTALL_LIBDIR}/libpsn00b/cmake/sdk.cmake -DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/examples + -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} ) # Ensure PSn00bSDK isn't being built using the toolchain file from PSn00bSDK @@ -120,35 +127,42 @@ endif() ExternalProject_Add( tools + PREFIX subprojects SOURCE_DIR ${PROJECT_SOURCE_DIR}/tools - BINARY_DIR tools-build - CMAKE_CACHE_ARGS ${_sdk_args} + CMAKE_CACHE_ARGS ${_tools_args} INSTALL_DIR tree ) ExternalProject_Add( mkpsxiso + PREFIX subprojects SOURCE_DIR ${PROJECT_SOURCE_DIR}/tools/mkpsxiso - BINARY_DIR mkpsxiso-build - CMAKE_CACHE_ARGS ${_sdk_args} + CMAKE_CACHE_ARGS ${_tools_args} + INSTALL_DIR tree +) +ExternalProject_Add( + libpsn00b-debug + PREFIX subprojects + SOURCE_DIR ${PROJECT_SOURCE_DIR}/libpsn00b + CMAKE_GENERATOR ${LIBPSN00B_GENERATOR} + CMAKE_CACHE_ARGS ${_libpsn00b_args} -DCMAKE_BUILD_TYPE:STRING=Debug INSTALL_DIR tree ) ExternalProject_Add( - libpsn00b + libpsn00b-release + PREFIX subprojects SOURCE_DIR ${PROJECT_SOURCE_DIR}/libpsn00b - BINARY_DIR libpsn00b-build CMAKE_GENERATOR ${LIBPSN00B_GENERATOR} - CMAKE_CACHE_ARGS ${_sdk_args} + CMAKE_CACHE_ARGS ${_libpsn00b_args} -DCMAKE_BUILD_TYPE:STRING=Release INSTALL_DIR tree - #DEPENDS tools ) ExternalProject_Add( examples + PREFIX subprojects SOURCE_DIR ${PROJECT_SOURCE_DIR}/examples - BINARY_DIR examples-build CMAKE_GENERATOR ${LIBPSN00B_GENERATOR} CMAKE_CACHE_ARGS ${_examples_args} INSTALL_DIR examples - DEPENDS libpsn00b tools mkpsxiso + DEPENDS libpsn00b-debug libpsn00b-release tools mkpsxiso EXCLUDE_FROM_ALL ${SKIP_EXAMPLES} ) diff --git a/CMakePresets.json b/CMakePresets.json index 89002a3..eca1478 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -1,8 +1,8 @@ { - "version": 2, + "version": 3, "cmakeMinimumRequired": { "major": 3, - "minor": 20, + "minor": 21, "patch": 0 }, "configurePresets": [ @@ -36,7 +36,7 @@ and performance reasons. ## Notable features -As of March 28, 2022 +As of October 11, 2022: * Extensive GPU support with lines, flat shaded or textured polygon and sprite primitives, high-speed DMA for VRAM transfers and ordering tables. All video @@ -54,26 +54,24 @@ As of March 28, 2022 HLE BIOS implementations and loader/menu type homebrew programs. * BIOS controller functions for polling controller input work as intended - thanks to proper handling of hardware interrupts. No crude direct I/O polling - of controllers in the main loop. - -* Complete Serial I/O support with SIOCONS driver for tty stdin/stdout - console access. Hardware flow control supported. - -* Full CD-ROM support using libpsxcd featuring data read, CD audio and XA audio - playback, built-in ISO9660 file system parser with no file count limit - (classic ISO9660 only, no Rock Ridge or Joliet extensions) and multi-session. - -* Preliminary MDEC support implemented with libpsxpress (no VLC decoding yet). - Sample encoder included. - -* Can target Konami System 573 arcade hardware with limited support - (see examples/io/system573/main.c for details) - -* Experimental support for compiling separate sections of an executable into - shared library files (DLLs) and linking them dynamically at runtime, plus - support for function and variable introspection by loading a map file - generated at build time. + thanks to proper handling of hardware interrupts. Optional limited support + for manual polling. + +* Complete Serial I/O support and console driver to redirect standard input and + output to the serial port. Hardware flow control supported. + +* Full CD-ROM support using `libpsxcd` featuring data reading, CD-DA and XA + audio playback, a built-in ISO9660 file system parser with no file count + limit and support for multi-session discs. + +* MDEC support, lossy image decompression and video playback using + `libpsxpress` (currently only bitstream versions 1 and 2 are supported). + +* Preliminary limited support for Konami System 573 arcade hardware. + +* Experimental support for dynamic linking at runtime, with support for + function and variable introspection by loading a map file generated at build + time. * Uses Sony SDK library syntax for familiarity to experienced programmers and makes porting existing homebrew projects to PSn00bSDK easier. @@ -113,21 +111,21 @@ apply to PSn00bSDK. ## To-do List -* psxspu: Plenty of work to be done. Hardware timer driven sound/music - system may need to be implemented (an equivalent to the Ss* series of - functions in libspu basically). Functions that make use of the SPU RAM - interrupt feature to play or capture streamed audio should also be added. +* `libpsxspu`: Plenty of work to be done. Some kind of MIDI sequencer (similar + to the one present in the official SDK) should be added at some point, along + with a proper API for audio streaming. -* psxcd: Implement a command queue mechanism for the CD-ROM? +* `libpsxcd`: Implement a command queue mechanism for the CD-ROM. -* libc: Improve the memory allocation framework with multiple allocators, GC - and maybe helpers to manage swapping between main RAM and VRAM/SPU RAM. +* `libpsxpress`: Add support for version 3 and IKI frame bitstreams. -* Further support for MDEC, and tooling to transcode videos to .STR files - (either reimplementing the container and compression format used by the Sony - SDK, or a custom format with better compression). +* `libc`: Improve the memory allocation framework with multiple allocators, + replace the string functions with optimized implementations and maybe add + helpers to manage swapping between main RAM and VRAM/SPU RAM. -* Pad and memory card libraries that don't use the BIOS routines. +* Add a full controller and memory card API that does not depend on the BIOS + controller driver, and possibly a library for interfacing to IDE/ATAPI drives + to make development for arcade systems easier. ## Credits @@ -138,8 +136,8 @@ Main developer/author/whatever: Contributors: -* **spicyjpeg**: dynamic linker, CMake scripts, some docs and examples - (`system/dynlink`, `sound/spustream`, `io/pads`, `io/system573`). +* **spicyjpeg**: dynamic linker, `libpsxpress`, CMake scripts, some docs and + examples. * **Silent**, **G4Vi**, **Chromaryu**: `mkpsxiso` and `dumpsxiso` (maintained as a [separate repo](https://github.com/Lameguy64/mkpsxiso)). diff --git a/cpack/fakeroot_fix.cmake b/cpack/fakeroot_fix.cmake index 1c1430d..e34baa0 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.20) +cmake_minimum_required(VERSION 3.21) set(_prefix ${CPACK_TEMPORARY_INSTALL_DIRECTORY}${CPACK_PACKAGING_INSTALL_PREFIX}) diff --git a/cpack/setup.cmake b/cpack/setup.cmake index 13da515..d585641 100644 --- a/cpack/setup.cmake +++ b/cpack/setup.cmake @@ -2,8 +2,6 @@ # rules to bundle the GCC toolchain and CMake in packages. It is included by # the main CMakeLists.txt script. -cmake_minimum_required(VERSION 3.20) - ## Settings # These can be set from the command line to completely disable bundling CMake @@ -130,11 +128,11 @@ set(CPACK_PACKAGE_INSTALL_DIRECTORY PSn00bSDK) ## DEB/RPM variables -set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.28), cmake (>= 3.20)") +set(CPACK_DEBIAN_PACKAGE_DEPENDS "libc6 (>= 2.28), cmake (>= 3.21)") 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.20") +set(CPACK_RPM_PACKAGE_REQUIRES "cmake >= 3.21") set(CPACK_RPM_PACKAGE_SUGGESTS "ninja-build >= 1.10, git >= 2.25") #set(CPACK_RPM_PACKAGE_RELOCATABLE ON) diff --git a/doc/cmake_reference.md b/doc/cmake_reference.md index 25a89ec..bceaac9 100644 --- a/doc/cmake_reference.md +++ b/doc/cmake_reference.md @@ -1,6 +1,15 @@ # PSn00bSDK CMake reference +- [Setup](#setup) +- [Targets](#targets) +- [Commands](#commands) +- [Target properties](#target-properties) +- [Preprocessor definitions](#preprocessor-definitions) +- [Cached settings](#cached-settings) +- [Internal settings](#internal-settings) +- [Read-only variables](#read-only-variables) + ## Setup The only requirement to use the SDK in CMake is to set the @@ -10,32 +19,35 @@ This can be done on the command line (`-DCMAKE_TOOLCHAIN_FILE=...`), in `CMakeLists.txt` (`set(CMAKE_TOOLCHAIN_FILE ...)` before `project()`) or using [presets](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html). -It's suggested to have a default preset that sets `CMAKE_TOOLCHAIN_FILE` to +It's suggested to have a default preset that sets the toolchain file to `$env{PSN00BSDK_LIBS}/cmake/sdk.cmake`, taking advantage of the `PSN00BSDK_LIBS` environment variable (used by former PSn00bSDK versions) to -automatically find the SDK. Such a preset can be created by placing a +automatically find the SDK if set. Such a preset can be created by placing a `CMakePresets.json` file in the project's root with the following contents: ```json { - "version": 2, + "version": 3, "cmakeMinimumRequired": { "major": 3, - "minor": 20, + "minor": 21, "patch": 0 }, "configurePresets": [ { - "name": "default", - "displayName": "Default configuration", - "description": "Use this preset to build the project using PSn00bSDK.", - "generator": "Ninja", - "binaryDir": "${sourceDir}/build", + "name": "default", + "displayName": "Default configuration", + "description": "Use this preset to build the project using PSn00bSDK.", + "generator": "Ninja", + "toolchainFile": "$env{PSN00BSDK_LIBS}/cmake/sdk.cmake", + "binaryDir": "${sourceDir}/build", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_TOOLCHAIN_FILE": "$env{PSN00BSDK_LIBS}/cmake/sdk.cmake", - "PSN00BSDK_TC": "", - "PSN00BSDK_TARGET": "mipsel-none-elf" + "CMAKE_BUILD_TYPE": "Debug", + "PSN00BSDK_TC": "", + "PSN00BSDK_TARGET": "mipsel-none-elf" + }, + "warnings": { + "dev": false } } ] @@ -43,230 +55,345 @@ automatically find the SDK. Such a preset can be created by placing a ``` To avoid having to pass variables to CMake each time the project is built, a -second presets file named `CMakeUserPresets.json` can be created and populated -with hardcoded values in the `cacheVariables` section. This file can be kept -private (e.g. by adding it to `.gitignore`); CMake will automatically load -presets from it instead of `CMakePresets.json` if it exists. +second file named `CMakeUserPresets.json` can be created and populated with +hardcoded values in the `cacheVariables` section. This file can be kept private +(e.g. by adding it to `.gitignore`), and CMake will automatically load presets +from it instead of `CMakePresets.json` if it exists. See the [template](../template/CMakeLists.txt) for an example CMake script showing how to build a simple project. ## Targets -These targets are defined when using PSn00bSDK. There is no need to explicitly -link against any of these, as the helper commands (see below) handle linking -behind the scenes. To avoid conflicts, however, no target should be given any -of these names. - -- `c`, `psxgpu`, `psxgte`, `psxspu`, `psxcd`, `psxsio`, `psxetc`, `psxapi`, `lzp` -- `psn00bsdk_common`, `psn00bsdk_object_lib` -- `psn00bsdk_static_exe` -- `psn00bsdk_dynamic_exe` -- `psn00bsdk_static_lib` -- `psn00bsdk_shared_lib`, `psn00bsdk_module_lib` +The toolchain script creates a target for each PSn00bSDK library. Currently +the following targets are defined: + +- `psxgpu` +- `psxgte` +- `psxspu` +- `psxcd` +- `psxpress` +- `psxsio` +- `psxetc` +- `psxapi` +- `lzp` +- `c` + +Note that these are not actual libraries but virtual targets that link to the +appropriate version of the respective library, depending on the value of the +`PSN00BSDK_TARGET_TYPE` property; refer to the target properties section for +more information. Linking manually using the `target_link_libraries()` command +is usually not necessary for executables as they are linked to all libraries +by default (see `PSN00BSDK_EXECUTABLE_LINK_LIBRARIES`). + +Additionally, two "hidden" libraries named `gcc` and `psn00bsdk` are linked by +default to all targets. The former is the GCC toolchain's `libgcc` (see +`PSN00BSDK_LIBGCC`) while the latter is a virtual target used to set compiler +flags and paths. ## Commands -- `psn00bsdk_add_executable(<name> <STATIC|DYNAMIC> [EXCLUDE_FROM_ALL] [sources...])` +### `psn00bsdk_add_executable` - A wrapper around `add_executable()` to create PS1 executables. Three files - will be generated for each call to this function: +```cmake +psn00bsdk_add_executable( + <target name> <GPREL|STATIC|NOGPREL|DYNAMIC> + [EXCLUDE_FROM_ALL] + [sources...] +) +``` - - `<name>.elf` (regular ELF executable) - - `<name>.exe` (executable converted to the format expected by the PS1) - - `<name>.map` (symbol map file for dynamic linking/introspection) +A wrapper around `add_executable()` to create PS1 executables. Three files will +be generated for each call to this function: - The `.exe` and `.map` extensions can be customized by overriding - `PSN00BSDK_EXECUTABLE_SUFFIX` and `PSN00BSDK_SYMBOL_MAP_SUFFIX` prior to - creating the executable. +- `<target name>.elf` (regular ELF executable) +- `<target name>.exe` (executable converted to the format expected by the PS1) +- `<target name>.map` (symbol map file for dynamic linking/introspection) - The second argument (mandatory) specifies whether the executable is going to - load DLLs at runtime. If set to `STATIC`, $gp-relative addressing (i.e. - reusing the $gp register normally used for DLL addressing) will be enabled, - slightly reducing executable size and RAM usage but breaking compatibility - with the dynamic linker. +The `.exe` and `.map` extensions can be customized by overriding +`PSN00BSDK_EXECUTABLE_SUFFIX` and `PSN00BSDK_SYMBOL_MAP_SUFFIX` prior to +creating the executable. -- `psn00bsdk_add_library(<name> <STATIC|OBJECT|SHARED|MODULE> [EXCLUDE_FROM_ALL] [sources...])` +The second argument (mandatory) specifies whether the executable is going to +load DLLs at runtime. If set to `GPREL` or `STATIC`, $gp-relative addressing +(i.e. reusing the $gp register normally used for DLL addressing to reference +global variables) will be enabled, slightly reducing executable size and RAM +usage but breaking compatibility with the dynamic linker. - Wraps `add_library()` to create static libraries or dynamically-linked - libraries (DLLs). +All executables are automatically linked to the libraries listed in +`PSN00BSDK_EXECUTABLE_LINK_LIBRARIES` (all SDK libraries by default). This +variable can be modified prior to creating the executable to select which +libraries to link. - The second argument (mandatory, unlike `add_library()`) specifies the type of - library to create. `STATIC` will create a static library named `lib<name>.a`. - `SHARED` and `MODULE` will compile a DLL, producing the following files (note - that there is no `lib` prefix for DLLs): +### `psn00bsdk_add_library` - - `<name>.so` (regular ELF shared library) - - `<name>.dll` (raw binary with some ELF headers prepended) +```cmake +psn00bsdk_add_library( + <target name> <STATIC|OBJECT|SHARED|MODULE> + [EXCLUDE_FROM_ALL] + [sources...] +) +``` - As with executables, the `.dll` extension can be customized by setting - `PSN00BSDK_SHARED_LIBRARY_SUFFIX`. +Wraps `add_library()` to create static libraries or dynamically-linked +libraries (DLLs). -- `psn00bsdk_add_cd_image(<name> <image name> <config file> [DEPENDS ...] [...])` +The second argument (mandatory, unlike CMake's regular `add_library()`) +specifies the type of library to create. `STATIC` will create a static library +named `lib<target name>.a`. `SHARED` and `MODULE` will compile a DLL, producing +the following files (there is no `lib` prefix for DLLs): - Creates a new target that will build a CD image using `mkpsxiso`. +- `<target name>.so` (regular ELF shared library) +- `<target name>.dll` (raw binary with some ELF headers prepended) - The first argument is the name of the target to create; next up is the name - of the generated image file (`<image name>.bin` + `<image name>.cue`). The - third argument is the path to the XML file (relative to the source directory) - passed to `mkpsxiso`. +The `.dll` extension can be customized by setting +`PSN00BSDK_SHARED_LIBRARY_SUFFIX` prior to creating the DLL. - The XML file is "configured" by CMake, i.e. any `${var}` or `@var@` - expressions are replaced with the values of the respective variables. In - particular `${CD_IMAGE_NAME}` is replaced with the second argument passed to - `psn00bsdk_add_cd_image()`; the file must properly set the output file names - like this: +All DLLs are automatically linked to the libraries listed in +`PSN00BSDK_SHARED_LIBRARY_LINK_LIBRARIES` (none by default). This variable can +be modified prior to creating the DLL to select which libraries to link. - ```xml - <?xml version="1.0" encoding="utf-8"?> - <iso_project - image_name="${CD_IMAGE_NAME}.bin" - cue_sheet="${CD_IMAGE_NAME}.cue" - > - ``` +**IMPORTANT**: when adding a static library using this command (or CMake's +`add_library()`), the `PSN00BSDK_TARGET_TYPE` property **must** be set on it +afterwards in order to let CMake know whether the static library is going to be +linked to an executable or a DLL. See `PSN00BSDK_TARGET_TYPE` for more +information. - Any additional argument is passed through to the underlying call to - `add_custom_target()`, so most of the options supported by - `add_custom_target()` (including `DEPENDS`) are also supported here. +### `psn00bsdk_add_cd_image` -- `psn00bsdk_target_incbin(<target> <PRIVATE|PUBLIC|INTERFACE> <symbol name> <binary file>)` +```cmake +psn00bsdk_add_cd_image( + <target name> + <image name> + <path to XML config file> + [DEPENDS <targets|files...>] + [other options...] +) +``` - Embeds the contents of a binary file into an executable or a library. +Creates a new virtual target that will build a CD image using `mkpsxiso`. The +CD image will always be considered out-of-date and built, even if none of its +dependencies or any other files have been modified. + +The first argument is the name of the target to create; next up is the name of +the generated image file (`<image name>.bin` + `<image name>.cue`). The third +argument is the path to the XML file (relative to the source directory) passed +to `mkpsxiso`. + +The XML file is "configured" by CMake, i.e. any `${var}` or `@var@` expressions +are replaced with the values of the respective variables. In particular +`${CD_IMAGE_NAME}` is replaced with the second argument passed to +`psn00bsdk_add_cd_image()`; the file must properly set the output file names +like this: + +```xml +<?xml version="1.0" encoding="utf-8"?> +<iso_project + image_name="${CD_IMAGE_NAME}.bin" + cue_sheet="${CD_IMAGE_NAME}.cue" +> + <!-- ... --> +</iso_project> +``` - A new symbol/object will be created with the given name, escaped by replacing - non-alphanumeric characters with underscores. The contents of the file will - be aligned to 4 bytes and placed in the `.data` section. An unsigned 32-bit - integer named `<symbol name>_size` will also be defined and set to the length - of the file in bytes (without taking alignment/padding into account). +Any additional argument is passed through to the underlying call to +`add_custom_target()`, so most of the options supported by +`add_custom_target()` (including `DEPENDS`) are also supported here. - Once added the file and its size can be accessed by C/C++ code by declaring - the respective symbols as an extern array and as an integer, like this: +### `psn00bsdk_target_incbin` - ```c - extern const uint8_t my_file[]; - extern const size_t my_file_size; - ``` +```cmake +psn00bsdk_target_incbin( + <target name> <PRIVATE|PUBLIC|INTERFACE> + <data symbol name> + <path to binary file> +) +``` - The fourth argument specifies the path to the binary file relative to the - source directory. This path can be prepended with `${PROJECT_BINARY_DIR}/` to - reference a file generated by the build script (such as an LZP archive): in - that case a file-level dependency will also be created, ensuring CMake does - not attempt to compile the executable or library before the file is built. +Embeds the contents of a binary file into an executable or a library. - **IMPORTANT**: in order for this command to work, assembly language support - must be enabled by specifying `LANGUAGES C ASM` (or `LANGUAGES C CXX ASM` if - C++ is also used) when invoking `project()`. +A new symbol/object will be created with the given name, escaped by replacing +non-alphanumeric characters with underscores. The contents of the file will be +aligned to 4 bytes and placed in the `.data` section. An unsigned 32-bit +integer named `<symbol name>_size` will also be defined and set to the length +of the file in bytes (without taking alignment/padding into account). -- `psn00bsdk_target_incbin_a(<target> <PRIVATE|PUBLIC|INTERFACE> <symbol name> <size symbol name> <binary file> <alignment>)` +Once added the file and its size can be accessed by C/C++ code by declaring the +respective symbols as an extern array and as an integer, like this: - Advanced variant of `psn00bsdk_target_incbin()` that allows specifying a - custom name for the size symbol and changing the default alignment setting. - Note that the size integer is always aligned to a multiple of 4 bytes as the - MIPS architecture doesn't support unaligned reads. +```c +extern const uint8_t my_file[]; +extern const size_t my_file_size; +``` -## Definitions +The fourth argument specifies the path to the binary file relative to the +source directory. This path can be prepended with `${PROJECT_BINARY_DIR}/` to +reference a file generated by the build script (such as an LZP archive): in +that case a file-level dependency will also be created, ensuring CMake does not +attempt to compile the executable or library before the file is built. + +**IMPORTANT**: in order for this command to work, assembly language support +must be enabled by specifying `LANGUAGES C ASM` (or `LANGUAGES C CXX ASM` to +enable C++ support as well) when invoking `project()`. + +### `psn00bsdk_target_incbin_a` + +```cmake +psn00bsdk_target_incbin_a( + <target name> <PRIVATE|PUBLIC|INTERFACE> + <data symbol name> + <size symbol name> + <path to binary file> + <section name> + <alignment> +) +``` -When compiling executables and libraries using the above commands the following -preprocessor macros are automatically `#define`'d: +Advanced variant of `psn00bsdk_target_incbin()` that allows specifying a custom +name for the size symbol and changing the default alignment setting. The value +of the size integer is always rounded up to a multiple of 4 bytes. -- `PLAYSTATION` +See `psn00bsdk_target_incbin()` above for more details. - Always set to 1. Can be used to implement different options or code paths for - libraries, so they can target both the host and PS1 (as it won't be defined - when compiling outside of the SDK). +## Target properties -- `DEBUG` +Each of the following properties can be set individually for each executable or +library using CMake's `set_property()` and `set_target_properties()` commands. - Defined and set to 1 in a debug configuration, i.e. when the - `CMAKE_BUILD_TYPE` variable is set to `Debug`. This value is used by the - PSn00bSDK libraries, and should be used in executables, to enable additional - debug logging. +### `PSN00BSDK_TARGET_TYPE` - Note that the default CMake configuration is usually debug, so it's - recommended to specify `-DCMAKE_BUILD_TYPE=Release` to get rid of the logging - overhead in release builds and reduce executable size. +Determines which SDK libraries are linked to and which compiler flags are added +to the target. Must be set to `EXECUTABLE_GPREL`, `EXECUTABLE_NOGPREL` or +`SHARED_LIBRARY`. -## Cached settings +This property is initialized automatically on executables and DLLs created via +`psn00bsdk_add_executable()` or `psn00bsdk_add_library()`, but *not* on static +libraries as CMake has no way to know about their intended usage (i.e. whether +they are going to be linked to an executable with or without $gp-relative +addressing, or to a DLL). Thus, `PSN00BSDK_TARGET_TYPE` must be set manually on +all static libraries and must match the value set on any executable or DLL the +static library is going to be linked to. + +There is no way to build a "hybrid" static library that can be linked to +multiple target types, short of building multiple copies of it. A workaround +(used internally by PSn00bSDK) is to create a virtual target and use CMake +generator expressions to link to one of the copies depending on the value of +`PSN00BSDK_TARGET_TYPE`. -These variables are stored in CMake's cache and can be edited by the project's -build script, from the CMake command line when configuring the project -(`-Dname=value`) or using an editor such as the CMake GUI. +## Preprocessor definitions -- `PSN00BSDK_TARGET` (`STRING`) +When compiling executables and libraries using the commands listed above the +following C/C++ preprocessor macros are automatically `#define`d: - The GCC toolchain's target triplet. PSn00bSDK assumes the toolchain targets - `mipsel-none-elf` by default, however this can be changed to e.g. use a MIPS - toolchain that was compiled for a slightly-different-but-equivalent target. +### `PSN00BSDK` - The following GCC target triplets have been confirmed to work with PSn00bSDK: +Always set to 1. Can be used to implement different options or code paths for +projects that target both PSn00bSDK and other platforms. - - `mipsel-none-elf` - - `mipsel-unknown-elf` - - ~~`mipsel-linux-gnu`~~ (has issues with linking) +### `NDEBUG` -- `PSN00BSDK_TC` (`PATH`) +Defined and set to 1 in a release configuration, i.e. when `CMAKE_BUILD_TYPE` +is set to `Release` or when a multi-configuration generator is building the +project in release mode; not defined if the project is being built in debug +mode. This value is used by the PSn00bSDK libraries, and should be used in +projects, to enable assertions and additional debug logging (the `assert()` +macro already resolves to a no-op in release mode). - Path to the GCC toolchain's installation prefix/directory. If not set, CMake - will attempt to find the toolchain in the `PATH` environment variable and - store its path in the project's variable cache (so the search does not have - to be repeated). It is recommended to add the toolchain's `bin` subfolder to - `PATH` rather than setting this variable. +Note that the default CMake configuration is usually debug. It is recommended +to build a project in release mode whenever appropriate (by specifying +`-DCMAKE_BUILD_TYPE=Release` or using the Ninja multi-configuration generator) +to get rid of logging overhead. - **IMPORTANT**: if the toolchain's target is not `mipsel-none-elf`, - `PSN00BSDK_TARGET` must be set regardless of whether or not `PSN00BSDK_TC` is - also set. +## Cached settings + +These variables are stored in CMake's cache and are meant to be set by the end +user when building the project (rather than by the project itself). They can be +modified by the build script after invoking `project()`, from the CMake command +line when configuring (`-Dname=value`) or using an IDE or other editor such as +the CMake GUI. + +### `PSN00BSDK_TARGET` (`STRING`) + +The GCC toolchain's target triplet. PSn00bSDK assumes the toolchain targets +`mipsel-none-elf` by default, however this can be changed to e.g. use a MIPS +toolchain that was compiled for `mipsel-unknown-elf` (as used by previous +versions of PSn00bSDK). + +Toolchains that target `mipsel-linux-gnu` are not supported by PSn00bSDK. + +### `PSN00BSDK_TC` (`PATH`) -- `PSN00BSDK_LIBGCC` (`FILEPATH`) +Path to the GCC toolchain's installation prefix/directory. If not set, CMake +will attempt to find the toolchain in the `PATH` environment variable and store +its path in the project's variable cache (so the search does not have to be +repeated). It is recommended to add the toolchain's `bin` subfolder to `PATH` +rather than setting this variable. - Path to the `libgcc.a` library bundled with the GCC toolchain. The contents - of this library are merged into `libc` when building the SDK, so this - variable is only actually needed when compiling `libpsn00b`. Setting this - variable manually usually isn't necessary as CMake will locate `libgcc.a` - automatically after finding the toolchain. +**IMPORTANT**: if the toolchain's target triplet is not `mipsel-none-elf`, +`PSN00BSDK_TARGET` must be set regardless of whether or not `PSN00BSDK_TC` is +also set. + +### `PSN00BSDK_LIBGCC` (`FILEPATH`) + +Path to the `libgcc` library bundled with the GCC toolchain. As required by GCC +this library is always linked to all targets, regardless of whether any SDK +libraries are linked or not. CMake will attempt to locate `libgcc` +automatically after finding the toolchain, so setting this variable manually is +not required in most cases. ## Internal settings These settings are not stored in CMake's cache and can only be set from within -the build script. +the build script after invoking `project()`. + +### `PSN00BSDK_EXECUTABLE_LINK_LIBRARIES`, `PSN00BSDK_SHARED_LIBRARY_LINK_LIBRARIES` (list of `STRING`) -- `PSN00BSDK_LIBRARIES` +Lists of SDK libraries to be linked automatically to all new executables and +DLLs, respectively. By default `PSN00BSDK_EXECUTABLE_LINK_LIBRARIES` includes +all libraries that ship with PSn00bSDK while +`PSN00BSDK_SHARED_LIBRARY_LINK_LIBRARIES` is empty. - List of libraries to link all created targets against. By default this - includes all PSn00bSDK libraries. +These variables can be modified before invoking `psn00bsdk_add_executable()` or +`psn00bsdk_add_library()` to only link a subset of the SDK. Static libraries +are *not* automatically linked to any SDK libraries. -- `PSN00BSDK_EXECUTABLE_SUFFIX`, `PSN00BSDK_SHARED_LIBRARY_SUFFIX`, - `PSN00BSDK_SYMBOL_MAP_SUFFIX` +### `PSN00BSDK_EXECUTABLE_SUFFIX`, `PSN00BSDK_SHARED_LIBRARY_SUFFIX`, `PSN00BSDK_SYMBOL_MAP_SUFFIX` (`STRING`) - File extensions to use for generated PS1 files. The default values are - `.exe`, `.dll` and `.map` respectively. Note that file names and extensions - can be changed anyway when building a CD image. +File extensions to use for generated PS1 files. The default values are `.exe`, +`.dll` and `.map` respectively. These extensions do not have to match the ones +used in the CD image (if any). ## Read-only variables -- `PSN00BSDK_VERSION`, `PSN00BSDK_BUILD_DATE`, `PSN00BSDK_GIT_TAG`, - `PSN00BSDK_GIT_COMMIT` +### `PSN00BSDK_VERSION`, `PSN00BSDK_BUILD_DATE`, `PSN00BSDK_GIT_TAG`, `PSN00BSDK_GIT_COMMIT` (`STRING`) + +These variables are loaded from `lib/libpsn00b/build.json` and contain +information about the SDK's version. `PSN00BSDK_GIT_TAG` and +`PSN00BSDK_GIT_COMMIT` might be empty strings as they are only populated in CI +builds of PSn00bSDK. + +### `PSN00BSDK_LIBRARIES` (list of `STRING`) - These variables are loaded from `lib/libpsn00b/build.json` and contain - information about the SDK's version. Note that `PSN00BSDK_GIT_TAG` and - `PSN00BSDK_GIT_COMMIT` are not populated by default when building PSn00bSDK - manually from source, so they might be empty strings. +List of all libraries that ship with PSn00bSDK, excluding `libgcc`. Each +library in this list is also defined as a target. See the targets section for +more details. -- `PSN00BSDK_TOOLS`, `PSN00BSDK_INCLUDE`, `PSN00BSDK_LDSCRIPTS` +### `PSN00BSDK_TOOLS`, `PSN00BSDK_INCLUDE`, `PSN00BSDK_LDSCRIPTS` (list of `PATH`) - Lists of paths used internally. Should not be set, manipulated or overridden - by scripts. +Lists of paths used internally. Should not be set, manipulated or overridden by +scripts. -- `TOOLCHAIN_NM` +### `TOOLCHAIN_NM` (`FILEPATH`) - Path to the `nm` executable used to generate symbol maps. Although not used - internally by CMake, this program is part of the GCC toolchain. +Path to the `nm` executable used to generate symbol maps. Although not used +internally by CMake, this program is part of the GCC toolchain. -- `ELF2X`, `ELF2CPE`, `MKPSXISO`, `LZPACK`, `SMXLINK` +### `ELF2X`, `ELF2CPE`, `MKPSXISO`, `LZPACK`, `SMXLINK` (`FILEPATH`) - Paths to the PSn00bSDK tools' executables. As no functions are currently - provided for building assets, `LZPACK` and `SMXLINK` can be used with - `add_custom_command()`/`add_custom_target()` to convert models and generate - LZP archives as part of the build pipeline. +Paths to the PSn00bSDK tools' executables. As no functions are currently +provided for building assets, `LZPACK` and `SMXLINK` can be used manually with +CMake's `add_custom_command()` and `add_custom_target()` to convert models and +generate LZP archives as part of the build pipeline. ----------------------------------------- -_Last updated on 2022-02-26 by spicyjpeg_ +_Last updated on 2022-10-11 by spicyjpeg_ diff --git a/doc/installation.md b/doc/installation.md index 382c721..70b7f3e 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -5,7 +5,7 @@ 1. Install prerequisites. Currently CMake is the only external dependency; you can install it from [here](https://cmake.org/download) or using MSys2 or - your distro's package manager. Make sure you have at least CMake 3.20. + your distro's package manager. Make sure you have at least CMake 3.21. 2. Head over to the releases page, download the latest release's ZIP for your operating system and extract its contents to a directory of your choice, @@ -44,7 +44,7 @@ being built without support for ripping CD audio tracks to FLAC, however the - `git` - `build-essential`, `base-devel` or similar - `make` or `ninja-build` - - `cmake` (3.20+ is required, download it from + - `cmake` (3.21 or later is required, download it from [here](https://cmake.org/download) if your package manager only provides older versions) @@ -185,4 +185,4 @@ The toolchain script defines a few CMake macros to create PS1 executables, DLLs and CD images. See the [reference](cmake_reference.md) for details. ----------------------------------------- -_Last updated on 2022-09-21 by spicyjpeg_ +_Last updated on 2022-10-11 by spicyjpeg_ diff --git a/doc/known_bugs.md b/doc/known_bugs.md index 3fbfdc2..620a805 100644 --- a/doc/known_bugs.md +++ b/doc/known_bugs.md @@ -7,14 +7,15 @@ fixed. ## Toolchain -- It is currently not possible to link static libraries (including the SDK - libraries themselves) with DLLs, since the build scripts currently assume that - static library object files are always going to be linked into executables. - This can be worked around by linking all static libraries as part of the main - executable rather than the DLLs: the dynamic linker will automatically search - the executable for undefined symbols used by a DLL and patch the code to use - them. It might be necessary to list such symbols in a dummy array to prevent - the compiler from stripping them away from the executable. +- ~~It is currently not possible to link static libraries (including the SDK~~ + ~~libraries themselves) with DLLs, since the build scripts currently assume~~ + ~~that static library object files are always going to be linked into~~ + ~~executables. This can be worked around by linking all static libraries as~~ + ~~part of the main executable rather than the DLLs: the dynamic linker will~~ + ~~automatically search the executable for undefined symbols used by a DLL~~ + ~~and patch the code to use them.~~ Static libraries are now fully supported, + and SDK libraries can be linked to both executables and DLLs. See the CMake + reference for more details. - Link-time optimization is broken due to GCC not supporting it when linking weak functions written in assembly. @@ -28,7 +29,7 @@ fixed. the length of the data *must* be a multiple of 32 bytes. Attempting to transfer any data whose length isn't a multiple of 32 bytes will result in `DrawSync()` hanging and never returning, however a warning will be printed - on the debug console. + on the debug console if the executable is built in debug mode. `psxspu`: @@ -48,4 +49,4 @@ fixed. See [README.md in the examples directory](../examples/README.md#examples-summary). ----------------------------------------- -_Last updated on 2022-08-21 by spicyjpeg_ +_Last updated on 2022-10-13 by spicyjpeg_ diff --git a/examples/CMakeLists.txt b/examples/CMakeLists.txt index 15212c8..2fbc4d9 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.20) +cmake_minimum_required(VERSION 3.21) project( PSn00bSDK-examples diff --git a/examples/README.md b/examples/README.md index 82d7698..ade94b0 100644 --- a/examples/README.md +++ b/examples/README.md @@ -24,7 +24,8 @@ Additional information may be found in the source code of each example. | [`io/pads`](./io/pads) | Demonstrates reading controllers via low-level access | EXE | 3 | | [`io/system573`](./io/system573) | Konami System 573 (PS1-based arcade board) example | CD | | | [`lowlevel/cartrom`](./lowlevel/cartrom) | ROM firmware for cheat devices written using GNU GAS | ROM | 4 | -| [`mdec/mdecimage`](./mdec/mdecimage) | Displays a (raw) MDEC format image using libpsxpress | EXE | | +| [`mdec/mdecimage`](./mdec/mdecimage) | Displays a (raw) MDEC format image | EXE | | +| [`mdec/strvideo`](./mdec/strvideo) | Plays a .STR video file using the MDEC | CD | 1 | | [`sound/spustream`](./sound/spustream) | Custom (non XA) CD-ROM audio streaming using the SPU | CD | | | [`sound/vagsample`](./sound/vagsample) | Demonstrates playing VAG sound files using the SPU | EXE | | | [`system/childexec`](./system/childexec) | Loading a child program and returning to parent | EXE | | @@ -35,9 +36,9 @@ Additional information may be found in the source code of each example. Notes: -1. `cdrom/cdxa` does not come with an example XA audio file. In order to run - this example you'll have to provide your own file and build the CD image - manually. +1. `cdrom/cdxa` and `mdec/strvideo` do not come with example files. In order + to run these examples you'll have to provide your own files and build the CD + image manually. 2. `demos/n00bdemo` suffers from flickering on real hardware, especially when masking/stencil buffering is used. 3. `io/pads` seems to work on real hardware, but fails to automatically enable @@ -48,7 +49,7 @@ Notes: ## Building the examples -The instructions below assume that PSn00bSDK, CMake 3.20+ and a GCC toolchain +The instructions below assume that PSn00bSDK, CMake 3.21+ and a GCC toolchain are already installed. Refer to the [installation guide](../doc/installation.md) for details. @@ -84,4 +85,4 @@ are for rebuilding the examples *after* the SDK has been installed. CD images for each example. ----------------------------------------- -_Last updated on 2022-08-11 by spicyjpeg_ +_Last updated on 2022-10-16 by spicyjpeg_ diff --git a/examples/beginner/cppdemo/CMakeLists.txt b/examples/beginner/cppdemo/CMakeLists.txt index c43d4a1..11a35ed 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.20) +cmake_minimum_required(VERSION 3.21) project( cppdemo @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.cpp) -psn00bsdk_add_executable(cppdemo STATIC ${_sources}) +psn00bsdk_add_executable(cppdemo GPREL ${_sources}) #psn00bsdk_add_cd_image(cppdemo_iso cppdemo iso.xml DEPENDS cppdemo) install(FILES ${PROJECT_BINARY_DIR}/cppdemo.exe TYPE BIN) diff --git a/examples/beginner/hello/CMakeLists.txt b/examples/beginner/hello/CMakeLists.txt index d8297c5..98ab43b 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.20) +cmake_minimum_required(VERSION 3.21) project( hello @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(hello STATIC ${_sources}) +psn00bsdk_add_executable(hello GPREL ${_sources}) #psn00bsdk_add_cd_image(hello_iso hello iso.xml DEPENDS hello) install(FILES ${PROJECT_BINARY_DIR}/hello.exe TYPE BIN) diff --git a/examples/cdrom/cdbrowse/CMakeLists.txt b/examples/cdrom/cdbrowse/CMakeLists.txt index c2e93fc..0cc091f 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.20) +cmake_minimum_required(VERSION 3.21) project( cdbrowse @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(cdbrowse STATIC ${_sources}) +psn00bsdk_add_executable(cdbrowse GPREL ${_sources}) psn00bsdk_add_cd_image(cdbrowse_iso cdbrowse iso.xml DEPENDS cdbrowse) psn00bsdk_target_incbin(cdbrowse PRIVATE ball16c ball16c.tim) diff --git a/examples/cdrom/cdbrowse/iso.xml b/examples/cdrom/cdbrowse/iso.xml index 5ffca18..771b0e9 100644 --- a/examples/cdrom/cdbrowse/iso.xml +++ b/examples/cdrom/cdbrowse/iso.xml @@ -17,7 +17,6 @@ <directory_tree> <file name="SYSTEM.CNF" type="data" source="${PROJECT_SOURCE_DIR}/system.cnf" /> <file name="CDBROWSE.EXE" type="data" source="cdbrowse.exe" /> - <file name="CDBROWSE.MAP" type="data" source="cdbrowse.map" /> <dir name="DIRA"> <dir name="DIRAA"> diff --git a/examples/cdrom/cdxa/CMakeLists.txt b/examples/cdrom/cdxa/CMakeLists.txt index 70ef77c..fd2f653 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.20) +cmake_minimum_required(VERSION 3.21) project( cdxa @@ -13,7 +13,7 @@ project( # TODO: add rules to actually generate a valid .XA file file(GLOB _sources *.c) -psn00bsdk_add_executable(cdxa STATIC ${_sources}) +psn00bsdk_add_executable(cdxa GPREL ${_sources}) #psn00bsdk_add_cd_image(cdxa_iso cdxa iso.xml DEPENDS cdxa) psn00bsdk_target_incbin(cdxa PRIVATE ball16c ball16c.tim) diff --git a/examples/cdrom/cdxa/iso.xml b/examples/cdrom/cdxa/iso.xml index b98a16f..6715f94 100644 --- a/examples/cdrom/cdxa/iso.xml +++ b/examples/cdrom/cdxa/iso.xml @@ -17,7 +17,6 @@ <directory_tree> <file name="SYSTEM.CNF" type="data" source="${PROJECT_SOURCE_DIR}/system.cnf" /> <file name="CDXA.EXE" type="data" source="cdxa.exe" /> - <file name="CDXA.MAP" type="data" source="cdxa.map" /> <!-- CD-XA file, you'll have to provide your own to make this example work --> <file name="XASAMPLE.XA" type="mixed" source="${PROJECT_SOURCE_DIR}/xasample.xa"/> diff --git a/examples/cdrom/cdxa/main.c b/examples/cdrom/cdxa/main.c index 4921658..93cf01a 100644 --- a/examples/cdrom/cdxa/main.c +++ b/examples/cdrom/cdxa/main.c @@ -349,7 +349,9 @@ int main(int argc, const char* argv[]) xa_loc = file.pos; /* Hook XA callback function to CdReadyCallback (for auto stop/loop */ + EnterCriticalSection(); CdReadyCallback(xa_callback); + ExitCriticalSection(); /* Set CD mode for XA streaming (2x speed, send XA to SPU, enable filter */ i = CdlModeSpeed|CdlModeRT|CdlModeSF; diff --git a/examples/demos/n00bdemo/CMakeLists.txt b/examples/demos/n00bdemo/CMakeLists.txt index a54c198..f7811b0 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.20) +cmake_minimum_required(VERSION 3.21) project( n00bdemo @@ -28,7 +28,7 @@ add_custom_command( ) file(GLOB _sources *.s *.c) -psn00bsdk_add_executable(n00bdemo STATIC ${_sources}) +psn00bsdk_add_executable(n00bdemo GPREL ${_sources}) #psn00bsdk_add_cd_image(n00bdemo_iso n00bdemo iso.xml DEPENDS n00bdemo) target_include_directories(n00bdemo PRIVATE ${PROJECT_SOURCE_DIR}) diff --git a/examples/graphics/balls/CMakeLists.txt b/examples/graphics/balls/CMakeLists.txt index deee473..03c2720 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.20) +cmake_minimum_required(VERSION 3.21) project( balls @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(balls STATIC ${_sources}) +psn00bsdk_add_executable(balls GPREL ${_sources}) #psn00bsdk_add_cd_image(balls_iso balls iso.xml DEPENDS balls) psn00bsdk_target_incbin(balls PRIVATE ball16c ball16c.tim) diff --git a/examples/graphics/billboard/CMakeLists.txt b/examples/graphics/billboard/CMakeLists.txt index 4fc668d..5288896 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.20) +cmake_minimum_required(VERSION 3.21) project( billboard @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(billboard STATIC ${_sources}) +psn00bsdk_add_executable(billboard GPREL ${_sources}) #psn00bsdk_add_cd_image(billboard_iso billboard iso.xml DEPENDS billboard) psn00bsdk_target_incbin(billboard PRIVATE tim_image texture64.tim) diff --git a/examples/graphics/fpscam/CMakeLists.txt b/examples/graphics/fpscam/CMakeLists.txt index cb0c086..2c21296 100644 --- a/examples/graphics/fpscam/CMakeLists.txt +++ b/examples/graphics/fpscam/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) project( fpscam @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(fpscam STATIC ${_sources}) +psn00bsdk_add_executable(fpscam GPREL ${_sources}) #psn00bsdk_add_cd_image(fpscam_iso fpscam iso.xml DEPENDS fpscam) install(FILES ${PROJECT_BINARY_DIR}/fpscam.exe TYPE BIN) diff --git a/examples/graphics/gte/CMakeLists.txt b/examples/graphics/gte/CMakeLists.txt index 90d897b..e9ab4f3 100644 --- a/examples/graphics/gte/CMakeLists.txt +++ b/examples/graphics/gte/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) project( gte @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(gte STATIC ${_sources}) +psn00bsdk_add_executable(gte GPREL ${_sources}) #psn00bsdk_add_cd_image(gte_iso gte iso.xml DEPENDS gte) psn00bsdk_target_incbin(gte PRIVATE tim_texture texture.tim) diff --git a/examples/graphics/hdtv/CMakeLists.txt b/examples/graphics/hdtv/CMakeLists.txt index 804b096..78411f5 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.20) +cmake_minimum_required(VERSION 3.21) project( hdtv @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(hdtv STATIC ${_sources}) +psn00bsdk_add_executable(hdtv GPREL ${_sources}) #psn00bsdk_add_cd_image(hdtv_iso hdtv iso.xml DEPENDS hdtv) install(FILES ${PROJECT_BINARY_DIR}/hdtv.exe TYPE BIN) diff --git a/examples/graphics/render2tex/CMakeLists.txt b/examples/graphics/render2tex/CMakeLists.txt index a97cf02..1ef36e0 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.20) +cmake_minimum_required(VERSION 3.21) project( render2tex @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(render2tex STATIC ${_sources}) +psn00bsdk_add_executable(render2tex GPREL ${_sources}) #psn00bsdk_add_cd_image(render2tex_iso render2tex iso.xml DEPENDS render2tex) psn00bsdk_target_incbin(render2tex PRIVATE tim_blendpattern blendpattern-16c.tim) diff --git a/examples/graphics/rgb24/CMakeLists.txt b/examples/graphics/rgb24/CMakeLists.txt index 9565b8b..de81db0 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.20) +cmake_minimum_required(VERSION 3.21) project( rgb24 @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(rgb24 STATIC ${_sources}) +psn00bsdk_add_executable(rgb24 GPREL ${_sources}) #psn00bsdk_add_cd_image(rgb24_iso rgb24 iso.xml DEPENDS rgb24) psn00bsdk_target_incbin(rgb24 PRIVATE tim_image bunpattern.tim) diff --git a/examples/graphics/tilesasm/CMakeLists.txt b/examples/graphics/tilesasm/CMakeLists.txt index 5b8de96..21ceafc 100644 --- a/examples/graphics/tilesasm/CMakeLists.txt +++ b/examples/graphics/tilesasm/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) project( tilesasm @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.s *.c) -psn00bsdk_add_executable(tilesasm STATIC ${_sources}) +psn00bsdk_add_executable(tilesasm GPREL ${_sources}) #psn00bsdk_add_cd_image(tilesasm_iso tilesasm iso.xml DEPENDS tilesasm) psn00bsdk_target_incbin(tilesasm PRIVATE tim_tileset tiles_256.tim) diff --git a/examples/io/pads/CMakeLists.txt b/examples/io/pads/CMakeLists.txt index cf5f817..618b696 100644 --- a/examples/io/pads/CMakeLists.txt +++ b/examples/io/pads/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) project( pads @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c *.s) -psn00bsdk_add_executable(pads STATIC ${_sources}) +psn00bsdk_add_executable(pads GPREL ${_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 index 17bf331..29bb527 100644 --- a/examples/io/pads/main.c +++ b/examples/io/pads/main.c @@ -62,17 +62,17 @@ static const char *const PAD_TYPEIDS[] = { #define BGCOLOR_B 0 typedef struct { - DISPENV disp; - DRAWENV draw; -} DB; + DISPENV disp; + DRAWENV draw; +} Framebuffer; typedef struct { - DB db[2]; - uint32_t db_active; -} CONTEXT; + Framebuffer db[2]; + int db_active; +} RenderContext; -void init_context(CONTEXT *ctx) { - DB *db; +void init_context(RenderContext *ctx) { + Framebuffer *db; ResetGraph(0); ctx->db_active = 0; @@ -99,8 +99,8 @@ void init_context(CONTEXT *ctx) { FntOpen(8, 16, 304, 208, 2, 512); } -void display(CONTEXT *ctx) { - DB *db; +void display(RenderContext *ctx) { + Framebuffer *db; DrawSync(0); VSync(0); @@ -227,7 +227,7 @@ void poll_cb(uint32_t port, const volatile uint8_t *buff, size_t rx_len) { /* Main */ -static CONTEXT ctx; +static RenderContext ctx; int main(int argc, const char* argv[]) { init_context(&ctx); diff --git a/examples/io/pads/spi.c b/examples/io/pads/spi.c index 133782c..292e682 100644 --- a/examples/io/pads/spi.c +++ b/examples/io/pads/spi.c @@ -46,9 +46,9 @@ typedef struct _SPI_Context { SPI_Callback callback; } SPI_Context; -static volatile SPI_Context _context; -static volatile SPI_Request volatile *_current_req; -static volatile SPI_Callback _default_cb; +static volatile SPI_Context _context; +static volatile SPI_Request *_current_req; +static volatile SPI_Callback _default_cb; /* Request queue management */ diff --git a/examples/io/system573/CMakeLists.txt b/examples/io/system573/CMakeLists.txt index 2a362e8..34b0d69 100644 --- a/examples/io/system573/CMakeLists.txt +++ b/examples/io/system573/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) project( system573 @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c *.s) -psn00bsdk_add_executable(system573 STATIC ${_sources}) +psn00bsdk_add_executable(system573 GPREL ${_sources}) psn00bsdk_add_cd_image(system573_iso system573 iso.xml DEPENDS system573) install( diff --git a/examples/io/system573/iso.xml b/examples/io/system573/iso.xml index 0bb84d5..2226089 100644 --- a/examples/io/system573/iso.xml +++ b/examples/io/system573/iso.xml @@ -30,8 +30,6 @@ --> <!--<file name="SYSTEM.CNF" type="data" source="${PROJECT_SOURCE_DIR}/system.cnf" />--> <file name="PSX.EXE" type="data" source="system573.exe" /> - <file name="PSX.MAP" type="data" source="system573.map" /> - <file name="QSY.DXD" type="data" source="system573.exe" /> <file name="SSW.BXF" type="data" source="system573.exe" /> <file name="TSV.AXG" type="data" source="system573.exe" /> diff --git a/examples/io/system573/main.c b/examples/io/system573/main.c index 3404ee4..39ddb64 100644 --- a/examples/io/system573/main.c +++ b/examples/io/system573/main.c @@ -83,17 +83,17 @@ const char *const IO_BOARD_TYPES[] = { #define BGCOLOR_B 0 typedef struct { - DISPENV disp; - DRAWENV draw; -} DB; + DISPENV disp; + DRAWENV draw; +} Framebuffer; typedef struct { - DB db[2]; - uint32_t db_active; -} CONTEXT; + Framebuffer db[2]; + int db_active; +} RenderContext; -void init_context(CONTEXT *ctx) { - DB *db; +void init_context(RenderContext *ctx) { + Framebuffer *db; ResetGraph(0); ctx->db_active = 0; @@ -120,8 +120,8 @@ void init_context(CONTEXT *ctx) { FntOpen(8, 16, 304, 208, 2, 512); } -void display(CONTEXT *ctx) { - DB *db; +void display(RenderContext *ctx) { + Framebuffer *db; DrawSync(0); VSync(0); @@ -135,7 +135,7 @@ void display(CONTEXT *ctx) { /* Main */ -static CONTEXT ctx; +static RenderContext ctx; #define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx); } #define SHOW_ERROR(...) { SHOW_STATUS(__VA_ARGS__); while (1) __asm__("nop"); } diff --git a/examples/lowlevel/cartrom/CMakeLists.txt b/examples/lowlevel/cartrom/CMakeLists.txt index 7d5e86e..6a94d9f 100644 --- a/examples/lowlevel/cartrom/CMakeLists.txt +++ b/examples/lowlevel/cartrom/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) project( cartrom @@ -17,9 +17,13 @@ file(GLOB _sources *.c *.s) # 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) +set_target_properties( + cartrom PROPERTIES + PREFIX "" + SUFFIX ".elf" + PSN00BSDK_TARGET_TYPE EXECUTABLE_NOGPREL +) target_include_directories(cartrom PRIVATE ${PROJECT_SOURCE_DIR}) diff --git a/examples/mdec/mdecimage/CMakeLists.txt b/examples/mdec/mdecimage/CMakeLists.txt index b76adb4..b3d8ae1 100644 --- a/examples/mdec/mdecimage/CMakeLists.txt +++ b/examples/mdec/mdecimage/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) project( mdecimage @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(mdecimage STATIC ${_sources}) +psn00bsdk_add_executable(mdecimage GPREL ${_sources}) #psn00bsdk_add_cd_image(mdecimage_iso mdecimage iso.xml DEPENDS mdecimage) psn00bsdk_target_incbin(mdecimage PRIVATE mdec_image image.bin) diff --git a/examples/mdec/strvideo/CMakeLists.txt b/examples/mdec/strvideo/CMakeLists.txt new file mode 100644 index 0000000..d41556b --- /dev/null +++ b/examples/mdec/strvideo/CMakeLists.txt @@ -0,0 +1,24 @@ +# PSn00bSDK example CMake script +# (C) 2021 spicyjpeg - MPL licensed + +cmake_minimum_required(VERSION 3.21) + +project( + strvideo + LANGUAGES C + VERSION 1.0.0 + DESCRIPTION "PSn00bSDK .STR video playback example" + HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk" +) + +file(GLOB _sources *.c) +psn00bsdk_add_executable(strvideo GPREL ${_sources}) +#psn00bsdk_add_cd_image(strvideo_iso strvideo iso.xml DEPENDS strvideo) + +install( + FILES + #${PROJECT_BINARY_DIR}/strvideo.bin + #${PROJECT_BINARY_DIR}/strvideo.cue + ${PROJECT_BINARY_DIR}/strvideo.exe + TYPE BIN +) diff --git a/examples/graphics/tilesasm/iso.xml b/examples/mdec/strvideo/iso.xml index 477c636..65e0ff5 100644 --- a/examples/graphics/tilesasm/iso.xml +++ b/examples/mdec/strvideo/iso.xml @@ -1,11 +1,4 @@ <?xml version="1.0" encoding="utf-8"?> -<!-- - This file is processed by CMake and used by mkpsxiso to build the CD image. - - NOTE: all paths are relative to the build directory; if you want to include - a file from the source tree, you'll have to prepend its path with - ${PROJECT_SOURCE_DIR}. ---> <iso_project image_name="${CD_IMAGE_NAME}.bin" cue_sheet="${CD_IMAGE_NAME}.cue" @@ -13,8 +6,8 @@ <track type="data"> <identifiers system ="PLAYSTATION" - volume ="PSN00BSDK_TEMPLATE" - volume_set ="PSN00BSDK_TEMPLATE" + volume ="STRVIDEO" + volume_set ="STRVIDEO" publisher ="MEIDOTEK" data_preparer ="PSN00BSDK ${PSN00BSDK_VERSION}" application ="PLAYSTATION" @@ -23,8 +16,9 @@ <directory_tree> <file name="SYSTEM.CNF" type="data" source="${PROJECT_SOURCE_DIR}/system.cnf" /> - <file name="TEMPLATE.EXE" type="data" source="template.exe" /> - <file name="TEMPLATE.MAP" type="data" source="template.map" /> + <file name="STRVIDEO.EXE" type="data" source="strvideo.exe" /> + + <file name="VIDEO.STR" type="mixed" source="${PROJECT_SOURCE_DIR}/video.str"/> <dummy sectors="1024"/> </directory_tree> diff --git a/examples/mdec/strvideo/main.c b/examples/mdec/strvideo/main.c new file mode 100644 index 0000000..842bbb8 --- /dev/null +++ b/examples/mdec/strvideo/main.c @@ -0,0 +1,439 @@ +/* + * PSn00bSDK .STR FMV playback example + * (C) 2022 spicyjpeg - MPL licensed + * + * This example demonstrates playback of full-motion video in the standard .STR + * format, using the MDEC for frame decoding and XA for audio. Decoded frames + * are transferred directly to the main framebuffer in this example, but could + * also be output to another VRAM location and used as a background or texture + * for a 2D or 3D scene. + * + * Playing video files requires setting up a fairly complex pipeline, involving + * several buffers and components working in parallel: + * + * - .STR sectors are read continuously from the CD and each frame, usually + * spanning multiple sectors, is reassembled (demuxed) into a buffer in + * memory. In this example the task is performed by cd_sector_handler(). The + * CD drive handles XA-ADPCM sectors automatically, so no CPU intervention is + * necessary to play the audio track interleaved with the video. + * - Once a full frame has been demuxed, the bitstream data is parsed and + * decompressed by the CPU (using DecDCTvlc()) to an array of run-length + * codes to be fed to the MDEC. This is done in the main loop. + * - At the same time the last frame decompressed is read from RAM by the MDEC, + * which decodes it and outputs one 16-pixel-wide vertical slice at a time. + * - When a slice is ready, it is uploaded by mdec_dma_handler() to the current + * framebuffer in VRAM while the MDEC is decoding the next slice. + * A text overlay is drawn on top of the framebuffer using the GPU after the + * entire frame has been decoded. + * + * Since pretty much all buffers used are going to be read and written at the + * same time, double buffering is required for all of them. Every part of the + * pipeline must also run in lockstep with each other to prevent frame + * corruption, hence several functions and flag variables are used to stall the + * main loop until a frame is available for decoding and the MDEC is ready. + * Playback is stopped when a sector with the end-of-file flag set in the XA + * subheader (added at the end of the file by most .STR encoders) is + * encountered; in order to access the subheader, this example requests 2340 + * bytes of data for each sector (rather than the usual 2048) from the drive. + * + * Note that PSn00bSDK's bitstream decoding API only supports version 1 and 2 + * bitstreams currently, so make sure your .STR files are encoded as v2 and not + * v3. + */ + +#include <stdint.h> +#include <string.h> +#include <psxetc.h> +#include <psxapi.h> +#include <psxgpu.h> +#include <psxgte.h> +#include <psxspu.h> +#include <psxcd.h> +#include <psxpress.h> +#include <hwregs_c.h> + +// Uncomment to display the video in 24bpp mode. Note that the GPU does not +// support 24bpp rendering, so the text overlay is only enabled in 16bpp mode. +//#define DISP_24BPP + +/* Display/GPU context utilities */ + +#define SCREEN_XRES 320 +#define SCREEN_YRES 240 + +#define BGCOLOR_R 0 +#define BGCOLOR_G 0 +#define BGCOLOR_B 0 + +typedef struct { + DISPENV disp; + DRAWENV draw; +} Framebuffer; + +typedef struct { + Framebuffer db[2]; + int db_active; +} RenderContext; + +void init_context(RenderContext *ctx) { + Framebuffer *db; + + ResetGraph(0); + ctx->db_active = 0; + + db = &(ctx->db[0]); + SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), 0, SCREEN_YRES, 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), 0, SCREEN_YRES, 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(4, 12, 312, 16, 2, 256); +} + +void display(RenderContext *ctx, int sync) { + Framebuffer *db; + ctx->db_active ^= 1; + + DrawSync(0); + if (sync) + VSync(0); + + db = &(ctx->db[ctx->db_active]); + PutDrawEnv(&(db->draw)); + PutDispEnv(&(db->disp)); + SetDispMask(1); +} + +/* CD and MDEC interrupt handlers */ + +#ifdef DISP_24BPP +#define BLOCK_SIZE 24 +#else +#define BLOCK_SIZE 16 +#define DRAW_OVERLAY +#endif + +#define VRAM_X_COORD(x) ((x) * BLOCK_SIZE / 16) + +// All non-audio sectors in .STR files begin with this 32-byte header, which +// contains metadata about the sector and is followed by a chunk of frame +// bitstream data. +// https://problemkaputt.de/psx-spx.htm#cdromfilevideostrstreamingandbspicturecompressionsony +typedef struct { + uint16_t magic; // Always 0x0160 + uint16_t type; // 0x8001 for MDEC + uint16_t sector_id; // Chunk number (0 = first chunk of this frame) + uint16_t sector_count; // Total number of chunks for this frame + uint32_t frame_id; // Frame number + uint32_t bs_length; // Total length of this frame in bytes + + uint16_t width, height; + uint8_t bs_header[8]; + uint32_t _reserved; +} STR_Header; + +// https://problemkaputt.de/psx-spx.htm#cdromxasubheaderfilechannelinterleave +typedef struct { + uint8_t file, channel; + uint8_t submode, coding_info; +} XA_Header; + +// https://problemkaputt.de/psx-spx.htm#cdromsectorencoding +typedef struct { + CdlLOC pos; + XA_Header xa_header[2]; + STR_Header str_header; + uint8_t data[2016]; + uint32_t edc; + uint8_t ecc[276]; +} STR_Sector; + +typedef struct { + uint16_t width, height; + uint32_t bs_data[0x2000]; // Bitstream data read from the disc + uint32_t mdec_data[0x8000]; // Decompressed data to be fed to the MDEC +} StreamBuffer; + +typedef struct { + StreamBuffer frames[2]; + uint32_t slices[2][BLOCK_SIZE * SCREEN_YRES / 2]; + + int frame_id, sector_count; + int dropped_frames; + RECT slice_pos; + int frame_width; + + volatile int8_t sector_pending, frame_ready; + volatile int8_t cur_frame, cur_slice; +} StreamContext; + +StreamContext str_ctx; + +// This buffer is used by cd_sector_handler() as a temporary area for sectors +// read from the CD. Due to DMA limitations it can't be allocated on the stack +// (especially not in the interrupt callbacks' stack, whose size is very +// limited). +STR_Sector sector_buffer; + +void cd_sector_handler(void) { + // Fetch the .STR header of the sector that has been read and check if the + // end-of-file bit is set in the XA header. + CdGetSector(§or_buffer, sizeof(STR_Sector) / 4); + + if ( + (sector_buffer.xa_header[0].submode & (1 << 7)) || + (sector_buffer.xa_header[1].submode & (1 << 7)) + ) { + CdControlF(CdlPause, 0); + str_ctx.frame_ready = -1; + return; + } + + STR_Header *header = §or_buffer.str_header; + StreamBuffer *frame = &str_ctx.frames[str_ctx.cur_frame]; + + // Ignore any non-MDEC sectors that might be present in the stream. + if (header->type != 0x8001) + return; + + // If this sector is actually part of a new frame, validate the sectors + // that have been read so far and flip the bitstream data buffers. + if (header->frame_id != str_ctx.frame_id) { + // Do not set the ready flag if any sector has been missed. + if (str_ctx.sector_count) + str_ctx.dropped_frames++; + else + str_ctx.frame_ready = 1; + + str_ctx.frame_id = header->frame_id; + str_ctx.sector_count = header->sector_count; + str_ctx.cur_frame ^= 1; + + frame = &str_ctx.frames[str_ctx.cur_frame]; + + // Initialize the next frame. Dimensions must be rounded up to the + // nearest multiple of 16 as the MDEC operates on 16x16 pixel blocks. + frame->width = (header->width + 15) & 0xfff0; + frame->height = (header->height + 15) & 0xfff0; + } + + // Append the payload contained in this sector to the current buffer. + memcpy( + &(frame->bs_data[2016 / 4 * header->sector_id]), + sector_buffer.data, + 2016 + ); + str_ctx.sector_count--; +} + +void mdec_dma_handler(void) { + // Handle any sectors that were not processed by cd_event_handler() (see + // below) while a DMA transfer from the MDEC was in progress. As the MDEC + // has just finished decoding a slice, they can be safely handled now. + if (str_ctx.sector_pending) { + cd_sector_handler(); + str_ctx.sector_pending = 0; + } + + // Upload the decoded slice to VRAM and start decoding the next slice (into + // another buffer) if any. + LoadImage(&str_ctx.slice_pos, str_ctx.slices[str_ctx.cur_slice]); + + str_ctx.cur_slice ^= 1; + str_ctx.slice_pos.x += BLOCK_SIZE; + + if (str_ctx.slice_pos.x < str_ctx.frame_width) + DecDCTout( + str_ctx.slices[str_ctx.cur_slice], + BLOCK_SIZE * str_ctx.slice_pos.h / 2 + ); +} + +void cd_event_handler(int event, uint8_t *payload) { + // Ignore all events other than a sector being ready. + if (event != CdlDataReady) + return; + + // Only handle sectors immediately if the MDEC is not decoding a frame, + // otherwise defer handling to mdec_dma_handler(). This is a workaround for + // a hardware conflict between the DMA channels used for the CD drive and + // MDEC output, which shall not run simultaneously. + if (DecDCTinSync(1)) + str_ctx.sector_pending = 1; + else + cd_sector_handler(); +} + +/* Stream helpers */ + +void init_stream(void) { + EnterCriticalSection(); + DMACallback(1, &mdec_dma_handler); + CdReadyCallback(&cd_event_handler); + ExitCriticalSection(); + + // Set the maximum amount of data DecDCTvlc() can output and copy the + // lookup table used for decompression to the scratchpad area. This is + // optional but makes the decompressor slightly faster. See the libpsxpress + // documentation for more details. + DecDCTvlcSize(0x8000); + DecDCTvlcCopyTable((DECDCTTAB *) 0x1f800000); + + str_ctx.dropped_frames = 0; + str_ctx.cur_frame = 0; + str_ctx.cur_slice = 0; +} + +StreamBuffer *get_next_frame(void) { + while (!str_ctx.frame_ready) + __asm__ volatile(""); + + if (str_ctx.frame_ready < 0) + return 0; + + str_ctx.frame_ready = 0; + return &str_ctx.frames[str_ctx.cur_frame ^ 1]; +} + +void start_stream(CdlFILE *file) { + str_ctx.frame_id = -1; + str_ctx.sector_pending = 0; + str_ctx.frame_ready = 0; + + CdSync(0, 0); + + // Configure the CD drive to read 2340-byte sectors at 2x speed and to + // play any XA-ADPCM sectors that might be interleaved with the video data. + uint8_t mode = CdlModeSize | CdlModeRT | CdlModeSpeed; + CdControl(CdlSetmode, (const uint8_t *) &mode, 0); + + // Start reading in real-time mode (i.e. without retrying in case of read + // errors) and wait for the first frame to be buffered. + CdControl(CdlReadS, &(file->pos), 0); + + get_next_frame(); +} + +/* Main */ + +static RenderContext ctx; + +#define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx, 1); } +#define SHOW_ERROR(...) { SHOW_STATUS(__VA_ARGS__); while (1) __asm__("nop"); } + +int main(int argc, const char* argv[]) { + init_context(&ctx); + + SHOW_STATUS("INITIALIZING\n"); + SpuInit(); + CdInit(); + InitGeom(); // Required for PSn00bSDK's DecDCTvlc() + DecDCTReset(0); + + SHOW_STATUS("OPENING VIDEO FILE\n"); + + CdlFILE file; + if (!CdSearchFile(&file, "\\VIDEO.STR")) + SHOW_ERROR("FAILED TO FIND VIDEO.STR\n"); + + init_stream(); + start_stream(&file); + + // Disable framebuffer clearing to get rid of flickering during playback. + display(&ctx, 1); + ctx.db[0].draw.isbg = 0; + ctx.db[1].draw.isbg = 0; +#ifdef DISP_24BPP + ctx.db[0].disp.isrgb24 = 1; + ctx.db[1].disp.isrgb24 = 1; +#endif + + int decode_errors = 0; + + while (1) { + // Wait for a full frame to be read from the disc and decompress the + // bitstream into the format expected by the MDEC. If the video has + // ended, restart playback from the beginning. + StreamBuffer *frame = get_next_frame(); + if (!frame) { + start_stream(&file); + continue; + } + +#ifdef DRAW_OVERLAY + // Measure CPU usage of the decompressor using the hblank counter. + int total_time = TIMER_VALUE(1) + 1; + TIMER_VALUE(1) = 0; +#endif + + if (DecDCTvlc(frame->bs_data, frame->mdec_data)) { + decode_errors++; + continue; + } + +#ifdef DRAW_OVERLAY + int cpu_usage = TIMER_VALUE(1) * 100 / total_time; +#endif + + // Wait for the MDEC to finish decoding the previous frame, then flip + // the framebuffers to display it and prepare the buffer for the next + // frame. + // NOTE: you should *not* call VSync(0) during playback, as the refresh + // rate of the GPU is not synced to the video's frame rate. If you want + // to minimize screen tearing, consider triple buffering instead (i.e. + // always keep 2 fully decoded frames in VRAM and use VSyncCallback() + // to register a function that displays the next decoded frame whenever + // vblank occurs). + DecDCTinSync(0); + DecDCToutSync(0); + +#ifdef DRAW_OVERLAY + FntPrint(-1, "FRAME:%5d READ ERRORS: %5d\n", str_ctx.frame_id, str_ctx.dropped_frames); + FntPrint(-1, "CPU: %5d%% DECODE ERRORS:%5d\n", cpu_usage, decode_errors); + FntFlush(-1); +#endif + display(&ctx, 0); + + // Feed the newly decompressed frame to the MDEC. The MDEC will not + // actually start decoding it until an output buffer is also configured + // by calling DecDCTout() (see below). +#ifdef DISP_24BPP + DecDCTin(frame->mdec_data, DECDCT_MODE_24BPP); +#else + DecDCTin(frame->mdec_data, DECDCT_MODE_16BPP); +#endif + + // Place the frame at the center of the currently active framebuffer + // and start decoding the first slice. Decoded slices will be uploaded + // to VRAM in the background by mdec_dma_handler(). + RECT *fb_clip = &(ctx.db[ctx.db_active].draw.clip); + int x_offset = (fb_clip->w - frame->width) / 2; + int y_offset = (fb_clip->h - frame->height) / 2; + + str_ctx.slice_pos.x = VRAM_X_COORD(fb_clip->x + x_offset); + str_ctx.slice_pos.y = fb_clip->y + y_offset; + str_ctx.slice_pos.w = BLOCK_SIZE; + str_ctx.slice_pos.h = frame->height; + str_ctx.frame_width = VRAM_X_COORD(frame->width); + + DecDCTout( + str_ctx.slices[str_ctx.cur_slice], + BLOCK_SIZE * str_ctx.slice_pos.h / 2 + ); + } + + return 0; +} diff --git a/examples/graphics/tilesasm/system.cnf b/examples/mdec/strvideo/system.cnf index e221726..d199117 100644 --- a/examples/graphics/tilesasm/system.cnf +++ b/examples/mdec/strvideo/system.cnf @@ -1,4 +1,4 @@ -BOOT=cdrom:\template.exe;1 +BOOT=cdrom:\strvideo.exe;1 TCB=4 EVENT=10 STACK=801FFFF0 diff --git a/examples/sound/spustream/CMakeLists.txt b/examples/sound/spustream/CMakeLists.txt index 397796a..63d113b 100644 --- a/examples/sound/spustream/CMakeLists.txt +++ b/examples/sound/spustream/CMakeLists.txt @@ -1,7 +1,7 @@ # PSn00bSDK example CMake script # (C) 2021 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) project( spustream @@ -13,7 +13,7 @@ project( # TODO: add rules to actually generate a valid STREAM.BIN file file(GLOB _sources *.c) -psn00bsdk_add_executable(spustream STATIC ${_sources}) +psn00bsdk_add_executable(spustream GPREL ${_sources}) psn00bsdk_add_cd_image(spustream_iso spustream iso.xml DEPENDS spustream) install( diff --git a/examples/sound/spustream/iso.xml b/examples/sound/spustream/iso.xml index 3807046..050d673 100644 --- a/examples/sound/spustream/iso.xml +++ b/examples/sound/spustream/iso.xml @@ -17,7 +17,6 @@ <directory_tree> <file name="SYSTEM.CNF" type="data" source="${PROJECT_SOURCE_DIR}/system.cnf" /> <file name="SPUSTRM.EXE" type="data" source="spustream.exe" /> - <file name="SPUSTRM.MAP" type="data" source="spustream.map" /> <file name="STREAM.BIN" type="data" source="${PROJECT_SOURCE_DIR}/stream.bin" /> diff --git a/examples/sound/spustream/main.c b/examples/sound/spustream/main.c index 6179179..1fee883 100644 --- a/examples/sound/spustream/main.c +++ b/examples/sound/spustream/main.c @@ -117,17 +117,17 @@ #define BGCOLOR_B 0 typedef struct { - DISPENV disp; - DRAWENV draw; -} DB; + DISPENV disp; + DRAWENV draw; +} Framebuffer; typedef struct { - DB db[2]; - int db_active; -} CONTEXT; + Framebuffer db[2]; + int db_active; +} RenderContext; -void init_context(CONTEXT *ctx) { - DB *db; +void init_context(RenderContext *ctx) { + Framebuffer *db; ResetGraph(0); ctx->db_active = 0; @@ -154,8 +154,8 @@ void init_context(CONTEXT *ctx) { FntOpen(8, 16, 304, 208, 2, 512); } -void display(CONTEXT *ctx) { - DB *db; +void display(RenderContext *ctx) { + Framebuffer *db; DrawSync(0); VSync(0); @@ -181,16 +181,14 @@ void display(CONTEXT *ctx) { #define CHUNK_SIZE (BUFFER_SIZE * NUM_CHANNELS) typedef struct { - uint32_t lba; - uint32_t length; - uint32_t pos; + int lba, length; - uint32_t spu_addr; - uint32_t spu_pos; - uint32_t db_active; + volatile int pos; + volatile int spu_addr, spu_pos; + volatile int db_active; } StreamContext; -static volatile StreamContext str_ctx; +static StreamContext str_ctx; // This buffer is used by cd_event_handler() as a temporary area for sectors // read from the CD and uploaded to SPU RAM. Due to DMA limitations it can't be @@ -316,10 +314,10 @@ void init_stream(CdlFILE *file) { } void start_stream(void) { - SPU_KEY_OFF = CHANNEL_MASK; + uint32_t addr = BUFFER_START_ADDR + CHUNK_SIZE * str_ctx.db_active; for (int i = 0; i < NUM_CHANNELS; i++) { - SPU_CH_ADDR(i) = SPU_RAM_ADDR(BUFFER_START_ADDR + BUFFER_SIZE * i); + SPU_CH_ADDR(i) = SPU_RAM_ADDR(addr + BUFFER_SIZE * i); SPU_CH_FREQ(i) = SAMPLE_RATE; SPU_CH_ADSR(i) = 0x1fee80ff; } @@ -336,9 +334,21 @@ void start_stream(void) { spu_irq_handler(); } +// This is basically a variant of reset_spu_channels() that only resets the +// channels used to play the stream, to (again) prevent them from triggering +// the SPU IRQ while the stream is paused. +void stop_stream(void) { + SPU_KEY_OFF = CHANNEL_MASK; + + for (int i = 0; i < NUM_CHANNELS; i++) + SPU_CH_ADDR(i) = SPU_RAM_ADDR(DUMMY_BLOCK_ADDR); + + SPU_KEY_ON = CHANNEL_MASK; +} + /* Main */ -static CONTEXT ctx; +static RenderContext ctx; #define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx); } #define SHOW_ERROR(...) { SHOW_STATUS(__VA_ARGS__); while (1) __asm__("nop"); } @@ -351,7 +361,13 @@ int main(int argc, const char* argv[]) { CdInit(); reset_spu_channels(); - SHOW_STATUS("LOCATING STREAM FILE\n"); + // Set up controller polling. + uint8_t pad_buff[2][34]; + InitPAD(pad_buff[0], 34, pad_buff[1], 34); + StartPAD(); + ChangeClearPAD(0); + + SHOW_STATUS("OPENING STREAM FILE\n"); CdlFILE file; if (!CdSearchFile(&file, "\\STREAM.BIN")) @@ -361,28 +377,27 @@ int main(int argc, const char* argv[]) { init_stream(&file); start_stream(); - // Set up controller polling. - uint8_t pad_buff[2][34]; - InitPAD(pad_buff[0], 34, pad_buff[1], 34); - StartPAD(); - ChangeClearPAD(0); + int paused = 0; uint16_t sample_rate = SAMPLE_RATE; uint16_t last_buttons = 0xffff; while (1) { - FntPrint(-1, "PLAYING SPU STREAM\n"); + FntPrint(-1, "PLAYING SPU STREAM\n\n"); + + FntPrint(-1, "BUFFER: %d\nSTATUS: ", str_ctx.db_active); if (str_ctx.spu_pos >= CHUNK_SIZE) - FntPrint(-1, "STATUS: IDLE\n\n"); - else if (!str_ctx.spu_pos) - FntPrint(-1, "STATUS: SEEKING\n\n"); + FntPrint(-1, "IDLE\n\n"); + else if (str_ctx.spu_pos) + FntPrint(-1, "BUFFERING\n\n"); else - FntPrint(-1, "STATUS: BUFFERING\n\n"); + FntPrint(-1, "SEEKING\n\n"); - FntPrint(-1, "POSITION=%5d/%5d\n", str_ctx.pos, str_ctx.length); - FntPrint(-1, "BUFFERED=%5d/%5d\n", str_ctx.spu_pos, CHUNK_SIZE); - FntPrint(-1, "SMP RATE=%5d HZ\n\n", (sample_rate * 44100) >> 12); + FntPrint(-1, "POSITION: %5d/%5d\n", str_ctx.pos, str_ctx.length); + FntPrint(-1, "BUFFERED: %5d/%5d\n", str_ctx.spu_pos, CHUNK_SIZE); + FntPrint(-1, "SMP RATE: %5d HZ\n\n", (sample_rate * 44100) >> 12); + FntPrint(-1, "[START] %s\n", paused ? "RESUME" : "PAUSE"); FntPrint(-1, "[LEFT/RIGHT] SEEK\n"); FntPrint(-1, "[O] RESET POSITION\n"); FntPrint(-1, "[UP/DOWN] CHANGE SAMPLE RATE\n"); @@ -399,6 +414,14 @@ int main(int argc, const char* argv[]) { if ((pad->type != 4) && (pad->type != 5) && (pad->type != 7)) continue; + if ((last_buttons & PAD_START) && !(pad->btn & PAD_START)) { + paused ^= 1; + if (paused) + stop_stream(); + else + start_stream(); + } + // Seeking by an arbitrary number of sectors isn't a problem as // spu_irq_handler() always realigns the counter. if (!(pad->btn & PAD_LEFT)) diff --git a/examples/sound/vagsample/CMakeLists.txt b/examples/sound/vagsample/CMakeLists.txt index c9849ae..7d06afd 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.20) +cmake_minimum_required(VERSION 3.21) project( vagsample @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(vagsample STATIC ${_sources}) +psn00bsdk_add_executable(vagsample GPREL ${_sources}) #psn00bsdk_add_cd_image(vagsample_iso vagsample iso.xml DEPENDS vagsample) psn00bsdk_target_incbin(vagsample PRIVATE proyt proyt.vag) diff --git a/examples/system/childexec/CMakeLists.txt b/examples/system/childexec/CMakeLists.txt index 7e91589..e542d2a 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.20) +cmake_minimum_required(VERSION 3.21) project( childexec @@ -13,14 +13,14 @@ project( file(GLOB _sources *.c) file(GLOB _child_sources child/*.c) -psn00bsdk_add_executable(parent STATIC ${_sources}) -psn00bsdk_add_executable(child STATIC ${_child_sources}) -#psn00bsdk_add_cd_image(childexec_iso childexec iso.xml DEPENDS parent) +psn00bsdk_add_executable(childexec GPREL ${_sources}) +psn00bsdk_add_executable(childexec_child GPREL ${_child_sources}) +#psn00bsdk_add_cd_image(childexec_iso childexec iso.xml DEPENDS childexec) -psn00bsdk_target_incbin(parent PRIVATE ball16c ball16c.tim) +psn00bsdk_target_incbin(childexec PRIVATE ball16c ball16c.tim) psn00bsdk_target_incbin( - parent PRIVATE child_exe - ${PROJECT_BINARY_DIR}/child.exe + childexec PRIVATE child_exe + ${PROJECT_BINARY_DIR}/childexec_child.exe ) # Relocate the child executable to a non-default address to prevent it from @@ -28,9 +28,9 @@ psn00bsdk_target_incbin( # NOTE: child executables are not position-independent and can't be relocated # at runtime. If you need your code to be relocatable (e.g. to load it into a # dynamically-allocated buffer), consider using a DLL instead. -target_link_options(child PRIVATE -Ttext=0x80030000) +target_link_options(childexec_child PRIVATE -Ttext=0x80030000) # Make sure the child executable is built before the parent. -add_dependencies(parent child) +add_dependencies(childexec childexec_child) -install(FILES ${PROJECT_BINARY_DIR}/parent.exe TYPE BIN) +install(FILES ${PROJECT_BINARY_DIR}/childexec.exe TYPE BIN) diff --git a/examples/system/childexec/child/child.c b/examples/system/childexec/child/child.c index bd17440..dcfbfaf 100644 --- a/examples/system/childexec/child/child.c +++ b/examples/system/childexec/child/child.c @@ -98,8 +98,8 @@ MATRIX light_mtx = { char pad_buff[2][34]; /* Function declarations */ -void init(); -void display(); +void init(void); +void display(void); /* Main function */ @@ -245,7 +245,7 @@ int main(int argc, const char *argv[]) { } -void init() { +void init(void) { /* Reset the GPU, also installs a VSync event handler */ ResetGraph( 0 ); @@ -300,7 +300,7 @@ void init() { } -void display() { +void display(void) { /* Wait for GPU to finish drawing and vertical retrace */ DrawSync( 0 ); diff --git a/examples/system/childexec/parent.c b/examples/system/childexec/parent.c index 3e7d218..83d964c 100644 --- a/examples/system/childexec/parent.c +++ b/examples/system/childexec/parent.c @@ -67,12 +67,12 @@ extern const uint32_t ball16c[]; TIM_IMAGE tim; -void run_child(); +void run_child(void); char pad_buff[2][34]; -void init() { +void init(void) { int i; @@ -270,10 +270,7 @@ typedef struct { // Child program address extern char child_exe[]; -// Manually defined as its not defined in psxapi by default -void SetDefaultExitFromException(); - -void run_child() { +void run_child(void) { // Arguments for the child program char *args[] = @@ -289,29 +286,23 @@ void run_child() { // Copy child executable to its intended adddress memcpy((void*)exe->param.t_addr, child_exe+2048, exe->param.t_size); - // Enter critical section to prepare for program execution - EnterCriticalSection(); + // Prepare for program execution and disable interrupts + //EnterCriticalSection(); + StopCallback(); // Stop pads, enable auto acknowledge StopPAD(); ChangeClearPAD(1); ChangeClearRCnt(3, 1); - - // Set default exception handler just in case - //SetDefaultExitFromException(); - - // Last three function calls could be relegated to - // a StopCallback() function in the future. - + // Execute child printf("Child exec!\n"); Exec(&exe->param, 3, args); // Restore interrupts for this PS-EXE - EnterCriticalSection(); RestartCallback(); - ExitCriticalSection(); - + //ExitCriticalSection(); + // Re-init and re-enable pads InitPAD(pad_buff[0], 34, pad_buff[1], 34); StartPAD(); diff --git a/examples/system/console/CMakeLists.txt b/examples/system/console/CMakeLists.txt index eeb8e62..c6fa573 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.20) +cmake_minimum_required(VERSION 3.21) project( console @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(console STATIC ${_sources}) +psn00bsdk_add_executable(console GPREL ${_sources}) #psn00bsdk_add_cd_image(console_iso console iso.xml DEPENDS console) psn00bsdk_target_incbin(console PRIVATE ball16c ball16c.tim) diff --git a/examples/system/dynlink/CMakeLists.txt b/examples/system/dynlink/CMakeLists.txt index f5f4ea8..e750fd1 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.20) +cmake_minimum_required(VERSION 3.21) project( dynlink @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(dynlink_main DYNAMIC ${_sources}) +psn00bsdk_add_executable(dynlink_main NOGPREL ${_sources}) psn00bsdk_add_library (dynlink_cube SHARED library/cube.c) psn00bsdk_add_library (dynlink_balls SHARED library/balls.c) psn00bsdk_add_cd_image( diff --git a/examples/system/dynlink/library/balls.c b/examples/system/dynlink/library/balls.c index c537167..457ec4e 100644 --- a/examples/system/dynlink/library/balls.c +++ b/examples/system/dynlink/library/balls.c @@ -21,7 +21,7 @@ typedef struct { int16_t x, y; int16_t xdir, ydir; uint8_t r, g, b, p; -} BALL_TYPE; +} Ball; #define MAX_BALLS 512 @@ -35,12 +35,13 @@ typedef struct { // initialize variables or hardware. static uint32_t frame = 0; -static BALL_TYPE balls[MAX_BALLS]; +static Ball balls[MAX_BALLS]; static TIM_IMAGE ball_tim; -void init(CONTEXT *ctx) { - GetTimInfo(ball16c, &ball_tim); +void init(RenderContext *ctx) { + Framebuffer *db = &(ctx->db[ctx->db_active]); + GetTimInfo(ball16c, &ball_tim); LoadImage(ball_tim.prect, ball_tim.paddr); if (ball_tim.mode & 8) LoadImage(ball_tim.crect, ball_tim.caddr); @@ -48,10 +49,10 @@ void init(CONTEXT *ctx) { // Initialize the balls by giving them a random initial position, velocity // and color. for (uint32_t i = 0; i < MAX_BALLS; i++) { - BALL_TYPE *b = &(balls[i]); + Ball *b = &(balls[i]); - b->x = rand() % (ctx->xres - 16); - b->y = rand() % (ctx->yres - 16); + b->x = rand() % (db->draw.clip.w - 16); + b->y = rand() % (db->draw.clip.h - 16); b->xdir = ((rand() & 1) ? 1 : -1) * ((rand() % 3) + 1); b->ydir = ((rand() & 1) ? 1 : -1) * ((rand() % 3) + 1); b->r = rand() & 0xff; @@ -60,12 +61,12 @@ void init(CONTEXT *ctx) { } } -void render(CONTEXT *ctx, uint16_t buttons) { - DB *db = &(ctx->db[ctx->db_active]); - SPRT_16 *sprt = (SPRT_16 *) ctx->db_nextpri; +void render(RenderContext *ctx, uint16_t buttons) { + Framebuffer *db = &(ctx->db[ctx->db_active]); + SPRT_16 *sprt = (SPRT_16 *) ctx->db_nextpri; for (uint32_t i = 0; i < MAX_BALLS; i++) { - BALL_TYPE *b = &(balls[i]); + Ball *b = &(balls[i]); setSprt16(sprt); @@ -85,12 +86,12 @@ void render(CONTEXT *ctx, uint16_t buttons) { if ( (b->x < 0) || - ((b->x + 16) > ctx->xres) + ((b->x + 16) > db->draw.clip.w) ) b->xdir *= -1; if ( (b->y < 0) || - ((b->y + 16) > ctx->yres) + ((b->y + 16) > db->draw.clip.h) ) b->ydir *= -1; } diff --git a/examples/system/dynlink/library/cube.c b/examples/system/dynlink/library/cube.c index 84fe552..22a805f 100644 --- a/examples/system/dynlink/library/cube.c +++ b/examples/system/dynlink/library/cube.c @@ -81,16 +81,18 @@ static SVECTOR rot = { 0 }; static VECTOR pos = { 0, 0, 400 }; static MATRIX mtx, lmtx; -void init(CONTEXT *ctx) { +void init(RenderContext *ctx) { + Framebuffer *db = &(ctx->db[ctx->db_active]); + InitGeom(); - gte_SetGeomOffset(ctx->xres / 2, ctx->yres / 2); - gte_SetGeomScreen(ctx->xres / 2); + gte_SetGeomOffset(db->draw.clip.w / 2, db->draw.clip.h / 2); + gte_SetGeomScreen(db->draw.clip.w / 2); gte_SetBackColor(63, 63, 63); gte_SetColorMatrix(&color_mtx); } -void render(CONTEXT *ctx, uint16_t buttons) { +void render(RenderContext *ctx, uint16_t buttons) { RotMatrix(&rot, &mtx); TransMatrix(&mtx, &pos); MulMatrix0(&light_mtx, &mtx, &lmtx); @@ -104,8 +106,8 @@ void render(CONTEXT *ctx, uint16_t buttons) { rot.vx += step; rot.vz += step; - DB *db = &(ctx->db[ctx->db_active]); - POLY_F4 *pol4 = (POLY_F4 *) ctx->db_nextpri; + Framebuffer *db = &(ctx->db[ctx->db_active]); + POLY_F4 *pol4 = (POLY_F4 *) ctx->db_nextpri; for (uint32_t i = 0; i < CUBE_FACES; i++) { int32_t p; diff --git a/examples/system/dynlink/library/dll_common.h b/examples/system/dynlink/library/dll_common.h index 315a993..6606bda 100644 --- a/examples/system/dynlink/library/dll_common.h +++ b/examples/system/dynlink/library/dll_common.h @@ -19,13 +19,12 @@ typedef struct { DRAWENV draw; uint32_t ot[OT_LEN]; uint8_t p[PACKET_LEN]; -} DB; +} Framebuffer; typedef struct { - uint16_t xres, yres; - DB db[2]; - uint32_t db_active; - uint8_t *db_nextpri; -} CONTEXT; + Framebuffer db[2]; + int db_active; + uint8_t *db_nextpri; +} RenderContext; #endif diff --git a/examples/system/dynlink/main.c b/examples/system/dynlink/main.c index fff7aa5..fcce5b1 100644 --- a/examples/system/dynlink/main.c +++ b/examples/system/dynlink/main.c @@ -83,12 +83,10 @@ static const char *const DLL_FILENAMES[] = { #define BGCOLOR_G 24 #define BGCOLOR_B 0 -void init_context(CONTEXT *ctx) { - DB *db; +void init_context(RenderContext *ctx) { + Framebuffer *db; ResetGraph(0); - ctx->xres = SCREEN_XRES; - ctx->yres = SCREEN_YRES; ctx->db_active = 0; db = &(ctx->db[0]); @@ -121,8 +119,8 @@ void init_context(CONTEXT *ctx) { FntOpen(4, 12, 312, 32, 2, 256); } -void display(CONTEXT *ctx) { - DB *db; +void display(RenderContext *ctx) { + Framebuffer *db; DrawSync(0); VSync(0); @@ -185,13 +183,14 @@ void *custom_resolver(DLL *dll, const char *name) { // and the pointers returned by DL_GetDLLSymbol() should be saved and reused as // much as possible. typedef struct { - void (*init)(CONTEXT *); - void (*render)(CONTEXT *, uint16_t buttons); + void (*init)(RenderContext *); + void (*render)(RenderContext *, uint16_t buttons); } DLL_API; static DLL *dll = 0; static DLL_API dll_api; -static CONTEXT ctx; + +static RenderContext ctx; /* Main */ diff --git a/examples/system/timer/CMakeLists.txt b/examples/system/timer/CMakeLists.txt index 328e07e..30efbf3 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.20) +cmake_minimum_required(VERSION 3.21) project( timer @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(timer STATIC ${_sources}) +psn00bsdk_add_executable(timer GPREL ${_sources}) #psn00bsdk_add_cd_image(timer_iso timer iso.xml DEPENDS timer) install(FILES ${PROJECT_BINARY_DIR}/timer.exe TYPE BIN) diff --git a/examples/system/tty/CMakeLists.txt b/examples/system/tty/CMakeLists.txt index 0664502..75ce689 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.20) +cmake_minimum_required(VERSION 3.21) project( tty @@ -12,7 +12,7 @@ project( ) file(GLOB _sources *.c) -psn00bsdk_add_executable(tty STATIC ${_sources}) +psn00bsdk_add_executable(tty GPREL ${_sources}) #psn00bsdk_add_cd_image(tty_iso tty iso.xml DEPENDS tty) install(FILES ${PROJECT_BINARY_DIR}/tty.exe TYPE BIN) diff --git a/indev/psxpad/makefile b/indev/psxpad/makefile deleted file mode 100644 index ab6a733..0000000 --- a/indev/psxpad/makefile +++ /dev/null @@ -1,39 +0,0 @@ -include ../../examples/sdk-common.mk - -TARGET = libpad.elf - -CFILES = $(notdir $(wildcard *.c)) -CPPFILES = $(notdir $(wildcard *.cpp)) -AFILES = $(notdir $(wildcard *.s)) - -OFILES = $(addprefix build/,$(CFILES:.c=.o) $(CPPFILES:.cpp=.o) $(AFILES:.s=.o)) - -INCLUDE += -LIBDIRS += - -LIBS = -lsiofs -lpsxsio -lpsxetc -lpsxgpu -lpsxgte -lpsxspu -lpsxapi -lc - -CFLAGS = -g -O2 -fno-builtin -fdata-sections -ffunction-sections -CPPFLAGS = $(CFLAGS) -fno-exceptions -AFLAGS = -g -msoft-float -LDFLAGS = -g -Ttext=0x80010000 -gc-sections -T $(GCC_BASE)/mipsel-unknown-elf/lib/ldscripts/elf32elmip.x - -CC = $(PREFIX)gcc -CXX = $(PREFIX)g++ -AS = $(PREFIX)as -LD = $(PREFIX)ld - -all: $(OFILES) - $(LD) $(LDFLAGS) $(LIBDIRS) $(OFILES) $(LIBS) -o $(TARGET) - elf2x -q $(TARGET) - -build/%.o: %.c - @mkdir -p $(dir $@) - $(CC) $(CFLAGS) $(INCLUDE) -c $< -o $@ - -build/%.o: %.s - @mkdir -p $(dir $@) - $(CC) $(AFLAGS) $(INCLUDE) -c $< -o $@ - -clean: - rm -rf build $(TARGET) $(TARGET:.elf=.exe) diff --git a/libpsn00b/CMakeLists.txt b/libpsn00b/CMakeLists.txt index a662448..602b3c8 100644 --- a/libpsn00b/CMakeLists.txt +++ b/libpsn00b/CMakeLists.txt @@ -1,7 +1,7 @@ # libpsn00b build script -# (C) 2021 spicyjpeg - MPL licensed +# (C) 2021-2022 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) # ${PROJECT_SOURCE_DIR} is not available until project() is called. set(CMAKE_TOOLCHAIN_FILE ${CMAKE_CURRENT_LIST_DIR}/cmake/sdk.cmake) @@ -13,14 +13,13 @@ project( HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk" ) -if(NOT DEFINED PSN00BSDK_LIBGCC) - message(FATAL_ERROR "Failed to obtain information about the GCC toolchain. Check your toolchain settings.") -elseif(PSN00BSDK_LIBGCC STREQUAL "PSN00BSDK_LIBGCC-NOTFOUND") - message(FATAL_ERROR "Failed to find libgcc in the GCC toolchain's files. Check your toolchain settings, or set the path to libgcc using -DPSN00BSDK_LIBGCC.") -endif() +include(${PROJECT_SOURCE_DIR}/cmake/flags.cmake) ## Libraries +set(_types EXECUTABLE_GPREL EXECUTABLE_NOGPREL SHARED_LIBRARY) +set(_suffixes _exe_gprel _exe_nogprel _dll) + foreach(_library IN LISTS PSN00BSDK_LIBRARIES) # libc needs special handling due to the different directory name. if(${_library} STREQUAL "c") @@ -31,37 +30,40 @@ foreach(_library IN LISTS PSN00BSDK_LIBRARIES) file( GLOB_RECURSE _sources - ${_path}/*.s - ${_path}/*.c - ${_path}/*.cpp - ${_path}/*.cxx + ${_path}/*.s ${_path}/*.c ${_path}/*.cpp ) - psn00bsdk_add_library(${_library} STATIC ${_sources}) + # Build a separate version of the library for each supported target type + # and create a virtual target that links the appropriate version of the + # library. + add_library(${_library} INTERFACE) + + foreach(_type _suffix IN ZIP_LISTS _types _suffixes) + set(_name ${_library}${_suffix}) + list(APPEND _libraries ${_name}) + + psn00bsdk_add_library(${_name} STATIC ${_sources}) + set_target_properties(${_name} PROPERTIES PSN00BSDK_TARGET_TYPE ${_type}) + target_link_libraries( + ${_library} INTERFACE + $<$<STREQUAL:$<UPPER_CASE:$<TARGET_PROPERTY:PSN00BSDK_TARGET_TYPE>>,${_type}>:${_name}> + ) + endforeach() endforeach() -psn00bsdk_target_incbin(psxgpu PRIVATE _gpu_debug_font psxgpu/dbugfont.tim) - -# Extract libgcc's contents and merge them into libc after building. -# Unfortunately glob expressions won't work on Windows, so we have to manually -# enumerate the contents of libgcc and save the list to a temporary file (as it -# is too long to be passed directly on the command line). This is actually the -# most reliable way I found to do this (I tried $<TARGET_OBJECTS> to no avail). -add_custom_command( - TARGET c POST_BUILD - COMMAND ${CMAKE_AR} t ${PSN00BSDK_LIBGCC} $<ANGLE-R>_libgcc.txt - COMMAND ${CMAKE_AR} x ${PSN00BSDK_LIBGCC} - COMMAND ${CMAKE_AR} q $<TARGET_FILE:c> \@_libgcc.txt - COMMAND ${CMAKE_AR} s $<TARGET_FILE:c> - #COMMAND ${CMAKE_AR} rsuU $<TARGET_FILE:c> *.o - COMMENT "Merging libgcc contents into SDK libc" -) +# Add binary assets to each version of the libraries. +foreach(_suffix IN LISTS _suffixes) + psn00bsdk_target_incbin( + psxgpu${_suffix} PRIVATE _gpu_debug_font + psxgpu/dbugfont.tim + ) +endforeach() ## Installation install( - TARGETS ${PSN00BSDK_LIBRARIES} - DESTINATION ${CMAKE_INSTALL_LIBDIR}/libpsn00b + TARGETS psn00bsdk ${PSN00BSDK_LIBRARIES} ${_libraries} + DESTINATION ${CMAKE_INSTALL_LIBDIR}/libpsn00b/$<LOWER_CASE:$<CONFIG>> EXPORT libpsn00b ) install( @@ -95,9 +97,11 @@ install( ) # Generate an import script, which will be used by the setup script to find the -# libraries. +# libraries. Note that CMake actually generates two separate import scripts +# (one setting configuration-specific options), so this won't create conflicts +# once the debug and release builds are merged into the same installation tree. install( EXPORT libpsn00b - DESTINATION ${CMAKE_INSTALL_LIBDIR}/libpsn00b/cmake + DESTINATION ${CMAKE_INSTALL_LIBDIR}/libpsn00b #EXPORT_LINK_INTERFACE_LIBRARIES ) diff --git a/libpsn00b/cmake/flags.cmake b/libpsn00b/cmake/flags.cmake index 5d9c751..1ca25c2 100644 --- a/libpsn00b/cmake/flags.cmake +++ b/libpsn00b/cmake/flags.cmake @@ -1,151 +1,90 @@ -# libpsn00b interface targets -# (C) 2021 spicyjpeg - MPL licensed +# PSn00bSDK interface targets +# (C) 2021-2022 spicyjpeg - MPL licensed # This script creates several "virtual" targets (psn00bsdk_*) that set include -# directories and compiler flags when a target is linked against them. The -# following targets are currently defined: -# - psn00bsdk_common -# - psn00bsdk_static_exe -# - psn00bsdk_dynamic_exe -# - psn00bsdk_static_lib -# - psn00bsdk_object_lib (same as psn00bsdk_static_lib) -# - psn00bsdk_shared_lib -# - psn00bsdk_module_lib (same as psn00bsdk_shared_lib) -# -# NOTE: building a static library and linking it as part of a DLL is currently -# *not* supported. +# directories and compiler flags when a target is linked against them. It is +# only used when building libpsn00b, as CMake automatically saves these targets +# into the export script once libpsn00b is installed. -if(NOT TARGET psn00bsdk_common) # Include guard +add_library (psn00bsdk INTERFACE) +link_libraries(psn00bsdk) -add_library(psn00bsdk_common INTERFACE) - -foreach( - _target IN ITEMS - static_exe dynamic_exe static_lib object_lib shared_lib module_lib -) - add_library (psn00bsdk_${_target} INTERFACE) - target_link_libraries(psn00bsdk_${_target} INTERFACE psn00bsdk_common) -endforeach() - -# Options common to all target types: -# - Define PLAYSTATION=1 -# - Optimize for MIPS R3000 -# - Inject zero checks into division operations (will throw breaks) -# - All standard libraries (including libgcc) disabled -# - Put all symbols into separate sections when building -# - C++ features that require runtime support disabled -# - Unused section stripping enabled target_compile_options( - psn00bsdk_common INTERFACE - # CPU options - -msoft-float - -march=r3000 - -mtune=r3000 - -mabi=32 - -mno-mt - -mno-llsc - -mdivide-breaks + psn00bsdk INTERFACE + # Options common to all target types + -g + -Wa,--strip-local-absolute -O2 - # Standard library options -ffreestanding -fno-builtin -nostdlib - # Other options - -g -fdata-sections -ffunction-sections -fsigned-char -fno-strict-overflow -fdiagnostics-color=always + -msoft-float + -march=r3000 + -mtune=r3000 + -mabi=32 + -mno-mt + -mno-llsc + -mdivide-breaks $<$<COMPILE_LANGUAGE:CXX>: - # C++ options + # Options common to all target types (C++) -fno-exceptions -fno-rtti -fno-unwind-tables -fno-threadsafe-statics -fno-use-cxa-atexit > -) -target_link_options( - psn00bsdk_common INTERFACE - -nostdlib - -Wl,-gc-sections -) -target_compile_definitions( - psn00bsdk_common INTERFACE - PLAYSTATION=1 - $<$<CONFIG:DEBUG>:DEBUG=1> -) - -# Options for executables without support for dynamic linking: -# - Position-independent code disabled -# - GP-relative addressing enabled only for local symbols -# - ABI-compatible calls disabled (incompatible with GP-relative addr) -target_compile_options( - psn00bsdk_static_exe INTERFACE + $<$<STREQUAL:$<UPPER_CASE:$<TARGET_PROPERTY:PSN00BSDK_TARGET_TYPE>>,EXECUTABLE_GPREL>: + # Options for executables with $gp-relative addressing -G8 -fno-pic -mno-abicalls -mgpopt -mno-extern-sdata -) -target_link_options( - psn00bsdk_static_exe INTERFACE - -G8 - -static -) - -# Options for executables with support for dynamic linking: -# - Position-independent code disabled -# - GP-relative addressing disabled -# - ABI-compatible calls disabled (must be performed manually) -target_compile_options( - psn00bsdk_dynamic_exe INTERFACE - -G0 - -fno-pic - -mno-abicalls - -mno-gpopt -) -target_link_options( - psn00bsdk_dynamic_exe INTERFACE - -G0 - -static -) - -# Options for static libraries: -# - Position-independent code disabled -# - GP-relative addressing disabled -# - ABI-compatible calls disabled -# - Local stripping enabled -target_compile_options( - psn00bsdk_static_lib INTERFACE + > + $<$<STREQUAL:$<UPPER_CASE:$<TARGET_PROPERTY:PSN00BSDK_TARGET_TYPE>>,EXECUTABLE_NOGPREL>: + # Options for executables without $gp-relative addressing -G0 -fno-pic -mno-abicalls -mno-gpopt - -Wa,--strip-local-absolute -) - -target_link_libraries(psn00bsdk_object_lib INTERFACE psn00bsdk_static_lib) - -# Options for dynamically-loaded libraries: -# - Position-independent code enabled -# - GP-relative addressing disabled (incompatible with ABI calls) -# - ABI-compatible calls enabled -target_compile_options( - psn00bsdk_shared_lib INTERFACE + > + $<$<STREQUAL:$<UPPER_CASE:$<TARGET_PROPERTY:PSN00BSDK_TARGET_TYPE>>,SHARED_LIBRARY>: + # Options for DLLs -G0 -fPIC -mabicalls -mno-gpopt -mshared + > ) target_link_options( - psn00bsdk_shared_lib INTERFACE + psn00bsdk INTERFACE + # Options common to all target types + -nostdlib + -Wl,-gc-sections + $<$<STREQUAL:$<UPPER_CASE:$<TARGET_PROPERTY:PSN00BSDK_TARGET_TYPE>>,EXECUTABLE_GPREL>: + # Options for executables with $gp-relative addressing + -G8 + -static + > + $<$<STREQUAL:$<UPPER_CASE:$<TARGET_PROPERTY:PSN00BSDK_TARGET_TYPE>>,EXECUTABLE_NOGPREL>: + # Options for executables without $gp-relative addressing + -G0 + -static + > + $<$<STREQUAL:$<UPPER_CASE:$<TARGET_PROPERTY:PSN00BSDK_TARGET_TYPE>>,SHARED_LIBRARY>: + # Options for DLLs -G0 -shared + > +) +target_compile_definitions( + psn00bsdk INTERFACE + PSN00BSDK=1 + $<$<CONFIG:Release>:NDEBUG=1> ) - -target_link_libraries(psn00bsdk_module_lib INTERFACE psn00bsdk_shared_lib) - -endif() diff --git a/libpsn00b/cmake/internal_setup.cmake b/libpsn00b/cmake/internal_setup.cmake index d293127..e78355f 100644 --- a/libpsn00b/cmake/internal_setup.cmake +++ b/libpsn00b/cmake/internal_setup.cmake @@ -1,12 +1,29 @@ # PSn00bSDK internal setup script for CMake -# (C) 2021 spicyjpeg - MPL licensed +# (C) 2021-2022 spicyjpeg - MPL licensed # This script is included automatically when using the toolchain file and # defines helper functions. -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) include(GNUInstallDirs) +## CMake configuration + +# Setting these variables and properties would technically be the toolchain +# script's responsibility, however they are overridden by project() so their +# setting is deferred to this script. +set(CMAKE_EXECUTABLE_SUFFIX ".elf") +set(CMAKE_STATIC_LIBRARY_PREFIX "lib") +set(CMAKE_STATIC_LIBRARY_SUFFIX ".a") +set(CMAKE_SHARED_LIBRARY_PREFIX "") +set(CMAKE_SHARED_LIBRARY_SUFFIX ".so") +set(CMAKE_SHARED_MODULE_PREFIX "") +set(CMAKE_SHARED_MODULE_SUFFIX ".so") + +set_property(GLOBAL PROPERTY TARGET_SUPPORTS_SHARED_LIBS ON) + +## PSn00bSDK initialization + # Fetch SDK version information from build.json. if(NOT DEFINED PSN00BSDK_VERSION) file(READ ${CMAKE_CURRENT_LIST_DIR}/../build.json _json) @@ -17,13 +34,10 @@ if(NOT DEFINED PSN00BSDK_VERSION) string(JSON PSN00BSDK_GIT_COMMIT GET ${_json} git_commit) endif() -## Settings (can be overridden by projects) - -set(PSN00BSDK_EXECUTABLE_SUFFIX ".exe") -set(PSN00BSDK_SHARED_LIBRARY_SUFFIX ".dll") -set(PSN00BSDK_SYMBOL_MAP_SUFFIX ".map") - -## SDK libraries +include(${CMAKE_CURRENT_LIST_DIR}/../libpsn00b.cmake OPTIONAL) +if(TARGET psn00bsdk) + link_libraries(psn00bsdk) +endif() # DON'T CHANGE THE ORDER or you'll break the libraries' internal dependencies. set( @@ -40,33 +54,33 @@ set( c ) -include(${CMAKE_CURRENT_LIST_DIR}/libpsn00b.cmake OPTIONAL) -include(${CMAKE_CURRENT_LIST_DIR}/flags.cmake) +## Settings (can be overridden by projects) -# Use the toolchain path to find libgcc (used to build libpsn00b). Of course -# different installers, packages and distros have different opinions when it -# comes to deciding where to install toolchains, so we have to bruteforce -# multiple combinations of paths. -if(CMAKE_C_COMPILER_VERSION) - string(REGEX MATCH "^([0-9]+)\." _dummy ${CMAKE_C_COMPILER_VERSION}) +set(PSN00BSDK_EXECUTABLE_LINK_LIBRARIES ${PSN00BSDK_LIBRARIES}) +set(PSN00BSDK_SHARED_LIBRARY_LINK_LIBRARIES "") - find_library( - PSN00BSDK_LIBGCC gcc - HINTS - ${PSN00BSDK_TC}/lib/gcc-cross/${PSN00BSDK_TARGET}/${CMAKE_C_COMPILER_VERSION} - ${PSN00BSDK_TC}/lib/gcc-cross/${PSN00BSDK_TARGET}/${CMAKE_MATCH_1} - ${PSN00BSDK_TC}/lib/gcc/${PSN00BSDK_TARGET}/${CMAKE_C_COMPILER_VERSION} - ${PSN00BSDK_TC}/lib/gcc/${PSN00BSDK_TARGET}/${CMAKE_MATCH_1} - ${PSN00BSDK_TC}/../lib/gcc-cross/${PSN00BSDK_TARGET}/${CMAKE_C_COMPILER_VERSION} - ${PSN00BSDK_TC}/../lib/gcc-cross/${PSN00BSDK_TARGET}/${CMAKE_MATCH_1} - ${PSN00BSDK_TC}/../lib/gcc/${PSN00BSDK_TARGET}/${CMAKE_C_COMPILER_VERSION} - ${PSN00BSDK_TC}/../lib/gcc/${PSN00BSDK_TARGET}/${CMAKE_MATCH_1} - NO_DEFAULT_PATH - DOC "Path to libgcc (bundled with the GCC toolchain)" - ) +set(PSN00BSDK_EXECUTABLE_SUFFIX ".exe") +set(PSN00BSDK_SHARED_LIBRARY_SUFFIX ".dll") +set(PSN00BSDK_SYMBOL_MAP_SUFFIX ".map") + +define_property( + TARGET PROPERTY PSN00BSDK_TARGET_TYPE + BRIEF_DOCS "Type of this target (EXECUTABLE_GPREL, EXECUTABLE_NOGPREL or SHARED_LIBRARY)" + FULL_DOCS "Type of this target (if executable or DLL) or of the executable/DLL this target is going to be linked to (if static library)" +) + +## Include paths + +set(PSN00BSDK_LDSCRIPTS ${CMAKE_CURRENT_LIST_DIR}/../ldscripts) +if(IS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../include) + set(PSN00BSDK_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/../include) +else() + set(PSN00BSDK_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/../../../include/libpsn00b) endif() -## Tools +include_directories(AFTER ${PSN00BSDK_INCLUDE}) + +## Tool paths set( PSN00BSDK_TOOLS @@ -80,24 +94,49 @@ find_program(SMXLINK smxlink HINTS ${PSN00BSDK_TOOLS}) find_program(LZPACK lzpack HINTS ${PSN00BSDK_TOOLS}) find_program(MKPSXISO mkpsxiso HINTS ${PSN00BSDK_TOOLS}) -## Helper functions for executables +## libgcc -set(PSN00BSDK_LDSCRIPTS ${CMAKE_CURRENT_LIST_DIR}/../ldscripts) -if(IS_DIRECTORY ${CMAKE_CURRENT_LIST_DIR}/../include) - set(PSN00BSDK_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/../include) -else() - set(PSN00BSDK_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/../../../include/libpsn00b) +# Use the toolchain path to find libgcc. Of course different installers, +# packages and distros have different opinions when it comes to deciding where +# to install toolchains, so we have to bruteforce multiple combinations of +# paths. +if(CMAKE_C_COMPILER_VERSION) + string(REGEX MATCH "^([0-9]+)\." _dummy ${CMAKE_C_COMPILER_VERSION}) + + find_library( + PSN00BSDK_LIBGCC gcc #REQUIRED + HINTS + ${PSN00BSDK_TC}/lib/gcc-cross/${PSN00BSDK_TARGET}/${CMAKE_C_COMPILER_VERSION} + ${PSN00BSDK_TC}/lib/gcc-cross/${PSN00BSDK_TARGET}/${CMAKE_MATCH_1} + ${PSN00BSDK_TC}/lib/gcc/${PSN00BSDK_TARGET}/${CMAKE_C_COMPILER_VERSION} + ${PSN00BSDK_TC}/lib/gcc/${PSN00BSDK_TARGET}/${CMAKE_MATCH_1} + ${PSN00BSDK_TC}/../lib/gcc-cross/${PSN00BSDK_TARGET}/${CMAKE_C_COMPILER_VERSION} + ${PSN00BSDK_TC}/../lib/gcc-cross/${PSN00BSDK_TARGET}/${CMAKE_MATCH_1} + ${PSN00BSDK_TC}/../lib/gcc/${PSN00BSDK_TARGET}/${CMAKE_C_COMPILER_VERSION} + ${PSN00BSDK_TC}/../lib/gcc/${PSN00BSDK_TARGET}/${CMAKE_MATCH_1} + NO_DEFAULT_PATH + DOC "Path to libgcc (bundled with the GCC toolchain)" + ) + if(PSN00BSDK_LIBGCC STREQUAL "PSN00BSDK_LIBGCC-NOTFOUND") + message(FATAL_ERROR "Failed to find libgcc in the GCC toolchain's files. Check your toolchain settings, or set the path to libgcc using -DPSN00BSDK_LIBGCC.") + endif() + + add_library (gcc STATIC IMPORTED) + set_target_properties(gcc PROPERTIES IMPORTED_LOCATION ${PSN00BSDK_LIBGCC}) + link_libraries (gcc) endif() -# psn00bsdk_add_executable( -# <target name> <STATIC|DYNAMIC> -# [EXCLUDE_FROM_ALL] -# <sources> ... -# ) +## Target helpers + function(psn00bsdk_add_executable name type) - string(TOLOWER ${type} _type) - if(NOT ${_type} MATCHES "^(static|dynamic)$") - message(FATAL_ERROR "Invalid executable type: ${type} (must be STATIC or DYNAMIC)") + string(TOUPPER ${type} _type) + + if(_type MATCHES "^(STATIC|GPREL)$") + set(_type EXECUTABLE_GPREL) + elseif(_type MATCHES "^(DYNAMIC|NOGPREL)$") + set(_type EXECUTABLE_NOGPREL) + else() + message(FATAL_ERROR "Invalid executable type: ${type} (must be STATIC, GPREL, DYNAMIC or NOGPREL)") endif() # Throw an error if elf2x was not found (which should never happen if the @@ -107,111 +146,79 @@ function(psn00bsdk_add_executable name type) endif() add_executable (${name} ${ARGN}) - 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}) + set_target_properties(${name} PROPERTIES PSN00BSDK_TARGET_TYPE ${_type}) + target_link_libraries(${name} PRIVATE ${PSN00BSDK_EXECUTABLE_LINK_LIBRARIES}) + target_link_options (${name} PRIVATE -T$<SHELL_PATH:${PSN00BSDK_LDSCRIPTS}/exe.ld>) # Add post-build steps to generate the .exe and symbol map once the # executable is built. + # FIXME: CMake does not (yet) allow target-dependent generator expressions + # to specify the byproducts, so we have to make sure the generated files + # have no prefix/suffix and are in the current build directory. + #set(_repl PATH:REPLACE_EXTENSION,LAST_ONLY,$<TARGET_FILE:${name}>) add_custom_command( - TARGET ${name} POST_BUILD - COMMAND ${ELF2X} -q ${name}.elf ${name}${PSN00BSDK_EXECUTABLE_SUFFIX} - COMMAND ${TOOLCHAIN_NM} -f posix -l -n ${name}.elf $<ANGLE-R>${name}${PSN00BSDK_SYMBOL_MAP_SUFFIX} - BYPRODUCTS ${name}${PSN00BSDK_EXECUTABLE_SUFFIX} ${name}${PSN00BSDK_SYMBOL_MAP_SUFFIX} + TARGET ${name} POST_BUILD + COMMAND + ${ELF2X} -q + $<SHELL_PATH:$<TARGET_FILE:${name}>> + #$<SHELL_PATH:$<${_repl},${PSN00BSDK_EXECUTABLE_SUFFIX}>> + $<SHELL_PATH:${CMAKE_CURRENT_BINARY_DIR}/${name}${PSN00BSDK_EXECUTABLE_SUFFIX}> + COMMAND + ${TOOLCHAIN_NM} -f posix -l -n + $<SHELL_PATH:$<TARGET_FILE:${name}>> + #$<ANGLE-R>$<SHELL_PATH:$<${_repl},${PSN00BSDK_SYMBOL_MAP_SUFFIX}>> + $<ANGLE-R>$<SHELL_PATH:${CMAKE_CURRENT_BINARY_DIR}/${name}${PSN00BSDK_SYMBOL_MAP_SUFFIX}> + BYPRODUCTS + #$<${_repl},${PSN00BSDK_EXECUTABLE_SUFFIX}> + #$<${_repl},${PSN00BSDK_SYMBOL_MAP_SUFFIX}> + ${CMAKE_CURRENT_BINARY_DIR}/${name}${PSN00BSDK_EXECUTABLE_SUFFIX} + ${CMAKE_CURRENT_BINARY_DIR}/${name}${PSN00BSDK_SYMBOL_MAP_SUFFIX} ) endfunction() -# psn00bsdk_add_library( -# <target name> <STATIC|SHARED|MODULE> -# [EXCLUDE_FROM_ALL] -# <sources> ... -# ) -# Note that SHARED and MODULE have the same meaning (both will create a DLL). -# SDK libraries are NOT statically linked in by default; if you need to link -# something, use target_link_libraries() manually. function(psn00bsdk_add_library name type) - string(TOUPPER ${type} _type_upper) - string(TOLOWER ${type} _type) - if(NOT ${_type} MATCHES "^(static|object|shared|module)$") - message(FATAL_ERROR "Invalid library type: ${type} (must be STATIC, OBJECT, SHARED or MODULE)") - endif() + string(TOUPPER ${type} _type) - add_library (${name} ${_type_upper} ${ARGN}) - target_link_libraries(${name} psn00bsdk_${_type}_lib) - - target_include_directories(${name} PRIVATE ${PSN00BSDK_INCLUDE}) - - if(${_type} MATCHES "^(shared|module)$") - set_target_properties(${name} PROPERTIES PREFIX "" SUFFIX ".so") - target_link_options (${name} PRIVATE -T${PSN00BSDK_LDSCRIPTS}/dll.ld) + if(_type MATCHES "^(STATIC|OBJECT)$") + add_library (${name} ${_type} ${ARGN}) + #target_link_libraries(${name} PRIVATE psn00bsdk) + elseif(_type MATCHES "^(SHARED|MODULE)$") + add_library (${name} ${_type} ${ARGN}) + set_target_properties(${name} PROPERTIES PSN00BSDK_TARGET_TYPE SHARED_LIBRARY) + target_link_libraries(${name} PRIVATE ${PSN00BSDK_SHARED_LIBRARY_LINK_LIBRARIES}) + target_link_options (${name} PRIVATE -T$<SHELL_PATH:${PSN00BSDK_LDSCRIPTS}/dll.ld>) # Add a post-build step to dump the DLL's raw contents into a new file # separate from the built ELF. + #set(_repl PATH:REPLACE_EXTENSION,LAST_ONLY,$<TARGET_FILE:${name}>) add_custom_command( - TARGET ${name} POST_BUILD - COMMAND ${CMAKE_OBJCOPY} -O binary ${name}.so ${name}${PSN00BSDK_SHARED_LIBRARY_SUFFIX} - BYPRODUCTS ${name}${PSN00BSDK_SHARED_LIBRARY_SUFFIX} + TARGET ${name} POST_BUILD + COMMAND + ${CMAKE_OBJCOPY} -O binary + $<SHELL_PATH:$<TARGET_FILE:${name}>> + #$<SHELL_PATH:$<${_repl},${PSN00BSDK_SHARED_LIBRARY_SUFFIX}>> + $<SHELL_PATH:${CMAKE_CURRENT_BINARY_DIR}/${name}${PSN00BSDK_SHARED_LIBRARY_SUFFIX}> + #BYPRODUCTS $<${_repl},${PSN00BSDK_SHARED_LIBRARY_SUFFIX}> + BYPRODUCTS ${CMAKE_CURRENT_BINARY_DIR}/${name}${PSN00BSDK_SHARED_LIBRARY_SUFFIX} ) else() - set_target_properties(${name} PROPERTIES PREFIX "lib" SUFFIX ".a") - - # Remove virtual target dependencies to make sure linking against the - # library does not also propagate static library flags. - set_target_properties(${name} PROPERTIES INTERFACE_LINK_LIBRARIES "") - endif() -endfunction() - -# psn00bsdk_add_cd_image( -# <target name> -# <image file name> -# <mkpsxiso config template> -# [DEPENDS <dependencies> ...] -# [additional options passed to add_custom_target()] -# ) -function(psn00bsdk_add_cd_image name image_name config_file) - # Throw an error if mkpsxiso was not found. Performing this check manually - # (instead of just marking mkpsxiso as required) allows simple projects to - # be built even if mkpsxiso is not installed. - if(MKPSXISO STREQUAL "MKPSXISO-NOTFOUND") - message(FATAL_ERROR "Failed to locate mkpsxiso. If mkpsxiso wasn't installed alongside the SDK, check your PATH environment variable.") + message(FATAL_ERROR "Invalid library type: ${type} (must be STATIC, OBJECT, SHARED or MODULE)") endif() - - cmake_path(HASH config_file _hash) - - set(CD_IMAGE_NAME ${image_name}) - set(CD_CONFIG_FILE cd_image_${_hash}.xml) - configure_file(${config_file} ${CD_CONFIG_FILE}) - - add_custom_target( - ${name} ALL - COMMAND ${MKPSXISO} -y ${CD_CONFIG_FILE} - BYPRODUCTS ${image_name}.bin ${image_name}.cue - COMMENT "Building CD image ${image_name}" - ${ARGN} - ) endfunction() -## Helper functions for assets +## Linking helpers -# psn00bsdk_target_incbin_a( -# <existing target name> <PRIVATE|PUBLIC|INTERFACE> -# <data symbol name> -# <size symbol name> -# <path to binary file> -# <linker section name> -# <alignment> -# ) function(psn00bsdk_target_incbin_a name type symbol_name size_name path section align) string(MAKE_C_IDENTIFIER ${symbol_name} _id) string(MAKE_C_IDENTIFIER ${size_name} _size) cmake_path(ABSOLUTE_PATH path OUTPUT_VARIABLE _path) + string(SHA1 _hash "${name} ${_id}") + set(_asm_file ${CMAKE_CURRENT_BINARY_DIR}/incbin_${_hash}.s) + # Generate an assembly source file that includes the binary file and add it # to the target's sources. The file is also added as a depedency to ensure # CMake builds it before the target (if it's not a static file). - set(_asm_file ${PROJECT_BINARY_DIR}/incbin_${name}_${_id}.s) file( CONFIGURE OUTPUT ${_asm_file} @@ -246,11 +253,6 @@ ${_size}: set_source_files_properties(${_asm_file} PROPERTIES OBJECT_DEPENDS ${_path}) endfunction() -# psn00bsdk_target_incbin( -# <existing target name> <PRIVATE|PUBLIC|INTERFACE> -# <symbol name> -# <path to binary file> -# ) function(psn00bsdk_target_incbin name type symbol_name path) string(MAKE_C_IDENTIFIER ${symbol_name} _id) @@ -264,3 +266,28 @@ function(psn00bsdk_target_incbin name type symbol_name path) 4 ) endfunction() + +## CD image and asset helpers + +function(psn00bsdk_add_cd_image name image_name config_file) + # Throw an error if mkpsxiso was not found. Performing this check manually + # (instead of just marking mkpsxiso as required) allows simple projects to + # be built even if mkpsxiso is not installed. + if(MKPSXISO STREQUAL "MKPSXISO-NOTFOUND") + message(FATAL_ERROR "Failed to locate mkpsxiso. If mkpsxiso wasn't installed alongside the SDK, check your PATH environment variable.") + endif() + + cmake_path(HASH config_file _hash) + + set(CD_IMAGE_NAME ${image_name}) + set(CD_CONFIG_FILE cd_image_${_hash}.xml) + configure_file(${config_file} ${CD_CONFIG_FILE}) + + add_custom_target( + ${name} ALL + COMMAND ${MKPSXISO} -y ${CD_CONFIG_FILE} + BYPRODUCTS ${image_name}.bin ${image_name}.cue + COMMENT "Building CD image ${image_name}" + ${ARGN} + ) +endfunction() diff --git a/libpsn00b/cmake/sdk.cmake b/libpsn00b/cmake/sdk.cmake index 8965e79..facee26 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 +# (C) 2021-2022 spicyjpeg - MPL licensed -cmake_minimum_required(VERSION 3.20) +cmake_minimum_required(VERSION 3.21) set( PSN00BSDK_TC "" @@ -14,7 +14,7 @@ set( ## CMake configuration -set(CMAKE_SYSTEM_NAME PlayStation) +set(CMAKE_SYSTEM_NAME Generic) set(CMAKE_SYSTEM_PROCESSOR mipsel) set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER) @@ -28,6 +28,11 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES PSN00BSDK_TC PSN00BSDK_TARGET PSN00BSDK_VERSION) +# Always generate compile_commands.json alongside build scripts. This allows +# some IDEs and tools (such as clangd) to automatically configure include +# directories and other options. +set(CMAKE_EXPORT_COMPILE_COMMANDS ON) + ## Toolchain path setup # Attempt to find GCC using a list of common installation locations. @@ -65,10 +70,9 @@ endif() ## Toolchain executables -# ${CMAKE_EXECUTABLE_SUFFIX} seems not to work in toolchain scripts, so we -# can't rely on it to determine the host OS extension for executables. The best -# workaround I found is to extract the extension from the path returned by -# find_program() using a regex. +# As we have overridden ${CMAKE_EXECUTABLE_SUFFIX} we can't rely on it to +# determine the host OS extension for executables. A workaround is to extract +# the extension from the path returned by find_program() using a regex. set(_prefix ${_bin}/${PSN00BSDK_TARGET}) string(REGEX MATCH ".+-gcc(.*)$" _dummy ${_gcc}) @@ -85,7 +89,6 @@ set(TOOLCHAIN_NM ${_prefix}-nm${CMAKE_MATCH_1}) ## SDK setup -# We can't set up the SDK here as the find_*() functions may fail if they are -# called before project(). We can however set a script to be executed right -# after project() is invoked. +# Continue initialization by running internal_setup.cmake after project() is +# invoked. set(CMAKE_PROJECT_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/internal_setup.cmake) diff --git a/libpsn00b/include/assert.h b/libpsn00b/include/assert.h index e27f2ed..32301e2 100644 --- a/libpsn00b/include/assert.h +++ b/libpsn00b/include/assert.h @@ -8,13 +8,13 @@ void _assert_abort(const char *file, int line, const char *expr); -#ifdef DEBUG +#ifdef NDEBUG +#define assert(x) +#else #define assert(expr) { \ if (!(expr)) \ _assert_abort(__FILE__, __LINE__, #expr); \ } -#else -#define assert(x) #endif #endif diff --git a/libpsn00b/include/psxcd.h b/libpsn00b/include/psxcd.h index 03ee792..0460f20 100644 --- a/libpsn00b/include/psxcd.h +++ b/libpsn00b/include/psxcd.h @@ -55,8 +55,10 @@ #define CdlModeAP 0x02 #define CdlModeRept 0x04 #define CdlModeSF 0x08 -#define CdlModeSize0 0x10 -#define CdlModeSize1 0x20 +//#define CdlModeSize0 0x10 +//#define CdlModeSize1 0x20 +#define CdlModeIgnore 0x10 +#define CdlModeSize 0x20 #define CdlModeRT 0x40 #define CdlModeSpeed 0x80 @@ -143,8 +145,10 @@ int CdControlF(uint8_t com, const void *param); int CdSync(int mode, uint8_t *result); uint32_t CdSyncCallback(CdlCB func); -long CdReadyCallback(CdlCB func); +int CdReadyCallback(CdlCB func); int CdGetSector(void *madr, int size); +int CdGetSector2(void *madr, int size); +int CdDataSync(int mode); CdlFILE* CdSearchFile(CdlFILE *loc, const char *filename); @@ -164,7 +168,7 @@ void CdCloseDir(CdlDIR* dir); int CdGetVolumeLabel(char* label); -long* CdAutoPauseCallback(void(*func)()); +int* CdAutoPauseCallback(void(*func)()); int CdIsoError(); int CdLoadSession(int session); diff --git a/libpsn00b/include/psxetc.h b/libpsn00b/include/psxetc.h index 24485d9..fcfec06 100644 --- a/libpsn00b/include/psxetc.h +++ b/libpsn00b/include/psxetc.h @@ -6,12 +6,27 @@ #ifndef __PSXETC_H #define __PSXETC_H +/* Macros */ + +// This macro is used internally by PSn00bSDK to log debug messages to a buffer +// which is then printed to stdout when calling VSync(). +#ifdef NDEBUG +#define _sdk_log(...) +#define _sdk_dump_log() +#else +#define _sdk_log(...) _sdk_log_inner(__VA_ARGS__) +#define _sdk_dump_log() _sdk_dump_log_inner() +#endif + /* Public API */ #ifdef __cplusplus extern "C" { #endif +void _sdk_log_inner(const char *fmt, ...); +void _sdk_dump_log_inner(void); + void *InterruptCallback(int irq, void (*func)(void)); void *GetInterruptCallback(int irq); void *DMACallback(int dma, void (*func)(void)); diff --git a/libpsn00b/include/psxgpu.h b/libpsn00b/include/psxgpu.h index 0e7ec00..f2568b0 100644 --- a/libpsn00b/include/psxgpu.h +++ b/libpsn00b/include/psxgpu.h @@ -488,20 +488,30 @@ void PutDrawEnvFast(DRAWENV *env); int GetODE(void); int VSync(int mode); -int DrawSync(int mode); - void *VSyncHaltFunction(void (*func)(void)); void *VSyncCallback(void (*func)(void)); + +int EnqueueDrawOp( + void (*func)(uint32_t, uint32_t, uint32_t), + uint32_t arg1, + uint32_t arg2, + uint32_t arg3 +); +int DrawSync(int mode); void *DrawSyncCallback(void (*func)(void)); -void LoadImage(const RECT *rect, const uint32_t *data); -void StoreImage(const RECT *rect, uint32_t *data); +int LoadImage(const RECT *rect, const uint32_t *data); +int StoreImage(const RECT *rect, uint32_t *data); +int MoveImage(const RECT *rect, int x, int y); +void LoadImage2(const RECT *rect, const uint32_t *data); +void StoreImage2(const RECT *rect, uint32_t *data); +void MoveImage2(const RECT *rect, int x, int y); void ClearOTagR(uint32_t *ot, size_t length); void ClearOTag(uint32_t *ot, size_t length); -void DrawOTag(const uint32_t *ot); +int DrawOTag(const uint32_t *ot); +int DrawOTagEnv(const uint32_t *ot, DRAWENV *env); void DrawOTag2(const uint32_t *ot); -void DrawOTagEnv(const uint32_t *ot, DRAWENV *env); void DrawPrim(const uint32_t *pri); void AddPrim(uint32_t *ot, const void *pri); diff --git a/libpsn00b/include/stdlib.h b/libpsn00b/include/stdlib.h index 1888c69..f0753c1 100644 --- a/libpsn00b/include/stdlib.h +++ b/libpsn00b/include/stdlib.h @@ -12,6 +12,16 @@ #define RAND_MAX 0x7fff +/* Structure definitions */ + +typedef struct _HeapUsage { + size_t total; // Total size of heap + stack + size_t heap; // Amount of memory currently reserved for heap + size_t stack; // Amount of memory currently reserved for stack + size_t alloc; // Amount of memory currently allocated + size_t alloc_max; // Maximum amount of memory ever allocated +} HeapUsage; + /* API */ #ifdef __cplusplus @@ -33,11 +43,12 @@ long double strtold(const char *nptr, char **endptr); double strtod(const char *nptr, char **endptr); float strtof(const char *nptr, char **endptr); -void _mem_init(size_t ram_size, size_t stack_max_size); void InitHeap(void *addr, size_t size); -//int SetHeapSize(size_t size); void *sbrk(ptrdiff_t incr); +void TrackHeapUsage(ptrdiff_t alloc_incr); +void GetHeapUsage(HeapUsage *usage); + void *malloc(size_t size); void *calloc(size_t num, size_t size); void *realloc(void *ptr, size_t size); diff --git a/libpsn00b/libc/abort.c b/libpsn00b/libc/abort.c index de4323d..2db5016 100644 --- a/libpsn00b/libc/abort.c +++ b/libpsn00b/libc/abort.c @@ -1,10 +1,10 @@ -#include <stdio.h> +#include <psxetc.h> /* Standard abort */ -void abort() { - printf("abort()\n"); +void abort(void) { + _sdk_log("abort()\n"); for (;;) __asm__ volatile(""); @@ -13,7 +13,7 @@ void abort() { /* Internal function used by assert() macro */ void _assert_abort(const char *file, int line, const char *expr) { - printf("%s:%d: assert(%s)\n", file, line, expr); + _sdk_log("%s:%d: assert(%s)\n", file, line, expr); for (;;) __asm__ volatile(""); @@ -22,7 +22,7 @@ void _assert_abort(const char *file, int line, const char *expr) { /* Pure virtual function call (C++) */ void __cxa_pure_virtual(void) { - printf("__cxa_pure_virtual()\n"); + _sdk_log("__cxa_pure_virtual()\n"); for (;;) __asm__ volatile(""); diff --git a/libpsn00b/libc/malloc.c b/libpsn00b/libc/malloc.c index 9d538cd..e9fd6f4 100644 --- a/libpsn00b/libc/malloc.c +++ b/libpsn00b/libc/malloc.c @@ -9,6 +9,8 @@ * latter being built on top of the former. This makes it possible to override * only InitHeap() and sbrk() while still using the default allocator, or * override malloc()/realloc()/free() while using the default heap manager. + * Custom allocators should call TrackHeapUsage() to let the heap manager know + * how much memory is allocated at a given time. */ #include <stddef.h> @@ -25,11 +27,13 @@ typedef struct _BlockHeader { size_t size; } BlockHeader; -/* Data */ +/* Internal globals */ static void *_heap_start, *_heap_end, *_heap_limit; -static void *_alloc_start = 0; -static BlockHeader *_alloc_head = 0, *_alloc_tail = 0; +static size_t _heap_alloc, _heap_alloc_max; + +static void *_alloc_start; +static BlockHeader *_alloc_head, *_alloc_tail; /* Heap management API */ @@ -37,6 +41,13 @@ __attribute__((weak)) void InitHeap(void *addr, size_t size) { _heap_start = addr; _heap_end = addr; _heap_limit = (void *) ((uintptr_t) addr + size); + + _heap_alloc = 0; + _heap_alloc_max = 0; + + _alloc_start = addr; + _alloc_head = 0; + _alloc_tail = 0; } __attribute__((weak)) void *sbrk(ptrdiff_t incr) { @@ -50,6 +61,22 @@ __attribute__((weak)) void *sbrk(ptrdiff_t incr) { return old_end; } +__attribute__((weak)) void TrackHeapUsage(ptrdiff_t alloc_incr) { + _heap_alloc += alloc_incr; + + if (_heap_alloc > _heap_alloc_max) + _heap_alloc_max = _heap_alloc; +} + +__attribute__((weak)) void GetHeapUsage(HeapUsage *usage) { + usage->total = _heap_limit - _heap_start; + usage->heap = _heap_end - _heap_start; + usage->stack = _heap_limit - _heap_end; + + usage->alloc = _heap_alloc; + usage->alloc_max = _heap_alloc_max; +} + /* Memory allocator */ static BlockHeader *_find_fit(BlockHeader *head, size_t size) { @@ -69,13 +96,16 @@ static BlockHeader *_find_fit(BlockHeader *head, size_t size) { } __attribute__((weak)) void *malloc(size_t size) { + if (!size) + return 0; + size_t _size = _align(size + sizeof(BlockHeader), 8); // Nothing's initialized yet? Let's just initialize the bottom of our heap, // flag it as allocated. if (!_alloc_head) { - if (!_alloc_start) - _alloc_start = sbrk(0); + //if (!_alloc_start) + //_alloc_start = sbrk(0); BlockHeader *new = (BlockHeader *) sbrk(_size); if (!new) @@ -89,6 +119,8 @@ __attribute__((weak)) void *malloc(size_t size) { _alloc_head = new; _alloc_tail = new; + + TrackHeapUsage(size); return ptr; } @@ -106,6 +138,8 @@ __attribute__((weak)) void *malloc(size_t size) { _alloc_head->prev = new; _alloc_head = new; + + TrackHeapUsage(size); return ptr; } @@ -122,6 +156,8 @@ __attribute__((weak)) void *malloc(size_t size) { (new->next)->prev = new; prev->next = new; + + TrackHeapUsage(size); return ptr; } @@ -138,6 +174,8 @@ __attribute__((weak)) void *malloc(size_t size) { _alloc_tail->next = new; _alloc_tail = new; + + TrackHeapUsage(size); return ptr; } @@ -153,13 +191,14 @@ __attribute__((weak)) void *realloc(void *ptr, size_t size) { if (!ptr) return malloc(size); - size_t _size = _align(size + sizeof(BlockHeader), 8); - + size_t _size = _align(size + sizeof(BlockHeader), 8); BlockHeader *prev = (BlockHeader *) ((uintptr_t) ptr - sizeof(BlockHeader)); // New memory block shorter? if (prev->size >= _size) { + TrackHeapUsage(size - prev->size); prev->size = _size; + if (!prev->next) sbrk((ptr - sbrk(0)) + _size); @@ -172,12 +211,14 @@ __attribute__((weak)) void *realloc(void *ptr, size_t size) { if (!new) return 0; + TrackHeapUsage(size - prev->size); prev->size = _size; return ptr; } // Do we have free memory after it? if (((prev->next)->ptr - ptr) > _size) { + TrackHeapUsage(size - prev->size); prev->size = _size; return ptr; } @@ -209,11 +250,13 @@ __attribute__((weak)) void free(void *ptr) { sbrk(-size); } + TrackHeapUsage(-(_alloc_head->size)); return; } // Finding the proper block BlockHeader *cur = _alloc_head; + for (cur = _alloc_head; ptr != cur->ptr; cur = cur->next) { if (!cur->next) return; @@ -221,15 +264,16 @@ __attribute__((weak)) void free(void *ptr) { if (cur->next) { // In the middle, just unlink it - cur->next->prev = cur->prev; + (cur->next)->prev = cur->prev; } else { // At the end, shrink heap - _alloc_tail = cur->prev; - void *top = sbrk(0); size_t size = (top - (cur->prev)->ptr) - (cur->prev)->size; + _alloc_tail = cur->prev; + sbrk(-size); } + TrackHeapUsage(-(cur->size)); (cur->prev)->next = cur->next; } diff --git a/libpsn00b/libc/memset.s b/libpsn00b/libc/memset.s index b3a3af3..5a1589d 100644 --- a/libpsn00b/libc/memset.s +++ b/libpsn00b/libc/memset.s @@ -1,25 +1,117 @@ -# High speed ASM memset implementation by Lameguy64 -# -# Part of PSn00bSDK +# PSn00bSDK optimized memset +# (C) 2022 spicyjpeg - MPL licensed .set noreorder -.section .text - -# Arguments: -# a0 - address to buffer -# a1 - value to set -# a2 - bytes to set +.section .text.memset .global memset -.type memset,@function +.type memset, @function memset: - move $v0, $a0 - blez $a2, .Lexit - addi $a2, -1 - sb $a1, 0($a0) - b memset - addiu $a0, 1 -.Lexit: - jr $ra - nop -
\ No newline at end of file + # If more than 16 bytes have to be written then take the "large" path, + # otherwise use the code below. + addiu $t0, $a2, -16 + bgtz $t0, .Llarge_fill + move $v0, $a0 # return_value = dest + + # Jump to one of the sb opcodes below. This is basically a cut-down Duff's + # device implementation with no looping. + la $t0, .Lsmall_duff + 0x40 # jump_addr = &small_duff[(16 - count) * 4] + sll $t1, $a2, 2 + subu $t0, $t1 + addu $a0, $a2 # dest -= 16 - count + jr $t0 + addiu $a0, -16 + +.Lsmall_duff: + sb $a1, 0x0($a0) + sb $a1, 0x1($a0) + sb $a1, 0x2($a0) + sb $a1, 0x3($a0) + sb $a1, 0x4($a0) + sb $a1, 0x5($a0) + sb $a1, 0x6($a0) + sb $a1, 0x7($a0) + sb $a1, 0x8($a0) + sb $a1, 0x9($a0) + sb $a1, 0xa($a0) + sb $a1, 0xb($a0) + sb $a1, 0xc($a0) + sb $a1, 0xd($a0) + sb $a1, 0xe($a0) + jr $ra + sb $a1, 0xf($a0) + +.Llarge_fill: + # Initialize fast filling by repeating the fill byte 4 times, so it can be + # written 32 bits at a time. + andi $a1, 0xff # ch &= 0xff + sll $t0, $a1, 8 # ch |= (ch << 8) | (ch << 16) | (ch << 24) + or $a1, $t0 + sll $t0, $a1, 16 + or $a1, $t0 + + # Fill the first 1-4 bytes (here the swr instruction does all the magic) + # and update dest and count accordingly. + swr $a1, 0($a0) + andi $t0, $a0, 3 # align = 4 - (dest % 4) + addiu $t0, -4 + addu $a2, $t0 # count -= align + subu $a0, $t0 # dest += align + + la $t1, .Llarge_duff + andi $t2, $a2, 3 # remainder = count % 4 + subu $a2, $t2 # count -= remainder + +.Llarge_fill_loop: + # If 128 bytes or more still have to be written, skip calculating the jump + # offset and execute the whole block of sw opcodes. + addiu $a2, -0x80 # count -= 0x80 + bgez $a2, .Llarge_duff + #nop + + # Jump to one of the sw opcodes below. This is the "full" Duff's device. + subu $t0, $t1, $a2 # jump_addr = &large_duff[0x80 - (count + 0x80)] + jr $t0 + addu $a0, $a2 # dest -= 0x80 - (count + 0x80) + +.Llarge_duff: + sw $a1, 0x00($a0) + sw $a1, 0x04($a0) + sw $a1, 0x08($a0) + sw $a1, 0x0c($a0) + sw $a1, 0x10($a0) + sw $a1, 0x14($a0) + sw $a1, 0x18($a0) + sw $a1, 0x1c($a0) + sw $a1, 0x20($a0) + sw $a1, 0x24($a0) + sw $a1, 0x28($a0) + sw $a1, 0x2c($a0) + sw $a1, 0x30($a0) + sw $a1, 0x34($a0) + sw $a1, 0x38($a0) + sw $a1, 0x3c($a0) + sw $a1, 0x40($a0) + sw $a1, 0x44($a0) + sw $a1, 0x48($a0) + sw $a1, 0x4c($a0) + sw $a1, 0x50($a0) + sw $a1, 0x54($a0) + sw $a1, 0x58($a0) + sw $a1, 0x5c($a0) + sw $a1, 0x60($a0) + sw $a1, 0x64($a0) + sw $a1, 0x68($a0) + sw $a1, 0x6c($a0) + sw $a1, 0x70($a0) + sw $a1, 0x74($a0) + sw $a1, 0x78($a0) + sw $a1, 0x7c($a0) + + bgtz $a2, .Llarge_fill_loop + addiu $a0, 0x80 # dest += 0x80 + + # Fill the remaining 1-4 bytes, using (again) an unaligned store. + addu $a0, $t2 # last_byte = dest + remainder - 1 + jr $ra + swl $a1, -1($a0) diff --git a/libpsn00b/libc/start.c b/libpsn00b/libc/start.c index 87ac951..9ff09c8 100644 --- a/libpsn00b/libc/start.c +++ b/libpsn00b/libc/start.c @@ -21,7 +21,7 @@ const char **__argv; static const char *_argv_buffer[ARGC_MAX]; static char _arg_string_buffer[132]; -static void _parse_kernel_args() { +static void _parse_kernel_args(void) { // Copy the argument string from kernel memory into a private buffer (which // won't be cleared or deallocated) and trim it at the first newline. memset(_arg_string_buffer, 0, 132); @@ -48,7 +48,7 @@ static void _parse_kernel_args() { } } -/* Heap initialization */ +/* Main */ // These are defined by the linker script. Note that these are *NOT* pointers, // they are virtual symbols whose location matches their value. The simplest @@ -58,20 +58,6 @@ 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); @@ -88,10 +74,9 @@ void _start_inner(int32_t override_argc, const char **override_argv) { for (uint32_t *i = (uint32_t *) __bss_start; i < (uint32_t *) _end; i++) *i = 0; - // 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); + // Initialize the heap and place it after the executable, assuming 2 MB of + // RAM. Note that InitHeap() can be called again in main(). + InitHeap((void *) _end + 4, (void *) 0x801ffff8 - (void *) _end); if (override_argv) { __argc = override_argc; diff --git a/libpsn00b/psxapi/drivers.s b/libpsn00b/psxapi/drivers.s index 1cf5050..d991f90 100644 --- a/libpsn00b/psxapi/drivers.s +++ b/libpsn00b/psxapi/drivers.s @@ -90,26 +90,26 @@ ListDev: jr $t2 li $t1, 0x49 -.section .text.InitCard -.global InitCard -.type InitCard, @function -InitCard: +.section .text.InitCARD +.global InitCARD +.type InitCARD, @function +InitCARD: li $t2, 0xb0 jr $t2 li $t1, 0x4a -.section .text.StartCard -.global StartCard -.type StartCard, @function -StartCard: +.section .text.StartCARD +.global StartCARD +.type StartCARD, @function +StartCARD: li $t2, 0xb0 jr $t2 li $t1, 0x4b -.section .text.StopCard -.global StopCard -.type StopCard, @function -StopCard: +.section .text.StopCARD +.global StopCARD +.type StopCARD, @function +StopCARD: li $t2, 0xb0 jr $t2 li $t1, 0x4c diff --git a/libpsn00b/psxapi/stubs.json b/libpsn00b/psxapi/stubs.json index 200cbc7..50ffb55 100644 --- a/libpsn00b/psxapi/stubs.json +++ b/libpsn00b/psxapi/stubs.json @@ -362,19 +362,19 @@ { "type": "b", "id": 74, - "name": "InitCard", + "name": "InitCARD", "file": "drivers.s" }, { "type": "b", "id": 75, - "name": "StartCard", + "name": "StartCARD", "file": "drivers.s" }, { "type": "b", "id": 76, - "name": "StopCard", + "name": "StopCARD", "file": "drivers.s" }, { diff --git a/libpsn00b/psxcd/cdgetsector.s b/libpsn00b/psxcd/cdgetsector.s deleted file mode 100644 index 6a29069..0000000 --- a/libpsn00b/psxcd/cdgetsector.s +++ /dev/null @@ -1,56 +0,0 @@ -.set noreorder - -.include "hwregs_a.inc" - -.section .text - -.global CdGetSector -.type CdGetSector, @function -CdGetSector: - - lui $a2, IOBASE - -#.Lwait_fifo: # Probably redundant as the BIOS CD-ROM -# lbu $v0, CD_REG0($a2) # routines do not not wait for this -# nop -# andi $v0, 0x40 -# beqz $v0, .Lwait_fifo -# nop - - lui $v0, 0x1 -# srl $a1, 2 # (the official implementation expects $a1/size - # to be in 32-bit words rather than bytes) - or $v0, $a1 - sw $a0, DMA3_MADR($a2) # Set DMA base address and transfer length - sw $v0, DMA3_BCR($a2) - - lui $v0, 0x1100 # Start DMA transfer - sw $v0, DMA3_CHCR($a2) - nop - nop - -.Ldma_wait: # Ensure DMA transfer has completed - lw $v0, DMA3_CHCR($a2) - nop - srl $v0, 24 - andi $v0, 0x1 - - bnez $v0, .Ldma_wait - nop - -# Not stable -# sb $0 , CD_REG0($a2) -#.Lflush_fifo: # Read out any remaining bytes in the buffer -# lbu $v1, CD_REG0($a2) -# li $v0, 0x40 -# and $v1, $v0 -# beqz $v1, .Lend_flush -# nop -# lbu $v0, CD_REG2($a2) -# b .Lflush_fifo -# nop -#.Lend_flush: - - jr $ra - li $v0, 1 - diff --git a/libpsn00b/psxcd/getsector.c b/libpsn00b/psxcd/getsector.c new file mode 100644 index 0000000..31d0ac7 --- /dev/null +++ b/libpsn00b/psxcd/getsector.c @@ -0,0 +1,51 @@ +/* + * PSn00bSDK CD drive library (sector DMA API) + * (C) 2022 spicyjpeg - MPL licensed + */ + +#include <stdint.h> +#include <psxetc.h> +#include <psxcd.h> +#include <hwregs_c.h> + +#define DATA_SYNC_TIMEOUT 0x100000 + +/* DMA transfer functions */ + +int CdGetSector(void *madr, int size) { + //while (!(CD_STAT & (1 << 6))) + //__asm__ volatile(""); + + DMA_MADR(3) = (uint32_t) madr; + DMA_BCR(3) = size | (1 << 16); + DMA_CHCR(3) = 0x11000000; + + while (DMA_CHCR(3) & (1 << 24)) + __asm__ volatile(""); + + return 1; +} + +int CdGetSector2(void *madr, int size) { + //while (!(CD_STAT & (1 << 6))) + //__asm__ volatile(""); + + DMA_MADR(3) = (uint32_t) madr; + DMA_BCR(3) = size | (1 << 16); + DMA_CHCR(3) = 0x11400100; // Transfer 1 word every 16 CPU cycles + + return 1; +} + +int CdDataSync(int mode) { + if (mode) + return (DMA_CHCR(3) >> 24) & 1; + + for (int i = DATA_SYNC_TIMEOUT; i; i--) { + if (!(DMA_CHCR(3) & (1 << 24))) + return 0; + } + + _sdk_log("psxcd: CdDataSync() timeout\n"); + return -1; +} diff --git a/libpsn00b/psxcd/isofs.c b/libpsn00b/psxcd/isofs.c index 582b8d9..0425c0d 100644 --- a/libpsn00b/psxcd/isofs.c +++ b/libpsn00b/psxcd/isofs.c @@ -1,15 +1,12 @@ #include <stdint.h> -#include <stdio.h> #include <stdlib.h> #include <string.h> #include <psxgpu.h> -#include <psxsio.h> +#include <psxapi.h> +#include <psxetc.h> #include "psxcd.h" #include "isofs.h" -// Uncommend to enable debug output -//#define DEBUG - #define DEFAULT_PATH_SEP '\\' #define IS_PATH_SEP(ch) (((ch) == '/') || ((ch) == '\\')) @@ -46,9 +43,8 @@ static int _CdReadIsoDescriptor(int session_offs) CdControl(CdlNop, 0, 0); if( (CdStatus()&0x10) ) { -#ifdef DEBUG - printf("psxcd: Lid is still open.\n"); -#endif + _sdk_log("psxcd: Lid is still open.\n"); + _cd_iso_error = CdlIsoLidOpen; return -1; } @@ -61,61 +57,47 @@ static int _CdReadIsoDescriptor(int session_offs) { return 0; } - -#ifdef DEBUG - printf("psxcd: Parsing ISO file system.\n"); -#endif + + _sdk_log("psxcd: Parsing ISO file system.\n"); // Seek to volume descriptor CdIntToPos(16+session_offs, &loc); if( !CdControl(CdlSetloc, (uint8_t*)&loc, 0) ) { -#ifdef DEBUG - printf("psxcd: Could not set seek destination.\n"); -#endif + _sdk_log("psxcd: Could not set seek destination.\n"); + _cd_iso_error = CdlIsoSeekError; return -1; } -#ifdef DEBUG - printf("psxcd: Set seek target.\n"); -#endif - -#ifdef DEBUG - printf("psxcd: Read sectors.\n"); -#endif + _sdk_log("psxcd: Read sectors.\n"); + // Read volume descriptor CdRead(1, (uint32_t*)_cd_iso_descriptor_buff, CdlModeSpeed); if( CdReadSync(0, 0) ) { -#ifdef DEBUG - printf("psxcd: Error reading ISO volume descriptor.\n"); -#endif + _sdk_log("psxcd: Error reading ISO volume descriptor.\n"); + _cd_iso_error = CdlIsoReadError; return -1; } - -#ifdef DEBUG - printf("psxcd: Read complete.\n"); -#endif - + + _sdk_log("psxcd: Read complete.\n"); + // Verify if volume descriptor is present descriptor = (ISO_DESCRIPTOR*)_cd_iso_descriptor_buff; if( strncmp("CD001", descriptor->header.id, 5) ) { -#ifdef DEBUG - printf("psxcd: Disc does not contain a ISO9660 file system.\n"); -#endif + _sdk_log("psxcd: Disc does not contain a ISO9660 file system.\n"); + _cd_iso_error = CdlIsoInvalidFs; return -1; } - -#ifdef DEBUG - printf("psxcd_dbg: Path table LBA = %d\n", descriptor->pathTable1Offs); - printf("psxcd_dbg: Path table len = %d\n", descriptor->pathTableSize.lsb); -#endif - + + _sdk_log("psxcd: Path table LBA = %d\n", descriptor->pathTable1Offs); + _sdk_log("psxcd: Path table len = %d\n", descriptor->pathTableSize.lsb); + // Allocate path table buffer i = ((2047+descriptor->pathTableSize.lsb)>>11)<<11; if( _cd_iso_pathtable_buff ) @@ -123,20 +105,17 @@ static int _CdReadIsoDescriptor(int session_offs) free(_cd_iso_pathtable_buff); } _cd_iso_pathtable_buff = (uint8_t*)malloc(i); - -#ifdef DEBUG - printf("psxcd_dbg: Allocated %d bytes for path table.\n", i); -#endif - + + _sdk_log("psxcd: Allocated %d bytes for path table.\n", i); + // Read path table CdIntToPos(descriptor->pathTable1Offs, &loc); CdControl(CdlSetloc, (uint8_t*)&loc, 0); CdRead(i>>11, (uint32_t*)_cd_iso_pathtable_buff, CdlModeSpeed); if( CdReadSync(0, 0) ) { -#ifdef DEBUG - printf("psxcd: Error reading ISO path table.\n"); -#endif + _sdk_log("psxcd: Error reading ISO path table.\n"); + _cd_iso_error = CdlIsoReadError; return -1; } @@ -162,14 +141,13 @@ static int _CdReadIsoDirectory(int lba) CdIntToPos(lba, &loc); i = CdPosToInt(&loc); -#ifdef DEBUG - printf("psxcd_dbg: Seek to sector %d\n", i); -#endif + + _sdk_log("psxcd: Seek to sector %d\n", i); + if( !CdControl(CdlSetloc, (uint8_t*)&loc, 0) ) { -#ifdef DEBUG - printf("psxcd: Could not set seek destination.\n"); -#endif + _sdk_log("psxcd: Could not set seek destination.\n"); + _cd_iso_error = CdlIsoSeekError; return -1; } @@ -184,28 +162,24 @@ static int _CdReadIsoDirectory(int lba) CdRead(1, (uint32_t*)_cd_iso_directory_buff, CdlModeSpeed); if( CdReadSync(0, 0) ) { -#ifdef DEBUG - printf("psxcd: Error reading initial directory record.\n"); -#endif + _sdk_log("psxcd: Error reading initial directory record.\n"); + _cd_iso_error = CdlIsoReadError; return -1; } direntry = (ISO_DIR_ENTRY*)_cd_iso_directory_buff; _cd_iso_directory_len = direntry->entrySize.lsb; - -#ifdef DEBUG - printf("psxcd_dbg: Location of directory record = %d\n", direntry->entryOffs.lsb); - printf("psxcd_dbg: Size of directory record = %d\n", _cd_iso_directory_len); -#endif + + _sdk_log("psxcd: Location of directory record = %d\n", direntry->entryOffs.lsb); + _sdk_log("psxcd: Size of directory record = %d\n", _cd_iso_directory_len); if( _cd_iso_directory_len > 2048 ) { if( !CdControl(CdlSetloc, (uint8_t*)&loc, 0) ) { -#ifdef DEBUG - printf("psxcd: Could not set seek destination.\n"); -#endif + _sdk_log("psxcd: Could not set seek destination.\n"); + _cd_iso_error = CdlIsoSeekError; return -1; } @@ -213,16 +187,14 @@ static int _CdReadIsoDirectory(int lba) free(_cd_iso_directory_buff); i = ((2047+_cd_iso_directory_len)>>11)<<11; _cd_iso_directory_buff = (uint8_t*)malloc(i); -#ifdef DEBUG - printf("psxcd_dbg: Allocated %d bytes for directory record.\n", i); -#endif + + _sdk_log("psxcd: Allocated %d bytes for directory record.\n", i); CdRead(i>>11, (uint32_t*)_cd_iso_directory_buff, CdlModeSpeed); if( CdReadSync(0, 0) ) { -#ifdef DEBUG - printf("psxcd: Error reading remaining directory record.\n"); -#endif + _sdk_log("psxcd: Error reading remaining directory record.\n"); + _cd_iso_error = CdlIsoReadError; return -1; } @@ -234,7 +206,7 @@ static int _CdReadIsoDirectory(int lba) return 0; } -#ifdef DEBUG +#ifndef NDEBUG static void dump_directory(void) { @@ -243,7 +215,7 @@ static void dump_directory(void) ISO_DIR_ENTRY *dir_entry; char namebuff[16]; - printf("psxcd_dbg: Cached directory record contents:\n"); + _sdk_log("psxcd: Cached directory record contents:\n"); i = 0; dir_pos = 0; @@ -254,7 +226,7 @@ static void dump_directory(void) strncpy(namebuff, _cd_iso_directory_buff+dir_pos+sizeof(ISO_DIR_ENTRY), dir_entry->identifierLen); - printf("P:%d L:%d %s\n", dir_pos, dir_entry->identifierLen, namebuff); + _sdk_log("psxcd: P:%d L:%d %s\n", dir_pos, dir_entry->identifierLen, namebuff); dir_pos += dir_entry->entryLength; i++; @@ -273,7 +245,7 @@ static void dump_directory(void) } } - printf("--\n"); + _sdk_log("psxcd: --\n"); } @@ -284,7 +256,7 @@ static void dump_pathtable(void) ISO_DESCRIPTOR *descriptor; char namebuff[16]; - printf("psxcd_dbg: Path table entries:\n"); + _sdk_log("psxcd: Path table entries:\n"); descriptor = (ISO_DESCRIPTOR*)_cd_iso_descriptor_buff; @@ -298,7 +270,7 @@ static void dump_pathtable(void) tbl_pos+sizeof(ISO_PATHTABLE_ENTRY), tbl_entry->nameLength); - printf("psxcd_dbg: %s\n", namebuff); + _sdk_log("psxcd: %s\n", namebuff); // Advance to next entry tbl_pos += sizeof(ISO_PATHTABLE_ENTRY) @@ -393,10 +365,8 @@ static int find_dir_entry(const char *name, ISO_DIR_ENTRY *dirent) int dir_pos; ISO_DIR_ENTRY *dir_entry; char namebuff[16]; - -#ifdef DEBUG - printf( "psxcd_dbg: Locating file %s.\n", name ); -#endif + + _sdk_log("psxcd: Locating file %s.\n", name); i = 0; dir_pos = 0; @@ -434,8 +404,8 @@ static int find_dir_entry(const char *name, ISO_DIR_ENTRY *dirent) static char* get_pathname(char *path, const char *filename) { - char *c = 0; - for (char *i = filename; *i; i++) { + const char *c = 0; + for (const char *i = filename; *i; i++) { if (IS_PATH_SEP(*i)) c = i; } @@ -453,8 +423,8 @@ static char* get_pathname(char *path, const char *filename) static char* get_filename(char *name, const char *filename) { - char *c = 0; - for (char *i = filename; *i; i++) { + const char *c = 0; + for (const char *i = filename; *i; i++) { if (IS_PATH_SEP(*i)) c = i; } @@ -489,40 +459,35 @@ CdlFILE *CdSearchFile(CdlFILE *fp, const char *filename) // Read ISO descriptor and path table if( _CdReadIsoDescriptor(0) ) { -#ifdef DEBUG - printf("psxcd: Could not read ISO file system.\n"); -#endif + _sdk_log("psxcd: Could not read ISO file system.\n"); return NULL; } -#ifdef DEBUG - // printf("psxcd: ISO file system cache updated.\n"); -#endif + + // _sdk_log("psxcd: ISO file system cache updated.\n"); // _cd_media_changed = 0; //} // Get number of directories in path table num_dirs = get_pathtable_entry(0, NULL, NULL); -#ifdef DEBUG - printf("psxcd_dbg: Directories in path table: %d\n", num_dirs); +#ifndef NDEBUG + _sdk_log("psxcd: Directories in path table: %d\n", num_dirs); rbuff = resolve_pathtable_path(num_dirs-1, tpath_rbuff+127); if( !rbuff ) { - printf("psxcd_dbg: Could not resolve path.\n"); + _sdk_log("psxcd: Could not resolve path.\n"); } else { - printf("psxcd_dbg: Longest path: %s|\n", rbuff); + _sdk_log("psxcd: Longest path: %s|\n", rbuff); } #endif if( get_pathname(search_path, filename) ) { -#ifdef DEBUG - printf("psxcd_dbg: Search path = %s|\n", search_path); -#endif + _sdk_log("psxcd: Search path = %s|\n", search_path); } // Search the pathtable for a matching path @@ -530,9 +495,8 @@ CdlFILE *CdSearchFile(CdlFILE *fp, const char *filename) for(i=1; i<num_dirs; i++) { rbuff = resolve_pathtable_path(i, tpath_rbuff+127); -#ifdef DEBUG - printf("psxcd_dbg: Found = %s|\n", rbuff); -#endif + _sdk_log("psxcd: Found = %s|\n", rbuff); + if( rbuff ) { if( strcmp(search_path, rbuff) == 0 ) @@ -545,24 +509,16 @@ CdlFILE *CdSearchFile(CdlFILE *fp, const char *filename) if( !found_dir ) { -#ifdef DEBUG - printf("psxcd_dbg: Directory path not found.\n"); -#endif + _sdk_log("psxcd: Directory path not found.\n"); return NULL; } - -#ifdef DEBUG - printf("psxcd_dbg: Found directory at record %d!\n", found_dir); -#endif - get_pathtable_entry(found_dir, &tbl_entry, NULL); + _sdk_log("psxcd: Found directory at record %d!\n", found_dir); -#ifdef DEBUG - printf("psxcd_dbg: Directory LBA = %d\n", tbl_entry.dirOffs); -#endif + get_pathtable_entry(found_dir, &tbl_entry, NULL); + _sdk_log("psxcd: Directory LBA = %d\n", tbl_entry.dirOffs); _CdReadIsoDirectory(tbl_entry.dirOffs); - get_filename(fp->name, filename); // Add version number if not specified @@ -571,21 +527,18 @@ CdlFILE *CdSearchFile(CdlFILE *fp, const char *filename) strcat(fp->name, ";1"); } -#ifdef DEBUG - dump_directory(); +#ifndef NDEBUG + //dump_directory(); #endif if( find_dir_entry(fp->name, &dir_entry) ) { -#ifdef DEBUG - printf("psxcd: Could not find file.\n"); -#endif + _sdk_log("psxcd: Could not find file.\n"); + return NULL; } - -#ifdef DEBUG - printf("psxcd_dbg: Located file at LBA %d.\n", dir_entry.entryOffs.lsb); -#endif + + _sdk_log("psxcd: Located file at LBA %d.\n", dir_entry.entryOffs.lsb); CdIntToPos(dir_entry.entryOffs.lsb, &fp->pos); fp->size = dir_entry.entrySize.lsb; @@ -609,14 +562,11 @@ CdlDIR *CdOpenDir(const char* path) // Read ISO descriptor and path table if( _CdReadIsoDescriptor( 0 ) ) { -#ifdef DEBUG - printf( "psxcd: Could not read ISO file system.\n" ); -#endif + _sdk_log( "psxcd: Could not read ISO file system.\n" ); return NULL; } -#ifdef DEBUG -// printf( "psxcd: ISO file system cache updated.\n" ); -#endif + +// _sdk_log( "psxcd: ISO file system cache updated.\n" ); // _cd_media_changed = 0; // } @@ -626,9 +576,8 @@ CdlDIR *CdOpenDir(const char* path) for( i=1; i<num_dirs; i++ ) { rbuff = resolve_pathtable_path( i, tpath_rbuff+127 ); -#ifdef DEBUG - printf( "psxcd_dbg: Found = %s|\n", rbuff ); -#endif + _sdk_log( "psxcd: Found = %s|\n", rbuff ); + if( rbuff ) { if( strcmp( path, rbuff ) == 0 ) @@ -641,21 +590,14 @@ CdlDIR *CdOpenDir(const char* path) if( !found_dir ) { -#ifdef DEBUG - printf( "psxcd_dbg: Directory path not found.\n" ); -#endif + _sdk_log( "psxcd: Directory path not found.\n" ); return NULL; } - -#ifdef DEBUG - printf( "psxcd_dbg: Found directory at record %d!\n", found_dir ); -#endif + + _sdk_log( "psxcd: Found directory at record %d!\n", found_dir ); get_pathtable_entry( found_dir, &tbl_entry, NULL ); - -#ifdef DEBUG - printf( "psxcd_dbg: Directory LBA = %d\n", tbl_entry.dirOffs ); -#endif + _sdk_log( "psxcd: Directory LBA = %d\n", tbl_entry.dirOffs ); _CdReadIsoDirectory( tbl_entry.dirOffs ); @@ -720,15 +662,11 @@ int CdReadDir(CdlDIR *dir, CdlFILE* file) file->size = dir_entry->entrySize.lsb; -#ifdef DEBUG - printf("dir_entry->entryLength = %d, ", dir_entry->entryLength); -#endif - + _sdk_log("psxcd: dir_entry->entryLength = %d, ", dir_entry->entryLength); + d_dir->_pos += dir_entry->entryLength; - -#ifdef DEBUG - printf("d_dir->_pos = %d\n", d_dir->_pos); -#endif + + _sdk_log("psxcd: d_dir->_pos = %d\n", d_dir->_pos); // Check if padding is reached (end of record sector) if( d_dir->_dir[d_dir->_pos] == 0 ) @@ -832,19 +770,13 @@ int CdLoadSession(int session) int i; // Seek to specified session -#ifdef DEBUG - printf("psxcd: CdLoadSession(): Seeking to session %d...\n", session); -#endif + _sdk_log("psxcd: CdLoadSession(): Seeking to session %d...\n", session); CdControl(CdlSetsession, (unsigned char*)&session, (unsigned char*)&resultbuff); if( CdSync(0, 0) == CdlDiskError ) { -#ifdef DEBUG - printf("psxcd: CdLoadSession(): Session seek failed, " - "session does not exist.\n"); - printf("psxcd: CdLoadSession(): Restarting CD-ROM...\n"); -#endif + _sdk_log("psxcd: CdLoadSession(): Session seek failed, session does not exist. Restarting CD-ROM...\n"); // Restart CD-ROM on session seek failure CdControl(CdlNop, 0, 0); @@ -855,39 +787,46 @@ int CdLoadSession(int session) } // Set search routine callback + EnterCriticalSection(); ready_oldcb = CdReadyCallback(_scan_callback); - + ExitCriticalSection(); + _ses_scanfound = 0; _ses_scancount = 0; _ses_scancomplete = 0; _ses_scanbuff = scanbuff; // Begin scan for an ISO volume descriptor -#ifdef DEBUG - printf("psxcd: CdLoadSession(): Scanning for ISO9660 volume descriptor.\n"); -#endif + _sdk_log("psxcd: CdLoadSession(): Scanning for ISO9660 volume descriptor.\n"); + i = CdlModeSpeed; CdControl(CdlSetmode, (unsigned char*)&i, 0); CdControl(CdlReadN, 0, (unsigned char*)resultbuff); // Wait until scan complete while(!_ses_scancomplete); - + + EnterCriticalSection(); CdReadyCallback((void*)_ready_oldcb); - + ExitCriticalSection(); + if( !_ses_scanfound ) { -#ifdef DEBUG - printf("psxcd: CdLoadSession(): Did not find volume descriptor.\n"); -#endif + _sdk_log("psxcd: CdLoadSession(): Did not find volume descriptor.\n"); + _cd_iso_error = CdlIsoInvalidFs; + EnterCriticalSection(); CdReadyCallback((CdlCB)ready_oldcb); + ExitCriticalSection(); + return -1; } // Restore old callback if any + EnterCriticalSection(); CdReadyCallback((CdlCB)ready_oldcb); - + ExitCriticalSection(); + // Wait until CD-ROM has completely stopped reading, to get a consistent // fix of the CD-ROM pickup's current location do @@ -901,16 +840,12 @@ int CdLoadSession(int session) CdSync(0, 0); loc = (CdlLOC*)resultbuff; - -#ifdef DEBUG - printf("psxcd: CdLoadSession(): Session found in %02d:%02d:%02d (LBA=%d)\n", + + _sdk_log("psxcd: CdLoadSession(): Session found in %02d:%02d:%02d (LBA=%d)\n", btoi(loc->minute), btoi(loc->second), btoi(loc->sector), CdPosToInt(loc)); -#endif - + i = CdPosToInt(loc)-17; -#ifdef DEBUG - printf("psxcd: CdLoadSession(): Session starting at LBA=%d\n", i); -#endif + _sdk_log("psxcd: CdLoadSession(): Session starting at LBA=%d\n", i); _cd_media_changed = 1; diff --git a/libpsn00b/psxcd/psxcd.c b/libpsn00b/psxcd/psxcd.c index 6340638..b914b5e 100644 --- a/libpsn00b/psxcd/psxcd.c +++ b/libpsn00b/psxcd/psxcd.c @@ -1,6 +1,7 @@ #include <stdint.h> -#include <stdio.h> #include <psxgpu.h> +#include <psxetc.h> +#include <psxapi.h> #include "psxcd.h" #define READ_TIMEOUT 600 // 10 seconds for NTSC @@ -25,8 +26,7 @@ void _cd_control(unsigned char com, const void *param, int plen); void _cd_wait_ack(void); void _cd_wait(void); -int CdInit(void) -{ +int CdInit(void) { // Sets up CD-ROM hardware and low-level subsystem _cd_init(); @@ -37,14 +37,11 @@ int CdInit(void) CdControl(CdlNop, 0, 0); CdControl(CdlInit, 0, 0); - if( CdSync(0, 0) != CdlDiskError ) - { + if(CdSync(0, 0) != CdlDiskError) { CdControl(CdlDemute, 0, 0); - printf("psxcd: Init Ok!\n"); - } - else - { - printf("psxcd: Error initializing. Bad disc/drive or no disc inserted.\n"); + _sdk_log("psxcd: setup done\n"); + } else { + _sdk_log("psxcd: setup error, bad disc/drive or no disc inserted\n"); } return 1; @@ -86,7 +83,7 @@ int CdControlB(unsigned char com, const void *param, unsigned char *result) int CdControlF(unsigned char com, const void *param) { int param_len=0; - + // Command specific parameters switch(com) { @@ -104,31 +101,26 @@ int CdControlF(unsigned char com, const void *param) param_len = 2; break; case CdlSetmode: - param_len = 1; - break; case CdlSetsession: - param_len = 1; - break; case CdlTest: - param_len = 1; - break; case CdlGetTD: param_len = 1; + break; + case CdlReadN: + case CdlReadS: + case CdlSeekL: + case CdlSeekP: + if( param ) + { + _cd_control(CdlSetloc, param, 3); + _cd_last_setloc = *((CdlLOC*)param); + } + break; } - - // Issue Setloc if parameters are specified on CdlReadN and CdlReadS - if( ( com == CdlReadN ) || ( com == CdlReadS ) ) - { - if( param ) - { - _cd_control(CdlSetloc, param, 3); - _cd_last_setloc = *((CdlLOC*)param); - } - } - + // Issue CD command _cd_control(com, param, param_len); - + return 1; } @@ -288,24 +280,18 @@ int CdRead(int sectors, uint32_t *buf, int mode) _cd_read_addr = buf; // Determine sector based on mode flags - if( mode & CdlModeSize0 ) - { - _cd_read_sector_sz = 2328 / 4; - } - else if( mode & CdlModeSize1 ) - { + if( mode & CdlModeSize ) _cd_read_sector_sz = 2340 / 4; - } else - { _cd_read_sector_sz = 2048 / 4; - } _cd_read_counter = VSync(-1); // Set read callback + EnterCriticalSection(); _cd_read_oldcb = CdReadyCallback(_CdReadReadyCallback); - + ExitCriticalSection(); + // Set specified mode CdControl(CdlSetmode, (uint8_t*)&mode, 0); @@ -319,7 +305,7 @@ static void CdDoRetry() { int cb; - printf( "CdRead: Retrying...\n" ); + _sdk_log("psxcd: retrying read...\n"); // Stop reading CdControl(CdlPause, 0, 0); @@ -331,9 +317,11 @@ static void CdDoRetry() // Reset timeout _cd_read_counter = VSync(-1); - + + EnterCriticalSection(); CdReadyCallback(_CdReadReadyCallback); - + ExitCriticalSection(); + // Retry read CdControl(CdlSetloc, (void*)&_cd_last_setloc, 0); CdControl(CdlReadN, 0, (uint8_t*)_cd_read_result); diff --git a/libpsn00b/psxcd/psxcd_asm.s b/libpsn00b/psxcd/psxcd_asm.s index 906ab32..c0a5312 100644 --- a/libpsn00b/psxcd/psxcd_asm.s +++ b/libpsn00b/psxcd/psxcd_asm.s @@ -413,17 +413,11 @@ CdAutoPauseCallback: lw $v0, 0($v1) la $v1, _cd_callback_int4 - - jal EnterCriticalSection - nop - + lw $a0, 4($sp) nop sw $a0, 0($v1) - - jal ExitCriticalSection - nop - + lw $ra, 0($sp) addiu $sp, 8 jr $ra @@ -443,17 +437,11 @@ CdReadyCallback: la $v1, _cd_callback_int1_data sw $v0, 8($sp) - - jal EnterCriticalSection - nop - + lw $a0, 4($sp) nop sw $a0, 0($v1) - - jal ExitCriticalSection - nop - + lw $ra, 0($sp) lw $v0, 8($sp) jr $ra @@ -472,17 +460,11 @@ CdSyncCallback: la $v1, _cd_sync_cb sw $v0, 8($sp) - - jal EnterCriticalSection - nop - + lw $a0, 4($sp) nop sw $a0, 0($v1) - - jal ExitCriticalSection - nop - + lw $ra, 0($sp) lw $v0, 8($sp) jr $ra @@ -490,13 +472,7 @@ CdSyncCallback: .section .data - -.global psxcd_credits -.type psxcd_credits, @object -psxgpu_credits: - .ascii "psxcd library programs by Lameguy64\n" - .asciiz "2020 PSn00bSDK Project / Meido-Tek Productions\n" - + .comm _cd_last_cmd, 1, 1 .comm _cd_last_mode, 1, 1 .comm _cd_ack_wait, 1, 1 diff --git a/libpsn00b/psxetc/dl.c b/libpsn00b/psxetc/dl.c index 6d37605..b85a7df 100644 --- a/libpsn00b/psxetc/dl.c +++ b/libpsn00b/psxetc/dl.c @@ -30,13 +30,11 @@ #include <elf.h> #include <dlfcn.h> #include <string.h> +#include <psxetc.h> #include <psxapi.h> /* Compile options */ -// Uncomment before building to enable debug logging (via printf()). -//#define DEBUG - // Comment before building to disable functions that rely on BIOS file APIs, // i.e. DL_LoadSymbolMapFromFile() and DL_LoadDLLFromFile(). // FIXME: those seem to be broken currently, and shouldn't be used anyway @@ -69,12 +67,6 @@ void *(*_dl_resolve_callback)(DLL *, const char *) = 0; /* Private utilities */ -#ifdef DEBUG -#define _LOG(...) printf(__VA_ARGS__) -#else -#define _LOG(...) -#endif - #define _ERROR(code, ret) { \ _error_code = code; \ return ret; \ @@ -95,7 +87,7 @@ void *_dl_resolve_helper(DLL *dll, uint32_t index) { address = DL_GetSymbolByName(_name); if (!address) { - _LOG("psxetc: FATAL! Can't resolve %s, locking up\n", _name); + _sdk_log("psxetc: FATAL! can't resolve %s, locking up\n", _name); while (1) __asm__ volatile("nop"); } @@ -136,7 +128,7 @@ static uint32_t _elf_hash(const char *str) { static uint8_t *_dl_load_file(const char *filename, size_t *size_output) { int32_t fd = open(filename, 1); if (fd < 0) { - _LOG("psxetc: Can't open %s, error = %d\n", filename, fd); + _sdk_log("psxetc: can't open %s, error = %d\n", filename, fd); _ERROR(RTLD_E_FILE_OPEN, 0); } @@ -147,11 +139,11 @@ static uint8_t *_dl_load_file(const char *filename, size_t *size_output) { uint8_t *buffer = malloc(size); if (!buffer) { - _LOG("psxetc: Unable to allocate %d bytes for %s\n", size, filename); + _sdk_log("psxetc: unable to allocate %d bytes for %s\n", size, filename); _ERROR(RTLD_E_FILE_ALLOC, 0); } - _LOG("psxetc: Loading %s (%d bytes)..", filename, size); + //_sdk_log("psxetc: loading %s (%d bytes)..", filename, size); for (uint32_t offset = 0; offset < size; ) { int32_t length = read(fd, &(buffer[offset]), 0x800); @@ -160,16 +152,16 @@ static uint8_t *_dl_load_file(const char *filename, size_t *size_output) { close(fd); free(buffer); - _LOG("failed, error = %d\n", length); + _sdk_log("failed, error = %d\n", length); _ERROR(RTLD_E_FILE_READ, 0); } - _LOG("."); + //_sdk_log("."); offset += length; } close(fd); - _LOG(" done\n"); + _sdk_log(" done\n"); if (size_output) *size_output = size; @@ -195,8 +187,8 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size) { // in order to minimize hash table size _symbol_map.nbucket = entries; _symbol_map.nchain = entries; - _LOG( - "psxetc: Allocating nbucket = %d, nchain = %d\n", + _sdk_log( + "psxetc: allocating nbucket = %d, nchain = %d\n", _symbol_map.nbucket, entries ); @@ -208,7 +200,7 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size) { _symbol_map.chain = malloc(sizeof(uint32_t) * entries); if (!_symbol_map.entries || !_symbol_map.bucket || !_symbol_map.chain) { - _LOG("psxetc: Unable to allocate symbol map table\n"); + _sdk_log("psxetc: unable to allocate symbol map table\n"); _ERROR(RTLD_E_MAP_ALLOC, -1); } @@ -251,13 +243,10 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size) { (_type == 'D') || // .data (_type == 'B') // .bss )) { - _LOG( - "psxetc: Map sym: %08x,%08x [%c %s]\n", - address, - _size, - _type, - name - ); + //_sdk_log( + //"psxetc: map sym: %08x,%08x [%c %s]\n", + //address, _size, _type, name + //); MapEntry *entry = &(_symbol_map.entries[index]); entry->hash = hash; @@ -280,7 +269,7 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size) { pos++; } - _LOG("psxetc: Parsed %d symbols\n", entries); + _sdk_log("psxetc: parsed %d symbols\n", entries); if (!entries) _ERROR(RTLD_E_NO_SYMBOLS, -1); @@ -313,7 +302,7 @@ void DL_UnloadSymbolMap(void) { void *DL_GetSymbolByName(const char *name) { if (!_symbol_map.entries) { - _LOG("psxetc: Attempted lookup with no map loaded\n"); + _sdk_log("psxetc: attempted lookup with no map loaded\n"); _ERROR(RTLD_E_NO_MAP, 0); } @@ -325,10 +314,9 @@ void *DL_GetSymbolByName(const char *name) { // calculated. for (uint32_t i = _symbol_map.bucket[hash_mod]; i != 0xffffffff;) { if (i >= _symbol_map.nchain) { - _LOG( - "psxetc: GetSymbolByName() index out of bounds (i = %d, n = %d)\n", - i, - _symbol_map.nchain + _sdk_log( + "psxetc: GetSymbolByName() index out of bounds (%d >= %d)\n", + i, _symbol_map.nchain ); _ERROR(RTLD_E_HASH_LOOKUP, 0); } @@ -336,14 +324,14 @@ void *DL_GetSymbolByName(const char *name) { MapEntry *entry = &(_symbol_map.entries[i]); if (hash == entry->hash) { - _LOG("psxetc: Map lookup [%s = %08x]\n", name, entry->ptr); + //_sdk_log("psxetc: map lookup [%s = %08x]\n", name, entry->ptr); return entry->ptr; } i = _symbol_map.chain[i]; } - _LOG("psxetc: Map lookup [%s not found]\n", name); + _sdk_log("psxetc: map lookup [%s not found]\n", name); _ERROR(RTLD_E_MAP_SYMBOL, 0); } @@ -359,14 +347,14 @@ DLL *DL_CreateDLL(void *ptr, size_t size, DL_ResolveMode mode) { DLL *dll = malloc(sizeof(DLL)); if (!dll) { - _LOG("psxetc: Unable to allocate DLL struct\n"); + _sdk_log("psxetc: unable to allocate DLL struct\n"); _ERROR(RTLD_E_DLL_ALLOC, 0); } dll->ptr = ptr; dll->malloc_ptr = (mode & RTLD_FREE_ON_DESTROY) ? ptr : 0; dll->size = size; - _LOG("psxetc: Initializing DLL at %08x\n", ptr); + _sdk_log("psxetc: initializing DLL at %08x\n", ptr); // Interpret the key-value pairs in the .dynamic section to obtain info // about all the other sections. The pairs are null-terminated, which makes @@ -375,128 +363,128 @@ DLL *DL_CreateDLL(void *ptr, size_t size, DL_ResolveMode mode) { uint32_t first_got_sym = 0; for (Elf32_Dyn *dyn = (Elf32_Dyn *) ptr; dyn->d_tag; dyn++) { - _LOG("psxetc: .dynamic %08x=%08x ", dyn->d_tag, dyn->d_un.d_val); + //_sdk_log("psxetc: .dynamic %08x=%08x ", dyn->d_tag, dyn->d_un.d_val); switch (dyn->d_tag) { // Offset of .got section case DT_PLTGOT: - _LOG("[PLTGOT]\n"); + //_sdk_log("[PLTGOT]\n"); dll->got = (void *) (ptr + dyn->d_un.d_val); break; // Offset of .hash section case DT_HASH: - _LOG("[HASH]\n"); + //_sdk_log("[HASH]\n"); dll->hash = (void *) (ptr + dyn->d_un.d_val); break; // Offset of .dynstr (NOT .strtab) section case DT_STRTAB: - _LOG("[STRTAB]\n"); + //_sdk_log("[STRTAB]\n"); dll->strtab = (void *) (ptr + dyn->d_un.d_val); break; // Offset of .dynsym (NOT .symtab) section case DT_SYMTAB: - _LOG("[SYMTAB]\n"); + //_sdk_log("[SYMTAB]\n"); dll->symtab = (void *) (ptr + dyn->d_un.d_val); break; // Length of .dynstr section //case DT_STRSZ: - //_LOG("[STRSZ]\n"); + //_sdk_log("[STRSZ]\n"); //break; // Length of each .dynsym entry case DT_SYMENT: - _LOG("[SYMENT]\n"); + //_sdk_log("[SYMENT]\n"); // Only 16-byte symbol table entries are supported. if (dyn->d_un.d_val != sizeof(Elf32_Sym)) { free(dll); - _LOG("psxetc: Invalid symtab entry size %d\n", dyn->d_un.d_val); + _sdk_log("psxetc: invalid DLL symtab entry size %d\n", dyn->d_un.d_val); _ERROR(RTLD_E_DLL_FORMAT, 0); } break; // MIPS ABI (?) version case DT_MIPS_RLD_VERSION: - _LOG("[MIPS_RLD_VERSION]\n"); + //_sdk_log("[MIPS_RLD_VERSION]\n"); // Versions other than 1 are unsupported (do they even exist?). if (dyn->d_un.d_val != 1) { free(dll); - _LOG("psxetc: Invalid DLL version %d\n", dyn->d_un.d_val); + _sdk_log("psxetc: invalid DLL version %d\n", dyn->d_un.d_val); _ERROR(RTLD_E_DLL_FORMAT, 0); } break; // DLL/ABI flags case DT_MIPS_FLAGS: - _LOG("[MIPS_FLAGS]\n"); + //_sdk_log("[MIPS_FLAGS]\n"); // Shortcut pointers (whatever they are) are not supported. if (dyn->d_un.d_val & RHF_QUICKSTART) { free(dll); - _LOG("psxetc: Invalid flags\n"); + _sdk_log("psxetc: invalid DLL flags\n"); _ERROR(RTLD_E_DLL_FORMAT, 0); } break; // Number of local (not to resolve) GOT entries case DT_MIPS_LOCAL_GOTNO: - _LOG("[MIPS_LOCAL_GOTNO]\n"); + //_sdk_log("[MIPS_LOCAL_GOTNO]\n"); local_got_len = dyn->d_un.d_val; break; // Base address DLL was compiled for case DT_MIPS_BASE_ADDRESS: - _LOG("[MIPS_BASE_ADDRESS]\n"); + //_sdk_log("[MIPS_BASE_ADDRESS]\n"); // Base addresses other than zero are not supported. It would // be easy enough to support them, but why? if (dyn->d_un.d_val) { free(dll); - _LOG("psxetc: Invalid base address %08x\n", dyn->d_un.d_val); + _sdk_log("psxetc: invalid DLL base address %08x\n", dyn->d_un.d_val); _ERROR(RTLD_E_DLL_FORMAT, 0); } break; // Number of symbol table entries case DT_MIPS_SYMTABNO: - _LOG("[MIPS_SYMTABNO]\n"); + //_sdk_log("[MIPS_SYMTABNO]\n"); dll->symbol_count = dyn->d_un.d_val; break; // Index of first unresolved symbol table entry //case DT_MIPS_UNREFEXTNO: - //_LOG("[MIPS_UNREFEXTNO]\n"); + //_sdk_log("[MIPS_UNREFEXTNO]\n"); //break; // Index of first symbol table entry which has a matching GOT entry case DT_MIPS_GOTSYM: - _LOG("[MIPS_GOTSYM]\n"); + //_sdk_log("[MIPS_GOTSYM]\n"); first_got_sym = dyn->d_un.d_val; break; // Number of pages the GOT is split into (does not apply to PS1) //case DT_MIPS_HIPAGENO: - //_LOG("[MIPS_HIPAGENO]\n"); + //_sdk_log("[MIPS_HIPAGENO]\n"); //break; - default: - _LOG("[ignored]\n"); + //default: + //_sdk_log("[ignored]\n"); } } @@ -508,10 +496,9 @@ DLL *DL_CreateDLL(void *ptr, size_t size, DL_ResolveMode mode) { ((uint32_t) ptr + size - (uint32_t) dll->got) / sizeof(uint32_t) - 2; dll->got_length = local_got_len + (dll->symbol_count - first_got_sym) - 2; - _LOG( + _sdk_log( "psxetc: %d symbols, %d GOT entries\n", - dll->symbol_count, - dll->got_length + dll->symbol_count, dll->got_length ); // Relocate the DLL by adding its base address to all pointers in the GOT @@ -538,12 +525,10 @@ DLL *DL_CreateDLL(void *ptr, size_t size, DL_ResolveMode mode) { continue; sym->st_value += (uint32_t) ptr; - _LOG( - "psxetc: DLL sym: %08x,%08x [%s]\n", - sym->st_value, - sym->st_size, - _name - ); + //_sdk_log( + //"psxetc: DLL sym: %08x,%08x [%s]\n", + //sym->st_value, sym->st_size, _name + //); // If RTLD_NOW was passed, resolve GOT entries ahead of time by // cross-referencing them with the symbol table. @@ -651,7 +636,7 @@ void *DL_GetDLLSymbol(const DLL *dll, const char *name) { // provided. for (uint32_t i = bucket[hash_mod]; i != 0xffffffff;) { if (i >= nchain) { - _LOG("psxetc: DL_GetDLLSymbol() index out of bounds (i = %d, n = %d)\n", i, nchain); + _sdk_log("psxetc: DL_GetDLLSymbol() index out of bounds (%d >= %d)\n", i, nchain); _ERROR(RTLD_E_HASH_LOOKUP, 0); } @@ -659,14 +644,14 @@ void *DL_GetDLLSymbol(const DLL *dll, const char *name) { const char *_name = &(dll->strtab[sym->st_name]); if (!strcmp(name, _name)) { - _LOG("psxetc: DLL lookup [%s = %08x]\n", name, sym->st_value); + //_sdk_log("psxetc: DLL lookup [%s = %08x]\n", name, sym->st_value); return sym->st_value; } i = chain[i]; } - _LOG("psxetc: DLL lookup [%s not found]\n", name); + _sdk_log("psxetc: DLL lookup [%s not found]\n", name); _ERROR(RTLD_E_DLL_SYMBOL, 0); } diff --git a/libpsn00b/psxetc/interrupts.c b/libpsn00b/psxetc/interrupts.c index 859209a..cc9d12c 100644 --- a/libpsn00b/psxetc/interrupts.c +++ b/libpsn00b/psxetc/interrupts.c @@ -53,44 +53,50 @@ static const struct JMP_BUF _isr_jmp_buf = { /* Internal IRQ and DMA handlers */ static void _global_isr(void) { - uint16_t stat = IRQ_STAT, mask = IRQ_MASK; + uint16_t stat = IRQ_STAT & IRQ_MASK; - // Clear all IRQ flags in one shot. This is not the "proper" way to do it - // but it's much faster than clearing one flag at a time. - IRQ_STAT = ~mask; + for (; stat; stat = IRQ_STAT & IRQ_MASK) { + //for (int i = 0; i < NUM_IRQ_CHANNELS; i++) { + for (int i = 0, mask = 1; stat; i++, stat >>= 1, mask <<= 1) { + if (!(stat & 1)) + continue; - //for (int i = 0; i < NUM_IRQ_CHANNELS; i++) { - for (int i = 0; stat; i++, stat >>= 1) { - if (!(stat & 1)) - continue; + // Acknowledge the current IRQ. Note that clearing all IRQ flags in one + // shot would result in hard-to-debug race conditions (been there, done + // that). + IRQ_STAT = (uint16_t) (mask ^ 0xffff); - if (_irq_handlers[i]) - _irq_handlers[i](); + if (_irq_handlers[i]) + _irq_handlers[i](); + } } ReturnFromException(); } static void _global_dma_handler(void) { - uint32_t stat = DMA_DICR; + uint32_t dicr = DMA_DICR; + uint32_t stat = (dicr >> 24) & 0x7f; + + for (; stat; dicr = DMA_DICR, stat = (dicr >> 24) & 0x7f) { + uint32_t base = dicr & 0x00ffffff; - // Clear all DMA IRQ flags in one shot (note that flags are cleared by - // writing 1 to them rather than 0). - stat &= 0x7fff0000; - DMA_DICR = stat; - stat >>= 24; + //for (int i = 0; i < NUM_DMA_CHANNELS; i++) { + for (int i = 0, mask = (1 << 24); stat; i++, stat >>= 1, mask <<= 1) { + if (!(stat & 1)) + continue; - //for (int i = 0; i < NUM_DMA_CHANNELS; i++) { - for (int i = 0; stat; i++, stat >>= 1) { - if (!(stat & 1)) - continue; + // Acknowledge the current DMA channel's IRQ. For whatever reason + // DMA IRQ flags are cleared by writing 1 to them rather than 0. + DMA_DICR = base | mask; - if (_dma_handlers[i]) - _dma_handlers[i](); + if (_dma_handlers[i]) + _dma_handlers[i](); + } } } -/* Callback registration API */ +/* IRQ and DMA handler API */ void *InterruptCallback(int irq, void (*func)(void)) { if ((irq < 0) || (irq >= NUM_IRQ_CHANNELS)) @@ -127,12 +133,12 @@ void *DMACallback(int dma, void (*func)(void)) { // the callback is being registered or removed. The main DMA IRQ dispatcher // is also registered if this is the first DMA callback being configured, // or disabled if it's the last one being removed. - if (func) { + if (!old_callback && func) { DMA_DICR |= (0x10000 << dma) | (1 << 23); if (!(_num_dma_handlers++)) InterruptCallback(3, &_global_dma_handler); - } else { + } else if (old_callback && !func) { if (--_num_dma_handlers) { DMA_DICR &= ~(0x10000 << dma); } else { @@ -158,7 +164,7 @@ int ResetCallback(void) { return -1; EnterCriticalSection(); - _saved_irq_mask = 1 << 3; // Enable DMA IRQ by default + _saved_irq_mask = 0; _saved_dma_dpcr = 0x03333333; _saved_dma_dicr = 0; @@ -167,10 +173,6 @@ int ResetCallback(void) { for (int i = 0; i < NUM_DMA_CHANNELS; i++) _dma_handlers[i] = (void *) 0; - // Set up the DMA IRQ handler. This handler shall *not* be overridden using - // InterruptCallback(). - _irq_handlers[3] = &_global_dma_handler; - _96_remove(); RestartCallback(); return 0; diff --git a/libpsn00b/psxetc/logging.c b/libpsn00b/psxetc/logging.c new file mode 100644 index 0000000..5199190 --- /dev/null +++ b/libpsn00b/psxetc/logging.c @@ -0,0 +1,50 @@ +/* + * PSn00bSDK internal debug logger + * (C) 2022 spicyjpeg - MPL licensed + * + * This file provides the (admittedly minimal) logging system used by all + * PSn00bSDK libraries. Log messages and warnings are issued using the + * _sdk_log() macro and collected into a buffer, whose contents can be flushed + * by calling _sdk_dump_log() (by default this is done by VSync()). Logging is + * only enabled in debug builds of libpsn00b. + */ + +#include <stddef.h> +#include <stdarg.h> +#include <stdio.h> +#include <psxapi.h> +#include <psxetc.h> + +#define LOG_BUFFER_SIZE 256 + +#ifndef NDEBUG + +/* Internal globals */ + +static char _log_buffer[LOG_BUFFER_SIZE]; +static size_t _log_buffer_length = 0; + +/* Internal logging API */ + +void _sdk_log_inner(const char *fmt, ...) { + va_list ap; + + va_start(ap, fmt); + _log_buffer_length += vsnprintf( + &_log_buffer[_log_buffer_length], + LOG_BUFFER_SIZE - _log_buffer_length, + fmt, + ap + ); + va_end(ap); +} + +void _sdk_dump_log_inner(void) { + if (!_log_buffer_length) + return; + + write(1, _log_buffer, _log_buffer_length); + _log_buffer_length = 0; +} + +#endif diff --git a/libpsn00b/psxgpu/common.c b/libpsn00b/psxgpu/common.c index cef1508..a262472 100644 --- a/libpsn00b/psxgpu/common.c +++ b/libpsn00b/psxgpu/common.c @@ -4,7 +4,6 @@ */ #include <stdint.h> -#include <stdio.h> #include <psxetc.h> #include <psxapi.h> #include <psxgpu.h> @@ -16,6 +15,13 @@ static void _default_vsync_halt(void); +/* Private types */ + +typedef struct { + void (*func)(uint32_t, uint32_t, uint32_t); + uint32_t arg1, arg2, arg3; +} QueueEntry; + /* Internal globals */ GPU_VideoMode _gpu_video_mode; @@ -24,12 +30,12 @@ static void (*_vsync_halt_func)(void) = &_default_vsync_halt; static void (*_vsync_callback)(void) = (void *) 0; static void (*_drawsync_callback)(void) = (void *) 0; -static const uint32_t *volatile _draw_queue[QUEUE_LENGTH]; -static volatile uint8_t _queue_head, _queue_tail, _queue_length; -static volatile uint32_t _vblank_counter; -static volatile uint16_t _last_hblank; +static volatile QueueEntry _draw_queue[QUEUE_LENGTH]; +static volatile uint8_t _queue_head, _queue_tail, _queue_length; +static volatile uint32_t _vblank_counter; +static volatile uint16_t _last_hblank; -/* Interrupt handlers */ +/* Private interrupt handlers */ static void _vblank_handler(void) { _vblank_counter++; @@ -43,11 +49,11 @@ static void _gpu_dma_handler(void) { while (!(GPU_GP1 & (1 << 26))) __asm__ volatile(""); - if (_queue_length) { - DrawOTag2(_draw_queue[_queue_head++]); - - _queue_length--; + if (--_queue_length) { + volatile QueueEntry *entry = &_draw_queue[_queue_head++]; _queue_head %= QUEUE_LENGTH; + + entry->func(entry->arg1, entry->arg2, entry->arg3); } else { GPU_GP1 = 0x04000000; // Disable DMA request @@ -69,7 +75,7 @@ void ResetGraph(int mode) { _gpu_video_mode = (GPU_GP1 >> 20) & 1; ExitCriticalSection(); - printf("psxgpu: setup done, default mode is %s\n", _gpu_video_mode ? "PAL" : "NTSC"); + _sdk_log("psxgpu: setup done, default mode is %s\n", _gpu_video_mode ? "PAL" : "NTSC"); } if (mode == 3) { @@ -97,18 +103,18 @@ void ResetGraph(int mode) { _last_hblank = 0; } -/* Syncing API */ +/* VSync() API */ // TODO: add support for no$psx's "halt" register static void _default_vsync_halt(void) { int counter = _vblank_counter; - for (int i = VSYNC_TIMEOUT; i; i--) { if (counter != _vblank_counter) return; } - printf("psxgpu: VSync() timeout\n"); + _sdk_log("psxgpu: VSync() timeout\n"); + _sdk_dump_log(); ChangeClearPAD(0); ChangeClearRCnt(3, 0); } @@ -124,6 +130,7 @@ int VSync(int mode) { // Wait for at least one vertical blank event to occur. do { + _sdk_dump_log(); _vsync_halt_func(); // If interlaced mode is enabled, wait until the GPU starts displaying @@ -138,27 +145,6 @@ int VSync(int mode) { return delta; } -int DrawSync(int mode) { - if (mode) - return (DMA_BCR(2) >> 16); - - // Wait for the queue to become empty. - // TODO: add a timeout - while (_queue_length) - __asm__ volatile(""); - - // Wait for any DMA transfer to finish if DMA is enabled. - if (GPU_GP1 & (3 << 29)) { - while (!(GPU_GP1 & (1 << 28)) || (DMA_CHCR(2) & (1 << 24))) - __asm__ volatile(""); - } - - while (!(GPU_GP1 & (1 << 26))) - __asm__ volatile(""); - - return 0; -} - void *VSyncHaltFunction(void (*func)(void)) { void *old_callback = _vsync_halt_func; _vsync_halt_func = func; @@ -176,6 +162,81 @@ void *VSyncCallback(void (*func)(void)) { return old_callback; } +/* Command queue API */ + +// This function is normally only used internally, but it is exposed for +// advanced use cases. +int EnqueueDrawOp( + void (*func)(uint32_t, uint32_t, uint32_t), + uint32_t arg1, + uint32_t arg2, + uint32_t arg3 +) { + // If GPU DMA is currently busy, append the command to the queue instead of + // executing it immediately. Note that interrupts must be disabled *prior* + // to checking if DMA is busy; disabling them afterwards would create a + // race condition where the DMA transfer could end while interrupts are + // being disabled. Interrupts are disabled through the IRQ_MASK register + // rather than by calling EnterCriticalSection() for performance reasons. + uint16_t mask = IRQ_MASK; + IRQ_MASK = 0; + + if (_queue_length) { + if (_queue_length >= QUEUE_LENGTH) { + IRQ_MASK = mask; + _sdk_log("psxgpu: draw queue overflow, dropping commands\n"); + return -1; + } + + int length = _queue_length; + _queue_length = length + 1; + + volatile QueueEntry *entry = &_draw_queue[_queue_tail++]; + _queue_tail %= QUEUE_LENGTH; + + entry->func = func; + entry->arg1 = arg1; + entry->arg2 = arg2; + entry->arg3 = arg3; + + IRQ_MASK = mask; + return length; + } + + _queue_length = 1; + + IRQ_MASK = mask; + func(arg1, arg2, arg3); + return 0; +} + +int DrawSync(int mode) { + if (mode) + return _queue_length; + + // Wait for the queue to become empty. + for (int i = VSYNC_TIMEOUT; i; i--) { + if (!_queue_length) + break; + } + + if (!_queue_length) { + // Wait for any DMA transfer to finish if DMA is enabled. + if (GPU_GP1 & (3 << 29)) { + while (!(GPU_GP1 & (1 << 28)) || (DMA_CHCR(2) & (1 << 24))) + __asm__ volatile(""); + } + + while (!(GPU_GP1 & (1 << 26))) + __asm__ volatile(""); + } else { + _sdk_log("psxgpu: DrawSync() timeout\n"); + _sdk_dump_log(); + } + + return _queue_length; +} + void *DrawSyncCallback(void (*func)(void)) { EnterCriticalSection(); @@ -208,45 +269,8 @@ void ClearOTag(uint32_t *ot, size_t length) { ot[length - 1] = 0x00ffffff; } -void DrawOTag(const uint32_t *ot) { - // If GPU DMA is currently busy, append the OT to the queue instead of - // drawing it immediately. Note that interrupts must be disabled *prior* to - // checking if DMA is busy; disabling them afterwards would create a race - // condition where the DMA transfer could end while interrupts are being - // disabled. Interrupts are disabled through the IRQ_MASK register rather - // than by calling EnterCriticalSection() for performance reasons. - uint16_t mask = IRQ_MASK; - IRQ_MASK = 0; - - if (DMA_CHCR(2) & (1 << 24)) { - if (_queue_length < QUEUE_LENGTH) { - _draw_queue[_queue_tail++] = ot; - - _queue_length++; - _queue_tail %= QUEUE_LENGTH; - - IRQ_MASK = mask; - return; - } - - IRQ_MASK = mask; - printf("psxgpu: DrawOTag() failed, draw queue full\n"); - return; - } - - IRQ_MASK = mask; - DrawOTag2(ot); -} - -void DrawOTag2(const uint32_t *ot) { - GPU_GP1 = 0x04000002; - - while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(2) & (1 << 24))) - __asm__ volatile(""); - - DMA_MADR(2) = (uint32_t) ot; - DMA_BCR(2) = 0; - DMA_CHCR(2) = 0x01000401; +void AddPrim(uint32_t *ot, const void *pri) { + addPrim(ot, pri); } void DrawPrim(const uint32_t *pri) { @@ -267,8 +291,19 @@ void DrawPrim(const uint32_t *pri) { DMA_CHCR(2) = 0x01000201; } -void AddPrim(uint32_t *ot, const void *pri) { - addPrim(ot, pri); +int DrawOTag(const uint32_t *ot) { + return EnqueueDrawOp((void *) &DrawOTag2, (uint32_t) ot, 0, 0); +} + +void DrawOTag2(const uint32_t *ot) { + GPU_GP1 = 0x04000002; + + while (!(GPU_GP1 & (1 << 26)) || (DMA_CHCR(2) & (1 << 24))) + __asm__ volatile(""); + + DMA_MADR(2) = (uint32_t) ot; + DMA_BCR(2) = 0; + DMA_CHCR(2) = 0x01000401; } /* Misc. functions */ diff --git a/libpsn00b/psxgpu/dbugfont.png b/libpsn00b/psxgpu/dbugfont.png Binary files differnew file mode 100644 index 0000000..ed84268 --- /dev/null +++ b/libpsn00b/psxgpu/dbugfont.png diff --git a/libpsn00b/psxgpu/dbugfont.tim b/libpsn00b/psxgpu/dbugfont.tim Binary files differindex 4e6cce2..1edd4af 100644 --- a/libpsn00b/psxgpu/dbugfont.tim +++ b/libpsn00b/psxgpu/dbugfont.tim diff --git a/libpsn00b/psxgpu/env.c b/libpsn00b/psxgpu/env.c index 5642ad4..f513727 100644 --- a/libpsn00b/psxgpu/env.c +++ b/libpsn00b/psxgpu/env.c @@ -37,7 +37,7 @@ DRAWENV *SetDefDrawEnv(DRAWENV *env, int x, int y, int w, int h) { return env; } -void DrawOTagEnv(const uint32_t *ot, DRAWENV *env) { +int DrawOTagEnv(const uint32_t *ot, DRAWENV *env) { DR_ENV *prim = &(env->dr_env); // All commands are grouped into a single display list packet for @@ -85,7 +85,7 @@ void DrawOTagEnv(const uint32_t *ot, DRAWENV *env) { //while (!(GPU_GP1 & (1 << 26))) //__asm__ volatile(""); - DrawOTag((const uint32_t *) prim); + return EnqueueDrawOp((void *) &DrawOTag2, (uint32_t) prim, 0, 0); } void PutDrawEnv(DRAWENV *env) { diff --git a/libpsn00b/psxgpu/font.c b/libpsn00b/psxgpu/font.c index 2d4105f..b1c3c7a 100644 --- a/libpsn00b/psxgpu/font.c +++ b/libpsn00b/psxgpu/font.c @@ -27,16 +27,16 @@ void FntLoad(int x, int y) { RECT pos; TIM_IMAGE tim; - GetTimInfo( (const uint32_t *) _gpu_debug_font, &tim ); + GetTimInfo((const uint32_t *) _gpu_debug_font, &tim); // Load font image pos = *tim.prect; pos.x = x; pos.y = y; - _font_tpage = getTPage( 0, 0, pos.x, pos.y ); + _font_tpage = getTPage(0, 0, pos.x, pos.y); - LoadImage( &pos, tim.paddr ); + LoadImage(&pos, tim.paddr); DrawSync(0); // Load font clut @@ -44,9 +44,9 @@ void FntLoad(int x, int y) { pos.x = x; pos.y = y+tim.prect->h; - _font_clut = getClut( pos.x, pos.y ); + _font_clut = getClut(pos.x, pos.y); - LoadImage( &pos, tim.caddr ); + LoadImage(&pos, tim.caddr); DrawSync(0); // Clear previously opened text streams @@ -193,10 +193,11 @@ char *FntFlush(int id) { if( i > 0 ) { i--; - setSprt8( sprt ); - setRGB0( sprt, 128, 128, 128 ); - setXY0( sprt, sx, sy ); - setUV0( sprt, (i%16)<<3, (i>>4)<<3 ); + setSprt8(sprt); + setShadeTex(sprt, 1); + setSemiTrans(sprt, 1); + setXY0(sprt, sx, sy); + setUV0(sprt, (i % 16) * 8, (i / 16) * 8); sprt->clut = _font_clut; setaddr(opri, sprt); opri = (char*)sprt; @@ -237,12 +238,13 @@ char *FntSort(uint32_t *ot, char *pri, int x, int y, const char *text) { if( i > 0 ) { i--; - setSprt8( sprt ); - setRGB0( sprt, 128, 128, 128 ); - setXY0( sprt, x, y ); - setUV0( sprt, (i%16)<<3, (i>>4)<<3 ); + setSprt8(sprt); + setShadeTex(sprt, 1); + setSemiTrans(sprt, 1); + setXY0(sprt, x, y); + setUV0(sprt, (i % 16) * 8, (i / 16) * 8); sprt->clut = _font_clut; - addPrim( ot, sprt ); + addPrim(ot, sprt); sprt++; } @@ -256,9 +258,9 @@ char *FntSort(uint32_t *ot, char *pri, int x, int y, const char *text) { tpage = (DR_TPAGE*)pri; tpage->code[0] = _font_tpage; - setlen( tpage, 1 ); - setcode( tpage, 0xe1 ); - addPrim( ot, pri ); + setlen(tpage, 1); + setcode(tpage, 0xe1); + addPrim(ot, pri); pri += sizeof(DR_TPAGE); return pri; diff --git a/libpsn00b/psxgpu/image.c b/libpsn00b/psxgpu/image.c index da51e7d..968dde5 100644 --- a/libpsn00b/psxgpu/image.c +++ b/libpsn00b/psxgpu/image.c @@ -4,42 +4,36 @@ */ #include <stdint.h> -#include <stdio.h> +#include <psxetc.h> #include <psxgpu.h> #include <hwregs_c.h> #define DMA_CHUNK_LENGTH 8 -/* VRAM transfer API */ +/* Private utilities */ -static void _load_store_image( - uint32_t command, - int mode, - const RECT *rect, - uint32_t *data -) { +static void _dma_transfer(const RECT *rect, uint32_t *data, int write) { size_t length = rect->w * rect->h; if (length % 2) - printf("psxgpu: can't transfer an odd number of pixels\n"); + _sdk_log("psxgpu: can't transfer an odd number of pixels\n"); length /= 2; if ((length >= DMA_CHUNK_LENGTH) && (length % DMA_CHUNK_LENGTH)) { - printf("psxgpu: transfer data length (%d) is not a multiple of %d, rounding\n", length, DMA_CHUNK_LENGTH); + _sdk_log("psxgpu: transfer data length (%d) is not a multiple of %d, rounding\n", length, DMA_CHUNK_LENGTH); length += DMA_CHUNK_LENGTH - 1; } - DrawSync(0); GPU_GP1 = 0x04000000; // Disable DMA request GPU_GP0 = 0x01000000; // Flush cache - GPU_GP0 = command; + GPU_GP0 = write ? 0xa0000000 : 0xc0000000; //GPU_GP0 = rect->x | (rect->y << 16); GPU_GP0 = *((const uint32_t *) &(rect->x)); //GPU_GP0 = rect->w | (rect->h << 16); GPU_GP0 = *((const uint32_t *) &(rect->w)); // Enable DMA request, route to GP0 (2) or from GPU_READ (3) - GPU_GP1 = 0x04000000 | mode; + GPU_GP1 = 0x04000002 | (write ^ 1); DMA_MADR(2) = (uint32_t) data; if (length < DMA_CHUNK_LENGTH) @@ -47,15 +41,42 @@ static void _load_store_image( else DMA_BCR(2) = DMA_CHUNK_LENGTH | ((length / DMA_CHUNK_LENGTH) << 16); - DMA_CHCR(2) = 0x01000200 | ((mode & 1) ^ 1); + DMA_CHCR(2) = 0x01000200 | write; +} + +/* VRAM transfer API */ + +int LoadImage(const RECT *rect, const uint32_t *data) { + return EnqueueDrawOp( + (void *) &_dma_transfer, (uint32_t) rect, (uint32_t) data, 1 + ); +} + +int StoreImage(const RECT *rect, uint32_t *data) { + return EnqueueDrawOp( + (void *) &_dma_transfer, (uint32_t) rect, (uint32_t) data, 0 + ); } -void LoadImage(const RECT *rect, const uint32_t *data) { - _load_store_image(0xa0000000, 2, rect, (uint32_t *) data); +int MoveImage(const RECT *rect, int x, int y) { + return EnqueueDrawOp((void *) &MoveImage2, (uint32_t) rect, x, y); } -void StoreImage(const RECT *rect, uint32_t *data) { - _load_store_image(0xc0000000, 3, rect, data); +void LoadImage2(const RECT *rect, const uint32_t *data) { + _dma_transfer(rect, (uint32_t *) data, 1); +} + +void StoreImage2(const RECT *rect, uint32_t *data) { + _dma_transfer(rect, data, 0); +} + +void MoveImage2(const RECT *rect, int x, int y) { + GPU_GP0 = 0x80000000; + //GPU_GP0 = rect->x | (rect->y << 16); + GPU_GP0 = *((const uint32_t *) &(rect->x)); + GPU_GP0 = (x & 0xffff) | (y << 16); + //GPU_GP0 = rect->w | (rect->h << 16); + GPU_GP0 = *((const uint32_t *) &(rect->w)); } /* .TIM image parsers */ diff --git a/libpsn00b/psxpress/mdec.c b/libpsn00b/psxpress/mdec.c index 9c82d6b..d43436f 100644 --- a/libpsn00b/psxpress/mdec.c +++ b/libpsn00b/psxpress/mdec.c @@ -4,13 +4,13 @@ */ #include <stdint.h> -#include <stdio.h> +#include <psxetc.h> #include <psxapi.h> #include <psxpress.h> #include <hwregs_c.h> #define DMA_CHUNK_LENGTH 32 -#define MDEC_SYNC_TIMEOUT 0x1000000 +#define MDEC_SYNC_TIMEOUT 0x100000 /* Default IDCT matrix and quantization tables */ @@ -127,7 +127,7 @@ void DecDCTin(const uint32_t *data, int mode) { // the stream. void DecDCTinRaw(const uint32_t *data, size_t length) { if ((length >= DMA_CHUNK_LENGTH) && (length % DMA_CHUNK_LENGTH)) { - printf("psxmdec: transfer data length (%d) is not a multiple of %d, rounding\n", length, DMA_CHUNK_LENGTH); + _sdk_log("psxpress: transfer data length (%d) is not a multiple of %d, rounding\n", length, DMA_CHUNK_LENGTH); length += DMA_CHUNK_LENGTH - 1; } @@ -149,13 +149,19 @@ int DecDCTinSync(int mode) { return 0; } - printf("psxpress: DecDCTinSync() timeout\n"); + _sdk_log("psxpress: DecDCTinSync() timeout\n"); + _sdk_dump_log(); return -1; } void DecDCTout(uint32_t *data, size_t length) { DecDCToutSync(0); + if ((length >= DMA_CHUNK_LENGTH) && (length % DMA_CHUNK_LENGTH)) { + _sdk_log("psxpress: transfer data length (%d) is not a multiple of %d, rounding\n", length, DMA_CHUNK_LENGTH); + length += DMA_CHUNK_LENGTH - 1; + } + DMA_MADR(1) = (uint32_t) data; if (length < DMA_CHUNK_LENGTH) DMA_BCR(1) = 0x00010000 | length; @@ -174,6 +180,7 @@ int DecDCToutSync(int mode) { return 0; } - printf("psxpress: DecDCToutSync() timeout\n"); + _sdk_log("psxpress: DecDCToutSync() timeout\n"); + _sdk_dump_log(); return -1; } diff --git a/libpsn00b/psxpress/vlc.s b/libpsn00b/psxpress/vlc.s index fe51642..885a3f7 100644 --- a/libpsn00b/psxpress/vlc.s +++ b/libpsn00b/psxpress/vlc.s @@ -131,10 +131,12 @@ _vlc_skip_context_load: #nop .Lprocess_dc_v2_coefficient: # if (!coeff_index && !is_v3) - # The DC coefficient in version 2 frames is not compressed. - srl $v0, $t0, 22 # *output = (window >> (32 - 10)) | quant_scale - or $v0, $t3 - addiu $t7, 1 # coeff_index++ + # The DC coefficient in version 2 frames is not compressed. Value 0x1ff is + # used to signal the end of the bitstream. + srl $v0, $t0, 22 # prefix = (window >> (32 - 10)) + li $v1, 0x01ff + beq $v0, $v1, .Lstop_processing # if (prefix == 0x1ff) break + or $v0, $t3 # *output = prefix | quant_scale sll $t0, 10 # window <<= 10 addiu $t5, -10 # bit_offset -= 10 b .Lwrite_value diff --git a/libpsn00b/psxpress/vlc2.c b/libpsn00b/psxpress/vlc2.c index 73b54b2..9eb99bf 100644 --- a/libpsn00b/psxpress/vlc2.c +++ b/libpsn00b/psxpress/vlc2.c @@ -141,7 +141,10 @@ int __attribute__((optimize(3))) DecDCTvlcContinue2( // TODO: version 3 is currently not supported. return -1; } else { - value = _get_bits_unsigned(10); + value = _get_bits_unsigned(10); + if (value == 0x1ff) + break; + *output = value | quant_scale; _advance_window(10); } diff --git a/libpsn00b/psxspu/common.c b/libpsn00b/psxspu/common.c index 55a3dba..7d90858 100644 --- a/libpsn00b/psxspu/common.c +++ b/libpsn00b/psxspu/common.c @@ -4,7 +4,7 @@ */ #include <stdint.h> -#include <stdio.h> +#include <psxetc.h> #include <psxspu.h> #include <hwregs_c.h> @@ -17,7 +17,7 @@ static SPU_TransferMode _transfer_mode = SPU_TRANSFER_BY_DMA; static uint16_t _transfer_addr = WRITABLE_AREA_ADDR; -/* SPU initialization */ +/* Private utilities */ static void _wait_status(uint16_t mask, uint16_t value) { for (int i = STATUS_TIMEOUT; i; i--) { @@ -25,9 +25,38 @@ static void _wait_status(uint16_t mask, uint16_t value) { return; } - printf("psxspu: status register timeout (0x%04x)\n", SPU_STAT); + _sdk_log("psxspu: status register timeout (0x%04x)\n", SPU_STAT); } +static void _dma_transfer(uint32_t *data, size_t length, int write) { + if (length % 4) + _sdk_log("psxspu: can't transfer a number of bytes that isn't multiple of 4\n"); + + length /= 4; + if ((length >= DMA_CHUNK_LENGTH) && (length % DMA_CHUNK_LENGTH)) { + _sdk_log("psxspu: transfer data length (%d) is not a multiple of %d, rounding\n", length, DMA_CHUNK_LENGTH); + length += DMA_CHUNK_LENGTH - 1; + } + + SPU_CTRL &= 0xffcf; // Disable DMA request + _wait_status(0x0030, 0x0000); + + // Enable DMA request for writing (2) or reading (3) + SPU_ADDR = _transfer_addr; + SPU_CTRL |= write ? 0x0020 : 0x0030; + _wait_status(0x0400, 0x0000); + + DMA_MADR(4) = (uint32_t) data; + if (length < DMA_CHUNK_LENGTH) + DMA_BCR(4) = 0x00010000 | length; + else + DMA_BCR(4) = DMA_CHUNK_LENGTH | ((length / DMA_CHUNK_LENGTH) << 16); + + DMA_CHCR(4) = 0x01000200 | write; +} + +/* Public API */ + void SpuInit(void) { SPU_CTRL = 0x0000; // SPU disabled _wait_status(0x001f, 0x0000); @@ -75,41 +104,12 @@ void SpuInit(void) { SPU_KEY_ON = 0x00ffffff; SPU_MASTER_VOL_L = 0x3fff; SPU_MASTER_VOL_R = 0x3fff; - SPU_CD_VOL_L = 0x3fff; - SPU_CD_VOL_R = 0x3fff; -} - -/* SPU RAM transfer API */ - -static void _load_store_data(uint32_t *data, size_t length, int mode) { - if (length % 4) - printf("psxspu: can't transfer a number of bytes that isn't multiple of 4\n"); - - length /= 4; - if ((length >= DMA_CHUNK_LENGTH) && (length % DMA_CHUNK_LENGTH)) { - printf("psxspu: transfer data length (%d) is not a multiple of %d, rounding\n", length, DMA_CHUNK_LENGTH); - length += DMA_CHUNK_LENGTH - 1; - } - - SPU_CTRL &= 0xffcf; // Disable DMA request - _wait_status(0x0030, 0x0000); - - // Enable DMA request for writing (2) or reading (3) - SPU_ADDR = _transfer_addr; - SPU_CTRL |= mode << 4; - _wait_status(0x0400, 0x0000); - - DMA_MADR(4) = (uint32_t) data; - if (length < DMA_CHUNK_LENGTH) - DMA_BCR(4) = 0x00010000 | length; - else - DMA_BCR(4) = DMA_CHUNK_LENGTH | ((length / DMA_CHUNK_LENGTH) << 16); - - DMA_CHCR(4) = 0x01000200 | ((mode & 1) ^ 1); + SPU_CD_VOL_L = 0x7fff; + SPU_CD_VOL_R = 0x7fff; } void SpuRead(uint32_t *data, size_t size) { - _load_store_data(data, size, 3); + _dma_transfer(data, size, 0); } void SpuWrite(const uint32_t *data, size_t size) { @@ -132,7 +132,7 @@ void SpuWrite(const uint32_t *data, size_t size) { return; } - _load_store_data((uint32_t *) data, size, 2); + _dma_transfer((uint32_t *) data, size, 1); } SPU_TransferMode SpuSetTransferMode(SPU_TransferMode mode) { diff --git a/template/CMakeLists.txt b/template/CMakeLists.txt index 4aa648f..a3399db 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.20) +cmake_minimum_required(VERSION 3.21) project( PSn00bSDK-template @@ -11,7 +11,7 @@ project( HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk" ) -psn00bsdk_add_executable(template STATIC main.c) +psn00bsdk_add_executable(template GPREL main.c) psn00bsdk_add_cd_image( iso # Target name diff --git a/template/CMakePresets.json b/template/CMakePresets.json index d08b334..97d8428 100644 --- a/template/CMakePresets.json +++ b/template/CMakePresets.json @@ -1,22 +1,25 @@ { - "version": 2, + "version": 3, "cmakeMinimumRequired": { "major": 3, - "minor": 20, + "minor": 21, "patch": 0 }, "configurePresets": [ { - "name": "default", - "displayName": "Default configuration", - "description": "Use this preset to build the project using PSn00bSDK.", - "generator": "Ninja", - "binaryDir": "${sourceDir}/build", + "name": "default", + "displayName": "Default configuration", + "description": "Use this preset to build the project using PSn00bSDK.", + "generator": "Ninja", + "toolchainFile": "$env{PSN00BSDK_LIBS}/cmake/sdk.cmake", + "binaryDir": "${sourceDir}/build", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Debug", - "CMAKE_TOOLCHAIN_FILE": "$env{PSN00BSDK_LIBS}/cmake/sdk.cmake", - "PSN00BSDK_TC": "", - "PSN00BSDK_TARGET": "mipsel-none-elf" + "CMAKE_BUILD_TYPE": "Debug", + "PSN00BSDK_TC": "", + "PSN00BSDK_TARGET": "mipsel-none-elf" + }, + "warnings": { + "dev": false } } ] diff --git a/template/iso.xml b/template/iso.xml index 477c636..29fbd2d 100644 --- a/template/iso.xml +++ b/template/iso.xml @@ -11,6 +11,21 @@ cue_sheet="${CD_IMAGE_NAME}.cue" > <track type="data"> + <!-- + The "volume", "volume_set", "publisher", "data_preparer" and + "copyright" strings below can be freely modified. The ISO9660 + specification, however, imposes the following limitations: + + - "volume" and "volume_set" must be 32 characters or less, and can + only contain uppercase letters, digits and underscores. + - "publisher" and "data_preparer" can be up to 128 characters long + and can additionally contain spaces and some special characters. + - "copyright" should be a path to a file on the disc, even one that + does not exist (but in practice it can be set to anything). + + "system" and "application" must always be set to "PLAYSTATION" in + order for the disc to be recognized as valid. + --> <identifiers system ="PLAYSTATION" volume ="PSN00BSDK_TEMPLATE" @@ -21,14 +36,51 @@ copyright ="README.TXT;1" /> + <!-- + You may optionally include a license file using the <license> tag. + Some consoles, particularly Japanese or PAL models with a modchip, + require the disc to contain valid license data and will refuse to + boot if it is missing. License files are usually not required on + US consoles or when booting via softmods or cheat cartridges. + + License files are region-specific and are not distributed with + PSn00bSDK for obvious reasons, but can be dumped from an official + game using dumpsxiso or extracted from the Sony SDK. + --> + <!--<license file="${PROJECT_SOURCE_DIR}/license.dat" />--> + + <!-- + Files and directories can be added to the disc by placing <file> + and <dir> tags below. All file names are case-insensitive and must + be in 8.3 format, i.e. no more than 8 characters for the name and 3 + for the optional extension. Directories cannot have extensions. + + A boot configuration file (SYSTEM.CNF) or executable (PSX.EXE) must + be present in the root directory. Due to BIOS limitations the root + directory cannot hold more than 30 files or directories, and the + entire disc must contain 45 directories or less. Subdirectories can + contain any number of files. + --> <directory_tree> <file name="SYSTEM.CNF" type="data" source="${PROJECT_SOURCE_DIR}/system.cnf" /> <file name="TEMPLATE.EXE" type="data" source="template.exe" /> - <file name="TEMPLATE.MAP" type="data" source="template.map" /> + <!-- + This file is only required if you are using dynamic linking + (see the system/dynlink example). It contains the executable's + symbol map and can be used to obtain the address of a function + or global variable by its name. + --> + <!--<file name="TEMPLATE.MAP" type="data" source="template.map" />--> <dummy sectors="1024"/> </directory_tree> </track> - <!--<track type="audio" source="track2.wav" />--> + <!-- + CD-DA tracks can be added to the CD image by using one or more <track> + tags. The source attribute must be a path to an audio file in WAV, FLAC + or MP3 format (using WAV or FLAC is highly recommended to preserve + audio quality if you have a lossless copy of the source track). + --> + <!--<track type="audio" source="${PROJECT_SOURCE_DIR}/track2.wav" />--> </iso_project> diff --git a/tools/CMakeLists.txt b/tools/CMakeLists.txt index 6f604d5..c48bc10 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.20) +cmake_minimum_required(VERSION 3.21) project( PSn00bSDK-tools |
