From 603b42797c4b0e7a3e2a3cac320daecf1ee34feb Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Sun, 28 Nov 2021 17:31:33 +0100 Subject: Add GitHub Actions CI/release workflow, rewrite changelog --- .github/scripts/generate_release_notes.py | 194 +++++++++ .github/workflows/build.yml | 208 +++++++++ CHANGELOG.md | 671 ++++++++++++++++++++++++++++++ CMakePresets.json | 13 + changelog.txt | 605 --------------------------- 5 files changed, 1086 insertions(+), 605 deletions(-) create mode 100644 .github/scripts/generate_release_notes.py create mode 100644 .github/workflows/build.yml create mode 100644 CHANGELOG.md delete mode 100644 changelog.txt diff --git a/.github/scripts/generate_release_notes.py b/.github/scripts/generate_release_notes.py new file mode 100644 index 0000000..f3e4870 --- /dev/null +++ b/.github/scripts/generate_release_notes.py @@ -0,0 +1,194 @@ +#!/usr/bin/env python3 +# PSn00bSDK release notes generator +# (C) 2021 spicyjpeg - MPL licensed + +import sys, re +from time import gmtime, strptime +from argparse import ArgumentParser, FileType + +## Helpers + +VERSION_REGEX = re.compile(r"^(?:refs\/tags\/)?(?:v|ver|version|release)? *(.*)") + +def parse_date(date): + return strptime(date.strip(), "%Y-%m-%d") + +def normalize_version(version): + match = VERSION_REGEX.match(version.lower()) + if not match: + raise ValueError(f"invalid version string: {version}") + + return match.group(1) + +## Changelog parser + +BLOCK_REGEX = re.compile(r"^#{2,}[ \t]*([0-9]{4}-[0-9]{2}-[0-9]{2})(?:[:\- \t]+(.+?))?$", re.MULTILINE) +AUTHOR_REGEX = re.compile(r"^([A-Za-z0-9_].*?)[ \t]*:.*?$", re.MULTILINE) +FIRST_VERSION = "initial" + +def parse_authors(block): + # [ _crap, author, body, author, body, ... ] + items = AUTHOR_REGEX.split(block.strip()) + + if items[0].strip(): + raise RuntimeError("a block has changes listed with no author specified") + + authors = {} + for i in range(1, len(items), 2): + name, body = items[i:i + 2] + + name = name.strip() + body = body.strip() + if name not in authors: + authors[name] = "" + + authors[name] += body + + return authors + +def parse_blocks(changelog): + # [ _crap, date, version, body, date, version, body, ... ] + items = BLOCK_REGEX.split(changelog.strip()) + + #if items[0].strip(): + #raise RuntimeError("the changelog doesn't start with a valid block") + + # Iterate over all blocks from bottom to top (i.e. oldest first). + last_version = FIRST_VERSION + + for i in range(len(items), 1, -3): + date, version, body = items[i - 3:i] + + # If no version is present in the header, assume it's the same as the + # previous block's version. + if version: + version = normalize_version(version) + last_version = version + else: + version = last_version + + yield parse_date(date), version, parse_authors(body) + +## Release notes generation + +VERSION_TEMPLATE = """New in version **{version}** (contributed by {authors}): + +{changes} + +""" +NOTES_TEMPLATE = """{notes} + +------------------------------------------------------- +_These notes have been generated automatically._ +_See the changelog or commit history for more details._ +""" + +NO_VERSIONS_TEMPLATE = "No information available about this release." +AUTHOR_LINK_TEMPLATE = "**{0}**" +#AUTHOR_LINK_TEMPLATE = "[{0}](https://github.com/{0})" + +def generate_notes(versions): + notes = "" + + for version, ( authors, changes ) in versions.items(): + _authors = list(set(authors)) + _authors.sort() + _authors = map(AUTHOR_LINK_TEMPLATE.format, _authors) + + notes += VERSION_TEMPLATE.format( + version = version, + authors = ", ".join(_authors), + changes = "\n\n".join(changes) + ) + + if not notes: + notes = NO_VERSIONS_TEMPLATE + + return NOTES_TEMPLATE.format(notes = notes.strip()) + +## Main + +def get_args(): + parser = ArgumentParser( + description = "Generates and outputs release notes from a Markdown changelog file.", + add_help = False + ) + + tools_group = parser.add_argument_group("Tools") + tools_group.add_argument( + "-h", "--help", + action = "help", + help = "Show this help message and exits" + ) + + files_group = parser.add_argument_group("Files") + files_group.add_argument( + "changelog", + type = FileType("rt"), + help = "Markdown changelog file to parse" + ) + files_group.add_argument( + "-o", "--output", + type = FileType("wt"), + default = sys.stdout, + help = "Where to output release notes (stdout by default)", + metavar = "file" + ) + + filter_group = parser.add_argument_group("Filters") + filter_group.add_argument( + "-v", "--version", + action = "append", + type = str, + help = "Ignore all changes not belonging to a version (can be specified multiple times)", + metavar = "name" + ) + filter_group.add_argument( + "-f", "--from-date", + type = parse_date, + default = parse_date("2000-01-01"), + help = "Ignore all changes before date", + metavar = "yyyy-mm-dd" + ) + filter_group.add_argument( + "-t", "--to-date", + type = parse_date, + default = gmtime(), + help = "Ignore all changes after date", + metavar = "yyyy-mm-dd" + ) + + return parser.parse_args() + +def main(): + args = get_args() + version_list = list(map(normalize_version, args.version or [])) + + with args.changelog as _file: + changelog = _file.read() + + # Iterate over all blocks in the changelog and sort them by version, + # merging all changes and authors for each version. + versions = {} + + for date, version, authors in parse_blocks(changelog): + # Apply version and date filters. + if version_list and (version not in version_list): + continue + if date < args.from_date or date > args.to_date: + continue + + if version not in versions: + versions[version] = [], [] + + _authors, _changes = versions[version] + _authors.extend(authors.keys()) + _changes.extend(authors.values()) + + notes = generate_notes(versions) + + with args.output as _file: + _file.write(notes) + +if __name__ == "__main__": + main() diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml new file mode 100644 index 0000000..d6746b0 --- /dev/null +++ b/.github/workflows/build.yml @@ -0,0 +1,208 @@ +# PSn00bSDK GitHub Actions CI script +# (C) 2021 spicyjpeg - MPL licensed + +# The GCC toolchain is stored in the GitHub Actions cache after being built. To +# minimize build times, all the toolchain build steps are skipped if there is a +# cached copy of the toolchain that has not expired (even though the build-gcc +# job still has to run in order to check the cache's contents). The cache is +# shared between all actions in a repo. + +name: Build PSn00bSDK +on: [ push, pull_request ] +env: + BINUTILS_VERSION: 2.36 + GCC_VERSION: 11.1.0 + GCC_TARGET: mipsel-none-elf + +jobs: + # This is based on doc/toolchain.md, no surprises here other than the cache. + build-gcc: + name: Build GCC toolchain + runs-on: ubuntu-latest + + steps: + - name: Initialize toolchain cache + id: _cache + uses: actions/cache@v2 + with: + key: gcc-${{ env.GCC_TARGET }}-${{ env.GCC_VERSION }} + path: gcc + + - name: Install prerequisites + if: ${{ steps._cache.outputs.cache-hit != 'true' }} + run: | + sudo apt-get update -y + sudo apt-get install -y --no-install-recommends make g++-mingw-w64-x86-64 + + - name: Download and extract sources + if: ${{ steps._cache.outputs.cache-hit != 'true' }} + run: | + wget -q -O binutils.tar.xz https://ftpmirror.gnu.org/gnu/binutils/binutils-${{ env.BINUTILS_VERSION }}.tar.xz + wget -q -O gcc.tar.xz https://ftpmirror.gnu.org/gnu/gcc/gcc-${{ env.GCC_VERSION }}/gcc-${{ env.GCC_VERSION }}.tar.xz + tar xf binutils.tar.xz + tar xf gcc.tar.xz + cd gcc-${{ env.GCC_VERSION }} + contrib/download_prerequisites + + - name: Build binutils for Linux + if: ${{ steps._cache.outputs.cache-hit != 'true' }} + run: | + mkdir binutils_linux + cd binutils_linux + ../binutils-${{ env.BINUTILS_VERSION }}/configure --prefix=${{ github.workspace }}/gcc/linux --target=${{ env.GCC_TARGET }} --disable-docs --disable-nls --with-float=soft + make -j 2 + make install-strip + echo "${{ github.workspace }}/gcc/linux/bin" >>$GITHUB_PATH + + - name: Build GCC for Linux + if: ${{ steps._cache.outputs.cache-hit != 'true' }} + run: | + mkdir gcc_linux + cd gcc_linux + ../gcc-${{ env.GCC_VERSION }}/configure --prefix=${{ github.workspace }}/gcc/linux --target=${{ env.GCC_TARGET }} --disable-docs --disable-nls --disable-libada --disable-libssp --disable-libquadmath --disable-libstdc++-v3 --with-float=soft --enable-languages=c,c++ --with-gnu-as --with-gnu-ld + make -j 2 + make install-strip + + - name: Build binutils for Windows + if: ${{ steps._cache.outputs.cache-hit != 'true' }} + run: | + mkdir binutils_windows + cd binutils_windows + ../binutils-${{ env.BINUTILS_VERSION }}/configure --prefix=${{ github.workspace }}/gcc/windows --build=x86_64-linux-gnu --host=x86_64-w64-mingw32 --target=${{ env.GCC_TARGET }} --disable-docs --disable-nls --with-float=soft + make -j 2 + make install-strip + + - name: Build GCC for Windows + if: ${{ steps._cache.outputs.cache-hit != 'true' }} + run: | + mkdir gcc_windows + cd gcc_windows + ../gcc-${{ env.GCC_VERSION }}/configure --prefix=${{ github.workspace }}/gcc/windows --build=x86_64-linux-gnu --host=x86_64-w64-mingw32 --target=${{ env.GCC_TARGET }} --disable-docs --disable-nls --disable-libada --disable-libssp --disable-libquadmath --disable-libstdc++-v3 --with-float=soft --enable-languages=c,c++ --with-gnu-as --with-gnu-ld + make -j 2 + make install-strip + + # No surprises here either. The GitHub Actions VMs even come with most of the + # dependencies required to build PSn00bSDK preinstalled. + build-sdk-windows: + name: Build PSn00bSDK on Windows + runs-on: windows-latest + needs: build-gcc + + steps: + # Due to a bug in the cache action (and in order to use Ninja and pacman) + # the directories MSys2 stores binaries in must be added to PATH. For + # some reason they are not present in PATH by default. + # https://github.com/actions/cache/issues/576 + - name: Add MSys2 to PATH + run: | + echo "C:\msys64\mingw64\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + echo "C:\msys64\usr\bin" | Out-File -FilePath $env:GITHUB_PATH -Encoding utf8 -Append + + - name: Initialize toolchain cache + uses: actions/cache@v2 + with: + key: gcc-${{ env.GCC_TARGET }}-${{ env.GCC_VERSION }} + path: gcc + + - name: Install prerequisites + run: | + pacman -S --noconfirm mingw-w64-x86_64-ninja mingw-w64-x86_64-gcc + + - name: Fetch repo contents + uses: actions/checkout@v2 + with: + path: sdk + + - name: Update repo submodules + run: | + cd sdk + git submodule update --init --recursive --remote + + - name: Build and package PSn00bSDK + run: | + cmake --preset ci -S sdk -DPSN00BSDK_TC=${{ github.workspace }}\gcc\windows + cmake --build build + cmake --build build -t package + + - name: Upload build artifacts + uses: actions/upload-artifact@v2 + with: + name: psn00bsdk-windows + path: | + build/packages/* + !build/packages/_CPack_Packages + + build-sdk-linux: + name: Build PSn00bSDK on Linux + runs-on: ubuntu-latest + needs: build-gcc + + steps: + - name: Initialize toolchain cache + uses: actions/cache@v2 + with: + key: gcc-${{ env.GCC_TARGET }}-${{ env.GCC_VERSION }} + path: gcc + + - name: Install prerequisites + run: | + sudo apt-get update -y + sudo apt-get install -y --no-install-recommends ninja-build + + - name: Fetch repo contents + uses: actions/checkout@v2 + with: + path: sdk + + - name: Update repo submodules + run: | + cd sdk + git submodule update --init --recursive --remote + + - name: Build and package PSn00bSDK + run: | + cmake --preset ci -S sdk -DPSN00BSDK_TC=${{ github.workspace }}/gcc/linux + cmake --build build + cmake --build build -t package + + - name: Upload build artifacts + uses: actions/upload-artifact@v2 + with: + name: psn00bsdk-linux + path: | + build/packages/* + !build/packages/_CPack_Packages + + # This job takes care of creating a new release and upload the build + # artifacts if the last commit is associated to a tag. + create-release: + name: Create release + runs-on: ubuntu-latest + needs: [ build-sdk-windows, build-sdk-linux ] + + steps: + - name: Fetch repo contents + uses: actions/checkout@v2 + with: + path: sdk + + - name: Fetch build artifacts + if: ${{ github.ref_type == 'tag' }} + uses: actions/download-artifact@v2 + with: + path: . + + - name: Generate release notes + if: ${{ github.ref_type == 'tag' }} + run: | + sdk/.github/scripts/generate_release_notes.py -v ${{ github.ref_name }} -o release.md sdk/CHANGELOG.md + + - name: Publish release + if: ${{ github.ref_type == 'tag' }} + uses: softprops/action-gh-release@v1 + with: + fail_on_unmatched_files: true + body_path: release.md + files: | + psn00bsdk-windows/* + psn00bsdk-linux/* diff --git a/CHANGELOG.md b/CHANGELOG.md new file mode 100644 index 0000000..c05105e --- /dev/null +++ b/CHANGELOG.md @@ -0,0 +1,671 @@ + +# PSn00bSDK changelog + +**NOTE**: this file is parsed by a script to generate release notes. When +contributing to PSn00bSDK, add a new block at the top following this template: + +``` +## --: + +: + +- ... + +- ... +``` + +You may run `.github/scripts/generate_release_notes.py CHANGELOG.md` afterwards +to ensure the changelog can be parsed correctly. + +-------- + +## 2021-11-28: 0.18 + +spicyjpeg: + +- libc: Removed `STACK_MAX_SIZE` and added `_mem_init()` back. RAM and stack + size can now be set by calling `_mem_init()` manually before allocating any + memory. + +- libc: `sprintf()` now supports fixed padding when using the `%@` (binary + integer) format specifier. + +- psxcd: File paths with forward slashes instead of backslashes are now + accepted. + +- psxapi: Added wrapper around BIOS function `GetSystemInfo()`. + +- examples: Added `pads` and `system573` examples. Also added CMake build + script to `cartrom`, which is now built alongside all other examples. + +- Header files are now installed to `include/libpsn00b` instead of + `lib/libpsn00b/include`. + +- Deprecated `malloc.h` and removed all references to it (`stdlib.h` should be + used instead). Moved `int*_t` and `uint*_t` types to `stdint.h`. + +- Fixed file permission errors when attempting to install the SDK on macOS and + made some small updates to `INSTALL.md`, which is now `doc/installation.md`. + +## 2021-10-31 + +spicyjpeg: + +- Added `tinyxml2` and `mkpsxiso` as git submodules and removed the (admittedly + short-lived) `SKIP_*` options completely. CMake's `ExternalProject` is no + longer used to download dependencies at build time. + +- Added CMake presets file with presets for building installer packages. + +## 2021-10-25 + +Lameguy64: + +- Made a very tiny change in the readme file. + +- Included some of the indev directory from the SVN repository. + +## 2021-10-17: 0.17 + +spicyjpeg: + +- Added `SKIP_TINYXML2` and `SKIP_MKPSXISO` build options in place of the old + `SKIP_DOWNLOAD` option, and a new `BUNDLE_TOOLCHAIN` option for building + packages. Updated `INSTALL.md` accordingly and fixed git branch error when + downloading mkpsxiso during build. + +- Rewritten `toolchain.txt` (which is now `TOOLCHAIN.md`) and added proper + Windows toolchain build instructions. + +- Added safety checks in CMake scripts to ensure `elf2x`/`mkpsxiso` are + installed before attempting to build an executable or CD image. + +- examples: Added CMake build script to (and removed makefile from) `vagsample`. + +## 2021-06-10 + +Lameguy64: + +- psxspu: Fixed register typo in `SpyKeyOn` causing unpredictable instability. + +- psxspu: Fixed bug where `SpuKeyOn` does not set the key-on bits for voices 16 + to 23. + +- examples: Added `vagsample` SPU example. + +## 2021-09-27 + +spicyjpeg: + +- liblzp, tools: Fixed tools not building on MSVC and cleaned up LZP API + declarations (replaced meaningless `void*` pointers with proper types). + +- libpsn00b: Added missing `PSN00BSDK_VERSION` CMake variable. + +- examples: Fixed childexec (`parent.exe`) example crashing due to the child + executable not being relocated in the new build script. Patched n00bdemo to + suppress `liblzp` pointer type warnings. + +- Fixed another MSVC linker error when building `tinyxml2` automatically, as + well as various toolchain/compiler test errors sometimes thrown by CMake. + Updated `INSTALL.md` with more details. + +## 2021-09-13 + +spicyjpeg: + +- Migrated libpsn00b, tools, examples and template to CMake, added a top-level + `CMakeLists.txt` and the `libpsn00b/cmake` directory for CMake setup scripts, + got rid of the old makefiles and installation method. + +- Updated docs to match the new build system, moved installation instructions + to `INSTALL.md` and added `doc/cmake_reference.md` describing the SDK's CMake + API and variables. + +- mkpsxiso: Added rules to automatically download, build and install mkpsxiso + (as well as its dependencies) as part of the SDK. + +- cpack: Added initial CPack support and created the cpack directory containing + branding assets for installers built through CPack. A shell script is + provided to regenerate the assets from their source SVGs. + +- liblzp: Copied library headers to `libpsn00b/include/lzp`. + +- psxetc: Fixed a random typo in a javadoc comment (those should be eventually + rewritten into proper documentation anyway). + +- Added CMake, VS Code and DLL related entries to `.gitignore`. + +## 2021-08-16: 0.16 + +spicyjpeg: + +- psxetc: Added dynamic linking APIs (`dl*`, `DL_*`), implemented in `dl.c` and + `_dl_resolve_wrapper.s`. The APIs can be accessed by including `dlfcn.h`, + while another header (`elf.h`) provides structs and enums to help with manual + parsing of library headers. + +- libc: Removed `_mem_init.s`, improved heap initialization code. Added support + for setting how much RAM to use for the stack (by defining `STACK_MAX_SIZE`) + and for overriding/replacing the default memory allocator. + +- psxapi: Added wrapper around BIOS function `FlushCache()`. + +- psxspu: Fixed wrong bounds check in `SpuSetTransferStartAddr()`. + +- examples: Added an example showing how to compile dynamically-loaded + libraries (DLLs) and load them at runtime using the new dynamic linker. + +- libpsn00b: Added `libpsn00b/ldscripts` directory and linker scripts for both + executables and DLLs. Also fixed several broken header files (`psxgpu.h` not + including `sys/types.h`, missing `scanf()` declarations, ...). + +- Rewritten most makefiles (both for libraries and examples) and centralized + all compiler flags into a single `psn00bsdk-setup.mk` file for consistency + and easier editing. Added flags to build libpsn00b without gp-relative + addressing for compatibility with the dynamic linker. Also added `nm` + commands to generate symbol maps required by the linker. + +## 2021-07-01 + +Lameguy64: + +- Libpsn00b: Added `int8_t`, `int16_t`, `int32_t`, `int64_t`, `uint8_t`, + `uint16_t`, `uint32_t` and `uint64_t` variable types in `sys/types.h`. + +- psxgte: Replaced unsigned int variable types with `u_long` to further improve + compatibility with code written for the official Sony SDK and to make my + tutorial examples easier to compile on PSn00bSDK. Example programs have been + updated to account for this change. + +- psxcd: Changed type of 2nd argument of `CdRead()` from `u_int` to `u_long`, + as well as changing the type of the size element in `CdlFILE` from `u_int` to + `u_long`. + +## 2021-02-17 + +Lameguy64: + +- Improved build instructions in readme file and fixed some typos. + +- Improved `toolchain.txt` instructions. + +- tools: Added experimental `elf2cpe` converter for executing PSn00bSDK + programs in official development units. No debug symbols however. + +- Fixed prefixes to allow SDK libraries and examples to be built + with `mipsel-none-elf`. + +- examples: Fixed typo in `plasma_tbl.h` causing multiple definitions when + compiling with newer versions of GCC in `n00bdemo`. + +- examples: `cartrom` example now marked as obsoleted, but still kept for + reference purposes. + +- Includes alextrevisan's GTE macros in `inline_c.h`. + +- Added makefile template. + +## 2021-01-05 + +Lameguy64: + +- psxgpu: Added struct names to many primitives. + +- psxgte: Defined `SquareRoot0()` and `SquareRoot12()`. + +- psxgte: Added `gte_lddp()` C macro. + +- examples: Added C++ demo. + +- Updated readme a bit. + +## 2020-11-29 + +Lameguy64: + +- libpsn00b: Defined MDEC hardware and related DMA registers in `hwregs_a.h` + file. + +- psxgte: Fixed entry typo in table of `squareroot.s` (pointed out by + SoapyMan). + +- libc: `memmove` updated to account for forward-looped memory move (by + ckosmic). + +- examples: Removed redundant toolchain executable definitions in the + makefiles. + +- examples: Included HDTV example for Github repo. + +## 2020-09-19 + +Lameguy64: + +- Revised makefiles for building the libraries and examples in a way to make + building with different toolchain versions easier with the `PSN00BSDK_TC` + environment variable. Library installation and linking is also made easier + with the `PSN00BSDK_LIBS` environment variable. See readme in the `libpsn00b` + directory for details. + +- examples: Fixed libgcc not found error when compiling some of the examples. + +- libc: Added `strtok()`. + +- libc: Added support for command line arguments. Pass arguments via + `SYSTEM.CNF` `BOOT=` string or a string array with `Exec()`. Arguments can + be read via `argc`/`argv[]` in `main()` or `__argc`/`__argv` anywhere else. + +- libc: Added `SetHeapSize()`. + +- psxgpu: Moved ISR and callback subsystem to `psxetc`. You'll have to link + psxetc after `psxgpu` and `psxapi` after `psxetc` in your library string. + +- psxetc: Moved debug font functions (`FntInit()`, `FntOpen()`, etc) to `psxgpu`. + +- psxetc: Fixed stack management in `RestartCallback()`. + +- examples: Added argument passing in `childexec` example. + +- psxcd: Fixed crashing on PSIO and possibly some emulators by implementing + a response buffer read limiter. + +- psxgpu: Interrupts are now disabled before setting up ISR and callbacks in + `ResetGraph()`, as `LoadExec()` still has interrupts enabled when jumping to + the loaded PS-EXE's entrypoint. Fixes programs made with PSn00bSDK crashing + at `ResetGraph()` on PSIO when loading from the Menu System and possibly some + emulators. + +## 2020-07-25 + +Lameguy64: + +- psxgte: Added `ScaleMatrixL()`. + +- doc: Corrected documentation for `CdReadSync()` function. + +- psxcd: Added new define: `CdlIsoLidOpen`. + +- psxcd: Updated media change detection logic, media change is checked + by lid open status bit in all CD-ROM file functions. `CdControl()` calls will + also trigger the media change status on lid open. + +- psxcd: Fixed bug in `CdGetVolumeLabel()` where it constantly reparses the + file system regardless of media change status. + +- examples: Updated `cdrom/cdbrowse` example slightly. + +- psxcd: Added `CdLoadSession()`. + +- psxcd: Fixed bug where `CdReadDir()` locks up in an infinite loop when it + encounters a NULL directory record, and the parser has not yet exceeded the + length of the directory record. + +- doc: Replaced library version numbers with SVN revision numbers in the + introduced fields. + +- doc: Started work on CD-ROM library overview. + +## 2020-04-24 + +Lameguy64: + +- Refined toolchain instructions a bit. + +- psxcd: Added automatic read retry for `CdRead()` operations, as long as + `CdReadSync()` is called until read completion. + +- psxapi: Added BIOS `atoi()` and `atol()` calls. Temporary, may be replaced + with a faster implementation. + +- psxsio: Added `ioctl()` support for `FIOCSCAN` to probe for pending input in + serial tty driver. + +- examples: Reorganized examples, added new `tty` and `console` examples. + +## 2020-03-11 + +Lameguy64: + +- psxcd: Fixed `CdInit()` syntax (there should be no arguments). + +- psxcd: Fixed `CdlFILE`, `loc` variable renamed to `pos`. + +- documentation: Improved and updated toolchain instructions a bit. + +## 2020-02-28 + +Lameguy64: + +- documentation: Added docs for `CdOpenDir()`, `CdReadDir()` and + `CdCloseDir()`. + +- psxgpu: Added `GetODE()`. + +- psxspu: Fixed `SpuKeyOn()` bug where only 16 of the 24 channels can be + activated, due to channel bits being written as a 16-bit word. + +- psxcd: Added `CdOpenDir()`, `CdReadDir()` and `CdCloseDir()` for parsing + directories. + +- psxcd: Issuing `CdlNop` (`GetStat`) commands now triggers the media change + flag internal to the libraries when CD lid is opened, so file system + functions can update the cached ISO descriptor when the disc has been + changed. + +- psxcd: Made internal variables and functions for iso9660 parsing static. + +## 2020-02-25 + +thp: + +- libc: Added `abs()` and `labs()` functions. + +## 2020-01-06 + +Lameguy64: + +- libpsn00b: Renamed `libpsxcd` directory to `psxcd` to match with other + libraries. + +- examples: Added obligatory hello world example. + +- documentation: Added CD-ROM library documentation. + +## 2019-11-22: 0.15b + +Lameguy64: + +- psxapi: Added root counter or timer functions and related definitions. + +- examples: Added timer example. + +- psxgpu: Added `DR_AREA`, `DR_OFFSET` and `DR_TWIN` primitives and + accompanying `setDrawArea()`, `setDrawOffset()` and `setTexWindow()` macros. + +- psxgpu: Added parenthesis to argument value in `setlen()`, `setaddr()` and + `setcode()` macros, preventing `addPrims()` from being used in a more + sensible manner (ie. `addPrims(ot, sub_ot+3, sub_ot)`). + +- examples: Added render2tex render to texture example. + +- psxspu: Fixed typo in `spuinit.s` on section specifier specifying a data + section instead of text section, resulting to jump to + non-instruction-aligned linker errors. + +- psxgpu: Increased ISR stack size to 2048 bytes. + +- psxsio: Added `kbhit()` to poll keyboard input asynchronously and stdin + is now buffered with an IRQ handler. + +- psxapi: Added `AddDummyTty()` (for psxsio `DelSIO()` fix). + +- psxsio: `DelSIO()` now calls `AddDummyTty()`. + +- libc: Fixed bug in `strncpy()` not placing a NULL byte at end of string. + +- libc: Fixed `strchr()` and `strrchr()` declarations commented out in + `string.h`. + +- libpsn00b: Added the long awaited libpsxcd library with cdxa example. + Documentation will come soon but existing libcd docs should be good + enough for awhile. + +- psxgpu: Fixed non functioning GPU DMA wait in `DrawSync()`. + +- LibPSn00b run-time library is now officially 0.15b. + +## 2019-10-11 + +Lameguy64: + +- psxetc: Added `FntOpen()`, `FntPrint()` and `FntFlush()` functions. + +- psxgpu: Fixed typo in `termPrim()` macro (value too long). + +- examples: Added billboarding sprites example. + +- psxapi: Transferred `putchar()` BIOS function to libc. + +- libpsn00b: Updated makefiles adding `-fdata-sections` and + `-ffunction-sections` so unused functions will be stripped out, which yields + smaller executables. + +- libc: Fixed negative integers not displaying properly in + `vsprintf()`/`vsnprintf()`. + +- libc: Fixed zero padding not working in `vsprintf()`/`vsnprintf()`. + +- fpscam: Added debug text using `FntOpen()`, `FntPrint()` and `FntFlush()`. + Also added `_boot()` call for returning to CD based serial loaders. + +## 2019-09-23 + +Lameguy64: + +- libc: Added `strcat()` function. + +- Included PSn00bSDK logo vector. + +- psxgte: Added `gte_stsz()` macro. + +- psxgpu: Fixed typos in `setUVWH()` macro. + +- Added `_boot()` BIOS function (A(A0h) aka Warmboot, useful for CD based + serial loaders). + +## 2019-08-17: 0.13b + +Lameguy64: + +- LibPSn00b run-time library is officially version 0.13b. + +- examples: updated balls and n00bdemo examples for the `setDrawTPage()` + correction. + +- psxgpu: Fixed error on `setDrawTPage()` syntax and removed + `setDrawTPageVal()` macro (not needed). + +- psxapi: Added event handler definitions related to memory cards. + +- psxapi: Added BIOS memory card functions. + +- examples: Added childexec example demonstrating a parent executable + transferring execution to a child executable and back. + +- elf2x: `s_addr` no longer set on PS-EXE header, console BIOS already sets it + by STACK value in `SYSTEM.CNF` and allows child executables to return to + parent if left zero. + +- psxapi: Renamed `_InitPad()`, `_StartPad()` and `_StopPad()` to `InitPAD()`, + `StartPAD()` and `StopPAD()` respectively. Fpscam example updated to new + syntax. + +## 2019-07-18 + +Lameguy64: + +- Added `fpscam` example. + +- Gave examples small descriptions. + +- Updated readme a little. + +## 2019-07-17: 0.12b + +Lameguy64: + +- LibPSn00b run-time library is officially version 0.12b. + +- libc: Added basic C++ support (many thanks to ijacquez). + +- libc: Updated start function that should make it possible for a child + executable to return to a parent executable, return logic automatically + calls `EnterCriticalSection()`. + +- libc: Updated build method which takes libgcc from the compiler and adds + its own object files into it, eliminating linker problems caused by having + to order libc and libgcc libraries in a specific manner. + +- psxgpu: Added `RestartCallback()`. + +- psxgpu: Added `StoreImage()` function. + +- psxgpu: Fixed bugged `setRECT()` macro. + +- libc: Added `assert.h`. + +- examples: Balls example now has 166% more balls. + +- psxgpu: Increased ISR stack size to 1024 bytes so printf can be used in + callbacks safely. + +- libc: Removed `int64` (long long) printing in `vsprintf()` for better + performance, as the R3000 does not support 64-bit arithmetic natively + so its emulated like floats. `int64` still used for processing floats and + doubles and old `vsprintf.c` file is still included for those who really + want int64 support for whatever reason. + +- libc: Removed `stdarg.h` which is part of GCC and not license compatible + with MPL. The toolchain compiled with libgcc provides `stdarg.h` and other + standard headers. + +- examples: Updated `sdk-common.mk` variable convention for better flexibility. + +- libpsn00b: Added `common.mk` file containing global values for all libraries. + +- Updated library and toolchain build instructions. + +- psxgpu: Fixed bug in DMACallback where the internal DMA handler would fail + to install due to `GetInterruptCallback()` retrieving the callback value + immediately in the branch delay slot of a jr instruction, which resuls to + an inconsistent return value. This also broke `DrawSyncCallback()`. + +- psxsio: Done fixes on `_sio_control()` from the aformentioned issues with + load instructions in delay slots. + +- psxgte: Added `DVECTOR` struct. + +- psxgpu: Added `setLineF2()`, `setLineG2()`, `setLineF3()` and `setLineG3()` + primitive macros. + +- Added more functions in documentation. + +## 2019-07-01 + +williamblair: + +- Fixed `FntLoad()` Y coordinate not working properly for debug font (due to + Y coordinate not being specified for the `getTPage()` macro. + +## 2019-06-23: 0.10b + +Lameguy64: + +- LibPSn00b Run-time Library is officially version 0.10b. + +- Reworked readme file with improved build instructions. + +- libpsn00b: Added missing C extern groups for C++ compatibility in + `psxapi.h`, `stdlib.h` and `string.h`. + +- libpsn00b: Removed changelogs in the readmes of each libpsn00b library + as its much easier to keep track of changes in a single changelog than + scattering them across multiple changelogs. + +- psxapi: Added `Exec()` function and EXEC struct. + +- psxgpu: Added `DrawPrim()` function (uses direct I/O). + +- psxgpu: `VSync()` function completely overhauled. Logic is now based on + Sony's code but more efficient and can return total number of vblanks + elapsed, number of hblanks since last call and wait up to n vblanks + specified by an appropriate argument value. It will also generate a timeout + when vblank interrupt stops working and would attempt to restart it. + +- psxapi: Added `gets()` and `getc()` BIOS functions. + +- psxapi: Corrected a `putc()`/`putchar()` discrepancy. `putc()` puts a + character to a file stream, `putchar()` puts a character to stdout. + +- Completely revised library reference manual with better formatting and + more functions documented. + +- psxgpu: Added `DrawSyncCallback()`. + +- psxgpu: Implemented DMA interrupt callback system with `DMACallback()` + allowing to handle DMA interrupts very easily. + +- libpsn00b: Implemented Serial I/O library (`psxsio`) with serial tty device + (installed using `AddSIO`) and serial callback support. Serial library + is also fully documented. + +- psxgpu: Implemented IRQ callback system with `InterruptCallback()` allowing + to set interrupt callbacks very easily. + +- psxgpu: Implemented proper IRQ handler installed using HookEntryInt or + `SetCustomExitFromException()` for handling VSync and other interrupts. + `ChangeClearPAD(0)` must now be called after `_InitPad()` or vsync timeout + will occur. + +- libpsn00b: Started making local labels prefixed with `.L` instead of `.` + making them not appear in symbol lists resulting in a cleaner symbol dump. + Still not possible to do function-scope local labels like in ASMPSX because + GAS syntax is ASS (or ASS GAS which is farts, GAS is farts). + +- psxgpu: `DrawSync()` function now waits for DMA completion and GPU transfer + ready instead of simply waiting for GPU transfer ready which is the likely + cause of subtle GPU related timing issues, it also sets GPU DMA transfer + mode to off afterwards. It can also read number of words remaining in DMA + transfer if a0 is non-zero but it likely only returns the correct value on + VRAM transfers. Exact way how `DrawSync()` returns the count in the official + SDK is currently unknown. + +## 2019-06-07 + +qbradq: + +- lzpack: Swapped a buffer length litteral with sizeof operator, fixing a stack + overflow bug in some instances. + +## 2019-05-23 + +Lameguy64: + +- Added `dev notes.txt` file in docs that includes notes about the many quirks + quirks about the console discovered during the development of this SDK + project. + +- Updated `libn00bref.odf` a little. + +- Added `cartrom` example. + +- Added `rgb24` example. + +- Got custom exit handler set using `SetCustomExitFromException()` (BIOS + function B(19h)) working. Currently used to acknowledge VSync IRQ but + actual VSync handling is still done with events and needs to be + transferred to the custom exit handler. At least it lets BIOS + controller functions to work now. See doc/dev `notes.txt` for details + on how this handler behaves. + +- Made stack usage in `ResetGraph()` less wasteful. You only need to + allocate N words on stack based on N arguments of the function + being called. + +- VSync IRQ handling now done using a custom exit from exception handler. + Actual VSync handling is yet to be moved to the custom exit handler + though. + +- Made stack usage in `start.s` of libc a lot less wasteful. + +- Implemented controller support via BIOS functions (use `_InitPad()`, + `_StartPad()` and `_StopPad()`). BIOS memory card functions may also + work as well but its not tested yet. + +- Removed duplicate `initpad.s` and `initcard.s` functions in psxapi. + +- Added `_InitCd()` function to psxapi which is a safer version of `_96_init()` + as it preserves other DMA channel settings. Use BIOS file functions such + as `open()`, `read()` and `close()` with path names starting with `cdrom:/` + to access files from CD after calling `_InitCd()`. diff --git a/CMakePresets.json b/CMakePresets.json index f550e82..7c9fdd3 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -24,6 +24,19 @@ "SKIP_EXAMPLES": "ON", "BUNDLE_TOOLCHAIN": "ON" } + }, + { + "name": "ci", + "displayName": "CI build", + "description": "This preset is used by GitHub Actions to build PSn00bSDK.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/../build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Release", + "BUNDLE_TOOLCHAIN": "ON", + "BUILD_INFO": "Built by GitHub Actions ($env{GITHUB_REF_NAME} $env{GITHUB_SHA})", + "PSN00BSDK_TARGET": "$env{GCC_TARGET}" + } } ] } diff --git a/changelog.txt b/changelog.txt deleted file mode 100644 index 3efa7a0..0000000 --- a/changelog.txt +++ /dev/null @@ -1,605 +0,0 @@ -PSn00bSDK changelog - -Items that are lower in the log are more recently implemented. - -11-19-2021 by spicyjpeg: - -* libc: Removed STACK_MAX_SIZE and added _mem_init() back. RAM and stack size - can now be set by calling _mem_init() manually before allocating any memory. - -* libc: sprintf() now supports fixed padding when using %@ (binary integer). - -* psxapi: Added wrapper around BIOS function GetSystemInfo(). - -* examples: Added pads example. Also added CMake build script to cartrom, which - is now built alongside all other examples. - -* Deprecated malloc.h and removed all references to it (stdlib.h should be used - instead). Moved int*_t and uint*_t types to stdint.h. - -* Fixed file permission errors when attempting to install the SDK on macOS. - Made some small updates to INSTALL.md. - - -10-31-2021 by spicyjpeg: - -* Added tinyxml2 and mkpsxiso as git submodules and removed the (admittedly - short-lived) SKIP_* options completely. CMake's ExternalProject is no longer - used to download dependencies at build time. - -* Added CMake presets file with presets for building installer packages. - - -10-25-2021 by Lameguy64: - -* Made a very tiny change in the readme file. - -* Included some of the indev directory from the SVN repository. - - -10-17-2021 by spicyjpeg: - -* Added SKIP_TINYXML2 and SKIP_MKPSXISO build options in place of the old - SKIP_DOWNLOAD option, and a new BUNDLE_TOOLCHAIN option for building packages. - Updated INSTALL.md accordingly and fixed git branch error when downloading - mkpsxiso during build. - -* Rewritten toolchain.txt (which is now TOOLCHAIN.md) and added proper Windows - toolchain build instructions. - -* Added safety checks in CMake scripts to ensure elf2x/mkpsxiso are installed - before attempting to build an executable or CD image. - -* examples: Added CMake build script to (and removed makefile from) vagsample. - - -10-06-2021 by Lameguy64: - -* psxspu: Fixed register typo in SpyKeyOn causing unpredictable instability. - -* psxspu: Fixed bug where SpuKeyOn does not set the key-on bits for voices 16 to 23. - -* examples: Added vagsample SPU example. - - -09-27-2021 by spicyjpeg: - -* liblzp, tools: Fixed tools not building on MSVC and cleaned up LZP API - declarations (replaced meaningless void* pointers with proper types). - -* libpsn00b: Added missing PSN00BSDK_VERSION CMake variable. - -* examples: Fixed childexec (parent.exe) example crashing due to the child - executable not being relocated in the new build script. Patched n00bdemo to - suppress liblzp pointer type warnings. - -* Fixed another MSVC linker error when building tinyxml2 automatically, as well - as various toolchain/compiler test errors sometimes thrown by CMake. Updated - INSTALL.md with more details. - - -09-13-2021 by spicyjpeg: - -* Migrated libpsn00b, tools, examples and template to CMake, added a top-level - CMakeLists.txt and the libpsn00b/cmake directory for CMake setup scripts, got - rid of the old makefiles and installation method. - -* Updated docs to match the new build system, moved installation instructions - to INSTALL.md and added doc/cmake_reference.md describing the SDK's CMake - API and variables. - -* mkpsxiso: Added rules to automatically download, build and install mkpsxiso - (as well as its dependencies) as part of the SDK. - -* cpack: Added initial CPack support and created the cpack directory containing - branding assets for installers built through CPack. A shell script is - provided to regenerate the assets from their source SVGs. - -* liblzp: Copied library headers to libpsn00b/include/lzp. - -* psxetc: Fixed a random typo in a javadoc comment (those should be eventually - rewritten into proper documentation anyway). - -* Added CMake, VS Code and DLL related entries to .gitignore. - - -08-16-2021 by spicyjpeg: - -* psxetc: Added dynamic linking APIs (dl*, DL_*), implemented in dl.c and - _dl_resolve_wrapper.s. The APIs can be accessed by including dlfcn.h, while - another header (elf.h) provides structs and enums to help with manual parsing - of library headers. - -* libc: Removed _mem_init.s, improved heap initialization code. Added support - for setting how much RAM to use for the stack (by defining STACK_MAX_SIZE) - and for overriding/replacing the default memory allocator. - -* psxapi: Added wrapper around BIOS function FlushCache(). - -* psxspu: Fixed wrong bounds check in SpuSetTransferStartAddr(). - -* examples: Added an example showing how to compile dynamically-loaded - libraries (DLLs) and load them at runtime using the new dynamic linker. - -* libpsn00b: Added libpsn00b/ldscripts directory and linker scripts for both - executables and DLLs. Also fixed several broken header files (psxgpu.h not - including sys/types.h, missing scanf() declarations, ...). - -* Rewritten most makefiles (both for libraries and examples) and centralized - all compiler flags into a single psn00bsdk-setup.mk file for consistency and - easier editing. Added flags to build libpsn00b without gp-relative addressing - for compatibility with the dynamic linker. Also added nm commands to generate - symbol maps required by the linker. - - -07-01-2021 by Lameguy64: - -* Libpsn00b: Added int8_t, int16_t, int32_t, int64_t, uint8_t, uint16_t, - uint32_t and uint64_t variable types in sys/types.h. - -* psxgte: Replaced unsigned int variable types with u_long to further - improve compatibility with code written for the official Sony SDK and - to make my tutorial examples easier to compile on PSn00bSDK. Example - programs have been updated to account for this change. - -* psxcd: Changed type of 2nd argument of CdRead() from u_int to u_long, - as well as changing the type of the size element in CdlFILE from u_int - to u_long. - - -02-17-2021 by Lameguy64: - -* Improved build instructions in readme file and fixed some typos. - -* Improved toolchain.txt instructions. - -* tools: Added experimental elf2cpe converter for executing PSn00bSDK - programs in official development units. No debug symbols however. - -* Fixed prefixes to allow SDK libraries and examples to be built - with mipsel-none-elf. - -* examples: Fixed typo in plasma_tbl.h causing multiple definitions when - compiling with newer versions of GCC in n00bdemo. - -* examples: cartrom example now marked as obsoleted, but still kept for - reference purposes. - -* Includes alextrevisan's GTE macros in inline_c.h. - -* Added makefile template. - - -01-05-2021 by Lameguy64: - -* psxgpu: Added struct names to many primitives. - -* psxgte: Defined SquareRoot0() and SquareRoot12(). - -* psxgte: Added gte_lddp() C macro. - -* examples: Added C++ demo. - -* Updated readme a bit. - - -11-29-2020 by Lameguy64: - -* libpsn00b: Defined MDEC hardware and related DMA registers in hwregs_a.h file. - -* psxgte: Fixed entry typo in table of squareroot.s (pointed out by SoapyMan). - -* libc: memmove updated to account for forward-looped memory move (by ckosmic). - -* examples: Removed redundant toolchain executable definitions in the makefiles. - -* examples: Included HDTV example for Github repo. - - -09-19-2020 by Lameguy64: - -* Revised makefiles for building the libraries and examples in a way to make - building with different toolchain versions easier with the PSN00BSDK_TC - environment variable. Library installation and linking is also made easier - with the PSN00BSDK_LIBS environment variable. See readme in the libpsn00b - directory for details. - -* examples: Fixed libgcc not found error when compiling some of the examples. - -* libc: Added strtok(). - -* libc: Added support for command line arguments. Pass arguments via - SYSTEM.CNF BOOT= string or a string array with Exec(). Arguments can - be read via argc/argv[] in main() or __argc/__argv anywhere else. - -* libc: Added SetHeapSize(). - -* psxgpu: Moved ISR and callback subsystem to psxetc. You'll have to link - psxetc after psxgpu and psxapi after psxetc in your library string. - -* psxetc: Moved debug font functions (FntInit(), FntOpen(), etc) to psxgpu. - -* psxetc: Fixed stack management in RestartCallback(). - -* examples: Added argument passing in childexec example. - -* psxcd: Fixed crashing on PSIO and possibly some emulators by implementing - a response buffer read limiter. - -* psxgpu: Interrupts are now disabled before setting up ISR and callbacks - in ResetGraph(), as LoadExec() still has interrupts enabled when jumping - to the loaded PS-EXE's entrypoint. Fixes programs made with PSn00bSDK - crashing at ResetGraph() on PSIO when loading from the Menu System and - possibly some emulators. - - -07-25-2020 by Lameguy64: - -* psxgte: Added ScaleMatrixL(). - -* doc: Corrected documentation for CdReadSync() function. - -* psxcd: Added new define: CdlIsoLidOpen. - -* psxcd: Updated media change detection logic, media change is checked - by lid open status bit in all CD-ROM file functions. CdControl() calls will - also trigger the media change status on lid open. - -* psxcd: Fixed bug in CdGetVolumeLabel() where it constantly reparses the file - system regardless of media change status. - -* examples: Updated cdrom/cdbrowse example slightly. - -* psxcd: Added CdLoadSession(). - -* psxcd: Fixed bug where CdReadDir() locks up in an infinite loop when it - encounters a NULL directory record, and the parser has not yet exceeded the - length of the directory record. - -* doc: Replaced library version numbers with SVN revision numbers in the - introduced fields. - -* doc: Started work on CD-ROM library overview. - - -04-24-2020 by Lameguy64: - -* Refined toolchain instructions a bit. - -* psxcd: Added automatic read retry for CdRead() operations, as long as - CdReadSync() is called until read completion. - -* psxapi: Added BIOS atoi() and atol() calls. Temporary, may be replaced with - a faster implementation - -* psxsio: Added ioctl() support for FIOCSCAN to probe for pending input in - serial tty driver. - -* examples: Reorganized examples, added new tty and console examples. - - -03-11-2020 by Lameguy64: - -* psxcd: Fixed CdInit() syntax (there should be no arguments). - -* psxcd: Fixed CdlFILE, loc variable renamed to pos. - -* documentation: Improved and updated toolchain instructions a bit. - - -02-28-2020 by Lameguy64: - -* documentation: Added docs for CdOpenDir(), CdReadDir() and CdCloseDir(). - -* psxgpu: Added GetODE(). - -* psxspu: Fixed SpuKeyOn() bug where only 16 of the 24 channels can be - activated, due to channel bits being written as a 16-bit word. - -* psxcd: Added CdOpenDir(), CdReadDir() and CdCloseDir() for parsing directories. - -* psxcd: Issuing CdlNop (GetStat) commands now triggers the media change flag - internal to the libraries when CD lid is opened, so file system functions can - update the cached ISO descriptor when the disc has been changed. - -* psxcd: Made internal variables and functions for iso9660 parsing static. - - -02-25-2020 by thp: - -* libc: Added abs and labs() functions. - - -01-06-2020 by Lameguy64: - -* libpsn00b: Renamed libpsxcd directory to psxcd to match with other libraries. - -* examples: Added obligatory hello world example. - -* documentation: Added CD-ROM library documentation. - - -11-22-2019 by Lameguy64: - -* psxapi: Added root counter or timer functions and related definitions. - -* examples: Added timer example. - -* psxgpu: Added DR_AREA, DR_OFFSET and DR_TWIN primitives and accompanying - setDrawArea(), setDrawOffset() and setTexWindow() macros. - -* psxgpu: Added parenthesis to argument value in setlen(), setaddr() and - setcode() macros, preventing addPrims() from being used in a more - sensible manner (ie. addPrims(ot, sub_ot+3, sub_ot)). - -* examples: Added render2tex render to texture example. - -* psxspu: Fixed typo in spuinit.s on section specifier specifying a data - section instead of text section, resulting to jump to - non-instruction-aligned linker errors. - -* psxgpu: Increased ISR stack size to 2048 bytes. - -* psxsio: Added kbhit() to poll keyboard input asynchronously and stdin - is now buffered with an IRQ handler. - -* psxapi: Added AddDummyTty() (for psxsio DelSIO() fix). - -* psxsio: DelSIO() now calls AddDummyTty(). - -* libc: Fixed bug in strncpy() not placing a NULL byte at end of string. - -* libc: Fixed strchr() and strrchr() declarations commented out in string.h. - -* libpsn00b: Added the long awaited libpsxcd library with cdxa example. - Documentation will come soon but existing libcd docs should be good - enough for awhile. - -* psxgpu: Fixed non functioning GPU DMA wait in DrawSync(). - -* LibPSn00b run-time library is now officially 0.15b. - - -10-11-2019 by Lameguy64: - -* psxetc: Added FntOpen(), FntPrint() and FntFlush() functions. - -* psxgpu: Fixed typo in termPrim() macro (value too long). - -* examples: Added billboarding sprites example. - -* psxapi: Transferred putchar() BIOS function to libc. - -* libpsn00b: Updated makefiles adding -fdata-sections and -ffunction-sections - so unused functions will be stripped out, which yields smaller executables. - -* libc: Fixed negative integers not displaying properly in vsprintf()/vsnprintf(). - -* libc: Fixed zero padding not working in vsprintf()/vsnprintf(). - -* fpscam: Added debug text using FntOpen(), FntPrint() and FntFlush(). Also added - _boot() call for returning to CD based serial loaders. - - -09-23-2019 by Lameguy64: - -* libc: Added strcat() function. - -* Included PSn00bSDK logo vector. - -* psxgte: Added gte_stsz() macro. - -* psxgpu: Fixed typos in setUVWH() macro. - -* Added _boot() BIOS function (A(A0h) aka Warmboot, useful for CD based - serial loaders). - - -08-17-2019 by Lameguy64: - -* LibPSn00b run-time library is officially version 0.13b. - -* examples: updated balls and n00bdemo examples for the setDrawTPage() - correction. - -* psxgpu: Fixed error on setDrawTPage() syntax and removed setDrawTPageVal() - macro (not needed). - -* psxapi: Added event handler definitions related to memory cards. - -* psxapi: Added BIOS memory card functions. - -* examples: Added childexec example demonstrating a parent executable - transferring execution to a child executable and back. - -* elf2x: s_addr no longer set on PS-EXE header, console BIOS already sets it - by STACK value in SYSTEM.CNF and allows child executables to return to - parent if left zero. - -* psxapi: Renamed _InitPad(), _StartPad() and _StopPad() to InitPAD(), - StartPAD() and StopPAD() respectively. Fpscam example updated to new - syntax. - - -07-18-2019 by Lameguy64: - -* Added fpscam example. - -* Gave examples small descriptions. - -* Updated readme a little. - - -07-17-2019 by Lameguy64: - -* LibPSn00b run-time library is officially version 0.12b. - -* libc: Added basic C++ support (many thanks to ijacquez). - -* libc: Updated start function that should make it possible for a child - executable to return to a parent executable, return logic automatically - calls EnterCriticalSection(). - -* libc: Updated build method which takes libgcc from the compiler and adds - its own object files into it, eliminating linker problems caused by having - to order libc and libgcc libraries in a specific manner. - -* psxgpu: Added RestartCallback(). - -* psxgpu: Added StoreImage() function. - -* psxgpu: Fixed bugged setRECT() macro. - -* libc: Added assert.h. - -* examples: Balls example now has 166% more balls. - -* psxgpu: Increased ISR stack size to 1024 bytes so printf can be used in - callbacks safely. - -* libc: Removed int64 (long long) printing in vsprintf() for better - performance, as the R3000 does not support 64-bit arithmetic natively - so its emulated like floats. int64 still used for processing floats and - doubles and old vsprintf.c file is still included for those who really - want int64 support for whatever reason. - -* libc: Removed stdarg.h which is part of GCC and not license compatible - with MPL. The toolchain compiled with libgcc provides stdarg.h and other - standard headers. - -* examples: Updated sdk-common.mk variable convention for better flexibility. - -* libpsn00b: Added common.mk file containing global values for all libraries. - -* Updated library and toolchain build instructions. - -* psxgpu: Fixed bug in DMACallback where the internal DMA handler would fail - to install due to GetInterruptCallback() retrieving the callback value - immediately in the branch delay slot of a jr instruction, which resuls to - an inconsistent return value. This also broke DrawSyncCallback(). - -* psxsio: Done fixes on _sio_control() from the aformentioned issues with load - instructions in delay slots. - -* psxgte: Added DVECTOR struct. - -* psxgpu: Added setLineF2(), setLineG2(), setLineF3() and setLineG3() - primitive macros. - -* Added more functions in documentation. - - -07-01-2019 by williamblair: - -* Fixed FntLoad() Y coordinate not working properly for debug font (due to - Y coordinate not being specified for the getTPage() macro. - - -06-23-2019 by Lameguy64: - -* LibPSn00b Run-time Library is officially version 0.10b. - -* Reworked readme file with improved build instructions. - -* libpsn00b: Added missing C extern groups for C++ compatibility in - psxapi.h, stdlib.h and string.h. - -* libpsn00b: Removed changelogs in the readmes of each libpsn00b library - as its much easier to keep track of changes in a single changelog than - scattering them across multiple changelogs. - -* psxapi: Added Exec() function and EXEC struct. - -* psxgpu: Added DrawPrim() function (uses direct I/O). - -* psxgpu: VSync() function completely overhauled. Logic is now based on - Sony's code but more efficient and can return total number of vblanks - elapsed, number of hblanks since last call and wait up to n vblanks - specified by an appropriate argument value. It will also generate a - timeout when vblank interrupt stops working and would attempt to - restart it. - -* psxapi: Added gets() and getc() BIOS functions. - -* psxapi: Corrected a putc()/putchar() discrepancy. putc() puts a character - to a file stream, putchar() puts a character to stdout. - -* Completely revised library reference manual with better formatting and - more functions documented. - -* psxgpu: Added DrawSyncCallback(). - -* psxgpu: Implemented DMA interrupt callback system with DMACallback() - allowing to handle DMA interrupts very easily. - -* libpsn00b: Implemented Serial I/O library (psxsio) with serial tty device - (installed using AddSIO) and serial callback support. Serial library - is also fully documented. - -* psxgpu: Implemented IRQ callback system with InterruptCallback() allowing - to set interrupt callbacks very easily. - -* psxgpu: Implemented proper IRQ handler installed using HookEntryInt or - SetCustomExitFromException() for handling VSync and other interrupts. - ChangeClearPAD(0) must now be called after _InitPad() or vsync timeout - will occur. - -* libpsn00b: Started making local labels prefixed with .L instead of . making - them not appear in symbol lists resulting in a cleaner symbol dump. Still - not possible to do function-scope local labels like in ASMPSX because GAS - syntax is ASS (or ASS GAS which is farts, GAS is farts). - -* psxgpu: DrawSync() function now waits for DMA completion and GPU transfer - ready instead of simply waiting for GPU transfer ready which is the likely - cause of subtle GPU related timing issues, it also sets GPU DMA transfer - mode to off afterwards. It can also read number of words remaining in DMA - transfer if a0 is non-zero but it likely only returns the correct value on - VRAM transfers. Exact way how DrawSync() returns the count in the official - SDK is currently unknown. - - -06-07-2019 by qbradq: - -* lzpack: Swapped a buffer length litteral with sizeof operator, fixing a - stack overflow bug in some instances. - - -05-23-2019 by Lameguy64: - -* Added dev notes.txt file in docs that includes notes about the many - quirks about the console discovered during the development of this - SDK project. - -* Updated libn00bref.odf a little. - -* Added turboboot example. - -* Added rgb24 example. - -* Got custom exit handler set using SetCustomExitFromException() (BIOS - function B(19h)) working. Currently used to acknowledge VSync IRQ but - actual VSync handling is still done with events and needs to be - transferred to the custom exit handler. At least it lets BIOS - controller functions to work now. See doc/dev notes.txt for details - on how this handler behaves. - -* Made stack usage in ResetGraph() less wasteful. You only need to - allocate N words on stack based on N arguments of the function - being called. - -* VSync IRQ handling now done using a custom exit from exception handler. - Actual VSync handling is yet to be moved to the custom exit handler - though. - -* Made stack usage in start.s of libc a lot less wasteful. - -* Implemented controller support via BIOS functions (use _InitPad(), - _StartPad() and _StopPad()). BIOS memory card functions may also - work as well but its not tested yet. - -* Removed duplicate initpad.s and initcard.s functions in psxapi. - -* Added _InitCd() function to psxapi which is a safer version of _96_init() - as it preserves other DMA channel settings. Use BIOS file functions such - as open(), read() and close() with path names starting with cdrom:/ to - access files from CD after calling _InitCd(). -- cgit v1.2.3 From 6d9ceda63aefe8910e798b6b38a7783d00b855f1 Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Sun, 28 Nov 2021 17:40:48 +0100 Subject: Refactor libpsn00b CMake scripts, move include directory --- cpack/setup.cmake | 14 +++- libpsn00b/CMakeLists.txt | 21 ++++- libpsn00b/build.json.template | 5 ++ libpsn00b/cmake/flags.cmake | 148 ++++++++++++++++++++++++++++++++++ libpsn00b/cmake/internal_setup.cmake | 22 +++-- libpsn00b/cmake/virtual_targets.cmake | 109 ------------------------- libpsn00b/ldscripts/dll.ld | 4 +- libpsn00b/ldscripts/exe.ld | 2 +- 8 files changed, 200 insertions(+), 125 deletions(-) create mode 100644 libpsn00b/build.json.template create mode 100644 libpsn00b/cmake/flags.cmake delete mode 100644 libpsn00b/cmake/virtual_targets.cmake diff --git a/cpack/setup.cmake b/cpack/setup.cmake index 33f127e..57568bb 100644 --- a/cpack/setup.cmake +++ b/cpack/setup.cmake @@ -14,10 +14,10 @@ set( BUNDLE_TOOLCHAIN OFF CACHE BOOL "Include the GCC toolchain in installer packages" ) -set( - BUNDLE_CMAKE OFF - CACHE BOOL "Include CMake in installer packages (Windows only)" -) +#set( + #BUNDLE_CMAKE OFF + #CACHE BOOL "Include CMake in installer packages (Windows only)" +#) ## Bundled components @@ -134,6 +134,7 @@ set( CPACK_NSIS_MENU_LINKS "${PROJECT_HOMEPAGE_URL}" "About PSn00bSDK" "https://github.com/Lameguy64/PSn00bSDK" "GitHub repo" + "Uninstall.exe" "Uninstall PSn00bSDK" ) # Paths in CPACK_NSIS_* variables are not converted to native paths by CMake @@ -166,6 +167,11 @@ cpack_add_component( DISPLAY_NAME "SDK documentation" DESCRIPTION "Select to install additional documentation files and a project template (recommended)." ) +cpack_add_component( + examples + DISPLAY_NAME "SDK examples" + DESCRIPTION "Select to copy the examples' source code to the documentation folder (recommended)." +) if(BUNDLE_TOOLCHAIN) cpack_add_component( diff --git a/libpsn00b/CMakeLists.txt b/libpsn00b/CMakeLists.txt index 7b5f7a5..2b2e76d 100644 --- a/libpsn00b/CMakeLists.txt +++ b/libpsn00b/CMakeLists.txt @@ -58,12 +58,29 @@ add_custom_command( ## Installation install( - TARGETS ${PSN00BSDK_LIBRARIES} ${PSN00BSDK_VIRTUAL_TARGETS} + TARGETS ${PSN00BSDK_LIBRARIES} DESTINATION ${CMAKE_INSTALL_LIBDIR}/libpsn00b EXPORT libpsn00b ) install( - DIRECTORY cmake include ldscripts + DIRECTORY cmake ldscripts + DESTINATION ${CMAKE_INSTALL_LIBDIR}/libpsn00b +) +install( + DIRECTORY include/ + DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/libpsn00b +) + +# Generate build.json. This file is used to determine the SDK version after +# installation and may contain additional metadata about the build. +string(TIMESTAMP PSN00BSDK_BUILD_DATE UTC) +configure_file( + build.json.template build.json + ESCAPE_QUOTES + NEWLINE_STYLE LF +) +install( + FILES ${PROJECT_BINARY_DIR}/build.json DESTINATION ${CMAKE_INSTALL_LIBDIR}/libpsn00b ) diff --git a/libpsn00b/build.json.template b/libpsn00b/build.json.template new file mode 100644 index 0000000..666bb43 --- /dev/null +++ b/libpsn00b/build.json.template @@ -0,0 +1,5 @@ +{ + "version": "${PSN00BSDK_VERSION}", + "build_date": "${PSN00BSDK_BUILD_DATE}", + "build_info": "${PSN00BSDK_BUILD_INFO}" +} diff --git a/libpsn00b/cmake/flags.cmake b/libpsn00b/cmake/flags.cmake new file mode 100644 index 0000000..e163e52 --- /dev/null +++ b/libpsn00b/cmake/flags.cmake @@ -0,0 +1,148 @@ +# libpsn00b interface targets +# (C) 2021 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_object_lib (same as psn00bsdk_common) +# - psn00bsdk_static_exe +# - psn00bsdk_dynamic_exe +# - 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. + +add_library(psn00bsdk_common INTERFACE) + +foreach( + _target IN ITEMS + object_lib + static_exe + dynamic_exe + static_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 + -mdivide-breaks + -O2 + # Standard library options + -ffreestanding + -fno-builtin + -nostdlib + # Other options + -fdata-sections + -ffunction-sections + -fsigned-char + -fno-strict-overflow + -fdiagnostics-color=always + $<$: + # C++ options + -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 + $<$: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 + -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 + -G0 + -fno-pic + -mno-abicalls + -mno-gpopt + -Wa,--strip-local-absolute +) + +# 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 + -G0 + -fPIC + -mabicalls + -mno-gpopt + -mshared +) +target_link_options( + psn00bsdk_shared_lib INTERFACE + -G0 + -shared +) + +target_link_libraries(psn00bsdk_module_lib INTERFACE psn00bsdk_shared_lib) diff --git a/libpsn00b/cmake/internal_setup.cmake b/libpsn00b/cmake/internal_setup.cmake index b0c4591..1d63e92 100644 --- a/libpsn00b/cmake/internal_setup.cmake +++ b/libpsn00b/cmake/internal_setup.cmake @@ -7,8 +7,14 @@ cmake_minimum_required(VERSION 3.20) include(GNUInstallDirs) -# IMPORTANT TODO: set a version number -set(PSN00BSDK_VERSION 0.1.0) +# Fetch the SDK version number from build.json. +if(NOT DEFINED PSN00BSDK_VERSION) + file(READ ${CMAKE_CURRENT_LIST_DIR}/../build.json _json) + + string(JSON PSN00BSDK_VERSION GET ${_json} version) + string(JSON PSN00BSDK_BUILD_DATE GET ${_json} build_date) + string(JSON PSN00BSDK_BUILD_INFO GET ${_json} build_info) +endif() ## Settings (can be overridden by projects) @@ -22,9 +28,7 @@ set(PSN00BSDK_SYMBOL_MAP_SUFFIX ".map") set(PSN00BSDK_LIBRARIES psxgpu psxgte psxspu psxcd psxsio psxetc psxapi lzp c) include(${CMAKE_CURRENT_LIST_DIR}/libpsn00b.cmake OPTIONAL) -if(NOT TARGET psn00bsdk_common) - include(${CMAKE_CURRENT_LIST_DIR}/virtual_targets.cmake) -endif() +include(${CMAKE_CURRENT_LIST_DIR}/flags.cmake) # Use the toolchain path to find libgcc (used to build libpsn00b). Of course # different installers, packages and distros have different opinions when it @@ -59,14 +63,18 @@ set( find_program(ELF2X elf2x HINTS ${PSN00BSDK_TOOLS}) find_program(ELF2CPE elf2cpe HINTS ${PSN00BSDK_TOOLS}) -find_program(SMXLINK elf2x HINTS ${PSN00BSDK_TOOLS}) +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 -set(PSN00BSDK_INCLUDE ${CMAKE_CURRENT_LIST_DIR}/../include) 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() # psn00bsdk_add_executable( # diff --git a/libpsn00b/cmake/virtual_targets.cmake b/libpsn00b/cmake/virtual_targets.cmake deleted file mode 100644 index 9afcebd..0000000 --- a/libpsn00b/cmake/virtual_targets.cmake +++ /dev/null @@ -1,109 +0,0 @@ -# libpsn00b interface targets -# (C) 2021 spicyjpeg - MPL licensed - -# This script creates several "virtual" targets (psn00bsdk_*) that set include -# directories and compiler flags when a target is linked against them. These -# all end up in the autogenerated libpsn00b setup script after installation, -# so this file is only included when building libpsn00b. - -# The following targets are currently defined: -# - psn00bsdk_common -# - psn00bsdk_object_lib (same as psn00bsdk_common) -# - psn00bsdk_static_exe -# - psn00bsdk_dynamic_exe -# - psn00bsdk_static_lib -# - psn00bsdk_shared_lib -# - psn00bsdk_module_lib (same as psn00bsdk_shared_lib) - -include(GNUInstallDirs) - -set(PSN00BSDK_VIRTUAL_TARGETS "") - -macro(_add_interface_target name) - add_library(${name} INTERFACE) - list(APPEND PSN00BSDK_VIRTUAL_TARGETS ${name}) - - target_compile_options( - ${name} INTERFACE - ${_aflags} - $<$:${_cflags}> - $<$:${_cxxflags}> - ) - target_link_options (${name} INTERFACE ${_ldflags}) - target_link_libraries(${name} INTERFACE ${ARGN}) -endmacro() - -macro(_add_alias_target name) - #add_library(${name} ALIAS ${ARGN}) - add_library(${name} INTERFACE) - list(APPEND PSN00BSDK_VIRTUAL_TARGETS ${name}) - - target_link_libraries(${name} INTERFACE ${ARGN}) -endmacro() - -# Options common to all target types: -# - Define PLAYSTATION=1 and set include directories -# - 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 -set(_aflags -msoft-float -march=r3000 -mtune=r3000 -mabi=32) -set(_cflags -mdivide-breaks -O2 -ffreestanding -fno-builtin -nostdlib -fdata-sections -ffunction-sections -fsigned-char -fno-strict-overflow -fdiagnostics-color=always) -set(_cxxflags -fno-exceptions -fno-rtti -fno-unwind-tables -fno-threadsafe-statics -fno-use-cxa-atexit) -set(_ldflags -nostdlib -Wl,-gc-sections) - -_add_interface_target(psn00bsdk_common) -_add_alias_target (psn00bsdk_object_lib psn00bsdk_common) - -target_compile_definitions( - psn00bsdk_common INTERFACE - PLAYSTATION=1 - $<$: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) -set(_aflags -G8) -set(_cflags -mno-abicalls -mgpopt -mno-extern-sdata) -set(_cxxflags) -set(_ldflags -G8 -static) - -_add_interface_target(psn00bsdk_static_exe psn00bsdk_common) - -# Options for executables with support for dynamic linking: -# - Position-independent code disabled -# - GP-relative addressing disabled -# - ABI-compatible calls disabled (must be performed manually) -set(_aflags -G0) -set(_cflags -mno-abicalls -mno-gpopt) -set(_cxxflags) -set(_ldflags -G0 -static) - -_add_interface_target(psn00bsdk_dynamic_exe psn00bsdk_common) - -# Options for static libraries: -# - GP-relative addressing disabled -# - ABI-compatible calls disabled -# - Local stripping enabled -set(_aflags -G0 -Wa,--strip-local-absolute) -set(_cflags -mno-abicalls -mno-gpopt) -set(_cxxflags) -set(_ldflags) - -_add_interface_target(psn00bsdk_static_lib psn00bsdk_common) - -# Options for dynamically-loaded libraries: -# - Position-independent code enabled -# - GP-relative addressing disabled (incompatible with ABI calls) -# - ABI-compatible calls enabled -set(_aflags -G0) -set(_cflags -mabicalls -mshared -mno-gpopt -fPIC) -set(_cxxflags) -set(_ldflags -G0 -shared) - -_add_interface_target(psn00bsdk_shared_lib psn00bsdk_common) -_add_alias_target (psn00bsdk_module_lib psn00bsdk_shared_lib) diff --git a/libpsn00b/ldscripts/dll.ld b/libpsn00b/ldscripts/dll.ld index 59cbb53..a03a504 100644 --- a/libpsn00b/ldscripts/dll.ld +++ b/libpsn00b/ldscripts/dll.ld @@ -7,8 +7,8 @@ * replaced by the global offset table (GOT) and .bss being merged with .data. */ -OUTPUT_FORMAT(elf32-littlemips) -/*ENTRY(_start) +/*OUTPUT_FORMAT(elf32-littlemips) +ENTRY(_start) STARTUP(start.o)*/ MEMORY { diff --git a/libpsn00b/ldscripts/exe.ld b/libpsn00b/ldscripts/exe.ld index 3033636..c6b9f29 100644 --- a/libpsn00b/ldscripts/exe.ld +++ b/libpsn00b/ldscripts/exe.ld @@ -8,7 +8,7 @@ * compatible with dynamic linking, as DLLs require GP to be unused. */ -OUTPUT_FORMAT(elf32-littlemips) +/*OUTPUT_FORMAT(elf32-littlemips)*/ ENTRY(_start) /*STARTUP(start.o)*/ -- cgit v1.2.3 From a75b3fa4b1a1b882c33f533645ddae75c09dd697 Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Sun, 28 Nov 2021 18:15:14 +0100 Subject: Switch to mipsel-none-elf, move docs, add template presets --- CMakeLists.txt | 17 +++- INSTALL.md | 156 ------------------------------- README.md | 2 +- TOOLCHAIN.md | 224 --------------------------------------------- doc/cmake_reference.md | 69 +++++++++----- doc/dev notes.txt | 15 +-- doc/installation.md | 155 +++++++++++++++++++++++++++++++ doc/toolchain.md | 224 +++++++++++++++++++++++++++++++++++++++++++++ libpsn00b/cmake/sdk.cmake | 8 +- template/CMakeLists.txt | 9 +- template/CMakePresets.json | 23 +++++ 11 files changed, 477 insertions(+), 425 deletions(-) delete mode 100644 INSTALL.md delete mode 100644 TOOLCHAIN.md create mode 100644 doc/installation.md create mode 100644 doc/toolchain.md create mode 100644 template/CMakePresets.json diff --git a/CMakeLists.txt b/CMakeLists.txt index 87980a5..720ca8c 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -26,11 +26,11 @@ include(GNUInstallDirs) # These are passed through to libpsn00b and the examples (they are defined in # the toolchain file). set( - PSN00BSDK_TC $ENV{PSN00BSDK_TC} - CACHE PATH "Path to the GCC toolchain's installation directory" + PSN00BSDK_TC "" + CACHE PATH "Path to the GCC toolchain's installation directory (if not in PATH)" ) set( - PSN00BSDK_TARGET mipsel-unknown-elf + PSN00BSDK_TARGET mipsel-none-elf CACHE STRING "GCC toolchain target triplet" ) @@ -38,6 +38,10 @@ set( SKIP_EXAMPLES OFF CACHE BOOL "Skip building SDK examples (not required for installation)" ) +set( + BUILD_INFO "" + CACHE STRING "Information about this build to be included in build.json" +) # Forward some important variables to mkpsxiso and to the subprojects (they are # not inherited automatically as they are not environment variables). This also @@ -49,6 +53,8 @@ set( COMMON_ARGS -DPSN00BSDK_TC:PATH=${PSN00BSDK_TC} -DPSN00BSDK_TARGET:STRING=${PSN00BSDK_TARGET} + -DPSN00BSDK_VERSION:STRING=${PROJECT_VERSION} + -DPSN00BSDK_BUILD_INFO:STRING=${BUILD_INFO} -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} ) set( @@ -118,6 +124,11 @@ install( DESTINATION ${CMAKE_INSTALL_DATADIR}/psn00bsdk COMPONENT docs ) +install( + DIRECTORY examples + DESTINATION ${CMAKE_INSTALL_DATADIR}/psn00bsdk + COMPONENT examples +) ## CPack configuration diff --git a/INSTALL.md b/INSTALL.md deleted file mode 100644 index 5ae56f5..0000000 --- a/INSTALL.md +++ /dev/null @@ -1,156 +0,0 @@ - -# Getting started with PSn00bSDK - -## Building and installing - -The instructions below are for Windows and Linux. Building on macOS hasn't been -tested extensively yet, however it should work once the GCC toolchain is built -and installed properly. - -1. Install prerequisites and a host compiler toolchain. On Linux (most distros) - install the following packages from your distro's package manager: - - - `git` - - `build-essential`, `base-devel` or similar - - `make` or `ninja-build` - - `cmake` (3.20+ is required, download it from - [here](https://cmake.org/download) if your package manager only provides - older versions) - - On Windows install [MSys2](https://www.msys2.org), then open the "MSys2 - MSYS" shell and run this command: - - ```bash - pacman -Syu git mingw-w64-x86_64-make mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc - ``` - - If you are prompted to close the shell, you may have to reopen it afterwards - and rerun the command to finish installation. - **Do not use the MSys2 shell for the next steps**, use a regular command - prompt or PowerShell instead. - - Add these directories (replace `C:\msys64` if you installed MSys2 to a - different location) to the `PATH` environment variable using System - Properties: - - - `C:\msys64\mingw64\bin` - - `C:\msys64\usr\bin` - -2. Build and install a GCC toolchain for `mipsel-unknown-elf`, as detailed in - [TOOLCHAIN.md](TOOLCHAIN.md). On Windows, you may download a precompiled - version from [Lameguy64's website](http://lameguy64.net?page=psn00bsdk) and - extract it into one of the directories listed below instead. - - **NOTE**: PSn00bSDK is also compatible with toolchains that target - `mipsel-none-elf`. If you already have such a toolchain (e.g. because you - have another PS1 SDK installed) you can skip this step. Make sure you pass - `-DPSN00BSDK_TARGET=mipsel-none-elf` to CMake when configuring the SDK - though (see step 5). - -3. If you chose a non-standard install location for the toolchain, add the - `bin` subfolder (inside the top-level toolchain directory) to the `PATH` - environment variable. This step is unnecessary if you installed/extracted - the toolchain into any of these directories: - - - `C:\Program Files\mipsel-unknown-elf` - - `C:\Program Files (x86)\mipsel-unknown-elf` - - `C:\mipsel-unknown-elf` - - `/usr/local/mipsel-unknown-elf` - - `/usr/mipsel-unknown-elf` - - `/opt/mipsel-unknown-elf` - -4. Clone the PSn00bSDK repo, then run the following command from the PSn00bSDK - repository to download additional dependencies: - - ```bash - git submodule update --init --recursive --remote - ``` - -5. Compile the libraries, tools and examples using CMake: - - ```bash - cmake --preset default . - cmake --build ./build - ``` - - If you want to install the SDK to a custom location rather than the default - one (`C:\Program Files\PSn00bSDK` or `/usr/local` depending on your OS), add - `--install-prefix ` to the first command. Add - `-DPSN00BSDK_TARGET=mipsel-none-elf` if your toolchain targets - `mipsel-none-elf` rather than `mipsel-unknown-elf`. - - **NOTE**: Ninja is used by default to build the SDK. If you can't get it to - work or don't have it installed, pass `-G "Unix Makefiles"` (or - `-G "MSYS Makefiles"` on Windows) to the first command to build using `make` - instead. - -6. Install the SDK to the path you chose (add `sudo` or run it from a command - prompt with admin privileges if necessary): - - ```bash - cmake --install ./build - ``` - - This will create and populate the following directories: - - - `/bin` - - `/lib/libpsn00b` - - `/share/psn00bsdk` - -7. Set the `PSN00BSDK_LIBS` environment variable to point to the `lib/libpsn00b` - subfolder inside the install directory. You might also want to add the `bin` - folder to `PATH` if it's not listed already. - -Although not strictly required, you'll probably want to install a PS1 emulator -with debugging capabilities such as [no$psx](https://problemkaputt.de/psx.htm) -(Windows only), [DuckStation](https://github.com/stenzek/duckstation) or -[pcsx-redux](https://github.com/grumpycoders/pcsx-redux). -**Avoid ePSXe and anything based on MAME** as they are inaccurate. - -## Building installer packages - -CPack can be used to build NSIS-based installers, DEB/RPM packages and zipped -releases that include built SDK libraries, headers as well as the GCC toolchain. -Distributing prebuilt releases is however discouraged since PSn00bSDK is still -far from being feature-complete. - -1. Follow steps 1-4 above to set up the toolchain, then install - [NSIS](https://nsis.sourceforge.io/Download) on Windows or `dpkg` and `rpm` - on Linux. - -2. Run the following commands from the PSn00bSDK directory (pass the - appropriate options to the first command if necessary): - - ```bash - cmake --preset package . - cmake --build ./build -t package - ``` - - All built packages will be copied to the `build/packages` folder. - -## Creating a project - -1. Copy the contents of `/share/psn00bsdk/template` (or the - `template` folder within the repo) to your new project's root directory. - -2. Configure and build the template by running: - - ```bash - cmake -S . -B ./build - cmake --build ./build - ``` - - If you did everything correctly there should be a `template.bin` CD image in - the `build` folder. Test it in an emulator to ensure it works. - -Note that, even though the template relies on the `PSN00BSDK_LIBS` environment -variable to locate the SDK by default, you can also specify the path directly -on the CMake command line by adding -`-DCMAKE_TOOLCHAIN_FILE=/lib/libpsn00b/cmake/sdk.cmake` to the -CMake command line. - -The toolchain script defines a few CMake macros to create PS1 executables, DLLs -and CD images. See the [reference](doc/cmake_reference.md) for details. - ------------------------------------------ -_Last updated on 2021-11-19 by spicyjpeg_ diff --git a/README.md b/README.md index a8807df..6fd4cd1 100644 --- a/README.md +++ b/README.md @@ -80,7 +80,7 @@ As of August 16, 2021 ## Obtaining PSn00bSDK PSn00bSDK has switched to a CMake-based build and installation system. See -[INSTALL.md](INSTALL.md) for details. +[installation.md](doc/installation.md) for details. Because PSn00bSDK is updated semi-regularly due to this project being in a work-in-progress state, it is better to obtain this SDK from source and diff --git a/TOOLCHAIN.md b/TOOLCHAIN.md deleted file mode 100644 index 2a78ccb..0000000 --- a/TOOLCHAIN.md +++ /dev/null @@ -1,224 +0,0 @@ - -# Building the GCC toolchain - -If you wish to build the toolchain yourself, beware that this process can get -pretty tedious if your machine is not fairly recent. Ensure you have at least a -quad-core processor and 4 GB of free space before continuing. - -You'll need a Linux environment, even if you want to build a Windows toolchain -(as GCC is basically impossible to build under Windows but can be cross-compiled -via MinGW). Due to how the GCC build process works, you'll have to build a Linux -version of the toolchain first to be able to compile it for Windows. This -basically means you will have to build the whole toolchain twice if you want to -target Windows. - -These instructions are for Debian/Ubuntu, however it should be relatively easy -to follow them if you are using another distro. If you do not have access to a -Linux system already, consider spinning up a VM (a headless Debian or Ubuntu -Server install is recommended) or using WSL, whose setup is out of the scope of -this guide. - -## Choosing a GCC version - -PSn00bSDK *should* work with any GCC version. In most cases you'll want to get -the latest stable release of GCC and binutils. If for some reason you are having -problems you may try building one of the following versions, which have been -tested extensively: - -- ~~GCC 7.4.0 with binutils 2.31~~ (the linker fails to build PS1 DLLs) -- GCC **11.1.0** with binutils **2.36** -- GCC **11.2.0** with binutils **2.37** - -If you wish to pick an older GCC release but don't know which binutils version -it requires, see [here](https://wiki.osdev.org/Cross-Compiler_Successful_Builds) -for a compatibility table. - -## Downloading GCC - -1. Run the following commands to install a host toolchain and prerequisites - (adapt them for non-Debian distros if necessary): - - ```bash - sudo apt update - sudo apt install -y build-essential make wget - ``` - -2. Create an empty directory to store build artifacts in. You'll be able to - delete it once the toolchain is installed. - -3. Download the GCC and binutils source packages from - [here](https://ftpmirror.gnu.org/gnu) and unzip them into the folder you - created, or run the following commands to do the same (replace `` - with the versions you chose): - - ```bash - wget https://ftpmirror.gnu.org/gnu/binutils/binutils-.tar.xz - wget https://ftpmirror.gnu.org/gnu/gcc/gcc-/gcc-.tar.xz - tar xvf binutils-.tar.xz - tar xvf gcc-.tar.xz - rm -f *.tar.xz - ``` - -4. From the extracted GCC directory run the `download_prerequisites` script to - download additional dependencies: - - ```bash - cd gcc- - ./contrib/download_prerequisites - ``` - -## Building binutils - -1. Go back to the folder you made earlier and create a new subdirectory to build - binutils in (don't create it inside the extracted binutils source directory). - Call it `binutils-build` or whatever. - -2. Run the binutils configuration script from that folder: - - ```bash - ../binutils-/configure \ - --prefix=/usr/local/mipsel-unknown-elf --target=mipsel-unknown-elf \ - --disable-docs --disable-nls --with-float=soft - ``` - - Replace `` as usual. If you don't want to install the toolchain into - `/usr/local/mipsel-unknown-elf` you can change the `--prefix` option. - -3. Compile and install binutils (this will take a few minutes to finish): - - ```bash - make -j 4 - sudo make install-strip - ``` - - Increase `-j 4` to speed up the build if your machine or VM has more than 4 - CPU cores. - - **NOTE**: if the build fails with a "`uint` undeclared" or similar error, try - editing the source file that caused the error and pasting this line at the - beginning: - - ```c - typedef unsigned int uint; - ``` - - Rerun `make` to resume the build after saving the file. - -## Building GCC - -The process is mostly the same as binutils, just with different configuration -options. - -1. Go back to the main directory and create an empty `gcc-build` (or whatever) - subfolder. - -2. Run the GCC configuration script from there: - - ```bash - ../gcc-/configure \ - --prefix=/usr/local/mipsel-unknown-elf --target=mipsel-unknown-elf \ - --disable-docs --disable-nls --disable-libada --disable-libssp \ - --disable-libquadmath --disable-libstdc++-v3 --with-float=soft \ - --enable-languages=c,c++ --with-gnu-as --with-gnu-ld - ``` - - If you previously set a custom installation path, remember to set it here as - well (it must be the same). - -3. Compile and install GCC (will take a long time, usually around half an hour): - - ```bash - make -j 4 - sudo make install-strip - ``` - - Increase `-j 4` to speed up the build if your machine or VM has more than 4 - threads. - -4. Add the toolchain to the `PATH` environment variable. This is required to - rebuild the toolchain for Windows (see below), but it will also allow - PSn00bSDK to find the toolchain if you installed it in a custom location. - - Edit the `.bashrc` or `.bash_profile` file in your home directory and add - this line at the end (replace the path with the install location you chose - earlier, but keep the `/bin` at the end): - - ```bash - export PATH=$PATH:/usr/local/mipsel-unknown-elf/bin - ``` - - Restart the shell by closing and reopening the terminal window or SSH - connection afterwards. - -## Rebuilding for Windows - -At this point you should be able to build and install PSn00bSDK on your Linux -system. The instructions below are for building a second copy of the toolchain -that runs on Windows. - -1. Install the MinGW host toolchain: - - ```bash - sudo apt install -y g++-mingw-w64-x86-64 - ``` - -2. Create two new `binutils-win` and `gcc-win` folders in the directory you - extracted/built everything in. - -3. From the `binutils-win` directory, rerun the binutils configuration script - with the following options (do not change the installation path): - - ```bash - ../binutils-/configure \ - --build=x86_64-linux-gnu --host=x86_64-w64-mingw32 \ - --prefix=/tmp/mipsel-unknown-elf --target=mipsel-unknown-elf \ - --disable-docs --disable-nls --with-float=soft - ``` - - Then build binutils again: - - ```bash - make -j 4 - make install-strip - ``` - -4. Do the same for GCC from the `gcc-win` directory: - - ```bash - ../gcc-/configure \ - --build=x86_64-linux-gnu --host=x86_64-w64-mingw32 \ - --prefix=/tmp/mipsel-unknown-elf --target=mipsel-unknown-elf \ - --disable-docs --disable-nls --disable-libada --disable-libssp \ - --disable-libquadmath --disable-libstdc++-v3 --with-float=soft \ - --enable-languages=c,c++ --with-gnu-as --with-gnu-ld - ``` - - And build it as usual: - - ```bash - make -j 4 - make install-strip - ``` - -5. Copy the entire `/tmp/mipsel-unknown-elf` directory over to your Windows - machine using VM shared folders, a network share, `scp` or whichever method - you prefer. It's recommended to put the toolchain in - `C:\Program Files\mipsel-unknown-elf` or `C:\mipsel-unknown-elf`. - -6. If you want to keep the toolchain in another location and/or use it from the - command line, add the `bin` subdirectory inside `mipsel-unknown-elf` to the - `PATH` environment variable (as you did on Linux) using System Properties. - -## Note regarding C++ support - -C++ support in PSn00bSDK, besides compile-time features like `constexpr`, only -goes as far as basic classes, namespaces and the ability to dynamically create -and delete class objects at any point of the program. The required dependencies -(which are just wrappers around `malloc()` and `free()`) are supplied by `libc`. - -Standard C++ libraries are not implemented and likely never going to be -implemented due to bloat concerns that it may introduce. Besides, the official -SDK lacks full C++ support as well. - ------------------------------------------ -_Last updated on 2021-10-31 by spicyjpeg_ diff --git a/doc/cmake_reference.md b/doc/cmake_reference.md index 3b586ab..9e134d9 100644 --- a/doc/cmake_reference.md +++ b/doc/cmake_reference.md @@ -10,15 +10,44 @@ can be done on the command line (`-DCMAKE_TOOLCHAIN_FILE=...`), in `CMakeLists.txt` (before calling `project()`) or using a [preset](https://cmake.org/cmake/help/latest/manual/cmake-presets.7.html). -It's recommended to put this snippet in `CMakeLists.txt` to automatically set -the toolchain file according to the `PSN00BSDK_LIBS` environment variable: - -```cmake -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() +It's suggested to have a default preset that sets `CMAKE_TOOLCHAIN_FILE` to +`$env{PSN00BSDK_LIBS}/cmake/sdk.cmake`, so the `PSN00BSDK_LIBS` environment +variable (used by former PSn00bSDK versions) is respected. Such a preset can be +created by placing a `CMakePresets.json` file in the project's root with the +following contents: + +```json +{ + "version": 2, + "cmakeMinimumRequired": { + "major": 3, + "minor": 20, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "displayName": "Default configuration", + "description": "Use this preset to build the project using PSn00bSDK.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_TOOLCHAIN_FILE": "$env{PSN00BSDK_LIBS}/cmake/sdk.cmake", + "PSN00BSDK_TC": "", + "PSN00BSDK_TARGET": "mipsel-none-elf" + } + } + ] +} ``` +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. + See the [template](../template/CMakeLists.txt) for an example CMake script showing how to build a simple project. @@ -130,28 +159,24 @@ build script, from the CMake command line when configuring the project - `PSN00BSDK_TARGET` (`STRING`) The GCC toolchain's target triplet. PSn00bSDK assumes the toolchain targets - `mipsel-unknown-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. + `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. The following GCC target triplets have been confirmed to work with PSn00bSDK: - - `mipsel-unknown-elf` - `mipsel-none-elf` + - `mipsel-unknown-elf` + - ~~`mipsel-linux-gnu`~~ (has issues with linking) - `PSN00BSDK_TC` (`PATH`) - Path to the GCC toolchain's installation prefix/directory. By default this is - initialized to the value of the `PSN00BSDK_TC` environment variable (if set). - Note that modifying the environment variable after the project has been - configured will *NOT* update this cache entry unless the project's cache is - cleared manually. - - If not set, CMake will attempt to find the toolchain in the `PATH` - environment variable and store its path in this variable (so the search does - not have to be repeated). + 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. - **IMPORTANT**: if the toolchain's target is not `mipsel-unknown-elf`, + **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. @@ -204,4 +229,4 @@ the build script. LZP archives as part of the build pipeline. ----------------------------------------- -_Last updated on 2021-09-27 by spicyjpeg_ +_Last updated on 2021-11-24 by spicyjpeg_ diff --git a/doc/dev notes.txt b/doc/dev notes.txt index 3aa2db5..65a3976 100644 --- a/doc/dev notes.txt +++ b/doc/dev notes.txt @@ -109,13 +109,14 @@ your exception handler must do the following: controller- or interrupt-related function is being called. This is necessary (although ugly) as libpsn00b often calls such functions internally. -* For some reason mipsel-unknown-elf-nm (symbol map generator) insists on -outputting 64-bit addresses (with the top 32 bits set, e.g. FFFFFFFF80010000) -even when feeding it a regular 32-bit MIPS executable, while the standard x86 -nm tool that ships with most GCC packages prints the proper 32-bit address. -Unclear whether this is a bug, intended behavior or the result of some ancient -ELF ABI flag crap. DL_ParseSymbolMap() will ignore the top 32 bits, so this -should only bother you if you're implementing your own symbol map parser. +* For some reason mipsel-unknown-elf-nm and mipsel-none-elf-nm (symbol map +generators) insist on outputting 64-bit addresses (with the top 32 bits set, +e.g. FFFFFFFF80010000) even when feeding it a regular 32-bit MIPS executable, +while the standard x86 nm tool that ships with most GCC packages prints the +proper 32-bit address. Unclear whether this is a bug, intended behavior or the +result of some ancient ELF ABI flag crap. DL_ParseSymbolMap() will ignore the +top 32 bits, so this should only bother you if you're implementing your own +symbol map parser. * I haven't worked on psxspu but, for those willing to write some code, this is the formula to calculate SPU pitch values for playing musical notes ("^" is the diff --git a/doc/installation.md b/doc/installation.md new file mode 100644 index 0000000..576eb7a --- /dev/null +++ b/doc/installation.md @@ -0,0 +1,155 @@ + +# Getting started with PSn00bSDK + +## Building and installing + +The instructions below are for Windows and Linux. Building on macOS hasn't been +tested extensively yet, however it should work once the GCC toolchain is built +and installed properly. + +1. Install prerequisites and a host compiler toolchain. On Linux (most distros) + install the following packages from your distro's package manager: + + - `git` + - `build-essential`, `base-devel` or similar + - `make` or `ninja-build` + - `cmake` (3.20+ is required, download it from + [here](https://cmake.org/download) if your package manager only provides + older versions) + + On Windows you can obtain these dependencies by installing + [MSys2](https://www.msys2.org), opening the "MSys2 MSYS" shell and running: + + ```bash + pacman -Syu git mingw-w64-x86_64-make mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc + ``` + + If you are prompted to close the shell, you may have to reopen it afterwards + and rerun the command to finish installation. + **Do not use the MSys2 shell for the next steps**, use a regular command + prompt or PowerShell instead. + + Add these directories (replace `C:\msys64` if you installed MSys2 to a + different location) to the `PATH` environment variable using System + Properties: + + - `C:\msys64\mingw64\bin` + - `C:\msys64\usr\bin` + +2. Download a precompiled copy of the GCC toolchain for `mipsel-none-elf` from + the releases page and extract it into one of the directories listed in + step 3. If you want to build the toolchain yourself, see + [toolchain.md](toolchain.md). + + **NOTE**: PSn00bSDK is also compatible with toolchains that target + `mipsel-unknown-elf`. If you already have such a toolchain, you can use it + by passing `-DPSN00BSDK_TARGET=mipsel-unknown-elf` to CMake when configuring + the SDK (see step 5). + +3. If you chose a non-standard install location for the toolchain, add the + `bin` subfolder (inside the top-level toolchain directory) to the `PATH` + environment variable. This step is unnecessary if you installed/extracted + the toolchain into any of these directories: + + - `C:\Program Files\mipsel-none-elf` + - `C:\Program Files (x86)\mipsel-none-elf` + - `C:\mipsel-none-elf` + - `/usr/local/mipsel-none-elf` + - `/usr/mipsel-none-elf` + - `/opt/mipsel-none-elf` + +4. Clone the PSn00bSDK repo, then run the following command from the PSn00bSDK + repository to download additional dependencies: + + ```bash + git submodule update --init --recursive --remote + ``` + +5. Compile the libraries, tools and examples using CMake: + + ```bash + cmake --preset default . + cmake --build ./build + ``` + + If you want to install the SDK to a custom location rather than the default + one (`C:\Program Files\PSn00bSDK` or `/usr/local` depending on your OS), add + `--install-prefix ` to the first command. Add + `-DPSN00BSDK_TARGET=mipsel-none-elf` if your toolchain targets + `mipsel-none-elf` rather than `mipsel-unknown-elf`. + + **NOTE**: Ninja is used by default to build the SDK. If you can't get it to + work or don't have it installed, pass `-G "Unix Makefiles"` (or + `-G "MSYS Makefiles"` on Windows) to the first command to build using `make` + instead. + +6. Install the SDK to the path you chose (add `sudo` or run it from a command + prompt with admin privileges if necessary): + + ```bash + cmake --install ./build + ``` + + This will create and populate the following directories: + + - `/bin` + - `/lib/libpsn00b` + - `/share/psn00bsdk` + +7. Set the `PSN00BSDK_LIBS` environment variable to point to the `lib/libpsn00b` + subfolder inside the install directory. You might also want to add the `bin` + folder to `PATH` if it's not listed already. + +Although not strictly required, you'll probably want to install a PS1 emulator +with debugging capabilities such as [no$psx](https://problemkaputt.de/psx.htm) +(Windows only), [DuckStation](https://github.com/stenzek/duckstation) or +[pcsx-redux](https://github.com/grumpycoders/pcsx-redux). +**Avoid ePSXe and anything based on MAME** as they are inaccurate. + +## Building installer packages + +CPack can be used to build NSIS-based installers, DEB/RPM packages and zipped +releases that include built SDK libraries, headers as well as the GCC toolchain. +Distributing prebuilt releases is however discouraged since PSn00bSDK is still +far from being feature-complete. + +1. Follow steps 1-4 above to set up the toolchain, then install + [NSIS](https://nsis.sourceforge.io/Download) on Windows or `dpkg` and `rpm` + on Linux. + +2. Run the following commands from the PSn00bSDK directory (pass the + appropriate options to the first command if necessary): + + ```bash + cmake --preset package . + cmake --build ./build -t package + ``` + + All built packages will be copied to the `build/packages` folder. + +## Creating a project + +1. Copy the contents of `/share/psn00bsdk/template` (or the + `template` folder within the repo) to your new project's root directory. + +2. Configure and build the template by running: + + ```bash + cmake -S . -B ./build + cmake --build ./build + ``` + + If you did everything correctly there should be a `template.bin` CD image in + the `build` folder. Test it in an emulator to ensure it works. + +Note that, even though the template relies on the `PSN00BSDK_LIBS` environment +variable to locate the SDK by default, you can also specify the path directly +on the CMake command line by adding +`-DCMAKE_TOOLCHAIN_FILE=/lib/libpsn00b/cmake/sdk.cmake` to the +CMake command line. + +The toolchain script defines a few CMake macros to create PS1 executables, DLLs +and CD images. See the [reference](doc/cmake_reference.md) for details. + +----------------------------------------- +_Last updated on 2021-11-19 by spicyjpeg_ diff --git a/doc/toolchain.md b/doc/toolchain.md new file mode 100644 index 0000000..8e28c24 --- /dev/null +++ b/doc/toolchain.md @@ -0,0 +1,224 @@ + +# Building the GCC toolchain + +If you wish to build the toolchain yourself, beware that this process can get +pretty tedious if your machine is not fairly recent. Ensure you have at least a +quad-core processor and 4 GB of free space before continuing. + +You'll need a Linux environment, even if you want to build a Windows toolchain +(as GCC is basically impossible to build under Windows but can be cross-compiled +via MinGW). Due to how the GCC build process works, you'll have to build a Linux +version of the toolchain first to be able to compile it for Windows. This +basically means you will have to build the whole toolchain twice if you want to +target Windows. + +These instructions are for Debian/Ubuntu, however it should be relatively easy +to follow them if you are using another distro. If you do not have access to a +Linux system already, consider spinning up a VM (a headless Debian or Ubuntu +Server install is recommended) or using WSL, whose setup is out of the scope of +this guide. + +## Choosing a GCC version + +PSn00bSDK *should* work with any GCC version. In most cases you'll want to get +the latest stable release of GCC and binutils. If for some reason you are having +problems you may try building one of the following versions, which have been +tested extensively: + +- ~~GCC 7.4.0 with binutils 2.31~~ (the linker fails to build PS1 DLLs) +- GCC **11.1.0** with binutils **2.36** +- GCC **11.2.0** with binutils **2.37** + +If you wish to pick an older GCC release but don't know which binutils version +it requires, see [here](https://wiki.osdev.org/Cross-Compiler_Successful_Builds) +for a compatibility table. + +## Downloading GCC + +1. Run the following commands to install a host toolchain and prerequisites + (adapt them for non-Debian distros if necessary): + + ```bash + sudo apt update + sudo apt install -y build-essential make wget + ``` + +2. Create an empty directory to store build artifacts in. You'll be able to + delete it once the toolchain is installed. + +3. Download the GCC and binutils source packages from + [here](https://ftpmirror.gnu.org/gnu) and unzip them into the folder you + created, or run the following commands to do the same (replace `` + with the versions you chose): + + ```bash + wget https://ftpmirror.gnu.org/gnu/binutils/binutils-.tar.xz + wget https://ftpmirror.gnu.org/gnu/gcc/gcc-/gcc-.tar.xz + tar xvf binutils-.tar.xz + tar xvf gcc-.tar.xz + rm -f *.tar.xz + ``` + +4. From the extracted GCC directory run the `download_prerequisites` script to + download additional dependencies: + + ```bash + cd gcc- + ./contrib/download_prerequisites + ``` + +## Building binutils + +1. Go back to the folder you made earlier and create a new subdirectory to build + binutils in (don't create it inside the extracted binutils source directory). + Call it `binutils-build` or whatever. + +2. Run the binutils configuration script from that folder: + + ```bash + ../binutils-/configure \ + --prefix=/usr/local/mipsel-none-elf --target=mipsel-none-elf \ + --disable-docs --disable-nls --with-float=soft + ``` + + Replace `` as usual. If you don't want to install the toolchain into + `/usr/local/mipsel-none-elf` you can change the `--prefix` option. + +3. Compile and install binutils (this will take a few minutes to finish): + + ```bash + make -j 4 + sudo make install-strip + ``` + + Increase `-j 4` to speed up the build if your machine or VM has more than 4 + CPU cores. + + **NOTE**: if the build fails with a "`uint` undeclared" or similar error, try + editing the source file that caused the error and pasting this line at the + beginning: + + ```c + typedef unsigned int uint; + ``` + + Rerun `make` to resume the build after saving the file. + +## Building GCC + +The process is mostly the same as binutils, just with different configuration +options. + +1. Go back to the main directory and create an empty `gcc-build` (or whatever) + subfolder. + +2. Run the GCC configuration script from there: + + ```bash + ../gcc-/configure \ + --prefix=/usr/local/mipsel-none-elf --target=mipsel-none-elf \ + --disable-docs --disable-nls --disable-libada --disable-libssp \ + --disable-libquadmath --disable-libstdc++-v3 --with-float=soft \ + --enable-languages=c,c++ --with-gnu-as --with-gnu-ld + ``` + + If you previously set a custom installation path, remember to set it here as + well (it must be the same). + +3. Compile and install GCC (will take a long time, usually around half an hour): + + ```bash + make -j 4 + sudo make install-strip + ``` + + Increase `-j 4` to speed up the build if your machine or VM has more than 4 + threads. + +4. Add the toolchain to the `PATH` environment variable. This is required to + rebuild the toolchain for Windows (see below), but it will also allow + PSn00bSDK to find the toolchain if you installed it in a custom location. + + Edit the `.bashrc` or `.bash_profile` file in your home directory and add + this line at the end (replace the path with the install location you chose + earlier, but keep the `/bin` at the end): + + ```bash + export PATH=$PATH:/usr/local/mipsel-none-elf/bin + ``` + + Restart the shell by closing and reopening the terminal window or SSH + connection afterwards. + +## Rebuilding for Windows + +At this point you should be able to build and install PSn00bSDK on your Linux +system. The instructions below are for building a second copy of the toolchain +that runs on Windows. + +1. Install the MinGW host toolchain: + + ```bash + sudo apt install -y g++-mingw-w64-x86-64 + ``` + +2. Create two new `binutils-win` and `gcc-win` folders in the directory you + extracted/built everything in. + +3. From the `binutils-win` directory, rerun the binutils configuration script + with the following options (do not change the installation path): + + ```bash + ../binutils-/configure \ + --build=x86_64-linux-gnu --host=x86_64-w64-mingw32 \ + --prefix=/tmp/mipsel-none-elf --target=mipsel-none-elf \ + --disable-docs --disable-nls --with-float=soft + ``` + + Then build binutils again: + + ```bash + make -j 4 + make install-strip + ``` + +4. Do the same for GCC from the `gcc-win` directory: + + ```bash + ../gcc-/configure \ + --build=x86_64-linux-gnu --host=x86_64-w64-mingw32 \ + --prefix=/tmp/mipsel-none-elf --target=mipsel-none-elf \ + --disable-docs --disable-nls --disable-libada --disable-libssp \ + --disable-libquadmath --disable-libstdc++-v3 --with-float=soft \ + --enable-languages=c,c++ --with-gnu-as --with-gnu-ld + ``` + + And build it as usual: + + ```bash + make -j 4 + make install-strip + ``` + +5. Copy the entire `/tmp/mipsel-none-elf` directory over to your Windows + machine using VM shared folders, a network share, `scp` or whichever method + you prefer. It's recommended to put the toolchain in + `C:\Program Files\mipsel-none-elf` or `C:\mipsel-none-elf`. + +6. If you want to keep the toolchain in another location and/or use it from the + command line, add the `bin` subdirectory inside `mipsel-none-elf` to the + `PATH` environment variable (as you did on Linux) using System Properties. + +## Note regarding C++ support + +C++ support in PSn00bSDK, besides compile-time features like `constexpr`, only +goes as far as basic classes, namespaces and the ability to dynamically create +and delete class objects at any point of the program. The required dependencies +(which are just wrappers around `malloc()` and `free()`) are supplied by `libc`. + +Standard C++ libraries are not implemented and likely never going to be +implemented due to bloat concerns that it may introduce. Besides, the official +SDK lacks full C++ support as well. + +----------------------------------------- +_Last updated on 2021-11-23 by spicyjpeg_ diff --git a/libpsn00b/cmake/sdk.cmake b/libpsn00b/cmake/sdk.cmake index ae23a06..1dfa431 100644 --- a/libpsn00b/cmake/sdk.cmake +++ b/libpsn00b/cmake/sdk.cmake @@ -4,11 +4,11 @@ cmake_minimum_required(VERSION 3.20) set( - PSN00BSDK_TC $ENV{PSN00BSDK_TC} - CACHE PATH "Path to the GCC toolchain's installation directory" + PSN00BSDK_TC "" + CACHE PATH "Path to the GCC toolchain's installation directory (if not in PATH)" ) set( - PSN00BSDK_TARGET mipsel-unknown-elf + PSN00BSDK_TARGET mipsel-none-elf CACHE STRING "GCC toolchain target triplet" ) @@ -56,7 +56,7 @@ cmake_path(GET _bin PARENT_PATH _toolchain) if(NOT IS_DIRECTORY PSN00BSDK_TC) set( PSN00BSDK_TC ${_toolchain} - CACHE PATH "Path to the GCC toolchain's installation directory" + CACHE PATH "Path to the GCC toolchain's installation directory (if not in PATH)" FORCE ) endif() diff --git a/template/CMakeLists.txt b/template/CMakeLists.txt index c0c05d0..4aa648f 100644 --- a/template/CMakeLists.txt +++ b/template/CMakeLists.txt @@ -3,14 +3,6 @@ cmake_minimum_required(VERSION 3.20) -# CMAKE_TOOLCHAIN_FILE must be set to point to cmake/sdk.cmake in the PSn00bSDK -# installation directory *before* calling project(). You don't have to hardcode -# the path in your CMakeLists.txt as you can set it from the command line when -# configuring (-DCMAKE_TOOLCHAIN_FILE=...) or using CMake presets. -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( PSn00bSDK-template LANGUAGES C CXX ASM @@ -20,6 +12,7 @@ project( ) psn00bsdk_add_executable(template STATIC main.c) + psn00bsdk_add_cd_image( iso # Target name template # Output file name (= template.bin + template.cue) diff --git a/template/CMakePresets.json b/template/CMakePresets.json new file mode 100644 index 0000000..d08b334 --- /dev/null +++ b/template/CMakePresets.json @@ -0,0 +1,23 @@ +{ + "version": 2, + "cmakeMinimumRequired": { + "major": 3, + "minor": 20, + "patch": 0 + }, + "configurePresets": [ + { + "name": "default", + "displayName": "Default configuration", + "description": "Use this preset to build the project using PSn00bSDK.", + "generator": "Ninja", + "binaryDir": "${sourceDir}/build", + "cacheVariables": { + "CMAKE_BUILD_TYPE": "Debug", + "CMAKE_TOOLCHAIN_FILE": "$env{PSN00BSDK_LIBS}/cmake/sdk.cmake", + "PSN00BSDK_TC": "", + "PSN00BSDK_TARGET": "mipsel-none-elf" + } + } + ] +} -- cgit v1.2.3 From 7186b911cc461dbdad9307afd8901be118e20893 Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Sun, 28 Nov 2021 18:46:06 +0100 Subject: CMake fixes, psxcd and printf/scanf improvements --- libpsn00b/cmake/flags.cmake | 9 ++++++--- libpsn00b/cmake/sdk.cmake | 2 +- libpsn00b/libc/scanf.c | 7 ++++++- libpsn00b/libc/string.c | 14 +++++++++++++- libpsn00b/libc/vsprintf.c | 11 +++++++++++ libpsn00b/psxcd/isofs.c | 28 ++++++++++++++++++++-------- 6 files changed, 57 insertions(+), 14 deletions(-) diff --git a/libpsn00b/cmake/flags.cmake b/libpsn00b/cmake/flags.cmake index e163e52..249c5b4 100644 --- a/libpsn00b/cmake/flags.cmake +++ b/libpsn00b/cmake/flags.cmake @@ -2,9 +2,8 @@ # (C) 2021 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: +# directories and compiler flags when a target is linked against them. The +# following targets are currently defined: # - psn00bsdk_common # - psn00bsdk_object_lib (same as psn00bsdk_common) # - psn00bsdk_static_exe @@ -16,6 +15,8 @@ # NOTE: building a static library and linking it as part of a DLL is currently # *not* supported. +if(NOT TARGET psn00bsdk_common) # Include guard + add_library(psn00bsdk_common INTERFACE) foreach( @@ -146,3 +147,5 @@ target_link_options( ) target_link_libraries(psn00bsdk_module_lib INTERFACE psn00bsdk_shared_lib) + +endif() diff --git a/libpsn00b/cmake/sdk.cmake b/libpsn00b/cmake/sdk.cmake index 1dfa431..d6d9bcd 100644 --- a/libpsn00b/cmake/sdk.cmake +++ b/libpsn00b/cmake/sdk.cmake @@ -26,7 +26,7 @@ set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY) # toolchain settings to the generated test projects. This dodges missing C++ # standard library errors. set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY) -set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES PSN00BSDK_TC PSN00BSDK_TARGET) +set(CMAKE_TRY_COMPILE_PLATFORM_VARIABLES PSN00BSDK_TC PSN00BSDK_TARGET PSN00BSDK_VERSION) ## Toolchain path setup diff --git a/libpsn00b/libc/scanf.c b/libpsn00b/libc/scanf.c index b6d4510..ae3c714 100644 --- a/libpsn00b/libc/scanf.c +++ b/libpsn00b/libc/scanf.c @@ -8,6 +8,9 @@ #include #include +// Uncomment to enable support for %f. +//#define ALLOW_FLOAT + char libc_vsscanf_buf[512]; char libc_vsscanf_allow[256]; @@ -354,7 +357,8 @@ int vsscanf(const char *str, const char *format, va_list ap) r++; } break; - + +#ifdef ALLOW_FLOAT case 'f': // Floating point number libc_vsscanf_get_element(libc_vsscanf_buf, &str[sp], elem_skip_space, fsz); fbuf = strtod(libc_vsscanf_buf, &ep); @@ -380,6 +384,7 @@ int vsscanf(const char *str, const char *format, va_list ap) conv = 0; break; +#endif } } diff --git a/libpsn00b/libc/string.c b/libpsn00b/libc/string.c index 445b227..a1a9a05 100644 --- a/libpsn00b/libc/string.c +++ b/libpsn00b/libc/string.c @@ -8,6 +8,10 @@ #include #include +// Uncomment to enable strtod(), strtold() and strtof(). Note that these +// functions use extremely slow software floats. +//#define ALLOW_FLOAT + int tolower(int chr) { return (chr >='A' && chr<='Z') ? (chr + 32) : (chr); @@ -252,6 +256,8 @@ long strtol(const char *nptr, char **endptr, int base) return (long)strtoll(nptr, endptr, base); } +#ifdef ALLOW_FLOAT + double strtod(const char *nptr, char **endptr) { char strbuf[64]; @@ -302,6 +308,8 @@ double strtod(const char *nptr, char **endptr) return (i + d)*s; } +#endif + /* implementation by Lameguy64, behaves like OpenWatcom's strtok() */ /* BIOS strtok seemed either bugged, or designed for wide chars */ @@ -344,6 +352,8 @@ char *strtok( char *s1, char *s2 ) } /* strtok */ +#ifdef ALLOW_FLOAT + long double strtold(const char *nptr, char **endptr) { return (long double)strtod(nptr, endptr); @@ -352,4 +362,6 @@ long double strtold(const char *nptr, char **endptr) float strtof(const char *nptr, char **endptr) { return (float)strtod(nptr, endptr); -} \ No newline at end of file +} + +#endif diff --git a/libpsn00b/libc/vsprintf.c b/libpsn00b/libc/vsprintf.c index 0a99dcc..9ca4cc5 100644 --- a/libpsn00b/libc/vsprintf.c +++ b/libpsn00b/libc/vsprintf.c @@ -6,6 +6,10 @@ #include #include #include +#include + +// Uncomment to enable support for %f. +//#define ALLOW_FLOAT #define SPRINTF_ALT_FLAG (1<<0) #define SPRINTF_ZERO_FLAG (1<<1) @@ -208,6 +212,8 @@ int libc_ulltoa(unsigned long i, char *dst, int n) return n2; } +#ifdef ALLOW_FLOAT + void libc_float_to_string(float fl, char *dst, int n) { unsigned int *p = (unsigned int*)&fl; @@ -365,6 +371,8 @@ void libc_double_to_string(double fl, char *dst, int n) char libc_sprintf_floatbuf[64]; +#endif + int vsnprintf(char *string, unsigned int size, const char *fmt, va_list ap) { int string_pos,fmt_pos; @@ -732,6 +740,7 @@ int vsnprintf(char *string, unsigned int size, const char *fmt, va_list ap) directive_coming = 0; break; +#ifdef ALLOW_FLOAT case 'f': libc_double_to_string(va_arg(ap, double), libc_sprintf_floatbuf, 64); @@ -740,6 +749,8 @@ int vsnprintf(char *string, unsigned int size, const char *fmt, va_list ap) directive_coming = 0; break; +#endif + case 'n': // Number of characters written *(va_arg(ap,unsigned int*)) = string_pos; diff --git a/libpsn00b/psxcd/isofs.c b/libpsn00b/psxcd/isofs.c index 40a40af..29bbb76 100644 --- a/libpsn00b/psxcd/isofs.c +++ b/libpsn00b/psxcd/isofs.c @@ -10,6 +10,9 @@ // Uncommend to enable debug output //#define DEBUG +#define DEFAULT_PATH_SEP '\\' +#define IS_PATH_SEP(ch) (((ch) == '/') || ((ch) == '\\')) + typedef struct _CdlDIR_INT { u_long _pos; @@ -374,7 +377,7 @@ static char* resolve_pathtable_path(int entry, char *rbuff) rbuff -= tbl_entry.nameLength; memcpy(rbuff, namebuff, tbl_entry.nameLength); rbuff--; - *rbuff = '\\'; + *rbuff = DEFAULT_PATH_SEP; // Parse to the parent entry = tbl_entry.dirLevel; @@ -431,12 +434,15 @@ static int find_dir_entry(const char *name, ISO_DIR_ENTRY *dirent) static char* get_pathname(char *path, const char *filename) { - char *c; - c = strrchr(filename, '\\'); + char *c = 0; + for (char *i = filename; *i; i++) { + if (IS_PATH_SEP(*i)) + c = i; + } if(( c == filename ) || ( !c )) { - path[0] = '\\'; + path[0] = DEFAULT_PATH_SEP; path[1] = 0; return NULL; } @@ -447,11 +453,17 @@ static char* get_pathname(char *path, const char *filename) static char* get_filename(char *name, const char *filename) { - char *c; - c = strrchr(filename, '\\'); + char *c = 0; + for (char *i = filename; *i; i++) { + if (IS_PATH_SEP(*i)) + c = i; + } - if(( c == filename ) || ( !c )) - { + if (!c) { + strcpy(name, filename); + return name; + } + if (c == filename) { strcpy(name, filename+1); return name; } -- cgit v1.2.3 From 31fba94b8e8c15c1d127925f01a38efc68933fd1 Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Mon, 29 Nov 2021 00:17:28 +0100 Subject: Add io/system573 example, rewrite dev notes, fix CI --- .github/workflows/build.yml | 27 ++- CHANGELOG.md | 14 +- README.md | 87 ++++---- doc/dev notes.txt | 250 ----------------------- doc/dev_notes.md | 280 ++++++++++++++++++++++++++ examples/io/system573/CMakeLists.txt | 27 +++ examples/io/system573/iso.xml | 34 ++++ examples/io/system573/main.c | 371 +++++++++++++++++++++++++++++++++++ 8 files changed, 785 insertions(+), 305 deletions(-) delete mode 100644 doc/dev notes.txt create mode 100644 doc/dev_notes.md create mode 100644 examples/io/system573/CMakeLists.txt create mode 100644 examples/io/system573/iso.xml create mode 100644 examples/io/system573/main.c diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index d6746b0..8c16ac3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -181,22 +181,38 @@ jobs: needs: [ build-sdk-windows, build-sdk-linux ] steps: + - name: Initialize toolchain cache + if: ${{ github.ref_type == 'tag' }} + uses: actions/cache@v2 + with: + key: gcc-${{ env.GCC_TARGET }}-${{ env.GCC_VERSION }} + path: gcc + + - name: Package GCC toolchains + if: ${{ github.ref_type == 'tag' }} + run: | + cd gcc/windows + zip -9 -q -r ../../gcc-${{ env.GCC_TARGET }}-${{ env.GCC_VERSION }}-windows.zip . + cd ../linux + zip -9 -q -r ../../gcc-${{ env.GCC_TARGET }}-${{ env.GCC_VERSION }}-linux.zip . + - name: Fetch repo contents + if: ${{ github.ref_type == 'tag' }} uses: actions/checkout@v2 with: path: sdk + - name: Generate release notes + if: ${{ github.ref_type == 'tag' }} + run: | + python3 sdk/.github/scripts/generate_release_notes.py -v ${{ github.ref_name }} -o release.md sdk/CHANGELOG.md + - name: Fetch build artifacts if: ${{ github.ref_type == 'tag' }} uses: actions/download-artifact@v2 with: path: . - - name: Generate release notes - if: ${{ github.ref_type == 'tag' }} - run: | - sdk/.github/scripts/generate_release_notes.py -v ${{ github.ref_name }} -o release.md sdk/CHANGELOG.md - - name: Publish release if: ${{ github.ref_type == 'tag' }} uses: softprops/action-gh-release@v1 @@ -204,5 +220,6 @@ jobs: fail_on_unmatched_files: true body_path: release.md files: | + *.zip psn00bsdk-windows/* psn00bsdk-linux/* diff --git a/CHANGELOG.md b/CHANGELOG.md index c05105e..9463b83 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -17,7 +17,7 @@ contributing to PSn00bSDK, add a new block at the top following this template: You may run `.github/scripts/generate_release_notes.py CHANGELOG.md` afterwards to ensure the changelog can be parsed correctly. --------- +------------------------------------------------------------------------------- ## 2021-11-28: 0.18 @@ -25,13 +25,13 @@ spicyjpeg: - libc: Removed `STACK_MAX_SIZE` and added `_mem_init()` back. RAM and stack size can now be set by calling `_mem_init()` manually before allocating any - memory. + memory (however this seems to be currently broken). - libc: `sprintf()` now supports fixed padding when using the `%@` (binary integer) format specifier. -- psxcd: File paths with forward slashes instead of backslashes are now - accepted. +- psxcd: File paths with forward slashes instead of backslashes, as well as + paths containing both slash types, are now accepted. - psxapi: Added wrapper around BIOS function `GetSystemInfo()`. @@ -44,8 +44,10 @@ spicyjpeg: - Deprecated `malloc.h` and removed all references to it (`stdlib.h` should be used instead). Moved `int*_t` and `uint*_t` types to `stdint.h`. -- Fixed file permission errors when attempting to install the SDK on macOS and - made some small updates to `INSTALL.md`, which is now `doc/installation.md`. +- Fixed file permission errors when attempting to install the SDK on macOS. + +- Cleaned up, updated and moved all documentation to the `doc` folder. + Rewritten this changelog and added a script to generate release notes. ## 2021-10-31 diff --git a/README.md b/README.md index 6fd4cd1..13ab7d1 100644 --- a/README.md +++ b/README.md @@ -1,3 +1,4 @@ + # PSn00bSDK PSn00bSDK is a 100% free and open source SDK project for the original Sony @@ -5,33 +6,32 @@ PlayStation for developing homebrew applications and games for the console 100% freely. This SDK can be used for freeware, commercial, and open source homebrew projects. -The SDK is composed mainly of libraries (libpsn00b) and some utilities that -provide a basic framework for developing software for the PlayStation -hardware, the compiler is separate (GCC) and should be acquired from GNU. -The library API is intentionally written to resemble the library API of the -official libraries as closely as possible. This design decision is not only -for familiarity reasons to experienced programmers, but also so that existing -sample code and tutorials would still apply to this SDK, as well as making -the process of porting over existing homebrew originally made with official -SDKs easier with minimal modification, provided it doesn't use libgs. +The SDK is composed mainly of libraries (`libpsn00b`) and some utilities that +provide a basic framework for developing software for the PlayStation hardware, +the compiler is separate (GCC) and should be acquired from GNU. The library API +is intentionally written to resemble the library API of the official libraries +as closely as possible. This design decision is not only for familiarity +reasons to experienced programmers, but also so that existing sample code and +tutorials would still apply to this SDK, as well as making the process of +porting over existing homebrew originally made with official SDKs easier with +minimal modification, provided it doesn't use `libgs`. PSn00bSDK is currently a work in progress and cannot really be considered -production ready, but what is currently implemented should be enough to -produce some interesting homebrew with the SDK, especially with its extensive -support for the GPU and GTE hardware. There's no reason not to fully support -hardware features of a target platform when said hardware features have been -fully documented for years (nocash's PSX specs document in this case). +production ready, but what is currently implemented should be enough to produce +some interesting homebrew with the SDK, especially with its extensive support +for the GPU and GTE hardware. There's no reason not to fully support hardware +features of a target platform when said hardware features have been fully +documented for years (nocash's PSX specs document in this case). -Most of libpsn00b is written mostly in MIPS assembly, moreso functions that +Most of `libpsn00b` is written mostly in MIPS assembly, moreso functions that interface with the hardware. Many of the standard C functions are implemented in custom MIPS assembly instead of equivalents found in the BIOS ROM, for both -stability (the BIOS libc implementation of the PlayStation is actually buggy) +stability (the BIOS `libc` implementation of the PlayStation is actually buggy) and performance reasons. - ## Notable features -As of August 16, 2021 +As of November 28, 2021 * Extensive GPU support with polygon, line and sprite primitives, high-speed DMA transfers for VRAM data and ordering tables. All video modes for both @@ -76,28 +76,25 @@ As of August 16, 2021 * Fully expandable and customizable to your heart's content. - ## Obtaining PSn00bSDK PSn00bSDK has switched to a CMake-based build and installation system. See [installation.md](doc/installation.md) for details. -Because PSn00bSDK is updated semi-regularly due to this project being in -a work-in-progress state, it is better to obtain this SDK from source and -building it yourself in the long run. Pre-compiled packages for Debian and -Msys2 are being planned however (it is already possible to build installers, -DEB and RPM packages through CPack so it's only a matter of time). +Prebuilt SDK packages and versions of the GCC toolchain for Windows and Linux +(DEB/RPM) are available through GitHub Actions. Stable releases haven't yet +been published however, due to this project being in a work-in-progress state. +It is still recommended to build the SDK from source for the time being. ## Examples -There are a few examples and complete source code of n00bdemo included in -the examples directory. More example programs may be added in the future -and contributed example programs are welcome. - -There's also Lameguy's PlayStation Programming Tutorial Series at -http://lameguy64.net/tutorials/pstutorials/ for learning how to program -for the PlayStation. The tutorials should still apply to PSn00bSDK. +There are a few examples and complete source code of `n00bdemo` included in the +`examples` directory. More example programs may be added in the future and +contributed example programs are welcome. +There's also [Lameguy's PlayStation Programming Tutorial Series](http://lameguy64.net/tutorials/pstutorials) +for learning how to program for the PlayStation. The tutorials should still +apply to PSn00bSDK. ## To-do List @@ -121,33 +118,35 @@ for the PlayStation. The tutorials should still apply to PSn00bSDK. ## Usage terms (or lack thereof) -PSn00bSDK falls under the terms and conditions of the Mozilla Public -License. A quick summary of this license is that PSn00bSDK can be used -freely in both free and open source projects and commercial closed source -projects as projects using PSn00bSDK does not necessarily have to follow -the MPL as well. +PSn00bSDK falls under the terms and conditions of the Mozilla Public License. A +quick summary of this license is that PSn00bSDK can be used freely in both free +and open source projects and commercial closed source projects as projects +using PSn00bSDK does not necessarily have to follow the MPL as well. If modifications to the SDK were made as part of the development of such -projects that enhance its functionality, such changes must be contributed -back in return. +projects that enhance its functionality, such changes must be contributed back +in return. Homebrew made with PSn00bSDK may not be released under 'annoyingmous'. Although there's nothing that would enforce it, this term may as well be ignored despite it annoying this SDK's author. - ## Credits -Main developer: -* Lameguy64 +Main developers: + +* **Lameguy64** +* **spicyjpeg**: dynamic linker, CMake scripts, some docs and examples Honorable mentions: -* ijacquez - helpful suggestions for getting C++ working. -* NicolasNoble - his OpenBIOS project gave insight to how the BIOS works + +* **ijacquez**: helpful suggestions for getting C++ working. +* **Nicolas Noble**: his OpenBIOS project gave insight to how the BIOS works internally. Helpful contributors can be found in the changelog. References used: -* nocash's PlayStation specs document (http://problemkaputt.de/psx-spx.htm) + +* [nocash's PlayStation specs document](http://problemkaputt.de/psx-spx.htm) * Tails92's PSXSDK project (during PSn00bSDK's infancy). diff --git a/doc/dev notes.txt b/doc/dev notes.txt deleted file mode 100644 index 65a3976..0000000 --- a/doc/dev notes.txt +++ /dev/null @@ -1,250 +0,0 @@ -These are some development notes I've put together that would be of great -aid to those willing to contribute to the PSn00bSDK project. Many of these -came from my own experience dealing with the PS1 at low-level when I ran -into some unexplained quirks while some are from disassembly observations -and clarification of existing documents. More entries will be added when -I run into more previously undocumented quirks in the future. - - Lameguy64 - -* When calling C functions (ie. BIOS functions) from assembly code you'll -need to allocate N words on the stack first when calling a function that has -N arguments (addiu $sp, -(4*N) where N = number of arguments of the function -being called) even if the arguments are on registers a0 to a3 and the C -functions don't always use the space allotted in stack. When calling a -function with a variable number of arguments (printf) always allocate -16 bytes of stack. - -* Hooking a custom handler using BIOS function HookEntryInt (B(19h), known -as SetCustomExitFromException in nocash docs) is only triggered when there's -an IRQ that is not yet acknowledged by previous IRQ handlers built into the -kernel. This is also the best point to acknowledge any IRQs without breaking -compatibility with built-in BIOS IRQ handlers and is what the official SDK -uses to handle IRQs. To make sure this handler is triggered on every interrupt -you must call ChangeClearPad(0) and ChangeClearRCnt(3, 0) (which are functions -(B(5Bh)) and C(0Ah) respectively) otherwise the pad and root counter handlers -in the kernel will acknowledge the interrupt before your handler, preventing -you from handling them yourself. - -* It is not advisable to handle interrupts using event handlers like in -PSXSDK as it breaks BIOS features that depend on interrupts. Clearing the -VBlank IRQ in a event handler for example prevents the BIOS controller -functions from working as it depends on the VBlank IRQ to determine when to -query controllers. Acknowledge interrupts using a custom handler set by BIOS -function HookEntryInt (B(19h), known as SetCustomExitFromException in nocash -docs). - -* When running in high resolution mode you must additionally wait for bit 31 -in GPUSTAT (1F801814h) to alternate on every frame (frame 0: wait until 0, -frame 1: wait until 1, frame 2: wait until 0) before waiting for VSync -otherwise the GPU will only draw the first field if you don't have drawing -to displayed area enabled. Performing this check in a low resolution/non -interlaced mode is harmless. - -* There's a hardware bug in the GPU FillVRAM command GP0(02h) where if you -set the height to 512 pixels the primitive is processed with a height of 0 -as the hardware does not appear to interpret the last bit of the height -value. This is most apparent when putting a DRAWENV with the height of 512 -pixels (for PAL for example) and background clearing is enabled, hence why -DRAWENV.isbg is not effective in the official SDK. - -* In the official SDK, DMA IRQs appear to be enabled only when a callback -function is set (ie. DrawSyncCallback() enables IRQ for DMA channel 2). DMA -IRQs are only triggered on transfer completion. - -Additional notes by spicyjpeg: - -* The controller/memory card SPI interface is poorly implemented in most -emulators, making custom controller polling code insanely hard to write and -debug. The only emulator that comes close to real hardware is no$psx, which -seems to correctly implement all features of the SPI port (even those not used -by the BIOS pad driver, such as TX/RX interrupts). DuckStation only emulates -the bare minimum required by the BIOS and Sony libraries, and pcsx-redux has -major bugs that break most custom polling implementations. This pretty much -means TX/RX IRQs and many flags in the JOY_* registers should not be used -unless you are willing to break compatibility with emulators. - -* As if communicating with controllers wasn't difficult enough already, -DualShock pads also have a built-in watchdog timer that gets enabled when first -putting them in configuration mode (and is NOT disabled after exiting config -mode). If no polling commands are sent to the controller for about 1 second, -vibration motors are switched off and analog mode is disabled; the same happens -if the analog button is pressed while in analog mode. In order to always keep -the pad in analog mode you must: - - * Poll both controller ports at least once per frame by sending command 42h. - Polling at a higher rate might be desirable in some cases (such as rhythm - games) to increase timing accuracy. - * If a digital pad response (type = 4) is received from a port that hasn't - previously been flagged as digital-only, attempt to put the pad into config - mode using command 43h *twice* (as the proper response is delayed). - * If the pad doesn't recognize the config command, flag it as digital-only - and treat all further digital pad responses from it as valid. - * If the pad recognizes the command, it will reply by identifying as an - analog pad in config mode (type = 15). Send command 44h immediately - (without sending 42h first, otherwise the pad will exit config mode) to - enable analog mode and turn on the LED. - * Pressing the analog button will result in the controller identifying as - digital even though it is not flagged as such, thus re-triggering the - configuration process and putting it back into analog mode. - * All analog pad responses (type = 7) can be treated as valid, as they will - come from controllers that have already been configured. - * If no valid response is received, assume no controller is connected and - reset the port's digital-only flag. - -* The SDK provides no support yet for replacing the BIOS exception handler with -a custom one, however it can be done (if you are ok with losing all BIOS -controller, memory card and file functionality). In order not to break anything -your exception handler must do the following: - - * prevent GTE opcodes from being executed twice due to a hardware glitch (the - nocash docs explain how to do this); - * define _irq_func_table[12] as an *extern* array of function pointers, call - the appropriate entry when an IRQ occurs and clear the respective flag in - register 1F801070h; - * handle syscalls 01h-02h, i.e. EnterCriticalSection and ExitCriticalSection - properly. You should also handle syscall FF00h (invalid API usage), as well - as breaks 1800h and 1C00h (division errors injected by GCC), by locking up - and maybe showing a BSOD or similar; - * overwrite the default BIOS API vectors with a passthrough that checks no - controller- or interrupt-related function is being called. This is necessary - (although ugly) as libpsn00b often calls such functions internally. - -* For some reason mipsel-unknown-elf-nm and mipsel-none-elf-nm (symbol map -generators) insist on outputting 64-bit addresses (with the top 32 bits set, -e.g. FFFFFFFF80010000) even when feeding it a regular 32-bit MIPS executable, -while the standard x86 nm tool that ships with most GCC packages prints the -proper 32-bit address. Unclear whether this is a bug, intended behavior or the -result of some ancient ELF ABI flag crap. DL_ParseSymbolMap() will ignore the -top 32 bits, so this should only bother you if you're implementing your own -symbol map parser. - -* I haven't worked on psxspu but, for those willing to write some code, this is -the formula to calculate SPU pitch values for playing musical notes ("^" is the -power operator, not xor): - - frequency = (ref / 32) * (2 ^ ((note - 9) / 12)) - spu_pitch = frequency / 44100 * 4096 - - ref = frequency the sample should be played at to play a middle A (MIDI note 69) - note = MIDI note number (usually 0-127, 60 is middle C) - -* If you are overriding any of the memory allocation functions, DO NOT ENABLE -LINK-TIME OPTIMIZATION. GCC has a long-standing bug with LTO and weak functions -written in assembly, also LTO hasn't been tested at all yet. - -Obscure CMake issues and related stuff: - -* Toolchain files are loaded "early" according to the CMake docs. What this -means in practice is that a lot of commands, such as find_*(), won't work -properly in a toolchain script as they rely on variables initialized by the -project() command. The poorly documented solution to this is to move such -commands to a separate file and set CMAKE_PROJECT_INCLUDE to point to it, so -project() will execute it immediately after initialization. - -* After executing the toolchain file, CMake generates and attempts to build -several dummy projects to test the compiler. Each of these projects re-includes -the toolchain script (which is why you'll see commands executed multiple times) -and uses the same variable values as the main project, however CMake will *NOT* -pass custom variables through by default. If your toolchain script has options -that can be set via custom variables (like PSN00BSDK_TC and PSN00BSDK_PREFIX in -PSn00bSDK), you'll have to set CMAKE_TRY_COMPILE_PLATFORM_VARIABLES to a list -of variable names to be exported to generated dummy projects. - -* There is no way to use multiple toolchains (PS1 + host) in a single project, -even if you use add_subdirectory() to execute multiple project files (which, -confusingly, adds their targets to the parent project rather than treating them -as separate projects). Thankfully though CMake provides support for automating -the build process of independent CMake projects via the ExternalProject module. -Which brings me to the next issue... - -* If you run CPack on a "superbuild" project (i.e. a project that calls -ExternalProject_Add() to configure, compile and install subprojects at build -time), you'll likely run into a weird issue with CPack bundling folders from -your build directory into DEB and RPM packages. This is caused by the DEB/RPM -generators running "cmake --install" in a chroot/fakeroot to prepare the files -to be packaged, which seems to interfere with absolute paths in the project -cache or something like that (?). The only workaround I know of is to use -CPACK_PRE_BUILD_SCRIPTS to trigger a custom script that deletes anything other -than the actual files to be packaged (see cpack/fakeroot_fix.cmake). - -* Project installation might fail on macOS (and possibly some Linux distros) if -CMake attempts to set permissions on system directories such as /usr/local. -This is usually caused by install() commands that copy files to the root -installation directory rather than to a subfolder, like this: - - install( - DIRECTORY install_tree/ - DESTINATION . - USE_SOURCE_PERMISSIONS - ) - -If the USE_SOURCE_PERMISSIONS flag is specified CMake will attempt to set -permissions on the DESTINATION folder, which in this case would be the root -prefix (/usr/local by default on macOS), to match the source directory. This -will however fail as macOS restricts top-level system directories from having -their permissions changed. The simplest workaround is to avoid using -"DESTINATION ." and install each subdirectory explicitly instead, like this: - - foreach( - _dir IN ITEMS - ${CMAKE_INSTALL_BINDIR} - ${CMAKE_INSTALL_LIBDIR} - ${CMAKE_INSTALL_INCLUDEDIR} - ${CMAKE_INSTALL_DATADIR} - ) - install( - DIRECTORY install_tree/${_dir}/ - DESTINATION ${_dir} - USE_SOURCE_PERMISSIONS - ) - endforeach() - -* Depending on how you find external dependencies (find_package(), vcpkg, -pkg-config...), CMake may end up outputting an executable that relies on a DLL -installed system-wide. To correctly install the DLL alongside the executable -you have to specify a regex as follows: - - install( - TARGETS my_executable - RUNTIME_DEPENDENCIES - PRE_EXCLUDE_REGEXES ".*" - PRE_INCLUDE_REGEXES "tinyxml2" - ) - -CMake will scan the executable at install time and copy all the required DLLs -that match the second regex. If no regex is specified CMake will also copy OS -DLLs like libc or msvcrt, which usually isn't the desired behavior. - -* Using interface targets to set include directories can be finicky. Not only -do you have to use generator expressions to conditionally use different paths -depending on whether the targets are installed, but CMake can get confused on -which options to pass to the compiler. I spent hours trying to get CMake to use --I rather than -isystem for include directories (for some reason GCC would -ignore -isystem completely). I eventually gave up and just set the include -directories manually for each target, and for some reason CMake actually -started passing -I instead of -isystem to GCC. - -* When using CPack with NSIS, all CPACK_NSIS_* variables are passed to NSIS -verbatim, i.e. without the usual slash-to-backslash path conversion that CMake -does on Windows. Most Windows programs accept paths with slashes without issue, -unfortunately the NSIS builder is not one of those. To add insult to injury, -CMake doesn't even escape backslashes by default when quoting strings in the -generated CPack config file! So you have to convert the paths manually *and* -tell CMake to enable escaping by setting CPACK_VERBATIM_VARIABLES, like this: - - set(CPACK_VERBATIM_VARIABLES ON) - foreach( - _var IN ITEMS - CPACK_NSIS_MUI_ICON - CPACK_NSIS_MUI_UNIICON - CPACK_NSIS_MUI_HEADERIMAGE - CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP - CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP - ) - cmake_path(NATIVE_PATH ${_var} ${_var}) - endforeach() - -* Not a CMake/CPack bug per se, but NSIS is picky about the banner and header -images shown in generated installers. They must be Windows BMP version 3 files -with no alpha channel, no compression and no metadata. They can either be -24-bit RGB or indexed, though it's common to use indexed colors to save space. diff --git a/doc/dev_notes.md b/doc/dev_notes.md new file mode 100644 index 0000000..3aa2304 --- /dev/null +++ b/doc/dev_notes.md @@ -0,0 +1,280 @@ + +# Development notes + +These are some development notes I've put together that would be of great aid +to those willing to contribute to the PSn00bSDK project. Many of these came +from my own experience dealing with the PS1 at low-level when I ran into some +unexplained quirks while some are from disassembly observations and +clarification of existing documents. More entries will be added when I run into +more previously undocumented quirks in the future. _- Lameguy64_ + +Porting PSn00bSDK to CMake also uncovered a lot of bugs and undocumented +behavior in CMake itself. I documented [below](#cmake) all issues I ran into. +_- spicyjpeg_ + +## MIPS ABI / compiler + +- When calling C functions (ie. BIOS functions) from assembly code you'll need + to allocate N words on the stack first when calling a function that has N + arguments (`addiu $sp, -(4*N)` where N = number of arguments of the function + being called) even if the arguments are on registers `a0` to `a3` and the C + functions don't always use the space allotted in stack. When calling a + function with a variable number of arguments (`printf`) always allocate 16 + bytes of stack. + +- For some reason `mipsel-unknown-elf-nm` and `mipsel-none-elf-nm` (symbol map + generators) insist on outputting 64-bit addresses (with the top 32 bits set, + e.g. `FFFFFFFF80010000`) even when feeding it a regular 32-bit MIPS + executable, while the standard x86 nm tool that ships with most GCC packages + prints the proper 32-bit address. Unclear whether this is a bug, intended + behavior or the result of some ancient ELF ABI flag crap. + `DL_ParseSymbolMap()` will ignore the top 32 bits, so this should only bother + you if you're implementing your own symbol map parser. + +- If you are overriding any of the memory allocation functions, + **DO NOT ENABLE LINK-TIME OPTIMIZATION**. GCC has a long-standing bug with + LTO and weak functions written in assembly, also LTO hasn't been tested at + all yet. + +## BIOS and interrupts + +- Hooking a custom handler using BIOS function `HookEntryInt` (`B(19h)`, known + as `SetCustomExitFromException` in nocash docs) is only triggered when + there's an IRQ that is not yet acknowledged by previous IRQ handlers built + into the kernel. This is also the best point to acknowledge any IRQs without + breaking compatibility with built-in BIOS IRQ handlers and is what the + official SDK uses to handle IRQs. To make sure this handler is triggered on + every interrupt you must call `ChangeClearPad(0)` and `ChangeClearRCnt(3, 0)` + (which are functions `B(5Bh)` and `C(0Ah)` respectively) otherwise the pad + and root counter handlers in the kernel will acknowledge the interrupt before + your handler, preventing you from handling them yourself. + +- It is not advisable to handle interrupts using event handlers like in PSXSDK + as it breaks BIOS features that depend on interrupts. Clearing the VBlank IRQ + in a event handler for example prevents the BIOS controller functions from + working as it depends on the VBlank IRQ to determine when to query + controllers. Acknowledge interrupts using a custom handler set by BIOS + function `HookEntryInt` (`B(19h)`, known as `SetCustomExitFromException` in + nocash docs). + +- In the official SDK, DMA IRQs appear to be enabled only when a callback + function is set (ie. `DrawSyncCallback()` enables IRQ for DMA channel 2). DMA + IRQs are only triggered on transfer completion. + +- PSn00bSDK provides no support yet for replacing the BIOS exception handler + with a custom one, however it can be done (if you are ok with losing all BIOS + controller, memory card and file functionality). In order not to break + anything your exception handler must do the following: + + - prevent GTE opcodes from being executed twice due to a hardware glitch (the + nocash docs explain how to do this); + - define `_irq_func_table[12]` as an *extern* array of function pointers, + call the appropriate entry when an IRQ occurs and clear the respective flag + in register `1F801070h`; + - handle syscalls `01h`-`02h`, i.e. `EnterCriticalSection` and + `ExitCriticalSection`, properly. You should also handle syscall `FF00h` + (invalid API usage), as well as breaks `1800h` and `1C00h` (division errors + injected by GCC), by locking up and maybe showing a BSOD or similar; + - overwrite the default BIOS API vectors with a passthrough that checks no + controller- or interrupt-related function is being called. This is necessary + (although ugly) as `libpsn00b` often calls such functions internally. + +## Hardware + +- When running in high resolution mode you must additionally wait for bit 31 in + `GPUSTAT` (`1F801814h`) to alternate on every frame (frame 0: wait until 0, + frame 1: wait until 1, frame 2: wait until 0) before waiting for VSync + otherwise the GPU will only draw the first field if you don't have drawing to + displayed area enabled. Performing this check in a low resolution/non + interlaced mode is harmless. + +- There's a hardware bug in the GPU `FillVRAM` command `GP0(02h)` where if you + set the height to 512 pixels the primitive is processed with a height of 0 as + the hardware does not appear to interpret the last bit of the height value. + This is most apparent when putting a DRAWENV with the height of 512 pixels + (for PAL for example) and background clearing is enabled, hence why + `DRAWENV.isbg` is not effective in the official SDK. + +- The controller/memory card SPI interface is poorly implemented in most + emulators, making custom controller polling code insanely hard to write and + debug. The only emulator that comes close to real hardware is no$psx, which + seems to correctly implement all features of the SPI port (even those not + used by the BIOS pad driver, such as TX/RX interrupts). DuckStation only + emulates the bare minimum required by the BIOS and Sony libraries, and + pcsx-redux has major bugs that break most custom polling implementations. + This pretty much means TX/RX IRQs and many flags in the `JOY_*` registers + should **not** be used unless you are willing to break compatibility with + emulators. + +- As if communicating with controllers wasn't difficult enough already, + DualShock pads also have a built-in watchdog timer that gets enabled when + first putting them in configuration mode (and is **not** disabled after + exiting config mode). If no polling commands are sent to the controller for + about 1 second, vibration motors are switched off and analog mode is + disabled; the same happens if the analog button is pressed while in analog + mode. In order to always keep the pad in analog mode you must: + + 1. Poll both controller ports at least once per frame by sending command + `42h`. Polling at a higher rate might be desirable in some cases (such as + rhythm games) to increase timing accuracy. + 2. If a digital pad response (type = 4) is received from a port that hasn't + previously been flagged as digital-only, attempt to put the pad into + config mode using command `43h` *twice* (as the proper response is + delayed). + - If the pad doesn't recognize the config command, flag it as digital-only + and treat all further digital pad responses from it as valid. + - If the pad recognizes the command, it will reply by identifying as an + analog pad in config mode (type = 15). Send command `44h` immediately + (without sending `42h` first, otherwise the pad will exit config mode) + to enable analog mode and turn on the LED. + - Pressing the analog button will result in the controller identifying as + digital even though it is not flagged as such, thus re-triggering the + configuration process and putting it back into analog mode. + 3. All analog pad responses (type = 7) can be treated as valid, as they will + come from controllers that have already been configured. + 4. If no valid response is received, assume no controller is connected and + reset the port's digital-only flag. + +- I haven't worked on `psxspu` but, for those willing to write some code, this + is the formula to calculate SPU pitch values for playing musical notes (`^` + is the power operator, not xor): + + ``` + frequency = (ref / 32) * (2 ^ ((note - 9) / 12)) + spu_pitch = frequency / 44100 * 4096 + + ref = frequency the sample should be played at to play a middle A (MIDI note 69) + note = MIDI note number (usually 0-127, 60 is middle C) + ``` + +## CMake + +- Toolchain files are loaded "early" according to the CMake docs. What this + means in practice is that a lot of commands, such as `find_*()`, won't work + properly in a toolchain script as they rely on variables initialized by the + `project()` command. The poorly documented solution to this is to move such + commands to a separate file and set `CMAKE_PROJECT_INCLUDE` to point to it, + so `project()` will execute it immediately after initialization. + +- After executing the toolchain file, CMake generates and attempts to build + several dummy projects to test the compiler. Each of these projects + re-includes the toolchain script (which is why you'll see commands executed + multiple times) and uses the same variable values as the main project, + however CMake will *not* pass custom variables through by default. If your + toolchain script has options that can be set via custom variables (like + `PSN00BSDK_TC` and `PSN00BSDK_PREFIX` in PSn00bSDK), you'll have to set + `CMAKE_TRY_COMPILE_PLATFORM_VARIABLES` to a list of variable names to be + exported to generated dummy projects. + +- There is no way to use multiple toolchains (PS1 + host) in a single project, + even if you use `add_subdirectory()` to execute multiple project files + (which, confusingly, adds their targets to the parent project rather than + treating them as separate projects). Thankfully though CMake provides support + for automating the build process of independent CMake projects via the + `ExternalProject` module. Which brings me to the next issue... + +- If you run CPack on a "superbuild" project (i.e. a project that calls + `ExternalProject_Add()` to configure, compile and install subprojects at + build time), you'll likely run into a weird issue with CPack bundling folders + from your build directory into DEB and RPM packages. This is caused by the + DEB/RPM generators running `cmake --install` in a chroot/fakeroot to prepare + the files to be packaged, which seems to interfere with absolute paths in the + project cache or something like that (?). The only workaround I know of is to + use `CPACK_PRE_BUILD_SCRIPTS` to trigger a custom script that deletes + anything other than the actual files to be packaged (see + `cpack/fakeroot_fix.cmake`). + +- Project installation might fail on macOS (and possibly some Linux distros) if + CMake attempts to set permissions on system directories such as `/usr/local`. + This is usually caused by `install()` commands that copy files to the root + installation directory rather than to a subfolder, like this: + + ```cmake + install( + DIRECTORY install_tree/ + DESTINATION . + USE_SOURCE_PERMISSIONS + ) + ``` + + If the `USE_SOURCE_PERMISSIONS` flag is specified CMake will attempt to set + permissions on the `DESTINATION` folder, which in this case would be the root + prefix (`/usr/local` by default on macOS), to match the source directory. + This will however fail as macOS restricts top-level system directories from + having their permissions changed. The simplest workaround is to avoid using + `DESTINATION .` and install each subdirectory explicitly instead, like this: + + ```cmake + foreach( + _dir IN ITEMS + ${CMAKE_INSTALL_BINDIR} + ${CMAKE_INSTALL_LIBDIR} + ${CMAKE_INSTALL_INCLUDEDIR} + ${CMAKE_INSTALL_DATADIR} + ) + install( + DIRECTORY install_tree/${_dir}/ + DESTINATION ${_dir} + USE_SOURCE_PERMISSIONS + ) + endforeach() + ``` + +- Depending on how you find external dependencies (`find_package()`, vcpkg, + pkg-config...), CMake may end up outputting an executable that relies on a + DLL installed system-wide. To correctly install the DLL alongside the + executable you have to specify a regex as follows: + + ```cmake + install( + TARGETS my_executable + RUNTIME_DEPENDENCIES + PRE_EXCLUDE_REGEXES ".*" + PRE_INCLUDE_REGEXES "tinyxml2" + ) + ``` + + CMake will scan the executable at install time and copy all the required DLLs + that match the second regex. If no regex is specified CMake will also copy OS + DLLs like `libc` or `msvcrt`, which usually isn't the desired behavior. + +- Using interface targets to set include directories can be finicky. Not only + do you have to use generator expressions to conditionally use different paths + depending on whether the targets are installed, but CMake can get confused on + which options to pass to the compiler. I spent hours trying to get CMake to + use `-I` rather than `-isystem` for include directories (for some reason GCC + would ignore `-isystem` completely). I eventually gave up and just set the + include directories manually for each target, and for some reason CMake + actually started passing `-I` instead of `-isystem` to GCC. + +- When using CPack with NSIS, all `CPACK_NSIS_*` variables are passed to NSIS + verbatim, i.e. without the usual slash-to-backslash path conversion that + CMake does on Windows. Most Windows programs accept paths with slashes + without issue, unfortunately the NSIS builder is not one of those. To add + insult to injury, CMake doesn't even escape backslashes by default when + quoting strings in the generated CPack config file! So you have to convert + the paths manually *and* tell CMake to enable escaping by setting + `CPACK_VERBATIM_VARIABLES`, like this: + + ```cmake + set(CPACK_VERBATIM_VARIABLES ON) + foreach( + _var IN ITEMS + CPACK_NSIS_MUI_ICON + CPACK_NSIS_MUI_UNIICON + CPACK_NSIS_MUI_HEADERIMAGE + CPACK_NSIS_MUI_WELCOMEFINISHPAGE_BITMAP + CPACK_NSIS_MUI_UNWELCOMEFINISHPAGE_BITMAP + ) + cmake_path(NATIVE_PATH ${_var} ${_var}) + endforeach() + ``` + +- Not a CMake/CPack bug per se, but NSIS is picky about the banner and header + images shown in generated installers. They must be Windows BMP version 3 + files with no alpha channel, no compression and no metadata. They can either + be 24-bit RGB or indexed, though it's common to use indexed colors to save + space. + +----------------------------------------- +_Last updated on 2021-11-28 by spicyjpeg_ diff --git a/examples/io/system573/CMakeLists.txt b/examples/io/system573/CMakeLists.txt new file mode 100644 index 0000000..34c9153 --- /dev/null +++ b/examples/io/system573/CMakeLists.txt @@ -0,0 +1,27 @@ +# PSn00bSDK example CMake script +# (C) 2021 spicyjpeg - MPL licensed + +cmake_minimum_required(VERSION 3.21) + +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) + set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) +endif() + +project( + system573 + LANGUAGES C ASM + VERSION 1.0.0 + DESCRIPTION "PSn00bSDK Konami System 573 example" + HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk" +) + +file(GLOB _sources *.c *.s) +psn00bsdk_add_executable(system573 STATIC ${_sources}) +psn00bsdk_add_cd_image(system573_iso system573 iso.xml DEPENDS system573) + +install( + FILES + ${PROJECT_BINARY_DIR}/system573.bin + ${PROJECT_BINARY_DIR}/system573.cue + TYPE BIN +) diff --git a/examples/io/system573/iso.xml b/examples/io/system573/iso.xml new file mode 100644 index 0000000..09b4d85 --- /dev/null +++ b/examples/io/system573/iso.xml @@ -0,0 +1,34 @@ + + + + + + + + + + + + + + + + + diff --git a/examples/io/system573/main.c b/examples/io/system573/main.c new file mode 100644 index 0000000..67a98da --- /dev/null +++ b/examples/io/system573/main.c @@ -0,0 +1,371 @@ +/* + * PSn00bSDK Konami System 573 example + * (C) 2021 spicyjpeg - MPL licensed + * + * This is a minimal example demonstrating how to target the Konami System 573 + * using PSn00bSDK. The System 573 is a PS1-based arcade motherboard that + * powered various Konami arcade games throughout the late 1990s, most notably + * Dance Dance Revolution and other Bemani rhythm games. It came in several + * configurations, with slightly different I/O connectors depending on the game + * and two optional add-on modules (known as the "analog I/O" and "digital I/O" + * boards respectively) providing light control outputs and, in the case of the + * digital I/O board, MP3 audio playback. + * + * Unlike other arcade systems based on PS1 hardware, the 573 is mostly + * identical to a regular PS1, with almost all custom extensions mapped into + * the expansion port region at 0x1f000000. The major differences are: + * + * - RAM is 4 MB instead of 2, and VRAM is 2 MB instead of 1. It is recommended + * *not* to use the additional memory to preserve PS1 compatibility. + * + * - The CD drive is replaced by a standard IDE/ATAPI drive (which most of the + * time is going to be an aftermarket DVD drive, as the original drives the + * system shipped with were prone to failure and couldn't read CD-Rs). This + * also means the 573 has no support at all for XA audio playback, as XA is + * not part of the CD-ROM specification implemented by IDE drives. CD audio + * is supported by most IDE drives, but 573 units with the digital I/O board + * installed have the 4-pin audio cable plugged into that instead of the + * drive. The IDE bus is connected to IRQ10 and DMA5 (expansion port) instead + * of IRQ2 and DMA3, which go unused. + * + * - The BIOS seems to have most file I/O APIs removed and exposes no functions + * whatsoever for accessing the IDE drive or the filesystem on the disc. The + * launcher/shell is completely different from Sony's shell and is capable of + * loading an executable from the CD drive, a PCMCIA memory-mapped flash card + * or the internal 16 MB flash memory. + * + * - The SPI controller bus seems to be left unconnected. Inputs are routed to + * a JAMMA PCB edge connector and handled through two custom/relabeled chips, + * which expose the inputs as memory-mapped registers. There is also a JVS + * port (i.e. RS-485 serial bus wired to a USB-A connector, commonly used for + * daisy-chaining peripherals in arcade cabinets) managed by a + * microcontroller. + * + * - There is a "security cartridge" slot, which breaks out the serial port as + * well as several GPIO pins. All security cartridge communication and DRM is + * handled by games rather than by the BIOS, so a security cartridge is *not* + * required to boot homebrew. Each game came with a different cartridge type; + * many of them expose the serial port or provide additional game-specific + * I/O connectors. + * + * Currently the only publicly available documentation for the custom registers + * is the System 573 MAME driver. Also keep in mind that the psxcd library does + * not yet support IDE drives, so the 573's drive can only be accessed by + * writing a custom ATAPI driver and ISO9660 parser (which is out of the scope + * of this example). + * + * https://github.com/mamedev/mame/blob/master/src/mame/drivers/ksys573.cpp + * https://github.com/mamedev/mame/blob/master/src/mame/machine/k573dio.cpp + */ + +#include +#include +#include +#include +#include +#include +#include +#include + +/* Register definitions */ + +#define EXP1_ADDR *((volatile uint32_t *) 0x1f801000) +#define EXP1_CTRL *((volatile uint32_t *) 0x1f801008) + +#define K573_IN0 *((volatile uint16_t *) 0x1f400000) +#define K573_IN1_L *((volatile uint16_t *) 0x1f400004) +#define K573_IN1_H *((volatile uint16_t *) 0x1f400006) +#define K573_IN2 *((volatile uint16_t *) 0x1f400008) +#define K573_IN3_L *((volatile uint16_t *) 0x1f40000c) +#define K573_IN3_H *((volatile uint16_t *) 0x1f40000e) +#define K573_BANK *((volatile uint16_t *) 0x1f500000) +#define K573_WATCHDOG *((volatile uint16_t *) 0x1f5c0000) + +#define K573_IDE_CS0 ((volatile uint16_t *) 0x1f480000) +#define K573_IDE_CS1 ((volatile uint16_t *) 0x1f4c0000) +#define K573_RTC ((volatile uint16_t *) 0x1f620000) +#define K573_IO_BOARD ((volatile uint16_t *) 0x1f640000) + +typedef enum { + ANALOG_IO_LIGHTS0 = 0x20, + ANALOG_IO_LIGHTS1 = 0x22, + ANALOG_IO_LIGHTS2 = 0x24, + ANALOG_IO_LIGHTS3 = 0x26, + + // The digital I/O board has a lot more registers than these, but there + // seems to be no DIGITAL_IO_LIGHTS6 register. WTF + DIGITAL_IO_LIGHTS1 = 0x70, + DIGITAL_IO_LIGHTS0 = 0x71, + DIGITAL_IO_LIGHTS3 = 0x72, + DIGITAL_IO_LIGHTS7 = 0x73, + DIGITAL_IO_LIGHTS4 = 0x7d, + DIGITAL_IO_LIGHTS5 = 0x7e, + DIGITAL_IO_LIGHTS2 = 0x7f +} IO_BOARD_REG; + +// The 573's real-time clock chip is an M48T58, which behaves like a standard +// 8 KB battery-backed SRAM with a bunch of special registers. Official games +// store highscores and settings in RTC RAM. +typedef enum { + RTC_CTRL = 0x1ff8, + RTC_SECONDS = 0x1ff9, + RTC_MINUTES = 0x1ffa, + RTC_HOURS = 0x1ffb, + RTC_DAY_OF_WEEK = 0x1ffc, + RTC_DAY_OF_MONTH = 0x1ffd, + RTC_MONTH = 0x1ffe, + RTC_YEAR = 0x1fff +} RTC_REG; + +#define btoi(x) ((((x) >> 4) & 0xf) * 10 + ((x) & 0xf)) + +/* Display/GPU context utilities */ + +#define SCREEN_XRES 320 +#define SCREEN_YRES 240 + +#define BGCOLOR_R 48 +#define BGCOLOR_G 24 +#define BGCOLOR_B 0 + +typedef struct { + DISPENV disp; + DRAWENV draw; +} DB; + +typedef struct { + DB db[2]; + uint32_t db_active; +} CONTEXT; + +void init_context(CONTEXT *ctx) { + DB *db; + + ResetGraph(0); + ctx->db_active = 0; + + db = &(ctx->db[0]); + SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); + setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); + db->draw.isbg = 1; + db->draw.dtd = 1; + + db = &(ctx->db[1]); + SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES); + setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); + db->draw.isbg = 1; + db->draw.dtd = 1; + + PutDrawEnv(&(db->draw)); + //PutDispEnv(&(db->disp)); + + // Create a text stream at the top of the screen. + FntLoad(960, 0); + FntOpen(8, 16, 304, 208, 2, 512); +} + +void display(CONTEXT *ctx) { + DB *db; + + DrawSync(0); + VSync(0); + ctx->db_active ^= 1; + + db = &(ctx->db[ctx->db_active]); + PutDrawEnv(&(db->draw)); + PutDispEnv(&(db->disp)); + SetDispMask(1); +} + +/* Input polling utilities */ + +typedef struct { + uint8_t p1_joy, p1_btn; + uint8_t p2_joy, p2_btn; + uint8_t coin, dip_sw; +} JAMMA_INPUTS; + +void get_jamma_inputs(JAMMA_INPUTS *output) { + uint16_t in1l = K573_IN1_L; + uint16_t in1h = K573_IN1_H; + uint16_t in2 = K573_IN2; + uint16_t in3l = K573_IN3_L; + uint16_t in3h = K573_IN3_H; + uint8_t p1_btn, p2_btn, coin; + + // Rearrange the bits read from the input register into something that's + // easier to parse and display. Refer to MAME for information on what each + // bit in the IN* registers does. + p1_btn = ((in2 >> 15) & 0x0001); // Bit 0 = start button + p1_btn |= ((in2 >> 8) & 0x0007) << 1; // Bit 1-3 = buttons 1-3 + p1_btn |= ((in3l >> 8) & 0x0003) << 4; // Bit 4-5 = buttons 4-5 + p1_btn |= ((in3l >> 11) & 0x0001) << 6; // Bit 6 = button 6 + p2_btn = ((in2 >> 7) & 0x0001); // Bit 0 = start button + p2_btn |= ((in2 >> 4) & 0x0007) << 1; // Bit 1-3 = buttons 1-3 + p2_btn |= ((in3h >> 8) & 0x0003) << 4; // Bit 4-5 = buttons 4-5 + p2_btn |= ((in3h >> 11) & 0x0001) << 6; // Bit 6 = button 6 + coin = ((in1h >> 8) & 0x0003); // Bit 0-1 = coin switches + coin |= ((in1h >> 12) & 0x0001) << 2; // Bit 2 = service button + coin |= ((in3l >> 10) & 0x0001) << 3; // Bit 3 = test button + coin |= ((in1h >> 10) & 0x0003) << 4; // Bit 4-5 = PCMCIA cards + + output->p1_joy = (in2 >> 8) & 0x000f; + output->p1_btn = p1_btn; + output->p2_joy = in2 & 0x000f; + output->p2_btn = p2_btn; + output->coin = coin; + output->dip_sw = in1l & 0x000f; +} + +/* I/O board (light control) utilities */ + +// This function controls light outputs on analog I/O boards. +void set_lights_analog(uint32_t lights) { + uint32_t bits; + + bits = (lights & 0x01010101) << 7; // Lamp 0 -> bit 7 + bits |= (lights & 0x02020202) << 5; // Lamp 1 -> bit 6 + bits |= (lights & 0x04040404) >> 1; // Lamp 2 -> bit 1 + bits |= (lights & 0x08080808) >> 3; // Lamp 3 -> bit 0 + bits |= (lights & 0x10101010) << 1; // Lamp 4 -> bit 5 + bits |= (lights & 0x20202020) >> 1; // Lamp 5 -> bit 4 + bits |= (lights & 0x40404040) >> 3; // Lamp 6 -> bit 3 + bits |= (lights & 0x80808080) >> 5; // Lamp 7 -> bit 2 + + K573_IO_BOARD[ANALOG_IO_LIGHTS0] = (bits) & 0xff; + K573_IO_BOARD[ANALOG_IO_LIGHTS1] = (bits >> 8) & 0xff; + K573_IO_BOARD[ANALOG_IO_LIGHTS2] = (bits >> 16) & 0xff; + K573_IO_BOARD[ANALOG_IO_LIGHTS3] = (bits >> 24) & 0xff; +} + +// This function controls light outputs on digital I/O boards (i.e. the ones +// that include MP3 playback hardware in addition to the light control). +// TODO: test this on real hardware -- it might not work if lights are handled +// by the board's FPGA, which requires a binary blob... +void set_lights_digital(uint32_t lights) { + uint32_t bits; + + bits = (lights & 0x11111111); // Lamp 0 -> bit 0 + bits |= (lights & 0x22222222) << 1; // Lamp 1 -> bit 2 + bits |= (lights & 0x44444444) << 1; // Lamp 2 -> bit 3 + bits |= (lights & 0x88888888) >> 2; // Lamp 3 -> bit 1 + + K573_IO_BOARD[DIGITAL_IO_LIGHTS0] = ((bits) & 0xf) << 12; + K573_IO_BOARD[DIGITAL_IO_LIGHTS1] = ((bits >> 4) & 0xf) << 12; + K573_IO_BOARD[DIGITAL_IO_LIGHTS2] = ((bits >> 8) & 0xf) << 12; + K573_IO_BOARD[DIGITAL_IO_LIGHTS3] = ((bits >> 12) & 0xf) << 12; + K573_IO_BOARD[DIGITAL_IO_LIGHTS4] = ((bits >> 16) & 0xf) << 12; + K573_IO_BOARD[DIGITAL_IO_LIGHTS5] = ((bits >> 20) & 0xf) << 12; + //K573_IO_BOARD[DIGITAL_IO_LIGHTS6] = ((bits >> 24) & 0xf) << 12; + K573_IO_BOARD[DIGITAL_IO_LIGHTS7] = ((bits >> 28) & 0xf) << 12; +} + +/* Main */ + +static CONTEXT ctx; + +#define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx); } +#define SHOW_ERROR(...) { SHOW_STATUS(__VA_ARGS__); while (1) __asm__("nop"); } + +int main(int argc, const char* argv[]) { + // Reinitialize the heap and relocate the stack to allow the 573's full 4 + // MB of RAM to be used. This isn't strictly required; executables designed + // for 2 MB of RAM will also run fine on the 573 (obviously). + // FIXME: this seems to be broken currently + //__asm__ volatile("li $sp, 0x803fffe0"); + //_mem_init(0x400000, 0x20000); + + EXP1_ADDR = 0x1f000000; + EXP1_CTRL = 0x24173f47; // 573 BIOS uses this value + K573_WATCHDOG = 0; + + init_context(&ctx); + + // Determine whether we are running on a 573 by fetching the version string + // from the BIOS. + const char *const version = (const char *const) GetSystemInfo(0x02); + //if (strncmp(version, "Konami OS", 9)) + //SHOW_ERROR("ERROR: NOT RUNNING ON A SYSTEM 573!\n\n[%s]\n", version); + + uint32_t counter = 0; + uint8_t last_joystick = 0xff; + uint8_t last_buttons = 0xff; + uint32_t current_light = 0; + uint32_t is_digital = 0; + + while (1) { + FntPrint(-1, "COUNTER=%d\n", counter++); + + JAMMA_INPUTS inputs; + get_jamma_inputs(&inputs); + + FntPrint(-1, "\nJAMMA INPUTS:\n"); + FntPrint(-1, " P1 JOYSTICK =%04@\n", inputs.p1_joy); + FntPrint(-1, " P1 BUTTONS =%07@\n", inputs.p1_btn); + FntPrint(-1, " P2 JOYSTICK =%04@\n", inputs.p2_joy); + FntPrint(-1, " P2 BUTTONS =%07@\n", inputs.p2_btn); + FntPrint(-1, " COIN/SERVICE=%04@\n", inputs.coin); + FntPrint(-1, " DIP SWITCHES=%04@\n", inputs.dip_sw); + + FntPrint(-1, "\nCABINET LIGHTS:\n"); + FntPrint(-1, " BOARD=%s I/O\n", is_digital ? "DIGITAL" : "ANALOG"); + FntPrint(-1, " LIGHT=%d\n\n", current_light); + FntPrint(-1, " [START] CHANGE BOARD TYPE\n"); + FntPrint(-1, " [LEFT/RIGHT] SELECT LIGHT TO TEST\n"); + + // Request the current date/time from the RTC and display it. + K573_RTC[RTC_CTRL] |= 0x40; + FntPrint(-1, "\nRTC:\n"); + FntPrint( + -1, + " %02d-%02d-%02d %02d:%02d:%02d\n", + btoi(K573_RTC[RTC_YEAR]), + btoi(K573_RTC[RTC_MONTH]), + btoi(K573_RTC[RTC_DAY_OF_MONTH] & 0x3f), + btoi(K573_RTC[RTC_HOURS]), + btoi(K573_RTC[RTC_MINUTES]), + btoi(K573_RTC[RTC_SECONDS] & 0x7f) + ); + + FntPrint(-1, "\nSYSTEM:\n"); + FntPrint(-1, " KERNEL=%s\n", version); + FntPrint(-1, " PCMCIA=%02@\n", inputs.coin >> 4); + + FntFlush(-1); + display(&ctx); + + // Reset the watchdog. This must be done at least once per frame to + // prevent the 573 from rebooting. + K573_WATCHDOG = 0; + + if (is_digital) + set_lights_digital(1 << current_light); + else + set_lights_analog(1 << current_light); + + // Handle inputs. + if ((last_joystick & 0x01) && !(inputs.p1_joy & 0x01)) // Left + current_light--; + if ((last_joystick & 0x02) && !(inputs.p1_joy & 0x02)) // Right + current_light++; + if ((last_buttons & 0x02) && !(inputs.p1_btn & 0x02)) // Button 1 + current_light--; + if ((last_buttons & 0x04) && !(inputs.p1_btn & 0x04)) // Button 2 + current_light++; + if ((last_buttons & 0x01) && !(inputs.p1_btn & 0x01)) { // Start + is_digital = !is_digital; + if (is_digital) + set_lights_analog(0); + else + set_lights_digital(0); + } + + current_light %= 32; + last_joystick = inputs.p1_joy; + last_buttons = inputs.p1_btn; + } + + return 0; +} -- cgit v1.2.3 From 6c7512ff42805e9399dfee8b67e2f70fa55909d3 Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Thu, 23 Dec 2021 21:58:56 +0100 Subject: Add sound/spustream example --- examples/sound/spustream/CMakeLists.txt | 29 ++ examples/sound/spustream/convert_stream.py | 112 +++++++ examples/sound/spustream/iso.xml | 29 ++ examples/sound/spustream/main.c | 456 +++++++++++++++++++++++++++++ examples/sound/spustream/system.cnf | 4 + 5 files changed, 630 insertions(+) create mode 100644 examples/sound/spustream/CMakeLists.txt create mode 100644 examples/sound/spustream/convert_stream.py create mode 100644 examples/sound/spustream/iso.xml create mode 100644 examples/sound/spustream/main.c create mode 100644 examples/sound/spustream/system.cnf diff --git a/examples/sound/spustream/CMakeLists.txt b/examples/sound/spustream/CMakeLists.txt new file mode 100644 index 0000000..91243cf --- /dev/null +++ b/examples/sound/spustream/CMakeLists.txt @@ -0,0 +1,29 @@ +# PSn00bSDK example CMake script +# (C) 2021 spicyjpeg - MPL licensed + +cmake_minimum_required(VERSION 3.20) + +if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) + set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) +endif() + +project( + spustream + LANGUAGES C + VERSION 1.0.0 + DESCRIPTION "PSn00bSDK SPU custom streaming example" + HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk" +) + +# TODO: add rules to actually generate a valid STREAM.BIN file +file(GLOB _sources *.c) +psn00bsdk_add_executable(spustream STATIC ${_sources}) +#psn00bsdk_add_cd_image(spustream_iso spustream iso.xml DEPENDS spustream) + +install( + FILES + #${PROJECT_BINARY_DIR}/spustream.bin + #${PROJECT_BINARY_DIR}/spustream.cue + ${PROJECT_BINARY_DIR}/spustream.exe + TYPE BIN +) diff --git a/examples/sound/spustream/convert_stream.py b/examples/sound/spustream/convert_stream.py new file mode 100644 index 0000000..1b1696f --- /dev/null +++ b/examples/sound/spustream/convert_stream.py @@ -0,0 +1,112 @@ +#!/usr/bin/env python3 +# Simple .VAG to STREAM.BIN interleaving tool +# (C) 2021 spicyjpeg - MPL licensed + +import sys +from warnings import warn +from struct import Struct +from itertools import zip_longest +from argparse import ArgumentParser, FileType + +VAG_HEADER = Struct("> 4s I 4x 2I 12x 16s") +VAG_MAGIC = b"VAGp" +SAMPLE_RATE = 44100 +BUFFER_SIZE = 26624 # (26624 / 16 * 28) / 44100 = 1.05 seconds +ALIGN_SIZE = 2048 + +## Helpers + +def align(data, size): + chunks = (len(data) + size - 1) // size + + return data.ljust(chunks * size, b"\x00") + +def set_loop_flag(data): + last_block = bytearray(data[-16:]) + last_block[1] = 0x03 # Jump to loop point + sustain + + return data[:-16] + last_block + +## .VAG file reader + +def read_vag(_file, chunk_size): + with _file: + header = _file.read(VAG_HEADER.size) + ( + magic, + version, + size, + sample_rate, + name + ) = VAG_HEADER.unpack(header) + + #if magic != VAG_MAGIC: + #raise RuntimeError(f"{_file.name} is not a valid .VAG file") + if sample_rate != SAMPLE_RATE: + warn(RuntimeWarning(f"{_file.name} sample rate is not {SAMPLE_RATE} Hz")) + + for i in range(0, size, chunk_size): + chunk = _file.read(chunk_size) + + if len(chunk) % 16: + warn(RuntimeWarning(f"{_file.name} is not 16-byte aligned, trimming")) + chunk = chunk[0:len(chunk) // 16 * 16] + + chunk = set_loop_flag(chunk) + + yield chunk.ljust(chunk_size, b"\x00") + +## Main + +def get_args(): + parser = ArgumentParser( + description = "Generates interleaved stream data from one or more .VAG files." + ) + parser.add_argument( + "input_file", + nargs = "+", + type = FileType("rb"), + help = f"mono input files for each channel (must be {SAMPLE_RATE} Hz .VAG)" + ) + parser.add_argument( + "-o", "--output", + type = FileType("wb"), + default = "stream.bin", + help = "where to output converted stream data (stream.bin by default)", + metavar = "file" + ) + parser.add_argument( + "-b", "--buffer-size", + type = int, + default = BUFFER_SIZE, + help = f"size of each interleaved chunk (one per channel, default {BUFFER_SIZE})", + metavar = "bytes" + ) + parser.add_argument( + "-a", "--align", + type = int, + default = ALIGN_SIZE, + help = f"align each group of chunks to N bytes (default {ALIGN_SIZE})", + metavar = "bytes" + ) + + return parser.parse_args() + +def main(): + args = get_args() + if args.buffer_size % 16: + raise ValueError("buffer size must be a multiple of 16 bytes") + + interleave = zip_longest( + *( read_vag(_file, args.buffer_size) for _file in args.input_file ), + fillvalue = b"\x00" * args.buffer_size + ) + + with args.output as _file: + for chunks in interleave: + data = b"".join(chunks) + + _file.write(align(data, args.align)) + +if __name__ == "__main__": + main() diff --git a/examples/sound/spustream/iso.xml b/examples/sound/spustream/iso.xml new file mode 100644 index 0000000..3807046 --- /dev/null +++ b/examples/sound/spustream/iso.xml @@ -0,0 +1,29 @@ + + + + + + + + + + + + + + + + + + diff --git a/examples/sound/spustream/main.c b/examples/sound/spustream/main.c new file mode 100644 index 0000000..2032df5 --- /dev/null +++ b/examples/sound/spustream/main.c @@ -0,0 +1,456 @@ +/* + * PSn00bSDK SPU audio streaming example + * (C) 2021 spicyjpeg - MPL licensed + * + * This example demonstrates how to play a large multi-channel audio file + * "manually" by streaming it through the SPU, without having to rely on the CD + * drive's ability to play audio tracks or XA files. + * + * The way this works is by splitting the audio file into a series of ~1 second + * "chunks", each of which in turn is an array of concatenated buffers holding + * SPU ADPCM data (one for each channel, so a stereo stream would have 2 + * buffers per chunk). All buffers in a chunk are played simultaneously using + * multiple SPU channels; each buffer has the loop flag set at the end, so each + * channel will jump to its loop address (SPU_CHANNELS[n].loop_addr) once the + * chunk is played. + * + * Since the loop point doesn't necessarily have to be within the chunk itself, + * we can abuse it to "queue" another set of buffers to be played immediately + * after the currently playing chunk. This allows us to fetch a chunk from the + * CD, upload it to SPU RAM (2048 bytes at a time to avoid having to keep + * another large buffer in main RAM) and queue it for playback while a + * previously buffered chunk is playing in the background. SPU RAM always holds + * two chunks, one of which is played while the other one is buffered. This is + * the layout used in this example: + * + * /================================================\ + * | /==================\ | + * v Loop point | v Loop point | + * +-------+----------------+----------------+----------------+----------------+ + * | Dummy | Left buffer 0 | Right buffer 0 | Left buffer 1 | Right buffer 1 | + * +-------+----------------+----------------+----------------+----------------+ + * \____________Chunk 0____________/ \____________Chunk 1____________/ + * + * It's pretty much the same thing as GPU double buffering (aka page flipping), + * just with chunks instead of framebuffers. + * + * We need to know when the chunk we've buffered actually starts playing in + * order to start buffering the next one. The SPU can be configured to trigger + * an interrupt whenever a specific address in SPU RAM is read by a channel, so + * we can just point it to the beginning of the buffered chunk's first buffer. + * The interrupt callback will then kick off CD reading and adjust the loop/IRQ + * addresses to the ones of the chunk that is going to be buffered next. + * + * Chunks are read from a STREAM.BIN file which is just a series of sector + * aligned chunks, arranged as follows: + * + * +--Sector--+--Sector--+--Sector--+--Sector--+--Sector--+--Sector--+---- + * | +--------------------------+--------------------------+ | + * | | Left channel data | Right channel data | Padding | ... + * | +--------------------------+--------------------------+ | + * +----------+----------+----------+----------+----------+----------+---- + * \________________________Chunk________________________/ + * + * Such file isn't provided as PSn00bSDK doesn't yet have a tool for audio + * transcoding. A Python script is included to generate STREAM.BIN from one or + * more SPU ADPCM (.VAG) files, one for each channel (the .VAG format only + * supports mono). + * + * Of course SPU streaming isn't the only way to play music, as the CD drive + * can play CD-DA tracks and XA files natively with zero CPU overhead. However + * streaming has a number of advantages over CD audio or XA: + * + * - Any sample rate up to 44.1 kHz can be used. The sample rate can also be + * changed on-the-fly to play the stream at different speeds and pitches (as + * long as the CD drive can keep up of course), or even interpolated for + * effects like tape stops or DJ scratches. + * - Manual streaming is not limited to mono or stereo but can be expanded to + * as many channels as needed, only limited by the amount of SPU RAM required + * for chunks and CD bandwidth. Having more than 2 channels can be useful for + * e.g. crossfading between tracks (not possible with XA) or controlling + * volume and panning of each individual instrument. + * - Depending on how streaming/interleaving is implemented it is possible to + * have 500-1000ms idle periods during which the CD drive isn't buffering the + * stream, that can be used to read small amounts of other data without ever + * interrupting playback. This is different from XA-style interleaving as the + * drive is free to seek to *any* region of the disc during these periods + * (it must seek back to the stream's next chunk afterwards though). + * - Thanks to the idle periods it is possible to seek back to the beginning of + * the stream and preload the first chunk before the end is reached, allowing + * the track to be looped seamlessly without having to resort to tricks like + * filler samples. + * - Unlike XA, SPU streaming can be used on some PS1-based arcade boards such + * as the Konami System 573. These systems usually use IDE/SCSI CD drives or + * flash memory, neither of which supports XA playback. + */ + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +// To maximize STREAM.BIN packing efficiency and get rid of padding between +// chunks, buffer size should be a multiple of sector size (2048 bytes). Buffer +// size can be increased to get more idle time between CD reads, however it is +// usually best to keep it to 1-2 seconds as SPU RAM is only 512 KB. +#define SAMPLE_RATE 0x1000 // 44100 Hz +#define BUFFER_SIZE 26624 // (26624 / 16 * 28) / 44100 = 1.05 seconds + +#define NUM_CHANNELS 2 +#define CHANNEL_MASK 0x03 + +/* Register definitions */ + +// For some reason SpuVoiceRaw doesn't actually match the layout of SPU +// registers, so here we go. +typedef struct { + uint16_t vol_left; + uint16_t vol_right; + uint16_t freq; + uint16_t addr; + uint32_t adsr_param; + uint16_t _reserved; + uint16_t loop_addr; +} SPUCHANNEL; + +#define SPU_CTRL *((volatile uint16_t *) 0x1f801daa) +#define SPU_IRQ_ADDR *((volatile uint16_t *) 0x1f801da4) +#define SPU_KEY_ON *((volatile uint32_t *) 0x1f801d88) +#define SPU_KEY_OFF *((volatile uint32_t *) 0x1f801d8c) + +// SPU RAM is addressed in 8-byte units, using 16-bit pointers. +#define SPU_CHANNELS ((volatile SPUCHANNEL *) 0x1f801c00) +#define SPU_RAM_ADDR(x) ((uint16_t) (((uint32_t) (x)) >> 3)) + +/* Display/GPU context utilities */ + +#define SCREEN_XRES 320 +#define SCREEN_YRES 240 + +#define BGCOLOR_R 48 +#define BGCOLOR_G 24 +#define BGCOLOR_B 0 + +typedef struct { + DISPENV disp; + DRAWENV draw; +} DB; + +typedef struct { + DB db[2]; + uint32_t db_active; +} CONTEXT; + +void init_context(CONTEXT *ctx) { + DB *db; + + ResetGraph(0); + ctx->db_active = 0; + + db = &(ctx->db[0]); + SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); + setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); + db->draw.isbg = 1; + db->draw.dtd = 1; + + db = &(ctx->db[1]); + SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES); + setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); + db->draw.isbg = 1; + db->draw.dtd = 1; + + PutDrawEnv(&(db->draw)); + //PutDispEnv(&(db->disp)); + + // Create a text stream at the top of the screen. + FntLoad(960, 0); + FntOpen(8, 16, 304, 208, 2, 512); +} + +void display(CONTEXT *ctx) { + DB *db; + + DrawSync(0); + VSync(0); + ctx->db_active ^= 1; + + db = &(ctx->db[ctx->db_active]); + PutDrawEnv(&(db->draw)); + PutDispEnv(&(db->disp)); + SetDispMask(1); +} + +/* Stream interrupt handlers */ + +// This is a silent looping sample used to keep unused SPU channels busy, +// preventing them from accidentally triggering the SPU RAM interrupt and +// throwing off the timing (all channels are always reading sample data, even +// when "stopped"). It is 64 bytes as that is the minimum size for SPU DMA +// transfers, however only the first 16 bytes are kept. The rest is going to be +// overwritten by chunks. +// https://problemkaputt.de/psx-spx.htm#spuinterrupt +const uint8_t SPU_DUMMY_BLOCK[] = { + 0, 5, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, + 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 +}; + +// The first 4 KB of SPU RAM are reserved for capture buffers, so we have to +// place stream buffers after those. Sony's SPU library additionally places a +// dummy sample at 0x1000; we are going to do the same with the block above. +#define DUMMY_BLOCK_ADDR 0x1000 +#define BUFFER_START_ADDR 0x1010 +#define CHUNK_SIZE (BUFFER_SIZE * NUM_CHANNELS) + +typedef struct { + uint32_t lba; + uint32_t length; + uint32_t pos; + + uint32_t spu_addr; + uint32_t spu_pos; + uint32_t db_active; +} STREAMCONTEXT; + +static volatile 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 +// allocated on the stack (especially not in the interrupt callbacks' stack, +// whose size is very limited). +static uint8_t sector_buffer[2048]; + +void spu_irq_handler(void) { + // Acknowledge the interrupt to ensure it can be triggered again. The only + // way to do this is actually to disable the interrupt entirely; we'll + // enable it again once the buffer is ready. + SPU_CTRL &= 0xffbf; + + str_ctx.db_active ^= 1; + str_ctx.spu_pos = 0; + + // Align the sector counter to the size of a chunk (to prevent glitches + // after seeking) and reset it if it exceeds the stream's length. + str_ctx.pos %= str_ctx.length; + str_ctx.pos -= str_ctx.pos % ((CHUNK_SIZE + 2047) / 2048); + + // Configure to SPU to trigger an IRQ once the buffer that is going to be + // filled now starts playing (so the next buffer can be loaded) and + // override both channels' loop addresses to make them "jump" to the new + // buffer rather than actually looping when they encounter the loop flag at + // the end of the currently playing buffer. + str_ctx.spu_addr = BUFFER_START_ADDR + CHUNK_SIZE * str_ctx.db_active; + SPU_IRQ_ADDR = SPU_RAM_ADDR(str_ctx.spu_addr); + + for (uint32_t i = 0; i < NUM_CHANNELS; i++) + SPU_CHANNELS[i].loop_addr = SPU_RAM_ADDR(str_ctx.spu_addr + BUFFER_SIZE * i); + + // Start loading the next chunk. cd_event_handler() will be called + // repeatedly for each sector until the entire chunk is read. + CdlLOC pos; + CdIntToPos(str_ctx.lba + str_ctx.pos, &pos); + CdControlF(CdlReadN, &pos); +} + +void cd_event_handler(int32_t event, uint8_t *payload) { + // Ignore all events other than a sector being ready. + // TODO: read errors should be handled properly + if (event != CdlDataReady) + return; + + // Fetch the sector that has been read from the drive. + CdGetSector(sector_buffer, 512); + str_ctx.pos++; + + // Set loop flags to make sure the buffer will loop (actually jump to the + // other buffer, as we're overriding loop addresses) at the end. + // NOTE: this isn't actually necessary here as the stream converter script + // already sets these flags in the file. + /*for (uint32_t i = 0; i < NUM_CHANNELS; i++) { + if ( + str_ctx.spu_pos >= (BUFFER_SIZE * i - 2048) && + str_ctx.spu_pos < (BUFFER_SIZE * i) + ) + sector[(BUFFER_SIZE * i - str_ctx.spu_pos) - 15] = 0x03; + }*/ + + // Copy the sector to SPU RAM, appending it to the buffer that is not + // playing currently. As the left and right buffers are adjacent, we can + // just treat the chunk as a single blob of data and copy it as-is; we only + // have to trim the padding at the end (if any) to avoid overwriting other + // data in SPU RAM. + uint32_t length = CHUNK_SIZE - str_ctx.spu_pos; + if (length > 2048) + length = 2048; + + SpuSetTransferStartAddr(str_ctx.spu_addr + str_ctx.spu_pos); + SpuWrite(sector_buffer, length); + str_ctx.spu_pos += length; + + // If the buffer has been filled completely, stop reading and re-enable the + // SPU IRQ. + // TODO TODO: preload first sector + if (str_ctx.spu_pos >= CHUNK_SIZE) { + CdControlF(CdlPause, 0); + SPU_CTRL |= 0x0040; + } +} + +/* Stream helpers */ + +void init_spu_channels(void) { + // Upload the dummy block to the SPU and play it on all channels, locking + // them up and stopping them from messing with the SPU interrupt. + // TODO: is this really necessary? (needs testing on real hardware) + SpuSetTransferStartAddr(DUMMY_BLOCK_ADDR); + SpuWrite(SPU_DUMMY_BLOCK, 64); + + SPU_KEY_OFF = 0x00ffffff; + + for (uint32_t i = 0; i < 24; i++) + SPU_CHANNELS[i].addr = SPU_RAM_ADDR(DUMMY_BLOCK_ADDR); + + SPU_KEY_ON = 0x00ffffff; +} + +void init_stream(CdlFILE *file) { + EnterCriticalSection(); + InterruptCallback(9, &spu_irq_handler); + CdReadyCallback(&cd_event_handler); + ExitCriticalSection(); + + // Set the initial LBA of the stream file, which is going to be incremented + // as the stream is played. + str_ctx.lba = CdPosToInt(&(file->pos)); + str_ctx.length = file->size / 2048; + str_ctx.pos = 0; + + // Ensure at least one chunk is in SPU RAM by invoking the SPU IRQ handler + // manually and blocking until the chunk has loaded. + str_ctx.db_active = 1; + spu_irq_handler(); + + while (str_ctx.spu_pos < CHUNK_SIZE) + __asm__("nop"); +} + +void start_stream(void) { + SPU_KEY_OFF = CHANNEL_MASK; + + for (uint32_t i = 0; i < NUM_CHANNELS; i++) { + SPU_CHANNELS[i].addr = SPU_RAM_ADDR(BUFFER_START_ADDR + BUFFER_SIZE * i); + SPU_CHANNELS[i].freq = SAMPLE_RATE; + SPU_CHANNELS[i].adsr_param = 0xdfee80ff; // or 0x9fc080ff, 0xdff18087 + } + + // Unmute the channels and route them for stereo output. You'll want to + // edit this if you are using more than 2 channels, and/or if you want to + // provide an option to output mono audio instead of stereo. + SPU_CHANNELS[0].vol_left = 0x3fff; + SPU_CHANNELS[0].vol_right = 0x0000; + SPU_CHANNELS[1].vol_left = 0x0000; + SPU_CHANNELS[1].vol_right = 0x3fff; + + SPU_KEY_ON = CHANNEL_MASK; + spu_irq_handler(); +} + +/* Main */ + +static CONTEXT ctx; + +#define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx); } +#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(); + init_spu_channels(); + + SHOW_STATUS("LOCATING STREAM FILE\n"); + + CdlFILE file; + if (!CdSearchFile(&file, "\\STREAM.BIN")) + SHOW_ERROR("FAILED TO FIND STREAM.BIN\n"); + + SHOW_STATUS("BUFFERING STREAM\n"); + 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); + + uint16_t sample_rate = SAMPLE_RATE; + uint16_t last_buttons = 0xffff; + + while (1) { + FntPrint(-1, "PLAYING SPU STREAM\n"); + 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"); + else + FntPrint(-1, "STATUS: BUFFERING\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, "[LEFT/RIGHT] SEEK\n"); + FntPrint(-1, "[O] RESET POSITION\n"); + FntPrint(-1, "[UP/DOWN] CHANGE SAMPLE RATE\n"); + FntPrint(-1, "[X] RESET SAMPLE RATE\n"); + + FntFlush(-1); + display(&ctx); + + // Check if a compatible controller is connected and handle button + // presses. + PADTYPE *pad = (PADTYPE *) pad_buff[0]; + if (pad->stat) + continue; + if ((pad->type != 4) && (pad->type != 5) && (pad->type != 7)) + continue; + + // Seeking by an arbitrary number of sectors isn't a problem as + // spu_irq_handler() always realigns the counter. + if (!(pad->btn & PAD_LEFT)) + str_ctx.pos -= 16; + if (!(pad->btn & PAD_RIGHT)) + str_ctx.pos += 16; + if ((last_buttons & PAD_CIRCLE) && !(pad->btn & PAD_CIRCLE)) + str_ctx.pos = 0; + + if (!(pad->btn & PAD_DOWN) && (sample_rate > 0x400)) + sample_rate -= 0x40; + if (!(pad->btn & PAD_UP) && (sample_rate < 0x2000)) + sample_rate += 0x40; + if ((last_buttons & PAD_CROSS) && !(pad->btn & PAD_CROSS)) + sample_rate = SAMPLE_RATE; + + // Only set the sample rate registers if necessary. + if (pad->btn != 0xffff) { + for (uint32_t i = 0; i < NUM_CHANNELS; i++) + SPU_CHANNELS[i].freq = sample_rate; + } + + last_buttons = pad->btn; + } + + return 0; +} diff --git a/examples/sound/spustream/system.cnf b/examples/sound/spustream/system.cnf new file mode 100644 index 0000000..0c4561a --- /dev/null +++ b/examples/sound/spustream/system.cnf @@ -0,0 +1,4 @@ +BOOT=cdrom:\spustrm.exe;1 +TCB=4 +EVENT=10 +STACK=801FFFF0 -- cgit v1.2.3 From 2e6625481cd006d0a9d68285ce557f195030718e Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Thu, 23 Dec 2021 22:09:06 +0100 Subject: Fix CdGetSector size inconsistency, update changelog --- .github/scripts/generate_release_notes.py | 46 ++++++++++--------------------- CHANGELOG.md | 29 +++++++++++++------ examples/cdrom/cdxa/main.c | 2 +- examples/io/system573/main.c | 2 +- libpsn00b/psxcd/cdgetsector.s | 3 +- libpsn00b/psxcd/isofs.c | 2 +- libpsn00b/psxcd/psxcd.c | 8 +++--- 7 files changed, 44 insertions(+), 48 deletions(-) diff --git a/.github/scripts/generate_release_notes.py b/.github/scripts/generate_release_notes.py index f3e4870..e3fbc7f 100644 --- a/.github/scripts/generate_release_notes.py +++ b/.github/scripts/generate_release_notes.py @@ -3,7 +3,7 @@ # (C) 2021 spicyjpeg - MPL licensed import sys, re -from time import gmtime, strptime +from time import gmtime, strptime, struct_time from argparse import ArgumentParser, FileType ## Helpers @@ -11,14 +11,13 @@ from argparse import ArgumentParser, FileType VERSION_REGEX = re.compile(r"^(?:refs\/tags\/)?(?:v|ver|version|release)? *(.*)") def parse_date(date): + if isinstance(date, struct_time): + return date + return strptime(date.strip(), "%Y-%m-%d") def normalize_version(version): - match = VERSION_REGEX.match(version.lower()) - if not match: - raise ValueError(f"invalid version string: {version}") - - return match.group(1) + return VERSION_REGEX.match(version.lower()).group(1) ## Changelog parser @@ -50,9 +49,6 @@ def parse_blocks(changelog): # [ _crap, date, version, body, date, version, body, ... ] items = BLOCK_REGEX.split(changelog.strip()) - #if items[0].strip(): - #raise RuntimeError("the changelog doesn't start with a valid block") - # Iterate over all blocks from bottom to top (i.e. oldest first). last_version = FIRST_VERSION @@ -110,51 +106,39 @@ def generate_notes(versions): def get_args(): parser = ArgumentParser( - description = "Generates and outputs release notes from a Markdown changelog file.", - add_help = False + description = "Generates and outputs release notes from a Markdown changelog file." ) - - tools_group = parser.add_argument_group("Tools") - tools_group.add_argument( - "-h", "--help", - action = "help", - help = "Show this help message and exits" - ) - - files_group = parser.add_argument_group("Files") - files_group.add_argument( + parser.add_argument( "changelog", type = FileType("rt"), help = "Markdown changelog file to parse" ) - files_group.add_argument( + parser.add_argument( "-o", "--output", type = FileType("wt"), default = sys.stdout, - help = "Where to output release notes (stdout by default)", + help = "where to output release notes (stdout by default)", metavar = "file" ) - - filter_group = parser.add_argument_group("Filters") - filter_group.add_argument( + parser.add_argument( "-v", "--version", action = "append", type = str, - help = "Ignore all changes not belonging to a version (can be specified multiple times)", + help = "ignore all changes not belonging to a version (can be specified multiple times)", metavar = "name" ) - filter_group.add_argument( + parser.add_argument( "-f", "--from-date", type = parse_date, default = parse_date("2000-01-01"), - help = "Ignore all changes before date", + help = "ignore all changes before date", metavar = "yyyy-mm-dd" ) - filter_group.add_argument( + parser.add_argument( "-t", "--to-date", type = parse_date, default = gmtime(), - help = "Ignore all changes after date", + help = "ignore all changes after date", metavar = "yyyy-mm-dd" ) diff --git a/CHANGELOG.md b/CHANGELOG.md index 9463b83..ea4f4d2 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -5,7 +5,7 @@ contributing to PSn00bSDK, add a new block at the top following this template: ``` -## --: +## --: [optional new version] : @@ -19,6 +19,17 @@ to ensure the changelog can be parsed correctly. ------------------------------------------------------------------------------- +## 2021-12-23 + +spicyjpeg: + +- psxcd: `CdGetSector()` now expects the sector size to be in 32-bit word units + (rather than bytes) for consistency with the official CD-ROM library. The + library's ISO9660 parser and helper functions have been updated accordingly. + **This is a breaking change**. + +- examples: Added `spustream` audio streaming example. + ## 2021-11-28: 0.18 spicyjpeg: @@ -453,7 +464,7 @@ Lameguy64: - psxgpu: Fixed typos in `setUVWH()` macro. -- Added `_boot()` BIOS function (A(A0h) aka Warmboot, useful for CD based +- Added `_boot()` BIOS function (`A(A0h)` aka Warmboot, useful for CD based serial loaders). ## 2019-08-17: 0.13b @@ -505,9 +516,9 @@ Lameguy64: executable to return to a parent executable, return logic automatically calls `EnterCriticalSection()`. -- libc: Updated build method which takes libgcc from the compiler and adds +- libc: Updated build method which takes `libgcc` from the compiler and adds its own object files into it, eliminating linker problems caused by having - to order libc and libgcc libraries in a specific manner. + to order `libc` and `libgcc` libraries in a specific manner. - psxgpu: Added `RestartCallback()`. @@ -526,7 +537,7 @@ Lameguy64: performance, as the R3000 does not support 64-bit arithmetic natively so its emulated like floats. `int64` still used for processing floats and doubles and old `vsprintf.c` file is still included for those who really - want int64 support for whatever reason. + want `int64` support for whatever reason. - libc: Removed `stdarg.h` which is part of GCC and not license compatible with MPL. The toolchain compiled with libgcc provides `stdarg.h` and other @@ -540,7 +551,7 @@ Lameguy64: - psxgpu: Fixed bug in DMACallback where the internal DMA handler would fail to install due to `GetInterruptCallback()` retrieving the callback value - immediately in the branch delay slot of a jr instruction, which resuls to + immediately in the branch delay slot of a `jr` instruction, which resuls to an inconsistent return value. This also broke `DrawSyncCallback()`. - psxsio: Done fixes on `_sio_control()` from the aformentioned issues with @@ -619,7 +630,7 @@ Lameguy64: ready instead of simply waiting for GPU transfer ready which is the likely cause of subtle GPU related timing issues, it also sets GPU DMA transfer mode to off afterwards. It can also read number of words remaining in DMA - transfer if a0 is non-zero but it likely only returns the correct value on + transfer if `a0` is non-zero but it likely only returns the correct value on VRAM transfers. Exact way how `DrawSync()` returns the count in the official SDK is currently unknown. @@ -645,10 +656,10 @@ Lameguy64: - Added `rgb24` example. - Got custom exit handler set using `SetCustomExitFromException()` (BIOS - function B(19h)) working. Currently used to acknowledge VSync IRQ but + function `B(19h)`) working. Currently used to acknowledge VSync IRQ but actual VSync handling is still done with events and needs to be transferred to the custom exit handler. At least it lets BIOS - controller functions to work now. See doc/dev `notes.txt` for details + controller functions to work now. See `doc/dev notes.txt` for details on how this handler behaves. - Made stack usage in `ResetGraph()` less wasteful. You only need to diff --git a/examples/cdrom/cdxa/main.c b/examples/cdrom/cdxa/main.c index 5f11d8d..284b92f 100644 --- a/examples/cdrom/cdxa/main.c +++ b/examples/cdrom/cdxa/main.c @@ -199,7 +199,7 @@ void xa_callback(int intr, unsigned char *result) if (intr == CdlDataReady) { /* Fetch data sector */ - CdGetSector((u_long*)&xa_sector_buff, 2048); + CdGetSector((u_long*)&xa_sector_buff, 512); /* Quirk: This CdGetSector() implementation must fetch 2048 bytes */ /* or more otherwise the following sectors will be read in an */ diff --git a/examples/io/system573/main.c b/examples/io/system573/main.c index 67a98da..95c3155 100644 --- a/examples/io/system573/main.c +++ b/examples/io/system573/main.c @@ -306,7 +306,7 @@ int main(int argc, const char* argv[]) { FntPrint(-1, " P1 BUTTONS =%07@\n", inputs.p1_btn); FntPrint(-1, " P2 JOYSTICK =%04@\n", inputs.p2_joy); FntPrint(-1, " P2 BUTTONS =%07@\n", inputs.p2_btn); - FntPrint(-1, " COIN/SERVICE=%04@\n", inputs.coin); + FntPrint(-1, " COIN/SERVICE=%04@\n", inputs.coin & 0xf); FntPrint(-1, " DIP SWITCHES=%04@\n", inputs.dip_sw); FntPrint(-1, "\nCABINET LIGHTS:\n"); diff --git a/libpsn00b/psxcd/cdgetsector.s b/libpsn00b/psxcd/cdgetsector.s index 9af3543..dbe95cb 100644 --- a/libpsn00b/psxcd/cdgetsector.s +++ b/libpsn00b/psxcd/cdgetsector.s @@ -18,7 +18,8 @@ CdGetSector: # nop lui $v0, 0x1 - srl $a1, 2 +# srl $a1, 2 # (the official implementation expects $a1/size + # to be in 32-bit words rather than bytes) or $v0, $a1 sw $a0, D3_MADR($a2) # Set DMA base address and transfer length sw $v0, D3_BCR($a2) diff --git a/libpsn00b/psxcd/isofs.c b/libpsn00b/psxcd/isofs.c index 29bbb76..d1c1b18 100644 --- a/libpsn00b/psxcd/isofs.c +++ b/libpsn00b/psxcd/isofs.c @@ -795,7 +795,7 @@ static void _scan_callback(int status, unsigned char *result) { if( status == CdlDataReady ) { - CdGetSector((void*)_ses_scanbuff, 2048); + CdGetSector((void*)_ses_scanbuff, 512); if( _ses_scanbuff[0] == 0x1 ) { diff --git a/libpsn00b/psxcd/psxcd.c b/libpsn00b/psxcd/psxcd.c index 74c6c1c..76415f9 100644 --- a/libpsn00b/psxcd/psxcd.c +++ b/libpsn00b/psxcd/psxcd.c @@ -255,7 +255,7 @@ static void _CdReadReadyCallback(int status, unsigned char *result) CdGetSector((void*)_cd_read_addr, _cd_read_sector_sz); // Increment destination address - _cd_read_addr += _cd_read_sector_sz>>2; + _cd_read_addr += _cd_read_sector_sz; // Subtract sector count _cd_sector_count--; @@ -290,15 +290,15 @@ int CdRead(int sectors, u_long *buf, int mode) // Determine sector based on mode flags if( mode & CdlModeSize0 ) { - _cd_read_sector_sz = 2328; + _cd_read_sector_sz = 2328 / 4; } else if( mode & CdlModeSize1 ) { - _cd_read_sector_sz = 2340; + _cd_read_sector_sz = 2340 / 4; } else { - _cd_read_sector_sz = 2048; + _cd_read_sector_sz = 2048 / 4; } _cd_read_counter = VSync(-1); -- cgit v1.2.3 From d60b046bf362fcc9332f463823e8d02147d516de Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Thu, 23 Dec 2021 23:20:12 +0100 Subject: Remove PSN00BSDK_LIBS leftovers, improve CI artifact upload --- .github/workflows/build.yml | 32 +++++++++++++++++++++++------ doc/installation.md | 25 +++++++++++----------- examples/beginner/cppdemo/CMakeLists.txt | 4 ---- examples/beginner/hello/CMakeLists.txt | 4 ---- examples/cdrom/cdbrowse/CMakeLists.txt | 4 ---- examples/cdrom/cdxa/CMakeLists.txt | 4 ---- examples/demos/n00bdemo/CMakeLists.txt | 4 ---- examples/graphics/balls/CMakeLists.txt | 4 ---- examples/graphics/billboard/CMakeLists.txt | 4 ---- examples/graphics/fpscam/CMakeLists.txt | 4 ---- examples/graphics/gte/CMakeLists.txt | 4 ---- examples/graphics/hdtv/CMakeLists.txt | 4 ---- examples/graphics/render2tex/CMakeLists.txt | 4 ---- examples/graphics/rgb24/CMakeLists.txt | 4 ---- examples/io/pads/CMakeLists.txt | 4 ---- examples/io/system573/CMakeLists.txt | 4 ---- examples/lowlevel/cartrom/CMakeLists.txt | 4 ---- examples/sound/spustream/CMakeLists.txt | 4 ---- examples/sound/vagsample/CMakeLists.txt | 4 ---- examples/system/childexec/CMakeLists.txt | 4 ---- examples/system/console/CMakeLists.txt | 4 ---- examples/system/dynlink/CMakeLists.txt | 4 ---- examples/system/timer/CMakeLists.txt | 4 ---- examples/system/tty/CMakeLists.txt | 4 ---- 24 files changed, 38 insertions(+), 107 deletions(-) diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 8c16ac3..3cbefb1 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -124,13 +124,20 @@ jobs: cmake --build build cmake --build build -t package + # The GitHub Actions UI doesn't allow downloading individual files from + # an artifact, so it's best to upload each package type as a separate + # artifact. - name: Upload build artifacts uses: actions/upload-artifact@v2 with: name: psn00bsdk-windows - path: | - build/packages/* - !build/packages/_CPack_Packages + path: build/packages/*.zip + + - name: Upload build artifacts (NSIS) + uses: actions/upload-artifact@v2 + with: + name: psn00bsdk-windows-nsis + path: build/packages/*.exe build-sdk-linux: name: Build PSn00bSDK on Linux @@ -169,9 +176,19 @@ jobs: uses: actions/upload-artifact@v2 with: name: psn00bsdk-linux - path: | - build/packages/* - !build/packages/_CPack_Packages + path: build/packages/*.zip + + - name: Upload build artifacts (DEB) + uses: actions/upload-artifact@v2 + with: + name: psn00bsdk-linux-deb + path: build/packages/*.deb + + - name: Upload build artifacts (RPM) + uses: actions/upload-artifact@v2 + with: + name: psn00bsdk-linux-rpm + path: build/packages/*.rpm # This job takes care of creating a new release and upload the build # artifacts if the last commit is associated to a tag. @@ -222,4 +239,7 @@ jobs: files: | *.zip psn00bsdk-windows/* + psn00bsdk-windows-nsis/* psn00bsdk-linux/* + psn00bsdk-linux-deb/* + psn00bsdk-linux-rpm/* diff --git a/doc/installation.md b/doc/installation.md index 576eb7a..ca3aab4 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -96,9 +96,9 @@ and installed properly. - `/lib/libpsn00b` - `/share/psn00bsdk` -7. Set the `PSN00BSDK_LIBS` environment variable to point to the `lib/libpsn00b` - subfolder inside the install directory. You might also want to add the `bin` - folder to `PATH` if it's not listed already. +7. You may optionally set the `PSN00BSDK_LIBS` environment variable to point to + the `lib/libpsn00b` subfolder inside the install directory. You might also + want to add the `bin` folder to `PATH` if it's not listed already. Although not strictly required, you'll probably want to install a PS1 emulator with debugging capabilities such as [no$psx](https://problemkaputt.de/psx.htm) @@ -132,24 +132,23 @@ far from being feature-complete. 1. Copy the contents of `/share/psn00bsdk/template` (or the `template` folder within the repo) to your new project's root directory. -2. Configure and build the template by running: +2. If you haven't set the `PSN00BSDK_LIBS` environment variable previously or + if you want to use a different PSn00bSDK installation for the project, edit + `CMakePresets.json` to set the path you installed the SDK to. See the + [setup guide](cmake_reference.md#setup) for details. + +3. Configure and build the template by running: ```bash - cmake -S . -B ./build + cmake --preset default . cmake --build ./build ``` If you did everything correctly there should be a `template.bin` CD image in the `build` folder. Test it in an emulator to ensure it works. -Note that, even though the template relies on the `PSN00BSDK_LIBS` environment -variable to locate the SDK by default, you can also specify the path directly -on the CMake command line by adding -`-DCMAKE_TOOLCHAIN_FILE=/lib/libpsn00b/cmake/sdk.cmake` to the -CMake command line. - The toolchain script defines a few CMake macros to create PS1 executables, DLLs -and CD images. See the [reference](doc/cmake_reference.md) for details. +and CD images. See the [reference](cmake_reference.md) for details. ----------------------------------------- -_Last updated on 2021-11-19 by spicyjpeg_ +_Last updated on 2021-12-23 by spicyjpeg_ diff --git a/examples/beginner/cppdemo/CMakeLists.txt b/examples/beginner/cppdemo/CMakeLists.txt index becf464..c43d4a1 100644 --- a/examples/beginner/cppdemo/CMakeLists.txt +++ b/examples/beginner/cppdemo/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( cppdemo LANGUAGES CXX diff --git a/examples/beginner/hello/CMakeLists.txt b/examples/beginner/hello/CMakeLists.txt index 7fb7c22..d8297c5 100644 --- a/examples/beginner/hello/CMakeLists.txt +++ b/examples/beginner/hello/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( hello LANGUAGES C diff --git a/examples/cdrom/cdbrowse/CMakeLists.txt b/examples/cdrom/cdbrowse/CMakeLists.txt index e5ec759..e36407d 100644 --- a/examples/cdrom/cdbrowse/CMakeLists.txt +++ b/examples/cdrom/cdbrowse/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( cdbrowse LANGUAGES C diff --git a/examples/cdrom/cdxa/CMakeLists.txt b/examples/cdrom/cdxa/CMakeLists.txt index 18dcc69..7b90f59 100644 --- a/examples/cdrom/cdxa/CMakeLists.txt +++ b/examples/cdrom/cdxa/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( cdxa LANGUAGES C diff --git a/examples/demos/n00bdemo/CMakeLists.txt b/examples/demos/n00bdemo/CMakeLists.txt index c62c4ef..1c211b3 100644 --- a/examples/demos/n00bdemo/CMakeLists.txt +++ b/examples/demos/n00bdemo/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( n00bdemo LANGUAGES C ASM diff --git a/examples/graphics/balls/CMakeLists.txt b/examples/graphics/balls/CMakeLists.txt index 5886484..f5297c3 100644 --- a/examples/graphics/balls/CMakeLists.txt +++ b/examples/graphics/balls/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( balls LANGUAGES C diff --git a/examples/graphics/billboard/CMakeLists.txt b/examples/graphics/billboard/CMakeLists.txt index 8cd31a9..1b417d2 100644 --- a/examples/graphics/billboard/CMakeLists.txt +++ b/examples/graphics/billboard/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( billboard LANGUAGES C ASM diff --git a/examples/graphics/fpscam/CMakeLists.txt b/examples/graphics/fpscam/CMakeLists.txt index 791f6c2..cb0c086 100644 --- a/examples/graphics/fpscam/CMakeLists.txt +++ b/examples/graphics/fpscam/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( fpscam LANGUAGES C diff --git a/examples/graphics/gte/CMakeLists.txt b/examples/graphics/gte/CMakeLists.txt index 85b2942..f95c5ff 100644 --- a/examples/graphics/gte/CMakeLists.txt +++ b/examples/graphics/gte/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( gte LANGUAGES C diff --git a/examples/graphics/hdtv/CMakeLists.txt b/examples/graphics/hdtv/CMakeLists.txt index f92faeb..804b096 100644 --- a/examples/graphics/hdtv/CMakeLists.txt +++ b/examples/graphics/hdtv/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( hdtv LANGUAGES C diff --git a/examples/graphics/render2tex/CMakeLists.txt b/examples/graphics/render2tex/CMakeLists.txt index 360840d..70e489e 100644 --- a/examples/graphics/render2tex/CMakeLists.txt +++ b/examples/graphics/render2tex/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( render2tex LANGUAGES C ASM diff --git a/examples/graphics/rgb24/CMakeLists.txt b/examples/graphics/rgb24/CMakeLists.txt index bf8a8fa..449981a 100644 --- a/examples/graphics/rgb24/CMakeLists.txt +++ b/examples/graphics/rgb24/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( rgb24 LANGUAGES C ASM diff --git a/examples/io/pads/CMakeLists.txt b/examples/io/pads/CMakeLists.txt index 5bd7f5d..cf5f817 100644 --- a/examples/io/pads/CMakeLists.txt +++ b/examples/io/pads/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( pads LANGUAGES C ASM diff --git a/examples/io/system573/CMakeLists.txt b/examples/io/system573/CMakeLists.txt index 34c9153..1c74347 100644 --- a/examples/io/system573/CMakeLists.txt +++ b/examples/io/system573/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.21) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( system573 LANGUAGES C ASM diff --git a/examples/lowlevel/cartrom/CMakeLists.txt b/examples/lowlevel/cartrom/CMakeLists.txt index 3e807a3..107cc3d 100644 --- a/examples/lowlevel/cartrom/CMakeLists.txt +++ b/examples/lowlevel/cartrom/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.21) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( cartrom LANGUAGES C ASM diff --git a/examples/sound/spustream/CMakeLists.txt b/examples/sound/spustream/CMakeLists.txt index 91243cf..9e84fa3 100644 --- a/examples/sound/spustream/CMakeLists.txt +++ b/examples/sound/spustream/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( spustream LANGUAGES C diff --git a/examples/sound/vagsample/CMakeLists.txt b/examples/sound/vagsample/CMakeLists.txt index 1a15d9c..f37be97 100644 --- a/examples/sound/vagsample/CMakeLists.txt +++ b/examples/sound/vagsample/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( vagsample LANGUAGES C diff --git a/examples/system/childexec/CMakeLists.txt b/examples/system/childexec/CMakeLists.txt index 88168e0..ca0c110 100644 --- a/examples/system/childexec/CMakeLists.txt +++ b/examples/system/childexec/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( childexec LANGUAGES C ASM diff --git a/examples/system/console/CMakeLists.txt b/examples/system/console/CMakeLists.txt index 6dc6154..d58f212 100644 --- a/examples/system/console/CMakeLists.txt +++ b/examples/system/console/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( console LANGUAGES C diff --git a/examples/system/dynlink/CMakeLists.txt b/examples/system/dynlink/CMakeLists.txt index 5834647..aae3bb3 100644 --- a/examples/system/dynlink/CMakeLists.txt +++ b/examples/system/dynlink/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( dynlink LANGUAGES C diff --git a/examples/system/timer/CMakeLists.txt b/examples/system/timer/CMakeLists.txt index 58daf9b..328e07e 100644 --- a/examples/system/timer/CMakeLists.txt +++ b/examples/system/timer/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( timer LANGUAGES C diff --git a/examples/system/tty/CMakeLists.txt b/examples/system/tty/CMakeLists.txt index 4e0ca36..0664502 100644 --- a/examples/system/tty/CMakeLists.txt +++ b/examples/system/tty/CMakeLists.txt @@ -3,10 +3,6 @@ cmake_minimum_required(VERSION 3.20) -if(NOT DEFINED CMAKE_TOOLCHAIN_FILE AND DEFINED ENV{PSN00BSDK_LIBS}) - set(CMAKE_TOOLCHAIN_FILE $ENV{PSN00BSDK_LIBS}/cmake/sdk.cmake) -endif() - project( tty LANGUAGES C -- cgit v1.2.3 From fe42ce7f1c98947baa49427835deb5ce70470afb Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Wed, 29 Dec 2021 17:30:56 +0100 Subject: Fix CMake and CI bugs, set version number, update docs --- .github/workflows/build.yml | 4 ++-- CMakeLists.txt | 33 ++++++++++++++++++++++----------- CMakePresets.json | 9 +++++---- cpack/setup.cmake | 3 +-- cpack/welcome.txt | 3 --- doc/cmake_reference.md | 10 +++++++--- doc/installation.md | 9 ++++----- examples/sound/spustream/main.c | 2 +- libpsn00b/CMakeLists.txt | 1 - libpsn00b/build.json.template | 9 ++++++--- libpsn00b/cmake/internal_setup.cmake | 5 +++-- 11 files changed, 51 insertions(+), 37 deletions(-) delete mode 100644 cpack/welcome.txt diff --git a/.github/workflows/build.yml b/.github/workflows/build.yml index 3cbefb1..a72f5d3 100644 --- a/.github/workflows/build.yml +++ b/.github/workflows/build.yml @@ -116,7 +116,7 @@ jobs: - name: Update repo submodules run: | cd sdk - git submodule update --init --recursive --remote + git submodule update --init --recursive - name: Build and package PSn00bSDK run: | @@ -164,7 +164,7 @@ jobs: - name: Update repo submodules run: | cd sdk - git submodule update --init --recursive --remote + git submodule update --init --recursive - name: Build and package PSn00bSDK run: | diff --git a/CMakeLists.txt b/CMakeLists.txt index 720ca8c..6a20a0e 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -12,7 +12,7 @@ include(ExternalProject) project( PSn00bSDK LANGUAGES NONE - VERSION 0.1.0 + VERSION 0.18 DESCRIPTION "Open source PlayStation 1 SDK" HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk" ) @@ -39,9 +39,15 @@ set( CACHE BOOL "Skip building SDK examples (not required for installation)" ) set( - BUILD_INFO "" - CACHE STRING "Information about this build to be included in build.json" + PSN00BSDK_GIT_TAG "" + CACHE STRING "Git tag or branch name (used by CI)" ) +set( + PSN00BSDK_GIT_COMMIT "" + CACHE STRING "Git commit hash (used by CI)" +) + +string(TIMESTAMP PSN00BSDK_BUILD_DATE UTC) # Forward some important variables to mkpsxiso and to the subprojects (they are # not inherited automatically as they are not environment variables). This also @@ -50,20 +56,24 @@ set( # invoked (ExternalProject_Add() runs the subprojects' install step at build # time). set( - COMMON_ARGS + _common_args -DPSN00BSDK_TC:PATH=${PSN00BSDK_TC} -DPSN00BSDK_TARGET:STRING=${PSN00BSDK_TARGET} -DPSN00BSDK_VERSION:STRING=${PROJECT_VERSION} - -DPSN00BSDK_BUILD_INFO:STRING=${BUILD_INFO} + -DPSN00BSDK_BUILD_DATE:STRING=${PSN00BSDK_BUILD_DATE} + -DPSN00BSDK_GIT_TAG:STRING=${PSN00BSDK_GIT_TAG} + -DPSN00BSDK_GIT_COMMIT:STRING=${PSN00BSDK_GIT_COMMIT} -DCMAKE_BUILD_TYPE:STRING=${CMAKE_BUILD_TYPE} ) set( - SUBPROJECT_ARGS + _sdk_args + ${_common_args} -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${CMAKE_TOOLCHAIN_FILE} -DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/install_tree ) set( - EXAMPLES_ARGS + _examples_args + ${_common_args} -DCMAKE_TOOLCHAIN_FILE:FILEPATH=${PROJECT_BINARY_DIR}/install_tree/${CMAKE_INSTALL_LIBDIR}/libpsn00b/cmake/sdk.cmake -DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/examples ) @@ -77,26 +87,26 @@ endif() ExternalProject_Add( tools SOURCE_DIR ${PROJECT_SOURCE_DIR}/tools - CMAKE_CACHE_ARGS ${COMMON_ARGS} ${SUBPROJECT_ARGS} + CMAKE_CACHE_ARGS ${_sdk_args} INSTALL_DIR install_tree ) ExternalProject_Add( mkpsxiso SOURCE_DIR ${PROJECT_SOURCE_DIR}/tools/mkpsxiso - CMAKE_CACHE_ARGS ${COMMON_ARGS} ${SUBPROJECT_ARGS} + CMAKE_CACHE_ARGS ${_sdk_args} INSTALL_DIR install_tree ) ExternalProject_Add( libpsn00b SOURCE_DIR ${PROJECT_SOURCE_DIR}/libpsn00b - CMAKE_CACHE_ARGS ${COMMON_ARGS} ${SUBPROJECT_ARGS} + CMAKE_CACHE_ARGS ${_sdk_args} INSTALL_DIR install_tree #DEPENDS tools ) ExternalProject_Add( examples SOURCE_DIR ${PROJECT_SOURCE_DIR}/examples - CMAKE_CACHE_ARGS ${COMMON_ARGS} ${EXAMPLES_ARGS} + CMAKE_CACHE_ARGS ${_examples_args} INSTALL_DIR examples DEPENDS libpsn00b tools mkpsxiso EXCLUDE_FROM_ALL ${SKIP_EXAMPLES} @@ -108,6 +118,7 @@ foreach( _subdir IN ITEMS ${CMAKE_INSTALL_BINDIR} ${CMAKE_INSTALL_LIBDIR} + ${CMAKE_INSTALL_INCLUDEDIR} ${CMAKE_INSTALL_DATADIR} ) install( diff --git a/CMakePresets.json b/CMakePresets.json index 7c9fdd3..d71c1ae 100644 --- a/CMakePresets.json +++ b/CMakePresets.json @@ -32,10 +32,11 @@ "generator": "Ninja", "binaryDir": "${sourceDir}/../build", "cacheVariables": { - "CMAKE_BUILD_TYPE": "Release", - "BUNDLE_TOOLCHAIN": "ON", - "BUILD_INFO": "Built by GitHub Actions ($env{GITHUB_REF_NAME} $env{GITHUB_SHA})", - "PSN00BSDK_TARGET": "$env{GCC_TARGET}" + "CMAKE_BUILD_TYPE": "Release", + "BUNDLE_TOOLCHAIN": "ON", + "PSN00BSDK_TARGET": "$env{GCC_TARGET}", + "PSN00BSDK_GIT_TAG": "$env{GITHUB_REF_NAME}", + "PSN00BSDK_GIT_COMMIT": "$env{GITHUB_SHA}" } } ] diff --git a/cpack/setup.cmake b/cpack/setup.cmake index 57568bb..a5d71ff 100644 --- a/cpack/setup.cmake +++ b/cpack/setup.cmake @@ -21,7 +21,7 @@ set( ## Bundled components -# "Install" the toolchain and CMake (by pulling files from its their install +# "Install" the toolchain and CMake (by pulling files from their install # locations). This is only useful when building installers, as CPack will pick # up these installation rules and bundle the toolchain in the installers. # NOTE: unfortunately there is no easy way to reuse the toolchain finding logic @@ -105,7 +105,6 @@ set(CPACK_RESOURCE_FILE_README ${PROJECT_SOURCE_DIR}/README.md) set(CPACK_RESOURCE_FILE_LICENSE ${PROJECT_SOURCE_DIR}/LICENSE.md) set(CPACK_PACKAGE_ICON ${CMAKE_CURRENT_LIST_DIR}/icon.ico) set(CPACK_PACKAGE_DESCRIPTION_FILE ${CMAKE_CURRENT_LIST_DIR}/description.txt) -set(CPACK_RESOURCE_FILE_WELCOME ${CMAKE_CURRENT_LIST_DIR}/welcome.txt) set(CPACK_PRE_BUILD_SCRIPTS ${CMAKE_CURRENT_LIST_DIR}/fakeroot_fix.cmake) set(CPACK_PACKAGE_INSTALL_DIRECTORY PSn00bSDK) diff --git a/cpack/welcome.txt b/cpack/welcome.txt deleted file mode 100644 index e024336..0000000 --- a/cpack/welcome.txt +++ /dev/null @@ -1,3 +0,0 @@ -This installer will set up PSn00bSDK, including prebuilt libraries, CMake scripts as well as several command-line utilities for PlayStation 1 homebrew development. - -NOTE: CMake and the GCC toolchain must be installed separately. diff --git a/doc/cmake_reference.md b/doc/cmake_reference.md index 9e134d9..3c89da3 100644 --- a/doc/cmake_reference.md +++ b/doc/cmake_reference.md @@ -207,9 +207,13 @@ the build script. ## Read-only variables -- `PSN00BSDK_VERSION` +- `PSN00BSDK_VERSION`, `PSN00BSDK_BUILD_DATE`, `PSN00BSDK_GIT_TAG`, + `PSN00BSDK_GIT_COMMIT` - The SDK's version number (`major.minor.patch`). + 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. - `PSN00BSDK_TOOLS`, `PSN00BSDK_INCLUDE`, `PSN00BSDK_LDSCRIPTS` @@ -229,4 +233,4 @@ the build script. LZP archives as part of the build pipeline. ----------------------------------------- -_Last updated on 2021-11-24 by spicyjpeg_ +_Last updated on 2021-12-29 by spicyjpeg_ diff --git a/doc/installation.md b/doc/installation.md index ca3aab4..1646653 100644 --- a/doc/installation.md +++ b/doc/installation.md @@ -62,7 +62,7 @@ and installed properly. repository to download additional dependencies: ```bash - git submodule update --init --recursive --remote + git submodule update --init --recursive ``` 5. Compile the libraries, tools and examples using CMake: @@ -74,9 +74,8 @@ and installed properly. If you want to install the SDK to a custom location rather than the default one (`C:\Program Files\PSn00bSDK` or `/usr/local` depending on your OS), add - `--install-prefix ` to the first command. Add - `-DPSN00BSDK_TARGET=mipsel-none-elf` if your toolchain targets - `mipsel-none-elf` rather than `mipsel-unknown-elf`. + `--install-prefix ` to the first command. Remember to add + `-DPSN00BSDK_TARGET=mipsel-unknown-elf` if necessary. **NOTE**: Ninja is used by default to build the SDK. If you can't get it to work or don't have it installed, pass `-G "Unix Makefiles"` (or @@ -151,4 +150,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 2021-12-23 by spicyjpeg_ +_Last updated on 2021-12-29 by spicyjpeg_ diff --git a/examples/sound/spustream/main.c b/examples/sound/spustream/main.c index 2032df5..e268c7d 100644 --- a/examples/sound/spustream/main.c +++ b/examples/sound/spustream/main.c @@ -349,7 +349,7 @@ void start_stream(void) { for (uint32_t i = 0; i < NUM_CHANNELS; i++) { SPU_CHANNELS[i].addr = SPU_RAM_ADDR(BUFFER_START_ADDR + BUFFER_SIZE * i); SPU_CHANNELS[i].freq = SAMPLE_RATE; - SPU_CHANNELS[i].adsr_param = 0xdfee80ff; // or 0x9fc080ff, 0xdff18087 + SPU_CHANNELS[i].adsr_param = 0x1fee80ff; // or 0x9fc080ff, 0xdff18087 } // Unmute the channels and route them for stereo output. You'll want to diff --git a/libpsn00b/CMakeLists.txt b/libpsn00b/CMakeLists.txt index 2b2e76d..829a2f7 100644 --- a/libpsn00b/CMakeLists.txt +++ b/libpsn00b/CMakeLists.txt @@ -73,7 +73,6 @@ install( # Generate build.json. This file is used to determine the SDK version after # installation and may contain additional metadata about the build. -string(TIMESTAMP PSN00BSDK_BUILD_DATE UTC) configure_file( build.json.template build.json ESCAPE_QUOTES diff --git a/libpsn00b/build.json.template b/libpsn00b/build.json.template index 666bb43..374b22a 100644 --- a/libpsn00b/build.json.template +++ b/libpsn00b/build.json.template @@ -1,5 +1,8 @@ { - "version": "${PSN00BSDK_VERSION}", - "build_date": "${PSN00BSDK_BUILD_DATE}", - "build_info": "${PSN00BSDK_BUILD_INFO}" + "version": "${PSN00BSDK_VERSION}", + "build_date": "${PSN00BSDK_BUILD_DATE}", + "git_tag": "${PSN00BSDK_GIT_TAG}", + "git_commit": "${PSN00BSDK_GIT_COMMIT}", + "cmake_version": "${CMAKE_VERSION}", + "host_system": "${CMAKE_HOST_SYSTEM_NAME}" } diff --git a/libpsn00b/cmake/internal_setup.cmake b/libpsn00b/cmake/internal_setup.cmake index 1d63e92..7d6bfdd 100644 --- a/libpsn00b/cmake/internal_setup.cmake +++ b/libpsn00b/cmake/internal_setup.cmake @@ -7,13 +7,14 @@ cmake_minimum_required(VERSION 3.20) include(GNUInstallDirs) -# Fetch the SDK version number from build.json. +# Fetch SDK version information from build.json. if(NOT DEFINED PSN00BSDK_VERSION) file(READ ${CMAKE_CURRENT_LIST_DIR}/../build.json _json) string(JSON PSN00BSDK_VERSION GET ${_json} version) string(JSON PSN00BSDK_BUILD_DATE GET ${_json} build_date) - string(JSON PSN00BSDK_BUILD_INFO GET ${_json} build_info) + string(JSON PSN00BSDK_GIT_TAG GET ${_json} git_tag) + string(JSON PSN00BSDK_GIT_COMMIT GET ${_json} git_commit) endif() ## Settings (can be overridden by projects) -- cgit v1.2.3 From ba03884e3d52d47a4fa1b474ca7dc6b419ee6898 Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Thu, 30 Dec 2021 14:56:55 +0100 Subject: Refactor dynamic linker API, fix system/dynlink example --- examples/system/dynlink/display.c | 74 ----------------- examples/system/dynlink/main.c | 148 +++++++++++++++++++++++++++------ libpsn00b/include/dlfcn.h | 101 ++++++++++++---------- libpsn00b/psxetc/_dl_resolve_wrapper.s | 8 +- libpsn00b/psxetc/dl.c | 51 +++++------- 5 files changed, 207 insertions(+), 175 deletions(-) delete mode 100644 examples/system/dynlink/display.c diff --git a/examples/system/dynlink/display.c b/examples/system/dynlink/display.c deleted file mode 100644 index 573f17c..0000000 --- a/examples/system/dynlink/display.c +++ /dev/null @@ -1,74 +0,0 @@ -/* - * PSn00bSDK dynamic linker example (utilities) - * (C) 2021 spicyjpeg - MPL licensed - */ - -#include - -#include "library/dll_common.h" - -#define SCREEN_XRES 320 -#define SCREEN_YRES 240 - -#define BGCOLOR_R 48 -#define BGCOLOR_G 24 -#define BGCOLOR_B 0 - -/* Display/GPU context utilities */ - -void init_context(CONTEXT *ctx) { - DB *db; - - ResetGraph(0); - ctx->xres = SCREEN_XRES; - ctx->yres = SCREEN_YRES; - ctx->db_active = 0; - - db = &(ctx->db[0]); - SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES); - SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); - setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); - db->draw.isbg = 1; - db->draw.dtd = 1; - - db = &(ctx->db[1]); - SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); - SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES); - setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); - db->draw.isbg = 1; - db->draw.dtd = 1; - - // Set up the ordering tables and primitive buffers. - db = &(ctx->db[0]); - ctx->db_nextpri = db->p; - ClearOTagR((u_long *) db->ot, OT_LEN); - - PutDrawEnv(&(db->draw)); - //PutDispEnv(&(db->disp)); - - db = &(ctx->db[1]); - ClearOTagR((u_long *) db->ot, OT_LEN); - - // Create a text stream at the top of the screen. - FntLoad(960, 0); - FntOpen(4, 12, 312, 32, 2, 256); -} - -void display(CONTEXT *ctx) { - DB *db; - - DrawSync(0); - VSync(0); - ctx->db_active ^= 1; - - db = &(ctx->db[ctx->db_active]); - ctx->db_nextpri = db->p; - ClearOTagR((u_long *) db->ot, OT_LEN); - - PutDrawEnv(&(db->draw)); - PutDispEnv(&(db->disp)); - SetDispMask(1); - - db = &(ctx->db[!ctx->db_active]); - DrawOTag((u_long *) &(db->ot[OT_LEN - 1])); -} diff --git a/examples/system/dynlink/main.c b/examples/system/dynlink/main.c index 6d93e71..690371e 100644 --- a/examples/system/dynlink/main.c +++ b/examples/system/dynlink/main.c @@ -11,14 +11,14 @@ * however this may be expanded in the future. * * Being able to introspect local symbols at runtime, in turn, allows us to use - * the dl*() set of APIs to load, link and execute code from an external file + * another set of APIs to load, link and execute code from an external file * (compiled with the dll.ld linker script). A dynamically-loaded library can * reference and access any non-static function or variable within the main * executable (and the libraries the main executable has been compiled with); * the dynamic linker will automatically patch the DLL's code and resolve these * references so that they point to the addresses listed in the map file. DLLs * also have their own symbol tables, and any symbol in a DLL is accessible to - * the main executable through dlsym(). + * the main executable through DL_GetDLLSymbol(). * * This example shows how DLLs can be loaded and unloaded at any time. Pressing * START will unload the current DLL and load an alternate one on-the-fly. A @@ -47,6 +47,7 @@ #include #include #include +#include #include "library/dll_common.h" @@ -67,14 +68,77 @@ const void *const DO_NOT_STRIP[] __attribute__((section(".dummy"))) = { }; static const char *const DLL_FILENAMES[] = { - "cdrom:CUBE.DLL;1", - "cdrom:BALLS.DLL;1" + "\\CUBE.DLL;1", + "\\BALLS.DLL;1" }; #define DLL_COUNT 2 -void init_context(CONTEXT *ctx); -void display(CONTEXT *ctx); +/* Display/GPU context utilities */ + +#define SCREEN_XRES 320 +#define SCREEN_YRES 240 + +#define BGCOLOR_R 48 +#define BGCOLOR_G 24 +#define BGCOLOR_B 0 + +void init_context(CONTEXT *ctx) { + DB *db; + + ResetGraph(0); + ctx->xres = SCREEN_XRES; + ctx->yres = SCREEN_YRES; + ctx->db_active = 0; + + db = &(ctx->db[0]); + SetDefDispEnv(&(db->disp), 0, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); + setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); + db->draw.isbg = 1; + db->draw.dtd = 1; + + db = &(ctx->db[1]); + SetDefDispEnv(&(db->disp), SCREEN_XRES, 0, SCREEN_XRES, SCREEN_YRES); + SetDefDrawEnv(&(db->draw), 0, 0, SCREEN_XRES, SCREEN_YRES); + setRGB0(&(db->draw), BGCOLOR_R, BGCOLOR_G, BGCOLOR_B); + db->draw.isbg = 1; + db->draw.dtd = 1; + + // Set up the ordering tables and primitive buffers. + db = &(ctx->db[0]); + ctx->db_nextpri = db->p; + ClearOTagR((u_long *) db->ot, OT_LEN); + + PutDrawEnv(&(db->draw)); + //PutDispEnv(&(db->disp)); + + db = &(ctx->db[1]); + ClearOTagR((u_long *) db->ot, OT_LEN); + + // Create a text stream at the top of the screen. + FntLoad(960, 0); + FntOpen(4, 12, 312, 32, 2, 256); +} + +void display(CONTEXT *ctx) { + DB *db; + + DrawSync(0); + VSync(0); + ctx->db_active ^= 1; + + db = &(ctx->db[ctx->db_active]); + ctx->db_nextpri = db->p; + ClearOTagR((u_long *) db->ot, OT_LEN); + + PutDrawEnv(&(db->draw)); + PutDispEnv(&(db->disp)); + SetDispMask(1); + + db = &(ctx->db[!ctx->db_active]); + DrawOTag((u_long *) &(db->ot[OT_LEN - 1])); +} /* Symbol overriding example */ @@ -118,8 +182,8 @@ void *custom_resolver(DLL *dll, const char *name) { // Define a struct to store pointers to a DLL's functions into. This is not // strictly required, however looking up symbols is a relatively slow operation -// and the pointers returned by dlsym() should be saved and reused as much as -// possible. +// 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); @@ -134,47 +198,79 @@ static CONTEXT ctx; #define SHOW_STATUS(...) { FntPrint(-1, __VA_ARGS__); FntFlush(-1); display(&ctx); } #define SHOW_ERROR(...) { SHOW_STATUS(__VA_ARGS__); while (1) __asm__("nop"); } +// This is a simple function to read a CD-ROM file into memory (the dynamic +// linker no longer provides APIs that take file paths directly). You might +// want to replace this with code that e.g. loads the DLL from a compressed +// archive or even from the serial port. +size_t load_file(const char *filename, void **ptr) { + SHOW_STATUS("LOADING %s\n", filename); + + CdlFILE file; + if (!CdSearchFile(&file, filename)) + SHOW_ERROR("FAILED TO FIND %s\n", filename); + + // Round up the file size so it matches the number of sectors occupied by + // the file (as CdRead() can only return entire sectors). + size_t len = (file.size + 2047) & 0xfffff800; + void *_ptr = malloc(len); + if (!_ptr) + SHOW_ERROR("FAILED TO ALLOCATE %d BYTES\n", len); + + CdControl(CdlSetloc, &(file.pos), 0); + CdRead(len / 2048, _ptr, CdlModeSpeed); + if (CdReadSync(0, 0) < 0) + SHOW_ERROR("FAILED TO READ %s\n", filename); + + *ptr = _ptr; + return file.size; +} + void load_dll(const char *filename) { + // As we're passing RTLD_FREE_ON_DESTROY to DL_CreateDLL(), calling + // DL_DestroyDLL() will also deallocate the buffer the DLL was loaded into. if (dll) - dlclose(dll); + DL_DestroyDLL(dll); - SHOW_STATUS("LOADING %s\n", filename); + void *ptr; + size_t len = load_file(filename, &ptr); - dll = dlopen(filename, RTLD_LAZY); + dll = DL_CreateDLL(ptr, len, RTLD_LAZY | RTLD_FREE_ON_DESTROY); if (!dll) - SHOW_ERROR("FAILED TO LOAD %s\nERROR=%d\n", filename, (int32_t) dlerror()); + SHOW_ERROR("FAILED TO PARSE %s\nERROR=%d\n", filename, (int32_t) DL_GetLastError()); - dll_api.init = dlsym(dll, "init"); - dll_api.render = dlsym(dll, "render"); + dll_api.init = DL_GetDLLSymbol(dll, "init"); + dll_api.render = DL_GetDLLSymbol(dll, "render"); printf("DLL init() @ %08x, render() @ %08x\n", dll_api.init, dll_api.render); // Unfortunately, due to how position-independent code works, function - // pointers returned by dlsym() can't be called directly. We have to use - // the DL_CALL() macro instead, which sets up register $t9 to ensure the - // function can locate and reference the DLL's relocation table. + // pointers returned by DL_GetDLLSymbol() can't be called directly. We have + // to use the DL_CALL() macro instead, which sets up register $t9 to ensure + // the function can locate and reference the DLL's relocation table. DL_CALL(dll_api.init, &ctx); } int main(int argc, const char* argv[]) { - // As DL_LoadSymbolMap() and dlopen() rely on BIOS file APIs, the BIOS CD - // driver must be initialized by calling _InitCd() prior to loading the - // symbol map (but after setting up the GPU, for some reason). init_context(&ctx); SHOW_STATUS("INITIALIZING CD\n"); - _InitCd(); + CdInit(); + + // Load the symbol map file, let the dynamic linker parse it and then + // unload it. + void *ptr; + size_t len = load_file("\\MAIN.MAP;1", &ptr); - SHOW_STATUS("LOADING SYMBOL MAP\n"); + if (!DL_ParseSymbolMap(ptr, len)) + SHOW_ERROR("FAILED TO PARSE SYMBOL MAP\nERROR=%d\n", (int32_t) DL_GetLastError()); - if (!DL_LoadSymbolMap("cdrom:MAIN.MAP;1")) - SHOW_ERROR("FAILED TO LOAD SYMBOL MAP\nERROR=%d\n", (int32_t) dlerror()); + free(ptr); // Try to obtain a reference to a local function. void (*_display)() = DL_GetSymbolByName("display"); if (!_display) - SHOW_ERROR("FAILED TO LOOK UP LOCAL FUNCTION\nERROR=%d\n", (int32_t) dlerror()); + SHOW_ERROR("FAILED TO LOOK UP LOCAL FUNCTION\nERROR=%d\n", (int32_t) DL_GetLastError()); printf("Symbol map test, display() @ %08x\n", _display); @@ -220,7 +316,7 @@ int main(int argc, const char* argv[]) { last_buttons = pad->btn; } - //dlclose(dll); + //DL_DestroyDLL(dll); //DL_UnloadSymbolMap(); return 0; } diff --git a/libpsn00b/include/dlfcn.h b/libpsn00b/include/dlfcn.h index b3a5cec..6874d06 100644 --- a/libpsn00b/include/dlfcn.h +++ b/libpsn00b/include/dlfcn.h @@ -21,39 +21,39 @@ #define RTLD_DEFAULT ((DLL *) 0) typedef enum _DL_Error { - RTLD_E_NONE = 0, // No error - RTLD_E_FILE_OPEN = 1, // Unable to find or open file - RTLD_E_FILE_ALLOC = 2, // Unable to allocate buffer to load file into - RTLD_E_FILE_READ = 3, // Failed to read file - RTLD_E_NO_MAP = 4, // No symbol map has been loaded yet - RTLD_E_MAP_ALLOC = 5, // Unable to allocate symbol map structures - RTLD_E_NO_SYMBOLS = 6, // No symbols found in symbol map - RTLD_E_DLL_NULL = 7, // Unable to initialize DLL from null pointer - RTLD_E_DLL_ALLOC = 8, // Unable to allocate DLL metadata structures - RTLD_E_DLL_FORMAT = 9, // Unsupported DLL type or format - RTLD_E_NO_FILE_API = 10, // psxetc has been built without file support - RTLD_E_MAP_SYMBOL = 11, // Symbol not found in symbol map - RTLD_E_DLL_SYMBOL = 12, // Symbol not found in DLL - RTLD_E_HASH_LOOKUP = 13 // Hash table lookup failed due to internal error + RTLD_E_NONE = 0, // No error + RTLD_E_FILE_OPEN = 1, // Unable to find or open file + RTLD_E_FILE_ALLOC = 2, // Unable to allocate buffer to load file into + RTLD_E_FILE_READ = 3, // Failed to read file + RTLD_E_NO_MAP = 4, // No symbol map has been loaded yet + RTLD_E_MAP_ALLOC = 5, // Unable to allocate symbol map structures + RTLD_E_NO_SYMBOLS = 6, // No symbols found in symbol map + RTLD_E_DLL_NULL = 7, // Unable to initialize DLL from null pointer + RTLD_E_DLL_ALLOC = 8, // Unable to allocate DLL metadata structures + RTLD_E_DLL_FORMAT = 9, // Unsupported DLL type or format + RTLD_E_MAP_SYMBOL = 10, // Symbol not found in symbol map + RTLD_E_DLL_SYMBOL = 11, // Symbol not found in DLL + RTLD_E_HASH_LOOKUP = 12 // Hash table lookup failed due to internal error } DL_Error; typedef enum _DL_ResolveMode { - RTLD_LAZY = 1, // Resolve functions when they are first called (default) - RTLD_NOW = 2 // Resolve all symbols immediately on load + RTLD_LAZY = 1, // Resolve functions when they are first called (default) + RTLD_NOW = 2, // Resolve all symbols immediately on load + RTLD_FREE_ON_DESTROY = 4 // Automatically free DLL buffer when closing DLL } DL_ResolveMode; // Members of this struct should not be accessed directly in most cases, but // they are intentionally exposed for easier expandability. typedef struct _DLL { - void *ptr; - void *malloc_ptr; - size_t size; - const uint32_t *hash; - uint32_t *got; - Elf32_Sym *symtab; - const char *strtab; - uint16_t symbol_count; - uint16_t got_length; + void *ptr; + void *malloc_ptr; + size_t size; + const uint32_t *hash; + uint32_t *got; + Elf32_Sym *symtab; + const char *strtab; + uint16_t symbol_count; + uint16_t got_length; } DLL; /* API */ @@ -62,8 +62,6 @@ typedef struct _DLL { extern "C" { #endif -// TODO: rewrite these javadoc comments into proper documentation - /** * @brief Reads the symbol table from the provided string buffer (which may or * may not be null-terminated), parses it and stores the parsed entries into a @@ -80,13 +78,14 @@ extern "C" { * file in the appropriate format after building the executable, by using this * command: * - * mipsel-unknown-elf-nm -f posix -l -n executable.elf >executable.map + * mipsel-none-elf-nm -f posix -l -n executable.elf >executable.map * * @param ptr * @param size * @return -1 or number of entries parsed */ int32_t DL_ParseSymbolMap(const char *ptr, size_t size); + /** * @brief File wrapper around DL_ParseSymbolMap(). Allocates a temporary buffer * then loads the specified map file into it (using BIOS APIs) and calls @@ -96,14 +95,16 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size); * @param filename Must always contain device name, e.g. "cdrom:MODULE.DLL;1" * @return -1 or number of entries parsed */ -int32_t DL_LoadSymbolMap(const char *filename); +//int32_t DL_LoadSymbolMapFromFile(const char *filename); + /** * @brief Frees internal buffers containing the currently loaded symbol map. * This is automatically done before loading a new symbol map so there is no * need to call this function in most cases, however it can still be useful to * free up space on the heap once the symbol map is no longer needed. */ -void DL_UnloadSymbolMap(void); +void DL_UnloadSymbolMap(void); + /** * @brief Queries the currently loaded symbol map for the symbol with the given * name and returns a pointer to it, which can then be used to directly access @@ -112,7 +113,8 @@ void DL_UnloadSymbolMap(void); * @param name * @return NULL or pointer to symbol (any type) */ -void *DL_GetSymbolByName(const char *name); +void *DL_GetSymbolByName(const char *name); + /** * @brief Sets a custom function to be called for resolving symbols in DLLs. * The function will be given a pointer to the current DLL and the unresolved @@ -123,7 +125,7 @@ void *DL_GetSymbolByName(const char *name); * * @param callback NULL or pointer to callback function */ -void DL_SetResolveCallback(void *(*callback)(DLL *, const char *)); +void DL_SetResolveCallback(void *(*callback)(DLL *, const char *)); /** * @brief Initializes a buffer holding the contents of a dynamically-loaded @@ -131,8 +133,8 @@ void DL_SetResolveCallback(void *(*callback)(DLL *, const char *)); * binary) *in-place*. A new DLL struct is allocated to store metadata but, * unlike DL_ParseSymbolMap(), the DLL's actual code, data and tables are * referenced directly from the provided buffer. The buffer must not be moved - * or deallocated, at least not before calling dlclose() on the DLL struct - * returned by this function. + * or deallocated, at least not before calling DL_DestroyDLL() on the DLL + * struct returned by this function. * * The third argument specifies when symbols in the DLL should be resolved. * Setting it to RTLD_LAZY defers resolution of undefined functions to when @@ -145,7 +147,8 @@ void DL_SetResolveCallback(void *(*callback)(DLL *, const char *)); * @param mode RTLD_LAZY or RTLD_NOW * @return NULL or pointer to a new DLL struct */ -DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode); +DLL *DL_CreateDLL(void *ptr, size_t size, DL_ResolveMode mode); + /** * @brief File wrapper around dlinit(). Allocates a new buffer, loads the * specified file into it (using BIOS APIs) and calls dlinit() on that. When @@ -153,19 +156,22 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode); * automatically destroyed. * * @param filename Must always contain device name, e.g. "cdrom:MODULE.DLL;1" - * @param mode RTLD_LAZY or RTLD_NOW + * @param mode RTLD_LAZY or RTLD_NOW + optionally RTLD_FREE_ON_DESTROY * @return NULL or pointer to a new DLL struct */ -DLL *dlopen(const char *filename, DL_ResolveMode mode); +//DLL *DL_LoadDLLFromFile(const char *filename, DL_ResolveMode mode); + /** * @brief Destroys a loaded DLL by calling its global destructors and freeing - * the buffer it's loaded in. Any pointer passed to dlclose() should no longer - * be used after the call. If the DLL was initialized in-place using dlinit(), - * dlclose() will *NOT* free the buffer initially passed to dlinit(). + * the buffer it's loaded in. Any pointer passed to DL_DestroyDLL() should no + * longer be used after the call. If the DLL was initialized in-place using + * DL_CreateDLL(), DL_DestroyDLL() will only free the buffer initially passed + * to DL_CreateDLL() if RTLD_FREE_ON_DESTROY was used. * * @param dll */ -void dlclose(DLL *dll); +void DL_DestroyDLL(DLL *dll); + /** * @brief Returns a pointer to the DLL symbol with the given name, or null if * it can't be found. If null or RTLD_DEFAULT is passed as first argument, the @@ -176,7 +182,8 @@ void dlclose(DLL *dll); * @param name * @return NULL or pointer to symbol (any type) */ -void *dlsym(DLL *dll, const char *name); +void *DL_GetDLLSymbol(const DLL *dll, const char *name); + /** * @brief Returns a code describing the last error that occurred, or DL_E_NONE * if no error has occurred since the last call to dlerror() (i.e. calling this @@ -184,7 +191,15 @@ void *dlsym(DLL *dll, const char *name); * * @return NULL or member of DL_Error enum */ -DL_Error dlerror(void); +DL_Error DL_GetLastError(void); + +/* POSIX "compatibility" macros */ + +#define dlinit(ptr, size, mode) DL_CreateDLL(ptr, size, mode) +//#define dlopen(filename, mode) DL_LoadDLLFromFile(filename, mode) +#define dlsym(dll, name) DL_GetDLLSymbol(dll, name) +#define dlclose(dll) DL_DestroyDLL(dll) +#define dlerror() DL_GetLastError() #ifdef __cplusplus } diff --git a/libpsn00b/psxetc/_dl_resolve_wrapper.s b/libpsn00b/psxetc/_dl_resolve_wrapper.s index 069ee84..7f1132b 100644 --- a/libpsn00b/psxetc/_dl_resolve_wrapper.s +++ b/libpsn00b/psxetc/_dl_resolve_wrapper.s @@ -4,7 +4,7 @@ # This function is called by the lazy loader stubs generated by GCC in the # .plt/.MIPS.stubs section when attempting to call a GOT entry whose address # hasn't yet been resolved. The generated stubs conform to the MIPS ABI and -# uses the following registers to pass the following parameters: +# uses the following registers: # - $t7 = address the resolved function should return to (i.e. $ra of the # caller that triggered the stub) # - $t8 = index of the function in the .dynsym symbol table @@ -25,13 +25,13 @@ _dl_resolve_wrapper: # Figure out where the DLL's struct is. dlinit() places a pointer to the # struct in the second GOT entry, so it's just a matter of indexing the GOT - # using GP. Then call _dl_resolve_helper with the struct and $t8 as + # using $gp. Then call _dl_resolve_helper with the struct and $t8 as # arguments, and store the return value into $t0. - lw $a0, -0x7fec($gp) # sizeof(uint32_t) - 0x7ff0 (see dll.ld) + lw $a0, -0x7fec($gp) # (DLL *) sizeof(uint32_t) - 0x7ff0 [see dll.ld] move $a1, $t8 jal _dl_resolve_helper - addiu $sp, -8 # (branch delay) + addiu $sp, -8 addiu $sp, 8 move $t0, $v0 diff --git a/libpsn00b/psxetc/dl.c b/libpsn00b/psxetc/dl.c index e43374f..cbdcb66 100644 --- a/libpsn00b/psxetc/dl.c +++ b/libpsn00b/psxetc/dl.c @@ -38,8 +38,9 @@ //#define DEBUG // Comment before building to disable functions that rely on BIOS file APIs, -// i.e. DL_LoadSymbolMap() and dlopen(). -#define USE_FILE_API +// i.e. DL_LoadSymbolMapFromFile() and DL_LoadDLLFromFile(). +// FIXME: those seem to be broken currently, and shouldn't be used anyway +//#define USE_FILE_API /* Private types */ @@ -132,7 +133,7 @@ static uint32_t _elf_hash(const char *str) { } #ifdef USE_FILE_API -static uint8_t *_load_file(const char *filename, size_t *size_output) { +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); @@ -286,10 +287,10 @@ int32_t DL_ParseSymbolMap(const char *ptr, size_t size) { return entries; } -int32_t DL_LoadSymbolMap(const char *filename) { #ifdef USE_FILE_API +int32_t DL_LoadSymbolMapFromFile(const char *filename) { size_t size; - char *ptr = _load_file(filename, &size); + char *ptr = _dl_load_file(filename, &size); if (!ptr) return -1; @@ -297,10 +298,8 @@ int32_t DL_LoadSymbolMap(const char *filename) { free(ptr); return entries; -#else - _ERROR(RTLD_E_NO_FILE_API, -1); -#endif } +#endif void DL_UnloadSymbolMap(void) { if (!_symbol_map.entries) @@ -354,7 +353,7 @@ void DL_SetResolveCallback(void *(*callback)(DLL *, const char *)) { /* Library loading and linking API */ -DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) { +DLL *DL_CreateDLL(void *ptr, size_t size, DL_ResolveMode mode) { if (!ptr) _ERROR(RTLD_E_DLL_NULL, 0); @@ -365,7 +364,7 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) { } dll->ptr = ptr; - dll->malloc_ptr = 0; + dll->malloc_ptr = (mode & RTLD_FREE_ON_DESTROY) ? ptr : 0; dll->size = size; _LOG("psxetc: Initializing DLL at %08x\n", ptr); @@ -548,7 +547,7 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) { // If RTLD_NOW was passed, resolve GOT entries ahead of time by // cross-referencing them with the symbol table. - if (mode != RTLD_NOW) + if (!(mode & RTLD_NOW)) continue; for (uint32_t j = got_offset; j < dll->got_length; j++) { @@ -584,7 +583,7 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) { // _start() for regular executables, but we have to do it outside of the // DLL as there's no _start() or even a defined entry point within the // DLL itself. - const uint32_t *ctor_list = dlsym(dll, "__CTOR_LIST__"); + const uint32_t *ctor_list = DL_GetDLLSymbol(dll, "__CTOR_LIST__"); if (ctor_list) { for (uint32_t i = ((uint32_t) ctor_list[0]); i >= 1; i--) { void (*ctor)(void) = (void (*)(void)) ctor_list[i]; @@ -595,32 +594,28 @@ DLL *dlinit(void *ptr, size_t size, DL_ResolveMode mode) { return dll; } -DLL *dlopen(const char *filename, DL_ResolveMode mode) { #ifdef USE_FILE_API +DLL *DL_LoadDLLFromFile(const char *filename, DL_ResolveMode mode) { size_t size; - char *ptr = _load_file(filename, &size); + char *ptr = _dl_load_file(filename, &size); if (!ptr) return 0; - DLL *dll = dlinit(ptr, size, mode); - if (dll) - dll->malloc_ptr = dll->ptr; - else + DLL *dll = DL_CreateDLL(ptr, size, mode | RTLD_FREE_ON_DESTROY); + if (!dll) free(ptr); return dll; -#else - _ERROR(RTLD_E_NO_FILE_API, 0); -#endif } +#endif -void dlclose(DLL *dll) { +void DL_DestroyDLL(DLL *dll) { if (dll == RTLD_DEFAULT) return; if (dll->ptr) { // Call the DLL's global destructors. - const uint32_t *dtor_list = dlsym(dll, "__DTOR_LIST__"); + const uint32_t *dtor_list = DL_GetDLLSymbol(dll, "__DTOR_LIST__"); if (dtor_list) { for (uint32_t i = 0; i < ((uint32_t) dtor_list[0]); i++) { void (*dtor)(void) = (void (*)(void)) dtor_list[i + 1]; @@ -629,15 +624,15 @@ void dlclose(DLL *dll) { } } - // If the DLL is associated to a buffer allocated by dlopen(), free that - // buffer. + // If the DLL is associated to a buffer allocated by DL_LoadDLLFromFile(), + // free that buffer. if (dll->malloc_ptr) free(dll->malloc_ptr); free(dll); } -void *dlsym(DLL *dll, const char *name) { +void *DL_GetDLLSymbol(const DLL *dll, const char *name) { if (dll == RTLD_DEFAULT) return DL_GetSymbolByName(name); //return _dl_resolve_callback(RTLD_DEFAULT, name); @@ -654,7 +649,7 @@ void *dlsym(DLL *dll, const char *name) { // provided. for (uint32_t i = bucket[hash_mod]; i != 0xffffffff;) { if (i >= nchain) { - _LOG("psxetc: dlsym() index out of bounds (i = %d, n = %d)\n", i, nchain); + _LOG("psxetc: DL_GetDLLSymbol() index out of bounds (i = %d, n = %d)\n", i, nchain); _ERROR(RTLD_E_HASH_LOOKUP, 0); } @@ -673,7 +668,7 @@ void *dlsym(DLL *dll, const char *name) { _ERROR(RTLD_E_DLL_SYMBOL, 0); } -DL_Error dlerror(void) { +DL_Error DL_GetLastError(void) { DL_Error last = _error_code; _error_code = RTLD_E_NONE; -- cgit v1.2.3 From de9047f568f2f3509b56a2b566d7353cae616eb7 Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Thu, 30 Dec 2021 14:58:14 +0100 Subject: Add known_bugs.md, fix sound/vagsample and declarations --- CMakeLists.txt | 6 ++++ doc/known_bugs.md | 52 ++++++++++++++++++++++++++++++ examples/sound/vagsample/0proyt.h | 2 +- examples/sound/vagsample/threedeeffeggzz.h | 2 +- libpsn00b/include/psxcd.h | 6 ++-- libpsn00b/include/psxspu.h | 4 +-- libpsn00b/psxcd/psxcd.c | 8 ++--- 7 files changed, 69 insertions(+), 11 deletions(-) create mode 100644 doc/known_bugs.md diff --git a/CMakeLists.txt b/CMakeLists.txt index 6a20a0e..2079fd1 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -78,6 +78,12 @@ set( -DCMAKE_INSTALL_PREFIX:PATH=${PROJECT_BINARY_DIR}/examples ) +# Ensure PSn00bSDK isn't being built using the toolchain file from PSn00bSDK +# itself (or from another version of it). +if(CMAKE_TOOLCHAIN_FILE MATCHES ".*libpsn00b[/\\]cmake[/\\]sdk\.cmake$") + message(FATAL_ERROR "CMAKE_TOOLCHAIN_FILE is set to the toolchain file of an existing PSn00bSDK installation. It must be unset or overridden by passing '-DCMAKE_TOOLCHAIN_FILE=\"\"' to CMake.") +endif() + ## Subprojects if(NOT EXISTS ${PROJECT_SOURCE_DIR}/tools/mkpsxiso/CMakeLists.txt) diff --git a/doc/known_bugs.md b/doc/known_bugs.md new file mode 100644 index 0000000..2af9e3f --- /dev/null +++ b/doc/known_bugs.md @@ -0,0 +1,52 @@ + +# Known PSn00bSDK bugs + +This is an incomplete list of things that are currently broken (or not behaving +as they should, or untested on real hardware) and haven't yet been fixed. + +## Libraries + +`psxspu`: + +- Calls to `SpuSetTransferMode()` are ignored. SPU transfers are always + performed using DMA, which imposes limitations such as the data length having + to be a multiple of 64 bytes. + +`psxetc`: + +- `DL_LoadSymbolMapFromFile()`, `DL_LoadDLLFromFile()` and `dlopen()` have been + disabled due to bugs in the BIOS file APIs. The dynamic linker can still be + used by loading DLL binaries into RAM manually and calling `DL_CreateDLL()` + on them (see the `system/dynlink` example). + +## Tools + +- The `mkpsxiso` submodule is temporarily set to point to a fork of `mkpsxiso` + with bugfixed CMake scripts (the main repo is broken to the point it fails to + build). There is [another fork](https://github.com/CookiePLMonster/mkpsxiso) + which is currently work-in-progress and includes more fixes as well as a tool + to dump existing CD images: PSn00bSDK will switch back to the main `mkpsxiso` + repo once the changes get upstreamed. + +## Examples + +- `cdrom/cdxa` and `sound/spustream` demonstrate how to stream an audio file + from CD-ROM. Such a file isn't provided however, as PSn00bSDK does not yet + come with the tooling required for transcoding audio from a source file. In + order to run these examples you'll have to provide your own audio files, + convert them and build the CD image manually. + +- `demos/n00bdemo` suffers from flickering on real hardware, especially when + masking/stencil buffering is used. + +- `graphics/render2tex` gets stuck after initialization on real hardware. + +- `io/pads` seems to work on real hardware, but fails to automatically enable + analog mode on DualShock controllers. This example needs more testing with + official and unofficial controllers. + +- `io/system573` hasn't been tested on a real Konami System 573. It runs on + MAME, however MAME's System 573 emulation is *very* inaccurate. + +----------------------------------------- +_Last updated on 2021-12-30 by spicyjpeg_ diff --git a/examples/sound/vagsample/0proyt.h b/examples/sound/vagsample/0proyt.h index 4402b9a..73629f9 100644 --- a/examples/sound/vagsample/0proyt.h +++ b/examples/sound/vagsample/0proyt.h @@ -12616,6 +12616,6 @@ unsigned char proyt[] = { 0xf2,0xf0,0xf2,0x15,0xc2,0x1b,0x00,0x00,0x01,0xe3,0x31,0x13,0xf3,0x1f,0xe2, 0x2f,0x13,0xd6,0x20,0x6e,0x2d,0x1b,0x00,0x03,0x42,0xc3,0x14,0x30,0x20,0x21, 0x21,0x32,0x0e,0x32,0x12,0x42,0xf0,0x39,0x01,0x13,0x12,0x04,0xb3,0x06,0x1f, -0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x00,0x77,0x77,0x77,0x77,0x77, +0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x00,0x07,0x05,0x77,0x77,0x77,0x77,0x77, 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77 }; diff --git a/examples/sound/vagsample/threedeeffeggzz.h b/examples/sound/vagsample/threedeeffeggzz.h index ed0e098..f0815aa 100644 --- a/examples/sound/vagsample/threedeeffeggzz.h +++ b/examples/sound/vagsample/threedeeffeggzz.h @@ -15194,6 +15194,6 @@ unsigned char tdfx[] = { 0x0b,0x1e,0x21,0xa0,0x4f,0xe2,0x2d,0x17,0x00,0xd2,0x02,0x1d,0xd1,0xf0,0x21, 0xfa,0x2e,0xc4,0x1e,0xfe,0xf2,0x2f,0x24,0x17,0x00,0xef,0xee,0x1f,0xf2,0xce, 0x0d,0x25,0x01,0x3e,0xcb,0xe1,0x30,0xeb,0xc4,0x17,0x01,0x1f,0x1f,0xaf,0x22, -0xfd,0xe0,0xf3,0x0b,0x2e,0xdd,0xc3,0xde,0x12,0x2d,0x07,0x00,0x77,0x77,0x77, +0xfd,0xe0,0xf3,0x0b,0x2e,0xdd,0xc3,0xde,0x12,0x2d,0x07,0x05,0x77,0x77,0x77, 0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77,0x77 }; diff --git a/libpsn00b/include/psxcd.h b/libpsn00b/include/psxcd.h index ffbe86b..3336963 100644 --- a/libpsn00b/include/psxcd.h +++ b/libpsn00b/include/psxcd.h @@ -132,9 +132,9 @@ CdlLOC* CdIntToPos(int i, CdlLOC *p); int CdPosToInt(CdlLOC *p); int CdGetToc(CdlLOC *toc); -int CdControl(u_char com, u_char *param, u_char *result); -int CdControlB(u_char com, u_char *param, u_char *result); -int CdControlF(u_char com, u_char *param); +int CdControl(u_char com, const void *param, u_char *result); +int CdControlB(u_char com, const void *param, u_char *result); +int CdControlF(u_char com, const void *param); int CdSync(int mode, u_char *result); u_long CdSyncCallback(CdlCB func); diff --git a/libpsn00b/include/psxspu.h b/libpsn00b/include/psxspu.h index a87e347..da000e3 100644 --- a/libpsn00b/include/psxspu.h +++ b/libpsn00b/include/psxspu.h @@ -115,7 +115,7 @@ extern "C" { void SpuInit(); -void SpuSetVoiceRaw( int voice, SpuVoiceRaw* param ); +void SpuSetVoiceRaw( int voice, const SpuVoiceRaw* param ); void SpuReverbOn( int voice ); void SpuSetReverb(); @@ -128,7 +128,7 @@ void SpuSetKey(int on_off, u_int voice_bit); // SPU transfer functions int SpuSetTransferMode(int mode); int SpuSetTransferStartAddr(int addr); -int SpuWrite(unsigned char* addr, int size); +int SpuWrite(const unsigned char* addr, int size); void SpuWait(); #ifdef __cplusplus diff --git a/libpsn00b/psxcd/psxcd.c b/libpsn00b/psxcd/psxcd.c index 76415f9..8f19c8d 100644 --- a/libpsn00b/psxcd/psxcd.c +++ b/libpsn00b/psxcd/psxcd.c @@ -21,7 +21,7 @@ volatile int _cd_last_sector_count; int _cd_media_changed; void _cd_init(void); -void _cd_control(unsigned char com, unsigned char *param, int plen); +void _cd_control(unsigned char com, const void *param, int plen); void _cd_wait_ack(void); void _cd_wait(void); @@ -50,7 +50,7 @@ int CdInit(void) return 1; } -int CdControl(unsigned char com, unsigned char *param, unsigned char *result) +int CdControl(unsigned char com, const void *param, unsigned char *result) { // Don't issue command if ack is not received yet if( _cd_ack_wait ) @@ -72,7 +72,7 @@ int CdControl(unsigned char com, unsigned char *param, unsigned char *result) return 1; } -int CdControlB(unsigned char com, unsigned char *param, unsigned char *result) +int CdControlB(unsigned char com, const void *param, unsigned char *result) { if( !CdControl(com, param, result) ) { @@ -83,7 +83,7 @@ int CdControlB(unsigned char com, unsigned char *param, unsigned char *result) return 1; } -int CdControlF(unsigned char com, unsigned char *param) +int CdControlF(unsigned char com, const void *param) { int param_len=0; -- cgit v1.2.3 From de38196a978548b61c4b45115d24ef743b9eef90 Mon Sep 17 00:00:00 2001 From: spicyjpeg <88942473+spicyjpeg@users.noreply.github.com> Date: Mon, 17 Jan 2022 15:57:04 +0100 Subject: Minor psxgpu/psxpad header changes --- examples/io/pads/main.c | 37 ++-- libpsn00b/include/psxgpu.h | 2 +- libpsn00b/include/psxpad.h | 328 ++++++++++++++++++--------------- libpsn00b/psxetc/_dl_resolve_wrapper.s | 6 +- libpsn00b/psxgpu/gettimimage.c | 2 +- 5 files changed, 203 insertions(+), 172 deletions(-) diff --git a/examples/io/pads/main.c b/examples/io/pads/main.c index 92beb1c..cc4ef56 100644 --- a/examples/io/pads/main.c +++ b/examples/io/pads/main.c @@ -118,16 +118,16 @@ static volatile uint8_t pad_buff[2][34]; static volatile size_t pad_buff_len[2]; static volatile uint32_t pad_digital_only[2] = { 0, 0 }; -// Just a wrapper around spi_new_request(). This does not send the command +// Just a wrapper around SPI_CreateRequest(). This does not send the command // immediately but adds it to the driver's request queue. void send_pad_cmd( - uint32_t port, - PAD_COMMAND cmd, - uint8_t arg1, - uint8_t arg2, - SPICALLBACK callback + uint32_t port, + PadCommand cmd, + uint8_t arg1, + uint8_t arg2, + SPI_Callback callback ) { - SPIREQUEST *req = spi_new_request(); + SPI_request *req = SPI_CreateRequest(); req->len = 9; req->port = port; @@ -150,12 +150,12 @@ void send_pad_cmd( // actually a DualShock in digital mode by checking if it started identifying // as CONFIG_MODE after receiving a configuration command. void dualshock_init_cb(uint32_t port, const volatile uint8_t *buff, size_t rx_len) { - PADTYPE *pad = (PADTYPE *) buff; + PadResponse *pad = (PadResponse *) buff; if ( (rx_len < 2) || - (pad->raw.prefix != 0x5a) || - (pad->raw.type != PAD_ID_CONFIG_MODE) + (pad->prefix != 0x5a) || + (pad->type != PAD_ID_CONFIG_MODE) ) { printf("no, pad is digital-only (len = %d)\n", rx_len); @@ -187,7 +187,7 @@ void poll_cb(uint32_t port, const volatile uint8_t *buff, size_t rx_len) { if (rx_len) memcpy((void *) pad_buff[port], (void *) buff, rx_len); - PADTYPE *pad = (PADTYPE *) buff; + PadResponse *pad = (PadResponse *) buff; // If this pad identifies as a digital pad and hasn't been flagged as a // digital-only pad already, attempt to put it into analog mode by entering @@ -196,8 +196,8 @@ void poll_cb(uint32_t port, const volatile uint8_t *buff, size_t rx_len) { // returning digital pad responses. if ( rx_len && - (pad->raw.prefix == 0x5a) && - (pad->raw.type == PAD_ID_DIGITAL) + (pad->prefix == 0x5a) && + (pad->type == PAD_ID_DIGITAL) ) { if (!pad_digital_only[port]) { printf("Detecting if pad %d supports config mode... ", port + 1); @@ -221,7 +221,7 @@ static CONTEXT ctx; int main(int argc, const char* argv[]) { init_context(&ctx); - spi_init(&poll_cb); + SPI_Init(&poll_cb); uint32_t counter = 0; @@ -238,15 +238,14 @@ int main(int argc, const char* argv[]) { continue; } - PADTYPE *pad = (PADTYPE *) pad_buff[port]; - PAD_TYPEID type = pad->raw.type; + PadResponse *pad = (PadResponse *) pad_buff[port]; // According to nocash docs, there is a hardware bug in DualShock // controllers that causes the prefix byte (normally 0x5a) to turn // into 0x00 if the analog button is pressed after configuration // commands have been used. Thus making sure the prefix is 0x5a // isn't enough to reliably detect pads. - /*if ((pad->raw.prefix != 0x5a) && (type != PAD_ID_ANALOG)) { + /*if ((pad->prefix != 0x5a) && (pad->type != PAD_ID_ANALOG)) { FntPrint(-1, "\n\nPORT %d: INVALID RESPONSE\n", port + 1); if ((counter % 64) < 32) FntPrint(-1, " CHECK CONNECTION..."); @@ -258,8 +257,8 @@ int main(int argc, const char* argv[]) { -1, "\n\nPORT %d: %s (TYPE=%d)\n", port + 1, - PAD_TYPEIDS[type], - type + PAD_TYPEIDS[pad->type], + pad->type ); // Print a hexdump of the payload returned by the pad. diff --git a/libpsn00b/include/psxgpu.h b/libpsn00b/include/psxgpu.h index f50b841..f061219 100644 --- a/libpsn00b/include/psxgpu.h +++ b/libpsn00b/include/psxgpu.h @@ -609,7 +609,7 @@ void AddPrim(u_long* ot, void* pri); // Function definitions (C) -int GetTimInfo(u_long *tim, TIM_IMAGE *timimg); /* ORIGINAL */ +int GetTimInfo(const u_long *tim, TIM_IMAGE *timimg); /* ORIGINAL */ DISPENV *SetDefDispEnv(DISPENV *disp, int x, int y, int w, int h); DRAWENV *SetDefDrawEnv(DRAWENV *draw, int x, int y, int w, int h); diff --git a/libpsn00b/include/psxpad.h b/libpsn00b/include/psxpad.h index d152896..9638ec1 100644 --- a/libpsn00b/include/psxpad.h +++ b/libpsn00b/include/psxpad.h @@ -14,43 +14,48 @@ #ifndef _PSXPAD_H #define _PSXPAD_H -// Pad button definitions for digital pad, joystick, dual analog, -// Dualshock and Jogcon -#define PAD_SELECT 1 -#define PAD_L3 2 -#define PAD_R3 4 -#define PAD_START 8 -#define PAD_UP 16 -#define PAD_RIGHT 32 -#define PAD_DOWN 64 -#define PAD_LEFT 128 -#define PAD_L2 256 -#define PAD_R2 512 -#define PAD_L1 1024 -#define PAD_R1 2048 -#define PAD_TRIANGLE 4096 -#define PAD_CIRCLE 8192 -#define PAD_CROSS 16384 -#define PAD_SQUARE 32768 - -// Mouse button definitions -#define MOUSE_RIGHT 1024 -#define MOUSE_LEFT 2048 - -// neGcon button definitions -#define NCON_START 8 -#define NCON_UP 16 -#define NCON_RIGHT 32 -#define NCON_DOWN 64 -#define NCON_LEFT 128 -#define NCON_R 256 -#define NCON_B 512 -#define NCON_A 1024 - -// Guncon button definitions -#define GCON_A 8 -#define GCON_TRIGGER 8192 -#define GCON_B 16384 +#include + +/* Controller type and button definitions */ + +typedef enum { + // Standard pads, analog joystick, Jogcon + PAD_SELECT = 1 << 0, + PAD_L3 = 1 << 1, + PAD_R3 = 1 << 2, + PAD_START = 1 << 3, + PAD_UP = 1 << 4, + PAD_RIGHT = 1 << 5, + PAD_DOWN = 1 << 6, + PAD_LEFT = 1 << 7, + PAD_L2 = 1 << 8, + PAD_R2 = 1 << 9, + PAD_L1 = 1 << 10, + PAD_R1 = 1 << 11, + PAD_TRIANGLE = 1 << 12, + PAD_CIRCLE = 1 << 13, + PAD_CROSS = 1 << 14, + PAD_SQUARE = 1 << 15, + + // Mouse + MOUSE_LEFT = 1 << 10, + MOUSE_RIGHT = 1 << 11, + + // neGcon + NCON_START = 1 << 3, + NCON_UP = 1 << 4, + NCON_RIGHT = 1 << 5, + NCON_DOWN = 1 << 6, + NCON_LEFT = 1 << 7, + NCON_R = 1 << 8, + NCON_B = 1 << 9, + NCON_A = 1 << 10, + + // Guncon + GCON_A = 1 << 3, + GCON_TRIGGER = 1 << 13, + GCON_B = 1 << 14 +} PadButton; typedef enum { PAD_ID_MOUSE = 0x1, // Sony PS1 mouse @@ -64,9 +69,10 @@ typedef enum { PAD_ID_JOGCON = 0xe, // Namco Jogcon PAD_ID_CONFIG_MODE = 0xf, // Dual Analog/DualShock in config mode (if len == 0x3) PAD_ID_NONE = 0xf // No pad connected (if len == 0xf) -} PAD_TYPEID; +} PadTypeID; + +/* Pad and memory card commands */ -// Controller command definitions typedef enum { PAD_CMD_INIT_PRESSURE = '@', // Initialize DS2 button pressure sensors (in config mode) PAD_CMD_READ = 'B', // Read pad state and set rumble @@ -74,131 +80,155 @@ typedef enum { PAD_CMD_SET_ANALOG = 'D', // Set analog mode/LED state (in config mode) PAD_CMD_GET_ANALOG = 'E', // Get analog mode/LED state (in config mode) PAD_CMD_REQUEST_CONFIG = 'M', // Configure request/unlock vibration (in config mode) - PAD_CMD_RESPONSE_CONFIG = 'O' // Configure response/unlock DS2 pressure (in config mode) -} PAD_COMMAND; + PAD_CMD_RESPONSE_CONFIG = 'O', // Configure response/unlock DS2 pressure (in config mode) -// Memory card command/response definitions -typedef enum { - MCD_CMD_READ = 'R', // Read sector - MCD_CMD_IDENTIFY = 'S', // Retrieve ID and card size information - MCD_CMD_WRITE = 'W' // Write sector -} MCD_COMMAND; + MCD_CMD_READ_SECTOR = 'R', // Read 128-byte sector + MCD_CMD_IDENTIFY = 'S', // Retrieve ID and card size information (Sony cards only) + MCD_CMD_WRITE_SECTOR = 'W' // Erase and write 128-byte sector +} PadCommand; typedef enum { MCD_STAT_OK = 'G', MCD_STAT_BAD_CHECKSUM = 'N', MCD_STAT_BAD_SECTOR = 0xff -} MCD_STATUS; - -#define MCD_CMD_READ_LEN 139 -#define MCD_CMD_IDENTIFY_LEN 9 -#define MCD_CMD_WRITE_LEN 137 - -// Memory card status flags -#define MCD_FLAG_WRITE_ERROR 4 // Last write command failed -#define MCD_FLAG_NOT_WRITTEN 8 // No writes have been issued yet -#define MCD_FLAG_UNKNOWN 16 // Might be set on third-party cards - -// Struct for data returned by controllers -typedef struct _PADTYPE { - union { // Header: - struct __attribute__((packed)) { // When parsing data returned by BIOS: - unsigned char stat; // Status - unsigned char len:4; // Payload length / 2, 0 for multitap - unsigned char type:4; // Device type (PAD_TYPEID) +} MemCardStatus; + +typedef enum { + MCD_FLAG_WRITE_ERROR = 1 << 2, // Last write command failed + MCD_FLAG_NOT_WRITTEN = 1 << 3, // No writes have been issued yet + MCD_FLAG_UNKNOWN = 1 << 4 // Might be set on third-party cards +} MemCardStatusFlag; + +#define MEMCARD_CMD_READ_LEN 139 +#define MEMCARD_CMD_IDENTIFY_LEN 9 +#define MEMCARD_CMD_WRITE_LEN 137 + +/* Controller response as returned by BIOS driver */ + +typedef struct __attribute__((packed)) _PADTYPE { + uint8_t stat; // Status + uint8_t len:4; // Payload length / 2, 0 for multitap + uint8_t type:4; // Device type (PadTypeID) + + uint16_t btn; // Button states + union { + struct { // Analog controller: + uint8_t rs_x,rs_y; // - Right stick coordinates + uint8_t ls_x,ls_y; // - Left stick coordinates + uint8_t press[12]; // - Button pressure (DualShock 2 only) }; - struct __attribute__((packed)) { // When parsing raw controller response: - unsigned char len:4; // Payload length / 2, 0 for multitap - unsigned char type:4; // Device type (PAD_TYPEID) - unsigned char prefix; // Must be 0x5a - } raw; - }; - struct { // Payload: - unsigned short btn; // Button states - union { - struct { // Analog controller: - unsigned char rs_x,rs_y; // Right stick coordinates - unsigned char ls_x,ls_y; // Left stick coordinates - unsigned char press[12]; // Button pressure (DualShock 2 only) - }; - struct { // Mouse: - char x_mov; // X movement of mouse - char y_mov; // Y movement of mouse - }; - struct { // neGcon: - unsigned char twist; // Controller twist - unsigned char btn_i; // I button value - unsigned char btn_ii; // II button value - unsigned char trg_l; // L trigger value - }; - struct { // Jogcon: - unsigned short jog_rot; // Jog rotation - }; - struct { // Guncon: - unsigned short gun_x; // Gun X position in dotclocks - unsigned short gun_y; // Gun Y position in scanlines - }; + struct { // Mouse: + int8_t x_mov; // - X movement of mouse + int8_t y_mov; // - Y movement of mouse + }; + struct { // neGcon: + uint8_t twist; // - Controller twist + uint8_t btn_i; // - I button value + uint8_t btn_ii; // - II button value + uint8_t trg_l; // - L trigger value + }; + struct { // Jogcon: + uint16_t jog_rot; // - Jog rotation + }; + struct { // Guncon: + uint16_t gun_x; // - Gun X position in dotclocks + uint16_t gun_y; // - Gun Y position in scanlines }; }; } PADTYPE; -typedef struct _MCDRESPONSE { - unsigned char flags; // Status flags - unsigned char type1; // Must be 0x5a - unsigned char type2; // Must be 0x5d +//typedef struct _PADTYPE MOUSETYPE; +//typedef struct _PADTYPE NCONTYPE; +//typedef struct _PADTYPE JCONTYPE; +//typedef struct _PADTYPE GCONTYPE; + +/* Raw responses */ + +typedef struct __attribute__((packed)) _PadResponse { + uint8_t len:4; // Payload length / 2, 0 for multitap + uint8_t type:4; // Device type (PadTypeID) + uint8_t prefix; // Must be 0x5a + + uint16_t btn; // Button states + union { + struct { // Analog controller: + uint8_t rs_x,rs_y; // - Right stick coordinates + uint8_t ls_x,ls_y; // - Left stick coordinates + uint8_t press[12]; // - Button pressure (DualShock 2 only) + }; + struct { // Mouse: + int8_t x_mov; // - X movement of mouse + int8_t y_mov; // - Y movement of mouse + }; + struct { // neGcon: + uint8_t twist; // - Controller twist + uint8_t btn_i; // - I button value + uint8_t btn_ii; // - II button value + uint8_t trg_l; // - L trigger value + }; + struct { // Jogcon: + uint16_t jog_rot; // - Jog rotation + }; + struct { // Guncon: + uint16_t gun_x; // - Gun X position in dotclocks + uint16_t gun_y; // - Gun Y position in scanlines + }; + }; +} PadResponse; + +typedef struct __attribute__((packed)) _MemCardResponse { + uint8_t flags; // Status flags (MemCardStatusFlag) + uint8_t type1; // Must be 0x5a + uint8_t type2; // Must be 0x5d + union { - struct { // MCD_CMD_READ response: - unsigned char dummy[2]; - unsigned char ack1; // Must be 0x5c - unsigned char ack2; // Must be 0x5d - unsigned char lba_h; - unsigned char lba_l; - unsigned char data[128]; - unsigned char checksum; // = lba_h ^ lba_l ^ data - unsigned char stat; // Status (MCD_STATUS) + struct { // CMD_READ response: + uint8_t dummy[2]; + uint8_t ack1; // Must be 0x5c + uint8_t ack2; // Must be 0x5d + uint8_t lba_h; + uint8_t lba_l; + uint8_t data[128]; + uint8_t checksum; // = lba_h ^ lba_l ^ data + uint8_t stat; // Status (MemCardStatus) } read; - struct { // MCD_CMD_IDENTIFY response: - unsigned char ack1; // Must be 0x5c - unsigned char ack2; // Must be 0x5d - unsigned char size_h; // Card capacity bits 8-15 (0x04 = 128KB) - unsigned char size_l; // Card capacity bits 0-7 (0x00 = 128KB) - unsigned char blksize_h; // Sector size bits 8-15 (must be 0x00) - unsigned char blksize_l; // Sector size bits 0-7 (must be 0x80) + struct { // CMD_IDENTIFY response: + uint8_t ack1; // Must be 0x5c + uint8_t ack2; // Must be 0x5d + uint8_t size_h; // Card capacity bits 8-15 (0x04 = 128KB) + uint8_t size_l; // Card capacity bits 0-7 (0x00 = 128KB) + uint8_t blksize_h; // Sector size bits 8-15 (must be 0x00) + uint8_t blksize_l; // Sector size bits 0-7 (must be 0x80) } identify; - struct { // MCD_CMD_WRITE response: - unsigned char dummy[131]; - unsigned char ack1; // Must be 0x5c - unsigned char ack2; // Must be 0x5d - unsigned char stat; // Status (MCD_STATUS) + struct { // CMD_WRITE response: + uint8_t dummy[131]; + uint8_t ack1; // Must be 0x5c + uint8_t ack2; // Must be 0x5d + uint8_t stat; // Status (MemCardStatus) } write; }; -} MCDRESPONSE; - -//typedef PADTYPE MOUSETYPE; -//typedef PADTYPE NCONTYPE; -//typedef PADTYPE JCONTYPE; -//typedef PADTYPE GCONTYPE; - -// Structs for raw controller request -typedef struct _PADREQUEST { - unsigned char addr; // Must be 0x01 (or 02/03/04 for multitap pads) - unsigned char cmd; // Command (PAD_COMMAND) - unsigned char tap_mode; // 0x01 to enable multitap response - unsigned char motor_r; // Right motor control (on/off) - unsigned char motor_l; // Left motor control (PWM) - unsigned char dummy[4]; -} PADREQUEST; - -// Structs for raw memory card request -typedef struct _MCDREQUEST { - unsigned char addr; // Must be 0x81 (or 02/03/04 for multitap cards) - unsigned char cmd; // Command (MCD_COMMAND) - unsigned char dummy[2]; - unsigned char lba_h; // Sector address bits 8-15 (dummy for CMD_IDENTIFY) - unsigned char lba_l; // Sector address bits 0-7 (dummy for CMD_IDENTIFY) - unsigned char data[128]; // Sector payload (dummy for CMD_READ/CMD_IDENTIFY) - unsigned char checksum; // = lba_h ^ lba_l ^ data (CMD_WRITE only) - unsigned char dummy2[3]; -} MCDREQUEST; +} MemCardResponse; + +/* Raw requests */ + +typedef struct __attribute__((packed)) _PadRequest { + uint8_t addr; // Must be 0x01 (or 02/03/04 for multitap pads) + uint8_t cmd; // Command (PadCommand) + uint8_t tap_mode; // 0x01 to enable multitap response + uint8_t motor_r; // Right motor control (on/off) + uint8_t motor_l; // Left motor control (PWM) + uint8_t dummy[4]; +} PadRequest; + +typedef struct __attribute__((packed)) _MemCardRequest { + uint8_t addr; // Must be 0x81 (or 02/03/04 for multitap cards) + uint8_t cmd; // Command (MemCardCommand) + uint8_t dummy[2]; + uint8_t lba_h; // Sector address bits 8-15 (dummy for CMD_IDENTIFY) + uint8_t lba_l; // Sector address bits 0-7 (dummy for CMD_IDENTIFY) + uint8_t data[128]; // Sector payload (dummy for CMD_READ/CMD_IDENTIFY) + uint8_t checksum; // = lba_h ^ lba_l ^ data (CMD_WRITE only) + uint8_t dummy2[3]; +} MemCardRequest; #endif \ No newline at end of file diff --git a/libpsn00b/psxetc/_dl_resolve_wrapper.s b/libpsn00b/psxetc/_dl_resolve_wrapper.s index 7f1132b..01ebf3a 100644 --- a/libpsn00b/psxetc/_dl_resolve_wrapper.s +++ b/libpsn00b/psxetc/_dl_resolve_wrapper.s @@ -48,7 +48,9 @@ _dl_resolve_wrapper: jr $t0 nop -.global _dl_credits -.type _dl_credits, @object +.section .data + +.global _dl_credits +.type _dl_credits, @object _dl_credits: .asciiz "psxetc runtime dynamic linker by spicyjpeg\n" diff --git a/libpsn00b/psxgpu/gettimimage.c b/libpsn00b/psxgpu/gettimimage.c index d9cf1bf..5598e07 100644 --- a/libpsn00b/psxgpu/gettimimage.c +++ b/libpsn00b/psxgpu/gettimimage.c @@ -1,7 +1,7 @@ #include #include -int GetTimInfo(u_long *tim, TIM_IMAGE *timimg) { +int GetTimInfo(const u_long *tim, TIM_IMAGE *timimg) { u_long *rtim; -- cgit v1.2.3