aboutsummaryrefslogtreecommitdiff
path: root/doc
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 /doc
parent08de895e8582dbc70b639ae5f511ab9ebfb4d68a (diff)
parente9475e283a82665fe6c19bebc3318b5084f15a2e (diff)
downloadpsn00bsdk-05d44488bd5587786f4bd0286fc0f555c79aa46a.tar.gz
Merge pull request #44 from spicyjpeg/actions
GitHub Actions CI, psxcd and libc fixes, new examples
Diffstat (limited to 'doc')
-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.md153
-rw-r--r--doc/known_bugs.md52
-rw-r--r--doc/toolchain.md224
6 files changed, 762 insertions, 273 deletions
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/doc/installation.md b/doc/installation.md
new file mode 100644
index 0000000..1646653
--- /dev/null
+++ b/doc/installation.md
@@ -0,0 +1,153 @@
+
+# Getting started with PSn00bSDK
+
+## Building and installing
+
+The instructions below are for Windows and Linux. Building on macOS hasn't been
+tested extensively yet, however it should work once the GCC toolchain is built
+and installed properly.
+
+1. Install prerequisites and a host compiler toolchain. On Linux (most distros)
+ install the following packages from your distro's package manager:
+
+ - `git`
+ - `build-essential`, `base-devel` or similar
+ - `make` or `ninja-build`
+ - `cmake` (3.20+ is required, download it from
+ [here](https://cmake.org/download) if your package manager only provides
+ older versions)
+
+ On Windows you can obtain these dependencies by installing
+ [MSys2](https://www.msys2.org), opening the "MSys2 MSYS" shell and running:
+
+ ```bash
+ pacman -Syu git mingw-w64-x86_64-make mingw-w64-x86_64-ninja mingw-w64-x86_64-cmake mingw-w64-x86_64-gcc
+ ```
+
+ If you are prompted to close the shell, you may have to reopen it afterwards
+ and rerun the command to finish installation.
+ **Do not use the MSys2 shell for the next steps**, use a regular command
+ prompt or PowerShell instead.
+
+ Add these directories (replace `C:\msys64` if you installed MSys2 to a
+ different location) to the `PATH` environment variable using System
+ Properties:
+
+ - `C:\msys64\mingw64\bin`
+ - `C:\msys64\usr\bin`
+
+2. Download a precompiled copy of the GCC toolchain for `mipsel-none-elf` from
+ the releases page and extract it into one of the directories listed in
+ step 3. If you want to build the toolchain yourself, see
+ [toolchain.md](toolchain.md).
+
+ **NOTE**: PSn00bSDK is also compatible with toolchains that target
+ `mipsel-unknown-elf`. If you already have such a toolchain, you can use it
+ by passing `-DPSN00BSDK_TARGET=mipsel-unknown-elf` to CMake when configuring
+ the SDK (see step 5).
+
+3. If you chose a non-standard install location for the toolchain, add the
+ `bin` subfolder (inside the top-level toolchain directory) to the `PATH`
+ environment variable. This step is unnecessary if you installed/extracted
+ the toolchain into any of these directories:
+
+ - `C:\Program Files\mipsel-none-elf`
+ - `C:\Program Files (x86)\mipsel-none-elf`
+ - `C:\mipsel-none-elf`
+ - `/usr/local/mipsel-none-elf`
+ - `/usr/mipsel-none-elf`
+ - `/opt/mipsel-none-elf`
+
+4. Clone the PSn00bSDK repo, then run the following command from the PSn00bSDK
+ repository to download additional dependencies:
+
+ ```bash
+ git submodule update --init --recursive
+ ```
+
+5. Compile the libraries, tools and examples using CMake:
+
+ ```bash
+ cmake --preset default .
+ cmake --build ./build
+ ```
+
+ If you want to install the SDK to a custom location rather than the default
+ one (`C:\Program Files\PSn00bSDK` or `/usr/local` depending on your OS), add
+ `--install-prefix <INSTALL_PATH>` to the first command. 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
+ `-G "MSYS Makefiles"` on Windows) to the first command to build using `make`
+ instead.
+
+6. Install the SDK to the path you chose (add `sudo` or run it from a command
+ prompt with admin privileges if necessary):
+
+ ```bash
+ cmake --install ./build
+ ```
+
+ This will create and populate the following directories:
+
+ - `<INSTALL_PATH>/bin`
+ - `<INSTALL_PATH>/lib/libpsn00b`
+ - `<INSTALL_PATH>/share/psn00bsdk`
+
+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)
+(Windows only), [DuckStation](https://github.com/stenzek/duckstation) or
+[pcsx-redux](https://github.com/grumpycoders/pcsx-redux).
+**Avoid ePSXe and anything based on MAME** as they are inaccurate.
+
+## Building installer packages
+
+CPack can be used to build NSIS-based installers, DEB/RPM packages and zipped
+releases that include built SDK libraries, headers as well as the GCC toolchain.
+Distributing prebuilt releases is however discouraged since PSn00bSDK is still
+far from being feature-complete.
+
+1. Follow steps 1-4 above to set up the toolchain, then install
+ [NSIS](https://nsis.sourceforge.io/Download) on Windows or `dpkg` and `rpm`
+ on Linux.
+
+2. Run the following commands from the PSn00bSDK directory (pass the
+ appropriate options to the first command if necessary):
+
+ ```bash
+ cmake --preset package .
+ cmake --build ./build -t package
+ ```
+
+ All built packages will be copied to the `build/packages` folder.
+
+## Creating a project
+
+1. Copy the contents of `<INSTALL_PATH>/share/psn00bsdk/template` (or the
+ `template` folder within the repo) to your new project's root directory.
+
+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 --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.
+
+The toolchain script defines a few CMake macros to create PS1 executables, DLLs
+and CD images. See the [reference](cmake_reference.md) for details.
+
+-----------------------------------------
+_Last updated on 2021-12-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/doc/toolchain.md b/doc/toolchain.md
new file mode 100644
index 0000000..8e28c24
--- /dev/null
+++ b/doc/toolchain.md
@@ -0,0 +1,224 @@
+
+# Building the GCC toolchain
+
+If you wish to build the toolchain yourself, beware that this process can get
+pretty tedious if your machine is not fairly recent. Ensure you have at least a
+quad-core processor and 4 GB of free space before continuing.
+
+You'll need a Linux environment, even if you want to build a Windows toolchain
+(as GCC is basically impossible to build under Windows but can be cross-compiled
+via MinGW). Due to how the GCC build process works, you'll have to build a Linux
+version of the toolchain first to be able to compile it for Windows. This
+basically means you will have to build the whole toolchain twice if you want to
+target Windows.
+
+These instructions are for Debian/Ubuntu, however it should be relatively easy
+to follow them if you are using another distro. If you do not have access to a
+Linux system already, consider spinning up a VM (a headless Debian or Ubuntu
+Server install is recommended) or using WSL, whose setup is out of the scope of
+this guide.
+
+## Choosing a GCC version
+
+PSn00bSDK *should* work with any GCC version. In most cases you'll want to get
+the latest stable release of GCC and binutils. If for some reason you are having
+problems you may try building one of the following versions, which have been
+tested extensively:
+
+- ~~GCC 7.4.0 with binutils 2.31~~ (the linker fails to build PS1 DLLs)
+- GCC **11.1.0** with binutils **2.36**
+- GCC **11.2.0** with binutils **2.37**
+
+If you wish to pick an older GCC release but don't know which binutils version
+it requires, see [here](https://wiki.osdev.org/Cross-Compiler_Successful_Builds)
+for a compatibility table.
+
+## Downloading GCC
+
+1. Run the following commands to install a host toolchain and prerequisites
+ (adapt them for non-Debian distros if necessary):
+
+ ```bash
+ sudo apt update
+ sudo apt install -y build-essential make wget
+ ```
+
+2. Create an empty directory to store build artifacts in. You'll be able to
+ delete it once the toolchain is installed.
+
+3. Download the GCC and binutils source packages from
+ [here](https://ftpmirror.gnu.org/gnu) and unzip them into the folder you
+ created, or run the following commands to do the same (replace `<VERSION>`
+ with the versions you chose):
+
+ ```bash
+ wget https://ftpmirror.gnu.org/gnu/binutils/binutils-<VERSION>.tar.xz
+ wget https://ftpmirror.gnu.org/gnu/gcc/gcc-<VERSION>/gcc-<VERSION>.tar.xz
+ tar xvf binutils-<VERSION>.tar.xz
+ tar xvf gcc-<VERSION>.tar.xz
+ rm -f *.tar.xz
+ ```
+
+4. From the extracted GCC directory run the `download_prerequisites` script to
+ download additional dependencies:
+
+ ```bash
+ cd gcc-<VERSION>
+ ./contrib/download_prerequisites
+ ```
+
+## Building binutils
+
+1. Go back to the folder you made earlier and create a new subdirectory to build
+ binutils in (don't create it inside the extracted binutils source directory).
+ Call it `binutils-build` or whatever.
+
+2. Run the binutils configuration script from that folder:
+
+ ```bash
+ ../binutils-<VERSION>/configure \
+ --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-none-elf` you can change the `--prefix` option.
+
+3. Compile and install binutils (this will take a few minutes to finish):
+
+ ```bash
+ make -j 4
+ sudo make install-strip
+ ```
+
+ Increase `-j 4` to speed up the build if your machine or VM has more than 4
+ CPU cores.
+
+ **NOTE**: if the build fails with a "`uint` undeclared" or similar error, try
+ editing the source file that caused the error and pasting this line at the
+ beginning:
+
+ ```c
+ typedef unsigned int uint;
+ ```
+
+ Rerun `make` to resume the build after saving the file.
+
+## Building GCC
+
+The process is mostly the same as binutils, just with different configuration
+options.
+
+1. Go back to the main directory and create an empty `gcc-build` (or whatever)
+ subfolder.
+
+2. Run the GCC configuration script from there:
+
+ ```bash
+ ../gcc-<VERSION>/configure \
+ --prefix=/usr/local/mipsel-none-elf --target=mipsel-none-elf \
+ --disable-docs --disable-nls --disable-libada --disable-libssp \
+ --disable-libquadmath --disable-libstdc++-v3 --with-float=soft \
+ --enable-languages=c,c++ --with-gnu-as --with-gnu-ld
+ ```
+
+ If you previously set a custom installation path, remember to set it here as
+ well (it must be the same).
+
+3. Compile and install GCC (will take a long time, usually around half an hour):
+
+ ```bash
+ make -j 4
+ sudo make install-strip
+ ```
+
+ Increase `-j 4` to speed up the build if your machine or VM has more than 4
+ threads.
+
+4. Add the toolchain to the `PATH` environment variable. This is required to
+ rebuild the toolchain for Windows (see below), but it will also allow
+ PSn00bSDK to find the toolchain if you installed it in a custom location.
+
+ Edit the `.bashrc` or `.bash_profile` file in your home directory and add
+ this line at the end (replace the path with the install location you chose
+ earlier, but keep the `/bin` at the end):
+
+ ```bash
+ export PATH=$PATH:/usr/local/mipsel-none-elf/bin
+ ```
+
+ Restart the shell by closing and reopening the terminal window or SSH
+ connection afterwards.
+
+## Rebuilding for Windows
+
+At this point you should be able to build and install PSn00bSDK on your Linux
+system. The instructions below are for building a second copy of the toolchain
+that runs on Windows.
+
+1. Install the MinGW host toolchain:
+
+ ```bash
+ sudo apt install -y g++-mingw-w64-x86-64
+ ```
+
+2. Create two new `binutils-win` and `gcc-win` folders in the directory you
+ extracted/built everything in.
+
+3. From the `binutils-win` directory, rerun the binutils configuration script
+ with the following options (do not change the installation path):
+
+ ```bash
+ ../binutils-<VERSION>/configure \
+ --build=x86_64-linux-gnu --host=x86_64-w64-mingw32 \
+ --prefix=/tmp/mipsel-none-elf --target=mipsel-none-elf \
+ --disable-docs --disable-nls --with-float=soft
+ ```
+
+ Then build binutils again:
+
+ ```bash
+ make -j 4
+ make install-strip
+ ```
+
+4. Do the same for GCC from the `gcc-win` directory:
+
+ ```bash
+ ../gcc-<VERSION>/configure \
+ --build=x86_64-linux-gnu --host=x86_64-w64-mingw32 \
+ --prefix=/tmp/mipsel-none-elf --target=mipsel-none-elf \
+ --disable-docs --disable-nls --disable-libada --disable-libssp \
+ --disable-libquadmath --disable-libstdc++-v3 --with-float=soft \
+ --enable-languages=c,c++ --with-gnu-as --with-gnu-ld
+ ```
+
+ And build it as usual:
+
+ ```bash
+ make -j 4
+ make install-strip
+ ```
+
+5. Copy the entire `/tmp/mipsel-none-elf` directory over to your Windows
+ machine using VM shared folders, a network share, `scp` or whichever method
+ you prefer. It's recommended to put the toolchain in
+ `C:\Program Files\mipsel-none-elf` or `C:\mipsel-none-elf`.
+
+6. If you want to keep the toolchain in another location and/or use it from the
+ command line, add the `bin` subdirectory inside `mipsel-none-elf` to the
+ `PATH` environment variable (as you did on Linux) using System Properties.
+
+## Note regarding C++ support
+
+C++ support in PSn00bSDK, besides compile-time features like `constexpr`, only
+goes as far as basic classes, namespaces and the ability to dynamically create
+and delete class objects at any point of the program. The required dependencies
+(which are just wrappers around `malloc()` and `free()`) are supplied by `libc`.
+
+Standard C++ libraries are not implemented and likely never going to be
+implemented due to bloat concerns that it may introduce. Besides, the official
+SDK lacks full C++ support as well.
+
+-----------------------------------------
+_Last updated on 2021-11-23 by spicyjpeg_