aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorJohn "Lameguy" Wilbert Villamor <lameguy64@gmail.com>2022-01-18 08:31:14 +0800
committerGitHub <noreply@github.com>2022-01-18 08:31:14 +0800
commit05d44488bd5587786f4bd0286fc0f555c79aa46a (patch)
tree5740f396d10a9580c3a39ca536544436898ff1b6
parent08de895e8582dbc70b639ae5f511ab9ebfb4d68a (diff)
parente9475e283a82665fe6c19bebc3318b5084f15a2e (diff)
downloadpsn00bsdk-05d44488bd5587786f4bd0286fc0f555c79aa46a.tar.gz
Merge pull request #44 from spicyjpeg/actions
GitHub Actions CI, psxcd and libc fixes, new examples
-rw-r--r--.github/scripts/generate_release_notes.py178
-rw-r--r--.github/workflows/build.yml245
-rw-r--r--CHANGELOG.md704
-rw-r--r--CMakeLists.txt52
-rw-r--r--CMakePresets.json14
-rw-r--r--README.md56
-rw-r--r--changelog.txt626
-rw-r--r--cpack/setup.cmake17
-rw-r--r--cpack/welcome.txt3
-rw-r--r--doc/cmake_reference.md77
-rw-r--r--doc/dev notes.txt249
-rw-r--r--doc/dev_notes.md280
-rw-r--r--doc/installation.md (renamed from INSTALL.md)63
-rw-r--r--doc/known_bugs.md52
-rw-r--r--doc/toolchain.md (renamed from TOOLCHAIN.md)20
-rw-r--r--examples/README.md82
-rw-r--r--examples/beginner/cppdemo/CMakeLists.txt4
-rw-r--r--examples/beginner/hello/CMakeLists.txt4
-rw-r--r--examples/cdrom/cdbrowse/CMakeLists.txt4
-rw-r--r--examples/cdrom/cdxa/CMakeLists.txt4
-rw-r--r--examples/cdrom/cdxa/main.c2
-rw-r--r--examples/demos/n00bdemo/CMakeLists.txt4
-rw-r--r--examples/graphics/balls/CMakeLists.txt4
-rw-r--r--examples/graphics/billboard/CMakeLists.txt4
-rw-r--r--examples/graphics/fpscam/CMakeLists.txt4
-rw-r--r--examples/graphics/gte/CMakeLists.txt4
-rw-r--r--examples/graphics/hdtv/CMakeLists.txt4
-rw-r--r--examples/graphics/render2tex/CMakeLists.txt4
-rw-r--r--examples/graphics/rgb24/CMakeLists.txt4
-rw-r--r--examples/graphics/tilesasm/CMakeLists.txt12
-rw-r--r--examples/graphics/tilesasm/data.s.template (renamed from examples/graphics/tilesasm/data.s)69
-rw-r--r--examples/io/pads/CMakeLists.txt4
-rw-r--r--examples/io/pads/main.c37
-rw-r--r--examples/io/pads/spi.c50
-rw-r--r--examples/io/pads/spi.h25
-rw-r--r--examples/io/system573/CMakeLists.txt23
-rw-r--r--examples/io/system573/iso.xml34
-rw-r--r--examples/io/system573/main.c371
-rw-r--r--examples/lowlevel/cartrom/CMakeLists.txt4
-rw-r--r--examples/readme.txt64
-rw-r--r--examples/sound/spustream/CMakeLists.txt25
-rw-r--r--examples/sound/spustream/convert_stream.py112
-rw-r--r--examples/sound/spustream/iso.xml29
-rw-r--r--examples/sound/spustream/main.c456
-rw-r--r--examples/sound/spustream/system.cnf4
-rw-r--r--examples/sound/vagsample/0proyt.h2
-rw-r--r--examples/sound/vagsample/CMakeLists.txt4
-rw-r--r--examples/sound/vagsample/threedeeffeggzz.h2
-rw-r--r--examples/system/childexec/CMakeLists.txt4
-rw-r--r--examples/system/console/CMakeLists.txt4
-rw-r--r--examples/system/dynlink/CMakeLists.txt4
-rw-r--r--examples/system/dynlink/display.c74
-rw-r--r--examples/system/dynlink/main.c148
-rw-r--r--examples/system/timer/CMakeLists.txt4
-rw-r--r--examples/system/tty/CMakeLists.txt4
-rw-r--r--libpsn00b/CMakeLists.txt20
-rw-r--r--libpsn00b/build.json.template8
-rw-r--r--libpsn00b/cmake/flags.cmake151
-rw-r--r--libpsn00b/cmake/internal_setup.cmake23
-rw-r--r--libpsn00b/cmake/sdk.cmake10
-rw-r--r--libpsn00b/cmake/virtual_targets.cmake109
-rw-r--r--libpsn00b/include/dlfcn.h101
-rw-r--r--libpsn00b/include/psxcd.h6
-rw-r--r--libpsn00b/include/psxgpu.h2
-rw-r--r--libpsn00b/include/psxpad.h328
-rw-r--r--libpsn00b/include/psxspu.h4
-rw-r--r--libpsn00b/ldscripts/dll.ld4
-rw-r--r--libpsn00b/ldscripts/exe.ld2
-rw-r--r--libpsn00b/libc/scanf.c7
-rw-r--r--libpsn00b/libc/string.c14
-rw-r--r--libpsn00b/libc/vsprintf.c11
-rw-r--r--libpsn00b/psxcd/cdgetsector.s3
-rw-r--r--libpsn00b/psxcd/isofs.c30
-rw-r--r--libpsn00b/psxcd/psxcd.c16
-rw-r--r--libpsn00b/psxetc/_dl_resolve_wrapper.s14
-rw-r--r--libpsn00b/psxetc/dl.c51
-rw-r--r--libpsn00b/psxgpu/gettimimage.c2
-rw-r--r--template/CMakeLists.txt9
-rw-r--r--template/CMakePresets.json23
79 files changed, 3568 insertions, 1717 deletions
diff --git a/.github/scripts/generate_release_notes.py b/.github/scripts/generate_release_notes.py
new file mode 100644
index 0000000..e3fbc7f
--- /dev/null
+++ b/.github/scripts/generate_release_notes.py
@@ -0,0 +1,178 @@
+#!/usr/bin/env python3
+# PSn00bSDK release notes generator
+# (C) 2021 spicyjpeg - MPL licensed
+
+import sys, re
+from time import gmtime, strptime, struct_time
+from argparse import ArgumentParser, FileType
+
+## Helpers
+
+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):
+ return VERSION_REGEX.match(version.lower()).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())
+
+ # 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."
+ )
+ parser.add_argument(
+ "changelog",
+ type = FileType("rt"),
+ help = "Markdown changelog file to parse"
+ )
+ parser.add_argument(
+ "-o", "--output",
+ type = FileType("wt"),
+ default = sys.stdout,
+ help = "where to output release notes (stdout by default)",
+ metavar = "file"
+ )
+ parser.add_argument(
+ "-v", "--version",
+ action = "append",
+ type = str,
+ help = "ignore all changes not belonging to a version (can be specified multiple times)",
+ metavar = "name"
+ )
+ parser.add_argument(
+ "-f", "--from-date",
+ type = parse_date,
+ default = parse_date("2000-01-01"),
+ help = "ignore all changes before date",
+ metavar = "yyyy-mm-dd"
+ )
+ parser.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..a72f5d3
--- /dev/null
+++ b/.github/workflows/build.yml
@@ -0,0 +1,245 @@
+# 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
+
+ - 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
+
+ # 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/*.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
+ 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
+
+ - 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/*.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.
+ create-release:
+ name: Create release
+ runs-on: ubuntu-latest
+ 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: Publish release
+ if: ${{ github.ref_type == 'tag' }}
+ uses: softprops/action-gh-release@v1
+ with:
+ fail_on_unmatched_files: true
+ body_path: release.md
+ files: |
+ *.zip
+ psn00bsdk-windows/*
+ psn00bsdk-windows-nsis/*
+ psn00bsdk-linux/*
+ psn00bsdk-linux-deb/*
+ psn00bsdk-linux-rpm/*
diff --git a/CHANGELOG.md b/CHANGELOG.md
new file mode 100644
index 0000000..0203265
--- /dev/null
+++ b/CHANGELOG.md
@@ -0,0 +1,704 @@
+
+# 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:
+
+```
+## <year>-<month>-<day>: [optional new version]
+
+<contributor>:
+
+- ...
+
+- ...
+```
+
+You may run `.github/scripts/generate_release_notes.py CHANGELOG.md` afterwards
+to ensure the changelog can be parsed correctly.
+
+-------------------------------------------------------------------------------
+
+## 2022-01-17
+
+Lameguy64:
+
+- docs: Removed old and incomplete `libn00bref.odt` document (a percussor of
+ the LibPSn00b Library Reference document) as it got included into a commit by
+ accident at some point.
+
+- examples: Improved description of `hdtv` example. Examples directory is now
+ copied into `share/psn00bsdk` directory for both installation and package
+ building. Build instructions for examples also included.
+
+- docs: Removed documentation for `SetDrawTPageVal()` as the function was
+ removed ages ago. Added documentation to `DR_AREA`, `DR_TWIN` and `DR_OFFSET`
+ primitives and their associated macros.
+
+- examples: Added `tilesasm` example.
+
+- Updated readme file.
+
+## 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:
+
+- 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 (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, as well as
+ paths containing both slash types, 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.
+
+- 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
+
+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/CMakeLists.txt b/CMakeLists.txt
index 3d974a5..2079fd1 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"
)
@@ -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,16 @@ set(
SKIP_EXAMPLES OFF
CACHE BOOL "Skip building SDK examples (not required for installation)"
)
+set(
+ 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
@@ -46,22 +56,34 @@ 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_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
)
+# 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)
@@ -71,26 +93,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}
@@ -102,6 +124,7 @@ foreach(
_subdir IN ITEMS
${CMAKE_INSTALL_BINDIR}
${CMAKE_INSTALL_LIBDIR}
+ ${CMAKE_INSTALL_INCLUDEDIR}
${CMAKE_INSTALL_DATADIR}
)
install(
@@ -114,10 +137,15 @@ foreach(
endforeach()
install(
- DIRECTORY doc template examples
+ DIRECTORY doc template
DESTINATION ${CMAKE_INSTALL_DATADIR}/psn00bsdk
COMPONENT docs
)
+install(
+ DIRECTORY examples
+ DESTINATION ${CMAKE_INSTALL_DATADIR}/psn00bsdk
+ COMPONENT examples
+)
## CPack configuration
diff --git a/CMakePresets.json b/CMakePresets.json
index f550e82..d71c1ae 100644
--- a/CMakePresets.json
+++ b/CMakePresets.json
@@ -24,6 +24,20 @@
"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",
+ "PSN00BSDK_TARGET": "$env{GCC_TARGET}",
+ "PSN00BSDK_GIT_TAG": "$env{GITHUB_REF_NAME}",
+ "PSN00BSDK_GIT_COMMIT": "$env{GITHUB_SHA}"
+ }
}
]
}
diff --git a/README.md b/README.md
index 8363799..f3fa7f5 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
@@ -7,7 +8,7 @@ projects as far as what the SDK currently supports. Out of all the open
source PS1 SDK projects that have come and gone from active development
over the years, PSn00bSDK is arguably the most capable of them all.
-Much of the SDK is merely just a set of libraries (libpsn00b) and some
+Much of the SDK is merely just a set of libraries (`libpsn00b`) and some
utilities for converting executables and data files to formats more usable
on the target platform. The compiler used is just the standard GNU GCC
toolchain compiled to target mipsel and has to be acquired separately.
@@ -26,16 +27,15 @@ 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 more so functions that
+Most of `libpsn00b` is written mostly in MIPS assembly more so 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
@@ -80,29 +80,26 @@ 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
-[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
-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. Much of the tutorials should apply for 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
@@ -124,19 +121,30 @@ for the PlayStation. Much of the tutorials should apply for PSn00bSDK.
* Pad and memory card libraries that don't use the BIOS routines.
-
## Credits
Main developer/author/whatever:
-* Lameguy64 (John "Lameguy" Wilbert Villamor)
+
+* **Lameguy64** (John "Lameguy" Wilbert Villamor)
+
+Contributors:
+
+* **spicyjpeg**: dynamic linker, CMake scripts, some docs and examples
+ (`system/dynlink`, `sound/spustream`, `io/pads`, `io/system573`).
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)
+ and Nicolas Noble's [updated version](https://psx-spx.consoledev.net).
+* MIPS and System V ABI specs (for the dynamic linker).
* Tails92's PSXSDK project (during PSn00bSDK's infancy).
+
+Additional references can be found in individual source files.
diff --git a/changelog.txt b/changelog.txt
deleted file mode 100644
index cb4b2e8..0000000
--- a/changelog.txt
+++ /dev/null
@@ -1,626 +0,0 @@
-PSn00bSDK changelog
-
-Items that are lower in the log are more recently implemented.
-
-01-17-2022 by Lameguy64:
-
-* docs: Removed old and incomplete libn00bref.odt document (a percussor of the
- LibPSn00b Library Reference document) as it got included into a commit by
- accident at some point.
-
-* examples: Improved description of hdtv example.
-
-* Examples directory is now copied into share/psn00bsdk directory for both
- installation and package building. Build instructions for examples also
- included.
-
-* docs: Removed documentation for SetDrawTPageVal() as the function was removed
- ages ago. Added documentation to DR_AREA, DR_TWIN and DR_OFFSET primitives
- and their associated macros.
-
-* examples: Added tilesasm example.
-
-* Updated readme file.
-
-
-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.
-
-* psxgpu: 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().
diff --git a/cpack/setup.cmake b/cpack/setup.cmake
index 33f127e..a5d71ff 100644
--- a/cpack/setup.cmake
+++ b/cpack/setup.cmake
@@ -14,14 +14,14 @@ 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
-# "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)
@@ -134,6 +133,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 +166,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/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 3b586ab..3c89da3 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.
@@ -182,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`
@@ -204,4 +233,4 @@ the build script.
LZP archives as part of the build pipeline.
-----------------------------------------
-_Last updated on 2021-09-27 by spicyjpeg_
+_Last updated on 2021-12-29 by spicyjpeg_
diff --git a/doc/dev notes.txt b/doc/dev notes.txt
deleted file mode 100644
index 3aa2db5..0000000
--- a/doc/dev notes.txt
+++ /dev/null
@@ -1,249 +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 (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.
-
-* 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/INSTALL.md b/doc/installation.md
index 5ae56f5..1646653 100644
--- a/INSTALL.md
+++ b/doc/installation.md
@@ -17,8 +17,8 @@ and installed properly.
[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:
+ 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
@@ -36,34 +36,33 @@ and installed properly.
- `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.
+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-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).
+ `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-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`
+ - `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
+ git submodule update --init --recursive
```
5. Compile the libraries, tools and examples using CMake:
@@ -75,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 <INSTALL_PATH>` to the first command. Add
- `-DPSN00BSDK_TARGET=mipsel-none-elf` if your toolchain targets
- `mipsel-none-elf` rather than `mipsel-unknown-elf`.
+ `--install-prefix <INSTALL_PATH>` 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
@@ -97,9 +95,9 @@ and installed properly.
- `<INSTALL_PATH>/lib/libpsn00b`
- `<INSTALL_PATH>/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)
@@ -133,24 +131,23 @@ far from being feature-complete.
1. Copy the contents of `<INSTALL_PATH>/share/psn00bsdk/template` (or the
`template` folder within the repo) to your new project's root directory.
-2. Configure and build the template by running:
+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=<INSTALL_PATH>/lib/libpsn00b/cmake/sdk.cmake` to the
-CMake command line.
-
The toolchain script defines a few CMake macros to create PS1 executables, DLLs
-and CD images. See the [reference](doc/cmake_reference.md) for details.
+and CD images. See the [reference](cmake_reference.md) for details.
-----------------------------------------
-_Last updated on 2021-11-19 by spicyjpeg_
+_Last updated on 2021-12-29 by spicyjpeg_
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/TOOLCHAIN.md b/doc/toolchain.md
index 2a78ccb..8e28c24 100644
--- a/TOOLCHAIN.md
+++ b/doc/toolchain.md
@@ -77,12 +77,12 @@ for a compatibility table.
```bash
../binutils-<VERSION>/configure \
- --prefix=/usr/local/mipsel-unknown-elf --target=mipsel-unknown-elf \
+ --prefix=/usr/local/mipsel-none-elf --target=mipsel-none-elf \
--disable-docs --disable-nls --with-float=soft
```
Replace `<VERSION>` as usual. If you don't want to install the toolchain into
- `/usr/local/mipsel-unknown-elf` you can change the `--prefix` option.
+ `/usr/local/mipsel-none-elf` you can change the `--prefix` option.
3. Compile and install binutils (this will take a few minutes to finish):
@@ -116,7 +116,7 @@ options.
```bash
../gcc-<VERSION>/configure \
- --prefix=/usr/local/mipsel-unknown-elf --target=mipsel-unknown-elf \
+ --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
@@ -144,7 +144,7 @@ options.
earlier, but keep the `/bin` at the end):
```bash
- export PATH=$PATH:/usr/local/mipsel-unknown-elf/bin
+ export PATH=$PATH:/usr/local/mipsel-none-elf/bin
```
Restart the shell by closing and reopening the terminal window or SSH
@@ -171,7 +171,7 @@ that runs on Windows.
```bash
../binutils-<VERSION>/configure \
--build=x86_64-linux-gnu --host=x86_64-w64-mingw32 \
- --prefix=/tmp/mipsel-unknown-elf --target=mipsel-unknown-elf \
+ --prefix=/tmp/mipsel-none-elf --target=mipsel-none-elf \
--disable-docs --disable-nls --with-float=soft
```
@@ -187,7 +187,7 @@ that runs on Windows.
```bash
../gcc-<VERSION>/configure \
--build=x86_64-linux-gnu --host=x86_64-w64-mingw32 \
- --prefix=/tmp/mipsel-unknown-elf --target=mipsel-unknown-elf \
+ --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
@@ -200,13 +200,13 @@ that runs on Windows.
make install-strip
```
-5. Copy the entire `/tmp/mipsel-unknown-elf` directory over to your Windows
+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-unknown-elf` or `C:\mipsel-unknown-elf`.
+ `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-unknown-elf` to 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
@@ -221,4 +221,4 @@ implemented due to bloat concerns that it may introduce. Besides, the official
SDK lacks full C++ support as well.
-----------------------------------------
-_Last updated on 2021-10-31 by spicyjpeg_
+_Last updated on 2021-11-23 by spicyjpeg_
diff --git a/examples/README.md b/examples/README.md
new file mode 100644
index 0000000..dfea465
--- /dev/null
+++ b/examples/README.md
@@ -0,0 +1,82 @@
+
+# PSn00bSDK Example Programs
+
+## Examples summary
+
+The following list is a brief summary of all the example programs included.
+Additional information may be found in the source code of each example.
+
+| Path | Description | Type | Notes |
+| :--------------------------------------------- | :---------------------------------------------------- | :--: | :---: |
+| [`beginner/cppdemo`](./beginner/cppdemo) | Simple demonstration of (dynamic) C++ classes | EXE | |
+| [`beginner/hello`](./beginner/hello) | The obligatory "Hello World" example program | EXE | |
+| [`cdrom/cdbrowse`](./cdrom/cdbrowse) | File browser using libpsxcd's directory functions | CD | |
+| [`cdrom/cdxa`](./cdrom/cdxa) | CD-XA ADPCM audio player | CD | 1 |
+| [`demos/n00bdemo`](./demos/n00bdemo) | The premiere demonstration program of PSn00bSDK | EXE | |
+| [`graphics/balls`](./graphics/balls) | Draws colored balls bouncing around the screen | EXE | |
+| [`graphics/billboard`](./graphics/billboard) | Demonstrates how to draw 2D sprites in a 3D space | EXE | |
+| [`graphics/fpscam`](./graphics/fpscam) | First-person perspective camera with look-at | EXE | |
+| [`graphics/gte`](./graphics/gte) | Displays a rotating cube using GTE macros | EXE | |
+| [`graphics/hdtv`](./graphics/hdtv) | Demonstrates anamorphic widescreen at 704x480 | EXE | |
+| [`graphics/render2tex`](./graphics/render2tex) | Procedural texture effects using off-screen drawing | EXE | |
+| [`graphics/rgb24`](./graphics/rgb24) | Displays a 640x480 24-bit RGB image | EXE | |
+| [`graphics/tilesasm`](./graphics/tilesasm) | Drawing a tile-map with assembly language | EXE | |
+| [`io/pads`](./io/pads) | Demonstrates reading controllers via low-level access | EXE | |
+| [`io/system573`](./io/system573) | Konami System 573 (PS1-based arcade board) example | CD | |
+| [`lowlevel/cartrom`](./lowlevel/cartrom) | ROM firmware for cheat devices written using GNU GAS | ROM | 2 |
+| [`sound/spustream`](./sound/spustream) | Custom (non XA) CD-ROM audio streaming using the SPU | CD | 1 |
+| [`sound/vagsample`](./sound/vagsample) | Demonstrates playing VAG sound files with the SPU | EXE | |
+| [`system/childexec`](./system/childexec) | Loading a child program and returning to parent | EXE | |
+| [`system/console`](./system/console) | TTY based text console that interrupts gameplay | EXE | |
+| [`system/dynlink`](./system/dynlink) | Demonstrates dynamically linked libraries | CD | |
+| [`system/timer`](./system/timer) | Demonstrates using hardware timers with interrupts | EXE | |
+| [`system/tty`](./system/tty) | Using TTY as a remote text console interface | EXE | |
+
+Notes:
+
+1. `cdrom/cdxa` and `sound/spustream` do not come with example audio files. In
+ order to run these examples you'll have to provide your own files (and, in
+ the case of `spustream`, convert them using the included Python script) and
+ build the CD image manually.
+2. The `lowlevel/cartrom` example is outdated and does not use SDK libraries.
+ It is kept for reference purposes only.
+
+## Building the examples
+
+The instructions below assume that PSn00bSDK, CMake 3.20+ and a GCC toolchain
+are already installed. Refer to the [installation guide](../doc/installation.md)
+for details.
+
+**NOTE**: all examples are compiled by default when building the PSn00bSDK
+libraries and tools (check the `build/examples` directory). These instructions
+are for rebuilding the examples *after* the SDK has been installed.
+
+1. Copy the contents of this directory (`share/psn00bsdk/examples` within the
+ PSn00bSDK installation directory) to your home directory or to another
+ folder you have write access to.
+
+2. Configure and build the examples by running:
+
+ ```bash
+ cmake -S . -B ./build -G "Ninja" -DCMAKE_TOOLCHAIN_FILE=<INSTALL_PATH>/lib/libpsn00b/cmake/sdk.cmake
+ cmake --build ./build
+ ```
+
+ Replace `<INSTALL_PATH>` with the installation prefix you chose when
+ installing the SDK (usually `C:\Program Files\PSn00bSDK` or `/usr/local`,
+ so the full path to `sdk.cmake` would be
+ `C:\Program Files\PSn00bSDK\lib\libpsn00b\cmake\sdk.cmake` or
+ `/usr/local/lib/libpsn00b/cmake/sdk.cmake` respectively).
+
+ Add `-DPSN00BSDK_TARGET=mipsel-unknown-elf` to the first command if your
+ toolchain targets `mipsel-unknown-elf` rather than `mipsel-none-elf`. If you
+ can't get Ninja to work or don't have it installed, you can also replace
+ `-G "Ninja"` with `-G "Unix Makefiles"` (`-G "MSYS Makefiles"` on Windows)
+ to build using `make` instead.
+
+ This should create a `build` directory whose structure mirrors the one of
+ the parent directory, with each subfolder containing built executables and
+ CD images for each example.
+
+-----------------------------------------
+_Last updated on 2022-01-17 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/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/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/graphics/tilesasm/CMakeLists.txt b/examples/graphics/tilesasm/CMakeLists.txt
index 59ef665..3384875 100644
--- a/examples/graphics/tilesasm/CMakeLists.txt
+++ b/examples/graphics/tilesasm/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(
tilesasm
LANGUAGES C ASM
@@ -15,8 +11,14 @@ project(
HOMEPAGE_URL "http://lameguy64.net/?page=psn00bsdk"
)
+configure_file(data.s.template data.s)
+
file(GLOB _sources *.s *.c)
-psn00bsdk_add_executable(tilesasm STATIC ${_sources})
+psn00bsdk_add_executable(
+ tilesasm STATIC
+ ${_sources}
+ ${PROJECT_BINARY_DIR}/data.s
+)
#psn00bsdk_add_cd_image(tilesasm_iso tilesasm iso.xml DEPENDS tilesasm)
install(FILES ${PROJECT_BINARY_DIR}/tilesasm.exe TYPE BIN)
diff --git a/examples/graphics/tilesasm/data.s b/examples/graphics/tilesasm/data.s.template
index c64ebbc..1c4b01e 100644
--- a/examples/graphics/tilesasm/data.s
+++ b/examples/graphics/tilesasm/data.s.template
@@ -1,35 +1,34 @@
-#
-# LibPSn00b Example Programs
-#
-# Drawing Tile-maps with Assembler Routines
-# 2022 Meido-Tek Productions / PSn00bSDK Project
-#
-# Example by John "Lameguy" Wilbert Villamor (Lameguy64)
-#
-# This assembler file is used to include the file tiles.tim as an array named
-# 'tim_tileset' for use in this example program. Note how the variable name
-# itself is leading with an underscore (_) in this file. This is because
-# GNU C requires leading underscores for global variables, perhaps to prevent
-# function names and variable names from mixing up during the linking stage.
-
-# Tell assembler that the contents that follow must be in the .data section
-.section .data
-
-# This directive define the 'tim_tileset' label as a global symbol so that
-# main.c and other program modules can see this symbol during linking
-.global tim_tileset
-
-# This directive is not really required, but its best to define symbols
-# not pointing to program code as an object to help identify it as a
-# variable in debuggers
-.type tim_tileset, @object
-
-# The following line defines the variable 'tim_tileset' itself filled with the
-# contents of the file 'tiles.tim' by using the .incbin directive
-#
-# Remember the variable type of a symbol is always governed by how it is
-# declared in the C code
-#
-tim_tileset:
- .incbin "../tiles_256.tim"
- \ No newline at end of file
+#
+# LibPSn00b Example Programs
+#
+# Drawing Tile-maps with Assembler Routines
+# 2022 Meido-Tek Productions / PSn00bSDK Project
+#
+# Example by John "Lameguy" Wilbert Villamor (Lameguy64)
+#
+# This assembler file is used to include the file tiles.tim as an array named
+# 'tim_tileset' for use in this example program. Note how the variable name
+# itself is leading with an underscore (_) in this file. This is because
+# GNU C requires leading underscores for global variables, perhaps to prevent
+# function names and variable names from mixing up during the linking stage.
+
+# Tell assembler that the contents that follow must be in the .data section
+.section .data
+
+# This directive define the 'tim_tileset' label as a global symbol so that
+# main.c and other program modules can see this symbol during linking
+.global tim_tileset
+
+# This directive is not really required, but its best to define symbols
+# not pointing to program code as an object to help identify it as a
+# variable in debuggers
+.type tim_tileset, @object
+
+# The following line defines the variable 'tim_tileset' itself filled with the
+# contents of the file 'tiles.tim' by using the .incbin directive
+#
+# Remember the variable type of a symbol is always governed by how it is
+# declared in the C code
+#
+tim_tileset:
+ .incbin "${PROJECT_SOURCE_DIR}/tiles_256.tim"
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/pads/main.c b/examples/io/pads/main.c
index 92beb1c..d100482 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/examples/io/pads/spi.c b/examples/io/pads/spi.c
index e01b3f6..ef75ffc 100644
--- a/examples/io/pads/spi.c
+++ b/examples/io/pads/spi.c
@@ -54,21 +54,21 @@
/* Internal structures and globals */
-typedef struct _SPICONTEXT {
- uint8_t tx_buff[SPI_BUFF_LEN];
- uint8_t rx_buff[SPI_BUFF_LEN];
- uint32_t tx_len, rx_len, port;
- SPICALLBACK callback;
-} SPICONTEXT;
+typedef struct _SPI_CONTEXT {
+ uint8_t tx_buff[SPI_BUFF_LEN];
+ uint8_t rx_buff[SPI_BUFF_LEN];
+ uint32_t tx_len, rx_len, port;
+ SPI_Callback callback;
+} SPI_Context;
-static volatile SPICONTEXT ctx;
-static volatile SPIREQUEST volatile *current_req;
-static SPICALLBACK default_cb;
+static volatile SPI_Context ctx;
+static volatile SPI_Request volatile *current_req;
+static SPI_Callback default_cb;
/* Request queue management */
-static void prepare_poll_req(void) {
- PADREQUEST *req = (PADREQUEST *) ctx.tx_buff;
+static void _spi_create_poll_req(void) {
+ PadRequest *req = (PadRequest *) ctx.tx_buff;
req->addr = 0x01;
req->cmd = PAD_CMD_READ;
@@ -82,7 +82,7 @@ static void prepare_poll_req(void) {
ctx.callback = default_cb;
}
-static void prepare_next_req(void) {
+static void _spi_next_req(void) {
// Copy the contents of the first request in the queue into the TX buffer.
memcpy((void *) ctx.tx_buff, (void *) current_req->data, current_req->len);
@@ -93,7 +93,7 @@ static void prepare_next_req(void) {
// Pop the first request from the queue by deallocating it and adjusting
// the pointer to the first queue item.
- SPIREQUEST *next = current_req->next;
+ SPI_Request *next = current_req->next;
free((void *) current_req);
current_req = next;
@@ -101,7 +101,7 @@ static void prepare_next_req(void) {
/* Interrupt handlers */
-static void poll_timer_tick(void) {
+static void _spi_poll_irq_handler(void) {
// Fetch the last response byte, which wasn't followed by a pulse on /ACK,
// from the RX FIFO.
if (JOY_STAT & 0x0002)
@@ -112,9 +112,9 @@ static void poll_timer_tick(void) {
// If the request queue is empty, create a pad polling request.
if (current_req)
- prepare_next_req();
+ _spi_next_req();
else
- prepare_poll_req();
+ _spi_create_poll_req();
// Prepare the SPI port by clearing any pending IRQ, pulling /CS high and
// enabling the /ACK IRQ. In order to communicate with controllers, /CS has
@@ -132,7 +132,7 @@ static void poll_timer_tick(void) {
JOY_TXRX = ctx.tx_buff[0];
}
-static void spi_ack_handler(void) {
+static void _spi_ack_irq_handler(void) {
// Wait until /ACK is pulled up by the controller before sending the next
// byte. According to nocash docs, this has to be done before resetting the
// IRQ.
@@ -166,8 +166,8 @@ static void spi_ack_handler(void) {
/* Public API */
-SPIREQUEST *spi_new_request(void) {
- SPIREQUEST *req = malloc(sizeof(SPIREQUEST));
+SPI_Request *SPI_CreateRequest(void) {
+ SPI_Request *req = malloc(sizeof(SPI_Request));
req->len = 0;
req->port = 0;
@@ -179,7 +179,7 @@ SPIREQUEST *spi_new_request(void) {
if (!current_req) {
current_req = req;
} else {
- volatile SPIREQUEST *volatile last = current_req;
+ volatile SPI_Request *volatile last = current_req;
while (last->next)
last = last->next;
@@ -189,7 +189,7 @@ SPIREQUEST *spi_new_request(void) {
return req;
}
-void spi_set_poll_rate(uint32_t value) {
+void SPI_SetPollRate(uint32_t value) {
TIM_CTRL(2) = 0x0258; // CLK/8 input, IRQ on reload, disable one-shot IRQ
if (value < 65)
@@ -198,21 +198,21 @@ void spi_set_poll_rate(uint32_t value) {
TIM_RELOAD(2) = (F_CPU / 8) / value;
}
-void spi_init(SPICALLBACK callback) {
+void SPI_Init(SPI_Callback callback) {
// Disable the BIOS timer handler (which for some stupid reason is enabled
// by default, even though it does nothing) and set up custom interrupt
// handlers.
EnterCriticalSection();
ChangeClearRCnt(2, 0);
- InterruptCallback(6, &poll_timer_tick);
- InterruptCallback(7, &spi_ack_handler);
+ InterruptCallback(6, &_spi_poll_irq_handler);
+ InterruptCallback(7, &_spi_ack_irq_handler);
ExitCriticalSection();
JOY_CTRL = 0x0040; // Reset all registers
JOY_MODE = 0x000d; // 1x multiplier, 8 data bits, no parity
JOY_BAUD = 0x0088; // 250000 bps
- spi_set_poll_rate(250);
+ SPI_SetPollRate(250);
current_req = 0;
default_cb = callback;
}
diff --git a/examples/io/pads/spi.h b/examples/io/pads/spi.h
index 1c473cd..c50e065 100644
--- a/examples/io/pads/spi.h
+++ b/examples/io/pads/spi.h
@@ -9,23 +9,24 @@
#include <stdint.h>
#include <psxpad.h>
+// Maximum request/response length (34 bytes for pads, 140 for memory cards)
//#define SPI_BUFF_LEN 34
#define SPI_BUFF_LEN 140
/* Request structures */
-typedef void (*SPICALLBACK)(uint32_t port, const volatile uint8_t *buff, size_t rx_len);
+typedef void (*SPI_Callback)(uint32_t port, const volatile uint8_t *buff, size_t rx_len);
-typedef struct _SPIREQUEST {
+typedef struct _SPI_Request {
union {
- uint8_t data[SPI_BUFF_LEN];
- PADREQUEST pad_req;
- MCDREQUEST mcd_req;
+ uint8_t data[SPI_BUFF_LEN];
+ PadRequest pad_req;
+ MemCardRequest mcd_req;
};
- uint32_t len, port;
- SPICALLBACK callback;
- struct _SPIREQUEST *next;
-} SPIREQUEST;
+ uint32_t len, port;
+ SPI_Callback callback;
+ struct _SPI_Request *next;
+} SPI_Request;
/* Public API */
@@ -34,7 +35,7 @@ typedef struct _SPIREQUEST {
* object must be populated afterwards by setting the length, callback and
* filling in the TX data buffer.
*/
-SPIREQUEST *spi_new_request(void);
+SPI_Request *SPI_CreateRequest(void);
/**
* @brief Changes the controller polling rate. The lowest supported rate is 65
@@ -43,7 +44,7 @@ SPIREQUEST *spi_new_request(void);
*
* @param value
*/
-void spi_set_poll_rate(uint32_t value);
+void SPI_SetPollRate(uint32_t value);
/**
* @brief Installs the SPI and timer 2 interrupt handlers and starts the poll
@@ -56,6 +57,6 @@ void spi_set_poll_rate(uint32_t value);
*
* @param callback
*/
-void spi_init(SPICALLBACK callback);
+void SPI_Init(SPI_Callback callback);
#endif
diff --git a/examples/io/system573/CMakeLists.txt b/examples/io/system573/CMakeLists.txt
new file mode 100644
index 0000000..1c74347
--- /dev/null
+++ b/examples/io/system573/CMakeLists.txt
@@ -0,0 +1,23 @@
+# PSn00bSDK example CMake script
+# (C) 2021 spicyjpeg - MPL licensed
+
+cmake_minimum_required(VERSION 3.21)
+
+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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<iso_project
+ image_name="${CD_IMAGE_NAME}.bin"
+ cue_sheet="${CD_IMAGE_NAME}.cue"
+>
+ <track type="data">
+ <identifiers
+ system ="PLAYSTATION"
+ volume ="SYSTEM573"
+ volume_set ="SYSTEM573"
+ publisher ="MEIDOTEK"
+ data_preparer ="PSN00BSDK ${PSN00BSDK_VERSION}"
+ application ="PLAYSTATION"
+ copyright ="README.TXT;1"
+ />
+
+ <directory_tree>
+ <!--
+ The System 573 BIOS does not parse SYSTEM.CNF, it always looks
+ for an executable named PSX.EXE. Note that this behavior can be
+ abused to make multi-system CDs with different executables for
+ PS1 and 573 (i.e. have both PSX.EXE and SYSTEM.CNF pointing to
+ a different executable).
+ -->
+ <!--<file name="SYSTEM.CNF" type="data" source="${PROJECT_SOURCE_DIR}/system.cnf" />-->
+ <file name="PSX.EXE" type="data" source="system573.exe" />
+ <file name="PSX.MAP" type="data" source="system573.map" />
+
+ <dummy sectors="1024"/>
+ </directory_tree>
+ </track>
+
+ <!--<track type="audio" source="track2.wav" />-->
+</iso_project>
diff --git a/examples/io/system573/main.c b/examples/io/system573/main.c
new file mode 100644
index 0000000..a06c4e5
--- /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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <psxetc.h>
+#include <psxapi.h>
+#include <psxgpu.h>
+#include <psxpad.h>
+
+/* 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;
+} JAMMAInputs;
+
+void get_jamma_inputs(JAMMAInputs *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 n*8+0 -> bit n*8+7
+ bits |= (lights & 0x02020202) << 5; // Lamp n*8+1 -> bit n*8+6
+ bits |= (lights & 0x04040404) >> 1; // Lamp n*8+2 -> bit n*8+1
+ bits |= (lights & 0x08080808) >> 3; // Lamp n*8+3 -> bit n*8+0
+ bits |= (lights & 0x10101010) << 1; // Lamp n*8+4 -> bit n*8+5
+ bits |= (lights & 0x20202020) >> 1; // Lamp n*8+5 -> bit n*8+4
+ bits |= (lights & 0x40404040) >> 3; // Lamp n*8+6 -> bit n*8+3
+ bits |= (lights & 0x80808080) >> 5; // Lamp n*8+7 -> bit n*8+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 n*4+0 -> bit n*4+0
+ bits |= (lights & 0x22222222) << 1; // Lamp n*4+1 -> bit n*4+2
+ bits |= (lights & 0x44444444) << 1; // Lamp n*4+2 -> bit n*4+3
+ bits |= (lights & 0x88888888) >> 2; // Lamp n*4+3 -> bit n*4+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++);
+
+ JAMMAInputs 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 & 0xf);
+ 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;
+}
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/readme.txt b/examples/readme.txt
deleted file mode 100644
index 80747de..0000000
--- a/examples/readme.txt
+++ /dev/null
@@ -1,64 +0,0 @@
-PSn00bSDK Example Programs
-2019 - 2022 Meido-Tek Productions / PSn00bSDK Project
-
-## Building the examples ##
-
-The instructions below assumes that a mipsel-unknown-elf or mipsel-none-elf GNU
-toolchain is installed, CMake and Ninja or make is installed and the PSn00bSDK
-libraries and tools are compiled and installed. CMake version must be at least
-version 3.20 or newer.
-
-1. If the examples are in /usr/local/share, copy the directory into your home
- directory.
-
-2. Configure the examples with CMake by running:
-
- cmake -S . -B ./build -DCMAKE_TOOLCHAIN_FILE=<path to sdk.cmake>
-
- <path to sdk.cmake> must point to the sdk.cmake file provided by the SDK.
- Unless you've installed the SDK with a custom path, normally this file is
- located in /usr/local/lib/libpsn00b/cmake on *nix style systems or
- C:\Program Files\PSn00bSDK\lib\libpsn00b\cmake in Windows.
-
- If the mipsel toolchain has a different prefix (ie. mipsel-none-elf), specify
- -DPSN00BSDK_TARGET=<prefix> to override the default toolchain prefix.
-
- If Ninja does not work for you or don't have it installed, pass
- -G "Unix Makefiles" (or -G "MSys Makefiles" on Windows) to build using make
- instead.
-
-3. Build the example programs by running:
-
- cmake --build ./build
-
- This should create a build directory with a directory structure that mirrors
- the parent directory. The directories should contain compiled versions of the
- example programs as a PS-EXE or ISO file.
-
-
-## Examples summary ##
-
-The following list is a brief summary of all the example programs included.
-Additional information may be found in the source code of each example.
-
- beginner/cppdemo Simple demonstration of (dynamic) C++ classes
- beginner/hello The obligatory "Hello World" example program
- cdrom/cdbrowse File browser using libpsxcd's directory functions
- cdrom/cdxa Plays CD-XA audio (XA audio not included)
- demos/n00bdemo The premiere demonstration program of PSn00bSDK
- graphics/balls Draws colored balls bouncing around the screen
- graphics/billboard Demonstrates how to draw 2D sprites in a 3D space
- graphics/fpscam First-person perspective camera with look-at
- graphics/gte Displays a rotating cube using GTE macros
- graphics/hdtv Demonstrates anamorphic widescreen at 704x480
- graphics/render2tex Procedural texture effects using off-screen drawing
- graphics/rgb24 Displays a 640x480 24-bit RGB image
- graphics/tilesasm Drawing a tile-map with assembly language
- io/pads Demonstrates reading controllers via low-level access
- lowlevel/cartrom ROM firmware for cheat devices written using GNU GAS
- sound/vagsample Demonstrates playing VAG sound files with the SPU
- system/childexec Loading a child program and returning to parent
- system/console TTY based text console that interrupts gameplay
- system/dynlink Demonstrates dynamically linked libraries
- system/timer Demonstrates using hardware timers with interrupts
- system/tty Using TTY as a remote text console interface \ No newline at end of file
diff --git a/examples/sound/spustream/CMakeLists.txt b/examples/sound/spustream/CMakeLists.txt
new file mode 100644
index 0000000..9e84fa3
--- /dev/null
+++ b/examples/sound/spustream/CMakeLists.txt
@@ -0,0 +1,25 @@
+# PSn00bSDK example CMake script
+# (C) 2021 spicyjpeg - MPL licensed
+
+cmake_minimum_required(VERSION 3.20)
+
+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 @@
+<?xml version="1.0" encoding="utf-8"?>
+<iso_project
+ image_name="${CD_IMAGE_NAME}.bin"
+ cue_sheet="${CD_IMAGE_NAME}.cue"
+>
+ <track type="data">
+ <identifiers
+ system ="PLAYSTATION"
+ volume ="SPUSTREAM"
+ volume_set ="SPUSTREAM"
+ publisher ="MEIDOTEK"
+ data_preparer ="PSN00BSDK ${PSN00BSDK_VERSION}"
+ application ="PLAYSTATION"
+ copyright ="README.TXT;1"
+ />
+
+ <directory_tree>
+ <file name="SYSTEM.CNF" type="data" source="${PROJECT_SOURCE_DIR}/system.cnf" />
+ <file name="SPUSTRM.EXE" type="data" source="spustream.exe" />
+ <file name="SPUSTRM.MAP" type="data" source="spustream.map" />
+
+ <file name="STREAM.BIN" type="data" source="${PROJECT_SOURCE_DIR}/stream.bin" />
+
+ <dummy sectors="1024"/>
+ </directory_tree>
+ </track>
+
+ <!--<track type="audio" source="track2.wav" />-->
+</iso_project>
diff --git a/examples/sound/spustream/main.c b/examples/sound/spustream/main.c
new file mode 100644
index 0000000..be095cb
--- /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 <stdint.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <psxetc.h>
+#include <psxapi.h>
+#include <psxgpu.h>
+#include <psxpad.h>
+#include <psxspu.h>
+#include <psxcd.h>
+
+// 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 = 0x1fee80ff; // 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
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/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/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/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/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 <psxgpu.h>
-
-#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 <psxgte.h>
#include <psxgpu.h>
#include <psxpad.h>
+#include <psxcd.h>
#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/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
diff --git a/libpsn00b/CMakeLists.txt b/libpsn00b/CMakeLists.txt
index 7b5f7a5..829a2f7 100644
--- a/libpsn00b/CMakeLists.txt
+++ b/libpsn00b/CMakeLists.txt
@@ -58,12 +58,28 @@ 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.
+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..374b22a
--- /dev/null
+++ b/libpsn00b/build.json.template
@@ -0,0 +1,8 @@
+{
+ "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/flags.cmake b/libpsn00b/cmake/flags.cmake
new file mode 100644
index 0000000..249c5b4
--- /dev/null
+++ b/libpsn00b/cmake/flags.cmake
@@ -0,0 +1,151 @@
+# 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.
+
+if(NOT TARGET psn00bsdk_common) # Include guard
+
+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
+ $<$<COMPILE_LANGUAGE:CXX>:
+ # 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
+ $<$<CONFIG:DEBUG>:DEBUG=1>
+)
+
+# Options for executables without support for dynamic linking:
+# - Position-independent code disabled
+# - GP-relative addressing enabled only for local symbols
+# - ABI-compatible calls disabled (incompatible with GP-relative addr)
+target_compile_options(
+ psn00bsdk_static_exe INTERFACE
+ -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)
+
+endif()
diff --git a/libpsn00b/cmake/internal_setup.cmake b/libpsn00b/cmake/internal_setup.cmake
index b0c4591..7d6bfdd 100644
--- a/libpsn00b/cmake/internal_setup.cmake
+++ b/libpsn00b/cmake/internal_setup.cmake
@@ -7,8 +7,15 @@
cmake_minimum_required(VERSION 3.20)
include(GNUInstallDirs)
-# IMPORTANT TODO: set a version number
-set(PSN00BSDK_VERSION 0.1.0)
+# 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_GIT_TAG GET ${_json} git_tag)
+ string(JSON PSN00BSDK_GIT_COMMIT GET ${_json} git_commit)
+endif()
## Settings (can be overridden by projects)
@@ -22,9 +29,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 +64,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(
# <target name> <STATIC|DYNAMIC>
diff --git a/libpsn00b/cmake/sdk.cmake b/libpsn00b/cmake/sdk.cmake
index ae23a06..d6d9bcd 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"
)
@@ -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
@@ -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/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}
- $<$<COMPILE_LANGUAGE:C,CXX>:${_cflags}>
- $<$<COMPILE_LANGUAGE:CXX>:${_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
- $<$<CONFIG:DEBUG>:DEBUG=1>
-)
-
-# Options for executables without support for dynamic linking:
-# - Position-independent code disabled
-# - GP-relative addressing enabled only for local symbols
-# - ABI-compatible calls disabled (incompatible with GP-relative addr)
-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/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/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/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 <stdint.h>
+
+/* 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/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/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)*/
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 <string.h>
#include <strings.h>
+// 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 <string.h>
#include <stdlib.h>
+// 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 <stdio.h>
#include <stdlib.h>
#include <string.h>
+#include <stdarg.h>
+
+// 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/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 40a40af..d1c1b18 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;
}
@@ -783,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..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;
@@ -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);
diff --git a/libpsn00b/psxetc/_dl_resolve_wrapper.s b/libpsn00b/psxetc/_dl_resolve_wrapper.s
index 069ee84..01ebf3a 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
@@ -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/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;
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 <sys/types.h>
#include <psxgpu.h>
-int GetTimInfo(u_long *tim, TIM_IMAGE *timimg) {
+int GetTimInfo(const u_long *tim, TIM_IMAGE *timimg) {
u_long *rtim;
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"
+ }
+ }
+ ]
+}